/* test two schannel connections */ bool torture_rpc_schannel2(struct torture_context *torture) { struct test_join *join_ctx; NTSTATUS status; const char *binding = torture_setting_string(torture, "binding", NULL); struct dcerpc_binding *b; struct dcerpc_pipe *p1 = NULL, *p2 = NULL; struct cli_credentials *credentials1, *credentials2; uint32_t dcerpc_flags = DCERPC_SCHANNEL | DCERPC_SIGN; join_ctx = torture_join_domain(torture, talloc_asprintf(torture, "%s2", TEST_MACHINE_NAME), ACB_WSTRUST, &credentials1); torture_assert(torture, join_ctx != NULL, "Failed to join domain with acct_flags=ACB_WSTRUST"); credentials2 = cli_credentials_shallow_copy(torture, credentials1); cli_credentials_set_netlogon_creds(credentials1, NULL); cli_credentials_set_netlogon_creds(credentials2, NULL); status = dcerpc_parse_binding(torture, binding, &b); torture_assert_ntstatus_ok(torture, status, "Bad binding string"); status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(torture, status, "set flags"); torture_comment(torture, "Opening first connection\n"); status = dcerpc_pipe_connect_b(torture, &p1, b, &ndr_table_netlogon, credentials1, torture->ev, torture->lp_ctx); torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel"); torture_comment(torture, "Opening second connection\n"); status = dcerpc_pipe_connect_b(torture, &p2, b, &ndr_table_netlogon, credentials2, torture->ev, torture->lp_ctx); torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel"); cli_credentials_set_netlogon_creds(credentials1, NULL); cli_credentials_set_netlogon_creds(credentials2, NULL); torture_comment(torture, "Testing logon on pipe1\n"); if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL)) return false; torture_comment(torture, "Testing logon on pipe2\n"); if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL)) return false; torture_comment(torture, "Again on pipe1\n"); if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL)) return false; torture_comment(torture, "Again on pipe2\n"); if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL)) return false; torture_leave_domain(torture, join_ctx); return true; }
static bool torture_rpc_setup_machine_bdc(struct torture_context *tctx, void **data) { NTSTATUS status; struct dcerpc_binding *binding; struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase, struct torture_rpc_tcase); struct torture_rpc_tcase_data *tcase_data; status = torture_rpc_binding(tctx, &binding); if (NT_STATUS_IS_ERR(status)) return false; *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); tcase_data->credentials = popt_get_cmdline_credentials(); tcase_data->join_ctx = torture_join_domain(tctx, tcase->machine_name, ACB_SVRTRUST, &tcase_data->credentials); if (tcase_data->join_ctx == NULL) torture_fail(tctx, "Failed to join as BDC"); status = dcerpc_pipe_connect_b(tctx, &(tcase_data->pipe), binding, tcase->table, tcase_data->credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); return NT_STATUS_IS_OK(status); }
static bool test_bind(struct torture_context *tctx, const void *private_data) { struct dcerpc_binding *binding; struct dcerpc_pipe *p; const uint32_t *flags = (const uint32_t *)private_data; torture_assert_ntstatus_ok(tctx, torture_rpc_binding(tctx, &binding), "failed to parse binding string"); binding->flags &= ~DCERPC_AUTH_OPTIONS; binding->flags |= *flags; torture_assert_ntstatus_ok(tctx, dcerpc_pipe_connect_b(tctx, &p, binding, &ndr_table_lsarpc, cmdline_credentials, tctx->ev, tctx->lp_ctx), "failed to connect pipe"); torture_assert(tctx, test_openpolicy(tctx, p), "failed to test openpolicy"); talloc_free(p); return true; }
static bool torture_rpc_setup_anonymous(struct torture_context *tctx, void **data) { NTSTATUS status; struct dcerpc_binding *binding; struct torture_rpc_tcase_data *tcase_data; struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase, struct torture_rpc_tcase); status = torture_rpc_binding(tctx, &binding); if (NT_STATUS_IS_ERR(status)) return false; *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data); tcase_data->credentials = cli_credentials_init_anon(tctx); status = dcerpc_pipe_connect_b(tctx, &(tcase_data->pipe), binding, tcase->table, tcase_data->credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Error connecting to server"); return NT_STATUS_IS_OK(status); }
/** * open a rpc connection to a specific transport */ NTSTATUS torture_rpc_connection_transport(struct torture_context *tctx, struct dcerpc_pipe **p, const struct ndr_interface_table *table, enum dcerpc_transport_t transport, uint32_t assoc_group_id) { NTSTATUS status; struct dcerpc_binding *binding; status = torture_rpc_binding(tctx, &binding); if (NT_STATUS_IS_ERR(status)) return status; binding->transport = transport; binding->assoc_group_id = assoc_group_id; status = dcerpc_pipe_connect_b(tctx, p, binding, table, cmdline_credentials, tctx->ev, tctx->lp_ctx); if (NT_STATUS_IS_ERR(status)) { *p = NULL; } return status; }
/* spoolss_RemoteFindFirstPrinterChangeNotifyEx */ static WERROR dcesrv_spoolss_RemoteFindFirstPrinterChangeNotifyEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct spoolss_RemoteFindFirstPrinterChangeNotifyEx *r) { struct dcerpc_pipe *p; struct dcerpc_binding *binding; NTSTATUS status; struct spoolss_ReplyOpenPrinter rop; struct cli_credentials *creds; struct policy_handle notify_handle; DEBUG(2, ("Received RFFPCNex from %s\n", r->in.local_machine)); /* * TODO: for now just open a connection to the client and drop it again * to keep the w2k3 PrintServer * happy to allow to open the Add Printer GUI * and the torture suite passing */ binding = talloc_zero(mem_ctx, struct dcerpc_binding); binding->transport = NCACN_NP; if (strncmp(r->in.local_machine, "\\\\", 2)) return WERR_INVALID_COMPUTERNAME; binding->host = r->in.local_machine+2; creds = cli_credentials_init_anon(mem_ctx); /* FIXME: Use machine credentials instead ? */ status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_spoolss, creds, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx); if (NT_STATUS_IS_ERR(status)) { DEBUG(0, ("unable to call back to %s\n", r->in.local_machine)); return WERR_SERVER_UNAVAILABLE; } ZERO_STRUCT(rop); rop.in.server_name = lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx); W_ERROR_HAVE_NO_MEMORY(rop.in.server_name); rop.in.printer_local = 0; rop.in.type = REG_NONE; rop.in.bufsize = 0; rop.in.buffer = NULL; rop.out.handle = ¬ify_handle; status = dcerpc_spoolss_ReplyOpenPrinter(p, mem_ctx, &rop); if (NT_STATUS_IS_ERR(status)) { DEBUG(0, ("unable to open remote printer %s\n", r->in.local_machine)); return WERR_SERVER_UNAVAILABLE; } talloc_free(p); return WERR_OK; }
static bool test_secrets(struct torture_context *torture, const void *_data) { struct dcerpc_pipe *p; struct policy_handle *handle; struct dcerpc_binding *binding; const struct secret_settings *settings = (const struct secret_settings *)_data; NTSTATUS status; struct dcerpc_binding_handle *b; lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp client:keyexchange", settings->keyexchange?"True":"False"); lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp_client:ntlm2", settings->ntlm2?"True":"False"); lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp_client:lm_key", settings->lm_key?"True":"False"); torture_assert_ntstatus_ok(torture, torture_rpc_binding(torture, &binding), "Getting bindoptions"); status = dcerpc_binding_set_flags(binding, settings->bindoptions, 0); torture_assert_ntstatus_ok(torture, status, "dcerpc_binding_set_flags"); status = dcerpc_pipe_connect_b(torture, &p, binding, &ndr_table_lsarpc, cmdline_credentials, torture->ev, torture->lp_ctx); torture_assert_ntstatus_ok(torture, status, "connect"); b = p->binding_handle; if (!test_lsa_OpenPolicy2(b, torture, &handle)) { talloc_free(p); return false; } torture_assert(torture, handle, "OpenPolicy2 failed. This test cannot run against this server"); if (!test_CreateSecret_basic(p, torture, handle)) { talloc_free(p); return false; } talloc_free(p); return true; }
/** * open a rpc connection to a specific transport */ NTSTATUS torture_rpc_connection_transport(struct torture_context *tctx, struct dcerpc_pipe **p, const struct ndr_interface_table *table, enum dcerpc_transport_t transport, uint32_t assoc_group_id, uint32_t extra_flags) { NTSTATUS status; struct dcerpc_binding *binding; *p = NULL; status = torture_rpc_binding(tctx, &binding); if (!NT_STATUS_IS_OK(status)) { return status; } status = dcerpc_binding_set_transport(binding, transport); if (!NT_STATUS_IS_OK(status)) { return status; } status = dcerpc_binding_set_assoc_group_id(binding, assoc_group_id); if (!NT_STATUS_IS_OK(status)) { return status; } status = dcerpc_binding_set_flags(binding, extra_flags, 0); if (!NT_STATUS_IS_OK(status)) { return status; } status = dcerpc_pipe_connect_b(tctx, p, binding, table, popt_get_cmdline_credentials(), tctx->ev, tctx->lp_ctx); if (!NT_STATUS_IS_OK(status)) { *p = NULL; return status; } return NT_STATUS_OK; }
/** * open a rpc connection to the chosen binding string */ _PUBLIC_ NTSTATUS torture_rpc_connection_with_binding(struct torture_context *tctx, struct dcerpc_binding *binding, struct dcerpc_pipe **p, const struct ndr_interface_table *table) { NTSTATUS status; dcerpc_init(); status = dcerpc_pipe_connect_b(tctx, p, binding, table, popt_get_cmdline_credentials(), tctx->ev, tctx->lp_ctx); if (NT_STATUS_IS_ERR(status)) { torture_warning(tctx, "Failed to connect to remote server: %s %s\n", dcerpc_binding_string(tctx, binding), nt_errstr(status)); } return status; }
/** * Connect to a specific interface, but ask the user * for information not specified */ struct dcerpc_binding_handle *gtk_connect_rpc_interface(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, struct loadparm_context *lp_ctx, const struct ndr_interface_table *table) { GtkRpcBindingDialog *d; NTSTATUS status; struct dcerpc_binding_handle *pipe; struct cli_credentials *cred; gint result; d = SAMBAGTK_RPC_BINDING_DIALOG(gtk_rpc_binding_dialog_new(NULL)); result = gtk_dialog_run(GTK_DIALOG(d)); if (result != GTK_RESPONSE_ACCEPT) { gtk_widget_destroy(GTK_WIDGET(d)); return NULL; } cred = cli_credentials_init(mem_ctx); cli_credentials_guess(cred, lp_ctx); cli_credentials_set_gtk_callbacks(cred); status = dcerpc_pipe_connect_b(mem_ctx, &pipe, gtk_rpc_binding_dialog_get_binding(d, mem_ctx), table, cred, ev_ctx, lp_ctx); if(!NT_STATUS_IS_OK(status)) { gtk_show_ntstatus(NULL, "While connecting to interface", status); gtk_widget_destroy(GTK_WIDGET(d)); talloc_free(cred); return NULL; } gtk_widget_destroy(GTK_WIDGET(d)); talloc_free(cred); return pipe; }
/* test a schannel connection with the given flags */ static bool test_schannel(struct torture_context *tctx, uint16_t acct_flags, uint32_t dcerpc_flags, int i) { struct test_join *join_ctx; NTSTATUS status; const char *binding = torture_setting_string(tctx, "binding", NULL); struct dcerpc_binding *b; struct dcerpc_pipe *p = NULL; struct dcerpc_pipe *p_netlogon = NULL; struct dcerpc_pipe *p_netlogon2 = NULL; struct dcerpc_pipe *p_netlogon3 = NULL; struct dcerpc_pipe *p_samr2 = NULL; struct dcerpc_pipe *p_lsa = NULL; struct netlogon_creds_CredentialState *creds; struct cli_credentials *credentials; enum dcerpc_transport_t transport; join_ctx = torture_join_domain(tctx, talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, i), acct_flags, &credentials); torture_assert(tctx, join_ctx != NULL, "Failed to join domain"); status = dcerpc_parse_binding(tctx, binding, &b); torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(tctx, status, "set flags"); status = dcerpc_pipe_connect_b(tctx, &p, b, &ndr_table_samr, credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Failed to connect to samr with schannel"); torture_assert(tctx, test_samr_ops(tctx, p->binding_handle), "Failed to process schannel secured SAMR ops"); /* Also test that when we connect to the netlogon pipe, that * the credentials we setup on the first pipe are valid for * the second */ /* Swap the binding details from SAMR to NETLOGON */ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "epm map"); status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(tctx, status, "set flags"); status = dcerpc_secondary_auth_connection(p, b, &ndr_table_netlogon, credentials, tctx->lp_ctx, tctx, &p_netlogon); torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); creds = cli_credentials_get_netlogon_creds(credentials); torture_assert(tctx, (creds != NULL), "schannel creds"); /* checks the capabilities */ torture_assert(tctx, test_netlogon_capabilities(p_netlogon, tctx, credentials, creds), "Failed to process schannel secured capability ops (on fresh connection)"); /* do a couple of logins */ torture_assert(tctx, test_netlogon_ops(p_netlogon, tctx, credentials, creds), "Failed to process schannel secured NETLOGON ops"); torture_assert(tctx, test_netlogon_ex_ops(p_netlogon, tctx, credentials, creds), "Failed to process schannel secured NETLOGON EX ops"); /* we *MUST* use ncacn_np for openpolicy etc. */ transport = dcerpc_binding_get_transport(b); status = dcerpc_binding_set_transport(b, NCACN_NP); torture_assert_ntstatus_ok(tctx, status, "set transport"); /* Swap the binding details from SAMR to LSARPC */ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "epm map"); torture_assert_ntstatus_ok(tctx, dcerpc_pipe_connect_b(tctx, &p_lsa, b, &ndr_table_lsarpc, credentials, tctx->ev, tctx->lp_ctx), "failed to connect lsarpc with schannel"); torture_assert(tctx, test_lsa_ops(tctx, p_lsa), "Failed to process schannel secured LSA ops"); talloc_free(p_lsa); p_lsa = NULL; /* we *MUST* use ncacn_ip_tcp for lookupsids3/lookupnames4 */ status = dcerpc_binding_set_transport(b, NCACN_IP_TCP); torture_assert_ntstatus_ok(tctx, status, "set transport"); torture_assert_ntstatus_ok(tctx, dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx), "failed to call epm map"); torture_assert_ntstatus_ok(tctx, dcerpc_pipe_connect_b(tctx, &p_lsa, b, &ndr_table_lsarpc, credentials, tctx->ev, tctx->lp_ctx), "failed to connect lsarpc with schannel"); torture_assert(tctx, test_many_LookupSids(p_lsa, tctx, NULL), "LsaLookupSids3 failed!\n"); status = dcerpc_binding_set_transport(b, transport); torture_assert_ntstatus_ok(tctx, status, "set transport"); /* Drop the socket, we want to start from scratch */ talloc_free(p); p = NULL; /* Now see what we are still allowed to do */ status = dcerpc_parse_binding(tctx, binding, &b); torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(tctx, status, "set flags"); status = dcerpc_pipe_connect_b(tctx, &p_samr2, b, &ndr_table_samr, credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Failed to connect with schannel"); /* do a some SAMR operations. We have *not* done a new serverauthenticate */ torture_assert (tctx, test_samr_ops(tctx, p_samr2->binding_handle), "Failed to process schannel secured SAMR ops (on fresh connection)"); /* Swap the binding details from SAMR to NETLOGON */ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "epm"); status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(tctx, status, "set flags"); status = dcerpc_secondary_auth_connection(p_samr2, b, &ndr_table_netlogon, credentials, tctx->lp_ctx, tctx, &p_netlogon2); torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection"); /* checks the capabilities */ torture_assert(tctx, test_netlogon_capabilities(p_netlogon2, tctx, credentials, creds), "Failed to process schannel secured capability ops (on fresh connection)"); /* Try the schannel-only SamLogonEx operation */ torture_assert(tctx, test_netlogon_ex_ops(p_netlogon2, tctx, credentials, creds), "Failed to process schannel secured NETLOGON EX ops (on fresh connection)"); /* And the more traditional style, proving that the * credentials chaining state is fully present */ torture_assert(tctx, test_netlogon_ops(p_netlogon2, tctx, credentials, creds), "Failed to process schannel secured NETLOGON ops (on fresh connection)"); /* Drop the socket, we want to start from scratch (again) */ talloc_free(p_samr2); /* We don't want schannel for this test */ status = dcerpc_binding_set_flags(b, 0, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(tctx, status, "set flags"); status = dcerpc_pipe_connect_b(tctx, &p_netlogon3, b, &ndr_table_netlogon, credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel"); torture_assert(tctx, !test_netlogon_ex_ops(p_netlogon3, tctx, credentials, creds), "Processed NOT schannel secured NETLOGON EX ops without SCHANNEL (unsafe)"); /* Required because the previous call will mark the current context as having failed */ tctx->last_result = TORTURE_OK; tctx->last_reason = NULL; torture_assert(tctx, test_netlogon_ops(p_netlogon3, tctx, credentials, creds), "Failed to processed NOT schannel secured NETLOGON ops without new ServerAuth"); torture_leave_domain(tctx, join_ctx); return true; }
static bool _test_DsBind(struct torture_context *tctx, struct DsGetinfoTest *ctx, struct cli_credentials *credentials, struct DsGetinfoBindInfo *b) { NTSTATUS status; bool ret = true; status = dcerpc_pipe_connect_b(ctx, &b->drs_pipe, ctx->drsuapi_binding, &ndr_table_drsuapi, credentials, tctx->ev, tctx->lp_ctx); if (!NT_STATUS_IS_OK(status)) { printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status)); return false; } b->drs_handle = b->drs_pipe->binding_handle; status = dcerpc_drsuapi_DsBind_r(b->drs_handle, ctx, &b->req); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); printf("dcerpc_drsuapi_DsBind failed - %s\n", errstr); ret = false; } else if (!W_ERROR_IS_OK(b->req.out.result)) { printf("DsBind failed - %s\n", win_errstr(b->req.out.result)); ret = false; } ZERO_STRUCT(b->peer_bind_info28); if (b->req.out.bind_info) { switch (b->req.out.bind_info->length) { case 24: { struct drsuapi_DsBindInfo24 *info24; info24 = &b->req.out.bind_info->info.info24; b->peer_bind_info28.supported_extensions= info24->supported_extensions; b->peer_bind_info28.site_guid = info24->site_guid; b->peer_bind_info28.pid = info24->pid; b->peer_bind_info28.repl_epoch = 0; break; } case 48: { struct drsuapi_DsBindInfo48 *info48; info48 = &b->req.out.bind_info->info.info48; b->peer_bind_info28.supported_extensions= info48->supported_extensions; b->peer_bind_info28.site_guid = info48->site_guid; b->peer_bind_info28.pid = info48->pid; b->peer_bind_info28.repl_epoch = info48->repl_epoch; break; } case 28: { b->peer_bind_info28 = b->req.out.bind_info->info.info28; break; } case 32: { struct drsuapi_DsBindInfo32 *info32; info32 = &b->req.out.bind_info->info.info32; b->peer_bind_info28.supported_extensions= info32->supported_extensions; b->peer_bind_info28.site_guid = info32->site_guid; b->peer_bind_info28.pid = info32->pid; b->peer_bind_info28.repl_epoch = info32->repl_epoch; break; } case 52: { struct drsuapi_DsBindInfo52 *info52; info52 = &b->req.out.bind_info->info.info52; b->peer_bind_info28.supported_extensions= info52->supported_extensions; b->peer_bind_info28.site_guid = info52->site_guid; b->peer_bind_info28.pid = info52->pid; b->peer_bind_info28.repl_epoch = info52->repl_epoch; break; } default: printf("DsBind - warning: unknown BindInfo length: %u\n", b->req.out.bind_info->length); } } return ret; }
/* test a schannel connection with the given flags */ static bool test_schannel(struct torture_context *tctx, uint16_t acct_flags, uint32_t dcerpc_flags, int i) { struct test_join *join_ctx; NTSTATUS status; const char *binding = torture_setting_string(tctx, "binding", NULL); struct dcerpc_binding *b; struct dcerpc_pipe *p = NULL; struct dcerpc_pipe *p_netlogon = NULL; struct dcerpc_pipe *p_netlogon2 = NULL; struct dcerpc_pipe *p_netlogon3 = NULL; struct dcerpc_pipe *p_samr2 = NULL; struct dcerpc_pipe *p_lsa = NULL; struct netlogon_creds_CredentialState *creds; struct cli_credentials *credentials; join_ctx = torture_join_domain(tctx, talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, i), acct_flags, &credentials); torture_assert(tctx, join_ctx != NULL, "Failed to join domain"); status = dcerpc_parse_binding(tctx, binding, &b); torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); b->flags &= ~DCERPC_AUTH_OPTIONS; b->flags |= dcerpc_flags; status = dcerpc_pipe_connect_b(tctx, &p, b, &ndr_table_samr, credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Failed to connect with schannel"); torture_assert(tctx, test_samr_ops(tctx, p->binding_handle), "Failed to process schannel secured SAMR ops"); /* Also test that when we connect to the netlogon pipe, that * the credentials we setup on the first pipe are valid for * the second */ /* Swap the binding details from SAMR to NETLOGON */ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "epm map"); status = dcerpc_secondary_connection(p, &p_netlogon, b); torture_assert_ntstatus_ok(tctx, status, "seconday connection"); status = dcerpc_bind_auth(p_netlogon, &ndr_table_netlogon, credentials, lpcfg_gensec_settings(tctx, tctx->lp_ctx), DCERPC_AUTH_TYPE_SCHANNEL, dcerpc_auth_level(p->conn), NULL); torture_assert_ntstatus_ok(tctx, status, "bind auth"); status = dcerpc_schannel_creds(p_netlogon->conn->security_state.generic_state, tctx, &creds); torture_assert_ntstatus_ok(tctx, status, "schannel creds"); /* do a couple of logins */ torture_assert(tctx, test_netlogon_ops(p_netlogon, tctx, credentials, creds), "Failed to process schannel secured NETLOGON ops"); torture_assert(tctx, test_netlogon_ex_ops(p_netlogon, tctx, credentials, creds), "Failed to process schannel secured NETLOGON EX ops"); /* Swap the binding details from SAMR to LSARPC */ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "epm map"); status = dcerpc_secondary_connection(p, &p_lsa, b); torture_assert_ntstatus_ok(tctx, status, "seconday connection"); status = dcerpc_bind_auth(p_lsa, &ndr_table_lsarpc, credentials, lpcfg_gensec_settings(tctx, tctx->lp_ctx), DCERPC_AUTH_TYPE_SCHANNEL, dcerpc_auth_level(p->conn), NULL); torture_assert_ntstatus_ok(tctx, status, "bind auth"); torture_assert(tctx, test_lsa_ops(tctx, p_lsa), "Failed to process schannel secured LSA ops"); /* Drop the socket, we want to start from scratch */ talloc_free(p); p = NULL; /* Now see what we are still allowed to do */ status = dcerpc_parse_binding(tctx, binding, &b); torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); b->flags &= ~DCERPC_AUTH_OPTIONS; b->flags |= dcerpc_flags; status = dcerpc_pipe_connect_b(tctx, &p_samr2, b, &ndr_table_samr, credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Failed to connect with schannel"); /* do a some SAMR operations. We have *not* done a new serverauthenticate */ torture_assert (tctx, test_samr_ops(tctx, p_samr2->binding_handle), "Failed to process schannel secured SAMR ops (on fresh connection)"); /* Swap the binding details from SAMR to NETLOGON */ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "epm"); status = dcerpc_secondary_connection(p_samr2, &p_netlogon2, b); torture_assert_ntstatus_ok(tctx, status, "seconday connection"); /* and now setup an SCHANNEL bind on netlogon */ status = dcerpc_bind_auth(p_netlogon2, &ndr_table_netlogon, credentials, lpcfg_gensec_settings(tctx, tctx->lp_ctx), DCERPC_AUTH_TYPE_SCHANNEL, dcerpc_auth_level(p_samr2->conn), NULL); torture_assert_ntstatus_ok(tctx, status, "auth failed"); /* Try the schannel-only SamLogonEx operation */ torture_assert(tctx, test_netlogon_ex_ops(p_netlogon2, tctx, credentials, creds), "Failed to process schannel secured NETLOGON EX ops (on fresh connection)"); /* And the more traditional style, proving that the * credentials chaining state is fully present */ torture_assert(tctx, test_netlogon_ops(p_netlogon2, tctx, credentials, creds), "Failed to process schannel secured NETLOGON ops (on fresh connection)"); /* Drop the socket, we want to start from scratch (again) */ talloc_free(p_samr2); /* We don't want schannel for this test */ b->flags &= ~DCERPC_AUTH_OPTIONS; status = dcerpc_pipe_connect_b(tctx, &p_netlogon3, b, &ndr_table_netlogon, credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel"); torture_assert(tctx, !test_netlogon_ex_ops(p_netlogon3, tctx, credentials, creds), "Processed NOT schannel secured NETLOGON EX ops without SCHANNEL (unsafe)"); /* Required because the previous call will mark the current context as having failed */ tctx->last_result = TORTURE_OK; tctx->last_reason = NULL; torture_assert(tctx, test_netlogon_ops(p_netlogon3, tctx, credentials, creds), "Failed to processed NOT schannel secured NETLOGON ops without new ServerAuth"); torture_leave_domain(tctx, join_ctx); return true; }
/* * complete a domain join, when joining to a AD domain: * 1.) connect and bind to the DRSUAPI pipe * 2.) do a DsCrackNames() to find the machine account dn * 3.) connect to LDAP * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...) * 6.) do a DsCrackNames() to find the domain dn * 7.) find out Site specific stuff, look at libnet_JoinSite() for details */ static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r) { NTSTATUS status; TALLOC_CTX *tmp_ctx; const char *realm = r->out.realm; struct dcerpc_binding *samr_binding = r->out.samr_binding; struct dcerpc_pipe *drsuapi_pipe; struct dcerpc_binding *drsuapi_binding; struct drsuapi_DsBind r_drsuapi_bind; struct drsuapi_DsCrackNames r_crack_names; struct drsuapi_DsNameString names[1]; struct policy_handle drsuapi_bind_handle; struct GUID drsuapi_bind_guid; struct ldb_context *remote_ldb; struct ldb_dn *account_dn; const char *account_dn_str; const char *remote_ldb_url; struct ldb_result *res; struct ldb_message *msg; int ret, rtn; unsigned int kvno; const char * const attrs[] = { "msDS-KeyVersionNumber", "servicePrincipalName", "dNSHostName", NULL, }; r->out.error_string = NULL; /* We need to convert between a samAccountName and domain to a * DN in the directory. The correct way to do this is with * DRSUAPI CrackNames */ /* Fiddle with the bindings, so get to DRSUAPI on * NCACN_IP_TCP, sealed */ tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context"); if (!tmp_ctx) { r->out.error_string = NULL; return NT_STATUS_NO_MEMORY; } drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding); if (!drsuapi_binding) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } *drsuapi_binding = *samr_binding; /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */ if (drsuapi_binding->transport != NCALRPC) { drsuapi_binding->transport = NCACN_IP_TCP; } drsuapi_binding->endpoint = NULL; drsuapi_binding->flags |= DCERPC_SEAL; status = dcerpc_pipe_connect_b(tmp_ctx, &drsuapi_pipe, drsuapi_binding, &dcerpc_table_drsuapi, ctx->cred, ctx->event_ctx); if (!NT_STATUS_IS_OK(status)) { r->out.error_string = talloc_asprintf(r, "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s", r->in.domain_name, nt_errstr(status)); talloc_free(tmp_ctx); return status; } /* get a DRSUAPI pipe handle */ GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid); r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid; r_drsuapi_bind.in.bind_info = NULL; r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle; status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsBind failed - %s", dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code)); talloc_free(tmp_ctx); return status; } else { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsBind failed - %s", nt_errstr(status)); talloc_free(tmp_ctx); return status; } } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) { r->out.error_string = talloc_asprintf(r, "DsBind failed - %s", win_errstr(r_drsuapi_bind.out.result)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* Actually 'crack' the names */ ZERO_STRUCT(r_crack_names); r_crack_names.in.bind_handle = &drsuapi_bind_handle; r_crack_names.in.level = 1; r_crack_names.in.req.req1.unknown1 = 0x000004e4; r_crack_names.in.req.req1.unknown2 = 0x00000407; r_crack_names.in.req.req1.count = 1; r_crack_names.in.req.req1.names = names; r_crack_names.in.req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; r_crack_names.in.req.req1.format_offered= DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY; r_crack_names.in.req.req1.format_desired= DRSUAPI_DS_NAME_FORMAT_FQDN_1779; names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid); if (!names[0].str) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s", names[0].str, dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code)); talloc_free(tmp_ctx); return status; } else { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s", names[0].str, nt_errstr(status)); talloc_free(tmp_ctx); return status; } } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } else if (r_crack_names.out.level != 1 || !r_crack_names.out.ctr.ctr1 || r_crack_names.out.ctr.ctr1->count != 1) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed"); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } else if (r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr.ctr1->array[0].status); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } else if (r_crack_names.out.ctr.ctr1->array[0].result_name == NULL) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name"); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } /* Store the DN of our machine account. */ account_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name; account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str); if (! ldb_dn_validate(account_dn)) { r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s", account_dn_str); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* Now we know the user's DN, open with LDAP, read and modify a few things */ remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", drsuapi_binding->target_hostname); if (!remote_ldb_url) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } remote_ldb = ldb_wrap_connect(tmp_ctx, remote_ldb_url, NULL, ctx->cred, 0, NULL); if (!remote_ldb) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* search for the user's record */ ret = ldb_search(remote_ldb, account_dn, LDB_SCOPE_BASE, NULL, attrs, &res); if (ret != LDB_SUCCESS) { r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s", account_dn_str, ldb_errstring(remote_ldb)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } talloc_steal(tmp_ctx, res); if (res->count != 1) { r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries", account_dn_str, res->count); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* If we have a kvno recorded in AD, we need it locally as well */ kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0); /* Prepare a new message, for the modify */ msg = ldb_msg_new(tmp_ctx); if (!msg) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } msg->dn = res->msgs[0]->dn; { int i; const char *service_principal_name[6]; const char *dns_host_name = strlower_talloc(tmp_ctx, talloc_asprintf(tmp_ctx, "%s.%s", r->in.netbios_name, realm)); if (!dns_host_name) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name); service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(tmp_ctx, r->in.netbios_name)); service_principal_name[2] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, realm); service_principal_name[3] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), realm); service_principal_name[4] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, r->out.domain_name); service_principal_name[5] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), r->out.domain_name); for (i=0; i < ARRAY_SIZE(service_principal_name); i++) { if (!service_principal_name[i]) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[i]); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } } rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name); if (rtn == -1) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } rtn = samdb_replace(remote_ldb, tmp_ctx, msg); if (rtn != 0) { r->out.error_string = talloc_asprintf(r, "Failed to replace entries on %s", ldb_dn_get_linearized(msg->dn)); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } } /* DsCrackNames to find out the DN of the domain. */ r_crack_names.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; r_crack_names.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name); if (!names[0].str) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s", r->in.domain_name, dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code)); talloc_free(tmp_ctx); return status; } else { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s", r->in.domain_name, nt_errstr(status)); talloc_free(tmp_ctx); return status; } } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } else if (r_crack_names.out.level != 1 || !r_crack_names.out.ctr.ctr1 || r_crack_names.out.ctr.ctr1->count != 1 || !r_crack_names.out.ctr.ctr1->array[0].result_name || r_crack_names.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed"); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* Store the account DN. */ r->out.account_dn_str = account_dn_str; talloc_steal(r, account_dn_str); /* Store the domain DN. */ r->out.domain_dn_str = r_crack_names.out.ctr.ctr1->array[0].result_name; talloc_steal(r, r_crack_names.out.ctr.ctr1->array[0].result_name); r->out.kvno = kvno; if (r->in.acct_type == ACB_SVRTRUST) { status = libnet_JoinSite(remote_ldb, r); } talloc_free(tmp_ctx); return status; }
static bool test_schannel_anonymous_setPassword(struct torture_context *tctx, uint32_t dcerpc_flags, bool use2) { NTSTATUS status, result; const char *binding = torture_setting_string(tctx, "binding", NULL); struct dcerpc_binding *b; struct dcerpc_pipe *p = NULL; struct cli_credentials *credentials; bool ok = true; credentials = cli_credentials_init(NULL); torture_assert(tctx, credentials != NULL, "Bad credentials"); cli_credentials_set_anonymous(credentials); status = dcerpc_parse_binding(tctx, binding, &b); torture_assert_ntstatus_ok(tctx, status, "Bad binding string"); status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS); torture_assert_ntstatus_ok(tctx, status, "set flags"); status = dcerpc_pipe_connect_b(tctx, &p, b, &ndr_table_netlogon, credentials, tctx->ev, tctx->lp_ctx); torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel"); if (use2) { struct netr_ServerPasswordSet2 r = {}; struct netr_Authenticator credential = {}; struct netr_Authenticator return_authenticator = {}; struct netr_CryptPassword new_password = {}; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); r.in.secure_channel_type = 0; r.in.computer_name = TEST_MACHINE_NAME; r.in.credential = &credential; r.in.new_password = &new_password; r.out.return_authenticator = &return_authenticator; status = dcerpc_netr_ServerPasswordSet2_r(p->binding_handle, tctx, &r); result = r.out.result; } else { struct netr_ServerPasswordSet r = {}; struct netr_Authenticator credential = {}; struct netr_Authenticator return_authenticator = {}; struct samr_Password new_password = {}; r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME); r.in.secure_channel_type = 0; r.in.computer_name = TEST_MACHINE_NAME; r.in.credential = &credential; r.in.new_password = &new_password; r.out.return_authenticator = &return_authenticator; status = dcerpc_netr_ServerPasswordSet_r(p->binding_handle, tctx, &r); result = r.out.result; } torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet failed"); if (NT_STATUS_IS_OK(result)) { torture_fail(tctx, "unexpectedly received NT_STATUS_OK"); } return ok; }
bool torture_domain_close_samr(struct torture_context *torture) { bool ret = true; NTSTATUS status; TALLOC_CTX *mem_ctx = NULL; struct libnet_context *ctx; struct lsa_String domain_name; struct dcerpc_binding *binding; uint32_t access_mask; struct policy_handle h; struct dcerpc_pipe *p; struct libnet_DomainClose r; struct dom_sid *sid; status = torture_rpc_binding(torture, &binding); if (!NT_STATUS_IS_OK(status)) { return false; } ctx = libnet_context_init(torture->ev, torture->lp_ctx); if (ctx == NULL) { d_printf("failed to create libnet context\n"); ret = false; goto done; } ctx->cred = cmdline_credentials; mem_ctx = talloc_init("torture_domain_close_samr"); status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_samr, ctx->cred, torture->ev, torture->lp_ctx); if (!NT_STATUS_IS_OK(status)) { d_printf("failed to connect to server: %s\n", nt_errstr(status)); ret = false; goto done; } domain_name.string = talloc_strdup(mem_ctx, lp_workgroup(torture->lp_ctx)); if (!test_opendomain_samr(p, torture, &h, &domain_name, &access_mask, &sid)) { d_printf("failed to open domain on samr service\n"); ret = false; goto done; } ctx->samr.pipe = p; ctx->samr.name = talloc_steal(ctx, domain_name.string); ctx->samr.access_mask = access_mask; ctx->samr.handle = h; ctx->samr.sid = talloc_steal(ctx, sid); /* we have to use pipe's event context, otherwise the call will hang indefinitely - this wouldn't be the case if pipe was opened by means of libnet call */ ctx->event_ctx = p->conn->event_ctx; ZERO_STRUCT(r); r.in.type = DOMAIN_SAMR; r.in.domain_name = domain_name.string; status = libnet_DomainClose(ctx, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { ret = false; goto done; } done: talloc_free(mem_ctx); talloc_free(ctx); return ret; }
bool torture_domain_close_lsa(struct torture_context *torture) { bool ret = true; NTSTATUS status; TALLOC_CTX *mem_ctx=NULL; struct libnet_context *ctx; struct lsa_String domain_name; struct dcerpc_binding *binding; uint32_t access_mask; struct policy_handle h; struct dcerpc_pipe *p; struct libnet_DomainClose r; status = torture_rpc_binding(torture, &binding); if (!NT_STATUS_IS_OK(status)) { return false; } ctx = libnet_context_init(torture->ev, torture->lp_ctx); if (ctx == NULL) { d_printf("failed to create libnet context\n"); ret = false; goto done; } ctx->cred = cmdline_credentials; mem_ctx = talloc_init("torture_domain_close_lsa"); status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_lsarpc, cmdline_credentials, torture->ev, torture->lp_ctx); if (!NT_STATUS_IS_OK(status)) { d_printf("failed to connect to server: %s\n", nt_errstr(status)); ret = false; goto done; } domain_name.string = lp_workgroup(torture->lp_ctx); if (!test_opendomain_lsa(p, torture, &h, &domain_name, &access_mask)) { d_printf("failed to open domain on lsa service\n"); ret = false; goto done; } ctx->lsa.pipe = p; ctx->lsa.name = domain_name.string; ctx->lsa.access_mask = access_mask; ctx->lsa.handle = h; /* we have to use pipe's event context, otherwise the call will hang indefinitely */ ctx->event_ctx = p->conn->event_ctx; ZERO_STRUCT(r); r.in.type = DOMAIN_LSA; r.in.domain_name = domain_name.string; status = libnet_DomainClose(ctx, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { ret = false; goto done; } done: talloc_free(mem_ctx); talloc_free(ctx); return ret; }
/* * complete a domain join, when joining to a AD domain: * 1.) connect and bind to the DRSUAPI pipe * 2.) do a DsCrackNames() to find the machine account dn * 3.) connect to LDAP * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...) * 6.) do a DsCrackNames() to find the domain dn * 7.) find out Site specific stuff, look at libnet_JoinSite() for details */ static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r) { NTSTATUS status; TALLOC_CTX *tmp_ctx; const char *realm = r->out.realm; struct dcerpc_binding *samr_binding = r->out.samr_binding; struct dcerpc_pipe *drsuapi_pipe; struct dcerpc_binding *drsuapi_binding; struct drsuapi_DsBind r_drsuapi_bind; struct drsuapi_DsCrackNames r_crack_names; struct drsuapi_DsNameString names[1]; struct policy_handle drsuapi_bind_handle; struct GUID drsuapi_bind_guid; struct ldb_context *remote_ldb; struct ldb_dn *account_dn; const char *account_dn_str; const char *remote_ldb_url; struct ldb_result *res; struct ldb_message *msg; int ret, rtn; const char * const attrs[] = { "msDS-KeyVersionNumber", "servicePrincipalName", "dNSHostName", "objectGUID", NULL, }; r->out.error_string = NULL; /* We need to convert between a samAccountName and domain to a * DN in the directory. The correct way to do this is with * DRSUAPI CrackNames */ /* Fiddle with the bindings, so get to DRSUAPI on * NCACN_IP_TCP, sealed */ tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context"); if (!tmp_ctx) { r->out.error_string = NULL; return NT_STATUS_NO_MEMORY; } drsuapi_binding = talloc_zero(tmp_ctx, struct dcerpc_binding); if (!drsuapi_binding) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } *drsuapi_binding = *samr_binding; /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */ if (drsuapi_binding->transport != NCALRPC) { drsuapi_binding->transport = NCACN_IP_TCP; } drsuapi_binding->endpoint = NULL; drsuapi_binding->flags |= DCERPC_SEAL; status = dcerpc_pipe_connect_b(tmp_ctx, &drsuapi_pipe, drsuapi_binding, &ndr_table_drsuapi, ctx->cred, ctx->event_ctx, ctx->lp_ctx); if (!NT_STATUS_IS_OK(status)) { r->out.error_string = talloc_asprintf(r, "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s", r->out.domain_name, nt_errstr(status)); talloc_free(tmp_ctx); return status; } /* get a DRSUAPI pipe handle */ GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid); r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid; r_drsuapi_bind.in.bind_info = NULL; r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle; status = dcerpc_drsuapi_DsBind_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_drsuapi_bind); if (!NT_STATUS_IS_OK(status)) { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsBind failed - %s", nt_errstr(status)); talloc_free(tmp_ctx); return status; } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) { r->out.error_string = talloc_asprintf(r, "DsBind failed - %s", win_errstr(r_drsuapi_bind.out.result)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* Actually 'crack' the names */ ZERO_STRUCT(r_crack_names); r_crack_names.in.bind_handle = &drsuapi_bind_handle; r_crack_names.in.level = 1; r_crack_names.in.req = talloc(r, union drsuapi_DsNameRequest); if (!r_crack_names.in.req) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } r_crack_names.in.req->req1.codepage = 1252; /* western european */ r_crack_names.in.req->req1.language = 0x00000407; /* german */ r_crack_names.in.req->req1.count = 1; r_crack_names.in.req->req1.names = names; r_crack_names.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY; r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid); if (!names[0].str) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } r_crack_names.out.ctr = talloc(r, union drsuapi_DsNameCtr); r_crack_names.out.level_out = talloc(r, uint32_t); if (!r_crack_names.out.ctr || !r_crack_names.out.level_out) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names); if (!NT_STATUS_IS_OK(status)) { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s", names[0].str, nt_errstr(status)); talloc_free(tmp_ctx); return status; } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } else if (*r_crack_names.out.level_out != 1 || !r_crack_names.out.ctr->ctr1 || r_crack_names.out.ctr->ctr1->count != 1) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed"); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } else if (r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr->ctr1->array[0].status); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } else if (r_crack_names.out.ctr->ctr1->array[0].result_name == NULL) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name"); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } /* Store the DN of our machine account. */ account_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name; /* Now we know the user's DN, open with LDAP, read and modify a few things */ remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", drsuapi_binding->target_hostname); if (!remote_ldb_url) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } remote_ldb = ldb_wrap_connect(tmp_ctx, ctx->event_ctx, ctx->lp_ctx, remote_ldb_url, NULL, ctx->cred, 0); if (!remote_ldb) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str); if (account_dn == NULL) { r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s", account_dn_str); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* search for the user's record */ ret = ldb_search(remote_ldb, tmp_ctx, &res, account_dn, LDB_SCOPE_BASE, attrs, NULL); if (ret != LDB_SUCCESS) { r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s", account_dn_str, ldb_errstring(remote_ldb)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } if (res->count != 1) { r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries", account_dn_str, res->count); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* Prepare a new message, for the modify */ msg = ldb_msg_new(tmp_ctx); if (!msg) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } msg->dn = res->msgs[0]->dn; { unsigned int i; const char *service_principal_name[2]; const char *dns_host_name = strlower_talloc(msg, talloc_asprintf(msg, "%s.%s", r->in.netbios_name, realm)); if (!dns_host_name) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } service_principal_name[0] = talloc_asprintf(msg, "HOST/%s", dns_host_name); service_principal_name[1] = talloc_asprintf(msg, "HOST/%s", r->in.netbios_name); for (i=0; i < ARRAY_SIZE(service_principal_name); i++) { if (!service_principal_name[i]) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } rtn = ldb_msg_add_string(msg, "servicePrincipalName", service_principal_name[i]); if (rtn != LDB_SUCCESS) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } } rtn = ldb_msg_add_string(msg, "dNSHostName", dns_host_name); if (rtn != LDB_SUCCESS) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } rtn = dsdb_replace(remote_ldb, msg, 0); if (rtn != LDB_SUCCESS) { r->out.error_string = talloc_asprintf(r, "Failed to replace entries on %s", ldb_dn_get_linearized(msg->dn)); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } } msg = ldb_msg_new(tmp_ctx); if (!msg) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } msg->dn = res->msgs[0]->dn; rtn = samdb_msg_add_uint(remote_ldb, msg, msg, "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES); if (rtn != LDB_SUCCESS) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } rtn = dsdb_replace(remote_ldb, msg, 0); /* The remote server may not support this attribute, if it * isn't a modern schema */ if (rtn != LDB_SUCCESS && rtn != LDB_ERR_NO_SUCH_ATTRIBUTE) { r->out.error_string = talloc_asprintf(r, "Failed to replace msDS-SupportedEncryptionTypes on %s", ldb_dn_get_linearized(msg->dn)); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* DsCrackNames to find out the DN of the domain. */ r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name); if (!names[0].str) { r->out.error_string = NULL; talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names); if (!NT_STATUS_IS_OK(status)) { r->out.error_string = talloc_asprintf(r, "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s", r->in.domain_name, nt_errstr(status)); talloc_free(tmp_ctx); return status; } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result)); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } else if (*r_crack_names.out.level_out != 1 || !r_crack_names.out.ctr->ctr1 || r_crack_names.out.ctr->ctr1->count != 1 || !r_crack_names.out.ctr->ctr1->array[0].result_name || r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { r->out.error_string = talloc_asprintf(r, "DsCrackNames failed"); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; } /* Store the account DN. */ r->out.account_dn_str = account_dn_str; talloc_steal(r, account_dn_str); /* Store the domain DN. */ r->out.domain_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name; talloc_steal(r, r_crack_names.out.ctr->ctr1->array[0].result_name); /* Store the KVNO of the account, critical for some kerberos * operations */ r->out.kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0); /* Store the account GUID. */ r->out.account_guid = samdb_result_guid(res->msgs[0], "objectGUID"); if (r->in.acct_type == ACB_SVRTRUST) { status = libnet_JoinSite(ctx, remote_ldb, r); } talloc_free(tmp_ctx); return status; }
/* test a schannel connection with the given flags */ static BOOL test_schannel(TALLOC_CTX *mem_ctx, uint16_t acct_flags, uint32_t dcerpc_flags, int i) { BOOL ret = True; struct test_join *join_ctx; NTSTATUS status; const char *binding = lp_parm_string(-1, "torture", "binding"); struct dcerpc_binding *b; struct dcerpc_pipe *p = NULL; struct dcerpc_pipe *p_netlogon = NULL; struct dcerpc_pipe *p_netlogon2 = NULL; struct dcerpc_pipe *p_netlogon3 = NULL; struct dcerpc_pipe *p_samr2 = NULL; struct dcerpc_pipe *p_lsa = NULL; struct creds_CredentialState *creds; struct cli_credentials *credentials; TALLOC_CTX *test_ctx = talloc_named(mem_ctx, 0, "test_schannel context"); join_ctx = torture_join_domain(talloc_asprintf(mem_ctx, "%s%d", TEST_MACHINE_NAME, i), acct_flags, &credentials); if (!join_ctx) { printf("Failed to join domain with acct_flags=0x%x\n", acct_flags); talloc_free(test_ctx); return False; } status = dcerpc_parse_binding(test_ctx, binding, &b); if (!NT_STATUS_IS_OK(status)) { printf("Bad binding string %s\n", binding); goto failed; } b->flags &= ~DCERPC_AUTH_OPTIONS; b->flags |= dcerpc_flags; status = dcerpc_pipe_connect_b(test_ctx, &p, b, &dcerpc_table_samr, credentials, NULL); if (!NT_STATUS_IS_OK(status)) { printf("Failed to connect with schannel: %s\n", nt_errstr(status)); goto failed; } if (!test_samr_ops(p, test_ctx)) { printf("Failed to process schannel secured SAMR ops\n"); ret = False; } /* Also test that when we connect to the netlogon pipe, that * the credentials we setup on the first pipe are valid for * the second */ /* Swap the binding details from SAMR to NETLOGON */ status = dcerpc_epm_map_binding(test_ctx, b, &dcerpc_table_netlogon, NULL); if (!NT_STATUS_IS_OK(status)) { goto failed; } status = dcerpc_secondary_connection(p, &p_netlogon, b); if (!NT_STATUS_IS_OK(status)) { goto failed; } status = dcerpc_bind_auth(p_netlogon, &dcerpc_table_netlogon, credentials, DCERPC_AUTH_TYPE_SCHANNEL, dcerpc_auth_level(p->conn), NULL); if (!NT_STATUS_IS_OK(status)) { goto failed; } status = dcerpc_schannel_creds(p_netlogon->conn->security_state.generic_state, test_ctx, &creds); if (!NT_STATUS_IS_OK(status)) { goto failed; } /* do a couple of logins */ if (!test_netlogon_ops(p_netlogon, test_ctx, credentials, creds)) { printf("Failed to process schannel secured NETLOGON ops\n"); ret = False; } if (!test_netlogon_ex_ops(p_netlogon, test_ctx, credentials, creds)) { printf("Failed to process schannel secured NETLOGON EX ops\n"); ret = False; } /* Swap the binding details from SAMR to LSARPC */ status = dcerpc_epm_map_binding(test_ctx, b, &dcerpc_table_lsarpc, NULL); if (!NT_STATUS_IS_OK(status)) { goto failed; } status = dcerpc_secondary_connection(p, &p_lsa, b); if (!NT_STATUS_IS_OK(status)) { goto failed; } status = dcerpc_bind_auth(p_lsa, &dcerpc_table_lsarpc, credentials, DCERPC_AUTH_TYPE_SCHANNEL, dcerpc_auth_level(p->conn), NULL); if (!NT_STATUS_IS_OK(status)) { goto failed; } if (!test_lsa_ops(p_lsa, test_ctx)) { printf("Failed to process schannel secured LSA ops\n"); ret = False; } /* Drop the socket, we want to start from scratch */ talloc_free(p); p = NULL; /* Now see what we are still allowed to do */ status = dcerpc_parse_binding(test_ctx, binding, &b); if (!NT_STATUS_IS_OK(status)) { printf("Bad binding string %s\n", binding); goto failed; } b->flags &= ~DCERPC_AUTH_OPTIONS; b->flags |= dcerpc_flags; status = dcerpc_pipe_connect_b(test_ctx, &p_samr2, b, &dcerpc_table_samr, credentials, NULL); if (!NT_STATUS_IS_OK(status)) { printf("Failed to connect with schannel: %s\n", nt_errstr(status)); goto failed; } /* do a some SAMR operations. We have *not* done a new serverauthenticate */ if (!test_samr_ops(p_samr2, test_ctx)) { printf("Failed to process schannel secured SAMR ops (on fresh connection)\n"); goto failed; } /* Swap the binding details from SAMR to NETLOGON */ status = dcerpc_epm_map_binding(test_ctx, b, &dcerpc_table_netlogon, NULL); if (!NT_STATUS_IS_OK(status)) { goto failed; } status = dcerpc_secondary_connection(p_samr2, &p_netlogon2, b); if (!NT_STATUS_IS_OK(status)) { goto failed; } /* and now setup an SCHANNEL bind on netlogon */ status = dcerpc_bind_auth(p_netlogon2, &dcerpc_table_netlogon, credentials, DCERPC_AUTH_TYPE_SCHANNEL, dcerpc_auth_level(p_samr2->conn), NULL); if (!NT_STATUS_IS_OK(status)) { goto failed; } /* Try the schannel-only SamLogonEx operation */ if (!test_netlogon_ex_ops(p_netlogon2, test_ctx, credentials, creds)) { printf("Failed to process schannel secured NETLOGON EX ops (on fresh connection)\n"); ret = False; } /* And the more traditional style, proving that the * credentials chaining state is fully present */ if (!test_netlogon_ops(p_netlogon2, test_ctx, credentials, creds)) { printf("Failed to process schannel secured NETLOGON ops (on fresh connection)\n"); ret = False; } /* Drop the socket, we want to start from scratch (again) */ talloc_free(p_samr2); /* We don't want schannel for this test */ b->flags &= ~DCERPC_AUTH_OPTIONS; status = dcerpc_pipe_connect_b(test_ctx, &p_netlogon3, b, &dcerpc_table_netlogon, credentials, NULL); if (!NT_STATUS_IS_OK(status)) { printf("Failed to connect without schannel: %s\n", nt_errstr(status)); goto failed; } if (test_netlogon_ex_ops(p_netlogon3, test_ctx, credentials, creds)) { printf("Processed NOT schannel secured NETLOGON EX ops without SCHANNEL (unsafe)\n"); ret = False; } if (!test_netlogon_ops(p_netlogon3, test_ctx, credentials, creds)) { printf("Failed to processed NOT schannel secured NETLOGON ops without new ServerAuth\n"); ret = False; } torture_leave_domain(join_ctx); talloc_free(test_ctx); return ret; failed: torture_leave_domain(join_ctx); talloc_free(test_ctx); return False; }