5472edc2203c7a9ad36fd9110a48a636922abc75
[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 ((id == 0) ||
658                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
659                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
660                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
661                                 id, ctx->filter_low_id, ctx->filter_high_id));
662                         continue;
663                 }
664
665                 /* mapped */
666                 map->xid.type = type;
667                 map->xid.id = id;
668                 map->status = ID_MAPPED;
669
670                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
671                            (unsigned long)map->xid.id,
672                            map->xid.type));
673         }
674
675         if (res) {
676                 ads_msgfree(ctx->ads, res);
677         }
678
679         if (ids[idx]) { /* still some values to map */
680                 goto again;
681         }
682
683         ret = NT_STATUS_OK;
684
685         /* mark all unknwoni/expired ones as unmapped */
686         for (i = 0; ids[i]; i++) {
687                 if (ids[i]->status != ID_MAPPED) 
688                         ids[i]->status = ID_UNMAPPED;
689         }
690
691 done:
692         talloc_free(memctx);
693         return ret;
694 }
695
696 /************************************************************************
697  ***********************************************************************/
698
699 static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
700 {
701         struct idmap_ad_context * ctx;
702
703         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
704
705         if (ctx->ads != NULL) {
706                 /* we own this ADS_STRUCT so make sure it goes away */
707                 ctx->ads->is_mine = True;
708                 ads_destroy( &ctx->ads );
709                 ctx->ads = NULL;
710         }
711
712         TALLOC_FREE( ctx->ad_schema );
713         
714         return NT_STATUS_OK;
715 }
716
717 /*
718  * nss_info_{sfu,sfu20,rfc2307}
719  */
720
721 /************************************************************************
722  Initialize the {sfu,sfu20,rfc2307} state
723  ***********************************************************************/
724
725 static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
726 static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
727 static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
728 static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
729 static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
730 static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
731
732 static const char *ad_map_type_string(enum wb_posix_mapping map_type)
733 {
734         switch (map_type) {
735                 case WB_POSIX_MAP_TEMPLATE:
736                         return wb_posix_map_template_string;
737                 case WB_POSIX_MAP_SFU:
738                         return wb_posix_map_sfu_string;
739                 case WB_POSIX_MAP_SFU20:
740                         return wb_posix_map_sfu20_string;
741                 case WB_POSIX_MAP_RFC2307:
742                         return wb_posix_map_rfc2307_string;
743                 case WB_POSIX_MAP_UNIXINFO:
744                         return wb_posix_map_unixinfo_string;
745                 default:
746                         return wb_posix_map_unknown_string;
747         }
748 }
749
750 static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
751                                     enum wb_posix_mapping new_ad_map_type)
752 {
753         struct idmap_domain *dom;
754         struct idmap_ad_context *ctx;
755
756         if (e->state != NULL) {
757                 dom = talloc_get_type(e->state, struct idmap_domain);
758         } else {
759                 dom = TALLOC_ZERO_P(e, struct idmap_domain);
760                 if (dom == NULL) {
761                         DEBUG(0, ("Out of memory!\n"));
762                         return NT_STATUS_NO_MEMORY;
763                 }
764                 e->state = dom;
765         }
766
767         if (e->domain != NULL) {
768                 dom->name = talloc_strdup(dom, e->domain);
769                 if (dom->name == NULL) {
770                         DEBUG(0, ("Out of memory!\n"));
771                         return NT_STATUS_NO_MEMORY;
772                 }
773         }
774
775         if (dom->private_data != NULL) {
776                 ctx = talloc_get_type(dom->private_data,
777                                       struct idmap_ad_context);
778         } else {
779                 ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context);
780                 if (ctx == NULL) {
781                         DEBUG(0, ("Out of memory!\n"));
782                         return NT_STATUS_NO_MEMORY;
783                 }
784                 ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
785                 dom->private_data = ctx;
786         }
787
788         if ((ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
789             (ctx->ad_map_type != new_ad_map_type))
790         {
791                 DEBUG(2, ("nss_ad_generic_init: "
792                           "Warning: overriding previously set posix map type "
793                           "%s for domain %s with map type %s.\n",
794                           ad_map_type_string(ctx->ad_map_type),
795                           dom->name,
796                           ad_map_type_string(new_ad_map_type)));
797         }
798
799         ctx->ad_map_type = new_ad_map_type;
800
801         return NT_STATUS_OK;
802 }
803
804 static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
805 {
806         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
807 }
808
809 static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
810 {
811         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
812 }
813
814 static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
815 {
816         return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
817 }
818
819
820 /************************************************************************
821  ***********************************************************************/
822
823 static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
824                                   const struct dom_sid *sid,
825                                   TALLOC_CTX *mem_ctx,
826                                   ADS_STRUCT *ads, 
827                                   LDAPMessage *msg,
828                                   const char **homedir,
829                                   const char **shell,
830                                   const char **gecos,
831                                   uint32 *gid )
832 {
833         const char *attrs[] = {NULL, /* attr_homedir */
834                                NULL, /* attr_shell */
835                                NULL, /* attr_gecos */
836                                NULL, /* attr_gidnumber */
837                                NULL };
838         char *filter = NULL;
839         LDAPMessage *msg_internal = NULL;
840         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
841         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
842         char *sidstr = NULL;
843         struct idmap_domain *dom;
844         struct idmap_ad_context *ctx;
845
846         DEBUG(10, ("nss_ad_get_info called for sid [%s] in domain '%s'\n",
847                    sid_string_dbg(sid), e->domain?e->domain:"NULL"));
848
849         /* Only do query if we are online */
850         if (idmap_is_offline()) {
851                 return NT_STATUS_FILE_IS_OFFLINE;
852         }
853
854         dom = talloc_get_type(e->state, struct idmap_domain);
855         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
856
857         ads_status = ad_idmap_cached_connection(dom);
858         if (!ADS_ERR_OK(ads_status)) {
859                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
860         }
861
862         if (!ctx->ad_schema) {
863                 DEBUG(10, ("nss_ad_get_info: no ad_schema configured!\n"));
864                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
865         }
866
867         if (!sid || !homedir || !shell || !gecos) {
868                 return NT_STATUS_INVALID_PARAMETER;
869         }
870
871         /* See if we can use the ADS connection struct swe were given */
872
873         if (ads) {
874                 DEBUG(10, ("nss_ad_get_info: using given ads connection and "
875                            "LDAP message (%p)\n", msg));
876
877                 *homedir = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_homedir_attr );
878                 *shell   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_shell_attr );
879                 *gecos   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_gecos_attr );
880
881                 if (gid) {
882                         if ( !ads_pull_uint32(ads, msg, ctx->ad_schema->posix_gidnumber_attr, gid ) )
883                                 *gid = (uint32)-1;
884                 }
885
886                 nt_status = NT_STATUS_OK;
887                 goto done;
888         }
889
890         /* Have to do our own query */
891
892         DEBUG(10, ("nss_ad_get_info: no ads connection given, doing our "
893                    "own query\n"));
894
895         attrs[0] = ctx->ad_schema->posix_homedir_attr;
896         attrs[1] = ctx->ad_schema->posix_shell_attr;
897         attrs[2] = ctx->ad_schema->posix_gecos_attr;
898         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
899
900         sidstr = sid_binstring(mem_ctx, sid);
901         filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
902         TALLOC_FREE(sidstr);
903
904         if (!filter) {
905                 nt_status = NT_STATUS_NO_MEMORY;
906                 goto done;
907         }
908
909         ads_status = ads_search_retry(ctx->ads, &msg_internal, filter, attrs);
910         if (!ADS_ERR_OK(ads_status)) {
911                 nt_status = ads_ntstatus(ads_status);
912                 goto done;
913         }
914
915         *homedir = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_homedir_attr);
916         *shell   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_shell_attr);
917         *gecos   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_gecos_attr);
918
919         if (gid) {
920                 if (!ads_pull_uint32(ctx->ads, msg_internal, ctx->ad_schema->posix_gidnumber_attr, gid))
921                         *gid = (uint32)-1;
922         }
923
924         nt_status = NT_STATUS_OK;
925
926 done:
927         if (msg_internal) {
928                 ads_msgfree(ctx->ads, msg_internal);
929         }
930
931         return nt_status;
932 }
933
934 /**********************************************************************
935  *********************************************************************/
936
937 static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
938                                     struct nss_domain_entry *e,
939                                     const char *name,
940                                     char **alias)
941 {
942         const char *attrs[] = {NULL, /* attr_uid */
943                                NULL };
944         char *filter = NULL;
945         LDAPMessage *msg = NULL;
946         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
947         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
948         struct idmap_domain *dom;
949         struct idmap_ad_context *ctx = NULL;
950
951         /* Check incoming parameters */
952
953         if ( !e || !e->domain || !name || !*alias) {
954                 nt_status = NT_STATUS_INVALID_PARAMETER;
955                 goto done;
956         }
957
958         /* Only do query if we are online */
959
960         if (idmap_is_offline()) {
961                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
962                 goto done;
963         }
964
965         dom = talloc_get_type(e->state, struct idmap_domain);
966         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
967
968         ads_status = ad_idmap_cached_connection(dom);
969         if (!ADS_ERR_OK(ads_status)) {
970                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
971         }
972
973         if (!ctx->ad_schema) {
974                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
975                 goto done;
976         }
977
978         attrs[0] = ctx->ad_schema->posix_uid_attr;
979
980         filter = talloc_asprintf(mem_ctx,
981                                  "(sAMAccountName=%s)",
982                                  name);
983         if (!filter) {
984                 nt_status = NT_STATUS_NO_MEMORY;
985                 goto done;
986         }
987
988         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
989         if (!ADS_ERR_OK(ads_status)) {
990                 nt_status = ads_ntstatus(ads_status);
991                 goto done;
992         }
993
994         *alias = ads_pull_string(ctx->ads, mem_ctx, msg, ctx->ad_schema->posix_uid_attr);
995
996         if (!*alias) {
997                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
998         }
999
1000         nt_status = NT_STATUS_OK;
1001
1002 done:
1003         if (filter) {
1004                 talloc_destroy(filter);
1005         }
1006         if (msg) {
1007                 ads_msgfree(ctx->ads, msg);
1008         }
1009
1010         return nt_status;
1011 }
1012
1013 /**********************************************************************
1014  *********************************************************************/
1015
1016 static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
1017                                              struct nss_domain_entry *e,
1018                                              const char *alias,
1019                                              char **name )
1020 {
1021         const char *attrs[] = {"sAMAccountName",
1022                                NULL };
1023         char *filter = NULL;
1024         LDAPMessage *msg = NULL;
1025         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1026         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1027         char *username;
1028         struct idmap_domain *dom;
1029         struct idmap_ad_context *ctx = NULL;
1030
1031         /* Check incoming parameters */
1032
1033         if ( !alias || !name) {
1034                 nt_status = NT_STATUS_INVALID_PARAMETER;
1035                 goto done;
1036         }
1037
1038         /* Only do query if we are online */
1039
1040         if (idmap_is_offline()) {
1041                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
1042                 goto done;
1043         }
1044
1045         dom = talloc_get_type(e->state, struct idmap_domain);
1046         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
1047
1048         ads_status = ad_idmap_cached_connection(dom);
1049         if (!ADS_ERR_OK(ads_status)) {
1050                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1051         }
1052
1053         if (!ctx->ad_schema) {
1054                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1055                 goto done;
1056         }
1057
1058         filter = talloc_asprintf(mem_ctx,
1059                                  "(%s=%s)",
1060                                  ctx->ad_schema->posix_uid_attr,
1061                                  alias);
1062         if (!filter) {
1063                 nt_status = NT_STATUS_NO_MEMORY;
1064                 goto done;
1065         }
1066
1067         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
1068         if (!ADS_ERR_OK(ads_status)) {
1069                 nt_status = ads_ntstatus(ads_status);
1070                 goto done;
1071         }
1072
1073         username = ads_pull_string(ctx->ads, mem_ctx, msg,
1074                                    "sAMAccountName");
1075         if (!username) {
1076                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1077         }
1078
1079         *name = talloc_asprintf(mem_ctx, "%s\\%s",
1080                                 lp_workgroup(),
1081                                 username);
1082         if (!*name) {
1083                 nt_status = NT_STATUS_NO_MEMORY;
1084                 goto done;
1085         }
1086
1087         nt_status = NT_STATUS_OK;
1088
1089 done:
1090         if (filter) {
1091                 talloc_destroy(filter);
1092         }
1093         if (msg) {
1094                 ads_msgfree(ctx->ads, msg);
1095         }
1096
1097         return nt_status;
1098 }
1099
1100
1101 /************************************************************************
1102  ***********************************************************************/
1103
1104 static NTSTATUS nss_ad_close( void )
1105 {
1106         /* nothing to do.  All memory is free()'d by the idmap close_fn() */
1107
1108         return NT_STATUS_OK;
1109 }
1110
1111 /************************************************************************
1112  Function dispatch tables for the idmap and nss plugins
1113  ***********************************************************************/
1114
1115 static struct idmap_methods ad_methods = {
1116         .init            = idmap_ad_initialize,
1117         .unixids_to_sids = idmap_ad_unixids_to_sids,
1118         .sids_to_unixids = idmap_ad_sids_to_unixids,
1119         .close_fn        = idmap_ad_close
1120 };
1121
1122 /* The SFU and RFC2307 NSS plugins share everything but the init
1123    function which sets the intended schema model to use */
1124   
1125 static struct nss_info_methods nss_rfc2307_methods = {
1126         .init           = nss_rfc2307_init,
1127         .get_nss_info   = nss_ad_get_info,
1128         .map_to_alias   = nss_ad_map_to_alias,
1129         .map_from_alias = nss_ad_map_from_alias,
1130         .close_fn       = nss_ad_close
1131 };
1132
1133 static struct nss_info_methods nss_sfu_methods = {
1134         .init           = nss_sfu_init,
1135         .get_nss_info   = nss_ad_get_info,
1136         .map_to_alias   = nss_ad_map_to_alias,
1137         .map_from_alias = nss_ad_map_from_alias,
1138         .close_fn       = nss_ad_close
1139 };
1140
1141 static struct nss_info_methods nss_sfu20_methods = {
1142         .init           = nss_sfu20_init,
1143         .get_nss_info   = nss_ad_get_info,
1144         .map_to_alias   = nss_ad_map_to_alias,
1145         .map_from_alias = nss_ad_map_from_alias,
1146         .close_fn       = nss_ad_close
1147 };
1148
1149
1150
1151 /************************************************************************
1152  Initialize the plugins
1153  ***********************************************************************/
1154
1155 NTSTATUS idmap_ad_init(void)
1156 {
1157         static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
1158         static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
1159         static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
1160         static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL;
1161
1162         /* Always register the AD method first in order to get the
1163            idmap_domain interface called */
1164
1165         if ( !NT_STATUS_IS_OK(status_idmap_ad) ) {
1166                 status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, 
1167                                                      "ad", &ad_methods);
1168                 if ( !NT_STATUS_IS_OK(status_idmap_ad) )
1169                         return status_idmap_ad;         
1170         }
1171         
1172         if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
1173                 status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1174                                                             "rfc2307",  &nss_rfc2307_methods );         
1175                 if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
1176                         return status_nss_rfc2307;
1177         }
1178
1179         if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
1180                 status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1181                                                         "sfu",  &nss_sfu_methods );             
1182                 if ( !NT_STATUS_IS_OK(status_nss_sfu) )
1183                         return status_nss_sfu;          
1184         }
1185
1186         if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) {
1187                 status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1188                                                         "sfu20",  &nss_sfu20_methods );         
1189                 if ( !NT_STATUS_IS_OK(status_nss_sfu20) )
1190                         return status_nss_sfu20;                
1191         }
1192
1193         return NT_STATUS_OK;    
1194 }
1195