Imported Upstream version 1.7.4p4
[debian/sudo] / auth / pam.c
index d289a06ef5c3510fdfd34211e5bfc1a6f831d64a..ca2ef10695754e0c49c4610ed2f67567b6b92e0e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 1999-2005, 2007-2010 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +18,7 @@
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
  */
 
-#include "config.h"
+#include <config.h>
 
 #include <sys/types.h>
 #include <sys/param.h>
 # endif
 #endif /* STDC_HEADERS */
 #ifdef HAVE_STRING_H
-# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
-#  include <memory.h>
-# endif
 # include <string.h>
-#else
-# ifdef HAVE_STRINGS_H
-#  include <strings.h>
-# endif
 #endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <pwd.h>
+#include <errno.h>
 
 #ifdef HAVE_PAM_PAM_APPL_H
 # include <pam/pam_appl.h>
 # include <security/pam_appl.h>
 #endif
 
+#ifdef HAVE_DGETTEXT
+# include <libintl.h>
+# if defined(__LINUX_PAM__)
+#  define PAM_TEXT_DOMAIN      "Linux-PAM"
+# elif defined(__sun__)
+#  define PAM_TEXT_DOMAIN      "SUNW_OST_SYSOSPAM"
+# endif
+#endif
+
 #include "sudo.h"
 #include "sudo_auth.h"
 
 /* Only OpenPAM and Linux PAM use const qualifiers. */
-#if defined(_OPENPAM) || defined(__LIBPAM_VERSION)
+#if defined(_OPENPAM) || defined(OPENPAM_VERSION) || \
+    defined(__LIBPAM_VERSION) || defined(__LINUX_PAM__)
 # define PAM_CONST     const
 #else
 # define PAM_CONST
 #endif
 
-#ifndef lint
-static const char rcsid[] = "$Sudo: pam.c,v 1.43 2004/06/28 14:51:50 millert Exp $";
-#endif /* lint */
-
 static int sudo_conv __P((int, PAM_CONST struct pam_message **,
-                         struct pam_response **, VOID *));
-static char *def_prompt;
+                         struct pam_response **, void *));
+static char *def_prompt = "Password:";
+static int gotintr;
 
 #ifndef PAM_DATA_SILENT
 #define PAM_DATA_SILENT        0
@@ -87,15 +91,38 @@ pam_init(pw, promptp, auth)
 
     /* Initial PAM setup */
     if (auth != NULL)
-       auth->data = (VOID *) &pam_status;
+       auth->data = (void *) &pam_status;
     pam_conv.conv = sudo_conv;
-    pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh);
+#ifdef HAVE_PAM_LOGIN
+    if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
+       pam_status = pam_start("sudo-i", pw->pw_name, &pam_conv, &pamh);
+    else
+#endif
+       pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh);
+
     if (pam_status != PAM_SUCCESS) {
        log_error(USE_ERRNO|NO_EXIT|NO_MAIL, "unable to initialize PAM");
        return(AUTH_FATAL);
     }
-    if (strcmp(user_tty, "unknown"))
-       (void) pam_set_item(pamh, PAM_TTY, user_tty);
+
+    /*
+     * Set PAM_RUSER to the invoking user (the "from" user).
+     * We set PAM_RHOST to avoid a bug in Solaris 7 and below.
+     */
+    (void) pam_set_item(pamh, PAM_RUSER, user_name);
+#ifdef __sun__
+    (void) pam_set_item(pamh, PAM_RHOST, user_host);
+#endif
+
+    /*
+     * Some versions of pam_lastlog have a bug that
+     * will cause a crash if PAM_TTY is not set so if
+     * there is no tty, set PAM_TTY to the empty string.
+     */
+    if (user_ttypath == NULL)
+       (void) pam_set_item(pamh, PAM_TTY, "");
+    else
+       (void) pam_set_item(pamh, PAM_TTY, user_ttypath);
 
     return(AUTH_SUCCESS);
 }
@@ -146,6 +173,10 @@ pam_verify(pw, prompt, auth)
            }
            /* FALLTHROUGH */
        case PAM_AUTH_ERR:
+           if (gotintr) {
+               /* error or ^C from tgetpass() */
+               return(AUTH_INTR);
+           }
        case PAM_MAXTRIES:
        case PAM_PERM_DENIED:
            return(AUTH_FAILURE);
@@ -172,18 +203,20 @@ pam_cleanup(pw, auth)
 }
 
 int
-pam_prep_user(pw)
+pam_begin_session(pw)
     struct passwd *pw;
 {
+    int status =  PAM_SUCCESS;
+
+    /* If the user did not have to authenticate there is no pam handle yet. */
     if (pamh == NULL)
        pam_init(pw, NULL, NULL);
 
     /*
-     * Set PAM_USER to the user we are changing *to* and
-     * set PAM_RUSER to the user we are coming *from*.
+     * Update PAM_USER to reference the user we are running the command
+     * as, as opposed to the user we authenticated as.
      */
     (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
-    (void) pam_set_item(pamh, PAM_RUSER, user_name);
 
     /*
      * Set credentials (may include resource limits, device ownership, etc).
@@ -195,10 +228,28 @@ pam_prep_user(pw)
      */
     (void) pam_setcred(pamh, PAM_ESTABLISH_CRED);
 
-    if (pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT) == PAM_SUCCESS)
-       return(AUTH_SUCCESS);
-    else
-       return(AUTH_FAILURE);
+#ifndef NO_PAM_SESSION
+    status = pam_open_session(pamh, 0);
+     if (status != PAM_SUCCESS) {
+       (void) pam_end(pamh, status | PAM_DATA_SILENT);
+       pamh = NULL;
+    }
+#endif
+    return(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
+}
+
+int
+pam_end_session()
+{
+    int status = PAM_SUCCESS;
+
+    if (pamh != NULL) {
+#ifndef NO_PAM_SESSION
+       (void) pam_close_session(pamh, 0);
+#endif
+       status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
+    }
+    return(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
 }
 
 /*
@@ -210,17 +261,16 @@ sudo_conv(num_msg, msg, response, appdata_ptr)
     int num_msg;
     PAM_CONST struct pam_message **msg;
     struct pam_response **response;
-    VOID *appdata_ptr;
+    void *appdata_ptr;
 {
     struct pam_response *pr;
     PAM_CONST struct pam_message *pm;
-    const char *p = def_prompt;
+    const char *prompt;
     char *pass;
-    int n, flags;
-    extern int nil_pw;
+    int n, flags, std_prompt;
 
     if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)
-       return(PAM_CONV_ERR);
+       return(PAM_SYSTEM_ERR);
     zero_bytes(*response, num_msg * sizeof(struct pam_response));
 
     for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {
@@ -229,17 +279,43 @@ sudo_conv(num_msg, msg, response, appdata_ptr)
            case PAM_PROMPT_ECHO_ON:
                SET(flags, TGP_ECHO);
            case PAM_PROMPT_ECHO_OFF:
+               prompt = def_prompt;
+
+               /* Error out if the last password read was interrupted. */
+               if (gotintr)
+                   goto err;
+
+               /* Is the sudo prompt standard? (If so, we'l just use PAM's) */
+               std_prompt =  strncmp(def_prompt, "Password:", 9) == 0 &&
+                   (def_prompt[9] == '\0' ||
+                   (def_prompt[9] == ' ' && def_prompt[10] == '\0'));
+
                /* Only override PAM prompt if it matches /^Password: ?/ */
-               if (strncmp(pm->msg, "Password:", 9) || (pm->msg[9] != '\0'
-                   && (pm->msg[9] != ' ' || pm->msg[10] != '\0')))
-                   p = pm->msg;
-               /* Read the password. */
-               pass = tgetpass(p, def_passwd_timeout * 60, flags);
-               pr->resp = estrdup(pass ? pass : "");
-               if (*pr->resp == '\0')
-                   nil_pw = 1;         /* empty password */
-               else
-                   zero_bytes(pass, strlen(pass));
+#if defined(PAM_TEXT_DOMAIN) && defined(HAVE_DGETTEXT)
+               if (!def_passprompt_override && (std_prompt ||
+                   (strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password: ")) &&
+                   strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password:")))))
+                   prompt = pm->msg;
+#else
+               if (!def_passprompt_override && (std_prompt ||
+                   strncmp(pm->msg, "Password:", 9) || (pm->msg[9] != '\0'
+                   && (pm->msg[9] != ' ' || pm->msg[10] != '\0'))))
+                   prompt = pm->msg;
+#endif
+               /* Read the password unless interrupted. */
+               pass = tgetpass(prompt, def_passwd_timeout * 60, flags);
+               if (pass == NULL) {
+                   /* We got ^C instead of a password; abort quickly. */
+                   if (errno == EINTR)
+                       gotintr = 1;
+#if defined(__darwin__) || defined(__APPLE__)
+                   pass = "";
+#else
+                   goto err;
+#endif
+               }
+               pr->resp = estrdup(pass);
+               zero_bytes(pass, strlen(pass));
                break;
            case PAM_TEXT_INFO:
                if (pm->msg)
@@ -252,20 +328,23 @@ sudo_conv(num_msg, msg, response, appdata_ptr)
                }
                break;
            default:
-               /* Zero and free allocated memory and return an error. */
-               for (pr = *response, n = num_msg; n--; pr++) {
-                   if (pr->resp != NULL) {
-                       zero_bytes(pr->resp, strlen(pr->resp));
-                       free(pr->resp);
-                       pr->resp = NULL;
-                   }
-               }
-               zero_bytes(*response, num_msg * sizeof(struct pam_response));
-               free(*response);
-               *response = NULL;
-               return(PAM_CONV_ERR);
+               goto err;
        }
     }
 
     return(PAM_SUCCESS);
+
+err:
+    /* Zero and free allocated memory and return an error. */
+    for (pr = *response, n = num_msg; n--; pr++) {
+       if (pr->resp != NULL) {
+           zero_bytes(pr->resp, strlen(pr->resp));
+           free(pr->resp);
+           pr->resp = NULL;
+       }
+    }
+    zero_bytes(*response, num_msg * sizeof(struct pam_response));
+    free(*response);
+    *response = NULL;
+    return(gotintr ? PAM_AUTH_ERR : PAM_CONV_ERR);
 }