2 Unix SMB/CIFS implementation.
4 endpoint server for the drsuapi pipe
7 Copyright (C) Stefan Metzmacher 2004
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "librpc/gen_ndr/ndr_drsuapi.h"
27 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
28 #include "rpc_server/common/common.h"
29 #include "lib/ldb/include/ldb.h"
30 #include "lib/ldb/include/ldb_errors.h"
31 #include "system/kerberos.h"
32 #include "auth/kerberos/kerberos.h"
33 #include "dsdb/samdb/samdb.h"
34 #include "libcli/ldap/ldap.h"
35 #include "auth/auth.h"
37 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
38 struct smb_krb5_context *smb_krb5_context,
39 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
40 const struct ldb_dn *name_dn, const char *name,
41 const char *domain_filter, const char *result_filter,
42 struct drsuapi_DsNameInfo1 *info1);
43 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
44 uint32_t format_offered, uint32_t format_desired,
45 const struct ldb_dn *name_dn, const char *name,
46 struct drsuapi_DsNameInfo1 *info1);
48 static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx,
50 const char *alias_from,
55 struct ldb_result *res;
56 struct ldb_message_element *spnmappings;
57 struct ldb_dn *service_dn = ldb_dn_string_compose(mem_ctx, samdb_base_dn(mem_ctx),
58 "CN=Directory Service,CN=Windows NT"
59 ",CN=Services,CN=Configuration");
60 char *service_dn_str = ldb_dn_linearize(mem_ctx, service_dn);
61 const char *directory_attrs[] = {
66 ret = ldb_search(ldb_ctx, service_dn, LDB_SCOPE_BASE, "(objectClass=nTDSService)",
67 directory_attrs, &res);
68 talloc_steal(mem_ctx, res);
70 if (ret != LDB_SUCCESS) {
71 DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx)));
72 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
73 } else if (res->count > 1) {
74 DEBUG(1, ("ldb_search: dn: %s found %d times!", service_dn_str, res->count));
75 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
78 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
79 if (!spnmappings || spnmappings->num_values == 0) {
80 DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str));
81 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
84 for (i = 0; i < spnmappings->num_values; i++) {
85 char *mapping, *p, *str;
86 mapping = talloc_strdup(mem_ctx,
87 (const char *)spnmappings->values[i].data);
89 DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
90 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
93 /* C string manipulation sucks */
95 p = strchr(mapping, '=');
97 DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
98 service_dn_str, mapping));
99 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
110 if (strcasecmp(str, alias_from) == 0) {
112 return DRSUAPI_DS_NAME_STATUS_OK;
116 DEBUG(1, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
117 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
120 /* When cracking a ServicePrincipalName, many services may be served
121 * by the host/ servicePrincipalName. The incoming query is for cifs/
122 * but we translate it here, and search on host/. This is done after
123 * the cifs/ entry has been searched for, making this a fallback */
125 static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
126 struct smb_krb5_context *smb_krb5_context,
127 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
128 const char *name, struct drsuapi_DsNameInfo1 *info1)
132 krb5_principal principal;
136 enum drsuapi_DsNameStatus namestatus;
138 /* parse principal */
139 ret = krb5_parse_name_norealm(smb_krb5_context->krb5_context,
142 DEBUG(2, ("Could not parse principal: %s: %s",
143 name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
148 /* grab cifs/, http/ etc */
150 /* This is checked for in callers, but be safe */
151 if (principal->name.name_string.len < 2) {
152 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
155 service = principal->name.name_string.val[0];
158 namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context,
160 service, &new_service);
162 if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
163 info1->status = namestatus;
168 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
172 /* ooh, very nasty playing around in the Principal... */
173 free(principal->name.name_string.val[0]);
174 principal->name.name_string.val[0] = strdup(new_service);
175 if (!principal->name.name_string.val[0]) {
176 krb5_free_principal(smb_krb5_context->krb5_context, principal);
180 /* reform principal */
181 ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &new_princ);
183 krb5_free_principal(smb_krb5_context->krb5_context, principal);
189 wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
195 /* Subcase of CrackNames, for the userPrincipalName */
197 static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
198 struct smb_krb5_context *smb_krb5_context,
199 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
200 const char *name, struct drsuapi_DsNameInfo1 *info1)
203 const char *domain_filter = NULL;
204 const char *result_filter = NULL;
206 krb5_principal principal;
208 char *unparsed_name_short;
210 /* Prevent recursion */
212 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
216 ret = krb5_parse_name_mustrealm(smb_krb5_context->krb5_context, name, &principal);
218 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
222 domain_filter = NULL;
223 realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
224 domain_filter = talloc_asprintf(mem_ctx,
225 "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
226 ldb_binary_encode_string(mem_ctx, *realm),
227 ldb_binary_encode_string(mem_ctx, *realm));
228 ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &unparsed_name_short);
229 krb5_free_principal(smb_krb5_context->krb5_context, principal);
232 free(unparsed_name_short);
236 /* This may need to be extended for more userPrincipalName variations */
237 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
238 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
239 if (!result_filter || !domain_filter) {
240 free(unparsed_name_short);
243 status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
245 format_flags, format_offered, format_desired,
246 NULL, unparsed_name_short, domain_filter, result_filter,
248 free(unparsed_name_short);
252 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
254 WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
255 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
256 const char *name, struct drsuapi_DsNameInfo1 *info1)
259 const char *domain_filter = NULL;
260 const char *result_filter = NULL;
261 struct ldb_dn *name_dn = NULL;
263 struct smb_krb5_context *smb_krb5_context;
264 ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context);
270 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
271 info1->dns_domain_name = NULL;
272 info1->result_name = NULL;
275 return WERR_INVALID_PARAM;
278 /* TODO: - fill the correct names in all cases!
279 * - handle format_flags
282 /* here we need to set the domain_filter and/or the result_filter */
283 switch (format_offered) {
284 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
287 str = talloc_strdup(mem_ctx, name);
288 WERR_TALLOC_CHECK(str);
290 if (strlen(str) == 0 || str[strlen(str)-1] != '/') {
291 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
295 str[strlen(str)-1] = '\0';
297 domain_filter = talloc_asprintf(mem_ctx,
298 "(&(&(&(dnsRoot=%s)(objectclass=crossRef)))(nETBIOSName=*)(ncName=*))",
299 ldb_binary_encode_string(mem_ctx, str));
300 WERR_TALLOC_CHECK(domain_filter);
304 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
307 const char *account = NULL;
309 domain = talloc_strdup(mem_ctx, name);
310 WERR_TALLOC_CHECK(domain);
312 p = strchr(domain, '\\');
314 /* invalid input format */
315 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
324 domain_filter = talloc_asprintf(mem_ctx,
325 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
326 ldb_binary_encode_string(mem_ctx, domain));
327 WERR_TALLOC_CHECK(domain_filter);
329 result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
330 ldb_binary_encode_string(mem_ctx, account));
331 WERR_TALLOC_CHECK(result_filter);
337 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
338 name_dn = ldb_dn_explode(mem_ctx, name);
339 domain_filter = NULL;
341 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
346 case DRSUAPI_DS_NAME_FORMAT_GUID: {
350 domain_filter = NULL;
352 nt_status = GUID_from_string(name, &guid);
353 if (!NT_STATUS_IS_OK(nt_status)) {
354 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
358 ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
362 result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
364 WERR_TALLOC_CHECK(result_filter);
367 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
368 domain_filter = NULL;
370 result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
371 ldb_binary_encode_string(mem_ctx, name),
372 ldb_binary_encode_string(mem_ctx, name));
373 WERR_TALLOC_CHECK(result_filter);
377 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
378 struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
381 domain_filter = NULL;
383 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
386 ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
391 result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
393 WERR_TALLOC_CHECK(result_filter);
396 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
397 krb5_principal principal;
399 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
401 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
405 domain_filter = NULL;
407 ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name);
409 krb5_free_principal(smb_krb5_context->krb5_context, principal);
413 krb5_free_principal(smb_krb5_context->krb5_context, principal);
414 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))",
415 ldb_binary_encode_string(mem_ctx, unparsed_name));
418 WERR_TALLOC_CHECK(result_filter);
421 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
422 krb5_principal principal;
423 char *unparsed_name_short;
425 ret = krb5_parse_name_norealm(smb_krb5_context->krb5_context, name, &principal);
427 /* perhaps it's a principal with a realm, so return the right 'domain only' response */
429 ret = krb5_parse_name_mustrealm(smb_krb5_context->krb5_context, name, &principal);
431 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
435 /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
436 realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
438 info1->dns_domain_name = talloc_strdup(info1, *realm);
439 krb5_free_principal(smb_krb5_context->krb5_context, principal);
441 WERR_TALLOC_CHECK(info1->dns_domain_name);
443 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
446 } else if (principal->name.name_string.len < 2) {
447 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
451 domain_filter = NULL;
453 ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &unparsed_name_short);
455 krb5_free_principal(smb_krb5_context->krb5_context, principal);
459 service = principal->name.name_string.val[0];
460 if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) {
461 /* the 'cn' attribute is just the leading part of the name */
463 computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1],
464 strcspn(principal->name.name_string.val[1], "."));
465 if (computer_name == NULL) {
469 result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
470 ldb_binary_encode_string(mem_ctx, unparsed_name_short),
471 ldb_binary_encode_string(mem_ctx, computer_name));
473 result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
474 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
476 krb5_free_principal(smb_krb5_context->krb5_context, principal);
477 free(unparsed_name_short);
478 WERR_TALLOC_CHECK(result_filter);
483 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
489 if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
490 return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
491 name_dn, name, info1);
494 return DsCrackNameOneFilter(sam_ctx, mem_ctx,
496 format_flags, format_offered, format_desired,
498 domain_filter, result_filter,
502 /* Subcase of CrackNames. It is possible to translate a LDAP-style DN
503 * (FQDN_1779) into a canoical name without actually searching the
506 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
507 uint32_t format_offered, uint32_t format_desired,
508 const struct ldb_dn *name_dn, const char *name,
509 struct drsuapi_DsNameInfo1 *info1)
512 if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
513 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
517 switch (format_desired) {
518 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
519 cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
521 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
522 cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
525 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
528 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
529 info1->result_name = cracked;
537 /* Given a filter for the domain, and one for the result, perform the
538 * ldb search. The format offered and desired flags change the
539 * behaviours, including what attributes to return.
541 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
544 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
545 struct smb_krb5_context *smb_krb5_context,
546 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
547 const struct ldb_dn *name_dn, const char *name,
548 const char *domain_filter, const char *result_filter,
549 struct drsuapi_DsNameInfo1 *info1)
552 struct ldb_message **domain_res = NULL;
553 const char * const *domain_attrs;
554 const char * const *result_attrs;
555 struct ldb_message **result_res = NULL;
556 const struct ldb_dn *result_basedn;
558 /* here we need to set the attrs lists for domain and result lookups */
559 switch (format_desired) {
560 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
561 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
562 const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL};
563 const char * const _result_attrs[] = { NULL};
565 domain_attrs = _domain_attrs;
566 result_attrs = _result_attrs;
569 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
570 const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL};
571 const char * const _result_attrs[] = { "canonicalName", NULL };
573 domain_attrs = _domain_attrs;
574 result_attrs = _result_attrs;
577 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
578 const char * const _domain_attrs[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
579 const char * const _result_attrs[] = { "sAMAccountName", "objectSid", NULL};
581 domain_attrs = _domain_attrs;
582 result_attrs = _result_attrs;
585 case DRSUAPI_DS_NAME_FORMAT_GUID: {
586 const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL};
587 const char * const _result_attrs[] = { "objectGUID", NULL};
589 domain_attrs = _domain_attrs;
590 result_attrs = _result_attrs;
593 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
594 const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL};
595 const char * const _result_attrs[] = { "displayName", "samAccountName", NULL};
597 domain_attrs = _domain_attrs;
598 result_attrs = _result_attrs;
606 /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
607 ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, domain_attrs,
608 "%s", domain_filter);
610 ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, domain_attrs,
611 "(ncName=%s)", ldb_dn_linearize(mem_ctx, samdb_base_dn(mem_ctx)));
618 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
621 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
624 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
628 info1->dns_domain_name = samdb_result_string(domain_res[0], "dnsRoot", NULL);
629 WERR_TALLOC_CHECK(info1->dns_domain_name);
630 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
633 result_basedn = samdb_result_dn(mem_ctx, domain_res[0], "ncName", NULL);
635 ldb_ret = gendb_search(sam_ctx, mem_ctx, result_basedn, &result_res,
636 result_attrs, "%s", result_filter);
637 } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
638 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
641 name_dn = samdb_result_dn(mem_ctx, domain_res[0], "ncName", NULL);
642 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
650 switch (format_offered) {
651 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
652 return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
654 format_flags, format_offered, format_desired,
657 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
658 return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
659 format_flags, format_offered, format_desired,
662 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
665 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
668 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
672 /* here we can use result_res[0] and domain_res[0] */
673 switch (format_desired) {
674 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
675 info1->result_name = ldb_dn_linearize(mem_ctx, result_res[0]->dn);
676 WERR_TALLOC_CHECK(info1->result_name);
678 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
681 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
682 info1->result_name = samdb_result_string(result_res[0], "canonicalName", NULL);
683 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
686 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
687 /* Not in the virtual ldb attribute */
688 return DsCrackNameOneSyntactical(mem_ctx,
689 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
690 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
691 result_res[0]->dn, name, info1);
693 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
694 const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result_res[0], "objectSid");
695 const char *_acc = "", *_dom = "";
697 if (!sid || (sid->num_auths < 4) || (sid->num_auths > 5)) {
698 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
702 if (sid->num_auths == 4) {
703 ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, domain_attrs,
704 "(ncName=%s)", ldb_dn_linearize(mem_ctx, result_res[0]->dn));
706 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
709 _dom = samdb_result_string(domain_res[0], "nETBIOSName", NULL);
710 WERR_TALLOC_CHECK(_dom);
712 } else if (sid->num_auths == 5) {
713 const char *attrs[] = { NULL };
714 struct ldb_message **domain_res2;
715 struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
719 dom_sid->num_auths--;
720 ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, attrs,
721 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
723 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
726 ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res2, domain_attrs,
727 "(ncName=%s)", ldb_dn_linearize(mem_ctx, domain_res[0]->dn));
729 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
733 _dom = samdb_result_string(domain_res2[0], "nETBIOSName", NULL);
734 WERR_TALLOC_CHECK(_dom);
736 _acc = samdb_result_string(result_res[0], "sAMAccountName", NULL);
737 WERR_TALLOC_CHECK(_acc);
740 info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
741 WERR_TALLOC_CHECK(info1->result_name);
743 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
746 case DRSUAPI_DS_NAME_FORMAT_GUID: {
749 guid = samdb_result_guid(result_res[0], "objectGUID");
751 info1->result_name = GUID_string2(mem_ctx, &guid);
752 WERR_TALLOC_CHECK(info1->result_name);
754 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
757 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
758 info1->result_name = samdb_result_string(result_res[0], "displayName", NULL);
759 if (!info1->result_name) {
760 info1->result_name = samdb_result_string(result_res[0], "sAMAccountName", NULL);
762 if (!info1->result_name) {
763 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
765 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
773 return WERR_INVALID_PARAM;
776 /* Given a user Principal Name (such as foo@bar.com),
777 * return the user and domain DNs. This is used in the KDC to then
778 * return the Keys and evaluate policy */
780 NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
782 const char *user_principal_name,
783 struct ldb_dn **user_dn,
784 struct ldb_dn **domain_dn)
787 struct drsuapi_DsNameInfo1 info1;
788 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
789 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
790 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
793 if (!W_ERROR_IS_OK(werr)) {
794 return werror_to_ntstatus(werr);
796 switch (info1.status) {
797 case DRSUAPI_DS_NAME_STATUS_OK:
799 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
800 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
801 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
802 return NT_STATUS_NO_SUCH_USER;
803 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
805 return NT_STATUS_UNSUCCESSFUL;
808 *user_dn = ldb_dn_explode(mem_ctx, info1.result_name);
811 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
812 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
813 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
814 talloc_asprintf(mem_ctx, "%s/",
815 info1.dns_domain_name),
817 if (!W_ERROR_IS_OK(werr)) {
818 return werror_to_ntstatus(werr);
820 switch (info1.status) {
821 case DRSUAPI_DS_NAME_STATUS_OK:
823 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
824 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
825 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
826 return NT_STATUS_NO_SUCH_USER;
827 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
829 return NT_STATUS_UNSUCCESSFUL;
832 *domain_dn = ldb_dn_explode(mem_ctx, info1.result_name);
839 /* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
840 * return the user and domain DNs. This is used in the KDC to then
841 * return the Keys and evaluate policy */
843 NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
845 const char *service_principal_name,
846 struct ldb_dn **user_dn,
847 struct ldb_dn **domain_dn)
850 struct drsuapi_DsNameInfo1 info1;
851 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
852 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
853 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
854 service_principal_name,
856 if (!W_ERROR_IS_OK(werr)) {
857 return werror_to_ntstatus(werr);
859 switch (info1.status) {
860 case DRSUAPI_DS_NAME_STATUS_OK:
862 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
863 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
864 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
865 return NT_STATUS_NO_SUCH_USER;
866 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
868 return NT_STATUS_UNSUCCESSFUL;
871 *user_dn = ldb_dn_explode(mem_ctx, info1.result_name);
874 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
875 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
876 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
877 talloc_asprintf(mem_ctx, "%s/",
878 info1.dns_domain_name),
880 if (!W_ERROR_IS_OK(werr)) {
881 return werror_to_ntstatus(werr);
883 switch (info1.status) {
884 case DRSUAPI_DS_NAME_STATUS_OK:
886 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
887 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
888 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
889 return NT_STATUS_NO_SUCH_USER;
890 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
892 return NT_STATUS_UNSUCCESSFUL;
895 *domain_dn = ldb_dn_explode(mem_ctx, info1.result_name);
902 NTSTATUS crack_dn_to_nt4_name(TALLOC_CTX *mem_ctx,
904 const char **nt4_domain, const char **nt4_account)
907 struct drsuapi_DsNameInfo1 info1;
908 struct ldb_context *ldb;
911 /* Handle anonymous bind */
918 ldb = samdb_connect(mem_ctx, system_session(mem_ctx));
920 return NT_STATUS_INTERNAL_DB_CORRUPTION;
923 werr = DsCrackNameOneName(ldb, mem_ctx, 0,
924 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
925 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
928 if (!W_ERROR_IS_OK(werr)) {
929 return werror_to_ntstatus(werr);
931 switch (info1.status) {
932 case DRSUAPI_DS_NAME_STATUS_OK:
934 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
935 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
936 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
937 return NT_STATUS_NO_SUCH_USER;
938 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
940 return NT_STATUS_UNSUCCESSFUL;
943 *nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
945 p = strchr(*nt4_domain, '\\');
947 return NT_STATUS_INVALID_PARAMETER;
952 *nt4_account = talloc_strdup(mem_ctx, &p[1]);
955 if (!*nt4_account || !*nt4_domain) {
956 return NT_STATUS_NO_MEMORY;