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