s3-secrets: only include secrets.h when needed.
[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 ((id == 0) ||
462                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
463                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
464                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
465                                 id, ctx->filter_low_id, ctx->filter_high_id));
466                         continue;
467                 }
468
469                 map = find_map_by_id(&ids[bidx], type, id);
470                 if (!map) {
471                         DEBUG(2, ("WARNING: couldn't match result with requested ID\n"));
472                         continue;
473                 }
474
475                 sid_copy(map->sid, &sid);
476
477                 /* mapped */
478                 map->status = ID_MAPPED;
479
480                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
481                            (unsigned long)map->xid.id,
482                            map->xid.type));
483         }
484
485         if (res) {
486                 ads_msgfree(ctx->ads, res);
487         }
488
489         if (ids[idx]) { /* still some values to map */
490                 goto again;
491         }
492
493         ret = NT_STATUS_OK;
494
495         /* mark all unknown/expired ones as unmapped */
496         for (i = 0; ids[i]; i++) {
497                 if (ids[i]->status != ID_MAPPED) 
498                         ids[i]->status = ID_UNMAPPED;
499         }
500
501 done:
502         talloc_free(memctx);
503         return ret;
504 }
505
506 /************************************************************************
507  ***********************************************************************/
508
509 static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
510 {
511         NTSTATUS ret;
512         TALLOC_CTX *memctx;
513         struct idmap_ad_context *ctx;
514         ADS_STATUS rc;
515         const char *attrs[] = { "sAMAccountType", 
516                                 "objectSid",
517                                 NULL, /* attr_uidnumber */
518                                 NULL, /* attr_gidnumber */
519                                 NULL };
520         LDAPMessage *res = NULL;
521         LDAPMessage *entry = NULL;
522         char *filter = NULL;
523         int idx = 0;
524         int bidx = 0;
525         int count;
526         int i;
527         char *sidstr;
528
529         /* initialize the status to avoid suprise */
530         for (i = 0; ids[i]; i++) {
531                 ids[i]->status = ID_UNKNOWN;
532         }
533
534         /* Only do query if we are online */
535         if (idmap_is_offline()) {
536                 return NT_STATUS_FILE_IS_OFFLINE;
537         }
538
539         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);      
540
541         if ( (memctx = talloc_new(ctx)) == NULL ) {             
542                 DEBUG(0, ("Out of memory!\n"));
543                 return NT_STATUS_NO_MEMORY;
544         }
545
546         rc = ad_idmap_cached_connection(dom);
547         if (!ADS_ERR_OK(rc)) {
548                 DEBUG(1, ("ADS uninitialized: %s\n", ads_errstr(rc)));
549                 ret = NT_STATUS_UNSUCCESSFUL;
550                 /* ret = ads_ntstatus(rc); */
551                 goto done;
552         }
553
554         if (ctx->ad_schema == NULL) {
555                 DEBUG(0, ("haven't got ctx->ad_schema ! \n"));
556                 ret = NT_STATUS_UNSUCCESSFUL;
557                 goto done;
558         }
559
560         attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
561         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
562
563 again:
564         filter = talloc_asprintf(memctx, "(&(|"
565                                  "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
566                                  "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
567                                  ")(|",
568                                  ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
569                                  ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
570                 
571         CHECK_ALLOC_DONE(filter);
572
573         bidx = idx;
574         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
575
576                 ids[idx]->status = ID_UNKNOWN;
577
578                 sidstr = sid_binstring(talloc_tos(), ids[idx]->sid);
579                 filter = talloc_asprintf_append_buffer(filter, "(objectSid=%s)", sidstr);
580                         
581                 TALLOC_FREE(sidstr);
582                 CHECK_ALLOC_DONE(filter);
583         }
584         filter = talloc_asprintf_append_buffer(filter, "))");
585         CHECK_ALLOC_DONE(filter);
586         DEBUG(10, ("Filter: [%s]\n", filter));
587
588         rc = ads_search_retry(ctx->ads, &res, filter, attrs);
589         if (!ADS_ERR_OK(rc)) {
590                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
591                 ret = NT_STATUS_UNSUCCESSFUL;
592                 goto done;
593         }
594
595         if ( (count = ads_count_replies(ctx->ads, res)) == 0 ) {
596                 DEBUG(10, ("No IDs found\n"));
597         }
598
599         entry = res;    
600         for (i = 0; (i < count) && entry; i++) {
601                 struct dom_sid sid;
602                 enum id_type type;
603                 struct id_map *map;
604                 uint32_t id;
605                 uint32_t atype;
606
607                 if (i == 0) { /* first entry */
608                         entry = ads_first_entry(ctx->ads, entry);
609                 } else { /* following ones */
610                         entry = ads_next_entry(ctx->ads, entry);
611                 }
612
613                 if ( !entry ) {
614                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
615                         break;
616                 }
617
618                 /* first check if the SID is present */
619                 if (!ads_pull_sid(ctx->ads, entry, "objectSid", &sid)) {
620                         DEBUG(2, ("Could not retrieve SID from entry\n"));
621                         continue;
622                 }
623
624                 map = find_map_by_sid(&ids[bidx], &sid);
625                 if (!map) {
626                         DEBUG(2, ("WARNING: couldn't match result with requested SID\n"));
627                         continue;
628                 }
629
630                 /* get type */
631                 if (!ads_pull_uint32(ctx->ads, entry, "sAMAccountType", &atype)) {
632                         DEBUG(1, ("could not get SAM account type\n"));
633                         continue;
634                 }
635
636                 switch (atype & 0xF0000000) {
637                 case ATYPE_SECURITY_GLOBAL_GROUP:
638                 case ATYPE_SECURITY_LOCAL_GROUP:
639                         type = ID_TYPE_GID;
640                         break;
641                 case ATYPE_NORMAL_ACCOUNT:
642                 case ATYPE_WORKSTATION_TRUST:
643                 case ATYPE_INTERDOMAIN_TRUST:
644                         type = ID_TYPE_UID;
645                         break;
646                 default:
647                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
648                         continue;
649                 }
650
651                 if (!ads_pull_uint32(ctx->ads, entry, (type==ID_TYPE_UID) ?
652                                                  ctx->ad_schema->posix_uidnumber_attr :
653                                                  ctx->ad_schema->posix_gidnumber_attr,
654                                      &id)) 
655                 {
656                         DEBUG(1, ("Could not get unix ID\n"));
657                         continue;
658                 }
659                 if ((id == 0) ||
660                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
661                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
662                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
663                                 id, ctx->filter_low_id, ctx->filter_high_id));
664                         continue;
665                 }
666
667                 /* mapped */
668                 map->xid.type = type;
669                 map->xid.id = id;
670                 map->status = ID_MAPPED;
671
672                 DEBUG(10, ("Mapped %s -> %lu (%d)\n", sid_string_dbg(map->sid),
673                            (unsigned long)map->xid.id,
674                            map->xid.type));
675         }
676
677         if (res) {
678                 ads_msgfree(ctx->ads, res);
679         }
680
681         if (ids[idx]) { /* still some values to map */
682                 goto again;
683         }
684
685         ret = NT_STATUS_OK;
686
687         /* mark all unknwoni/expired ones as unmapped */
688         for (i = 0; ids[i]; i++) {
689                 if (ids[i]->status != ID_MAPPED) 
690                         ids[i]->status = ID_UNMAPPED;
691         }
692
693 done:
694         talloc_free(memctx);
695         return ret;
696 }
697
698 /************************************************************************
699  ***********************************************************************/
700
701 static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
702 {
703         struct idmap_ad_context * ctx;
704
705         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
706
707         if (ctx->ads != NULL) {
708                 /* we own this ADS_STRUCT so make sure it goes away */
709                 ctx->ads->is_mine = True;
710                 ads_destroy( &ctx->ads );
711                 ctx->ads = NULL;
712         }
713
714         TALLOC_FREE( ctx->ad_schema );
715         
716         return NT_STATUS_OK;
717 }
718
719 /*
720  * nss_info_{sfu,sfu20,rfc2307}
721  */
722
723 /************************************************************************
724  Initialize the {sfu,sfu20,rfc2307} state
725  ***********************************************************************/
726
727 static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
728 static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
729 static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
730 static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
731 static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
732 static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
733
734 static const char *ad_map_type_string(enum wb_posix_mapping map_type)
735 {
736         switch (map_type) {
737                 case WB_POSIX_MAP_TEMPLATE:
738                         return wb_posix_map_template_string;
739                 case WB_POSIX_MAP_SFU:
740                         return wb_posix_map_sfu_string;
741                 case WB_POSIX_MAP_SFU20:
742                         return wb_posix_map_sfu20_string;
743                 case WB_POSIX_MAP_RFC2307:
744                         return wb_posix_map_rfc2307_string;
745                 case WB_POSIX_MAP_UNIXINFO:
746                         return wb_posix_map_unixinfo_string;
747                 default:
748                         return wb_posix_map_unknown_string;
749         }
750 }
751
752 static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
753                                     enum wb_posix_mapping new_ad_map_type)
754 {
755         struct idmap_domain *dom;
756         struct idmap_ad_context *ctx;
757
758         if (e->state != NULL) {
759                 dom = talloc_get_type(e->state, struct idmap_domain);
760         } else {
761                 dom = TALLOC_ZERO_P(e, struct idmap_domain);
762                 if (dom == NULL) {
763                         DEBUG(0, ("Out of memory!\n"));
764                         return NT_STATUS_NO_MEMORY;
765                 }
766                 e->state = dom;
767         }
768
769         if (e->domain != NULL) {
770                 dom->name = talloc_strdup(dom, e->domain);
771                 if (dom->name == NULL) {
772                         DEBUG(0, ("Out of memory!\n"));
773                         return NT_STATUS_NO_MEMORY;
774                 }
775         }
776
777         if (dom->private_data != NULL) {
778                 ctx = talloc_get_type(dom->private_data,
779                                       struct idmap_ad_context);
780         } else {
781                 ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context);
782                 if (ctx == NULL) {
783                         DEBUG(0, ("Out of memory!\n"));
784                         return NT_STATUS_NO_MEMORY;
785                 }
786                 ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
787                 dom->private_data = ctx;
788         }
789
790         if ((ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
791             (ctx->ad_map_type != new_ad_map_type))
792         {
793                 DEBUG(2, ("nss_ad_generic_init: "
794                           "Warning: overriding previously set posix map type "
795                           "%s for domain %s with map type %s.\n",
796                           ad_map_type_string(ctx->ad_map_type),
797                           dom->name,
798                           ad_map_type_string(new_ad_map_type)));
799         }
800
801         ctx->ad_map_type = new_ad_map_type;
802
803         return NT_STATUS_OK;
804 }
805
806 static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
807 {
808         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
809 }
810
811 static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
812 {
813         return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
814 }
815
816 static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
817 {
818         return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
819 }
820
821
822 /************************************************************************
823  ***********************************************************************/
824
825 static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
826                                   const struct dom_sid *sid,
827                                   TALLOC_CTX *mem_ctx,
828                                   ADS_STRUCT *ads, 
829                                   LDAPMessage *msg,
830                                   const char **homedir,
831                                   const char **shell,
832                                   const char **gecos,
833                                   uint32 *gid )
834 {
835         const char *attrs[] = {NULL, /* attr_homedir */
836                                NULL, /* attr_shell */
837                                NULL, /* attr_gecos */
838                                NULL, /* attr_gidnumber */
839                                NULL };
840         char *filter = NULL;
841         LDAPMessage *msg_internal = NULL;
842         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
843         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
844         char *sidstr = NULL;
845         struct idmap_domain *dom;
846         struct idmap_ad_context *ctx;
847
848         DEBUG(10, ("nss_ad_get_info called for sid [%s] in domain '%s'\n",
849                    sid_string_dbg(sid), e->domain?e->domain:"NULL"));
850
851         /* Only do query if we are online */
852         if (idmap_is_offline()) {
853                 return NT_STATUS_FILE_IS_OFFLINE;
854         }
855
856         dom = talloc_get_type(e->state, struct idmap_domain);
857         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
858
859         ads_status = ad_idmap_cached_connection(dom);
860         if (!ADS_ERR_OK(ads_status)) {
861                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
862         }
863
864         if (!ctx->ad_schema) {
865                 DEBUG(10, ("nss_ad_get_info: no ad_schema configured!\n"));
866                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
867         }
868
869         if (!sid || !homedir || !shell || !gecos) {
870                 return NT_STATUS_INVALID_PARAMETER;
871         }
872
873         /* See if we can use the ADS connection struct swe were given */
874
875         if (ads) {
876                 DEBUG(10, ("nss_ad_get_info: using given ads connection and "
877                            "LDAP message (%p)\n", msg));
878
879                 *homedir = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_homedir_attr );
880                 *shell   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_shell_attr );
881                 *gecos   = ads_pull_string( ads, mem_ctx, msg, ctx->ad_schema->posix_gecos_attr );
882
883                 if (gid) {
884                         if ( !ads_pull_uint32(ads, msg, ctx->ad_schema->posix_gidnumber_attr, gid ) )
885                                 *gid = (uint32)-1;
886                 }
887
888                 nt_status = NT_STATUS_OK;
889                 goto done;
890         }
891
892         /* Have to do our own query */
893
894         DEBUG(10, ("nss_ad_get_info: no ads connection given, doing our "
895                    "own query\n"));
896
897         attrs[0] = ctx->ad_schema->posix_homedir_attr;
898         attrs[1] = ctx->ad_schema->posix_shell_attr;
899         attrs[2] = ctx->ad_schema->posix_gecos_attr;
900         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
901
902         sidstr = sid_binstring(mem_ctx, sid);
903         filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr);
904         TALLOC_FREE(sidstr);
905
906         if (!filter) {
907                 nt_status = NT_STATUS_NO_MEMORY;
908                 goto done;
909         }
910
911         ads_status = ads_search_retry(ctx->ads, &msg_internal, filter, attrs);
912         if (!ADS_ERR_OK(ads_status)) {
913                 nt_status = ads_ntstatus(ads_status);
914                 goto done;
915         }
916
917         *homedir = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_homedir_attr);
918         *shell   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_shell_attr);
919         *gecos   = ads_pull_string(ctx->ads, mem_ctx, msg_internal, ctx->ad_schema->posix_gecos_attr);
920
921         if (gid) {
922                 if (!ads_pull_uint32(ctx->ads, msg_internal, ctx->ad_schema->posix_gidnumber_attr, gid))
923                         *gid = (uint32)-1;
924         }
925
926         nt_status = NT_STATUS_OK;
927
928 done:
929         if (msg_internal) {
930                 ads_msgfree(ctx->ads, msg_internal);
931         }
932
933         return nt_status;
934 }
935
936 /**********************************************************************
937  *********************************************************************/
938
939 static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
940                                     struct nss_domain_entry *e,
941                                     const char *name,
942                                     char **alias)
943 {
944         const char *attrs[] = {NULL, /* attr_uid */
945                                NULL };
946         char *filter = NULL;
947         LDAPMessage *msg = NULL;
948         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
949         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
950         struct idmap_domain *dom;
951         struct idmap_ad_context *ctx = NULL;
952
953         /* Check incoming parameters */
954
955         if ( !e || !e->domain || !name || !*alias) {
956                 nt_status = NT_STATUS_INVALID_PARAMETER;
957                 goto done;
958         }
959
960         /* Only do query if we are online */
961
962         if (idmap_is_offline()) {
963                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
964                 goto done;
965         }
966
967         dom = talloc_get_type(e->state, struct idmap_domain);
968         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
969
970         ads_status = ad_idmap_cached_connection(dom);
971         if (!ADS_ERR_OK(ads_status)) {
972                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
973         }
974
975         if (!ctx->ad_schema) {
976                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
977                 goto done;
978         }
979
980         attrs[0] = ctx->ad_schema->posix_uid_attr;
981
982         filter = talloc_asprintf(mem_ctx,
983                                  "(sAMAccountName=%s)",
984                                  name);
985         if (!filter) {
986                 nt_status = NT_STATUS_NO_MEMORY;
987                 goto done;
988         }
989
990         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
991         if (!ADS_ERR_OK(ads_status)) {
992                 nt_status = ads_ntstatus(ads_status);
993                 goto done;
994         }
995
996         *alias = ads_pull_string(ctx->ads, mem_ctx, msg, ctx->ad_schema->posix_uid_attr);
997
998         if (!*alias) {
999                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1000         }
1001
1002         nt_status = NT_STATUS_OK;
1003
1004 done:
1005         if (filter) {
1006                 talloc_destroy(filter);
1007         }
1008         if (msg) {
1009                 ads_msgfree(ctx->ads, msg);
1010         }
1011
1012         return nt_status;
1013 }
1014
1015 /**********************************************************************
1016  *********************************************************************/
1017
1018 static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
1019                                              struct nss_domain_entry *e,
1020                                              const char *alias,
1021                                              char **name )
1022 {
1023         const char *attrs[] = {"sAMAccountName",
1024                                NULL };
1025         char *filter = NULL;
1026         LDAPMessage *msg = NULL;
1027         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1028         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1029         char *username;
1030         struct idmap_domain *dom;
1031         struct idmap_ad_context *ctx = NULL;
1032
1033         /* Check incoming parameters */
1034
1035         if ( !alias || !name) {
1036                 nt_status = NT_STATUS_INVALID_PARAMETER;
1037                 goto done;
1038         }
1039
1040         /* Only do query if we are online */
1041
1042         if (idmap_is_offline()) {
1043                 nt_status = NT_STATUS_FILE_IS_OFFLINE;
1044                 goto done;
1045         }
1046
1047         dom = talloc_get_type(e->state, struct idmap_domain);
1048         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
1049
1050         ads_status = ad_idmap_cached_connection(dom);
1051         if (!ADS_ERR_OK(ads_status)) {
1052                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1053         }
1054
1055         if (!ctx->ad_schema) {
1056                 nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1057                 goto done;
1058         }
1059
1060         filter = talloc_asprintf(mem_ctx,
1061                                  "(%s=%s)",
1062                                  ctx->ad_schema->posix_uid_attr,
1063                                  alias);
1064         if (!filter) {
1065                 nt_status = NT_STATUS_NO_MEMORY;
1066                 goto done;
1067         }
1068
1069         ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
1070         if (!ADS_ERR_OK(ads_status)) {
1071                 nt_status = ads_ntstatus(ads_status);
1072                 goto done;
1073         }
1074
1075         username = ads_pull_string(ctx->ads, mem_ctx, msg,
1076                                    "sAMAccountName");
1077         if (!username) {
1078                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1079         }
1080
1081         *name = talloc_asprintf(mem_ctx, "%s\\%s",
1082                                 lp_workgroup(),
1083                                 username);
1084         if (!*name) {
1085                 nt_status = NT_STATUS_NO_MEMORY;
1086                 goto done;
1087         }
1088
1089         nt_status = NT_STATUS_OK;
1090
1091 done:
1092         if (filter) {
1093                 talloc_destroy(filter);
1094         }
1095         if (msg) {
1096                 ads_msgfree(ctx->ads, msg);
1097         }
1098
1099         return nt_status;
1100 }
1101
1102
1103 /************************************************************************
1104  ***********************************************************************/
1105
1106 static NTSTATUS nss_ad_close( void )
1107 {
1108         /* nothing to do.  All memory is free()'d by the idmap close_fn() */
1109
1110         return NT_STATUS_OK;
1111 }
1112
1113 /************************************************************************
1114  Function dispatch tables for the idmap and nss plugins
1115  ***********************************************************************/
1116
1117 static struct idmap_methods ad_methods = {
1118         .init            = idmap_ad_initialize,
1119         .unixids_to_sids = idmap_ad_unixids_to_sids,
1120         .sids_to_unixids = idmap_ad_sids_to_unixids,
1121         .close_fn        = idmap_ad_close
1122 };
1123
1124 /* The SFU and RFC2307 NSS plugins share everything but the init
1125    function which sets the intended schema model to use */
1126   
1127 static struct nss_info_methods nss_rfc2307_methods = {
1128         .init           = nss_rfc2307_init,
1129         .get_nss_info   = nss_ad_get_info,
1130         .map_to_alias   = nss_ad_map_to_alias,
1131         .map_from_alias = nss_ad_map_from_alias,
1132         .close_fn       = nss_ad_close
1133 };
1134
1135 static struct nss_info_methods nss_sfu_methods = {
1136         .init           = nss_sfu_init,
1137         .get_nss_info   = nss_ad_get_info,
1138         .map_to_alias   = nss_ad_map_to_alias,
1139         .map_from_alias = nss_ad_map_from_alias,
1140         .close_fn       = nss_ad_close
1141 };
1142
1143 static struct nss_info_methods nss_sfu20_methods = {
1144         .init           = nss_sfu20_init,
1145         .get_nss_info   = nss_ad_get_info,
1146         .map_to_alias   = nss_ad_map_to_alias,
1147         .map_from_alias = nss_ad_map_from_alias,
1148         .close_fn       = nss_ad_close
1149 };
1150
1151
1152
1153 /************************************************************************
1154  Initialize the plugins
1155  ***********************************************************************/
1156
1157 NTSTATUS idmap_ad_init(void)
1158 {
1159         static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
1160         static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
1161         static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
1162         static NTSTATUS status_nss_sfu20 = NT_STATUS_UNSUCCESSFUL;
1163
1164         /* Always register the AD method first in order to get the
1165            idmap_domain interface called */
1166
1167         if ( !NT_STATUS_IS_OK(status_idmap_ad) ) {
1168                 status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, 
1169                                                      "ad", &ad_methods);
1170                 if ( !NT_STATUS_IS_OK(status_idmap_ad) )
1171                         return status_idmap_ad;         
1172         }
1173         
1174         if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
1175                 status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1176                                                             "rfc2307",  &nss_rfc2307_methods );         
1177                 if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
1178                         return status_nss_rfc2307;
1179         }
1180
1181         if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
1182                 status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1183                                                         "sfu",  &nss_sfu_methods );             
1184                 if ( !NT_STATUS_IS_OK(status_nss_sfu) )
1185                         return status_nss_sfu;          
1186         }
1187
1188         if ( !NT_STATUS_IS_OK( status_nss_sfu20 ) ) {
1189                 status_nss_sfu20 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
1190                                                         "sfu20",  &nss_sfu20_methods );         
1191                 if ( !NT_STATUS_IS_OK(status_nss_sfu20) )
1192                         return status_nss_sfu20;                
1193         }
1194
1195         return NT_STATUS_OK;    
1196 }
1197