Revert "s4:remove "util_ldb" submodule and integrate the three gendb_* calls in ...
[Samba.git] / source4 / dsdb / samdb / cracknames.c
blob6df140fed376bfd7efd819fae6d14bba62f423f2
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 "dsdb/common/util.h"
38 #include "param/param.h"
40 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
41 struct smb_krb5_context *smb_krb5_context,
42 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
43 enum drsuapi_DsNameFormat format_desired,
44 struct ldb_dn *name_dn, const char *name,
45 const char *domain_filter, const char *result_filter,
46 struct drsuapi_DsNameInfo1 *info1);
47 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
48 enum drsuapi_DsNameFormat format_offered,
49 enum drsuapi_DsNameFormat format_desired,
50 struct ldb_dn *name_dn, const char *name,
51 struct drsuapi_DsNameInfo1 *info1);
53 static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context,
54 const char *name,
55 struct drsuapi_DsNameInfo1 *info1)
57 krb5_error_code ret;
58 krb5_principal principal;
59 /* perhaps it's a principal with a realm, so return the right 'domain only' response */
60 const char *realm;
61 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
62 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
63 if (ret) {
64 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
65 return WERR_OK;
68 /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
69 realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
71 info1->dns_domain_name = talloc_strdup(mem_ctx, realm);
72 krb5_free_principal(smb_krb5_context->krb5_context, principal);
74 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
76 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
77 return WERR_OK;
80 static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx,
81 TALLOC_CTX *mem_ctx,
82 const char *alias_from,
83 char **alias_to)
85 unsigned int i;
86 int ret;
87 struct ldb_result *res;
88 struct ldb_message_element *spnmappings;
89 TALLOC_CTX *tmp_ctx;
90 struct ldb_dn *service_dn;
91 char *service_dn_str;
93 const char *directory_attrs[] = {
94 "sPNMappings",
95 NULL
98 tmp_ctx = talloc_new(mem_ctx);
99 if (!tmp_ctx) {
100 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
103 service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
104 if ( ! ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb_ctx))) {
105 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
107 service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
108 if ( ! service_dn_str) {
109 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
112 ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
113 directory_attrs, "(objectClass=nTDSService)");
115 if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
116 DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx)));
117 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
118 } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
119 DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
120 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
121 } else if (res->count != 1) {
122 talloc_free(res);
123 DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
124 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
127 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
128 if (!spnmappings || spnmappings->num_values == 0) {
129 DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str));
130 talloc_free(tmp_ctx);
131 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
134 for (i = 0; i < spnmappings->num_values; i++) {
135 char *mapping, *p, *str;
136 mapping = talloc_strdup(tmp_ctx,
137 (const char *)spnmappings->values[i].data);
138 if (!mapping) {
139 DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
140 talloc_free(tmp_ctx);
141 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
144 /* C string manipulation sucks */
146 p = strchr(mapping, '=');
147 if (!p) {
148 DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
149 service_dn_str, mapping));
150 talloc_free(tmp_ctx);
151 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
153 p[0] = '\0';
154 p++;
155 do {
156 str = p;
157 p = strchr(p, ',');
158 if (p) {
159 p[0] = '\0';
160 p++;
162 if (strcasecmp(str, alias_from) == 0) {
163 *alias_to = mapping;
164 talloc_steal(mem_ctx, mapping);
165 talloc_free(tmp_ctx);
166 return DRSUAPI_DS_NAME_STATUS_OK;
168 } while (p);
170 DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
171 talloc_free(tmp_ctx);
172 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
175 /* When cracking a ServicePrincipalName, many services may be served
176 * by the host/ servicePrincipalName. The incoming query is for cifs/
177 * but we translate it here, and search on host/. This is done after
178 * the cifs/ entry has been searched for, making this a fallback */
180 static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
181 struct smb_krb5_context *smb_krb5_context,
182 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
183 enum drsuapi_DsNameFormat format_desired,
184 const char *name, struct drsuapi_DsNameInfo1 *info1)
186 WERROR wret;
187 krb5_error_code ret;
188 krb5_principal principal;
189 const char *service, *dns_name;
190 char *new_service;
191 char *new_princ;
192 enum drsuapi_DsNameStatus namestatus;
194 /* parse principal */
195 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
196 name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
197 if (ret) {
198 DEBUG(2, ("Could not parse principal: %s: %s",
199 name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
200 ret, mem_ctx)));
201 return WERR_NOMEM;
204 /* grab cifs/, http/ etc */
206 /* This is checked for in callers, but be safe */
207 if (principal->name.name_string.len < 2) {
208 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
209 return WERR_OK;
211 service = principal->name.name_string.val[0];
212 dns_name = principal->name.name_string.val[1];
214 /* MAP it */
215 namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context,
216 sam_ctx, mem_ctx,
217 service, &new_service);
219 if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
220 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
221 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
222 if (!info1->dns_domain_name) {
223 krb5_free_principal(smb_krb5_context->krb5_context, principal);
224 return WERR_NOMEM;
226 return WERR_OK;
227 } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
228 info1->status = namestatus;
229 krb5_free_principal(smb_krb5_context->krb5_context, principal);
230 return WERR_OK;
233 /* ooh, very nasty playing around in the Principal... */
234 free(principal->name.name_string.val[0]);
235 principal->name.name_string.val[0] = strdup(new_service);
236 if (!principal->name.name_string.val[0]) {
237 krb5_free_principal(smb_krb5_context->krb5_context, principal);
238 return WERR_NOMEM;
241 /* reform principal */
242 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
243 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
245 if (ret) {
246 krb5_free_principal(smb_krb5_context->krb5_context, principal);
247 return WERR_NOMEM;
250 wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
251 new_princ, info1);
252 free(new_princ);
253 if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
254 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
255 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
256 if (!info1->dns_domain_name) {
257 wret = WERR_NOMEM;
260 krb5_free_principal(smb_krb5_context->krb5_context, principal);
261 return wret;
264 /* Subcase of CrackNames, for the userPrincipalName */
266 static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
267 struct smb_krb5_context *smb_krb5_context,
268 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
269 enum drsuapi_DsNameFormat format_desired,
270 const char *name, struct drsuapi_DsNameInfo1 *info1)
272 int ldb_ret;
273 WERROR status;
274 const char *domain_filter = NULL;
275 const char *result_filter = NULL;
276 krb5_error_code ret;
277 krb5_principal principal;
278 const char *realm;
279 char *unparsed_name_short;
280 const char *domain_attrs[] = { NULL };
281 struct ldb_result *domain_res = NULL;
283 /* Prevent recursion */
284 if (!name) {
285 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
286 return WERR_OK;
289 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
290 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
291 if (ret) {
292 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
293 return WERR_OK;
296 realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
298 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
299 samdb_partitions_dn(sam_ctx, mem_ctx),
300 LDB_SCOPE_ONELEVEL,
301 domain_attrs,
302 "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
303 ldb_binary_encode_string(mem_ctx, realm),
304 ldb_binary_encode_string(mem_ctx, realm));
306 if (ldb_ret != LDB_SUCCESS) {
307 DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx)));
308 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
309 return WERR_OK;
312 switch (domain_res->count) {
313 case 1:
314 break;
315 case 0:
316 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
317 name, info1);
318 default:
319 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
320 return WERR_OK;
323 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
324 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
325 krb5_free_principal(smb_krb5_context->krb5_context, principal);
327 if (ret) {
328 free(unparsed_name_short);
329 return WERR_NOMEM;
332 /* This may need to be extended for more userPrincipalName variations */
333 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
334 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
336 domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
338 if (!result_filter || !domain_filter) {
339 free(unparsed_name_short);
340 return WERR_NOMEM;
342 status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
343 smb_krb5_context,
344 format_flags, format_offered, format_desired,
345 NULL, unparsed_name_short, domain_filter, result_filter,
346 info1);
347 free(unparsed_name_short);
349 return status;
352 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
354 WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
355 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
356 enum drsuapi_DsNameFormat format_desired,
357 const char *name, struct drsuapi_DsNameInfo1 *info1)
359 krb5_error_code ret;
360 const char *domain_filter = NULL;
361 const char *result_filter = NULL;
362 struct ldb_dn *name_dn = NULL;
364 struct smb_krb5_context *smb_krb5_context = NULL;
366 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
367 info1->dns_domain_name = NULL;
368 info1->result_name = NULL;
370 if (!name) {
371 return WERR_INVALID_PARAM;
374 /* TODO: - fill the correct names in all cases!
375 * - handle format_flags
378 /* here we need to set the domain_filter and/or the result_filter */
379 switch (format_offered) {
380 case DRSUAPI_DS_NAME_FORMAT_UNKNOWN:
382 unsigned int i;
383 enum drsuapi_DsNameFormat formats[] = {
384 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
385 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL,
386 DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY,
387 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
388 DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
389 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
391 WERROR werr;
392 for (i=0; i < ARRAY_SIZE(formats); i++) {
393 werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1);
394 if (!W_ERROR_IS_OK(werr)) {
395 return werr;
397 if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
398 return werr;
401 return werr;
404 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
405 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
407 char *str, *s, *account;
409 if (strlen(name) == 0) {
410 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
411 return WERR_OK;
414 str = talloc_strdup(mem_ctx, name);
415 W_ERROR_HAVE_NO_MEMORY(str);
417 if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
418 /* Look backwards for the \n, and replace it with / */
419 s = strrchr(str, '\n');
420 if (!s) {
421 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
422 return WERR_OK;
424 s[0] = '/';
427 s = strchr(str, '/');
428 if (!s) {
429 /* there must be at least one / */
430 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
431 return WERR_OK;
434 s[0] = '\0';
435 s++;
437 domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))",
438 ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str)));
439 W_ERROR_HAVE_NO_MEMORY(domain_filter);
441 /* There may not be anything after the domain component (search for the domain itself) */
442 if (s[0]) {
444 account = strrchr(s, '/');
445 if (!account) {
446 account = s;
447 } else {
448 account++;
450 account = ldb_binary_encode_string(mem_ctx, account);
451 W_ERROR_HAVE_NO_MEMORY(account);
452 result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
453 account);
454 W_ERROR_HAVE_NO_MEMORY(result_filter);
456 break;
458 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
459 char *p;
460 char *domain;
461 struct ldb_dn *dn_domain;
462 const char *account = NULL;
464 domain = talloc_strdup(mem_ctx, name);
465 W_ERROR_HAVE_NO_MEMORY(domain);
467 p = strchr(domain, '\\');
468 if (!p) {
469 /* invalid input format */
470 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
471 return WERR_OK;
473 p[0] = '\0';
475 if (p[1]) {
476 account = &p[1];
479 /* it could be in DNS domain form */
480 dn_domain = samdb_dns_domain_to_dn(sam_ctx, mem_ctx, domain);
481 W_ERROR_HAVE_NO_MEMORY(dn_domain);
483 domain_filter = talloc_asprintf(mem_ctx,
484 "(&(&(|(nETBIOSName=%s)(nCName=%s))(objectclass=crossRef))(ncName=*))",
485 ldb_binary_encode_string(mem_ctx, domain),
486 ldb_dn_get_linearized(dn_domain));
487 W_ERROR_HAVE_NO_MEMORY(domain_filter);
488 if (account) {
489 result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
490 ldb_binary_encode_string(mem_ctx, account));
491 W_ERROR_HAVE_NO_MEMORY(result_filter);
494 talloc_free(domain);
495 break;
498 /* A LDAP DN as a string */
499 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
500 domain_filter = NULL;
501 name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
502 if (! ldb_dn_validate(name_dn)) {
503 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
504 return WERR_OK;
506 break;
509 /* A GUID as a string */
510 case DRSUAPI_DS_NAME_FORMAT_GUID: {
511 struct GUID guid;
512 char *ldap_guid;
513 NTSTATUS nt_status;
514 domain_filter = NULL;
516 nt_status = GUID_from_string(name, &guid);
517 if (!NT_STATUS_IS_OK(nt_status)) {
518 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
519 return WERR_OK;
522 ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
523 if (!ldap_guid) {
524 return WERR_NOMEM;
526 result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
527 ldap_guid);
528 W_ERROR_HAVE_NO_MEMORY(result_filter);
529 break;
531 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
532 domain_filter = NULL;
534 result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
535 ldb_binary_encode_string(mem_ctx, name),
536 ldb_binary_encode_string(mem_ctx, name));
537 W_ERROR_HAVE_NO_MEMORY(result_filter);
538 break;
541 /* A S-1234-5678 style string */
542 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
543 struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
544 char *ldap_sid;
546 domain_filter = NULL;
547 if (!sid) {
548 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
549 return WERR_OK;
551 ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
552 sid);
553 if (!ldap_sid) {
554 return WERR_NOMEM;
556 result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
557 ldap_sid);
558 W_ERROR_HAVE_NO_MEMORY(result_filter);
559 break;
561 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
562 krb5_principal principal;
563 char *unparsed_name;
565 ret = smb_krb5_init_context(mem_ctx,
566 ldb_get_event_context(sam_ctx),
567 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
568 &smb_krb5_context);
570 if (ret) {
571 return WERR_NOMEM;
574 /* Ensure we reject compleate junk first */
575 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
576 if (ret) {
577 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
578 return WERR_OK;
581 domain_filter = NULL;
583 /* By getting the unparsed name here, we ensure the escaping is correct (and trust the client less) */
584 ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name);
585 if (ret) {
586 krb5_free_principal(smb_krb5_context->krb5_context, principal);
587 return WERR_NOMEM;
590 krb5_free_principal(smb_krb5_context->krb5_context, principal);
592 /* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */
593 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))",
594 ldb_binary_encode_string(mem_ctx, unparsed_name));
596 free(unparsed_name);
597 W_ERROR_HAVE_NO_MEMORY(result_filter);
598 break;
600 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
601 krb5_principal principal;
602 char *unparsed_name_short;
603 char *service;
605 ret = smb_krb5_init_context(mem_ctx,
606 ldb_get_event_context(sam_ctx),
607 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
608 &smb_krb5_context);
610 if (ret) {
611 return WERR_NOMEM;
614 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
615 if (ret == 0 && principal->name.name_string.len < 2) {
616 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
617 krb5_free_principal(smb_krb5_context->krb5_context, principal);
618 return WERR_OK;
620 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
621 KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
622 if (ret) {
623 krb5_free_principal(smb_krb5_context->krb5_context, principal);
625 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
626 name, info1);
629 domain_filter = NULL;
631 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
632 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
633 if (ret) {
634 krb5_free_principal(smb_krb5_context->krb5_context, principal);
635 return WERR_NOMEM;
638 service = principal->name.name_string.val[0];
639 if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) {
640 /* the 'cn' attribute is just the leading part of the name */
641 char *computer_name;
642 computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1],
643 strcspn(principal->name.name_string.val[1], "."));
644 if (computer_name == NULL) {
645 return WERR_NOMEM;
648 result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
649 ldb_binary_encode_string(mem_ctx, unparsed_name_short),
650 ldb_binary_encode_string(mem_ctx, computer_name));
651 } else {
652 result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
653 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
655 krb5_free_principal(smb_krb5_context->krb5_context, principal);
656 free(unparsed_name_short);
657 W_ERROR_HAVE_NO_MEMORY(result_filter);
659 break;
661 default: {
662 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
663 return WERR_OK;
668 if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
669 return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
670 name_dn, name, info1);
673 return DsCrackNameOneFilter(sam_ctx, mem_ctx,
674 smb_krb5_context,
675 format_flags, format_offered, format_desired,
676 name_dn, name,
677 domain_filter, result_filter,
678 info1);
681 /* Subcase of CrackNames. It is possible to translate a LDAP-style DN
682 * (FQDN_1779) into a canoical name without actually searching the
683 * database */
685 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
686 enum drsuapi_DsNameFormat format_offered,
687 enum drsuapi_DsNameFormat format_desired,
688 struct ldb_dn *name_dn, const char *name,
689 struct drsuapi_DsNameInfo1 *info1)
691 char *cracked;
692 if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
693 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
694 return WERR_OK;
697 switch (format_desired) {
698 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
699 cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
700 break;
701 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
702 cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
703 break;
704 default:
705 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
706 return WERR_OK;
708 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
709 info1->result_name = cracked;
710 if (!cracked) {
711 return WERR_NOMEM;
714 return WERR_OK;
717 /* Given a filter for the domain, and one for the result, perform the
718 * ldb search. The format offered and desired flags change the
719 * behaviours, including what attributes to return.
721 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
724 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
725 struct smb_krb5_context *smb_krb5_context,
726 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
727 enum drsuapi_DsNameFormat format_desired,
728 struct ldb_dn *name_dn, const char *name,
729 const char *domain_filter, const char *result_filter,
730 struct drsuapi_DsNameInfo1 *info1)
732 int ldb_ret;
733 struct ldb_result *domain_res = NULL;
734 const char * const *domain_attrs;
735 const char * const *result_attrs;
736 struct ldb_message **result_res = NULL;
737 struct ldb_message *result = NULL;
738 int i;
739 char *p;
740 struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
742 const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
743 const char * const _result_attrs_null[] = { NULL };
745 const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
746 const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
748 const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
749 const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
751 const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
752 const char * const _result_attrs_guid[] = { "objectGUID", NULL};
754 const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
755 const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
757 const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
758 const char * const _result_attrs_none[] = { NULL};
760 /* here we need to set the attrs lists for domain and result lookups */
761 switch (format_desired) {
762 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
763 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
764 domain_attrs = _domain_attrs_1779;
765 result_attrs = _result_attrs_null;
766 break;
767 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
768 domain_attrs = _domain_attrs_canonical;
769 result_attrs = _result_attrs_canonical;
770 break;
771 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
772 domain_attrs = _domain_attrs_nt4;
773 result_attrs = _result_attrs_nt4;
774 break;
775 case DRSUAPI_DS_NAME_FORMAT_GUID:
776 domain_attrs = _domain_attrs_guid;
777 result_attrs = _result_attrs_guid;
778 break;
779 case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
780 domain_attrs = _domain_attrs_display;
781 result_attrs = _result_attrs_display;
782 break;
783 default:
784 domain_attrs = _domain_attrs_none;
785 result_attrs = _result_attrs_none;
786 break;
789 if (domain_filter) {
790 /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
791 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
792 partitions_basedn,
793 LDB_SCOPE_ONELEVEL,
794 domain_attrs,
795 "%s", domain_filter);
797 if (ldb_ret != LDB_SUCCESS) {
798 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
799 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
800 return WERR_OK;
803 switch (domain_res->count) {
804 case 1:
805 break;
806 case 0:
807 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
808 return WERR_OK;
809 default:
810 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
811 return WERR_OK;
814 info1->dns_domain_name = ldb_msg_find_attr_as_string(domain_res->msgs[0], "dnsRoot", NULL);
815 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
816 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
817 } else {
818 info1->dns_domain_name = NULL;
819 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
822 if (result_filter) {
823 int ret;
824 struct ldb_result *res;
825 uint32_t dsdb_flags = 0;
826 struct ldb_dn *search_dn;
828 if (domain_res) {
829 dsdb_flags = 0;
830 search_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
831 } else {
832 dsdb_flags = DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
833 search_dn = ldb_get_root_basedn(sam_ctx);
836 /* search with the 'phantom root' flag */
837 ret = dsdb_search(sam_ctx, mem_ctx, &res,
838 search_dn,
839 LDB_SCOPE_SUBTREE,
840 result_attrs,
841 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
842 "%s", result_filter);
843 if (ret != LDB_SUCCESS) {
844 DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s",
845 ldb_errstring(sam_ctx)));
846 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
847 return WERR_OK;
850 ldb_ret = res->count;
851 result_res = res->msgs;
852 } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
853 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
854 result_attrs);
855 } else if (domain_res) {
856 name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
857 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
858 result_attrs);
859 } else {
860 /* Can't happen */
861 DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not available: This can't happen..."));
862 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
863 return WERR_OK;
866 switch (ldb_ret) {
867 case 1:
868 result = result_res[0];
869 break;
870 case 0:
871 switch (format_offered) {
872 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
873 return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
874 smb_krb5_context,
875 format_flags, format_offered, format_desired,
876 name, info1);
878 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
879 return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
880 format_flags, format_offered, format_desired,
881 name, info1);
882 default:
883 break;
885 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
886 return WERR_OK;
887 case -1:
888 DEBUG(2, ("DsCrackNameOneFilter result search failed: %s", ldb_errstring(sam_ctx)));
889 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
890 return WERR_OK;
891 default:
892 switch (format_offered) {
893 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
894 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
896 const char *canonical_name = NULL; /* Not required, but we get warnings... */
897 /* We may need to manually filter further */
898 for (i = 0; i < ldb_ret; i++) {
899 switch (format_offered) {
900 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
901 canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
902 break;
903 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
904 canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
905 break;
906 default:
907 break;
909 if (strcasecmp_m(canonical_name, name) == 0) {
910 result = result_res[i];
911 break;
914 if (!result) {
915 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
916 return WERR_OK;
919 default:
920 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
921 return WERR_OK;
925 info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
926 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
927 p = strchr(info1->dns_domain_name, '/');
928 if (p) {
929 p[0] = '\0';
932 /* here we can use result and domain_res[0] */
933 switch (format_desired) {
934 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
935 info1->result_name = ldb_dn_alloc_linearized(mem_ctx, result->dn);
936 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
938 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
939 return WERR_OK;
941 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
942 info1->result_name = ldb_msg_find_attr_as_string(result, "canonicalName", NULL);
943 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
944 return WERR_OK;
946 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
947 /* Not in the virtual ldb attribute */
948 return DsCrackNameOneSyntactical(mem_ctx,
949 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
950 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
951 result->dn, name, info1);
953 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
955 const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
956 const char *_acc = "", *_dom = "";
958 if (samdb_find_attribute(sam_ctx, result, "objectClass", "domain")) {
960 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
961 partitions_basedn,
962 LDB_SCOPE_ONELEVEL,
963 domain_attrs,
964 "(ncName=%s)", ldb_dn_get_linearized(result->dn));
966 if (ldb_ret != LDB_SUCCESS) {
967 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
968 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
969 return WERR_OK;
972 switch (domain_res->count) {
973 case 1:
974 break;
975 case 0:
976 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
977 return WERR_OK;
978 default:
979 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
980 return WERR_OK;
982 _dom = ldb_msg_find_attr_as_string(domain_res->msgs[0], "nETBIOSName", NULL);
983 W_ERROR_HAVE_NO_MEMORY(_dom);
984 } else {
985 _acc = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
986 if (!_acc) {
987 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
988 return WERR_OK;
990 if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) {
991 _dom = "BUILTIN";
992 } else {
993 const char *attrs[] = { NULL };
994 struct ldb_result *domain_res2;
995 struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
996 if (!dom_sid) {
997 return WERR_OK;
999 dom_sid->num_auths--;
1000 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1001 NULL,
1002 LDB_SCOPE_BASE,
1003 attrs,
1004 "(&(objectSid=%s)(objectClass=domain))",
1005 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
1007 if (ldb_ret != LDB_SUCCESS) {
1008 DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s", ldb_errstring(sam_ctx)));
1009 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1010 return WERR_OK;
1013 switch (domain_res->count) {
1014 case 1:
1015 break;
1016 case 0:
1017 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1018 return WERR_OK;
1019 default:
1020 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1021 return WERR_OK;
1024 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2,
1025 partitions_basedn,
1026 LDB_SCOPE_ONELEVEL,
1027 domain_attrs,
1028 "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
1030 if (ldb_ret != LDB_SUCCESS) {
1031 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
1032 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1033 return WERR_OK;
1036 switch (domain_res2->count) {
1037 case 1:
1038 break;
1039 case 0:
1040 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1041 return WERR_OK;
1042 default:
1043 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1044 return WERR_OK;
1046 _dom = ldb_msg_find_attr_as_string(domain_res2->msgs[0], "nETBIOSName", NULL);
1047 W_ERROR_HAVE_NO_MEMORY(_dom);
1051 info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
1052 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1054 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1055 return WERR_OK;
1057 case DRSUAPI_DS_NAME_FORMAT_GUID: {
1058 struct GUID guid;
1060 guid = samdb_result_guid(result, "objectGUID");
1062 info1->result_name = GUID_string2(mem_ctx, &guid);
1063 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1065 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1066 return WERR_OK;
1068 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
1069 info1->result_name = ldb_msg_find_attr_as_string(result, "displayName", NULL);
1070 if (!info1->result_name) {
1071 info1->result_name = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
1073 if (!info1->result_name) {
1074 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1075 } else {
1076 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1078 return WERR_OK;
1080 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
1081 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1082 return WERR_OK;
1084 case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
1085 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
1086 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1087 return WERR_OK;
1089 default:
1090 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1091 return WERR_OK;
1095 /* Given a user Principal Name (such as foo@bar.com),
1096 * return the user and domain DNs. This is used in the KDC to then
1097 * return the Keys and evaluate policy */
1099 NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
1100 TALLOC_CTX *mem_ctx,
1101 const char *user_principal_name,
1102 struct ldb_dn **user_dn,
1103 struct ldb_dn **domain_dn)
1105 WERROR werr;
1106 struct drsuapi_DsNameInfo1 info1;
1107 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1108 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
1109 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1110 user_principal_name,
1111 &info1);
1112 if (!W_ERROR_IS_OK(werr)) {
1113 return werror_to_ntstatus(werr);
1115 switch (info1.status) {
1116 case DRSUAPI_DS_NAME_STATUS_OK:
1117 break;
1118 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1119 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1120 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1121 return NT_STATUS_NO_SUCH_USER;
1122 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1123 default:
1124 return NT_STATUS_UNSUCCESSFUL;
1127 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1129 if (domain_dn) {
1130 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1131 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1132 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1133 talloc_asprintf(mem_ctx, "%s/",
1134 info1.dns_domain_name),
1135 &info1);
1136 if (!W_ERROR_IS_OK(werr)) {
1137 return werror_to_ntstatus(werr);
1139 switch (info1.status) {
1140 case DRSUAPI_DS_NAME_STATUS_OK:
1141 break;
1142 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1143 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1144 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1145 return NT_STATUS_NO_SUCH_USER;
1146 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1147 default:
1148 return NT_STATUS_UNSUCCESSFUL;
1151 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1154 return NT_STATUS_OK;
1157 /* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
1158 * return the user and domain DNs. This is used in the KDC to then
1159 * return the Keys and evaluate policy */
1161 NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
1162 TALLOC_CTX *mem_ctx,
1163 const char *service_principal_name,
1164 struct ldb_dn **user_dn,
1165 struct ldb_dn **domain_dn)
1167 WERROR werr;
1168 struct drsuapi_DsNameInfo1 info1;
1169 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1170 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
1171 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1172 service_principal_name,
1173 &info1);
1174 if (!W_ERROR_IS_OK(werr)) {
1175 return werror_to_ntstatus(werr);
1177 switch (info1.status) {
1178 case DRSUAPI_DS_NAME_STATUS_OK:
1179 break;
1180 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1181 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1182 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1183 return NT_STATUS_NO_SUCH_USER;
1184 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1185 default:
1186 return NT_STATUS_UNSUCCESSFUL;
1189 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1191 if (domain_dn) {
1192 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1193 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1194 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1195 talloc_asprintf(mem_ctx, "%s/",
1196 info1.dns_domain_name),
1197 &info1);
1198 if (!W_ERROR_IS_OK(werr)) {
1199 return werror_to_ntstatus(werr);
1201 switch (info1.status) {
1202 case DRSUAPI_DS_NAME_STATUS_OK:
1203 break;
1204 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1205 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1206 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1207 return NT_STATUS_NO_SUCH_USER;
1208 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1209 default:
1210 return NT_STATUS_UNSUCCESSFUL;
1213 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1216 return NT_STATUS_OK;
1219 NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1220 struct tevent_context *ev_ctx,
1221 struct loadparm_context *lp_ctx,
1222 enum drsuapi_DsNameFormat format_offered,
1223 const char *name,
1224 const char **nt4_domain, const char **nt4_account)
1226 WERROR werr;
1227 struct drsuapi_DsNameInfo1 info1;
1228 struct ldb_context *ldb;
1229 char *p;
1231 /* Handle anonymous bind */
1232 if (!name || !*name) {
1233 *nt4_domain = "";
1234 *nt4_account = "";
1235 return NT_STATUS_OK;
1238 ldb = samdb_connect(mem_ctx, ev_ctx, lp_ctx, system_session(lp_ctx), 0);
1239 if (ldb == NULL) {
1240 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1243 werr = DsCrackNameOneName(ldb, mem_ctx, 0,
1244 format_offered,
1245 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
1246 name,
1247 &info1);
1248 if (!W_ERROR_IS_OK(werr)) {
1249 return werror_to_ntstatus(werr);
1251 switch (info1.status) {
1252 case DRSUAPI_DS_NAME_STATUS_OK:
1253 break;
1254 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1255 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1256 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1257 return NT_STATUS_NO_SUCH_USER;
1258 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1259 default:
1260 return NT_STATUS_UNSUCCESSFUL;
1263 *nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
1264 if (*nt4_domain == NULL) {
1265 return NT_STATUS_NO_MEMORY;
1268 p = strchr(*nt4_domain, '\\');
1269 if (!p) {
1270 return NT_STATUS_INVALID_PARAMETER;
1272 p[0] = '\0';
1274 *nt4_account = talloc_strdup(mem_ctx, &p[1]);
1275 if (*nt4_account == NULL) {
1276 return NT_STATUS_NO_MEMORY;
1279 return NT_STATUS_OK;
1282 NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1283 struct tevent_context *ev_ctx,
1284 struct loadparm_context *lp_ctx,
1285 const char *name,
1286 const char **nt4_domain,
1287 const char **nt4_account)
1289 enum drsuapi_DsNameFormat format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN;
1291 /* Handle anonymous bind */
1292 if (!name || !*name) {
1293 *nt4_domain = "";
1294 *nt4_account = "";
1295 return NT_STATUS_OK;
1298 if (strchr_m(name, '=')) {
1299 format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
1300 } else if (strchr_m(name, '@')) {
1301 format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
1302 } else if (strchr_m(name, '\\')) {
1303 format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
1304 } else if (strchr_m(name, '/')) {
1305 format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
1306 } else {
1307 return NT_STATUS_NO_SUCH_USER;
1310 return crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, format_offered, name, nt4_domain, nt4_account);
1314 WERROR dcesrv_drsuapi_ListRoles(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1315 const struct drsuapi_DsNameRequest1 *req1,
1316 struct drsuapi_DsNameCtr1 **ctr1)
1318 struct drsuapi_DsNameInfo1 *names;
1319 uint32_t i;
1320 uint32_t count = 5;/*number of fsmo role owners we are going to return*/
1322 *ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
1323 W_ERROR_HAVE_NO_MEMORY(*ctr1);
1324 names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1325 W_ERROR_HAVE_NO_MEMORY(names);
1327 for (i = 0; i < count; i++) {
1328 WERROR werr;
1329 struct ldb_dn *role_owner_dn, *fsmo_role_dn, *server_dn;
1330 werr = dsdb_get_fsmo_role_info(mem_ctx, sam_ctx, i,
1331 &fsmo_role_dn, &role_owner_dn);
1332 if(!W_ERROR_IS_OK(werr)) {
1333 return werr;
1335 server_dn = ldb_dn_copy(mem_ctx, role_owner_dn);
1336 ldb_dn_remove_child_components(server_dn, 1);
1337 names[i].status = DRSUAPI_DS_NAME_STATUS_OK;
1338 names[i].dns_domain_name = samdb_dn_to_dnshostname(sam_ctx, mem_ctx,
1339 server_dn);
1340 if(!names[i].dns_domain_name) {
1341 DEBUG(4, ("list_roles: Failed to find dNSHostName for server %s",
1342 ldb_dn_get_linearized(server_dn)));
1344 names[i].result_name = talloc_strdup(mem_ctx, ldb_dn_get_linearized(role_owner_dn));
1347 (*ctr1)->count = count;
1348 (*ctr1)->array = names;
1350 return WERR_OK;
1353 WERROR dcesrv_drsuapi_CrackNamesByNameFormat(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1354 const struct drsuapi_DsNameRequest1 *req1,
1355 struct drsuapi_DsNameCtr1 **ctr1)
1357 struct drsuapi_DsNameInfo1 *names;
1358 uint32_t i, count;
1359 WERROR status;
1361 *ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
1362 W_ERROR_HAVE_NO_MEMORY(*ctr1);
1364 count = req1->count;
1365 names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1366 W_ERROR_HAVE_NO_MEMORY(names);
1368 for (i=0; i < count; i++) {
1369 status = DsCrackNameOneName(sam_ctx, mem_ctx,
1370 req1->format_flags,
1371 req1->format_offered,
1372 req1->format_desired,
1373 req1->names[i].str,
1374 &names[i]);
1375 if (!W_ERROR_IS_OK(status)) {
1376 return status;
1380 (*ctr1)->count = count;
1381 (*ctr1)->array = names;
1383 return WERR_OK;