newgrp.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*-
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2002 Tim J. Robbins.
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  20. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26. * SUCH DAMAGE.
  27. */
  28. /*
  29. * newgrp -- change to a new group
  30. */
  31. #include <sys/types.h>
  32. #include <err.h>
  33. #include <errno.h>
  34. #include <grp.h>
  35. #include <limits.h>
  36. #include <login_cap.h>
  37. #include <paths.h>
  38. #include <pwd.h>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <unistd.h>
  43. static void addgroup(const char *grpname);
  44. static void doshell(void);
  45. static int inarray(gid_t, const gid_t[], int);
  46. static void loginshell(void);
  47. static void restoregrps(void);
  48. static void usage(void);
  49. static struct passwd *pwd;
  50. static uid_t euid;
  51. extern char **environ;
  52. /* Manipulate effective user ID. */
  53. #define PRIV_START do { \
  54. if (seteuid(euid) < 0) \
  55. err(1, "seteuid"); \
  56. } while (0)
  57. #define PRIV_END do { \
  58. if (seteuid(getuid()) < 0) \
  59. err(1, "seteuid"); \
  60. } while (0)
  61. int
  62. main(int argc, char *argv[])
  63. {
  64. int ch, login;
  65. if ((euid = geteuid()) != 0)
  66. warnx("need root permissions to function properly, check setuid bit");
  67. if (seteuid(getuid()) < 0)
  68. err(1, "seteuid");
  69. if ((pwd = getpwuid(getuid())) == NULL)
  70. errx(1, "unknown user");
  71. login = 0;
  72. while ((ch = getopt(argc, argv, "-l")) != -1) {
  73. switch (ch) {
  74. case '-': /* Obsolescent */
  75. case 'l':
  76. login = 1;
  77. break;
  78. default:
  79. usage();
  80. }
  81. }
  82. argc -= optind;
  83. argv += optind;
  84. switch (argc) {
  85. case 0:
  86. restoregrps();
  87. break;
  88. case 1:
  89. addgroup(*argv);
  90. break;
  91. default:
  92. usage();
  93. }
  94. if (seteuid(euid) < 0)
  95. err(1, "seteuid");
  96. if (setuid(getuid()) < 0)
  97. err(1, "setuid");
  98. if (login)
  99. loginshell();
  100. else
  101. doshell();
  102. /*NOTREACHED*/
  103. exit(1);
  104. }
  105. static void
  106. usage(void)
  107. {
  108. fprintf(stderr, "usage: newgrp [-l] [group]\n");
  109. exit(1);
  110. }
  111. static void
  112. restoregrps(void)
  113. {
  114. int initres, setres;
  115. PRIV_START;
  116. initres = initgroups(pwd->pw_name, pwd->pw_gid);
  117. setres = setgid(pwd->pw_gid);
  118. PRIV_END;
  119. if (initres < 0)
  120. warn("initgroups");
  121. if (setres < 0)
  122. warn("setgid");
  123. }
  124. static void
  125. addgroup(const char *grpname)
  126. {
  127. gid_t *grps;
  128. long lgid, ngrps_max;
  129. int dbmember, i, ngrps;
  130. gid_t egid;
  131. struct group *grp;
  132. char *ep, *pass, *cryptpw;
  133. char **p;
  134. egid = getegid();
  135. /* Try it as a group name, then a group id. */
  136. if ((grp = getgrnam(grpname)) == NULL)
  137. if ((lgid = strtol(grpname, &ep, 10)) <= 0 || *ep != '\0' ||
  138. (grp = getgrgid((gid_t)lgid)) == NULL ) {
  139. warnx("%s: bad group name", grpname);
  140. return;
  141. }
  142. /*
  143. * If the user is not a member of the requested group and the group
  144. * has a password, prompt and check it.
  145. */
  146. dbmember = 0;
  147. if (pwd->pw_gid == grp->gr_gid)
  148. dbmember = 1;
  149. for (p = grp->gr_mem; *p != NULL; p++)
  150. if (strcmp(*p, pwd->pw_name) == 0) {
  151. dbmember = 1;
  152. break;
  153. }
  154. if (!dbmember && *grp->gr_passwd != '\0' && getuid() != 0) {
  155. pass = getpass("Password:");
  156. if (pass == NULL)
  157. return;
  158. cryptpw = crypt(pass, grp->gr_passwd);
  159. if (cryptpw == NULL || strcmp(grp->gr_passwd, cryptpw) != 0) {
  160. fprintf(stderr, "Sorry\n");
  161. return;
  162. }
  163. }
  164. ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1;
  165. if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL)
  166. err(1, "malloc");
  167. if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) {
  168. warn("getgroups");
  169. goto end;
  170. }
  171. /* Remove requested gid from supp. list if it exists. */
  172. if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) {
  173. for (i = 0; i < ngrps; i++)
  174. if (grps[i] == grp->gr_gid)
  175. break;
  176. ngrps--;
  177. memmove(&grps[i], &grps[i + 1], (ngrps - i) * sizeof(gid_t));
  178. PRIV_START;
  179. if (setgroups(ngrps, (const gid_t *)grps) < 0) {
  180. PRIV_END;
  181. warn("setgroups");
  182. goto end;
  183. }
  184. PRIV_END;
  185. }
  186. PRIV_START;
  187. if (setgid(grp->gr_gid)) {
  188. PRIV_END;
  189. warn("setgid");
  190. goto end;
  191. }
  192. PRIV_END;
  193. grps[0] = grp->gr_gid;
  194. /* Add old effective gid to supp. list if it does not exist. */
  195. if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) {
  196. if (ngrps == ngrps_max)
  197. warnx("too many groups");
  198. else {
  199. grps[ngrps++] = egid;
  200. PRIV_START;
  201. if (setgroups(ngrps, (const gid_t *)grps)) {
  202. PRIV_END;
  203. warn("setgroups");
  204. goto end;
  205. }
  206. PRIV_END;
  207. }
  208. }
  209. end:
  210. free(grps);
  211. }
  212. static int
  213. inarray(gid_t gid, const gid_t grps[], int ngrps)
  214. {
  215. int i;
  216. for (i = 0; i < ngrps; i++)
  217. if (grps[i] == gid)
  218. return (1);
  219. return (0);
  220. }
  221. /*
  222. * Set the environment to what would be expected if the user logged in
  223. * again; this performs the same steps as su(1)'s -l option.
  224. */
  225. static void
  226. loginshell(void)
  227. {
  228. char *args[2], **cleanenv, *term, *ticket;
  229. const char *shell;
  230. login_cap_t *lc;
  231. shell = pwd->pw_shell;
  232. if (*shell == '\0')
  233. shell = _PATH_BSHELL;
  234. if (chdir(pwd->pw_dir) < 0) {
  235. warn("%s", pwd->pw_dir);
  236. chdir("/");
  237. }
  238. term = getenv("TERM");
  239. ticket = getenv("KRBTKFILE");
  240. if ((cleanenv = calloc(20, sizeof(char *))) == NULL)
  241. err(1, "calloc");
  242. *cleanenv = NULL;
  243. environ = cleanenv;
  244. lc = login_getpwclass(pwd);
  245. setusercontext(lc, pwd, pwd->pw_uid,
  246. LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
  247. login_close(lc);
  248. setenv("USER", pwd->pw_name, 1);
  249. setenv("SHELL", shell, 1);
  250. setenv("HOME", pwd->pw_dir, 1);
  251. if (term != NULL)
  252. setenv("TERM", term, 1);
  253. if (ticket != NULL)
  254. setenv("KRBTKFILE", ticket, 1);
  255. if (asprintf(args, "-%s", shell) < 0)
  256. err(1, "asprintf");
  257. args[1] = NULL;
  258. execv(shell, args);
  259. err(1, "%s", shell);
  260. }
  261. static void
  262. doshell(void)
  263. {
  264. const char *shell;
  265. shell = pwd->pw_shell;
  266. if (*shell == '\0')
  267. shell = _PATH_BSHELL;
  268. execl(shell, shell, (char *)NULL);
  269. err(1, "%s", shell);
  270. }