From 9abffdacc216e2186b24eaf8efe0dfab7b17ab5d Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 22 Oct 2015 16:48:21 +0100 Subject: [PATCH] If samlogon for trusted child domain user fails attempt to reroute request When kerboros authentication fails we may attempt to fallback to samlogon. However schannel netlogon connections from a domain child winbindd to the domain controller when that domain is not 'our' domain are dissallowed and thus the credentials are not available. The samlogon request when this happens cannot be serviced. This patch detects if the samlogon fallback will occur for a non primary domain winbindd child, in this case it will return a status of NT_STATUS_MORE_PROCESSING_REQUIRED to the parent. The parent then will then retry the authentication by chosing and sending the request to a domain child that should be able to handle it. Signed-off-by: Noel Power --- nsswitch/winbind_struct_protocol.h | 7 +++++ source3/winbindd/winbindd_pam.c | 24 ++++++++++++++- source3/winbindd/winbindd_pam_auth.c | 58 ++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/nsswitch/winbind_struct_protocol.h b/nsswitch/winbind_struct_protocol.h index 0dffa4b..3400e14 100644 --- a/nsswitch/winbind_struct_protocol.h +++ b/nsswitch/winbind_struct_protocol.h @@ -224,6 +224,13 @@ typedef struct winbindd_gr { /* Flag to tell winbind the NTLMv2 blob is too big for the struct and is in the * extra_data field */ #define WBFLAG_BIG_NTLMV2_BLOB 0x00010000 +/* + * Flag to tell winbind a previous error is stored in the + * extra_data field. This is used to pass any KRB5 error (which occured in + * trusted domain winbindd child) to the primary domain winbindd child + * which is processing the samlogon. + */ +#define WBFLAG_INTERNAL_PREV_KRB5_ERROR 0x00020000 #define WINBINDD_MAX_EXTRA_DATA (128*1024) diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c index 8910423..d9ab0d2 100644 --- a/source3/winbindd/winbindd_pam.c +++ b/source3/winbindd/winbindd_pam.c @@ -1753,6 +1753,16 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain, } if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) { + if (domain->primary == false) { + /* + * return error to parent and indicate we + * need to do further processing + */ + state->response->data.auth.reject_reason + = NT_STATUS_V(result); + result = NT_STATUS_MORE_PROCESSING_REQUIRED; + goto done; + } DEBUG(3,("falling back to samlogon\n")); goto sam_logon; } else { @@ -1763,13 +1773,25 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain, sam_logon: /* Check for Samlogon authentication */ if (domain->online) { + if (state->request->flags & WBFLAG_INTERNAL_PREV_KRB5_ERROR) { + uint32_t err; + if (state->request->extra_len != sizeof(err)) { + DEBUG(3,("winbindd_dual_pam_auth_samlogon unexpected length for extra_data %d\n", state->request->extra_len)); + result = NT_STATUS_INVALID_PARAMETER; + goto done; + } + memcpy(&err, state->request->extra_data.data, + state->request->extra_len); + krb5_result = NT_STATUS(err); + DEBUG(10,("winbindd_dual_pam_auth_samlogon received previous error from parent: %s\n", + nt_errstr(krb5_result))); + } result = winbindd_dual_pam_auth_samlogon( state->mem_ctx, domain, state->request->data.auth.user, state->request->data.auth.pass, state->request->flags, &info3); - if (NT_STATUS_IS_OK(result)) { DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n")); /* add the Krb5 err if we have one */ diff --git a/source3/winbindd/winbindd_pam_auth.c b/source3/winbindd/winbindd_pam_auth.c index 4f963a3..0810e20 100644 --- a/source3/winbindd/winbindd_pam_auth.c +++ b/source3/winbindd/winbindd_pam_auth.c @@ -53,6 +53,14 @@ struct tevent_req *winbindd_pam_auth_send(TALLOC_CTX *mem_ctx, DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)cli->pid, request->data.auth.user)); + /* + * Prevent data associated with handling of internal flag + * WBFLAG_INTERNAL_PREV_KRB5_ERROR from being used, the flag + * and/or data could be erronously set (or uninitialised) by client. + */ + request->flags &= ~WBFLAG_INTERNAL_PREV_KRB5_ERROR; + request->extra_len = 0; + request->extra_data.data = NULL; if (!check_request_flags(request->flags)) { tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); return tevent_req_post(req, ev); @@ -97,6 +105,7 @@ static void winbindd_pam_auth_done(struct tevent_req *subreq) struct winbindd_pam_auth_state *state = tevent_req_data( req, struct winbindd_pam_auth_state); int res, err; + NTSTATUS status; res = wb_domain_request_recv(subreq, state, &state->response, &err); TALLOC_FREE(subreq); @@ -104,6 +113,55 @@ static void winbindd_pam_auth_done(struct tevent_req *subreq) tevent_req_nterror(req, map_nt_error_from_unix(err)); return; } + status = NT_STATUS(state->response->data.auth.nt_status); + + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + + struct winbindd_domain *domain = NULL; + struct winbindd_request *request = state->request; + fstring name_domain, name_user; + uint32_t flags = request->flags; + + if (!parse_domain_user(request->data.auth.user, + name_domain, name_user)) { + tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER); + tevent_req_post(req, winbind_event_context()); + return; + } + + flags &= ~WBFLAG_PAM_CONTACT_TRUSTDOM; + domain = find_auth_domain(flags, name_domain); + if (domain) { + uint32_t krb5err = + state->response->data.auth.reject_reason; + /* don't attempt kerberos again, samlogon instead */ + state->request->flags &= ~WBFLAG_PAM_KRB5; + /* + * transfer krb5 error to the child winbindd handling + * the samlogon fallback. + */ + if (krb5err) { + state->request->flags |= WBFLAG_INTERNAL_PREV_KRB5_ERROR; + state->request->extra_data.data = + talloc_zero_array(state, char, + sizeof(krb5err)); + state->request->extra_len = sizeof(krb5err); + memcpy(state->request->extra_data.data, + &krb5err, sizeof(krb5err)); + } + subreq = wb_domain_request_send(state, + winbind_event_context(), + domain, + state->request); + if (!subreq) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + tevent_req_set_callback(subreq, + winbindd_pam_auth_done, req); + return; + } + } tevent_req_done(req); }