From 8cee914d3f1644839e34ab61f0373eec8aa0f3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Deschner?= Date: Fri, 7 Jan 2011 17:28:29 +0100 Subject: [PATCH] s3-winbindd: let winbind try to use samlogon validation level 6. (bug #7945) The benefit of this that it makes us more robust to secure channel resets triggered from tools outside the winbind process. Long term we need to have a shared tdb secure channel store though as well. Guenther Signed-off-by: Stefan Metzmacher (similar to commit f60398d7b20869d7b09d81854f3727fdcd897430) (similar to commit 7add712498fe93603b1bffff2c633e097ce8fbdf) --- source3/auth/auth_domain.c | 1 + source3/auth/auth_netlogond.c | 1 + source3/include/proto.h | 2 + source3/rpc_client/cli_netlogon.c | 100 +++++++++++++++++++++++++++++++++++-- source3/winbindd/winbindd.h | 1 + source3/winbindd/winbindd_cm.c | 1 + source3/winbindd/winbindd_pam.c | 97 +++++++++++++++++++++++++++++++++++- 7 files changed, 198 insertions(+), 5 deletions(-) diff --git a/source3/auth/auth_domain.c b/source3/auth/auth_domain.c index c527360..9677ce7 100644 --- a/source3/auth/auth_domain.c +++ b/source3/auth/auth_domain.c @@ -310,6 +310,7 @@ static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx, user_info->client_domain, /* domain name */ user_info->wksta_name, /* workstation name */ chal, /* 8 byte challenge. */ + 3, /* validation level */ user_info->lm_resp, /* lanman 24 byte response */ user_info->nt_resp, /* nt 24 byte response */ &info3); /* info3 out */ diff --git a/source3/auth/auth_netlogond.c b/source3/auth/auth_netlogond.c index ebfed83..2347dae 100644 --- a/source3/auth/auth_netlogond.c +++ b/source3/auth/auth_netlogond.c @@ -85,6 +85,7 @@ static NTSTATUS netlogond_validate(TALLOC_CTX *mem_ctx, user_info->client_domain, /* domain name */ user_info->wksta_name, /* workstation name */ (uchar *)auth_context->challenge.data, /* 8 byte challenge. */ + 3, /* validation level */ user_info->lm_resp, /* lanman 24 byte response */ user_info->nt_resp, /* nt 24 byte response */ &info3); /* info3 out */ diff --git a/source3/include/proto.h b/source3/include/proto.h index bfcd012..ce33b70 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -5260,6 +5260,7 @@ NTSTATUS rpccli_netlogon_sam_network_logon(struct rpc_pipe_client *cli, const char *domain, const char *workstation, const uint8 chal[8], + uint16_t validation_level, DATA_BLOB lm_response, DATA_BLOB nt_response, struct netr_SamInfo3 **info3); @@ -5271,6 +5272,7 @@ NTSTATUS rpccli_netlogon_sam_network_logon_ex(struct rpc_pipe_client *cli, const char *domain, const char *workstation, const uint8 chal[8], + uint16_t validation_level, DATA_BLOB lm_response, DATA_BLOB nt_response, struct netr_SamInfo3 **info3); diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c index 9cb0bc6..ca66faa 100644 --- a/source3/rpc_client/cli_netlogon.c +++ b/source3/rpc_client/cli_netlogon.c @@ -283,6 +283,92 @@ NTSTATUS rpccli_netlogon_sam_logon(struct rpc_pipe_client *cli, return result; } +#define COPY_LSA_STRING(mem_ctx, in, out, name) do { \ + if (in->name.string) { \ + out->name.string = talloc_strdup(mem_ctx, in->name.string); \ + NT_STATUS_HAVE_NO_MEMORY(out->name.string); \ + } \ +} while (0) + +static NTSTATUS copy_netr_SamBaseInfo(TALLOC_CTX *mem_ctx, + const struct netr_SamBaseInfo *in, + struct netr_SamBaseInfo *out) +{ + /* first copy all, then realloc pointers */ + *out = *in; + + COPY_LSA_STRING(mem_ctx, in, out, account_name); + COPY_LSA_STRING(mem_ctx, in, out, full_name); + COPY_LSA_STRING(mem_ctx, in, out, logon_script); + COPY_LSA_STRING(mem_ctx, in, out, profile_path); + COPY_LSA_STRING(mem_ctx, in, out, home_directory); + COPY_LSA_STRING(mem_ctx, in, out, home_drive); + + if (in->groups.count) { + out->groups.rids = (struct samr_RidWithAttribute *) + talloc_memdup(mem_ctx, in->groups.rids, + (sizeof(struct samr_RidWithAttribute) * + in->groups.count)); + NT_STATUS_HAVE_NO_MEMORY(out->groups.rids); + } + + COPY_LSA_STRING(mem_ctx, in, out, logon_server); + COPY_LSA_STRING(mem_ctx, in, out, domain); + + if (in->domain_sid) { + out->domain_sid = sid_dup_talloc(mem_ctx, in->domain_sid); + NT_STATUS_HAVE_NO_MEMORY(out->domain_sid); + } + + return NT_STATUS_OK; +} + +static NTSTATUS map_validation_to_info3(TALLOC_CTX *mem_ctx, + uint16_t validation_level, + union netr_Validation *validation, + struct netr_SamInfo3 **info3_p) +{ + struct netr_SamInfo3 *info3; + NTSTATUS status; + + if (validation == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + switch (validation_level) { + case 3: + if (validation->sam3 == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + info3 = talloc_move(mem_ctx, &validation->sam3); + break; + case 6: + if (validation->sam6 == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + info3 = talloc_zero(mem_ctx, struct netr_SamInfo3); + if (info3 == NULL) { + return NT_STATUS_NO_MEMORY; + } + status = copy_netr_SamBaseInfo(info3, &validation->sam6->base, &info3->base); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(info3); + return status; + } + + info3->sidcount = validation->sam6->sidcount; + info3->sids = talloc_move(info3, &validation->sam6->sids); + break; + default: + return NT_STATUS_BAD_VALIDATION_CLASS; + } + + *info3_p = info3; + + return NT_STATUS_OK; +} /** * Logon domain user with an 'network' SAM logon @@ -298,12 +384,12 @@ NTSTATUS rpccli_netlogon_sam_network_logon(struct rpc_pipe_client *cli, const char *domain, const char *workstation, const uint8 chal[8], + uint16_t validation_level, DATA_BLOB lm_response, DATA_BLOB nt_response, struct netr_SamInfo3 **info3) { NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - int validation_level = 3; const char *workstation_name_slash; const char *server_name_slash; uint8 zeros[16]; @@ -397,7 +483,10 @@ NTSTATUS rpccli_netlogon_sam_network_logon(struct rpc_pipe_client *cli, netlogon_creds_decrypt_samlogon(cli->dc, validation_level, &validation); - *info3 = validation.sam3; + result = map_validation_to_info3(mem_ctx, validation_level, &validation, info3); + if (!NT_STATUS_IS_OK(result)) { + return result; + } return result; } @@ -410,12 +499,12 @@ NTSTATUS rpccli_netlogon_sam_network_logon_ex(struct rpc_pipe_client *cli, const char *domain, const char *workstation, const uint8 chal[8], + uint16_t validation_level, DATA_BLOB lm_response, DATA_BLOB nt_response, struct netr_SamInfo3 **info3) { NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - int validation_level = 3; const char *workstation_name_slash; const char *server_name_slash; uint8 zeros[16]; @@ -498,7 +587,10 @@ NTSTATUS rpccli_netlogon_sam_network_logon_ex(struct rpc_pipe_client *cli, netlogon_creds_decrypt_samlogon(cli->dc, validation_level, &validation); - *info3 = validation.sam3; + result = map_validation_to_info3(mem_ctx, validation_level, &validation, info3); + if (!NT_STATUS_IS_OK(result)) { + return result; + } return result; } diff --git a/source3/winbindd/winbindd.h b/source3/winbindd/winbindd.h index f1815ac..56b0ad9 100644 --- a/source3/winbindd/winbindd.h +++ b/source3/winbindd/winbindd.h @@ -167,6 +167,7 @@ struct winbindd_domain { * we don't have to try _ex every time. */ bool can_do_ncacn_ip_tcp; + bool can_do_validation6; /* Lookup methods for this domain (LDAP or RPC) */ struct winbindd_methods *methods; diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index 77e5003..cc3e3ed 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -1945,6 +1945,7 @@ done: domain->name, domain->active_directory ? "" : "NOT ")); domain->can_do_ncacn_ip_tcp = domain->active_directory; + domain->can_do_validation6 = domain->active_directory; TALLOC_FREE(cli); diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c index e958a7e..aab3b72 100644 --- a/source3/winbindd/winbindd_pam.c +++ b/source3/winbindd/winbindd_pam.c @@ -1185,6 +1185,7 @@ typedef NTSTATUS (*netlogon_fn_t)(struct rpc_pipe_client *cli, const char *domain, const char *workstation, const uint8 chal[8], + uint16_t validation_level, DATA_BLOB lm_response, DATA_BLOB nt_response, struct netr_SamInfo3 **info3); @@ -1296,6 +1297,8 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain, do { netlogon_fn_t logon_fn; + const struct cli_pipe_auth_data *auth; + uint32_t neg_flags = 0; ZERO_STRUCTP(my_info3); retry = false; @@ -1306,6 +1309,10 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain, DEBUG(3, ("could not open handle to NETLOGON pipe\n")); goto done; } + auth = netlogon_pipe->auth; + if (netlogon_pipe->dc) { + neg_flags = netlogon_pipe->dc->negotiate_flags; + } /* It is really important to try SamLogonEx here, * because in a clustered environment, we want to use @@ -1326,8 +1333,35 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain, * wrapping SamLogon context. * * -- abartlet 21 April 2008 + * + * It's also important to use NetlogonValidationSamInfo4 (6), + * because it relies on the rpc transport encryption + * and avoids using the global netlogon schannel + * session key to en/decrypt secret information + * like the user_session_key for network logons. + * + * [MS-APDS] 3.1.5.2 NTLM Network Logon + * says NETLOGON_NEG_CROSS_FOREST_TRUSTS and + * NETLOGON_NEG_AUTHENTICATED_RPC set together + * are the indication that the server supports + * NetlogonValidationSamInfo4 (6). And must only + * be used if "SealSecureChannel" is used. + * + * -- metze 4 February 2011 */ + if (auth == NULL) { + domain->can_do_validation6 = false; + } else if (auth->auth_type != PIPE_AUTH_TYPE_SCHANNEL) { + domain->can_do_validation6 = false; + } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) { + domain->can_do_validation6 = false; + } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) { + domain->can_do_validation6 = false; + } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) { + domain->can_do_validation6 = false; + } + logon_fn = contact_domain->can_do_samlogon_ex ? rpccli_netlogon_sam_network_logon_ex : rpccli_netlogon_sam_network_logon; @@ -1340,20 +1374,42 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(struct winbindd_domain *domain, name_domain, /* target domain */ global_myname(), /* workstation */ chal, + domain->can_do_validation6 ? 6 : 3, lm_resp, nt_resp, &my_info3); - attempts += 1; if ((NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) && contact_domain->can_do_samlogon_ex) { DEBUG(3, ("Got a DC that can not do NetSamLogonEx, " "retrying with NetSamLogon\n")); contact_domain->can_do_samlogon_ex = false; + /* + * It's likely that the server also does not support + * validation level 6 + */ + domain->can_do_validation6 = false; + retry = true; + continue; + } + + if (domain->can_do_validation6 && + (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) || + NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) || + NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) { + DEBUG(3,("Got a DC that can not do validation level 6, " + "retrying with level 3\n")); + domain->can_do_validation6 = false; retry = true; continue; } + /* + * we increment this after the "feature negotiation" + * for can_do_samlogon_ex and can_do_validation6 + */ + attempts += 1; + /* We have to try a second time as cm_connect_netlogon might not yet have noticed that the DC has killed our connection. */ @@ -1889,6 +1945,8 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, do { netlogon_fn_t logon_fn; + const struct cli_pipe_auth_data *auth; + uint32_t neg_flags = 0; retry = false; @@ -1900,6 +1958,22 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, nt_errstr(result))); goto done; } + auth = netlogon_pipe->auth; + if (netlogon_pipe->dc) { + neg_flags = netlogon_pipe->dc->negotiate_flags; + } + + if (auth == NULL) { + domain->can_do_validation6 = false; + } else if (auth->auth_type != PIPE_AUTH_TYPE_SCHANNEL) { + domain->can_do_validation6 = false; + } else if (auth->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) { + domain->can_do_validation6 = false; + } else if (!(neg_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) { + domain->can_do_validation6 = false; + } else if (!(neg_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) { + domain->can_do_validation6 = false; + } logon_fn = contact_domain->can_do_samlogon_ex ? rpccli_netlogon_sam_network_logon_ex @@ -1914,6 +1988,7 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, /* Bug #3248 - found by Stefan Burkei. */ workstation, /* We carefully set this above so use it... */ state->request->data.auth_crap.chal, + domain->can_do_validation6 ? 6 : 3, lm_resp, nt_resp, &info3); @@ -1923,10 +1998,30 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, DEBUG(3, ("Got a DC that can not do NetSamLogonEx, " "retrying with NetSamLogon\n")); contact_domain->can_do_samlogon_ex = false; + /* + * It's likely that the server also does not support + * validation level 6 + */ + domain->can_do_validation6 = false; retry = true; continue; } + if (domain->can_do_validation6 && + (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) || + NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) || + NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL))) { + DEBUG(3,("Got a DC that can not do validation level 6, " + "retrying with level 3\n")); + domain->can_do_validation6 = false; + retry = true; + continue; + } + + /* + * we increment this after the "feature negotiation" + * for can_do_samlogon_ex and can_do_validation6 + */ attempts += 1; /* We have to try a second time as cm_connect_netlogon -- 1.7.0.4