s3:param: Fix old-style function definition
[Samba.git] / source4 / dsdb / samdb / cracknames.c
blob4852a0ef9bd200d528c23d3f7d7e1676cb985196
1 /*
2 Unix SMB/CIFS implementation.
4 crachnames implementation for the drsuapi pipe
5 DsCrackNames()
7 Copyright (C) Stefan Metzmacher 2004
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
9 Copyright (C) Matthieu Patou <mat@matws.net> 2012
10 Copyright (C) Catalyst .Net Ltd 2017
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "librpc/gen_ndr/drsuapi.h"
28 #include "lib/events/events.h"
29 #include <ldb.h>
30 #include <ldb_errors.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 #undef strcasecmp
42 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
43 struct smb_krb5_context *smb_krb5_context,
44 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
45 enum drsuapi_DsNameFormat format_desired,
46 struct ldb_dn *name_dn, const char *name,
47 const char *domain_filter, const char *result_filter,
48 struct drsuapi_DsNameInfo1 *info1, int scope, struct ldb_dn *search_dn);
49 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
50 enum drsuapi_DsNameFormat format_offered,
51 enum drsuapi_DsNameFormat format_desired,
52 struct ldb_dn *name_dn, const char *name,
53 struct drsuapi_DsNameInfo1 *info1);
55 static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context,
56 const char *name,
57 struct drsuapi_DsNameInfo1 *info1)
59 krb5_error_code ret;
60 krb5_principal principal;
61 /* perhaps it's a principal with a realm, so return the right 'domain only' response */
62 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
63 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
64 if (ret) {
65 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
66 return WERR_OK;
69 info1->dns_domain_name = smb_krb5_principal_get_realm(
70 mem_ctx, smb_krb5_context->krb5_context, principal);
71 krb5_free_principal(smb_krb5_context->krb5_context, principal);
73 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
75 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
76 return WERR_OK;
79 static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(struct ldb_context *ldb_ctx,
80 TALLOC_CTX *mem_ctx,
81 const char *alias_from,
82 char **alias_to)
85 * Some of the logic of this function is mirrored in find_spn_alias()
86 * in source4/dsdb.samdb/ldb_modules/samldb.c. If you change this to
87 * not return the first matched alias, you will need to rethink that
88 * function too.
90 unsigned int i;
91 int ret;
92 struct ldb_result *res;
93 struct ldb_message_element *spnmappings;
94 TALLOC_CTX *tmp_ctx;
95 struct ldb_dn *service_dn;
96 char *service_dn_str;
98 const char *directory_attrs[] = {
99 "sPNMappings",
100 NULL
103 tmp_ctx = talloc_new(mem_ctx);
104 if (!tmp_ctx) {
105 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
108 service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
109 if ( ! ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb_ctx))) {
110 talloc_free(tmp_ctx);
111 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
113 service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
114 if ( ! service_dn_str) {
115 talloc_free(tmp_ctx);
116 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
119 ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
120 directory_attrs, "(objectClass=nTDSService)");
122 if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
123 DEBUG(1, ("ldb_search: dn: %s not found: %s\n", service_dn_str, ldb_errstring(ldb_ctx)));
124 talloc_free(tmp_ctx);
125 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
126 } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
127 DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
128 talloc_free(tmp_ctx);
129 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
130 } else if (res->count != 1) {
131 DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
132 talloc_free(tmp_ctx);
133 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
136 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
137 if (!spnmappings || spnmappings->num_values == 0) {
138 DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute\n", service_dn_str));
139 talloc_free(tmp_ctx);
140 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
143 for (i = 0; i < spnmappings->num_values; i++) {
144 char *mapping, *p, *str;
145 mapping = talloc_strdup(tmp_ctx,
146 (const char *)spnmappings->values[i].data);
147 if (!mapping) {
148 DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
149 talloc_free(tmp_ctx);
150 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
153 /* C string manipulation sucks */
155 p = strchr(mapping, '=');
156 if (!p) {
157 DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
158 service_dn_str, mapping));
159 talloc_free(tmp_ctx);
160 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
162 p[0] = '\0';
163 p++;
164 do {
165 str = p;
166 p = strchr(p, ',');
167 if (p) {
168 p[0] = '\0';
169 p++;
171 if (strcasecmp(str, alias_from) == 0) {
172 *alias_to = mapping;
173 talloc_steal(mem_ctx, mapping);
174 talloc_free(tmp_ctx);
175 return DRSUAPI_DS_NAME_STATUS_OK;
177 } while (p);
179 DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
180 talloc_free(tmp_ctx);
181 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
184 /* When cracking a ServicePrincipalName, many services may be served
185 * by the host/ servicePrincipalName. The incoming query is for cifs/
186 * but we translate it here, and search on host/. This is done after
187 * the cifs/ entry has been searched for, making this a fallback */
189 static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
190 struct smb_krb5_context *smb_krb5_context,
191 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
192 enum drsuapi_DsNameFormat format_desired,
193 const char *name, struct drsuapi_DsNameInfo1 *info1)
195 WERROR wret;
196 krb5_error_code ret;
197 krb5_principal principal;
198 const krb5_data *component;
199 const char *service, *dns_name;
200 char *new_service;
201 char *new_princ;
202 enum drsuapi_DsNameStatus namestatus;
204 /* parse principal */
205 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
206 name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
207 if (ret) {
208 DEBUG(2, ("Could not parse principal: %s: %s\n",
209 name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
210 ret, mem_ctx)));
211 return WERR_NOT_ENOUGH_MEMORY;
214 /* grab cifs/, http/ etc */
216 /* This is checked for in callers, but be safe */
217 if (krb5_princ_size(smb_krb5_context->krb5_context, principal) < 2) {
218 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
219 krb5_free_principal(smb_krb5_context->krb5_context, principal);
220 return WERR_OK;
222 component = krb5_princ_component(smb_krb5_context->krb5_context,
223 principal, 0);
224 service = (const char *)component->data;
225 component = krb5_princ_component(smb_krb5_context->krb5_context,
226 principal, 1);
227 dns_name = (const char *)component->data;
229 /* MAP it */
230 namestatus = LDB_lookup_spn_alias(sam_ctx, mem_ctx,
231 service, &new_service);
233 if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
234 wret = WERR_OK;
235 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
236 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
237 if (!info1->dns_domain_name) {
238 wret = WERR_NOT_ENOUGH_MEMORY;
240 krb5_free_principal(smb_krb5_context->krb5_context, principal);
241 return wret;
242 } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
243 info1->status = namestatus;
244 krb5_free_principal(smb_krb5_context->krb5_context, principal);
245 return WERR_OK;
248 /* reform principal */
249 new_princ = talloc_asprintf(mem_ctx, "%s/%s", new_service, dns_name);
250 if (!new_princ) {
251 krb5_free_principal(smb_krb5_context->krb5_context, principal);
252 return WERR_NOT_ENOUGH_MEMORY;
255 wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
256 new_princ, info1);
257 talloc_free(new_princ);
258 if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
259 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
260 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
261 if (!info1->dns_domain_name) {
262 wret = WERR_NOT_ENOUGH_MEMORY;
265 krb5_free_principal(smb_krb5_context->krb5_context, principal);
266 return wret;
269 /* Subcase of CrackNames, for the userPrincipalName */
271 static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
272 struct smb_krb5_context *smb_krb5_context,
273 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
274 enum drsuapi_DsNameFormat format_desired,
275 const char *name, struct drsuapi_DsNameInfo1 *info1)
277 int ldb_ret;
278 WERROR status;
279 const char *domain_filter = NULL;
280 const char *result_filter = NULL;
281 krb5_error_code ret;
282 krb5_principal principal;
283 char *realm;
284 char *unparsed_name_short;
285 const char *domain_attrs[] = { NULL };
286 struct ldb_result *domain_res = NULL;
288 /* Prevent recursion */
289 if (!name) {
290 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
291 return WERR_OK;
294 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
295 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
296 if (ret) {
297 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
298 return WERR_OK;
301 realm = smb_krb5_principal_get_realm(
302 mem_ctx, smb_krb5_context->krb5_context, principal);
304 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
305 samdb_partitions_dn(sam_ctx, mem_ctx),
306 LDB_SCOPE_ONELEVEL,
307 domain_attrs,
308 "(&(objectClass=crossRef)(|(dnsRoot=%s)(netbiosName=%s))(systemFlags:%s:=%u))",
309 ldb_binary_encode_string(mem_ctx, realm),
310 ldb_binary_encode_string(mem_ctx, realm),
311 LDB_OID_COMPARATOR_AND,
312 SYSTEM_FLAG_CR_NTDS_DOMAIN);
313 TALLOC_FREE(realm);
315 if (ldb_ret != LDB_SUCCESS) {
316 DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
317 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
318 krb5_free_principal(smb_krb5_context->krb5_context, principal);
319 return WERR_OK;
322 switch (domain_res->count) {
323 case 1:
324 break;
325 case 0:
326 krb5_free_principal(smb_krb5_context->krb5_context, principal);
327 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
328 name, info1);
329 default:
330 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
331 krb5_free_principal(smb_krb5_context->krb5_context, principal);
332 return WERR_OK;
336 * The important thing here is that a samAccountName may have
337 * a space in it, and this must not be kerberos escaped to
338 * match this filter, so we specify
339 * KRB5_PRINCIPAL_UNPARSE_DISPLAY
341 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
342 KRB5_PRINCIPAL_UNPARSE_NO_REALM |
343 KRB5_PRINCIPAL_UNPARSE_DISPLAY,
344 &unparsed_name_short);
345 krb5_free_principal(smb_krb5_context->krb5_context, principal);
347 if (ret) {
348 free(unparsed_name_short);
349 return WERR_NOT_ENOUGH_MEMORY;
352 /* This may need to be extended for more userPrincipalName variations */
353 result_filter = talloc_asprintf(mem_ctx, "(&(samAccountName=%s)(objectClass=user))",
354 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
356 domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
358 if (!result_filter || !domain_filter) {
359 free(unparsed_name_short);
360 return WERR_NOT_ENOUGH_MEMORY;
362 status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
363 smb_krb5_context,
364 format_flags, format_offered, format_desired,
365 NULL, unparsed_name_short, domain_filter, result_filter,
366 info1, LDB_SCOPE_SUBTREE, NULL);
367 free(unparsed_name_short);
369 return status;
373 * This function will workout the filtering parameter in order to be able to do
374 * the adapted search when the incomming format is format_functional.
375 * This boils down to defining the search_dn (passed as pointer to ldb_dn *) and the
376 * ldap filter request.
377 * Main input parameters are:
378 * * name, which is the portion of the functional name after the
379 * first '/'.
380 * * domain_filter, which is a ldap search filter used to find the NC DN given the
381 * function name to crack.
383 static WERROR get_format_functional_filtering_param(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
384 char *name, struct drsuapi_DsNameInfo1 *info1,
385 struct ldb_dn **psearch_dn, const char *domain_filter, const char **presult_filter)
387 struct ldb_result *domain_res = NULL;
388 const char * const domain_attrs[] = {"ncName", NULL};
389 struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
390 int ldb_ret;
391 char *account, *s, *result_filter = NULL;
392 struct ldb_dn *search_dn = NULL;
394 *psearch_dn = NULL;
395 *presult_filter = NULL;
397 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
398 partitions_basedn,
399 LDB_SCOPE_ONELEVEL,
400 domain_attrs,
401 "%s", domain_filter);
403 if (ldb_ret != LDB_SUCCESS) {
404 DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
405 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
406 return WERR_FOOBAR;
409 if (domain_res->count == 1) {
410 struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
411 const char * const name_attrs[] = {"name", NULL};
413 account = name;
414 s = strchr(account, '/');
415 talloc_free(domain_res);
416 while(s) {
417 s[0] = '\0';
418 s++;
420 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
421 tmp_dn,
422 LDB_SCOPE_ONELEVEL,
423 name_attrs,
424 "name=%s", account);
426 if (ldb_ret != LDB_SUCCESS) {
427 DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
428 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
429 return WERR_OK;
431 talloc_free(tmp_dn);
432 switch (domain_res->count) {
433 case 1:
434 break;
435 case 0:
436 talloc_free(domain_res);
437 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
438 return WERR_OK;
439 default:
440 talloc_free(domain_res);
441 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
442 return WERR_OK;
445 tmp_dn = talloc_steal(mem_ctx, domain_res->msgs[0]->dn);
446 talloc_free(domain_res);
447 search_dn = tmp_dn;
448 account = s;
449 s = strchr(account, '/');
451 account = ldb_binary_encode_string(mem_ctx, account);
452 W_ERROR_HAVE_NO_MEMORY(account);
453 result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
454 account);
455 W_ERROR_HAVE_NO_MEMORY(result_filter);
457 *psearch_dn = search_dn;
458 *presult_filter = result_filter;
459 return WERR_OK;
462 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
464 WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
465 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
466 enum drsuapi_DsNameFormat format_desired,
467 const char *name, struct drsuapi_DsNameInfo1 *info1)
469 krb5_error_code ret;
470 const char *domain_filter = NULL;
471 const char *result_filter = NULL;
472 struct ldb_dn *name_dn = NULL;
473 struct ldb_dn *search_dn = NULL;
475 struct smb_krb5_context *smb_krb5_context = NULL;
476 int scope = LDB_SCOPE_SUBTREE;
478 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
479 info1->dns_domain_name = NULL;
480 info1->result_name = NULL;
482 if (!name) {
483 return WERR_INVALID_PARAMETER;
486 /* TODO: - fill the correct names in all cases!
487 * - handle format_flags
489 if (format_desired == DRSUAPI_DS_NAME_FORMAT_UNKNOWN) {
490 return WERR_OK;
492 /* here we need to set the domain_filter and/or the result_filter */
493 switch (format_offered) {
494 case DRSUAPI_DS_NAME_FORMAT_UNKNOWN:
496 unsigned int i;
497 enum drsuapi_DsNameFormat formats[] = {
498 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
499 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL,
500 DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY,
501 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
502 DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
503 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
505 WERROR werr;
506 for (i=0; i < ARRAY_SIZE(formats); i++) {
507 werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1);
508 if (!W_ERROR_IS_OK(werr)) {
509 return werr;
511 if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND &&
512 (formats[i] != DRSUAPI_DS_NAME_FORMAT_CANONICAL ||
513 info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR))
515 return werr;
518 return werr;
521 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
522 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
524 char *str, *s, *account;
525 scope = LDB_SCOPE_ONELEVEL;
527 if (strlen(name) == 0) {
528 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
529 return WERR_OK;
532 str = talloc_strdup(mem_ctx, name);
533 W_ERROR_HAVE_NO_MEMORY(str);
535 if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
536 /* Look backwards for the \n, and replace it with / */
537 s = strrchr(str, '\n');
538 if (!s) {
539 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
540 return WERR_OK;
542 s[0] = '/';
545 s = strchr(str, '/');
546 if (!s) {
547 /* there must be at least one / */
548 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
549 return WERR_OK;
552 s[0] = '\0';
553 s++;
555 domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))",
556 ldb_binary_encode_string(mem_ctx, str),
557 LDB_OID_COMPARATOR_AND,
558 SYSTEM_FLAG_CR_NTDS_DOMAIN);
559 W_ERROR_HAVE_NO_MEMORY(domain_filter);
561 /* There may not be anything after the domain component (search for the domain itself) */
562 account = s;
563 if (account && *account) {
564 WERROR werr = get_format_functional_filtering_param(sam_ctx,
565 mem_ctx,
566 account,
567 info1,
568 &search_dn,
569 domain_filter,
570 &result_filter);
571 if (!W_ERROR_IS_OK(werr)) {
572 return werr;
574 if (info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR)
575 return WERR_OK;
577 break;
579 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
580 char *p;
581 char *domain;
582 const char *account = NULL;
584 domain = talloc_strdup(mem_ctx, name);
585 W_ERROR_HAVE_NO_MEMORY(domain);
587 p = strchr(domain, '\\');
588 if (!p) {
589 /* invalid input format */
590 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
591 return WERR_OK;
593 p[0] = '\0';
595 if (p[1]) {
596 account = &p[1];
599 domain_filter = talloc_asprintf(mem_ctx,
600 "(&(objectClass=crossRef)(netbiosName=%s)(systemFlags:%s:=%u))",
601 ldb_binary_encode_string(mem_ctx, domain),
602 LDB_OID_COMPARATOR_AND,
603 SYSTEM_FLAG_CR_NTDS_DOMAIN);
604 W_ERROR_HAVE_NO_MEMORY(domain_filter);
605 if (account) {
606 result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
607 ldb_binary_encode_string(mem_ctx, account));
608 W_ERROR_HAVE_NO_MEMORY(result_filter);
611 talloc_free(domain);
612 break;
615 /* A LDAP DN as a string */
616 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
617 domain_filter = NULL;
618 name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
619 if (! ldb_dn_validate(name_dn)) {
620 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
621 return WERR_OK;
623 break;
626 /* A GUID as a string */
627 case DRSUAPI_DS_NAME_FORMAT_GUID: {
628 struct GUID guid;
629 char *ldap_guid;
630 NTSTATUS nt_status;
631 domain_filter = NULL;
633 nt_status = GUID_from_string(name, &guid);
634 if (!NT_STATUS_IS_OK(nt_status)) {
635 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
636 return WERR_OK;
639 ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
640 if (!ldap_guid) {
641 return WERR_NOT_ENOUGH_MEMORY;
643 result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
644 ldap_guid);
645 W_ERROR_HAVE_NO_MEMORY(result_filter);
646 break;
648 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
649 domain_filter = NULL;
651 result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
652 ldb_binary_encode_string(mem_ctx, name),
653 ldb_binary_encode_string(mem_ctx, name));
654 W_ERROR_HAVE_NO_MEMORY(result_filter);
655 break;
658 /* A S-1234-5678 style string */
659 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
660 struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
661 char *ldap_sid;
663 domain_filter = NULL;
664 if (!sid) {
665 info1->dns_domain_name = NULL;
666 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
667 return WERR_OK;
669 ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
670 sid);
671 if (!ldap_sid) {
672 return WERR_NOT_ENOUGH_MEMORY;
674 result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
675 ldap_sid);
676 W_ERROR_HAVE_NO_MEMORY(result_filter);
677 break;
679 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
680 krb5_principal principal;
681 char *unparsed_name;
683 ret = smb_krb5_init_context(mem_ctx,
684 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
685 &smb_krb5_context);
687 if (ret) {
688 return WERR_NOT_ENOUGH_MEMORY;
691 /* Ensure we reject complete junk first */
692 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
693 if (ret) {
694 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
695 return WERR_OK;
698 domain_filter = NULL;
701 * By getting the unparsed name here, we ensure the
702 * escaping is removed correctly (and trust the client
703 * less). The important thing here is that a
704 * userPrincipalName may have a space in it, and this
705 * must not be kerberos escaped to match this filter,
706 * so we specify KRB5_PRINCIPAL_UNPARSE_DISPLAY
708 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context,
709 principal,
710 KRB5_PRINCIPAL_UNPARSE_DISPLAY,
711 &unparsed_name);
712 if (ret) {
713 krb5_free_principal(smb_krb5_context->krb5_context, principal);
714 return WERR_NOT_ENOUGH_MEMORY;
717 krb5_free_principal(smb_krb5_context->krb5_context, principal);
719 /* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */
720 result_filter = talloc_asprintf(mem_ctx, "(&(userPrincipalName=%s)(objectClass=user))",
721 ldb_binary_encode_string(mem_ctx, unparsed_name));
723 free(unparsed_name);
724 W_ERROR_HAVE_NO_MEMORY(result_filter);
725 break;
727 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
728 krb5_principal principal;
729 char *unparsed_name_short;
730 const krb5_data *component;
731 char *service;
733 ret = smb_krb5_init_context(mem_ctx,
734 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
735 &smb_krb5_context);
737 if (ret) {
738 return WERR_NOT_ENOUGH_MEMORY;
741 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
742 if (ret == 0 &&
743 krb5_princ_size(smb_krb5_context->krb5_context,
744 principal) < 2) {
745 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
746 krb5_free_principal(smb_krb5_context->krb5_context, principal);
747 return WERR_OK;
748 } else if (ret == 0) {
749 krb5_free_principal(smb_krb5_context->krb5_context, principal);
751 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
752 KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
753 if (ret) {
754 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
755 name, info1);
758 domain_filter = NULL;
760 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
761 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
762 if (ret) {
763 krb5_free_principal(smb_krb5_context->krb5_context, principal);
764 return WERR_NOT_ENOUGH_MEMORY;
767 component = krb5_princ_component(smb_krb5_context->krb5_context,
768 principal, 0);
769 service = (char *)component->data;
770 if ((krb5_princ_size(smb_krb5_context->krb5_context,
771 principal) == 2) &&
772 (strcasecmp(service, "host") == 0)) {
773 /* the 'cn' attribute is just the leading part of the name */
774 char *computer_name;
775 component = krb5_princ_component(
776 smb_krb5_context->krb5_context,
777 principal, 1);
778 computer_name = talloc_strndup(mem_ctx, (char *)component->data,
779 strcspn((char *)component->data, "."));
780 if (computer_name == NULL) {
781 krb5_free_principal(smb_krb5_context->krb5_context, principal);
782 free(unparsed_name_short);
783 return WERR_NOT_ENOUGH_MEMORY;
786 result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
787 ldb_binary_encode_string(mem_ctx, unparsed_name_short),
788 ldb_binary_encode_string(mem_ctx, computer_name));
789 } else {
790 result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
791 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
793 krb5_free_principal(smb_krb5_context->krb5_context, principal);
794 free(unparsed_name_short);
795 W_ERROR_HAVE_NO_MEMORY(result_filter);
797 break;
799 default: {
800 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
801 return WERR_OK;
805 if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
806 return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
807 name_dn, name, info1);
810 return DsCrackNameOneFilter(sam_ctx, mem_ctx,
811 smb_krb5_context,
812 format_flags, format_offered, format_desired,
813 name_dn, name,
814 domain_filter, result_filter,
815 info1, scope, search_dn);
818 /* Subcase of CrackNames. It is possible to translate a LDAP-style DN
819 * (FQDN_1779) into a canoical name without actually searching the
820 * database */
822 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
823 enum drsuapi_DsNameFormat format_offered,
824 enum drsuapi_DsNameFormat format_desired,
825 struct ldb_dn *name_dn, const char *name,
826 struct drsuapi_DsNameInfo1 *info1)
828 char *cracked;
829 if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
830 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
831 return WERR_OK;
834 switch (format_desired) {
835 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
836 cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
837 break;
838 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
839 cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
840 break;
841 default:
842 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
843 return WERR_OK;
845 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
846 info1->result_name = cracked;
847 if (!cracked) {
848 return WERR_NOT_ENOUGH_MEMORY;
851 return WERR_OK;
854 /* Given a filter for the domain, and one for the result, perform the
855 * ldb search. The format offered and desired flags change the
856 * behaviours, including what attributes to return.
858 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
861 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
862 struct smb_krb5_context *smb_krb5_context,
863 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
864 enum drsuapi_DsNameFormat format_desired,
865 struct ldb_dn *name_dn, const char *name,
866 const char *domain_filter, const char *result_filter,
867 struct drsuapi_DsNameInfo1 *info1,
868 int scope, struct ldb_dn *search_dn)
870 int ldb_ret;
871 struct ldb_result *domain_res = NULL;
872 const char * const *domain_attrs;
873 const char * const *result_attrs;
874 struct ldb_message **result_res = NULL;
875 struct ldb_message *result = NULL;
876 int i;
877 char *p;
878 struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
880 const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
881 const char * const _result_attrs_null[] = { NULL };
883 const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
884 const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
886 const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
887 const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
889 const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
890 const char * const _result_attrs_guid[] = { "objectGUID", NULL};
892 const char * const _domain_attrs_upn[] = { "ncName", "dnsRoot", NULL};
893 const char * const _result_attrs_upn[] = { "userPrincipalName", NULL};
895 const char * const _domain_attrs_spn[] = { "ncName", "dnsRoot", NULL};
896 const char * const _result_attrs_spn[] = { "servicePrincipalName", NULL};
898 const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
899 const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
901 const char * const _domain_attrs_sid[] = { "ncName", "dnsRoot", NULL};
902 const char * const _result_attrs_sid[] = { "objectSid", NULL};
904 const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
905 const char * const _result_attrs_none[] = { NULL};
907 /* here we need to set the attrs lists for domain and result lookups */
908 switch (format_desired) {
909 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
910 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
911 domain_attrs = _domain_attrs_1779;
912 result_attrs = _result_attrs_null;
913 break;
914 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
915 domain_attrs = _domain_attrs_canonical;
916 result_attrs = _result_attrs_canonical;
917 break;
918 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
919 domain_attrs = _domain_attrs_nt4;
920 result_attrs = _result_attrs_nt4;
921 break;
922 case DRSUAPI_DS_NAME_FORMAT_GUID:
923 domain_attrs = _domain_attrs_guid;
924 result_attrs = _result_attrs_guid;
925 break;
926 case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
927 domain_attrs = _domain_attrs_display;
928 result_attrs = _result_attrs_display;
929 break;
930 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
931 domain_attrs = _domain_attrs_upn;
932 result_attrs = _result_attrs_upn;
933 break;
934 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
935 domain_attrs = _domain_attrs_spn;
936 result_attrs = _result_attrs_spn;
937 break;
938 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY:
939 domain_attrs = _domain_attrs_sid;
940 result_attrs = _result_attrs_sid;
941 break;
942 default:
943 domain_attrs = _domain_attrs_none;
944 result_attrs = _result_attrs_none;
945 break;
948 if (domain_filter) {
949 /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
950 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
951 partitions_basedn,
952 LDB_SCOPE_ONELEVEL,
953 domain_attrs,
954 "%s", domain_filter);
956 if (ldb_ret != LDB_SUCCESS) {
957 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
958 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
959 return WERR_OK;
962 switch (domain_res->count) {
963 case 1:
964 break;
965 case 0:
966 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
967 return WERR_OK;
968 default:
969 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
970 return WERR_OK;
973 info1->dns_domain_name = ldb_msg_find_attr_as_string(domain_res->msgs[0], "dnsRoot", NULL);
974 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
975 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
976 } else {
977 info1->dns_domain_name = NULL;
978 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
981 if (result_filter) {
982 int ret;
983 struct ldb_result *res;
984 uint32_t dsdb_flags = 0;
985 struct ldb_dn *real_search_dn = NULL;
986 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
989 * From 4.1.4.2.11 of MS-DRSR
990 * if DS_NAME_FLAG_GCVERIFY in flags then
991 * rt := select all O from all
992 * where attrValue in GetAttrVals(O, att, false)
993 * else
994 * rt := select all O from subtree DefaultNC()
995 * where attrValue in GetAttrVals(O, att, false)
996 * endif
997 * return rt
999 if (format_flags & DRSUAPI_DS_NAME_FLAG_GCVERIFY ||
1000 format_offered == DRSUAPI_DS_NAME_FORMAT_GUID)
1002 dsdb_flags = DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
1003 } else if (domain_res) {
1004 if (!search_dn) {
1005 struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
1006 real_search_dn = tmp_dn;
1007 } else {
1008 real_search_dn = search_dn;
1010 } else {
1011 real_search_dn = ldb_get_default_basedn(sam_ctx);
1013 if (format_offered == DRSUAPI_DS_NAME_FORMAT_GUID){
1014 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
1016 /* search with the 'phantom root' flag */
1017 ret = dsdb_search(sam_ctx, mem_ctx, &res,
1018 real_search_dn,
1019 scope,
1020 result_attrs,
1021 dsdb_flags,
1022 "%s", result_filter);
1023 if (ret != LDB_SUCCESS) {
1024 DEBUG(2, ("DsCrackNameOneFilter search from '%s' with flags 0x%08x failed: %s\n",
1025 ldb_dn_get_linearized(real_search_dn),
1026 dsdb_flags,
1027 ldb_errstring(sam_ctx)));
1028 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1029 return WERR_OK;
1032 ldb_ret = res->count;
1033 result_res = res->msgs;
1034 } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
1035 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
1036 result_attrs);
1037 } else if (domain_res) {
1038 name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
1039 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
1040 result_attrs);
1041 } else {
1042 /* Can't happen */
1043 DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not available: This can't happen...\n"));
1044 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1045 return WERR_OK;
1048 switch (ldb_ret) {
1049 case 1:
1050 result = result_res[0];
1051 break;
1052 case 0:
1053 switch (format_offered) {
1054 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
1055 return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
1056 smb_krb5_context,
1057 format_flags, format_offered, format_desired,
1058 name, info1);
1060 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
1061 return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
1062 format_flags, format_offered, format_desired,
1063 name, info1);
1064 default:
1065 break;
1067 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1068 return WERR_OK;
1069 case -1:
1070 DEBUG(2, ("DsCrackNameOneFilter result search failed: %s\n", ldb_errstring(sam_ctx)));
1071 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1072 return WERR_OK;
1073 default:
1074 switch (format_offered) {
1075 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
1076 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
1078 const char *canonical_name = NULL; /* Not required, but we get warnings... */
1079 /* We may need to manually filter further */
1080 for (i = 0; i < ldb_ret; i++) {
1081 switch (format_offered) {
1082 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
1083 canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
1084 break;
1085 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
1086 canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
1087 break;
1088 default:
1089 break;
1091 if (strcasecmp_m(canonical_name, name) == 0) {
1092 result = result_res[i];
1093 break;
1096 if (!result) {
1097 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1098 return WERR_OK;
1101 FALL_THROUGH;
1102 default:
1103 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1104 return WERR_OK;
1108 info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
1109 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
1110 p = strchr(info1->dns_domain_name, '/');
1111 if (p) {
1112 p[0] = '\0';
1115 /* here we can use result and domain_res[0] */
1116 switch (format_desired) {
1117 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
1118 info1->result_name = ldb_dn_alloc_linearized(mem_ctx, result->dn);
1119 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1121 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1122 return WERR_OK;
1124 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
1125 info1->result_name = ldb_msg_find_attr_as_string(result, "canonicalName", NULL);
1126 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1127 return WERR_OK;
1129 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
1130 /* Not in the virtual ldb attribute */
1131 return DsCrackNameOneSyntactical(mem_ctx,
1132 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1133 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
1134 result->dn, name, info1);
1136 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
1138 const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
1139 const char *_acc = "", *_dom = "";
1140 if (sid == NULL) {
1141 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1142 return WERR_OK;
1145 if (samdb_find_attribute(sam_ctx, result, "objectClass",
1146 "domain")) {
1147 /* This can also find a DomainDNSZones entry,
1148 * but it won't have the SID we just
1149 * checked. */
1150 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1151 partitions_basedn,
1152 LDB_SCOPE_ONELEVEL,
1153 domain_attrs,
1154 "(ncName=%s)", ldb_dn_get_linearized(result->dn));
1156 if (ldb_ret != LDB_SUCCESS) {
1157 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
1158 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1159 return WERR_OK;
1162 switch (domain_res->count) {
1163 case 1:
1164 break;
1165 case 0:
1166 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1167 return WERR_OK;
1168 default:
1169 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1170 return WERR_OK;
1172 _dom = ldb_msg_find_attr_as_string(domain_res->msgs[0], "nETBIOSName", NULL);
1173 W_ERROR_HAVE_NO_MEMORY(_dom);
1174 } else {
1175 _acc = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
1176 if (!_acc) {
1177 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1178 return WERR_OK;
1180 if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) {
1181 _dom = "BUILTIN";
1182 } else {
1183 const char *attrs[] = { NULL };
1184 struct ldb_result *domain_res2;
1185 struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
1186 if (!dom_sid) {
1187 return WERR_OK;
1189 dom_sid->num_auths--;
1190 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1191 NULL,
1192 LDB_SCOPE_BASE,
1193 attrs,
1194 "(&(objectSid=%s)(objectClass=domain))",
1195 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
1197 if (ldb_ret != LDB_SUCCESS) {
1198 DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s\n", ldb_errstring(sam_ctx)));
1199 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1200 return WERR_OK;
1203 switch (domain_res->count) {
1204 case 1:
1205 break;
1206 case 0:
1207 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1208 return WERR_OK;
1209 default:
1210 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1211 return WERR_OK;
1214 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2,
1215 partitions_basedn,
1216 LDB_SCOPE_ONELEVEL,
1217 domain_attrs,
1218 "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
1220 if (ldb_ret != LDB_SUCCESS) {
1221 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
1222 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1223 return WERR_OK;
1226 switch (domain_res2->count) {
1227 case 1:
1228 break;
1229 case 0:
1230 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1231 return WERR_OK;
1232 default:
1233 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1234 return WERR_OK;
1236 _dom = ldb_msg_find_attr_as_string(domain_res2->msgs[0], "nETBIOSName", NULL);
1237 W_ERROR_HAVE_NO_MEMORY(_dom);
1241 info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
1242 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1244 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1245 return WERR_OK;
1247 case DRSUAPI_DS_NAME_FORMAT_GUID: {
1248 struct GUID guid;
1250 guid = samdb_result_guid(result, "objectGUID");
1252 info1->result_name = GUID_string2(mem_ctx, &guid);
1253 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1255 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1256 return WERR_OK;
1258 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
1259 info1->result_name = ldb_msg_find_attr_as_string(result, "displayName", NULL);
1260 if (!info1->result_name) {
1261 info1->result_name = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
1263 if (!info1->result_name) {
1264 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1265 } else {
1266 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1268 return WERR_OK;
1270 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
1271 struct ldb_message_element *el
1272 = ldb_msg_find_element(result,
1273 "servicePrincipalName");
1274 if (el == NULL) {
1275 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1276 return WERR_OK;
1277 } else if (el->num_values > 1) {
1278 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1279 return WERR_OK;
1282 info1->result_name = ldb_msg_find_attr_as_string(result, "servicePrincipalName", NULL);
1283 if (!info1->result_name) {
1284 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1285 } else {
1286 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1288 return WERR_OK;
1290 case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: {
1291 info1->dns_domain_name = NULL;
1292 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1293 return WERR_OK;
1295 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
1296 const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
1298 if (sid == NULL) {
1299 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1300 return WERR_OK;
1303 info1->result_name = dom_sid_string(mem_ctx, sid);
1304 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1306 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1307 return WERR_OK;
1309 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
1310 info1->result_name = ldb_msg_find_attr_as_string(result, "userPrincipalName", NULL);
1311 if (!info1->result_name) {
1312 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1313 } else {
1314 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1316 return WERR_OK;
1318 default:
1319 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1320 return WERR_OK;
1324 /* Given a user Principal Name (such as foo@bar.com),
1325 * return the user and domain DNs. This is used in the KDC to then
1326 * return the Keys and evaluate policy */
1328 NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
1329 TALLOC_CTX *mem_ctx,
1330 const char *user_principal_name,
1331 struct ldb_dn **user_dn,
1332 struct ldb_dn **domain_dn)
1334 WERROR werr;
1335 struct drsuapi_DsNameInfo1 info1;
1336 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1337 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
1338 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1339 user_principal_name,
1340 &info1);
1341 if (!W_ERROR_IS_OK(werr)) {
1342 return werror_to_ntstatus(werr);
1344 switch (info1.status) {
1345 case DRSUAPI_DS_NAME_STATUS_OK:
1346 break;
1347 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1348 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1349 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1350 return NT_STATUS_NO_SUCH_USER;
1351 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1352 default:
1353 return NT_STATUS_UNSUCCESSFUL;
1356 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1358 if (domain_dn) {
1359 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1360 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1361 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1362 talloc_asprintf(mem_ctx, "%s/",
1363 info1.dns_domain_name),
1364 &info1);
1365 if (!W_ERROR_IS_OK(werr)) {
1366 return werror_to_ntstatus(werr);
1368 switch (info1.status) {
1369 case DRSUAPI_DS_NAME_STATUS_OK:
1370 break;
1371 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1372 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1373 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1374 return NT_STATUS_NO_SUCH_USER;
1375 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1376 default:
1377 return NT_STATUS_UNSUCCESSFUL;
1380 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1383 return NT_STATUS_OK;
1386 /* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
1387 * return the user and domain DNs. This is used in the KDC to then
1388 * return the Keys and evaluate policy */
1390 NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
1391 TALLOC_CTX *mem_ctx,
1392 const char *service_principal_name,
1393 struct ldb_dn **user_dn,
1394 struct ldb_dn **domain_dn)
1396 WERROR werr;
1397 struct drsuapi_DsNameInfo1 info1;
1398 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1399 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
1400 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1401 service_principal_name,
1402 &info1);
1403 if (!W_ERROR_IS_OK(werr)) {
1404 return werror_to_ntstatus(werr);
1406 switch (info1.status) {
1407 case DRSUAPI_DS_NAME_STATUS_OK:
1408 break;
1409 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1410 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1411 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1412 return NT_STATUS_NO_SUCH_USER;
1413 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1414 default:
1415 return NT_STATUS_UNSUCCESSFUL;
1418 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1420 if (domain_dn) {
1421 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1422 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1423 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1424 talloc_asprintf(mem_ctx, "%s/",
1425 info1.dns_domain_name),
1426 &info1);
1427 if (!W_ERROR_IS_OK(werr)) {
1428 return werror_to_ntstatus(werr);
1430 switch (info1.status) {
1431 case DRSUAPI_DS_NAME_STATUS_OK:
1432 break;
1433 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1434 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1435 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1436 return NT_STATUS_NO_SUCH_USER;
1437 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1438 default:
1439 return NT_STATUS_UNSUCCESSFUL;
1442 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1445 return NT_STATUS_OK;
1448 NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1449 struct ldb_context *ldb,
1450 enum drsuapi_DsNameFormat format_offered,
1451 const char *name,
1452 const char **nt4_domain, const char **nt4_account)
1454 WERROR werr;
1455 struct drsuapi_DsNameInfo1 info1;
1456 char *p;
1458 /* Handle anonymous bind */
1459 if (!name || !*name) {
1460 *nt4_domain = "";
1461 *nt4_account = "";
1462 return NT_STATUS_OK;
1465 werr = DsCrackNameOneName(ldb, mem_ctx, 0,
1466 format_offered,
1467 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
1468 name,
1469 &info1);
1470 if (!W_ERROR_IS_OK(werr)) {
1471 return werror_to_ntstatus(werr);
1473 switch (info1.status) {
1474 case DRSUAPI_DS_NAME_STATUS_OK:
1475 break;
1476 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1477 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1478 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1479 return NT_STATUS_NO_SUCH_USER;
1480 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1481 default:
1482 return NT_STATUS_UNSUCCESSFUL;
1485 *nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
1486 if (*nt4_domain == NULL) {
1487 return NT_STATUS_NO_MEMORY;
1490 p = strchr(*nt4_domain, '\\');
1491 if (!p) {
1492 return NT_STATUS_INVALID_PARAMETER;
1494 p[0] = '\0';
1496 *nt4_account = talloc_strdup(mem_ctx, &p[1]);
1497 if (*nt4_account == NULL) {
1498 return NT_STATUS_NO_MEMORY;
1501 return NT_STATUS_OK;
1504 NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1505 struct ldb_context *ldb,
1506 const char *name,
1507 const char **nt4_domain,
1508 const char **nt4_account)
1510 enum drsuapi_DsNameFormat format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN;
1512 /* Handle anonymous bind */
1513 if (!name || !*name) {
1514 *nt4_domain = "";
1515 *nt4_account = "";
1516 return NT_STATUS_OK;
1520 * Here we only consider a subset of the possible name forms listed in
1521 * [MS-ADTS] 5.1.1.1.1, and we don't retry with a different name form if
1522 * the first attempt fails.
1525 if (strchr_m(name, '=')) {
1526 format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
1527 } else if (strchr_m(name, '@')) {
1528 format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
1529 } else if (strchr_m(name, '\\')) {
1530 format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
1531 } else if (strchr_m(name, '\n')) {
1532 format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX;
1533 } else if (strchr_m(name, '/')) {
1534 format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
1535 } else if ((name[0] == 'S' || name[0] == 's') && name[1] == '-') {
1536 format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
1537 } else {
1538 return NT_STATUS_NO_SUCH_USER;
1541 return crack_name_to_nt4_name(mem_ctx, ldb, format_offered, name, nt4_domain, nt4_account);
1545 WERROR dcesrv_drsuapi_ListRoles(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1546 const struct drsuapi_DsNameRequest1 *req1,
1547 struct drsuapi_DsNameCtr1 **ctr1)
1549 struct drsuapi_DsNameInfo1 *names;
1550 uint32_t i;
1551 uint32_t count = 5;/*number of fsmo role owners we are going to return*/
1553 *ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
1554 W_ERROR_HAVE_NO_MEMORY(*ctr1);
1555 names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1556 W_ERROR_HAVE_NO_MEMORY(names);
1558 for (i = 0; i < count; i++) {
1559 WERROR werr;
1560 struct ldb_dn *role_owner_dn, *fsmo_role_dn, *server_dn;
1561 werr = dsdb_get_fsmo_role_info(mem_ctx, sam_ctx, i,
1562 &fsmo_role_dn, &role_owner_dn);
1563 if(!W_ERROR_IS_OK(werr)) {
1564 return werr;
1566 server_dn = ldb_dn_copy(mem_ctx, role_owner_dn);
1567 ldb_dn_remove_child_components(server_dn, 1);
1568 names[i].status = DRSUAPI_DS_NAME_STATUS_OK;
1569 names[i].dns_domain_name = samdb_dn_to_dnshostname(sam_ctx, mem_ctx,
1570 server_dn);
1571 if(!names[i].dns_domain_name) {
1572 DEBUG(4, ("list_roles: Failed to find dNSHostName for server %s\n",
1573 ldb_dn_get_linearized(server_dn)));
1575 names[i].result_name = talloc_strdup(mem_ctx, ldb_dn_get_linearized(role_owner_dn));
1578 (*ctr1)->count = count;
1579 (*ctr1)->array = names;
1581 return WERR_OK;
1584 WERROR dcesrv_drsuapi_CrackNamesByNameFormat(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1585 const struct drsuapi_DsNameRequest1 *req1,
1586 struct drsuapi_DsNameCtr1 **ctr1)
1588 struct drsuapi_DsNameInfo1 *names;
1589 uint32_t i, count;
1590 WERROR status;
1592 *ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
1593 W_ERROR_HAVE_NO_MEMORY(*ctr1);
1595 count = req1->count;
1596 names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1597 W_ERROR_HAVE_NO_MEMORY(names);
1599 for (i=0; i < count; i++) {
1600 status = DsCrackNameOneName(sam_ctx, mem_ctx,
1601 req1->format_flags,
1602 req1->format_offered,
1603 req1->format_desired,
1604 req1->names[i].str,
1605 &names[i]);
1606 if (!W_ERROR_IS_OK(status)) {
1607 return status;
1611 (*ctr1)->count = count;
1612 (*ctr1)->array = names;
1614 return WERR_OK;
1617 WERROR dcesrv_drsuapi_ListInfoServer(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1618 const struct drsuapi_DsNameRequest1 *req1,
1619 struct drsuapi_DsNameCtr1 **_ctr1)
1621 struct drsuapi_DsNameInfo1 *names;
1622 struct ldb_result *res;
1623 struct ldb_dn *server_dn, *dn;
1624 struct drsuapi_DsNameCtr1 *ctr1;
1625 int ret, i;
1626 const char *str;
1627 const char *attrs[] = {
1628 "dn",
1629 "dNSHostName",
1630 "serverReference",
1631 NULL
1634 *_ctr1 = NULL;
1636 ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
1637 W_ERROR_HAVE_NO_MEMORY(ctr1);
1640 * No magic value here, we have to return 3 entries according to the
1641 * MS-DRSR.pdf
1643 ctr1->count = 3;
1644 names = talloc_zero_array(ctr1, struct drsuapi_DsNameInfo1,
1645 ctr1->count);
1646 W_ERROR_HAVE_NO_MEMORY(names);
1647 ctr1->array = names;
1649 for (i=0; i < ctr1->count; i++) {
1650 names[i].status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1652 *_ctr1 = ctr1;
1654 if (req1->count != 1) {
1655 DEBUG(1, ("Expected a count of 1 for the ListInfoServer crackname \n"));
1656 return WERR_OK;
1659 if (req1->names[0].str == NULL) {
1660 return WERR_OK;
1663 server_dn = ldb_dn_new(mem_ctx, sam_ctx, req1->names[0].str);
1664 W_ERROR_HAVE_NO_MEMORY(server_dn);
1666 ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_ONELEVEL,
1667 NULL, "(objectClass=nTDSDSA)");
1669 if (ret != LDB_SUCCESS) {
1670 DEBUG(1, ("Search for objectClass=nTDSDSA "
1671 "returned less than 1 objects\n"));
1672 return WERR_OK;
1675 if (res->count != 1) {
1676 DEBUG(1, ("Search for objectClass=nTDSDSA "
1677 "returned less than 1 objects\n"));
1678 return WERR_OK;
1681 if (res->msgs[0]->dn) {
1682 names[0].result_name = ldb_dn_alloc_linearized(names, res->msgs[0]->dn);
1683 W_ERROR_HAVE_NO_MEMORY(names[0].result_name);
1684 names[0].status = DRSUAPI_DS_NAME_STATUS_OK;
1687 talloc_free(res);
1689 ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_BASE,
1690 attrs, "(objectClass=*)");
1691 if (ret != LDB_SUCCESS) {
1692 DEBUG(1, ("Search for objectClass=* on dn %s"
1693 "returned %s\n", req1->names[0].str,
1694 ldb_strerror(ret)));
1695 return WERR_OK;
1698 if (res->count != 1) {
1699 DEBUG(1, ("Search for objectClass=* on dn %s"
1700 "returned less than 1 objects\n", req1->names[0].str));
1701 return WERR_OK;
1704 str = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
1705 if (str != NULL) {
1706 names[1].result_name = talloc_strdup(names, str);
1707 W_ERROR_HAVE_NO_MEMORY(names[1].result_name);
1708 names[1].status = DRSUAPI_DS_NAME_STATUS_OK;
1711 dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, res->msgs[0], "serverReference");
1712 if (dn != NULL) {
1713 names[2].result_name = ldb_dn_alloc_linearized(names, dn);
1714 W_ERROR_HAVE_NO_MEMORY(names[2].result_name);
1715 names[2].status = DRSUAPI_DS_NAME_STATUS_OK;
1718 talloc_free(dn);
1719 talloc_free(res);
1721 return WERR_OK;