/* try a netlogon SamLogon */ bool test_netlogon_ex_ops(struct dcerpc_pipe *p, struct torture_context *tctx, struct cli_credentials *credentials, struct netlogon_creds_CredentialState *creds) { NTSTATUS status; struct netr_LogonSamLogonEx r; struct netr_NetworkInfo ninfo; union netr_LogonLevel logon; union netr_Validation validation; uint8_t authoritative = 0; uint32_t _flags = 0; DATA_BLOB names_blob, chal, lm_resp, nt_resp; int i; int flags = CLI_CRED_NTLM_AUTH; struct dcerpc_binding_handle *b = p->binding_handle; struct netr_UserSessionKey key; struct netr_LMSessionKey LMSessKey; uint32_t validation_levels[] = { 2, 3 }; struct netr_SamBaseInfo *base = NULL; const char *crypto_alg = ""; bool can_do_validation_6 = true; enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE; if (lpcfg_client_lanman_auth(tctx->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; } if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx)) { flags |= CLI_CRED_NTLMv2_AUTH; } cli_credentials_get_ntlm_username_domain(cmdline_credentials, tctx, &ninfo.identity_info.account_name.string, &ninfo.identity_info.domain_name.string); generate_random_buffer(ninfo.challenge, sizeof(ninfo.challenge)); chal = data_blob_const(ninfo.challenge, sizeof(ninfo.challenge)); names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(credentials), cli_credentials_get_domain(credentials)); status = cli_credentials_get_ntlm_response(cmdline_credentials, tctx, &flags, chal, names_blob, &lm_resp, &nt_resp, NULL, NULL); torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); ninfo.lm.data = lm_resp.data; ninfo.lm.length = lm_resp.length; ninfo.nt.data = nt_resp.data; ninfo.nt.length = nt_resp.length; ninfo.identity_info.parameter_control = 0; ninfo.identity_info.logon_id_low = 0; ninfo.identity_info.logon_id_high = 0; ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); logon.network = &ninfo; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.logon_level = NetlogonNetworkInformation; r.in.logon= &logon; r.in.flags = &_flags; r.out.validation = &validation; r.out.authoritative = &authoritative; r.out.flags = &_flags; /* - retrieve level6 - save usrsession and lmsession key - retrieve level 2 - calculate, compare - retrieve level 3 - calculate, compare */ if (creds) { if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { crypto_alg = "AES"; } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { crypto_alg = "ARCFOUR"; } } dcerpc_binding_handle_auth_info(b, NULL, &auth_level); if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { r.in.validation_level = 6; torture_comment(tctx, "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", ninfo.identity_info.account_name.string, crypto_alg, r.in.validation_level); torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), "LogonSamLogonEx failed"); } else { torture_comment(tctx, "Skip auth_level[%u] Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", auth_level, ninfo.identity_info.account_name.string, crypto_alg, r.in.validation_level); r.out.result = NT_STATUS_INVALID_INFO_CLASS; } if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) { can_do_validation_6 = false; } else { torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogonEx failed"); key = r.out.validation->sam6->base.key; LMSessKey = r.out.validation->sam6->base.LMSessKey; DEBUG(1,("unencrypted session keys from validation_level 6:\n")); dump_data(1, r.out.validation->sam6->base.key.key, 16); dump_data(1, r.out.validation->sam6->base.LMSessKey.key, 8); } for (i=0; i < ARRAY_SIZE(validation_levels); i++) { r.in.validation_level = validation_levels[i]; torture_comment(tctx, "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n", ninfo.identity_info.account_name.string, crypto_alg, r.in.validation_level); torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), "LogonSamLogonEx failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogonEx failed"); if (creds == NULL) { /* when this test is called without creds no point in * testing the session keys */ continue; } switch (validation_levels[i]) { case 2: base = &r.out.validation->sam2->base; break; case 3: base = &r.out.validation->sam3->base; break; default: break; } DEBUG(1,("encrypted keys validation_level %d:\n", validation_levels[i])); dump_data(1, base->key.key, 16); dump_data(1, base->LMSessKey.key, 8); if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { netlogon_creds_aes_decrypt(creds, base->key.key, 16); netlogon_creds_aes_decrypt(creds, base->LMSessKey.key, 8); } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { netlogon_creds_arcfour_crypt(creds, base->key.key, 16); netlogon_creds_arcfour_crypt(creds, base->LMSessKey.key, 8); } DEBUG(1,("decryped keys validation_level %d\n", validation_levels[i])); dump_data(1, base->key.key, 16); dump_data(1, base->LMSessKey.key, 8); if (!can_do_validation_6) { /* we cant compare against unencrypted keys */ continue; } torture_assert_mem_equal(tctx, base->key.key, key.key, 16, "unexpected user session key\n"); torture_assert_mem_equal(tctx, base->LMSessKey.key, LMSessKey.key, 8, "unexpected LM session key\n"); } return true; }
NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r) { NTSTATUS nt_status, dbsync_nt_status; TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx; struct netlogon_creds_CredentialState *creds; struct netr_DatabaseSync dbsync; struct netr_Authenticator credential, return_authenticator; struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; struct cli_credentials *machine_account; struct dcerpc_pipe *p; struct libnet_context *machine_net_ctx; struct libnet_RpcConnect *c; struct libnet_SamSync_state *state; const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; unsigned int i; samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context"); if (!r->in.machine_account) { machine_account = cli_credentials_init(samsync_ctx); if (!machine_account) { talloc_free(samsync_ctx); return NT_STATUS_NO_MEMORY; } cli_credentials_set_conf(machine_account, ctx->lp_ctx); nt_status = cli_credentials_set_machine_account(machine_account, ctx->lp_ctx); if (!NT_STATUS_IS_OK(nt_status)) { r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?"); talloc_free(samsync_ctx); return nt_status; } } else { machine_account = r->in.machine_account; } /* We cannot do this unless we are a BDC. Check, before we get odd errors later */ if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) { r->out.error_string = talloc_asprintf(mem_ctx, "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC", cli_credentials_get_domain(machine_account), cli_credentials_get_secure_channel_type(machine_account)); talloc_free(samsync_ctx); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } c = talloc_zero(samsync_ctx, struct libnet_RpcConnect); if (!c) { r->out.error_string = NULL; talloc_free(samsync_ctx); return NT_STATUS_NO_MEMORY; } c->level = LIBNET_RPC_CONNECT_DC_INFO; if (r->in.binding_string) { c->in.binding = r->in.binding_string; c->in.name = NULL; } else { c->in.binding = NULL; c->in.name = cli_credentials_get_domain(machine_account); } /* prepare connect to the NETLOGON pipe of PDC */ c->in.dcerpc_iface = &ndr_table_netlogon; /* We must do this as the machine, not as any command-line * user. So we override the credentials in the * libnet_context */ machine_net_ctx = talloc(samsync_ctx, struct libnet_context); if (!machine_net_ctx) { r->out.error_string = NULL; talloc_free(samsync_ctx); return NT_STATUS_NO_MEMORY; } *machine_net_ctx = *ctx; machine_net_ctx->cred = machine_account; /* connect to the NETLOGON pipe of the PDC */ nt_status = libnet_RpcConnect(machine_net_ctx, samsync_ctx, c); if (!NT_STATUS_IS_OK(nt_status)) { if (r->in.binding_string) { r->out.error_string = talloc_asprintf(mem_ctx, "Connection to NETLOGON pipe of DC %s failed: %s", r->in.binding_string, c->out.error_string); } else { r->out.error_string = talloc_asprintf(mem_ctx, "Connection to NETLOGON pipe of DC for %s failed: %s", c->in.name, c->out.error_string); } talloc_free(samsync_ctx); return nt_status; } /* This makes a new pipe, on which we can do schannel. We * should do this in the RpcConnect code, but the abstaction * layers do not suit yet */ nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p, c->out.dcerpc_pipe->binding); if (!NT_STATUS_IS_OK(nt_status)) { r->out.error_string = talloc_asprintf(mem_ctx, "Secondary connection to NETLOGON pipe of DC %s failed: %s", dcerpc_server_name(p), nt_errstr(nt_status)); talloc_free(samsync_ctx); return nt_status; } nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &ndr_table_netlogon, machine_account, ctx->lp_ctx, DCERPC_AUTH_LEVEL_PRIVACY); if (!NT_STATUS_IS_OK(nt_status)) { r->out.error_string = talloc_asprintf(mem_ctx, "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s", dcerpc_server_name(p), nt_errstr(nt_status)); talloc_free(samsync_ctx); return nt_status; } state = talloc(samsync_ctx, struct libnet_SamSync_state); if (!state) { r->out.error_string = NULL; talloc_free(samsync_ctx); return nt_status; } state->domain_name = c->out.domain_name; state->domain_sid = c->out.domain_sid; state->realm = c->out.realm; state->domain_guid = c->out.guid; state->machine_net_ctx = machine_net_ctx; state->netlogon_pipe = p; /* initialise the callback layer. It may wish to contact the * server with ldap, now we know the name */ if (r->in.init_fn) { char *error_string; nt_status = r->in.init_fn(samsync_ctx, r->in.fn_ctx, state, &error_string); if (!NT_STATUS_IS_OK(nt_status)) { r->out.error_string = talloc_steal(mem_ctx, error_string); talloc_free(samsync_ctx); return nt_status; } } /* get NETLOGON credentials */ nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds); if (!NT_STATUS_IS_OK(nt_status)) { r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer"); talloc_free(samsync_ctx); return nt_status; } /* Setup details for the synchronisation */ ZERO_STRUCT(return_authenticator); dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p)); dbsync.in.computername = cli_credentials_get_workstation(machine_account); dbsync.in.preferredmaximumlength = (uint32_t)-1; dbsync.in.return_authenticator = &return_authenticator; dbsync.out.return_authenticator = &return_authenticator; dbsync.out.delta_enum_array = &delta_enum_array; for (i=0;i< ARRAY_SIZE(database_ids); i++) { uint32_t sync_context = 0; dbsync.in.database_id = database_ids[i]; dbsync.in.sync_context = &sync_context; dbsync.out.sync_context = &sync_context; do { uint32_t d; loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context"); netlogon_creds_client_authenticator(creds, &credential); dbsync.in.credential = &credential; dbsync_nt_status = dcerpc_netr_DatabaseSync_r(p->binding_handle, loop_ctx, &dbsync); if (NT_STATUS_IS_OK(dbsync_nt_status) && !NT_STATUS_IS_OK(dbsync.out.result)) { dbsync_nt_status = dbsync.out.result; } if (!NT_STATUS_IS_OK(dbsync_nt_status) && !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) { r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status)); talloc_free(samsync_ctx); return nt_status; } if (!netlogon_creds_client_check(creds, &dbsync.out.return_authenticator->cred)) { r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed"); talloc_free(samsync_ctx); return NT_STATUS_ACCESS_DENIED; } dbsync.in.sync_context = dbsync.out.sync_context; /* For every single remote 'delta' entry: */ for (d=0; d < delta_enum_array->num_deltas; d++) { char *error_string = NULL; delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context"); /* 'Fix' elements, by decrypting and * de-obfuscating the data */ nt_status = samsync_fix_delta(delta_ctx, creds, dbsync.in.database_id, &delta_enum_array->delta_enum[d]); if (!NT_STATUS_IS_OK(nt_status)) { r->out.error_string = talloc_steal(mem_ctx, error_string); talloc_free(samsync_ctx); return nt_status; } /* Now call the callback. This will * do something like print the data or * write to an ldb */ nt_status = r->in.delta_fn(delta_ctx, r->in.fn_ctx, dbsync.in.database_id, &delta_enum_array->delta_enum[d], &error_string); if (!NT_STATUS_IS_OK(nt_status)) { r->out.error_string = talloc_steal(mem_ctx, error_string); talloc_free(samsync_ctx); return nt_status; } talloc_free(delta_ctx); } talloc_free(loop_ctx); } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)); if (!NT_STATUS_IS_OK(dbsync_nt_status)) { r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status)); talloc_free(samsync_ctx); return dbsync_nt_status; } nt_status = NT_STATUS_OK; } talloc_free(samsync_ctx); return nt_status; }
static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct schannel_state *state = (struct schannel_state *)gensec_security->private_data; NTSTATUS status; enum ndr_err_code ndr_err; struct NL_AUTH_MESSAGE bind_schannel; struct NL_AUTH_MESSAGE bind_schannel_ack; struct netlogon_creds_CredentialState *creds; const char *workstation; const char *domain; *out = data_blob(NULL, 0); switch (gensec_security->gensec_role) { case GENSEC_CLIENT: if (state->state != SCHANNEL_STATE_START) { /* we could parse the bind ack, but we don't know what it is yet */ return NT_STATUS_OK; } state->creds = cli_credentials_get_netlogon_creds(gensec_security->credentials); if (state->creds == NULL) { return NT_STATUS_INVALID_PARAMETER_MIX; } /* * We need to create a reference here or we don't get * updates performed on the credentials if we create a * copy. */ state->creds = talloc_reference(state, state->creds); if (state->creds == NULL) { return NT_STATUS_NO_MEMORY; } bind_schannel.MessageType = NL_NEGOTIATE_REQUEST; #if 0 /* to support this we'd need to have access to the full domain name */ /* 0x17, 23 */ bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME | NL_FLAG_OEM_NETBIOS_COMPUTER_NAME | NL_FLAG_UTF8_DNS_DOMAIN_NAME | NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME; bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials); bind_schannel.oem_netbios_computer.a = cli_credentials_get_workstation(gensec_security->credentials); bind_schannel.utf8_dns_domain = cli_credentials_get_realm(gensec_security->credentials); /* w2k3 refuses us if we use the full DNS workstation? why? perhaps because we don't fill in the dNSHostName attribute in the machine account? */ bind_schannel.utf8_netbios_computer = cli_credentials_get_workstation(gensec_security->credentials); #else bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME | NL_FLAG_OEM_NETBIOS_COMPUTER_NAME; bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials); bind_schannel.oem_netbios_computer.a = cli_credentials_get_workstation(gensec_security->credentials); #endif ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel, (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(3, ("Could not create schannel bind: %s\n", nt_errstr(status))); return status; } state->state = SCHANNEL_STATE_UPDATE_1; return NT_STATUS_MORE_PROCESSING_REQUIRED; case GENSEC_SERVER: if (state->state != SCHANNEL_STATE_START) { /* no third leg on this protocol */ return NT_STATUS_INVALID_PARAMETER; } /* parse the schannel startup blob */ ndr_err = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel, (ndr_pull_flags_fn_t)ndr_pull_NL_AUTH_MESSAGE); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(3, ("Could not parse incoming schannel bind: %s\n", nt_errstr(status))); return status; } if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_DOMAIN_NAME) { domain = bind_schannel.oem_netbios_domain.a; if (strcasecmp_m(domain, lpcfg_workgroup(gensec_security->settings->lp_ctx)) != 0) { DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n", domain, lpcfg_workgroup(gensec_security->settings->lp_ctx))); return NT_STATUS_LOGON_FAILURE; } } else if (bind_schannel.Flags & NL_FLAG_UTF8_DNS_DOMAIN_NAME) { domain = bind_schannel.utf8_dns_domain.u; if (strcasecmp_m(domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx)) != 0) { DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n", domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx))); return NT_STATUS_LOGON_FAILURE; } } else { DEBUG(3, ("Request for schannel to without domain\n")); return NT_STATUS_LOGON_FAILURE; } if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_COMPUTER_NAME) { workstation = bind_schannel.oem_netbios_computer.a; } else if (bind_schannel.Flags & NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME) { workstation = bind_schannel.utf8_netbios_computer.u; } else { DEBUG(3, ("Request for schannel to without netbios workstation\n")); return NT_STATUS_LOGON_FAILURE; } status = schannel_get_creds_state(out_mem_ctx, gensec_security->settings->lp_ctx, workstation, &creds); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n", workstation, nt_errstr(status))); if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { return NT_STATUS_LOGON_FAILURE; } return status; } state->creds = talloc_steal(state, creds); bind_schannel_ack.MessageType = NL_NEGOTIATE_RESPONSE; bind_schannel_ack.Flags = 0; bind_schannel_ack.Buffer.dummy = 0x6c0000; /* actually I think * this does not have * any meaning here * - gd */ ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack, (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n", workstation, nt_errstr(status))); return status; } state->state = SCHANNEL_STATE_UPDATE_1; return NT_STATUS_OK; } return NT_STATUS_INVALID_PARAMETER; }
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_ntlmssp_context *gensec_ntlmssp = talloc_get_type_abort(gensec_security->private_data, struct gensec_ntlmssp_context); struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state; uint32_t chal_flags, ntlmssp_command, unkn1, unkn2; DATA_BLOB server_domain_blob; DATA_BLOB challenge_blob; DATA_BLOB target_info = data_blob(NULL, 0); char *server_domain; const char *chal_parse_string; const char *auth_gen_string; DATA_BLOB lm_response = data_blob(NULL, 0); DATA_BLOB nt_response = data_blob(NULL, 0); DATA_BLOB session_key = data_blob(NULL, 0); DATA_BLOB lm_session_key = data_blob(NULL, 0); DATA_BLOB encrypted_session_key = data_blob(NULL, 0); NTSTATUS nt_status; int flags = 0; const char *user, *domain; TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } if (!msrpc_parse(mem_ctx, &in, "CdBd", "NTLMSSP", &ntlmssp_command, &server_domain_blob, &chal_flags)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } data_blob_free(&server_domain_blob); DEBUG(3, ("Got challenge flags:\n")); debug_ntlmssp_flags(chal_flags); ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); if (ntlmssp_state->unicode) { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdUdbddB"; } else { chal_parse_string = "CdUdbdd"; } auth_gen_string = "CdBBUUUBd"; } else { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdAdbddB"; } else { chal_parse_string = "CdAdbdd"; } auth_gen_string = "CdBBAAABd"; } if (!msrpc_parse(mem_ctx, &in, chal_parse_string, "NTLMSSP", &ntlmssp_command, &server_domain, &chal_flags, &challenge_blob, 8, &unkn1, &unkn2, &target_info)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } if (chal_flags & NTLMSSP_TARGET_TYPE_SERVER) { ntlmssp_state->server.is_standalone = true; } else { ntlmssp_state->server.is_standalone = false; } /* TODO: parse struct_blob and fill in the rest */ ntlmssp_state->server.netbios_name = ""; ntlmssp_state->server.netbios_domain = server_domain; ntlmssp_state->server.dns_name = ""; ntlmssp_state->server.dns_domain = ""; if (challenge_blob.length != 8) { talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx, &user, &domain); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { flags |= CLI_CRED_NTLM2; } if (ntlmssp_state->use_ntlmv2) { flags |= CLI_CRED_NTLMv2_AUTH; } if (ntlmssp_state->use_nt_response) { flags |= CLI_CRED_NTLM_AUTH; } if (lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; } nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx, &flags, challenge_blob, target_info, &lm_response, &nt_response, &lm_session_key, &session_key); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } if (!(flags & CLI_CRED_LANMAN_AUTH)) { /* LM Key is still possible, just silly, so we do not * allow it. Fortunetly all LM crypto is off by * default and we require command line options to end * up here */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } if (!(flags & CLI_CRED_NTLM2)) { /* NTLM2 is incompatible... */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; } if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx) && lm_session_key.length == 16) { DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16); if (lm_response.length == 24) { SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, new_session_key.data); } else { static const uint8_t zeros[24]; SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, new_session_key.data); } session_key = new_session_key; dump_data_pw("LM session key\n", session_key.data, session_key.length); } /* Key exchange encryptes a new client-generated session key with the password-derived key */ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { /* Make up a new session key */ uint8_t client_session_key[16]; generate_secret_buffer(client_session_key, sizeof(client_session_key)); /* Encrypt the new session key with the old one */ encrypted_session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); /* Mark the new session key as the 'real' session key */ session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key)); } DEBUG(3, ("NTLMSSP: Set final flags:\n")); debug_ntlmssp_flags(ntlmssp_state->neg_flags); /* this generates the actual auth packet */ nt_status = msrpc_gen(mem_ctx, out, auth_gen_string, "NTLMSSP", NTLMSSP_AUTH, lm_response.data, lm_response.length, nt_response.data, nt_response.length, domain, user, cli_credentials_get_workstation(gensec_security->credentials), encrypted_session_key.data, encrypted_session_key.length, ntlmssp_state->neg_flags); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return nt_status; } ntlmssp_state->session_key = session_key; talloc_steal(ntlmssp_state, session_key.data); talloc_steal(out_mem_ctx, out->data); ntlmssp_state->expected_state = NTLMSSP_DONE; if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) { nt_status = ntlmssp_sign_init(ntlmssp_state); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status))); talloc_free(mem_ctx); return nt_status; } } talloc_free(mem_ctx); return NT_STATUS_OK; }
NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) { struct gensec_ntlmssp_context *gensec_ntlmssp; struct ntlmssp_state *ntlmssp_state; NTSTATUS nt_status; nt_status = gensec_ntlmssp_start(gensec_security); NT_STATUS_NOT_OK_RETURN(nt_status); gensec_ntlmssp = talloc_get_type_abort(gensec_security->private_data, struct gensec_ntlmssp_context); ntlmssp_state = talloc_zero(gensec_ntlmssp, struct ntlmssp_state); if (!ntlmssp_state) { return NT_STATUS_NO_MEMORY; } gensec_ntlmssp->ntlmssp_state = ntlmssp_state; ntlmssp_state = gensec_ntlmssp->ntlmssp_state; ntlmssp_state->role = NTLMSSP_CLIENT; ntlmssp_state->client.netbios_domain = lpcfg_workgroup(gensec_security->settings->lp_ctx); ntlmssp_state->client.netbios_name = cli_credentials_get_workstation(gensec_security->credentials); ntlmssp_state->unicode = gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "unicode", true); ntlmssp_state->use_nt_response = gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "send_nt_reponse", true); ntlmssp_state->allow_lm_key = (lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx) && (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "allow_lm_key", false) || gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "lm_key", false))); ntlmssp_state->use_ntlmv2 = lpcfg_client_ntlmv2_auth(gensec_security->settings->lp_ctx); ntlmssp_state->expected_state = NTLMSSP_INITIAL; ntlmssp_state->neg_flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET; if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "128bit", true)) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128; } if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "56bit", false)) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56; } if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "lm_key", false)) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; } if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "keyexchange", true)) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; } if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "alwayssign", true)) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; } if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "ntlm2", true)) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; } else { /* apparently we can't do ntlmv2 if we don't do ntlm2 */ ntlmssp_state->use_ntlmv2 = false; } if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { /* * We need to set this to allow a later SetPassword * via the SAMR pipe to succeed. Strange.... We could * also add NTLMSSP_NEGOTIATE_SEAL here. JRA. * * Without this, Windows will not create the master key * that it thinks is only used for NTLMSSP signing and * sealing. (It is actually pulled out and used directly) */ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; } if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; } if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; } return NT_STATUS_OK; }
/* try a netlogon SamLogon */ bool test_netlogon_ex_ops(struct dcerpc_pipe *p, struct torture_context *tctx, struct cli_credentials *credentials, struct netlogon_creds_CredentialState *creds) { NTSTATUS status; struct netr_LogonSamLogonEx r; struct netr_NetworkInfo ninfo; union netr_LogonLevel logon; union netr_Validation validation; uint8_t authoritative = 0; uint32_t _flags = 0; DATA_BLOB names_blob, chal, lm_resp, nt_resp; int i; int flags = CLI_CRED_NTLM_AUTH; struct dcerpc_binding_handle *b = p->binding_handle; if (lpcfg_client_lanman_auth(tctx->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; } if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx)) { flags |= CLI_CRED_NTLMv2_AUTH; } cli_credentials_get_ntlm_username_domain(cmdline_credentials, tctx, &ninfo.identity_info.account_name.string, &ninfo.identity_info.domain_name.string); generate_random_buffer(ninfo.challenge, sizeof(ninfo.challenge)); chal = data_blob_const(ninfo.challenge, sizeof(ninfo.challenge)); names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(credentials), cli_credentials_get_domain(credentials)); status = cli_credentials_get_ntlm_response(cmdline_credentials, tctx, &flags, chal, names_blob, &lm_resp, &nt_resp, NULL, NULL); torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed"); ninfo.lm.data = lm_resp.data; ninfo.lm.length = lm_resp.length; ninfo.nt.data = nt_resp.data; ninfo.nt.length = nt_resp.length; ninfo.identity_info.parameter_control = 0; ninfo.identity_info.logon_id_low = 0; ninfo.identity_info.logon_id_high = 0; ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); logon.network = &ninfo; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.logon_level = 2; r.in.logon= &logon; r.in.flags = &_flags; r.out.validation = &validation; r.out.authoritative = &authoritative; r.out.flags = &_flags; torture_comment(tctx, "Testing LogonSamLogonEx with name %s\n", ninfo.identity_info.account_name.string); for (i=2;i<3;i++) { r.in.validation_level = i; torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r), "LogonSamLogon failed"); torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed"); } return true; }
static PyObject *py_creds_get_workstation(PyObject *self, PyObject *unused) { return PyString_FromStringOrNULL(cli_credentials_get_workstation(PyCredentials_AsCliCredentials(self))); }
static bool test_PACVerify(struct torture_context *tctx, struct dcerpc_pipe *p, struct cli_credentials *credentials) { NTSTATUS status; struct netr_LogonSamLogon r; union netr_LogonLevel logon; union netr_Validation validation; uint8_t authoritative; struct netr_Authenticator return_authenticator; struct netr_GenericInfo generic; struct netr_Authenticator auth, auth2; struct netlogon_creds_CredentialState *creds; struct gensec_security *gensec_client_context; struct gensec_security *gensec_server_context; DATA_BLOB client_to_server, server_to_client, pac_wrapped, payload; struct PAC_Validate pac_wrapped_struct; enum ndr_err_code ndr_err; struct auth_session_info *session_info; char *tmp_dir; TALLOC_CTX *tmp_ctx = talloc_new(tctx); torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed"); if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS, credentials, SEC_CHAN_BDC, &creds)) { return false; } status = torture_temp_dir(tctx, "PACVerify", &tmp_dir); torture_assert_ntstatus_ok(tctx, status, "torture_temp_dir failed"); status = gensec_client_start(tctx, &gensec_client_context, tctx->ev, lp_gensec_settings(tctx, tctx->lp_ctx)); torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed"); status = gensec_set_target_hostname(gensec_client_context, TEST_MACHINE_NAME); status = gensec_set_credentials(gensec_client_context, cmdline_credentials); torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed"); status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSSAPI"); torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed"); status = gensec_server_start(tctx, tctx->ev, lp_gensec_settings(tctx, tctx->lp_ctx), NULL, &gensec_server_context); torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); status = gensec_set_credentials(gensec_server_context, credentials); torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); status = gensec_start_mech_by_sasl_name(gensec_server_context, "GSSAPI"); torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed"); server_to_client = data_blob(NULL, 0); do { /* Do a client-server update dance */ status = gensec_update(gensec_client_context, tmp_ctx, server_to_client, &client_to_server); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { ; torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed"); } status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { ; torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); } if (NT_STATUS_IS_OK(status)) { break; } } while (1); /* Extract the PAC using Samba's code */ status = gensec_session_info(gensec_server_context, &session_info); torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length; pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type; pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length; pac_wrapped_struct.ChecksumAndSignature = payload = data_blob_talloc(tmp_ctx, NULL, pac_wrapped_struct.ChecksumLength + pac_wrapped_struct.SignatureLength); memcpy(&payload.data[0], session_info->server_info->pac_srv_sig.signature.data, pac_wrapped_struct.ChecksumLength); memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], session_info->server_info->pac_kdc_sig.signature.data, pac_wrapped_struct.SignatureLength); ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct, (ndr_push_flags_fn_t)ndr_push_PAC_Validate); torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); generic.length = pac_wrapped.length; generic.data = pac_wrapped.data; /* Validate it over the netlogon pipe */ generic.identity_info.parameter_control = 0; generic.identity_info.logon_id_high = 0; generic.identity_info.logon_id_low = 0; generic.identity_info.domain_name.string = session_info->server_info->domain_name; generic.identity_info.account_name.string = session_info->server_info->account_name; generic.identity_info.workstation.string = TEST_MACHINE_NAME; generic.package_name.string = "Kerberos"; logon.generic = &generic; ZERO_STRUCT(auth2); netlogon_creds_client_authenticator(creds, &auth); r.in.credential = &auth; r.in.return_authenticator = &auth2; r.in.logon = &logon; r.in.logon_level = NetlogonGenericInformation; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.validation_level = NetlogonValidationGenericInfo2; r.out.validation = &validation; r.out.authoritative = &authoritative; r.out.return_authenticator = &return_authenticator; status = dcerpc_netr_LogonSamLogon(p, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed"); /* This will break the signature nicely (even in the crypto wrapping), check we get a logon failure */ generic.data[generic.length-1]++; logon.generic = &generic; ZERO_STRUCT(auth2); netlogon_creds_client_authenticator(creds, &auth); r.in.credential = &auth; r.in.return_authenticator = &auth2; r.in.logon_level = NetlogonGenericInformation; r.in.logon = &logon; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.validation_level = NetlogonValidationGenericInfo2; status = dcerpc_netr_LogonSamLogon(p, tctx, &r); torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed"); torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), "Credential chaining failed"); /* This will break the parsing nicely (even in the crypto wrapping), check we get INVALID_PARAMETER */ generic.length--; logon.generic = &generic; ZERO_STRUCT(auth2); netlogon_creds_client_authenticator(creds, &auth); r.in.credential = &auth; r.in.return_authenticator = &auth2; r.in.logon_level = NetlogonGenericInformation; r.in.logon = &logon; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.validation_level = NetlogonValidationGenericInfo2; status = dcerpc_netr_LogonSamLogon(p, tctx, &r); torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed"); torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), "Credential chaining failed"); pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length; pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type; /* Break the SignatureType */ pac_wrapped_struct.SignatureType++; pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length; pac_wrapped_struct.ChecksumAndSignature = payload = data_blob_talloc(tmp_ctx, NULL, pac_wrapped_struct.ChecksumLength + pac_wrapped_struct.SignatureLength); memcpy(&payload.data[0], session_info->server_info->pac_srv_sig.signature.data, pac_wrapped_struct.ChecksumLength); memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], session_info->server_info->pac_kdc_sig.signature.data, pac_wrapped_struct.SignatureLength); ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct, (ndr_push_flags_fn_t)ndr_push_PAC_Validate); torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); generic.length = pac_wrapped.length; generic.data = pac_wrapped.data; logon.generic = &generic; ZERO_STRUCT(auth2); netlogon_creds_client_authenticator(creds, &auth); r.in.credential = &auth; r.in.return_authenticator = &auth2; r.in.logon_level = NetlogonGenericInformation; r.in.logon = &logon; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.validation_level = NetlogonValidationGenericInfo2; status = dcerpc_netr_LogonSamLogon(p, tctx, &r); torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed"); torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), "Credential chaining failed"); pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length; pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type; pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length; pac_wrapped_struct.ChecksumAndSignature = payload = data_blob_talloc(tmp_ctx, NULL, pac_wrapped_struct.ChecksumLength + pac_wrapped_struct.SignatureLength); memcpy(&payload.data[0], session_info->server_info->pac_srv_sig.signature.data, pac_wrapped_struct.ChecksumLength); memcpy(&payload.data[pac_wrapped_struct.ChecksumLength], session_info->server_info->pac_kdc_sig.signature.data, pac_wrapped_struct.SignatureLength); /* Break the signature length */ pac_wrapped_struct.SignatureLength++; ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct, (ndr_push_flags_fn_t)ndr_push_PAC_Validate); torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed"); torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption"); netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length); generic.length = pac_wrapped.length; generic.data = pac_wrapped.data; logon.generic = &generic; ZERO_STRUCT(auth2); netlogon_creds_client_authenticator(creds, &auth); r.in.credential = &auth; r.in.return_authenticator = &auth2; r.in.logon_level = NetlogonGenericInformation; r.in.logon = &logon; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.validation_level = NetlogonValidationGenericInfo2; status = dcerpc_netr_LogonSamLogon(p, tctx, &r); torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed"); torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred), "Credential chaining failed"); return true; }
/* try a netlogon SamLogon */ BOOL test_netlogon_ex_ops(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct cli_credentials *credentials, struct creds_CredentialState *creds) { NTSTATUS status; struct netr_LogonSamLogonEx r; struct netr_NetworkInfo ninfo; DATA_BLOB names_blob, chal, lm_resp, nt_resp; int i; BOOL ret = True; int flags = CLI_CRED_NTLM_AUTH; if (lp_client_lanman_auth()) { flags |= CLI_CRED_LANMAN_AUTH; } if (lp_client_ntlmv2_auth()) { flags |= CLI_CRED_NTLMv2_AUTH; } cli_credentials_get_ntlm_username_domain(cmdline_credentials, mem_ctx, &ninfo.identity_info.account_name.string, &ninfo.identity_info.domain_name.string); generate_random_buffer(ninfo.challenge, sizeof(ninfo.challenge)); chal = data_blob_const(ninfo.challenge, sizeof(ninfo.challenge)); names_blob = NTLMv2_generate_names_blob(mem_ctx, cli_credentials_get_workstation(credentials), cli_credentials_get_domain(credentials)); status = cli_credentials_get_ntlm_response(cmdline_credentials, mem_ctx, &flags, chal, names_blob, &lm_resp, &nt_resp, NULL, NULL); if (!NT_STATUS_IS_OK(status)) { printf("cli_credentials_get_ntlm_response failed: %s\n", nt_errstr(status)); return False; } ninfo.lm.data = lm_resp.data; ninfo.lm.length = lm_resp.length; ninfo.nt.data = nt_resp.data; ninfo.nt.length = nt_resp.length; ninfo.identity_info.parameter_control = 0; ninfo.identity_info.logon_id_low = 0; ninfo.identity_info.logon_id_high = 0; ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials); r.in.server_name = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = cli_credentials_get_workstation(credentials); r.in.logon_level = 2; r.in.logon.network = &ninfo; r.in.flags = 0; printf("Testing LogonSamLogonEx with name %s\n", ninfo.identity_info.account_name.string); for (i=2;i<3;i++) { r.in.validation_level = i; status = dcerpc_netr_LogonSamLogonEx(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { printf("LogonSamLogon failed: %s\n", nt_errstr(status)); return False; } } return ret; }
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_ntlmssp_context *gensec_ntlmssp = talloc_get_type_abort(gensec_security->private_data, struct gensec_ntlmssp_context); struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state; uint32_t chal_flags, ntlmssp_command, unkn1 = 0, unkn2 = 0; DATA_BLOB server_domain_blob; DATA_BLOB challenge_blob; DATA_BLOB target_info = data_blob(NULL, 0); char *server_domain; const char *chal_parse_string; const char *chal_parse_string_short = NULL; const char *auth_gen_string; DATA_BLOB lm_response = data_blob(NULL, 0); DATA_BLOB nt_response = data_blob(NULL, 0); DATA_BLOB session_key = data_blob(NULL, 0); DATA_BLOB lm_session_key = data_blob(NULL, 0); DATA_BLOB encrypted_session_key = data_blob(NULL, 0); NTSTATUS nt_status; int flags = 0; const char *user = NULL, *domain = NULL, *workstation = NULL; bool is_anonymous = false; const DATA_BLOB version_blob = ntlmssp_version_blob(); TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } if (!msrpc_parse(mem_ctx, &in, "CdBd", "NTLMSSP", &ntlmssp_command, &server_domain_blob, &chal_flags)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } data_blob_free(&server_domain_blob); DEBUG(3, ("Got challenge flags:\n")); debug_ntlmssp_flags(chal_flags); ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); if (ntlmssp_state->unicode) { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdUdbddB"; } else { chal_parse_string = "CdUdbdd"; chal_parse_string_short = "CdUdb"; } auth_gen_string = "CdBBUUUBdb"; } else { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdAdbddB"; } else { chal_parse_string = "CdAdbdd"; chal_parse_string_short = "CdAdb"; } auth_gen_string = "CdBBAAABdb"; } if (!msrpc_parse(mem_ctx, &in, chal_parse_string, "NTLMSSP", &ntlmssp_command, &server_domain, &chal_flags, &challenge_blob, 8, &unkn1, &unkn2, &target_info)) { bool ok = false; DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); if (chal_parse_string_short != NULL) { /* * In the case where NTLMSSP_NEGOTIATE_TARGET_INFO * is not used, some NTLMSSP servers don't return * the unused unkn1 and unkn2 fields. * See bug: * https://bugzilla.samba.org/show_bug.cgi?id=10016 * for packet traces. * Try and parse again without them. */ ok = msrpc_parse(mem_ctx, &in, chal_parse_string_short, "NTLMSSP", &ntlmssp_command, &server_domain, &chal_flags, &challenge_blob, 8); if (!ok) { DEBUG(1, ("Failed to short parse " "the NTLMSSP Challenge: (#2)\n")); } } if (!ok) { dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } } if (chal_flags & NTLMSSP_TARGET_TYPE_SERVER) { ntlmssp_state->server.is_standalone = true; } else { ntlmssp_state->server.is_standalone = false; } /* TODO: parse struct_blob and fill in the rest */ ntlmssp_state->server.netbios_name = ""; ntlmssp_state->server.netbios_domain = talloc_move(ntlmssp_state, &server_domain); ntlmssp_state->server.dns_name = ""; ntlmssp_state->server.dns_domain = ""; if (challenge_blob.length != 8) { talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } is_anonymous = cli_credentials_is_anonymous(gensec_security->credentials); cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx, &user, &domain); workstation = cli_credentials_get_workstation(gensec_security->credentials); if (user == NULL) { DEBUG(10, ("User is NULL, returning INVALID_PARAMETER\n")); return NT_STATUS_INVALID_PARAMETER; } if (domain == NULL) { DEBUG(10, ("Domain is NULL, returning INVALID_PARAMETER\n")); return NT_STATUS_INVALID_PARAMETER; } if (workstation == NULL) { DEBUG(10, ("Workstation is NULL, returning INVALID_PARAMETER\n")); return NT_STATUS_INVALID_PARAMETER; } if (is_anonymous) { ntlmssp_state->neg_flags |= NTLMSSP_ANONYMOUS; /* * don't use the ccache for anonymous auth */ ntlmssp_state->use_ccache = false; } if (ntlmssp_state->use_ccache) { struct samr_Password *nt_hash = NULL; /* * If we have a password given we don't * use the ccache */ nt_hash = cli_credentials_get_nt_hash(gensec_security->credentials, mem_ctx); if (nt_hash != NULL) { ZERO_STRUCTP(nt_hash); TALLOC_FREE(nt_hash); ntlmssp_state->use_ccache = false; } } if (ntlmssp_state->use_ccache) { struct wbcCredentialCacheParams params; struct wbcCredentialCacheInfo *info = NULL; struct wbcAuthErrorInfo *error = NULL; struct wbcNamedBlob auth_blobs[1]; const struct wbcBlob *wbc_auth_blob = NULL; const struct wbcBlob *wbc_session_key = NULL; wbcErr wbc_status; int i; params.account_name = user; params.domain_name = domain; params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP; auth_blobs[0].name = "challenge_blob"; auth_blobs[0].flags = 0; auth_blobs[0].blob.data = in.data; auth_blobs[0].blob.length = in.length; params.num_blobs = ARRAY_SIZE(auth_blobs); params.blobs = auth_blobs; wbc_status = wbcCredentialCache(¶ms, &info, &error); wbcFreeMemory(error); if (!WBC_ERROR_IS_OK(wbc_status)) { return NT_STATUS_WRONG_CREDENTIAL_HANDLE; } for (i=0; i<info->num_blobs; i++) { if (strequal(info->blobs[i].name, "auth_blob")) { wbc_auth_blob = &info->blobs[i].blob; } if (strequal(info->blobs[i].name, "session_key")) { wbc_session_key = &info->blobs[i].blob; } } if ((wbc_auth_blob == NULL) || (wbc_session_key == NULL)) { wbcFreeMemory(info); return NT_STATUS_WRONG_CREDENTIAL_HANDLE; } session_key = data_blob_talloc(mem_ctx, wbc_session_key->data, wbc_session_key->length); if (session_key.length != wbc_session_key->length) { wbcFreeMemory(info); return NT_STATUS_NO_MEMORY; } *out = data_blob_talloc(mem_ctx, wbc_auth_blob->data, wbc_auth_blob->length); if (out->length != wbc_auth_blob->length) { wbcFreeMemory(info); return NT_STATUS_NO_MEMORY; } wbcFreeMemory(info); goto done; } if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { flags |= CLI_CRED_NTLM2; } if (ntlmssp_state->use_ntlmv2) { flags |= CLI_CRED_NTLMv2_AUTH; } if (ntlmssp_state->use_nt_response) { flags |= CLI_CRED_NTLM_AUTH; } if (lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; } nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx, &flags, challenge_blob, target_info, &lm_response, &nt_response, &lm_session_key, &session_key); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } if (!(flags & CLI_CRED_LANMAN_AUTH)) { /* LM Key is still possible, just silly, so we do not * allow it. Fortunetly all LM crypto is off by * default and we require command line options to end * up here */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } if (!(flags & CLI_CRED_NTLM2)) { /* NTLM2 is incompatible... */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; } if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx) && lm_session_key.length == 16) { DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16); if (lm_response.length == 24) { SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, new_session_key.data); } else { static const uint8_t zeros[24]; SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, new_session_key.data); } session_key = new_session_key; dump_data_pw("LM session key\n", session_key.data, session_key.length); } /* Key exchange encryptes a new client-generated session key with the password-derived key */ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { /* Make up a new session key */ uint8_t client_session_key[16]; generate_secret_buffer(client_session_key, sizeof(client_session_key)); /* Encrypt the new session key with the old one */ encrypted_session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); /* Mark the new session key as the 'real' session key */ session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key)); } /* this generates the actual auth packet */ nt_status = msrpc_gen(mem_ctx, out, auth_gen_string, "NTLMSSP", NTLMSSP_AUTH, lm_response.data, lm_response.length, nt_response.data, nt_response.length, domain, user, workstation, encrypted_session_key.data, encrypted_session_key.length, ntlmssp_state->neg_flags, version_blob.data, version_blob.length); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return nt_status; } done: ntlmssp_state->session_key = session_key; talloc_steal(ntlmssp_state, session_key.data); DEBUG(3, ("NTLMSSP: Set final flags:\n")); debug_ntlmssp_flags(ntlmssp_state->neg_flags); talloc_steal(out_mem_ctx, out->data); ntlmssp_state->expected_state = NTLMSSP_DONE; if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) { nt_status = ntlmssp_sign_init(ntlmssp_state); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status))); talloc_free(mem_ctx); return nt_status; } } talloc_free(mem_ctx); return NT_STATUS_OK; }