diff options
author | Nathan Kinder <nkinder@redhat.com> | 2009-01-15 18:24:48 +0000 |
---|---|---|
committer | Nathan Kinder <nkinder@redhat.com> | 2009-01-15 18:24:48 +0000 |
commit | bf00ddf580bfd3f9340ab7703c23d867ad5524af (patch) | |
tree | acc9a2fb22abd377bf4dba5732031fabd653b8b9 | |
parent | 03ab7a78d9396e4583aa5fdcb70515c57512e96a (diff) | |
download | ds-bf00ddf580bfd3f9340ab7703c23d867ad5524af.tar.gz ds-bf00ddf580bfd3f9340ab7703c23d867ad5524af.tar.xz ds-bf00ddf580bfd3f9340ab7703c23d867ad5524af.zip |
Resolves: 184141
Summary: Make password modify extop work properly with the password policy control.
-rw-r--r-- | ldap/servers/slapd/extendop.c | 13 | ||||
-rw-r--r-- | ldap/servers/slapd/modify.c | 17 | ||||
-rw-r--r-- | ldap/servers/slapd/passwd_extop.c | 147 |
3 files changed, 126 insertions, 51 deletions
diff --git a/ldap/servers/slapd/extendop.c b/ldap/servers/slapd/extendop.c index 53947c6e..ced66c34 100644 --- a/ldap/servers/slapd/extendop.c +++ b/ldap/servers/slapd/extendop.c @@ -311,6 +311,19 @@ do_extended( Slapi_PBlock *pb ) 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 ); diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c index 4fd2a8b2..09ccd424 100644 --- a/ldap/servers/slapd/modify.c +++ b/ldap/servers/slapd/modify.c @@ -437,21 +437,30 @@ void slapi_modify_internal_set_pb (Slapi_PBlock *pb, const char *dn, LDAPMod **m 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; diff --git a/ldap/servers/slapd/passwd_extop.c b/ldap/servers/slapd/passwd_extop.c index 8b1185a5..5958171b 100644 --- a/ldap/servers/slapd/passwd_extop.c +++ b/ldap/servers/slapd/passwd_extop.c @@ -143,33 +143,48 @@ passwd_modify_getEntry( const char *dn, Slapi_Entry **e2 ) { /* 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 @@ static int passwd_apply_mods(const char *dn, Slapi_Mods *mods) /* 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 @@ static int passwd_modify_userpassword(Slapi_Entry *targetEntry, const char *newP 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 @@ passwd_modify_extop( Slapi_PBlock *pb ) 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 @@ parse_req_done: } 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 @@ parse_req_done: /* 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 @@ parse_req_done: 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 @@ parse_req_done: * 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 @@ parse_req_done: 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 @@ parse_req_done: 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); |