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/common/common.h"
28 #include "lib/ldb/include/ldb_errors.h"
29 #include "system/kerberos.h"
30 #include "auth/kerberos/kerberos.h"
31 #include "dsdb/samdb/samdb.h"
32 #include "libcli/ldap/ldap.h"
33 #include "auth/auth.h"
35 static WERROR
DsCrackNameOneFilter(struct ldb_context
*sam_ctx
, TALLOC_CTX
*mem_ctx
,
36 struct smb_krb5_context
*smb_krb5_context
,
37 uint32_t format_flags
, uint32_t format_offered
, uint32_t format_desired
,
38 const struct ldb_dn
*name_dn
, const char *name
,
39 const char *domain_filter
, const char *result_filter
,
40 struct drsuapi_DsNameInfo1
*info1
);
41 static WERROR
DsCrackNameOneSyntactical(TALLOC_CTX
*mem_ctx
,
42 uint32_t format_offered
, uint32_t format_desired
,
43 const struct ldb_dn
*name_dn
, const char *name
,
44 struct drsuapi_DsNameInfo1
*info1
);
46 static enum drsuapi_DsNameStatus
LDB_lookup_spn_alias(krb5_context context
, struct ldb_context
*ldb_ctx
,
48 const char *alias_from
,
53 struct ldb_result
*res
;
54 struct ldb_message_element
*spnmappings
;
56 struct ldb_dn
*service_dn
;
59 const char *directory_attrs
[] = {
64 tmp_ctx
= talloc_new(mem_ctx
);
66 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
;
69 service_dn
= ldb_dn_string_compose(tmp_ctx
, samdb_base_dn(mem_ctx
),
70 "CN=Directory Service,CN=Windows NT"
71 ",CN=Services,CN=Configuration");
72 service_dn_str
= ldb_dn_linearize(tmp_ctx
, service_dn
);
74 ret
= ldb_search(ldb_ctx
, service_dn
, LDB_SCOPE_BASE
, "(objectClass=nTDSService)",
75 directory_attrs
, &res
);
77 if (ret
!= LDB_SUCCESS
) {
78 DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str
, ldb_errstring(ldb_ctx
)));
79 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
;
80 } else if (res
->count
> 1) {
82 DEBUG(1, ("ldb_search: dn: %s found %d times!", service_dn_str
, res
->count
));
83 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
85 talloc_steal(tmp_ctx
, res
);
87 spnmappings
= ldb_msg_find_element(res
->msgs
[0], "sPNMappings");
88 if (!spnmappings
|| spnmappings
->num_values
== 0) {
89 DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str
));
91 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
94 for (i
= 0; i
< spnmappings
->num_values
; i
++) {
95 char *mapping
, *p
, *str
;
96 mapping
= talloc_strdup(tmp_ctx
,
97 (const char *)spnmappings
->values
[i
].data
);
99 DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str
));
100 talloc_free(tmp_ctx
);
101 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
104 /* C string manipulation sucks */
106 p
= strchr(mapping
, '=');
108 DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
109 service_dn_str
, mapping
));
110 talloc_free(tmp_ctx
);
111 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
122 if (strcasecmp(str
, alias_from
) == 0) {
124 talloc_steal(mem_ctx
, mapping
);
125 talloc_free(tmp_ctx
);
126 return DRSUAPI_DS_NAME_STATUS_OK
;
130 DEBUG(1, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from
));
131 talloc_free(tmp_ctx
);
132 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
135 /* When cracking a ServicePrincipalName, many services may be served
136 * by the host/ servicePrincipalName. The incoming query is for cifs/
137 * but we translate it here, and search on host/. This is done after
138 * the cifs/ entry has been searched for, making this a fallback */
140 static WERROR
DsCrackNameSPNAlias(struct ldb_context
*sam_ctx
, TALLOC_CTX
*mem_ctx
,
141 struct smb_krb5_context
*smb_krb5_context
,
142 uint32_t format_flags
, uint32_t format_offered
, uint32_t format_desired
,
143 const char *name
, struct drsuapi_DsNameInfo1
*info1
)
147 krb5_principal principal
;
151 enum drsuapi_DsNameStatus namestatus
;
153 /* parse principal */
154 ret
= krb5_parse_name_norealm(smb_krb5_context
->krb5_context
,
157 DEBUG(2, ("Could not parse principal: %s: %s",
158 name
, smb_get_krb5_error_message(smb_krb5_context
->krb5_context
,
163 /* grab cifs/, http/ etc */
165 /* This is checked for in callers, but be safe */
166 if (principal
->name
.name_string
.len
< 2) {
167 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
170 service
= principal
->name
.name_string
.val
[0];
173 namestatus
= LDB_lookup_spn_alias(smb_krb5_context
->krb5_context
,
175 service
, &new_service
);
177 if (namestatus
!= DRSUAPI_DS_NAME_STATUS_OK
) {
178 info1
->status
= namestatus
;
183 info1
->status
= DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
;
187 /* ooh, very nasty playing around in the Principal... */
188 free(principal
->name
.name_string
.val
[0]);
189 principal
->name
.name_string
.val
[0] = strdup(new_service
);
190 if (!principal
->name
.name_string
.val
[0]) {
191 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
195 /* reform principal */
196 ret
= krb5_unparse_name_norealm(smb_krb5_context
->krb5_context
, principal
, &new_princ
);
198 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
204 wret
= DsCrackNameOneName(sam_ctx
, mem_ctx
, format_flags
, format_offered
, format_desired
,
210 /* Subcase of CrackNames, for the userPrincipalName */
212 static WERROR
DsCrackNameUPN(struct ldb_context
*sam_ctx
, TALLOC_CTX
*mem_ctx
,
213 struct smb_krb5_context
*smb_krb5_context
,
214 uint32_t format_flags
, uint32_t format_offered
, uint32_t format_desired
,
215 const char *name
, struct drsuapi_DsNameInfo1
*info1
)
218 const char *domain_filter
= NULL
;
219 const char *result_filter
= NULL
;
221 krb5_principal principal
;
223 char *unparsed_name_short
;
225 /* Prevent recursion */
227 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
231 ret
= krb5_parse_name_mustrealm(smb_krb5_context
->krb5_context
, name
, &principal
);
233 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
237 domain_filter
= NULL
;
238 realm
= krb5_princ_realm(smb_krb5_context
->krb5_context
, principal
);
239 domain_filter
= talloc_asprintf(mem_ctx
,
240 "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
241 ldb_binary_encode_string(mem_ctx
, *realm
),
242 ldb_binary_encode_string(mem_ctx
, *realm
));
243 ret
= krb5_unparse_name_norealm(smb_krb5_context
->krb5_context
, principal
, &unparsed_name_short
);
244 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
247 free(unparsed_name_short
);
251 /* This may need to be extended for more userPrincipalName variations */
252 result_filter
= talloc_asprintf(mem_ctx
, "(&(objectClass=user)(samAccountName=%s))",
253 ldb_binary_encode_string(mem_ctx
, unparsed_name_short
));
254 if (!result_filter
|| !domain_filter
) {
255 free(unparsed_name_short
);
258 status
= DsCrackNameOneFilter(sam_ctx
, mem_ctx
,
260 format_flags
, format_offered
, format_desired
,
261 NULL
, unparsed_name_short
, domain_filter
, result_filter
,
263 free(unparsed_name_short
);
267 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
269 WERROR
DsCrackNameOneName(struct ldb_context
*sam_ctx
, TALLOC_CTX
*mem_ctx
,
270 uint32_t format_flags
, uint32_t format_offered
, uint32_t format_desired
,
271 const char *name
, struct drsuapi_DsNameInfo1
*info1
)
274 const char *domain_filter
= NULL
;
275 const char *result_filter
= NULL
;
276 struct ldb_dn
*name_dn
= NULL
;
278 struct smb_krb5_context
*smb_krb5_context
;
279 ret
= smb_krb5_init_context(mem_ctx
, &smb_krb5_context
);
285 info1
->status
= DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
;
286 info1
->dns_domain_name
= NULL
;
287 info1
->result_name
= NULL
;
290 return WERR_INVALID_PARAM
;
293 /* TODO: - fill the correct names in all cases!
294 * - handle format_flags
297 /* here we need to set the domain_filter and/or the result_filter */
298 switch (format_offered
) {
299 case DRSUAPI_DS_NAME_FORMAT_CANONICAL
: {
302 str
= talloc_strdup(mem_ctx
, name
);
303 WERR_TALLOC_CHECK(str
);
305 if (strlen(str
) == 0 || str
[strlen(str
)-1] != '/') {
306 info1
->status
= DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
;
310 str
[strlen(str
)-1] = '\0';
312 domain_filter
= talloc_asprintf(mem_ctx
,
313 "(&(&(&(dnsRoot=%s)(objectclass=crossRef)))(nETBIOSName=*)(ncName=*))",
314 ldb_binary_encode_string(mem_ctx
, str
));
315 WERR_TALLOC_CHECK(domain_filter
);
319 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT
: {
322 const char *account
= NULL
;
324 domain
= talloc_strdup(mem_ctx
, name
);
325 WERR_TALLOC_CHECK(domain
);
327 p
= strchr(domain
, '\\');
329 /* invalid input format */
330 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
339 domain_filter
= talloc_asprintf(mem_ctx
,
340 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
341 ldb_binary_encode_string(mem_ctx
, domain
));
342 WERR_TALLOC_CHECK(domain_filter
);
344 result_filter
= talloc_asprintf(mem_ctx
, "(sAMAccountName=%s)",
345 ldb_binary_encode_string(mem_ctx
, account
));
346 WERR_TALLOC_CHECK(result_filter
);
353 /* A LDAP DN as a string */
354 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779
: {
355 name_dn
= ldb_dn_explode(mem_ctx
, name
);
356 domain_filter
= NULL
;
358 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
364 /* A GUID as a string */
365 case DRSUAPI_DS_NAME_FORMAT_GUID
: {
369 domain_filter
= NULL
;
371 nt_status
= GUID_from_string(name
, &guid
);
372 if (!NT_STATUS_IS_OK(nt_status
)) {
373 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
377 ldap_guid
= ldap_encode_ndr_GUID(mem_ctx
, &guid
);
381 result_filter
= talloc_asprintf(mem_ctx
, "(objectGUID=%s)",
383 WERR_TALLOC_CHECK(result_filter
);
386 case DRSUAPI_DS_NAME_FORMAT_DISPLAY
: {
387 domain_filter
= NULL
;
389 result_filter
= talloc_asprintf(mem_ctx
, "(|(displayName=%s)(samAccountName=%s))",
390 ldb_binary_encode_string(mem_ctx
, name
),
391 ldb_binary_encode_string(mem_ctx
, name
));
392 WERR_TALLOC_CHECK(result_filter
);
396 /* A S-1234-5678 style string */
397 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY
: {
398 struct dom_sid
*sid
= dom_sid_parse_talloc(mem_ctx
, name
);
401 domain_filter
= NULL
;
403 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
406 ldap_sid
= ldap_encode_ndr_dom_sid(mem_ctx
,
411 result_filter
= talloc_asprintf(mem_ctx
, "(objectSid=%s)",
413 WERR_TALLOC_CHECK(result_filter
);
416 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL
: {
417 krb5_principal principal
;
419 ret
= krb5_parse_name(smb_krb5_context
->krb5_context
, name
, &principal
);
421 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
425 domain_filter
= NULL
;
427 ret
= krb5_unparse_name(smb_krb5_context
->krb5_context
, principal
, &unparsed_name
);
429 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
433 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
434 result_filter
= talloc_asprintf(mem_ctx
, "(&(objectClass=user)(userPrincipalName=%s))",
435 ldb_binary_encode_string(mem_ctx
, unparsed_name
));
438 WERR_TALLOC_CHECK(result_filter
);
441 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL
: {
442 krb5_principal principal
;
443 char *unparsed_name_short
;
445 ret
= krb5_parse_name_norealm(smb_krb5_context
->krb5_context
, name
, &principal
);
447 /* perhaps it's a principal with a realm, so return the right 'domain only' response */
449 ret
= krb5_parse_name_mustrealm(smb_krb5_context
->krb5_context
, name
, &principal
);
451 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
455 /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
456 realm
= krb5_princ_realm(smb_krb5_context
->krb5_context
, principal
);
458 info1
->dns_domain_name
= talloc_strdup(info1
, *realm
);
459 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
461 WERR_TALLOC_CHECK(info1
->dns_domain_name
);
463 info1
->status
= DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
;
466 } else if (principal
->name
.name_string
.len
< 2) {
467 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
471 domain_filter
= NULL
;
473 ret
= krb5_unparse_name_norealm(smb_krb5_context
->krb5_context
, principal
, &unparsed_name_short
);
475 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
479 service
= principal
->name
.name_string
.val
[0];
480 if ((principal
->name
.name_string
.len
== 2) && (strcasecmp(service
, "host") == 0)) {
481 /* the 'cn' attribute is just the leading part of the name */
483 computer_name
= talloc_strndup(mem_ctx
, principal
->name
.name_string
.val
[1],
484 strcspn(principal
->name
.name_string
.val
[1], "."));
485 if (computer_name
== NULL
) {
489 result_filter
= talloc_asprintf(mem_ctx
, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
490 ldb_binary_encode_string(mem_ctx
, unparsed_name_short
),
491 ldb_binary_encode_string(mem_ctx
, computer_name
));
493 result_filter
= talloc_asprintf(mem_ctx
, "(&(servicePrincipalName=%s)(objectClass=user))",
494 ldb_binary_encode_string(mem_ctx
, unparsed_name_short
));
496 krb5_free_principal(smb_krb5_context
->krb5_context
, principal
);
497 free(unparsed_name_short
);
498 WERR_TALLOC_CHECK(result_filter
);
503 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
509 if (format_flags
& DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY
) {
510 return DsCrackNameOneSyntactical(mem_ctx
, format_offered
, format_desired
,
511 name_dn
, name
, info1
);
514 return DsCrackNameOneFilter(sam_ctx
, mem_ctx
,
516 format_flags
, format_offered
, format_desired
,
518 domain_filter
, result_filter
,
522 /* Subcase of CrackNames. It is possible to translate a LDAP-style DN
523 * (FQDN_1779) into a canoical name without actually searching the
526 static WERROR
DsCrackNameOneSyntactical(TALLOC_CTX
*mem_ctx
,
527 uint32_t format_offered
, uint32_t format_desired
,
528 const struct ldb_dn
*name_dn
, const char *name
,
529 struct drsuapi_DsNameInfo1
*info1
)
532 if (format_offered
!= DRSUAPI_DS_NAME_FORMAT_FQDN_1779
) {
533 info1
->status
= DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING
;
537 switch (format_desired
) {
538 case DRSUAPI_DS_NAME_FORMAT_CANONICAL
:
539 cracked
= ldb_dn_canonical_string(mem_ctx
, name_dn
);
541 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
:
542 cracked
= ldb_dn_canonical_ex_string(mem_ctx
, name_dn
);
545 info1
->status
= DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING
;
548 info1
->status
= DRSUAPI_DS_NAME_STATUS_OK
;
549 info1
->result_name
= cracked
;
557 /* Given a filter for the domain, and one for the result, perform the
558 * ldb search. The format offered and desired flags change the
559 * behaviours, including what attributes to return.
561 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
564 static WERROR
DsCrackNameOneFilter(struct ldb_context
*sam_ctx
, TALLOC_CTX
*mem_ctx
,
565 struct smb_krb5_context
*smb_krb5_context
,
566 uint32_t format_flags
, uint32_t format_offered
, uint32_t format_desired
,
567 const struct ldb_dn
*name_dn
, const char *name
,
568 const char *domain_filter
, const char *result_filter
,
569 struct drsuapi_DsNameInfo1
*info1
)
572 struct ldb_message
**domain_res
= NULL
;
573 const char * const *domain_attrs
;
574 const char * const *result_attrs
;
575 struct ldb_message
**result_res
= NULL
;
576 const struct ldb_dn
*result_basedn
;
578 /* here we need to set the attrs lists for domain and result lookups */
579 switch (format_desired
) {
580 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779
:
581 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
: {
582 const char * const _domain_attrs
[] = { "ncName", "dnsRoot", NULL
};
583 const char * const _result_attrs
[] = { NULL
};
585 domain_attrs
= _domain_attrs
;
586 result_attrs
= _result_attrs
;
589 case DRSUAPI_DS_NAME_FORMAT_CANONICAL
: {
590 const char * const _domain_attrs
[] = { "ncName", "dnsRoot", NULL
};
591 const char * const _result_attrs
[] = { "canonicalName", NULL
};
593 domain_attrs
= _domain_attrs
;
594 result_attrs
= _result_attrs
;
597 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT
: {
598 const char * const _domain_attrs
[] = { "ncName", "dnsRoot", "nETBIOSName", NULL
};
599 const char * const _result_attrs
[] = { "sAMAccountName", "objectSid", NULL
};
601 domain_attrs
= _domain_attrs
;
602 result_attrs
= _result_attrs
;
605 case DRSUAPI_DS_NAME_FORMAT_GUID
: {
606 const char * const _domain_attrs
[] = { "ncName", "dnsRoot", NULL
};
607 const char * const _result_attrs
[] = { "objectGUID", NULL
};
609 domain_attrs
= _domain_attrs
;
610 result_attrs
= _result_attrs
;
613 case DRSUAPI_DS_NAME_FORMAT_DISPLAY
: {
614 const char * const _domain_attrs
[] = { "ncName", "dnsRoot", NULL
};
615 const char * const _result_attrs
[] = { "displayName", "samAccountName", NULL
};
617 domain_attrs
= _domain_attrs
;
618 result_attrs
= _result_attrs
;
626 /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
627 ldb_ret
= gendb_search(sam_ctx
, mem_ctx
, NULL
, &domain_res
, domain_attrs
,
628 "%s", domain_filter
);
630 ldb_ret
= gendb_search(sam_ctx
, mem_ctx
, NULL
, &domain_res
, domain_attrs
,
631 "(ncName=%s)", ldb_dn_linearize(mem_ctx
, samdb_base_dn(mem_ctx
)));
638 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
641 info1
->status
= DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
;
644 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
;
648 info1
->dns_domain_name
= samdb_result_string(domain_res
[0], "dnsRoot", NULL
);
649 WERR_TALLOC_CHECK(info1
->dns_domain_name
);
650 info1
->status
= DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
;
653 result_basedn
= samdb_result_dn(mem_ctx
, domain_res
[0], "ncName", NULL
);
655 ldb_ret
= gendb_search(sam_ctx
, mem_ctx
, result_basedn
, &result_res
,
656 result_attrs
, "%s", result_filter
);
657 } else if (format_offered
== DRSUAPI_DS_NAME_FORMAT_FQDN_1779
) {
658 ldb_ret
= gendb_search_dn(sam_ctx
, mem_ctx
, name_dn
, &result_res
,
661 name_dn
= samdb_result_dn(mem_ctx
, domain_res
[0], "ncName", NULL
);
662 ldb_ret
= gendb_search_dn(sam_ctx
, mem_ctx
, name_dn
, &result_res
,
670 switch (format_offered
) {
671 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL
:
672 return DsCrackNameSPNAlias(sam_ctx
, mem_ctx
,
674 format_flags
, format_offered
, format_desired
,
677 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL
:
678 return DsCrackNameUPN(sam_ctx
, mem_ctx
, smb_krb5_context
,
679 format_flags
, format_offered
, format_desired
,
682 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
685 info1
->status
= DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
;
688 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
;
692 /* here we can use result_res[0] and domain_res[0] */
693 switch (format_desired
) {
694 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779
: {
695 info1
->result_name
= ldb_dn_linearize(mem_ctx
, result_res
[0]->dn
);
696 WERR_TALLOC_CHECK(info1
->result_name
);
698 info1
->status
= DRSUAPI_DS_NAME_STATUS_OK
;
701 case DRSUAPI_DS_NAME_FORMAT_CANONICAL
: {
702 info1
->result_name
= samdb_result_string(result_res
[0], "canonicalName", NULL
);
703 info1
->status
= DRSUAPI_DS_NAME_STATUS_OK
;
706 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
: {
707 /* Not in the virtual ldb attribute */
708 return DsCrackNameOneSyntactical(mem_ctx
,
709 DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
710 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
,
711 result_res
[0]->dn
, name
, info1
);
713 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT
: {
714 const struct dom_sid
*sid
= samdb_result_dom_sid(mem_ctx
, result_res
[0], "objectSid");
715 const char *_acc
= "", *_dom
= "";
717 if (!sid
|| (sid
->num_auths
< 4) || (sid
->num_auths
> 5)) {
718 info1
->status
= DRSUAPI_DS_NAME_STATUS_NO_MAPPING
;
722 if (sid
->num_auths
== 4) {
723 ldb_ret
= gendb_search(sam_ctx
, mem_ctx
, NULL
, &domain_res
, domain_attrs
,
724 "(ncName=%s)", ldb_dn_linearize(mem_ctx
, result_res
[0]->dn
));
726 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
729 _dom
= samdb_result_string(domain_res
[0], "nETBIOSName", NULL
);
730 WERR_TALLOC_CHECK(_dom
);
732 } else if (sid
->num_auths
== 5) {
733 const char *attrs
[] = { NULL
};
734 struct ldb_message
**domain_res2
;
735 struct dom_sid
*dom_sid
= dom_sid_dup(mem_ctx
, sid
);
739 dom_sid
->num_auths
--;
740 ldb_ret
= gendb_search(sam_ctx
, mem_ctx
, NULL
, &domain_res
, attrs
,
741 "(&(objectSid=%s)(objectClass=domain))", ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid
));
743 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
746 ldb_ret
= gendb_search(sam_ctx
, mem_ctx
, NULL
, &domain_res2
, domain_attrs
,
747 "(ncName=%s)", ldb_dn_linearize(mem_ctx
, domain_res
[0]->dn
));
749 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
753 _dom
= samdb_result_string(domain_res2
[0], "nETBIOSName", NULL
);
754 WERR_TALLOC_CHECK(_dom
);
756 _acc
= samdb_result_string(result_res
[0], "sAMAccountName", NULL
);
757 WERR_TALLOC_CHECK(_acc
);
760 info1
->result_name
= talloc_asprintf(mem_ctx
, "%s\\%s", _dom
, _acc
);
761 WERR_TALLOC_CHECK(info1
->result_name
);
763 info1
->status
= DRSUAPI_DS_NAME_STATUS_OK
;
766 case DRSUAPI_DS_NAME_FORMAT_GUID
: {
769 guid
= samdb_result_guid(result_res
[0], "objectGUID");
771 info1
->result_name
= GUID_string2(mem_ctx
, &guid
);
772 WERR_TALLOC_CHECK(info1
->result_name
);
774 info1
->status
= DRSUAPI_DS_NAME_STATUS_OK
;
777 case DRSUAPI_DS_NAME_FORMAT_DISPLAY
: {
778 info1
->result_name
= samdb_result_string(result_res
[0], "displayName", NULL
);
779 if (!info1
->result_name
) {
780 info1
->result_name
= samdb_result_string(result_res
[0], "sAMAccountName", NULL
);
782 if (!info1
->result_name
) {
783 info1
->status
= DRSUAPI_DS_NAME_STATUS_NOT_FOUND
;
785 info1
->status
= DRSUAPI_DS_NAME_STATUS_OK
;
793 return WERR_INVALID_PARAM
;
796 /* Given a user Principal Name (such as foo@bar.com),
797 * return the user and domain DNs. This is used in the KDC to then
798 * return the Keys and evaluate policy */
800 NTSTATUS
crack_user_principal_name(struct ldb_context
*sam_ctx
,
802 const char *user_principal_name
,
803 struct ldb_dn
**user_dn
,
804 struct ldb_dn
**domain_dn
)
807 struct drsuapi_DsNameInfo1 info1
;
808 werr
= DsCrackNameOneName(sam_ctx
, mem_ctx
, 0,
809 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL
,
810 DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
813 if (!W_ERROR_IS_OK(werr
)) {
814 return werror_to_ntstatus(werr
);
816 switch (info1
.status
) {
817 case DRSUAPI_DS_NAME_STATUS_OK
:
819 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND
:
820 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
:
821 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
:
822 return NT_STATUS_NO_SUCH_USER
;
823 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
:
825 return NT_STATUS_UNSUCCESSFUL
;
828 *user_dn
= ldb_dn_explode(mem_ctx
, info1
.result_name
);
831 werr
= DsCrackNameOneName(sam_ctx
, mem_ctx
, 0,
832 DRSUAPI_DS_NAME_FORMAT_CANONICAL
,
833 DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
834 talloc_asprintf(mem_ctx
, "%s/",
835 info1
.dns_domain_name
),
837 if (!W_ERROR_IS_OK(werr
)) {
838 return werror_to_ntstatus(werr
);
840 switch (info1
.status
) {
841 case DRSUAPI_DS_NAME_STATUS_OK
:
843 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND
:
844 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
:
845 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
:
846 return NT_STATUS_NO_SUCH_USER
;
847 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
:
849 return NT_STATUS_UNSUCCESSFUL
;
852 *domain_dn
= ldb_dn_explode(mem_ctx
, info1
.result_name
);
859 /* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
860 * return the user and domain DNs. This is used in the KDC to then
861 * return the Keys and evaluate policy */
863 NTSTATUS
crack_service_principal_name(struct ldb_context
*sam_ctx
,
865 const char *service_principal_name
,
866 struct ldb_dn
**user_dn
,
867 struct ldb_dn
**domain_dn
)
870 struct drsuapi_DsNameInfo1 info1
;
871 werr
= DsCrackNameOneName(sam_ctx
, mem_ctx
, 0,
872 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL
,
873 DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
874 service_principal_name
,
876 if (!W_ERROR_IS_OK(werr
)) {
877 return werror_to_ntstatus(werr
);
879 switch (info1
.status
) {
880 case DRSUAPI_DS_NAME_STATUS_OK
:
882 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND
:
883 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
:
884 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
:
885 return NT_STATUS_NO_SUCH_USER
;
886 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
:
888 return NT_STATUS_UNSUCCESSFUL
;
891 *user_dn
= ldb_dn_explode(mem_ctx
, info1
.result_name
);
894 werr
= DsCrackNameOneName(sam_ctx
, mem_ctx
, 0,
895 DRSUAPI_DS_NAME_FORMAT_CANONICAL
,
896 DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
897 talloc_asprintf(mem_ctx
, "%s/",
898 info1
.dns_domain_name
),
900 if (!W_ERROR_IS_OK(werr
)) {
901 return werror_to_ntstatus(werr
);
903 switch (info1
.status
) {
904 case DRSUAPI_DS_NAME_STATUS_OK
:
906 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND
:
907 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
:
908 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
:
909 return NT_STATUS_NO_SUCH_USER
;
910 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
:
912 return NT_STATUS_UNSUCCESSFUL
;
915 *domain_dn
= ldb_dn_explode(mem_ctx
, info1
.result_name
);
922 NTSTATUS
crack_dn_to_nt4_name(TALLOC_CTX
*mem_ctx
,
924 const char **nt4_domain
, const char **nt4_account
)
927 struct drsuapi_DsNameInfo1 info1
;
928 struct ldb_context
*ldb
;
931 /* Handle anonymous bind */
938 ldb
= samdb_connect(mem_ctx
, system_session(mem_ctx
));
940 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
943 werr
= DsCrackNameOneName(ldb
, mem_ctx
, 0,
944 DRSUAPI_DS_NAME_FORMAT_FQDN_1779
,
945 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT
,
948 if (!W_ERROR_IS_OK(werr
)) {
949 return werror_to_ntstatus(werr
);
951 switch (info1
.status
) {
952 case DRSUAPI_DS_NAME_STATUS_OK
:
954 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND
:
955 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
:
956 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
:
957 return NT_STATUS_NO_SUCH_USER
;
958 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR
:
960 return NT_STATUS_UNSUCCESSFUL
;
963 *nt4_domain
= talloc_strdup(mem_ctx
, info1
.result_name
);
965 p
= strchr(*nt4_domain
, '\\');
967 return NT_STATUS_INVALID_PARAMETER
;
972 *nt4_account
= talloc_strdup(mem_ctx
, &p
[1]);
975 if (!*nt4_account
|| !*nt4_domain
) {
976 return NT_STATUS_NO_MEMORY
;