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
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
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>
42 #include <sys/resource.h>
57 #include <login_cap.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
,
74 int use_kerberos5
= 1;
78 #define LOGIN_CAP_ARG(x) x
80 #define LOGIN_CAP_ARG(x)
82 #if defined(KERBEROS5)
83 #define KERBEROS_ARG(x) x
85 #define KERBEROS_ARG(x)
87 #define COMMON_ARG(x) x
88 #define ARGSTR "-" COMMON_ARG("flm") LOGIN_CAP_ARG("c:") KERBEROS_ARG("K")
92 static void usage(void);
94 extern char **environ
;
97 main(int argc
, char **argv
)
104 const char *p
, *user
, *shell
= NULL
;
105 const char **nargv
, **np
;
106 char **g
, **cleanenv
, *username
, *entered_pass
;
110 int asme
, ch
, asthem
, fastlogin
, prio
, i
;
111 enum { UNSET
, YES
, NO
} iscsh
= UNSET
;
117 #if defined(KERBEROS5)
121 char *su_principal_name
, *ccname
;
122 krb5_context context
;
123 krb5_principal su_principal
;
125 char shellbuf
[MAXPATHLEN
];
130 asme
= asthem
= fastlogin
= 0;
132 while((ch
= getopt(argc
, argv
, ARGSTR
)) != -1)
134 #if defined(KERBEROS5)
161 if (optind
< argc
&& strcmp(argv
[optind
], "-") == 0) {
168 user
= argv
[optind
++];
170 if (strlen(user
) > MAXLOGNAME
- 1) {
171 fprintf(stderr
, "su: username too long.\n");
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
];
189 #if defined(KERBEROS5)
190 k
= auth_getval("auth_list");
191 if (k
&& !strstr(k
, "kerberos")) {
194 su_principal_name
= NULL
;
196 if (krb5_init_context(&context
) != 0)
200 prio
= getpriority(PRIO_PROCESS
, 0);
203 setpriority(PRIO_PROCESS
, 0, -2);
204 openlog("su", LOG_CONS
, 0);
206 /* get current login name and shell */
208 username
= getlogin();
209 if (username
== NULL
|| (pwd
= getpwnam(username
)) == NULL
||
211 pwd
= getpwuid(ruid
);
213 errx(1, "who are you?");
214 username
= strdup(pwd
->pw_name
);
216 if (username
== NULL
)
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';
224 shell
= _PATH_BSHELL
;
229 /* get target login information, default to root */
230 if ((pwd
= getpwnam(user
)) == NULL
) {
231 errx(1, "unknown login: %s", user
);
235 lc
= login_getpwclass(pwd
);
238 errx(1, "only root may use -c");
239 lc
= login_getclass(class);
241 errx(1, "unknown class: %s", class);
246 targetpass
= strdup(pwd
->pw_passwd
);
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
);
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
) {
275 "you are not in the correct group (%s) to su %s.",
279 if (strcmp(username
, *g
) == 0) {
287 /* if target requires a password, verify it */
288 if (*pwd
->pw_passwd
) {
292 pwd
= getpwnam(username
);
295 entered_pass
= skey_getpass("Password:", pwd
, 1);
296 if (!(!strcmp(pwd
->pw_passwd
, skey_crypt(entered_pass
,
297 pwd
->pw_passwd
, pwd
, 1))
299 || (iswheelsu
&& !strcmp(targetpass
,
300 crypt(entered_pass
, targetpass
)))
304 entered_pass
= getpass("Password:");
305 if (strcmp(pwd
->pw_passwd
, crypt(entered_pass
,
309 if (use_kerberos5
&& kerberos5(context
,
310 username
, user
, su_principal
,
314 fprintf(stderr
, "Sorry\n");
315 syslog(LOG_AUTH
|LOG_WARNING
,
316 "BAD SU %s to %s%s", username
, user
,
320 #if defined(KERBEROS5)
326 pwd
= getpwnam(user
);
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
,
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
;
347 shell
= _PATH_BSHELL
;
351 /* if we're forking a csh, we want to slightly muck the args */
352 if (iscsh
== UNSET
) {
353 p
= strrchr(shell
, '/');
358 if ((iscsh
= strcmp(p
, "csh") ? NO
: YES
) == NO
)
359 iscsh
= strcmp(p
, "tcsh") ? NO
: YES
;
362 setpriority(PRIO_PROCESS
, 0, prio
);
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");
376 /* set permissions */
377 if (setgid(pwd
->pw_gid
) < 0)
379 if (initgroups(user
, pwd
->pw_gid
))
380 errx(1, "initgroups failed");
381 if (setuid(pwd
->pw_uid
) < 0)
389 ccname
= getenv("KRB5CCNAME");
391 if ((cleanenv
= calloc(20, sizeof(char*))) == NULL
)
396 /* set the su'd user's environment & umask */
397 setusercontext(lc
, pwd
, pwd
->pw_uid
, LOGIN_SETPATH
|LOGIN_SETUMASK
|LOGIN_SETENV
);
399 if (setenv("PATH", _PATH_DEFPATH
, 1) == -1)
400 err(1, "setenv: cannot set PATH=%s", _PATH_DEFPATH
);
403 if (setenv("TERM", p
, 1) == -1)
404 err(1, "setenv: cannot set TERM=%s", p
);
408 if (setenv("KRB5CCNAME", ccname
, 1) == -1)
409 err(1, "setenv: cannot set KRB5CCNAME=%s", ccname
);
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
);
431 /* csh strips the first character... */
432 *np
= asthem
? "-su" : iscsh
== YES
? "_su" : "su";
435 syslog(LOG_NOTICE
|LOG_AUTH
, "%s to %s%s",
436 username
, user
, ontty());
442 execv(shell
, __DECONST(char * const *, np
));
449 fprintf(stderr
, "usage: su [-] [-%s] %s[login [args]]\n",
450 KERBEROS_ARG("K") COMMON_ARG("flm"),
467 while (!r
&& (cp
= getusershell()) != NULL
)
468 r
= strcmp(cp
, sh
) == 0;
477 static char buf
[MAXPATHLEN
+ 4];
480 p
= ttyname(STDERR_FILENO
);
482 snprintf(buf
, sizeof(buf
), " on %s", p
);
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
497 * Returns 0 if authentication was successful, or a com_err error code if
501 kerberos5(krb5_context context
, const char *current_user
,
502 const char *target_user
, krb5_principal su_principal
,
506 krb5_get_init_creds_opt gic_opt
;
507 krb5_verify_init_creds_opt vic_opt
;
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
);
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
));
520 krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_opt
, 1);
521 rv
= krb5_verify_init_creds(context
, &creds
, NULL
, NULL
, NULL
,
523 krb5_free_cred_contents(context
, &creds
);
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
));
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.
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
;
556 char *principal_name
, *ccname
, *p
;
560 *su_principal
= NULL
;
561 default_principal
= NULL
;
562 /* Lower privs while messing about with the credentials
567 rv
= seteuid(getuid());
570 p
= getenv("KRB5CCNAME");
574 asprintf(&ccname
, "%s%lu", KRB5_DEFAULT_CCROOT
,
575 (unsigned long)ruid
);
579 rv
= krb5_cc_resolve(context
, ccname
, &ccache
);
582 rv
= krb5_cc_get_principal(context
, ccache
,
584 krb5_cc_close(context
, ccache
);
586 default_principal
= NULL
; /* just to be safe */
591 if (default_principal
== NULL
) {
592 rv
= krb5_make_principal(context
, &default_principal
, NULL
,
595 warnx("Could not determine default principal name.");
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
);
606 warnx("krb5_unparse_name: %s", krb5_get_err_text(context
, rv
));
609 if (strcmp(target_user
, superuser
) == 0) {
610 p
= strrchr(principal_name
, '@');
612 warnx("malformed principal name `%s'", principal_name
);
613 free(principal_name
);
617 asprintf(su_principal_name
, "%s/%s@%s", principal_name
,
619 free(principal_name
);
621 *su_principal_name
= principal_name
;
622 if (*su_principal_name
== NULL
)
624 rv
= krb5_parse_name(context
, *su_principal_name
, &default_principal
);
626 warnx("krb5_parse_name `%s': %s", *su_principal_name
,
627 krb5_get_err_text(context
, rv
));
628 free(*su_principal_name
);
631 *su_principal
= default_principal
;