Browse Source

203043 - Support password generation when using the password modify extended operation

Nathan Kinder 19 years ago
parent
commit
596d83cfe6
1 changed files with 122 additions and 42 deletions
  1. 122 42
      ldap/servers/slapd/passwd_extop.c

+ 122 - 42
ldap/servers/slapd/passwd_extop.c

@@ -68,6 +68,12 @@
 #define LDAP_EXTOP_PASSMOD_TAG_OLDPWD	0x81U
 #define LDAP_EXTOP_PASSMOD_TAG_NEWPWD	0x82U
 
+/* ber tags for the PasswdModifyResponseValue sequence */
+#define LDAP_EXTOP_PASSMOD_TAG_GENPWD	0x80U
+
+/* number of bytes used for random password generation */
+#define LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN 8
+
 /* OID of the extended operation handled by this plug-in */
 #define EXOP_PASSWD_OID	"1.3.6.1.4.1.4203.1.11.1"
 
@@ -191,6 +197,34 @@ 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)
+{
+	unsigned char data[ LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN ];
+	char enc[ 1 + LDIF_BASE64_LEN( LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN) ];
+
+	if (genpasswd == NULL) {
+		return LDAP_OPERATIONS_ERROR;
+	}
+
+	/* get 8 random bytes from NSS */
+	PK11_GenerateRandom( data, LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN );
+
+	/* b64 encode the 8 bytes to get a password made up of
+	 * printable characters. ldif_base64_encode() will
+	 * zero-terminate the string */
+	(void)ldif_base64_encode( data, enc, LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN, -1 );
+
+	/* This will get freed by the caller */
+	*genpasswd = slapi_ch_malloc( 1 + LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN);
+
+	/* trim the password to the proper length. */
+	PL_strncpyz( *genpasswd, enc, 1 + LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN );
+
+	return LDAP_SUCCESS;
+}
+
+
 /* Password Modify Extended operation plugin function */
 int
 passwd_modify_extop( Slapi_PBlock *pb )
@@ -205,22 +239,14 @@ passwd_modify_extop( Slapi_PBlock *pb )
 	int             ret=0, rc=0, sasl_ssf=0;
 	unsigned long	tag=0, len=-1;
 	struct berval *extop_value = NULL;
+	struct berval *gen_passwd = NULL;
 	BerElement	*ber = NULL;
+	BerElement	*response_ber = NULL;
 	Slapi_Entry *targetEntry=NULL;
 	Connection      *conn = NULL;
 	/* Slapi_DN sdn; */
 
     	LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_extop\n", 0, 0, 0 );
-	/* Get the pb ready for sending Password Modify Extended Responses back to the client. 
-	 * The only requirement is to set the LDAP OID of the extended response to the EXOP_PASSWD_OID. */
-
-	if ( slapi_pblock_set( pb, SLAPI_EXT_OP_RET_OID, EXOP_PASSWD_OID ) != 0 ) {
-		errMesg = "Could not set extended response oid.\n";
-		rc = LDAP_OPERATIONS_ERROR;
-		slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
-				 errMesg );
-		goto free_and_return;
-	}
 
 	/* Before going any further, we'll make sure that the right extended operation plugin
 	 * has been called: i.e., the OID shipped whithin the extended operation request must 
@@ -278,22 +304,25 @@ passwd_modify_extop( Slapi_PBlock *pb )
 	}
 
 	/* Format of request to parse
-	*
-   	* PasswdModifyRequestValue ::= SEQUENCE {
-     	* userIdentity    [0]  OCTET STRING OPTIONAL
-     	* oldPasswd       [1]  OCTET STRING OPTIONAL
-     	* newPasswd       [2]  OCTET STRING OPTIONAL }
-	*/
-	slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+	 *
+   	 * PasswdModifyRequestValue ::= SEQUENCE {
+     	 * userIdentity    [0]  OCTET STRING OPTIONAL
+     	 * oldPasswd       [1]  OCTET STRING OPTIONAL
+     	 * newPasswd       [2]  OCTET STRING OPTIONAL }
+	 *
+	 * The request value field is optional. If it is
+	 * provided, at least one field must be filled in.
+	 */
 
 	/* ber parse code */
 	if ( ber_scanf( ber, "{") == LBER_ERROR )
     	{
-    		LDAPDebug( LDAP_DEBUG_ANY,
-    		    "ber_scanf failed :{\n", 0, 0, 0 );
-    		errMesg = "ber_scanf failed\n";
-		rc = LDAP_PROTOCOL_ERROR;
-		goto free_and_return;
+		/* The request field wasn't provided.  We'll
+		 * now try to determine the userid and verify
+		 * knowledge of the old password via other
+		 * means.
+		 */
+		goto parse_req_done;
     	} else {
 		tag = ber_peek_tag( ber, &len);
 	}
@@ -343,12 +372,9 @@ passwd_modify_extop( Slapi_PBlock *pb )
 		rc = LDAP_PROTOCOL_ERROR;
 		goto free_and_return;
     		}
-	} else {
-		errMesg = "New passwd must be supplied by the user.\n";
-		rc = LDAP_PARAM_ERROR;
-		goto free_and_return;
 	}
-	
+
+parse_req_done:	
 	/* Uncomment for debugging, otherwise we don't want to leak the password values into the log... */
 	/* LDAPDebug( LDAP_DEBUG_ARGS, "passwd: dn (%s), oldPasswd (%s) ,newPasswd (%s)\n",
 					 dn, oldPasswd, newPasswd); */
@@ -376,13 +402,60 @@ passwd_modify_extop( Slapi_PBlock *pb )
         }
 	 }
 	 
-	 /* We don't implement password generation, so if the request implies 
-	  * that they asked us to do that, we must refuse to process it  */
-	 if (newPasswd == NULL || *newPasswd == '\0') {
-	 /* Refuse to handle this operation because we don't allow password generation */
-		errMesg = "New passwd must be supplied by the user.\n";
-		rc = LDAP_PARAM_ERROR;
-		goto free_and_return;
+	/* A new password was not supplied in the request, so we need to generate
+	 * a random one and return it to the user in a response.
+	 */
+	if (newPasswd == NULL || *newPasswd == '\0') {
+		/* Do a free of newPasswd here to be safe, otherwise we may leak 1 byte */
+		slapi_ch_free_string( &newPasswd );
+
+		/* Generate a new password */ 
+		if (passwd_modify_generate_passwd( &newPasswd ) != LDAP_SUCCESS) {
+			errMesg = "Error generating new password.\n";
+			rc = LDAP_OPERATIONS_ERROR;
+			goto free_and_return;
+		}
+
+		/* Make sure a passwd was actually generated */
+		if (newPasswd == NULL || *newPasswd == '\0') {
+			errMesg = "Error generating new password.\n";
+			rc = LDAP_OPERATIONS_ERROR;
+			goto free_and_return;
+		}
+
+		/*
+		 * Create the response message since we generated a new password.
+		 *
+		 * PasswdModifyResponseValue ::= SEQUENCE {
+		 *     genPasswd       [0]     OCTET STRING OPTIONAL }
+		 */
+		if ( (response_ber = ber_alloc()) == NULL ) {
+                        rc = LDAP_NO_MEMORY;
+			goto free_and_return;
+                }
+
+		if ( LBER_ERROR == ( ber_printf( response_ber, "{" ) ) ) {
+			ber_free( response_ber, 1 );
+			rc = LDAP_ENCODING_ERROR;
+			goto free_and_return;
+                }
+
+		if ( LBER_ERROR == ( ber_printf( response_ber, "ts", LDAP_EXTOP_PASSMOD_TAG_GENPWD,
+				newPasswd ) ) ) {
+			ber_free( response_ber, 1 );
+			rc = LDAP_ENCODING_ERROR;
+			goto free_and_return;
+		}
+
+		if ( LBER_ERROR == ( ber_printf( response_ber, "}" ) ) ) {
+			ber_free( response_ber, 1 );
+			rc = LDAP_ENCODING_ERROR;
+		}
+
+		ber_flatten(response_ber, &gen_passwd);
+
+		/* We're done with response_ber now, so free it */
+		ber_free( response_ber, 1 );
 	 }
 	 
 	 
@@ -463,21 +536,25 @@ passwd_modify_extop( Slapi_PBlock *pb )
 		rc = ret;
 		goto free_and_return;
 	}
+
+	if (gen_passwd && (gen_passwd->bv_val != '\0')) {
+		/* Set the reponse to let the user know the generated password */
+		slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, gen_passwd);
+	}
 	
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_extop: %d\n", rc, 0, 0 );
 	
 	/* Free anything that we allocated above */
 	free_and_return:
-
-    slapi_ch_free_string(&oldPasswd);
-    slapi_ch_free_string(&newPasswd);
-    /* Either this is the same pointer that we allocated and set above,
-       or whoever used it should have freed it and allocated a new
-       value that we need to free here */
+	slapi_ch_free_string(&oldPasswd);
+	slapi_ch_free_string(&newPasswd);
+	/* Either this is the same pointer that we allocated and set above,
+	 * or whoever used it should have freed it and allocated a new
+	 * value that we need to free here */
 	slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET, &dn );
-    slapi_ch_free_string(&dn);
+	slapi_ch_free_string(&dn);
 	slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, NULL );
-    slapi_ch_free_string(&authmethod);
+	slapi_ch_free_string(&authmethod);
 
 	if ( targetEntry != NULL ){
 		slapi_entry_free (targetEntry); 
@@ -493,6 +570,9 @@ passwd_modify_extop( Slapi_PBlock *pb )
 	send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
 	
 
+	/* We can free the generated password bval now */
+	ber_bvfree(gen_passwd);
+
 	return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
 
 }/* passwd_modify_extop */