aboutsummaryrefslogtreecommitdiff
path: root/modules/pam_unix/passverify.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_unix/passverify.c')
-rw-r--r--modules/pam_unix/passverify.c190
1 files changed, 105 insertions, 85 deletions
diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
index 81b10d88..e8d0b91d 100644
--- a/modules/pam_unix/passverify.c
+++ b/modules/pam_unix/passverify.c
@@ -5,6 +5,7 @@
#include <security/_pam_macros.h>
#include <security/pam_modules.h>
#include "support.h"
+#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
@@ -52,7 +53,7 @@
static void
strip_hpux_aging(char *hash)
{
- static const char valid[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ static const char *const valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789./";
if ((*hash != '$') && (strlen(hash) > 13)) {
@@ -75,9 +76,13 @@ PAMH_ARG_DECL(int verify_pwd_hash,
strip_hpux_aging(hash);
hash_len = strlen(hash);
- if (!hash_len) {
+
+ if (p && p[0] == '\0' && !nullok) {
+ /* The passed password is empty */
+ retval = PAM_AUTH_ERR;
+ } else if (!hash_len) {
/* the stored password is NULL */
- if (nullok) { /* this means we've succeeded */
+ if (p && p[0] == '\0' && nullok) { /* this means we've succeeded */
D(("user has empty password - access granted"));
retval = PAM_SUCCESS;
} else {
@@ -89,7 +94,7 @@ PAMH_ARG_DECL(int verify_pwd_hash,
} else {
if (pam_str_skip_prefix(hash, "$1$") != NULL) {
pp = Goodcrypt_md5(p, hash);
- if (pp && strcmp(pp, hash) != 0) {
+ if (pp && !pam_consttime_streq(pp, hash)) {
_pam_delete(pp);
pp = Brokencrypt_md5(p, hash);
}
@@ -143,9 +148,8 @@ PAMH_ARG_DECL(int verify_pwd_hash,
#endif
#ifdef HAVE_CRYPT_R
struct crypt_data *cdata;
- cdata = malloc(sizeof(*cdata));
+ cdata = calloc(1, sizeof(*cdata));
if (cdata != NULL) {
- cdata->initialized = 0;
pp = x_strdup(crypt_r(p, hash, cdata));
pam_overwrite_object(cdata);
free(cdata);
@@ -157,9 +161,9 @@ PAMH_ARG_DECL(int verify_pwd_hash,
p = NULL; /* no longer needed here */
/* the moment of truth -- do we agree with the password? */
- D(("comparing state of pp[%s] and hash[%s]", pp, hash));
+ D(("comparing state of pp[%s] and hash[%s]", pp ? pp : "(null)", hash));
- if (pp && strcmp(pp, hash) == 0) {
+ if (pp && pam_consttime_streq(pp, hash)) {
retval = PAM_SUCCESS;
} else {
retval = PAM_AUTH_ERR;
@@ -237,20 +241,21 @@ PAMH_ARG_DECL(int get_account_info,
return PAM_UNIX_RUN_HELPER;
#endif
} else if (is_pwd_shadowed(*pwd)) {
+#ifdef HELPER_COMPILE
/*
- * ...and shadow password file entry for this user,
+ * shadow password file entry for this user,
* if shadowing is enabled
*/
- *spwdent = pam_modutil_getspnam(pamh, name);
- if (*spwdent == NULL) {
-#ifndef HELPER_COMPILE
- /* still a chance the user can authenticate */
- return PAM_UNIX_RUN_HELPER;
-#endif
- return PAM_AUTHINFO_UNAVAIL;
- }
- if ((*spwdent)->sp_pwdp == NULL)
+ *spwdent = getspnam(name);
+ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
return PAM_AUTHINFO_UNAVAIL;
+#else
+ /*
+ * The helper has to be invoked to deal with
+ * the shadow password file entry.
+ */
+ return PAM_UNIX_RUN_HELPER;
+#endif
}
} else {
return PAM_USER_UNKNOWN;
@@ -279,14 +284,29 @@ PAMH_ARG_DECL(int get_pwd_hash,
return PAM_SUCCESS;
}
+/*
+ * invariant: 0 <= num1
+ * invariant: 0 <= num2
+ */
+static int
+subtract(long num1, long num2)
+{
+ long value = num1 - num2;
+ if (value < INT_MIN)
+ return INT_MIN;
+ if (value > INT_MAX)
+ return INT_MAX;
+ return (int)value;
+}
+
PAMH_ARG_DECL(int check_shadow_expiry,
struct spwd *spent, int *daysleft)
{
- long int curdays;
+ long int curdays, passed;
*daysleft = -1;
curdays = (long int)(time(NULL) / (60 * 60 * 24));
- D(("today is %d, last change %d", curdays, spent->sp_lstchg));
- if ((curdays >= spent->sp_expire) && (spent->sp_expire != -1)) {
+ D(("today is %ld, last change %ld", curdays, spent->sp_lstchg));
+ if (spent->sp_expire >= 0 && curdays >= spent->sp_expire) {
D(("account expired"));
return PAM_ACCT_EXPIRED;
}
@@ -295,31 +315,41 @@ PAMH_ARG_DECL(int check_shadow_expiry,
*daysleft = 0;
return PAM_NEW_AUTHTOK_REQD;
}
+ if (spent->sp_lstchg < 0) {
+ D(("password aging disabled"));
+ return PAM_SUCCESS;
+ }
if (curdays < spent->sp_lstchg) {
pam_syslog(pamh, LOG_DEBUG,
"account %s has password changed in future",
spent->sp_namp);
return PAM_SUCCESS;
}
- if ((curdays - spent->sp_lstchg > spent->sp_max)
- && (curdays - spent->sp_lstchg > spent->sp_inact)
- && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
- && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
- *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
- D(("authtok expired"));
- return PAM_AUTHTOK_EXPIRED;
- }
- if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
- D(("need a new password 2"));
- return PAM_NEW_AUTHTOK_REQD;
- }
- if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
- && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
- *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
- D(("warn before expiry"));
+ passed = curdays - spent->sp_lstchg;
+ if (spent->sp_max >= 0) {
+ if (spent->sp_inact >= 0) {
+ long inact = spent->sp_max < LONG_MAX - spent->sp_inact ?
+ spent->sp_max + spent->sp_inact : LONG_MAX;
+ if (passed >= inact) {
+ *daysleft = subtract(inact, passed);
+ D(("authtok expired"));
+ return PAM_AUTHTOK_EXPIRED;
+ }
+ }
+ if (passed >= spent->sp_max) {
+ D(("need a new password 2"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+ if (spent->sp_warn > 0) {
+ long warn = spent->sp_warn > spent->sp_max ? -1 :
+ spent->sp_max - spent->sp_warn;
+ if (passed >= warn) {
+ *daysleft = subtract(spent->sp_max, passed);
+ D(("warn before expiry"));
+ }
+ }
}
- if ((curdays - spent->sp_lstchg < spent->sp_min)
- && (spent->sp_min != -1)) {
+ if (spent->sp_min > 0 && passed < spent->sp_min) {
/*
* The last password change was too recent. This error will be ignored
* if no password change is attempted.
@@ -334,7 +364,7 @@ PAMH_ARG_DECL(int check_shadow_expiry,
#define PW_TMPFILE "/etc/npasswd"
#define SH_TMPFILE "/etc/nshadow"
-#define OPW_TMPFILE SCONFIGDIR "/nopasswd"
+#define OPW_TMPFILE SCONFIG_DIR "/nopasswd"
/*
* i64c - convert an integer to a radix 64 character
@@ -372,7 +402,7 @@ crypt_make_salt(char *where, int length)
int fd;
int rv;
- if ((rv = fd = open(PAM_PATH_RANDOMDEV, O_RDONLY)) != -1) {
+ if ((rv = fd = open(PAM_PATH_RANDOMDEV, O_RDONLY | O_CLOEXEC)) != -1) {
while ((rv = read(fd, where, length)) != length && errno == EINTR);
close (fd);
}
@@ -476,9 +506,8 @@ PAMH_ARG_DECL(char * create_password_hash,
#endif /* CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY */
#ifdef HAVE_CRYPT_R
sp = NULL;
- cdata = malloc(sizeof(*cdata));
+ cdata = calloc(1, sizeof(*cdata));
if (cdata != NULL) {
- cdata->initialized = 0;
sp = crypt_r(password, salt, cdata);
}
#else
@@ -529,7 +558,7 @@ unix_selinux_confined(void)
}
/* let's try opening shadow read only */
- if ((fd=open("/etc/shadow", O_RDONLY)) != -1) {
+ if ((fd=open("/etc/shadow", O_RDONLY | O_CLOEXEC)) != -1) {
close(fd);
confined = 0;
return confined;
@@ -624,16 +653,16 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
int howmany)
#endif
{
- static char buf[16384];
- static char nbuf[16384];
+ char *buf = NULL;
char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
int npas;
FILE *pwfile, *opwfile;
int err = 0;
- int oldmask;
+ mode_t oldmask;
int found = 0;
struct passwd *pwd = NULL;
struct stat st;
+ size_t bufsize = 0;
size_t len = strlen(forwho);
#ifdef WITH_SELINUX
char *prev_context_raw = NULL;
@@ -667,14 +696,14 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
freecon(passwd_context_raw);
}
#endif
- pwfile = fopen(OPW_TMPFILE, "w");
+ pwfile = fopen(OPW_TMPFILE, "we");
umask(oldmask);
if (pwfile == NULL) {
err = 1;
goto done;
}
- opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ opwfile = fopen(OLD_PASSWORDS_FILE, "re");
if (opwfile == NULL) {
fclose(pwfile);
err = 1;
@@ -701,9 +730,10 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
goto done;
}
- while (fgets(buf, 16380, opwfile)) {
- if (!strncmp(buf, forwho, len) && strchr(":,\n", buf[len]) != NULL) {
- char *sptr = NULL;
+ for (; getline(&buf, &bufsize, opwfile) != -1; pam_overwrite_n(buf, bufsize)) {
+ if (!strncmp(buf, forwho, len) && strchr(":\n", buf[len]) != NULL) {
+ char *ep, *sptr = NULL;
+ long value;
found = 1;
if (howmany == 0)
continue;
@@ -723,8 +753,12 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
found = 0;
continue;
}
- s_pas = strtok_r(NULL, ":", &sptr);
- npas = strtol(s_npas, NULL, 10) + 1;
+ s_pas = strtok_r(NULL, "", &sptr);
+ value = strtol(s_npas, &ep, 10);
+ if (value < 0 || value >= INT_MAX || s_npas == ep || *ep != '\0')
+ npas = 0;
+ else
+ npas = (int)value + 1;
while (npas > howmany && s_pas != NULL) {
s_pas = strpbrk(s_pas, ",");
if (s_pas != NULL)
@@ -733,21 +767,21 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
}
pass = crypt_md5_wrapper(oldpass);
if (s_pas == NULL)
- snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
- s_luser, s_uid, npas, pass);
+ err = fprintf(pwfile, "%s:%s:%d:%s\n",
+ s_luser, s_uid, npas, pass) < 0;
else
- snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
- s_luser, s_uid, npas, s_pas, pass);
+ err = fprintf(pwfile, "%s:%s:%d:%s,%s\n",
+ s_luser, s_uid, npas, s_pas, pass) < 0;
_pam_delete(pass);
- if (fputs(nbuf, pwfile) < 0) {
- err = 1;
+ if (err)
break;
- }
} else if (fputs(buf, pwfile) < 0) {
err = 1;
break;
}
}
+ pam_overwrite_n(buf, bufsize);
+ free(buf);
fclose(opwfile);
if (!found) {
@@ -756,12 +790,9 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
err = 1;
} else {
pass = crypt_md5_wrapper(oldpass);
- snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
- forwho, (unsigned long)pwd->pw_uid, pass);
+ err = fprintf(pwfile, "%s:%lu:1:%s\n",
+ forwho, (unsigned long)pwd->pw_uid, pass) < 0;
_pam_delete(pass);
- if (fputs(nbuf, pwfile) < 0) {
- err = 1;
- }
}
}
@@ -805,7 +836,7 @@ PAMH_ARG_DECL(int unix_update_passwd,
struct stat st;
FILE *pwfile, *opwfile;
int err = 1;
- int oldmask;
+ mode_t oldmask;
#ifdef WITH_SELINUX
char *prev_context_raw = NULL;
#endif
@@ -829,14 +860,14 @@ PAMH_ARG_DECL(int unix_update_passwd,
freecon(passwd_context_raw);
}
#endif
- pwfile = fopen(PW_TMPFILE, "w");
+ pwfile = fopen(PW_TMPFILE, "we");
umask(oldmask);
if (pwfile == NULL) {
err = 1;
goto done;
}
- opwfile = fopen("/etc/passwd", "r");
+ opwfile = fopen("/etc/passwd", "re");
if (opwfile == NULL) {
fclose(pwfile);
err = 1;
@@ -928,7 +959,7 @@ PAMH_ARG_DECL(int unix_update_shadow,
struct stat st;
FILE *pwfile, *opwfile;
int err = 0;
- int oldmask;
+ mode_t oldmask;
int wroteentry = 0;
#ifdef WITH_SELINUX
char *prev_context_raw = NULL;
@@ -954,14 +985,14 @@ PAMH_ARG_DECL(int unix_update_shadow,
freecon(shadow_context_raw);
}
#endif
- pwfile = fopen(SH_TMPFILE, "w");
+ pwfile = fopen(SH_TMPFILE, "we");
umask(oldmask);
if (pwfile == NULL) {
err = 1;
goto done;
}
- opwfile = fopen("/etc/shadow", "r");
+ opwfile = fopen("/etc/shadow", "re");
if (opwfile == NULL) {
fclose(pwfile);
err = 1;
@@ -1082,12 +1113,6 @@ helper_verify_password(const char *name, const char *p, int nullok)
if (pwd == NULL || hash == NULL) {
helper_log_err(LOG_NOTICE, "check pass; user unknown");
retval = PAM_USER_UNKNOWN;
- } else if (p[0] == '\0' && nullok) {
- if (hash[0] == '\0') {
- retval = PAM_SUCCESS;
- } else {
- retval = PAM_AUTH_ERR;
- }
} else {
retval = verify_pwd_hash(p, hash, nullok);
}
@@ -1103,7 +1128,6 @@ helper_verify_password(const char *name, const char *p, int nullok)
}
void
-PAM_FORMAT((printf, 2, 3))
helper_log_err(int err, const char *format, ...)
{
va_list args;
@@ -1128,7 +1152,7 @@ su_sighandler(int sig)
}
#endif
if (sig > 0) {
- _exit(sig);
+ _exit(128 + (sig & 127));
}
}
@@ -1161,16 +1185,12 @@ char *
getuidname(uid_t uid)
{
struct passwd *pw;
- static char username[256];
pw = getpwuid(uid);
if (pw == NULL)
return NULL;
- strncpy(username, pw->pw_name, sizeof(username));
- username[sizeof(username) - 1] = '\0';
-
- return username;
+ return pw->pw_name;
}
#endif