From owner-freebsd-security Fri Jan 19 10:14:31 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 6983137B400 for ; Fri, 19 Jan 2001 10:14:00 -0800 (PST) Received: from jenkins.web.us.uu.net (localhost.web.us.uu.net [127.0.0.1]) by jenkins.web.us.uu.net (Postfix) with ESMTP id 9A71212685; Fri, 19 Jan 2001 13:13:59 -0500 (EST) To: freebsd-security@FreeBSD.ORG Cc: djm@web.us.uu.net Subject: improved: PAM support for login, rshd, and su In-Reply-To: Message from "David J. MacKenzie" of "Thu, 18 Jan 2001 10:09:22 EST." <20010118150923.111FB3E5B@catapult.web.us.uu.net> Date: Fri, 19 Jan 2001 13:13:59 -0500 From: "David J. MacKenzie" Message-Id: <20010119181359.9A71212685@jenkins.web.us.uu.net> Sender: owner-freebsd-security@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org In reviewing the patches I sent to this list recently, I realized that I had neglected to add support for PAM "template users" to rshd and su. I also noticed that rshd didn't use auth_checknologin() when LOGIN_CAP was defined (not my bug, but I have fixed it). And I failed to add dependencies for LIBPAM to the Makefiles. Here is a revised version of my patches, which supercedes the previous one. The patches to login didn't change. --- ./libexec/rshd/Makefile 2001/01/17 00:04:57 1.1 +++ ./libexec/rshd/Makefile 2001/01/19 17:33:26 @@ -8,9 +8,9 @@ #CFLAGS+= -DCRYPT # For login_cap handling -CFLAGS+=-DLOGIN_CAP -Wall -DPADD+= ${LIBUTIL} -LDADD+= -lutil +CFLAGS+=-DLOGIN_CAP -DUSE_PAM -Wall +DPADD+= ${LIBUTIL} ${LIBPAM} +LDADD+= -lutil -lpam # IPv6 support CFLAGS+= -DINET6 --- ./libexec/rshd/rshd.c 2000/11/12 07:00:38 1.1 +++ ./libexec/rshd/rshd.c 2001/01/19 17:58:29 @@ -80,6 +80,12 @@ #include #endif +#ifdef USE_PAM +#include +#include +static pam_handle_t *pamh; +#endif /* USE_PAM */ + /* wrapper for KAME-special getnameinfo() */ #ifndef NI_WITHSCOPEID #define NI_WITHSCOPEID 0 @@ -219,6 +225,10 @@ #ifdef LOGIN_CAP login_cap_t *lc; #endif +#ifdef USE_PAM + static struct pam_conv conv = { misc_conv, NULL }; + int retcode; +#endif /* USE_PAM */ (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); @@ -341,6 +351,43 @@ 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, "pam_start: %s\n", pam_strerror(pamh, retcode)); + exit(1); + } + pam_set_item (pamh, PAM_RUSER, remuser); + pam_set_item (pamh, PAM_RHOST, fromhost); + pam_set_item (pamh, PAM_TTY, "tty"); + + retcode = pam_authenticate(pamh, 0); + if (retcode == PAM_SUCCESS) { + if ((retcode = pam_get_item(pamh, PAM_USER, &cp)) == PAM_SUCCESS) { + strncpy(locuser, cp, sizeof(locuser)); + } else + syslog(LOG_ERR|LOG_AUTH, "Couldn't get PAM_USER: %s", + pam_strerror(pamh, retcode)); + retcode = pam_acct_mgmt(pamh, 0); + } + if (retcode == PAM_SUCCESS) { + retcode = pam_open_session(pamh,0); + } + if (retcode == PAM_SUCCESS) { + retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (retcode != PAM_SUCCESS) + pam_close_session(pamh, 0); + } + if (retcode != PAM_SUCCESS) { + pam_end(pamh, retcode); + syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", + remuser, fromhost, locuser, pam_strerror(pamh, retcode), cmdbuf); + error("Login incorrect.\n"); + exit(1); + } +#endif /* USE_PAM */ + setpwent(); pwd = getpwnam(locuser); if (pwd == NULL) { @@ -349,11 +396,42 @@ 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 */ + +#ifdef LOGIN_CAP lc = login_getpwclass(pwd); -#endif + if (pwd->pw_uid) + auth_checknologin(lc); +#else /* !LOGIN_CAP */ + if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { + error("Logins currently disabled.\n"); + exit(1); + } +#endif /* LOGIN_CAP */ + if (chdir(pwd->pw_dir) < 0) { #ifdef LOGIN_CAP if (chdir("/") < 0 || @@ -377,30 +455,6 @@ 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]; @@ -569,6 +623,10 @@ (doencrypt && FD_ISSET(pv1[0], &readfrom)) || #endif FD_ISSET(pv[0], &readfrom)); +#ifdef USE_PAM + pam_close_session(pamh, 0); + pam_end(pamh, PAM_SUCCESS); +#endif /* USE_PAM */ exit(0); } setpgrp(0, getpid()); --- ./usr.bin/login/login.c 2000/08/08 03:12:59 1.1 +++ ./usr.bin/login/login.c 2001/01/18 03:24:07 @@ -81,6 +81,7 @@ #ifndef NO_PAM #include #include +#include #endif #include "pathnames.h" @@ -106,6 +107,7 @@ #ifndef NO_PAM static int auth_pam __P((void)); +pam_handle_t *pamh = NULL; #endif static int auth_traditional __P((void)); extern void login __P((struct utmp *)); @@ -150,6 +152,10 @@ char tname[sizeof(_PATH_TTY) + 10]; char *shell = NULL; login_cap_t *lc = NULL; +#ifndef NO_PAM + pid_t pid; + int e; +#endif /* NO_PAM */ (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGINT, SIG_IGN); @@ -548,6 +554,35 @@ if (!pflag) environ = envinit; +#ifndef NO_PAM + if (pamh) { + /* + * We must fork() before setuid() because we need to call + * pam_close_session() as root. + */ + pid = fork(); + if (pid < 0) { + err(1, "fork"); + 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)); + exit(0); + } else if (pid) { + /* parent - wait for child to finish, then cleanup session */ + wait(NULL); + 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)); + exit(0); + } else { + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + } + } +#endif /* NO_PAM */ + /* * We don't need to be root anymore, so * set the user and session context @@ -562,6 +597,17 @@ exit(1); } +#ifndef NO_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 /* NO_PAM */ + (void)setenv("SHELL", pwd->pw_shell, 1); (void)setenv("HOME", pwd->pw_dir, 1); if (term != NULL && *term != '\0') @@ -663,7 +709,6 @@ static int auth_pam() { - pam_handle_t *pamh = NULL; const char *tmpl_user; const void *item; int rval; @@ -724,13 +769,36 @@ 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; + } else if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); + rval = -1; + } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); + rval = -1; + pam_close_session(pamh, 0); + } + } + + 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; } @@ -745,7 +813,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/19 17:41:13 @@ -4,9 +4,9 @@ PROG= su SRCS= su.c -COPTS+= -DLOGIN_CAP -DSKEY -DPADD= ${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT} -LDADD= -lutil -lskey -lmd -lcrypt +COPTS+= -DLOGIN_CAP -DSKEY -DUSE_PAM +DPADD= ${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBPAM} +LDADD= -lutil -lskey -lmd -lcrypt -lpam .if defined(WHEELSU) COPTS+= -DWHEELSU --- ./usr.bin/su/su.c 2000/02/24 21:06:21 1.1 +++ ./usr.bin/su/su.c 2001/01/19 17:48:55 @@ -65,6 +65,20 @@ #include #endif +#ifdef USE_PAM +#include +#include +#include +#include +#define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \ + fprintf(stderr,"su: PAM error: %s\n",pam_strerror(pamh, retcode)); \ + syslog(LOG_ERR,"PAM error: %s",pam_strerror(pamh, retcode)); \ + pam_end(pamh, retcode); exit(1); \ + } +#define PAM_END { retcode = pam_close_session(pamh,0); \ + pam_end(pamh,retcode); } +#endif /* USE_PAM */ + #ifdef SKEY #include #endif @@ -107,8 +121,7 @@ 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; @@ -118,6 +131,18 @@ 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 @@ -214,6 +239,28 @@ } } +#ifdef USE_PAM + retcode = pam_start("su", user, &conv, &pamh); + PAM_FAIL_CHECK; + + if (ruid) { + retcode = pam_authenticate(pamh, 0); + PAM_FAIL_CHECK; + + if ((retcode = pam_get_item(pamh, PAM_USER, &p)) == PAM_SUCCESS) { + user = p; + } else + syslog(LOG_ERR|LOG_AUTH, "Couldn't get 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); + PAM_FAIL_CHECK; + } + +#endif /* USE_PAM */ + /* get target login information, default to root */ if ((pwd = getpwnam(user)) == NULL) { errx(1, "unknown login: %s", user); @@ -230,6 +277,7 @@ } #endif +#ifndef USE_PAM #ifdef WHEELSU targetpass = strdup(pwd->pw_passwd); #endif /* WHEELSU */ @@ -280,11 +328,12 @@ #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 @@ -308,6 +357,7 @@ exit(1); } } +#endif /* USE_PAM */ if (asme) { /* if asme and non-standard target shell, must be root */ @@ -334,6 +384,60 @@ (void)setpriority(PRIO_PROCESS, 0, prio); +#ifdef USE_PAM + gethostname(myhost, sizeof(myhost)); + retcode = pam_set_item(pamh, PAM_RHOST, myhost); + PAM_FAIL_CHECK; + + mytty = ttyname(STDERR_FILENO); + if (!mytty) + mytty = "tty"; + retcode = pam_set_item(pamh, PAM_TTY, mytty); + PAM_FAIL_CHECK; + + retcode = pam_open_session(pamh, 0); + PAM_FAIL_CHECK; + + retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); + PAM_FAIL_CHECK; + + env = (const char * const *)pam_getenvlist(pamh); + if (env != NULL) { + for (i=0; env[i]; i++) + putenv(env[i]); + } + + /* + * We must fork() before setuid() because we need to call + * pam_close_session() 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: + pam_end(pamh, retcode); +#endif /* USE_PAM */ + #ifdef LOGIN_CAP /* Set everything now except the environment & umask */ setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; @@ -361,10 +465,7 @@ #ifdef KERBEROS k = getenv("KRBTKFILE"); #endif - if ((cleanenv = calloc(20, sizeof(char*))) == NULL) - errx(1, "calloc"); - cleanenv[0] = NULL; - environ = cleanenv; + environ = &cleanenv; #ifdef LOGIN_CAP /* set the su'd user's environment & umask */ setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); @@ -403,6 +504,9 @@ execv(shell, np); err(1, "%s", shell); +#ifdef USE_PAM + } +#endif /* USE_PAM */ } static void To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-security" in the body of the message