aboutsummaryrefslogtreecommitdiff
path: root/modules/pam_unix/support.c
diff options
context:
space:
mode:
authorSam Hartman <hartmans@debian.org>2024-12-02 09:55:34 -0700
committerSam Hartman <hartmans@debian.org>2024-12-02 09:55:34 -0700
commit4f3cfaf827bfa42a239c255092a128a3a02198bf (patch)
treeeac7f023f043739b79b2a51bd68c3006acb12964 /modules/pam_unix/support.c
parent6408d4b1baff9a7e58fd66e1d1c0871be0823777 (diff)
parent7c9fb6472dcfae34ddbf4fbc9ecfafae2cf173c3 (diff)
downloadpam-4f3cfaf827bfa42a239c255092a128a3a02198bf.tar.gz
pam-4f3cfaf827bfa42a239c255092a128a3a02198bf.tar.bz2
pam-4f3cfaf827bfa42a239c255092a128a3a02198bf.zip
Update upstream source from tag 'upstream/1.7.0'
Update to upstream version '1.7.0' with Debian dir 0b3cd490884352e14273caeca2f05c6a525499fa
Diffstat (limited to 'modules/pam_unix/support.c')
-rw-r--r--modules/pam_unix/support.c189
1 files changed, 117 insertions, 72 deletions
diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c
index 043273d2..dfdc9fc1 100644
--- a/modules/pam_unix/support.c
+++ b/modules/pam_unix/support.c
@@ -46,6 +46,18 @@ int _make_remark(pam_handle_t * pamh, unsigned long long ctrl,
return retval;
}
+static int _unix_strtoi(const char *str, int minval, int *result)
+{
+ char *ep;
+ long value = strtol(str, &ep, 10);
+ if (value < minval || value > INT_MAX || str == ep || *ep != '\0') {
+ *result = minval;
+ return -1;
+ }
+ *result = (int)value;
+ return 0;
+}
+
/*
* set the control flags for the UNIX module.
*/
@@ -97,17 +109,6 @@ unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember,
ctrl |= unix_args[j].flag; /* for turning things on */
}
free (val);
-
- /* read number of rounds for crypt algo */
- if (rounds && (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl))) {
- val = pam_modutil_search_key(pamh, LOGIN_DEFS, "SHA_CRYPT_MAX_ROUNDS");
-
- if (val) {
- *rounds = strtol(val, NULL, 10);
- set(UNIX_ALGO_ROUNDS, ctrl);
- free (val);
- }
- }
}
/* now parse the arguments to this module */
@@ -137,9 +138,11 @@ unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember,
"option remember not allowed for this module type");
continue;
}
- *remember = strtol(str, NULL, 10);
- if ((*remember == INT_MIN) || (*remember == INT_MAX))
- *remember = -1;
+ if (_unix_strtoi(str, -1, remember)) {
+ pam_syslog(pamh, LOG_ERR,
+ "option remember invalid [%s]", str);
+ continue;
+ }
if (*remember > 400)
*remember = 400;
} else if (j == UNIX_MIN_PASS_LEN) {
@@ -148,14 +151,22 @@ unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember,
"option minlen not allowed for this module type");
continue;
}
- *pass_min_len = atoi(str);
+ if (_unix_strtoi(str, 0, pass_min_len)) {
+ pam_syslog(pamh, LOG_ERR,
+ "option minlen invalid [%s]", str);
+ continue;
+ }
} else if (j == UNIX_ALGO_ROUNDS) {
if (rounds == NULL) {
pam_syslog(pamh, LOG_ERR,
"option rounds not allowed for this module type");
continue;
}
- *rounds = strtol(str, NULL, 10);
+ if (_unix_strtoi(str, 0, rounds)) {
+ pam_syslog(pamh, LOG_ERR,
+ "option rounds invalid [%s]", str);
+ continue;
+ }
}
ctrl &= unix_args[j].mask; /* for turning things off */
@@ -175,6 +186,29 @@ unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember,
set(UNIX__NONULL, ctrl);
}
+ /* Read number of rounds for sha256, sha512 and yescrypt */
+ if (off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) {
+ const char *key = NULL;
+ if (on(UNIX_YESCRYPT_PASS, ctrl))
+ key = "YESCRYPT_COST_FACTOR";
+ else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl))
+ key = "SHA_CRYPT_MAX_ROUNDS";
+ else
+ key = NULL;
+
+ if (key != NULL) {
+ val = pam_modutil_search_key(pamh, LOGIN_DEFS, key);
+ if (val) {
+ if (_unix_strtoi(val, 0, rounds))
+ pam_syslog(pamh, LOG_ERR,
+ "option %s invalid [%s]", key, val);
+ else
+ set(UNIX_ALGO_ROUNDS, ctrl);
+ free (val);
+ }
+ }
+ }
+
/* Set default rounds for blowfish, gost-yescrypt and yescrypt */
if (off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) {
if (on(UNIX_BLOWFISH_PASS, ctrl) ||
@@ -189,11 +223,15 @@ unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember,
if (on(UNIX_ALGO_ROUNDS, ctrl)) {
if (on(UNIX_GOST_YESCRYPT_PASS, ctrl) ||
on(UNIX_YESCRYPT_PASS, ctrl)) {
- if (*rounds < 3 || *rounds > 11)
- *rounds = 5;
+ if (*rounds < 3)
+ *rounds = 3;
+ else if (*rounds > 11)
+ *rounds = 11;
} else if (on(UNIX_BLOWFISH_PASS, ctrl)) {
- if (*rounds < 4 || *rounds > 31)
- *rounds = 5;
+ if (*rounds < 4)
+ *rounds = 4;
+ else if (*rounds > 31)
+ *rounds = 31;
} else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) {
if ((*rounds < 1000) || (*rounds == INT_MAX)) {
/* don't care about bogus values */
@@ -279,7 +317,8 @@ static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
tty ? (const char *)tty : "", ruser ? (const char *)ruser : "",
rhost ? (const char *)rhost : "",
(failure->user && failure->user[0] != '\0')
- ? " user=" : "", failure->user
+ ? " user=" : "",
+ failure->user ? failure->user : ""
);
if (failure->count > UNIX_MAX_RETRIES) {
@@ -308,33 +347,40 @@ static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_statu
int _unix_getpwnam(pam_handle_t *pamh, const char *name,
int files, int nis, struct passwd **ret)
{
- FILE *passwd;
- char buf[16384];
- int matched = 0, buflen;
- char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
+ char *buf = NULL;
+ int matched = 0;
- memset(buf, 0, sizeof(buf));
+ if (!matched && files && strchr(name, ':') == NULL) {
+ FILE *passwd;
- if (!matched && files) {
- int userlen = strlen(name);
- passwd = fopen("/etc/passwd", "r");
+ passwd = fopen("/etc/passwd", "re");
if (passwd != NULL) {
- while (fgets(buf, sizeof(buf), passwd) != NULL) {
- if ((buf[userlen] == ':') &&
+ size_t n = 0, userlen;
+ ssize_t r;
+
+ userlen = strlen(name);
+
+ while ((r = getline(&buf, &n, passwd)) != -1) {
+ if ((size_t)r > userlen && (buf[userlen] == ':') &&
(strncmp(name, buf, userlen) == 0)) {
+ char *p;
+
p = buf + strlen(buf) - 1;
- while (isspace(*p) && (p >= buf)) {
+ while (isspace((unsigned char)*p) && (p >= buf)) {
*p-- = '\0';
}
matched = 1;
break;
}
}
+ if (!matched) {
+ _pam_drop(buf);
+ }
fclose(passwd);
}
}
-#if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined (HAVE_YP_BIND) && defined (HAVE_YP_MATCH) && defined (HAVE_YP_UNBIND)
+#if defined(HAVE_NIS) && defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined (HAVE_YP_BIND) && defined (HAVE_YP_MATCH) && defined (HAVE_YP_UNBIND)
if (!matched && nis) {
char *userinfo = NULL, *domain = NULL;
int len = 0, i;
@@ -346,9 +392,7 @@ int _unix_getpwnam(pam_handle_t *pamh, const char *name,
i = yp_match(domain, "passwd.byname", name,
strlen(name), &userinfo, &len);
yp_unbind(domain);
- if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) {
- strncpy(buf, userinfo, sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = '\0';
+ if (i == YPERR_SUCCESS && (buf = strdup(userinfo)) != NULL) {
matched = 1;
}
}
@@ -359,70 +403,68 @@ int _unix_getpwnam(pam_handle_t *pamh, const char *name,
#endif
if (matched && (ret != NULL)) {
+ char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
+ size_t retlen;
+
*ret = NULL;
slogin = buf;
spasswd = strchr(slogin, ':');
if (spasswd == NULL) {
- return matched;
+ goto fail;
}
*spasswd++ = '\0';
suid = strchr(spasswd, ':');
if (suid == NULL) {
- return matched;
+ goto fail;
}
*suid++ = '\0';
sgid = strchr(suid, ':');
if (sgid == NULL) {
- return matched;
+ goto fail;
}
*sgid++ = '\0';
sgecos = strchr(sgid, ':');
if (sgecos == NULL) {
- return matched;
+ goto fail;
}
*sgecos++ = '\0';
shome = strchr(sgecos, ':');
if (shome == NULL) {
- return matched;
+ goto fail;
}
*shome++ = '\0';
sshell = strchr(shome, ':');
if (sshell == NULL) {
- return matched;
+ goto fail;
}
*sshell++ = '\0';
- buflen = sizeof(struct passwd) +
+ retlen = sizeof(struct passwd) +
strlen(slogin) + 1 +
strlen(spasswd) + 1 +
strlen(sgecos) + 1 +
strlen(shome) + 1 +
strlen(sshell) + 1;
- *ret = malloc(buflen);
+ *ret = calloc(retlen, sizeof(char));
if (*ret == NULL) {
- return matched;
+ goto fail;
}
- memset(*ret, '\0', buflen);
(*ret)->pw_uid = strtol(suid, &p, 10);
if ((strlen(suid) == 0) || (*p != '\0')) {
- free(*ret);
- *ret = NULL;
- return matched;
+ goto fail;
}
(*ret)->pw_gid = strtol(sgid, &p, 10);
if ((strlen(sgid) == 0) || (*p != '\0')) {
- free(*ret);
- *ret = NULL;
- return matched;
+ goto fail;
}
p = ((char*)(*ret)) + sizeof(struct passwd);
@@ -436,15 +478,23 @@ int _unix_getpwnam(pam_handle_t *pamh, const char *name,
p += strlen(p) + 1;
(*ret)->pw_shell = strcpy(p, sshell);
- snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
+ _pam_drop(buf);
+ if (asprintf(&buf, "_pam_unix_getpwnam_%s", name) < 0) {
+ buf = NULL;
+ goto fail;
+ }
if (pam_set_data(pamh, buf,
*ret, _unix_cleanup) != PAM_SUCCESS) {
- free(*ret);
- *ret = NULL;
+ goto fail;
}
}
+ _pam_drop(buf);
+ return matched;
+fail:
+ _pam_drop(buf);
+ _pam_drop(*ret);
return matched;
}
@@ -513,13 +563,13 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
_exit(PAM_AUTHINFO_UNAVAIL);
}
- if (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...) */
- if (setuid(0) == -1) {
- D(("setuid failed"));
- _exit(PAM_AUTHINFO_UNAVAIL);
- }
+ /* must set the real uid to 0 so the helper will not error
+ out if pam is called from setuid binary (su, sudo...) */
+ if (setuid(0) == -1) {
+ D(("setuid failed"));
+ if (geteuid() == 0) {
+ _exit(PAM_AUTHINFO_UNAVAIL);
+ }
}
/* exec binary helper */
@@ -543,7 +593,7 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
/* if the stored password is NULL */
int rc=0;
if (passwd != NULL) { /* send the password to the child */
- int len = strlen(passwd);
+ size_t len = strlen(passwd);
if (len > PAM_MAX_RESP_SIZE)
len = PAM_MAX_RESP_SIZE;
@@ -609,7 +659,7 @@ _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name
/*
* This function does not have to be too smart if something goes
- * wrong, return FALSE and let this case to be treated somewhere
+ * wrong, return FALSE and let this case be treated somewhere
* else (CG)
*/
@@ -670,12 +720,10 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
D(("called"));
-#ifdef HAVE_PAM_FAIL_DELAY
if (off(UNIX_NODELAY, ctrl)) {
D(("setting delay"));
(void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
}
-#endif
/* locate the entry for this user */
@@ -683,12 +731,9 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
retval = get_pwd_hash(pamh, name, &pwd, &salt);
- data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
- if (data_name == NULL) {
+ if (asprintf(&data_name, "%s%s", FAIL_PREFIX, name) < 0) {
pam_syslog(pamh, LOG_CRIT, "no memory for data-name");
- } else {
- strcpy(data_name, FAIL_PREFIX);
- strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
+ data_name = NULL;
}
if (p != NULL && strlen(p) > PAM_MAX_RESP_SIZE) {
@@ -790,7 +835,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
rhost ? (const char *)rhost : "",
(new->user && new->user[0] != '\0')
? " user=" : "",
- new->user
+ new->user ? new->user : ""
);
new->count = 1;
}