From 36c593d32d092ff1b4bec1595ebe1ed0726f5240 Mon Sep 17 00:00:00 2001 From: Simon Pichugin Date: Apr 08 2020 09:56:20 +0000 Subject: Issue 50875 - Refactor passwordUserAttributes's and passwordBadWords's code Bug Description: Searches on cn=config takes values with spaces and makes multiple attributes out of them. If we set passwordUserAttributes to "cn uid givenname", it will transform it in a multi-valued attribute. Fix Description: Change passwordUserAttributes's and passwordBadWords's type to CONFIG_STRING (it was CONFIG_CHARRAY). Add an additional parameter to store the array (and use it in pw.c). The string and array processing is similar to nsslapd-allowed-sasl-mechanisms. Add tests for both attributes. https://pagure.io/389-ds-base/issue/50875 Reviewed by: mreynolds, tbordaz, firstyear (Thanks!) --- diff --git a/dirsrvtests/tests/suites/password/pwdPolicy_syntax_test.py b/dirsrvtests/tests/suites/password/pwdPolicy_syntax_test.py index 82d1a97..291a6fd 100644 --- a/dirsrvtests/tests/suites/password/pwdPolicy_syntax_test.py +++ b/dirsrvtests/tests/suites/password/pwdPolicy_syntax_test.py @@ -48,6 +48,7 @@ def create_user(topology_st): 'gidNumber': '4000', 'homeDirectory': '/home/user', 'description': 'd_e_s_c', + 'loginShell': USER_RDN, 'userPassword': PASSWORD }) @@ -61,7 +62,8 @@ def setPolicy(inst, attr, value): value = str(value) inst.config.set(attr, value) - inst.simple_bind_s(USER_DN, PASSWORD) + policy = inst.config.get_attr_val_utf8(attr) + assert policy == value def resetPasswd(inst): @@ -84,6 +86,7 @@ def tryPassword(inst, policy_attr, value, reset_value, pw_bad, pw_good, msg): """ setPolicy(inst, policy_attr, value) + inst.simple_bind_s(USER_DN, PASSWORD) users = UserAccounts(inst, DEFAULT_SUFFIX) user = users.get(USER_RDN) try: @@ -250,17 +253,17 @@ def test_basic(topology_st, create_user, password_policy): # Sequences tryPassword(topology_st.standalone, 'passwordMaxSequence', 3, 0, 'Za1_1234', - '13_#Kad472h', 'Max montonic sequence is not allowed') + '13_#Kad472h', 'Max monotonic sequence is not allowed') tryPassword(topology_st.standalone, 'passwordMaxSequence', 3, 0, 'Za1_4321', - '13_#Kad472h', 'Max montonic sequence is not allowed') + '13_#Kad472h', 'Max monotonic sequence is not allowed') tryPassword(topology_st.standalone, 'passwordMaxSequence', 3, 0, 'Za1_abcd', - '13_#Kad472h', 'Max montonic sequence is not allowed') + '13_#Kad472h', 'Max monotonic sequence is not allowed') tryPassword(topology_st.standalone, 'passwordMaxSequence', 3, 0, 'Za1_dcba', - '13_#Kad472h', 'Max montonic sequence is not allowed') + '13_#Kad472h', 'Max monotonic sequence is not allowed') # Sequence Sets tryPassword(topology_st.standalone, 'passwordMaxSeqSets', 2, 0, 'Za1_123--123', - '13_#Kad472h', 'Max montonic sequence is not allowed') + '13_#Kad472h', 'Max monotonic sequence is not allowed') # Max characters in a character class tryPassword(topology_st.standalone, 'passwordMaxClassChars', 3, 0, 'Za1_9376', @@ -273,16 +276,94 @@ def test_basic(topology_st, create_user, password_policy): '13_#Kad472h', 'Too may consecutive characters from the same class') # Bad words - tryPassword(topology_st.standalone, 'passwordBadWords', 'redhat fedora', 'none', 'Za1_redhat', - '13_#Kad472h', 'Too may consecutive characters from the same class') - tryPassword(topology_st.standalone, 'passwordBadWords', 'redhat fedora', 'none', 'Za1_fedora', + tryPassword(topology_st.standalone, 'passwordBadWords', 'redhat', 'none', 'Za1_redhat', '13_#Kad472h', 'Too may consecutive characters from the same class') # User Attributes tryPassword(topology_st.standalone, 'passwordUserAttributes', 'description', 0, 'Za1_d_e_s_c', '13_#Kad472h', 'Password found in user entry') - log.info('pwdPolicy tests PASSED') + +@pytest.mark.bz1816857 +@pytest.mark.ds50875 +@pytest.mark.skipif(ds_is_older("1.4.1.18"), reason="Not implemented") +def test_config_set_few_user_attributes(topology_st, create_user, password_policy): + """Test that we can successfully set multiple values to passwordUserAttributes + + :id: 188e0aee-6e29-4857-910c-27d5606f8c08 + :setup: Standalone instance + :steps: + 1. Set passwordUserAttributes to "description loginShell" + 2. Verify passwordUserAttributes has the values + 3. Verify passwordUserAttributes enforced the policy + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + """ + + standalone = topology_st.standalone + + standalone.log.info('Set passwordUserAttributes to "description loginShell"') + standalone.config.set('passwordUserAttributes', 'description loginshell') + + standalone.restart() + + standalone.log.info("Verify passwordUserAttributes has the values") + user_attrs = standalone.config.get_attr_val_utf8('passwordUserAttributes') + assert "description" in user_attrs + assert "loginshell" in user_attrs + standalone.log.info("Reset passwordUserAttributes") + standalone.config.remove_all('passwordUserAttributes') + + standalone.log.info("Verify passwordUserAttributes enforced the policy") + attributes = ['description, loginShell', 'description,loginShell', 'description loginShell'] + values = ['Za1_d_e_s_c', f'Za1_{USER_RDN}', f'Za1_d_e_s_c{USER_RDN}'] + for attr in attributes: + for value in values: + tryPassword(standalone, 'passwordUserAttributes', attr, 0, value, + '13_#Kad472h', 'Password found in user entry') + + +@pytest.mark.bz1816857 +@pytest.mark.ds50875 +@pytest.mark.skipif(ds_is_older("1.4.1.18"), reason="Not implemented") +def test_config_set_few_bad_words(topology_st, create_user, password_policy): + """Test that we can successfully set multiple values to passwordBadWords + + :id: 2977094c-921c-4b2f-af91-4c7a45ded48b + :setup: Standalone instance + :steps: + 1. Set passwordBadWords to "fedora redhat" + 2. Verify passwordBadWords has the values + 3. Verify passwordBadWords enforced the policy + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + """ + + standalone = topology_st.standalone + + standalone.log.info('Set passwordBadWords to "fedora redhat"') + standalone.config.set('passwordBadWords', 'fedora redhat') + + standalone.restart() + + standalone.log.info("Verify passwordBadWords has the values") + user_attrs = standalone.config.get_attr_val_utf8('passwordBadWords') + assert "fedora" in user_attrs + assert "redhat" in user_attrs + standalone.log.info("Reset passwordBadWords") + standalone.config.remove_all('passwordBadWords') + + standalone.log.info("Verify passwordBadWords enforced the policy") + attributes = ['redhat, fedora', 'redhat,fedora', 'redhat fedora'] + values = ['Za1_redhat_fedora', 'Za1_fedora', 'Za1_redhat'] + for attr in attributes: + for value in values: + tryPassword(standalone, 'passwordBadWords', attr, 'none', value, + '13_#Kad472h', 'Too may consecutive characters from the same class') if __name__ == '__main__': diff --git a/ldap/servers/slapd/back-ldbm/vlv.c b/ldap/servers/slapd/back-ldbm/vlv.c index b50bb5a..ef7f26e 100644 --- a/ldap/servers/slapd/back-ldbm/vlv.c +++ b/ldap/servers/slapd/back-ldbm/vlv.c @@ -1962,20 +1962,6 @@ vlv_find_index_by_filter(struct backend *be, const char *base, Slapi_Filter *f) return vlv_find_index_by_filter_txn(be, base, f, NULL); } -/* replace c with c2 in string -- probably exists somewhere but I can't find it slapi maybe? */ - -static void -replace_char(char *name, char c, char c2) -{ - int x; - - for (x = 0; name[x] != '\0'; x++) { - if (c == name[x]) { - name[x] = c2; - } - } -} - /* similar to what the console GUI does */ char * diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c index f107815..0d3d9a9 100644 --- a/ldap/servers/slapd/libglobs.c +++ b/ldap/servers/slapd/libglobs.c @@ -173,7 +173,6 @@ typedef enum { static int32_t config_set_onoff(const char *attrname, char *value, int32_t *configvalue, char *errorbuf, int apply); static int config_set_schemareplace(const char *attrname, char *value, char *errorbuf, int apply); -static void remove_commas(char *str); static int invalid_sasl_mech(char *str); @@ -535,12 +534,12 @@ static struct config_get_and_set {CONFIG_PW_USERATTRS_ATTRIBUTE, config_set_pw_user_attrs, NULL, 0, (void **)&global_slapdFrontendConfig.pw_policy.pw_cmp_attrs, - CONFIG_CHARRAY, NULL, NULL, NULL}, + CONFIG_STRING, NULL, "", NULL}, /* password bad work list */ {CONFIG_PW_BAD_WORDS_ATTRIBUTE, config_set_pw_bad_words, NULL, 0, (void **)&global_slapdFrontendConfig.pw_policy.pw_bad_words, - CONFIG_CHARRAY, NULL, NULL, NULL}, + CONFIG_STRING, NULL, "", NULL}, /* password max sequence */ {CONFIG_PW_MAX_SEQ_ATTRIBUTE, config_set_pw_max_seq, NULL, 0, @@ -2946,70 +2945,118 @@ config_set_pw_dict_path(const char *attrname, char *value, char *errorbuf, int a return retVal; } +char ** +config_get_pw_user_attrs_array(void) +{ + /* + * array of password user attributes. If is null, returns NULL thanks to ch_array_dup. + * Caller must free! + */ + char **retVal; + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + + CFG_LOCK_READ(slapdFrontendConfig); + retVal = slapi_ch_array_dup(slapdFrontendConfig->pw_policy.pw_cmp_attrs_array); + CFG_UNLOCK_READ(slapdFrontendConfig); + + return retVal; +} + int32_t config_set_pw_user_attrs(const char *attrname, char *value, char *errorbuf, int apply) { int retVal = LDAP_SUCCESS; - char **attrs = NULL; slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); if (config_value_is_null(attrname, value, errorbuf, 0)) { value = NULL; } if (apply) { - if (value) { + /* During a reset, the value is "", so we have to handle this case. */ + if (strcmp(value, "") != 0) { + char **nval_array; + char *nval = slapi_ch_strdup(value); + /* A separate variable is used because slapi_str2charray_ext can change it and nval'd become corrupted */ + char *tmp_array_nval = slapi_ch_strdup(nval); + + /* We should accept comma-separated lists but slapi_str2charray_ext will process only space-separated */ + replace_char(tmp_array_nval, ',', ' '); /* Take list of attributes and break it up into a char array */ - char *attr = NULL; - char *token = NULL; - char *next = NULL; - - token = slapi_ch_strdup(value); - for (attr = ldap_utf8strtok_r(token, " ", &next); attr != NULL; - attr = ldap_utf8strtok_r(NULL, " ", &next)) - { - slapi_ch_array_add(&attrs, slapi_ch_strdup(attr)); - } - slapi_ch_free_string(&token); - } + nval_array = slapi_str2charray_ext(tmp_array_nval, " ", 0); + slapi_ch_free_string(&tmp_array_nval); - CFG_LOCK_WRITE(slapdFrontendConfig); - slapi_ch_array_free(slapdFrontendConfig->pw_policy.pw_cmp_attrs); - slapdFrontendConfig->pw_policy.pw_cmp_attrs = attrs; - CFG_UNLOCK_WRITE(slapdFrontendConfig); + CFG_LOCK_WRITE(slapdFrontendConfig); + slapi_ch_free_string(&slapdFrontendConfig->pw_policy.pw_cmp_attrs); + slapi_ch_array_free(slapdFrontendConfig->pw_policy.pw_cmp_attrs_array); + slapdFrontendConfig->pw_policy.pw_cmp_attrs = nval; + slapdFrontendConfig->pw_policy.pw_cmp_attrs_array = nval_array; + CFG_UNLOCK_WRITE(slapdFrontendConfig); + } else { + CFG_LOCK_WRITE(slapdFrontendConfig); + slapi_ch_free_string(&slapdFrontendConfig->pw_policy.pw_cmp_attrs); + slapi_ch_array_free(slapdFrontendConfig->pw_policy.pw_cmp_attrs_array); + slapdFrontendConfig->pw_policy.pw_cmp_attrs = NULL; + slapdFrontendConfig->pw_policy.pw_cmp_attrs_array = NULL; + CFG_UNLOCK_WRITE(slapdFrontendConfig); + } } return retVal; } +char ** +config_get_pw_bad_words_array(void) +{ + /* + * array of words to reject. If is null, returns NULL thanks to ch_array_dup. + * Caller must free! + */ + char **retVal; + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + + CFG_LOCK_READ(slapdFrontendConfig); + retVal = slapi_ch_array_dup(slapdFrontendConfig->pw_policy.pw_bad_words_array); + CFG_UNLOCK_READ(slapdFrontendConfig); + + return retVal; +} + int32_t config_set_pw_bad_words(const char *attrname, char *value, char *errorbuf, int apply) { int retVal = LDAP_SUCCESS; - char **words = NULL; slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); if (config_value_is_null(attrname, value, errorbuf, 0)) { value = NULL; } if (apply) { - if (value) { + /* During a reset, the value is "", so we have to handle this case. */ + if (strcmp(value, "") != 0) { + char **nval_array; + char *nval = slapi_ch_strdup(value); + /* A separate variable is used because slapi_str2charray_ext can change it and nval'd become corrupted */ + char *tmp_array_nval = slapi_ch_strdup(nval); + + /* We should accept comma-separated lists but slapi_str2charray_ext will process only space-separated */ + replace_char(tmp_array_nval, ',', ' '); /* Take list of attributes and break it up into a char array */ - char *word = NULL; - char *token = NULL; - char *next = NULL; - - token = slapi_ch_strdup(value); - for (word = ldap_utf8strtok_r(token, " ", &next); word != NULL; - word = ldap_utf8strtok_r(NULL, " ", &next)) - { - slapi_ch_array_add(&words, slapi_ch_strdup(word)); - } - slapi_ch_free_string(&token); - } + nval_array = slapi_str2charray_ext(tmp_array_nval, " ", 0); + slapi_ch_free_string(&tmp_array_nval); - CFG_LOCK_WRITE(slapdFrontendConfig); - slapi_ch_array_free(slapdFrontendConfig->pw_policy.pw_bad_words); - slapdFrontendConfig->pw_policy.pw_bad_words = words; - CFG_UNLOCK_WRITE(slapdFrontendConfig); + CFG_LOCK_WRITE(slapdFrontendConfig); + slapi_ch_free_string(&slapdFrontendConfig->pw_policy.pw_bad_words); + slapi_ch_array_free(slapdFrontendConfig->pw_policy.pw_bad_words_array); + slapdFrontendConfig->pw_policy.pw_bad_words = nval; + slapdFrontendConfig->pw_policy.pw_bad_words_array = nval_array; + CFG_UNLOCK_WRITE(slapdFrontendConfig); + } else { + CFG_LOCK_WRITE(slapdFrontendConfig); + slapi_ch_free_string(&slapdFrontendConfig->pw_policy.pw_bad_words); + slapi_ch_array_free(slapdFrontendConfig->pw_policy.pw_bad_words_array); + slapdFrontendConfig->pw_policy.pw_bad_words = NULL; + slapdFrontendConfig->pw_policy.pw_bad_words_array = NULL; + CFG_UNLOCK_WRITE(slapdFrontendConfig); + } } return retVal; } @@ -7338,13 +7385,13 @@ config_set_allowed_sasl_mechs(const char *attrname, char *value, char *errorbuf /* During a reset, the value is "", so we have to handle this case. */ if (strcmp(value, "") != 0) { + char **nval_array; char *nval = slapi_ch_strdup(value); /* A separate variable is used because slapi_str2charray_ext can change it and nval'd become corrupted */ char *tmp_array_nval; /* cyrus sasl doesn't like comma separated lists */ - remove_commas(nval); - tmp_array_nval = slapi_ch_strdup(nval); + replace_char(nval, ',', ' '); if (invalid_sasl_mech(nval)) { slapi_log_err(SLAPI_LOG_ERR, "config_set_allowed_sasl_mechs", @@ -7353,15 +7400,18 @@ config_set_allowed_sasl_mechs(const char *attrname, char *value, char *errorbuf "digits, hyphens, or underscores\n", nval); slapi_ch_free_string(&nval); - slapi_ch_free_string(&tmp_array_nval); return LDAP_UNWILLING_TO_PERFORM; } + + tmp_array_nval = slapi_ch_strdup(nval); + nval_array = slapi_str2charray_ext(tmp_array_nval, " ", 0); + slapi_ch_free_string(&tmp_array_nval); + CFG_LOCK_WRITE(slapdFrontendConfig); slapi_ch_free_string(&slapdFrontendConfig->allowed_sasl_mechs); slapi_ch_array_free(slapdFrontendConfig->allowed_sasl_mechs_array); slapdFrontendConfig->allowed_sasl_mechs = nval; - slapdFrontendConfig->allowed_sasl_mechs_array = slapi_str2charray_ext(tmp_array_nval, " ", 0); - slapi_ch_free_string(&tmp_array_nval); + slapdFrontendConfig->allowed_sasl_mechs_array = nval_array; CFG_UNLOCK_WRITE(slapdFrontendConfig); } else { /* If this value is "", we need to set the list to *all* possible mechs */ @@ -8593,19 +8643,6 @@ slapi_err2string(int result) return ldap_err2string(result); } -/* replace commas with spaces */ -static void -remove_commas(char *str) -{ - int i; - - for (i = 0; str && str[i]; i++) { - if (str[i] == ',') { - str[i] = ' '; - } - } -} - /* * Check the SASL mechanism values * diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index d4111cb..41308f1 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -304,7 +304,9 @@ int config_set_pw_syntax(const char *attrname, char *value, char *errorbuf, int int32_t config_set_pw_palindrome(const char *attrname, char *value, char *errorbuf, int apply); int32_t config_set_pw_dict_check(const char *attrname, char *value, char *errorbuf, int apply); int32_t config_set_pw_dict_path(const char *attrname, char *value, char *errorbuf, int apply); +char **config_get_pw_user_attrs_array(void); int32_t config_set_pw_user_attrs(const char *attrname, char *value, char *errorbuf, int apply); +char **config_get_pw_bad_words_array(void); int32_t config_set_pw_bad_words(const char *attrname, char *value, char *errorbuf, int apply); int32_t config_set_pw_max_seq_sets(const char *attrname, char *value, char *errorbuf, int apply); int32_t config_set_pw_max_seq(const char *attrname, char *value, char *errorbuf, int apply); @@ -862,6 +864,7 @@ void slapd_nasty(char *str, int c, int err); int strarray2str(char **a, char *buf, size_t buflen, int include_quotes); int slapd_chown_if_not_owner(const char *filename, uid_t uid, gid_t gid); int slapd_comp_path(char *p0, char *p1); +void replace_char(char *name, char c, char c2); /* diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c index 7d67a67..2472cb4 100644 --- a/ldap/servers/slapd/pw.c +++ b/ldap/servers/slapd/pw.c @@ -1078,6 +1078,7 @@ check_pw_syntax_ext(Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals, c int num_repeated = 0; int max_repeated = 0; int num_categories = 0; + char **bad_words_array; pwd = (char *)slapi_value_get_string(vals[i]); @@ -1099,13 +1100,16 @@ check_pw_syntax_ext(Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals, c } /* Check for bad words */ - if (pwpolicy->pw_bad_words) { - for (size_t b = 0; pwpolicy->pw_bad_words && pwpolicy->pw_bad_words[b]; b++) { - if (strcasestr(pwd, pwpolicy->pw_bad_words[b])) { + bad_words_array = config_get_pw_bad_words_array(); + if (bad_words_array) { + for (size_t b = 0; bad_words_array && bad_words_array[b]; b++) { + if (strcasestr(pwd, bad_words_array[b])) { report_pw_violation(pb, pwresponse_req, "Password contains a restricted word"); + charray_free(bad_words_array); return (1); } } + charray_free(bad_words_array); } /* Check for sequences */ @@ -1320,6 +1324,7 @@ check_pw_syntax_ext(Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals, c /* check for trivial words if syntax checking is enabled */ if (pwpolicy->pw_syntax == LDAP_ON) { + char **user_attrs_array; /* e is null if this is an add operation*/ if (check_trivial_words(pb, e, vals, "uid", pwpolicy->pw_mintokenlength, smods) == 1 || check_trivial_words(pb, e, vals, "cn", pwpolicy->pw_mintokenlength, smods) == 1 || @@ -1334,15 +1339,18 @@ check_pw_syntax_ext(Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals, c return 1; } /* Check user attributes */ - if (pwpolicy->pw_cmp_attrs) { - for (size_t a = 0; pwpolicy->pw_cmp_attrs && pwpolicy->pw_cmp_attrs[a]; a++) { - if (check_trivial_words(pb, e, vals, pwpolicy->pw_cmp_attrs[a], pwpolicy->pw_mintokenlength, smods) == 1 ){ + user_attrs_array = config_get_pw_user_attrs_array(); + if (user_attrs_array) { + for (size_t a = 0; user_attrs_array && user_attrs_array[a]; a++) { + if (check_trivial_words(pb, e, vals, user_attrs_array[a], pwpolicy->pw_mintokenlength, smods) == 1 ){ if (mod_op) { slapi_entry_free(e); } + charray_free(user_attrs_array); return 1; } } + charray_free(user_attrs_array); } } @@ -2247,35 +2255,32 @@ new_passwdPolicy(Slapi_PBlock *pb, const char *dn) } } else if (!strcasecmp(attr_name, "passwordUserAttributes")) { if ((sval = attr_get_present_values(attr))) { - char **attrs = NULL; - char *attr = NULL; - char *token = NULL; - char *next = NULL; - - token = slapi_ch_strdup(slapi_value_get_string(*sval)); - for (attr = ldap_utf8strtok_r(token, " ", &next); attr != NULL; - attr = ldap_utf8strtok_r(NULL, " ", &next)) - { - slapi_ch_array_add(&attrs, slapi_ch_strdup(attr)); - } - slapi_ch_free_string(&token); + char *attrs = slapi_ch_strdup(slapi_value_get_string(*sval)); + /* we need a separate string because it gets corrupted after slapi_str2charray_ext */ + char *tmp_array_attrs = slapi_ch_strdup(attrs); + + /* we should accept comma-separated lists but slapi_str2charray_ext will process only space-separated */ + replace_char(tmp_array_attrs, ',', ' '); + pwdpolicy->pw_cmp_attrs = attrs; + /* Take list of attributes and break it up into a char array */ + pwdpolicy->pw_cmp_attrs_array = slapi_str2charray_ext(tmp_array_attrs, " ", 0); + slapi_ch_free_string(&tmp_array_attrs); } } else if (!strcasecmp(attr_name, "passwordBadWords")) { if ((sval = attr_get_present_values(attr))) { - char **words = NULL; - char *word = NULL; - char *token = NULL; - char *next = NULL; - - token = slapi_ch_strdup(slapi_value_get_string(*sval)); - for (word = ldap_utf8strtok_r(token, " ", &next); word != NULL; - word = ldap_utf8strtok_r(NULL, " ", &next)) - { - slapi_ch_array_add(&words, slapi_ch_strdup(word)); - } - slapi_ch_free_string(&token); + char *words = slapi_ch_strdup(slapi_value_get_string(*sval)); + /* we need a separate string because it gets corrupted after slapi_str2charray_ext */ + char *tmp_array_words = slapi_ch_strdup(words); + + /* we should accept comma-separated lists but slapi_str2charray_ext will process only space-separated */ + replace_char(tmp_array_words, ',', ' '); + pwdpolicy->pw_bad_words = words; + /* Take list of attributes and break it up into a char array */ + pwdpolicy->pw_bad_words_array = slapi_str2charray_ext(tmp_array_words, " ", 0); + + slapi_ch_free_string(&tmp_array_words); } } else if (!strcasecmp(attr_name, "passwordMaxSequence")) { if ((sval = attr_get_present_values(attr))) { diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h index 3b39a0a..a4cae78 100644 --- a/ldap/servers/slapd/slap.h +++ b/ldap/servers/slapd/slap.h @@ -1813,10 +1813,12 @@ typedef struct passwordpolicyarray the same character class. */ slapi_onoff_t pw_check_dict; char *pw_dict_path; /* custom dictionary */ - char **pw_cmp_attrs; /* Space-separated list of attributes to see if the + char *pw_cmp_attrs; /* Comma-separated list of attributes to see if the attribute values (and reversed values) in the entry are contained in the new password. */ - char **pw_bad_words; /* Space-separated list of words to reject */ + char **pw_cmp_attrs_array; /* Array of password user attributes */ + char *pw_bad_words; /* Comma-separated list of words to reject */ + char **pw_bad_words_array; /* Array of words to reject */ slapi_onoff_t pw_exp; slapi_onoff_t pw_send_expiring; diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c index 6ee1aac..e563528 100644 --- a/ldap/servers/slapd/util.c +++ b/ldap/servers/slapd/util.c @@ -467,6 +467,17 @@ slapi_escape_filter_value(char *filter_str, int len) } } +/* replace c with c2 in str */ +void +replace_char(char *str, char c, char c2) +{ + for (size_t i = 0; (str != NULL) && (str[i] != NULL); i++) { + if (c == str[i]) { + str[i] = c2; + } + } +} + /* ** This function takes a quoted attribute value of the form "abc", ** and strips off the enclosing quotes. It also deals with quoted @@ -1635,4 +1646,4 @@ mkdir_p(char *dir, unsigned int mode) } return 0; } -} \ No newline at end of file +}