Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/usr.bin/su Add PAM support to su



details:   https://anonhg.NetBSD.org/src/rev/c8f09e0ace12
branches:  trunk
changeset: 572545:c8f09e0ace12
user:      manu <manu%NetBSD.org@localhost>
date:      Fri Jan 07 22:34:20 2005 +0000

description:
Add PAM support to su

diffstat:

 usr.bin/su/Makefile |    7 ++-
 usr.bin/su/su.c     |  146 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 140 insertions(+), 13 deletions(-)

diffs (258 lines):

diff -r d2a8a3d02742 -r c8f09e0ace12 usr.bin/su/Makefile
--- a/usr.bin/su/Makefile       Fri Jan 07 22:22:49 2005 +0000
+++ b/usr.bin/su/Makefile       Fri Jan 07 22:34:20 2005 +0000
@@ -1,4 +1,4 @@
-#      $NetBSD: Makefile,v 1.34 2003/12/11 09:46:27 dyoung Exp $
+#      $NetBSD: Makefile,v 1.35 2005/01/07 22:34:20 manu Exp $
 #      from: @(#)Makefile      8.1 (Berkeley) 7/19/93
 
 .include <bsd.own.mk>
@@ -50,3 +50,8 @@
 .ifdef SU_ROOTAUTH
 CPPFLAGS+=-DSU_ROOTAUTH=\"${SU_ROOTAUTH}\"
 .endif
+
+.if (${USE_PAM} != "no")
+CPPFLAGS+=-DUSE_PAM
+LDADD+= -lpam
+.endif
diff -r d2a8a3d02742 -r c8f09e0ace12 usr.bin/su/su.c
--- a/usr.bin/su/su.c   Fri Jan 07 22:22:49 2005 +0000
+++ b/usr.bin/su/su.c   Fri Jan 07 22:34:20 2005 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: su.c,v 1.58 2004/01/05 23:23:37 jmmv Exp $     */
+/*     $NetBSD: su.c,v 1.59 2005/01/07 22:34:20 manu Exp $     */
 
 /*
  * Copyright (c) 1988 The Regents of the University of California.
@@ -40,12 +40,15 @@
 #if 0
 static char sccsid[] = "@(#)su.c       8.3 (Berkeley) 4/2/94";*/
 #else
-__RCSID("$NetBSD: su.c,v 1.58 2004/01/05 23:23:37 jmmv Exp $");
+__RCSID("$NetBSD: su.c,v 1.59 2005/01/07 22:34:20 manu Exp $");
 #endif
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/time.h>
+#ifdef USE_PAM
+#include <sys/wait.h>
+#endif
 #include <sys/resource.h>
 #include <err.h>
 #include <errno.h>
@@ -63,6 +66,21 @@
 #include <tzfile.h>
 #include <unistd.h>
 
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#include <security/openpam.h>   /* for openpam_ttyconv() */
+
+static pam_handle_t *pamh;
+static struct pam_conv pamc;
+
+#define fatal(x) { pam_end(pamh, pam_err); err x; }
+#define fatalx(x) { pam_end(pamh, pam_err); errx x; }
+#else
+#define fatal(x) err x
+#define fatalx(x) errx x
+
+#endif
+
 #ifdef LOGIN_CAP
 #include <login_cap.h>
 #endif
@@ -132,6 +150,14 @@
 #ifdef LOGIN_CAP
        login_cap_t *lc;
 #endif
+#ifdef USE_PAM
+       char **pam_envlist, **pam_env;
+       char hostname[MAXHOSTNAMELEN];
+       const char **usernamep;
+       char *tty; 
+       int pam_err, status;
+       pid_t pid;
+#endif
 
        asme = asthem = fastlogin = 0;
        gohome = 1;
@@ -186,16 +212,80 @@
        /* get current login name and shell */
        ruid = getuid();
        username = getlogin();
+
+#ifdef USE_PAM
+       pamc.conv = &openpam_ttyconv;
+       pam_start("su", username, &pamc, &pamh);
+
+       /* Fill in hostname, username and tty */        
+       if ((pam_err = pam_set_item(pamh, PAM_RUSER, username)) != PAM_SUCCESS)
+               goto pam_failed;
+
+       gethostname(hostname, sizeof(hostname));
+       if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
+               goto pam_failed;
+       
+       tty = ttyname(STDERR_FILENO);
+       if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
+               goto pam_failed;
+
+       /* authentication */
+       if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
+               goto pam_failed;
+
+       if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
+               pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+       if (pam_err != PAM_SUCCESS)
+               goto pam_failed;
+
+       /* Get user credentials */
+       if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
+               goto pam_failed;
+
+       /* Open the session */
+       if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
+               goto pam_failed;
+
+       /* New user name? */
+       usernamep = (const char **)&username;
+       pam_err = pam_get_item(pamh, PAM_USER, (const void **)usernamep);
+       if (pam_err != PAM_SUCCESS)
+               goto pam_failed;
+
+pam_failed:
+       if (pam_err != PAM_SUCCESS) {
+               warn("PAM failed, fallback to plain old authentication");
+               pam_end(pamh, pam_err);
+               username = getlogin();
+       }       
+
+       /* 
+        * Now any failure should use fatal/fatalx instead of err/errx
+        * so that pam_end() is called on errors.
+        */
+#endif
        if (username == NULL || (pwd = getpwnam(username)) == NULL ||
            pwd->pw_uid != ruid)
                pwd = getpwuid(ruid);
        if (pwd == NULL)
-               errx(1, "who are you?");
+               fatalx((1, "who are you?"));
+
        username = strdup(pwd->pw_name);
        userpass = strdup(pwd->pw_passwd);
        if (username == NULL || userpass == NULL)
-               err(1, "strdup");
+               fatal((1, "strdup"));
 
+#ifdef USE_PAM
+       /* Get PAM environment */
+       if ((pam_err == PAM_SUCCESS) && 
+           ((pam_envlist = pam_getenvlist(pamh)) != NULL)) {
+               for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
+                       putenv(*pam_env);
+                       free(*pam_env);
+               }
+               free(pam_envlist);
+       }
+#endif
 
        if (asme) {
                if (pwd->pw_shell && *pwd->pw_shell) {
@@ -211,13 +301,13 @@
        np = *argv ? argv : argv-1;
 
        if ((pwd = getpwnam(user)) == NULL)
-               errx(1, "unknown login %s", user);
+               fatalx((1, "unknown login %s", user));
 
 #ifdef LOGIN_CAP
        /* force the usage of specified class */
        if (class) {
                if (ruid)
-                       errx(1, "Only root may use -c");
+                       fatalx((1, "Only root may use -c"));
 
                pwd->pw_class = class;
        }
@@ -235,6 +325,9 @@
 #ifdef KERBEROS
            && (!use_kerberos || kerberos(username, user, pwd->pw_uid))
 #endif
+#ifdef USE_PAM 
+           && (pam_err != PAM_SUCCESS)
+#endif
            ) {
                char *pass = pwd->pw_passwd;
                int ok = pwd->pw_uid != 0;
@@ -260,17 +353,18 @@
                        ok = check_ingroup(-1, SU_GROUP, username, 1);
                }
                if (!ok)
-                       errx(1,
+                       fatalx((1,
            "you are not listed in the correct secondary group (%s) to su %s.",
-                                           SU_GROUP, user);
+                                           SU_GROUP, user));
                /* if target requires a password, verify it */
                if (*pass) {
                        p = getpass("Password:");
 #ifdef SKEY
                        if (strcasecmp(p, "s/key") == 0) {
-                               if (skey_haskey(user))
-                                       errx(1, "Sorry, you have no s/key.");
-                               else {
+                               if (skey_haskey(user)) {
+                                       fatalx((1, 
+                                           "Sorry, you have no s/key."));
+                               } else {
                                        if (skey_authenticate(user)) {
                                                goto badlogin;
                                        }
@@ -294,7 +388,7 @@
        if (asme) {
                /* if asme and non-standard target shell, must be root */
                if (!chshell(pwd->pw_shell) && ruid)
-                       errx(1,"permission denied (shell).");
+                       fatalx((1,"permission denied (shell)."));
        } else if (pwd->pw_shell && *pwd->pw_shell) {
                shell = pwd->pw_shell;
                iscsh = UNSET;
@@ -312,6 +406,34 @@
        if (iscsh == UNSET)
                iscsh = strstr(avshell, "csh") ? YES : NO;
 
+#ifdef USE_PAM
+       /* 
+        * If PAM is in use, we must release PAM ressources and close
+        * the session after su child exits. That require a fork now,
+        * before we drop the root privs (needed for PAM)
+        */
+       if (pam_err == PAM_SUCCESS) {
+               switch(pid = fork()) {
+               case -1:
+                       fatal((1, "fork"));
+                       break;
+               case 0:         /* child */
+                       break;
+               default:        /* parent */
+                       waitpid(pid, &status, 0);       
+                       pam_err = pam_close_session(pamh, 0);
+                       pam_end(pamh, pam_err);
+
+                       exit(WEXITSTATUS(status));
+                       break;
+               }
+       }
+
+       /* 
+        * Everything here happens in the child, it should not 
+        * do any PAM operation anymore (don't use fatal/fatalx)
+        */
+#endif
        /* set permissions */
 #ifdef LOGIN_CAP
        if (setusercontext(lc, pwd, pwd->pw_uid,



Home | Main Index | Thread Index | Old Index