diff options
-rw-r--r-- | src/providers/ldap/ldap_auth.c | 52 | ||||
-rw-r--r-- | src/providers/ldap/sdap.h | 5 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async.h | 6 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async_connection.c | 53 | ||||
-rw-r--r-- | src/sss_client/pam_sss.c | 82 | ||||
-rw-r--r-- | src/sss_client/sss_cli.h | 23 |
6 files changed, 201 insertions, 20 deletions
diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c index 52287030..8c77e3aa 100644 --- a/src/providers/ldap/ldap_auth.c +++ b/src/providers/ldap/ldap_auth.c @@ -7,6 +7,7 @@ Sumit Bose <sbose@redhat.com> Copyright (C) 2008 Red Hat + Copyright (C) 2010, rhafer@suse.de, Novell Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -135,6 +136,39 @@ static errno_t check_pwexpire_shadow(struct spwd *spwd, time_t now, return EOK; } +static errno_t check_pwexpire_ldap(struct pam_data *pd, + struct sdap_ppolicy_data *ppolicy, + enum sdap_result *result) +{ + if (ppolicy->grace > 0 || ppolicy->expire > 0) { + uint32_t *data; + uint32_t *ptr; + + data = talloc_size(pd, 2* sizeof(uint32_t)); + if (data == NULL) { + DEBUG(1, ("talloc_size failed.\n")); + return ENOMEM; + } + + ptr = data; + if (ppolicy->grace > 0) { + *ptr = SSS_PAM_USER_INFO_GRACE_LOGIN; + ptr++; + *ptr = ppolicy->grace; + } else if (ppolicy->expire > 0) { + *ptr = SSS_PAM_USER_INFO_EXPIRE_WARN; + ptr++; + *ptr = ppolicy->expire; + } + + pam_add_response(pd, SSS_PAM_USER_INFO, 2* sizeof(uint32_t), + (uint8_t*)data); + } + + *result = SDAP_AUTH_SUCCESS; + return EOK; +} + static errno_t string_to_shadowpw_days(const char *s, long *d) { long l; @@ -569,8 +603,15 @@ static void auth_bind_user_done(struct tevent_req *subreq) struct auth_state *state = tevent_req_data(req, struct auth_state); int ret; - - ret = sdap_auth_recv(subreq, &state->result); + struct sdap_ppolicy_data *ppolicy; + + ret = sdap_auth_recv(subreq, state, &state->result, &ppolicy); + if (ppolicy != NULL) { + DEBUG(9,("Found ppolicy data, " + "assuming LDAP password policies are active.\n")); + state->pw_expire_type = PWEXPIRE_LDAP_PASSWORD_POLICY; + state->pw_expire_data = ppolicy; + } talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); @@ -960,6 +1001,13 @@ static void sdap_pam_auth_done(struct tevent_req *req) } break; case PWEXPIRE_LDAP_PASSWORD_POLICY: + ret = check_pwexpire_ldap(state->pd, pw_expire_data, &result); + if (ret != EOK) { + DEBUG(1, ("check_pwexpire_ldap failed.\n")); + state->pd->pam_status = PAM_SYSTEM_ERR; + goto done; + } + break; case PWEXPIRE_NONE: break; default: diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index 007185fc..f0e345ec 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -85,6 +85,11 @@ struct sdap_service { char *uri; }; +struct sdap_ppolicy_data { + int grace; + int expire; +}; + #define SYSDB_SHADOWPW_LASTCHANGE "shadowLastChange" #define SYSDB_SHADOWPW_MIN "shadowMin" #define SYSDB_SHADOWPW_MAX "shadowMax" diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index 3c52d236..888df6b4 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -76,7 +76,11 @@ struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, const char *user_dn, const char *authtok_type, struct dp_opt_blob authtok); -int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result); + +int sdap_auth_recv(struct tevent_req *req, + TALLOC_CTX *memctx, + enum sdap_result *result, + struct sdap_ppolicy_data **ppolicy); struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx, struct tevent_context *ev, diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index 586733f4..f8c69569 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -4,6 +4,7 @@ Async LDAP Helper routines Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009 + Copyright (C) 2010, rhafer@suse.de, Novell Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -278,6 +279,7 @@ struct simple_bind_state { struct sdap_op *op; struct sdap_msg *reply; + struct sdap_ppolicy_data *ppolicy; int result; }; @@ -401,6 +403,7 @@ static void simple_bind_done(struct sdap_op *op, if (response_controls == NULL) { DEBUG(5, ("Server returned no controls.\n")); + state->ppolicy = NULL; } else { for (c = 0; response_controls[c] != NULL; c++) { DEBUG(9, ("Server returned control [%s].\n", @@ -420,12 +423,30 @@ static void simple_bind_done(struct sdap_op *op, 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)) || - (state->result == LDAP_INVALID_CREDENTIALS && - pp_error == PP_passwordExpired ) ) { - DEBUG(4, ("User must set a new password.\n")); + state->ppolicy = talloc(state, struct sdap_ppolicy_data); + if (state->ppolicy == NULL) { + DEBUG(1, ("talloc failed.\n")); + ret = ENOMEM; + goto done; + } + state->ppolicy->grace = pp_grace; + state->ppolicy->expire = pp_expire; + if (state->result == LDAP_SUCCESS) { + if (pp_error == PP_changeAfterReset) { + DEBUG(4, ("Password was reset. " + "User must set a new password.\n")); + state->result = LDAP_X_SSSD_PASSWORD_EXPIRED; + } else if (pp_grace > 0) { + DEBUG(4, ("Password expired. " + "[%d] grace logins remaining.\n", pp_grace)); + } else if (pp_expire > 0) { + DEBUG(4, ("Password will expire in [%d] seconds.\n", + pp_expire)); + } + } else if (state->result == LDAP_INVALID_CREDENTIALS && + pp_error == PP_passwordExpired) { + DEBUG(4, + ("Password expired user must set a new password.\n")); state->result = LDAP_X_SSSD_PASSWORD_EXPIRED; } } @@ -446,7 +467,10 @@ done: } } -static int simple_bind_recv(struct tevent_req *req, int *ldaperr) +static int simple_bind_recv(struct tevent_req *req, + TALLOC_CTX *memctx, + int *ldaperr, + struct sdap_ppolicy_data **ppolicy) { struct simple_bind_state *state = tevent_req_data(req, struct simple_bind_state); @@ -455,6 +479,7 @@ static int simple_bind_recv(struct tevent_req *req, int *ldaperr) TEVENT_REQ_RETURN_ON_ERROR(req); *ldaperr = state->result; + *ppolicy = talloc_steal(memctx, state->ppolicy); return EOK; } @@ -704,6 +729,7 @@ int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result) struct sdap_auth_state { const char *user_dn; struct berval pw; + struct sdap_ppolicy_data *ppolicy; int result; bool is_sasl; @@ -766,8 +792,9 @@ static void sdap_auth_done(struct tevent_req *subreq) if (state->is_sasl) { ret = sasl_bind_recv(subreq, &state->result); + state->ppolicy = NULL; } else { - ret = simple_bind_recv(subreq, &state->result); + ret = simple_bind_recv(subreq, state, &state->result, &state->ppolicy); } if (ret != EOK) { tevent_req_error(req, ret); @@ -777,7 +804,10 @@ static void sdap_auth_done(struct tevent_req *subreq) tevent_req_done(req); } -int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result) +int sdap_auth_recv(struct tevent_req *req, + TALLOC_CTX *memctx, + enum sdap_result *result, + struct sdap_ppolicy_data **ppolicy) { struct sdap_auth_state *state = tevent_req_data(req, struct sdap_auth_state); @@ -785,6 +815,9 @@ int sdap_auth_recv(struct tevent_req *req, enum sdap_result *result) *result = SDAP_ERROR; TEVENT_REQ_RETURN_ON_ERROR(req); + if (ppolicy != NULL) { + *ppolicy = talloc_steal(memctx, state->ppolicy); + } switch (state->result) { case LDAP_SUCCESS: *result = SDAP_AUTH_SUCCESS; @@ -1078,7 +1111,7 @@ static void sdap_cli_auth_done(struct tevent_req *subreq) enum sdap_result result; int ret; - ret = sdap_auth_recv(subreq, &result); + ret = sdap_auth_recv(subreq, NULL, &result, NULL); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index 2ba6f158..07ed4e72 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -3,6 +3,7 @@ Sumit Bose <sbose@redhat.com> Copyright (C) 2009 Red Hat + Copyright (C) 2010, rhafer@suse.de, Novell Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -436,6 +437,81 @@ static int user_info_offline_auth(pam_handle_t *pamh, size_t buflen, return PAM_SUCCESS; } +static int user_info_grace_login(pam_handle_t *pamh, + size_t buflen, + uint8_t *buf) +{ + int ret; + uint32_t grace; + char user_msg[256]; + + if (buflen != 2* sizeof(uint32_t)) { + D(("User info response data has the wrong size")); + return PAM_BUF_ERR; + } + memcpy(&grace, buf + sizeof(uint32_t), sizeof(uint32_t)); + ret = snprintf(user_msg, sizeof(user_msg), + _("Your password has expired. " + "You have %d grace login(s) remaining."), + grace); + if (ret < 0 || ret >= sizeof(user_msg)) { + D(("snprintf failed.")); + return PAM_SYSTEM_ERR; + } + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, user_msg, NULL, NULL); + + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + return PAM_SUCCESS; +} + +#define MINSEC 60 +#define HOURSEC (60*MINSEC) +#define DAYSEC (24*HOURSEC) +static int user_info_expire_warn(pam_handle_t *pamh, + size_t buflen, + uint8_t *buf) +{ + int ret; + uint32_t expire; + char user_msg[256]; + const char* unit="second(s)"; + + if (buflen != 2* sizeof(uint32_t)) { + D(("User info response data has the wrong size")); + return PAM_BUF_ERR; + } + memcpy(&expire, buf + sizeof(uint32_t), sizeof(uint32_t)); + if (expire >= DAYSEC) { + expire /= DAYSEC; + unit = "day(s)"; + } else if (expire >= HOURSEC) { + expire /= HOURSEC; + unit = "hour(s)"; + } else if (expire >= MINSEC) { + expire /= MINSEC; + unit = "minute(s)"; + } + + ret = snprintf(user_msg, sizeof(user_msg), + _("Your password will expire in %d %s."), expire, unit); + if (ret < 0 || ret >= sizeof(user_msg)) { + D(("snprintf failed.")); + return PAM_SYSTEM_ERR; + } + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, user_msg, NULL, NULL); + + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + return PAM_SUCCESS; +} + static int user_info_offline_auth_delayed(pam_handle_t *pamh, size_t buflen, uint8_t *buf) { @@ -563,6 +639,12 @@ static int eval_user_info_response(pam_handle_t *pamh, size_t buflen, case SSS_PAM_USER_INFO_OFFLINE_AUTH: ret = user_info_offline_auth(pamh, buflen, buf); break; + case SSS_PAM_USER_INFO_GRACE_LOGIN: + ret = user_info_grace_login(pamh, buflen, buf); + break; + case SSS_PAM_USER_INFO_EXPIRE_WARN: + ret = user_info_expire_warn(pamh, buflen, buf); + break; case SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED: ret = user_info_offline_auth_delayed(pamh, buflen, buf); break; diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 2edd158d..f3872657 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -377,13 +377,22 @@ enum user_info_type { * possible to change the password while * the system is offline. This message * is generated by the PAM responder. */ - SSS_PAM_USER_INFO_CHPASS_ERROR /**< Tell the user that a password change - * failed and optionally give a reason. - * @param Size of the message as unsigned - * 32-bit integer value. A value of 0 - * indicates that no message is following. - * @param String with the specified - * length. */ + SSS_PAM_USER_INFO_CHPASS_ERROR, /**< Tell the user that a password change + * failed and optionally give a reason. + * @param Size of the message as unsigned + * 32-bit integer value. A value of 0 + * indicates that no message is following. + * @param String with the specified + * length. */ + SSS_PAM_USER_INFO_GRACE_LOGIN, /**< Warn the user that the password is + * expired and inform about the remaining + * number of grace logins. + * @param The number of remaining grace + * logins as uint32_t */ + SSS_PAM_USER_INFO_EXPIRE_WARN /**< Warn the user that the password will + * expire soon. + * @param Number of seconds before the user's + * password will expire. */ }; /** * @} |