295 lines
11 KiB
Diff
295 lines
11 KiB
Diff
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/doc/man/pam_pwquality.8.pod b/doc/man/pam_pwquality.8.pod
|
|
--- a/doc/man/pam_pwquality.8.pod 2026-06-24 13:39:51.505182998 +0200
|
|
+++ b/doc/man/pam_pwquality.8.pod 2026-06-24 13:47:32.042603712 +0200
|
|
@@ -250,6 +250,13 @@
|
|
a new password but use the one provided by the previously stacked
|
|
B<password> module.
|
|
|
|
+=item B<allowclasses=>I<< "ludo" >>
|
|
+
|
|
+The list of letters that represent allowed character classes that
|
|
+are allowed in a password (digits, uppercase, lowercase, others).
|
|
+"d" is for digits, "u" is for uppercase letters, "l" is for
|
|
+lowercase letters, "o" is for other characters.
|
|
+
|
|
=back
|
|
|
|
=head1 MODULE TYPES PROVIDED
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/doc/man/pwquality.conf.5.pod b/doc/man/pwquality.conf.5.pod
|
|
--- a/doc/man/pwquality.conf.5.pod 2026-06-24 13:39:51.505235241 +0200
|
|
+++ b/doc/man/pwquality.conf.5.pod 2026-06-24 13:50:30.694360330 +0200
|
|
@@ -85,7 +85,7 @@
|
|
Examples of such sequence are '12345' or 'fedcb'. Note
|
|
that most such passwords will not pass the simplicity check unless
|
|
the sequence is only a minor part of the password.
|
|
-The check is disabled if the value is 0. (default 0)
|
|
+The check is disabled if the value is 0. (default 0)
|
|
|
|
=item B<maxclassrepeat>
|
|
|
|
@@ -155,6 +155,13 @@
|
|
the following modules in the stack can use the B<use_authtok> option.
|
|
This option is off by default.
|
|
|
|
+=item B<allowclasses>
|
|
+
|
|
+The list of letters that represent allowed character classes that
|
|
+are allowed in a password (digits, uppercase, lowercase, others).
|
|
+"d" is for digits, "u" is for uppercase letters, "l" is for
|
|
+lowercase letters, "o" is for other characters. (default "ludo")
|
|
+
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/check.c b/src/check.c
|
|
--- a/src/check.c 2026-06-24 13:39:51.504779475 +0200
|
|
+++ b/src/check.c 2026-06-24 13:45:06.118168891 +0200
|
|
@@ -49,7 +49,7 @@
|
|
* the other
|
|
*/
|
|
|
|
-static int
|
|
+static int
|
|
distdifferent(const char *old, const char *new,
|
|
size_t i, size_t j)
|
|
{
|
|
@@ -202,6 +202,7 @@
|
|
int others = 0;
|
|
int size;
|
|
int i;
|
|
+ const char *allow_classes;
|
|
enum { NONE, DIGIT, UCASE, LCASE, OTHER } prevclass = NONE;
|
|
int sameclass = 0;
|
|
|
|
@@ -244,6 +245,35 @@
|
|
return PWQ_ERROR_MAX_CLASS_REPEAT;
|
|
}
|
|
}
|
|
+ pwquality_get_str_value(pwq, PWQ_SETTING_ALLOW_CLASSES, &allow_classes);
|
|
+ if (digits > 0) {
|
|
+ if (strpbrk(allow_classes, "d") == NULL) {
|
|
+ if (auxerror)
|
|
+ *auxerror = strdup("digits");
|
|
+ return PWQ_ERROR_DISALLOWED_CLASS;
|
|
+ }
|
|
+ }
|
|
+ if (uppers > 0) {
|
|
+ if (strpbrk(allow_classes, "u") == NULL) {
|
|
+ if (auxerror)
|
|
+ *auxerror = strdup("uppercase");
|
|
+ return PWQ_ERROR_DISALLOWED_CLASS;
|
|
+ }
|
|
+ }
|
|
+ if (lowers > 0) {
|
|
+ if (strpbrk(allow_classes, "l") == NULL) {
|
|
+ if (auxerror)
|
|
+ *auxerror = strdup("lowercase");
|
|
+ return PWQ_ERROR_DISALLOWED_CLASS;
|
|
+ }
|
|
+ }
|
|
+ if (others > 0) {
|
|
+ if (strpbrk(allow_classes, "o") == NULL) {
|
|
+ if (auxerror)
|
|
+ *auxerror = strdup("other");
|
|
+ return PWQ_ERROR_DISALLOWED_CLASS;
|
|
+ }
|
|
+ }
|
|
|
|
if ((pwq->dig_credit >= 0) && (digits > pwq->dig_credit))
|
|
digits = pwq->dig_credit;
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/error.c b/src/error.c
|
|
--- a/src/error.c 2026-06-24 13:39:51.504839606 +0200
|
|
+++ b/src/error.c 2026-06-24 13:45:29.032394205 +0200
|
|
@@ -149,6 +149,13 @@
|
|
return _("The configuration file is malformed");
|
|
case PWQ_ERROR_FATAL_FAILURE:
|
|
return _("Fatal failure");
|
|
+ case PWQ_ERROR_DISALLOWED_CLASS:
|
|
+ if (auxerror) {
|
|
+ snprintf(buf, len, _("The %s class of characters is not allowed"), (const char *)auxerror);
|
|
+ free(auxerror);
|
|
+ return buf;
|
|
+ }
|
|
+ return _("The password contains disallowed character class");
|
|
default:
|
|
return _("Unknown error");
|
|
}
|
|
@@ -188,4 +195,3 @@
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
-
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/generate.c b/src/generate.c
|
|
--- a/src/generate.c 2026-06-24 13:39:51.504750650 +0200
|
|
+++ b/src/generate.c 2026-06-24 13:45:38.556487851 +0200
|
|
@@ -95,7 +95,7 @@
|
|
}
|
|
|
|
*offset += bits;
|
|
- return low;
|
|
+ return low;
|
|
}
|
|
|
|
/* generate a random password according to the settings */
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/pam_pwquality.c b/src/pam_pwquality.c
|
|
--- a/src/pam_pwquality.c 2026-06-24 13:39:51.504861183 +0200
|
|
+++ b/src/pam_pwquality.c 2026-06-24 13:46:16.791863795 +0200
|
|
@@ -88,7 +88,7 @@
|
|
} else if (!strncmp(*argv, "try_first_pass", 14)) {
|
|
/* for pam_get_authtok, ignore */;
|
|
} else if (pwquality_set_option(pwq, *argv)) {
|
|
- pam_syslog(pamh, LOG_ERR,
|
|
+ pam_syslog(pamh, LOG_ERR,
|
|
"pam_parse: unknown or broken option; %s", *argv);
|
|
}
|
|
}
|
|
@@ -213,7 +213,7 @@
|
|
if (retval != PAM_SUCCESS || newtoken == NULL) {
|
|
if (retval == PAM_AUTHTOK_ERR || newtoken == NULL)
|
|
pam_syslog(pamh, LOG_INFO, "user aborted password change");
|
|
- else
|
|
+ else
|
|
pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s",
|
|
pam_strerror(pamh, retval));
|
|
pwquality_free_settings(options.pwq);
|
|
@@ -262,7 +262,7 @@
|
|
continue;
|
|
if (retval == PAM_AUTHTOK_ERR || newtoken == NULL)
|
|
pam_syslog(pamh, LOG_INFO, "user aborted password change");
|
|
- else
|
|
+ else
|
|
pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s",
|
|
pam_strerror(pamh, retval));
|
|
pwquality_free_settings(options.pwq);
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/pwqprivate.h b/src/pwqprivate.h
|
|
--- a/src/pwqprivate.h 2026-06-24 13:39:51.504670235 +0200
|
|
+++ b/src/pwqprivate.h 2026-06-24 13:43:52.963449598 +0200
|
|
@@ -33,6 +33,7 @@
|
|
int local_users_only;
|
|
char *bad_words;
|
|
char *dict_path;
|
|
+ char *allow_classes;
|
|
};
|
|
|
|
struct setting_mapping {
|
|
@@ -47,6 +48,7 @@
|
|
#define PWQ_DEFAULT_UP_CREDIT 0
|
|
#define PWQ_DEFAULT_LOW_CREDIT 0
|
|
#define PWQ_DEFAULT_OTH_CREDIT 0
|
|
+#define PWQ_DEFAULT_ALLOWCLASSES "ludo"
|
|
|
|
#ifdef HAVE_CRACK_H
|
|
#define PWQ_DEFAULT_DICT_CHECK 1
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/pwquality.conf b/src/pwquality.conf
|
|
--- a/src/pwquality.conf 2026-06-24 13:39:51.504930249 +0200
|
|
+++ b/src/pwquality.conf 2026-06-24 13:50:46.129512097 +0200
|
|
@@ -58,6 +58,10 @@
|
|
# The check is enabled if the value is greater than 0 and usercheck is enabled.
|
|
# usersubstr = 0
|
|
#
|
|
+# The allowed classes of characters (digits, uppercase, lowercase, others)
|
|
+# to be used in the password.
|
|
+# allowclasses = "ludo"
|
|
+#
|
|
# 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 -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/pwquality.h b/src/pwquality.h
|
|
--- a/src/pwquality.h 2026-06-24 13:39:51.504650547 +0200
|
|
+++ b/src/pwquality.h 2026-06-24 13:47:07.084358309 +0200
|
|
@@ -36,6 +36,7 @@
|
|
#define PWQ_SETTING_ENFORCE_ROOT 19
|
|
#define PWQ_SETTING_LOCAL_USERS 20
|
|
#define PWQ_SETTING_USER_SUBSTR 21
|
|
+#define PWQ_SETTING_ALLOW_CLASSES 22
|
|
|
|
#define PWQ_MAX_ENTROPY_BITS 256
|
|
#define PWQ_MIN_ENTROPY_BITS 56
|
|
@@ -72,6 +73,7 @@
|
|
#define PWQ_ERROR_MAX_CLASS_REPEAT -27
|
|
#define PWQ_ERROR_BAD_WORDS -28
|
|
#define PWQ_ERROR_MAX_SEQUENCE -29
|
|
+#define PWQ_ERROR_DISALLOWED_CLASS -30
|
|
|
|
typedef struct pwquality_settings pwquality_settings_t;
|
|
|
|
@@ -135,7 +137,7 @@
|
|
* is not returned.
|
|
* Not passing the *auxerror into pwquality_strerror() can lead to memory leaks.
|
|
* The score depends on PWQ_SETTING_MIN_LENGTH. If it is set higher,
|
|
- * the score for the same passwords will be lower. */
|
|
+ * the score for the same passwords will be lower. */
|
|
int
|
|
pwquality_check(pwquality_settings_t *pwq, const char *password,
|
|
const char *oldpassword, const char *user, void **auxerror);
|
|
diff -ruN '--exclude=*.rej' '--exclude=*.orig' a/src/settings.c b/src/settings.c
|
|
--- a/src/settings.c 2026-06-24 13:39:51.504810433 +0200
|
|
+++ b/src/settings.c 2026-06-24 13:44:29.312806994 +0200
|
|
@@ -26,9 +26,15 @@
|
|
{
|
|
pwquality_settings_t *pwq;
|
|
|
|
+ char *allow_classes;
|
|
+
|
|
pwq = calloc(1, sizeof(*pwq));
|
|
- if (!pwq)
|
|
+ allow_classes = strdup(PWQ_DEFAULT_ALLOWCLASSES);
|
|
+ if (!pwq || !allow_classes) {
|
|
+ free(pwq);
|
|
+ free(allow_classes);
|
|
return NULL;
|
|
+ }
|
|
|
|
pwq->diff_ok = PWQ_DEFAULT_DIFF_OK;
|
|
pwq->min_length = PWQ_DEFAULT_MIN_LENGTH;
|
|
@@ -43,6 +49,7 @@
|
|
pwq->retry_times = PWQ_DEFAULT_RETRY_TIMES;
|
|
pwq->enforce_for_root = PWQ_DEFAULT_ENFORCE_ROOT;
|
|
pwq->local_users_only = PWQ_DEFAULT_LOCAL_USERS;
|
|
+ pwq->allow_classes = allow_classes;
|
|
|
|
return pwq;
|
|
}
|
|
@@ -54,6 +61,7 @@
|
|
if (pwq) {
|
|
free(pwq->dict_path);
|
|
free(pwq->bad_words);
|
|
+ free(pwq->allow_classes);
|
|
free(pwq);
|
|
}
|
|
}
|
|
@@ -79,7 +87,8 @@
|
|
{ "dictpath", PWQ_SETTING_DICT_PATH, PWQ_TYPE_STR},
|
|
{ "retry", PWQ_SETTING_RETRY_TIMES, PWQ_TYPE_INT},
|
|
{ "enforce_for_root", PWQ_SETTING_ENFORCE_ROOT, PWQ_TYPE_SET},
|
|
- { "local_users_only", PWQ_SETTING_LOCAL_USERS, PWQ_TYPE_SET}
|
|
+ { "local_users_only", PWQ_SETTING_LOCAL_USERS, PWQ_TYPE_SET},
|
|
+ { "allowclasses", PWQ_SETTING_ALLOW_CLASSES, PWQ_TYPE_STR}
|
|
};
|
|
|
|
/* set setting name with value */
|
|
@@ -399,6 +408,10 @@
|
|
free(pwq->dict_path);
|
|
pwq->dict_path = dup;
|
|
break;
|
|
+ case PWQ_SETTING_ALLOW_CLASSES:
|
|
+ free(pwq->allow_classes);
|
|
+ pwq->allow_classes = dup;
|
|
+ break;
|
|
default:
|
|
free(dup);
|
|
return PWQ_ERROR_NON_STR_SETTING;
|
|
@@ -490,6 +503,12 @@
|
|
*value = NULL;
|
|
#endif
|
|
break;
|
|
+ case PWQ_SETTING_ALLOW_CLASSES:
|
|
+ if (pwq->allow_classes)
|
|
+ *value = pwq->allow_classes;
|
|
+ else
|
|
+ *value = PWQ_DEFAULT_ALLOWCLASSES;
|
|
+ break;
|
|
default:
|
|
return PWQ_ERROR_NON_STR_SETTING;
|
|
}
|