static bool test_handles_drsuapi(struct torture_context *torture) { NTSTATUS status; struct dcerpc_pipe *p1, *p2; struct policy_handle handle; struct policy_handle handle2; struct GUID bind_guid; struct drsuapi_DsBind r; struct drsuapi_DsUnbind c; TALLOC_CTX *mem_ctx = talloc_new(torture); torture_comment(torture, "RPC-HANDLE-DRSUAPI\n"); status = torture_rpc_connection(mem_ctx, &p1, &dcerpc_table_drsuapi); torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1"); status = torture_rpc_connection(mem_ctx, &p2, &dcerpc_table_drsuapi); torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1"); GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); r.in.bind_guid = &bind_guid; r.in.bind_info = NULL; r.out.bind_handle = &handle; status = dcerpc_drsuapi_DsBind(p1, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { torture_comment(torture, "drsuapi_DsBind not supported - skipping\n"); talloc_free(mem_ctx); return true; } c.in.bind_handle = &handle; c.out.bind_handle = &handle2; status = dcerpc_drsuapi_DsUnbind(p2, mem_ctx, &c); torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, "closing policy handle on p2"); torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, "closing policy handle on p2"); status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c); torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1"); status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c); torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT, "closing policy handle on p1 again"); torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH, "closing policy handle on p1 again"); talloc_free(mem_ctx); return true; }
/* get a DRSUAPI policy handle */ static bool get_policy_handle(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct policy_handle *handle) { NTSTATUS status; struct drsuapi_DsBind r; ZERO_STRUCT(r); r.out.bind_handle = handle; status = dcerpc_drsuapi_DsBind(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { printf("drsuapi_DsBind failed - %s\n", nt_errstr(status)); return false; } return true; }
bool test_DsBind(struct dcerpc_pipe *p, struct torture_context *tctx, struct DsPrivate *priv) { NTSTATUS status; struct drsuapi_DsBind r; GUID_from_string(DRSUAPI_DS_BIND_GUID, &priv->bind_guid); r.in.bind_guid = &priv->bind_guid; r.in.bind_info = NULL; r.out.bind_handle = &priv->bind_handle; torture_comment(tctx, "testing DsBind\n"); status = dcerpc_drsuapi_DsBind(p, tctx, &r); torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsBind"); return true; }
static NTSTATUS libnet_dssync_bind(TALLOC_CTX *mem_ctx, struct dssync_context *ctx) { NTSTATUS status; WERROR werr; struct GUID bind_guid; struct drsuapi_DsBindInfoCtr bind_info; struct drsuapi_DsBindInfo28 info28; struct dcerpc_binding_handle *b = ctx->cli->binding_handle; ZERO_STRUCT(info28); GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; info28.site_guid = GUID_zero(); info28.pid = 508; info28.repl_epoch = 0; bind_info.length = 28; bind_info.info.info28 = info28; status = dcerpc_drsuapi_DsBind(b, mem_ctx, &bind_guid, &bind_info, &ctx->bind_handle, &werr); if (!NT_STATUS_IS_OK(status)) { return status; } if (!W_ERROR_IS_OK(werr)) { return werror_to_ntstatus(werr); } ZERO_STRUCT(ctx->remote_info28); switch (bind_info.length) { case 24: { struct drsuapi_DsBindInfo24 *info24; info24 = &bind_info.info.info24; ctx->remote_info28.site_guid = info24->site_guid; ctx->remote_info28.supported_extensions = info24->supported_extensions; ctx->remote_info28.pid = info24->pid; ctx->remote_info28.repl_epoch = 0; break; } case 28: { ctx->remote_info28 = bind_info.info.info28; break; } case 32: { struct drsuapi_DsBindInfo32 *info32; info32 = &bind_info.info.info32; ctx->remote_info28.site_guid = info32->site_guid; ctx->remote_info28.supported_extensions = info32->supported_extensions; ctx->remote_info28.pid = info32->pid; ctx->remote_info28.repl_epoch = info32->repl_epoch; break; } case 48: { struct drsuapi_DsBindInfo48 *info48; info48 = &bind_info.info.info48; ctx->remote_info28.site_guid = info48->site_guid; ctx->remote_info28.supported_extensions = info48->supported_extensions; ctx->remote_info28.pid = info48->pid; ctx->remote_info28.repl_epoch = info48->repl_epoch; break; } case 52: { struct drsuapi_DsBindInfo52 *info52; info52 = &bind_info.info.info52; ctx->remote_info28.site_guid = info52->site_guid; ctx->remote_info28.supported_extensions = info52->supported_extensions; ctx->remote_info28.pid = info52->pid; ctx->remote_info28.repl_epoch = info52->repl_epoch; break; } default: DEBUG(1, ("Warning: invalid info length in bind info: %d\n", bind_info.length)); break; } return status; }
static WERROR cmd_drsuapi_cracknames(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS status; WERROR werr; int i; struct GUID bind_guid; struct policy_handle bind_handle; struct dcerpc_binding_handle *b = cli->binding_handle; union drsuapi_DsNameCtr ctr; if (argc < 2) { printf("usage: %s name\n", argv[0]); return WERR_OK; } GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); status = dcerpc_drsuapi_DsBind(b, mem_ctx, &bind_guid, NULL, &bind_handle, &werr); if (!NT_STATUS_IS_OK(status)) { return ntstatus_to_werror(status); } if (!W_ERROR_IS_OK(werr)) { return werr; } werr = cracknames(cli, mem_ctx, &bind_handle, DRSUAPI_DS_NAME_FORMAT_UNKNOWN, DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 1, argv+1, &ctr); if (!W_ERROR_IS_OK(werr)) { goto out; } for (i=0; i < ctr.ctr1->count; i++) { printf("status: %d\n", ctr.ctr1->array[i].status); printf("dns_domain_name: %s\n", ctr.ctr1->array[i].dns_domain_name); printf("result_name: %s\n", ctr.ctr1->array[i].result_name); } out: if (is_valid_policy_hnd(&bind_handle)) { WERROR _werr; dcerpc_drsuapi_DsUnbind(b, mem_ctx, &bind_handle, &_werr); } return werr; }
static WERROR cmd_drsuapi_getncchanges(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS status; WERROR werr; struct policy_handle bind_handle; struct dcerpc_binding_handle *b = cli->binding_handle; struct GUID bind_guid; struct drsuapi_DsBindInfoCtr bind_info; struct drsuapi_DsBindInfo28 info28; const char *nc_dn = NULL; DATA_BLOB session_key; uint32_t level = 8; bool single = false; uint32_t level_out = 0; union drsuapi_DsGetNCChangesRequest req; union drsuapi_DsGetNCChangesCtr ctr; struct drsuapi_DsReplicaObjectIdentifier nc; struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL; struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL; uint32_t out_level = 0; int y; uint32_t supported_extensions = 0; uint32_t replica_flags = DRSUAPI_DRS_WRIT_REP | DRSUAPI_DRS_INIT_SYNC | DRSUAPI_DRS_PER_SYNC | DRSUAPI_DRS_GET_ANC | DRSUAPI_DRS_NEVER_SYNCED; if (argc > 3) { printf("usage: %s [naming_context_or_object_dn [single]]\n", argv[0]); return WERR_OK; } if (argc >= 2) { nc_dn = argv[1]; } if (argc == 3) { if (strequal(argv[2], "single")) { single = true; } else { printf("warning: ignoring unknown argument '%s'\n", argv[2]); } } ZERO_STRUCT(info28); ZERO_STRUCT(req); GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7; info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT; info28.site_guid = GUID_zero(); info28.pid = 0; info28.repl_epoch = 0; bind_info.length = 28; bind_info.info.info28 = info28; status = dcerpc_drsuapi_DsBind(b, mem_ctx, &bind_guid, &bind_info, &bind_handle, &werr); if (!NT_STATUS_IS_OK(status)) { return ntstatus_to_werror(status); } if (!W_ERROR_IS_OK(werr)) { return werr; } if (bind_info.length == 24) { supported_extensions = bind_info.info.info24.supported_extensions; } else if (bind_info.length == 28) { supported_extensions = bind_info.info.info28.supported_extensions; } else if (bind_info.length == 32) { supported_extensions = bind_info.info.info32.supported_extensions; } else if (bind_info.length == 48) { supported_extensions = bind_info.info.info48.supported_extensions; } else if (bind_info.length == 52) { supported_extensions = bind_info.info.info52.supported_extensions; } if (!nc_dn) { union drsuapi_DsNameCtr crack_ctr; const char *name; name = talloc_asprintf(mem_ctx, "%s\\", lp_workgroup()); W_ERROR_HAVE_NO_MEMORY(name); werr = cracknames(cli, mem_ctx, &bind_handle, DRSUAPI_DS_NAME_FORMAT_UNKNOWN, DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 1, &name, &crack_ctr); if (!W_ERROR_IS_OK(werr)) { return werr; } if (crack_ctr.ctr1->count != 1) { return WERR_NO_SUCH_DOMAIN; } if (crack_ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { return WERR_NO_SUCH_DOMAIN; } nc_dn = talloc_strdup(mem_ctx, crack_ctr.ctr1->array[0].result_name); W_ERROR_HAVE_NO_MEMORY(nc_dn); printf("using: %s\n", nc_dn); } nc.dn = nc_dn; nc.guid = GUID_zero(); nc.sid = (struct dom_sid) {0}; if (supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) { level = 8; req.req8.naming_context = &nc; req.req8.replica_flags = replica_flags; req.req8.max_object_count = 402; req.req8.max_ndr_size = 402116; if (single) { req.req8.extended_op = DRSUAPI_EXOP_REPL_OBJ; } } else { level = 5; req.req5.naming_context = &nc; req.req5.replica_flags = replica_flags; req.req5.max_object_count = 402; req.req5.max_ndr_size = 402116; if (single) { req.req5.extended_op = DRSUAPI_EXOP_REPL_OBJ; } } for (y=0; ;y++) { if (level == 8) { DEBUG(1,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y, (long long)req.req8.highwatermark.tmp_highest_usn, (long long)req.req8.highwatermark.highest_usn)); } status = dcerpc_drsuapi_DsGetNCChanges(b, mem_ctx, &bind_handle, level, &req, &level_out, &ctr, &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); printf("Failed to get NC Changes: %s", get_friendly_nt_error_msg(status)); goto out; } if (!W_ERROR_IS_OK(werr)) { printf("Failed to get NC Changes: %s", get_friendly_werror_msg(werr)); goto out; } if (level_out == 1) { out_level = 1; ctr1 = &ctr.ctr1; } else if (level_out == 2 && ctr.ctr2.mszip1.ts) { out_level = 1; ctr1 = &ctr.ctr2.mszip1.ts->ctr1; } status = cli_get_session_key(mem_ctx, cli, &session_key); if (!NT_STATUS_IS_OK(status)) { printf("Failed to get Session Key: %s", nt_errstr(status)); return ntstatus_to_werror(status); } if (out_level == 1) { DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y, (long long)ctr1->new_highwatermark.tmp_highest_usn, (long long)ctr1->new_highwatermark.highest_usn)); #if 0 libnet_dssync_decrypt_attributes(mem_ctx, &session_key, ctr1->first_object); #endif if (ctr1->more_data) { req.req5.highwatermark = ctr1->new_highwatermark; continue; } } if (level_out == 6) { out_level = 6; ctr6 = &ctr.ctr6; } else if (level_out == 7 && ctr.ctr7.level == 6 && ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP && ctr.ctr7.ctr.mszip6.ts) { out_level = 6; ctr6 = &ctr.ctr7.ctr.mszip6.ts->ctr6; } else if (level_out == 7 && ctr.ctr7.level == 6 && ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS && ctr.ctr7.ctr.xpress6.ts) { out_level = 6; ctr6 = &ctr.ctr7.ctr.xpress6.ts->ctr6; } if (out_level == 6) { DEBUG(1,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y, (long long)ctr6->new_highwatermark.tmp_highest_usn, (long long)ctr6->new_highwatermark.highest_usn)); #if 0 libnet_dssync_decrypt_attributes(mem_ctx, &session_key, ctr6->first_object); #endif if (ctr6->more_data) { req.req8.highwatermark = ctr6->new_highwatermark; continue; } } break; } out: return werr; } /* List of commands exported by this module */ struct cmd_set drsuapi_commands[] = { { "DRSUAPI" }, { "dscracknames", RPC_RTYPE_WERROR, NULL, cmd_drsuapi_cracknames, &ndr_table_drsuapi, NULL, "Crack Name", "" }, { "dsgetdcinfo", RPC_RTYPE_WERROR, NULL, cmd_drsuapi_getdcinfo, &ndr_table_drsuapi, NULL, "Get Domain Controller Info", "" }, { "dsgetncchanges", RPC_RTYPE_WERROR, NULL, cmd_drsuapi_getncchanges, &ndr_table_drsuapi, NULL, "Get NC Changes", "" }, { "dswriteaccountspn", RPC_RTYPE_WERROR, NULL, cmd_drsuapi_writeaccountspn, &ndr_table_drsuapi, NULL, "Write Account SPN", "" }, { NULL } };
static WERROR cmd_drsuapi_writeaccountspn(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS status; WERROR werr; struct GUID bind_guid; struct policy_handle bind_handle; struct dcerpc_binding_handle *b = cli->binding_handle; struct drsuapi_DsNameString *spn_names = NULL; int i = 0; uint32_t level_out; union drsuapi_DsWriteAccountSpnRequest req; union drsuapi_DsWriteAccountSpnResult result; if (argc < 4) { printf("usage: %s [add|replace|delete] dn [spn_names]+\n", argv[0]); return WERR_OK; } req.req1.unknown1 = 0; /* Unused, must be 0 */ req.req1.object_dn = argv[2]; req.req1.count = argc - 3; if (strcmp(argv[1], "add") == 0) { req.req1.operation = DRSUAPI_DS_SPN_OPERATION_ADD; } else if (strcmp(argv[1], "replace") == 0) { req.req1.operation = DRSUAPI_DS_SPN_OPERATION_REPLACE; } else if (strcmp(argv[1], "delete") == 0) { req.req1.operation = DRSUAPI_DS_SPN_OPERATION_DELETE; } else { printf("usage: %s [add|replace|delete] dn [spn_names]+\n", argv[0]); return WERR_OK; } spn_names = talloc_zero_array(mem_ctx, struct drsuapi_DsNameString, req.req1.count); W_ERROR_HAVE_NO_MEMORY(spn_names); for (i=0; i<req.req1.count; i++) { spn_names[i].str = argv[i + 3]; } req.req1.spn_names = spn_names; GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); status = dcerpc_drsuapi_DsBind(b, mem_ctx, &bind_guid, NULL, &bind_handle, &werr); if (!NT_STATUS_IS_OK(status)) { return ntstatus_to_werror(status); } if (!W_ERROR_IS_OK(werr)) { return werr; } status = dcerpc_drsuapi_DsWriteAccountSpn(b, mem_ctx, &bind_handle, 1, &req, &level_out, &result, &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto out; } if (!W_ERROR_IS_OK(werr)) { goto out; } out: if (is_valid_policy_hnd(&bind_handle)) { WERROR _werr; dcerpc_drsuapi_DsUnbind(b, mem_ctx, &bind_handle, &_werr); } return werr; }
static WERROR cmd_drsuapi_getdcinfo(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, int argc, const char **argv) { NTSTATUS status; WERROR werr; struct GUID bind_guid; struct policy_handle bind_handle; struct dcerpc_binding_handle *b = cli->binding_handle; const char *domain = NULL; int32_t level = 1; int32_t level_out; union drsuapi_DsGetDCInfoRequest req; union drsuapi_DsGetDCInfoCtr ctr; if (argc < 2) { printf("usage: %s domain [level]\n", argv[0]); return WERR_OK; } domain = argv[1]; if (argc >= 3) { level = atoi(argv[2]); } GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid); status = dcerpc_drsuapi_DsBind(b, mem_ctx, &bind_guid, NULL, &bind_handle, &werr); if (!NT_STATUS_IS_OK(status)) { return ntstatus_to_werror(status); } if (!W_ERROR_IS_OK(werr)) { return werr; } req.req1.domain_name = domain; req.req1.level = level; status = dcerpc_drsuapi_DsGetDomainControllerInfo(b, mem_ctx, &bind_handle, 1, &req, &level_out, &ctr, &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto out; } if (!W_ERROR_IS_OK(werr)) { goto out; } display_domain_controller_info(level_out, &ctr); out: if (is_valid_policy_hnd(&bind_handle)) { WERROR _werr; dcerpc_drsuapi_DsUnbind(b, mem_ctx, &bind_handle, &_werr); } return werr; }
/* * 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(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(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 = 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, int32_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(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_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, NULL); 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 (! 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; } /* 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; { 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_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; }