diff options
| author | Noriko Hosoi <nhosoi@redhat.com> | 2006-11-28 18:02:07 +0000 |
|---|---|---|
| committer | Noriko Hosoi <nhosoi@redhat.com> | 2006-11-28 18:02:07 +0000 |
| commit | 99ce798de45f4e70085a447b59b4ea29c20de424 (patch) | |
| tree | 1df284a23e06b57bf0422895909054d8fe110af7 | |
| parent | 2d885470b8beff5e593413507b390ab32ddac891 (diff) | |
| download | ds-99ce798de45f4e70085a447b59b4ea29c20de424.tar.gz ds-99ce798de45f4e70085a447b59b4ea29c20de424.tar.xz ds-99ce798de45f4e70085a447b59b4ea29c20de424.zip | |
Resolves: #216983
Summary: Make random password generation work with policies
Changes: 1) Generate a password that meets the current password syntax rules.
2) Report errors when Min8Bit is set or MinCategories > 4
| -rw-r--r-- | ldap/servers/slapd/passwd_extop.c | 226 |
1 files changed, 213 insertions, 13 deletions
diff --git a/ldap/servers/slapd/passwd_extop.c b/ldap/servers/slapd/passwd_extop.c index 33fffda8..55d81c8a 100644 --- a/ldap/servers/slapd/passwd_extop.c +++ b/ldap/servers/slapd/passwd_extop.c @@ -205,33 +205,223 @@ static int passwd_modify_userpassword(Slapi_Entry *targetEntry, const char *newP return ret; } -/* Generate a new random password */ -static int passwd_modify_generate_passwd(char **genpasswd) +/* Generate a new, basic random password */ +static int passwd_modify_generate_basic_passwd( int passlen, char **genpasswd ) { - unsigned char data[ LDAP_EXTOP_PASSMOD_RANDOM_BYTES ]; - char enc[ 1 + LDIF_BASE64_LEN( LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN ) ]; + unsigned char *data = NULL; + char *enc = NULL; + int datalen = LDAP_EXTOP_PASSMOD_RANDOM_BYTES; + int enclen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN + 1; - if (genpasswd == NULL) { + if ( genpasswd == NULL ) { return LDAP_OPERATIONS_ERROR; } + if ( passlen > 0 ) { + datalen = passlen * 3 / 4 + 1; + enclen = datalen * 4; /* allocate the large enough space */ + } + + data = (unsigned char *)slapi_ch_calloc( datalen, 1 ); + enc = (char *)slapi_ch_calloc( enclen, 1 ); + /* get random bytes from NSS */ - PK11_GenerateRandom( data, LDAP_EXTOP_PASSMOD_RANDOM_BYTES ); + PK11_GenerateRandom( data, datalen ); /* b64 encode the random bytes to get a password made up - * of printable characters. ldif_base64_encode() will + * of printable characters. ldif_base64_encode() will * zero-terminate the string */ - (void)ldif_base64_encode( data, enc, LDAP_EXTOP_PASSMOD_RANDOM_BYTES, -1 ); + (void)ldif_base64_encode( data, enc, passlen, -1 ); + + /* This will get freed by the caller */ + *genpasswd = slapi_ch_malloc( 1 + passlen ); + + /* trim the password to the proper length */ + PL_strncpyz( *genpasswd, enc, passlen + 1 ); + + slapi_ch_free( (void **)&data ); + slapi_ch_free_string( &enc ); + + return LDAP_SUCCESS; +} + +/* Generate a new, password-policy-based random password */ +static int passwd_modify_generate_policy_passwd( passwdPolicy *pwpolicy, + char **genpasswd, char **errMesg ) +{ + unsigned char *data = NULL; + int passlen = 0; + int tmplen = 0; + enum { + idx_minuppers = 0, + idx_minlowers, + idx_mindigits, + idx_minspecials, + idx_end + }; + int my_policy[idx_end]; + struct { + int chr_start; + int chr_range; + } chr_table[] = { /* NOTE: the above enum order */ + { 65, 26 }, /* [ A - Z ] */ + { 97, 26 }, /* [ a - z ] */ + { 48, 10 }, /* [ 0 - 9 ] */ + { 58, 7 } /* [ : - @ ] */ + }; +#define gen_policy_pw_getchar(n, idx) \ +( chr_table[(idx)].chr_start + (n) % chr_table[(idx)].chr_range ) + int i; + + if ( genpasswd == NULL ) { + return LDAP_OPERATIONS_ERROR; + } + + my_policy[idx_mindigits] = pwpolicy->pw_mindigits; + my_policy[idx_minuppers] = pwpolicy->pw_minuppers; + my_policy[idx_minlowers] = pwpolicy->pw_minlowers; + my_policy[idx_minspecials] = pwpolicy->pw_minspecials; + + /* if only minalphas is set, divide it into minuppers and minlowers. */ + if ( pwpolicy->pw_minalphas > 0 && + ( my_policy[idx_minuppers] == 0 && my_policy[idx_minlowers] == 0 )) { + unsigned int x = (unsigned int)time(NULL); + my_policy[idx_minuppers] = slapi_rand_r(&x) % pwpolicy->pw_minalphas; + my_policy[idx_minlowers] = pwpolicy->pw_minalphas - my_policy[idx_minuppers]; + } + + if ( pwpolicy->pw_mincategories ) { + int categories = 0; + for ( i = 0; i < idx_end; i++ ) { + if ( my_policy[i] > 0 ) { + categories++; + } + } + if ( pwpolicy->pw_mincategories > categories ) { + categories = pwpolicy->pw_mincategories; + for ( i = 0; i < idx_end; i++ ) { + if ( my_policy[i] == 0 ) { + /* force to add a policy to match the pw_mincategories */ + my_policy[i] = 1; + } + if ( --categories == 0 ) { + break; + } + } + if ( categories > 0 ) { + /* password generator does not support passwordMin8Bit */ + LDAPDebug( LDAP_DEBUG_ANY, + "Unable to generate a password that meets the current " + "password syntax rules. A minimum categories setting " + "of %d is not supported with random password generation.\n", + pwpolicy->pw_mincategories, 0, 0 ); + *errMesg = "Unable to generate new random password. Please contact the Administrator."; + return LDAP_CONSTRAINT_VIOLATION; + } + } + } + + /* get the password length */ + tmplen = 0; + for ( i = 0; i < idx_end; i++ ) { + tmplen += my_policy[i]; + } + passlen = tmplen; + if ( passlen < pwpolicy->pw_minlength ) { + passlen = pwpolicy->pw_minlength; + } + if ( passlen < LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN ) { + passlen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN; + } + + data = (unsigned char *)slapi_ch_calloc( passlen, 1 ); + + /* get random bytes from NSS */ + PK11_GenerateRandom( data, passlen ); + + /* if password length is longer the sum of my_policy's, + let them share the burden */ + if ( passlen > tmplen ) { + unsigned int x = (unsigned int)time(NULL); + int delta = passlen - tmplen; + for ( i = 0; i < delta; i++ ) { + my_policy[(x = slapi_rand_r(&x)) % idx_end]++; + } + } /* This will get freed by the caller */ - *genpasswd = slapi_ch_malloc( 1 + LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN); + *genpasswd = slapi_ch_malloc( 1 + passlen ); + + for ( i = 0; i < passlen; i++ ) { + int idx = data[i] % idx_end; + int isfirst = 1; + /* choose a category based on the random value */ + while ( my_policy[idx] <= 0 ) { + if ( ++idx == idx_end ) { + idx = 0; /* if no rule is found, default is uppercase */ + if ( !isfirst ) { + break; + } + isfirst = 0; + } + } + my_policy[idx]--; + (*genpasswd)[i] = gen_policy_pw_getchar(data[i], idx); + } + (*genpasswd)[passlen] = '\0'; - /* trim the password to the proper length. */ - PL_strncpyz( *genpasswd, enc, 1 + LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN ); + slapi_ch_free( (void **)&data ); return LDAP_SUCCESS; } +/* Generate a new random password */ +static int passwd_modify_generate_passwd( passwdPolicy *pwpolicy, + char **genpasswd, char **errMesg ) +{ + int minalphalen = 0; + int passlen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN; + int rval = LDAP_SUCCESS; + + if ( genpasswd == NULL ) { + return LDAP_OPERATIONS_ERROR; + } + if ( pwpolicy->pw_min8bit > 0 ) { + LDAPDebug( LDAP_DEBUG_ANY, "Unable to generate a password that meets " + "the current password syntax rules. 8-bit syntax " + "restrictions are not supported with random password " + "generation.\n", 0, 0, 0 ); + *errMesg = "Unable to generate new random password. Please contact the Administrator."; + return LDAP_CONSTRAINT_VIOLATION; + } + + if ( pwpolicy->pw_minalphas || pwpolicy->pw_minuppers || + pwpolicy->pw_minlowers || pwpolicy->pw_mindigits || + pwpolicy->pw_minspecials || pwpolicy->pw_maxrepeats || + pwpolicy->pw_mincategories > 2 ) { + rval = passwd_modify_generate_policy_passwd( pwpolicy, genpasswd, + errMesg ); + } else { + /* find out the minimum length to fulfill the passwd policy + requirements */ + minalphalen = pwpolicy->pw_minuppers + pwpolicy->pw_minlowers; + if ( minalphalen < pwpolicy->pw_minalphas ) { + minalphalen = pwpolicy->pw_minalphas; + } + passlen = minalphalen + pwpolicy->pw_mindigits + + pwpolicy->pw_minspecials + pwpolicy->pw_min8bit; + if ( passlen < pwpolicy->pw_minlength ) { + passlen = pwpolicy->pw_minlength; + } + if ( passlen < LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN ) { + passlen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN; + } + rval = passwd_modify_generate_basic_passwd( passlen, genpasswd ); + } + + return rval; +} + /* Password Modify Extended operation plugin function */ int @@ -415,12 +605,22 @@ parse_req_done: * a random one and return it to the user in a response. */ if (newPasswd == NULL || *newPasswd == '\0') { + passwdPolicy *pwpolicy; + int rval; /* Do a free of newPasswd here to be safe, otherwise we may leak 1 byte */ slapi_ch_free_string( &newPasswd ); + + pwpolicy = new_passwdPolicy( pb, dn ); + /* Generate a new password */ - if (passwd_modify_generate_passwd( &newPasswd ) != LDAP_SUCCESS) { - errMesg = "Error generating new password.\n"; + rval = passwd_modify_generate_passwd( pwpolicy, &newPasswd, &errMesg ); + + delete_passwdPolicy(&pwpolicy); + + if (rval != LDAP_SUCCESS) { + if (!errMesg) + errMesg = "Error generating new password.\n"; rc = LDAP_OPERATIONS_ERROR; goto free_and_return; } |
