b22e5af94aece5bcc7975232c6816e81c26003a2
[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  * 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         /* Only do query if we are online */
308         if (idmap_is_offline()) {
309                 return NT_STATUS_FILE_IS_OFFLINE;
310         }
311
312         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
313
314         if ( (memctx = talloc_new(ctx)) == NULL ) {
315                 DEBUG(0, ("Out of memory!\n"));
316                 return NT_STATUS_NO_MEMORY;
317         }
318
319         rc = ad_idmap_cached_connection(dom);
320         if (!ADS_ERR_OK(rc)) {
321                 DEBUG(1, ("ADS uninitialized: %s\n", ads_errstr(rc)));
322                 ret = NT_STATUS_UNSUCCESSFUL;
323                 /* ret = ads_ntstatus(rc); */
324                 goto done;
325         }
326
327         attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
328         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
329
330 again:
331         bidx = idx;
332         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
333                 switch (ids[idx]->xid.type) {
334                 case ID_TYPE_UID:     
335                         if ( ! u_filter) {
336                                 u_filter = talloc_asprintf(memctx, "(&(|"
337                                                            "(sAMAccountType=%d)"
338                                                            "(sAMAccountType=%d)"
339                                                            "(sAMAccountType=%d))(|",
340                                                            ATYPE_NORMAL_ACCOUNT,
341                                                            ATYPE_WORKSTATION_TRUST,
342                                                            ATYPE_INTERDOMAIN_TRUST);
343                         }
344                         u_filter = talloc_asprintf_append_buffer(u_filter, "(%s=%lu)",
345                                                           ctx->ad_schema->posix_uidnumber_attr,
346                                                           (unsigned long)ids[idx]->xid.id);
347                         CHECK_ALLOC_DONE(u_filter);
348                         break;
349                                 
350                 case ID_TYPE_GID:
351                         if ( ! g_filter) {
352                                 g_filter = talloc_asprintf(memctx, "(&(|"
353                                                            "(sAMAccountType=%d)"
354                                                            "(sAMAccountType=%d))(|",
355                                                            ATYPE_SECURITY_GLOBAL_GROUP,
356                                                            ATYPE_SECURITY_LOCAL_GROUP);
357                         }
358                         g_filter = talloc_asprintf_append_buffer(g_filter, "(%s=%lu)",
359                                                           ctx->ad_schema->posix_gidnumber_attr,
360                                                           (unsigned long)ids[idx]->xid.id);
361                         CHECK_ALLOC_DONE(g_filter);
362                         break;
363
364                 default:
365                         DEBUG(3, ("Error: mapping requested but Unknown ID type\n"));
366                         ids[idx]->status = ID_UNKNOWN;
367                         continue;
368                 }
369         }
370         filter = talloc_asprintf(memctx, "(|");
371         CHECK_ALLOC_DONE(filter);
372         if ( u_filter) {
373                 filter = talloc_asprintf_append_buffer(filter, "%s))", u_filter);
374                 CHECK_ALLOC_DONE(filter);
375                         TALLOC_FREE(u_filter);
376         }
377         if ( g_filter) {
378                 filter = talloc_asprintf_append_buffer(filter, "%s))", g_filter);
379                 CHECK_ALLOC_DONE(filter);
380                 TALLOC_FREE(g_filter);
381         }
382         filter = talloc_asprintf_append_buffer(filter, ")");
383         CHECK_ALLOC_DONE(filter);
384
385         rc = ads_search_retry(ctx->ads, &res, filter, attrs);
386         if (!ADS_ERR_OK(rc)) {
387                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
388                 ret = NT_STATUS_UNSUCCESSFUL;
389                 goto done;
390         }
391
392         if ( (count = ads_count_replies(ctx->ads, res)) == 0 ) {
393                 DEBUG(10, ("No IDs found\n"));
394         }
395
396         entry = res;
397         for (i = 0; (i < count) && entry; i++) {
398                 DOM_SID sid;
399                 enum id_type type;
400                 struct id_map *map;
401                 uint32_t id;
402                 uint32_t atype;
403
404                 if (i == 0) { /* first entry */
405                         entry = ads_first_entry(ctx->ads, entry);
406                 } else { /* following ones */
407                         entry = ads_next_entry(ctx->ads, entry);
408                 }
409
410                 if ( !entry ) {
411                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
412                         break;
413                 }
414
415                 /* first check if the SID is present */
416                 if (!ads_pull_sid(ctx->ads, entry, "objectSid", &sid)) {
417                         DEBUG(2, ("Could not retrieve SID from entry\n"));
418                         continue;
419                 }
420
421                 /* get type */
422                 if (!ads_pull_uint32(ctx->ads, entry, "sAMAccountType", &atype)) {
423                         DEBUG(1, ("could not get SAM account type\n"));
424                         continue;
425                 }
426
427                 switch (atype & 0xF0000000) {
428                 case ATYPE_SECURITY_GLOBAL_GROUP:
429                 case ATYPE_SECURITY_LOCAL_GROUP:
430                         type = ID_TYPE_GID;
431                         break;
432                 case ATYPE_NORMAL_ACCOUNT:
433                 case ATYPE_WORKSTATION_TRUST:
434                 case ATYPE_INTERDOMAIN_TRUST:
435                         type = ID_TYPE_UID;
436                         break;
437                 default:
438                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
439                         continue;
440                 }
441
442                 if (!ads_pull_uint32(ctx->ads, entry, (type==ID_TYPE_UID) ?
443                                                  ctx->ad_schema->posix_uidnumber_attr :
444                                                  ctx->ad_schema->posix_gidnumber_attr,
445                                      &id)) 
446                 {
447                         DEBUG(1, ("Could not get unix ID\n"));
448                         continue;
449                 }
450
451                 if ((id == 0) ||
452                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
453                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
454                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
455                                 id, ctx->filter_low_id, ctx->filter_high_id));
456                         continue;
457                 }
458
459                 map = find_map_by_id(&ids[bidx], type, id);
460                 if (!map) {
461                         DEBUG(2, ("WARNING: couldn't match result with requested ID\n"));
462                         continue;
463                 }
464
465                 sid_copy(map->sid, &sid);
466
467                 /* mapped */
468                 map->status = ID_MAPPED;
469
470                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
471                            (unsigned long)map->xid.id,
472                            map->xid.type));
473         }
474
475         if (res) {
476                 ads_msgfree(ctx->ads, res);
477         }
478
479         if (ids[idx]) { /* still some values to map */
480                 goto again;
481         }
482
483         ret = NT_STATUS_OK;
484
485         /* mark all unknown/expired ones as unmapped */
486         for (i = 0; ids[i]; i++) {
487                 if (ids[i]->status != ID_MAPPED) 
488                         ids[i]->status = ID_UNMAPPED;
489         }
490
491 done:
492         talloc_free(memctx);
493         return ret;
494 }
495
496 /************************************************************************
497  ***********************************************************************/
498
499 static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
500 {
501         NTSTATUS ret;
502         TALLOC_CTX *memctx;
503         struct idmap_ad_context *ctx;
504         ADS_STATUS rc;
505         const char *attrs[] = { "sAMAccountType", 
506                                 "objectSid",
507                                 NULL, /* attr_uidnumber */
508                                 NULL, /* attr_gidnumber */
509                                 NULL };
510         LDAPMessage *res = NULL;
511         LDAPMessage *entry = NULL;
512         char *filter = NULL;
513         int idx = 0;
514         int bidx = 0;
515         int count;
516         int i;
517         char *sidstr;
518
519         /* Only do query if we are online */
520         if (idmap_is_offline()) {
521                 return NT_STATUS_FILE_IS_OFFLINE;
522         }
523
524         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);      
525
526         if ( (memctx = talloc_new(ctx)) == NULL ) {             
527                 DEBUG(0, ("Out of memory!\n"));
528                 return NT_STATUS_NO_MEMORY;
529         }
530
531         rc = ad_idmap_cached_connection(dom);
532         if (!ADS_ERR_OK(rc)) {
533                 DEBUG(1, ("ADS uninitialized: %s\n", ads_errstr(rc)));
534                 ret = NT_STATUS_UNSUCCESSFUL;
535                 /* ret = ads_ntstatus(rc); */
536                 goto done;
537         }
538
539         if (ctx->ad_schema == NULL) {
540                 DEBUG(0, ("haven't got ctx->ad_schema ! \n"));
541                 ret = NT_STATUS_UNSUCCESSFUL;
542                 goto done;
543         }
544
545         attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
546         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
547
548 again:
549         filter = talloc_asprintf(memctx, "(&(|"
550                                  "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
551                                  "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
552                                  ")(|",
553                                  ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
554                                  ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
555                 
556         CHECK_ALLOC_DONE(filter);
557
558         bidx = idx;
559         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
560
561                 ids[idx]->status = ID_UNKNOWN;
562
563                 sidstr = sid_binstring(ids[idx]->sid);
564                 filter = talloc_asprintf_append_buffer(filter, "(objectSid=%s)", sidstr);
565                         
566                 free(sidstr);
567                 CHECK_ALLOC_DONE(filter);
568         }
569         filter = talloc_asprintf_append_buffer(filter, "))");
570         CHECK_ALLOC_DONE(filter);
571         DEBUG(10, ("Filter: [%s]\n", filter));
572
573         rc = ads_search_retry(ctx->ads, &res, filter, attrs);
574         if (!ADS_ERR_OK(rc)) {
575                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
576                 ret = NT_STATUS_UNSUCCESSFUL;
577                 goto done;
578         }
579
580         if ( (count = ads_count_replies(ctx->ads, res)) == 0 ) {
581                 DEBUG(10, ("No IDs found\n"));
582         }
583
584         entry = res;    
585         for (i = 0; (i < count) && entry; i++) {
586                 DOM_SID sid;
587                 enum id_type type;
588                 struct id_map *map;
589                 uint32_t id;
590                 uint32_t atype;
591
592                 if (i == 0) { /* first entry */
593                         entry = ads_first_entry(ctx->ads, entry);
594                 } else { /* following ones */
595                         entry = ads_next_entry(ctx->ads, entry);
596                 }
597
598                 if ( !entry ) {
599                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
600                         break;
601                 }
602
603                 /* first check if the SID is present */
604                 if (!ads_pull_sid(ctx->ads, entry, "objectSid", &sid)) {
605                         DEBUG(2, ("Could not retrieve SID from entry\n"));
606                         continue;
607                 }
608
609                 map = find_map_by_sid(&ids[bidx], &sid);
610                 if (!map) {
611                         DEBUG(2, ("WARNING: couldn't match result with requested SID\n"));
612                         continue;
613                 }
614
615                 /* get type */
616                 if (!ads_pull_uint32(ctx->ads, entry, "sAMAccountType", &atype)) {
617                         DEBUG(1, ("could not get SAM account type\n"));
618                         continue;
619                 }
620
621                 switch (atype & 0xF0000000) {
622                 case ATYPE_SECURITY_GLOBAL_GROUP:
623                 case ATYPE_SECURITY_LOCAL_GROUP:
624                         type = ID_TYPE_GID;
625                         break;
626                 case ATYPE_NORMAL_ACCOUNT:
627                 case ATYPE_WORKSTATION_TRUST:
628                 case ATYPE_INTERDOMAIN_TRUST:
629                         type = ID_TYPE_UID;
630                         break;
631                 default:
632                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
633                         continue;
634                 }
635
636                 if (!ads_pull_uint32(ctx->ads, entry, (type==ID_TYPE_UID) ?
637                                                  ctx->ad_schema->posix_uidnumber_attr :
638                                                  ctx->ad_schema->posix_gidnumber_attr,
639                                      &id)) 
640                 {
641                         DEBUG(1, ("Could not get unix ID\n"));
642                         continue;
643                 }
644                 if ((id == 0) ||
645                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
646                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
647                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
648                                 id, ctx->filter_low_id, ctx->filter_high_id));
649                         continue;
650                 }
651
652                 /* mapped */
653                 map->xid.type = type;
654                 map->xid.id = id;
655                 map->status = ID_MAPPED;
656
657                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
658                            (unsigned long)map->xid.id,
659                            map->xid.type));
660         }
661
662         if (res) {
663                 ads_msgfree(ctx->ads, res);
664         }
665
666         if (ids[idx]) { /* still some values to map */
667                 goto again;
668         }
669
670         ret = NT_STATUS_OK;
671
672         /* mark all unknwoni/expired ones as unmapped */
673         for (i = 0; ids[i]; i++) {
674                 if (ids[i]->status != ID_MAPPED) 
675                         ids[i]->status = ID_UNMAPPED;
676         }
677
678 done:
679         talloc_free(memctx);
680         return ret;
681 }
682
683 /************************************************************************
684  ***********************************************************************/
685
686 static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
687 {
688         struct idmap_ad_context * ctx;
689
690         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
691
692         if (ctx->ads != NULL) {
693                 /* we own this ADS_STRUCT so make sure it goes away */
694                 ctx->ads->is_mine = True;
695                 ads_destroy( &ctx->ads );
696                 ctx->ads = NULL;
697         }
698
699         TALLOC_FREE( ctx->ad_schema );
700         
701         return NT_STATUS_OK;
702 }
703
704 /*
705  * nss_info_{sfu,sfu20,rfc2307}
706  */
707
708 /************************************************************************
709  Initialize the {sfu,sfu20,rfc2307} state
710  ***********************************************************************/
711
712 static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
713 static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
714 static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
715 static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
716 static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
717 static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
718
719 static const char *ad_map_type_string(enum wb_posix_mapping map_type)
720 {
721         switch (map_type) {
722                 case WB_POSIX_MAP_TEMPLATE:
723                         return wb_posix_map_template_string;
724                 case WB_POSIX_MAP_SFU:
725                         return wb_posix_map_sfu_string;
726                 case WB_POSIX_MAP_SFU20:
727                         return wb_posix_map_sfu20_string;
728                 case WB_POSIX_MAP_RFC2307:
729                         return wb_posix_map_rfc2307_string;
730                 case WB_POSIX_MAP_UNIXINFO:
731                         return wb_posix_map_unixinfo_string;
732                 default:
733                         return wb_posix_map_unknown_string;
734         }
735 }
736
737 static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
738                                     enum wb_posix_mapping new_ad_map_type)
739 {
740         struct idmap_domain *dom;
741         struct idmap_ad_context *ctx;
742
743         if (e->state != NULL) {
744                 dom = talloc_get_type(e->state, struct idmap_domain);
745         } else {
746                 dom = TALLOC_ZERO_P(e, struct idmap_domain);
747                 if (dom == NULL) {
748                         DEBUG(0, ("Out of memory!\n"));
749                         return NT_STATUS_NO_MEMORY;
750                 }
751                 e->state = dom;
752         }
753
754         if (e->domain != NULL) {
755                 dom->name = talloc_strdup(dom, e->domain);
756                 if (dom->name == NULL) {
757                         DEBUG(0, ("Out of memory!\n"));
758                         return NT_STATUS_NO_MEMORY;
759                 }
760         }
761
762         if (dom->private_data != NULL) {
763                 ctx = talloc_get_type(dom->private_data,
764                                       struct idmap_ad_context);
765         } else {
766                 ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context);
767                 if (ctx == NULL) {
768                         DEBUG(0, ("Out of memory!\n"));
769                         return NT_STATUS_NO_MEMORY;
770                 }
771                 ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
772                 dom->private_data = ctx;
773         }
774
775         if ((ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
776             (ctx->ad_map_type != new_ad_map_type))
777         {
778                 DEBUG(2, ("nss_ad_generic_init: "
779                           "Warning: overriding previously set posix map type "
780                           "%s for domain %s with map type %s.\n",
781                           ad_map_type_string(ctx->ad_map_type),
782                           dom->name,
783                           ad_map_type_string(new_ad_map_type)));
784         }
785
786         ctx->ad_map_type = new_ad_map_type;
787
788         return NT_STATUS_OK;
789 }
790
791 static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
792 {
793         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
794 }
795
796 static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
797 {
798         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
799 }
800
801 static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
802 {
803         return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
804 }
805
806
807 /************************************************************************
808  ***********************************************************************/
809
810 static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
811                                   const DOM_SID *sid, 
812                                   TALLOC_CTX *mem_ctx,
813                                   ADS_STRUCT *ads, 
814                                   LDAPMessage *msg,
815                                   char **homedir,
816                                   char **shell, 
817                                   char **gecos,
818                                   uint32 *gid )
819 {
820         const char *attrs[] = {NULL, /* attr_homedir */
821                                NULL, /* attr_shell */
822                                NULL, /* attr_gecos */
823                                NULL, /* attr_gidnumber */
824                                NULL };
825         char *filter = NULL;
826         LDAPMessage *msg_internal = NULL;
827         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
828         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
829         char *sidstr = NULL;
830         struct idmap_domain *dom;
831         struct idmap_ad_context *ctx;
832
833         DEBUG(10, ("nss_ad_get_info called for sid [%s] in domain '%s'\n",
834                    sid_string_dbg(sid), e->domain?e->domain:"NULL"));
835
836         /* Only do query if we are online */
837         if (idmap_is_offline()) {
838                 return NT_STATUS_FILE_IS_OFFLINE;
839         }
840
841         dom = talloc_get_type(e->state, struct idmap_domain);
842         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
843
844         ads_status = ad_idmap_cached_connection(dom);
845         if (!ADS_ERR_OK(ads_status)) {
846                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
847         }
848
849         if (!ctx->ad_schema) {
850                 DEBUG(10, ("nss_ad_get_info: no ad_schema configured!\n"));
851                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
852         }
853
854         if (!sid || !homedir || !shell || !gecos) {
855                 return NT_STATUS_INVALID_PARAMETER;
856         }
857
858         /* See if we can use the ADS connection struct swe were given */
859
860         if (ads) {
861                 DEBUG(10, ("nss_ad_get_info: using given ads connection and "
862                            "LDAP message (%p)\n", msg));
863
864                 *homedir = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_homedir_attr );
865                 *shell   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_shell_attr );
866                 *gecos   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_gecos_attr );
867
868                 if (gid) {
869                         if ( !ads_pull_uint32(ads, msg, ctx->ad_schema->posix_gidnumber_attr, gid ) )
870                                 *gid = (uint32)-1;
871                 }
872
873                 nt_status = NT_STATUS_OK;
874                 goto done;
875         }
876
877         /* Have to do our own query */
878
879         DEBUG(10, ("nss_ad_get_info: no ads connection given, doing our "
880                    "own query\n"));
881
882         attrs[0] = ctx->ad_schema->posix_homedir_attr;
883         attrs[1] = ctx->ad_schema->posix_shell_attr;
884         attrs[2] = ctx->ad_schema->posix_gecos_attr;
885         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
886
887         sidstr = sid_binstring(sid);
888         filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
889         SAFE_FREE(sidstr);
890
891         if (!filter) {
892                 nt_status = NT_STATUS_NO_MEMORY;
893                 goto done;
894         }
895
896         ads_status = ads_search_retry(ctx->ads, &msg_internal, filter, attrs);
897         if (!ADS_ERR_OK(ads_status)) {
898                 nt_status = ads_ntstatus(ads_status);
899                 goto done;
900         }
901
902         *homedir = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_homedir_attr);
903         *shell   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_shell_attr);
904         *gecos   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_gecos_attr);
905
906         if (gid) {
907                 if (!ads_pull_uint32(ctx->ads, msg_internal, ctx->ad_schema->posix_gidnumber_attr, gid))
908                         *gid = (uint32)-1;
909         }
910
911         nt_status = NT_STATUS_OK;
912
913 done:
914         if (msg_internal) {
915                 ads_msgfree(ctx->ads, msg_internal);
916         }
917
918         return nt_status;
919 }
920
921 /**********************************************************************
922  *********************************************************************/
923
924 static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
925                                     struct nss_domain_entry *e,
926                                     const char *name,
927                                     char **alias)
928 {
929         const char *attrs[] = {NULL, /* attr_uid */
930                                NULL };
931         char *filter = NULL;
932         LDAPMessage *msg = NULL;
933         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
934         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
935         struct idmap_domain *dom;
936         struct idmap_ad_context *ctx = NULL;
937
938         /* Check incoming parameters */
939
940         if ( !e || !e->domain || !name || !*alias) {
941                 nt_status = NT_STATUS_INVALID_PARAMETER;
942                 goto done;
943         }
944
945         /* Only do query if we are online */
946
947         if (idmap_is_offline()) {
948                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
949                 goto done;
950         }
951
952         dom = talloc_get_type(e->state, struct idmap_domain);
953         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
954
955         ads_status = ad_idmap_cached_connection(dom);
956         if (!ADS_ERR_OK(ads_status)) {
957                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
958         }
959
960         if (!ctx->ad_schema) {
961                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
962                 goto done;
963         }
964
965         attrs[0] = ctx->ad_schema->posix_uid_attr;
966
967         filter = talloc_asprintf(mem_ctx,
968                                  "(sAMAccountName=%s)",
969                                  name);
970         if (!filter) {
971                 nt_status = NT_STATUS_NO_MEMORY;
972                 goto done;
973         }
974
975         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
976         if (!ADS_ERR_OK(ads_status)) {
977                 nt_status = ads_ntstatus(ads_status);
978                 goto done;
979         }
980
981         *alias = ads_pull_string(ctx->ads, mem_ctx, msg, ctx->ad_schema->posix_uid_attr);
982
983         if (!*alias) {
984                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
985         }
986
987         nt_status = NT_STATUS_OK;
988
989 done:
990         if (filter) {
991                 talloc_destroy(filter);
992         }
993         if (msg) {
994                 ads_msgfree(ctx->ads, msg);
995         }
996
997         return nt_status;
998 }
999
1000 /**********************************************************************
1001  *********************************************************************/
1002
1003 static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
1004                                              struct nss_domain_entry *e,
1005                                              const char *alias,
1006                                              char **name )
1007 {
1008         const char *attrs[] = {"sAMAccountName",
1009                                NULL };
1010         char *filter = NULL;
1011         LDAPMessage *msg = NULL;
1012         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1013         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1014         char *username;
1015         struct idmap_domain *dom;
1016         struct idmap_ad_context *ctx = NULL;
1017
1018         /* Check incoming parameters */
1019
1020         if ( !alias || !name) {
1021                 nt_status = NT_STATUS_INVALID_PARAMETER;
1022                 goto done;
1023         }
1024
1025         /* Only do query if we are online */
1026
1027         if (idmap_is_offline()) {
1028                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
1029                 goto done;
1030         }
1031
1032         dom = talloc_get_type(e->state, struct idmap_domain);
1033         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
1034
1035         ads_status = ad_idmap_cached_connection(dom);
1036         if (!ADS_ERR_OK(ads_status)) {
1037                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1038         }
1039
1040         if (!ctx->ad_schema) {
1041                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1042                 goto done;
1043         }
1044
1045         filter = talloc_asprintf(mem_ctx,
1046                                  "(%s=%s)",
1047                                  ctx->ad_schema->posix_uid_attr,
1048                                  alias);
1049         if (!filter) {
1050                 nt_status = NT_STATUS_NO_MEMORY;
1051                 goto done;
1052         }
1053
1054         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
1055         if (!ADS_ERR_OK(ads_status)) {
1056                 nt_status = ads_ntstatus(ads_status);
1057                 goto done;
1058         }
1059
1060         username = ads_pull_string(ctx->ads, mem_ctx, msg,
1061                                    "sAMAccountName");
1062         if (!username) {
1063                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1064         }
1065
1066         *name = talloc_asprintf(mem_ctx, "%s\\%s",
1067                                 lp_workgroup(),
1068                                 username);
1069         if (!*name) {
1070                 nt_status = NT_STATUS_NO_MEMORY;
1071                 goto done;
1072         }
1073
1074         nt_status = NT_STATUS_OK;
1075
1076 done:
1077         if (filter) {
1078                 talloc_destroy(filter);
1079         }
1080         if (msg) {
1081                 ads_msgfree(ctx->ads, msg);
1082         }
1083
1084         return nt_status;
1085 }
1086
1087
1088 /************************************************************************
1089  ***********************************************************************/
1090
1091 static NTSTATUS nss_ad_close( void )
1092 {
1093         /* nothing to do.  All memory is free()'d by the idmap close_fn() */
1094
1095         return NT_STATUS_OK;
1096 }
1097
1098 /************************************************************************
1099  Function dispatch tables for the idmap and nss plugins
1100  ***********************************************************************/
1101
1102 static struct idmap_methods ad_methods = {
1103         .init            = idmap_ad_initialize,
1104         .unixids_to_sids = idmap_ad_unixids_to_sids,
1105         .sids_to_unixids = idmap_ad_sids_to_unixids,
1106         .close_fn        = idmap_ad_close
1107 };
1108
1109 /* The SFU and RFC2307 NSS plugins share everything but the init
1110    function which sets the intended schema model to use */
1111   
1112 static struct nss_info_methods nss_rfc2307_methods = {
1113         .init           = nss_rfc2307_init,
1114         .get_nss_info   = nss_ad_get_info,
1115         .map_to_alias   = nss_ad_map_to_alias,
1116         .map_from_alias = nss_ad_map_from_alias,
1117         .close_fn       = nss_ad_close
1118 };
1119
1120 static struct nss_info_methods nss_sfu_methods = {
1121         .init           = nss_sfu_init,
1122         .get_nss_info   = nss_ad_get_info,
1123         .map_to_alias   = nss_ad_map_to_alias,
1124         .map_from_alias = nss_ad_map_from_alias,
1125         .close_fn       = nss_ad_close
1126 };
1127
1128 static struct nss_info_methods nss_sfu20_methods = {
1129         .init           = nss_sfu20_init,
1130         .get_nss_info   = nss_ad_get_info,
1131         .map_to_alias   = nss_ad_map_to_alias,
1132         .map_from_alias = nss_ad_map_from_alias,
1133         .close_fn       = nss_ad_close
1134 };
1135
1136
1137
1138 /************************************************************************
1139  Initialize the plugins
1140  ***********************************************************************/
1141
1142 NTSTATUS idmap_ad_init(void)
1143 {
1144         static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
1145         static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
1146         static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
1147         static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL;
1148
1149         /* Always register the AD method first in order to get the
1150            idmap_domain interface called */
1151
1152         if ( !NT_STATUS_IS_OK(status_idmap_ad) ) {
1153                 status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, 
1154                                                      "ad", &ad_methods);
1155                 if ( !NT_STATUS_IS_OK(status_idmap_ad) )
1156                         return status_idmap_ad;         
1157         }
1158         
1159         if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
1160                 status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1161                                                             "rfc2307",  &nss_rfc2307_methods );         
1162                 if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
1163                         return status_nss_rfc2307;
1164         }
1165
1166         if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
1167                 status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1168                                                         "sfu",  &nss_sfu_methods );             
1169                 if ( !NT_STATUS_IS_OK(status_nss_sfu) )
1170                         return status_nss_sfu;          
1171         }
1172
1173         if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) {
1174                 status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1175                                                         "sfu20",  &nss_sfu20_methods );         
1176                 if ( !NT_STATUS_IS_OK(status_nss_sfu20) )
1177                         return status_nss_sfu20;                
1178         }
1179
1180         return NT_STATUS_OK;    
1181 }
1182