• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/dsdb/samdb/
1/*
2   Unix SMB/CIFS implementation.
3
4   endpoint server for the drsuapi pipe
5   DsCrackNames()
6
7   Copyright (C) Stefan Metzmacher 2004
8   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 3 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program.  If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "includes.h"
25#include "librpc/gen_ndr/drsuapi.h"
26#include "rpc_server/common/common.h"
27#include "lib/events/events.h"
28#include "lib/ldb/include/ldb.h"
29#include "lib/ldb/include/ldb_errors.h"
30#include "system/kerberos.h"
31#include "auth/kerberos/kerberos.h"
32#include "libcli/ldap/ldap_ndr.h"
33#include "libcli/security/security.h"
34#include "librpc/gen_ndr/ndr_misc.h"
35#include "auth/auth.h"
36#include "../lib/util/util_ldb.h"
37#include "dsdb/samdb/samdb.h"
38#include "param/param.h"
39
40static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
41				   struct smb_krb5_context *smb_krb5_context,
42				   uint32_t format_flags, uint32_t format_offered, uint32_t 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);
46static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
47					uint32_t format_offered, uint32_t format_desired,
48					struct ldb_dn *name_dn, const char *name,
49					struct drsuapi_DsNameInfo1 *info1);
50
51static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context,
52					const char *name,
53					struct drsuapi_DsNameInfo1 *info1)
54{
55	krb5_error_code ret;
56	krb5_principal principal;
57	/* perhaps it's a principal with a realm, so return the right 'domain only' response */
58	char *realm;
59	ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
60				    KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
61	if (ret) {
62		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
63		return WERR_OK;
64	}
65
66	/* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
67	realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
68
69	info1->dns_domain_name	= talloc_strdup(mem_ctx, realm);
70	krb5_free_principal(smb_krb5_context->krb5_context, principal);
71
72	W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
73
74	info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
75	return WERR_OK;
76}
77
78static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx,
79						      TALLOC_CTX *mem_ctx,
80						      const char *alias_from,
81						      char **alias_to)
82{
83	int i;
84	int ret;
85	struct ldb_result *res;
86	struct ldb_message_element *spnmappings;
87	TALLOC_CTX *tmp_ctx;
88	struct ldb_dn *service_dn;
89	char *service_dn_str;
90
91	const char *directory_attrs[] = {
92		"sPNMappings",
93		NULL
94	};
95
96	tmp_ctx = talloc_new(mem_ctx);
97	if (!tmp_ctx) {
98		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
99	}
100
101	service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
102	if ( ! ldb_dn_add_base(service_dn, samdb_config_dn(ldb_ctx))) {
103		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
104	}
105	service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
106	if ( ! service_dn_str) {
107		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
108	}
109
110	ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
111			 directory_attrs, "(objectClass=nTDSService)");
112
113	if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
114		DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx)));
115		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
116	} else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
117		DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
118		return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
119	} else if (res->count != 1) {
120		talloc_free(res);
121		DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
122		return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
123	}
124
125	spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
126	if (!spnmappings || spnmappings->num_values == 0) {
127		DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str));
128		talloc_free(tmp_ctx);
129		return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
130	}
131
132	for (i = 0; i < spnmappings->num_values; i++) {
133		char *mapping, *p, *str;
134		mapping = talloc_strdup(tmp_ctx,
135					(const char *)spnmappings->values[i].data);
136		if (!mapping) {
137			DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
138			talloc_free(tmp_ctx);
139			return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
140		}
141
142		/* C string manipulation sucks */
143
144		p = strchr(mapping, '=');
145		if (!p) {
146			DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
147				  service_dn_str, mapping));
148			talloc_free(tmp_ctx);
149			return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
150		}
151		p[0] = '\0';
152		p++;
153		do {
154			str = p;
155			p = strchr(p, ',');
156			if (p) {
157				p[0] = '\0';
158				p++;
159			}
160			if (strcasecmp(str, alias_from) == 0) {
161				*alias_to = mapping;
162				talloc_steal(mem_ctx, mapping);
163				talloc_free(tmp_ctx);
164				return DRSUAPI_DS_NAME_STATUS_OK;
165			}
166		} while (p);
167	}
168	DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
169	talloc_free(tmp_ctx);
170	return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
171}
172
173/* When cracking a ServicePrincipalName, many services may be served
174 * by the host/ servicePrincipalName.  The incoming query is for cifs/
175 * but we translate it here, and search on host/.  This is done after
176 * the cifs/ entry has been searched for, making this a fallback */
177
178static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
179				  struct smb_krb5_context *smb_krb5_context,
180				  uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
181				  const char *name, struct drsuapi_DsNameInfo1 *info1)
182{
183	WERROR wret;
184	krb5_error_code ret;
185	krb5_principal principal;
186	const char *service, *dns_name;
187	char *new_service;
188	char *new_princ;
189	enum drsuapi_DsNameStatus namestatus;
190
191	/* parse principal */
192	ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
193				    name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
194	if (ret) {
195		DEBUG(2, ("Could not parse principal: %s: %s",
196			  name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
197							   ret, mem_ctx)));
198		return WERR_NOMEM;
199	}
200
201	/* grab cifs/, http/ etc */
202
203	/* This is checked for in callers, but be safe */
204	if (principal->name.name_string.len < 2) {
205		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
206		return WERR_OK;
207	}
208	service = principal->name.name_string.val[0];
209	dns_name = principal->name.name_string.val[1];
210
211	/* MAP it */
212	namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context,
213					  sam_ctx, mem_ctx,
214					  service, &new_service);
215
216	if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
217		info1->status		= DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
218		info1->dns_domain_name	= talloc_strdup(mem_ctx, dns_name);
219		if (!info1->dns_domain_name) {
220			krb5_free_principal(smb_krb5_context->krb5_context, principal);
221			return WERR_NOMEM;
222		}
223		return WERR_OK;
224	} else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
225		info1->status = namestatus;
226		krb5_free_principal(smb_krb5_context->krb5_context, principal);
227		return WERR_OK;
228	}
229
230	/* ooh, very nasty playing around in the Principal... */
231	free(principal->name.name_string.val[0]);
232	principal->name.name_string.val[0] = strdup(new_service);
233	if (!principal->name.name_string.val[0]) {
234		krb5_free_principal(smb_krb5_context->krb5_context, principal);
235		return WERR_NOMEM;
236	}
237
238	/* reform principal */
239	ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
240				      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
241
242	if (ret) {
243		krb5_free_principal(smb_krb5_context->krb5_context, principal);
244		return WERR_NOMEM;
245	}
246
247	wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
248				  new_princ, info1);
249	free(new_princ);
250	if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
251		info1->status		= DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
252		info1->dns_domain_name	= talloc_strdup(mem_ctx, dns_name);
253		if (!info1->dns_domain_name) {
254			wret = WERR_NOMEM;
255		}
256	}
257	krb5_free_principal(smb_krb5_context->krb5_context, principal);
258	return wret;
259}
260
261/* Subcase of CrackNames, for the userPrincipalName */
262
263static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
264			     struct smb_krb5_context *smb_krb5_context,
265			     uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
266			     const char *name, struct drsuapi_DsNameInfo1 *info1)
267{
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;
278
279	/* Prevent recursion */
280	if (!name) {
281		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
282		return WERR_OK;
283	}
284
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;
290	}
291
292	realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
293
294	ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
295				     samdb_partitions_dn(sam_ctx, mem_ctx),
296				     LDB_SCOPE_ONELEVEL,
297				     domain_attrs,
298				     "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
299				     ldb_binary_encode_string(mem_ctx, realm),
300				     ldb_binary_encode_string(mem_ctx, realm));
301
302	if (ldb_ret != LDB_SUCCESS) {
303		DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx)));
304		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
305		return WERR_OK;
306	}
307
308	switch (domain_res->count) {
309	case 1:
310		break;
311	case 0:
312		return dns_domain_from_principal(mem_ctx, smb_krb5_context,
313						 name, info1);
314	default:
315		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
316		return WERR_OK;
317	}
318
319	ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
320				      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
321	krb5_free_principal(smb_krb5_context->krb5_context, principal);
322
323	if (ret) {
324		free(unparsed_name_short);
325		return WERR_NOMEM;
326	}
327
328	/* This may need to be extended for more userPrincipalName variations */
329	result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
330					ldb_binary_encode_string(mem_ctx, unparsed_name_short));
331
332	domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
333
334	if (!result_filter || !domain_filter) {
335		free(unparsed_name_short);
336		return WERR_NOMEM;
337	}
338	status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
339				      smb_krb5_context,
340				      format_flags, format_offered, format_desired,
341				      NULL, unparsed_name_short, domain_filter, result_filter,
342				      info1);
343	free(unparsed_name_short);
344
345	return status;
346}
347
348/* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
349
350WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
351			  uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
352			  const char *name, struct drsuapi_DsNameInfo1 *info1)
353{
354	krb5_error_code ret;
355	const char *domain_filter = NULL;
356	const char *result_filter = NULL;
357	struct ldb_dn *name_dn = NULL;
358
359	struct smb_krb5_context *smb_krb5_context = NULL;
360
361	info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
362	info1->dns_domain_name = NULL;
363	info1->result_name = NULL;
364
365	if (!name) {
366		return WERR_INVALID_PARAM;
367	}
368
369	/* TODO: - fill the correct names in all cases!
370	 *       - handle format_flags
371	 */
372
373	/* here we need to set the domain_filter and/or the result_filter */
374	switch (format_offered) {
375	case DRSUAPI_DS_NAME_FORMAT_UNKNOWN:
376	{
377		int i;
378		enum drsuapi_DsNameFormat formats[] = {
379			DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
380			DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL,
381			DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY,
382			DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
383			DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
384			DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
385		};
386		WERROR werr;
387		for (i=0; i < ARRAY_SIZE(formats); i++) {
388			werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1);
389			if (!W_ERROR_IS_OK(werr)) {
390				return werr;
391			}
392			if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
393				return werr;
394			}
395		}
396		return werr;
397	}
398
399	case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
400	case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
401	{
402		char *str, *s, *account;
403
404		if (strlen(name) == 0) {
405			info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
406			return WERR_OK;
407		}
408
409		str = talloc_strdup(mem_ctx, name);
410		W_ERROR_HAVE_NO_MEMORY(str);
411
412		if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
413			/* Look backwards for the \n, and replace it with / */
414			s = strrchr(str, '\n');
415			if (!s) {
416				info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
417				return WERR_OK;
418			}
419			s[0] = '/';
420		}
421
422		s = strchr(str, '/');
423		if (!s) {
424			/* there must be at least one / */
425			info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
426			return WERR_OK;
427		}
428
429		s[0] = '\0';
430		s++;
431
432		domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))",
433						ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str)));
434		W_ERROR_HAVE_NO_MEMORY(domain_filter);
435
436		/* There may not be anything after the domain component (search for the domain itself) */
437		if (s[0]) {
438
439			account = strrchr(s, '/');
440			if (!account) {
441				account = s;
442			} else {
443				account++;
444			}
445			account = ldb_binary_encode_string(mem_ctx, account);
446			W_ERROR_HAVE_NO_MEMORY(account);
447			result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
448							account);
449			W_ERROR_HAVE_NO_MEMORY(result_filter);
450		}
451		break;
452	}
453	case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
454		char *p;
455		char *domain;
456		const char *account = NULL;
457
458		domain = talloc_strdup(mem_ctx, name);
459		W_ERROR_HAVE_NO_MEMORY(domain);
460
461		p = strchr(domain, '\\');
462		if (!p) {
463			/* invalid input format */
464			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
465			return WERR_OK;
466		}
467		p[0] = '\0';
468
469		if (p[1]) {
470			account = &p[1];
471		}
472
473		domain_filter = talloc_asprintf(mem_ctx,
474						"(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
475						ldb_binary_encode_string(mem_ctx, domain));
476		W_ERROR_HAVE_NO_MEMORY(domain_filter);
477		if (account) {
478			result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
479							ldb_binary_encode_string(mem_ctx, account));
480			W_ERROR_HAVE_NO_MEMORY(result_filter);
481		}
482
483		talloc_free(domain);
484		break;
485	}
486
487		/* A LDAP DN as a string */
488	case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
489		domain_filter = NULL;
490		name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
491		if (! ldb_dn_validate(name_dn)) {
492			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
493			return WERR_OK;
494		}
495		break;
496	}
497
498		/* A GUID as a string */
499	case DRSUAPI_DS_NAME_FORMAT_GUID: {
500		struct GUID guid;
501		char *ldap_guid;
502		NTSTATUS nt_status;
503		domain_filter = NULL;
504
505		nt_status = GUID_from_string(name, &guid);
506		if (!NT_STATUS_IS_OK(nt_status)) {
507			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
508			return WERR_OK;
509		}
510
511		ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
512		if (!ldap_guid) {
513			return WERR_NOMEM;
514		}
515		result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
516						ldap_guid);
517		W_ERROR_HAVE_NO_MEMORY(result_filter);
518		break;
519	}
520	case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
521		domain_filter = NULL;
522
523		result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
524						ldb_binary_encode_string(mem_ctx, name),
525						ldb_binary_encode_string(mem_ctx, name));
526		W_ERROR_HAVE_NO_MEMORY(result_filter);
527		break;
528	}
529
530		/* A S-1234-5678 style string */
531	case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
532		struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
533		char *ldap_sid;
534
535		domain_filter = NULL;
536		if (!sid) {
537			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
538			return WERR_OK;
539		}
540		ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
541						   sid);
542		if (!ldap_sid) {
543			return WERR_NOMEM;
544		}
545		result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
546						ldap_sid);
547		W_ERROR_HAVE_NO_MEMORY(result_filter);
548		break;
549	}
550	case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
551		krb5_principal principal;
552		char *unparsed_name;
553
554		ret = smb_krb5_init_context(mem_ctx,
555					    ldb_get_event_context(sam_ctx),
556					    (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
557					    &smb_krb5_context);
558
559		if (ret) {
560			return WERR_NOMEM;
561		}
562
563		/* Ensure we reject compleate junk first */
564		ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
565		if (ret) {
566			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
567			return WERR_OK;
568		}
569
570		domain_filter = NULL;
571
572		/* By getting the unparsed name here, we ensure the escaping is correct (and trust the client less) */
573		ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name);
574		if (ret) {
575			krb5_free_principal(smb_krb5_context->krb5_context, principal);
576			return WERR_NOMEM;
577		}
578
579		krb5_free_principal(smb_krb5_context->krb5_context, principal);
580
581		/* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */
582		result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))",
583						ldb_binary_encode_string(mem_ctx, unparsed_name));
584
585		free(unparsed_name);
586		W_ERROR_HAVE_NO_MEMORY(result_filter);
587		break;
588	}
589	case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
590		krb5_principal principal;
591		char *unparsed_name_short;
592		char *service;
593
594		ret = smb_krb5_init_context(mem_ctx,
595					    ldb_get_event_context(sam_ctx),
596					    (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
597					    &smb_krb5_context);
598
599		if (ret) {
600			return WERR_NOMEM;
601		}
602
603		ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
604		if (ret == 0 && principal->name.name_string.len < 2) {
605			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
606			krb5_free_principal(smb_krb5_context->krb5_context, principal);
607			return WERR_OK;
608		}
609		ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
610					    KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
611		if (ret) {
612			krb5_free_principal(smb_krb5_context->krb5_context, principal);
613
614			return dns_domain_from_principal(mem_ctx, smb_krb5_context,
615							 name, info1);
616		}
617
618		domain_filter = NULL;
619
620		ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
621					      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
622		if (ret) {
623			krb5_free_principal(smb_krb5_context->krb5_context, principal);
624			return WERR_NOMEM;
625		}
626
627		service = principal->name.name_string.val[0];
628		if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) {
629			/* the 'cn' attribute is just the leading part of the name */
630			char *computer_name;
631			computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1],
632						      strcspn(principal->name.name_string.val[1], "."));
633			if (computer_name == NULL) {
634				return WERR_NOMEM;
635			}
636
637			result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
638							ldb_binary_encode_string(mem_ctx, unparsed_name_short),
639							ldb_binary_encode_string(mem_ctx, computer_name));
640		} else {
641			result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
642							ldb_binary_encode_string(mem_ctx, unparsed_name_short));
643		}
644		krb5_free_principal(smb_krb5_context->krb5_context, principal);
645		free(unparsed_name_short);
646		W_ERROR_HAVE_NO_MEMORY(result_filter);
647
648		break;
649	}
650	default: {
651		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
652		return WERR_OK;
653	}
654
655	}
656
657	if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
658		return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
659						 name_dn, name, info1);
660	}
661
662	return DsCrackNameOneFilter(sam_ctx, mem_ctx,
663				    smb_krb5_context,
664				    format_flags, format_offered, format_desired,
665				    name_dn, name,
666				    domain_filter, result_filter,
667				    info1);
668}
669
670/* Subcase of CrackNames.  It is possible to translate a LDAP-style DN
671 * (FQDN_1779) into a canoical name without actually searching the
672 * database */
673
674static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
675					uint32_t format_offered, uint32_t format_desired,
676					struct ldb_dn *name_dn, const char *name,
677					struct drsuapi_DsNameInfo1 *info1)
678{
679	char *cracked;
680	if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
681		info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
682		return WERR_OK;
683	}
684
685	switch (format_desired) {
686	case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
687		cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
688		break;
689	case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
690		cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
691		break;
692	default:
693		info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
694		return WERR_OK;
695	}
696	info1->status = DRSUAPI_DS_NAME_STATUS_OK;
697	info1->result_name	= cracked;
698	if (!cracked) {
699		return WERR_NOMEM;
700	}
701
702	return WERR_OK;
703}
704
705/* Given a filter for the domain, and one for the result, perform the
706 * ldb search. The format offered and desired flags change the
707 * behaviours, including what attributes to return.
708 *
709 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
710 */
711
712static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
713				   struct smb_krb5_context *smb_krb5_context,
714				   uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
715				   struct ldb_dn *name_dn, const char *name,
716				   const char *domain_filter, const char *result_filter,
717				   struct drsuapi_DsNameInfo1 *info1)
718{
719	int ldb_ret;
720	struct ldb_result *domain_res = NULL;
721	const char * const *domain_attrs;
722	const char * const *result_attrs;
723	struct ldb_message **result_res = NULL;
724	struct ldb_message *result = NULL;
725	struct ldb_dn *result_basedn = NULL;
726	int i;
727	char *p;
728	struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
729
730	const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
731	const char * const _result_attrs_null[] = { NULL };
732
733	const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
734	const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
735
736	const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
737	const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
738
739	const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
740	const char * const _result_attrs_guid[] = { "objectGUID", NULL};
741
742	const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
743	const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
744
745	const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
746	const char * const _result_attrs_none[] = { NULL};
747
748	/* here we need to set the attrs lists for domain and result lookups */
749	switch (format_desired) {
750	case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
751	case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
752		domain_attrs = _domain_attrs_1779;
753		result_attrs = _result_attrs_null;
754		break;
755	case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
756		domain_attrs = _domain_attrs_canonical;
757		result_attrs = _result_attrs_canonical;
758		break;
759	case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
760		domain_attrs = _domain_attrs_nt4;
761		result_attrs = _result_attrs_nt4;
762		break;
763	case DRSUAPI_DS_NAME_FORMAT_GUID:
764		domain_attrs = _domain_attrs_guid;
765		result_attrs = _result_attrs_guid;
766		break;
767	case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
768		domain_attrs = _domain_attrs_display;
769		result_attrs = _result_attrs_display;
770		break;
771	default:
772		domain_attrs = _domain_attrs_none;
773		result_attrs = _result_attrs_none;
774		break;
775	}
776
777	if (domain_filter) {
778		/* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
779		ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
780					     partitions_basedn,
781					     LDB_SCOPE_ONELEVEL,
782					     domain_attrs,
783					     "%s", domain_filter);
784
785		if (ldb_ret != LDB_SUCCESS) {
786			DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
787			info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
788			return WERR_OK;
789		}
790
791		switch (domain_res->count) {
792		case 1:
793			break;
794		case 0:
795			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
796			return WERR_OK;
797		default:
798			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
799			return WERR_OK;
800		}
801
802		info1->dns_domain_name	= samdb_result_string(domain_res->msgs[0], "dnsRoot", NULL);
803		W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
804		info1->status		= DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
805	} else {
806		info1->dns_domain_name	= NULL;
807		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
808	}
809
810	if (result_filter) {
811		int ret;
812		struct ldb_result *res;
813		if (domain_res) {
814			result_basedn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
815
816			ret = ldb_search(sam_ctx, mem_ctx, &res,
817						 result_basedn, LDB_SCOPE_SUBTREE,
818						 result_attrs, "%s", result_filter);
819			if (ret != LDB_SUCCESS) {
820				talloc_free(result_res);
821				info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
822				return WERR_OK;
823			}
824			ldb_ret = res->count;
825			result_res = res->msgs;
826		} else {
827			/* search with the 'phantom root' flag */
828			struct ldb_request *req;
829
830			res = talloc_zero(mem_ctx, struct ldb_result);
831			W_ERROR_HAVE_NO_MEMORY(res);
832
833			ret = ldb_build_search_req(&req, sam_ctx, mem_ctx,
834						   ldb_get_root_basedn(sam_ctx),
835						   LDB_SCOPE_SUBTREE,
836						   result_filter,
837						   result_attrs,
838						   NULL,
839						   res,
840						   ldb_search_default_callback,
841						   NULL);
842			if (ret == LDB_SUCCESS) {
843				struct ldb_search_options_control *search_options;
844				search_options = talloc(req, struct ldb_search_options_control);
845				W_ERROR_HAVE_NO_MEMORY(search_options);
846				search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
847
848				ret = ldb_request_add_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options);
849			}
850			if (ret != LDB_SUCCESS) {
851				talloc_free(res);
852				info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
853				return WERR_OK;
854			}
855
856			ret = ldb_request(sam_ctx, req);
857
858			if (ret == LDB_SUCCESS) {
859				ret = ldb_wait(req->handle, LDB_WAIT_ALL);
860			}
861
862			talloc_free(req);
863
864			if (ret != LDB_SUCCESS) {
865				DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s",
866					  ldb_errstring(sam_ctx)));
867				info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
868				return WERR_OK;
869			}
870			ldb_ret = res->count;
871			result_res = res->msgs;
872		}
873	} else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
874		ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
875					  result_attrs);
876	} else if (domain_res) {
877		name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
878		ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
879					  result_attrs);
880	} else {
881		/* Can't happen */
882		DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not availible: This can't happen..."));
883		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
884		return WERR_OK;
885	}
886
887	switch (ldb_ret) {
888	case 1:
889		result = result_res[0];
890		break;
891	case 0:
892		switch (format_offered) {
893		case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
894			return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
895						   smb_krb5_context,
896						   format_flags, format_offered, format_desired,
897						   name, info1);
898
899		case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
900			return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
901					      format_flags, format_offered, format_desired,
902					      name, info1);
903		}
904		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
905		return WERR_OK;
906	case -1:
907		DEBUG(2, ("DsCrackNameOneFilter result search failed: %s", ldb_errstring(sam_ctx)));
908		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
909		return WERR_OK;
910	default:
911		switch (format_offered) {
912		case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
913		case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
914		{
915			const char *canonical_name = NULL; /* Not required, but we get warnings... */
916			/* We may need to manually filter further */
917			for (i = 0; i < ldb_ret; i++) {
918				switch (format_offered) {
919				case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
920					canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
921					break;
922				case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
923					canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
924					break;
925				}
926				if (strcasecmp_m(canonical_name, name) == 0) {
927					result = result_res[i];
928					break;
929				}
930			}
931			if (!result) {
932				info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
933				return WERR_OK;
934			}
935		}
936		default:
937			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
938			return WERR_OK;
939		}
940	}
941
942	info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
943	W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
944	p = strchr(info1->dns_domain_name, '/');
945	if (p) {
946		p[0] = '\0';
947	}
948
949	/* here we can use result and domain_res[0] */
950	switch (format_desired) {
951	case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
952		info1->result_name	= ldb_dn_alloc_linearized(mem_ctx, result->dn);
953		W_ERROR_HAVE_NO_MEMORY(info1->result_name);
954
955		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
956		return WERR_OK;
957	}
958	case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
959		info1->result_name	= samdb_result_string(result, "canonicalName", NULL);
960		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
961		return WERR_OK;
962	}
963	case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
964		/* Not in the virtual ldb attribute */
965		return DsCrackNameOneSyntactical(mem_ctx,
966						 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
967						 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
968						 result->dn, name, info1);
969	}
970	case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
971
972		const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
973		const char *_acc = "", *_dom = "";
974
975		if (samdb_find_attribute(sam_ctx, result, "objectClass", "domain")) {
976
977			ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
978						     partitions_basedn,
979						     LDB_SCOPE_ONELEVEL,
980						     domain_attrs,
981						     "(ncName=%s)", ldb_dn_get_linearized(result->dn));
982
983			if (ldb_ret != LDB_SUCCESS) {
984				DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
985				info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
986				return WERR_OK;
987			}
988
989			switch (domain_res->count) {
990			case 1:
991				break;
992			case 0:
993				info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
994				return WERR_OK;
995			default:
996				info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
997				return WERR_OK;
998			}
999			_dom = samdb_result_string(domain_res->msgs[0], "nETBIOSName", NULL);
1000			W_ERROR_HAVE_NO_MEMORY(_dom);
1001		} else {
1002			_acc = samdb_result_string(result, "sAMAccountName", NULL);
1003			if (!_acc) {
1004				info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1005				return WERR_OK;
1006			}
1007			if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) {
1008				_dom = "BUILTIN";
1009			} else {
1010				const char *attrs[] = { NULL };
1011				struct ldb_result *domain_res2;
1012				struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
1013				if (!dom_sid) {
1014					return WERR_OK;
1015				}
1016				dom_sid->num_auths--;
1017				ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1018							     NULL,
1019							     LDB_SCOPE_BASE,
1020							     attrs,
1021							     "(&(objectSid=%s)(objectClass=domain))",
1022							     ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
1023
1024				if (ldb_ret != LDB_SUCCESS) {
1025					DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s", ldb_errstring(sam_ctx)));
1026					info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1027					return WERR_OK;
1028				}
1029
1030				switch (domain_res->count) {
1031				case 1:
1032					break;
1033				case 0:
1034					info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1035					return WERR_OK;
1036				default:
1037					info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1038					return WERR_OK;
1039				}
1040
1041				ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2,
1042							     partitions_basedn,
1043							     LDB_SCOPE_ONELEVEL,
1044							     domain_attrs,
1045							     "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
1046
1047				if (ldb_ret != LDB_SUCCESS) {
1048					DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
1049					info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1050					return WERR_OK;
1051				}
1052
1053				switch (domain_res2->count) {
1054				case 1:
1055					break;
1056				case 0:
1057					info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1058					return WERR_OK;
1059				default:
1060					info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1061					return WERR_OK;
1062				}
1063				_dom = samdb_result_string(domain_res2->msgs[0], "nETBIOSName", NULL);
1064				W_ERROR_HAVE_NO_MEMORY(_dom);
1065			}
1066		}
1067
1068		info1->result_name	= talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
1069		W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1070
1071		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
1072		return WERR_OK;
1073	}
1074	case DRSUAPI_DS_NAME_FORMAT_GUID: {
1075		struct GUID guid;
1076
1077		guid = samdb_result_guid(result, "objectGUID");
1078
1079		info1->result_name	= GUID_string2(mem_ctx, &guid);
1080		W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1081
1082		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
1083		return WERR_OK;
1084	}
1085	case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
1086		info1->result_name	= samdb_result_string(result, "displayName", NULL);
1087		if (!info1->result_name) {
1088			info1->result_name	= samdb_result_string(result, "sAMAccountName", NULL);
1089		}
1090		if (!info1->result_name) {
1091			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1092		} else {
1093			info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1094		}
1095		return WERR_OK;
1096	}
1097	case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
1098		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1099		return WERR_OK;
1100	}
1101	case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
1102	case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
1103		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1104		return WERR_OK;
1105	}
1106	default:
1107		info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1108		return WERR_OK;
1109	}
1110}
1111
1112/* Given a user Principal Name (such as foo@bar.com),
1113 * return the user and domain DNs.  This is used in the KDC to then
1114 * return the Keys and evaluate policy */
1115
1116NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
1117				   TALLOC_CTX *mem_ctx,
1118				   const char *user_principal_name,
1119				   struct ldb_dn **user_dn,
1120				   struct ldb_dn **domain_dn)
1121{
1122	WERROR werr;
1123	struct drsuapi_DsNameInfo1 info1;
1124	werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1125				  DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
1126				  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1127				  user_principal_name,
1128				  &info1);
1129	if (!W_ERROR_IS_OK(werr)) {
1130		return werror_to_ntstatus(werr);
1131	}
1132	switch (info1.status) {
1133	case DRSUAPI_DS_NAME_STATUS_OK:
1134		break;
1135	case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1136	case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1137	case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1138		return NT_STATUS_NO_SUCH_USER;
1139	case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1140	default:
1141		return NT_STATUS_UNSUCCESSFUL;
1142	}
1143
1144	*user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1145
1146	if (domain_dn) {
1147		werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1148					  DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1149					  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1150					  talloc_asprintf(mem_ctx, "%s/",
1151							  info1.dns_domain_name),
1152					  &info1);
1153		if (!W_ERROR_IS_OK(werr)) {
1154			return werror_to_ntstatus(werr);
1155		}
1156		switch (info1.status) {
1157		case DRSUAPI_DS_NAME_STATUS_OK:
1158			break;
1159		case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1160		case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1161		case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1162			return NT_STATUS_NO_SUCH_USER;
1163		case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1164		default:
1165			return NT_STATUS_UNSUCCESSFUL;
1166		}
1167
1168		*domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1169	}
1170
1171	return NT_STATUS_OK;
1172}
1173
1174/* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
1175 * return the user and domain DNs.  This is used in the KDC to then
1176 * return the Keys and evaluate policy */
1177
1178NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
1179				      TALLOC_CTX *mem_ctx,
1180				      const char *service_principal_name,
1181				      struct ldb_dn **user_dn,
1182				      struct ldb_dn **domain_dn)
1183{
1184	WERROR werr;
1185	struct drsuapi_DsNameInfo1 info1;
1186	werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1187				  DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
1188				  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1189				  service_principal_name,
1190				  &info1);
1191	if (!W_ERROR_IS_OK(werr)) {
1192		return werror_to_ntstatus(werr);
1193	}
1194	switch (info1.status) {
1195	case DRSUAPI_DS_NAME_STATUS_OK:
1196		break;
1197	case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1198	case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1199	case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1200		return NT_STATUS_NO_SUCH_USER;
1201	case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1202	default:
1203		return NT_STATUS_UNSUCCESSFUL;
1204	}
1205
1206	*user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1207
1208	if (domain_dn) {
1209		werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1210					  DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1211					  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1212					  talloc_asprintf(mem_ctx, "%s/",
1213							  info1.dns_domain_name),
1214					  &info1);
1215		if (!W_ERROR_IS_OK(werr)) {
1216			return werror_to_ntstatus(werr);
1217		}
1218		switch (info1.status) {
1219		case DRSUAPI_DS_NAME_STATUS_OK:
1220			break;
1221		case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1222		case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1223		case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1224			return NT_STATUS_NO_SUCH_USER;
1225		case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1226		default:
1227			return NT_STATUS_UNSUCCESSFUL;
1228		}
1229
1230		*domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1231	}
1232
1233	return NT_STATUS_OK;
1234}
1235
1236NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1237				struct tevent_context *ev_ctx,
1238				struct loadparm_context *lp_ctx,
1239				uint32_t format_offered,
1240				const char *name,
1241				const char **nt4_domain, const char **nt4_account)
1242{
1243	WERROR werr;
1244	struct drsuapi_DsNameInfo1 info1;
1245	struct ldb_context *ldb;
1246	char *p;
1247
1248	/* Handle anonymous bind */
1249	if (!name || !*name) {
1250		*nt4_domain = "";
1251		*nt4_account = "";
1252		return NT_STATUS_OK;
1253	}
1254
1255	ldb = samdb_connect(mem_ctx, ev_ctx, lp_ctx, system_session(mem_ctx, lp_ctx));
1256	if (ldb == NULL) {
1257		return NT_STATUS_INTERNAL_DB_CORRUPTION;
1258	}
1259
1260	werr = DsCrackNameOneName(ldb, mem_ctx, 0,
1261				  format_offered,
1262				  DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
1263				  name,
1264				  &info1);
1265	if (!W_ERROR_IS_OK(werr)) {
1266		return werror_to_ntstatus(werr);
1267	}
1268	switch (info1.status) {
1269	case DRSUAPI_DS_NAME_STATUS_OK:
1270		break;
1271	case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1272	case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1273	case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1274		return NT_STATUS_NO_SUCH_USER;
1275	case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1276	default:
1277		return NT_STATUS_UNSUCCESSFUL;
1278	}
1279
1280	*nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
1281	if (*nt4_domain == NULL) {
1282		return NT_STATUS_NO_MEMORY;
1283	}
1284
1285	p = strchr(*nt4_domain, '\\');
1286	if (!p) {
1287		return NT_STATUS_INVALID_PARAMETER;
1288	}
1289	p[0] = '\0';
1290
1291	if (p[1]) {
1292		*nt4_account = talloc_strdup(mem_ctx, &p[1]);
1293		if (*nt4_account == NULL) {
1294			return NT_STATUS_NO_MEMORY;
1295		}
1296	}
1297
1298	return NT_STATUS_OK;
1299}
1300
1301NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1302				     struct tevent_context *ev_ctx,
1303				     struct loadparm_context *lp_ctx,
1304				     const char *name,
1305				     const char **nt4_domain,
1306				     const char **nt4_account)
1307{
1308	uint32_t format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN;
1309
1310	/* Handle anonymous bind */
1311	if (!name || !*name) {
1312		*nt4_domain = "";
1313		*nt4_account = "";
1314		return NT_STATUS_OK;
1315	}
1316
1317	if (strchr_m(name, '=')) {
1318		format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
1319	} else if (strchr_m(name, '@')) {
1320		format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
1321	} else if (strchr_m(name, '\\')) {
1322		format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
1323	} else if (strchr_m(name, '/')) {
1324		format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
1325	} else {
1326		return NT_STATUS_NO_SUCH_USER;
1327	}
1328
1329	return crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, format_offered, name, nt4_domain, nt4_account);
1330}
1331