def41c6c116e3d4d76ccfa3a0c31bb4ae95f3d49
[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 #include "../libds/common/flags.h"
31 #include "ads.h"
32 #include "libads/ldap_schema.h"
33 #include "nss_info.h"
34
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_IDMAP
37
38 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
39
40 #define IDMAP_AD_MAX_IDS 30
41 #define CHECK_ALLOC_DONE(mem) do { \
42      if (!mem) { \
43            DEBUG(0, ("Out of memory!\n")); \
44            ret = NT_STATUS_NO_MEMORY; \
45            goto done; \
46       } \
47 } while (0)
48
49 struct idmap_ad_context {
50         uint32_t filter_low_id;
51         uint32_t filter_high_id;
52         ADS_STRUCT *ads;
53         struct posix_schema *ad_schema;
54         enum wb_posix_mapping ad_map_type; /* WB_POSIX_MAP_UNKNOWN */
55 };
56
57 NTSTATUS init_module(void);
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                         TALLOC_FREE( ctx->ad_schema );
102                 }
103         }
104
105         if (!local) {
106                 /* we don't want this to affect the users ccache */
107                 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
108         }
109
110         /*
111          * At this point we only have the NetBIOS domain name.
112          * Check if we can get server nam and realm from SAF cache
113          * and the domain list.
114          */
115         ldap_server = saf_fetch(dom->name);
116         DEBUG(10, ("ldap_server from saf cache: '%s'\n", ldap_server?ldap_server:""));
117
118         wb_dom = find_domain_from_name_noinit(dom->name);
119         if (wb_dom == NULL) {
120                 DEBUG(10, ("find_domain_from_name_noinit did not find domain '%s'\n",
121                            dom->name));
122                 realm = NULL;
123         } else {
124                 DEBUG(10, ("find_domain_from_name_noinit found realm '%s' for "
125                           " domain '%s'\n", wb_dom->alt_name, dom->name));
126                 realm = wb_dom->alt_name;
127         }
128
129         if ( (ads = ads_init(realm, dom->name, ldap_server)) == NULL ) {
130                 DEBUG(1,("ads_init failed\n"));
131                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
132         }
133
134         /* the machine acct password might have change - fetch it every time */
135         SAFE_FREE(ads->auth.password);
136         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
137
138         SAFE_FREE(ads->auth.realm);
139         ads->auth.realm = SMB_STRDUP(lp_realm());
140
141         /* setup server affinity */
142
143         get_dc_name(dom->name, realm, dc_name, &dc_ip );
144         
145         status = ads_connect(ads);
146         if (!ADS_ERR_OK(status)) {
147                 DEBUG(1, ("ad_idmap_init: failed to 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(NULL, ctx->ads, ctx->ad_map_type, &ctx->ad_schema);
188                 if ( !ADS_ERR_OK(status) ) {
189                         DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
190                 }
191         }
192         
193         return status;
194 }
195
196 /************************************************************************
197  ***********************************************************************/
198
199 static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom,
200                                     const char *params)
201 {
202         struct idmap_ad_context *ctx;
203         char *config_option;
204         const char *range = NULL;
205         const char *schema_mode = NULL; 
206
207         if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context)) == NULL ) {
208                 DEBUG(0, ("Out of memory!\n"));
209                 return NT_STATUS_NO_MEMORY;
210         }
211
212         if ( (config_option = talloc_asprintf(ctx, "idmap config %s", dom->name)) == NULL ) {
213                 DEBUG(0, ("Out of memory!\n"));
214                 talloc_free(ctx);
215                 return NT_STATUS_NO_MEMORY;
216         }
217
218         /* load ranges */
219         range = lp_parm_const_string(-1, config_option, "range", NULL);
220         if (range && range[0]) {
221                 if ((sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) ||
222                     (ctx->filter_low_id > ctx->filter_high_id)) {
223                         DEBUG(1, ("ERROR: invalid filter range [%s]", range));
224                         ctx->filter_low_id = 0;
225                         ctx->filter_high_id = 0;
226                 }
227         }
228
229         /* default map type */
230         ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
231
232         /* schema mode */
233         schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL);
234         if ( schema_mode && schema_mode[0] ) {
235                 if ( strequal(schema_mode, "sfu") )
236                         ctx->ad_map_type = WB_POSIX_MAP_SFU;
237                 else if ( strequal(schema_mode, "sfu20" ) )
238                         ctx->ad_map_type = WB_POSIX_MAP_SFU20;
239                 else if ( strequal(schema_mode, "rfc2307" ) )
240                         ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
241                 else
242                         DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n",
243                                  schema_mode));
244         }
245
246         dom->private_data = ctx;
247
248         talloc_free(config_option);
249
250         return NT_STATUS_OK;
251 }
252
253 /************************************************************************
254  Search up to IDMAP_AD_MAX_IDS entries in maps for a match.
255  ***********************************************************************/
256
257 static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id)
258 {
259         int i;
260
261         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
262                 if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
263                         return maps[i];
264                 }
265         }
266
267         return NULL;    
268 }
269
270 /************************************************************************
271  Search up to IDMAP_AD_MAX_IDS entries in maps for a match
272  ***********************************************************************/
273
274 static struct id_map *find_map_by_sid(struct id_map **maps, struct dom_sid *sid)
275 {
276         int i;
277
278         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
279                 if (sid_equal(maps[i]->sid, sid)) {
280                         return maps[i];
281                 }
282         }
283
284         return NULL;    
285 }
286
287 /************************************************************************
288  ***********************************************************************/
289
290 static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
291 {
292         NTSTATUS ret;
293         TALLOC_CTX *memctx;
294         struct idmap_ad_context *ctx;
295         ADS_STATUS rc;
296         const char *attrs[] = { "sAMAccountType", 
297                                 "objectSid",
298                                 NULL, /* uidnumber */
299                                 NULL, /* gidnumber */
300                                 NULL };
301         LDAPMessage *res = NULL;
302         LDAPMessage *entry = NULL;
303         char *filter = NULL;
304         int idx = 0;
305         int bidx = 0;
306         int count;
307         int i;
308         char *u_filter = NULL;
309         char *g_filter = NULL;
310
311         /* initialize the status to avoid suprise */
312         for (i = 0; ids[i]; i++) {
313                 ids[i]->status = ID_UNKNOWN;
314         }
315         
316         /* Only do query if we are online */
317         if (idmap_is_offline()) {
318                 return NT_STATUS_FILE_IS_OFFLINE;
319         }
320
321         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
322
323         if ( (memctx = talloc_new(ctx)) == NULL ) {
324                 DEBUG(0, ("Out of memory!\n"));
325                 return NT_STATUS_NO_MEMORY;
326         }
327
328         rc = ad_idmap_cached_connection(dom);
329         if (!ADS_ERR_OK(rc)) {
330                 DEBUG(1, ("ADS uninitialized: %s\n", ads_errstr(rc)));
331                 ret = NT_STATUS_UNSUCCESSFUL;
332                 /* ret = ads_ntstatus(rc); */
333                 goto done;
334         }
335
336         attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
337         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
338
339 again:
340         bidx = idx;
341         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
342                 switch (ids[idx]->xid.type) {
343                 case ID_TYPE_UID:     
344                         if ( ! u_filter) {
345                                 u_filter = talloc_asprintf(memctx, "(&(|"
346                                                            "(sAMAccountType=%d)"
347                                                            "(sAMAccountType=%d)"
348                                                            "(sAMAccountType=%d))(|",
349                                                            ATYPE_NORMAL_ACCOUNT,
350                                                            ATYPE_WORKSTATION_TRUST,
351                                                            ATYPE_INTERDOMAIN_TRUST);
352                         }
353                         u_filter = talloc_asprintf_append_buffer(u_filter, "(%s=%lu)",
354                                                           ctx->ad_schema->posix_uidnumber_attr,
355                                                           (unsigned long)ids[idx]->xid.id);
356                         CHECK_ALLOC_DONE(u_filter);
357                         break;
358                                 
359                 case ID_TYPE_GID:
360                         if ( ! g_filter) {
361                                 g_filter = talloc_asprintf(memctx, "(&(|"
362                                                            "(sAMAccountType=%d)"
363                                                            "(sAMAccountType=%d))(|",
364                                                            ATYPE_SECURITY_GLOBAL_GROUP,
365                                                            ATYPE_SECURITY_LOCAL_GROUP);
366                         }
367                         g_filter = talloc_asprintf_append_buffer(g_filter, "(%s=%lu)",
368                                                           ctx->ad_schema->posix_gidnumber_attr,
369                                                           (unsigned long)ids[idx]->xid.id);
370                         CHECK_ALLOC_DONE(g_filter);
371                         break;
372
373                 default:
374                         DEBUG(3, ("Error: mapping requested but Unknown ID type\n"));
375                         ids[idx]->status = ID_UNKNOWN;
376                         continue;
377                 }
378         }
379         filter = talloc_asprintf(memctx, "(|");
380         CHECK_ALLOC_DONE(filter);
381         if ( u_filter) {
382                 filter = talloc_asprintf_append_buffer(filter, "%s))", u_filter);
383                 CHECK_ALLOC_DONE(filter);
384                         TALLOC_FREE(u_filter);
385         }
386         if ( g_filter) {
387                 filter = talloc_asprintf_append_buffer(filter, "%s))", g_filter);
388                 CHECK_ALLOC_DONE(filter);
389                 TALLOC_FREE(g_filter);
390         }
391         filter = talloc_asprintf_append_buffer(filter, ")");
392         CHECK_ALLOC_DONE(filter);
393
394         rc = ads_search_retry(ctx->ads, &res, filter, attrs);
395         if (!ADS_ERR_OK(rc)) {
396                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
397                 ret = NT_STATUS_UNSUCCESSFUL;
398                 goto done;
399         }
400
401         if ( (count = ads_count_replies(ctx->ads, res)) == 0 ) {
402                 DEBUG(10, ("No IDs found\n"));
403         }
404
405         entry = res;
406         for (i = 0; (i < count) && entry; i++) {
407                 struct dom_sid sid;
408                 enum id_type type;
409                 struct id_map *map;
410                 uint32_t id;
411                 uint32_t atype;
412
413                 if (i == 0) { /* first entry */
414                         entry = ads_first_entry(ctx->ads, entry);
415                 } else { /* following ones */
416                         entry = ads_next_entry(ctx->ads, entry);
417                 }
418
419                 if ( !entry ) {
420                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
421                         break;
422                 }
423
424                 /* first check if the SID is present */
425                 if (!ads_pull_sid(ctx->ads, entry, "objectSid", &sid)) {
426                         DEBUG(2, ("Could not retrieve SID from entry\n"));
427                         continue;
428                 }
429
430                 /* get type */
431                 if (!ads_pull_uint32(ctx->ads, entry, "sAMAccountType", &atype)) {
432                         DEBUG(1, ("could not get SAM account type\n"));
433                         continue;
434                 }
435
436                 switch (atype & 0xF0000000) {
437                 case ATYPE_SECURITY_GLOBAL_GROUP:
438                 case ATYPE_SECURITY_LOCAL_GROUP:
439                         type = ID_TYPE_GID;
440                         break;
441                 case ATYPE_NORMAL_ACCOUNT:
442                 case ATYPE_WORKSTATION_TRUST:
443                 case ATYPE_INTERDOMAIN_TRUST:
444                         type = ID_TYPE_UID;
445                         break;
446                 default:
447                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
448                         continue;
449                 }
450
451                 if (!ads_pull_uint32(ctx->ads, entry, (type==ID_TYPE_UID) ?
452                                                  ctx->ad_schema->posix_uidnumber_attr :
453                                                  ctx->ad_schema->posix_gidnumber_attr,
454                                      &id)) 
455                 {
456                         DEBUG(1, ("Could not get unix ID\n"));
457                         continue;
458                 }
459
460                 if ((id == 0) ||
461                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
462                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
463                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
464                                 id, ctx->filter_low_id, ctx->filter_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 = sid_binstring(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 ((id == 0) ||
659                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
660                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
661                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
662                                 id, ctx->filter_low_id, ctx->filter_high_id));
663                         continue;
664                 }
665
666                 /* mapped */
667                 map->xid.type = type;
668                 map->xid.id = id;
669                 map->status = ID_MAPPED;
670
671                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
672                            (unsigned long)map->xid.id,
673                            map->xid.type));
674         }
675
676         if (res) {
677                 ads_msgfree(ctx->ads, res);
678         }
679
680         if (ids[idx]) { /* still some values to map */
681                 goto again;
682         }
683
684         ret = NT_STATUS_OK;
685
686         /* mark all unknwoni/expired ones as unmapped */
687         for (i = 0; ids[i]; i++) {
688                 if (ids[i]->status != ID_MAPPED) 
689                         ids[i]->status = ID_UNMAPPED;
690         }
691
692 done:
693         talloc_free(memctx);
694         return ret;
695 }
696
697 /************************************************************************
698  ***********************************************************************/
699
700 static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
701 {
702         struct idmap_ad_context * ctx;
703
704         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
705
706         if (ctx->ads != NULL) {
707                 /* we own this ADS_STRUCT so make sure it goes away */
708                 ctx->ads->is_mine = True;
709                 ads_destroy( &ctx->ads );
710                 ctx->ads = NULL;
711         }
712
713         TALLOC_FREE( ctx->ad_schema );
714         
715         return NT_STATUS_OK;
716 }
717
718 /*
719  * nss_info_{sfu,sfu20,rfc2307}
720  */
721
722 /************************************************************************
723  Initialize the {sfu,sfu20,rfc2307} state
724  ***********************************************************************/
725
726 static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
727 static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
728 static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
729 static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
730 static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
731 static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
732
733 static const char *ad_map_type_string(enum wb_posix_mapping map_type)
734 {
735         switch (map_type) {
736                 case WB_POSIX_MAP_TEMPLATE:
737                         return wb_posix_map_template_string;
738                 case WB_POSIX_MAP_SFU:
739                         return wb_posix_map_sfu_string;
740                 case WB_POSIX_MAP_SFU20:
741                         return wb_posix_map_sfu20_string;
742                 case WB_POSIX_MAP_RFC2307:
743                         return wb_posix_map_rfc2307_string;
744                 case WB_POSIX_MAP_UNIXINFO:
745                         return wb_posix_map_unixinfo_string;
746                 default:
747                         return wb_posix_map_unknown_string;
748         }
749 }
750
751 static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
752                                     enum wb_posix_mapping new_ad_map_type)
753 {
754         struct idmap_domain *dom;
755         struct idmap_ad_context *ctx;
756
757         if (e->state != NULL) {
758                 dom = talloc_get_type(e->state, struct idmap_domain);
759         } else {
760                 dom = TALLOC_ZERO_P(e, struct idmap_domain);
761                 if (dom == NULL) {
762                         DEBUG(0, ("Out of memory!\n"));
763                         return NT_STATUS_NO_MEMORY;
764                 }
765                 e->state = dom;
766         }
767
768         if (e->domain != NULL) {
769                 dom->name = talloc_strdup(dom, e->domain);
770                 if (dom->name == NULL) {
771                         DEBUG(0, ("Out of memory!\n"));
772                         return NT_STATUS_NO_MEMORY;
773                 }
774         }
775
776         if (dom->private_data != NULL) {
777                 ctx = talloc_get_type(dom->private_data,
778                                       struct idmap_ad_context);
779         } else {
780                 ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context);
781                 if (ctx == NULL) {
782                         DEBUG(0, ("Out of memory!\n"));
783                         return NT_STATUS_NO_MEMORY;
784                 }
785                 ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
786                 dom->private_data = ctx;
787         }
788
789         if ((ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
790             (ctx->ad_map_type != new_ad_map_type))
791         {
792                 DEBUG(2, ("nss_ad_generic_init: "
793                           "Warning: overriding previously set posix map type "
794                           "%s for domain %s with map type %s.\n",
795                           ad_map_type_string(ctx->ad_map_type),
796                           dom->name,
797                           ad_map_type_string(new_ad_map_type)));
798         }
799
800         ctx->ad_map_type = new_ad_map_type;
801
802         return NT_STATUS_OK;
803 }
804
805 static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
806 {
807         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
808 }
809
810 static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
811 {
812         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
813 }
814
815 static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
816 {
817         return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
818 }
819
820
821 /************************************************************************
822  ***********************************************************************/
823
824 static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
825                                   const struct dom_sid *sid,
826                                   TALLOC_CTX *mem_ctx,
827                                   ADS_STRUCT *ads, 
828                                   LDAPMessage *msg,
829                                   const char **homedir,
830                                   const char **shell,
831                                   const char **gecos,
832                                   uint32 *gid )
833 {
834         const char *attrs[] = {NULL, /* attr_homedir */
835                                NULL, /* attr_shell */
836                                NULL, /* attr_gecos */
837                                NULL, /* attr_gidnumber */
838                                NULL };
839         char *filter = NULL;
840         LDAPMessage *msg_internal = NULL;
841         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
842         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
843         char *sidstr = NULL;
844         struct idmap_domain *dom;
845         struct idmap_ad_context *ctx;
846
847         DEBUG(10, ("nss_ad_get_info called for sid [%s] in domain '%s'\n",
848                    sid_string_dbg(sid), e->domain?e->domain:"NULL"));
849
850         /* Only do query if we are online */
851         if (idmap_is_offline()) {
852                 return NT_STATUS_FILE_IS_OFFLINE;
853         }
854
855         dom = talloc_get_type(e->state, struct idmap_domain);
856         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
857
858         ads_status = ad_idmap_cached_connection(dom);
859         if (!ADS_ERR_OK(ads_status)) {
860                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
861         }
862
863         if (!ctx->ad_schema) {
864                 DEBUG(10, ("nss_ad_get_info: no ad_schema configured!\n"));
865                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
866         }
867
868         if (!sid || !homedir || !shell || !gecos) {
869                 return NT_STATUS_INVALID_PARAMETER;
870         }
871
872         /* See if we can use the ADS connection struct swe were given */
873
874         if (ads) {
875                 DEBUG(10, ("nss_ad_get_info: using given ads connection and "
876                            "LDAP message (%p)\n", msg));
877
878                 *homedir = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_homedir_attr );
879                 *shell   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_shell_attr );
880                 *gecos   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_gecos_attr );
881
882                 if (gid) {
883                         if ( !ads_pull_uint32(ads, msg, ctx->ad_schema->posix_gidnumber_attr, gid ) )
884                                 *gid = (uint32)-1;
885                 }
886
887                 nt_status = NT_STATUS_OK;
888                 goto done;
889         }
890
891         /* Have to do our own query */
892
893         DEBUG(10, ("nss_ad_get_info: no ads connection given, doing our "
894                    "own query\n"));
895
896         attrs[0] = ctx->ad_schema->posix_homedir_attr;
897         attrs[1] = ctx->ad_schema->posix_shell_attr;
898         attrs[2] = ctx->ad_schema->posix_gecos_attr;
899         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
900
901         sidstr = sid_binstring(mem_ctx, sid);
902         filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
903         TALLOC_FREE(sidstr);
904
905         if (!filter) {
906                 nt_status = NT_STATUS_NO_MEMORY;
907                 goto done;
908         }
909
910         ads_status = ads_search_retry(ctx->ads, &msg_internal, filter, attrs);
911         if (!ADS_ERR_OK(ads_status)) {
912                 nt_status = ads_ntstatus(ads_status);
913                 goto done;
914         }
915
916         *homedir = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_homedir_attr);
917         *shell   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_shell_attr);
918         *gecos   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_gecos_attr);
919
920         if (gid) {
921                 if (!ads_pull_uint32(ctx->ads, msg_internal, ctx->ad_schema->posix_gidnumber_attr, gid))
922                         *gid = (uint32)-1;
923         }
924
925         nt_status = NT_STATUS_OK;
926
927 done:
928         if (msg_internal) {
929                 ads_msgfree(ctx->ads, msg_internal);
930         }
931
932         return nt_status;
933 }
934
935 /**********************************************************************
936  *********************************************************************/
937
938 static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
939                                     struct nss_domain_entry *e,
940                                     const char *name,
941                                     char **alias)
942 {
943         const char *attrs[] = {NULL, /* attr_uid */
944                                NULL };
945         char *filter = NULL;
946         LDAPMessage *msg = NULL;
947         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
948         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
949         struct idmap_domain *dom;
950         struct idmap_ad_context *ctx = NULL;
951
952         /* Check incoming parameters */
953
954         if ( !e || !e->domain || !name || !*alias) {
955                 nt_status = NT_STATUS_INVALID_PARAMETER;
956                 goto done;
957         }
958
959         /* Only do query if we are online */
960
961         if (idmap_is_offline()) {
962                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
963                 goto done;
964         }
965
966         dom = talloc_get_type(e->state, struct idmap_domain);
967         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
968
969         ads_status = ad_idmap_cached_connection(dom);
970         if (!ADS_ERR_OK(ads_status)) {
971                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
972         }
973
974         if (!ctx->ad_schema) {
975                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
976                 goto done;
977         }
978
979         attrs[0] = ctx->ad_schema->posix_uid_attr;
980
981         filter = talloc_asprintf(mem_ctx,
982                                  "(sAMAccountName=%s)",
983                                  name);
984         if (!filter) {
985                 nt_status = NT_STATUS_NO_MEMORY;
986                 goto done;
987         }
988
989         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
990         if (!ADS_ERR_OK(ads_status)) {
991                 nt_status = ads_ntstatus(ads_status);
992                 goto done;
993         }
994
995         *alias = ads_pull_string(ctx->ads, mem_ctx, msg, ctx->ad_schema->posix_uid_attr);
996
997         if (!*alias) {
998                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
999         }
1000
1001         nt_status = NT_STATUS_OK;
1002
1003 done:
1004         if (filter) {
1005                 talloc_destroy(filter);
1006         }
1007         if (msg) {
1008                 ads_msgfree(ctx->ads, msg);
1009         }
1010
1011         return nt_status;
1012 }
1013
1014 /**********************************************************************
1015  *********************************************************************/
1016
1017 static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
1018                                              struct nss_domain_entry *e,
1019                                              const char *alias,
1020                                              char **name )
1021 {
1022         const char *attrs[] = {"sAMAccountName",
1023                                NULL };
1024         char *filter = NULL;
1025         LDAPMessage *msg = NULL;
1026         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1027         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1028         char *username;
1029         struct idmap_domain *dom;
1030         struct idmap_ad_context *ctx = NULL;
1031
1032         /* Check incoming parameters */
1033
1034         if ( !alias || !name) {
1035                 nt_status = NT_STATUS_INVALID_PARAMETER;
1036                 goto done;
1037         }
1038
1039         /* Only do query if we are online */
1040
1041         if (idmap_is_offline()) {
1042                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
1043                 goto done;
1044         }
1045
1046         dom = talloc_get_type(e->state, struct idmap_domain);
1047         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
1048
1049         ads_status = ad_idmap_cached_connection(dom);
1050         if (!ADS_ERR_OK(ads_status)) {
1051                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1052         }
1053
1054         if (!ctx->ad_schema) {
1055                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1056                 goto done;
1057         }
1058
1059         filter = talloc_asprintf(mem_ctx,
1060                                  "(%s=%s)",
1061                                  ctx->ad_schema->posix_uid_attr,
1062                                  alias);
1063         if (!filter) {
1064                 nt_status = NT_STATUS_NO_MEMORY;
1065                 goto done;
1066         }
1067
1068         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
1069         if (!ADS_ERR_OK(ads_status)) {
1070                 nt_status = ads_ntstatus(ads_status);
1071                 goto done;
1072         }
1073
1074         username = ads_pull_string(ctx->ads, mem_ctx, msg,
1075                                    "sAMAccountName");
1076         if (!username) {
1077                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1078         }
1079
1080         *name = talloc_asprintf(mem_ctx, "%s\\%s",
1081                                 lp_workgroup(),
1082                                 username);
1083         if (!*name) {
1084                 nt_status = NT_STATUS_NO_MEMORY;
1085                 goto done;
1086         }
1087
1088         nt_status = NT_STATUS_OK;
1089
1090 done:
1091         if (filter) {
1092                 talloc_destroy(filter);
1093         }
1094         if (msg) {
1095                 ads_msgfree(ctx->ads, msg);
1096         }
1097
1098         return nt_status;
1099 }
1100
1101
1102 /************************************************************************
1103  ***********************************************************************/
1104
1105 static NTSTATUS nss_ad_close( void )
1106 {
1107         /* nothing to do.  All memory is free()'d by the idmap close_fn() */
1108
1109         return NT_STATUS_OK;
1110 }
1111
1112 /************************************************************************
1113  Function dispatch tables for the idmap and nss plugins
1114  ***********************************************************************/
1115
1116 static struct idmap_methods ad_methods = {
1117         .init            = idmap_ad_initialize,
1118         .unixids_to_sids = idmap_ad_unixids_to_sids,
1119         .sids_to_unixids = idmap_ad_sids_to_unixids,
1120         .close_fn        = idmap_ad_close
1121 };
1122
1123 /* The SFU and RFC2307 NSS plugins share everything but the init
1124    function which sets the intended schema model to use */
1125   
1126 static struct nss_info_methods nss_rfc2307_methods = {
1127         .init           = nss_rfc2307_init,
1128         .get_nss_info   = nss_ad_get_info,
1129         .map_to_alias   = nss_ad_map_to_alias,
1130         .map_from_alias = nss_ad_map_from_alias,
1131         .close_fn       = nss_ad_close
1132 };
1133
1134 static struct nss_info_methods nss_sfu_methods = {
1135         .init           = nss_sfu_init,
1136         .get_nss_info   = nss_ad_get_info,
1137         .map_to_alias   = nss_ad_map_to_alias,
1138         .map_from_alias = nss_ad_map_from_alias,
1139         .close_fn       = nss_ad_close
1140 };
1141
1142 static struct nss_info_methods nss_sfu20_methods = {
1143         .init           = nss_sfu20_init,
1144         .get_nss_info   = nss_ad_get_info,
1145         .map_to_alias   = nss_ad_map_to_alias,
1146         .map_from_alias = nss_ad_map_from_alias,
1147         .close_fn       = nss_ad_close
1148 };
1149
1150
1151
1152 /************************************************************************
1153  Initialize the plugins
1154  ***********************************************************************/
1155
1156 NTSTATUS idmap_ad_init(void)
1157 {
1158         static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
1159         static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
1160         static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
1161         static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL;
1162
1163         /* Always register the AD method first in order to get the
1164            idmap_domain interface called */
1165
1166         if ( !NT_STATUS_IS_OK(status_idmap_ad) ) {
1167                 status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, 
1168                                                      "ad", &ad_methods);
1169                 if ( !NT_STATUS_IS_OK(status_idmap_ad) )
1170                         return status_idmap_ad;         
1171         }
1172         
1173         if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
1174                 status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1175                                                             "rfc2307",  &nss_rfc2307_methods );         
1176                 if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
1177                         return status_nss_rfc2307;
1178         }
1179
1180         if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
1181                 status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1182                                                         "sfu",  &nss_sfu_methods );             
1183                 if ( !NT_STATUS_IS_OK(status_nss_sfu) )
1184                         return status_nss_sfu;          
1185         }
1186
1187         if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) {
1188                 status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1189                                                         "sfu20",  &nss_sfu20_methods );         
1190                 if ( !NT_STATUS_IS_OK(status_nss_sfu20) )
1191                         return status_nss_sfu20;                
1192         }
1193
1194         return NT_STATUS_OK;    
1195 }
1196