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