s3:libads Make ads_get_dn() take a talloc context
[ira/wip.git] / source3 / winbindd / idmap_adex / provider_unified.c
1 /*
2  * idmap_adex
3  *
4  * Provider for RFC2307 and SFU AD Forests
5  *
6  * Copyright (C) Gerald (Jerry) Carter 2006-2008
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 #include "includes.h"
24 #include "idmap_adex.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_IDMAP
28
29 /* Information needed by the LDAP search filters */
30
31 enum filterType { SidFilter, IdFilter, AliasFilter };
32
33 struct lwcell_filter
34 {
35         enum filterType ftype;
36         bool use2307;
37         union {
38                 DOM_SID sid;
39                 struct {
40                         uint32_t id;
41                         enum id_type type;
42                 } id;
43                 fstring alias;
44         } filter;
45 };
46
47 /********************************************************************
48  *******************************************************************/
49
50 static char* build_id_filter(uint32_t id,
51                              enum id_type type,
52                              uint32_t search_flags)
53 {
54         char *filter = NULL;
55         char *oc_filter, *attr_filter;
56         NTSTATUS nt_status;
57         TALLOC_CTX *frame = talloc_stackframe();
58         bool use2307 = ((search_flags & LWCELL_FLAG_USE_RFC2307_ATTRS)
59                         == LWCELL_FLAG_USE_RFC2307_ATTRS);
60         bool use_gc = ((search_flags & LWCELL_FLAG_SEARCH_FOREST)
61                        == LWCELL_FLAG_SEARCH_FOREST);
62         const char *oc;
63
64         /* Construct search filter for objectclass and attributes */
65
66         switch (type) {
67         case ID_TYPE_UID:
68                 oc = ADEX_OC_USER;
69                 if (use2307) {
70                         oc = ADEX_OC_POSIX_USER;
71                         if (use_gc) {
72                                 oc = AD_USER;
73                         }
74                 }
75                 oc_filter = talloc_asprintf(frame, "objectclass=%s", oc);
76                 attr_filter = talloc_asprintf(frame, "%s=%u",
77                                               ADEX_ATTR_UIDNUM, id);
78                 break;
79
80         case ID_TYPE_GID:
81                 oc = ADEX_OC_GROUP;
82                 if (use2307) {
83                         oc = ADEX_OC_POSIX_GROUP;
84                         if (use_gc) {
85                                 oc = AD_GROUP;
86                         }
87                 }
88                 oc_filter = talloc_asprintf(frame, "objectclass=%s", oc);
89                 attr_filter = talloc_asprintf(frame, "%s=%u",
90                                               ADEX_ATTR_GIDNUM, id);
91                 break;
92         default:
93                 return NULL;
94         }
95
96         BAIL_ON_PTR_ERROR(oc_filter, nt_status);
97         BAIL_ON_PTR_ERROR(attr_filter, nt_status);
98
99         /* Use "keywords=%s" for non-schema cells */
100
101         if (use2307) {
102                 filter = talloc_asprintf(frame, "(&(%s)(%s))",
103                                          oc_filter, attr_filter);
104         } else {
105                 filter = talloc_asprintf(frame, "(&(keywords=%s)(keywords=%s))",
106                                          oc_filter, attr_filter);
107         }
108
109         talloc_destroy(oc_filter);
110         talloc_destroy(attr_filter);
111
112 done:
113         /* Don't destroy the stackframe CTX since we are returning
114            memory from it */
115
116         return filter;
117 }
118
119 /********************************************************************
120  *******************************************************************/
121
122 static char* build_alias_filter(const char *alias, uint32_t search_flags)
123 {
124         char *filter = NULL;
125         char *user_attr_filter, *group_attr_filter;
126         NTSTATUS nt_status;
127         TALLOC_CTX *frame = talloc_stackframe();
128         bool use2307 = ((search_flags & LWCELL_FLAG_USE_RFC2307_ATTRS)
129                         == LWCELL_FLAG_USE_RFC2307_ATTRS);
130         bool search_forest = ((search_flags & LWCELL_FLAG_SEARCH_FOREST)
131                               == LWCELL_FLAG_SEARCH_FOREST);
132
133         /* Construct search filter for objectclass and attributes */
134
135         user_attr_filter = talloc_asprintf(frame, "%s=%s",
136                                            ADEX_ATTR_UID, alias);
137         group_attr_filter = talloc_asprintf(frame, "%s=%s",
138                                             ADEX_ATTR_DISPLAYNAME, alias);
139         BAIL_ON_PTR_ERROR(user_attr_filter, nt_status);
140         BAIL_ON_PTR_ERROR(group_attr_filter, nt_status);
141
142         /* Use "keywords=%s" for non-schema cells */
143
144         if (use2307) {
145                 filter = talloc_asprintf(frame,
146                                          "(|(&(%s)(objectclass=%s))(&(%s)(objectclass=%s)))",
147                                          user_attr_filter,
148                                          search_forest ? AD_USER : ADEX_OC_POSIX_USER,
149                                          group_attr_filter,
150                                          search_forest ? AD_GROUP : ADEX_OC_POSIX_GROUP);
151         } else {
152                 filter = talloc_asprintf(frame,
153                                          "(|(keywords=%s)(keywords=%s))",
154                                          user_attr_filter,
155                                          group_attr_filter);
156         }
157
158         talloc_destroy(user_attr_filter);
159         talloc_destroy(group_attr_filter);
160
161 done:
162         /* Don't destroy the stackframe CTX since we are returning
163            memory from it */
164
165         return filter;
166 }
167
168
169 /********************************************************************
170  *******************************************************************/
171
172 static NTSTATUS search_cell(struct likewise_cell *c,
173                             LDAPMessage **msg,
174                             const struct lwcell_filter *fdata)
175 {
176         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
177         TALLOC_CTX* frame = talloc_stackframe();
178         char *filter = NULL;
179         const char *base = NULL;
180         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
181         const char *attrs[] = { "*", NULL };
182         int count;
183         char *sid_str;
184
185         /* get the filter and other search parameters */
186
187         switch (fdata->ftype) {
188         case SidFilter:
189                 sid_str = sid_string_talloc(frame, &fdata->filter.sid);
190                 BAIL_ON_PTR_ERROR(sid_str, nt_status);
191
192                 filter = talloc_asprintf(frame, "(keywords=backLink=%s)",
193                                          sid_str);
194                 break;
195         case IdFilter:
196                 filter = build_id_filter(fdata->filter.id.id,
197                                          fdata->filter.id.type,
198                                          cell_flags(c));
199                 break;
200         case AliasFilter:
201                 filter = build_alias_filter(fdata->filter.alias,
202                                             cell_flags(c));
203                 break;
204         default:
205                 nt_status = NT_STATUS_INVALID_PARAMETER;
206                 break;
207         }
208         BAIL_ON_PTR_ERROR(filter, nt_status);
209
210         base = cell_search_base(c);
211         BAIL_ON_PTR_ERROR(base, nt_status);
212
213         ads_status = cell_do_search(c, base, LDAP_SCOPE_SUBTREE,
214                                     filter, attrs, msg);
215
216         nt_status = ads_ntstatus(ads_status);
217         BAIL_ON_NTSTATUS_ERROR(nt_status);
218
219         /* Now check that we got only one reply */
220
221         count = ads_count_replies(c->conn, *msg);
222         if (count < 1) {
223                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
224                 BAIL_ON_NTSTATUS_ERROR(nt_status);
225         }
226
227         if ( count > 1) {
228                 nt_status = NT_STATUS_DUPLICATE_NAME;
229                 BAIL_ON_NTSTATUS_ERROR(nt_status);
230         }
231
232 done:
233         PRINT_NTSTATUS_ERROR(nt_status, "search_cell", 4);
234
235         talloc_destroy(CONST_DISCARD(char*, base));
236         talloc_destroy(frame);
237
238         return nt_status;
239 }
240
241 /********************************************************************
242  *******************************************************************/
243
244 static NTSTATUS search_domain(struct likewise_cell **cell,
245                               LDAPMessage **msg,
246                               const char *dn,
247                               const DOM_SID *sid)
248 {
249         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
250         TALLOC_CTX* frame = talloc_stackframe();
251         int count;
252
253         nt_status = dc_search_domains(cell, msg, dn, sid);
254         BAIL_ON_NTSTATUS_ERROR(nt_status);
255
256         /* Now check that we got only one reply */
257
258         count = ads_count_replies(cell_connection(*cell), *msg);
259         if (count < 1) {
260                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
261                 BAIL_ON_NTSTATUS_ERROR(nt_status);
262         }
263
264         if ( count > 1) {
265                 nt_status = NT_STATUS_DUPLICATE_NAME;
266                 BAIL_ON_NTSTATUS_ERROR(nt_status);
267         }
268
269 done:
270         PRINT_NTSTATUS_ERROR(nt_status, "search_domain", 4);
271         talloc_destroy(frame);
272
273         return nt_status;
274 }
275
276
277 /********************************************************************
278  Check that a DN is within the forest scope.
279  *******************************************************************/
280
281 static bool check_forest_scope(const char *dn)
282 {
283         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
284         TALLOC_CTX *frame = talloc_stackframe();
285         char *p = NULL;
286         char *q = NULL;
287         char *dns_domain = NULL;
288         struct winbindd_tdc_domain *domain;
289
290         /* If the DN does *not* contain "$LikewiseIdentityCell",
291            assume this is a schema mode forest and it is in the
292            forest scope by definition. */
293
294         if ((p = strstr_m(dn, ADEX_CELL_RDN)) == NULL) {
295                 nt_status = NT_STATUS_OK;
296                 goto done;
297         }
298
299         /* If this is a non-schema forest, then make sure that the DN
300            is in the form "...,cn=$LikewiseIdentityCell,DC=..." */
301
302         if ((q = strchr_m(p, ',')) == NULL) {
303                 nt_status = NT_STATUS_OBJECT_NAME_INVALID;
304                 BAIL_ON_NTSTATUS_ERROR(nt_status);
305         }
306
307         q++;
308         if (StrnCaseCmp(q, "dc=", 3) != 0) {
309                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
310                 BAIL_ON_NTSTATUS_ERROR(nt_status);
311         }
312
313
314         dns_domain = cell_dn_to_dns(q);
315         BAIL_ON_PTR_ERROR(dns_domain, nt_status);
316
317         domain = wcache_tdc_fetch_domain(frame, dns_domain);
318         if (!domain) {
319                 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
320                 BAIL_ON_NTSTATUS_ERROR(nt_status);
321         }
322
323         nt_status = NT_STATUS_OK;
324
325 done:
326         talloc_destroy(frame);
327         SAFE_FREE(dns_domain);
328
329         return NT_STATUS_IS_OK(nt_status);
330 }
331
332
333
334 /********************************************************************
335  Check that only one result was returned within the forest cell
336  scope.
337  *******************************************************************/
338
339 static NTSTATUS check_result_unique_scoped(ADS_STRUCT **ads_list,
340                                            LDAPMessage **msg_list,
341                                            int num_resp,
342                                            char **dn,
343                                            DOM_SID *user_sid)
344 {
345         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
346         int i;
347         ADS_STRUCT *ads = NULL;
348         LDAPMessage *msg = NULL;
349         int count = 0;
350         char *entry_dn = NULL;
351         TALLOC_CTX *frame = talloc_stackframe();
352
353         if (!dn || !user_sid) {
354                 nt_status = NT_STATUS_INVALID_PARAMETER;
355                 BAIL_ON_NTSTATUS_ERROR(nt_status);
356         }
357
358         *dn = NULL;
359
360         if (!ads_list || !msg_list || (num_resp == 0)) {
361                 nt_status = NT_STATUS_NO_SUCH_FILE;
362                 BAIL_ON_NTSTATUS_ERROR(nt_status);
363         }
364
365         /* Loop over all msgs */
366
367         for (i=0; i<num_resp; i++) {
368                 LDAPMessage *e = ads_first_entry(ads_list[i], msg_list[i]);
369
370                 while (e) {
371                         entry_dn = ads_get_dn(ads_list[i], NULL, e);
372                         BAIL_ON_PTR_ERROR(entry_dn, nt_status);
373
374                         if (check_forest_scope(entry_dn)) {
375                                 count++;
376
377                                 /* If we've already broken the condition, no
378                                    need to continue */
379
380                                 if (count > 1) {
381                                         nt_status = NT_STATUS_DUPLICATE_NAME;
382                                         BAIL_ON_NTSTATUS_ERROR(nt_status);
383                                 }
384
385                                 ads = ads_list[i];
386                                 msg = e;
387                                 *dn = SMB_STRDUP(entry_dn);
388                                 BAIL_ON_PTR_ERROR((*dn), nt_status);
389                         }
390
391                         e = ads_next_entry(ads_list[i], e);
392                         TALLOC_FREE(entry_dn);
393                 }
394         }
395
396         if (!ads || !msg) {
397                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
398                 BAIL_ON_NTSTATUS_ERROR(nt_status);
399         }
400
401         /* If we made is through the loop, then grab the user_sid and
402            run home to base */
403
404         /*
405            Try and get the SID from either objectSid or keywords.
406            We cannot use pull_sid() here since we want to try
407            both methods and not only one or the other (and we
408            have no full likewise_cell struct.
409
410            Fail if both are unavailable
411         */
412
413         if (!ads_pull_sid(ads, msg, "objectSid", user_sid)) {
414                 char **keywords;
415                 char *s;
416                 size_t num_lines = 0;
417
418                 keywords = ads_pull_strings(ads, frame, msg, "keywords",
419                                             &num_lines);
420                 BAIL_ON_PTR_ERROR(keywords, nt_status);
421
422                 s = find_attr_string(keywords, num_lines, "backLink");
423                 if (!s) {
424                         nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
425                         BAIL_ON_NTSTATUS_ERROR(nt_status);
426                 }
427
428                 if (!string_to_sid(user_sid, s)) {
429                         nt_status = NT_STATUS_INVALID_SID;
430                         BAIL_ON_NTSTATUS_ERROR(nt_status);
431                 }
432         }
433
434         nt_status = NT_STATUS_OK;
435
436 done:
437         if (!NT_STATUS_IS_OK(nt_status)) {
438                 SAFE_FREE(*dn);
439         }
440
441         talloc_destroy(frame);
442         TALLOC_FREE(entry_dn);
443
444         return nt_status;
445 }
446
447 /********************************************************************
448  Search all forests.  Each forest can have it's own forest-cell
449  settings so we have to generate the filter for each search.
450  We don't use gc_search_all_forests() since we may have a different
451  schema model in each forest and need to construct the search
452  filter for each GC search.
453  *******************************************************************/
454
455 static NTSTATUS search_forest(struct likewise_cell *forest_cell,
456                               LDAPMessage **msg,
457                               const struct lwcell_filter *fdata)
458 {
459         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
460         TALLOC_CTX *frame = talloc_stackframe();
461         char *filter = NULL;
462         char *dn = NULL;
463         struct gc_info *gc = NULL;
464         ADS_STRUCT **ads_list = NULL;
465         LDAPMessage **msg_list = NULL;
466         int num_resp = 0;
467         LDAPMessage *m;
468         DOM_SID user_sid;
469         struct likewise_cell *domain_cell = NULL;
470
471         if ((gc = gc_search_start()) == NULL) {
472                 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
473                 BAIL_ON_NTSTATUS_ERROR(nt_status);
474         }
475
476         while (gc) {
477                 char *sid_binstr = NULL;
478                 uint32_t flags = LWCELL_FLAG_SEARCH_FOREST;
479
480                 m = NULL;
481
482                 flags |= cell_flags(gc->forest_cell);
483
484                 switch (fdata->ftype) {
485                 case SidFilter:
486                         sid_binstr = sid_binstring(&fdata->filter.sid);
487                         BAIL_ON_PTR_ERROR(sid_binstr, nt_status);
488
489                         filter = talloc_asprintf(frame, "(objectSid=%s)", sid_binstr);
490                         SAFE_FREE(sid_binstr);
491                         break;
492                 case IdFilter:
493                         filter = build_id_filter(fdata->filter.id.id,
494                                                  fdata->filter.id.type, flags);
495                         break;
496                 case AliasFilter:
497                         filter = build_alias_filter(fdata->filter.alias, flags);
498                         break;
499                 }
500
501                 /* First find the sparse object in GC */
502                 nt_status = gc_search_forest(gc, &m, filter);
503                 if (!NT_STATUS_IS_OK(nt_status)) {
504                         gc = gc->next;
505                         continue;
506                 }
507
508                 nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
509                                                     m, &ads_list, &msg_list,
510                                                     &num_resp);
511                 BAIL_ON_NTSTATUS_ERROR(nt_status);
512
513                 gc = gc->next;
514         }
515
516         /* Uniqueness check across forests */
517
518         nt_status = check_result_unique_scoped(ads_list, msg_list, num_resp,
519                                                &dn, &user_sid);
520         BAIL_ON_NTSTATUS_ERROR(nt_status);
521
522         nt_status = search_domain(&domain_cell, &m, dn, &user_sid);
523         BAIL_ON_NTSTATUS_ERROR(nt_status);
524
525         /* Save the connection and results in the return parameters */
526
527         forest_cell->gc_search_cell = domain_cell;
528         *msg = m;
529
530 done:
531         PRINT_NTSTATUS_ERROR(nt_status, "search_forest", 4);
532
533         SAFE_FREE(dn);
534
535         free_result_array(ads_list, msg_list, num_resp);
536         talloc_destroy(frame);
537
538         return nt_status;
539 }
540
541 /********************************************************************
542  *******************************************************************/
543
544 static NTSTATUS search_cell_list(struct likewise_cell **c,
545                                  LDAPMessage **m,
546                                  const struct lwcell_filter *fdata)
547 {
548         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
549         struct likewise_cell *cell = NULL;
550         LDAPMessage *msg = NULL;
551         struct likewise_cell *result_cell = NULL;
552
553         if ((cell = cell_list_head()) == NULL) {
554                 nt_status = NT_STATUS_INVALID_SERVER_STATE;
555                 BAIL_ON_NTSTATUS_ERROR(nt_status);
556         }
557
558         while (cell) {
559                 /* Clear any previous GC search results */
560
561                 cell->gc_search_cell = NULL;
562
563                 if (cell_search_forest(cell)) {
564                         nt_status = search_forest(cell, &msg, fdata);
565                 } else {
566                         nt_status = search_cell(cell, &msg, fdata);
567                 }
568
569                 /* Always point to the search result cell.
570                    In forests this might be for another domain
571                    which means the schema model may be different */
572
573                 result_cell = cell->gc_search_cell ?
574                         cell->gc_search_cell : cell;
575
576                 /* Check if we are done */
577
578                 if (NT_STATUS_IS_OK(nt_status)) {
579                         break;
580                 }
581
582                 /* No luck.  Free memory and hit the next cell.
583                    Forest searches always set the gc_search_cell
584                    so give preference to that connection if possible. */
585
586                 ads_msgfree(cell_connection(result_cell), msg);
587                 msg = NULL;
588
589                 cell = cell->next;
590         }
591
592         /* This might be assigning NULL but that is ok as long as we
593            give back the proper error code */
594
595         *c = result_cell;
596         *m = msg;
597
598 done:
599         PRINT_NTSTATUS_ERROR(nt_status, "search_cell_list", 3);
600
601         return nt_status;
602 }
603
604 /********************************************************************
605  Pull the SID from an object which is always stored in the keywords
606  attribute as "backLink=S-1-5-21-..."
607  *******************************************************************/
608
609 static NTSTATUS pull_sid(struct likewise_cell *c,
610                          LDAPMessage *msg,
611                          DOM_SID *sid)
612 {
613         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
614         TALLOC_CTX *frame = talloc_stackframe();
615         ADS_STRUCT *ads = NULL;
616
617         ads = cell_connection(c);
618
619         /*
620            We have two ways of getting the sid:
621            (a) from the objectSID in case of a GC search,
622            (b) from backLink in the case of a cell search.
623            Pull the keywords attributes and grab the backLink.
624         */
625
626         if (!ads_pull_sid(ads, msg, "objectSid", sid)) {
627                 char **keywords;
628                 char *s;
629                 size_t num_lines = 0;
630
631                 keywords = ads_pull_strings(ads, frame, msg,
632                                             "keywords", &num_lines);
633                 BAIL_ON_PTR_ERROR(keywords, nt_status);
634
635                 s = find_attr_string(keywords, num_lines, "backLink");
636                 if (!s) {
637                         nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
638                         BAIL_ON_NTSTATUS_ERROR(nt_status);
639                 }
640
641                 if (!string_to_sid(sid, s)) {
642                         nt_status = NT_STATUS_INVALID_SID;
643                         BAIL_ON_NTSTATUS_ERROR(nt_status);
644                 }
645         }
646
647         nt_status = NT_STATUS_OK;
648
649 done:
650         talloc_destroy(frame);
651
652         return nt_status;
653 }
654
655 /********************************************************************
656  *******************************************************************/
657
658 static NTSTATUS get_object_type(struct likewise_cell *c,
659                                 LDAPMessage *msg,
660                                 enum id_type *type)
661 {
662         TALLOC_CTX *ctx = talloc_stackframe();
663         char **oc_list = NULL;
664         NTSTATUS nt_status = NT_STATUS_OK;
665         size_t list_size = 0;
666         char *s = NULL;
667         ADS_STRUCT *ads = NULL;
668
669         ads = cell_connection(c);
670
671         /* Deal with RFC 2307 support first */
672
673         if (cell_flags(c) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
674                 oc_list = ads_pull_strings(ads, ctx, msg,
675                                            "objectClass", &list_size);
676                 if (!oc_list) {
677                         nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
678                         goto done;
679                 }
680
681                 /* Check for posix classes and AD classes */
682
683                 if (is_object_class(oc_list, list_size, ADEX_OC_POSIX_USER)
684                     || is_object_class(oc_list, list_size, AD_USER)) {
685                         *type = ID_TYPE_UID;
686                 } else if (is_object_class(oc_list, list_size, ADEX_OC_POSIX_GROUP)
687                            || is_object_class(oc_list, list_size, AD_GROUP)) {
688                         *type = ID_TYPE_GID;
689                 } else {
690                         *type = ID_TYPE_NOT_SPECIFIED;
691                         nt_status = NT_STATUS_INVALID_PARAMETER;
692                 }
693         } else {
694                 /* Default to non-schema mode */
695
696                 oc_list = ads_pull_strings(ads, ctx, msg,
697                                            "keywords", &list_size);
698                 if (!oc_list) {
699                         nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
700                         goto done;
701                 }
702
703                 s = find_attr_string(oc_list, list_size, "objectClass");
704                 if (!s) {
705                         nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
706                         goto done;
707                 }
708
709                 if (strequal(s, ADEX_OC_USER)) {
710                         *type = ID_TYPE_UID;
711                 } else if (strequal(s, ADEX_OC_GROUP)) {
712                         *type = ID_TYPE_GID;
713                 } else {
714                         *type = ID_TYPE_NOT_SPECIFIED;
715                         nt_status = NT_STATUS_INVALID_PARAMETER;
716                 }
717         }
718
719         nt_status = NT_STATUS_OK;
720
721 done:
722         talloc_destroy(ctx);
723
724         return nt_status;
725 }
726
727 /********************************************************************
728  Pull an attribute uint32_t  value
729  *******************************************************************/
730
731 static NTSTATUS get_object_uint32(struct likewise_cell *c,
732                                   LDAPMessage *msg,
733                                   const char *attrib,
734                                   uint32_t *x)
735 {
736         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
737         char **keywords = NULL;
738         size_t list_size = 0;
739         TALLOC_CTX *frame = talloc_stackframe();
740         ADS_STRUCT *ads = NULL;
741
742         ads = cell_connection(c);
743
744         /* Deal with RFC2307 schema */
745
746         if (cell_flags(c) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
747                 if (!ads_pull_uint32(ads, msg, attrib, x)) {
748                         nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
749                         BAIL_ON_NTSTATUS_ERROR(nt_status);
750                 }
751         } else {
752                 /* Non-schema mode */
753                 char *s = NULL;
754                 uint32_t num;
755
756                 keywords = ads_pull_strings(ads, frame, msg, "keywords",
757                                             &list_size);
758                 BAIL_ON_PTR_ERROR(keywords, nt_status);
759
760                 s = find_attr_string(keywords, list_size, attrib);
761                 if (!s) {
762                         nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
763                         BAIL_ON_NTSTATUS_ERROR(nt_status);
764                 }
765
766                 num = strtoll(s, NULL, 10);
767                 if (errno == ERANGE) {
768                         nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
769                         BAIL_ON_NTSTATUS_ERROR(nt_status);
770                 }
771                 *x = num;
772         }
773
774         nt_status = NT_STATUS_OK;
775
776 done:
777         talloc_destroy(frame);
778
779         return nt_status;
780 }
781
782 /********************************************************************
783  *******************************************************************/
784
785 static NTSTATUS get_object_id(struct likewise_cell *c,
786                               LDAPMessage *msg,
787                               enum id_type type,
788                               uint32_t *id)
789 {
790         NTSTATUS nt_status = NT_STATUS_OK;
791         const char *id_attr;
792
793         /* Figure out which attribute we need to pull */
794
795         switch (type) {
796         case ID_TYPE_UID:
797                 id_attr = ADEX_ATTR_UIDNUM;
798                 break;
799         case ID_TYPE_GID:
800                 id_attr = ADEX_ATTR_GIDNUM;
801                 break;
802         default:
803                 nt_status = NT_STATUS_INVALID_PARAMETER;
804                 BAIL_ON_NTSTATUS_ERROR(nt_status);
805                 break;
806         }
807
808         nt_status = get_object_uint32(c, msg, id_attr, id);
809         BAIL_ON_NTSTATUS_ERROR(nt_status);
810
811 done:
812         return nt_status;
813 }
814
815 /********************************************************************
816  Pull the uid/gid and type from an object.  This differs depending on
817  the cell flags.
818  *******************************************************************/
819
820 static NTSTATUS pull_id(struct likewise_cell *c,
821                         LDAPMessage *msg,
822                         uint32_t *id,
823                         enum id_type *type)
824 {
825         NTSTATUS nt_status;
826
827         nt_status = get_object_type(c, msg, type);
828         BAIL_ON_NTSTATUS_ERROR(nt_status);
829
830         nt_status = get_object_id(c, msg, *type, id);
831         BAIL_ON_NTSTATUS_ERROR(nt_status);
832
833 done:
834         return nt_status;
835 }
836
837 /********************************************************************
838  Pull an attribute string value
839  *******************************************************************/
840
841 static NTSTATUS get_object_string(struct likewise_cell *c,
842                                   LDAPMessage *msg,
843                                   TALLOC_CTX *ctx,
844                                   const char *attrib,
845                                   char **string)
846 {
847         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
848         char **keywords = NULL;
849         size_t list_size = 0;
850         TALLOC_CTX *frame = talloc_stackframe();
851         ADS_STRUCT *ads = NULL;
852
853         *string = NULL;
854
855         ads = cell_connection(c);
856
857         /* Deal with RFC2307 schema */
858
859         if (cell_flags(c) & LWCELL_FLAG_USE_RFC2307_ATTRS) {
860                 *string = ads_pull_string(ads, ctx, msg, attrib);
861         } else {
862                 /* Non-schema mode */
863
864                 char *s = NULL;
865
866                 keywords = ads_pull_strings(ads, frame, msg,
867                                             "keywords", &list_size);
868                 if (!keywords) {
869                         nt_status = NT_STATUS_NO_MEMORY;
870                         BAIL_ON_NTSTATUS_ERROR(nt_status);
871                 }
872                 s = find_attr_string(keywords, list_size, attrib);
873                 if (s) {
874                         *string = talloc_strdup(ctx, s);
875                 }
876         }
877
878         if (!*string) {
879                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
880                 BAIL_ON_NTSTATUS_ERROR(nt_status);
881         }
882
883         nt_status = NT_STATUS_OK;
884
885 done:
886         talloc_destroy(frame);
887
888         return nt_status;
889 }
890
891 /********************************************************************
892  Pull the struct passwd fields for a user
893  *******************************************************************/
894
895 static NTSTATUS pull_nss_info(struct likewise_cell *c,
896                               LDAPMessage *msg,
897                               TALLOC_CTX *ctx,
898                               char **homedir,
899                               char **shell,
900                               char **gecos,
901                               gid_t *p_gid)
902 {
903         NTSTATUS nt_status;
904
905         nt_status = get_object_string(c, msg, ctx, ADEX_ATTR_HOMEDIR, homedir);
906         BAIL_ON_NTSTATUS_ERROR(nt_status);
907
908         nt_status = get_object_string(c, msg, ctx, ADEX_ATTR_SHELL, shell);
909         BAIL_ON_NTSTATUS_ERROR(nt_status);
910
911         nt_status = get_object_string(c, msg, ctx, ADEX_ATTR_GECOS, gecos);
912         /* Gecos is often not set so ignore failures */
913
914         nt_status = get_object_uint32(c, msg, ADEX_ATTR_GIDNUM, p_gid);
915         BAIL_ON_NTSTATUS_ERROR(nt_status);
916
917 done:
918         return nt_status;
919 }
920
921 /********************************************************************
922  Pull the struct passwd fields for a user
923  *******************************************************************/
924
925 static NTSTATUS pull_alias(struct likewise_cell *c,
926                            LDAPMessage *msg,
927                            TALLOC_CTX *ctx,
928                            char **alias)
929 {
930         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
931         enum id_type type;
932         const char *attr = NULL;
933
934         /* Figure out if this is a user or a group */
935
936         nt_status = get_object_type(c, msg, &type);
937         BAIL_ON_NTSTATUS_ERROR(nt_status);
938
939         switch (type) {
940         case ID_TYPE_UID:
941                 attr = ADEX_ATTR_UID;
942                 break;
943         case ID_TYPE_GID:
944                 /* What is the group attr for RFC2307 Forests? */
945                 attr = ADEX_ATTR_DISPLAYNAME;
946                 break;
947         default:
948                 nt_status = NT_STATUS_INVALID_PARAMETER;
949                 BAIL_ON_NTSTATUS_ERROR(nt_status);
950                 break;
951         }
952
953         nt_status = get_object_string(c, msg, ctx, attr, alias);
954         BAIL_ON_NTSTATUS_ERROR(nt_status);
955
956 done:
957         return nt_status;
958 }
959
960 /********************************************************************
961  *******************************************************************/
962
963 static NTSTATUS _ccp_get_sid_from_id(DOM_SID * sid,
964                                      uint32_t id, enum id_type type)
965 {
966         struct likewise_cell *cell = NULL;
967         LDAPMessage *msg = NULL;
968         NTSTATUS nt_status;
969         struct lwcell_filter filter;
970
971         filter.ftype = IdFilter;
972         filter.filter.id.id = id;
973         filter.filter.id.type = type;
974
975         nt_status = search_cell_list(&cell, &msg, &filter);
976         BAIL_ON_NTSTATUS_ERROR(nt_status);
977
978         nt_status = pull_sid(cell, msg, sid);
979         BAIL_ON_NTSTATUS_ERROR(nt_status);
980
981 done:
982         ads_msgfree(cell->conn, msg);
983
984         return nt_status;
985 }
986
987 /********************************************************************
988  *******************************************************************/
989
990 static NTSTATUS _ccp_get_id_from_sid(uint32_t * id,
991                                      enum id_type *type,
992                                      const DOM_SID * sid)
993 {
994         struct likewise_cell *cell = NULL;
995         LDAPMessage *msg = NULL;
996         NTSTATUS nt_status;
997         struct lwcell_filter filter;
998
999         filter.ftype = SidFilter;
1000         sid_copy(&filter.filter.sid, sid);
1001
1002         nt_status = search_cell_list(&cell, &msg, &filter);
1003         BAIL_ON_NTSTATUS_ERROR(nt_status);
1004
1005         nt_status = pull_id(cell, msg, id, type);
1006         BAIL_ON_NTSTATUS_ERROR(nt_status);
1007
1008         if (*id < min_id_value()) {
1009                 nt_status = NT_STATUS_INVALID_PARAMETER;
1010                 BAIL_ON_NTSTATUS_ERROR(nt_status);
1011         }
1012
1013 done:
1014         ads_msgfree(cell->conn, msg);
1015
1016         return nt_status;
1017 }
1018
1019 /********************************************************************
1020  *******************************************************************/
1021
1022 static NTSTATUS _ccp_nss_get_info(const DOM_SID * sid,
1023                                   TALLOC_CTX * ctx,
1024                                   char **homedir,
1025                                   char **shell,
1026                                   char **gecos, gid_t * p_gid)
1027 {
1028         struct likewise_cell *cell = NULL;
1029         LDAPMessage *msg = NULL;
1030         NTSTATUS nt_status;
1031         struct lwcell_filter filter;
1032         enum id_type type;
1033
1034         filter.ftype = SidFilter;
1035         sid_copy(&filter.filter.sid, sid);
1036
1037         nt_status = search_cell_list(&cell, &msg, &filter);
1038         BAIL_ON_NTSTATUS_ERROR(nt_status);
1039
1040         nt_status = get_object_type(cell, msg, &type);
1041         BAIL_ON_NTSTATUS_ERROR(nt_status);
1042
1043         if (type != ID_TYPE_UID) {
1044                 nt_status = NT_STATUS_NO_SUCH_USER;
1045                 BAIL_ON_NTSTATUS_ERROR(nt_status);
1046         }
1047
1048         nt_status = pull_nss_info(cell, msg, ctx, homedir, shell, gecos,
1049                                   (uint32_t*) p_gid);
1050         BAIL_ON_NTSTATUS_ERROR(nt_status);
1051
1052 done:
1053         ads_msgfree(cell->conn, msg);
1054
1055         return nt_status;
1056 }
1057
1058 /**********************************************************************
1059  *********************************************************************/
1060
1061 static NTSTATUS _ccp_map_to_alias(TALLOC_CTX *ctx,
1062                                   const char *domain,
1063                                   const char *name, char **alias)
1064 {
1065         TALLOC_CTX *frame = talloc_stackframe();
1066         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1067         DOM_SID sid;
1068         struct likewise_cell *cell = NULL;
1069         LDAPMessage *msg = NULL;
1070         struct lwcell_filter filter;
1071         enum lsa_SidType sid_type;
1072
1073         /* Convert the name to a SID */
1074
1075         nt_status = gc_name_to_sid(domain, name, &sid, &sid_type);
1076         BAIL_ON_NTSTATUS_ERROR(nt_status);
1077
1078         /* Find the user/group */
1079
1080         filter.ftype = SidFilter;
1081         sid_copy(&filter.filter.sid, &sid);
1082
1083         nt_status = search_cell_list(&cell, &msg, &filter);
1084         BAIL_ON_NTSTATUS_ERROR(nt_status);
1085
1086         /* Pull the alias and return */
1087
1088         nt_status = pull_alias(cell, msg, ctx, alias);
1089         BAIL_ON_NTSTATUS_ERROR(nt_status);
1090
1091 done:
1092         PRINT_NTSTATUS_ERROR(nt_status, "map_to_alias", 3);
1093
1094         talloc_destroy(frame);
1095         ads_msgfree(cell_connection(cell), msg);
1096
1097         return nt_status;
1098 }
1099
1100 /**********************************************************************
1101  Map from an alias name to the canonical, qualified name.
1102  Ensure that the alias is only pull from the closest in which
1103  the user or gorup is enabled in
1104  *********************************************************************/
1105
1106 static NTSTATUS _ccp_map_from_alias(TALLOC_CTX *mem_ctx,
1107                                     const char *domain,
1108                                     const char *alias, char **name)
1109 {
1110         TALLOC_CTX *frame = talloc_stackframe();
1111         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1112         DOM_SID sid;
1113         struct likewise_cell *cell_alias = NULL;
1114         LDAPMessage *msg_alias = NULL;
1115         struct likewise_cell *cell_sid = NULL;
1116         LDAPMessage *msg_sid = NULL;
1117         struct lwcell_filter filter;
1118         char *canonical_name = NULL;
1119         enum lsa_SidType type;
1120
1121         /* Find the user/group */
1122
1123         filter.ftype = AliasFilter;
1124         fstrcpy(filter.filter.alias, alias);
1125
1126         nt_status = search_cell_list(&cell_alias, &msg_alias, &filter);
1127         BAIL_ON_NTSTATUS_ERROR(nt_status);
1128
1129         nt_status = pull_sid(cell_alias, msg_alias, &sid);
1130         BAIL_ON_NTSTATUS_ERROR(nt_status);
1131
1132         /* Now search again for the SID according to the cell list.
1133            Verify that the cell of both search results is the same
1134            so that we only match an alias from the closest cell
1135            in which a user/group has been instantied. */
1136
1137         filter.ftype = SidFilter;
1138         sid_copy(&filter.filter.sid, &sid);
1139
1140         nt_status = search_cell_list(&cell_sid, &msg_sid, &filter);
1141         BAIL_ON_NTSTATUS_ERROR(nt_status);
1142
1143         if (cell_alias != cell_sid) {
1144                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1145                 BAIL_ON_NTSTATUS_ERROR(nt_status);
1146         }
1147
1148         /* Finally do the GC sid/name conversion */
1149
1150         nt_status = gc_sid_to_name(&sid, &canonical_name, &type);
1151         BAIL_ON_NTSTATUS_ERROR(nt_status);
1152
1153         *name = talloc_strdup(mem_ctx, canonical_name);
1154         BAIL_ON_PTR_ERROR((*name), nt_status);
1155
1156         nt_status = NT_STATUS_OK;
1157
1158 done:
1159         PRINT_NTSTATUS_ERROR(nt_status, "map_from_alias", 3);
1160
1161         ads_msgfree(cell_connection(cell_alias), msg_alias);
1162         ads_msgfree(cell_connection(cell_sid), msg_sid);
1163
1164         SAFE_FREE(canonical_name);
1165
1166         talloc_destroy(frame);
1167
1168         return nt_status;
1169 }
1170
1171 /********************************************************************
1172  *******************************************************************/
1173
1174 struct cell_provider_api ccp_unified = {
1175         .get_sid_from_id = _ccp_get_sid_from_id,
1176         .get_id_from_sid = _ccp_get_id_from_sid,
1177         .get_nss_info    = _ccp_nss_get_info,
1178         .map_to_alias    = _ccp_map_to_alias,
1179         .map_from_alias  = _ccp_map_from_alias
1180 };