diff options
author | markm <markm@FreeBSD.org> | 2001-03-27 19:40:51 +0000 |
---|---|---|
committer | markm <markm@FreeBSD.org> | 2001-03-27 19:40:51 +0000 |
commit | f767ca7e60f2f04f4d7f30da195b56abab4296df (patch) | |
tree | 2c45c3c7d9ffb06e8a26fdf6d772beb282ddfad9 /usr.bin/su | |
parent | 51ca15c2dcae271222dd76f75c0c909596323a8c (diff) | |
download | FreeBSD-src-f767ca7e60f2f04f4d7f30da195b56abab4296df.zip FreeBSD-src-f767ca7e60f2f04f4d7f30da195b56abab4296df.tar.gz |
Add full PAM support for account management and sessions.
The PAM_FAIL_CHECK and PAM_END macros in su.c came from the util-linux
package's PAM patches to the BSD login.c
Submitted by: "David J. MacKenzie" <djm@web.us.uu.net>
Diffstat (limited to 'usr.bin/su')
-rw-r--r-- | usr.bin/su/Makefile | 15 | ||||
-rw-r--r-- | usr.bin/su/su.1 | 7 | ||||
-rw-r--r-- | usr.bin/su/su.c | 272 |
3 files changed, 230 insertions, 64 deletions
diff --git a/usr.bin/su/Makefile b/usr.bin/su/Makefile index 99b44fb..c20800d 100644 --- a/usr.bin/su/Makefile +++ b/usr.bin/su/Makefile @@ -4,9 +4,18 @@ PROG= su SRCS= su.c -COPTS+= -DLOGIN_CAP -DSKEY -DPADD= ${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT} -LDADD= -lutil -lskey -lmd -lcrypt +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +.if !defined(NOPAM) +CFLAGS+= -DUSE_PAM +DPADD+= ${LIBPAM} +LDADD+= ${MINUSLPAM} +.else +COPTS+= -DSKEY +DPADD+= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} +LDADD+= -lskey -lmd -lcrypt +.endif .if defined(WHEELSU) COPTS+= -DWHEELSU diff --git a/usr.bin/su/su.1 b/usr.bin/su/su.1 index 2be4385..5024849 100644 --- a/usr.bin/su/su.1 +++ b/usr.bin/su/su.1 @@ -173,6 +173,13 @@ to remind one of its awesome power. .Bl -tag -width /etc/auth.conf -compact .It Pa /etc/auth.conf configure authentication services +.It Pa /etc/pam.conf +if +.Nm +is configured with PAM support, it uses +.Pa /etc/pam.conf +entries with service name +.Dq su .El .Sh SEE ALSO .Xr csh 1 , diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c index 3edacb4..efd46b8 100644 --- a/usr.bin/su/su.c +++ b/usr.bin/su/su.c @@ -60,36 +60,47 @@ static const char rcsid[] = #include <syslog.h> #include <unistd.h> #include <libutil.h> - -#ifdef LOGIN_CAP #include <login_cap.h> -#endif +#ifdef USE_PAM +#include <security/pam_appl.h> +#include <security/pam_misc.h> +#include <signal.h> +#include <sys/wait.h> + +static int export_pam_environment __P((void)); +static int ok_to_export __P((const char *)); + +static pam_handle_t *pamh = NULL; +static char **environ_pam; + +#define PAM_END { \ + if ((retcode = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) { \ + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode)); \ + } \ + if ((retcode = pam_end(pamh,retcode)) != PAM_SUCCESS) { \ + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, retcode)); \ + } \ +} +#else /* !USE_PAM */ #ifdef SKEY #include <skey.h> #endif +#endif /* USE_PAM */ #ifdef KERBEROS #include <openssl/des.h> #include <krb.h> #include <netdb.h> -#ifdef LOGIN_CAP #define ARGSTR "-Kflmc:" -#else -#define ARGSTR "-Kflm" -#endif static int kerberos(char *username, char *user, int uid, char *pword); static int koktologin(char *name, char *toname); int use_kerberos = 1; #else /* !KERBEROS */ -#ifdef LOGIN_CAP #define ARGSTR "-flmc:" -#else -#define ARGSTR "-flm" -#endif #endif /* KERBEROS */ char *ontty __P((void)); @@ -107,17 +118,24 @@ main(argc, argv) char *targetpass; int iswheelsu; #endif /* WHEELSU */ - char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np; - struct group *gr; + char *p, *user, *shell=NULL, *username, *cleanenv = NULL, **nargv, **np; uid_t ruid; gid_t gid; int asme, ch, asthem, fastlogin, prio, i; enum { UNSET, YES, NO } iscsh = UNSET; -#ifdef LOGIN_CAP login_cap_t *lc; char *class=NULL; int setwhat; -#endif +#ifdef USE_PAM + int retcode; + struct pam_conv conv = { misc_conv, NULL }; + char myhost[MAXHOSTNAMELEN + 1], *mytty; + int statusp=0; + int child_pid, child_pgrp, ret_pid; +#else /* !USE_PAM */ + char **g; + struct group *gr; +#endif /* USE_PAM */ #ifdef KERBEROS char *k; #endif @@ -147,11 +165,9 @@ main(argc, argv) asme = 1; asthem = 0; break; -#ifdef LOGIN_CAP case 'c': class = optarg; break; -#endif case '?': default: usage(); @@ -161,8 +177,7 @@ main(argc, argv) user = argv[optind++]; if (strlen(user) > MAXLOGNAME - 1) { - (void)fprintf(stderr, "su: username too long.\n"); - exit(1); + errx(1, "username too long"); } if (user == NULL) @@ -189,7 +204,7 @@ main(argc, argv) if (errno) prio = 0; (void)setpriority(PRIO_PROCESS, 0, -2); - openlog("su", LOG_CONS, 0); + openlog("su", LOG_CONS, LOG_AUTH); /* get current login name and shell */ ruid = getuid(); @@ -214,11 +229,61 @@ main(argc, argv) } } +#ifdef USE_PAM + retcode = pam_start("su", user, &conv, &pamh); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode)); + errx(1, "pam_start: %s", pam_strerror(pamh, retcode)); + } + + gethostname(myhost, sizeof(myhost)); + retcode = pam_set_item(pamh, PAM_RHOST, myhost); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode)); + errx(1, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode)); + } + + mytty = ttyname(STDERR_FILENO); + if (!mytty) + mytty = "tty"; + retcode = pam_set_item(pamh, PAM_TTY, mytty); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode)); + errx(1, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode)); + } + + if (ruid) { + retcode = pam_authenticate(pamh, 0); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, retcode)); + errx(1, "Sorry"); + } + + if ((retcode = pam_get_item(pamh, PAM_USER, (const void **) &p)) == PAM_SUCCESS) { + user = p; + } else + syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", + pam_strerror(pamh, retcode)); + + retcode = pam_acct_mgmt(pamh, 0); + if (retcode == PAM_NEW_AUTHTOK_REQD) { + retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, retcode)); + errx(1, "Sorry"); + } + } + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_acct_mgmt: %s", pam_strerror(pamh, retcode)); + errx(1, "Sorry"); + } + } +#endif /* USE_PAM */ + /* get target login information, default to root */ if ((pwd = getpwnam(user)) == NULL) { errx(1, "unknown login: %s", user); } -#ifdef LOGIN_CAP if (class==NULL) { lc = login_getpwclass(pwd); } else { @@ -228,8 +293,8 @@ main(argc, argv) if (lc == NULL) errx(1, "unknown class: %s", class); } -#endif +#ifndef USE_PAM #ifdef WHEELSU targetpass = strdup(pwd->pw_passwd); #endif /* WHEELSU */ @@ -280,18 +345,18 @@ main(argc, argv) #ifdef WHEELSU || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass))) #endif /* WHEELSU */ - )) { -#else + )) +#else /* !SKEY */ p = getpass("Password:"); - if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { -#endif + if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) +#endif /* SKEY */ + { #ifdef KERBEROS if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p))) #endif - { - fprintf(stderr, "Sorry\n"); + { syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); - exit(1); + errx(1, "Sorry"); } } #ifdef WHEELSU @@ -301,17 +366,17 @@ main(argc, argv) #endif /* WHEELSU */ } if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { - fprintf(stderr, "Sorry - account expired\n"); syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); - exit(1); + errx(1, "Sorry - account expired"); } } +#endif /* USE_PAM */ if (asme) { /* if asme and non-standard target shell, must be root */ - if (!chshell(pwd->pw_shell) && ruid) + if (ruid && !chshell(pwd->pw_shell)) errx(1, "permission denied (shell)."); } else if (pwd->pw_shell && *pwd->pw_shell) { shell = pwd->pw_shell; @@ -334,7 +399,49 @@ main(argc, argv) (void)setpriority(PRIO_PROCESS, 0, prio); -#ifdef LOGIN_CAP + /* + * PAM modules might add supplementary groups in + * pam_setcred(), so initialize them first. + */ + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0) + err(1, "setusercontext"); + +#ifdef USE_PAM + retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode)); + } + + /* + * We must fork() before setuid() because we need to call + * pam_setcred(pamh, PAM_DELETE_CRED) as root. + */ + + statusp = 1; + switch ((child_pid = fork())) { + default: + while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) { + if (WIFSTOPPED(statusp)) { + child_pgrp = tcgetpgrp(1); + kill(getpid(), SIGSTOP); + tcsetpgrp(1, child_pgrp); + kill(child_pid, SIGCONT); + statusp = 1; + continue; + } + break; + } + if (ret_pid == -1) + err(1, "waitpid"); + PAM_END; + exit(statusp); + case -1: + err(1, "fork"); + PAM_END; + exit (1); + case 0: +#endif /* USE_PAM */ + /* * Set all user context except for: * Environmental variables @@ -343,7 +450,7 @@ main(argc, argv) * Path */ setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | - LOGIN_SETLOGIN | LOGIN_SETPATH); + LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP); /* * Don't touch resource/priority settings if -m has been @@ -353,15 +460,6 @@ main(argc, argv) setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES); if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) err(1, "setusercontext"); -#else - /* set permissions */ - if (setgid(pwd->pw_gid) < 0) - err(1, "setgid"); - if (initgroups(user, pwd->pw_gid)) - errx(1, "initgroups failed"); - if (setuid(pwd->pw_uid) < 0) - err(1, "setuid"); -#endif if (!asme) { if (asthem) { @@ -369,16 +467,20 @@ main(argc, argv) #ifdef KERBEROS k = getenv("KRBTKFILE"); #endif - if ((cleanenv = calloc(20, sizeof(char*))) == NULL) - errx(1, "calloc"); - cleanenv[0] = NULL; - environ = cleanenv; -#ifdef LOGIN_CAP + environ = &cleanenv; + +#ifdef USE_PAM + /* + * Add any environmental variables that the + * PAM modules may have set. + */ + environ_pam = pam_getenvlist(pamh); + if (environ_pam) + export_pam_environment(); +#endif /* USE_PAM */ + /* set the su'd user's environment & umask */ setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); -#else - (void)setenv("PATH", _PATH_DEFPATH, 1); -#endif if (p) (void)setenv("TERM", p, 1); #ifdef KERBEROS @@ -393,6 +495,9 @@ main(argc, argv) (void)setenv("HOME", pwd->pw_dir, 1); (void)setenv("SHELL", shell, 1); } + + login_close(lc); + if (iscsh == YES) { if (fastlogin) *np-- = "-f"; @@ -404,20 +509,65 @@ main(argc, argv) *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; if (ruid != 0) - syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", + syslog(LOG_NOTICE, "%s to %s%s", username, user, ontty()); - login_close(lc); - execv(shell, np); err(1, "%s", shell); +#ifdef USE_PAM + } +#endif /* USE_PAM */ +} + +#ifdef USE_PAM +static int +export_pam_environment() +{ + char **pp; + + for (pp = environ_pam; *pp != NULL; pp++) { + if (ok_to_export(*pp)) + (void) putenv(*pp); + free(*pp); + } + return PAM_SUCCESS; +} + +/* + * Sanity checks on PAM environmental variables: + * - Make sure there is an '=' in the string. + * - Make sure the string doesn't run on too long. + * - Do not export certain variables. This list was taken from the + * Solaris pam_putenv(3) man page. + */ +static int +ok_to_export(s) + const char *s; +{ + static const char *noexport[] = { + "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH", + "IFS", "PATH", NULL + }; + const char **pp; + size_t n; + + if (strlen(s) > 1024 || strchr(s, '=') == NULL) + return 0; + if (strncmp(s, "LD_", 3) == 0) + return 0; + for (pp = noexport; *pp != NULL; pp++) { + n = strlen(*pp); + if (s[n] == '=' && strncmp(s, *pp, n) == 0) + return 0; + } + return 1; } +#endif /* USE_PAM */ static void usage() { - (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR); - exit(1); + errx(1, "usage: su [%s] [login [args]]", ARGSTR); } int @@ -501,7 +651,7 @@ kerberos(username, user, uid, pword) return (1); } warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); - syslog(LOG_NOTICE|LOG_AUTH, + syslog(LOG_NOTICE, "BAD Kerberos SU: %s to %s%s: %s", username, user, ontty(), krb_err_txt[kerno]); return (1); @@ -528,13 +678,13 @@ kerberos(username, user, uid, pword) if (kerno == KDC_PR_UNKNOWN) { warnx("Warning: TGT not verified."); - syslog(LOG_NOTICE|LOG_AUTH, + syslog(LOG_NOTICE, "%s to %s%s, TGT not verified (%s); %s.%s not registered?", username, user, ontty(), krb_err_txt[kerno], "rcmd", savehost); } else if (kerno != KSUCCESS) { warnx("Unable to use TGT: %s", krb_err_txt[kerno]); - syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", + syslog(LOG_NOTICE, "failed su: %s to %s%s: %s", username, user, ontty(), krb_err_txt[kerno]); dest_tkt(); return (1); @@ -548,9 +698,9 @@ kerberos(username, user, uid, pword) if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, &authdata, "")) != KSUCCESS) { - warnx("kerberos: unable to verify rcmd ticket: %s\n", + warnx("kerberos: unable to verify rcmd ticket: %s", krb_err_txt[kerno]); - syslog(LOG_NOTICE|LOG_AUTH, + syslog(LOG_NOTICE, "failed su: %s to %s%s: %s", username, user, ontty(), krb_err_txt[kerno]); dest_tkt(); |