s3:winbindd/idmap_ad: refactor core of nss_{sfu|sfu20|rfc2307}_init to common function.
[metze/samba/wip.git] / source3 / winbindd / idmap_ad.c
1 /*
2  *  idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts
3  *
4  * Unix SMB/CIFS implementation.
5  *
6  * Winbind ADS backend functions
7  *
8  * Copyright (C) Andrew Tridgell 2001
9  * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
10  * Copyright (C) Gerald (Jerry) Carter 2004-2007
11  * Copyright (C) Luke Howard 2001-2004
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, see <http://www.gnu.org/licenses/>.
25  */
26
27 #include "includes.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_IDMAP
31
32 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
33
34 #define IDMAP_AD_MAX_IDS 30
35 #define CHECK_ALLOC_DONE(mem) do { \
36      if (!mem) { \
37            DEBUG(0, ("Out of memory!\n")); \
38            ret = NT_STATUS_NO_MEMORY; \
39            goto done; \
40       } \
41 } while (0)
42
43 struct idmap_ad_context {
44         uint32_t filter_low_id;
45         uint32_t filter_high_id;
46 };
47
48 NTSTATUS init_module(void);
49
50 static ADS_STRUCT *ad_idmap_ads = NULL;
51 static struct posix_schema *ad_schema = NULL;
52 static enum wb_posix_mapping ad_map_type = WB_POSIX_MAP_UNKNOWN;
53
54 /************************************************************************
55  ***********************************************************************/
56
57 static ADS_STRUCT *ad_idmap_cached_connection_internal(void)
58 {
59         ADS_STRUCT *ads;
60         ADS_STATUS status;
61         bool local = False;
62         fstring dc_name;
63         struct sockaddr_storage dc_ip;
64
65         if (ad_idmap_ads != NULL) {
66
67                 time_t expire;
68                 time_t now = time(NULL);
69
70                 ads = ad_idmap_ads;
71
72                 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
73
74                 /* check for a valid structure */
75                 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
76                           (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
77
78                 if ( ads->config.realm && (expire > time(NULL))) {
79                         return ads;
80                 } else {
81                         /* we own this ADS_STRUCT so make sure it goes away */
82                         DEBUG(7,("Deleting expired krb5 credential cache\n"));
83                         ads->is_mine = True;
84                         ads_destroy( &ads );
85                         ads_kdestroy(WINBIND_CCACHE_NAME);
86                         ad_idmap_ads = NULL;
87                         TALLOC_FREE( ad_schema );                       
88                 }
89         }
90
91         if (!local) {
92                 /* we don't want this to affect the users ccache */
93                 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
94         }
95
96         if ( (ads = ads_init(lp_realm(), lp_workgroup(), NULL)) == NULL ) {
97                 DEBUG(1,("ads_init failed\n"));
98                 return NULL;
99         }
100
101         /* the machine acct password might have change - fetch it every time */
102         SAFE_FREE(ads->auth.password);
103         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
104
105         SAFE_FREE(ads->auth.realm);
106         ads->auth.realm = SMB_STRDUP(lp_realm());
107
108         /* setup server affinity */
109
110         get_dc_name( NULL, ads->auth.realm, dc_name, &dc_ip );
111         
112         status = ads_connect(ads);
113         if (!ADS_ERR_OK(status)) {
114                 DEBUG(1, ("ad_idmap_init: failed to connect to AD\n"));
115                 ads_destroy(&ads);
116                 return NULL;
117         }
118
119         ads->is_mine = False;
120
121         ad_idmap_ads = ads;
122
123         return ads;
124 }
125
126 /************************************************************************
127  ***********************************************************************/
128
129 static ADS_STRUCT *ad_idmap_cached_connection(void)
130 {
131         ADS_STRUCT *ads = ad_idmap_cached_connection_internal();
132         
133         if ( !ads )
134                 return NULL;
135
136         /* if we have a valid ADS_STRUCT and the schema model is
137            defined, then we can return here. */
138
139         if ( ad_schema )
140                 return ads;
141
142         /* Otherwise, set the schema model */
143
144         if ( (ad_map_type ==  WB_POSIX_MAP_SFU) ||
145              (ad_map_type ==  WB_POSIX_MAP_SFU20) || 
146              (ad_map_type ==  WB_POSIX_MAP_RFC2307) ) 
147         {
148                 ADS_STATUS schema_status;
149                 
150                 schema_status = ads_check_posix_schema_mapping( NULL, ads, ad_map_type, &ad_schema);
151                 if ( !ADS_ERR_OK(schema_status) ) {
152                         DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
153                         return NULL;                    
154                 }
155         }
156         
157         return ads;
158 }
159
160 /************************************************************************
161  ***********************************************************************/
162
163 static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom,
164                                     const char *params)
165 {
166         struct idmap_ad_context *ctx;
167         char *config_option;
168         const char *range = NULL;
169         const char *schema_mode = NULL; 
170
171         if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context)) == NULL ) {
172                 DEBUG(0, ("Out of memory!\n"));
173                 return NT_STATUS_NO_MEMORY;
174         }
175
176         if ( (config_option = talloc_asprintf(ctx, "idmap config %s", dom->name)) == NULL ) {
177                 DEBUG(0, ("Out of memory!\n"));
178                 talloc_free(ctx);
179                 return NT_STATUS_NO_MEMORY;
180         }
181
182         /* load ranges */
183         range = lp_parm_const_string(-1, config_option, "range", NULL);
184         if (range && range[0]) {
185                 if ((sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) ||
186                     (ctx->filter_low_id > ctx->filter_high_id)) {
187                         DEBUG(1, ("ERROR: invalid filter range [%s]", range));
188                         ctx->filter_low_id = 0;
189                         ctx->filter_high_id = 0;
190                 }
191         }
192
193         /* schema mode */
194         if ( ad_map_type == WB_POSIX_MAP_UNKNOWN )
195                 ad_map_type = WB_POSIX_MAP_RFC2307;
196         schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL);
197         if ( schema_mode && schema_mode[0] ) {
198                 if ( strequal(schema_mode, "sfu") )
199                         ad_map_type = WB_POSIX_MAP_SFU;
200                 else if ( strequal(schema_mode, "sfu20" ) )
201                         ad_map_type = WB_POSIX_MAP_SFU20;
202                 else if ( strequal(schema_mode, "rfc2307" ) )
203                         ad_map_type = WB_POSIX_MAP_RFC2307;
204                 else
205                         DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n",
206                                  schema_mode));
207         }
208
209         dom->private_data = ctx;
210
211         talloc_free(config_option);
212
213         return NT_STATUS_OK;
214 }
215
216 /************************************************************************
217  Search up to IDMAP_AD_MAX_IDS entries in maps for a match.
218  ***********************************************************************/
219
220 static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id)
221 {
222         int i;
223
224         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
225                 if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
226                         return maps[i];
227                 }
228         }
229
230         return NULL;    
231 }
232
233 /************************************************************************
234  Search up to IDMAP_AD_MAX_IDS entries in maps for a match
235  ***********************************************************************/
236
237 static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid)
238 {
239         int i;
240
241         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
242                 if (sid_equal(maps[i]->sid, sid)) {
243                         return maps[i];
244                 }
245         }
246
247         return NULL;    
248 }
249
250 /************************************************************************
251  ***********************************************************************/
252
253 static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
254 {
255         NTSTATUS ret;
256         TALLOC_CTX *memctx;
257         struct idmap_ad_context *ctx;
258         ADS_STATUS rc;
259         ADS_STRUCT *ads;
260         const char *attrs[] = { "sAMAccountType", 
261                                 "objectSid",
262                                 NULL, /* uidnumber */
263                                 NULL, /* gidnumber */
264                                 NULL };
265         LDAPMessage *res = NULL;
266         LDAPMessage *entry = NULL;
267         char *filter = NULL;
268         int idx = 0;
269         int bidx = 0;
270         int count;
271         int i;
272         char *u_filter = NULL;
273         char *g_filter = NULL;
274
275         /* Only do query if we are online */
276         if (idmap_is_offline()) {
277                 return NT_STATUS_FILE_IS_OFFLINE;
278         }
279
280         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
281
282         if ( (memctx = talloc_new(ctx)) == NULL ) {
283                 DEBUG(0, ("Out of memory!\n"));
284                 return NT_STATUS_NO_MEMORY;
285         }
286
287         if ( (ads = ad_idmap_cached_connection()) == NULL ) {
288                 DEBUG(1, ("ADS uninitialized\n"));
289                 ret = NT_STATUS_UNSUCCESSFUL;
290                 goto done;
291         }
292
293         attrs[2] = ad_schema->posix_uidnumber_attr;
294         attrs[3] = ad_schema->posix_gidnumber_attr;
295
296 again:
297         bidx = idx;
298         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
299                 switch (ids[idx]->xid.type) {
300                 case ID_TYPE_UID:     
301                         if ( ! u_filter) {
302                                 u_filter = talloc_asprintf(memctx, "(&(|"
303                                                            "(sAMAccountType=%d)"
304                                                            "(sAMAccountType=%d)"
305                                                            "(sAMAccountType=%d))(|",
306                                                            ATYPE_NORMAL_ACCOUNT,
307                                                            ATYPE_WORKSTATION_TRUST,
308                                                            ATYPE_INTERDOMAIN_TRUST);
309                         }
310                         u_filter = talloc_asprintf_append_buffer(u_filter, "(%s=%lu)",
311                                                           ad_schema->posix_uidnumber_attr,
312                                                           (unsigned long)ids[idx]->xid.id);
313                         CHECK_ALLOC_DONE(u_filter);
314                         break;
315                                 
316                 case ID_TYPE_GID:
317                         if ( ! g_filter) {
318                                 g_filter = talloc_asprintf(memctx, "(&(|"
319                                                            "(sAMAccountType=%d)"
320                                                            "(sAMAccountType=%d))(|",
321                                                            ATYPE_SECURITY_GLOBAL_GROUP,
322                                                            ATYPE_SECURITY_LOCAL_GROUP);
323                         }
324                         g_filter = talloc_asprintf_append_buffer(g_filter, "(%s=%lu)",
325                                                           ad_schema->posix_gidnumber_attr,
326                                                           (unsigned long)ids[idx]->xid.id);
327                         CHECK_ALLOC_DONE(g_filter);
328                         break;
329
330                 default:
331                         DEBUG(3, ("Error: mapping requested but Unknown ID type\n"));
332                         ids[idx]->status = ID_UNKNOWN;
333                         continue;
334                 }
335         }
336         filter = talloc_asprintf(memctx, "(|");
337         CHECK_ALLOC_DONE(filter);
338         if ( u_filter) {
339                 filter = talloc_asprintf_append_buffer(filter, "%s))", u_filter);
340                 CHECK_ALLOC_DONE(filter);
341                         TALLOC_FREE(u_filter);
342         }
343         if ( g_filter) {
344                 filter = talloc_asprintf_append_buffer(filter, "%s))", g_filter);
345                 CHECK_ALLOC_DONE(filter);
346                 TALLOC_FREE(g_filter);
347         }
348         filter = talloc_asprintf_append_buffer(filter, ")");
349         CHECK_ALLOC_DONE(filter);
350
351         rc = ads_search_retry(ads, &res, filter, attrs);
352         if (!ADS_ERR_OK(rc)) {
353                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
354                 ret = NT_STATUS_UNSUCCESSFUL;
355                 goto done;
356         }
357
358         if ( (count = ads_count_replies(ads, res)) == 0 ) {
359                 DEBUG(10, ("No IDs found\n"));
360         }
361
362         entry = res;
363         for (i = 0; (i < count) && entry; i++) {
364                 DOM_SID sid;
365                 enum id_type type;
366                 struct id_map *map;
367                 uint32_t id;
368                 uint32_t atype;
369
370                 if (i == 0) { /* first entry */
371                         entry = ads_first_entry(ads, entry);
372                 } else { /* following ones */
373                         entry = ads_next_entry(ads, entry);
374                 }
375
376                 if ( !entry ) {
377                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
378                         break;
379                 }
380
381                 /* first check if the SID is present */
382                 if (!ads_pull_sid(ads, entry, "objectSid", &sid)) {
383                         DEBUG(2, ("Could not retrieve SID from entry\n"));
384                         continue;
385                 }
386
387                 /* get type */
388                 if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) {
389                         DEBUG(1, ("could not get SAM account type\n"));
390                         continue;
391                 }
392
393                 switch (atype & 0xF0000000) {
394                 case ATYPE_SECURITY_GLOBAL_GROUP:
395                 case ATYPE_SECURITY_LOCAL_GROUP:
396                         type = ID_TYPE_GID;
397                         break;
398                 case ATYPE_NORMAL_ACCOUNT:
399                 case ATYPE_WORKSTATION_TRUST:
400                 case ATYPE_INTERDOMAIN_TRUST:
401                         type = ID_TYPE_UID;
402                         break;
403                 default:
404                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
405                         continue;
406                 }
407
408                 if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? 
409                                                  ad_schema->posix_uidnumber_attr : 
410                                                  ad_schema->posix_gidnumber_attr, 
411                                      &id)) 
412                 {
413                         DEBUG(1, ("Could not get unix ID\n"));
414                         continue;
415                 }
416
417                 if ((id == 0) ||
418                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
419                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
420                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
421                                 id, ctx->filter_low_id, ctx->filter_high_id));
422                         continue;
423                 }
424
425                 map = find_map_by_id(&ids[bidx], type, id);
426                 if (!map) {
427                         DEBUG(2, ("WARNING: couldn't match result with requested ID\n"));
428                         continue;
429                 }
430
431                 sid_copy(map->sid, &sid);
432
433                 /* mapped */
434                 map->status = ID_MAPPED;
435
436                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
437                            (unsigned long)map->xid.id,
438                            map->xid.type));
439         }
440
441         if (res) {
442                 ads_msgfree(ads, res);
443         }
444
445         if (ids[idx]) { /* still some values to map */
446                 goto again;
447         }
448
449         ret = NT_STATUS_OK;
450
451         /* mark all unknown/expired ones as unmapped */
452         for (i = 0; ids[i]; i++) {
453                 if (ids[i]->status != ID_MAPPED) 
454                         ids[i]->status = ID_UNMAPPED;
455         }
456
457 done:
458         talloc_free(memctx);
459         return ret;
460 }
461
462 /************************************************************************
463  ***********************************************************************/
464
465 static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
466 {
467         NTSTATUS ret;
468         TALLOC_CTX *memctx;
469         struct idmap_ad_context *ctx;
470         ADS_STATUS rc;
471         ADS_STRUCT *ads;
472         const char *attrs[] = { "sAMAccountType", 
473                                 "objectSid",
474                                 NULL, /* attr_uidnumber */
475                                 NULL, /* attr_gidnumber */
476                                 NULL };
477         LDAPMessage *res = NULL;
478         LDAPMessage *entry = NULL;
479         char *filter = NULL;
480         int idx = 0;
481         int bidx = 0;
482         int count;
483         int i;
484         char *sidstr;
485
486         /* Only do query if we are online */
487         if (idmap_is_offline()) {
488                 return NT_STATUS_FILE_IS_OFFLINE;
489         }
490
491         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);      
492
493         if ( (memctx = talloc_new(ctx)) == NULL ) {             
494                 DEBUG(0, ("Out of memory!\n"));
495                 return NT_STATUS_NO_MEMORY;
496         }
497
498         if ( (ads = ad_idmap_cached_connection()) == NULL ) {
499                 DEBUG(1, ("ADS uninitialized\n"));
500                 ret = NT_STATUS_UNSUCCESSFUL;
501                 goto done;
502         }
503
504         attrs[2] = ad_schema->posix_uidnumber_attr;
505         attrs[3] = ad_schema->posix_gidnumber_attr;
506
507 again:
508         filter = talloc_asprintf(memctx, "(&(|"
509                                  "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
510                                  "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
511                                  ")(|",
512                                  ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
513                                  ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
514                 
515         CHECK_ALLOC_DONE(filter);
516
517         bidx = idx;
518         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
519
520                 ids[idx]->status = ID_UNKNOWN;
521
522                 sidstr = sid_binstring(ids[idx]->sid);
523                 filter = talloc_asprintf_append_buffer(filter, "(objectSid=%s)", sidstr);
524                         
525                 free(sidstr);
526                 CHECK_ALLOC_DONE(filter);
527         }
528         filter = talloc_asprintf_append_buffer(filter, "))");
529         CHECK_ALLOC_DONE(filter);
530         DEBUG(10, ("Filter: [%s]\n", filter));
531
532         rc = ads_search_retry(ads, &res, filter, attrs);
533         if (!ADS_ERR_OK(rc)) {
534                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
535                 ret = NT_STATUS_UNSUCCESSFUL;
536                 goto done;
537         }
538
539         if ( (count = ads_count_replies(ads, res)) == 0 ) {
540                 DEBUG(10, ("No IDs found\n"));
541         }
542
543         entry = res;    
544         for (i = 0; (i < count) && entry; i++) {
545                 DOM_SID sid;
546                 enum id_type type;
547                 struct id_map *map;
548                 uint32_t id;
549                 uint32_t atype;
550
551                 if (i == 0) { /* first entry */
552                         entry = ads_first_entry(ads, entry);
553                 } else { /* following ones */
554                         entry = ads_next_entry(ads, entry);
555                 }
556
557                 if ( !entry ) {
558                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
559                         break;
560                 }
561
562                 /* first check if the SID is present */
563                 if (!ads_pull_sid(ads, entry, "objectSid", &sid)) {
564                         DEBUG(2, ("Could not retrieve SID from entry\n"));
565                         continue;
566                 }
567
568                 map = find_map_by_sid(&ids[bidx], &sid);
569                 if (!map) {
570                         DEBUG(2, ("WARNING: couldn't match result with requested SID\n"));
571                         continue;
572                 }
573
574                 /* get type */
575                 if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) {
576                         DEBUG(1, ("could not get SAM account type\n"));
577                         continue;
578                 }
579
580                 switch (atype & 0xF0000000) {
581                 case ATYPE_SECURITY_GLOBAL_GROUP:
582                 case ATYPE_SECURITY_LOCAL_GROUP:
583                         type = ID_TYPE_GID;
584                         break;
585                 case ATYPE_NORMAL_ACCOUNT:
586                 case ATYPE_WORKSTATION_TRUST:
587                 case ATYPE_INTERDOMAIN_TRUST:
588                         type = ID_TYPE_UID;
589                         break;
590                 default:
591                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
592                         continue;
593                 }
594
595                 if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? 
596                                                  ad_schema->posix_uidnumber_attr : 
597                                                  ad_schema->posix_gidnumber_attr, 
598                                      &id)) 
599                 {
600                         DEBUG(1, ("Could not get unix ID\n"));
601                         continue;
602                 }
603                 if ((id == 0) ||
604                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
605                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
606                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
607                                 id, ctx->filter_low_id, ctx->filter_high_id));
608                         continue;
609                 }
610
611                 /* mapped */
612                 map->xid.type = type;
613                 map->xid.id = id;
614                 map->status = ID_MAPPED;
615
616                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
617                            (unsigned long)map->xid.id,
618                            map->xid.type));
619         }
620
621         if (res) {
622                 ads_msgfree(ads, res);
623         }
624
625         if (ids[idx]) { /* still some values to map */
626                 goto again;
627         }
628
629         ret = NT_STATUS_OK;
630
631         /* mark all unknwoni/expired ones as unmapped */
632         for (i = 0; ids[i]; i++) {
633                 if (ids[i]->status != ID_MAPPED) 
634                         ids[i]->status = ID_UNMAPPED;
635         }
636
637 done:
638         talloc_free(memctx);
639         return ret;
640 }
641
642 /************************************************************************
643  ***********************************************************************/
644
645 static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
646 {
647         ADS_STRUCT *ads = ad_idmap_ads;
648
649         if (ads != NULL) {
650                 /* we own this ADS_STRUCT so make sure it goes away */
651                 ads->is_mine = True;
652                 ads_destroy( &ads );
653                 ad_idmap_ads = NULL;
654         }
655
656         TALLOC_FREE( ad_schema );
657         
658         return NT_STATUS_OK;
659 }
660
661 /*
662  * nss_info_{sfu,sfu20,rfc2307}
663  */
664
665 /************************************************************************
666  Initialize the {sfu,sfu20,rfc2307} state
667  ***********************************************************************/
668
669 static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
670 static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
671 static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
672 static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
673 static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
674 static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
675
676 static const char *ad_map_type_string(enum wb_posix_mapping map_type)
677 {
678         switch (map_type) {
679                 case WB_POSIX_MAP_TEMPLATE:
680                         return wb_posix_map_template_string;
681                         break;
682                 case WB_POSIX_MAP_SFU:
683                         return wb_posix_map_sfu_string;
684                         break;
685                 case WB_POSIX_MAP_SFU20:
686                         return wb_posix_map_sfu20_string;
687                         break;
688                 case WB_POSIX_MAP_RFC2307:
689                         return wb_posix_map_rfc2307_string;
690                         break;
691                 case WB_POSIX_MAP_UNIXINFO:
692                         return wb_posix_map_unixinfo_string;
693                         break;
694                 default:
695                         return WB_POSIX_MAP_UNKNOWN;
696         }
697 }
698
699 static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
700                                     enum wb_posix_mapping new_ad_map_type)
701 {
702         /* Sanity check if we have previously been called with a
703            different schema model */
704
705         if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
706              (ad_map_type != new_ad_map_type))
707         {
708                 DEBUG(0,("nss_ad_generic_init: "
709                          "Cannot set Posix map type to %s. "
710                          "Map type has already been set to %s."
711                          "Mixed schema models not supported!\n",
712                          ad_map_type_string(new_ad_map_type),
713                          ad_map_type_string(ad_map_type)));
714                 return NT_STATUS_NOT_SUPPORTED;
715         }
716
717         ad_map_type = new_ad_map_type;
718
719         return NT_STATUS_OK;
720 }
721
722 static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
723 {
724         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
725 }
726
727 static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
728 {
729         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
730 }
731
732 static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
733 {
734         return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
735 }
736
737
738 /************************************************************************
739  ***********************************************************************/
740 static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
741                                   const DOM_SID *sid, 
742                                   TALLOC_CTX *mem_ctx,
743                                   ADS_STRUCT *ads, 
744                                   LDAPMessage *msg,
745                                   char **homedir,
746                                   char **shell, 
747                                   char **gecos,
748                                   uint32 *gid )
749 {
750         ADS_STRUCT *ads_internal = NULL;
751         const char *attrs[] = {NULL, /* attr_homedir */
752                                NULL, /* attr_shell */
753                                NULL, /* attr_gecos */
754                                NULL, /* attr_gidnumber */
755                                NULL };
756         char *filter = NULL;
757         LDAPMessage *msg_internal = NULL;
758         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
759         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
760         char *sidstr = NULL;
761
762         /* Only do query if we are online */
763         if (idmap_is_offline()) {
764                 return NT_STATUS_FILE_IS_OFFLINE;
765         }
766
767         /* We are assuming that the internal ADS_STRUCT is for the 
768            same forest as the incoming *ads pointer */
769
770         ads_internal = ad_idmap_cached_connection();
771
772         if ( !ads_internal || !ad_schema ) {
773                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
774         }
775
776         if (!sid || !homedir || !shell || !gecos) {
777                 return NT_STATUS_INVALID_PARAMETER;
778         }
779
780         /* See if we can use the ADS connection struct swe were given */
781
782         if (ads) {
783                 *homedir = ads_pull_string( ads, mem_ctx, msg, ad_schema->posix_homedir_attr );
784                 *shell   = ads_pull_string( ads, mem_ctx, msg, ad_schema->posix_shell_attr );
785                 *gecos   = ads_pull_string( ads, mem_ctx, msg, ad_schema->posix_gecos_attr );
786
787                 if (gid) {
788                         if ( !ads_pull_uint32(ads, msg, ad_schema->posix_gidnumber_attr, gid ) )
789                                 *gid = (uint32)-1;
790                 }
791
792                 nt_status = NT_STATUS_OK;
793                 goto done;
794         }
795
796         /* Have to do our own query */
797
798         attrs[0] = ad_schema->posix_homedir_attr;
799         attrs[1] = ad_schema->posix_shell_attr;
800         attrs[2] = ad_schema->posix_gecos_attr;
801         attrs[3] = ad_schema->posix_gidnumber_attr;
802
803         sidstr = sid_binstring(sid);
804         filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
805         SAFE_FREE(sidstr);
806
807         if (!filter) {
808                 nt_status = NT_STATUS_NO_MEMORY;
809                 goto done;
810         }
811
812         ads_status = ads_search_retry(ads_internal, &msg_internal, filter, attrs);
813         if (!ADS_ERR_OK(ads_status)) {
814                 nt_status = ads_ntstatus(ads_status);
815                 goto done;
816         }
817
818         *homedir = ads_pull_string(ads_internal, mem_ctx, msg_internal, ad_schema->posix_homedir_attr);
819         *shell   = ads_pull_string(ads_internal, mem_ctx, msg_internal, ad_schema->posix_shell_attr);
820         *gecos   = ads_pull_string(ads_internal, mem_ctx, msg_internal, ad_schema->posix_gecos_attr);
821
822         if (gid) {
823                 if (!ads_pull_uint32(ads_internal, msg_internal, ad_schema->posix_gidnumber_attr, gid))
824                         *gid = (uint32)-1;
825         }
826
827         nt_status = NT_STATUS_OK;
828
829 done:
830         if (msg_internal) {
831                 ads_msgfree(ads_internal, msg_internal);
832         }
833
834         return nt_status;
835 }
836
837 /**********************************************************************
838  *********************************************************************/
839
840 static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
841                                     struct nss_domain_entry *e,
842                                     const char *name,
843                                     char **alias)
844 {
845         ADS_STRUCT *ads_internal = NULL;
846         const char *attrs[] = {NULL, /* attr_uid */
847                                NULL };
848         char *filter = NULL;
849         LDAPMessage *msg = NULL;
850         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
851         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
852
853         /* Check incoming parameters */
854
855         if ( !e || !e->domain || !name || !*alias) {
856                 nt_status = NT_STATUS_INVALID_PARAMETER;
857                 goto done;
858         }
859
860         /* Only do query if we are online */
861
862         if (idmap_is_offline()) {
863                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
864                 goto done;
865         }
866
867         ads_internal = ad_idmap_cached_connection();
868
869         if (!ads_internal || !ad_schema) {
870                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
871                 goto done;
872         }
873
874         attrs[0] = ad_schema->posix_uid_attr;
875
876         filter = talloc_asprintf(mem_ctx,
877                                  "(sAMAccountName=%s)",
878                                  name);
879         if (!filter) {
880                 nt_status = NT_STATUS_NO_MEMORY;
881                 goto done;
882         }
883
884         ads_status = ads_search_retry(ads_internal, &msg, filter, attrs);
885         if (!ADS_ERR_OK(ads_status)) {
886                 nt_status = ads_ntstatus(ads_status);
887                 goto done;
888         }
889
890         *alias = ads_pull_string(ads_internal, mem_ctx, msg, ad_schema->posix_uid_attr );
891
892         if (!*alias) {
893                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
894         }
895
896         nt_status = NT_STATUS_OK;
897
898 done:
899         if (filter) {
900                 talloc_destroy(filter);
901         }
902         if (msg) {
903                 ads_msgfree(ads_internal, msg);
904         }
905
906         return nt_status;
907 }
908
909 /**********************************************************************
910  *********************************************************************/
911
912 static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
913                                              struct nss_domain_entry *e,
914                                              const char *alias,
915                                              char **name )
916 {
917         ADS_STRUCT *ads_internal = NULL;
918         const char *attrs[] = {"sAMAccountName",
919                                NULL };
920         char *filter = NULL;
921         LDAPMessage *msg = NULL;
922         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
923         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
924         char *username;
925
926         /* Check incoming parameters */
927
928         if ( !alias || !name) {
929                 nt_status = NT_STATUS_INVALID_PARAMETER;
930                 goto done;
931         }
932
933         /* Only do query if we are online */
934
935         if (idmap_is_offline()) {
936                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
937                 goto done;
938         }
939
940         ads_internal = ad_idmap_cached_connection();
941
942         if (!ads_internal || !ad_schema) {
943                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
944                 goto done;
945         }
946
947         filter = talloc_asprintf(mem_ctx,
948                                  "(%s=%s)",
949                                  ad_schema->posix_uid_attr,
950                                  alias);
951         if (!filter) {
952                 nt_status = NT_STATUS_NO_MEMORY;
953                 goto done;
954         }
955
956         ads_status = ads_search_retry(ads_internal, &msg, filter, attrs);
957         if (!ADS_ERR_OK(ads_status)) {
958                 nt_status = ads_ntstatus(ads_status);
959                 goto done;
960         }
961
962         username = ads_pull_string(ads_internal, mem_ctx, msg,
963                                    "sAMAccountName");
964         if (!username) {
965                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
966         }
967
968         *name = talloc_asprintf(mem_ctx, "%s\\%s",
969                                 lp_workgroup(),
970                                 username);
971         if (!*name) {
972                 nt_status = NT_STATUS_NO_MEMORY;
973                 goto done;
974         }
975
976         nt_status = NT_STATUS_OK;
977
978 done:
979         if (filter) {
980                 talloc_destroy(filter);
981         }
982         if (msg) {
983                 ads_msgfree(ads_internal, msg);
984         }
985
986         return nt_status;
987 }
988
989
990 /************************************************************************
991  ***********************************************************************/
992
993 static NTSTATUS nss_ad_close( void )
994 {
995         /* nothing to do.  All memory is free()'d by the idmap close_fn() */
996
997         return NT_STATUS_OK;
998 }
999
1000 /************************************************************************
1001  Function dispatch tables for the idmap and nss plugins
1002  ***********************************************************************/
1003
1004 static struct idmap_methods ad_methods = {
1005         .init            = idmap_ad_initialize,
1006         .unixids_to_sids = idmap_ad_unixids_to_sids,
1007         .sids_to_unixids = idmap_ad_sids_to_unixids,
1008         .close_fn        = idmap_ad_close
1009 };
1010
1011 /* The SFU and RFC2307 NSS plugins share everything but the init
1012    function which sets the intended schema model to use */
1013   
1014 static struct nss_info_methods nss_rfc2307_methods = {
1015         .init           = nss_rfc2307_init,
1016         .get_nss_info   = nss_ad_get_info,
1017         .map_to_alias   = nss_ad_map_to_alias,
1018         .map_from_alias = nss_ad_map_from_alias,
1019         .close_fn       = nss_ad_close
1020 };
1021
1022 static struct nss_info_methods nss_sfu_methods = {
1023         .init           = nss_sfu_init,
1024         .get_nss_info   = nss_ad_get_info,
1025         .map_to_alias   = nss_ad_map_to_alias,
1026         .map_from_alias = nss_ad_map_from_alias,
1027         .close_fn       = nss_ad_close
1028 };
1029
1030 static struct nss_info_methods nss_sfu20_methods = {
1031         .init           = nss_sfu20_init,
1032         .get_nss_info   = nss_ad_get_info,
1033         .map_to_alias   = nss_ad_map_to_alias,
1034         .map_from_alias = nss_ad_map_from_alias,
1035         .close_fn       = nss_ad_close
1036 };
1037
1038
1039
1040 /************************************************************************
1041  Initialize the plugins
1042  ***********************************************************************/
1043
1044 NTSTATUS idmap_ad_init(void)
1045 {
1046         static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
1047         static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
1048         static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
1049         static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL;
1050
1051         /* Always register the AD method first in order to get the
1052            idmap_domain interface called */
1053
1054         if ( !NT_STATUS_IS_OK(status_idmap_ad) ) {
1055                 status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, 
1056                                                      "ad", &ad_methods);
1057                 if ( !NT_STATUS_IS_OK(status_idmap_ad) )
1058                         return status_idmap_ad;         
1059         }
1060         
1061         if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
1062                 status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1063                                                             "rfc2307",  &nss_rfc2307_methods );         
1064                 if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
1065                         return status_nss_rfc2307;
1066         }
1067
1068         if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
1069                 status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1070                                                         "sfu",  &nss_sfu_methods );             
1071                 if ( !NT_STATUS_IS_OK(status_nss_sfu) )
1072                         return status_nss_sfu;          
1073         }
1074
1075         if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) {
1076                 status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1077                                                         "sfu20",  &nss_sfu20_methods );         
1078                 if ( !NT_STATUS_IS_OK(status_nss_sfu20) )
1079                         return status_nss_sfu20;                
1080         }
1081
1082         return NT_STATUS_OK;    
1083 }
1084