Author: nkinder
Update of /cvs/dirsec/ldapserver/ldap/servers/slapd In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv21352/ldap/servers/slapd
Modified Files: extendop.c modify.c passwd_extop.c Log Message: Resolves: 184141 Summary: Make password modify extop work properly with the password policy control.
Index: extendop.c =================================================================== RCS file: /cvs/dirsec/ldapserver/ldap/servers/slapd/extendop.c,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- extendop.c 11 Dec 2008 23:05:23 -0000 1.11 +++ extendop.c 15 Jan 2009 18:24:48 -0000 1.12 @@ -311,6 +311,19 @@ goto free_and_return; }
+ /* decode the optional controls - put them in the pblock */ + if ( (lderr = get_ldapmessage_controls( pb, pb->pb_op->o_ber, NULL )) != 0 ) + { + char *dn = NULL; + slapi_pblock_get(pb, SLAPI_CONN_DN, &dn); + + op_shared_log_error_access (pb, "EXT", dn ? dn : "", "failed to decode LDAP controls"); + send_ldap_result( pb, lderr, NULL, NULL, 0, NULL ); + + slapi_ch_free_string(&dn); + goto free_and_return; + } + slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_OID, extoid ); slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_VALUE, &extval ); rc = plugin_call_exop_plugins( pb, extoid );
Index: modify.c =================================================================== RCS file: /cvs/dirsec/ldapserver/ldap/servers/slapd/modify.c,v retrieving revision 1.20 retrieving revision 1.21 diff -u -r1.20 -r1.21 --- modify.c 5 Dec 2008 22:41:52 -0000 1.20 +++ modify.c 15 Jan 2009 18:24:48 -0000 1.21 @@ -437,21 +437,30 @@
static int modify_internal_pb (Slapi_PBlock *pb) { - LDAPControl **controls; + LDAPControl **controls; + LDAPControl *pwpolicy_ctrl; Operation *op; - int opresult = 0; + int opresult = 0; LDAPMod **normalized_mods = NULL; LDAPMod **mods; LDAPMod **mod; Slapi_Mods smods; - int pw_change = 0; - char *old_pw = NULL; + int pw_change = 0; + char *old_pw = NULL;
PR_ASSERT (pb != NULL);
slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+ /* See if pwpolicy control is present. We need to do + * this before we call op_shared_allow_pw_change() since + * it looks for SLAPI_PWPOLICY in the pblock to determine + * if the response contorl is needed. */ + pwpolicy_ctrl = slapi_control_present( controls, + LDAP_X_CONTROL_PWPOLICY_REQUEST, NULL, NULL ); + slapi_pblock_set( pb, SLAPI_PWPOLICY, &pwpolicy_ctrl ); + if(mods == NULL) { opresult = LDAP_PARAM_ERROR;
Index: passwd_extop.c =================================================================== RCS file: /cvs/dirsec/ldapserver/ldap/servers/slapd/passwd_extop.c,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- passwd_extop.c 11 Dec 2008 23:05:23 -0000 1.18 +++ passwd_extop.c 15 Jan 2009 18:24:48 -0000 1.19 @@ -143,33 +143,48 @@ /* Construct Mods pblock and perform the modify operation * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT */ -static int passwd_apply_mods(const char *dn, Slapi_Mods *mods) +static int passwd_apply_mods(const char *dn, Slapi_Mods *mods, LDAPControl **req_controls, + LDAPControl ***resp_controls) { Slapi_PBlock pb; + LDAPControl **req_controls_copy = NULL; + LDAPControl **pb_resp_controls = NULL; int ret=0;
LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_apply_mods\n", 0, 0, 0 );
if (mods && (slapi_mods_get_num_mods(mods) > 0)) { + /* We need to dup the request controls since the original + * pblock owns the ones that have been passed in. */ + if (req_controls) { + slapi_add_controls(&req_controls_copy, req_controls, 1); + } + pblock_init(&pb); slapi_modify_internal_set_pb (&pb, dn, - slapi_mods_get_ldapmods_byref(mods), - NULL, /* Controls */ - NULL, /* UniqueID */ - pw_get_componentID(), /* PluginID */ - 0); /* Flags */ + slapi_mods_get_ldapmods_byref(mods), + req_controls_copy, NULL, /* UniqueID */ + pw_get_componentID(), /* PluginID */ + 0); /* Flags */
- ret =slapi_modify_internal_pb (&pb); + ret =slapi_modify_internal_pb (&pb);
- slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); + slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
- if (ret != LDAP_SUCCESS){ - LDAPDebug(LDAP_DEBUG_TRACE, "WARNING: passwordPolicy modify error %d on entry '%s'\n", - ret, dn, 0); - } + /* Retreive and duplicate the response controls since they will be + * destroyed along with the pblock used for the internal operation. */ + slapi_pblock_get(&pb, SLAPI_RESCONTROLS, &pb_resp_controls); + if (pb_resp_controls) { + slapi_add_controls(resp_controls, pb_resp_controls, 1); + }
- pblock_done(&pb); + if (ret != LDAP_SUCCESS){ + LDAPDebug(LDAP_DEBUG_TRACE, "WARNING: passwordPolicy modify error %d on entry '%s'\n", + ret, dn, 0); + } + + pblock_done(&pb); }
LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_apply_mods: %d\n", ret, 0, 0 ); @@ -180,7 +195,8 @@
/* Modify the userPassword attribute field of the entry */ -static int passwd_modify_userpassword(Slapi_Entry *targetEntry, const char *newPasswd) +static int passwd_modify_userpassword(Slapi_Entry *targetEntry, const char *newPasswd, + LDAPControl **req_controls, LDAPControl ***resp_controls) { char *dn = NULL; int ret = 0; @@ -193,7 +209,7 @@ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, SLAPI_USERPWD_ATTR, newPasswd);
- ret = passwd_apply_mods(dn, &smods); + ret = passwd_apply_mods(dn, &smods, req_controls, resp_controls);
slapi_mods_done(&smods); @@ -432,15 +448,18 @@ char *oldPasswd = NULL; char *newPasswd = NULL; char *errMesg = NULL; - int ret=0, rc=0, sasl_ssf=0; + int ret=0, rc=0, sasl_ssf=0, need_pwpolicy_ctrl=0; ber_tag_t tag=0; ber_len_t len=(ber_len_t)-1; struct berval *extop_value = NULL; struct berval *gen_passwd = NULL; BerElement *ber = NULL; BerElement *response_ber = NULL; - Slapi_Entry *targetEntry=NULL; + Slapi_Entry *targetEntry=NULL; Connection *conn = NULL; + LDAPControl **req_controls = NULL; + LDAPControl **resp_controls = NULL; + passwdPolicy *pwpolicy = NULL; /* Slapi_DN sdn; */
LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_extop\n", 0, 0, 0 ); @@ -589,33 +608,31 @@ }
if (oldPasswd == NULL || *oldPasswd == '\0') { - /* If user is authenticated, they already gave their password during - the bind operation (or used sasl or client cert auth or OS creds) */ - slapi_pblock_get(pb, SLAPI_CONN_AUTHMETHOD, &authmethod); - if (!authmethod || !strcmp(authmethod, SLAPD_AUTH_NONE)) { - errMesg = "User must be authenticated to the directory server.\n"; - rc = LDAP_INSUFFICIENT_ACCESS; - goto free_and_return; - } + /* If user is authenticated, they already gave their password during + * the bind operation (or used sasl or client cert auth or OS creds) */ + slapi_pblock_get(pb, SLAPI_CONN_AUTHMETHOD, &authmethod); + if (!authmethod || !strcmp(authmethod, SLAPD_AUTH_NONE)) { + errMesg = "User must be authenticated to the directory server.\n"; + rc = LDAP_INSUFFICIENT_ACCESS; + goto free_and_return; + } } + + /* Fetch the password policy. We need this in case we need to + * generate a password as well as for some policy checks. */ + pwpolicy = new_passwdPolicy( pb, dn ); /* 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') { - 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 */ rval = passwd_modify_generate_passwd( pwpolicy, &newPasswd, &errMesg );
- delete_passwdPolicy(&pwpolicy); - if (rval != LDAP_SUCCESS) { if (!errMesg) errMesg = "Error generating new password.\n"; @@ -659,8 +676,8 @@ /* Did they give us a DN ? */ if (dn == NULL || *dn == '\0') { /* Get the DN from the bind identity on this connection */ - slapi_ch_free_string(&dn); - dn = slapi_ch_strdup(bindDN); + slapi_ch_free_string(&dn); + dn = slapi_ch_strdup(bindDN); LDAPDebug( LDAP_DEBUG_ANY, "Missing userIdentity in request, using the bind DN instead.\n", 0, 0, 0 ); @@ -703,8 +720,14 @@ slapi_pblock_set(pb, SLAPI_BACKEND, be); }
+ /* Check if the pwpolicy control is present */ + slapi_pblock_get( pb, SLAPI_PWPOLICY, &need_pwpolicy_ctrl ); + ret = slapi_access_allowed ( pb, targetEntry, SLAPI_USERPWD_ATTR, NULL, SLAPI_ACL_WRITE ); - if ( ret != LDAP_SUCCESS ) { + if ( ret != LDAP_SUCCESS ) { + if (need_pwpolicy_ctrl) { + slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDMODNOTALLOWED ); + } errMesg = "Insufficient access rights\n"; rc = LDAP_INSUFFICIENT_ACCESS; goto free_and_return; @@ -714,21 +737,50 @@ * They gave us a password (old), check it against the target entry * Is the old password valid ? */ - if (oldPasswd && *oldPasswd) { - ret = passwd_check_pwd(targetEntry, oldPasswd); - if (ret) { - /* No, then we fail this operation */ - errMesg = "Invalid oldPasswd value.\n"; - rc = ret; - goto free_and_return; - } - } - + if (oldPasswd && *oldPasswd) { + ret = passwd_check_pwd(targetEntry, oldPasswd); + if (ret) { + /* No, then we fail this operation */ + errMesg = "Invalid oldPasswd value.\n"; + rc = ret; + goto free_and_return; + } + } + + /* Check if password policy allows users to change their passwords. We need to do + * this here since the normal modify code doesn't perform this check for + * internal operations. */ + if (!pb->pb_op->o_isroot && !pb->pb_conn->c_needpw && !pwpolicy->pw_change) { + Slapi_DN *bindSDN = slapi_sdn_new_dn_byref(bindDN); + /* Is this a user modifying their own password? */ + if (slapi_sdn_compare(bindSDN, slapi_entry_get_sdn(targetEntry))==0) { + if (need_pwpolicy_ctrl) { + slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDMODNOTALLOWED ); + } + errMesg = "User is not allowed to change password\n"; + rc = LDAP_UNWILLING_TO_PERFORM; + slapi_sdn_free(&bindSDN); + goto free_and_return; + } + slapi_sdn_free(&bindSDN); + }
+ /* Fetch any present request controls so we can use them when + * performing the modify operation. */ + slapi_pblock_get(pb, SLAPI_REQCONTROLS, &req_controls); + /* Now we're ready to make actual password change */ - ret = passwd_modify_userpassword(targetEntry, newPasswd); + ret = passwd_modify_userpassword(targetEntry, newPasswd, req_controls, &resp_controls); + + /* Set the response controls if necessary. We want to do this now + * so it is set for both the success and failure cases. The pblock + * will now own the controls. */ + if (resp_controls) { + slapi_pblock_set(pb, SLAPI_RESCONTROLS, resp_controls); + } + if (ret != LDAP_SUCCESS) { - /* Failed to modify the password, e.g. because insufficient access allowed */ + /* Failed to modify the password, e.g. because password policy, etc. */ errMesg = "Failed to update password\n"; rc = ret; goto free_and_return; @@ -742,7 +794,7 @@ LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_extop: %d\n", rc, 0, 0 ); /* Free anything that we allocated above */ - free_and_return: +free_and_return: slapi_ch_free_string(&bindDN); /* slapi_pblock_get SLAPI_CONN_DN does strdup */ slapi_ch_free_string(&oldPasswd); slapi_ch_free_string(&newPasswd); @@ -756,6 +808,7 @@ slapi_ch_free_string(&otdn); slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, NULL ); slapi_ch_free_string(&authmethod); + delete_passwdPolicy(&pwpolicy);
if ( targetEntry != NULL ){ slapi_entry_free (targetEntry);
389-commits@lists.fedoraproject.org