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 2 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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24 
25 #include "includes.h"
26 #include "librpc/gen_ndr/drsuapi.h"
27 #include "rpc_server/common/common.h"
28 #include "lib/ldb/include/ldb_errors.h"
29 #include "system/kerberos.h"
30 #include "auth/kerberos/kerberos.h"
31 #include "libcli/ldap/ldap.h"
32 #include "libcli/security/security.h"
33 #include "librpc/gen_ndr/ndr_misc.h"
34 #include "auth/auth.h"
35 #include "db_wrap.h"
36 #include "dsdb/samdb/samdb.h"
37 
38 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
39 				   struct smb_krb5_context *smb_krb5_context,
40 				   uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
41 				   struct ldb_dn *name_dn, const char *name,
42 				   const char *domain_filter, const char *result_filter,
43 				   struct drsuapi_DsNameInfo1 *info1);
44 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
45 					uint32_t format_offered, uint32_t format_desired,
46 					struct ldb_dn *name_dn, const char *name,
47 					struct drsuapi_DsNameInfo1 *info1);
48 
LDB_lookup_spn_alias(krb5_context context,struct ldb_context * ldb_ctx,TALLOC_CTX * mem_ctx,const char * alias_from,char ** alias_to)49 static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx,
50 						      TALLOC_CTX *mem_ctx,
51 						      const char *alias_from,
52 						      char **alias_to)
53 {
54 	int i;
55 	int ret;
56 	struct ldb_result *res;
57 	struct ldb_message_element *spnmappings;
58 	TALLOC_CTX *tmp_ctx;
59 	struct ldb_dn *service_dn;
60 	char *service_dn_str;
61 
62 	const char *directory_attrs[] = {
63 		"sPNMappings",
64 		NULL
65 	};
66 
67 	tmp_ctx = talloc_new(mem_ctx);
68 	if (!tmp_ctx) {
69 		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
70 	}
71 
72 	service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration");
73 	if ( ! ldb_dn_add_base(service_dn, samdb_base_dn(ldb_ctx))) {
74 		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
75 	}
76 	service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
77 	if ( ! service_dn_str) {
78 		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
79 	}
80 
81 	ret = ldb_search(ldb_ctx, service_dn, LDB_SCOPE_BASE, "(objectClass=nTDSService)",
82 			 directory_attrs, &res);
83 
84 	if (ret != LDB_SUCCESS) {
85 		DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx)));
86 		return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
87 	} else if (res->count != 1) {
88 		talloc_free(res);
89 		DEBUG(1, ("ldb_search: dn: %s found %d times!", service_dn_str, res->count));
90 		return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
91 	}
92 	talloc_steal(tmp_ctx, res);
93 
94 	spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
95 	if (!spnmappings || spnmappings->num_values == 0) {
96 		DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str));
97 		talloc_free(tmp_ctx);
98 		return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
99 	}
100 
101 	for (i = 0; i < spnmappings->num_values; i++) {
102 		char *mapping, *p, *str;
103 		mapping = talloc_strdup(tmp_ctx,
104 					(const char *)spnmappings->values[i].data);
105 		if (!mapping) {
106 			DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
107 			talloc_free(tmp_ctx);
108 			return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
109 		}
110 
111 		/* C string manipulation sucks */
112 
113 		p = strchr(mapping, '=');
114 		if (!p) {
115 			DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
116 				  service_dn_str, mapping));
117 			talloc_free(tmp_ctx);
118 			return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
119 		}
120 		p[0] = '\0';
121 		p++;
122 		do {
123 			str = p;
124 			p = strchr(p, ',');
125 			if (p) {
126 				p[0] = '\0';
127 				p++;
128 			}
129 			if (strcasecmp(str, alias_from) == 0) {
130 				*alias_to = mapping;
131 				talloc_steal(mem_ctx, mapping);
132 				talloc_free(tmp_ctx);
133 				return DRSUAPI_DS_NAME_STATUS_OK;
134 			}
135 		} while (p);
136 	}
137 	DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
138 	talloc_free(tmp_ctx);
139 	return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
140 }
141 
142 /* When cracking a ServicePrincipalName, many services may be served
143  * by the host/ servicePrincipalName.  The incoming query is for cifs/
144  * but we translate it here, and search on host/.  This is done after
145  * the cifs/ entry has been searched for, making this a fallback */
146 
DsCrackNameSPNAlias(struct ldb_context * sam_ctx,TALLOC_CTX * mem_ctx,struct smb_krb5_context * smb_krb5_context,uint32_t format_flags,uint32_t format_offered,uint32_t format_desired,const char * name,struct drsuapi_DsNameInfo1 * info1)147 static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
148 				  struct smb_krb5_context *smb_krb5_context,
149 				  uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
150 				  const char *name, struct drsuapi_DsNameInfo1 *info1)
151 {
152 	WERROR wret;
153 	krb5_error_code ret;
154 	krb5_principal principal;
155 	const char *service;
156 	char *new_service;
157 	char *new_princ;
158 	enum drsuapi_DsNameStatus namestatus;
159 
160 	/* parse principal */
161 	ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
162 				    name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
163 	if (ret) {
164 		DEBUG(2, ("Could not parse principal: %s: %s",
165 			  name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
166 							   ret, mem_ctx)));
167 		return WERR_NOMEM;
168 	}
169 
170 	/* grab cifs/, http/ etc */
171 
172 	/* This is checked for in callers, but be safe */
173 	if (principal->name.name_string.len < 2) {
174 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
175 		return WERR_OK;
176 	}
177 	service = principal->name.name_string.val[0];
178 
179 	/* MAP it */
180 	namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context,
181 					  sam_ctx, mem_ctx,
182 					  service, &new_service);
183 
184 	if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
185 		info1->status = namestatus;
186 		return WERR_OK;
187 	}
188 
189 	if (ret != 0) {
190 		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
191 		return WERR_OK;
192 	}
193 
194 	/* ooh, very nasty playing around in the Principal... */
195 	free(principal->name.name_string.val[0]);
196 	principal->name.name_string.val[0] = strdup(new_service);
197 	if (!principal->name.name_string.val[0]) {
198 		krb5_free_principal(smb_krb5_context->krb5_context, principal);
199 		return WERR_NOMEM;
200 	}
201 
202 	/* reform principal */
203 	ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
204 				      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
205 
206 	krb5_free_principal(smb_krb5_context->krb5_context, principal);
207 
208 	if (ret) {
209 		return WERR_NOMEM;
210 	}
211 
212 	wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
213 				  new_princ, info1);
214 	free(new_princ);
215 	return wret;
216 }
217 
218 /* Subcase of CrackNames, for the userPrincipalName */
219 
DsCrackNameUPN(struct ldb_context * sam_ctx,TALLOC_CTX * mem_ctx,struct smb_krb5_context * smb_krb5_context,uint32_t format_flags,uint32_t format_offered,uint32_t format_desired,const char * name,struct drsuapi_DsNameInfo1 * info1)220 static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
221 			     struct smb_krb5_context *smb_krb5_context,
222 			     uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
223 			     const char *name, struct drsuapi_DsNameInfo1 *info1)
224 {
225 	WERROR status;
226 	const char *domain_filter = NULL;
227 	const char *result_filter = NULL;
228 	krb5_error_code ret;
229 	krb5_principal principal;
230 	char **realm;
231 	char *unparsed_name_short;
232 
233 	/* Prevent recursion */
234 	if (!name) {
235 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
236 		return WERR_OK;
237 	}
238 
239 	ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
240 				    KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal);
241 	if (ret) {
242 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
243 		return WERR_OK;
244 	}
245 
246 	domain_filter = NULL;
247 	realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
248 	domain_filter = talloc_asprintf(mem_ctx,
249 					"(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
250 					ldb_binary_encode_string(mem_ctx, *realm),
251 					ldb_binary_encode_string(mem_ctx, *realm));
252 	ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
253 				      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
254 	krb5_free_principal(smb_krb5_context->krb5_context, principal);
255 
256 	if (ret) {
257 		free(unparsed_name_short);
258 		return WERR_NOMEM;
259 	}
260 
261 	/* This may need to be extended for more userPrincipalName variations */
262 	result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
263 					ldb_binary_encode_string(mem_ctx, unparsed_name_short));
264 	if (!result_filter || !domain_filter) {
265 		free(unparsed_name_short);
266 		return WERR_NOMEM;
267 	}
268 	status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
269 				      smb_krb5_context,
270 				      format_flags, format_offered, format_desired,
271 				      NULL, unparsed_name_short, domain_filter, result_filter,
272 				      info1);
273 	free(unparsed_name_short);
274 	return status;
275 }
276 
277 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
278 
DsCrackNameOneName(struct ldb_context * sam_ctx,TALLOC_CTX * mem_ctx,uint32_t format_flags,uint32_t format_offered,uint32_t format_desired,const char * name,struct drsuapi_DsNameInfo1 * info1)279 WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
280 			  uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
281 			  const char *name, struct drsuapi_DsNameInfo1 *info1)
282 {
283 	krb5_error_code ret;
284 	const char *domain_filter = NULL;
285 	const char *result_filter = NULL;
286 	struct ldb_dn *name_dn = NULL;
287 
288 	struct smb_krb5_context *smb_krb5_context;
289 	ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context);
290 
291 	if (ret) {
292 		return WERR_NOMEM;
293 	}
294 
295 	info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
296 	info1->dns_domain_name = NULL;
297 	info1->result_name = NULL;
298 
299 	if (!name) {
300 		return WERR_INVALID_PARAM;
301 	}
302 
303 	/* TODO: - fill the correct names in all cases!
304 	 *       - handle format_flags
305 	 */
306 
307 	/* here we need to set the domain_filter and/or the result_filter */
308 	switch (format_offered) {
309 	case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
310 		char *str;
311 
312 		str = talloc_strdup(mem_ctx, name);
313 		W_ERROR_HAVE_NO_MEMORY(str);
314 
315 		if (strlen(str) == 0 || str[strlen(str)-1] != '/') {
316 			info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
317 			return WERR_OK;
318 		}
319 
320 		str[strlen(str)-1] = '\0';
321 
322 		domain_filter = talloc_asprintf(mem_ctx,
323 						"(&(&(&(dnsRoot=%s)(objectclass=crossRef)))(nETBIOSName=*)(ncName=*))",
324 						ldb_binary_encode_string(mem_ctx, str));
325 		W_ERROR_HAVE_NO_MEMORY(domain_filter);
326 
327 		break;
328 	}
329 	case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
330 		char *p;
331 		char *domain;
332 		const char *account = NULL;
333 
334 		domain = talloc_strdup(mem_ctx, name);
335 		W_ERROR_HAVE_NO_MEMORY(domain);
336 
337 		p = strchr(domain, '\\');
338 		if (!p) {
339 			/* invalid input format */
340 			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
341 			return WERR_OK;
342 		}
343 		p[0] = '\0';
344 
345 		if (p[1]) {
346 			account = &p[1];
347 		}
348 
349 		domain_filter = talloc_asprintf(mem_ctx,
350 						"(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
351 						ldb_binary_encode_string(mem_ctx, domain));
352 		W_ERROR_HAVE_NO_MEMORY(domain_filter);
353 		if (account) {
354 			result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
355 							ldb_binary_encode_string(mem_ctx, account));
356 			W_ERROR_HAVE_NO_MEMORY(result_filter);
357 		}
358 
359 		talloc_free(domain);
360 		break;
361 	}
362 
363 		/* A LDAP DN as a string */
364 	case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
365 		domain_filter = NULL;
366 		name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
367 		if (! ldb_dn_validate(name_dn)) {
368 			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
369 			return WERR_OK;
370 		}
371 		break;
372 	}
373 
374 		/* A GUID as a string */
375 	case DRSUAPI_DS_NAME_FORMAT_GUID: {
376 		struct GUID guid;
377 		char *ldap_guid;
378 		NTSTATUS nt_status;
379 		domain_filter = NULL;
380 
381 		nt_status = GUID_from_string(name, &guid);
382 		if (!NT_STATUS_IS_OK(nt_status)) {
383 			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
384 			return WERR_OK;
385 		}
386 
387 		ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
388 		if (!ldap_guid) {
389 			return WERR_NOMEM;
390 		}
391 		result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
392 						ldap_guid);
393 		W_ERROR_HAVE_NO_MEMORY(result_filter);
394 		break;
395 	}
396 	case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
397 		domain_filter = NULL;
398 
399 		result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
400 						ldb_binary_encode_string(mem_ctx, name),
401 						ldb_binary_encode_string(mem_ctx, name));
402 		W_ERROR_HAVE_NO_MEMORY(result_filter);
403 		break;
404 	}
405 
406 		/* A S-1234-5678 style string */
407 	case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
408 		struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
409 		char *ldap_sid;
410 
411 		domain_filter = NULL;
412 		if (!sid) {
413 			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
414 			return WERR_OK;
415 		}
416 		ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
417 						   sid);
418 		if (!ldap_sid) {
419 			return WERR_NOMEM;
420 		}
421 		result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
422 						ldap_sid);
423 		W_ERROR_HAVE_NO_MEMORY(result_filter);
424 		break;
425 	}
426 	case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
427 		krb5_principal principal;
428 		char *unparsed_name;
429 		ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
430 		if (ret) {
431 			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
432 			return WERR_OK;
433 		}
434 
435 		domain_filter = NULL;
436 
437 		ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name);
438 		if (ret) {
439 			krb5_free_principal(smb_krb5_context->krb5_context, principal);
440 			return WERR_NOMEM;
441 		}
442 
443 		krb5_free_principal(smb_krb5_context->krb5_context, principal);
444 		result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))",
445 						ldb_binary_encode_string(mem_ctx, unparsed_name));
446 
447 		free(unparsed_name);
448 		W_ERROR_HAVE_NO_MEMORY(result_filter);
449 		break;
450 	}
451 	case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
452 		krb5_principal principal;
453 		char *unparsed_name_short;
454 		char *service;
455 		ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
456 					    KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
457 		if (ret) {
458 			/* perhaps it's a principal with a realm, so return the right 'domain only' response */
459 			char **realm;
460 			ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
461 						    KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal);
462 			if (ret) {
463 				info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
464 				return WERR_OK;
465 			}
466 
467 			/* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
468 			realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
469 
470 			info1->dns_domain_name	= talloc_strdup(info1, *realm);
471 			krb5_free_principal(smb_krb5_context->krb5_context, principal);
472 
473 			W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
474 
475 			info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
476 			return WERR_OK;
477 
478 		} else if (principal->name.name_string.len < 2) {
479 			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
480 			return WERR_OK;
481 		}
482 
483 		domain_filter = NULL;
484 
485 		ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
486 					      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
487 		if (ret) {
488 			krb5_free_principal(smb_krb5_context->krb5_context, principal);
489 			return WERR_NOMEM;
490 		}
491 
492 		service = principal->name.name_string.val[0];
493 		if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) {
494 			/* the 'cn' attribute is just the leading part of the name */
495 			char *computer_name;
496 			computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1],
497 						      strcspn(principal->name.name_string.val[1], "."));
498 			if (computer_name == NULL) {
499 				return WERR_NOMEM;
500 			}
501 
502 			result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
503 							ldb_binary_encode_string(mem_ctx, unparsed_name_short),
504 							ldb_binary_encode_string(mem_ctx, computer_name));
505 		} else {
506 			result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
507 							ldb_binary_encode_string(mem_ctx, unparsed_name_short));
508 		}
509 		krb5_free_principal(smb_krb5_context->krb5_context, principal);
510 		free(unparsed_name_short);
511 		W_ERROR_HAVE_NO_MEMORY(result_filter);
512 
513 		break;
514 	}
515 	default: {
516 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
517 		return WERR_OK;
518 	}
519 
520 	}
521 
522 	if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
523 		return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
524 						 name_dn, name, info1);
525 	}
526 
527 	return DsCrackNameOneFilter(sam_ctx, mem_ctx,
528 				    smb_krb5_context,
529 				    format_flags, format_offered, format_desired,
530 				    name_dn, name,
531 				    domain_filter, result_filter,
532 				    info1);
533 }
534 
535 /* Subcase of CrackNames.  It is possible to translate a LDAP-style DN
536  * (FQDN_1779) into a canoical name without actually searching the
537  * database */
538 
DsCrackNameOneSyntactical(TALLOC_CTX * mem_ctx,uint32_t format_offered,uint32_t format_desired,struct ldb_dn * name_dn,const char * name,struct drsuapi_DsNameInfo1 * info1)539 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
540 					uint32_t format_offered, uint32_t format_desired,
541 					struct ldb_dn *name_dn, const char *name,
542 					struct drsuapi_DsNameInfo1 *info1)
543 {
544 	char *cracked;
545 	if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
546 		info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
547 		return WERR_OK;
548 	}
549 
550 	switch (format_desired) {
551 	case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
552 		cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
553 		break;
554 	case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
555 		cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
556 		break;
557 	default:
558 		info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
559 		return WERR_OK;
560 	}
561 	info1->status = DRSUAPI_DS_NAME_STATUS_OK;
562 	info1->result_name	= cracked;
563 	if (!cracked) {
564 		return WERR_NOMEM;
565 	}
566 
567 	return WERR_OK;
568 }
569 
570 /* Given a filter for the domain, and one for the result, perform the
571  * ldb search. The format offered and desired flags change the
572  * behaviours, including what attributes to return.
573  *
574  * The smb_krb5_context is required because we use the krb5 libs for principal parsing
575  */
576 
DsCrackNameOneFilter(struct ldb_context * sam_ctx,TALLOC_CTX * mem_ctx,struct smb_krb5_context * smb_krb5_context,uint32_t format_flags,uint32_t format_offered,uint32_t format_desired,struct ldb_dn * name_dn,const char * name,const char * domain_filter,const char * result_filter,struct drsuapi_DsNameInfo1 * info1)577 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
578 				   struct smb_krb5_context *smb_krb5_context,
579 				   uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
580 				   struct ldb_dn *name_dn, const char *name,
581 				   const char *domain_filter, const char *result_filter,
582 				   struct drsuapi_DsNameInfo1 *info1)
583 {
584 	int ldb_ret;
585 	struct ldb_message **domain_res = NULL;
586 	const char * const *domain_attrs;
587 	const char * const *result_attrs;
588 	struct ldb_message **result_res = NULL;
589 	struct ldb_dn *result_basedn;
590 	struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
591 
592 	const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
593 	const char * const _result_attrs_null[] = { NULL };
594 
595 	const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
596 	const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
597 
598 	const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
599 	const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", NULL};
600 
601 	const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
602 	const char * const _result_attrs_guid[] = { "objectGUID", NULL};
603 
604 	const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
605 	const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
606 
607 	/* here we need to set the attrs lists for domain and result lookups */
608 	switch (format_desired) {
609 	case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
610 	case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
611 		domain_attrs = _domain_attrs_1779;
612 		result_attrs = _result_attrs_null;
613 		break;
614 	case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
615 		domain_attrs = _domain_attrs_canonical;
616 		result_attrs = _result_attrs_canonical;
617 		break;
618 	case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
619 		domain_attrs = _domain_attrs_nt4;
620 		result_attrs = _result_attrs_nt4;
621 		break;
622 	case DRSUAPI_DS_NAME_FORMAT_GUID:
623 		domain_attrs = _domain_attrs_guid;
624 		result_attrs = _result_attrs_guid;
625 		break;
626 	case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
627 		domain_attrs = _domain_attrs_display;
628 		result_attrs = _result_attrs_display;
629 		break;
630 	default:
631 		return WERR_OK;
632 	}
633 
634 	if (domain_filter) {
635 		/* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
636 		ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res, domain_attrs,
637 				       "%s", domain_filter);
638 	} else {
639 		ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res, domain_attrs,
640 				       "(ncName=%s)", ldb_dn_get_linearized(samdb_base_dn(sam_ctx)));
641 	}
642 
643 	switch (ldb_ret) {
644 	case 1:
645 		break;
646 	case 0:
647 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
648 		return WERR_OK;
649 	case -1:
650 		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
651 		return WERR_OK;
652 	default:
653 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
654 		return WERR_OK;
655 	}
656 
657 	info1->dns_domain_name	= samdb_result_string(domain_res[0], "dnsRoot", NULL);
658 	W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
659 	info1->status		= DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
660 
661 	if (result_filter) {
662 		result_basedn = samdb_result_dn(sam_ctx, mem_ctx, domain_res[0], "ncName", NULL);
663 
664 		ldb_ret = gendb_search(sam_ctx, mem_ctx, result_basedn, &result_res,
665 				       result_attrs, "%s", result_filter);
666 	} else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
667 		ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
668 					  result_attrs);
669 	} else {
670 		name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res[0], "ncName", NULL);
671 		ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
672 					  result_attrs);
673 	}
674 
675 	switch (ldb_ret) {
676 	case 1:
677 		break;
678 	case 0:
679 		switch (format_offered) {
680 		case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
681 			return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
682 						   smb_krb5_context,
683 						   format_flags, format_offered, format_desired,
684 						   name, info1);
685 
686 		case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
687 			return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
688 					      format_flags, format_offered, format_desired,
689 					      name, info1);
690 		}
691 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
692 		return WERR_OK;
693 	case -1:
694 		info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
695 		return WERR_OK;
696 	default:
697 		info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
698 		return WERR_OK;
699 	}
700 
701 	/* here we can use result_res[0] and domain_res[0] */
702 	switch (format_desired) {
703 	case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
704 		info1->result_name	= ldb_dn_alloc_linearized(mem_ctx, result_res[0]->dn);
705 		W_ERROR_HAVE_NO_MEMORY(info1->result_name);
706 
707 		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
708 		return WERR_OK;
709 	}
710 	case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
711 		info1->result_name	= samdb_result_string(result_res[0], "canonicalName", NULL);
712 		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
713 		return WERR_OK;
714 	}
715 	case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
716 		/* Not in the virtual ldb attribute */
717 		return DsCrackNameOneSyntactical(mem_ctx,
718 						 DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
719 						 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
720 						 result_res[0]->dn, name, info1);
721 	}
722 	case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
723 		const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result_res[0], "objectSid");
724 		const char *_acc = "", *_dom = "";
725 
726 		if (!sid || (sid->num_auths < 4) || (sid->num_auths > 5)) {
727 			info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
728 			return WERR_OK;
729 		}
730 
731 		if (sid->num_auths == 4) {
732 			ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res, domain_attrs,
733 					       "(ncName=%s)", ldb_dn_get_linearized(result_res[0]->dn));
734 			if (ldb_ret != 1) {
735 				info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
736 				return WERR_OK;
737 			}
738 			_dom = samdb_result_string(domain_res[0], "nETBIOSName", NULL);
739 			W_ERROR_HAVE_NO_MEMORY(_dom);
740 
741 		} else if (sid->num_auths == 5) {
742 			const char *attrs[] = { NULL };
743 			struct ldb_message **domain_res2;
744 			struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
745 			if (!dom_sid) {
746 				return WERR_OK;
747 			}
748 			dom_sid->num_auths--;
749 			ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, attrs,
750 					       "(&(objectSid=%s)(objectClass=domain))", ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
751 			if (ldb_ret != 1) {
752 				info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
753 				return WERR_OK;
754 			}
755 			ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res2, domain_attrs,
756 					       "(ncName=%s)", ldb_dn_get_linearized(domain_res[0]->dn));
757 			if (ldb_ret != 1) {
758 				info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
759 				return WERR_OK;
760 			}
761 
762 			_dom = samdb_result_string(domain_res2[0], "nETBIOSName", NULL);
763 			W_ERROR_HAVE_NO_MEMORY(_dom);
764 
765 			_acc = samdb_result_string(result_res[0], "sAMAccountName", NULL);
766 			W_ERROR_HAVE_NO_MEMORY(_acc);
767 		}
768 
769 		info1->result_name	= talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
770 		W_ERROR_HAVE_NO_MEMORY(info1->result_name);
771 
772 		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
773 		return WERR_OK;
774 	}
775 	case DRSUAPI_DS_NAME_FORMAT_GUID: {
776 		struct GUID guid;
777 
778 		guid = samdb_result_guid(result_res[0], "objectGUID");
779 
780 		info1->result_name	= GUID_string2(mem_ctx, &guid);
781 		W_ERROR_HAVE_NO_MEMORY(info1->result_name);
782 
783 		info1->status		= DRSUAPI_DS_NAME_STATUS_OK;
784 		return WERR_OK;
785 	}
786 	case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
787 		info1->result_name	= samdb_result_string(result_res[0], "displayName", NULL);
788 		if (!info1->result_name) {
789 			info1->result_name	= samdb_result_string(result_res[0], "sAMAccountName", NULL);
790 		}
791 		if (!info1->result_name) {
792 			info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
793 		} else {
794 			info1->status = DRSUAPI_DS_NAME_STATUS_OK;
795 		}
796 		return WERR_OK;
797 	}
798 	default:
799 		return WERR_OK;
800 	}
801 
802 	return WERR_INVALID_PARAM;
803 }
804 
805 /* Given a user Principal Name (such as foo@bar.com),
806  * return the user and domain DNs.  This is used in the KDC to then
807  * return the Keys and evaluate policy */
808 
crack_user_principal_name(struct ldb_context * sam_ctx,TALLOC_CTX * mem_ctx,const char * user_principal_name,struct ldb_dn ** user_dn,struct ldb_dn ** domain_dn)809 NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
810 				   TALLOC_CTX *mem_ctx,
811 				   const char *user_principal_name,
812 				   struct ldb_dn **user_dn,
813 				   struct ldb_dn **domain_dn)
814 {
815 	WERROR werr;
816 	struct drsuapi_DsNameInfo1 info1;
817 	werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
818 				  DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
819 				  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
820 				  user_principal_name,
821 				  &info1);
822 	if (!W_ERROR_IS_OK(werr)) {
823 		return werror_to_ntstatus(werr);
824 	}
825 	switch (info1.status) {
826 	case DRSUAPI_DS_NAME_STATUS_OK:
827 		break;
828 	case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
829 	case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
830 	case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
831 		return NT_STATUS_NO_SUCH_USER;
832 	case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
833 	default:
834 		return NT_STATUS_UNSUCCESSFUL;
835 	}
836 
837 	*user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
838 
839 	if (domain_dn) {
840 		werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
841 					  DRSUAPI_DS_NAME_FORMAT_CANONICAL,
842 					  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
843 					  talloc_asprintf(mem_ctx, "%s/",
844 							  info1.dns_domain_name),
845 					  &info1);
846 		if (!W_ERROR_IS_OK(werr)) {
847 			return werror_to_ntstatus(werr);
848 		}
849 		switch (info1.status) {
850 		case DRSUAPI_DS_NAME_STATUS_OK:
851 			break;
852 		case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
853 		case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
854 		case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
855 			return NT_STATUS_NO_SUCH_USER;
856 		case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
857 		default:
858 			return NT_STATUS_UNSUCCESSFUL;
859 		}
860 
861 		*domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
862 	}
863 
864 	return NT_STATUS_OK;
865 
866 }
867 
868 /* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
869  * return the user and domain DNs.  This is used in the KDC to then
870  * return the Keys and evaluate policy */
871 
crack_service_principal_name(struct ldb_context * sam_ctx,TALLOC_CTX * mem_ctx,const char * service_principal_name,struct ldb_dn ** user_dn,struct ldb_dn ** domain_dn)872 NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
873 				      TALLOC_CTX *mem_ctx,
874 				      const char *service_principal_name,
875 				      struct ldb_dn **user_dn,
876 				      struct ldb_dn **domain_dn)
877 {
878 	WERROR werr;
879 	struct drsuapi_DsNameInfo1 info1;
880 	werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
881 				  DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
882 				  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
883 				  service_principal_name,
884 				  &info1);
885 	if (!W_ERROR_IS_OK(werr)) {
886 		return werror_to_ntstatus(werr);
887 	}
888 	switch (info1.status) {
889 	case DRSUAPI_DS_NAME_STATUS_OK:
890 		break;
891 	case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
892 	case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
893 	case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
894 		return NT_STATUS_NO_SUCH_USER;
895 	case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
896 	default:
897 		return NT_STATUS_UNSUCCESSFUL;
898 	}
899 
900 	*user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
901 
902 	if (domain_dn) {
903 		werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
904 					  DRSUAPI_DS_NAME_FORMAT_CANONICAL,
905 					  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
906 					  talloc_asprintf(mem_ctx, "%s/",
907 							  info1.dns_domain_name),
908 					  &info1);
909 		if (!W_ERROR_IS_OK(werr)) {
910 			return werror_to_ntstatus(werr);
911 		}
912 		switch (info1.status) {
913 		case DRSUAPI_DS_NAME_STATUS_OK:
914 			break;
915 		case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
916 		case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
917 		case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
918 			return NT_STATUS_NO_SUCH_USER;
919 		case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
920 		default:
921 			return NT_STATUS_UNSUCCESSFUL;
922 		}
923 
924 		*domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
925 	}
926 
927 	return NT_STATUS_OK;
928 
929 }
930 
crack_dn_to_nt4_name(TALLOC_CTX * mem_ctx,const char * dn,const char ** nt4_domain,const char ** nt4_account)931 NTSTATUS crack_dn_to_nt4_name(TALLOC_CTX *mem_ctx,
932 			      const char *dn,
933 			      const char **nt4_domain, const char **nt4_account)
934 {
935 	WERROR werr;
936 	struct drsuapi_DsNameInfo1 info1;
937 	struct ldb_context *ldb;
938 	char *p;
939 
940 	/* Handle anonymous bind */
941 	if (!dn || !*dn) {
942 		*nt4_domain = "";
943 		*nt4_account = "";
944 		return NT_STATUS_OK;
945 	}
946 
947 	ldb = samdb_connect(mem_ctx, system_session(mem_ctx));
948 	if (ldb == NULL) {
949 		return NT_STATUS_INTERNAL_DB_CORRUPTION;
950 	}
951 
952 	werr = DsCrackNameOneName(ldb, mem_ctx, 0,
953 				  DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
954 				  DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
955 				  dn,
956 				  &info1);
957 	if (!W_ERROR_IS_OK(werr)) {
958 		return werror_to_ntstatus(werr);
959 	}
960 	switch (info1.status) {
961 	case DRSUAPI_DS_NAME_STATUS_OK:
962 		break;
963 	case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
964 	case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
965 	case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
966 		return NT_STATUS_NO_SUCH_USER;
967 	case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
968 	default:
969 		return NT_STATUS_UNSUCCESSFUL;
970 	}
971 
972 	*nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
973 
974 	p = strchr(*nt4_domain, '\\');
975 	if (!p) {
976 		return NT_STATUS_INVALID_PARAMETER;
977 	}
978 	p[0] = '\0';
979 
980 	if (p[1]) {
981 		*nt4_account = talloc_strdup(mem_ctx, &p[1]);
982 	}
983 
984 	if (!*nt4_account || !*nt4_domain) {
985 		return NT_STATUS_NO_MEMORY;
986 	}
987 
988 	return NT_STATUS_OK;
989 
990 }
991