lib/obscure.c: Introduce obscure_get_range

Unify the retrieval of PASS_MIN_LEN and PASS_MAX_LEN for output
in passwd and actual checks.

Fixes wrong output for minimum password lengths if no such
restriction is configured: 5 is printed, 0 is in effect.

How to reproduce:

1. Use passwd compiled without PAM support
2. Do not specify PASS_MIN_LEN in login.defs
3. Run passwd as a user and enter your old password, then
   - you will see that 5 characters are expected
   - you can just press enter twice

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This commit is contained in:
Tobias Stoeckmann 2025-12-19 11:36:59 +00:00 committed by Alejandro Colomar
parent deb192fe78
commit fa88997719
3 changed files with 66 additions and 62 deletions

View File

@ -115,17 +115,17 @@ static /*@observer@*//*@null@*/const char *obscure_msg (
/*@notnull@*/const char *old,
/*@notnull@*/const char *new)
{
size_t maxlen, oldlen, newlen;
int minlen;
int maxlen, minlen;
size_t oldlen, newlen;
char *new1, *old1;
const char *msg;
const char *result;
oldlen = strlen (old);
newlen = strlen (new);
minlen = getdef_num ("PASS_MIN_LEN", 0);
if (minlen != -1 && newlen < (size_t) minlen) {
obscure_get_range(&minlen, &maxlen);
if (newlen < (size_t) minlen) {
return _("too short");
}
@ -141,47 +141,26 @@ static /*@observer@*//*@null@*/const char *obscure_msg (
return msg;
}
result = getdef_str ("ENCRYPT_METHOD");
if (NULL == result) {
if (maxlen == -1) {
return NULL;
}
/* The traditional crypt() truncates passwords to 8 chars. It is
possible to circumvent the above checks by choosing an easy
8-char password and adding some random characters to it...
Example: "password$%^&*123". So check it again, this time
truncated to the maximum length. Idea from npasswd. --marekm */
if (getdef_bool ("MD5_CRYPT_ENAB")) {
return NULL;
}
} else {
if ( streq(result, "MD5")
#ifdef USE_SHA_CRYPT
|| streq(result, "SHA256")
|| streq(result, "SHA512")
#endif
#ifdef USE_BCRYPT
|| streq(result, "BCRYPT")
#endif
#ifdef USE_YESCRYPT
|| streq(result, "YESCRYPT")
#endif
) {
return NULL;
}
}
maxlen = getdef_num ("PASS_MAX_LEN", 8);
if ( (oldlen <= maxlen)
&& (newlen <= maxlen)) {
if ( (oldlen <= (size_t) maxlen)
&& (newlen <= (size_t) maxlen)) {
return NULL;
}
new1 = xstrdup (new);
old1 = xstrdup (old);
if (newlen > maxlen)
if (newlen > (size_t) maxlen)
stpcpy(&new1[maxlen], "");
if (oldlen > maxlen)
if (oldlen > (size_t) maxlen)
stpcpy(&old1[maxlen], "");
msg = password_check(old1, new1);
@ -211,3 +190,50 @@ obscure(const char *old, const char *new)
}
return true;
}
/*
* obscure_get_range - retrieve min and max password lengths
*
* Returns minimum and maximum allowed lengths of a password
* to pass obscure checks.
*/
void
obscure_get_range(int *minlen, int *maxlen)
{
int val;
const char *method;
/* Minimum length is 0, even if -1 is configured. */
val = getdef_num("PASS_MIN_LEN", 0);
*minlen = val == -1 ? 0 : val;
/* Maximum password length check is optional. */
*maxlen = -1;
if (!getdef_bool("OBSCURE_CHECKS_ENAB")) {
return;
}
method = getdef_str ("ENCRYPT_METHOD");
if (NULL == method) {
if (getdef_bool ("MD5_CRYPT_ENAB")) {
return;
}
} else {
if ( streq(method, "MD5")
#ifdef USE_SHA_CRYPT
|| streq(method, "SHA256")
|| streq(method, "SHA512")
#endif
#ifdef USE_BCRYPT
|| streq(method, "BCRYPT")
#endif
#ifdef USE_YESCRYPT
|| streq(method, "YESCRYPT")
#endif
) {
return;
}
}
*maxlen = getdef_num ("PASS_MAX_LEN", 8);
}

View File

@ -302,6 +302,7 @@ extern int do_pam_passwd_non_interactive (const char *pam_service,
/* obscure.c */
extern bool obscure (const char *, const char *);
extern void obscure_get_range(int *, int *);
/* pam_pass.c */
#ifdef USE_PAM

View File

@ -194,8 +194,6 @@ static int new_password (const struct passwd *pw)
int i; /* Counter for retries */
int ret;
bool warned;
int pass_max_len = -1;
const char *method;
/*
* Authenticate the user. The user will be prompted for their own
@ -245,41 +243,20 @@ static int new_password (const struct passwd *pw)
* be optionally tested for strength. The root user can circumvent
* tests. This provides an escape for initial login passwords.
*/
method = getdef_str ("ENCRYPT_METHOD");
if (NULL == method) {
if (!getdef_bool ("MD5_CRYPT_ENAB")) {
pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
}
} else {
if ( streq(method, "MD5")
#ifdef USE_SHA_CRYPT
|| streq(method, "SHA256")
|| streq(method, "SHA512")
#endif /* USE_SHA_CRYPT */
#ifdef USE_BCRYPT
|| streq(method, "BCRYPT")
#endif /* USE_BCRYPT*/
#ifdef USE_YESCRYPT
|| streq(method, "YESCRYPT")
#endif /* USE_YESCRYPT*/
) {
pass_max_len = -1;
} else {
pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
}
}
if (!qflg && !sflg) {
int pass_max_len, pass_min_len;
obscure_get_range(&pass_min_len, &pass_max_len);
if (pass_max_len == -1) {
(void) printf (_(
"Enter the new password (minimum of %d characters)\n"
"Please use a combination of upper and lower case letters and numbers.\n"),
getdef_num ("PASS_MIN_LEN", 5));
pass_min_len);
} else {
(void) printf (_(
"Enter the new password (minimum of %d, maximum of %d characters)\n"
"Please use a combination of upper and lower case letters and numbers.\n"),
getdef_num ("PASS_MIN_LEN", 5), pass_max_len);
pass_min_len, pass_max_len);
}
}