Diff for /embedaddon/sudo/plugins/sudoers/auth/pam.c between versions 1.1.1.1 and 1.1.1.5

version 1.1.1.1, 2012/02/21 16:23:02 version 1.1.1.5, 2014/06/15 16:12:54
Line 1 Line 1
 /*  /*
 * Copyright (c) 1999-2005, 2007-2011 Todd C. Miller <Todd.Miller@courtesan.com> * Copyright (c) 1999-2005, 2007-2013 Todd C. Miller <Todd.Miller@courtesan.com>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
Line 21 Line 21
 #include <config.h>  #include <config.h>
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/param.h>  
 #include <stdio.h>  #include <stdio.h>
 #ifdef STDC_HEADERS  #ifdef STDC_HEADERS
 # include <stdlib.h>  # include <stdlib.h>
Line 57 Line 56
 # endif  # endif
 #endif  #endif
   
   /* We don't want to translate the strings in the calls to dgt(). */
   #ifdef PAM_TEXT_DOMAIN
   # define dgt(d, t)      dgettext(d, t)
   #endif
   
 #include "sudoers.h"  #include "sudoers.h"
 #include "sudo_auth.h"  #include "sudo_auth.h"
   
Line 68 Line 72
 # define PAM_CONST  # define PAM_CONST
 #endif  #endif
   
 static int converse(int, PAM_CONST struct pam_message **,  
                     struct pam_response **, void *);  
 static char *def_prompt = "Password:";  
 static int gotintr;  
   
 #ifndef PAM_DATA_SILENT  #ifndef PAM_DATA_SILENT
 #define PAM_DATA_SILENT 0  #define PAM_DATA_SILENT 0
 #endif  #endif
   
   static int converse(int, PAM_CONST struct pam_message **,
                       struct pam_response **, void *);
   static char *def_prompt = "Password:";
   static int getpass_error;
 static pam_handle_t *pamh;  static pam_handle_t *pamh;
   
 int  int
pam_init(struct passwd *pw, sudo_auth *auth)sudo_pam_init(struct passwd *pw, sudo_auth *auth)
 {  {
     static struct pam_conv pam_conv;      static struct pam_conv pam_conv;
     static int pam_status;      static int pam_status;
       debug_decl(sudo_pam_init, SUDO_DEBUG_AUTH)
   
     /* Initial PAM setup */      /* Initial PAM setup */
    if (auth != NULL)    auth->data = (void *) &pam_status;
        auth->data = (void *) &pam_status; 
     pam_conv.conv = converse;      pam_conv.conv = converse;
#ifdef HAVE_PAM_LOGIN    pam_status = pam_start(ISSET(sudo_mode, MODE_LOGIN_SHELL) ?
    if (ISSET(sudo_mode, MODE_LOGIN_SHELL))        def_pam_login_service : def_pam_service, pw->pw_name, &pam_conv, &pamh);
        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) {      if (pam_status != PAM_SUCCESS) {
        log_error(USE_ERRNO|NO_EXIT|NO_MAIL, _("unable to initialize PAM"));        log_warning(USE_ERRNO|NO_MAIL, N_("unable to initialize PAM"));
        return AUTH_FATAL;        debug_return_int(AUTH_FATAL);
     }      }
   
     /*      /*
Line 119  pam_init(struct passwd *pw, sudo_auth *auth) Line 118  pam_init(struct passwd *pw, sudo_auth *auth)
     else      else
         (void) pam_set_item(pamh, PAM_TTY, user_ttypath);          (void) pam_set_item(pamh, PAM_TTY, user_ttypath);
   
    return AUTH_SUCCESS;    /*
      * If PAM session and setcred support is disabled we don't
      * need to keep a sudo process around to close the session.
      */
     if (!def_pam_session && !def_pam_setcred)
         auth->end_session = NULL;
 
     debug_return_int(AUTH_SUCCESS);
 }  }
   
 int  int
pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth)sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth)
 {  {
     const char *s;      const char *s;
     int *pam_status = (int *) auth->data;      int *pam_status = (int *) auth->data;
       debug_decl(sudo_pam_verify, SUDO_DEBUG_AUTH)
   
     def_prompt = prompt;        /* for converse */      def_prompt = prompt;        /* for converse */
   
Line 137  pam_verify(struct passwd *pw, char *prompt, sudo_auth  Line 144  pam_verify(struct passwd *pw, char *prompt, sudo_auth 
             *pam_status = pam_acct_mgmt(pamh, PAM_SILENT);              *pam_status = pam_acct_mgmt(pamh, PAM_SILENT);
             switch (*pam_status) {              switch (*pam_status) {
                 case PAM_SUCCESS:                  case PAM_SUCCESS:
                    return AUTH_SUCCESS;                    debug_return_int(AUTH_SUCCESS);
                 case PAM_AUTH_ERR:                  case PAM_AUTH_ERR:
                    log_error(NO_EXIT|NO_MAIL, _("account validation failure, "                    log_warning(NO_MAIL, N_("account validation failure, "
                         "is your account locked?"));                          "is your account locked?"));
                    return AUTH_FATAL;                    debug_return_int(AUTH_FATAL);
                 case PAM_NEW_AUTHTOK_REQD:                  case PAM_NEW_AUTHTOK_REQD:
                    log_error(NO_EXIT|NO_MAIL, _("Account or password is "                    log_warning(NO_MAIL, N_("Account or password is "
                         "expired, reset your password and try again"));                          "expired, reset your password and try again"));
                     *pam_status = pam_chauthtok(pamh,                      *pam_status = pam_chauthtok(pamh,
                         PAM_CHANGE_EXPIRED_AUTHTOK);                          PAM_CHANGE_EXPIRED_AUTHTOK);
                     if (*pam_status == PAM_SUCCESS)                      if (*pam_status == PAM_SUCCESS)
                        return AUTH_SUCCESS;                        debug_return_int(AUTH_SUCCESS);
                    if ((s = pam_strerror(pamh, *pam_status)))                    if ((s = pam_strerror(pamh, *pam_status)) != NULL) {
                        log_error(NO_EXIT|NO_MAIL, _("pam_chauthtok: %s"), s);                        log_warning(NO_MAIL,
                    return AUTH_FAILURE;                            N_("unable to change expired password: %s"), s);
                     }
                     debug_return_int(AUTH_FAILURE);
                 case PAM_AUTHTOK_EXPIRED:                  case PAM_AUTHTOK_EXPIRED:
                    log_error(NO_EXIT|NO_MAIL,                    log_warning(NO_MAIL,
                        _("Password expired, contact your system administrator"));                        N_("Password expired, contact your system administrator"));
                    return AUTH_FATAL;                    debug_return_int(AUTH_FATAL);
                 case PAM_ACCT_EXPIRED:                  case PAM_ACCT_EXPIRED:
                    log_error(NO_EXIT|NO_MAIL,                    log_warning(NO_MAIL,
                        _("Account expired or PAM config lacks an \"account\" "                        N_("Account expired or PAM config lacks an \"account\" "
                         "section for sudo, contact your system administrator"));                          "section for sudo, contact your system administrator"));
                    return AUTH_FATAL;                    debug_return_int(AUTH_FATAL);
             }              }
             /* FALLTHROUGH */              /* FALLTHROUGH */
         case PAM_AUTH_ERR:          case PAM_AUTH_ERR:
            if (gotintr) {        case PAM_AUTHINFO_UNAVAIL:
             if (getpass_error) {
                 /* error or ^C from tgetpass() */                  /* error or ^C from tgetpass() */
                return AUTH_INTR;                debug_return_int(AUTH_INTR);
             }              }
               /* FALLTHROUGH */
         case PAM_MAXTRIES:          case PAM_MAXTRIES:
         case PAM_PERM_DENIED:          case PAM_PERM_DENIED:
            return AUTH_FAILURE;            debug_return_int(AUTH_FAILURE);
         default:          default:
            if ((s = pam_strerror(pamh, *pam_status)))            if ((s = pam_strerror(pamh, *pam_status)) != NULL)
                log_error(NO_EXIT|NO_MAIL, _("pam_authenticate: %s"), s);                log_warning(NO_MAIL, N_("PAM authentication error: %s"), s);
            return AUTH_FATAL;            debug_return_int(AUTH_FATAL);
     }      }
 }  }
   
 int  int
pam_cleanup(struct passwd *pw, sudo_auth *auth)sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth)
 {  {
     int *pam_status = (int *) auth->data;      int *pam_status = (int *) auth->data;
       debug_decl(sudo_pam_cleanup, SUDO_DEBUG_AUTH)
   
    /* If successful, we can't close the session until pam_end_session() */    /* If successful, we can't close the session until sudo_pam_end_session() */
    if (*pam_status == AUTH_SUCCESS)    if (*pam_status != PAM_SUCCESS || auth->end_session == NULL) {
        return AUTH_SUCCESS;        *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT);
        pamh = NULL;
    *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT);    }
    pamh = NULL;    debug_return_int(*pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
    return *pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE; 
 }  }
   
 int  int
pam_begin_session(struct passwd *pw, sudo_auth *auth)sudo_pam_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth)
 {  {
    int status = PAM_SUCCESS;    int status = AUTH_SUCCESS;
     int *pam_status = (int *) auth->data;
     debug_decl(sudo_pam_begin_session, SUDO_DEBUG_AUTH)
   
     /*      /*
      * If there is no valid user we cannot open a PAM session.       * If there is no valid user we cannot open a PAM session.
Line 217  pam_begin_session(struct passwd *pw, sudo_auth *auth) Line 230  pam_begin_session(struct passwd *pw, sudo_auth *auth)
     (void) pam_set_item(pamh, PAM_USER, pw->pw_name);      (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
   
     /*      /*
     * Set credentials (may include resource limits, device ownership, etc).     * Reinitialize credentials when changing the user.
     * We don't check the return value here because in Linux-PAM 0.75     * We don't worry about a failure from pam_setcred() since with
     * it returns the last saved return code, not the return code     * stacked PAM auth modules a failure from one module may override
     * for the setcred module.  Because we haven't called pam_authenticate(),     * PAM_SUCCESS from another.  For example, given a non-local user,
     * this is not set and so pam_setcred() returns PAM_PERM_DENIED.     * pam_unix will fail but pam_ldap or pam_sss may succeed, but if
     * We can't call pam_acct_mgmt() with Linux-PAM for a similar reason.     * pam_unix is first in the stack, pam_setcred() will fail.
      */       */
    (void) pam_setcred(pamh, PAM_ESTABLISH_CRED);    if (def_pam_setcred)
         (void) pam_setcred(pamh, PAM_REINITIALIZE_CRED);
   
#ifndef NO_PAM_SESSION    if (def_pam_session) {
    status = pam_open_session(pamh, 0);        *pam_status = pam_open_session(pamh, 0);
    if (status != PAM_SUCCESS) {        if (*pam_status != PAM_SUCCESS) {
        (void) pam_end(pamh, status | PAM_DATA_SILENT);            (void) pam_end(pamh, *pam_status | PAM_DATA_SILENT);
        pamh = NULL;            pamh = NULL;
             status = AUTH_FAILURE;
             goto done;
         }
     }      }
 #endif  
   
   #ifdef HAVE_PAM_GETENVLIST
       /*
        * Update environment based on what is stored in pamh.
        * If no authentication is done we will only have environment
        * variables if pam_env is called via session.
        */
       if (user_envp != NULL) {
           char **pam_envp = pam_getenvlist(pamh);
           if (pam_envp != NULL) {
               /* Merge pam env with user env. */
               env_init(*user_envp);
               env_merge(pam_envp);
               *user_envp = env_get();
               env_init(NULL);
               efree(pam_envp);
               /* XXX - we leak any duplicates that were in pam_envp */
           }
       }
   #endif /* HAVE_PAM_GETENVLIST */
   
 done:  done:
    return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;    debug_return_int(status);
 }  }
   
 int  int
pam_end_session(struct passwd *pw, sudo_auth *auth)sudo_pam_end_session(struct passwd *pw, sudo_auth *auth)
 {  {
    int status = PAM_SUCCESS;    int status = AUTH_SUCCESS;
     debug_decl(sudo_pam_end_session, SUDO_DEBUG_AUTH)
   
     if (pamh != NULL) {      if (pamh != NULL) {
 #ifndef NO_PAM_SESSION  
         /*          /*
          * Update PAM_USER to reference the user we are running the command           * Update PAM_USER to reference the user we are running the command
         * as to match the call to pam_open_session().         * as, as opposed to the user we authenticated as.
          * XXX - still needed now that session init is in parent?
          */           */
         (void) pam_set_item(pamh, PAM_USER, pw->pw_name);          (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
        (void) pam_close_session(pamh, PAM_SILENT);        if (def_pam_session)
#endif            (void) pam_close_session(pamh, PAM_SILENT);
        status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);        if (def_pam_setcred)
             (void) pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
         if (pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT) != PAM_SUCCESS)
             status = AUTH_FAILURE;
         pamh = NULL;          pamh = NULL;
     }      }
   
    return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;    debug_return_int(status);
 }  }
   
 /*  /*
Line 272  converse(int num_msg, PAM_CONST struct pam_message **m Line 312  converse(int num_msg, PAM_CONST struct pam_message **m
     const char *prompt;      const char *prompt;
     char *pass;      char *pass;
     int n, type, std_prompt;      int n, type, std_prompt;
       int ret = PAM_AUTH_ERR;
       debug_decl(converse, SUDO_DEBUG_AUTH)
   
     if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)      if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)
        return PAM_SYSTEM_ERR;        debug_return_int(PAM_SYSTEM_ERR);
    zero_bytes(*response, num_msg * sizeof(struct pam_response));    memset(*response, 0, num_msg * sizeof(struct pam_response));
   
     for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {      for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {
         type = SUDO_CONV_PROMPT_ECHO_OFF;          type = SUDO_CONV_PROMPT_ECHO_OFF;
         switch (pm->msg_style) {          switch (pm->msg_style) {
             case PAM_PROMPT_ECHO_ON:              case PAM_PROMPT_ECHO_ON:
                 type = SUDO_CONV_PROMPT_ECHO_ON;                  type = SUDO_CONV_PROMPT_ECHO_ON;
                   /* FALLTHROUGH */
             case PAM_PROMPT_ECHO_OFF:              case PAM_PROMPT_ECHO_OFF:
                 prompt = def_prompt;                  prompt = def_prompt;
   
                 /* Error out if the last password read was interrupted. */                  /* Error out if the last password read was interrupted. */
                if (gotintr)                if (getpass_error)
                    goto err;                    goto done;
   
                /* Is the sudo prompt standard? (If so, we'l just use PAM's) */                /* Is the sudo prompt standard? (If so, we'll just use PAM's) */
                 std_prompt =  strncmp(def_prompt, "Password:", 9) == 0 &&                  std_prompt =  strncmp(def_prompt, "Password:", 9) == 0 &&
                     (def_prompt[9] == '\0' ||                      (def_prompt[9] == '\0' ||
                     (def_prompt[9] == ' ' && def_prompt[10] == '\0'));                      (def_prompt[9] == ' ' && def_prompt[10] == '\0'));
Line 297  converse(int num_msg, PAM_CONST struct pam_message **m Line 340  converse(int num_msg, PAM_CONST struct pam_message **m
                 /* Only override PAM prompt if it matches /^Password: ?/ */                  /* Only override PAM prompt if it matches /^Password: ?/ */
 #if defined(PAM_TEXT_DOMAIN) && defined(HAVE_LIBINTL_H)  #if defined(PAM_TEXT_DOMAIN) && defined(HAVE_LIBINTL_H)
                 if (!def_passprompt_override && (std_prompt ||                  if (!def_passprompt_override && (std_prompt ||
                    (strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password: ")) &&                    (strcmp(pm->msg, dgt(PAM_TEXT_DOMAIN, "Password: ")) &&
                    strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password:")))))                    strcmp(pm->msg, dgt(PAM_TEXT_DOMAIN, "Password:")))))
                     prompt = pm->msg;                      prompt = pm->msg;
 #else  #else
                 if (!def_passprompt_override && (std_prompt ||                  if (!def_passprompt_override && (std_prompt ||
Line 309  converse(int num_msg, PAM_CONST struct pam_message **m Line 352  converse(int num_msg, PAM_CONST struct pam_message **m
                 /* Read the password unless interrupted. */                  /* Read the password unless interrupted. */
                 pass = auth_getpass(prompt, def_passwd_timeout * 60, type);                  pass = auth_getpass(prompt, def_passwd_timeout * 60, type);
                 if (pass == NULL) {                  if (pass == NULL) {
                    /* We got ^C instead of a password; abort quickly. */                    /* Error (or ^C) reading password, don't try again. */
                    if (errno == EINTR)                    getpass_error = 1;
                        gotintr = 1;#if (defined(__darwin__) || defined(__APPLE__)) && !defined(OPENPAM_VERSION)
#if defined(__darwin__) || defined(__APPLE__) 
                     pass = "";                      pass = "";
 #else  #else
                    goto err;                    goto done;
 #endif  #endif
                 }                  }
                 pr->resp = estrdup(pass);                  pr->resp = estrdup(pass);
                zero_bytes(pass, strlen(pass));                memset_s(pass, SUDO_CONV_REPL_MAX, 0, strlen(pass));
                 break;                  break;
             case PAM_TEXT_INFO:              case PAM_TEXT_INFO:
                 if (pm->msg)                  if (pm->msg)
Line 332  converse(int num_msg, PAM_CONST struct pam_message **m Line 374  converse(int num_msg, PAM_CONST struct pam_message **m
                 }                  }
                 break;                  break;
             default:              default:
                goto err;                ret = PAM_CONV_ERR;
                 goto done;
         }          }
     }      }
       ret = PAM_SUCCESS;
   
    return PAM_SUCCESS;done:
    if (ret != PAM_SUCCESS) {
err:        /* Zero and free allocated memory and return an error. */
    /* Zero and free allocated memory and return an error. */        for (pr = *response, n = num_msg; n--; pr++) {
    for (pr = *response, n = num_msg; n--; pr++) {            if (pr->resp != NULL) {
        if (pr->resp != NULL) {                memset_s(pr->resp, SUDO_CONV_REPL_MAX, 0, strlen(pr->resp));
            zero_bytes(pr->resp, strlen(pr->resp));                free(pr->resp);
            free(pr->resp);                pr->resp = NULL;
            pr->resp = NULL;            }
         }          }
           free(*response);
           *response = NULL;
     }      }
    zero_bytes(*response, num_msg * sizeof(struct pam_response));    debug_return_int(ret);
    free(*response); 
    *response = NULL; 
    return gotintr ? PAM_AUTH_ERR : PAM_CONV_ERR; 
 }  }

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>