diff options
author | Danny Sauer <dsauer@suse.com> | 2020-08-03 05:28:42 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-03 12:28:42 +0200 |
commit | bfef79dbe6aa525e9557bf4b0a61e6dde12749c4 (patch) | |
tree | 5c277405a7257799623f4011fca391c6aced5456 | |
parent | 0c9cc1ffa122f80fd567be6000f8416fe50bc934 (diff) | |
download | libpwquality-git-bfef79dbe6aa525e9557bf4b0a61e6dde12749c4.tar.gz |
Add usersubstr check
Check for substrings of N characters from the username. Potentially useful for
sites with usernames like firstnamelastname or similar.
Also rename usercheck to "wordcheck" since it's also used for arbitrary words :)
Signed-off-by: Danny Sauer <dsauer@suse.com>
-rw-r--r-- | doc/man/pam_pwquality.8.pod | 6 | ||||
-rw-r--r-- | doc/man/pwquality.conf.5.pod | 6 | ||||
-rw-r--r-- | src/check.c | 75 | ||||
-rw-r--r-- | src/pwqprivate.h | 2 | ||||
-rw-r--r-- | src/pwquality.conf | 4 | ||||
-rw-r--r-- | src/pwquality.h | 1 | ||||
-rw-r--r-- | src/settings.c | 5 |
7 files changed, 78 insertions, 21 deletions
diff --git a/doc/man/pam_pwquality.8.pod b/doc/man/pam_pwquality.8.pod index 017c4d0..7a74b48 100644 --- a/doc/man/pam_pwquality.8.pod +++ b/doc/man/pam_pwquality.8.pod @@ -206,6 +206,12 @@ contains the user name in some form. The default is 1 which means that this check is enabled. It is not performed for user names shorter than 3 characters. +=item B<usersubstr=>I<N> + +If greater than 3 (due to the minimum length in usercheck), check whether the +password contains a substring of at least I<N> length in some form. +The default is 0, which means this check is disabled. + =item B<enforcing=>I<N> If nonzero, reject the password if it fails the checks, otherwise diff --git a/doc/man/pwquality.conf.5.pod b/doc/man/pwquality.conf.5.pod index 47d6fd0..ada22d0 100644 --- a/doc/man/pwquality.conf.5.pod +++ b/doc/man/pwquality.conf.5.pod @@ -111,6 +111,12 @@ If nonzero, check whether the password (with possible modifications) contains the user name in some form. It is not performed for user names shorter than 3 characters. (default 1) +=item B<usersubstr=>I<N> + +If greater than 3 (due to the minimum length in usercheck), check whether the +password contains a substring of at least I<N> length in some form. +(default 0) + =item B<enforcing=>I<N> If nonzero, reject the password if it fails the checks, otherwise diff --git a/src/check.c b/src/check.c index 44983c6..6560535 100644 --- a/src/check.c +++ b/src/check.c @@ -368,28 +368,28 @@ static int sequence(pwquality_settings_t *pwq, const char *new, void **auxerror) } static int -usercheck(pwquality_settings_t *pwq, const char *new, - char *user) +wordcheck(pwquality_settings_t *pwq, const char *new, + char *word) { char *f, *b; - int dist, userlen = strlen(user); + int dist, wordlen = strlen(word); - /* No point to check for username in password in 1-3 char - * usernames; it will be contained one way or another anyway. */ - if (userlen < PWQ_MIN_WORD_LENGTH) + /* No point to check for word in password for 1-3 char + * words; it will be contained one way or another anyway. */ + if (wordlen < PWQ_MIN_WORD_LENGTH) return 0; - if (strstr(new, user) != NULL) - return 1; + if (strstr(new, word) != NULL) + return PWQ_ERROR_BAD_WORDS; - dist = distance(new, user); + dist = distance(new, word); if (dist >= 0 && dist < PWQ_DEFAULT_DIFF_OK) - return 1; + return PWQ_ERROR_BAD_WORDS; - /* now reverse the username, we can do that in place + /* now reverse the wordname, we can do that in place as it is strdup-ed */ - f = user; - b = user + userlen - 1; + f = word; + b = word + wordlen - 1; while (f < b) { char c; @@ -400,16 +400,50 @@ usercheck(pwquality_settings_t *pwq, const char *new, ++f; } - if (strstr(new, user) != NULL) - return 1; + if (strstr(new, word) != NULL) + return PWQ_ERROR_BAD_WORDS; - dist = distance(new, user); + dist = distance(new, word); if (dist >= 0 && dist < PWQ_DEFAULT_DIFF_OK) - return 1; + return PWQ_ERROR_BAD_WORDS; return 0; } +static int +usercheck(pwquality_settings_t *pwq, const char *new, + char *user) +{ + int i, userlen; + int rv = 0; + char *subuser = calloc(pwq->user_substr+1, sizeof(char)); + + if (subuser == NULL) { + return PWQ_ERROR_MEM_ALLOC; + } + + userlen = strlen(user); + if (pwq->user_substr >= PWQ_MIN_WORD_LENGTH && + userlen > pwq->user_substr) { + for(i = 0; !rv && (i <= userlen - pwq->user_substr); i++) { + strncpy(subuser, user+i, pwq->user_substr+1); + subuser[pwq->user_substr] = '\0'; + rv = wordcheck(pwq, new, subuser); + } + } + else { + // if we already tested substrings, there's no need to test + // the whole username; all substrings would've been found :) + if (!rv) + rv = wordcheck(pwq, new, user); + } + // translate wordcheck return + if (rv == PWQ_ERROR_BAD_WORDS) + rv = PWQ_ERROR_USER_CHECK; + free(subuser); + return rv; +} + static char * str_lower(char *string) { @@ -445,7 +479,7 @@ wordlistcheck(pwquality_settings_t *pwq, const char *new, if (strlen(p) >= PWQ_MIN_WORD_LENGTH) { str_lower(p); - if (usercheck(pwq, new, p)) { + if (wordcheck(pwq, new, p)) { free(list); return PWQ_ERROR_BAD_WORDS; } @@ -557,9 +591,8 @@ password_check(pwquality_settings_t *pwq, if (!rv && sequence(pwq, new, auxerror)) rv = PWQ_ERROR_MAX_SEQUENCE; - if (!rv && usermono && pwq->user_check && - usercheck(pwq, newmono, usermono)) - rv = PWQ_ERROR_USER_CHECK; + if (!rv && usermono && pwq->user_check) + rv = usercheck(pwq, newmono, usermono); if (!rv && user && pwq->gecos_check) rv = gecoscheck(pwq, newmono, user); diff --git a/src/pwqprivate.h b/src/pwqprivate.h index 38fc93f..defdaca 100644 --- a/src/pwqprivate.h +++ b/src/pwqprivate.h @@ -26,6 +26,7 @@ struct pwquality_settings { int gecos_check; int dict_check; int user_check; + int user_substr; int enforcing; int retry_times; int enforce_for_root; @@ -48,6 +49,7 @@ struct setting_mapping { #define PWQ_DEFAULT_OTH_CREDIT 0 #define PWQ_DEFAULT_DICT_CHECK 1 #define PWQ_DEFAULT_USER_CHECK 1 +#define PWQ_DEFAULT_USER_SUBSTR 0 #define PWQ_DEFAULT_ENFORCING 1 #define PWQ_DEFAULT_RETRY_TIMES 1 #define PWQ_DEFAULT_ENFORCE_ROOT 0 diff --git a/src/pwquality.conf b/src/pwquality.conf index 1b05cb4..63eb315 100644 --- a/src/pwquality.conf +++ b/src/pwquality.conf @@ -54,6 +54,10 @@ # The check is enabled if the value is not 0. # usercheck = 1 # +# Length of substrings from the username to check for in the password +# The check is enabled if the value is greater than 0 and usercheck is enabled. +# usersubstr = 0 +# # Whether the check is enforced by the PAM module and possibly other # applications. # The new password is rejected if it fails the check and the value is not 0. diff --git a/src/pwquality.h b/src/pwquality.h index 73474cd..8f163bb 100644 --- a/src/pwquality.h +++ b/src/pwquality.h @@ -35,6 +35,7 @@ extern "C" { #define PWQ_SETTING_RETRY_TIMES 18 #define PWQ_SETTING_ENFORCE_ROOT 19 #define PWQ_SETTING_LOCAL_USERS 20 +#define PWQ_SETTING_USER_SUBSTR 21 #define PWQ_MAX_ENTROPY_BITS 256 #define PWQ_MIN_ENTROPY_BITS 56 diff --git a/src/settings.c b/src/settings.c index 3c428cc..6a5f832 100644 --- a/src/settings.c +++ b/src/settings.c @@ -36,6 +36,7 @@ pwquality_default_settings(void) pwq->oth_credit = PWQ_DEFAULT_OTH_CREDIT; pwq->dict_check = PWQ_DEFAULT_DICT_CHECK; pwq->user_check = PWQ_DEFAULT_USER_CHECK; + pwq->user_substr = PWQ_DEFAULT_USER_SUBSTR; pwq->enforcing = PWQ_DEFAULT_ENFORCING; pwq->retry_times = PWQ_DEFAULT_RETRY_TIMES; pwq->enforce_for_root = PWQ_DEFAULT_ENFORCE_ROOT; @@ -70,6 +71,7 @@ static const struct setting_mapping s_map[] = { { "gecoscheck", PWQ_SETTING_GECOS_CHECK, PWQ_TYPE_INT}, { "dictcheck", PWQ_SETTING_DICT_CHECK, PWQ_TYPE_INT}, { "usercheck", PWQ_SETTING_USER_CHECK, PWQ_TYPE_INT}, + { "usersubstr", PWQ_SETTING_USER_SUBSTR, PWQ_TYPE_INT}, { "enforcing", PWQ_SETTING_ENFORCING, PWQ_TYPE_INT}, { "badwords", PWQ_SETTING_BAD_WORDS, PWQ_TYPE_STR}, { "dictpath", PWQ_SETTING_DICT_PATH, PWQ_TYPE_STR}, @@ -349,6 +351,9 @@ pwquality_set_int_value(pwquality_settings_t *pwq, int setting, int value) case PWQ_SETTING_USER_CHECK: pwq->user_check = value; break; + case PWQ_SETTING_USER_SUBSTR: + pwq->user_substr = value; + break; case PWQ_SETTING_ENFORCING: pwq->enforcing = value; break; |