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