Remove support for Solaris pre-8
[openafs.git] / src / pam / afs_auth.c
index 90772fb..8bdc107 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
 #include <afsconfig.h>
 #include <afs/param.h>
 
-RCSID("$Header$");
+#include <roken.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include <afs/kautils.h>
 
 #include <security/pam_appl.h>
 #include <security/pam_modules.h>
-#include <syslog.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pwd.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <afs/kautils.h>
+
 #include "afs_message.h"
 #include "afs_util.h"
-#include <signal.h>
-#include <sys/wait.h>
-#include <errno.h>
 
-#define RET(x) { retcode = (x); goto out; }
 
+#define RET(x) { retcode = (x); goto out; }
 
 extern int
-pam_sm_authenticate(
-       pam_handle_t    *pamh,
-       int             flags,
-       int             argc,
-       const char      **argv)
+pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc,
+                   const char **argv)
 {
     int retcode = PAM_SUCCESS;
     int errcode = PAM_SUCCESS;
@@ -45,29 +39,38 @@ pam_sm_authenticate(
     int nowarn = 0;
     int use_first_pass = 0;
     int try_first_pass = 0;
-    int ignore_root = 0;
-    int trust_root = 0;
-    int catch_su = 0;
-    int set_expires = 0;  /* This option is only used in pam_set_cred() */
-    int got_authtok = 0;       /* got PAM_AUTHTOK upon entry */
-    int nouser = 0;
+    int ignore_uid = 0;
+    uid_t ignore_uid_id = 0;
     char my_password_buf[256];
-    char *user = NULL, *password = NULL;
-    int torch_password = 1;
+    char *cell_ptr = NULL;
+    /*
+     * these options are added to handle stupid apps, which won't call
+     * pam_set_cred()
+     */
+    int refresh_token = 0;
+    int set_token = 0;
+    int dont_fork = 0;
+    /* satisfy kdm 2.x
+     */
+    int use_klog = 0;
+    int set_expires = 0;       /* This option is only used in pam_set_cred() */
+    int got_authtok = 0;       /* got PAM_AUTHTOK upon entry */
+    PAM_CONST char *user = NULL, *password = NULL;
+    afs_int32 password_expires = -1;
+    char *torch_password = NULL;
     int i;
-    struct pam_conv *pam_convp = NULL;
+    PAM_CONST struct pam_conv *pam_convp = NULL;
     int auth_ok;
-    uid_t uid;
     struct passwd unix_pwd, *upwd = NULL;
     char upwd_buf[2048];       /* size is a guess. */
-    char*      reason = NULL;
+    char *reason = NULL;
     pid_t cpid, rcpid;
-    int   status;
+    int status;
     struct sigaction newAction, origAction;
 
 
-#ifndef AFS_SUN56_ENV
-    openlog(pam_afs_ident, LOG_CONS, LOG_AUTH);
+#ifndef AFS_SUN5_ENV
+    openlog(pam_afs_ident, LOG_CONS | LOG_PID, LOG_AUTH);
 #endif
     origmask = setlogmask(logmask);
 
@@ -75,23 +78,57 @@ pam_sm_authenticate(
      * Parse the user options.  Log an error for any unknown options.
      */
     for (i = 0; i < argc; i++) {
-       if (       strcasecmp(argv[i], "debug"         ) == 0) {
+       if (strcasecmp(argv[i], "debug") == 0) {
            logmask |= LOG_MASK(LOG_DEBUG);
-           (void) setlogmask(logmask);
-       } else if (strcasecmp(argv[i], "nowarn"        ) == 0) {
+           (void)setlogmask(logmask);
+       } else if (strcasecmp(argv[i], "nowarn") == 0) {
            nowarn = 1;
        } else if (strcasecmp(argv[i], "use_first_pass") == 0) {
            use_first_pass = 1;
        } else if (strcasecmp(argv[i], "try_first_pass") == 0) {
            try_first_pass = 1;
-       } else if (strcasecmp(argv[i], "ignore_root"   ) == 0) {
-           ignore_root = 1;
-       } else if (strcasecmp(argv[i], "trust_root"   ) == 0) {
-           trust_root = 1;
-       } else if (strcasecmp(argv[i], "catch_su"   ) == 0) {
-           catch_su = 1;
+       } else if (strcasecmp(argv[i], "ignore_root") == 0) {
+           ignore_uid = 1;
+           ignore_uid_id = 0;
+       } else if (strcasecmp(argv[i], "ignore_uid") == 0) {
+           i++;
+           if (i == argc) {
+               pam_afs_syslog(LOG_ERR, PAMAFS_IGNOREUID,
+                              "ignore_uid missing argument");
+               ignore_uid = 0;
+           } else {
+               ignore_uid = 1;
+               ignore_uid_id = (uid_t) strtol(argv[i], (char **)NULL, 10);
+               if ((ignore_uid_id < 0) || (ignore_uid_id > IGNORE_MAX)) {
+                   ignore_uid = 0;
+                   pam_afs_syslog(LOG_ERR, PAMAFS_IGNOREUID, argv[i]);
+               }
+           }
+       } else if (strcasecmp(argv[i], "cell") == 0) {
+           i++;
+           if (i == argc) {
+               pam_afs_syslog(LOG_ERR, PAMAFS_OTHERCELL,
+                              "cell missing argument");
+           } else {
+               cell_ptr = (char *)argv[i];
+               pam_afs_syslog(LOG_INFO, PAMAFS_OTHERCELL, cell_ptr);
+           }
+       } else if (strcasecmp(argv[i], "refresh_token") == 0) {
+           refresh_token = 1;
+       } else if (strcasecmp(argv[i], "set_token") == 0) {
+           set_token = 1;
+       } else if (strcasecmp(argv[i], "dont_fork") == 0) {
+           if (!use_klog)
+               dont_fork = 1;
+           else
+               pam_afs_syslog(LOG_ERR, PAMAFS_CONFLICTOPT, "dont_fork");
+       } else if (strcasecmp(argv[i], "use_klog") == 0) {
+           if (!dont_fork)
+               use_klog = 1;
+           else
+               pam_afs_syslog(LOG_ERR, PAMAFS_CONFLICTOPT, "use_klog");
        } else if (strcasecmp(argv[i], "setenv_password_expires") == 0) {
-            set_expires = 1;
+           set_expires = 1;
        } else {
            pam_afs_syslog(LOG_ERR, PAMAFS_UNKNOWNOPT, argv[i]);
        }
@@ -99,90 +136,89 @@ pam_sm_authenticate(
 
     /* Later we use try_first_pass to see if we can try again.    */
     /* If use_first_pass is true we don't want to ever try again, */
-    /* so turn that flag off right now.                                  */
-    if (use_first_pass) try_first_pass = 0;
+    /* so turn that flag off right now.                           */
+    if (use_first_pass)
+       try_first_pass = 0;
 
-    pam_afs_syslog(LOG_DEBUG, PAMAFS_OPTIONS, nowarn, use_first_pass, try_first_pass);
+    if (logmask && LOG_MASK(LOG_DEBUG))
+       pam_afs_syslog(LOG_DEBUG, PAMAFS_OPTIONS, nowarn, use_first_pass,
+                      try_first_pass, ignore_uid, ignore_uid_id,
+                      refresh_token, set_token, dont_fork, use_klog);
 
     /* Try to get the user-interaction info, if available. */
-    errcode = pam_get_item(pamh, PAM_CONV, (void **) &pam_convp);
+    errcode = pam_get_item(pamh, PAM_CONV, (PAM_CONST void **)&pam_convp);
     if (errcode != PAM_SUCCESS) {
        pam_afs_syslog(LOG_WARNING, PAMAFS_NO_USER_INT);
        pam_convp = NULL;
     }
 
     /* Who are we trying to authenticate here? */
-    if ((errcode = pam_get_user(pamh, &user, "login: ")) != PAM_SUCCESS) {
+    if ((errcode =
+        pam_get_user(pamh, &user,
+                     "login: ")) != PAM_SUCCESS) {
        pam_afs_syslog(LOG_ERR, PAMAFS_NOUSER, errcode);
        RET(PAM_USER_UNKNOWN);
     }
 
-    if ((!strncmp ("root", user, 4)) && trust_root) {
-       pam_afs_syslog(LOG_INFO, PAMAFS_TRUSTROOT, user);
-       RET(PAM_SUCCESS);
-    }
-
-    pam_afs_syslog(LOG_DEBUG, PAMAFS_USERNAMEDEBUG, user);
+    if (logmask && LOG_MASK(LOG_DEBUG))
+       pam_afs_syslog(LOG_DEBUG, PAMAFS_USERNAMEDEBUG, user);
 
     /*
      * If the user has a "local" (or via nss, possibly nss_dce) pwent,
      * and its uid==0, and "ignore_root" was given in pam.conf,
      * ignore the user.
      */
-#if    defined(AFS_HPUX_ENV)
-#if     defined(AFS_HPUX110_ENV)
+    /* enhanced: use "ignore_uid <number>" to specify the largest uid
+     * which should be ignored by this module
+     */
+#if    defined(AFS_HPUX_ENV) || defined(AFS_DARWIN100_ENV) || defined(AFS_SUN5_ENV)
+#if     defined(AFS_HPUX110_ENV) || defined(AFS_DARWIN100_ENV) || defined(AFS_SUN5_ENV)
     i = getpwnam_r(user, &unix_pwd, upwd_buf, sizeof(upwd_buf), &upwd);
-#else   /* AFS_HPUX110_ENV */
+#else /* AFS_HPUX110_ENV */
     i = getpwnam_r(user, &unix_pwd, upwd_buf, sizeof(upwd_buf));
-    if ( i == 0 )                      /* getpwnam_r success */
-       upwd = &unix_pwd; 
-#endif  /* else AFS_HPUX110_ENV */
-    if (ignore_root && i == 0  && upwd->pw_uid == 0) {
+    if (i == 0)                        /* getpwnam_r success */
+       upwd = &unix_pwd;
+#endif /* else AFS_HPUX110_ENV */
+    if (ignore_uid && i == 0 && upwd && upwd->pw_uid <= ignore_uid_id) {
        pam_afs_syslog(LOG_INFO, PAMAFS_IGNORINGROOT, user);
        RET(PAM_AUTH_ERR);
     }
 #else
-#ifdef AFS_LINUX20_ENV
+#if     defined(AFS_LINUX20_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV) || defined(AFS_NBSD_ENV)
     upwd = getpwnam(user);
 #else
     upwd = getpwnam_r(user, &unix_pwd, upwd_buf, sizeof(upwd_buf));
 #endif
-    if (upwd != NULL && upwd->pw_uid == 0) {
-       if (ignore_root) { 
-               pam_afs_syslog(LOG_INFO, PAMAFS_IGNORINGROOT, user);
-               RET(PAM_AUTH_ERR);
-       } else if (trust_root && !catch_su) {
-               pam_afs_syslog(LOG_INFO, PAMAFS_TRUSTROOT, user);
-               RET(PAM_SUCCESS);
-       }
+    if (ignore_uid && upwd != NULL && upwd->pw_uid <= ignore_uid_id) {
+       pam_afs_syslog(LOG_INFO, PAMAFS_IGNORINGROOT, user);
+       RET(PAM_AUTH_ERR);
     }
 #endif
-    errcode = pam_get_item(pamh, PAM_AUTHTOK, (void **) &password);
+    errcode = pam_get_item(pamh, PAM_AUTHTOK, (PAM_CONST void **)&password);
     if (errcode != PAM_SUCCESS || password == NULL) {
        if (use_first_pass) {
            pam_afs_syslog(LOG_ERR, PAMAFS_PASSWD_REQ, user);
            RET(PAM_AUTH_ERR);
        }
        password = NULL;        /* In case it isn't already NULL */
-       pam_afs_syslog(LOG_DEBUG, PAMAFS_NOFIRSTPASS, user);
+       if (logmask && LOG_MASK(LOG_DEBUG))
+           pam_afs_syslog(LOG_DEBUG, PAMAFS_NOFIRSTPASS, user);
     } else if (password[0] == '\0') {
        /* Actually we *did* get one but it was empty. */
-       torch_password = 0;
        pam_afs_syslog(LOG_INFO, PAMAFS_NILPASSWORD, user);
        RET(PAM_NEW_AUTHTOK_REQD);
     } else {
-       pam_afs_syslog(LOG_DEBUG, PAMAFS_GOTPASS, user);
-       torch_password = 0;
+       if (logmask && LOG_MASK(LOG_DEBUG))
+           pam_afs_syslog(LOG_DEBUG, PAMAFS_GOTPASS, user);
        got_authtok = 1;
     }
     if (!(use_first_pass || try_first_pass)) {
        password = NULL;
     }
 
-try_auth:
+  try_auth:
     if (password == NULL) {
-
-       torch_password = 1;
+       char *prompt_password;
 
        if (use_first_pass)
            RET(PAM_AUTH_ERR);  /* shouldn't happen */
@@ -194,78 +230,157 @@ try_auth:
            RET(PAM_AUTH_ERR);
        }
 
-       errcode = pam_afs_prompt(pam_convp, &password, 0, PAMAFS_PWD_PROMPT);
-       if (errcode != PAM_SUCCESS || password == NULL) {
+       errcode = pam_afs_prompt(pam_convp, &prompt_password, 0, PAMAFS_PWD_PROMPT);
+       if (errcode != PAM_SUCCESS || prompt_password == NULL) {
            pam_afs_syslog(LOG_ERR, PAMAFS_GETPASS_FAILED);
            RET(PAM_AUTH_ERR);
        }
-       if (password[0] == '\0') {
+       if (prompt_password[0] == '\0') {
            pam_afs_syslog(LOG_INFO, PAMAFS_NILPASSWORD, user);
            RET(PAM_NEW_AUTHTOK_REQD);
        }
 
        /*
-         * We aren't going to free the password later (we will wipe it,
-         * though), because the storage for it if we get it from other
-         * paths may belong to someone else.  Since we do need to free
-         * this storage, copy it to a buffer that won't need to be freed
-         * later, and free this storage now.
-         */
-        strncpy(my_password_buf, password, sizeof(my_password_buf));
-        my_password_buf[sizeof(my_password_buf)-1] = '\0';
-        memset(password, 0, strlen(password));
-        free(password);
-        password = my_password_buf;
+        * We aren't going to free the password later (we will wipe it,
+        * though), because the storage for it if we get it from other
+        * paths may belong to someone else.  Since we do need to free
+        * this storage, copy it to a buffer that won't need to be freed
+        * later, and free this storage now.
+        */
 
+       strncpy(my_password_buf, prompt_password, sizeof(my_password_buf));
+       my_password_buf[sizeof(my_password_buf) - 1] = '\0';
+       memset(prompt_password, 0, strlen(prompt_password));
+       free(prompt_password);
+       password = torch_password = my_password_buf;
 
     }
 
-    /* Prepare for fork(): set SIGCHLD signal handler to default */
-    sigemptyset(&newAction.sa_mask);
-    newAction.sa_handler   = SIG_DFL;
-    newAction.sa_flags     = 0;
-    code = sigaction(SIGCHLD, &newAction, &origAction);
-    if (code) {
-       pam_afs_syslog(LOG_ERR, PAMAFS_PAMERROR, errno);
-       RET(PAM_AUTH_ERR);
-    }
-
-    /* Fork a process and let it verify authentication. So that any
-     * memory/sockets allocated will get cleaned up when the child
-     * exits: defect 11686.
+    /* Be sure to allocate a PAG here if we should set a token,
+     * All of the remaining stuff to authenticate the user and to
+     * get a token is done in a child process - if not suppressed by the config,
+     * see below
+     * But dont get a PAG if the refresh_token option was set
+     * We have to do this in such a way because some
+     * apps (such as screensavers) wont call setcred but authenticate :-(
      */
-    cpid = fork();
-    if (cpid <= 0) {     /* The child process */
-       code = ka_VerifyUserPassword(KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG,
-                                   user, /* kerberos name */
-                                   (char *)0, /* instance */
-                                   (char *)0, /* realm */
-                                   password, /* password */
-                                   0, /* spare 2 */
-                                   &reason /* error string */ );
-       if (code) {
-         pam_afs_syslog(LOG_ERR, PAMAFS_LOGIN_FAILED, user, reason);
-         auth_ok = 0;
-       } else {
-         auth_ok = 1;
-       }
-       if (cpid == 0) exit(auth_ok);
-    } else {
-       do {
-         rcpid = waitpid(cpid, &status, 0);
-       } while ((rcpid == -1) && (errno == EINTR));
-       
-       if ((rcpid == cpid) && WIFEXITED(status)) {
-         auth_ok = WEXITSTATUS(status);
-       } else {
-         auth_ok = 0;
-       }
+    if (!refresh_token) {
+       setpag();
+#ifdef AFS_KERBEROS_ENV
+       ktc_newpag();
+#endif
+       if (logmask && LOG_MASK(LOG_DEBUG))
+           syslog(LOG_DEBUG, "New PAG created in pam_authenticate()");
     }
 
-    /* Restore old signal handler */
-    code = sigaction(SIGCHLD, &origAction, (struct sigaction *)0);
-    if (code) {
-       pam_afs_syslog(LOG_ERR, PAMAFS_PAMERROR, errno);
+    if (!dont_fork) {
+       /* Prepare for fork(): set SIGCHLD signal handler to default */
+       sigemptyset(&newAction.sa_mask);
+       newAction.sa_handler = SIG_DFL;
+       newAction.sa_flags = 0;
+       code = sigaction(SIGCHLD, &newAction, &origAction);
+       if (code) {
+           pam_afs_syslog(LOG_ERR, PAMAFS_PAMERROR, errno);
+           RET(PAM_AUTH_ERR);
+       }
+
+       /* Fork a process and let it verify authentication. So that any
+        * memory/sockets allocated will get cleaned up when the child
+        * exits: defect 11686.
+        */
+       if (use_klog) {         /* used by kdm 2.x */
+           if (refresh_token || set_token) {
+               i = do_klog(user, password, NULL, cell_ptr);
+           } else {
+               i = do_klog(user, password, "00:00:01", cell_ptr);
+               ktc_ForgetAllTokens();
+           }
+           if (logmask && LOG_MASK(LOG_DEBUG))
+               syslog(LOG_DEBUG, "do_klog returned %d", i);
+           auth_ok = i ? 0 : 1;
+       } else {
+           if (logmask && LOG_MASK(LOG_DEBUG))
+               syslog(LOG_DEBUG, "forking ...");
+           cpid = fork();
+           if (cpid <= 0) {    /* The child process */
+               if (logmask && LOG_MASK(LOG_DEBUG))
+                   syslog(LOG_DEBUG, "in child");
+               if (refresh_token || set_token)
+                   code = ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION, (char *)user,        /* kerberos name */
+                                                     NULL,     /* instance */
+                                                     cell_ptr, /* realm */
+                                                     (char *)password, /* password */
+                                                     0,        /* default lifetime */
+                                                     &password_expires, 0,     /* spare 2 */
+                                                     &reason
+                                                     /* error string */ );
+               else
+                   code = ka_VerifyUserPassword(KA_USERAUTH_VERSION, (char *)user,     /* kerberos name */
+                                                NULL,  /* instance */
+                                                cell_ptr,      /* realm */
+                                                (char *)password,      /* password */
+                                                0,     /* spare 2 */
+                                                &reason /* error string */ );
+               if (code) {
+                   pam_afs_syslog(LOG_ERR, PAMAFS_LOGIN_FAILED, user,
+                                  reason);
+                   auth_ok = 0;
+               } else {
+                   auth_ok = 1;
+               }
+               if (logmask && LOG_MASK(LOG_DEBUG))
+                   syslog(LOG_DEBUG, "child: auth_ok=%d", auth_ok);
+               if (cpid == 0)
+                   exit(auth_ok);
+           } else {
+               do {
+                   if (logmask && LOG_MASK(LOG_DEBUG))
+                       syslog(LOG_DEBUG, "in parent, waiting ...");
+                   rcpid = waitpid(cpid, &status, 0);
+               } while ((rcpid == -1) && (errno == EINTR));
+
+               if ((rcpid == cpid) && WIFEXITED(status)) {
+                   auth_ok = WEXITSTATUS(status);
+               } else {
+                   auth_ok = 0;
+               }
+               if (logmask && LOG_MASK(LOG_DEBUG))
+                   syslog(LOG_DEBUG, "parent: auth_ok=%d", auth_ok);
+           }
+       }
+       /* Restore old signal handler */
+       code = sigaction(SIGCHLD, &origAction, NULL);
+       if (code) {
+           pam_afs_syslog(LOG_ERR, PAMAFS_PAMERROR, errno);
+       }
+    } else {                   /* dont_fork, used by httpd */
+       if (logmask && LOG_MASK(LOG_DEBUG))
+           syslog(LOG_DEBUG, "dont_fork");
+       if (refresh_token || set_token)
+           code = ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION, (char *)user,        /* kerberos name */
+                                             NULL,     /* instance */
+                                             cell_ptr, /* realm */
+                                             (char *)password, /* password */
+                                             0,        /* default lifetime */
+                                             &password_expires, 0,     /* spare 2 */
+                                             &reason /* error string */ );
+       else
+           code = ka_VerifyUserPassword(KA_USERAUTH_VERSION, (char *)user,     /* kerberos name */
+                                        NULL,  /* instance */
+                                        cell_ptr,      /* realm */
+                                        (char *)password,      /* password */
+                                        0,     /* spare 2 */
+                                        &reason /* error string */ );
+       if (logmask && LOG_MASK(LOG_DEBUG))
+           syslog(LOG_DEBUG, "dont_fork, code = %d", code);
+       if (code) {
+           pam_afs_syslog(LOG_ERR, PAMAFS_LOGIN_FAILED, user, reason);
+           auth_ok = 0;
+       } else {
+           auth_ok = 1;
+       }
+       if (logmask && LOG_MASK(LOG_DEBUG))
+           syslog(LOG_DEBUG, "dont_fork: auth_ok=%d", auth_ok);
     }
 
     if (!auth_ok && try_first_pass) {
@@ -274,23 +389,31 @@ try_auth:
     }
 
     /* We don't care if this fails; all we can do is try. */
-    if (auth_ok && !got_authtok) {
-       torch_password = 0;
-        pam_set_item(pamh, PAM_AUTHTOK, password);
+    /* It is not reasonable to store the password only if it was correct
+     * because it could satisfy another module that is called in the chain
+     * after pam_afs
+     */
+    if (!got_authtok) {
+       torch_password = NULL;
+       (void)pam_set_item(pamh, PAM_AUTHTOK, password);
     }
 
+    if (logmask && LOG_MASK(LOG_DEBUG))
+       syslog(LOG_DEBUG, "leaving auth: auth_ok=%d", auth_ok);
+    if (code == KANOENT)
+       RET(PAM_USER_UNKNOWN);
     RET(auth_ok ? PAM_SUCCESS : PAM_AUTH_ERR);
-       
- out:
-    if ( password  )
-    {
+
+  out:
+    if (password) {
        /* we store the password in the data portion */
-       char*   tmp = strdup(password);
-       (void) pam_set_data(pamh, pam_afs_lh, tmp, lc_cleanup);
-        if ( torch_password) memset(password, 0, strlen(password));
+       char *tmp = strdup(password);
+       (void)pam_set_data(pamh, pam_afs_lh, tmp, lc_cleanup);
+       if (torch_password)
+           memset(torch_password, 0, strlen(torch_password));
     }
-    (void) setlogmask(origmask);
-#ifndef AFS_SUN56_ENV
+    (void)setlogmask(origmask);
+#ifndef AFS_SUN5_ENV
     closelog();
 #endif
     return retcode;