diff options
-rw-r--r-- | src/providers/krb5/krb5_auth.c | 147 | ||||
-rw-r--r-- | src/providers/krb5/krb5_child.c | 389 | ||||
-rw-r--r-- | src/util/util_errors.c | 7 | ||||
-rw-r--r-- | src/util/util_errors.h | 5 |
4 files changed, 251 insertions, 297 deletions
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 0b56f3a5..e41e1a1e 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -927,59 +927,24 @@ static void krb5_auth_done(struct tevent_req *subreq) /* If the child request failed, but did not return an offline error code, * return with the status */ - if (res->msg_status != PAM_SUCCESS && - res->msg_status != PAM_AUTHINFO_UNAVAIL && - res->msg_status != PAM_AUTHTOK_LOCK_BUSY && - res->msg_status != PAM_NEW_AUTHTOK_REQD) { - state->pam_status = res->msg_status; - state->dp_err = DP_ERR_OK; - ret = EOK; - goto done; - } else { - state->pam_status = res->msg_status; - } - - /* If the password is expired we can safely remove the ccache from the - * cache and disk if it is not actively used anymore. This will allow to - * create a new random ccache if sshd with privilege separation is used. */ - if (res->msg_status == PAM_NEW_AUTHTOK_REQD) { - if (pd->cmd == SSS_PAM_AUTHENTICATE && !kr->active_ccache) { - if (kr->old_ccname != NULL) { - ret = safe_remove_old_ccache_file(kr->cc_be, kr->upn, - kr->old_ccname, "dummy"); - if (ret != EOK) { - DEBUG(1, ("Failed to remove old ccache file [%s], " - "please remove it manually.\n", kr->old_ccname)); - } - - ret = krb5_delete_ccname(state, state->sysdb, state->domain, - pd->user, kr->old_ccname); - if (ret != EOK) { - DEBUG(1, ("krb5_delete_ccname failed.\n")); - } - } + switch (res->msg_status) { + case ERR_OK: + /* If the child request was successful and we run the first pass of the + * change password request just return success. */ + if (pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) { + state->pam_status = PAM_SUCCESS; + state->dp_err = DP_ERR_OK; + ret = EOK; + goto done; } + break; - state->pam_status = res->msg_status; - state->dp_err = DP_ERR_OK; - ret = EOK; - goto done; - } - - /* If the child request was successful and we run the first pass of the - * change password request just return success. */ - if (res->msg_status == PAM_SUCCESS && pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) { - state->pam_status = PAM_SUCCESS; - state->dp_err = DP_ERR_OK; - ret = EOK; - goto done; - } + case ERR_NETWORK_IO: + if (kr->kpasswd_srv != NULL && + (pd->cmd == SSS_PAM_CHAUTHTOK || + pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) { + /* if using a dedicated kpasswd server for a chpass operation... */ - /* if using a dedicated kpasswd server for a chpass operation... */ - if (kr->kpasswd_srv != NULL && - (pd->cmd == SSS_PAM_CHAUTHTOK || pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) { - /* ..which is unreachable by now.. */ - if (res->msg_status == PAM_AUTHTOK_LOCK_BUSY) { be_fo_set_port_status(state->be_ctx, state->krb5_ctx->kpasswd_service->name, kr->kpasswd_srv, PORT_NOT_WORKING); @@ -995,20 +960,10 @@ static void krb5_auth_done(struct tevent_req *subreq) } tevent_req_set_callback(subreq, krb5_auth_resolve_done, req); return; - } else { + } else if (kr->srv != NULL) { + /* failed to use the KDC... */ be_fo_set_port_status(state->be_ctx, - state->krb5_ctx->kpasswd_service->name, - kr->kpasswd_srv, PORT_WORKING); - } - } - - /* if the KDC for auth (PAM_AUTHINFO_UNAVAIL) or - * chpass (PAM_AUTHTOK_LOCK_BUSY) was not available while using KDC - * also for chpass operation... */ - if (res->msg_status == PAM_AUTHINFO_UNAVAIL || - (kr->kpasswd_srv == NULL && res->msg_status == PAM_AUTHTOK_LOCK_BUSY)) { - if (kr->srv != NULL) { - be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name, + state->krb5_ctx->service->name, kr->srv, PORT_NOT_WORKING); /* ..try to resolve next KDC */ state->search_kpasswd = false; @@ -1023,7 +978,63 @@ static void krb5_auth_done(struct tevent_req *subreq) tevent_req_set_callback(subreq, krb5_auth_resolve_done, req); return; } + break; + + case ERR_CREDS_EXPIRED: + /* If the password is expired we can safely remove the ccache from the + * cache and disk if it is not actively used anymore. This will allow + * to create a new random ccache if sshd with privilege separation is + * used. */ + if (pd->cmd == SSS_PAM_AUTHENTICATE && !kr->active_ccache) { + if (kr->old_ccname != NULL) { + ret = safe_remove_old_ccache_file(kr->cc_be, kr->upn, + kr->old_ccname, "dummy"); + if (ret != EOK) { + DEBUG(1, ("Failed to remove old ccache file [%s], " + "please remove it manually.\n", kr->old_ccname)); + } + + ret = krb5_delete_ccname(state, state->sysdb, state->domain, + pd->user, kr->old_ccname); + if (ret != EOK) { + DEBUG(1, ("krb5_delete_ccname failed.\n")); + } + } + } + + state->pam_status = PAM_NEW_AUTHTOK_REQD; + state->dp_err = DP_ERR_OK; + ret = EOK; + goto done; + + case ERR_NO_CREDS: + state->pam_status = PAM_CRED_UNAVAIL; + state->dp_err = DP_ERR_OK; + ret = EOK; + goto done; + + case ERR_AUTH_FAILED: + state->pam_status = PAM_AUTH_ERR; + state->dp_err = DP_ERR_OK; + ret = EOK; + goto done; + + default: + state->pam_status = PAM_SYSTEM_ERR; + state->dp_err = DP_ERR_OK; + ret = EOK; + goto done; + } + + if (kr->kpasswd_srv != NULL && + (pd->cmd == SSS_PAM_CHAUTHTOK || + pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM)) { + /* found a dedicated kpasswd server for a chpass operation */ + be_fo_set_port_status(state->be_ctx, + state->krb5_ctx->service->name, + kr->kpasswd_srv, PORT_WORKING); } else if (kr->srv != NULL) { + /* found a KDC */ be_fo_set_port_status(state->be_ctx, state->krb5_ctx->service->name, kr->srv, PORT_WORKING); } @@ -1062,11 +1073,13 @@ static void krb5_auth_done(struct tevent_req *subreq) goto done; } - if (res->msg_status == PAM_SUCCESS && - dp_opt_get_int(kr->krb5_ctx->opts, KRB5_RENEW_INTERVAL) > 0 && - (pd->cmd == SSS_PAM_AUTHENTICATE || pd->cmd == SSS_CMD_RENEW || + if (res->msg_status == ERR_OK && + (dp_opt_get_int(kr->krb5_ctx->opts, KRB5_RENEW_INTERVAL) > 0) && + (pd->cmd == SSS_PAM_AUTHENTICATE || + pd->cmd == SSS_CMD_RENEW || pd->cmd == SSS_PAM_CHAUTHTOK) && - res->tgtt.renew_till > res->tgtt.endtime && kr->ccname != NULL) { + (res->tgtt.renew_till > res->tgtt.endtime) && + (kr->ccname != NULL)) { DEBUG(7, ("Adding [%s] for automatic renewal.\n", kr->ccname)); ret = add_tgt_to_renew_table(kr->krb5_ctx, kr->ccname, &(res->tgtt), pd, kr->upn); diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 42dffe7b..9cc0f554 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -525,9 +525,11 @@ create_ccache(uid_t uid, gid_t gid, krb5_context ctx, return EINVAL; /* Should never get here */ } -static errno_t pack_response_packet(struct response *resp, int status, - struct pam_data *pd) +static errno_t pack_response_packet(TALLOC_CTX *mem_ctx, errno_t error, + struct response_data *resp_list, + uint8_t **_buf, size_t *_len) { + uint8_t *buf; size_t size = 0; size_t p = 0; struct response_data *pdr; @@ -544,116 +546,69 @@ static errno_t pack_response_packet(struct response *resp, int status, size = sizeof(int32_t); - pdr = pd->resp_list; - while (pdr != NULL) { + for (pdr = resp_list; pdr != NULL; pdr = pdr->next) { size += 2*sizeof(int32_t) + pdr->len; - pdr = pdr->next; } - resp->buf = talloc_array(resp, uint8_t, size); - if (!resp->buf) { + buf = talloc_array(mem_ctx, uint8_t, size); + if (!buf) { DEBUG(1, ("Insufficient memory to create message.\n")); return ENOMEM; } - SAFEALIGN_SET_INT32(&resp->buf[p], status, &p); + SAFEALIGN_SET_INT32(&buf[p], error, &p); - pdr = pd->resp_list; - while(pdr != NULL) { - SAFEALIGN_SET_INT32(&resp->buf[p], pdr->type, &p); - SAFEALIGN_SET_INT32(&resp->buf[p], pdr->len, &p); - safealign_memcpy(&resp->buf[p], pdr->data, pdr->len, &p); - - pdr = pdr->next; + for (pdr = resp_list; pdr != NULL; pdr = pdr->next) { + SAFEALIGN_SET_INT32(&buf[p], pdr->type, &p); + SAFEALIGN_SET_INT32(&buf[p], pdr->len, &p); + safealign_memcpy(&buf[p], pdr->data, pdr->len, &p); } - resp->size = p; - DEBUG(SSSDBG_TRACE_INTERNAL, ("response packet size: [%d]\n", p)); + *_buf = buf; + *_len = p; return EOK; } -static struct response *prepare_response_message(struct krb5_req *kr, - krb5_error_code kerr, - int pam_status) +static errno_t k5c_attach_ccname_msg(struct krb5_req *kr) { char *msg = NULL; - const char *krb5_msg = NULL; int ret; - struct response *resp; - resp = talloc_zero(kr, struct response); - if (resp == NULL) { - DEBUG(1, ("Initializing response failed.\n")); - return NULL; + if (kr->ccname == NULL) { + DEBUG(1, ("Error obtaining ccname.\n")); + return ERR_INTERNAL; } - DEBUG(SSSDBG_TRACE_FUNC, ("Building response for result [%d]\n", kerr)); - - if (kerr == 0) { - if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) { - pam_status = PAM_SUCCESS; - ret = EOK; - } else if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) { - ret = EOK; - } else { - if (kr->ccname == NULL) { - DEBUG(1, ("Error obtaining ccname.\n")); - return NULL; - } - - msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, kr->ccname); - if (msg == NULL) { - DEBUG(1, ("talloc_asprintf failed.\n")); - return NULL; - } - - pam_status = PAM_SUCCESS; - ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM, strlen(msg) + 1, - (uint8_t *) msg); - talloc_zfree(msg); - } - } else { - krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr); - if (krb5_msg == NULL) { - DEBUG(1, ("sss_krb5_get_error_message failed.\n")); - return NULL; - } - - ret = pam_add_response(kr->pd, SSS_PAM_SYSTEM_INFO, - strlen(krb5_msg) + 1, - (const uint8_t *) krb5_msg); - sss_krb5_free_error_message(krb5_error_ctx, krb5_msg); - } - if (ret != EOK) { - DEBUG(1, ("pam_add_response failed.\n")); + msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, kr->ccname); + if (msg == NULL) { + DEBUG(1, ("talloc_asprintf failed.\n")); + return ENOMEM; } - ret = pack_response_packet(resp, pam_status, kr->pd); - if (ret != EOK) { - DEBUG(1, ("pack_response_packet failed.\n")); - return NULL; - } + ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM, + strlen(msg) + 1, (uint8_t *)msg); + talloc_zfree(msg); - return resp; + return ret; } -static errno_t k5c_send_data(struct krb5_req *kr, int fd, - krb5_error_code kerr, int status) +static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error) { - struct response *resp; size_t written; + uint8_t *buf; + size_t len; int ret; - resp = prepare_response_message(kr, kerr, status); - if (resp == NULL) { - DEBUG(1, ("prepare_response_message failed.\n")); - return ENOMEM; + ret = pack_response_packet(kr, error, kr->pd->resp_list, &buf, &len); + if (ret != EOK) { + DEBUG(1, ("pack_response_packet failed.\n")); + return ret; } errno = 0; - written = sss_atomic_write_s(fd, resp->buf, resp->size); + written = sss_atomic_write_s(fd, buf, len); if (written == -1) { ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, @@ -661,10 +616,10 @@ static errno_t k5c_send_data(struct krb5_req *kr, int fd, return ret; } - if (written != resp->size) { + if (written != len) { DEBUG(SSSDBG_CRIT_FAILURE, ("Write error, wrote [%d] bytes, expected [%d]\n", - written, resp->size)); + written, len)); return EOK; } @@ -915,10 +870,9 @@ done: static krb5_error_code get_and_save_tgt(struct krb5_req *kr, const char *password) { - krb5_error_code kerr = 0; - int ret; const char *realm_name; int realm_length; + krb5_error_code kerr; kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, kr->options, @@ -957,10 +911,10 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, /* We drop root privileges which were needed to read the keytab file * for the validation of the credentials or for FAST here to run the * ccache I/O operations with user privileges. */ - ret = become_user(kr->uid, kr->gid); - if (ret != EOK) { + kerr = become_user(kr->uid, kr->gid); + if (kerr != 0) { DEBUG(1, ("become_user failed.\n")); - return ret; + return kerr; } } @@ -973,8 +927,8 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, goto done; } - ret = add_ticket_times_and_upn_to_response(kr); - if (ret != EOK) { + kerr = add_ticket_times_and_upn_to_response(kr); + if (kerr != 0) { DEBUG(1, ("add_ticket_times_and_upn_to_response failed.\n")); } @@ -987,59 +941,46 @@ done: } -static int kerr_handle_error(krb5_error_code kerr) +static errno_t map_krb5_error(krb5_error_code kerr) { - int pam_status; - KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + switch (kerr) { - case KRB5_LIBOS_CANTREADPWD: - pam_status = PAM_CRED_UNAVAIL; - break; - case KRB5_KDC_UNREACH: - pam_status = PAM_AUTHINFO_UNAVAIL; - break; - case KRB5KDC_ERR_KEY_EXP: - pam_status = PAM_NEW_AUTHTOK_REQD; - break; - case KRB5KRB_AP_ERR_BAD_INTEGRITY: - pam_status = PAM_AUTH_ERR; - break; - case KRB5_PREAUTH_FAILED: - case KRB5KDC_ERR_PREAUTH_FAILED: - pam_status = PAM_CRED_ERR; - break; - default: - pam_status = PAM_SYSTEM_ERR; - break; - } + case 0: + return ERR_OK; - return pam_status; -} + case KRB5_LIBOS_CANTREADPWD: + return ERR_NO_CREDS; -static int kerr_to_status(krb5_error_code kerr) -{ - if (kerr == 0) { - return PAM_SUCCESS; - } + case KRB5_KDC_UNREACH: + return ERR_NETWORK_IO; + + case KRB5KDC_ERR_KEY_EXP: + return ERR_CREDS_EXPIRED; - return kerr_handle_error(kerr); + case KRB5KRB_AP_ERR_BAD_INTEGRITY: + case KRB5_PREAUTH_FAILED: + case KRB5KDC_ERR_PREAUTH_FAILED: + return ERR_AUTH_FAILED; + + default: + return ERR_INTERNAL; + } } -static errno_t changepw_child(struct krb5_req *kr, int *_status) +static errno_t changepw_child(struct krb5_req *kr, bool prelim) { int ret; krb5_error_code kerr = 0; const char *password = NULL; const char *newpassword = NULL; - int pam_status = PAM_SYSTEM_ERR; int result_code = -1; krb5_data result_code_string; krb5_data result_string; char *user_error_message = NULL; size_t user_resp_len; uint8_t *user_resp; - krb5_prompter_fct prompter = sss_krb5_prompter; + krb5_prompter_fct prompter = NULL; const char *realm_name; int realm_length; krb5_get_init_creds_opt *chagepw_options; @@ -1050,20 +991,18 @@ static errno_t changepw_child(struct krb5_req *kr, int *_status) if (ret != EOK) { DEBUG(1, ("Failed to fetch current password [%d] %s.\n", ret, strerror(ret))); - pam_status = PAM_CRED_INSUFFICIENT; - kerr = KRB5KRB_ERR_GENERIC; - goto done; + return ERR_NO_CREDS; } - if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) { + if (!prelim) { /* We do not need a password expiration warning here. */ - prompter = NULL; + prompter = sss_krb5_prompter; } kerr = get_changepw_options(kr->ctx, &chagepw_options); if (kerr != 0) { DEBUG(SSSDBG_OP_FAILURE, ("get_changepw_options failed.\n")); - goto done; + return kerr; } sss_krb5_princ_realm(kr->ctx, kr->princ, &realm_name, &realm_length); @@ -1077,27 +1016,24 @@ static errno_t changepw_child(struct krb5_req *kr, int *_status) chagepw_options); sss_krb5_get_init_creds_opt_free(kr->ctx, chagepw_options); if (kerr != 0) { - pam_status = kerr_handle_error(kerr); - goto done; + return kerr; } sss_authtok_set_empty(&kr->pd->authtok); - if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM) { + if (prelim) { DEBUG(SSSDBG_TRACE_LIBS, ("Initial authentication for change password operation " "successful.\n")); krb5_free_cred_contents(kr->ctx, kr->creds); - pam_status = PAM_SUCCESS; - goto done; + return EOK; } ret = sss_authtok_get_password(&kr->pd->newauthtok, &newpassword, NULL); if (ret != EOK) { DEBUG(1, ("Failed to fetch new password [%d] %s.\n", ret, strerror(ret))); - kerr = KRB5KRB_ERR_GENERIC; - goto done; + return ERR_NO_CREDS; } memset(&result_code_string, 0, sizeof(krb5_data)); @@ -1107,15 +1043,12 @@ static errno_t changepw_child(struct krb5_req *kr, int *_status) &result_code_string, &result_string); if (kerr == KRB5_KDC_UNREACH) { - pam_status = PAM_AUTHTOK_LOCK_BUSY; - goto done; + return ERR_NETWORK_IO; } if (kerr != 0 || result_code != 0) { if (kerr != 0) { KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); - } else { - kerr = KRB5KRB_ERR_GENERIC; } if (result_code_string.length > 0) { @@ -1153,8 +1086,7 @@ static errno_t changepw_child(struct krb5_req *kr, int *_status) } } - pam_status = PAM_AUTHTOK_ERR; - goto done; + return ERR_CHPASS_FAILED; } krb5_free_cred_contents(kr->ctx, kr->creds); @@ -1163,88 +1095,90 @@ static errno_t changepw_child(struct krb5_req *kr, int *_status) sss_authtok_set_empty(&kr->pd->newauthtok); - pam_status = kerr_to_status(kerr); - -done: - *_status = pam_status; - return kerr; + if (kerr == 0) { + kerr = k5c_attach_ccname_msg(kr); + } + return map_krb5_error(kerr); } -static errno_t tgt_req_child(struct krb5_req *kr, int *_status) +static errno_t tgt_req_child(struct krb5_req *kr) { - int ret; - krb5_error_code kerr = 0; - const char *password = NULL; - int pam_status = PAM_SYSTEM_ERR; krb5_get_init_creds_opt *chagepw_options; + const char *password = NULL; + krb5_error_code kerr; + int ret; DEBUG(SSSDBG_TRACE_LIBS, ("Attempting to get a TGT\n")); ret = sss_authtok_get_password(&kr->pd->authtok, &password, NULL); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("Unknown authtok type\n")); - pam_status = PAM_CRED_INSUFFICIENT; - kerr = KRB5KRB_ERR_GENERIC; - goto done; + switch (ret) { + if (ret == EACCES) { + DEBUG(SSSDBG_OP_FAILURE, ("Invalid authtok type\n")); + return ERR_INVALID_CRED_TYPE; + } + DEBUG(SSSDBG_OP_FAILURE, ("No credentials available\n")); + return ERR_NO_CREDS; } kerr = get_and_save_tgt(kr, password); + if (kerr != KRB5KDC_ERR_KEY_EXP) { + if (kerr == 0) { + kerr = k5c_attach_ccname_msg(kr); + } + ret = map_krb5_error(kerr); + goto done; + } + /* If the password is expired the KDC will always return KRB5KDC_ERR_KEY_EXP regardless if the supplied password is correct or not. In general the password can still be used to get a changepw ticket. So we validate the password by trying to get a changepw ticket. */ - if (kerr == KRB5KDC_ERR_KEY_EXP) { - DEBUG(SSSDBG_TRACE_LIBS, ("Password was expired\n")); - kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, - kr->options, - NULL, NULL); - if (kerr != 0) { - KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); - DEBUG(1, ("Failed to unset expire callback, continue ...\n")); - } + DEBUG(SSSDBG_TRACE_LIBS, ("Password was expired\n")); + kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, + kr->options, + NULL, NULL); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + DEBUG(1, ("Failed to unset expire callback, continue ...\n")); + } - kerr = get_changepw_options(kr->ctx, &chagepw_options); - if (kerr != 0) { - DEBUG(SSSDBG_OP_FAILURE, ("get_changepw_options failed.\n")); - goto done; - } + kerr = get_changepw_options(kr->ctx, &chagepw_options); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("get_changepw_options failed.\n")); + return kerr; + } - kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, - discard_const(password), - sss_krb5_prompter, kr, 0, - SSSD_KRB5_CHANGEPW_PRINCIPAL, - chagepw_options); + kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, + discard_const(password), + sss_krb5_prompter, kr, 0, + SSSD_KRB5_CHANGEPW_PRINCIPAL, + chagepw_options); - sss_krb5_get_init_creds_opt_free(kr->ctx, chagepw_options); + sss_krb5_get_init_creds_opt_free(kr->ctx, chagepw_options); - krb5_free_cred_contents(kr->ctx, kr->creds); - if (kerr == 0) { - kerr = KRB5KDC_ERR_KEY_EXP; - } + krb5_free_cred_contents(kr->ctx, kr->creds); + if (kerr == 0) { + ret = ERR_CREDS_EXPIRED; + } else { + ret = map_krb5_error(kerr); } - sss_authtok_set_empty(&kr->pd->authtok); - - pam_status = kerr_to_status(kerr); - done: - *_status = pam_status; - return kerr; + sss_authtok_set_empty(&kr->pd->authtok); + return ret; } -static errno_t kuserok_child(struct krb5_req *kr, int *_status) +static errno_t kuserok_child(struct krb5_req *kr) { krb5_boolean access_allowed; - int ret; krb5_error_code kerr; DEBUG(SSSDBG_TRACE_LIBS, ("Verifying if principal can log in as user\n")); /* krb5_kuserok tries to verify that kr->pd->user is a locally known * account, so we have to unset _SSS_LOOPS to make getpwnam() work. */ - ret = unsetenv("_SSS_LOOPS"); - if (ret != EOK) { + if (unsetenv("_SSS_LOOPS") != 0) { DEBUG(1, ("Failed to unset _SSS_LOOPS, " "krb5_kuserok will most certainly fail.\n")); } @@ -1259,17 +1193,19 @@ static errno_t kuserok_child(struct krb5_req *kr, int *_status) DEBUG(SSSDBG_TRACE_LIBS, ("Access was %s\n", access_allowed ? "allowed" : "denied")); - *_status = access_allowed ? 0 : 1; - return kerr; + if (access_allowed) { + return EOK; + } + + return ERR_AUTH_DENIED; } -static errno_t renew_tgt_child(struct krb5_req *kr, int *_status) +static errno_t renew_tgt_child(struct krb5_req *kr) { - int ret; - int status = PAM_AUTHTOK_ERR; - int kerr; const char *ccname; krb5_ccache ccache = NULL; + krb5_error_code kerr; + int ret; DEBUG(SSSDBG_TRACE_LIBS, ("Renewing a ticket\n")); @@ -1278,8 +1214,7 @@ static errno_t renew_tgt_child(struct krb5_req *kr, int *_status) DEBUG(SSSDBG_OP_FAILURE, ("Unsupported authtok type for TGT renewal [%d].\n", sss_authtok_get_type(&kr->pd->authtok))); - kerr = EINVAL; - goto done; + return ERR_INVALID_CRED_TYPE; } kerr = krb5_cc_resolve(kr->ctx, ccname, &ccache); @@ -1290,7 +1225,6 @@ static errno_t renew_tgt_child(struct krb5_req *kr, int *_status) kerr = krb5_get_renewed_creds(kr->ctx, kr->creds, kr->princ, ccache, NULL); if (kerr != 0) { - status = kerr_handle_error(kerr); goto done; } @@ -1309,10 +1243,9 @@ static errno_t renew_tgt_child(struct krb5_req *kr, int *_status) /* We drop root privileges which were needed to read the keytab file * for the validation of the credentials or for FAST here to run the * ccache I/O operations with user privileges. */ - ret = become_user(kr->uid, kr->gid); - if (ret != EOK) { + kerr = become_user(kr->uid, kr->gid); + if (kerr != 0) { DEBUG(1, ("become_user failed.\n")); - kerr = ret; goto done; } } @@ -1329,13 +1262,12 @@ static errno_t renew_tgt_child(struct krb5_req *kr, int *_status) goto done; } - ret = add_ticket_times_and_upn_to_response(kr); - if (ret != EOK) { + kerr = add_ticket_times_and_upn_to_response(kr); + if (kerr != 0) { DEBUG(1, ("add_ticket_times_and_upn_to_response failed.\n")); } - status = PAM_SUCCESS; - kerr = 0; + kerr = k5c_attach_ccname_msg(kr); done: krb5_free_cred_contents(kr->ctx, kr->creds); @@ -1344,26 +1276,24 @@ done: krb5_cc_close(kr->ctx, ccache); } - *_status = status; - return kerr; + return map_krb5_error(kerr); } -static errno_t create_empty_ccache(struct krb5_req *kr, int *_status) +static errno_t create_empty_ccache(struct krb5_req *kr) { - int ret; - int pam_status = PAM_SUCCESS; + krb5_error_code kerr; DEBUG(SSSDBG_TRACE_LIBS, ("Creating empty ccache\n")); - ret = create_ccache(kr->uid, kr->gid, kr->ctx, - kr->princ, kr->ccname, NULL); - if (ret != 0) { - KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, ret); - pam_status = PAM_SYSTEM_ERR; + kerr = create_ccache(kr->uid, kr->gid, kr->ctx, + kr->princ, kr->ccname, NULL); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + } else { + kerr = k5c_attach_ccname_msg(kr); } - *_status = pam_status; - return ret; + return map_krb5_error(kerr); } static errno_t unpack_authtok(TALLOC_CTX *mem_ctx, struct sss_auth_token *tok, @@ -1898,7 +1828,6 @@ int main(int argc, const char *argv[]) int opt; poptContext pc; int debug_fd = -1; - int status; errno_t ret; struct poptOption long_options[] = { @@ -1972,30 +1901,32 @@ int main(int argc, const char *argv[]) /* If we are offline, we need to create an empty ccache file */ if (offline) { DEBUG(SSSDBG_TRACE_FUNC, ("Will perform offline auth\n")); - ret = create_empty_ccache(kr, &status); + ret = create_empty_ccache(kr); } else { DEBUG(SSSDBG_TRACE_FUNC, ("Will perform online auth\n")); - ret = tgt_req_child(kr, &status); + ret = tgt_req_child(kr); } break; case SSS_PAM_CHAUTHTOK: - case SSS_PAM_CHAUTHTOK_PRELIM: DEBUG(SSSDBG_TRACE_FUNC, ("Will perform password change\n")); - ret = changepw_child(kr, &status); + ret = changepw_child(kr, false); + break; + case SSS_PAM_CHAUTHTOK_PRELIM: + DEBUG(SSSDBG_TRACE_FUNC, ("Will perform password change checks\n")); + ret = changepw_child(kr, true); break; case SSS_PAM_ACCT_MGMT: DEBUG(SSSDBG_TRACE_FUNC, ("Will perform account management\n")); - ret = kuserok_child(kr, &status); + ret = kuserok_child(kr); break; case SSS_CMD_RENEW: - if (!offline) { - DEBUG(SSSDBG_TRACE_FUNC, ("Will perform ticket renewal\n")); - ret = renew_tgt_child(kr, &status); - } else { + if (offline) { DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot renew TGT while offline\n")); ret = KRB5_KDC_UNREACH; goto done; } + DEBUG(SSSDBG_TRACE_FUNC, ("Will perform ticket renewal\n")); + ret = renew_tgt_child(kr); break; default: DEBUG(1, ("PAM command [%d] not supported.\n", kr->pd->cmd)); @@ -2003,7 +1934,7 @@ int main(int argc, const char *argv[]) goto done; } - ret = k5c_send_data(kr, STDOUT_FILENO, ret, status); + ret = k5c_send_data(kr, STDOUT_FILENO, ret); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to send reply\n")); } diff --git a/src/util/util_errors.c b/src/util/util_errors.c index c196aae3..1760c8d8 100644 --- a/src/util/util_errors.c +++ b/src/util/util_errors.c @@ -28,10 +28,15 @@ struct err_string error_to_str[] = { { "Invalid Error" }, /* ERR_INVALID */ { "Internal Error" }, /* ERR_INTERNAL */ { "Account Unknown" }, /* ERR_ACCOUNT_UNKNOWN */ + { "Invalid credential type" }, /* ERR_INVALID_CRED_TYPE */ + { "No credentials available" }, /* ERR_NO_CREDS */ + { "Credentials are expired" }, /* ERR_CREDS_EXPIRED */ { "No cached credentials available" }, /* ERR_NO_CACHED_CREDS */ { "Cached credentials are expired" }, /* ERR_CACHED_CREDS_EXPIRED */ { "Authentication Denied" }, /* ERR_AUTH_DENIED */ - { "Authentication Failed" }, /* ERR_AUTH_DENIED */ + { "Authentication Failed" }, /* ERR_AUTH_FAILED */ + { "Password Change Failed" }, /* ERR_CHPASS_FAILED */ + { "Network I/O Error" }, /* ERR_NETWORK_IO */ }; diff --git a/src/util/util_errors.h b/src/util/util_errors.h index 870d9d44..9292c995 100644 --- a/src/util/util_errors.h +++ b/src/util/util_errors.h @@ -50,10 +50,15 @@ enum sssd_errors { ERR_INVALID = ERR_BASE + 0, ERR_INTERNAL, ERR_ACCOUNT_UNKNOWN, + ERR_INVALID_CRED_TYPE, + ERR_NO_CREDS, + ERR_CREDS_EXPIRED, ERR_NO_CACHED_CREDS, ERR_CACHED_CREDS_EXPIRED, ERR_AUTH_DENIED, ERR_AUTH_FAILED, + ERR_CHPASS_FAILED, + ERR_NETWORK_IO, ERR_LAST /* ALWAYS LAST */ }; |