From ef3dc7ce13049e6c344850bec1241af69735bfe9 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Fri, 30 Apr 2010 16:11:10 +0200 Subject: Try all servers during Kerberos auth The Kerberos backend would previously try only the first server and if it was unreachable, it immediatelly went offline. --- src/providers/krb5/krb5_auth.c | 127 +++++++++++++++++++++++++++++++++-------- 1 file changed, 104 insertions(+), 23 deletions(-) (limited to 'src/providers') diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index e16bc3933..c98a5f78d 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -888,7 +888,6 @@ static void krb5_resolve_kpasswd_done(struct tevent_req *subreq) * kdc seems ok. Password changes are not possible but * authentication. We return an PAM error here, but do not mark the * backend offline. */ - state->pam_status = PAM_AUTHTOK_LOCK_BUSY; state->dp_err = DP_ERR_OK; tevent_req_done(req); @@ -1021,6 +1020,10 @@ done: } } +static struct tevent_req *krb5_next_server(struct tevent_req *req); +static struct tevent_req *krb5_next_kdc(struct tevent_req *req); +static struct tevent_req *krb5_next_kpasswd(struct tevent_req *req); + static void krb5_child_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); @@ -1043,9 +1046,9 @@ static void krb5_child_done(struct tevent_req *subreq) if (ret != EOK) { DEBUG(1, ("child failed (%d [%s])\n", ret, strerror(ret))); if (ret == ETIMEDOUT) { - state->pam_status = PAM_AUTHINFO_UNAVAIL; - state->dp_err = DP_ERR_OFFLINE; - tevent_req_done(req); + if (krb5_next_server(req) == NULL) { + tevent_req_error(req, ENOMEM); + } } else { tevent_req_error(req, ret); } @@ -1076,7 +1079,8 @@ static void krb5_child_done(struct tevent_req *subreq) goto done; } - if (*msg_status != PAM_SUCCESS && *msg_status != PAM_AUTHINFO_UNAVAIL) { + if (*msg_status != PAM_SUCCESS && *msg_status != PAM_AUTHINFO_UNAVAIL && + *msg_status != PAM_AUTHTOK_LOCK_BUSY) { state->pam_status = *msg_status; state->dp_err = DP_ERR_OK; @@ -1099,6 +1103,38 @@ static void krb5_child_done(struct tevent_req *subreq) goto done; } + /* if using a dedicated kpasswd server.. */ + if (kr->kpasswd_srv != NULL) { + /* ..which is unreachable by now.. */ + if (*msg_status == PAM_AUTHTOK_LOCK_BUSY) { + fo_set_port_status(kr->kpasswd_srv, PORT_NOT_WORKING); + /* ..try to resolve next kpasswd server */ + if (krb5_next_kpasswd(req) == NULL) { + tevent_req_error(req, ENOMEM); + } + return; + } else { + fo_set_port_status(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 (*msg_status == PAM_AUTHINFO_UNAVAIL || + (kr->kpasswd_srv == NULL && *msg_status == PAM_AUTHTOK_LOCK_BUSY)) { + if (kr->srv != NULL) { + fo_set_port_status(kr->srv, PORT_NOT_WORKING); + /* ..try to resolve next KDC */ + if (krb5_next_kdc(req) == NULL) { + tevent_req_error(req, ENOMEM); + } + return; + } + } else if (kr->srv != NULL) { + fo_set_port_status(kr->srv, PORT_WORKING); + } + pref_len = strlen(CCACHE_ENV_NAME)+1; if (*msg_len > pref_len && strncmp((const char *) &buf[p], CCACHE_ENV_NAME"=", pref_len) == 0) { @@ -1116,24 +1152,6 @@ static void krb5_child_done(struct tevent_req *subreq) goto done; } - if (*msg_status == PAM_AUTHINFO_UNAVAIL) { - if (kr->srv != NULL) { - fo_set_port_status(kr->srv, PORT_NOT_WORKING); - } - be_mark_offline(state->be_ctx); - kr->is_offline = true; - } else if (kr->srv != NULL) { - fo_set_port_status(kr->srv, PORT_WORKING); - } - - if (kr->kpasswd_srv != NULL) { - if (*msg_status == PAM_AUTHTOK_LOCK_BUSY) { - fo_set_port_status(kr->kpasswd_srv, PORT_NOT_WORKING); - } else { - fo_set_port_status(kr->kpasswd_srv, PORT_WORKING); - } - } - struct sysdb_attrs *attrs; attrs = sysdb_new_attrs(state); ret = sysdb_attrs_add_string(attrs, SYSDB_CCACHE_FILE, kr->ccname); @@ -1161,6 +1179,69 @@ done: } } +static struct tevent_req *krb5_next_server(struct tevent_req *req) +{ + struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state); + struct pam_data *pd = state->pd; + struct tevent_req *next_req = NULL; + + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: + fo_set_port_status(state->kr->srv, PORT_NOT_WORKING); + next_req = krb5_next_kdc(req); + break; + case SSS_PAM_CHAUTHTOK: + case SSS_PAM_CHAUTHTOK_PRELIM: + if (state->kr->kpasswd_srv) { + fo_set_port_status(state->kr->kpasswd_srv, PORT_NOT_WORKING); + next_req = krb5_next_kpasswd(req); + break; + } else { + fo_set_port_status(state->kr->srv, PORT_NOT_WORKING); + next_req = krb5_next_kdc(req); + break; + } + default: + DEBUG(1, ("Unexpected PAM task\n")); + } + + return next_req; +} + +static struct tevent_req *krb5_next_kdc(struct tevent_req *req) +{ + struct tevent_req *next_req; + struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state); + + next_req = be_resolve_server_send(state, state->ev, + state->be_ctx, + state->krb5_ctx->service->name); + if (next_req == NULL) { + DEBUG(1, ("be_resolve_server_send failed.\n")); + return NULL; + } + tevent_req_set_callback(next_req, krb5_resolve_kdc_done, req); + + return next_req; +} + +static struct tevent_req *krb5_next_kpasswd(struct tevent_req *req) +{ + struct tevent_req *next_req; + struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state); + + next_req = be_resolve_server_send(state, state->ev, + state->be_ctx, + state->krb5_ctx->kpasswd_service->name); + if (next_req == NULL) { + DEBUG(1, ("be_resolve_server_send failed.\n")); + return NULL; + } + tevent_req_set_callback(next_req, krb5_resolve_kpasswd_done, req); + + return next_req; +} + static void krb5_save_ccname_done(struct tevent_req *req) { struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state); -- cgit