[<min>-<max>:]<class>[,<class>...]
where <class> is one of C<upper>, C<lower>, C<digit>, or C<symbol>
-(without quote marks). The symbol class includes all characters other
-than alphanumeric characters, including space. The listed classes must
-appear in the password. Separate multiple required classes with a comma
-(and no space).
+(without quote marks), or an integer representing a minimum number of
+character classes. The symbol class includes all characters other than
+alphanumeric characters, including space. The listed classes must appear
+in the password. Separate multiple required classes with a comma (and no
+space).
The character class checks will be done in whatever locale the plugin or
password check program is run in, which will normally be the POSIX C
have no character class restrictions. (This example is probably used in
conjunction with C<minimum_length = 8>.)
+C<require_classes> also supports specifying the minimum number of
+character classes a password should contain. For example:
+
+ require_classes = 3
+
+would require all passwords to have a minimum of any three of the
+character classes.
+
+This can also be used with <min> and <max> ranges, as above. For example:
+
+ require_classes = 8-11:3 12-19:2
+
+requires all passwords from 8 to 11 characters contain at least three
+different character classes, and passwords from 12 to 19 characters
+contain at least two different character classes. Ranges can overlap, as
+in the examples above, but this makes less sense when specifying a minimum
+number of classes.
+
+Minimum numbers of character classes can be combined with specific
+character classes. For example:
+
+ require_classes = symbol,3
+
+requires all passwords contain three distinct character classes and must
+contain a symbol character.
+
=item require_non_letter
If set to a true boolean value, the password must contain at least one
bool upper;
bool digit;
bool symbol;
+ unsigned long num_classes;
};
else
classes->symbol = true;
}
+
+ if (classes->lower)
+ classes->num_classes++;
+ if (classes->upper)
+ classes->num_classes++;
+ if (classes->digit)
+ classes->num_classes++;
+ if (classes->symbol)
+ classes->num_classes++;
}
{
if (length < rule->min || (rule->max > 0 && length > rule->max))
return 0;
+
+ if (classes->num_classes < rule->num_classes)
+ return strength_error_class((ctx),
+ "password must have %lu character classes",
+ rule->num_classes);
+
if (rule->lower && !classes->lower)
return strength_error_class((ctx), ERROR_CLASS_LOWER);
if (rule->upper && !classes->upper)
#include <plugin/internal.h>
#include <util/macros.h>
+/* maximum number of character classes */
+#define MAX_CLASSES 4
+
/* The representation of the realm differs between MIT and Kerberos. */
#ifdef HAVE_KRB5_REALM
typedef krb5_realm realm_type;
return strength_error_system(ctx, "cannot allocate memory");
/*
- * If the rule starts with a digit, it starts with a range of affected
- * password lengths. Parse that range.
+ * If the rule starts with a digit and contains a '-', it starts
+ * with a range of affected password lengths. Parse that range.
*/
- if (isdigit((unsigned char) *spec)) {
+ if (isdigit((unsigned char) *spec) && strchr(spec, '-') != NULL) {
okay = parse_number(spec, &(*rule)->min, &end);
if (okay)
okay = (*end == '-');
(*rule)->digit = true;
else if (strcmp(classes->strings[i], "symbol") == 0)
(*rule)->symbol = true;
+ else if (isdigit((unsigned char) *classes->strings[i])) {
+ okay = parse_number(classes->strings[i],
+ &(*rule)->num_classes, &end);
+ if (!okay || *end != '\0' || (*rule)->num_classes > MAX_CLASSES) {
+ code = strength_error_config(ctx, "bad character class"
+ " requirement in configuration:"
+ " %s",
+ classes->strings[i]);
+ goto fail;
+ }
+ }
else {
code = strength_error_config(ctx, "unknown character class %s",
classes->strings[i]);
bool upper;
bool digit;
bool symbol;
+ unsigned long num_classes;
struct class_rule *next;
};