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