UNFINISHED - s3:idmap_ad: multi-domain
[amitay/samba.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         ADS_STRUCT *ad_idmap_ads;
47         struct posix_schema *ad_schema;
48         enum wb_posix_mapping ad_map_type = WB_POSIX_MAP_UNKNOWN;
49 };
50
51 NTSTATUS init_module(void);
52
53 /************************************************************************
54  ***********************************************************************/
55
56 static ADS_STRUCT *ad_idmap_cached_connection_internal(struct idmap_domain *dom)
57 {
58         ADS_STRUCT *ads;
59         ADS_STATUS status;
60         bool local = False;
61         fstring dc_name;
62         struct sockaddr_storage dc_ip;
63         struct idmap_ad_context *ctx;
64
65         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
66
67         if (ctx->ad_idmap_ads != NULL) {
68
69                 time_t expire;
70                 time_t now = time(NULL);
71
72                 ads = ctx->ad_idmap_ads;
73
74                 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
75
76                 /* check for a valid structure */
77                 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
78                           (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
79
80                 if ( ads->config.realm && (expire > time(NULL))) {
81                         return ads;
82                 } else {
83                         /* we own this ADS_STRUCT so make sure it goes away */
84                         DEBUG(7,("Deleting expired krb5 credential cache\n"));
85                         ads->is_mine = True;
86                         ads_destroy( &ads );
87                         ads_kdestroy(WINBIND_CCACHE_NAME);
88                         ctx->ad_idmap_ads = NULL;
89                         TALLOC_FREE(ctx->ad_schema);
90                 }
91         }
92
93         if (!local) {
94                 /* we don't want this to affect the users ccache */
95                 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
96         }
97
98         if ( (ads = ads_init(lp_realm(), lp_workgroup(), NULL)) == NULL ) {
99                 DEBUG(1,("ads_init failed\n"));
100                 return NULL;
101         }
102
103         /* the machine acct password might have change - fetch it every time */
104         SAFE_FREE(ads->auth.password);
105         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
106
107         SAFE_FREE(ads->auth.realm);
108         ads->auth.realm = SMB_STRDUP(lp_realm());
109
110         /* setup server affinity */
111
112         get_dc_name( NULL, ads->auth.realm, dc_name, &dc_ip );
113         
114         status = ads_connect(ads);
115         if (!ADS_ERR_OK(status)) {
116                 DEBUG(1, ("ad_idmap_init: failed to connect to AD\n"));
117                 ads_destroy(&ads);
118                 return NULL;
119         }
120
121         ads->is_mine = False;
122
123         ctx->ad_idmap_ads = ads;
124
125         return ads;
126 }
127
128 /************************************************************************
129  ***********************************************************************/
130
131 static ADS_STRUCT *ad_idmap_cached_connection(struct idmap_domain *dom)
132 {
133         ADS_STRUCT *ads = ad_idmap_cached_connection_internal(dom);
134         struct idmap_ad_context *ctx;
135
136         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
137
138         if ( !ads )
139                 return NULL;
140
141         /* if we have a valid ADS_STRUCT and the schema model is
142            defined, then we can return here. */
143
144         if ( ctx->ad_schema )
145                 return ads;
146
147         /* Otherwise, set the schema model */
148
149         if ( (ctx->ad_map_type ==  WB_POSIX_MAP_SFU) ||
150              (ctx->ad_map_type ==  WB_POSIX_MAP_SFU20) ||
151              (ctx->ad_map_type ==  WB_POSIX_MAP_RFC2307) )
152         {
153                 ADS_STATUS schema_status;
154                 
155                 schema_status = ads_check_posix_schema_mapping( NULL, ads, ctx->ad_map_type, &ctx->ad_schema);
156                 if ( !ADS_ERR_OK(schema_status) ) {
157                         DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
158                         return NULL;                    
159                 }
160         }
161         
162         return ads;
163 }
164
165 /************************************************************************
166  ***********************************************************************/
167
168 static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom,
169                                     const char *params)
170 {
171         struct idmap_ad_context *ctx;
172         char *config_option;
173         const char *range = NULL;
174         const char *schema_mode = NULL; 
175
176         if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context)) == NULL ) {
177                 DEBUG(0, ("Out of memory!\n"));
178                 return NT_STATUS_NO_MEMORY;
179         }
180
181         if ( (config_option = talloc_asprintf(ctx, "idmap config %s", dom->name)) == NULL ) {
182                 DEBUG(0, ("Out of memory!\n"));
183                 talloc_free(ctx);
184                 return NT_STATUS_NO_MEMORY;
185         }
186
187         /* load ranges */
188         range = lp_parm_const_string(-1, config_option, "range", NULL);
189         if (range && range[0]) {
190                 if ((sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) ||
191                     (ctx->filter_low_id > ctx->filter_high_id)) {
192                         DEBUG(1, ("ERROR: invalid filter range [%s]", range));
193                         ctx->filter_low_id = 0;
194                         ctx->filter_high_id = 0;
195                 }
196         }
197
198         /* schema mode */
199         if ( ctx->ad_map_type == WB_POSIX_MAP_UNKNOWN )
200                 ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
201         schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL);
202         if ( schema_mode && schema_mode[0] ) {
203                 if ( strequal(schema_mode, "sfu") )
204                         ctx->ad_map_type = WB_POSIX_MAP_SFU;
205                 else if ( strequal(schema_mode, "sfu20" ) )
206                         ctx->ad_map_type = WB_POSIX_MAP_SFU20;
207                 else if ( strequal(schema_mode, "rfc2307" ) )
208                         ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
209                 else
210                         DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n",
211                                  schema_mode));
212         }
213
214         dom->private_data = ctx;
215
216         talloc_free(config_option);
217
218         return NT_STATUS_OK;
219 }
220
221 /************************************************************************
222  Search up to IDMAP_AD_MAX_IDS entries in maps for a match.
223  ***********************************************************************/
224
225 static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id)
226 {
227         int i;
228
229         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
230                 if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
231                         return maps[i];
232                 }
233         }
234
235         return NULL;    
236 }
237
238 /************************************************************************
239  Search up to IDMAP_AD_MAX_IDS entries in maps for a match
240  ***********************************************************************/
241
242 static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid)
243 {
244         int i;
245
246         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
247                 if (sid_equal(maps[i]->sid, sid)) {
248                         return maps[i];
249                 }
250         }
251
252         return NULL;    
253 }
254
255 /************************************************************************
256  ***********************************************************************/
257
258 static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
259 {
260         NTSTATUS ret;
261         TALLOC_CTX *memctx;
262         struct idmap_ad_context *ctx;
263         ADS_STATUS rc;
264         ADS_STRUCT *ads;
265         const char *attrs[] = { "sAMAccountType", 
266                                 "objectSid",
267                                 NULL, /* uidnumber */
268                                 NULL, /* gidnumber */
269                                 NULL };
270         LDAPMessage *res = NULL;
271         LDAPMessage *entry = NULL;
272         char *filter = NULL;
273         int idx = 0;
274         int bidx = 0;
275         int count;
276         int i;
277         char *u_filter = NULL;
278         char *g_filter = NULL;
279
280         /* Only do query if we are online */
281         if (idmap_is_offline()) {
282                 return NT_STATUS_FILE_IS_OFFLINE;
283         }
284
285         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
286
287         if ( (memctx = talloc_new(ctx)) == NULL ) {
288                 DEBUG(0, ("Out of memory!\n"));
289                 return NT_STATUS_NO_MEMORY;
290         }
291
292         if ( (ads = ad_idmap_cached_connection(dom)) == NULL ) {
293                 DEBUG(1, ("ADS uninitialized\n"));
294                 ret = NT_STATUS_UNSUCCESSFUL;
295                 goto done;
296         }
297
298         attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
299         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
300
301 again:
302         bidx = idx;
303         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
304                 switch (ids[idx]->xid.type) {
305                 case ID_TYPE_UID:     
306                         if ( ! u_filter) {
307                                 u_filter = talloc_asprintf(memctx, "(&(|"
308                                                            "(sAMAccountType=%d)"
309                                                            "(sAMAccountType=%d)"
310                                                            "(sAMAccountType=%d))(|",
311                                                            ATYPE_NORMAL_ACCOUNT,
312                                                            ATYPE_WORKSTATION_TRUST,
313                                                            ATYPE_INTERDOMAIN_TRUST);
314                         }
315                         u_filter = talloc_asprintf_append_buffer(u_filter, "(%s=%lu)",
316                                                           ctx->ad_schema->posix_uidnumber_attr,
317                                                           (unsigned long)ids[idx]->xid.id);
318                         CHECK_ALLOC_DONE(u_filter);
319                         break;
320                                 
321                 case ID_TYPE_GID:
322                         if ( ! g_filter) {
323                                 g_filter = talloc_asprintf(memctx, "(&(|"
324                                                            "(sAMAccountType=%d)"
325                                                            "(sAMAccountType=%d))(|",
326                                                            ATYPE_SECURITY_GLOBAL_GROUP,
327                                                            ATYPE_SECURITY_LOCAL_GROUP);
328                         }
329                         g_filter = talloc_asprintf_append_buffer(g_filter, "(%s=%lu)",
330                                                           ctx->ad_schema->posix_gidnumber_attr,
331                                                           (unsigned long)ids[idx]->xid.id);
332                         CHECK_ALLOC_DONE(g_filter);
333                         break;
334
335                 default:
336                         DEBUG(3, ("Error: mapping requested but Unknown ID type\n"));
337                         ids[idx]->status = ID_UNKNOWN;
338                         continue;
339                 }
340         }
341         filter = talloc_asprintf(memctx, "(|");
342         CHECK_ALLOC_DONE(filter);
343         if ( u_filter) {
344                 filter = talloc_asprintf_append_buffer(filter, "%s))", u_filter);
345                 CHECK_ALLOC_DONE(filter);
346                         TALLOC_FREE(u_filter);
347         }
348         if ( g_filter) {
349                 filter = talloc_asprintf_append_buffer(filter, "%s))", g_filter);
350                 CHECK_ALLOC_DONE(filter);
351                 TALLOC_FREE(g_filter);
352         }
353         filter = talloc_asprintf_append_buffer(filter, ")");
354         CHECK_ALLOC_DONE(filter);
355
356         rc = ads_search_retry(ads, &res, filter, attrs);
357         if (!ADS_ERR_OK(rc)) {
358                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
359                 ret = NT_STATUS_UNSUCCESSFUL;
360                 goto done;
361         }
362
363         if ( (count = ads_count_replies(ads, res)) == 0 ) {
364                 DEBUG(10, ("No IDs found\n"));
365         }
366
367         entry = res;
368         for (i = 0; (i < count) && entry; i++) {
369                 DOM_SID sid;
370                 enum id_type type;
371                 struct id_map *map;
372                 uint32_t id;
373                 uint32_t atype;
374
375                 if (i == 0) { /* first entry */
376                         entry = ads_first_entry(ads, entry);
377                 } else { /* following ones */
378                         entry = ads_next_entry(ads, entry);
379                 }
380
381                 if ( !entry ) {
382                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
383                         break;
384                 }
385
386                 /* first check if the SID is present */
387                 if (!ads_pull_sid(ads, entry, "objectSid", &sid)) {
388                         DEBUG(2, ("Could not retrieve SID from entry\n"));
389                         continue;
390                 }
391
392                 /* get type */
393                 if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) {
394                         DEBUG(1, ("could not get SAM account type\n"));
395                         continue;
396                 }
397
398                 switch (atype & 0xF0000000) {
399                 case ATYPE_SECURITY_GLOBAL_GROUP:
400                 case ATYPE_SECURITY_LOCAL_GROUP:
401                         type = ID_TYPE_GID;
402                         break;
403                 case ATYPE_NORMAL_ACCOUNT:
404                 case ATYPE_WORKSTATION_TRUST:
405                 case ATYPE_INTERDOMAIN_TRUST:
406                         type = ID_TYPE_UID;
407                         break;
408                 default:
409                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
410                         continue;
411                 }
412
413                 if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ?
414                                                  ctx->ad_schema->posix_uidnumber_attr : 
415                                                  ctx->ad_schema->posix_gidnumber_attr, 
416                                      &id))
417                 {
418                         DEBUG(1, ("Could not get unix ID\n"));
419                         continue;
420                 }
421
422                 if ((id == 0) ||
423                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
424                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
425                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
426                                 id, ctx->filter_low_id, ctx->filter_high_id));
427                         continue;
428                 }
429
430                 map = find_map_by_id(&ids[bidx], type, id);
431                 if (!map) {
432                         DEBUG(2, ("WARNING: couldn't match result with requested ID\n"));
433                         continue;
434                 }
435
436                 sid_copy(map->sid, &sid);
437
438                 /* mapped */
439                 map->status = ID_MAPPED;
440
441                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
442                            (unsigned long)map->xid.id,
443                            map->xid.type));
444         }
445
446         if (res) {
447                 ads_msgfree(ads, res);
448         }
449
450         if (ids[idx]) { /* still some values to map */
451                 goto again;
452         }
453
454         ret = NT_STATUS_OK;
455
456         /* mark all unknown/expired ones as unmapped */
457         for (i = 0; ids[i]; i++) {
458                 if (ids[i]->status != ID_MAPPED) 
459                         ids[i]->status = ID_UNMAPPED;
460         }
461
462 done:
463         talloc_free(memctx);
464         return ret;
465 }
466
467 /************************************************************************
468  ***********************************************************************/
469
470 static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
471 {
472         NTSTATUS ret;
473         TALLOC_CTX *memctx;
474         struct idmap_ad_context *ctx;
475         ADS_STATUS rc;
476         ADS_STRUCT *ads;
477         const char *attrs[] = { "sAMAccountType", 
478                                 "objectSid",
479                                 NULL, /* attr_uidnumber */
480                                 NULL, /* attr_gidnumber */
481                                 NULL };
482         LDAPMessage *res = NULL;
483         LDAPMessage *entry = NULL;
484         char *filter = NULL;
485         int idx = 0;
486         int bidx = 0;
487         int count;
488         int i;
489         char *sidstr;
490
491         /* Only do query if we are online */
492         if (idmap_is_offline()) {
493                 return NT_STATUS_FILE_IS_OFFLINE;
494         }
495
496         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);      
497
498         if ( (memctx = talloc_new(ctx)) == NULL ) {             
499                 DEBUG(0, ("Out of memory!\n"));
500                 return NT_STATUS_NO_MEMORY;
501         }
502
503         if ( (ads = ad_idmap_cached_connection(dom)) == NULL ) {
504                 DEBUG(1, ("ADS uninitialized\n"));
505                 ret = NT_STATUS_UNSUCCESSFUL;
506                 goto done;
507         }
508
509         attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
510         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
511
512 again:
513         filter = talloc_asprintf(memctx, "(&(|"
514                                  "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
515                                  "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
516                                  ")(|",
517                                  ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
518                                  ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
519                 
520         CHECK_ALLOC_DONE(filter);
521
522         bidx = idx;
523         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
524
525                 ids[idx]->status = ID_UNKNOWN;
526
527                 sidstr = sid_binstring(ids[idx]->sid);
528                 filter = talloc_asprintf_append_buffer(filter, "(objectSid=%s)", sidstr);
529                         
530                 free(sidstr);
531                 CHECK_ALLOC_DONE(filter);
532         }
533         filter = talloc_asprintf_append_buffer(filter, "))");
534         CHECK_ALLOC_DONE(filter);
535         DEBUG(10, ("Filter: [%s]\n", filter));
536
537         rc = ads_search_retry(ads, &res, filter, attrs);
538         if (!ADS_ERR_OK(rc)) {
539                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
540                 ret = NT_STATUS_UNSUCCESSFUL;
541                 goto done;
542         }
543
544         if ( (count = ads_count_replies(ads, res)) == 0 ) {
545                 DEBUG(10, ("No IDs found\n"));
546         }
547
548         entry = res;    
549         for (i = 0; (i < count) && entry; i++) {
550                 DOM_SID sid;
551                 enum id_type type;
552                 struct id_map *map;
553                 uint32_t id;
554                 uint32_t atype;
555
556                 if (i == 0) { /* first entry */
557                         entry = ads_first_entry(ads, entry);
558                 } else { /* following ones */
559                         entry = ads_next_entry(ads, entry);
560                 }
561
562                 if ( !entry ) {
563                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
564                         break;
565                 }
566
567                 /* first check if the SID is present */
568                 if (!ads_pull_sid(ads, entry, "objectSid", &sid)) {
569                         DEBUG(2, ("Could not retrieve SID from entry\n"));
570                         continue;
571                 }
572
573                 map = find_map_by_sid(&ids[bidx], &sid);
574                 if (!map) {
575                         DEBUG(2, ("WARNING: couldn't match result with requested SID\n"));
576                         continue;
577                 }
578
579                 /* get type */
580                 if (!ads_pull_uint32(ads, entry, "sAMAccountType", &atype)) {
581                         DEBUG(1, ("could not get SAM account type\n"));
582                         continue;
583                 }
584
585                 switch (atype & 0xF0000000) {
586                 case ATYPE_SECURITY_GLOBAL_GROUP:
587                 case ATYPE_SECURITY_LOCAL_GROUP:
588                         type = ID_TYPE_GID;
589                         break;
590                 case ATYPE_NORMAL_ACCOUNT:
591                 case ATYPE_WORKSTATION_TRUST:
592                 case ATYPE_INTERDOMAIN_TRUST:
593                         type = ID_TYPE_UID;
594                         break;
595                 default:
596                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
597                         continue;
598                 }
599
600                 if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ?
601                                                  ctx->ad_schema->posix_uidnumber_attr : 
602                                                  ctx->ad_schema->posix_gidnumber_attr, 
603                                      &id))
604                 {
605                         DEBUG(1, ("Could not get unix ID\n"));
606                         continue;
607                 }
608                 if ((id == 0) ||
609                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
610                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
611                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
612                                 id, ctx->filter_low_id, ctx->filter_high_id));
613                         continue;
614                 }
615
616                 /* mapped */
617                 map->xid.type = type;
618                 map->xid.id = id;
619                 map->status = ID_MAPPED;
620
621                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
622                            (unsigned long)map->xid.id,
623                            map->xid.type));
624         }
625
626         if (res) {
627                 ads_msgfree(ads, res);
628         }
629
630         if (ids[idx]) { /* still some values to map */
631                 goto again;
632         }
633
634         ret = NT_STATUS_OK;
635
636         /* mark all unknwoni/expired ones as unmapped */
637         for (i = 0; ids[i]; i++) {
638                 if (ids[i]->status != ID_MAPPED) 
639                         ids[i]->status = ID_UNMAPPED;
640         }
641
642 done:
643         talloc_free(memctx);
644         return ret;
645 }
646
647 /************************************************************************
648  ***********************************************************************/
649
650 static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
651 {
652         ADS_STRUCT *ads = ad_idmap_ads;
653
654         if (ads != NULL) {
655                 /* we own this ADS_STRUCT so make sure it goes away */
656                 ads->is_mine = True;
657                 ads_destroy( &ads );
658                 ad_idmap_ads = NULL;
659         }
660
661         TALLOC_FREE( ctx->ad_schema );
662         
663         return NT_STATUS_OK;
664 }
665
666 /*
667  * nss_info_{sfu,sfu20,rfc2307}
668  */
669
670 /************************************************************************
671  Initialize the {sfu,sfu20,rfc2307} state
672  ***********************************************************************/
673
674 static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
675 {
676         /* Sanity check if we have previously been called with a
677            different schema model */
678
679         if ( (ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
680              (ctx->ad_map_type != WB_POSIX_MAP_SFU) )
681         {
682                 DEBUG(0,("nss_sfu_init: Posix Map type has already been set.  "
683                          "Mixed schema models not supported!\n"));
684                 return NT_STATUS_NOT_SUPPORTED;
685         }
686         
687         ctx->ad_map_type = WB_POSIX_MAP_SFU;
688
689         return NT_STATUS_OK;
690 }
691
692 static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
693 {
694         /* Sanity check if we have previously been called with a
695            different schema model */
696
697         if ( (ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
698              (ctx->ad_map_type != WB_POSIX_MAP_SFU20) )
699         {
700                 DEBUG(0,("nss_sfu20_init: Posix Map type has already been set.  "
701                          "Mixed schema models not supported!\n"));
702                 return NT_STATUS_NOT_SUPPORTED;
703         }
704         
705         ctx->ad_map_type = WB_POSIX_MAP_SFU20;
706
707         return NT_STATUS_OK;
708 }
709
710 static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
711 {
712         /* Sanity check if we have previously been called with a
713            different schema model */
714          
715         if ( (ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
716              (ctx->ad_map_type != WB_POSIX_MAP_RFC2307) )
717         {
718                 DEBUG(0,("nss_rfc2307_init: Posix Map type has already been set.  "
719                          "Mixed schema models not supported!\n"));
720                 return NT_STATUS_NOT_SUPPORTED;
721         }
722         
723         ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
724
725         return NT_STATUS_OK;
726 }
727
728
729 /************************************************************************
730  ***********************************************************************/
731 static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
732                                   const DOM_SID *sid, 
733                                   TALLOC_CTX *mem_ctx,
734                                   ADS_STRUCT *ads, 
735                                   LDAPMessage *msg,
736                                   char **homedir,
737                                   char **shell, 
738                                   char **gecos,
739                                   uint32 *gid )
740 {
741         ADS_STRUCT *ads_internal = NULL;
742         const char *attrs[] = {NULL, /* attr_homedir */
743                                NULL, /* attr_shell */
744                                NULL, /* attr_gecos */
745                                NULL, /* attr_gidnumber */
746                                NULL };
747         char *filter = NULL;
748         LDAPMessage *msg_internal = NULL;
749         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
750         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
751         char *sidstr = NULL;
752         struct idmap_ad_context *ctx;
753
754         ctx = talloc_get_type(e->state, struct idmap_ad_context);
755
756         /* Only do query if we are online */
757         if (idmap_is_offline()) {
758                 return NT_STATUS_FILE_IS_OFFLINE;
759         }
760
761         /* We are assuming that the internal ADS_STRUCT is for the 
762            same forest as the incoming *ads pointer */
763
764         ads_internal = ad_idmap_cached_connection();
765
766         if ( !ads_internal || !ctx->ad_schema ) {
767                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
768         }
769
770         if (!sid || !homedir || !shell || !gecos) {
771                 return NT_STATUS_INVALID_PARAMETER;
772         }
773
774         /* See if we can use the ADS connection struct swe were given */
775
776         if (ads) {
777                 *homedir = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_homedir_attr );
778                 *shell   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_shell_attr );
779                 *gecos   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_gecos_attr );
780
781                 if (gid) {
782                         if ( !ads_pull_uint32(ads, msg, ctx->ad_schema->posix_gidnumber_attr, gid ) )
783                                 *gid = (uint32)-1;
784                 }
785
786                 nt_status = NT_STATUS_OK;
787                 goto done;
788         }
789
790         /* Have to do our own query */
791
792         attrs[0] = ctx->ad_schema->posix_homedir_attr;
793         attrs[1] = ctx->ad_schema->posix_shell_attr;
794         attrs[2] = ctx->ad_schema->posix_gecos_attr;
795         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
796
797         sidstr = sid_binstring(sid);
798         filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
799         SAFE_FREE(sidstr);
800
801         if (!filter) {
802                 nt_status = NT_STATUS_NO_MEMORY;
803                 goto done;
804         }
805
806         ads_status = ads_search_retry(ads_internal, &msg_internal, filter, attrs);
807         if (!ADS_ERR_OK(ads_status)) {
808                 nt_status = ads_ntstatus(ads_status);
809                 goto done;
810         }
811
812         *homedir = ads_pull_string(ads_internal, mem_ctx, msg_internal, ctx->ad_schema->posix_homedir_attr);
813         *shell   = ads_pull_string(ads_internal, mem_ctx, msg_internal, ctx->ad_schema->posix_shell_attr);
814         *gecos   = ads_pull_string(ads_internal, mem_ctx, msg_internal, ctx->ad_schema->posix_gecos_attr);
815
816         if (gid) {
817                 if (!ads_pull_uint32(ads_internal, msg_internal, ctx->ad_schema->posix_gidnumber_attr, gid))
818                         *gid = (uint32)-1;
819         }
820
821         nt_status = NT_STATUS_OK;
822
823 done:
824         if (msg_internal) {
825                 ads_msgfree(ads_internal, msg_internal);
826         }
827
828         return nt_status;
829 }
830
831 /**********************************************************************
832  *********************************************************************/
833
834 static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
835                                     const char *domain,
836                                     const char *name,
837                                     char **alias)
838 {
839         ADS_STRUCT *ads_internal = NULL;
840         const char *attrs[] = {NULL, /* attr_uid */
841                                NULL };
842         char *filter = NULL;
843         LDAPMessage *msg = NULL;
844         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
845         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
846         struct idmap_ad_context *ctx;
847
848         ctx = talloc_get_type(e->state, struct idmap_ad_context);
849
850         /* Check incoming parameters */
851
852         if ( !domain || !name || !*alias) {
853                 nt_status = NT_STATUS_INVALID_PARAMETER;
854                 goto done;
855         }
856
857         /* Only do query if we are online */
858
859         if (idmap_is_offline()) {
860                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
861                 goto done;
862         }
863
864         ads_internal = ad_idmap_cached_connection();
865
866         if (!ads_internal || !ctx->ad_schema) {
867                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
868                 goto done;
869         }
870
871         attrs[0] = ctx->ad_schema->posix_uid_attr;
872
873         filter = talloc_asprintf(mem_ctx,
874                                  "(sAMAccountName=%s)",
875                                  name);
876         if (!filter) {
877                 nt_status = NT_STATUS_NO_MEMORY;
878                 goto done;
879         }
880
881         ads_status = ads_search_retry(ads_internal, &msg, filter, attrs);
882         if (!ADS_ERR_OK(ads_status)) {
883                 nt_status = ads_ntstatus(ads_status);
884                 goto done;
885         }
886
887         *alias = ads_pull_string(ads_internal, mem_ctx, msg, ctx->ad_schema->posix_uid_attr );
888
889         if (!*alias) {
890                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
891         }
892
893         nt_status = NT_STATUS_OK;
894
895 done:
896         if (filter) {
897                 talloc_destroy(filter);
898         }
899         if (msg) {
900                 ads_msgfree(ads_internal, msg);
901         }
902
903         return nt_status;
904 }
905
906 /**********************************************************************
907  *********************************************************************/
908
909 static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
910                                              const char *domain,
911                                              const char *alias,
912                                              char **name )
913 {
914         ADS_STRUCT *ads_internal = NULL;
915         const char *attrs[] = {"sAMAccountName",
916                                NULL };
917         char *filter = NULL;
918         LDAPMessage *msg = NULL;
919         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
920         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
921         char *username;
922         struct idmap_ad_context *ctx;
923
924         ctx = talloc_get_type(e->state, struct idmap_ad_context);
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 || !ctx->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                                  ctx->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