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