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