From owner-freebsd-security Tue Jan 23 10:47: 3 2001 Delivered-To: freebsd-security@freebsd.org Received: from jenkins.web.us.uu.net (jenkins.web.us.uu.net [208.240.88.32]) by hub.freebsd.org (Postfix) with ESMTP id CE1AF37B699 for ; Tue, 23 Jan 2001 10:46:18 -0800 (PST) Received: from dagger.web.us.uu.net (dagger.web.us.uu.net [208.211.134.28]) by jenkins.web.us.uu.net (Postfix) with ESMTP id 7455C12686; Tue, 23 Jan 2001 13:46:17 -0500 (EST) Received: by dagger.web.us.uu.net (Postfix, from userid 515) id E675046C7; Tue, 23 Jan 2001 13:46:11 -0500 (EST) From: "David J. MacKenzie" To: freebsd-security@freebsd.org Cc: djm@web.us.uu.net Subject: PAM patches, iteration 4 X-Tom-Swiftie: "I've finished counting the horses," Tom said summarily Message-Id: <20010123184611.E675046C7@dagger.web.us.uu.net> Date: Tue, 23 Jan 2001 13:46:11 -0500 (EST) Sender: owner-freebsd-security@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org I've updated the docs to reflect PAM, cleaned up some error handling, and included my patch to work around the pam_setcred() dispatch problem. I also removed the non-logincap code path from su and rshd, since it's already mandatory in login. As before, this replaces my earlier patches. I'd welcome having PAM experts examine them closely. I think they're ready for a wider audience. --- ./contrib/libpam/doc/man/pam_setcred.3 1998/11/18 01:20:54 1.1 +++ ./contrib/libpam/doc/man/pam_setcred.3 2001/01/23 01:24:58 @@ -16,7 +16,7 @@ This function is used to establish, maintain and delete the credentials of a user. It should be called after a user has been -authenticated and before a session is opened for the user (with +authenticated and after a session is opened for the user (with .BR pam_open_session "(3))." It should be noted that credentials come in many forms. Examples @@ -29,6 +29,23 @@ .BR initgroups "(2) " (or equivalent) should have been performed. +This function runs the +.BI pam_sm_setcred() +functions for all +.BR auth +modules defined for the current PAM service, even if a control flag +of +.BR sufficient +or +.BR requisite +is present. This is to ensure that a module whose +.BI pam_sm_setcred() +function returns +.BR PAM_SUCCESS +even if that module did not succeed in +.BI pam_authenticate() +does not prevent modules listed later from setting their credentials. +It also appears to be the behavior of Solaris PAM. .SH "VALID FLAGS" .TP .BR PAM_ESTABLISH_CRED --- ./contrib/libpam/libpam/pam_handlers.c 2001/01/22 20:19:52 1.1 +++ ./contrib/libpam/libpam/pam_handlers.c 2001/01/22 20:22:44 @@ -500,6 +500,8 @@ #endif char *mod_full_path=NULL; servicefn func, func2; + int actions2buf[_PAM_RETURN_VALUES]; + int *actions2 = actions; int success; D(("called.")); @@ -649,6 +651,19 @@ _sym = "_pam_sm_authenticate"; _sym2 = "_pam_sm_setcred"; #endif + actions2 = actions2buf; + /* Always run the pam_sm_setcred for all listed auth modules. + Otherwise, we can end up not running the pam_sm_setcred + for auth module(s) that authenticated successfully, + e.g. if an earlier auth module is "sufficient" and + its authenticate fails but its setcred succeeds. + This is also apparently what Solaris PAM does. */ + { + int i; + for (i = 0; i < _PAM_RETURN_VALUES; i++) + actions2[i] = _PAM_ACTION_IGNORE; + actions2[PAM_SUCCESS] = _PAM_ACTION_OK; + } break; case PAM_T_SESS: handler_p = &the_handlers->open_session; @@ -780,7 +795,7 @@ (*handler_p2)->must_fail = must_fail; /* failure forced? */ (*handler_p2)->func = func2; - memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions)); + memcpy((*handler_p2)->actions,actions2,sizeof((*handler_p2)->actions)); (*handler_p2)->argc = argc; if (argv) { if (((*handler_p2)->argv = malloc(argvlen)) == NULL) { --- ./libexec/rshd/Makefile 2001/01/17 00:04:57 1.1 +++ ./libexec/rshd/Makefile 2001/01/23 18:03:41 @@ -7,10 +7,15 @@ #CFLAGS+= -DCRYPT -# For login_cap handling -CFLAGS+=-DLOGIN_CAP -Wall +CFLAGS+= -Wall DPADD+= ${LIBUTIL} LDADD+= -lutil + +.if !defined(NOPAM) +CFLAGS+= -DUSE_PAM +DPADD+= ${LIBPAM} +LDADD+= ${MINUSLPAM} +.endif # IPv6 support CFLAGS+= -DINET6 --- ./libexec/rshd/rshd.8 2001/01/23 01:05:26 1.1 +++ ./libexec/rshd/rshd.8 2001/01/23 01:16:21 @@ -238,6 +238,16 @@ .It Pa Ev $HOME /.rhosts .Sm on .It Pa /var/run/nologin +.It Pa /etc/pam.conf +if +.Nm +is configured with PAM support, it uses +.Pa /etc/pam.conf +entries with service name +.Dq rsh . +authentication modules requiring passwords (such as +.Nm pam_unix ) +are not supported .El .Sh BUGS The authentication procedure used here assumes the integrity --- ./libexec/rshd/rshd.c 2000/11/12 07:00:38 1.1 +++ ./libexec/rshd/rshd.c 2001/01/23 18:01:58 @@ -76,9 +76,21 @@ #include #include #include -#ifdef LOGIN_CAP #include -#endif + +#ifdef USE_PAM +#include +#include +static pam_handle_t *pamh; +#define PAM_END { \ + if ((retcode = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \ + syslog(LOG_ERR|LOG_AUTH, "pam_setcred: %s", pam_strerror(pamh, retcode)); \ + if ((retcode = pam_close_session(pamh,0)) != PAM_SUCCESS) \ + syslog(LOG_ERR|LOG_AUTH, "pam_close_session: %s", pam_strerror(pamh, retcode)); \ + if ((retcode = pam_end(pamh, retcode)) != PAM_SUCCESS) \ + syslog(LOG_ERR|LOG_AUTH, "pam_end: %s", pam_strerror(pamh, retcode)); \ +} +#endif /* USE_PAM */ /* wrapper for KAME-special getnameinfo() */ #ifndef NI_WITHSCOPEID @@ -188,6 +200,20 @@ return(0); } +#ifdef USE_PAM +/* + * We can't have a conversation with the client over the rsh connection. + * You must use auth methods that don't require one, like pam_rhosts. + */ + +int null_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + syslog(LOG_ERR, "PAM conversation is not supported"); + return PAM_CONV_ERR; +} +#endif /* USE_PAM */ + char username[20] = "USER="; char homedir[64] = "HOME="; char shell[64] = "SHELL="; @@ -216,9 +242,12 @@ int rc; int pv1[2], pv2[2]; #endif -#ifdef LOGIN_CAP login_cap_t *lc; -#endif +#ifdef USE_PAM + static struct pam_conv conv = { null_conv, NULL }; + int retcode, i; + const char * const *env; +#endif /* USE_PAM */ (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); @@ -229,7 +258,7 @@ && af != AF_INET6 #endif ) { - syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", af); + syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); exit(1); } err = getnameinfo((struct sockaddr *)fromp, fromp->su_len, numericname, @@ -341,6 +370,56 @@ getstr(locuser, sizeof(locuser), "locuser"); getstr(cmdbuf, sizeof(cmdbuf), "command"); + +#ifdef USE_PAM + retcode = pam_start("rsh", locuser, &conv, &pamh); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR|LOG_AUTH, "pam_start: %s", pam_strerror(pamh, retcode)); + error("Login incorrect.\n"); + exit(1); + } + + retcode = pam_set_item (pamh, PAM_RUSER, remuser); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_RUSER): %s", pam_strerror(pamh, retcode)); + pam_end(pamh, retcode); + error("Login incorrect.\n"); + exit(1); + } + retcode = pam_set_item (pamh, PAM_RHOST, fromhost); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode)); + pam_end(pamh, retcode); + error("Login incorrect.\n"); + exit(1); + } + retcode = pam_set_item (pamh, PAM_TTY, "tty"); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode)); + pam_end(pamh, retcode); + error("Login incorrect.\n"); + exit(1); + } + + retcode = pam_authenticate(pamh, 0); + if (retcode == PAM_SUCCESS) { + if ((retcode = pam_get_item(pamh, PAM_USER, (const void **) &cp)) == PAM_SUCCESS) { + strncpy(locuser, cp, sizeof(locuser)); + locuser[sizeof(locuser) - 1] = '\0'; + } else + syslog(LOG_ERR|LOG_AUTH, "pam_get_item(PAM_USER): %s", + pam_strerror(pamh, retcode)); + retcode = pam_acct_mgmt(pamh, 0); + } + if (retcode != PAM_SUCCESS) { + syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", + remuser, fromhost, locuser, pam_strerror(pamh, retcode), cmdbuf); + pam_end(pamh, retcode); + error("Login incorrect.\n"); + exit(1); + } +#endif /* USE_PAM */ + setpwent(); pwd = getpwnam(locuser); if (pwd == NULL) { @@ -349,13 +428,36 @@ remuser, fromhost, locuser, cmdbuf); if (errorstr == NULL) errorstr = "Login incorrect.\n"; - goto fail; + error(errorstr, fromhost); + exit(1); + } + +#ifndef USE_PAM + if (errorstr || + (pwd->pw_expire && time(NULL) >= pwd->pw_expire) || + iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0, + remuser, locuser) < 0) { + if (__rcmd_errstr) + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: permission denied (%s). cmd='%.80s'", + remuser, fromhost, locuser, __rcmd_errstr, + cmdbuf); + else + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: permission denied. cmd='%.80s'", + remuser, fromhost, locuser, cmdbuf); + if (errorstr == NULL) + errorstr = "Login incorrect.\n"; + error(errorstr, fromhost); + exit(1); } -#ifdef LOGIN_CAP +#endif /* USE_PAM */ + lc = login_getpwclass(pwd); -#endif + if (pwd->pw_uid) + auth_checknologin(lc); + if (chdir(pwd->pw_dir) < 0) { -#ifdef LOGIN_CAP if (chdir("/") < 0 || login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) { syslog(LOG_INFO|LOG_AUTH, @@ -364,44 +466,9 @@ error("No remote home directory.\n"); exit(0); } -#else - (void) chdir("/"); -#ifdef notdef - syslog(LOG_INFO|LOG_AUTH, - "%s@%s as %s: no home directory. cmd='%.80s'", - remuser, fromhost, locuser, cmdbuf); - error("No remote directory.\n"); - exit(1); -#endif -#endif pwd->pw_dir = "/"; } - if (errorstr || - (pwd->pw_expire && time(NULL) >= pwd->pw_expire) || - iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0, - remuser, locuser) < 0) { - if (__rcmd_errstr) - syslog(LOG_INFO|LOG_AUTH, - "%s@%s as %s: permission denied (%s). cmd='%.80s'", - remuser, fromhost, locuser, __rcmd_errstr, - cmdbuf); - else - syslog(LOG_INFO|LOG_AUTH, - "%s@%s as %s: permission denied. cmd='%.80s'", - remuser, fromhost, locuser, cmdbuf); -fail: - if (errorstr == NULL) - errorstr = "Login incorrect.\n"; - error(errorstr, fromhost); - exit(1); - } - - if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { - error("Logins currently disabled.\n"); - exit(1); - } -#ifdef LOGIN_CAP if (lc != NULL && fromp->su_family == AF_INET) { /*XXX*/ char remote_ip[MAXHOSTNAMELEN]; @@ -421,13 +488,30 @@ exit(1); } } -#endif /* !LOGIN_CAP */ #if BSD > 43 /* before fork, while we're session leader */ if (setlogin(pwd->pw_name) < 0) syslog(LOG_ERR, "setlogin() failed: %m"); #endif + /* + * PAM modules might add supplementary groups in + * pam_setcred(), so initialize them first. + * But we need to open the session as root. + */ + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { + syslog(LOG_ERR, "setusercontext: %m"); + exit(1); + } + +#ifdef USE_PAM + if ((retcode = pam_open_session(pamh, 0)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, retcode)); + } else if ((retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode)); + } +#endif /* USE_PAM */ + (void) write(STDERR_FILENO, "\0", 1); sent_null = 1; @@ -451,6 +535,9 @@ pid = fork(); if (pid == -1) { error("Can't fork; try again.\n"); +#ifdef USE_PAM + PAM_END; +#endif /* USE_PAM */ exit(1); } if (pid) { @@ -569,6 +656,9 @@ (doencrypt && FD_ISSET(pv1[0], &readfrom)) || #endif FD_ISSET(pv[0], &readfrom)); +#ifdef USE_PAM + PAM_END; +#endif /* USE_PAM */ exit(0); } setpgrp(0, getpid()); @@ -586,6 +676,23 @@ dup2(pv[1], 2); close(pv[1]); } +#ifdef USE_PAM + else { + pid = fork(); + if (pid == -1) { + error("Can't fork; try again.\n"); + PAM_END; + exit(1); + } + if (pid) { + /* Parent. */ + wait(NULL); + PAM_END; + exit(0); + } + } +#endif /* USE_PAM */ + if (*pwd->pw_shell == '\0') pwd->pw_shell = _PATH_BSHELL; environ = envinit; @@ -598,17 +705,20 @@ cp++; else cp = pwd->pw_shell; -#ifdef LOGIN_CAP - if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { + + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~LOGIN_SETGROUP) != 0) { syslog(LOG_ERR, "setusercontext: %m"); exit(1); } login_close(lc); -#else - (void) setgid((gid_t)pwd->pw_gid); - initgroups(pwd->pw_name, pwd->pw_gid); - (void) setuid((uid_t)pwd->pw_uid); -#endif +#ifdef USE_PAM + env = (const char * const *)pam_getenvlist(pamh); + if (env != NULL) { + for (i=0; env[i]; i++) + putenv(env[i]); + } +#endif /* USE_PAM */ + endpwent(); if (log_success || pwd->pw_uid == 0) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", --- ./usr.bin/login/Makefile 2001/01/21 04:44:21 1.1 +++ ./usr.bin/login/Makefile 2001/01/21 04:44:45 @@ -11,9 +11,8 @@ DPADD= ${LIBUTIL} ${LIBCRYPT} LDADD= -lutil -lcrypt -.if defined(NOPAM) -CFLAGS+= -DNO_PAM -.else +.if !defined(NOPAM) +CFLAGS+= -DUSE_PAM DPADD+= ${LIBPAM} LDADD+= ${MINUSLPAM} .endif --- ./usr.bin/login/login.1 2001/01/23 01:04:52 1.1 +++ ./usr.bin/login/login.1 2001/01/23 01:16:45 @@ -177,6 +177,13 @@ makes login quieter .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 login .El .Sh SEE ALSO .Xr builtin 1 , --- ./usr.bin/login/login.c 2000/08/08 03:12:59 1.1 +++ ./usr.bin/login/login.c 2001/01/23 00:53:51 @@ -78,10 +78,11 @@ #include #include -#ifndef NO_PAM +#ifdef USE_PAM #include #include -#endif +#include +#endif /* USE_PAM */ #include "pathnames.h" @@ -104,9 +105,18 @@ int login_access __P((char *, char *)); void login_fbtab __P((char *, uid_t, gid_t)); -#ifndef NO_PAM +#ifdef USE_PAM static int auth_pam __P((void)); -#endif +pam_handle_t *pamh = NULL; +#define PAM_END { \ + if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \ + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); \ + if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) \ + syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); \ + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) \ + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); \ +} +#endif /* USE_PAM */ static int auth_traditional __P((void)); extern void login __P((struct utmp *)); static void usage __P((void)); @@ -150,6 +160,10 @@ char tname[sizeof(_PATH_TTY) + 10]; char *shell = NULL; login_cap_t *lc = NULL; +#ifdef USE_PAM + pid_t pid; + int e; +#endif /* USE_PAM */ (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGINT, SIG_IGN); @@ -309,19 +323,19 @@ (void)setpriority(PRIO_PROCESS, 0, -4); -#ifndef NO_PAM +#ifdef USE_PAM /* * Try to authenticate using PAM. If a PAM system error * occurs, perhaps because of a botched configuration, * then fall back to using traditional Unix authentication. */ if ((rval = auth_pam()) == -1) -#endif /* NO_PAM */ +#endif /* USE_PAM */ rval = auth_traditional(); (void)setpriority(PRIO_PROCESS, 0, 0); -#ifndef NO_PAM +#ifdef USE_PAM /* * PAM authentication may have changed "pwd" to the * entry for the template user. Check again to see if @@ -329,7 +343,7 @@ */ if (pwd != NULL && pwd->pw_uid == 0) rootlogin = 1; -#endif /* NO_PAM */ +#endif /* USE_PAM */ ttycheck: /* @@ -549,6 +563,43 @@ environ = envinit; /* + * PAM modules might add supplementary groups during pam_setcred(). + */ + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { + syslog(LOG_ERR, "setusercontext() failed - exiting"); + exit(1); + } + +#ifdef USE_PAM + if (pamh) { + if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); + } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); + } + + /* + * We must fork() before setuid() because we need to call + * pam_close_session() as root. + */ + pid = fork(); + if (pid < 0) { + err(1, "fork"); + PAM_END; + exit(0); + } else if (pid) { + /* parent - wait for child to finish, then cleanup session */ + wait(NULL); + PAM_END; + exit(0); + } else { + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + } + } +#endif /* USE_PAM */ + + /* * We don't need to be root anymore, so * set the user and session context */ @@ -557,7 +608,7 @@ exit(1); } if (setusercontext(lc, pwd, pwd->pw_uid, - LOGIN_SETALL & ~LOGIN_SETLOGIN) != 0) { + LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) { syslog(LOG_ERR, "setusercontext() failed - exiting"); exit(1); } @@ -573,6 +624,17 @@ (void)setenv("USER", username, 1); (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0); +#ifdef USE_PAM + if (pamh) { + const char * const *env = (const char * const *)pam_getenvlist(pamh); + int i; + if (env != NULL) { + for (i=0; env[i]; i++) + putenv(env[i]); + } + } +#endif /* USE_PAM */ + if (!quietlog) { char *cw; @@ -652,7 +714,7 @@ return rval; } -#ifndef NO_PAM +#ifdef USE_PAM /* * Attempt to authenticate the user using PAM. Returns 0 if the user is * authenticated, or 1 if not authenticated. If some sort of PAM system @@ -663,7 +725,6 @@ static int auth_pam() { - pam_handle_t *pamh = NULL; const char *tmpl_user; const void *item; int rval; @@ -677,12 +738,14 @@ if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, e)); + pam_end(pamh, e); return -1; } if (hostname != NULL && (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, e)); + pam_end(pamh, e); return -1; } e = pam_authenticate(pamh, 0); @@ -712,8 +775,8 @@ if (strcmp(username, tmpl_user) != 0) pwd = getpwnam(tmpl_user); } else - syslog(LOG_ERR, "Couldn't get PAM_USER: %s", - pam_strerror(pamh, e)); + syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", + pam_strerror(pamh, e)); rval = 0; break; @@ -724,17 +787,33 @@ break; default: - syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e)); + syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); rval = -1; break; } - if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); - rval = -1; + + if (rval != -1) { + e = pam_acct_mgmt(pamh, 0); + if (e == PAM_NEW_AUTHTOK_REQD) { + e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e)); + rval = -1; + } + } else if (e != PAM_SUCCESS) { + rval = 1; + } + } + + if (rval == -1) { + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + } + pamh = NULL; } return rval; } -#endif /* NO_PAM */ +#endif /* USE_PAM */ static void usage() @@ -745,7 +824,7 @@ /* * Allow for authentication style and/or kerberos instance - * */ + */ #define NBUFSIZ UT_NAMESIZE + 64 --- ./usr.bin/su/Makefile 2001/01/16 21:33:47 1.1 +++ ./usr.bin/su/Makefile 2001/01/23 18:07:48 @@ -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 --- ./usr.bin/su/su.1 2001/01/23 01:02:52 1.1 +++ ./usr.bin/su/su.1 2001/01/23 01:16:33 @@ -173,6 +173,13 @@ .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 , --- ./usr.bin/su/su.c 2000/02/24 21:06:21 1.1 +++ ./usr.bin/su/su.c 2001/01/23 18:05:14 @@ -60,36 +60,40 @@ #include #include #include - -#ifdef LOGIN_CAP #include -#endif +#ifdef USE_PAM +#include +#include +#include +#include +#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 #endif +#endif /* USE_PAM */ #ifdef KERBEROS #include #include #include -#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 +111,26 @@ 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; + pam_handle_t *pamh = NULL; + struct pam_conv conv = { misc_conv, NULL }; + char myhost[MAXHOSTNAMELEN + 1], *mytty; + int statusp=0; + int child_pid, child_pgrp, ret_pid; + const char * const *env; +#else /* !USE_PAM */ + char **g; + struct group *gr; +#endif /* USE_PAM */ #ifdef KERBEROS char *k; #endif @@ -147,11 +160,9 @@ asme = 1; asthem = 0; break; -#ifdef LOGIN_CAP case 'c': class = optarg; break; -#endif case '?': default: usage(); @@ -161,8 +172,7 @@ 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 +199,7 @@ 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 +224,67 @@ } } +#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)); + pam_end(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)); + pam_end(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)); + pam_end(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)); + pam_end(pamh, retcode); + errx(1, "Sorry"); + } + } + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_acct_mgmt: %s", pam_strerror(pamh, retcode)); + pam_end(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 +294,8 @@ if (lc == NULL) errx(1, "unknown class: %s", class); } -#endif +#ifndef USE_PAM #ifdef WHEELSU targetpass = strdup(pwd->pw_passwd); #endif /* WHEELSU */ @@ -280,18 +346,18 @@ #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 +367,17 @@ #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,9 +400,51 @@ (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 everything now except the environment & umask */ - setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; + setwhat = LOGIN_SETUSER|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; /* * Don't touch resource/priority settings if -m has been * used or -l and -c hasn't, and we're not su'ing to root. @@ -345,15 +453,6 @@ 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) { @@ -361,16 +460,9 @@ #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; /* 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 @@ -385,6 +477,17 @@ (void)setenv("HOME", pwd->pw_dir, 1); (void)setenv("SHELL", shell, 1); } + + login_close(lc); + +#ifdef USE_PAM + env = (const char * const *)pam_getenvlist(pamh); + if (env != NULL) { + for (i=0; env[i]; i++) + putenv(env[i]); + } +#endif /* USE_PAM */ + if (iscsh == YES) { if (fastlogin) *np-- = "-f"; @@ -396,20 +499,20 @@ *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 */ } static void usage() { - (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR); - exit(1); + errx(1, "usage: su [%s] [login [args]]", ARGSTR); } int @@ -493,7 +596,7 @@ 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); @@ -520,13 +623,13 @@ 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); @@ -540,9 +643,9 @@ 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(); To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-security" in the body of the message