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