s3: libsmb: Plumb in additional_flags2 = FLAGS2_REPARSE_PATH to cli_chkpath_send().
[Samba.git] / source4 / dsdb / samdb / cracknames.c
blobae334b4a60e06f6e74bc327ed5e7ce1768af42da
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
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "librpc/gen_ndr/drsuapi.h"
27 #include "lib/events/events.h"
28 #include <ldb.h>
29 #include <ldb_errors.h>
30 #include "auth/kerberos/kerberos.h"
31 #include "libcli/ldap/ldap_ndr.h"
32 #include "libcli/security/security.h"
33 #include "auth/auth.h"
34 #include "../lib/util/util_ldb.h"
35 #include "dsdb/samdb/samdb.h"
36 #include "dsdb/common/util.h"
37 #include "param/param.h"
39 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
40 struct smb_krb5_context *smb_krb5_context,
41 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
42 enum drsuapi_DsNameFormat format_desired,
43 struct ldb_dn *name_dn, const char *name,
44 const char *domain_filter, const char *result_filter,
45 struct drsuapi_DsNameInfo1 *info1, int scope, struct ldb_dn *search_dn);
46 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
47 enum drsuapi_DsNameFormat format_offered,
48 enum drsuapi_DsNameFormat format_desired,
49 struct ldb_dn *name_dn, const char *name,
50 struct drsuapi_DsNameInfo1 *info1);
52 static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context,
53 const char *name,
54 struct drsuapi_DsNameInfo1 *info1)
56 krb5_error_code ret;
57 krb5_principal principal;
58 /* perhaps it's a principal with a realm, so return the right 'domain only' response */
59 char *realm;
60 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
61 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
62 if (ret) {
63 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
64 return WERR_OK;
67 realm = smb_krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
69 info1->dns_domain_name = talloc_strdup(mem_ctx, realm);
70 krb5_free_principal(smb_krb5_context->krb5_context, principal);
71 free(realm);
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(krb5_context context, struct ldb_context *ldb_ctx,
80 TALLOC_CTX *mem_ctx,
81 const char *alias_from,
82 char **alias_to)
84 unsigned int i;
85 int ret;
86 struct ldb_result *res;
87 struct ldb_message_element *spnmappings;
88 TALLOC_CTX *tmp_ctx;
89 struct ldb_dn *service_dn;
90 char *service_dn_str;
92 const char *directory_attrs[] = {
93 "sPNMappings",
94 NULL
97 tmp_ctx = talloc_new(mem_ctx);
98 if (!tmp_ctx) {
99 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
102 service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
103 if ( ! ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb_ctx))) {
104 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
106 service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
107 if ( ! service_dn_str) {
108 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
111 ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
112 directory_attrs, "(objectClass=nTDSService)");
114 if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
115 DEBUG(1, ("ldb_search: dn: %s not found: %s\n", service_dn_str, ldb_errstring(ldb_ctx)));
116 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
117 } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
118 DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
119 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
120 } else if (res->count != 1) {
121 talloc_free(res);
122 DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
123 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
126 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
127 if (!spnmappings || spnmappings->num_values == 0) {
128 DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute\n", service_dn_str));
129 talloc_free(tmp_ctx);
130 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
133 for (i = 0; i < spnmappings->num_values; i++) {
134 char *mapping, *p, *str;
135 mapping = talloc_strdup(tmp_ctx,
136 (const char *)spnmappings->values[i].data);
137 if (!mapping) {
138 DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
139 talloc_free(tmp_ctx);
140 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
143 /* C string manipulation sucks */
145 p = strchr(mapping, '=');
146 if (!p) {
147 DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
148 service_dn_str, mapping));
149 talloc_free(tmp_ctx);
150 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
152 p[0] = '\0';
153 p++;
154 do {
155 str = p;
156 p = strchr(p, ',');
157 if (p) {
158 p[0] = '\0';
159 p++;
161 if (strcasecmp(str, alias_from) == 0) {
162 *alias_to = mapping;
163 talloc_steal(mem_ctx, mapping);
164 talloc_free(tmp_ctx);
165 return DRSUAPI_DS_NAME_STATUS_OK;
167 } while (p);
169 DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
170 talloc_free(tmp_ctx);
171 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
174 /* When cracking a ServicePrincipalName, many services may be served
175 * by the host/ servicePrincipalName. The incoming query is for cifs/
176 * but we translate it here, and search on host/. This is done after
177 * the cifs/ entry has been searched for, making this a fallback */
179 static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
180 struct smb_krb5_context *smb_krb5_context,
181 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
182 enum drsuapi_DsNameFormat format_desired,
183 const char *name, struct drsuapi_DsNameInfo1 *info1)
185 WERROR wret;
186 krb5_error_code ret;
187 krb5_principal principal;
188 const krb5_data *component;
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\n",
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 (krb5_princ_size(smb_krb5_context->krb5_context, principal) < 2) {
208 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
209 krb5_free_principal(smb_krb5_context->krb5_context, principal);
210 return WERR_OK;
212 component = krb5_princ_component(smb_krb5_context->krb5_context,
213 principal, 0);
214 service = (const char *)component->data;
215 component = krb5_princ_component(smb_krb5_context->krb5_context,
216 principal, 1);
217 dns_name = (const char *)component->data;
219 /* MAP it */
220 namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context,
221 sam_ctx, mem_ctx,
222 service, &new_service);
224 if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
225 wret = WERR_OK;
226 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
227 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
228 if (!info1->dns_domain_name) {
229 wret = WERR_NOMEM;
231 krb5_free_principal(smb_krb5_context->krb5_context, principal);
232 return wret;
233 } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
234 info1->status = namestatus;
235 krb5_free_principal(smb_krb5_context->krb5_context, principal);
236 return WERR_OK;
239 /* reform principal */
240 new_princ = talloc_asprintf(mem_ctx, "%s/%s", new_service, dns_name);
241 if (!new_princ) {
242 krb5_free_principal(smb_krb5_context->krb5_context, principal);
243 return WERR_NOMEM;
246 wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
247 new_princ, info1);
248 talloc_free(new_princ);
249 if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
250 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
251 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
252 if (!info1->dns_domain_name) {
253 wret = WERR_NOMEM;
256 krb5_free_principal(smb_krb5_context->krb5_context, principal);
257 return wret;
260 /* Subcase of CrackNames, for the userPrincipalName */
262 static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
263 struct smb_krb5_context *smb_krb5_context,
264 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
265 enum drsuapi_DsNameFormat format_desired,
266 const char *name, struct drsuapi_DsNameInfo1 *info1)
268 int ldb_ret;
269 WERROR status;
270 const char *domain_filter = NULL;
271 const char *result_filter = NULL;
272 krb5_error_code ret;
273 krb5_principal principal;
274 char *realm;
275 char *unparsed_name_short;
276 const char *domain_attrs[] = { NULL };
277 struct ldb_result *domain_res = NULL;
279 /* Prevent recursion */
280 if (!name) {
281 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
282 return WERR_OK;
285 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
286 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
287 if (ret) {
288 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
289 return WERR_OK;
292 realm = smb_krb5_principal_get_realm(smb_krb5_context->krb5_context,
293 principal);
295 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
296 samdb_partitions_dn(sam_ctx, mem_ctx),
297 LDB_SCOPE_ONELEVEL,
298 domain_attrs,
299 "(&(objectClass=crossRef)(|(dnsRoot=%s)(netbiosName=%s))(systemFlags:%s:=%u))",
300 ldb_binary_encode_string(mem_ctx, realm),
301 ldb_binary_encode_string(mem_ctx, realm),
302 LDB_OID_COMPARATOR_AND,
303 SYSTEM_FLAG_CR_NTDS_DOMAIN);
304 free(realm);
306 if (ldb_ret != LDB_SUCCESS) {
307 DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
308 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
309 krb5_free_principal(smb_krb5_context->krb5_context, principal);
310 return WERR_OK;
313 switch (domain_res->count) {
314 case 1:
315 break;
316 case 0:
317 krb5_free_principal(smb_krb5_context->krb5_context, principal);
318 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
319 name, info1);
320 default:
321 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
322 krb5_free_principal(smb_krb5_context->krb5_context, principal);
323 return WERR_OK;
327 * The important thing here is that a samAccountName may have
328 * a space in it, and this must not be kerberos escaped to
329 * match this filter, so we specify
330 * KRB5_PRINCIPAL_UNPARSE_DISPLAY
332 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
333 KRB5_PRINCIPAL_UNPARSE_NO_REALM |
334 KRB5_PRINCIPAL_UNPARSE_DISPLAY,
335 &unparsed_name_short);
336 krb5_free_principal(smb_krb5_context->krb5_context, principal);
338 if (ret) {
339 free(unparsed_name_short);
340 return WERR_NOMEM;
343 /* This may need to be extended for more userPrincipalName variations */
344 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
345 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
347 domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
349 if (!result_filter || !domain_filter) {
350 free(unparsed_name_short);
351 return WERR_NOMEM;
353 status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
354 smb_krb5_context,
355 format_flags, format_offered, format_desired,
356 NULL, unparsed_name_short, domain_filter, result_filter,
357 info1, LDB_SCOPE_SUBTREE, NULL);
358 free(unparsed_name_short);
360 return status;
364 * This function will workout the filtering parameter in order to be able to do
365 * the adapted search when the incomming format is format_functional.
366 * This boils down to defining the search_dn (passed as pointer to ldb_dn *) and the
367 * ldap filter request.
368 * Main input parameters are:
369 * * name, which is the portion of the functional name after the
370 * first '/'.
371 * * domain_filter, which is a ldap search filter used to find the NC DN given the
372 * function name to crack.
374 static WERROR get_format_functional_filtering_param(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
375 char *name, struct drsuapi_DsNameInfo1 *info1,
376 struct ldb_dn **psearch_dn, const char *domain_filter, const char **presult_filter)
378 struct ldb_result *domain_res = NULL;
379 const char * const domain_attrs[] = {"ncName", NULL};
380 struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
381 int ldb_ret;
382 char *account, *s, *result_filter = NULL;
383 struct ldb_dn *search_dn = NULL;
385 *psearch_dn = NULL;
386 *presult_filter = NULL;
388 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
389 partitions_basedn,
390 LDB_SCOPE_ONELEVEL,
391 domain_attrs,
392 "%s", domain_filter);
394 if (ldb_ret != LDB_SUCCESS) {
395 DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
396 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
397 return WERR_FOOBAR;
400 if (domain_res->count == 1) {
401 struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
402 const char * const name_attrs[] = {"name", NULL};
404 account = name;
405 s = strchr(account, '/');
406 talloc_free(domain_res);
407 while(s) {
408 s[0] = '\0';
409 s++;
411 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
412 tmp_dn,
413 LDB_SCOPE_ONELEVEL,
414 name_attrs,
415 "name=%s", account);
417 if (ldb_ret != LDB_SUCCESS) {
418 DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
419 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
420 return WERR_OK;
422 talloc_free(tmp_dn);
423 switch (domain_res->count) {
424 case 1:
425 break;
426 case 0:
427 talloc_free(domain_res);
428 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
429 return WERR_OK;
430 default:
431 talloc_free(domain_res);
432 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
433 return WERR_OK;
436 tmp_dn = talloc_steal(mem_ctx, domain_res->msgs[0]->dn);
437 talloc_free(domain_res);
438 search_dn = tmp_dn;
439 account = s;
440 s = strchr(account, '/');
442 account = ldb_binary_encode_string(mem_ctx, account);
443 W_ERROR_HAVE_NO_MEMORY(account);
444 result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
445 account);
446 W_ERROR_HAVE_NO_MEMORY(result_filter);
448 *psearch_dn = search_dn;
449 *presult_filter = result_filter;
450 return WERR_OK;
453 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
455 WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
456 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
457 enum drsuapi_DsNameFormat format_desired,
458 const char *name, struct drsuapi_DsNameInfo1 *info1)
460 krb5_error_code ret;
461 const char *domain_filter = NULL;
462 const char *result_filter = NULL;
463 struct ldb_dn *name_dn = NULL;
464 struct ldb_dn *search_dn = NULL;
466 struct smb_krb5_context *smb_krb5_context = NULL;
467 int scope = LDB_SCOPE_SUBTREE;
469 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
470 info1->dns_domain_name = NULL;
471 info1->result_name = NULL;
473 if (!name) {
474 return WERR_INVALID_PARAM;
477 /* TODO: - fill the correct names in all cases!
478 * - handle format_flags
480 if (format_desired == DRSUAPI_DS_NAME_FORMAT_UNKNOWN) {
481 return WERR_OK;
483 /* here we need to set the domain_filter and/or the result_filter */
484 switch (format_offered) {
485 case DRSUAPI_DS_NAME_FORMAT_UNKNOWN:
487 unsigned int i;
488 enum drsuapi_DsNameFormat formats[] = {
489 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
490 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL,
491 DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY,
492 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
493 DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
494 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
496 WERROR werr;
497 for (i=0; i < ARRAY_SIZE(formats); i++) {
498 werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1);
499 if (!W_ERROR_IS_OK(werr)) {
500 return werr;
502 if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND &&
503 (formats[i] != DRSUAPI_DS_NAME_FORMAT_CANONICAL ||
504 info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR))
506 return werr;
509 return werr;
512 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
513 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
515 char *str, *s, *account;
516 scope = LDB_SCOPE_ONELEVEL;
518 if (strlen(name) == 0) {
519 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
520 return WERR_OK;
523 str = talloc_strdup(mem_ctx, name);
524 W_ERROR_HAVE_NO_MEMORY(str);
526 if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
527 /* Look backwards for the \n, and replace it with / */
528 s = strrchr(str, '\n');
529 if (!s) {
530 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
531 return WERR_OK;
533 s[0] = '/';
536 s = strchr(str, '/');
537 if (!s) {
538 /* there must be at least one / */
539 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
540 return WERR_OK;
543 s[0] = '\0';
544 s++;
546 domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))",
547 ldb_binary_encode_string(mem_ctx, str),
548 LDB_OID_COMPARATOR_AND,
549 SYSTEM_FLAG_CR_NTDS_DOMAIN);
550 W_ERROR_HAVE_NO_MEMORY(domain_filter);
552 /* There may not be anything after the domain component (search for the domain itself) */
553 account = s;
554 if (account && *account) {
555 WERROR werr = get_format_functional_filtering_param(sam_ctx,
556 mem_ctx,
557 account,
558 info1,
559 &search_dn,
560 domain_filter,
561 &result_filter);
562 if (!W_ERROR_IS_OK(werr)) {
563 return werr;
565 if (info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR)
566 return WERR_OK;
568 break;
570 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
571 char *p;
572 char *domain;
573 const char *account = NULL;
575 domain = talloc_strdup(mem_ctx, name);
576 W_ERROR_HAVE_NO_MEMORY(domain);
578 p = strchr(domain, '\\');
579 if (!p) {
580 /* invalid input format */
581 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
582 return WERR_OK;
584 p[0] = '\0';
586 if (p[1]) {
587 account = &p[1];
590 domain_filter = talloc_asprintf(mem_ctx,
591 "(&(objectClass=crossRef)(netbiosName=%s)(systemFlags:%s:=%u))",
592 ldb_binary_encode_string(mem_ctx, domain),
593 LDB_OID_COMPARATOR_AND,
594 SYSTEM_FLAG_CR_NTDS_DOMAIN);
595 W_ERROR_HAVE_NO_MEMORY(domain_filter);
596 if (account) {
597 result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
598 ldb_binary_encode_string(mem_ctx, account));
599 W_ERROR_HAVE_NO_MEMORY(result_filter);
602 talloc_free(domain);
603 break;
606 /* A LDAP DN as a string */
607 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
608 domain_filter = NULL;
609 name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
610 if (! ldb_dn_validate(name_dn)) {
611 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
612 return WERR_OK;
614 break;
617 /* A GUID as a string */
618 case DRSUAPI_DS_NAME_FORMAT_GUID: {
619 struct GUID guid;
620 char *ldap_guid;
621 NTSTATUS nt_status;
622 domain_filter = NULL;
624 nt_status = GUID_from_string(name, &guid);
625 if (!NT_STATUS_IS_OK(nt_status)) {
626 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
627 return WERR_OK;
630 ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
631 if (!ldap_guid) {
632 return WERR_NOMEM;
634 result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
635 ldap_guid);
636 W_ERROR_HAVE_NO_MEMORY(result_filter);
637 break;
639 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
640 domain_filter = NULL;
642 result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
643 ldb_binary_encode_string(mem_ctx, name),
644 ldb_binary_encode_string(mem_ctx, name));
645 W_ERROR_HAVE_NO_MEMORY(result_filter);
646 break;
649 /* A S-1234-5678 style string */
650 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
651 struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
652 char *ldap_sid;
654 domain_filter = NULL;
655 if (!sid) {
656 info1->dns_domain_name = NULL;
657 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
658 return WERR_OK;
660 ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
661 sid);
662 if (!ldap_sid) {
663 return WERR_NOMEM;
665 result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
666 ldap_sid);
667 W_ERROR_HAVE_NO_MEMORY(result_filter);
668 break;
670 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
671 krb5_principal principal;
672 char *unparsed_name;
674 ret = smb_krb5_init_context(mem_ctx,
675 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
676 &smb_krb5_context);
678 if (ret) {
679 return WERR_NOMEM;
682 /* Ensure we reject compleate junk first */
683 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
684 if (ret) {
685 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
686 return WERR_OK;
689 domain_filter = NULL;
692 * By getting the unparsed name here, we ensure the
693 * escaping is removed correctly (and trust the client
694 * less). The important thing here is that a
695 * userPrincipalName may have a space in it, and this
696 * must not be kerberos escaped to match this filter,
697 * so we specify KRB5_PRINCIPAL_UNPARSE_DISPLAY
699 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context,
700 principal,
701 KRB5_PRINCIPAL_UNPARSE_DISPLAY,
702 &unparsed_name);
703 if (ret) {
704 krb5_free_principal(smb_krb5_context->krb5_context, principal);
705 return WERR_NOMEM;
708 krb5_free_principal(smb_krb5_context->krb5_context, principal);
710 /* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */
711 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))",
712 ldb_binary_encode_string(mem_ctx, unparsed_name));
714 free(unparsed_name);
715 W_ERROR_HAVE_NO_MEMORY(result_filter);
716 break;
718 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
719 krb5_principal principal;
720 char *unparsed_name_short;
721 const krb5_data *component;
722 char *service;
724 ret = smb_krb5_init_context(mem_ctx,
725 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
726 &smb_krb5_context);
728 if (ret) {
729 return WERR_NOMEM;
732 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
733 if (ret == 0 &&
734 krb5_princ_size(smb_krb5_context->krb5_context,
735 principal) < 2) {
736 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
737 krb5_free_principal(smb_krb5_context->krb5_context, principal);
738 return WERR_OK;
739 } else if (ret == 0) {
740 krb5_free_principal(smb_krb5_context->krb5_context, principal);
742 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
743 KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
744 if (ret) {
745 return dns_domain_from_principal(mem_ctx, smb_krb5_context,
746 name, info1);
749 domain_filter = NULL;
751 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
752 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
753 if (ret) {
754 krb5_free_principal(smb_krb5_context->krb5_context, principal);
755 return WERR_NOMEM;
758 component = krb5_princ_component(smb_krb5_context->krb5_context,
759 principal, 0);
760 service = (char *)component->data;
761 if ((krb5_princ_size(smb_krb5_context->krb5_context,
762 principal) == 2) &&
763 (strcasecmp(service, "host") == 0)) {
764 /* the 'cn' attribute is just the leading part of the name */
765 char *computer_name;
766 component = krb5_princ_component(
767 smb_krb5_context->krb5_context,
768 principal, 1);
769 computer_name = talloc_strndup(mem_ctx, (char *)component->data,
770 strcspn((char *)component->data, "."));
771 if (computer_name == NULL) {
772 krb5_free_principal(smb_krb5_context->krb5_context, principal);
773 free(unparsed_name_short);
774 return WERR_NOMEM;
777 result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
778 ldb_binary_encode_string(mem_ctx, unparsed_name_short),
779 ldb_binary_encode_string(mem_ctx, computer_name));
780 } else {
781 result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
782 ldb_binary_encode_string(mem_ctx, unparsed_name_short));
784 krb5_free_principal(smb_krb5_context->krb5_context, principal);
785 free(unparsed_name_short);
786 W_ERROR_HAVE_NO_MEMORY(result_filter);
788 break;
790 default: {
791 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
792 return WERR_OK;
796 if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
797 return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
798 name_dn, name, info1);
801 return DsCrackNameOneFilter(sam_ctx, mem_ctx,
802 smb_krb5_context,
803 format_flags, format_offered, format_desired,
804 name_dn, name,
805 domain_filter, result_filter,
806 info1, scope, search_dn);
809 /* Subcase of CrackNames. It is possible to translate a LDAP-style DN
810 * (FQDN_1779) into a canoical name without actually searching the
811 * database */
813 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
814 enum drsuapi_DsNameFormat format_offered,
815 enum drsuapi_DsNameFormat format_desired,
816 struct ldb_dn *name_dn, const char *name,
817 struct drsuapi_DsNameInfo1 *info1)
819 char *cracked;
820 if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
821 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
822 return WERR_OK;
825 switch (format_desired) {
826 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
827 cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
828 break;
829 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
830 cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
831 break;
832 default:
833 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
834 return WERR_OK;
836 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
837 info1->result_name = cracked;
838 if (!cracked) {
839 return WERR_NOMEM;
842 return WERR_OK;
845 /* Given a filter for the domain, and one for the result, perform the
846 * ldb search. The format offered and desired flags change the
847 * behaviours, including what attributes to return.
849 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
852 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
853 struct smb_krb5_context *smb_krb5_context,
854 uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
855 enum drsuapi_DsNameFormat format_desired,
856 struct ldb_dn *name_dn, const char *name,
857 const char *domain_filter, const char *result_filter,
858 struct drsuapi_DsNameInfo1 *info1,
859 int scope, struct ldb_dn *search_dn)
861 int ldb_ret;
862 struct ldb_result *domain_res = NULL;
863 const char * const *domain_attrs;
864 const char * const *result_attrs;
865 struct ldb_message **result_res = NULL;
866 struct ldb_message *result = NULL;
867 int i;
868 char *p;
869 struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
871 const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
872 const char * const _result_attrs_null[] = { NULL };
874 const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
875 const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
877 const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
878 const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
880 const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
881 const char * const _result_attrs_guid[] = { "objectGUID", NULL};
883 const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
884 const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
886 const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
887 const char * const _result_attrs_none[] = { NULL};
889 /* here we need to set the attrs lists for domain and result lookups */
890 switch (format_desired) {
891 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
892 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
893 domain_attrs = _domain_attrs_1779;
894 result_attrs = _result_attrs_null;
895 break;
896 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
897 domain_attrs = _domain_attrs_canonical;
898 result_attrs = _result_attrs_canonical;
899 break;
900 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
901 domain_attrs = _domain_attrs_nt4;
902 result_attrs = _result_attrs_nt4;
903 break;
904 case DRSUAPI_DS_NAME_FORMAT_GUID:
905 domain_attrs = _domain_attrs_guid;
906 result_attrs = _result_attrs_guid;
907 break;
908 case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
909 domain_attrs = _domain_attrs_display;
910 result_attrs = _result_attrs_display;
911 break;
912 default:
913 domain_attrs = _domain_attrs_none;
914 result_attrs = _result_attrs_none;
915 break;
918 if (domain_filter) {
919 /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
920 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
921 partitions_basedn,
922 LDB_SCOPE_ONELEVEL,
923 domain_attrs,
924 "%s", domain_filter);
926 if (ldb_ret != LDB_SUCCESS) {
927 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
928 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
929 return WERR_OK;
932 switch (domain_res->count) {
933 case 1:
934 break;
935 case 0:
936 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
937 return WERR_OK;
938 default:
939 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
940 return WERR_OK;
943 info1->dns_domain_name = ldb_msg_find_attr_as_string(domain_res->msgs[0], "dnsRoot", NULL);
944 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
945 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
946 } else {
947 info1->dns_domain_name = NULL;
948 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
951 if (result_filter) {
952 int ret;
953 struct ldb_result *res;
954 uint32_t dsdb_flags = 0;
955 struct ldb_dn *real_search_dn = NULL;
956 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
959 * From 4.1.4.2.11 of MS-DRSR
960 * if DS_NAME_FLAG_GCVERIFY in flags then
961 * rt := select all O from all
962 * where attrValue in GetAttrVals(O, att, false)
963 * else
964 * rt := select all O from subtree DefaultNC()
965 * where attrValue in GetAttrVals(O, att, false)
966 * endif
967 * return rt
969 if (format_flags & DRSUAPI_DS_NAME_FLAG_GCVERIFY ||
970 format_offered == DRSUAPI_DS_NAME_FORMAT_GUID)
972 dsdb_flags = DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
973 } else if (domain_res) {
974 if (!search_dn) {
975 struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
976 real_search_dn = tmp_dn;
977 } else {
978 real_search_dn = search_dn;
980 } else {
981 real_search_dn = ldb_get_default_basedn(sam_ctx);
983 if (format_desired == DRSUAPI_DS_NAME_FORMAT_GUID){
984 dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
986 /* search with the 'phantom root' flag */
987 ret = dsdb_search(sam_ctx, mem_ctx, &res,
988 real_search_dn,
989 scope,
990 result_attrs,
991 dsdb_flags,
992 "%s", result_filter);
993 if (ret != LDB_SUCCESS) {
994 DEBUG(2, ("DsCrackNameOneFilter search from '%s' with flags 0x%08x failed: %s\n",
995 ldb_dn_get_linearized(real_search_dn),
996 dsdb_flags,
997 ldb_errstring(sam_ctx)));
998 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
999 return WERR_OK;
1002 ldb_ret = res->count;
1003 result_res = res->msgs;
1004 } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
1005 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
1006 result_attrs);
1007 } else if (domain_res) {
1008 name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
1009 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
1010 result_attrs);
1011 } else {
1012 /* Can't happen */
1013 DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not available: This can't happen...\n"));
1014 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1015 return WERR_OK;
1018 switch (ldb_ret) {
1019 case 1:
1020 result = result_res[0];
1021 break;
1022 case 0:
1023 switch (format_offered) {
1024 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
1025 return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
1026 smb_krb5_context,
1027 format_flags, format_offered, format_desired,
1028 name, info1);
1030 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
1031 return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
1032 format_flags, format_offered, format_desired,
1033 name, info1);
1034 default:
1035 break;
1037 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1038 return WERR_OK;
1039 case -1:
1040 DEBUG(2, ("DsCrackNameOneFilter result search failed: %s\n", ldb_errstring(sam_ctx)));
1041 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1042 return WERR_OK;
1043 default:
1044 switch (format_offered) {
1045 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
1046 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
1048 const char *canonical_name = NULL; /* Not required, but we get warnings... */
1049 /* We may need to manually filter further */
1050 for (i = 0; i < ldb_ret; i++) {
1051 switch (format_offered) {
1052 case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
1053 canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
1054 break;
1055 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
1056 canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
1057 break;
1058 default:
1059 break;
1061 if (strcasecmp_m(canonical_name, name) == 0) {
1062 result = result_res[i];
1063 break;
1066 if (!result) {
1067 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1068 return WERR_OK;
1071 /* FALL TROUGH */
1072 default:
1073 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1074 return WERR_OK;
1078 info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
1079 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
1080 p = strchr(info1->dns_domain_name, '/');
1081 if (p) {
1082 p[0] = '\0';
1085 /* here we can use result and domain_res[0] */
1086 switch (format_desired) {
1087 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
1088 info1->result_name = ldb_dn_alloc_linearized(mem_ctx, result->dn);
1089 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1091 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1092 return WERR_OK;
1094 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
1095 info1->result_name = ldb_msg_find_attr_as_string(result, "canonicalName", NULL);
1096 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1097 return WERR_OK;
1099 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
1100 /* Not in the virtual ldb attribute */
1101 return DsCrackNameOneSyntactical(mem_ctx,
1102 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1103 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
1104 result->dn, name, info1);
1106 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
1108 const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
1109 const char *_acc = "", *_dom = "";
1110 if (sid == NULL) {
1111 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1112 return WERR_OK;
1115 if (samdb_find_attribute(sam_ctx, result, "objectClass",
1116 "domain")) {
1117 /* This can also find a DomainDNSZones entry,
1118 * but it won't have the SID we just
1119 * checked. */
1120 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1121 partitions_basedn,
1122 LDB_SCOPE_ONELEVEL,
1123 domain_attrs,
1124 "(ncName=%s)", ldb_dn_get_linearized(result->dn));
1126 if (ldb_ret != LDB_SUCCESS) {
1127 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
1128 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1129 return WERR_OK;
1132 switch (domain_res->count) {
1133 case 1:
1134 break;
1135 case 0:
1136 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1137 return WERR_OK;
1138 default:
1139 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1140 return WERR_OK;
1142 _dom = ldb_msg_find_attr_as_string(domain_res->msgs[0], "nETBIOSName", NULL);
1143 W_ERROR_HAVE_NO_MEMORY(_dom);
1144 } else {
1145 _acc = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
1146 if (!_acc) {
1147 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1148 return WERR_OK;
1150 if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) {
1151 _dom = "BUILTIN";
1152 } else {
1153 const char *attrs[] = { NULL };
1154 struct ldb_result *domain_res2;
1155 struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
1156 if (!dom_sid) {
1157 return WERR_OK;
1159 dom_sid->num_auths--;
1160 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1161 NULL,
1162 LDB_SCOPE_BASE,
1163 attrs,
1164 "(&(objectSid=%s)(objectClass=domain))",
1165 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
1167 if (ldb_ret != LDB_SUCCESS) {
1168 DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s\n", ldb_errstring(sam_ctx)));
1169 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1170 return WERR_OK;
1173 switch (domain_res->count) {
1174 case 1:
1175 break;
1176 case 0:
1177 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1178 return WERR_OK;
1179 default:
1180 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1181 return WERR_OK;
1184 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2,
1185 partitions_basedn,
1186 LDB_SCOPE_ONELEVEL,
1187 domain_attrs,
1188 "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
1190 if (ldb_ret != LDB_SUCCESS) {
1191 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
1192 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1193 return WERR_OK;
1196 switch (domain_res2->count) {
1197 case 1:
1198 break;
1199 case 0:
1200 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1201 return WERR_OK;
1202 default:
1203 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1204 return WERR_OK;
1206 _dom = ldb_msg_find_attr_as_string(domain_res2->msgs[0], "nETBIOSName", NULL);
1207 W_ERROR_HAVE_NO_MEMORY(_dom);
1211 info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
1212 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1214 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1215 return WERR_OK;
1217 case DRSUAPI_DS_NAME_FORMAT_GUID: {
1218 struct GUID guid;
1220 guid = samdb_result_guid(result, "objectGUID");
1222 info1->result_name = GUID_string2(mem_ctx, &guid);
1223 W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1225 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1226 return WERR_OK;
1228 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
1229 info1->result_name = ldb_msg_find_attr_as_string(result, "displayName", NULL);
1230 if (!info1->result_name) {
1231 info1->result_name = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
1233 if (!info1->result_name) {
1234 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1235 } else {
1236 info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1238 return WERR_OK;
1240 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
1241 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1242 return WERR_OK;
1244 case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
1245 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
1246 info1->dns_domain_name = NULL;
1247 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1248 return WERR_OK;
1250 default:
1251 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1252 return WERR_OK;
1256 /* Given a user Principal Name (such as foo@bar.com),
1257 * return the user and domain DNs. This is used in the KDC to then
1258 * return the Keys and evaluate policy */
1260 NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
1261 TALLOC_CTX *mem_ctx,
1262 const char *user_principal_name,
1263 struct ldb_dn **user_dn,
1264 struct ldb_dn **domain_dn)
1266 WERROR werr;
1267 struct drsuapi_DsNameInfo1 info1;
1268 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1269 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
1270 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1271 user_principal_name,
1272 &info1);
1273 if (!W_ERROR_IS_OK(werr)) {
1274 return werror_to_ntstatus(werr);
1276 switch (info1.status) {
1277 case DRSUAPI_DS_NAME_STATUS_OK:
1278 break;
1279 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1280 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1281 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1282 return NT_STATUS_NO_SUCH_USER;
1283 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1284 default:
1285 return NT_STATUS_UNSUCCESSFUL;
1288 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1290 if (domain_dn) {
1291 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1292 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1293 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1294 talloc_asprintf(mem_ctx, "%s/",
1295 info1.dns_domain_name),
1296 &info1);
1297 if (!W_ERROR_IS_OK(werr)) {
1298 return werror_to_ntstatus(werr);
1300 switch (info1.status) {
1301 case DRSUAPI_DS_NAME_STATUS_OK:
1302 break;
1303 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1304 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1305 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1306 return NT_STATUS_NO_SUCH_USER;
1307 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1308 default:
1309 return NT_STATUS_UNSUCCESSFUL;
1312 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1315 return NT_STATUS_OK;
1318 /* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
1319 * return the user and domain DNs. This is used in the KDC to then
1320 * return the Keys and evaluate policy */
1322 NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
1323 TALLOC_CTX *mem_ctx,
1324 const char *service_principal_name,
1325 struct ldb_dn **user_dn,
1326 struct ldb_dn **domain_dn)
1328 WERROR werr;
1329 struct drsuapi_DsNameInfo1 info1;
1330 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1331 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
1332 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1333 service_principal_name,
1334 &info1);
1335 if (!W_ERROR_IS_OK(werr)) {
1336 return werror_to_ntstatus(werr);
1338 switch (info1.status) {
1339 case DRSUAPI_DS_NAME_STATUS_OK:
1340 break;
1341 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1342 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1343 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1344 return NT_STATUS_NO_SUCH_USER;
1345 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1346 default:
1347 return NT_STATUS_UNSUCCESSFUL;
1350 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1352 if (domain_dn) {
1353 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1354 DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1355 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1356 talloc_asprintf(mem_ctx, "%s/",
1357 info1.dns_domain_name),
1358 &info1);
1359 if (!W_ERROR_IS_OK(werr)) {
1360 return werror_to_ntstatus(werr);
1362 switch (info1.status) {
1363 case DRSUAPI_DS_NAME_STATUS_OK:
1364 break;
1365 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1366 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1367 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1368 return NT_STATUS_NO_SUCH_USER;
1369 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1370 default:
1371 return NT_STATUS_UNSUCCESSFUL;
1374 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1377 return NT_STATUS_OK;
1380 NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1381 struct tevent_context *ev_ctx,
1382 struct loadparm_context *lp_ctx,
1383 enum drsuapi_DsNameFormat format_offered,
1384 const char *name,
1385 const char **nt4_domain, const char **nt4_account)
1387 WERROR werr;
1388 struct drsuapi_DsNameInfo1 info1;
1389 struct ldb_context *ldb;
1390 char *p;
1392 /* Handle anonymous bind */
1393 if (!name || !*name) {
1394 *nt4_domain = "";
1395 *nt4_account = "";
1396 return NT_STATUS_OK;
1399 ldb = samdb_connect(mem_ctx, ev_ctx, lp_ctx, system_session(lp_ctx), 0);
1400 if (ldb == NULL) {
1401 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1404 werr = DsCrackNameOneName(ldb, mem_ctx, 0,
1405 format_offered,
1406 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
1407 name,
1408 &info1);
1409 if (!W_ERROR_IS_OK(werr)) {
1410 return werror_to_ntstatus(werr);
1412 switch (info1.status) {
1413 case DRSUAPI_DS_NAME_STATUS_OK:
1414 break;
1415 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1416 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1417 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1418 return NT_STATUS_NO_SUCH_USER;
1419 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1420 default:
1421 return NT_STATUS_UNSUCCESSFUL;
1424 *nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
1425 if (*nt4_domain == NULL) {
1426 return NT_STATUS_NO_MEMORY;
1429 p = strchr(*nt4_domain, '\\');
1430 if (!p) {
1431 return NT_STATUS_INVALID_PARAMETER;
1433 p[0] = '\0';
1435 *nt4_account = talloc_strdup(mem_ctx, &p[1]);
1436 if (*nt4_account == NULL) {
1437 return NT_STATUS_NO_MEMORY;
1440 return NT_STATUS_OK;
1443 NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1444 struct tevent_context *ev_ctx,
1445 struct loadparm_context *lp_ctx,
1446 const char *name,
1447 const char **nt4_domain,
1448 const char **nt4_account)
1450 enum drsuapi_DsNameFormat format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN;
1452 /* Handle anonymous bind */
1453 if (!name || !*name) {
1454 *nt4_domain = "";
1455 *nt4_account = "";
1456 return NT_STATUS_OK;
1459 if (strchr_m(name, '=')) {
1460 format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
1461 } else if (strchr_m(name, '@')) {
1462 format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
1463 } else if (strchr_m(name, '\\')) {
1464 format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
1465 } else if (strchr_m(name, '/')) {
1466 format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
1467 } else {
1468 return NT_STATUS_NO_SUCH_USER;
1471 return crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, format_offered, name, nt4_domain, nt4_account);
1475 WERROR dcesrv_drsuapi_ListRoles(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1476 const struct drsuapi_DsNameRequest1 *req1,
1477 struct drsuapi_DsNameCtr1 **ctr1)
1479 struct drsuapi_DsNameInfo1 *names;
1480 uint32_t i;
1481 uint32_t count = 5;/*number of fsmo role owners we are going to return*/
1483 *ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
1484 W_ERROR_HAVE_NO_MEMORY(*ctr1);
1485 names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1486 W_ERROR_HAVE_NO_MEMORY(names);
1488 for (i = 0; i < count; i++) {
1489 WERROR werr;
1490 struct ldb_dn *role_owner_dn, *fsmo_role_dn, *server_dn;
1491 werr = dsdb_get_fsmo_role_info(mem_ctx, sam_ctx, i,
1492 &fsmo_role_dn, &role_owner_dn);
1493 if(!W_ERROR_IS_OK(werr)) {
1494 return werr;
1496 server_dn = ldb_dn_copy(mem_ctx, role_owner_dn);
1497 ldb_dn_remove_child_components(server_dn, 1);
1498 names[i].status = DRSUAPI_DS_NAME_STATUS_OK;
1499 names[i].dns_domain_name = samdb_dn_to_dnshostname(sam_ctx, mem_ctx,
1500 server_dn);
1501 if(!names[i].dns_domain_name) {
1502 DEBUG(4, ("list_roles: Failed to find dNSHostName for server %s\n",
1503 ldb_dn_get_linearized(server_dn)));
1505 names[i].result_name = talloc_strdup(mem_ctx, ldb_dn_get_linearized(role_owner_dn));
1508 (*ctr1)->count = count;
1509 (*ctr1)->array = names;
1511 return WERR_OK;
1514 WERROR dcesrv_drsuapi_CrackNamesByNameFormat(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1515 const struct drsuapi_DsNameRequest1 *req1,
1516 struct drsuapi_DsNameCtr1 **ctr1)
1518 struct drsuapi_DsNameInfo1 *names;
1519 uint32_t i, count;
1520 WERROR status;
1522 *ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
1523 W_ERROR_HAVE_NO_MEMORY(*ctr1);
1525 count = req1->count;
1526 names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1527 W_ERROR_HAVE_NO_MEMORY(names);
1529 for (i=0; i < count; i++) {
1530 status = DsCrackNameOneName(sam_ctx, mem_ctx,
1531 req1->format_flags,
1532 req1->format_offered,
1533 req1->format_desired,
1534 req1->names[i].str,
1535 &names[i]);
1536 if (!W_ERROR_IS_OK(status)) {
1537 return status;
1541 (*ctr1)->count = count;
1542 (*ctr1)->array = names;
1544 return WERR_OK;
1547 WERROR dcesrv_drsuapi_ListInfoServer(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1548 const struct drsuapi_DsNameRequest1 *req1,
1549 struct drsuapi_DsNameCtr1 **_ctr1)
1551 struct drsuapi_DsNameInfo1 *names;
1552 struct ldb_result *res;
1553 struct ldb_dn *server_dn, *dn;
1554 struct drsuapi_DsNameCtr1 *ctr1;
1555 int ret, i;
1556 const char *str;
1557 const char *attrs[] = {
1558 "dn",
1559 "dNSHostName",
1560 "serverReference",
1561 NULL
1564 *_ctr1 = NULL;
1566 ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
1567 W_ERROR_HAVE_NO_MEMORY(ctr1);
1570 * No magic value here, we have to return 3 entries according to the
1571 * MS-DRSR.pdf
1573 ctr1->count = 3;
1574 names = talloc_zero_array(ctr1, struct drsuapi_DsNameInfo1,
1575 ctr1->count);
1576 W_ERROR_HAVE_NO_MEMORY(names);
1577 ctr1->array = names;
1579 for (i=0; i < ctr1->count; i++) {
1580 names[i].status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1582 *_ctr1 = ctr1;
1584 if (req1->count != 1) {
1585 DEBUG(1, ("Expected a count of 1 for the ListInfoServer crackname \n"));
1586 return WERR_OK;
1589 if (req1->names[0].str == NULL) {
1590 return WERR_OK;
1593 server_dn = ldb_dn_new(mem_ctx, sam_ctx, req1->names[0].str);
1594 W_ERROR_HAVE_NO_MEMORY(server_dn);
1596 ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_ONELEVEL,
1597 NULL, "(objectClass=nTDSDSA)");
1599 if (ret != LDB_SUCCESS) {
1600 DEBUG(1, ("Search for objectClass=nTDSDSA "
1601 "returned less than 1 objects\n"));
1602 return WERR_OK;
1605 if (res->count != 1) {
1606 DEBUG(1, ("Search for objectClass=nTDSDSA "
1607 "returned less than 1 objects\n"));
1608 return WERR_OK;
1611 if (res->msgs[0]->dn) {
1612 names[0].result_name = ldb_dn_alloc_linearized(names, res->msgs[0]->dn);
1613 W_ERROR_HAVE_NO_MEMORY(names[0].result_name);
1614 names[0].status = DRSUAPI_DS_NAME_STATUS_OK;
1617 talloc_free(res);
1619 ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_BASE,
1620 attrs, "(objectClass=*)");
1621 if (ret != LDB_SUCCESS) {
1622 DEBUG(1, ("Search for objectClass=* on dn %s"
1623 "returned %s\n", req1->names[0].str,
1624 ldb_strerror(ret)));
1625 return WERR_OK;
1628 if (res->count != 1) {
1629 DEBUG(1, ("Search for objectClass=* on dn %s"
1630 "returned less than 1 objects\n", req1->names[0].str));
1631 return WERR_OK;
1634 str = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
1635 if (str != NULL) {
1636 names[1].result_name = talloc_strdup(names, str);
1637 W_ERROR_HAVE_NO_MEMORY(names[1].result_name);
1638 names[1].status = DRSUAPI_DS_NAME_STATUS_OK;
1641 dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, res->msgs[0], "serverReference");
1642 if (dn != NULL) {
1643 names[2].result_name = ldb_dn_alloc_linearized(names, dn);
1644 W_ERROR_HAVE_NO_MEMORY(names[2].result_name);
1645 names[2].status = DRSUAPI_DS_NAME_STATUS_OK;
1648 talloc_free(dn);
1649 talloc_free(res);
1651 return WERR_OK;