summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoriko Hosoi <nhosoi@redhat.com>2006-11-28 18:02:07 +0000
committerNoriko Hosoi <nhosoi@redhat.com>2006-11-28 18:02:07 +0000
commit99ce798de45f4e70085a447b59b4ea29c20de424 (patch)
tree1df284a23e06b57bf0422895909054d8fe110af7
parent2d885470b8beff5e593413507b390ab32ddac891 (diff)
downloadds-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.c226
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;
}