Remove spurious EISA definitions left over from the initial port of the
[dragonfly.git] / usr.bin / su / su.c
blob900f8933da189a33f6c3809eca2bf7c4afdd52fb
1 /*
2 * Copyright (c) 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 * @(#) Copyright (c) 1988, 1993, 1994 The Regents of the University of California. All rights reserved.
34 * @(#)su.c 8.3 (Berkeley) 4/2/94
35 * $FreeBSD: src/usr.bin/su/su.c,v 1.34.2.4 2002/06/16 21:04:15 nectar Exp $
36 * $DragonFly: src/usr.bin/su/su.c,v 1.9 2006/01/12 13:43:11 corecode Exp $
39 #include <sys/cdefs.h>
40 #include <sys/param.h>
41 #include <sys/time.h>
42 #include <sys/resource.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <grp.h>
47 #include <paths.h>
48 #include <pwd.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <syslog.h>
53 #include <unistd.h>
54 #include <libutil.h>
56 #ifdef LOGIN_CAP
57 #include <login_cap.h>
58 #endif
60 #ifdef SKEY
61 #include <skey.h>
62 #endif
64 #ifdef KERBEROS5
65 #include <krb5.h>
67 static long get_su_principal(krb5_context context, const char *target_user,
68 const char *current_user, char **su_principal_name,
69 krb5_principal *su_principal);
70 static long kerberos5(krb5_context context, const char *current_user,
71 const char *target_user, krb5_principal su_principal,
72 const char *pass);
74 int use_kerberos5 = 1;
75 #endif
77 #ifdef LOGIN_CAP
78 #define LOGIN_CAP_ARG(x) x
79 #else
80 #define LOGIN_CAP_ARG(x)
81 #endif
82 #if defined(KERBEROS5)
83 #define KERBEROS_ARG(x) x
84 #else
85 #define KERBEROS_ARG(x)
86 #endif
87 #define COMMON_ARG(x) x
88 #define ARGSTR "-" COMMON_ARG("flm") LOGIN_CAP_ARG("c:") KERBEROS_ARG("K")
90 char *ontty(void);
91 int chshell(char *);
92 static void usage(void);
94 extern char **environ;
96 int
97 main(int argc, char **argv)
99 struct passwd *pwd;
100 #ifdef WHEELSU
101 char *targetpass;
102 int iswheelsu;
103 #endif /* WHEELSU */
104 const char *p, *user, *shell = NULL;
105 const char **nargv, **np;
106 char **g, **cleanenv, *username, *entered_pass;
107 struct group *gr;
108 uid_t ruid;
109 gid_t gid;
110 int asme, ch, asthem, fastlogin, prio, i;
111 enum { UNSET, YES, NO } iscsh = UNSET;
112 #ifdef LOGIN_CAP
113 login_cap_t *lc;
114 char *class=NULL;
115 int setwhat;
116 #endif
117 #if defined(KERBEROS5)
118 char *k;
119 #endif
120 #ifdef KERBEROS5
121 char *su_principal_name, *ccname;
122 krb5_context context;
123 krb5_principal su_principal;
124 #endif
125 char shellbuf[MAXPATHLEN];
127 #ifdef WHEELSU
128 iswheelsu =
129 #endif /* WHEELSU */
130 asme = asthem = fastlogin = 0;
131 user = "root";
132 while((ch = getopt(argc, argv, ARGSTR)) != -1)
133 switch((char)ch) {
134 #if defined(KERBEROS5)
135 case 'K':
136 use_kerberos5 = 0;
137 break;
138 #endif
139 case 'f':
140 fastlogin = 1;
141 break;
142 case '-':
143 case 'l':
144 asme = 0;
145 asthem = 1;
146 break;
147 case 'm':
148 asme = 1;
149 asthem = 0;
150 break;
151 #ifdef LOGIN_CAP
152 case 'c':
153 class = optarg;
154 break;
155 #endif
156 case '?':
157 default:
158 usage();
161 if (optind < argc && strcmp(argv[optind], "-") == 0) {
162 asme = 0;
163 asthem = 1;
164 optind++;
167 if (optind < argc)
168 user = argv[optind++];
170 if (strlen(user) > MAXLOGNAME - 1) {
171 fprintf(stderr, "su: username too long.\n");
172 exit(1);
175 if (user == NULL)
176 usage();
178 if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) {
179 errx(1, "malloc failure");
182 nargv[argc + 3] = NULL;
183 for (i = argc; i >= optind; i--)
184 nargv[i + 3] = argv[i];
185 np = &nargv[i + 3];
187 argv += optind;
189 #if defined(KERBEROS5)
190 k = auth_getval("auth_list");
191 if (k && !strstr(k, "kerberos")) {
192 use_kerberos5 = 0;
194 su_principal_name = NULL;
195 su_principal = NULL;
196 if (krb5_init_context(&context) != 0)
197 use_kerberos5 = 0;
198 #endif
199 errno = 0;
200 prio = getpriority(PRIO_PROCESS, 0);
201 if (errno)
202 prio = 0;
203 setpriority(PRIO_PROCESS, 0, -2);
204 openlog("su", LOG_CONS, 0);
206 /* get current login name and shell */
207 ruid = getuid();
208 username = getlogin();
209 if (username == NULL || (pwd = getpwnam(username)) == NULL ||
210 pwd->pw_uid != ruid)
211 pwd = getpwuid(ruid);
212 if (pwd == NULL)
213 errx(1, "who are you?");
214 username = strdup(pwd->pw_name);
215 gid = pwd->pw_gid;
216 if (username == NULL)
217 err(1, NULL);
218 if (asme) {
219 if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') {
220 /* copy: pwd memory is recycled */
221 shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf);
222 shellbuf[sizeof shellbuf - 1] = '\0';
223 } else {
224 shell = _PATH_BSHELL;
225 iscsh = NO;
229 /* get target login information, default to root */
230 if ((pwd = getpwnam(user)) == NULL) {
231 errx(1, "unknown login: %s", user);
233 #ifdef LOGIN_CAP
234 if (class==NULL) {
235 lc = login_getpwclass(pwd);
236 } else {
237 if (ruid)
238 errx(1, "only root may use -c");
239 lc = login_getclass(class);
240 if (lc == NULL)
241 errx(1, "unknown class: %s", class);
243 #endif
245 #ifdef WHEELSU
246 targetpass = strdup(pwd->pw_passwd);
247 #endif /* WHEELSU */
249 if (ruid) {
250 #ifdef KERBEROS5
251 if (use_kerberos5) {
252 if (get_su_principal(context, user, username,
253 &su_principal_name, &su_principal) != 0 ||
254 !krb5_kuserok(context, su_principal, user)) {
255 warnx("kerberos5: not in %s's ACL.", user);
256 use_kerberos5 = 0;
259 #endif
261 * Only allow those with pw_gid==0 or those listed in
262 * group zero to su to root. If group zero entry is
263 * missing or empty, then allow anyone to su to root.
264 * iswheelsu will only be set if the user is EXPLICITLY
265 * listed in group zero.
267 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) &&
268 gr->gr_mem && *(gr->gr_mem)) {
269 for (g = gr->gr_mem;; ++g) {
270 if (!*g) {
271 if (gid == 0)
272 break;
273 else
274 errx(1,
275 "you are not in the correct group (%s) to su %s.",
276 gr->gr_name,
277 user);
279 if (strcmp(username, *g) == 0) {
280 #ifdef WHEELSU
281 iswheelsu = 1;
282 #endif /* WHEELSU */
283 break;
287 /* if target requires a password, verify it */
288 if (*pwd->pw_passwd) {
289 #ifdef SKEY
290 #ifdef WHEELSU
291 if (iswheelsu) {
292 pwd = getpwnam(username);
294 #endif /* WHEELSU */
295 entered_pass = skey_getpass("Password:", pwd, 1);
296 if (!(!strcmp(pwd->pw_passwd, skey_crypt(entered_pass,
297 pwd->pw_passwd, pwd, 1))
298 #ifdef WHEELSU
299 || (iswheelsu && !strcmp(targetpass,
300 crypt(entered_pass, targetpass)))
301 #endif /* WHEELSU */
302 )) {
303 #else
304 entered_pass = getpass("Password:");
305 if (strcmp(pwd->pw_passwd, crypt(entered_pass,
306 pwd->pw_passwd))) {
307 #endif
308 #ifdef KERBEROS5
309 if (use_kerberos5 && kerberos5(context,
310 username, user, su_principal,
311 entered_pass) == 0)
312 goto authok;
313 #endif
314 fprintf(stderr, "Sorry\n");
315 syslog(LOG_AUTH|LOG_WARNING,
316 "BAD SU %s to %s%s", username, user,
317 ontty());
318 exit(1);
320 #if defined(KERBEROS5)
321 authok:
323 #endif
324 #ifdef WHEELSU
325 if (iswheelsu) {
326 pwd = getpwnam(user);
328 #endif /* WHEELSU */
330 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) {
331 fprintf(stderr, "Sorry - account expired\n");
332 syslog(LOG_AUTH|LOG_WARNING,
333 "BAD SU %s to %s%s", username,
334 user, ontty());
335 exit(1);
339 if (asme) {
340 /* if asme and non-standard target shell, must be root */
341 if (!chshell(pwd->pw_shell) && ruid)
342 errx(1, "permission denied (shell).");
343 } else if (pwd->pw_shell && *pwd->pw_shell) {
344 shell = pwd->pw_shell;
345 iscsh = UNSET;
346 } else {
347 shell = _PATH_BSHELL;
348 iscsh = NO;
351 /* if we're forking a csh, we want to slightly muck the args */
352 if (iscsh == UNSET) {
353 p = strrchr(shell, '/');
354 if (p)
355 ++p;
356 else
357 p = shell;
358 if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
359 iscsh = strcmp(p, "tcsh") ? NO : YES;
362 setpriority(PRIO_PROCESS, 0, prio);
364 #ifdef LOGIN_CAP
365 /* Set everything now except the environment & umask */
366 setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
368 * Don't touch resource/priority settings if -m has been
369 * used or -l and -c hasn't, and we're not su'ing to root.
371 if ((asme || (!asthem && class == NULL)) && pwd->pw_uid)
372 setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
373 if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
374 err(1, "setusercontext");
375 #else
376 /* set permissions */
377 if (setgid(pwd->pw_gid) < 0)
378 err(1, "setgid");
379 if (initgroups(user, pwd->pw_gid))
380 errx(1, "initgroups failed");
381 if (setuid(pwd->pw_uid) < 0)
382 err(1, "setuid");
383 #endif
385 if (!asme) {
386 if (asthem) {
387 p = getenv("TERM");
388 #ifdef KERBEROS5
389 ccname = getenv("KRB5CCNAME");
390 #endif
391 if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
392 errx(1, "calloc");
393 cleanenv[0] = NULL;
394 environ = cleanenv;
395 #ifdef LOGIN_CAP
396 /* set the su'd user's environment & umask */
397 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
398 #else
399 if (setenv("PATH", _PATH_DEFPATH, 1) == -1)
400 err(1, "setenv: cannot set PATH=%s", _PATH_DEFPATH);
401 #endif
402 if (p) {
403 if (setenv("TERM", p, 1) == -1)
404 err(1, "setenv: cannot set TERM=%s", p);
406 #ifdef KERBEROS5
407 if (ccname) {
408 if (setenv("KRB5CCNAME", ccname, 1) == -1)
409 err(1, "setenv: cannot set KRB5CCNAME=%s", ccname);
411 #endif
412 if (chdir(pwd->pw_dir) < 0)
413 errx(1, "no directory");
415 if (asthem || pwd->pw_uid) {
416 if (setenv("USER", pwd->pw_name, 1) == -1)
417 err(1, "setenv: cannot set USER=%s", pwd->pw_name);
419 if (setenv("HOME", pwd->pw_dir, 1) == -1)
420 err(1, "setenv: cannot set HOME=%s", pwd->pw_dir);
421 if (setenv("SHELL", shell, 1) == -1)
422 err(1, "setenv: cannot set SHELL=%s", shell);
424 if (iscsh == YES) {
425 if (fastlogin)
426 *np-- = "-f";
427 if (asme)
428 *np-- = "-m";
431 /* csh strips the first character... */
432 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
434 if (ruid != 0)
435 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
436 username, user, ontty());
438 #ifdef LOGIN_CAP
439 login_close(lc);
440 #endif
442 execv(shell, __DECONST(char * const *, np));
443 err(1, "%s", shell);
446 static void
447 usage(void)
449 fprintf(stderr, "usage: su [-] [-%s] %s[login [args]]\n",
450 KERBEROS_ARG("K") COMMON_ARG("flm"),
451 #ifdef LOGIN_CAP
452 "[-c class] "
453 #else
455 #endif
457 exit(1);
461 chshell(char *sh)
463 int r = 0;
464 char *cp;
466 setusershell();
467 while (!r && (cp = getusershell()) != NULL)
468 r = strcmp(cp, sh) == 0;
469 endusershell();
470 return r;
473 char *
474 ontty(void)
476 char *p;
477 static char buf[MAXPATHLEN + 4];
479 buf[0] = 0;
480 p = ttyname(STDERR_FILENO);
481 if (p)
482 snprintf(buf, sizeof(buf), " on %s", p);
483 return (buf);
486 #ifdef KERBEROS5
487 const char superuser[] = "root";
489 /* Authenticate using Kerberos 5.
490 * context -- An initialized krb5_context.
491 * current_user -- The current username.
492 * target_user -- The target account name.
493 * su_principal -- The target krb5_principal.
494 * pass -- The user's password.
495 * Note that a valid keytab in the default location with a host entry
496 * must be available.
497 * Returns 0 if authentication was successful, or a com_err error code if
498 * it was not.
500 static long
501 kerberos5(krb5_context context, const char *current_user,
502 const char *target_user, krb5_principal su_principal,
503 const char *pass)
505 krb5_creds creds;
506 krb5_get_init_creds_opt gic_opt;
507 krb5_verify_init_creds_opt vic_opt;
508 long rv;
510 krb5_get_init_creds_opt_init(&gic_opt);
511 krb5_verify_init_creds_opt_init(&vic_opt);
512 rv = krb5_get_init_creds_password(context, &creds, su_principal,
513 pass, NULL, NULL, 0, NULL, &gic_opt);
514 if (rv != 0) {
515 syslog(LOG_NOTICE|LOG_AUTH, "BAD Kerberos5 SU: %s to %s%s: %s",
516 current_user, target_user, ontty(),
517 krb5_get_err_text(context, rv));
518 return (rv);
520 krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_opt, 1);
521 rv = krb5_verify_init_creds(context, &creds, NULL, NULL, NULL,
522 &vic_opt);
523 krb5_free_cred_contents(context, &creds);
524 if (rv != 0) {
525 syslog(LOG_NOTICE|LOG_AUTH, "BAD Kerberos5 SU: %s to %s%s: %s",
526 current_user, target_user, ontty(),
527 krb5_get_err_text(context, rv));
528 return (rv);
530 return (0);
533 /* Determine the target principal given the current user and the target user.
534 * context -- An initialized krb5_context.
535 * target_user -- The target username.
536 * current_user -- The current username.
537 * su_principal_name -- (out) The target principal name.
538 * su_principal -- (out) The target krb5_principal.
540 * When target_user is `root', the su_principal will be a `root
541 * instance', e.g. `luser/root@REA.LM'. Otherwise, the su_principal
542 * will simply be the current user's default principal name. Note that
543 * in any case, if KRB5CCNAME is set and a credentials cache exists, the
544 * principal name found there will be the `starting point', rather than
545 * the current_user parameter.
547 * Returns 0 for success, or a com_err error code on failure.
549 static long
550 get_su_principal(krb5_context context, const char *target_user,
551 const char *current_user, char **su_principal_name,
552 krb5_principal *su_principal)
554 krb5_principal default_principal;
555 krb5_ccache ccache;
556 char *principal_name, *ccname, *p;
557 long rv;
558 uid_t euid, ruid;
560 *su_principal = NULL;
561 default_principal = NULL;
562 /* Lower privs while messing about with the credentials
563 * cache.
565 ruid = getuid();
566 euid = geteuid();
567 rv = seteuid(getuid());
568 if (rv != 0)
569 return (errno);
570 p = getenv("KRB5CCNAME");
571 if (p != NULL)
572 ccname = strdup(p);
573 else {
574 asprintf(&ccname, "%s%lu", KRB5_DEFAULT_CCROOT,
575 (unsigned long)ruid);
577 if (ccname == NULL)
578 return (errno);
579 rv = krb5_cc_resolve(context, ccname, &ccache);
580 free(ccname);
581 if (rv == 0) {
582 rv = krb5_cc_get_principal(context, ccache,
583 &default_principal);
584 krb5_cc_close(context, ccache);
585 if (rv != 0)
586 default_principal = NULL; /* just to be safe */
588 rv = seteuid(euid);
589 if (rv != 0)
590 return (errno);
591 if (default_principal == NULL) {
592 rv = krb5_make_principal(context, &default_principal, NULL,
593 current_user, NULL);
594 if (rv != 0) {
595 warnx("Could not determine default principal name.");
596 return (rv);
599 /* Now that we have some principal, if the target account is
600 * `root', then transform it into a `root' instance, e.g.
601 * `user@REA.LM' -> `user/root@REA.LM'.
603 rv = krb5_unparse_name(context, default_principal, &principal_name);
604 krb5_free_principal(context, default_principal);
605 if (rv != 0) {
606 warnx("krb5_unparse_name: %s", krb5_get_err_text(context, rv));
607 return (rv);
609 if (strcmp(target_user, superuser) == 0) {
610 p = strrchr(principal_name, '@');
611 if (p == NULL) {
612 warnx("malformed principal name `%s'", principal_name);
613 free(principal_name);
614 return (rv);
616 *p++ = '\0';
617 asprintf(su_principal_name, "%s/%s@%s", principal_name,
618 superuser, p);
619 free(principal_name);
620 } else
621 *su_principal_name = principal_name;
622 if (*su_principal_name == NULL)
623 return errno;
624 rv = krb5_parse_name(context, *su_principal_name, &default_principal);
625 if (rv != 0) {
626 warnx("krb5_parse_name `%s': %s", *su_principal_name,
627 krb5_get_err_text(context, rv));
628 free(*su_principal_name);
629 return (rv);
631 *su_principal = default_principal;
632 return 0;
635 #endif