mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-10-25 14:40:16 -05:00
Add support for libpwpolicy credit to password policy
We already have libpwquality plumbed in. The ask here was to support the quality credits as well. There are four credits: digits, uppers, lowers and others These either count complexity towards satisfying the minimum length or set the minimum number of a type allowed. If any of the four credits is configured then it will cause the password policy plugin to go through the libpwquality. Reminder that libpwquality sets its own min password length floor that should be overridden already. If min_classes is set as well then that is still enforced. Fixes: https://pagure.io/freeipa/issue/9835 Signed-off-by: Rob Crittenden <rcritten@redhat.com> Reviewed-By: David Hanina <dhanina@redhat.com>
This commit is contained in:
1 parent
db4ff37792
commit
892fea881a
15 files changed
+523
-106
No files matched your search
@@ -247,9 +247,9 @@ aci: (targetfilter = "(|(objectclass=ipapwdpolicy)(objectclass=krbpwdpolicy))")(
|
||||
dn: cn=IPA.EXAMPLE,cn=kerberos,dc=ipa,dc=example
|
||||
aci: (targetfilter = "(|(objectclass=ipapwdpolicy)(objectclass=krbpwdpolicy))")(version 3.0;acl "permission:System: Delete Group Password Policy";allow (delete) groupdn = "ldap:///cn=System: Delete Group Password Policy,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=IPA.EXAMPLE,cn=kerberos,dc=ipa,dc=example
|
||||
aci: (targetattr = "ipapwddictcheck || ipapwdmaxrepeat || ipapwdmaxsequence || ipapwdusercheck || krbmaxpwdlife || krbminpwdlife || krbpwdfailurecountinterval || krbpwdhistorylength || krbpwdlockoutduration || krbpwdmaxfailure || krbpwdmindiffchars || krbpwdminlength || passwordgracelimit")(targetfilter = "(|(objectclass=ipapwdpolicy)(objectclass=krbpwdpolicy))")(version 3.0;acl "permission:System: Modify Group Password Policy";allow (write) groupdn = "ldap:///cn=System: Modify Group Password Policy,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
aci: (targetattr = "ipapwddcredit || ipapwddictcheck || ipapwdlcredit || ipapwdmaxrepeat || ipapwdmaxsequence || ipapwdocredit || ipapwducredit || ipapwdusercheck || krbmaxpwdlife || krbminpwdlife || krbpwdfailurecountinterval || krbpwdhistorylength || krbpwdlockoutduration || krbpwdmaxfailure || krbpwdmindiffchars || krbpwdminlength || passwordgracelimit")(targetfilter = "(|(objectclass=ipapwdpolicy)(objectclass=krbpwdpolicy))")(version 3.0;acl "permission:System: Modify Group Password Policy";allow (write) groupdn = "ldap:///cn=System: Modify Group Password Policy,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=IPA.EXAMPLE,cn=kerberos,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || cospriority || createtimestamp || entryusn || ipapwddictcheck || ipapwdmaxrepeat || ipapwdmaxsequence || ipapwdusercheck || krbmaxpwdlife || krbminpwdlife || krbpwdfailurecountinterval || krbpwdhistorylength || krbpwdlockoutduration || krbpwdmaxfailure || krbpwdmindiffchars || krbpwdminlength || modifytimestamp || objectclass || passwordgracelimit")(targetfilter = "(|(objectclass=ipapwdpolicy)(objectclass=krbpwdpolicy))")(version 3.0;acl "permission:System: Read Group Password Policy";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Group Password Policy,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
aci: (targetattr = "cn || cospriority || createtimestamp || entryusn || ipapwddcredit || ipapwddictcheck || ipapwdlcredit || ipapwdmaxrepeat || ipapwdmaxsequence || ipapwdocredit || ipapwducredit || ipapwdusercheck || krbmaxpwdlife || krbminpwdlife || krbpwdfailurecountinterval || krbpwdhistorylength || krbpwdlockoutduration || krbpwdmaxfailure || krbpwdmindiffchars || krbpwdminlength || modifytimestamp || objectclass || passwordgracelimit")(targetfilter = "(|(objectclass=ipapwdpolicy)(objectclass=krbpwdpolicy))")(version 3.0;acl "permission:System: Read Group Password Policy";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Group Password Policy,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=radiusproxy,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || description || entryusn || ipatokenradiusretries || ipatokenradiusserver || ipatokenradiustimeout || ipatokenusermapattribute || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipatokenradiusconfiguration)")(version 3.0;acl "permission:System: Read Radius Servers";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Radius Servers,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=Realm Domains,cn=ipa,cn=etc,dc=ipa,dc=example
|
||||
|
||||
@@ -4134,14 +4134,18 @@ output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: pwpolicy_add/1
|
||||
args: 1,19,3
|
||||
args: 1,23,3
|
||||
arg: Str('cn', cli_name='group')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Int('cospriority', cli_name='priority')
|
||||
option: Int('ipapwddcredit?', cli_name='dcredit', default=0)
|
||||
option: Bool('ipapwddictcheck?', cli_name='dictcheck', default=False)
|
||||
option: Int('ipapwducredit?', cli_name='ucredit', default=0)
|
||||
option: Int('ipapwdlcredit?', cli_name='lcredit', default=0)
|
||||
option: Int('ipapwdmaxrepeat?', cli_name='maxrepeat', default=0)
|
||||
option: Int('ipapwdmaxsequence?', cli_name='maxsequence', default=0)
|
||||
option: Int('ipapwdocredit?', cli_name='ocredit', default=0)
|
||||
option: Bool('ipapwdusercheck?', cli_name='usercheck', default=False)
|
||||
option: Int('krbmaxpwdlife?', cli_name='maxlife')
|
||||
option: Int('krbminpwdlife?', cli_name='minlife')
|
||||
@@ -4167,14 +4171,18 @@ output: Output('result', type=[<type 'dict'>])
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: ListOfPrimaryKeys('value')
|
||||
command: pwpolicy_find/1
|
||||
args: 1,21,4
|
||||
args: 1,25,4
|
||||
arg: Str('criteria?')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('cn?', autofill=False, cli_name='group')
|
||||
option: Int('cospriority?', autofill=False, cli_name='priority')
|
||||
option: Int('ipapwddcredit?', autofill=False, cli_name='dcredit', default=0)
|
||||
option: Bool('ipapwddictcheck?', autofill=False, cli_name='dictcheck', default=False)
|
||||
option: Int('ipapwducredit?', autofill=False, cli_name='ucredit', default=0)
|
||||
option: Int('ipapwdlcredit?', autofill=False, cli_name='lcredit', default=0)
|
||||
option: Int('ipapwdmaxrepeat?', autofill=False, cli_name='maxrepeat', default=0)
|
||||
option: Int('ipapwdmaxsequence?', autofill=False, cli_name='maxsequence', default=0)
|
||||
option: Int('ipapwdocredit?', autofill=False, cli_name='ocredit', default=0)
|
||||
option: Bool('ipapwdusercheck?', autofill=False, cli_name='usercheck', default=False)
|
||||
option: Int('krbmaxpwdlife?', autofill=False, cli_name='maxlife')
|
||||
option: Int('krbminpwdlife?', autofill=False, cli_name='minlife')
|
||||
@@ -4195,15 +4203,19 @@ output: ListOfEntries('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: Output('truncated', type=[<type 'bool'>])
|
||||
command: pwpolicy_mod/1
|
||||
args: 1,21,3
|
||||
args: 1,25,3
|
||||
arg: Str('cn?', cli_name='group')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Int('cospriority?', autofill=False, cli_name='priority')
|
||||
option: Str('delattr*', cli_name='delattr')
|
||||
option: Int('ipapwddcredit?', autofill=False, cli_name='dcredit', default=0)
|
||||
option: Bool('ipapwddictcheck?', autofill=False, cli_name='dictcheck', default=False)
|
||||
option: Int('ipapwducredit?', autofill=False, cli_name='ucredit', default=0)
|
||||
option: Int('ipapwdlcredit?', autofill=False, cli_name='lcredit', default=0)
|
||||
option: Int('ipapwdmaxrepeat?', autofill=False, cli_name='maxrepeat', default=0)
|
||||
option: Int('ipapwdmaxsequence?', autofill=False, cli_name='maxsequence', default=0)
|
||||
option: Int('ipapwdocredit?', autofill=False, cli_name='ocredit', default=0)
|
||||
option: Bool('ipapwdusercheck?', autofill=False, cli_name='usercheck', default=False)
|
||||
option: Int('krbmaxpwdlife?', autofill=False, cli_name='maxlife')
|
||||
option: Int('krbminpwdlife?', autofill=False, cli_name='minlife')
|
||||
|
||||
+2
-2
@@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
|
||||
# #
|
||||
########################################################
|
||||
define(IPA_API_VERSION_MAJOR, 2)
|
||||
# Last change: add keeponly option to batch command
|
||||
define(IPA_API_VERSION_MINOR, 254)
|
||||
# Last change: add pwpolicy credit options
|
||||
define(IPA_API_VERSION_MINOR, 255)
|
||||
|
||||
########################################################
|
||||
# Following values are auto-generated from values above
|
||||
|
||||
@@ -50,6 +50,10 @@ static krb5_error_code ipapwd_error_to_kerr(krb5_context context,
|
||||
krb5_set_error_message(context, kerr, "Password reuse not permitted");
|
||||
break;
|
||||
case IPAPWD_POLICY_PWD_COMPLEXITY:
|
||||
case IPAPWD_POLICY_PWD_MIN_DIGITS:
|
||||
case IPAPWD_POLICY_PWD_MIN_UPPERS:
|
||||
case IPAPWD_POLICY_PWD_MIN_LOWERS:
|
||||
case IPAPWD_POLICY_PWD_MIN_OTHERS:
|
||||
kerr = KADM5_PASS_Q_CLASS;
|
||||
krb5_set_error_message(context, kerr, "Password is too simple");
|
||||
break;
|
||||
|
||||
@@ -38,6 +38,10 @@ char *std_pwdpolicy_attrs[] = {
|
||||
"ipapwdmaxsequence",
|
||||
"ipapwddictcheck",
|
||||
"ipapwdusercheck",
|
||||
"ipapwddcredit",
|
||||
"ipapwducredit",
|
||||
"ipapwdlcredit",
|
||||
"ipapwdocredit",
|
||||
|
||||
NULL
|
||||
};
|
||||
@@ -51,6 +55,7 @@ krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx,
|
||||
LDAPMessage *res = NULL;
|
||||
LDAPMessage *lentry;
|
||||
uint32_t result;
|
||||
int iresult;
|
||||
bool resbool;
|
||||
int ret;
|
||||
|
||||
@@ -146,8 +151,28 @@ krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx,
|
||||
pol->usercheck = 1;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_int(ipactx->lcontext, lentry,
|
||||
"ipaPwdDcredit", &iresult);
|
||||
if (ret == 0) {
|
||||
pol->max_sequence = result;
|
||||
pol->dcredit = iresult;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_int(ipactx->lcontext, lentry,
|
||||
"ipaPwdUcredit", &iresult);
|
||||
if (ret == 0) {
|
||||
pol->ucredit = iresult;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_int(ipactx->lcontext, lentry,
|
||||
"ipaPwdLcredit", &iresult);
|
||||
if (ret == 0) {
|
||||
pol->lcredit = iresult;
|
||||
}
|
||||
|
||||
ret = ipadb_ldap_attr_to_int(ipactx->lcontext, lentry,
|
||||
"ipaPwdOcredit", &iresult);
|
||||
if (ret == 0) {
|
||||
pol->ocredit = iresult;
|
||||
}
|
||||
|
||||
*_pol = pol;
|
||||
|
||||
@@ -333,7 +333,9 @@ int ipapwd_getPolicy(const char *dn,
|
||||
"krbPwdMinDiffChars", "krbPwdMinLength",
|
||||
"krbPwdHistoryLength", "ipaPwdMaxRepeat",
|
||||
"ipaPwdMaxSequence", "ipaPwdDictCheck",
|
||||
"ipaPwdUserCheck", NULL};
|
||||
"ipaPwdUserCheck", "ipapwdDcredit",
|
||||
"ipaPwdUcredit", "ipaPwdLcredit",
|
||||
"ipaPwdOcredit", NULL};
|
||||
Slapi_Entry **es = NULL;
|
||||
Slapi_Entry *pe = NULL;
|
||||
int ret, res, scope;
|
||||
@@ -418,6 +420,10 @@ int ipapwd_getPolicy(const char *dn,
|
||||
policy->max_sequence = slapi_entry_attr_get_int(pe, "ipaPwdMaxSequence");
|
||||
policy->dictcheck = slapi_entry_attr_get_bool(pe, "ipaPwdDictCheck");
|
||||
policy->usercheck = slapi_entry_attr_get_bool(pe, "ipaPwdUserCheck");
|
||||
policy->dcredit = slapi_entry_attr_get_int(pe, "ipaPwdDcredit");
|
||||
policy->ucredit = slapi_entry_attr_get_int(pe, "ipaPwdUcredit");
|
||||
policy->lcredit = slapi_entry_attr_get_int(pe, "ipaPwdLcredit");
|
||||
policy->ocredit = slapi_entry_attr_get_int(pe, "ipaPwdOcredit");
|
||||
|
||||
ret = 0;
|
||||
|
||||
|
||||
@@ -29,6 +29,14 @@ Add a new group password policy.
|
||||
* Default: False
|
||||
* ipapwdusercheck : :ref:`Bool<Bool>`
|
||||
* Default: False
|
||||
* ipapwddcredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwducredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwdlcredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwdocredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* passwordgracelimit : :ref:`Int<Int>`
|
||||
* Default: -1
|
||||
* setattr : :ref:`Str<Str>`
|
||||
@@ -47,4 +55,4 @@ Add a new group password policy.
|
||||
|
||||
### Notes
|
||||
|
||||
### Version differences
|
||||
### Version differences
|
||||
@@ -30,6 +30,14 @@ Search for group password policies.
|
||||
* Default: False
|
||||
* ipapwdusercheck : :ref:`Bool<Bool>`
|
||||
* Default: False
|
||||
* ipapwddcredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwducredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwdlcredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwdocredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* passwordgracelimit : :ref:`Int<Int>`
|
||||
* Default: -1
|
||||
* timelimit : :ref:`Int<Int>`
|
||||
@@ -51,4 +59,4 @@ Search for group password policies.
|
||||
|
||||
### Notes
|
||||
|
||||
### Version differences
|
||||
### Version differences
|
||||
@@ -31,6 +31,14 @@ Modify a group password policy.
|
||||
* Default: False
|
||||
* ipapwdusercheck : :ref:`Bool<Bool>`
|
||||
* Default: False
|
||||
* ipapwddcredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwducredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwdlcredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* ipapwdocredit : :ref:`Int<Int>`
|
||||
* Default: 0
|
||||
* passwordgracelimit : :ref:`Int<Int>`
|
||||
* Default: -1
|
||||
* setattr : :ref:`Str<Str>`
|
||||
@@ -50,4 +58,4 @@ Modify a group password policy.
|
||||
|
||||
### Notes
|
||||
|
||||
### Version differences
|
||||
### Version differences
|
||||
@@ -58,4 +58,8 @@ attributeTypes: (2.16.840.1.113730.3.8.23.2 NAME 'ipaPwdMaxRepeat' EQUALITY inte
|
||||
attributeTypes: (2.16.840.1.113730.3.8.23.3 NAME 'ipaPwdMaxSequence' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4')
|
||||
attributeTypes: (2.16.840.1.113730.3.8.23.4 NAME 'ipaPwdDictCheck' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4')
|
||||
attributeTypes: (2.16.840.1.113730.3.8.23.5 NAME 'ipaPwdUserCheck' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4')
|
||||
objectClasses: (2.16.840.1.113730.3.8.24.1 NAME 'ipaPwdPolicy' DESC 'IPA Password policy object class' SUP top MAY (ipaPwdMaxRepeat $ ipaPwdMaxSequence $ ipaPwdDictCheck $ ipaPwdUserCheck $ passwordGraceLimit) X-ORIGIN 'IPA v4')
|
||||
attributeTypes: (2.16.840.1.113730.3.8.23.28 NAME 'ipaPwdDcredit' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4')
|
||||
attributeTypes: (2.16.840.1.113730.3.8.23.29 NAME 'ipaPwdUcredit' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4')
|
||||
attributeTypes: (2.16.840.1.113730.3.8.23.30 NAME 'ipaPwdLcredit' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4')
|
||||
attributeTypes: (2.16.840.1.113730.3.8.23.31 NAME 'ipaPwdOcredit' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4')
|
||||
objectClasses: (2.16.840.1.113730.3.8.24.1 NAME 'ipaPwdPolicy' DESC 'IPA Password policy object class' SUP top MAY (ipaPwdMaxRepeat $ ipaPwdMaxSequence $ ipaPwdDictCheck $ ipaPwdUserCheck $ ipaPwdDcredit $ ipaPwdUcredit $ ipaPwdLcredit $ ipaPwdOcredit $ passwordGraceLimit) X-ORIGIN 'IPA v4')
|
||||
@@ -66,6 +66,20 @@ Grace period defines the number of LDAP logins allowed after expiration.
|
||||
-1 means do not enforce expiration to match previous behavior. 0 allows
|
||||
no additional logins after expiration.
|
||||
|
||||
The pwquality options are either mutually exclusive, or take
|
||||
precedence, over the standard password policy values. In the case
|
||||
of minimum password length if any pwquality-based options are used then
|
||||
the minimum length must be >= 6.
|
||||
|
||||
The "credit" settings are used to adjust password complexity requirements.
|
||||
- With a value of 0, the default, the option is ignored.
|
||||
- With a positive value each character of that type in the password
|
||||
contributes towards meeting the mininum length requirement.
|
||||
For example, with a password policy of `minlength=6`, `dcredit=1`,
|
||||
these passwords are valid: abcdef or abcd1
|
||||
- With a negative value signifies a minimum number of that character type
|
||||
that must be present.
|
||||
|
||||
EXAMPLES:
|
||||
|
||||
Modify the global policy:
|
||||
@@ -249,6 +263,7 @@ class pwpolicy(LDAPObject):
|
||||
'krbpwdlockoutduration', 'ipapwdmaxrepeat',
|
||||
'ipapwdmaxsequence', 'ipapwddictcheck',
|
||||
'ipapwdusercheck', 'passwordgracelimit',
|
||||
'ipapwddcredit', 'ipapwducredit', 'ipapwdlcredit', 'ipapwdocredit',
|
||||
]
|
||||
managed_permissions = {
|
||||
'System: Read Group Password Policy': {
|
||||
@@ -261,7 +276,8 @@ class pwpolicy(LDAPObject):
|
||||
'krbpwdlockoutduration', 'krbpwdmaxfailure',
|
||||
'krbpwdmindiffchars', 'krbpwdminlength', 'objectclass',
|
||||
'ipapwdmaxrepeat', 'ipapwdmaxsequence', 'ipapwddictcheck',
|
||||
'ipapwdusercheck', 'passwordgracelimit',
|
||||
'ipapwdusercheck', 'passwordgracelimit', 'ipapwddcredit',
|
||||
'ipapwducredit', 'ipapwdlcredit', 'ipapwdocredit',
|
||||
},
|
||||
'default_privileges': {
|
||||
'Password Policy Readers',
|
||||
@@ -289,7 +305,9 @@ class pwpolicy(LDAPObject):
|
||||
'krbpwdhistorylength', 'krbpwdlockoutduration',
|
||||
'krbpwdmaxfailure', 'krbpwdmindiffchars', 'krbpwdminlength',
|
||||
'ipapwdmaxrepeat', 'ipapwdmaxsequence', 'ipapwddictcheck',
|
||||
'ipapwdusercheck', 'passwordgracelimit',
|
||||
'ipapwdusercheck', 'passwordgracelimit', 'ipapwddcredit',
|
||||
'ipapwducredit', 'ipapwdlcredit', 'ipapwdocredit',
|
||||
|
||||
},
|
||||
'replaces': [
|
||||
'(targetattr = "krbmaxpwdlife || krbminpwdlife || krbpwdhistorylength || krbpwdmindiffchars || krbpwdminlength || krbpwdmaxfailure || krbpwdfailurecountinterval || krbpwdlockoutduration")(target = "ldap:///cn=*,cn=$REALM,cn=kerberos,$SUFFIX")(version 3.0;acl "permission:Modify Group Password Policy";allow (write) groupdn = "ldap:///cn=Modify Group Password Policy,cn=permissions,cn=pbac,$SUFFIX";)',
|
||||
@@ -400,6 +418,42 @@ class pwpolicy(LDAPObject):
|
||||
doc=_('Check if the password contains the username'),
|
||||
default=False,
|
||||
),
|
||||
Int(
|
||||
'ipapwddcredit?',
|
||||
cli_name='dcredit',
|
||||
label=_('Digit Credit'),
|
||||
doc=_('The max credit for digits in the password.'),
|
||||
minvalue=-256,
|
||||
maxvalue=256,
|
||||
default=0,
|
||||
),
|
||||
Int(
|
||||
'ipapwducredit?',
|
||||
cli_name='ucredit',
|
||||
label=_('Uppercase Credit'),
|
||||
doc=_('The max credit for uppercase characters in the password.'),
|
||||
minvalue=-256,
|
||||
maxvalue=256,
|
||||
default=0,
|
||||
),
|
||||
Int(
|
||||
'ipapwdlcredit?',
|
||||
cli_name='lcredit',
|
||||
label=_('Lowercase Credit'),
|
||||
doc=_('The max credit for lowercase characters in the password.'),
|
||||
minvalue=-256,
|
||||
maxvalue=256,
|
||||
default=0,
|
||||
),
|
||||
Int(
|
||||
'ipapwdocredit?',
|
||||
cli_name='ocredit',
|
||||
label=_('Other Credit'),
|
||||
doc=_('The max credit for other characters in the password.'),
|
||||
minvalue=-256,
|
||||
maxvalue=256,
|
||||
default=0,
|
||||
),
|
||||
Int(
|
||||
'passwordgracelimit?',
|
||||
cli_name='gracelimit',
|
||||
@@ -455,14 +509,26 @@ class pwpolicy(LDAPObject):
|
||||
|
||||
def has_pwquality_set(entry):
|
||||
for attr in ['ipapwdmaxrepeat', 'ipapwdmaxsequence',
|
||||
'ipapwddictcheck', 'ipapwdusercheck']:
|
||||
'ipapwddictcheck', 'ipapwdusercheck',
|
||||
'ipapwddcredit', 'ipapwducredit',
|
||||
'ipapwdlcredit', 'ipapwdocredit',]:
|
||||
val = get_val(entry, attr)
|
||||
if val not in (False, 'FALSE', '0', 0, None):
|
||||
return True
|
||||
return False
|
||||
|
||||
def has_credit_set(entry):
|
||||
for attr in ['ipapwddcredit', 'ipapwducredit',
|
||||
'ipapwdlcredit', 'ipapwdocredit',]:
|
||||
val = get_val(entry, attr)
|
||||
if val not in (False, 'FALSE', '0', 0, None):
|
||||
return True
|
||||
return False
|
||||
|
||||
has_pwquality_value = False
|
||||
has_credit_value = False
|
||||
min_length = 0
|
||||
minclasses = False
|
||||
if not add:
|
||||
if len(keys) > 0:
|
||||
existing_entry = self.api.Command.pwpolicy_show(
|
||||
@@ -473,11 +539,15 @@ class pwpolicy(LDAPObject):
|
||||
existing_entry.update(entry_attrs)
|
||||
if existing_entry.get('krbpwdminlength'):
|
||||
min_length = int(get_val(existing_entry, 'krbpwdminlength'))
|
||||
if existing_entry.get('krbpwdmindiffchars'):
|
||||
minclasses = int(get_val(existing_entry, 'krbpwdmindiffchars'))
|
||||
has_pwquality_value = has_pwquality_set(existing_entry)
|
||||
has_credit_value = has_credit_set(existing_entry)
|
||||
else:
|
||||
if entry_attrs.get('krbpwdminlength'):
|
||||
min_length = int(get_val(entry_attrs, 'krbpwdminlength'))
|
||||
has_pwquality_value = has_pwquality_set(entry_attrs)
|
||||
has_credit_value = has_credit_set(entry_attrs)
|
||||
|
||||
if min_length < 6 and has_pwquality_value:
|
||||
raise errors.ValidationError(
|
||||
@@ -485,6 +555,12 @@ class pwpolicy(LDAPObject):
|
||||
error=_('Minimum length must be >= 6 if maxrepeat, '
|
||||
'maxsequence, dictcheck or usercheck are defined')
|
||||
)
|
||||
if minclasses > 0 and has_credit_value:
|
||||
raise errors.ValidationError(
|
||||
name='minclasses',
|
||||
error=_('Minimum number of classes cannot be used when '
|
||||
'character credit are defined')
|
||||
)
|
||||
|
||||
def validate_lifetime(self, entry_attrs, add=False, *keys):
|
||||
"""
|
||||
|
||||
@@ -55,15 +55,20 @@ class TestPWPolicy(IntegrationTest):
|
||||
),
|
||||
)
|
||||
|
||||
def reset_password(self, host, user=USER, password=PASSWORD):
|
||||
def reset_password(self, host, user=USER, password=PASSWORD,
|
||||
unlock=False):
|
||||
tasks.kinit_admin(host)
|
||||
host.run_command(
|
||||
['ipa', 'passwd', user],
|
||||
stdin_text='{password}\n{password}\n'.format(password=password),
|
||||
)
|
||||
if unlock:
|
||||
host.run_command(['ipa', 'user-unlock', user])
|
||||
tasks.kdestroy_all(host)
|
||||
|
||||
def set_pwpolicy(self, minlength=None, maxrepeat=None, maxsequence=None,
|
||||
dictcheck=None, usercheck=None, minclasses=None):
|
||||
dictcheck=None, usercheck=None, minclasses=None,
|
||||
dcredit=None, ucredit=None, lcredit=None, ocredit=None):
|
||||
tasks.kinit_admin(self.master)
|
||||
args = ["ipa", "pwpolicy-mod", POLICY]
|
||||
if minlength is not None:
|
||||
@@ -78,6 +83,14 @@ class TestPWPolicy(IntegrationTest):
|
||||
args.append("--usercheck={}".format(usercheck))
|
||||
if minclasses is not None:
|
||||
args.append("--minclasses={}".format(minclasses))
|
||||
if dcredit is not None:
|
||||
args.append("--dcredit={}".format(dcredit))
|
||||
if ucredit is not None:
|
||||
args.append("--ucredit={}".format(ucredit))
|
||||
if lcredit is not None:
|
||||
args.append("--lcredit={}".format(lcredit))
|
||||
if ocredit is not None:
|
||||
args.append("--ocredit={}".format(ocredit))
|
||||
self.master.run_command(args)
|
||||
|
||||
self.reset_password(self.master)
|
||||
@@ -92,7 +105,11 @@ class TestPWPolicy(IntegrationTest):
|
||||
"--dictcheck" ,"false",
|
||||
"--minlife", "0",
|
||||
"--minlength", "0",
|
||||
"--minclasses", "0",],
|
||||
"--minclasses", "0",
|
||||
"--dcredit", "0",
|
||||
"--ucredit", "0",
|
||||
"--lcredit", "0",
|
||||
"--ocredit", "0",],
|
||||
)
|
||||
# minlength => 6 is required for any of the libpwquality settings
|
||||
self.master.run_command(
|
||||
@@ -226,6 +243,89 @@ class TestPWPolicy(IntegrationTest):
|
||||
self.kinit_as_user(self.master, PASSWORD, valid)
|
||||
self.reset_password(self.master)
|
||||
|
||||
def test_credits(self, reset_pwpolicy):
|
||||
"""Test each credit individually. This would be all copy/paste
|
||||
if done individually so provide a config and set of good
|
||||
and bad passwords to test.
|
||||
|
||||
Reminder: a positive credit reduces minlength
|
||||
a zero credit is a no-op
|
||||
a negative credit is the minimum required
|
||||
"""
|
||||
def test_policy(good, bad, msg, **options):
|
||||
"""Validate the configuration.
|
||||
|
||||
Given the options in **options, the other arguments are:
|
||||
good: tuple of valid passwords
|
||||
bad: tuple of invalid passwords
|
||||
msg: tuple of expected messages. LDAP may return a different
|
||||
one than kinit.
|
||||
"""
|
||||
|
||||
tasks.kinit_admin(self.master)
|
||||
self.clean_pwpolicy()
|
||||
self.set_pwpolicy(**options)
|
||||
|
||||
for password in good:
|
||||
self.kinit_as_user(self.master, PASSWORD, password)
|
||||
self.reset_password(self.master)
|
||||
|
||||
for password in bad:
|
||||
result = self.kinit_as_user(self.master, PASSWORD, password,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
for message in msg:
|
||||
if message in result.stdout_text:
|
||||
break
|
||||
else:
|
||||
assert False, "%s not in %s" % (
|
||||
result.stdout_text, ', '.join(msg)
|
||||
)
|
||||
result = tasks.ldappasswd_user_change(USER, PASSWORD, password,
|
||||
self.master,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
for message in msg:
|
||||
if message in result.stdout_text:
|
||||
break
|
||||
else:
|
||||
assert False, "%s not in %s" % (
|
||||
result.stdout_text, ', '.join(msg)
|
||||
)
|
||||
|
||||
# hint: minlength == 6
|
||||
# dcredit
|
||||
test_policy(('Pass12', 'password1'), ('123', 'passw'),
|
||||
('Password is too short',), **{'dcredit': 1})
|
||||
test_policy(('Passw0rd1', 'password12'), ('Passwd1', 'password1'),
|
||||
('Password does not contain enough character',
|
||||
'Password contains too few digits',),
|
||||
**{'dcredit': -2})
|
||||
|
||||
# ucredit
|
||||
test_policy(('Passw', 'PASSWORD'), ('PAS', 'passw'),
|
||||
('Password is too short',), **{'ucredit': 1})
|
||||
test_policy(('Passw0rD1', 'PasswordS'), ('Passwd1', 'password1'),
|
||||
('Password does not contain enough character',
|
||||
'Password contains too few upper characters',),
|
||||
**{'ucredit': -2})
|
||||
|
||||
# lcredit
|
||||
test_policy(('PASSw', 'password'), ('pas', 'PASSW'),
|
||||
('Password is too short',), **{'lcredit': 1})
|
||||
test_policy(('Passw0rD1', 'PasswordS'), ('PASSWd1', 'PASSWoRD1'),
|
||||
('Password does not contain enough character',
|
||||
'Password contains too few lower characters',),
|
||||
**{'lcredit': -2})
|
||||
|
||||
# ocredit
|
||||
test_policy(('passw!', 'password#'), ('pas#', 'PAS$'),
|
||||
('Password is too short',), **{'ocredit': 1})
|
||||
test_policy(('Passw@rD1$', 'P&sswordS%'), ('passwd*', 'password^'),
|
||||
('Password does not contain enough character',
|
||||
'Password contains too few special characters',),
|
||||
**{'ocredit': -2})
|
||||
|
||||
def test_minlength_mod(self, reset_pwpolicy):
|
||||
"""Test that the pwpolicy minlength overrides our policy
|
||||
"""
|
||||
@@ -245,7 +345,7 @@ class TestPWPolicy(IntegrationTest):
|
||||
assert result.returncode != 0
|
||||
assert 'minlength' in result.stderr_text
|
||||
|
||||
# With any pwq value set, setting minlife < 6 should fail
|
||||
# With any pwq value set, setting minlength < 6 should fail
|
||||
for values in (('--maxrepeat', '4'),
|
||||
('--maxsequence', '4'),
|
||||
('--dictcheck', 'true'),
|
||||
@@ -388,6 +488,7 @@ class TestPWPolicy(IntegrationTest):
|
||||
dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
|
||||
user=USER, base_dn=str(self.master.domain.basedn))
|
||||
|
||||
tasks.kinit_admin(self.master)
|
||||
self.master.run_command(
|
||||
["ipa", "pwpolicy-mod", POLICY, "--gracelimit", "0", ],
|
||||
)
|
||||
|
||||
+137
-75
@@ -393,6 +393,128 @@ cleanup:
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns True/False if any of the pwquality config is set
|
||||
*
|
||||
* A history element is a base64 string of a hash+salt buffer, prepended
|
||||
* by the hash type enclosed within curly braces.
|
||||
*
|
||||
* @param policy The policy to check against
|
||||
*
|
||||
* @return a boolean
|
||||
*/
|
||||
static bool has_pwquality_rules(struct ipapwd_policy *p)
|
||||
{
|
||||
return (p->max_repeat || p->max_sequence ||
|
||||
p->dictcheck || p->usercheck ||
|
||||
p->dcredit || p->ucredit ||
|
||||
p->lcredit || p->ocredit );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Runs the libpwquality checks as configured in the policy
|
||||
*
|
||||
* If any libpwquality policies are defined then the password is
|
||||
* passed into libpwquality for validation. The result is converted
|
||||
* into a IPA password policy response and returned.
|
||||
*
|
||||
* @param policy The policy to check against
|
||||
*
|
||||
* @return int of the result
|
||||
*/
|
||||
|
||||
#if defined(USE_PWQUALITY)
|
||||
static int call_pwquality(const struct ipapwd_policy *policy,
|
||||
const char *password,
|
||||
const char *user)
|
||||
{
|
||||
int entropy = 0;
|
||||
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
|
||||
void *auxerror;
|
||||
struct pwquality_settings *pwq = pwquality_default_settings();
|
||||
|
||||
if (!pwq) {
|
||||
syslog(LOG_ERR, "pwquality defaults failed");
|
||||
return IPAPWD_POLICY_ERROR;
|
||||
}
|
||||
|
||||
/* Call libpwquality */
|
||||
openlog(NULL, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
|
||||
if (pwq == NULL) {
|
||||
syslog(LOG_ERR, "Not able to set pwquality defaults\n");
|
||||
return IPAPWD_POLICY_ERROR;
|
||||
}
|
||||
if (policy->min_pwd_length < 6)
|
||||
syslog(LOG_WARNING, "password policy min length is < 6. Will be enforced as 6\n");
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_MIN_LENGTH, policy->min_pwd_length);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_MAX_REPEAT, policy->max_repeat);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_MAX_SEQUENCE, policy->max_sequence);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, policy->dictcheck);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_USER_CHECK, policy->usercheck);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_DIG_CREDIT, policy->dcredit);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_UP_CREDIT, policy->ucredit);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_LOW_CREDIT, policy->lcredit);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_OTH_CREDIT, policy->ocredit);
|
||||
|
||||
entropy = pwquality_check(pwq, password, NULL, user, &auxerror);
|
||||
pwquality_free_settings(pwq);
|
||||
|
||||
#ifdef TEST
|
||||
if (user != NULL) {
|
||||
fprintf(stderr, "Checking password for %s\n", user);
|
||||
} else {
|
||||
fprintf(stderr, "No user provided\n");
|
||||
}
|
||||
|
||||
fprintf(stderr, "min length %d\n", policy->min_pwd_length);
|
||||
fprintf(stderr, "max repeat %d\n", policy->max_repeat);
|
||||
fprintf(stderr, "max sequence %d\n", policy->max_sequence);
|
||||
fprintf(stderr, "dict check %d\n", policy->dictcheck);
|
||||
fprintf(stderr, "user check %d\n", policy->usercheck);
|
||||
fprintf(stderr, "digit credit %d\n", policy->dcredit);
|
||||
fprintf(stderr, "upper credit %d\n", policy->ucredit);
|
||||
fprintf(stderr, "lower credit %d\n", policy->lcredit);
|
||||
fprintf(stderr, "other credit %d\n", policy->ocredit);
|
||||
#endif
|
||||
|
||||
if (entropy < 0) {
|
||||
#ifdef TEST
|
||||
fprintf(stderr, "Bad password '%s': %s\n", password, pwquality_strerror(buf, sizeof(buf), entropy, auxerror));
|
||||
#endif
|
||||
syslog(LOG_ERR, "Password is rejected with error %d: %s\n", entropy, pwquality_strerror(buf, sizeof(buf), entropy, auxerror));
|
||||
switch (entropy) {
|
||||
case PWQ_ERROR_MIN_LENGTH:
|
||||
return IPAPWD_POLICY_PWD_TOO_SHORT;
|
||||
case PWQ_ERROR_PALINDROME:
|
||||
return IPAPWD_POLICY_PWD_PALINDROME;
|
||||
case PWQ_ERROR_MAX_CONSECUTIVE:
|
||||
return IPAPWD_POLICY_PWD_CONSECUTIVE;
|
||||
case PWQ_ERROR_MAX_SEQUENCE:
|
||||
return IPAPWD_POLICY_PWD_SEQUENCE;
|
||||
case PWQ_ERROR_CRACKLIB_CHECK:
|
||||
return IPAPWD_POLICY_PWD_DICT_WORD;
|
||||
case PWQ_ERROR_USER_CHECK:
|
||||
return IPAPWD_POLICY_PWD_USER;
|
||||
case PWQ_ERROR_MIN_DIGITS:
|
||||
return IPAPWD_POLICY_PWD_MIN_DIGITS;
|
||||
case PWQ_ERROR_MIN_UPPERS:
|
||||
return IPAPWD_POLICY_PWD_MIN_UPPERS;
|
||||
case PWQ_ERROR_MIN_LOWERS:
|
||||
return IPAPWD_POLICY_PWD_MIN_LOWERS;
|
||||
case PWQ_ERROR_MIN_OTHERS:
|
||||
return IPAPWD_POLICY_PWD_MIN_OTHERS;
|
||||
default:
|
||||
return IPAPWD_POLICY_PWD_COMPLEXITY;
|
||||
}
|
||||
#ifdef TEST
|
||||
} else {
|
||||
fprintf(stderr, "Password '%s' is ok, entropy is %d\n", password, entropy);
|
||||
#endif
|
||||
}
|
||||
return IPAPWD_POLICY_OK;
|
||||
}
|
||||
#endif /* USE_PWQUALITY */
|
||||
|
||||
/**
|
||||
* @brief Funtion used to check password policies on a password change.
|
||||
*
|
||||
@@ -417,13 +539,6 @@ int ipapwd_check_policy(struct ipapwd_policy *policy,
|
||||
{
|
||||
int pwdlen, blen;
|
||||
int ret;
|
||||
#if defined(USE_PWQUALITY)
|
||||
pwquality_settings_t *pwq;
|
||||
int check_pwquality = 0;
|
||||
int entropy = 0;
|
||||
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
|
||||
void *auxerror;
|
||||
#endif
|
||||
|
||||
if (!policy || !password) {
|
||||
return IPAPWD_POLICY_ERROR;
|
||||
@@ -453,12 +568,6 @@ int ipapwd_check_policy(struct ipapwd_policy *policy,
|
||||
|
||||
pwdlen = strlen_utf8(password, &blen);
|
||||
|
||||
if (policy->min_pwd_length) {
|
||||
if (pwdlen < policy->min_pwd_length) {
|
||||
return IPAPWD_POLICY_PWD_TOO_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (policy->min_complexity) {
|
||||
int num_digits = 0;
|
||||
int num_alphas = 0;
|
||||
@@ -541,70 +650,19 @@ int ipapwd_check_policy(struct ipapwd_policy *policy,
|
||||
* because there are a number of checks that don't have knobs
|
||||
* so preserve the previous behavior.
|
||||
*/
|
||||
check_pwquality = policy->max_repeat + policy->max_sequence + policy->dictcheck + policy->usercheck;
|
||||
|
||||
if (check_pwquality > 0) {
|
||||
/* Call libpwquality */
|
||||
openlog(NULL, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
|
||||
pwq = pwquality_default_settings();
|
||||
if (pwq == NULL) {
|
||||
syslog(LOG_ERR, "Not able to set pwquality defaults\n");
|
||||
return IPAPWD_POLICY_ERROR;
|
||||
}
|
||||
if (policy->min_pwd_length < 6)
|
||||
syslog(LOG_WARNING, "password policy min length is < 6. Will be enforced as 6\n");
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_MIN_LENGTH, policy->min_pwd_length);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_MAX_REPEAT, policy->max_repeat);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_MAX_SEQUENCE, policy->max_sequence);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, policy->dictcheck);
|
||||
pwquality_set_int_value(pwq, PWQ_SETTING_USER_CHECK, policy->usercheck);
|
||||
|
||||
entropy = pwquality_check(pwq, password, NULL, user, &auxerror);
|
||||
pwquality_free_settings(pwq);
|
||||
|
||||
#ifdef TEST
|
||||
if (user != NULL) {
|
||||
fprintf(stderr, "Checking password for %s\n", user);
|
||||
} else {
|
||||
fprintf(stderr, "No user provided\n");
|
||||
}
|
||||
|
||||
fprintf(stderr, "min length %d\n", policy->min_pwd_length);
|
||||
fprintf(stderr, "max repeat %d\n", policy->max_repeat);
|
||||
fprintf(stderr, "max sequence %d\n", policy->max_sequence);
|
||||
fprintf(stderr, "dict check %d\n", policy->dictcheck);
|
||||
fprintf(stderr, "user check %d\n", policy->usercheck);
|
||||
#endif
|
||||
|
||||
if (entropy < 0) {
|
||||
#ifdef TEST
|
||||
fprintf(stderr, "Bad password '%s': %s\n", password, pwquality_strerror(buf, sizeof(buf), entropy, auxerror));
|
||||
#endif
|
||||
syslog(LOG_ERR, "Password is rejected with error %d: %s\n", entropy, pwquality_strerror(buf, sizeof(buf), entropy, auxerror));
|
||||
switch (entropy) {
|
||||
case PWQ_ERROR_MIN_LENGTH:
|
||||
if (has_pwquality_rules(policy)) {
|
||||
ret = call_pwquality(policy, password, user);
|
||||
if (ret != IPAPWD_POLICY_OK)
|
||||
return ret;
|
||||
} else
|
||||
#endif /* USE_PWQUALITY */
|
||||
{
|
||||
if (policy->min_pwd_length) {
|
||||
if (pwdlen < policy->min_pwd_length) {
|
||||
return IPAPWD_POLICY_PWD_TOO_SHORT;
|
||||
case PWQ_ERROR_PALINDROME:
|
||||
return IPAPWD_POLICY_PWD_PALINDROME;
|
||||
case PWQ_ERROR_MAX_CONSECUTIVE:
|
||||
return IPAPWD_POLICY_PWD_CONSECUTIVE;
|
||||
case PWQ_ERROR_MAX_SEQUENCE:
|
||||
return IPAPWD_POLICY_PWD_SEQUENCE;
|
||||
case PWQ_ERROR_CRACKLIB_CHECK:
|
||||
return IPAPWD_POLICY_PWD_DICT_WORD;
|
||||
case PWQ_ERROR_USER_CHECK:
|
||||
return IPAPWD_POLICY_PWD_USER;
|
||||
default:
|
||||
return IPAPWD_POLICY_PWD_COMPLEXITY;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
} else {
|
||||
fprintf(stderr, "Password '%s' is ok, entropy is %d\n", password, entropy);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif /* USE_PWQUALITY */
|
||||
|
||||
if (pwd_history) {
|
||||
char *hash;
|
||||
@@ -634,13 +692,17 @@ char * IPAPWD_ERROR_STRINGS[] = {
|
||||
"Password contains a monotonic sequence",
|
||||
"Password is based on a dictionary word",
|
||||
"Password is a palindrone",
|
||||
"Password contains username"
|
||||
"Password contains username",
|
||||
"Password contains too few digits",
|
||||
"Password contains too few upper characters",
|
||||
"Password contains too few lower characters",
|
||||
"Password contains too few special characters"
|
||||
};
|
||||
|
||||
char * IPAPWD_ERROR_STRING_GENERAL = "Password does not meet the policy requirements";
|
||||
|
||||
char * ipapwd_error2string(enum ipapwd_error err) {
|
||||
if (err < 0 || err > IPAPWD_POLICY_PWD_USER) {
|
||||
if (err < 0 || err > IPAPWD_POLICY_PWD_MIN_OTHERS) {
|
||||
/* IPAPWD_POLICY_ERROR or out of boundary, return general error */
|
||||
return IPAPWD_ERROR_STRING_GENERAL;
|
||||
}
|
||||
|
||||
+11
-1
@@ -46,7 +46,11 @@ enum ipapwd_error {
|
||||
IPAPWD_POLICY_PWD_SEQUENCE = 7,
|
||||
IPAPWD_POLICY_PWD_DICT_WORD = 8,
|
||||
IPAPWD_POLICY_PWD_PALINDROME = 9,
|
||||
IPAPWD_POLICY_PWD_USER = 10
|
||||
IPAPWD_POLICY_PWD_USER = 10,
|
||||
IPAPWD_POLICY_PWD_MIN_DIGITS = 11,
|
||||
IPAPWD_POLICY_PWD_MIN_UPPERS = 12,
|
||||
IPAPWD_POLICY_PWD_MIN_LOWERS = 13,
|
||||
IPAPWD_POLICY_PWD_MIN_OTHERS = 14
|
||||
};
|
||||
|
||||
struct ipapwd_policy {
|
||||
@@ -58,11 +62,17 @@ struct ipapwd_policy {
|
||||
int max_fail;
|
||||
int failcnt_interval;
|
||||
int lockout_duration;
|
||||
/* begin libpwquality options */
|
||||
int max_repeat;
|
||||
int max_sequence;
|
||||
int max_classrepeat;
|
||||
int dictcheck;
|
||||
int usercheck;
|
||||
int dcredit;
|
||||
int ucredit;
|
||||
int lcredit;
|
||||
int ocredit;
|
||||
/* end libpwquality options */
|
||||
};
|
||||
|
||||
time_t ipapwd_gentime_to_time_t(char *timestr);
|
||||
|
||||
+103
-10
@@ -16,7 +16,8 @@ static void
|
||||
set_policy(struct ipapwd_policy *policy,
|
||||
int min_pwd_length, int min_diff_chars, int max_repeat,
|
||||
int max_sequence, int max_class_repeat, int dict_check,
|
||||
int user_check)
|
||||
int user_check, int dcredit, int ucredit, int lcredit,
|
||||
int ocredit)
|
||||
|
||||
{
|
||||
/* defaults for things we aren't testing */
|
||||
@@ -32,6 +33,10 @@ set_policy(struct ipapwd_policy *policy,
|
||||
policy->max_classrepeat = max_class_repeat;
|
||||
policy->dictcheck = dict_check;
|
||||
policy->usercheck = user_check;
|
||||
policy->dcredit = dcredit;
|
||||
policy->ucredit = ucredit;
|
||||
policy->lcredit = lcredit;
|
||||
policy->ocredit = ocredit;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@@ -41,7 +46,7 @@ int main(int argc, const char *argv[]) {
|
||||
struct ipapwd_policy policy = {0};
|
||||
|
||||
/* No policy applied */
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "Secret123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
assert(ipapwd_check_policy(&policy, "password", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
assert(ipapwd_check_policy(&policy, "abcddcba", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
@@ -50,7 +55,7 @@ int main(int argc, const char *argv[]) {
|
||||
assert(ipapwd_check_policy(&policy, "abc", NULL, 3, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
|
||||
/* Max repeats of 1 */
|
||||
set_policy(&policy, 0, 0, 1, 0, 0, 0, 0);
|
||||
set_policy(&policy, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "password", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_CONSECUTIVE);
|
||||
assert(ipapwd_check_policy(&policy, "Assembly", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_CONSECUTIVE);
|
||||
|
||||
@@ -58,37 +63,125 @@ int main(int argc, const char *argv[]) {
|
||||
assert(ipapwd_check_policy(&policy, "abc", NULL, 3, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_TOO_SHORT);
|
||||
|
||||
/* Max repeats of 2 */
|
||||
set_policy(&policy, 0, 0, 2, 0, 0, 0, 0);
|
||||
set_policy(&policy, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "password", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
assert(ipapwd_check_policy(&policy, "Assembly", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
assert(ipapwd_check_policy(&policy, "permisssive", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_CONSECUTIVE);
|
||||
|
||||
/* Max sequence of 1 */
|
||||
set_policy(&policy, 0, 0, 0, 1, 0, 0, 0);
|
||||
set_policy(&policy, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "abacab", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_SEQUENCE);
|
||||
assert(ipapwd_check_policy(&policy, "AbacAb", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_SEQUENCE);
|
||||
|
||||
/* Max sequence of 2 */
|
||||
set_policy(&policy, 0, 0, 0, 2, 0, 0, 0);
|
||||
set_policy(&policy, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "AbacAb", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
assert(ipapwd_check_policy(&policy, "abacabc", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_SEQUENCE);
|
||||
|
||||
/* Palindrone */
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0); /* Note there is no policy */
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); /* Note there is no policy */
|
||||
assert(ipapwd_check_policy(&policy, "password", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
assert(ipapwd_check_policy(&policy, "abccba", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 3, 0, 0, 0, 0); /* Set anything */
|
||||
set_policy(&policy, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0); /* Set anything */
|
||||
assert(ipapwd_check_policy(&policy, "abccba", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_PALINDROME);
|
||||
|
||||
/* Dictionary check */
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 1, 0);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "password", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_DICT_WORD);
|
||||
assert(ipapwd_check_policy(&policy, "Secret123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_DICT_WORD);
|
||||
|
||||
/* User check */
|
||||
assert(ipapwd_check_policy(&policy, "userPDQ123", "user", 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 1);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "userPDQ123", "user", 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_USER);
|
||||
|
||||
/* Digit check.
|
||||
* Negative == minimum # of digits required
|
||||
* Zero == skip check
|
||||
* Positive == amount to add towards min length
|
||||
*/
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "Secret123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, -19, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "Secret123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_MIN_DIGITS);
|
||||
|
||||
/* dcredit > 0 gives a "credit" to a shorter password for having
|
||||
* complexity so allows a shorter than minimum length password.
|
||||
* Reminder that with libpwquality the minimum password len is
|
||||
* hardcoded at 6.
|
||||
*/
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "abcd1", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "ab21", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
|
||||
/* Verify that no credits are added automatically. We need to set some
|
||||
* pwquality option in order to validate it, so set length.
|
||||
*/
|
||||
set_policy(&policy, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "abcd1", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_TOO_SHORT);
|
||||
|
||||
/* Upper check.
|
||||
* Negative == minimum # of uppers required
|
||||
* Zero == skip check
|
||||
* Positive == amount to add towards min length
|
||||
*/
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "Secret123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, -19, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "Secret123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_MIN_UPPERS);
|
||||
|
||||
/* ucredit > 0 gives a "credit" to a shorter password for having
|
||||
* complexity so allows a shorter than minimum length password.
|
||||
* Reminder that with libpwquality the minimum password len is
|
||||
* hardcoded at 6.
|
||||
*/
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "abcdE", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "abDE", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
|
||||
/* Verify that no credits are added automatically. We need to set some
|
||||
* pwquality option in order to validate it, so set length.
|
||||
*/
|
||||
set_policy(&policy, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "abcdE", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_TOO_SHORT);
|
||||
|
||||
/* Lower check.
|
||||
* Negative == minimum # of uppers required
|
||||
* Zero == skip check
|
||||
* Positive == amount to add towards min length
|
||||
*/
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0);
|
||||
assert(ipapwd_check_policy(&policy, "SECREt123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 0, -19, 0);
|
||||
assert(ipapwd_check_policy(&policy, "SECREt123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_MIN_LOWERS);
|
||||
|
||||
/* lcredit > 0 gives a "credit" to a shorter password for having
|
||||
* complexity so allows a shorter than minimum length password.
|
||||
* Reminder that with libpwquality the minimum password len is
|
||||
* hardcoded at 6.
|
||||
*/
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
|
||||
assert(ipapwd_check_policy(&policy, "ABCEe", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0);
|
||||
assert(ipapwd_check_policy(&policy, "ABcd", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
|
||||
/* Verify that no credits are added automatically. We need to set some
|
||||
* pwquality option in order to validate it, so set length.
|
||||
*/
|
||||
set_policy(&policy, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert(ipapwd_check_policy(&policy, "ABCDE", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_TOO_SHORT);
|
||||
|
||||
/* Mixed credit checks */
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, 0);
|
||||
assert(ipapwd_check_policy(&policy, "SecreT123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_OK);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, 0);
|
||||
assert(ipapwd_check_policy(&policy, "SECREt123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_MIN_LOWERS);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, 0);
|
||||
assert(ipapwd_check_policy(&policy, "Secret123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_MIN_UPPERS);
|
||||
set_policy(&policy, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1);
|
||||
assert(ipapwd_check_policy(&policy, "SecreT123", NULL, 0, 0, 0, 0, NULL) == IPAPWD_POLICY_PWD_MIN_OTHERS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user