The branch, master has been updated via d90f332 s4:torture/rpc: add rpc.netlogon.ServerReqChallengeGlobal via 321ebc9 s4:rpc_server/netlogon: keep a global challenge table from 4580702 lib/util: move memcache.[ch] to the toplevel 'samba-util' library
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit d90f3323ee001080645dcd25da8b8ce1367b1377 Author: Stefan Metzmacher <me...@samba.org> Date: Thu Jul 17 16:05:12 2014 +0200 s4:torture/rpc: add rpc.netlogon.ServerReqChallengeGlobal This demonstrates that the challenge table should be global. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10723 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> Autobuild-User(master): Stefan Metzmacher <me...@samba.org> Autobuild-Date(master): Sat Jul 19 12:51:39 CEST 2014 on sn-devel-104 commit 321ebc99b5a00f82265aee741a48aa84b214d6e8 Author: Stefan Metzmacher <me...@samba.org> Date: Thu Jul 17 14:20:58 2014 +0200 s4:rpc_server/netlogon: keep a global challenge table Some clients call netr_ServerReqChallenge() and netr_ServerAuthenticate3() on different connections. This works against Windows DCs as they have a global challenge table. A VMware provisioning task for Windows VMs seemy to rely on this behavior. As a fallback we're storing the challenge in a global memcache with a fixed size. This should allow these strange clients to work against a Samba AD DC. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10723 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> ----------------------------------------------------------------------- Summary of changes: source4/rpc_server/netlogon/dcerpc_netlogon.c | 91 +++++++++++++++++++++++- source4/torture/rpc/netlogon.c | 73 ++++++++++++++++++++ 2 files changed, 160 insertions(+), 4 deletions(-) Changeset truncated at 500 lines: diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index c7fed22..49eb5c3 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -27,6 +27,7 @@ #include "auth/auth_sam_reply.h" #include "dsdb/samdb/samdb.h" #include "../lib/util/util_ldb.h" +#include "../lib/util/memcache.h" #include "../libcli/auth/schannel.h" #include "libcli/security/security.h" #include "param/param.h" @@ -39,6 +40,8 @@ #include "librpc/gen_ndr/ndr_irpc.h" #include "lib/socket/netif.h" +static struct memcache *global_challenge_table; + struct netlogon_server_pipe_state { struct netr_Credential client_challenge; struct netr_Credential server_challenge; @@ -49,9 +52,27 @@ static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_cal { struct netlogon_server_pipe_state *pipe_state = talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state); + DATA_BLOB key, val; ZERO_STRUCTP(r->out.return_credentials); + if (global_challenge_table == NULL) { + /* + * We maintain a global challenge table + * with a fixed size (8k) + * + * This is required for the strange clients + * which use different connections for + * netr_ServerReqChallenge() and netr_ServerAuthenticate3() + * + */ + global_challenge_table = memcache_init(talloc_autofree_context(), + 8192); + if (global_challenge_table == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + /* destroyed on pipe shutdown */ if (pipe_state) { @@ -71,6 +92,11 @@ static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_cal dce_call->context->private_data = pipe_state; + key = data_blob_string_const(r->in.computer_name); + val = data_blob_const(pipe_state, sizeof(*pipe_state)); + + memcache_add(global_challenge_table, SINGLETON_CACHE, key, val); + return NT_STATUS_OK; } @@ -79,6 +105,9 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca { struct netlogon_server_pipe_state *pipe_state = talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state); + DATA_BLOB challenge_key; + bool challenge_valid = false; + struct netlogon_server_pipe_state challenge; struct netlogon_creds_CredentialState *creds; struct ldb_context *sam_ctx; struct samr_Password *mach_pwd; @@ -100,6 +129,57 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca ZERO_STRUCTP(r->out.return_credentials); *r->out.rid = 0; + challenge_key = data_blob_string_const(r->in.computer_name); + if (pipe_state != NULL) { + dce_call->context->private_data = NULL; + + /* + * If we had a challenge remembered on the connection + * consider this for usage. This can't be cleanup + * by other clients. + * + * This is the default code path for typical clients + * which call netr_ServerReqChallenge() and + * netr_ServerAuthenticate3() on the same dcerpc connection. + */ + challenge = *pipe_state; + TALLOC_FREE(pipe_state); + challenge_valid = true; + } else { + DATA_BLOB val; + bool ok; + + /* + * Fallback and try to get the challenge from + * the global cache. + * + * If too many clients are using this code path, + * they may destroy their cache entries as the + * global_challenge_table memcache has a fixed size. + * + * Note: this handles global_challenge_table == NULL fine + */ + ok = memcache_lookup(global_challenge_table, SINGLETON_CACHE, + challenge_key, &val); + if (ok && val.length == sizeof(challenge)) { + memcpy(&challenge, val.data, sizeof(challenge)); + challenge_valid = true; + } else { + ZERO_STRUCT(challenge); + } + } + + /* + * At this point we can cleanup the cache entry, + * if we fail the client needs to call netr_ServerReqChallenge + * again. + * + * Note: this handles global_challenge_table == NULL + * and also a non existing record just fine. + */ + memcache_delete(global_challenge_table, + SINGLETON_CACHE, challenge_key); + server_flags = NETLOGON_NEG_ACCOUNT_LOCKOUT | NETLOGON_NEG_PERSISTENT_SAMREPL | NETLOGON_NEG_ARCFOUR | @@ -273,8 +353,11 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca return NT_STATUS_ACCESS_DENIED; } - if (!pipe_state) { - DEBUG(1, ("No challenge requested by client, cannot authenticate\n")); + if (!challenge_valid) { + DEBUG(1, ("No challenge requested by client [%s/%s], " + "cannot authenticate\n", + r->in.computer_name, + r->in.account_name)); return NT_STATUS_ACCESS_DENIED; } @@ -282,8 +365,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca r->in.account_name, r->in.computer_name, r->in.secure_channel_type, - &pipe_state->client_challenge, - &pipe_state->server_challenge, + &challenge.client_challenge, + &challenge.server_challenge, mach_pwd, r->in.credentials, r->out.return_credentials, diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c index 90bfe7e..76135a3 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -1103,6 +1103,78 @@ static bool test_invalidAuthenticate2(struct torture_context *tctx, return true; } +static bool test_ServerReqChallengeGlobal(struct torture_context *tctx, + struct dcerpc_pipe *p1, + struct cli_credentials *machine_credentials) +{ + uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES; + struct netr_ServerReqChallenge r; + struct netr_ServerAuthenticate3 a; + struct netr_Credential credentials1, credentials2, credentials3; + struct netlogon_creds_CredentialState *creds; + struct samr_Password mach_password; + uint32_t rid; + const char *machine_name; + const char *plain_pass; + struct dcerpc_binding_handle *b1 = p1->binding_handle; + struct dcerpc_pipe *p2 = NULL; + struct dcerpc_binding_handle *b2 = NULL; + + machine_name = cli_credentials_get_workstation(machine_credentials); + plain_pass = cli_credentials_get_password(machine_credentials); + + torture_comment(tctx, "Testing ServerReqChallenge on b1\n"); + + torture_assert_ntstatus_ok(tctx, + dcerpc_pipe_connect_b(tctx, &p2, p1->binding, + &ndr_table_netlogon, + machine_credentials, + tctx->ev, tctx->lp_ctx), + "dcerpc_pipe_connect_b failed"); + b2 = p2->binding_handle; + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), + "ServerReqChallenge failed on b1"); + torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1"); + + E_md4hash(plain_pass, mach_password.hash); + + a.in.server_name = NULL; + a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name); + a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials); + a.in.computer_name = machine_name; + a.in.negotiate_flags = &flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + a.out.negotiate_flags = &flags; + a.out.rid = &rid; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + &mach_password, &credentials3, + flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a), + "ServerAuthenticate3 failed on b2"); + torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2"); + torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed"); + + return true; +} + static bool test_SamLogon_NULL_domain(struct torture_context *tctx, struct dcerpc_pipe *p, struct cli_credentials *credentials) @@ -3943,6 +4015,7 @@ struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test(tcase, "LogonUasLogoff", test_LogonUasLogoff); torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon); torture_rpc_tcase_add_test_creds(tcase, "invalidAuthenticate2", test_invalidAuthenticate2); + torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeGlobal", test_ServerReqChallengeGlobal); torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword); torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2); torture_rpc_tcase_add_test_creds(tcase, "SetPassword2_AES", test_SetPassword2_AES); -- Samba Shared Repository