s4:dsdb/samdb/cracknames - Remove unused header and add more "const"
[Samba.git] / source4 / dsdb / samdb / cracknames.c
blob23811d3a9271abbb60411d21e10ba7cdd399c8fa
1 /*
2 Unix SMB/CIFS implementation.
4 endpoint server for the drsuapi pipe
5 DsCrackNames()
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 3 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, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "librpc/gen_ndr/drsuapi.h"
26 #include "lib/events/events.h"
27 #include "rpc_server/common/common.h"
28 #include "lib/ldb/include/ldb.h"
29 #include "lib/ldb/include/ldb_errors.h"
30 #include "system/kerberos.h"
31 #include "auth/kerberos/kerberos.h"
32 #include "libcli/ldap/ldap_ndr.h"
33 #include "libcli/security/security.h"
34 #include "auth/auth.h"
35 #include "../lib/util/util_ldb.h"
36 #include "dsdb/samdb/samdb.h"
37 #include "param/param.h"
39 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
40 struct smb_krb5_context *smb_krb5_context,
41 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
42 struct ldb_dn *name_dn, const char *name,
43 const char *domain_filter, const char *result_filter,
44 struct drsuapi_DsNameInfo1 *info1);
45 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
46 uint32_t format_offered, uint32_t format_desired,
47 struct ldb_dn *name_dn, const char *name,
48 struct drsuapi_DsNameInfo1 *info1);
50 static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context,
51 const char *name,
52 struct drsuapi_DsNameInfo1 *info1)
54 krb5_error_code ret;
55 krb5_principal principal;
56 /* perhaps it's a principal with a realm, so return the right 'domain only' response */
57 const char *realm;
58 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
59 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
60 if (ret) {
61 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
62 return WERR_OK;
65 /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
66 realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
68 info1->dns_domain_name = talloc_strdup(mem_ctx, realm);
69 krb5_free_principal(smb_krb5_context->krb5_context, principal);
71 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
73 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
74 return WERR_OK;
77 static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx,
78 TALLOC_CTX *mem_ctx,
79 const char *alias_from,
80 char **alias_to)
82 int i;
83 int ret;
84 struct ldb_result *res;
85 struct ldb_message_element *spnmappings;
86 TALLOC_CTX *tmp_ctx;
87 struct ldb_dn *service_dn;
88 char *service_dn_str;
90 const char *directory_attrs[] = {
91 "sPNMappings",
92 NULL
95 tmp_ctx = talloc_new(mem_ctx);
96 if (!tmp_ctx) {
97 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
100 service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
101 if ( ! ldb_dn_add_base(service_dn, samdb_config_dn(ldb_ctx))) {
102 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
104 service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
105 if ( ! service_dn_str) {
106 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
109 ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
110 directory_attrs, "(objectClass=nTDSService)");
112 if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
113 DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx)));
114 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
115 } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
116 DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
117 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
118 } else if (res->count != 1) {
119 talloc_free(res);
120 DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
121 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
124 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
125 if (!spnmappings || spnmappings->num_values == 0) {
126 DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str));
127 talloc_free(tmp_ctx);
128 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
131 for (i = 0; i < spnmappings->num_values; i++) {
132 char *mapping, *p, *str;
133 mapping = talloc_strdup(tmp_ctx,
134 (const char *)spnmappings->values[i].data);
135 if (!mapping) {
136 DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
137 talloc_free(tmp_ctx);
138 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
141 /* C string manipulation sucks */
143 p = strchr(mapping, '=');
144 if (!p) {
145 DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
146 service_dn_str, mapping));
147 talloc_free(tmp_ctx);
148 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
150 p[0] = '\0';
151 p++;
152 do {
153 str = p;
154 p = strchr(p, ',');
155 if (p) {
156 p[0] = '\0';
157 p++;
159 if (strcasecmp(str, alias_from) == 0) {
160 *alias_to = mapping;
161 talloc_steal(mem_ctx, mapping);
162 talloc_free(tmp_ctx);
163 return DRSUAPI_DS_NAME_STATUS_OK;
165 } while (p);
167 DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
168 talloc_free(tmp_ctx);
169 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
172 /* When cracking a ServicePrincipalName, many services may be served
173 * by the host/ servicePrincipalName. The incoming query is for cifs/
174 * but we translate it here, and search on host/. This is done after
175 * the cifs/ entry has been searched for, making this a fallback */
177 static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
178 struct smb_krb5_context *smb_krb5_context,
179 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
180 const char *name, struct drsuapi_DsNameInfo1 *info1)
182 WERROR wret;
183 krb5_error_code ret;
184 krb5_principal principal;
185 const char *service, *dns_name;
186 char *new_service;
187 char *new_princ;
188 enum drsuapi_DsNameStatus namestatus;
190 /* parse principal */
191 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
192 name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
193 if (ret) {
194 DEBUG(2, ("Could not parse principal: %s: %s",
195 name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
196 ret, mem_ctx)));
197 return WERR_NOMEM;
200 /* grab cifs/, http/ etc */
202 /* This is checked for in callers, but be safe */
203 if (principal->name.name_string.len < 2) {
204 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
205 return WERR_OK;
207 service = principal->name.name_string.val[0];
208 dns_name = principal->name.name_string.val[1];
210 /* MAP it */
211 namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context,
212 sam_ctx, mem_ctx,
213 service, &new_service);
215 if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
216 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
217 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
218 if (!info1->dns_domain_name) {
219 krb5_free_principal(smb_krb5_context->krb5_context, principal);
220 return WERR_NOMEM;
222 return WERR_OK;
223 } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
224 info1->status = namestatus;
225 krb5_free_principal(smb_krb5_context->krb5_context, principal);
226 return WERR_OK;
229 /* ooh, very nasty playing around in the Principal... */
230 free(principal->name.name_string.val[0]);
231 principal->name.name_string.val[0] = strdup(new_service);
232 if (!principal->name.name_string.val[0]) {
233 krb5_free_principal(smb_krb5_context->krb5_context, principal);
234 return WERR_NOMEM;
237 /* reform principal */
238 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
239 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
241 if (ret) {
242 krb5_free_principal(smb_krb5_context->krb5_context, principal);
243 return WERR_NOMEM;
246 wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
247 new_princ, info1);
248 free(new_princ);
249 if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
250 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
251 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
252 if (!info1->dns_domain_name) {
253 wret = WERR_NOMEM;
256 krb5_free_principal(smb_krb5_context->krb5_context, principal);
257 return wret;
260 /* Subcase of CrackNames, for the userPrincipalName */
262 static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
263 struct smb_krb5_context *smb_krb5_context,
264 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
265 const char *name, struct drsuapi_DsNameInfo1 *info1)
267 int ldb_ret;
268 WERROR status;
269 const char *domain_filter = NULL;
270 const char *result_filter = NULL;
271 krb5_error_code ret;
272 krb5_principal principal;
273 const char *realm;
274 char *unparsed_name_short;
275 const char *domain_attrs[] = { NULL };
276 struct ldb_result *domain_res = NULL;
278 /* Prevent recursion */
279 if (!name) {
280 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
281 return WERR_OK;
284 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
285 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
286 if (ret) {
287 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
288 return WERR_OK;
291 realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
293 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
294 samdb_partitions_dn(sam_ctx, mem_ctx),
295 LDB_SCOPE_ONELEVEL,
296 domain_attrs,
297 "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
298 ldb_binary_encode_string(mem_ctx, realm),
299 ldb_binary_encode_string(mem_ctx, realm));
301 if (ldb_ret != LDB_SUCCESS) {
302 DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx)));
303 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
304 return WERR_OK;
307 switch (domain_res->count) {
308 case 1:
309 break;
310 case 0:
311 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
312 name, info1);
313 default:
314 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
315 return WERR_OK;
318 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
319 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
320 krb5_free_principal(smb_krb5_context->krb5_context, principal);
322 if (ret) {
323 free(unparsed_name_short);
324 return WERR_NOMEM;
327 /* This may need to be extended for more userPrincipalName variations */
328 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
329 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
331 domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
333 if (!result_filter || !domain_filter) {
334 free(unparsed_name_short);
335 return WERR_NOMEM;
337 status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
338 smb_krb5_context,
339 format_flags, format_offered, format_desired,
340 NULL, unparsed_name_short, domain_filter, result_filter,
341 info1);
342 free(unparsed_name_short);
344 return status;
347 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
349 WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
350 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
351 const char *name, struct drsuapi_DsNameInfo1 *info1)
353 krb5_error_code ret;
354 const char *domain_filter = NULL;
355 const char *result_filter = NULL;
356 struct ldb_dn *name_dn = NULL;
358 struct smb_krb5_context *smb_krb5_context = NULL;
360 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
361 info1->dns_domain_name = NULL;
362 info1->result_name = NULL;
364 if (!name) {
365 return WERR_INVALID_PARAM;
368 /* TODO: - fill the correct names in all cases!
369 * - handle format_flags
372 /* here we need to set the domain_filter and/or the result_filter */
373 switch (format_offered) {
374 case DRSUAPI_DS_NAME_FORMAT_UNKNOWN:
376 int i;
377 enum drsuapi_DsNameFormat formats[] = {
378 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
379 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL,
380 DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY,
381 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
382 DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
383 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
385 WERROR werr;
386 for (i=0; i < ARRAY_SIZE(formats); i++) {
387 werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1);
388 if (!W_ERROR_IS_OK(werr)) {
389 return werr;
391 if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
392 return werr;
395 return werr;
398 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
399 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
401 char *str, *s, *account;
403 if (strlen(name) == 0) {
404 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
405 return WERR_OK;
408 str = talloc_strdup(mem_ctx, name);
409 W_ERROR_HAVE_NO_MEMORY(str);
411 if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
412 /* Look backwards for the \n, and replace it with / */
413 s = strrchr(str, '\n');
414 if (!s) {
415 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
416 return WERR_OK;
418 s[0] = '/';
421 s = strchr(str, '/');
422 if (!s) {
423 /* there must be at least one / */
424 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
425 return WERR_OK;
428 s[0] = '\0';
429 s++;
431 domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))",
432 ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str)));
433 W_ERROR_HAVE_NO_MEMORY(domain_filter);
435 /* There may not be anything after the domain component (search for the domain itself) */
436 if (s[0]) {
438 account = strrchr(s, '/');
439 if (!account) {
440 account = s;
441 } else {
442 account++;
444 account = ldb_binary_encode_string(mem_ctx, account);
445 W_ERROR_HAVE_NO_MEMORY(account);
446 result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
447 account);
448 W_ERROR_HAVE_NO_MEMORY(result_filter);
450 break;
452 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
453 char *p;
454 char *domain;
455 const char *account = NULL;
457 domain = talloc_strdup(mem_ctx, name);
458 W_ERROR_HAVE_NO_MEMORY(domain);
460 p = strchr(domain, '\\');
461 if (!p) {
462 /* invalid input format */
463 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
464 return WERR_OK;
466 p[0] = '\0';
468 if (p[1]) {
469 account = &p[1];
472 domain_filter = talloc_asprintf(mem_ctx,
473 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
474 ldb_binary_encode_string(mem_ctx, domain));
475 W_ERROR_HAVE_NO_MEMORY(domain_filter);
476 if (account) {
477 result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
478 ldb_binary_encode_string(mem_ctx, account));
479 W_ERROR_HAVE_NO_MEMORY(result_filter);
482 talloc_free(domain);
483 break;
486 /* A LDAP DN as a string */
487 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
488 domain_filter = NULL;
489 name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
490 if (! ldb_dn_validate(name_dn)) {
491 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
492 return WERR_OK;
494 break;
497 /* A GUID as a string */
498 case DRSUAPI_DS_NAME_FORMAT_GUID: {
499 struct GUID guid;
500 char *ldap_guid;
501 NTSTATUS nt_status;
502 domain_filter = NULL;
504 nt_status = GUID_from_string(name, &guid);
505 if (!NT_STATUS_IS_OK(nt_status)) {
506 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
507 return WERR_OK;
510 ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
511 if (!ldap_guid) {
512 return WERR_NOMEM;
514 result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
515 ldap_guid);
516 W_ERROR_HAVE_NO_MEMORY(result_filter);
517 break;
519 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
520 domain_filter = NULL;
522 result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
523 ldb_binary_encode_string(mem_ctx, name),
524 ldb_binary_encode_string(mem_ctx, name));
525 W_ERROR_HAVE_NO_MEMORY(result_filter);
526 break;
529 /* A S-1234-5678 style string */
530 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
531 struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
532 char *ldap_sid;
534 domain_filter = NULL;
535 if (!sid) {
536 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
537 return WERR_OK;
539 ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
540 sid);
541 if (!ldap_sid) {
542 return WERR_NOMEM;
544 result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
545 ldap_sid);
546 W_ERROR_HAVE_NO_MEMORY(result_filter);
547 break;
549 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
550 krb5_principal principal;
551 char *unparsed_name;
553 ret = smb_krb5_init_context(mem_ctx,
554 ldb_get_event_context(sam_ctx),
555 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
556 &smb_krb5_context);
558 if (ret) {
559 return WERR_NOMEM;
562 /* Ensure we reject compleate junk first */
563 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
564 if (ret) {
565 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
566 return WERR_OK;
569 domain_filter = NULL;
571 /* By getting the unparsed name here, we ensure the escaping is correct (and trust the client less) */
572 ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name);
573 if (ret) {
574 krb5_free_principal(smb_krb5_context->krb5_context, principal);
575 return WERR_NOMEM;
578 krb5_free_principal(smb_krb5_context->krb5_context, principal);
580 /* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */
581 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))",
582 ldb_binary_encode_string(mem_ctx, unparsed_name));
584 free(unparsed_name);
585 W_ERROR_HAVE_NO_MEMORY(result_filter);
586 break;
588 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
589 krb5_principal principal;
590 char *unparsed_name_short;
591 char *service;
593 ret = smb_krb5_init_context(mem_ctx,
594 ldb_get_event_context(sam_ctx),
595 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
596 &smb_krb5_context);
598 if (ret) {
599 return WERR_NOMEM;
602 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
603 if (ret == 0 && principal->name.name_string.len < 2) {
604 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
605 krb5_free_principal(smb_krb5_context->krb5_context, principal);
606 return WERR_OK;
608 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
609 KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
610 if (ret) {
611 krb5_free_principal(smb_krb5_context->krb5_context, principal);
613 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
614 name, info1);
617 domain_filter = NULL;
619 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
620 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
621 if (ret) {
622 krb5_free_principal(smb_krb5_context->krb5_context, principal);
623 return WERR_NOMEM;
626 service = principal->name.name_string.val[0];
627 if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) {
628 /* the 'cn' attribute is just the leading part of the name */
629 char *computer_name;
630 computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1],
631 strcspn(principal->name.name_string.val[1], "."));
632 if (computer_name == NULL) {
633 return WERR_NOMEM;
636 result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
637 ldb_binary_encode_string(mem_ctx, unparsed_name_short),
638 ldb_binary_encode_string(mem_ctx, computer_name));
639 } else {
640 result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
641 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
643 krb5_free_principal(smb_krb5_context->krb5_context, principal);
644 free(unparsed_name_short);
645 W_ERROR_HAVE_NO_MEMORY(result_filter);
647 break;
649 default: {
650 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
651 return WERR_OK;
656 if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
657 return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
658 name_dn, name, info1);
661 return DsCrackNameOneFilter(sam_ctx, mem_ctx,
662 smb_krb5_context,
663 format_flags, format_offered, format_desired,
664 name_dn, name,
665 domain_filter, result_filter,
666 info1);
669 /* Subcase of CrackNames. It is possible to translate a LDAP-style DN
670 * (FQDN_1779) into a canoical name without actually searching the
671 * database */
673 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
674 uint32_t format_offered, uint32_t format_desired,
675 struct ldb_dn *name_dn, const char *name,
676 struct drsuapi_DsNameInfo1 *info1)
678 char *cracked;
679 if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
680 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
681 return WERR_OK;
684 switch (format_desired) {
685 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
686 cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
687 break;
688 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
689 cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
690 break;
691 default:
692 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
693 return WERR_OK;
695 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
696 info1->result_name = cracked;
697 if (!cracked) {
698 return WERR_NOMEM;
701 return WERR_OK;
704 /* Given a filter for the domain, and one for the result, perform the
705 * ldb search. The format offered and desired flags change the
706 * behaviours, including what attributes to return.
708 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
711 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
712 struct smb_krb5_context *smb_krb5_context,
713 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
714 struct ldb_dn *name_dn, const char *name,
715 const char *domain_filter, const char *result_filter,
716 struct drsuapi_DsNameInfo1 *info1)
718 int ldb_ret;
719 struct ldb_result *domain_res = NULL;
720 const char * const *domain_attrs;
721 const char * const *result_attrs;
722 struct ldb_message **result_res = NULL;
723 struct ldb_message *result = NULL;
724 struct ldb_dn *result_basedn = NULL;
725 int i;
726 char *p;
727 struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
729 const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
730 const char * const _result_attrs_null[] = { NULL };
732 const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
733 const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
735 const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
736 const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
738 const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
739 const char * const _result_attrs_guid[] = { "objectGUID", NULL};
741 const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
742 const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
744 const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
745 const char * const _result_attrs_none[] = { NULL};
747 /* here we need to set the attrs lists for domain and result lookups */
748 switch (format_desired) {
749 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
750 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
751 domain_attrs = _domain_attrs_1779;
752 result_attrs = _result_attrs_null;
753 break;
754 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
755 domain_attrs = _domain_attrs_canonical;
756 result_attrs = _result_attrs_canonical;
757 break;
758 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
759 domain_attrs = _domain_attrs_nt4;
760 result_attrs = _result_attrs_nt4;
761 break;
762 case DRSUAPI_DS_NAME_FORMAT_GUID:
763 domain_attrs = _domain_attrs_guid;
764 result_attrs = _result_attrs_guid;
765 break;
766 case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
767 domain_attrs = _domain_attrs_display;
768 result_attrs = _result_attrs_display;
769 break;
770 default:
771 domain_attrs = _domain_attrs_none;
772 result_attrs = _result_attrs_none;
773 break;
776 if (domain_filter) {
777 /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
778 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
779 partitions_basedn,
780 LDB_SCOPE_ONELEVEL,
781 domain_attrs,
782 "%s", domain_filter);
784 if (ldb_ret != LDB_SUCCESS) {
785 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
786 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
787 return WERR_OK;
790 switch (domain_res->count) {
791 case 1:
792 break;
793 case 0:
794 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
795 return WERR_OK;
796 default:
797 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
798 return WERR_OK;
801 info1->dns_domain_name = samdb_result_string(domain_res->msgs[0], "dnsRoot", NULL);
802 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
803 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
804 } else {
805 info1->dns_domain_name = NULL;
806 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
809 if (result_filter) {
810 int ret;
811 struct ldb_result *res;
812 if (domain_res) {
813 result_basedn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
815 ret = ldb_search(sam_ctx, mem_ctx, &res,
816 result_basedn, LDB_SCOPE_SUBTREE,
817 result_attrs, "%s", result_filter);
818 if (ret != LDB_SUCCESS) {
819 talloc_free(result_res);
820 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
821 return WERR_OK;
823 ldb_ret = res->count;
824 result_res = res->msgs;
825 } else {
826 /* search with the 'phantom root' flag */
827 struct ldb_request *req;
829 res = talloc_zero(mem_ctx, struct ldb_result);
830 W_ERROR_HAVE_NO_MEMORY(res);
832 ret = ldb_build_search_req(&req, sam_ctx, mem_ctx,
833 ldb_get_root_basedn(sam_ctx),
834 LDB_SCOPE_SUBTREE,
835 result_filter,
836 result_attrs,
837 NULL,
838 res,
839 ldb_search_default_callback,
840 NULL);
841 if (ret == LDB_SUCCESS) {
842 struct ldb_search_options_control *search_options;
843 search_options = talloc(req, struct ldb_search_options_control);
844 W_ERROR_HAVE_NO_MEMORY(search_options);
845 search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
847 ret = ldb_request_add_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options);
849 if (ret != LDB_SUCCESS) {
850 talloc_free(res);
851 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
852 return WERR_OK;
855 ret = ldb_request(sam_ctx, req);
857 if (ret == LDB_SUCCESS) {
858 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
861 talloc_free(req);
863 if (ret != LDB_SUCCESS) {
864 DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s",
865 ldb_errstring(sam_ctx)));
866 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
867 return WERR_OK;
869 ldb_ret = res->count;
870 result_res = res->msgs;
872 } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
873 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
874 result_attrs);
875 } else if (domain_res) {
876 name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
877 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
878 result_attrs);
879 } else {
880 /* Can't happen */
881 DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not availible: This can't happen..."));
882 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
883 return WERR_OK;
886 switch (ldb_ret) {
887 case 1:
888 result = result_res[0];
889 break;
890 case 0:
891 switch (format_offered) {
892 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
893 return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
894 smb_krb5_context,
895 format_flags, format_offered, format_desired,
896 name, info1);
898 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
899 return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
900 format_flags, format_offered, format_desired,
901 name, info1);
903 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
904 return WERR_OK;
905 case -1:
906 DEBUG(2, ("DsCrackNameOneFilter result search failed: %s", ldb_errstring(sam_ctx)));
907 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
908 return WERR_OK;
909 default:
910 switch (format_offered) {
911 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
912 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
914 const char *canonical_name = NULL; /* Not required, but we get warnings... */
915 /* We may need to manually filter further */
916 for (i = 0; i < ldb_ret; i++) {
917 switch (format_offered) {
918 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
919 canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
920 break;
921 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
922 canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
923 break;
925 if (strcasecmp_m(canonical_name, name) == 0) {
926 result = result_res[i];
927 break;
930 if (!result) {
931 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
932 return WERR_OK;
935 default:
936 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
937 return WERR_OK;
941 info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
942 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
943 p = strchr(info1->dns_domain_name, '/');
944 if (p) {
945 p[0] = '\0';
948 /* here we can use result and domain_res[0] */
949 switch (format_desired) {
950 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
951 info1->result_name = ldb_dn_alloc_linearized(mem_ctx, result->dn);
952 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
954 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
955 return WERR_OK;
957 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
958 info1->result_name = samdb_result_string(result, "canonicalName", NULL);
959 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
960 return WERR_OK;
962 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
963 /* Not in the virtual ldb attribute */
964 return DsCrackNameOneSyntactical(mem_ctx,
965 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
966 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
967 result->dn, name, info1);
969 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
971 const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
972 const char *_acc = "", *_dom = "";
974 if (samdb_find_attribute(sam_ctx, result, "objectClass", "domain")) {
976 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
977 partitions_basedn,
978 LDB_SCOPE_ONELEVEL,
979 domain_attrs,
980 "(ncName=%s)", ldb_dn_get_linearized(result->dn));
982 if (ldb_ret != LDB_SUCCESS) {
983 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
984 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
985 return WERR_OK;
988 switch (domain_res->count) {
989 case 1:
990 break;
991 case 0:
992 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
993 return WERR_OK;
994 default:
995 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
996 return WERR_OK;
998 _dom = samdb_result_string(domain_res->msgs[0], "nETBIOSName", NULL);
999 W_ERROR_HAVE_NO_MEMORY(_dom);
1000 } else {
1001 _acc = samdb_result_string(result, "sAMAccountName", NULL);
1002 if (!_acc) {
1003 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1004 return WERR_OK;
1006 if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) {
1007 _dom = "BUILTIN";
1008 } else {
1009 const char *attrs[] = { NULL };
1010 struct ldb_result *domain_res2;
1011 struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
1012 if (!dom_sid) {
1013 return WERR_OK;
1015 dom_sid->num_auths--;
1016 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1017 NULL,
1018 LDB_SCOPE_BASE,
1019 attrs,
1020 "(&(objectSid=%s)(objectClass=domain))",
1021 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
1023 if (ldb_ret != LDB_SUCCESS) {
1024 DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s", ldb_errstring(sam_ctx)));
1025 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1026 return WERR_OK;
1029 switch (domain_res->count) {
1030 case 1:
1031 break;
1032 case 0:
1033 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1034 return WERR_OK;
1035 default:
1036 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1037 return WERR_OK;
1040 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2,
1041 partitions_basedn,
1042 LDB_SCOPE_ONELEVEL,
1043 domain_attrs,
1044 "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
1046 if (ldb_ret != LDB_SUCCESS) {
1047 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
1048 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1049 return WERR_OK;
1052 switch (domain_res2->count) {
1053 case 1:
1054 break;
1055 case 0:
1056 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1057 return WERR_OK;
1058 default:
1059 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1060 return WERR_OK;
1062 _dom = samdb_result_string(domain_res2->msgs[0], "nETBIOSName", NULL);
1063 W_ERROR_HAVE_NO_MEMORY(_dom);
1067 info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
1068 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1070 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1071 return WERR_OK;
1073 case DRSUAPI_DS_NAME_FORMAT_GUID: {
1074 struct GUID guid;
1076 guid = samdb_result_guid(result, "objectGUID");
1078 info1->result_name = GUID_string2(mem_ctx, &guid);
1079 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1081 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1082 return WERR_OK;
1084 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
1085 info1->result_name = samdb_result_string(result, "displayName", NULL);
1086 if (!info1->result_name) {
1087 info1->result_name = samdb_result_string(result, "sAMAccountName", NULL);
1089 if (!info1->result_name) {
1090 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1091 } else {
1092 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1094 return WERR_OK;
1096 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
1097 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1098 return WERR_OK;
1100 case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
1101 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
1102 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1103 return WERR_OK;
1105 default:
1106 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1107 return WERR_OK;
1111 /* Given a user Principal Name (such as foo@bar.com),
1112 * return the user and domain DNs. This is used in the KDC to then
1113 * return the Keys and evaluate policy */
1115 NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
1116 TALLOC_CTX *mem_ctx,
1117 const char *user_principal_name,
1118 struct ldb_dn **user_dn,
1119 struct ldb_dn **domain_dn)
1121 WERROR werr;
1122 struct drsuapi_DsNameInfo1 info1;
1123 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1124 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
1125 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1126 user_principal_name,
1127 &info1);
1128 if (!W_ERROR_IS_OK(werr)) {
1129 return werror_to_ntstatus(werr);
1131 switch (info1.status) {
1132 case DRSUAPI_DS_NAME_STATUS_OK:
1133 break;
1134 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1135 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1136 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1137 return NT_STATUS_NO_SUCH_USER;
1138 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1139 default:
1140 return NT_STATUS_UNSUCCESSFUL;
1143 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1145 if (domain_dn) {
1146 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1147 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1148 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1149 talloc_asprintf(mem_ctx, "%s/",
1150 info1.dns_domain_name),
1151 &info1);
1152 if (!W_ERROR_IS_OK(werr)) {
1153 return werror_to_ntstatus(werr);
1155 switch (info1.status) {
1156 case DRSUAPI_DS_NAME_STATUS_OK:
1157 break;
1158 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1159 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1160 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1161 return NT_STATUS_NO_SUCH_USER;
1162 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1163 default:
1164 return NT_STATUS_UNSUCCESSFUL;
1167 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1170 return NT_STATUS_OK;
1173 /* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
1174 * return the user and domain DNs. This is used in the KDC to then
1175 * return the Keys and evaluate policy */
1177 NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
1178 TALLOC_CTX *mem_ctx,
1179 const char *service_principal_name,
1180 struct ldb_dn **user_dn,
1181 struct ldb_dn **domain_dn)
1183 WERROR werr;
1184 struct drsuapi_DsNameInfo1 info1;
1185 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1186 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
1187 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1188 service_principal_name,
1189 &info1);
1190 if (!W_ERROR_IS_OK(werr)) {
1191 return werror_to_ntstatus(werr);
1193 switch (info1.status) {
1194 case DRSUAPI_DS_NAME_STATUS_OK:
1195 break;
1196 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1197 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1198 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1199 return NT_STATUS_NO_SUCH_USER;
1200 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1201 default:
1202 return NT_STATUS_UNSUCCESSFUL;
1205 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1207 if (domain_dn) {
1208 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1209 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1210 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1211 talloc_asprintf(mem_ctx, "%s/",
1212 info1.dns_domain_name),
1213 &info1);
1214 if (!W_ERROR_IS_OK(werr)) {
1215 return werror_to_ntstatus(werr);
1217 switch (info1.status) {
1218 case DRSUAPI_DS_NAME_STATUS_OK:
1219 break;
1220 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1221 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1222 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1223 return NT_STATUS_NO_SUCH_USER;
1224 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1225 default:
1226 return NT_STATUS_UNSUCCESSFUL;
1229 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1232 return NT_STATUS_OK;
1235 NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1236 struct tevent_context *ev_ctx,
1237 struct loadparm_context *lp_ctx,
1238 uint32_t format_offered,
1239 const char *name,
1240 const char **nt4_domain, const char **nt4_account)
1242 WERROR werr;
1243 struct drsuapi_DsNameInfo1 info1;
1244 struct ldb_context *ldb;
1245 char *p;
1247 /* Handle anonymous bind */
1248 if (!name || !*name) {
1249 *nt4_domain = "";
1250 *nt4_account = "";
1251 return NT_STATUS_OK;
1254 ldb = samdb_connect(mem_ctx, ev_ctx, lp_ctx, system_session(mem_ctx, lp_ctx));
1255 if (ldb == NULL) {
1256 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1259 werr = DsCrackNameOneName(ldb, mem_ctx, 0,
1260 format_offered,
1261 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
1262 name,
1263 &info1);
1264 if (!W_ERROR_IS_OK(werr)) {
1265 return werror_to_ntstatus(werr);
1267 switch (info1.status) {
1268 case DRSUAPI_DS_NAME_STATUS_OK:
1269 break;
1270 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1271 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1272 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1273 return NT_STATUS_NO_SUCH_USER;
1274 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1275 default:
1276 return NT_STATUS_UNSUCCESSFUL;
1279 *nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
1280 if (*nt4_domain == NULL) {
1281 return NT_STATUS_NO_MEMORY;
1284 p = strchr(*nt4_domain, '\\');
1285 if (!p) {
1286 return NT_STATUS_INVALID_PARAMETER;
1288 p[0] = '\0';
1290 *nt4_account = talloc_strdup(mem_ctx, &p[1]);
1291 if (*nt4_account == NULL) {
1292 return NT_STATUS_NO_MEMORY;
1295 return NT_STATUS_OK;
1298 NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1299 struct tevent_context *ev_ctx,
1300 struct loadparm_context *lp_ctx,
1301 const char *name,
1302 const char **nt4_domain,
1303 const char **nt4_account)
1305 uint32_t format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN;
1307 /* Handle anonymous bind */
1308 if (!name || !*name) {
1309 *nt4_domain = "";
1310 *nt4_account = "";
1311 return NT_STATUS_OK;
1314 if (strchr_m(name, '=')) {
1315 format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
1316 } else if (strchr_m(name, '@')) {
1317 format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
1318 } else if (strchr_m(name, '\\')) {
1319 format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
1320 } else if (strchr_m(name, '/')) {
1321 format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
1322 } else {
1323 return NT_STATUS_NO_SUCH_USER;
1326 return crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, format_offered, name, nt4_domain, nt4_account);