]> granicus.if.org Git - linux-pam/blobdiff - modules/pam_unix/pam_unix_passwd.c
Relevant BUGIDs: Debian bugs #95220, #175900
[linux-pam] / modules / pam_unix / pam_unix_passwd.c
index 7212952e5126f7c99495f27e815560a41dd7a954..c8ee54924fbbaa8aa278a9297eaf7240ed9bfcf8 100644 (file)
@@ -78,16 +78,13 @@ static security_context_t prev_context=NULL;
 #define PAM_SM_PASSWORD
 
 #include <security/pam_modules.h>
-
-#ifndef LINUX_PAM
-#include <security/pam_appl.h>
-#endif                         /* LINUX_PAM */
-
+#include <security/pam_ext.h>
 #include <security/pam_modutil.h>
 
 #include "yppasswd.h"
 #include "md5.h"
 #include "support.h"
+#include "bigcrypt.h"
 
 #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
 extern int getrpcport(const char *host, unsigned long prognum,
@@ -103,8 +100,6 @@ extern int getrpcport(const char *host, unsigned long prognum,
 # include "./lckpwdf.-c"
 #endif
 
-extern char *bigcrypt(const char *key, const char *salt);
-
 /*
    How it works:
    Gets in username (has to be done) from the calling program
@@ -197,24 +192,24 @@ static char *getNISserver(pam_handle_t *pamh)
        int port, err;
 
        if ((err = yp_get_default_domain(&domainname)) != 0) {
-               _log_err(LOG_WARNING, pamh, "can't get local yp domain: %s\n",
+               pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
                         yperr_string(err));
                return NULL;
        }
        if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
-               _log_err(LOG_WARNING, pamh, "can't find the master ypserver: %s\n",
+               pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
                         yperr_string(err));
                return NULL;
        }
        port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
        if (port == 0) {
-               _log_err(LOG_WARNING, pamh,
-                        "yppasswdd not running on NIS master host\n");
+               pam_syslog(pamh, LOG_WARNING,
+                        "yppasswdd not running on NIS master host");
                return NULL;
        }
        if (port >= IPPORT_RESERVED) {
-               _log_err(LOG_WARNING, pamh,
-                        "yppasswd daemon running on illegal port.\n");
+               pam_syslog(pamh, LOG_WARNING,
+                        "yppasswd daemon running on illegal port");
                return NULL;
        }
        return master;
@@ -267,6 +262,13 @@ static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const
                   close(i);
          }
        }
+
+        if (SELINUX_ENABLED && geteuid() == 0) {
+          /* must set the real uid to 0 so the helper will not error
+             out if pam is called from setuid binary (su, sudo...) */
+          setuid(0);
+        }
+
        /* exec binary helper */
        args[0] = x_strdup(CHKPWD_HELPER);
        args[1] = x_strdup(user);
@@ -295,7 +297,7 @@ static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const
        close(fds[1]);
        rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
        if (rc<0) {
-         _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
+         pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
          retval = PAM_AUTH_ERR;
        } else {
          retval = WEXITSTATUS(retval);
@@ -307,7 +309,7 @@ static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const
        retval = PAM_AUTH_ERR;
     }
 
-    if (sighandler != NULL) {
+    if (sighandler != SIG_ERR) {
         (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
     }
 
@@ -328,11 +330,12 @@ static int check_old_password(const char *forwho, const char *newpass)
 
        while (fgets(buf, 16380, opwfile)) {
                if (!strncmp(buf, forwho, strlen(forwho))) {
+                       char *sptr;
                        buf[strlen(buf) - 1] = '\0';
-                       s_luser = strtok(buf, ":,");
-                       s_uid = strtok(NULL, ":,");
-                       s_npas = strtok(NULL, ":,");
-                       s_pas = strtok(NULL, ":,");
+                       s_luser = strtok_r(buf, ":,", &sptr);
+                       s_uid = strtok_r(NULL, ":,", &sptr);
+                       s_npas = strtok_r(NULL, ":,", &sptr);
+                       s_pas = strtok_r(NULL, ":,", &sptr);
                        while (s_pas != NULL) {
                                char *md5pass = Goodcrypt_md5(newpass, s_pas);
                                if (!strcmp(md5pass, s_pas)) {
@@ -340,7 +343,7 @@ static int check_old_password(const char *forwho, const char *newpass)
                                        retval = PAM_AUTHTOK_ERR;
                                        break;
                                }
-                               s_pas = strtok(NULL, ":,");
+                               s_pas = strtok_r(NULL, ":,", &sptr);
                                _pam_delete(md5pass);
                        }
                        break;
@@ -430,11 +433,12 @@ static int save_old_password(pam_handle_t *pamh,
 
     while (fgets(buf, 16380, opwfile)) {
        if (!strncmp(buf, forwho, strlen(forwho))) {
+           char *sptr;
            buf[strlen(buf) - 1] = '\0';
-           s_luser = strtok(buf, ":");
-           s_uid = strtok(NULL, ":");
-           s_npas = strtok(NULL, ":");
-           s_pas = strtok(NULL, ":");
+           s_luser = strtok_r(buf, ":", &sptr);
+           s_uid = strtok_r(NULL, ":", &sptr);
+           s_npas = strtok_r(NULL, ":", &sptr);
+           s_pas = strtok_r(NULL, ":", &sptr);
            npas = strtol(s_npas, NULL, 10) + 1;
            while (npas > howmany) {
                s_pas = strpbrk(s_pas, ",");
@@ -468,8 +472,8 @@ static int save_old_password(pam_handle_t *pamh,
            err = 1;
        } else {
            pass = crypt_md5_wrapper(oldpass);
-           snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n",
-                    forwho, pwd->pw_uid, pass);
+           snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
+                    forwho, (unsigned long)pwd->pw_uid, pass);
            _pam_delete(pass);
            if (fputs(nbuf, pwfile) < 0) {
                err = 1;
@@ -478,8 +482,7 @@ static int save_old_password(pam_handle_t *pamh,
     }
 
     if (fclose(pwfile)) {
-       D(("error writing entries to old passwords file: %s\n",
-          strerror(errno)));
+       D(("error writing entries to old passwords file: %m"));
        err = 1;
     }
 
@@ -582,7 +585,7 @@ static int _update_passwd(pam_handle_t *pamh,
            err = 0;
        }
        if (putpwent(tmpent, pwfile)) {
-           D(("error writing entry to password file: %s\n", strerror(errno)));
+           D(("error writing entry to password file: %m"));
            err = 1;
            break;
        }
@@ -591,14 +594,14 @@ static int _update_passwd(pam_handle_t *pamh,
     fclose(opwfile);
 
     if (fclose(pwfile)) {
-       D(("error writing entries to password file: %s\n", strerror(errno)));
+       D(("error writing entries to password file: %m"));
        err = 1;
     }
 
 done:
     if (!err) {
        if (!rename(PW_TMPFILE, "/etc/passwd"))
-           _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
+           pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
        else
            err = 1;
     }
@@ -697,7 +700,7 @@ static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
        }
 
        if (putspent(stmpent, pwfile)) {
-           D(("error writing entry to shadow file: %s\n", strerror(errno)));
+           D(("error writing entry to shadow file: %m"));
            err = 1;
            break;
        }
@@ -707,14 +710,14 @@ static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
     fclose(opwfile);
 
     if (fclose(pwfile)) {
-       D(("error writing entries to shadow file: %s\n", strerror(errno)));
+       D(("error writing entries to shadow file: %m"));
        err = 1;
     }
 
  done:
     if (!err) {
        if (!rename(SH_TMPFILE, "/etc/shadow"))
-           _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
+           pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
        else
            err = 1;
     }
@@ -762,7 +765,7 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
                struct yppasswd yppwd;
                CLIENT *clnt;
                int status;
-               int err = 0;
+               enum clnt_stat err;
 
                /* Unlock passwd file to avoid deadlock */
 #ifdef USE_LCKPWDF
@@ -808,7 +811,7 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
                }
                D(("The password has%s been changed on %s.",
                   (err || status) ? " not" : "", master));
-               _log_err(LOG_NOTICE, pamh, "password%s changed for %s on %s",
+               pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
                         (err || status) ? " not" : "", pwd->pw_name, master);
 
                auth_destroy(clnt->cl_auth);
@@ -920,10 +923,22 @@ static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned in
                if (off(UNIX__IAMROOT, ctrl)) {
                        /* Get the current number of days since 1970 */
                        curdays = time(NULL) / (60 * 60 * 24);
-                       if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
-                           && (spwdent->sp_min != -1))
+                       if (curdays < spwdent->sp_lstchg) {
+                               pam_syslog(pamh, LOG_DEBUG,
+                                       "account %s has password changed in future",
+                                       user);
+                               curdays = spwdent->sp_lstchg;
+                       }
+                       if ((curdays - spwdent->sp_lstchg < spwdent->sp_min)
+                                && (spwdent->sp_min != -1))
+                               /*
+                                * The last password change was too recent.
+                                */
                                retval = PAM_AUTHTOK_ERR;
-                       else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
+                       else if ((curdays - spwdent->sp_lstchg > spwdent->sp_max)
+                                && (curdays - spwdent->sp_lstchg > spwdent->sp_inact)
+                                && (curdays - spwdent->sp_lstchg >
+                                    spwdent->sp_max + spwdent->sp_inact)
                                 && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
                                 && (spwdent->sp_lstchg != 0))
                                /*
@@ -956,7 +971,7 @@ static int _pam_unix_approve_pass(pam_handle_t * pamh
 
        if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
                if (on(UNIX_DEBUG, ctrl)) {
-                       _log_err(LOG_DEBUG, pamh, "bad authentication token");
+                       pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
                }
                _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
                        _("No password supplied") : _("Password unchanged"));
@@ -970,7 +985,7 @@ static int _pam_unix_approve_pass(pam_handle_t * pamh
        retval = pam_get_item(pamh, PAM_USER, &user);
        if (retval != PAM_SUCCESS) {
                if (on(UNIX_DEBUG, ctrl)) {
-                       _log_err(LOG_ERR, pamh, "Can not get username");
+                       pam_syslog(pamh, LOG_ERR, "Can not get username");
                        return PAM_AUTHTOK_ERR;
                }
        }
@@ -987,7 +1002,7 @@ static int _pam_unix_approve_pass(pam_handle_t * pamh
                        if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
                          remark = _("Password has been already used. Choose another.");
                        if (retval == PAM_ABORT) {
-                               _log_err(LOG_ERR, pamh, "can't open %s file to check old passwords",
+                               pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
                                        OLD_PASSWORDS_FILE);
                                return retval;
                        }
@@ -1024,20 +1039,19 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
        if (retval == PAM_SUCCESS) {
                /*
                 * Various libraries at various times have had bugs related to
-                * '+' or '-' as the first character of a user name. Don't take
-                * any chances here. Require that the username starts with an
-                * alphanumeric character.
+                * '+' or '-' as the first character of a user name. Don't
+                * allow them.
                 */
-               if (user == NULL || !isalnum(*user)) {
-                       _log_err(LOG_ERR, pamh, "bad username [%s]", user);
+               if (user == NULL || user[0] == '-' || user[0] == '+') {
+                       pam_syslog(pamh, LOG_ERR, "bad username [%s]", user);
                        return PAM_USER_UNKNOWN;
                }
                if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
-                       _log_err(LOG_DEBUG, pamh, "username [%s] obtained",
+                       pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
                                 user);
        } else {
                if (on(UNIX_DEBUG, ctrl))
-                       _log_err(LOG_DEBUG, pamh,
+                       pam_syslog(pamh, LOG_DEBUG,
                                 "password - could not identify user");
                return retval;
        }
@@ -1052,7 +1066,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
         * came from, nor should it.  That's our job.
         */
        if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
-               _log_err(LOG_DEBUG, pamh,
+               pam_syslog(pamh, LOG_DEBUG,
                         "user \"%s\" does not exist in /etc/passwd%s",
                         user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
                return PAM_USER_UNKNOWN;
@@ -1060,18 +1074,11 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                struct passwd *pwd;
                _unix_getpwnam(pamh, user, 1, 1, &pwd);
                if (pwd == NULL) {
-                       _log_err(LOG_DEBUG, pamh,
+                       pam_syslog(pamh, LOG_DEBUG,
                                "user \"%s\" has corrupted passwd entry",
                                user);
                        return PAM_USER_UNKNOWN;
                }
-               if (!_unix_shadowed(pwd) &&
-                   (strchr(pwd->pw_passwd, '*') != NULL)) {
-                       _log_err(LOG_DEBUG, pamh,
-                               "user \"%s\" does not have modifiable password",
-                               user);
-                       return PAM_USER_UNKNOWN;
-               }
        }
 
        /*
@@ -1092,18 +1099,13 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                if (_unix_blankpasswd(pamh, ctrl, user)) {
                        return PAM_SUCCESS;
                } else if (off(UNIX__IAMROOT, ctrl)) {
-
                        /* instruct user what is happening */
-#define greeting "Changing password for "
-                       Announce = (char *) malloc(sizeof(greeting) + strlen(user));
-                       if (Announce == NULL) {
-                               _log_err(LOG_CRIT, pamh,
+                       if (asprintf(&Announce, _("Changing password for %s."),
+                               user) < 0) {
+                               pam_syslog(pamh, LOG_CRIT,
                                         "password - out of memory");
                                return PAM_BUF_ERR;
                        }
-                       (void) strcpy(Announce, greeting);
-                       (void) strcpy(Announce + sizeof(greeting) - 1, user);
-#undef greeting
 
                        lctrl = ctrl;
                        set(UNIX__OLD_PASSWD, lctrl);
@@ -1116,8 +1118,8 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                        free(Announce);
 
                        if (retval != PAM_SUCCESS) {
-                               _log_err(LOG_NOTICE, pamh
-                                ,"password - (old) token not obtained");
+                               pam_syslog(pamh, LOG_NOTICE,
+                                   "password - (old) token not obtained");
                                return retval;
                        }
                        /* verify that this is the password for this user */
@@ -1137,7 +1139,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
                pass_old = NULL;
                if (retval != PAM_SUCCESS) {
-                       _log_err(LOG_CRIT, pamh,
+                       pam_syslog(pamh, LOG_CRIT,
                                 "failed to set PAM_OLDAUTHTOK");
                }
                retval = _unix_verify_shadow(pamh,user, ctrl);
@@ -1183,7 +1185,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                D(("pass_old [%s]", pass_old));
 
                if (retval != PAM_SUCCESS) {
-                       _log_err(LOG_NOTICE, pamh, "user not authenticated");
+                       pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
                        return retval;
                }
 
@@ -1211,8 +1213,8 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 
                        if (retval != PAM_SUCCESS) {
                                if (on(UNIX_DEBUG, ctrl)) {
-                                       _log_err(LOG_ALERT, pamh
-                                                ,"password - new password not obtained");
+                                       pam_syslog(pamh, LOG_ALERT,
+                                                "password - new password not obtained");
                                }
                                pass_old = NULL;        /* tidy up */
                                return retval;
@@ -1232,7 +1234,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                }
 
                if (retval != PAM_SUCCESS) {
-                       _log_err(LOG_NOTICE, pamh,
+                       pam_syslog(pamh, LOG_NOTICE,
                                 "new password not acceptable");
                        pass_new = pass_old = NULL;     /* tidy up */
                        return retval;
@@ -1257,7 +1259,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                if (pass_old) {
                        retval = _unix_verify_password(pamh, user, pass_old, ctrl);
                        if (retval != PAM_SUCCESS) {
-                               _log_err(LOG_NOTICE, pamh, "user password changed by another process");
+                               pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
 #ifdef USE_LCKPWDF
                                ulckpwdf();
 #endif
@@ -1267,7 +1269,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 
                retval = _unix_verify_shadow(pamh, user, ctrl);
                if (retval != PAM_SUCCESS) {
-                       _log_err(LOG_NOTICE, pamh, "user not authenticated 2");
+                       pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
 #ifdef USE_LCKPWDF
                        ulckpwdf();
 #endif
@@ -1276,7 +1278,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 
                retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
                if (retval != PAM_SUCCESS) {
-                       _log_err(LOG_NOTICE, pamh,
+                       pam_syslog(pamh, LOG_NOTICE,
                                 "new password not acceptable 2");
                        pass_new = pass_old = NULL;     /* tidy up */
 #ifdef USE_LCKPWDF
@@ -1320,7 +1322,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                                char *temp = malloc(9);
 
                                if (temp == NULL) {
-                                       _log_err(LOG_CRIT, pamh,
+                                       pam_syslog(pamh, LOG_CRIT,
                                                 "out of memory for password");
                                        pass_new = pass_old = NULL;     /* tidy up */
 #ifdef USE_LCKPWDF
@@ -1352,7 +1354,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                _pam_delete(tpass);
                pass_old = pass_new = NULL;
        } else {                /* something has broken with the module */
-               _log_err(LOG_ALERT, pamh,
+               pam_syslog(pamh, LOG_ALERT,
                         "password received unknown request");
                retval = PAM_ABORT;
        }