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