>From 316291baf060097d37579c675e06a9194e42c251 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Wed, 7 Oct 2009 18:15:27 +0200 Subject: [PATCH] add support for server side LDAP password policies - password policy request controls are send during bind and change password extended operation - the response control is evaluated to see if the password is expired or will expire, soon --- server/providers/ldap/ldap_auth.c | 4 + server/providers/ldap/sdap.h | 3 +- server/providers/ldap/sdap_async.c | 129 +++++++++++++++++++++++++++++++++--- 3 files changed, 125 insertions(+), 11 deletions(-) diff --git a/server/providers/ldap/ldap_auth.c b/server/providers/ldap/ldap_auth.c index b1667c4..487fb07 100644 --- a/server/providers/ldap/ldap_auth.c +++ b/server/providers/ldap/ldap_auth.c @@ -404,6 +404,7 @@ static void sdap_auth4chpass_done(struct tevent_req *req) switch (result) { case SDAP_AUTH_SUCCESS: + case SDAP_AUTH_PW_EXPIRED: DEBUG(7, ("user [%s] successfully authenticated.\n", state->dn)); subreq = sdap_exop_modify_passwd_send(state, state->breq->be_ctx->ev, @@ -541,6 +542,9 @@ static void sdap_pam_auth_done(struct tevent_req *req) case SDAP_UNAVAIL: state->pd->pam_status = PAM_AUTHINFO_UNAVAIL; break; + case SDAP_AUTH_PW_EXPIRED: + state->pd->pam_status = PAM_AUTHTOK_EXPIRED; + break; default: state->pd->pam_status = PAM_SYSTEM_ERR; } diff --git a/server/providers/ldap/sdap.h b/server/providers/ldap/sdap.h index cb98668..92771de 100644 --- a/server/providers/ldap/sdap.h +++ b/server/providers/ldap/sdap.h @@ -66,7 +66,8 @@ enum sdap_result { SDAP_RETRY, SDAP_ERROR, SDAP_AUTH_SUCCESS, - SDAP_AUTH_FAILED + SDAP_AUTH_FAILED, + SDAP_AUTH_PW_EXPIRED }; enum sdap_basic_opt { diff --git a/server/providers/ldap/sdap_async.c b/server/providers/ldap/sdap_async.c index f68a31c..6fd2837 100644 --- a/server/providers/ldap/sdap_async.c +++ b/server/providers/ldap/sdap_async.c @@ -28,6 +28,8 @@ #define REALM_SEPARATOR '@' +#define LDAP_X_SSSD_PASSWORD_EXPIRED 0x555D + static void make_realm_upper_case(const char *upn) { char *c; @@ -658,6 +660,7 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, int ret = EOK; int msgid; int ldap_err; + LDAPControl *request_controls[2]; req = tevent_req_create(memctx, &state, struct simple_bind_state); if (!req) return NULL; @@ -673,10 +676,19 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx, state->user_dn = user_dn; state->pw = pw; + ret = ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST, 0, NULL, 0, + &request_controls[0]); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_control_create failed.\n")); + goto fail; + } + request_controls[1] = NULL; + DEBUG(4, ("Executing simple bind as: %s\n", state->user_dn)); ret = ldap_sasl_bind(state->sh->ldap, state->user_dn, LDAP_SASL_SIMPLE, - state->pw, NULL, NULL, &msgid); + state->pw, request_controls, NULL, &msgid); + ldap_control_free(request_controls[0]); if (ret == -1 || msgid == -1) { ret = ldap_get_option(state->sh->ldap, LDAP_OPT_RESULT_CODE, &ldap_err); @@ -727,6 +739,11 @@ static void simple_bind_done(struct sdap_op *op, struct simple_bind_state); char *errmsg; int ret; + LDAPControl **response_controls; + int c; + ber_int_t pp_grace; + ber_int_t pp_expire; + LDAPPasswordPolicyError pp_error; if (error) { tevent_req_error(req, error); @@ -736,17 +753,57 @@ static void simple_bind_done(struct sdap_op *op, state->reply = talloc_steal(state, reply); ret = ldap_parse_result(state->sh->ldap, state->reply->msg, - &state->result, NULL, &errmsg, NULL, NULL, 0); + &state->result, NULL, &errmsg, NULL, + &response_controls, 0); if (ret != LDAP_SUCCESS) { DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid)); - tevent_req_error(req, EIO); - return; + ret = EIO; + goto done; + } + + if (response_controls == NULL) { + DEBUG(5, ("Server returned no controls.\n")); + } else { + for (c = 0; response_controls[c] != NULL; c++) { + DEBUG(9, ("Server returned control [%s].\n", + response_controls[c]->ldctl_oid)); + if (strcmp(response_controls[c]->ldctl_oid, + LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) { + ret = ldap_parse_passwordpolicy_control(state->sh->ldap, + response_controls[c], + &pp_expire, &pp_grace, + &pp_error); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_parse_passwordpolicy_control failed.\n")); + ret = EIO; + goto done; + } + + DEBUG(7, ("Password Policy Response: expire [%d] grace [%d] " + "error [%s].\n", pp_expire, pp_grace, + ldap_passwordpolicy_err2txt(pp_error))); + + if (state->result == LDAP_SUCCESS && + (pp_error == PP_changeAfterReset || pp_grace > 0)) { + DEBUG(4, ("User must set a new password.\n")); + state->result = LDAP_X_SSSD_PASSWORD_EXPIRED; + } + } + } } DEBUG(3, ("Bind result: %s(%d), %s\n", ldap_err2string(state->result), state->result, errmsg)); - tevent_req_done(req); + ret = LDAP_SUCCESS; +done: + ldap_controls_free(response_controls); + + if (ret == LDAP_SUCCESS) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } } static int simple_bind_recv(struct tevent_req *req, int *ldaperr) @@ -1218,6 +1275,9 @@ int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result) case LDAP_INVALID_CREDENTIALS: *result = SDAP_AUTH_FAILED; break; + case LDAP_X_SSSD_PASSWORD_EXPIRED: + *result = SDAP_AUTH_PW_EXPIRED; + break; default: *result = SDAP_ERROR; } @@ -2605,6 +2665,7 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx, BerElement *ber = NULL; struct berval *bv = NULL; int msgid; + LDAPControl *request_controls[2]; req = tevent_req_create(memctx, &state, struct sdap_exop_modify_passwd_state); @@ -2638,11 +2699,20 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx, return NULL; } + ret = ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST, 0, NULL, 0, + &request_controls[0]); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_control_create failed.\n")); + goto fail; + } + request_controls[1] = NULL; + DEBUG(4, ("Executing extended operation\n")); ret = ldap_extended_operation(state->sh->ldap, LDAP_EXOP_MODIFY_PASSWD, - bv, NULL, NULL, &msgid); + bv, request_controls, NULL, &msgid); ber_bvfree(bv); + ldap_control_free(request_controls[0]); if (ret == -1 || msgid == -1) { DEBUG(1, ("ldap_extended_operation failed\n")); goto fail; @@ -2674,6 +2744,11 @@ static void sdap_exop_modify_passwd_done(struct sdap_op *op, struct sdap_exop_modify_passwd_state); char *errmsg; int ret; + LDAPControl **response_controls = NULL; + int c; + ber_int_t pp_grace; + ber_int_t pp_expire; + LDAPPasswordPolicyError pp_error; if (error) { tevent_req_error(req, error); @@ -2681,17 +2756,51 @@ static void sdap_exop_modify_passwd_done(struct sdap_op *op, } ret = ldap_parse_result(state->sh->ldap, reply->msg, - &state->result, NULL, &errmsg, NULL, NULL, 0); + &state->result, NULL, &errmsg, NULL, + &response_controls, 0); if (ret != LDAP_SUCCESS) { DEBUG(2, ("ldap_parse_result failed (%d)\n", state->op->msgid)); - tevent_req_error(req, EIO); - return; + ret = EIO; + goto done; + } + + if (response_controls == NULL) { + DEBUG(5, ("Server returned no controls.\n")); + } else { + for (c = 0; response_controls[c] != NULL; c++) { + DEBUG(9, ("Server returned control [%s].\n", + response_controls[c]->ldctl_oid)); + if (strcmp(response_controls[c]->ldctl_oid, + LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) { + ret = ldap_parse_passwordpolicy_control(state->sh->ldap, + response_controls[c], + &pp_expire, &pp_grace, + &pp_error); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("ldap_parse_passwordpolicy_control failed.\n")); + ret = EIO; + goto done; + } + + DEBUG(7, ("Password Policy Response: expire [%d] grace [%d] " + "error [%s].\n", pp_expire, pp_grace, + ldap_passwordpolicy_err2txt(pp_error))); + } + } } DEBUG(3, ("ldap_extended_operation result: %s(%d), %s\n", ldap_err2string(state->result), state->result, errmsg)); - tevent_req_done(req); + ret = LDAP_SUCCESS; +done: + ldap_controls_free(response_controls); + + if (ret == LDAP_SUCCESS) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } } int sdap_exop_modify_passwd_recv(struct tevent_req *req, -- 1.6.2.5