summaryrefslogtreecommitdiffstats
path: root/usr.bin/su/su.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/su/su.c')
-rw-r--r--usr.bin/su/su.c272
1 files changed, 211 insertions, 61 deletions
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();
OpenPOWER on IntegriCloud