/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- gc_list_head
- is_subdomain
- gc_find_forest_root
- gc_add_forest
- gc_server_list_destroy
- gc_init_list
- gc_search_start
- gc_search_forest
- gc_search_all_forests
- gc_search_all_forests_unique
- gc_name_to_sid
- get_object_account_name
- gc_sid_to_name
- add_ads_result_to_array
- free_result_array
- check_result_unique
1 /*
2 * idmap_adex: Global Catalog search interface
3 *
4 * Copyright (C) Gerald (Jerry) Carter 2007-2008
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "idmap_adex.h"
23
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_IDMAP
26
27 static struct gc_info *_gc_server_list = NULL;
28
29
30 /**********************************************************************
31 *********************************************************************/
32
33 static struct gc_info *gc_list_head(void)
/* [<][>][^][v][top][bottom][index][help] */
34 {
35 return _gc_server_list;
36 }
37
38 /**********************************************************************
39 Checks if either of the domains is a subdomain of the other
40 *********************************************************************/
41
42 static bool is_subdomain(const char* a, const char *b)
/* [<][>][^][v][top][bottom][index][help] */
43 {
44 char *s;
45 TALLOC_CTX *frame = talloc_stackframe();
46 char *x, *y;
47 bool ret = false;
48
49 /* Trivial cases */
50
51 if (!a && !b)
52 return true;
53
54 if (!a || !b)
55 return false;
56
57 /* Normalize the case */
58
59 x = talloc_strdup(frame, a);
60 y = talloc_strdup(frame, b);
61 if (!x || !y) {
62 ret = false;
63 goto done;
64 }
65
66 strupper_m(x);
67 strupper_m(y);
68
69 /* Exact match */
70
71 if (strcmp(x, y) == 0) {
72 ret = true;
73 goto done;
74 }
75
76 /* Check for trailing substrings */
77
78 s = strstr_m(x, y);
79 if (s && (strlen(s) == strlen(y))) {
80 ret = true;
81 goto done;
82 }
83
84 s = strstr_m(y, x);
85 if (s && (strlen(s) == strlen(x))) {
86 ret = true;
87 goto done;
88 }
89
90 done:
91 talloc_destroy(frame);
92
93 return ret;
94 }
95
96 /**********************************************************************
97 *********************************************************************/
98
99 NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
/* [<][>][^][v][top][bottom][index][help] */
100 {
101 ADS_STRUCT *ads = NULL;
102 ADS_STATUS ads_status;
103 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
104 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
105 TALLOC_CTX *frame = talloc_stackframe();
106
107 if (!gc || !domain) {
108 return NT_STATUS_INVALID_PARAMETER;
109 }
110
111 ZERO_STRUCT(cldap_reply);
112
113 ads = ads_init(domain, NULL, NULL);
114 BAIL_ON_PTR_ERROR(ads, nt_status);
115
116 ads->auth.flags = ADS_AUTH_NO_BIND;
117 ads_status = ads_connect(ads);
118 if (!ADS_ERR_OK(ads_status)) {
119 DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
120 domain, ads_errstr(ads_status)));
121 }
122 nt_status = ads_ntstatus(ads_status);
123 BAIL_ON_NTSTATUS_ERROR(nt_status);
124
125 if (!ads_cldap_netlogon_5(frame,
126 ads->config.ldap_server_name,
127 ads->config.realm,
128 &cldap_reply))
129 {
130 DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
131 ads->server.ldap_server));
132 nt_status = NT_STATUS_IO_TIMEOUT;
133 BAIL_ON_NTSTATUS_ERROR(nt_status);
134 }
135
136 gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
137 BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
138
139 done:
140 if (ads) {
141 ads_destroy(&ads);
142 }
143
144 return nt_status;
145 }
146
147 /**********************************************************************
148 *********************************************************************/
149
150 static NTSTATUS gc_add_forest(const char *domain)
/* [<][>][^][v][top][bottom][index][help] */
151 {
152 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
153 struct gc_info *gc = NULL;
154 struct gc_info *find_gc = NULL;
155 char *dn;
156 ADS_STRUCT *ads = NULL;
157 struct likewise_cell *primary_cell = NULL;
158
159 primary_cell = cell_list_head();
160 if (!primary_cell) {
161 nt_status = NT_STATUS_INVALID_SERVER_STATE;
162 BAIL_ON_NTSTATUS_ERROR(nt_status);
163 }
164
165 /* Check for duplicates based on domain name first as this
166 requires no connection */
167
168 find_gc = gc_list_head();
169 while (find_gc) {
170 if (strequal (find_gc->forest_name, domain))
171 break;
172 find_gc = find_gc->next;
173 }
174
175 if (find_gc) {
176 DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
177 return NT_STATUS_OK;
178 }
179
180 if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
181 nt_status = NT_STATUS_NO_MEMORY;
182 BAIL_ON_NTSTATUS_ERROR(nt_status);
183 }
184
185 /* Query the rootDSE for the forest root naming conect first.
186 Check that the a GC server for the forest has not already
187 been added */
188
189 nt_status = gc_find_forest_root(gc, domain);
190 BAIL_ON_NTSTATUS_ERROR(nt_status);
191
192 find_gc = gc_list_head();
193 while (find_gc) {
194 if (strequal (find_gc->forest_name, gc->forest_name))
195 break;
196 find_gc = find_gc->next;
197 }
198
199 if (find_gc) {
200 DEBUG(10,("gc_add_forest: Forest %s already in list\n",
201 find_gc->forest_name));
202 return NT_STATUS_OK;
203 }
204
205 /* Not found, so add it here. Make sure we connect to
206 a DC in _this_ domain and not the forest root. */
207
208 dn = ads_build_dn(gc->forest_name);
209 BAIL_ON_PTR_ERROR(dn, nt_status);
210
211 gc->search_base = talloc_strdup(gc, dn);
212 SAFE_FREE(dn);
213 BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
214
215 #if 0
216 /* Can't use cell_connect_dn() here as there is no way to
217 specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
218
219 nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
220 BAIL_ON_NTSTATUS_ERROR(nt_status);
221 #else
222
223 gc->forest_cell = cell_new();
224 BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
225
226 /* Set the DNS domain, dn, etc ... and add it to the list */
227
228 cell_set_dns_domain(gc->forest_cell, gc->forest_name);
229 cell_set_dn(gc->forest_cell, gc->search_base);
230 cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
231 #endif
232
233 /* It is possible to belong to a non-forest cell and a
234 non-provisioned forest (at our domain levele). In that
235 case, we should just inherit the flags from our primary
236 cell since the GC searches will match our own schema
237 model. */
238
239 if (strequal(primary_cell->forest_name, gc->forest_name)
240 || is_subdomain(primary_cell->dns_domain, gc->forest_name))
241 {
242 cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
243 } else {
244 /* outside of our domain */
245
246 nt_status = cell_connect(gc->forest_cell);
247 BAIL_ON_NTSTATUS_ERROR(nt_status);
248
249 nt_status = cell_lookup_settings(gc->forest_cell);
250 BAIL_ON_NTSTATUS_ERROR(nt_status);
251
252 /* Drop the connection now that we have the settings */
253
254 ads = cell_connection(gc->forest_cell);
255 ads_destroy(&ads);
256 cell_set_connection(gc->forest_cell, NULL);
257 }
258
259 DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
260
261 DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
262 gc->forest_name));
263
264 nt_status = NT_STATUS_OK;
265
266 done:
267 if (!NT_STATUS_IS_OK(nt_status)) {
268 talloc_destroy(gc);
269 DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
270 domain, nt_errstr(nt_status)));
271 }
272
273 return nt_status;
274 }
275
276 /**********************************************************************
277 *********************************************************************/
278
279 static void gc_server_list_destroy(void)
/* [<][>][^][v][top][bottom][index][help] */
280 {
281 struct gc_info *gc = gc_list_head();
282
283 while (gc) {
284 struct gc_info *p = gc->next;
285
286 cell_destroy(gc->forest_cell);
287 talloc_destroy(gc);
288
289 gc = p;
290 }
291
292 _gc_server_list = NULL;
293
294 return;
295 }
296
297 /**********************************************************************
298 Setup the initial list of forests and initial the forest cell
299 settings for each. FIXME!!!
300 *********************************************************************/
301
302 NTSTATUS gc_init_list(void)
/* [<][>][^][v][top][bottom][index][help] */
303 {
304 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
305 struct winbindd_tdc_domain *domains = NULL;
306 size_t num_domains = 0;
307 int i;
308
309 if (_gc_server_list != NULL) {
310 gc_server_list_destroy();
311 }
312
313 if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
314 nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
315 BAIL_ON_NTSTATUS_ERROR(nt_status);
316 }
317
318 /* Find our forest first. Have to try all domains here starting
319 with our own. gc_add_forest() filters duplicates */
320
321 nt_status = gc_add_forest(lp_realm());
322 WARN_ON_NTSTATUS_ERROR(nt_status);
323
324 for (i=0; i<num_domains; i++) {
325 uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
326
327 /* I think we should be able to break out of loop once
328 we add a GC for our forest and not have to test every one.
329 In fact, this entire loop is probably irrelevant since
330 the GC location code should always find a GC given lp_realm().
331 Will have to spend time testing before making the change.
332 --jerry */
333
334 if ((domains[i].trust_flags & flags) == flags) {
335 nt_status = gc_add_forest(domains[i].dns_name);
336 WARN_ON_NTSTATUS_ERROR(nt_status);
337 /* Don't BAIL here since not every domain may
338 have a GC server */
339 }
340 }
341
342 /* Now add trusted forests. gc_add_forest() will filter out
343 duplicates. Check everything with an incoming trust path
344 that is not in our own forest. */
345
346 for (i=0; i<num_domains; i++) {
347 uint32_t flags = domains[i].trust_flags;
348 uint32_t attribs = domains[i].trust_attribs;
349
350 /* Skip non_AD domains */
351
352 if (strlen(domains[i].dns_name) == 0) {
353 continue;
354 }
355
356 /* Only add a GC for a forest outside of our own.
357 Ignore QUARANTINED/EXTERNAL trusts */
358
359 if ((flags & NETR_TRUST_FLAG_INBOUND)
360 && !(flags & NETR_TRUST_FLAG_IN_FOREST)
361 && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
362 {
363 nt_status = gc_add_forest(domains[i].dns_name);
364 WARN_ON_NTSTATUS_ERROR(nt_status);
365 }
366 }
367
368 nt_status = NT_STATUS_OK;
369
370 done:
371 if (!NT_STATUS_IS_OK(nt_status)) {
372 DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
373 nt_errstr(nt_status)));
374 }
375
376 TALLOC_FREE(domains);
377
378 return nt_status;
379 }
380
381
382 /**********************************************************************
383 *********************************************************************/
384
385 struct gc_info *gc_search_start(void)
/* [<][>][^][v][top][bottom][index][help] */
386 {
387 NTSTATUS nt_status = NT_STATUS_OK;
388 struct gc_info *gc = gc_list_head();
389
390 if (!gc) {
391 nt_status = gc_init_list();
392 BAIL_ON_NTSTATUS_ERROR(nt_status);
393
394 gc = gc_list_head();
395 }
396
397 done:
398 if (!NT_STATUS_IS_OK(nt_status)) {
399 DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
400 nt_errstr(nt_status)));
401 }
402
403 return gc;
404 }
405
406 /**********************************************************************
407 Search Global Catalog. Always search our own forest. The flags set
408 controls whether or not we search cross forest. Assume that the
409 resulting set is always returned from one GC so that we don't have to
410 both combining the LDAPMessage * results
411 *********************************************************************/
412
413 NTSTATUS gc_search_forest(struct gc_info *gc,
/* [<][>][^][v][top][bottom][index][help] */
414 LDAPMessage **msg,
415 const char *filter)
416 {
417 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
418 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
419 const char *attrs[] = {"*", NULL};
420 LDAPMessage *m = NULL;
421
422 if (!gc || !msg || !filter) {
423 nt_status = NT_STATUS_INVALID_PARAMETER;
424 BAIL_ON_NTSTATUS_ERROR(nt_status);
425 }
426
427 /* When you have multiple domain trees in a forest, the
428 GC will search all naming contexts when you send it
429 and empty ("") base search suffix. Tested against
430 Windows 2003. */
431
432 ads_status = cell_do_search(gc->forest_cell, "",
433 LDAP_SCOPE_SUBTREE, filter, attrs, &m);
434 nt_status = ads_ntstatus(ads_status);
435 BAIL_ON_NTSTATUS_ERROR(nt_status);
436
437 *msg = m;
438
439 done:
440 if (!NT_STATUS_IS_OK(nt_status)) {
441 DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
442 filter, nt_errstr(nt_status)));
443 }
444
445 return nt_status;
446 }
447
448 /**********************************************************************
449 Search all forests via GC and return the results in an array of
450 ADS_STRUCT/LDAPMessage pairs.
451 *********************************************************************/
452
453 NTSTATUS gc_search_all_forests(const char *filter,
/* [<][>][^][v][top][bottom][index][help] */
454 ADS_STRUCT ***ads_list,
455 LDAPMessage ***msg_list,
456 int *num_resp, uint32_t flags)
457 {
458 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
459 struct gc_info *gc = NULL;
460 uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
461
462 *ads_list = NULL;
463 *msg_list = NULL;
464 *num_resp = 0;
465
466 if ((gc = gc_search_start()) == NULL) {
467 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
468 BAIL_ON_NTSTATUS_ERROR(nt_status);
469 }
470
471 while (gc) {
472 LDAPMessage *m = NULL;
473
474 nt_status = gc_search_forest(gc, &m, filter);
475 if (!NT_STATUS_IS_OK(nt_status)) {
476 gc = gc->next;
477 continue;
478 }
479
480 nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
481 m, ads_list, msg_list,
482 num_resp);
483 BAIL_ON_NTSTATUS_ERROR(nt_status);
484
485 /* If there can only be one match, then we are done */
486
487 if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
488 break;
489 }
490
491 gc = gc->next;
492 }
493
494 if (*num_resp == 0) {
495 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
496 BAIL_ON_NTSTATUS_ERROR(nt_status);
497 }
498
499 nt_status = NT_STATUS_OK;
500
501 done:
502 return nt_status;
503 }
504
505 /**********************************************************************
506 Search all forests via GC and return the results in an array of
507 ADS_STRUCT/LDAPMessage pairs.
508 *********************************************************************/
509
510 NTSTATUS gc_search_all_forests_unique(const char *filter,
/* [<][>][^][v][top][bottom][index][help] */
511 ADS_STRUCT **ads,
512 LDAPMessage **msg)
513 {
514 ADS_STRUCT **ads_list = NULL;
515 LDAPMessage **msg_list = NULL;
516 int num_resp;
517 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
518
519 nt_status = gc_search_all_forests(filter, &ads_list,
520 &msg_list, &num_resp,
521 ADEX_GC_SEARCH_CHECK_UNIQUE);
522 BAIL_ON_NTSTATUS_ERROR(nt_status);
523
524 nt_status = check_result_unique(ads_list[0], msg_list[0]);
525 BAIL_ON_NTSTATUS_ERROR(nt_status);
526
527 *ads = ads_list[0];
528 *msg = msg_list[0];
529
530 done:
531 /* Be care that we don't free the msg result being returned */
532
533 if (!NT_STATUS_IS_OK(nt_status)) {
534 free_result_array(ads_list, msg_list, num_resp);
535 } else {
536 talloc_destroy(ads_list);
537 talloc_destroy(msg_list);
538 }
539
540 return nt_status;
541 }
542
543 /*********************************************************************
544 ********************************************************************/
545
546 NTSTATUS gc_name_to_sid(const char *domain,
/* [<][>][^][v][top][bottom][index][help] */
547 const char *name,
548 DOM_SID *sid,
549 enum lsa_SidType *sid_type)
550 {
551 TALLOC_CTX *frame = talloc_stackframe();
552 char *p, *name_user;
553 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
554 char *name_filter;
555 ADS_STRUCT *ads = NULL;
556 LDAPMessage *msg = NULL;
557 LDAPMessage *e = NULL;
558 char *dn = NULL;
559 char *dns_domain = NULL;
560 ADS_STRUCT **ads_list = NULL;
561 LDAPMessage **msg_list = NULL;
562 int num_resp = 0;
563 int i;
564
565 /* Strip the "DOMAIN\" prefix if necessary and search for
566 a matching sAMAccountName in the forest */
567
568 if ((p = strchr_m( name, '\\' )) == NULL)
569 name_user = talloc_strdup( frame, name );
570 else
571 name_user = talloc_strdup( frame, p+1 );
572 BAIL_ON_PTR_ERROR(name_user, nt_status);
573
574 name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
575 BAIL_ON_PTR_ERROR(name_filter, nt_status);
576
577 nt_status = gc_search_all_forests(name_filter, &ads_list,
578 &msg_list, &num_resp, 0);
579 BAIL_ON_NTSTATUS_ERROR(nt_status);
580
581 /* Assume failure until we know otherwise*/
582
583 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
584
585 /* Match the domain name from the DN */
586
587 for (i=0; i<num_resp; i++) {
588 ads = ads_list[i];
589 msg = msg_list[i];
590
591 e = ads_first_entry(ads, msg);
592 while (e) {
593 struct winbindd_tdc_domain *domain_rec;
594
595 dn = ads_get_dn(ads, frame, e);
596 BAIL_ON_PTR_ERROR(dn, nt_status);
597
598 dns_domain = cell_dn_to_dns(dn);
599 TALLOC_FREE(dn);
600 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
601
602 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
603 SAFE_FREE(dns_domain);
604
605 /* Ignore failures and continue the search */
606
607 if (!domain_rec) {
608 e = ads_next_entry(ads, e);
609 continue;
610 }
611
612 /* Check for a match on the domain name */
613
614 if (strequal(domain, domain_rec->domain_name)) {
615 if (!ads_pull_sid(ads, e, "objectSid", sid)) {
616 nt_status = NT_STATUS_INVALID_SID;
617 BAIL_ON_NTSTATUS_ERROR(nt_status);
618 }
619
620 talloc_destroy(domain_rec);
621
622 nt_status = get_sid_type(ads, msg, sid_type);
623 BAIL_ON_NTSTATUS_ERROR(nt_status);
624
625 /* We're done! */
626 nt_status = NT_STATUS_OK;
627 break;
628 }
629
630 /* once more around thew merry-go-round */
631
632 talloc_destroy(domain_rec);
633 e = ads_next_entry(ads, e);
634 }
635 }
636
637 done:
638 free_result_array(ads_list, msg_list, num_resp);
639 talloc_destroy(frame);
640
641 return nt_status;
642 }
643
644 /********************************************************************
645 Pull an attribute string value
646 *******************************************************************/
647
648 static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
/* [<][>][^][v][top][bottom][index][help] */
649 LDAPMessage *msg,
650 char **name)
651 {
652 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
653 char *sam_name = NULL;
654 struct winbindd_tdc_domain *domain_rec = NULL;
655 char *dns_domain = NULL;
656 char *dn = NULL;
657 TALLOC_CTX *frame = talloc_stackframe();
658 int len;
659
660 /* Check parameters */
661
662 if (!ads || !msg || !name) {
663 nt_status = NT_STATUS_INVALID_PARAMETER;
664 BAIL_ON_NTSTATUS_ERROR(nt_status);
665 }
666
667 /* get the name and domain */
668
669 dn = ads_get_dn(ads, frame, msg);
670 BAIL_ON_PTR_ERROR(dn, nt_status);
671
672 DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
673
674 dns_domain = cell_dn_to_dns(dn);
675 TALLOC_FREE(dn);
676 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
677
678 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
679 SAFE_FREE(dns_domain);
680
681 if (!domain_rec) {
682 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
683 BAIL_ON_NTSTATUS_ERROR(nt_status);
684 }
685
686 sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
687 BAIL_ON_PTR_ERROR(sam_name, nt_status);
688
689 len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
690 if (len == -1) {
691 *name = NULL;
692 BAIL_ON_PTR_ERROR((*name), nt_status);
693 }
694
695 nt_status = NT_STATUS_OK;
696
697 done:
698 talloc_destroy(frame);
699
700 return nt_status;
701 }
702
703 /*********************************************************************
704 ********************************************************************/
705
706 NTSTATUS gc_sid_to_name(const DOM_SID *sid,
/* [<][>][^][v][top][bottom][index][help] */
707 char **name,
708 enum lsa_SidType *sid_type)
709 {
710 TALLOC_CTX *frame = talloc_stackframe();
711 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
712 char *filter;
713 ADS_STRUCT *ads = NULL;
714 LDAPMessage *msg = NULL;
715 char *sid_string;
716
717 *name = NULL;
718
719 sid_string = sid_binstring(sid);
720 BAIL_ON_PTR_ERROR(sid_string, nt_status);
721
722 filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
723 SAFE_FREE(sid_string);
724 BAIL_ON_PTR_ERROR(filter, nt_status);
725
726 nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
727 BAIL_ON_NTSTATUS_ERROR(nt_status);
728
729 nt_status = get_object_account_name(ads, msg, name);
730 BAIL_ON_NTSTATUS_ERROR(nt_status);
731
732 nt_status = get_sid_type(ads, msg, sid_type);
733 BAIL_ON_NTSTATUS_ERROR(nt_status);
734
735 done:
736 ads_msgfree(ads, msg);
737 talloc_destroy(frame);
738
739 return nt_status;
740 }
741
742 /**********************************************************************
743 *********************************************************************/
744
745 NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
/* [<][>][^][v][top][bottom][index][help] */
746 LDAPMessage *msg,
747 ADS_STRUCT ***ads_list,
748 LDAPMessage ***msg_list,
749 int *size)
750 {
751 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
752 ADS_STRUCT **ads_tmp = NULL;
753 LDAPMessage **msg_tmp = NULL;
754 int count = *size;
755
756 if (!ads || !msg) {
757 nt_status = NT_STATUS_INVALID_PARAMETER;
758 BAIL_ON_NTSTATUS_ERROR(nt_status);
759 }
760
761 #if 0
762 /* Don't add a response with no entries */
763
764 if (ads_count_replies(ads, msg) == 0) {
765 return NT_STATUS_OK;
766 }
767 #endif
768
769 if (count == 0) {
770 ads_tmp = TALLOC_ARRAY(NULL, ADS_STRUCT*, 1);
771 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
772
773 msg_tmp = TALLOC_ARRAY(NULL, LDAPMessage*, 1);
774 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
775 } else {
776 ads_tmp = TALLOC_REALLOC_ARRAY(*ads_list, *ads_list, ADS_STRUCT*,
777 count+1);
778 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
779
780 msg_tmp = TALLOC_REALLOC_ARRAY(*msg_list, *msg_list, LDAPMessage*,
781 count+1);
782 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
783 }
784
785 ads_tmp[count] = ads;
786 msg_tmp[count] = msg;
787 count++;
788
789 *ads_list = ads_tmp;
790 *msg_list = msg_tmp;
791 *size = count;
792
793 nt_status = NT_STATUS_OK;
794
795 done:
796 if (!NT_STATUS_IS_OK(nt_status)) {
797 talloc_destroy(ads_tmp);
798 talloc_destroy(msg_tmp);
799 }
800
801 return nt_status;
802 }
803
804 /**********************************************************************
805 Frees search results. Do not free the ads_list as these are
806 references back to the GC search structures.
807 *********************************************************************/
808
809 void free_result_array(ADS_STRUCT **ads_list,
/* [<][>][^][v][top][bottom][index][help] */
810 LDAPMessage **msg_list,
811 int num_resp)
812 {
813 int i;
814
815 for (i=0; i<num_resp; i++) {
816 ads_msgfree(ads_list[i], msg_list[i]);
817 }
818
819 talloc_destroy(ads_list);
820 talloc_destroy(msg_list);
821 }
822
823 /**********************************************************************
824 Check that we have exactly one entry from the search
825 *********************************************************************/
826
827 NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
/* [<][>][^][v][top][bottom][index][help] */
828 {
829 NTSTATUS nt_status;
830 int count;
831
832 count = ads_count_replies(ads, msg);
833
834 if (count <= 0) {
835 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
836 BAIL_ON_NTSTATUS_ERROR(nt_status);
837 }
838
839 if (count > 1) {
840 nt_status = NT_STATUS_DUPLICATE_NAME;
841 BAIL_ON_NTSTATUS_ERROR(nt_status);
842 }
843
844 nt_status = NT_STATUS_OK;
845
846 done:
847 return nt_status;
848 }