static WERROR cracknames(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, struct policy_handle *bind_handle, enum drsuapi_DsNameFormat format_offered, enum drsuapi_DsNameFormat format_desired, int argc, const char **argv, union drsuapi_DsNameCtr *ctr) { NTSTATUS status; WERROR werr; int i; uint32_t level = 1; union drsuapi_DsNameRequest req; uint32_t level_out; struct drsuapi_DsNameString *names; struct dcerpc_binding_handle *b = cli->binding_handle; names = talloc_zero_array(mem_ctx, struct drsuapi_DsNameString, argc); W_ERROR_HAVE_NO_MEMORY(names); for (i=0; i<argc; i++) { names[i].str = argv[i]; } req.req1.codepage = 1252; /* german */ req.req1.language = 0x00000407; /* german */ req.req1.count = argc; req.req1.names = names; req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; req.req1.format_offered = format_offered; req.req1.format_desired = format_desired; status = dcerpc_drsuapi_DsCrackNames(b, mem_ctx, bind_handle, level, &req, &level_out, ctr, &werr); if (!NT_STATUS_IS_OK(status)) { return ntstatus_to_werror(status); } if (!W_ERROR_IS_OK(werr)) { return werr; } return WERR_OK; }
static NTSTATUS libnet_dssync_lookup_nc(TALLOC_CTX *mem_ctx, struct dssync_context *ctx) { NTSTATUS status; WERROR werr; uint32_t level = 1; union drsuapi_DsNameRequest req; uint32_t level_out; struct drsuapi_DsNameString names[1]; union drsuapi_DsNameCtr ctr; struct dcerpc_binding_handle *b = ctx->cli->binding_handle; names[0].str = talloc_asprintf(mem_ctx, "%s\\", ctx->domain_name); NT_STATUS_HAVE_NO_MEMORY(names[0].str); req.req1.codepage = 1252; /* german */ req.req1.language = 0x00000407; /* german */ req.req1.count = 1; req.req1.names = names; req.req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN; req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; status = dcerpc_drsuapi_DsCrackNames(b, mem_ctx, &ctx->bind_handle, level, &req, &level_out, &ctr, &werr); if (!NT_STATUS_IS_OK(status)) { ctx->error_message = talloc_asprintf(ctx, "Failed to lookup DN for domain name: %s", get_friendly_nt_error_msg(status)); return status; } if (!W_ERROR_IS_OK(werr)) { ctx->error_message = talloc_asprintf(ctx, "Failed to lookup DN for domain name: %s", get_friendly_werror_msg(werr)); return werror_to_ntstatus(werr); } if (ctr.ctr1->count != 1) { return NT_STATUS_UNSUCCESSFUL; } if (ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { return NT_STATUS_UNSUCCESSFUL; } ctx->nc_dn = talloc_strdup(mem_ctx, ctr.ctr1->array[0].result_name); NT_STATUS_HAVE_NO_MEMORY(ctx->nc_dn); if (!ctx->dns_domain_name) { ctx->dns_domain_name = talloc_strdup_upper(mem_ctx, ctr.ctr1->array[0].dns_domain_name); NT_STATUS_HAVE_NO_MEMORY(ctx->dns_domain_name); } return NT_STATUS_OK; }
bool test_DsCrackNames(struct torture_context *tctx, struct DsPrivate *priv) { NTSTATUS status; const char *err_msg; struct drsuapi_DsCrackNames r; union drsuapi_DsNameRequest req; int32_t level_out; union drsuapi_DsNameCtr ctr; struct drsuapi_DsNameString names[1]; const char *dns_domain; const char *nt4_domain; const char *FQDN_1779_name; struct ldb_context *ldb; struct ldb_dn *FQDN_1779_dn; struct ldb_dn *realm_dn; const char *realm_dn_str; const char *realm_canonical; const char *realm_canonical_ex; const char *user_principal_name; char *user_principal_name_short; const char *service_principal_name; const char *canonical_name; const char *canonical_ex_name; const char *dom_sid; const char *test_dc = torture_join_netbios_name(priv->join); struct dcerpc_pipe *p = priv->drs_pipe; TALLOC_CTX *mem_ctx = priv; ZERO_STRUCT(r); r.in.bind_handle = &priv->bind_handle; r.in.level = 1; r.in.req = &req; r.in.req->req1.codepage = 1252; /* german */ r.in.req->req1.language = 0x00000407; /* german */ r.in.req->req1.count = 1; r.in.req->req1.names = names; r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY; r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; r.out.level_out = &level_out; r.out.ctr = &ctr; dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join)); names[0].str = dom_sid; torture_comment(tctx, "testing DsCrackNames with name '%s' desired format:%d\n", names[0].str, r.in.req->req1.format_desired); status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); torture_fail(tctx, err_msg); } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } dns_domain = r.out.ctr->ctr1->array[0].dns_domain_name; nt4_domain = r.out.ctr->ctr1->array[0].result_name; r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_GUID; torture_comment(tctx, "testing DsCrackNames with name '%s' desired format:%d\n", names[0].str, r.in.req->req1.format_desired); status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); torture_fail(tctx, err_msg); } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } priv->domain_dns_name = r.out.ctr->ctr1->array[0].dns_domain_name; priv->domain_guid_str = r.out.ctr->ctr1->array[0].result_name; GUID_from_string(priv->domain_guid_str, &priv->domain_guid); r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; torture_comment(tctx, "testing DsCrackNames with name '%s' desired format:%d\n", names[0].str, r.in.req->req1.format_desired); status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); torture_fail(tctx, err_msg); } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } ldb = ldb_init(mem_ctx, tctx->ev); realm_dn_str = r.out.ctr->ctr1->array[0].result_name; realm_dn = ldb_dn_new(mem_ctx, ldb, realm_dn_str); realm_canonical = ldb_dn_canonical_string(mem_ctx, realm_dn); if (strcmp(realm_canonical, talloc_asprintf(mem_ctx, "%s/", dns_domain))!= 0) { err_msg = talloc_asprintf(mem_ctx, "local Round trip on canonical name failed: %s != %s!", realm_canonical, talloc_asprintf(mem_ctx, "%s/", dns_domain)); torture_fail(tctx, err_msg); }; realm_canonical_ex = ldb_dn_canonical_ex_string(mem_ctx, realm_dn); if (strcmp(realm_canonical_ex, talloc_asprintf(mem_ctx, "%s\n", dns_domain))!= 0) { err_msg = talloc_asprintf(mem_ctx, "local Round trip on canonical ex name failed: %s != %s!", realm_canonical, talloc_asprintf(mem_ctx, "%s\n", dns_domain)); torture_fail(tctx, err_msg); }; r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; names[0].str = nt4_domain; torture_comment(tctx, "testing DsCrackNames with name '%s' desired format:%d\n", names[0].str, r.in.req->req1.format_desired); status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); torture_fail(tctx, err_msg); } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } priv->domain_obj_dn = r.out.ctr->ctr1->array[0].result_name; r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; names[0].str = talloc_asprintf(mem_ctx, "%s%s$", nt4_domain, test_dc); torture_comment(tctx, "testing DsCrackNames with name '%s' desired format:%d\n", names[0].str, r.in.req->req1.format_desired); status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); torture_fail(tctx, err_msg); } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } FQDN_1779_name = r.out.ctr->ctr1->array[0].result_name; r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_GUID; r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; names[0].str = priv->domain_guid_str; torture_comment(tctx, "testing DsCrackNames with name '%s' desired format:%d\n", names[0].str, r.in.req->req1.format_desired); status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result)); torture_fail(tctx, err_msg); } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } if (strcmp(priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name) != 0) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed to return same DNS name - expected %s got %s", priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name); torture_fail(tctx, err_msg); } FQDN_1779_dn = ldb_dn_new(mem_ctx, ldb, FQDN_1779_name); canonical_name = ldb_dn_canonical_string(mem_ctx, FQDN_1779_dn); canonical_ex_name = ldb_dn_canonical_ex_string(mem_ctx, FQDN_1779_dn); user_principal_name = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, dns_domain); /* form up a user@DOMAIN */ user_principal_name_short = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, nt4_domain); /* variable nt4_domain includs a trailing \ */ user_principal_name_short[strlen(user_principal_name_short) - 1] = '\0'; service_principal_name = talloc_asprintf(mem_ctx, "HOST/%s", test_dc); { struct { enum drsuapi_DsNameFormat format_offered; enum drsuapi_DsNameFormat format_desired; const char *comment; const char *str; const char *expected_str; const char *expected_dns; enum drsuapi_DsNameStatus status; enum drsuapi_DsNameStatus alternate_status; enum drsuapi_DsNameFlags flags; bool skip; } crack[] = { { .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, .str = user_principal_name, .expected_str = FQDN_1779_name, .status = DRSUAPI_DS_NAME_STATUS_OK }, { .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779, .str = user_principal_name_short, .expected_str = FQDN_1779_name, .status = DRSUAPI_DS_NAME_STATUS_OK }, { .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
static bool test_DsCrackNamesMatrix(struct torture_context *tctx, struct DsPrivate *priv, const char *dn, const char *user_principal_name, const char *service_principal_name) { NTSTATUS status; const char *err_msg; struct drsuapi_DsCrackNames r; union drsuapi_DsNameRequest req; int32_t level_out; union drsuapi_DsNameCtr ctr; struct dcerpc_pipe *p = priv->drs_pipe; TALLOC_CTX *mem_ctx = priv; enum drsuapi_DsNameFormat formats[] = { DRSUAPI_DS_NAME_FORMAT_UNKNOWN, DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_DISPLAY, DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_CANONICAL, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN }; struct drsuapi_DsNameString names[ARRAY_SIZE(formats)]; int i, j; const char *n_matrix[ARRAY_SIZE(formats)][ARRAY_SIZE(formats)]; const char *n_from[ARRAY_SIZE(formats)]; ZERO_STRUCT(r); r.in.bind_handle = &priv->bind_handle; r.in.level = 1; r.in.req = &req; r.in.req->req1.codepage = 1252; /* german */ r.in.req->req1.language = 0x00000407; /* german */ r.in.req->req1.count = 1; r.in.req->req1.names = names; r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS; r.out.level_out = &level_out; r.out.ctr = &ctr; n_matrix[0][0] = dn; for (i = 0; i < ARRAY_SIZE(formats); i++) { r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; r.in.req->req1.format_desired = formats[i]; names[0].str = dn; status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d failed - %s", names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d failed - %s", names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, win_errstr(r.out.result)); torture_fail(tctx, err_msg); } switch (formats[i]) { case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE) { err_msg = talloc_asprintf(mem_ctx, "Unexpected error (%d): This name lookup should fail", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } torture_comment(tctx, __location__ ": (expected) error\n"); break; case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NO_MAPPING) { err_msg = talloc_asprintf(mem_ctx, "Unexpected error (%d): This name lookup should fail", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } torture_comment(tctx, __location__ ": (expected) error\n"); break; case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: /* should fail as we ask server to convert to Unknown format */ case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR) { err_msg = talloc_asprintf(mem_ctx, "Unexpected error (%d): This name lookup should fail", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } torture_comment(tctx, __location__ ": (expected) error\n"); break; default: if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) { err_msg = talloc_asprintf(mem_ctx, "DsCrackNames error: %d", r.out.ctr->ctr1->array[0].status); torture_fail(tctx, err_msg); } break; } switch (formats[i]) { case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: n_from[i] = user_principal_name; break; case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: n_from[i] = service_principal_name; break; case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: n_from[i] = NULL; break; default: n_from[i] = r.out.ctr->ctr1->array[0].result_name; printf("%s\n", n_from[i]); break; } } for (i = 0; i < ARRAY_SIZE(formats); i++) { for (j = 0; j < ARRAY_SIZE(formats); j++) { r.in.req->req1.format_offered = formats[i]; r.in.req->req1.format_desired = formats[j]; if (!n_from[i]) { n_matrix[i][j] = NULL; continue; } names[0].str = n_from[i]; status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } err_msg = talloc_asprintf(mem_ctx, "testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s", names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, errstr); torture_fail(tctx, err_msg); } else if (!W_ERROR_IS_OK(r.out.result)) { err_msg = talloc_asprintf(mem_ctx, "testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s", names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired, win_errstr(r.out.result)); torture_fail(tctx, err_msg); } if (r.out.ctr->ctr1->array[0].status == DRSUAPI_DS_NAME_STATUS_OK) { n_matrix[i][j] = r.out.ctr->ctr1->array[0].result_name; } else { n_matrix[i][j] = NULL; } } } for (i = 0; i < ARRAY_SIZE(formats); i++) { for (j = 0; j < ARRAY_SIZE(formats); j++) { if (n_matrix[i][j] == n_from[j]) { /* We don't have a from name for these yet (and we can't map to them to find it out) */ } else if (n_matrix[i][j] == NULL && n_from[i] == NULL) { /* we can't map to these two */ } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL) { } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL) { } else if (n_matrix[i][j] == NULL && n_from[j] != NULL) { err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s", formats[i], formats[j], n_matrix[i][j], n_from[j]); torture_fail(tctx, err_msg); } else if (n_matrix[i][j] != NULL && n_from[j] == NULL) { err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s", formats[i], formats[j], n_matrix[i][j], n_from[j]); torture_fail(tctx, err_msg); } else if (strcmp(n_matrix[i][j], n_from[j]) != 0) { err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s", formats[i], formats[j], n_matrix[i][j], n_from[j]); torture_fail(tctx, err_msg); } } } 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; 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; }