void netlogon_creds_decrypt_samlogon(struct netlogon_creds_CredentialState *creds, uint16_t validation_level, union netr_Validation *validation) { static const char zeros[16]; struct netr_SamBaseInfo *base = NULL; switch (validation_level) { case 2: if (validation->sam2) { base = &validation->sam2->base; } break; case 3: if (validation->sam3) { base = &validation->sam3->base; } break; case 6: if (validation->sam6) { base = &validation->sam6->base; } break; default: /* If we can't find it, we can't very well decrypt it */ return; } if (!base) { return; } /* find and decyrpt the session keys, return in parameters above */ if (validation_level == 6) { /* they aren't encrypted! */ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { if (memcmp(base->key.key, zeros, sizeof(base->key.key)) != 0) { netlogon_creds_arcfour_crypt(creds, base->key.key, sizeof(base->key.key)); } if (memcmp(base->LMSessKey.key, zeros, sizeof(base->LMSessKey.key)) != 0) { netlogon_creds_arcfour_crypt(creds, base->LMSessKey.key, sizeof(base->LMSessKey.key)); } } else { if (memcmp(base->LMSessKey.key, zeros, sizeof(base->LMSessKey.key)) != 0) { netlogon_creds_des_decrypt_LMKey(creds, &base->LMSessKey); } } }
/* 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 rpccli_netlogon_sam_logon(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, uint32 logon_parameters, const char *domain, const char *username, const char *password, const char *workstation, uint16_t validation_level, int logon_type) { NTSTATUS result = NT_STATUS_UNSUCCESSFUL; NTSTATUS status; struct netr_Authenticator clnt_creds; struct netr_Authenticator ret_creds; union netr_LogonLevel *logon; union netr_Validation validation; uint8_t authoritative; fstring clnt_name_slash; struct dcerpc_binding_handle *b = cli->binding_handle; ZERO_STRUCT(ret_creds); logon = talloc_zero(mem_ctx, union netr_LogonLevel); if (!logon) { return NT_STATUS_NO_MEMORY; } if (workstation) { fstr_sprintf( clnt_name_slash, "\\\\%s", workstation ); } else { fstr_sprintf( clnt_name_slash, "\\\\%s", lp_netbios_name() ); } /* Initialise input parameters */ netlogon_creds_client_authenticator(cli->dc, &clnt_creds); switch (logon_type) { case NetlogonInteractiveInformation: { struct netr_PasswordInfo *password_info; struct samr_Password lmpassword; struct samr_Password ntpassword; password_info = talloc_zero(mem_ctx, struct netr_PasswordInfo); if (!password_info) { return NT_STATUS_NO_MEMORY; } nt_lm_owf_gen(password, ntpassword.hash, lmpassword.hash); if (cli->dc->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { netlogon_creds_aes_encrypt(cli->dc, lmpassword.hash, 16); netlogon_creds_aes_encrypt(cli->dc, ntpassword.hash, 16); } else if (cli->dc->negotiate_flags & NETLOGON_NEG_ARCFOUR) { netlogon_creds_arcfour_crypt(cli->dc, lmpassword.hash, 16); netlogon_creds_arcfour_crypt(cli->dc, ntpassword.hash, 16); } else { netlogon_creds_des_encrypt(cli->dc, &lmpassword); netlogon_creds_des_encrypt(cli->dc, &ntpassword); } password_info->identity_info.domain_name.string = domain; password_info->identity_info.parameter_control = logon_parameters; password_info->identity_info.logon_id_low = 0xdead; password_info->identity_info.logon_id_high = 0xbeef; password_info->identity_info.account_name.string = username; password_info->identity_info.workstation.string = clnt_name_slash; password_info->lmpassword = lmpassword; password_info->ntpassword = ntpassword; logon->password = password_info; break; } case NetlogonNetworkInformation: { struct netr_NetworkInfo *network_info; uint8 chal[8]; unsigned char local_lm_response[24]; unsigned char local_nt_response[24]; struct netr_ChallengeResponse lm; struct netr_ChallengeResponse nt; ZERO_STRUCT(lm); ZERO_STRUCT(nt); network_info = talloc_zero(mem_ctx, struct netr_NetworkInfo); if (!network_info) { return NT_STATUS_NO_MEMORY; } generate_random_buffer(chal, 8); SMBencrypt(password, chal, local_lm_response); SMBNTencrypt(password, chal, local_nt_response); lm.length = 24; lm.data = local_lm_response; nt.length = 24; nt.data = local_nt_response; network_info->identity_info.domain_name.string = domain; network_info->identity_info.parameter_control = logon_parameters; network_info->identity_info.logon_id_low = 0xdead; network_info->identity_info.logon_id_high = 0xbeef; network_info->identity_info.account_name.string = username; network_info->identity_info.workstation.string = clnt_name_slash; memcpy(network_info->challenge, chal, 8); network_info->nt = nt; network_info->lm = lm; logon->network = network_info; break; } default: DEBUG(0, ("switch value %d not supported\n", logon_type)); return NT_STATUS_INVALID_INFO_CLASS; } status = dcerpc_netr_LogonSamLogon(b, mem_ctx, cli->srv_name_slash, lp_netbios_name(), &clnt_creds, &ret_creds, logon_type, logon, validation_level, &validation, &authoritative, &result); if (!NT_STATUS_IS_OK(status)) { return status; } /* Always check returned credentials */ if (!netlogon_creds_client_check(cli->dc, &ret_creds.cred)) { DEBUG(0,("rpccli_netlogon_sam_logon: credentials chain check failed\n")); return NT_STATUS_ACCESS_DENIED; } return result; }
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; }