s3-nss_info: only include nss_info.h where needed.
[rusty/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 "libads/ldap_schema.h"
32 #include "nss_info.h"
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_IDMAP
36
37 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
38
39 #define IDMAP_AD_MAX_IDS 30
40 #define CHECK_ALLOC_DONE(mem) do { \
41      if (!mem) { \
42            DEBUG(0, ("Out of memory!\n")); \
43            ret = NT_STATUS_NO_MEMORY; \
44            goto done; \
45       } \
46 } while (0)
47
48 struct idmap_ad_context {
49         uint32_t filter_low_id;
50         uint32_t filter_high_id;
51         ADS_STRUCT *ads;
52         struct posix_schema *ad_schema;
53         enum wb_posix_mapping ad_map_type; /* WB_POSIX_MAP_UNKNOWN */
54 };
55
56 NTSTATUS init_module(void);
57
58 /************************************************************************
59  ***********************************************************************/
60
61 static ADS_STATUS ad_idmap_cached_connection_internal(struct idmap_domain *dom)
62 {
63         ADS_STRUCT *ads;
64         ADS_STATUS status;
65         bool local = False;
66         fstring dc_name;
67         struct sockaddr_storage dc_ip;
68         struct idmap_ad_context *ctx;
69         char *ldap_server = NULL;
70         char *realm = NULL;
71         struct winbindd_domain *wb_dom;
72
73         DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
74                    dom->name));
75
76         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
77
78         if (ctx->ads != NULL) {
79
80                 time_t expire;
81                 time_t now = time(NULL);
82
83                 ads = ctx->ads;
84
85                 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
86
87                 /* check for a valid structure */
88                 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
89                           (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
90
91                 if ( ads->config.realm && (expire > time(NULL))) {
92                         return ADS_SUCCESS;
93                 } else {
94                         /* we own this ADS_STRUCT so make sure it goes away */
95                         DEBUG(7,("Deleting expired krb5 credential cache\n"));
96                         ads->is_mine = True;
97                         ads_destroy( &ads );
98                         ads_kdestroy(WINBIND_CCACHE_NAME);
99                         ctx->ads = NULL;
100                         TALLOC_FREE( ctx->ad_schema );
101                 }
102         }
103
104         if (!local) {
105                 /* we don't want this to affect the users ccache */
106                 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
107         }
108
109         /*
110          * At this point we only have the NetBIOS domain name.
111          * Check if we can get server nam and realm from SAF cache
112          * and the domain list.
113          */
114         ldap_server = saf_fetch(dom->name);
115         DEBUG(10, ("ldap_server from saf cache: '%s'\n", ldap_server?ldap_server:""));
116
117         wb_dom = find_domain_from_name_noinit(dom->name);
118         if (wb_dom == NULL) {
119                 DEBUG(10, ("find_domain_from_name_noinit did not find domain '%s'\n",
120                            dom->name));
121                 realm = NULL;
122         } else {
123                 DEBUG(10, ("find_domain_from_name_noinit found realm '%s' for "
124                           " domain '%s'\n", wb_dom->alt_name, dom->name));
125                 realm = wb_dom->alt_name;
126         }
127
128         if ( (ads = ads_init(realm, dom->name, ldap_server)) == NULL ) {
129                 DEBUG(1,("ads_init failed\n"));
130                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
131         }
132
133         /* the machine acct password might have change - fetch it every time */
134         SAFE_FREE(ads->auth.password);
135         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
136
137         SAFE_FREE(ads->auth.realm);
138         ads->auth.realm = SMB_STRDUP(lp_realm());
139
140         /* setup server affinity */
141
142         get_dc_name(dom->name, realm, dc_name, &dc_ip );
143         
144         status = ads_connect(ads);
145         if (!ADS_ERR_OK(status)) {
146                 DEBUG(1, ("ad_idmap_init: failed to connect to AD\n"));
147                 ads_destroy(&ads);
148                 return status;
149         }
150
151         ads->is_mine = False;
152
153         ctx->ads = ads;
154
155         return ADS_SUCCESS;
156 }
157
158 /************************************************************************
159  ***********************************************************************/
160
161 static ADS_STATUS ad_idmap_cached_connection(struct idmap_domain *dom)
162 {
163         ADS_STATUS status;
164         struct idmap_ad_context * ctx;
165
166         status = ad_idmap_cached_connection_internal(dom);
167         if (!ADS_ERR_OK(status)) {
168                 return status;
169         }
170
171         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
172
173         /* if we have a valid ADS_STRUCT and the schema model is
174            defined, then we can return here. */
175
176         if ( ctx->ad_schema ) {
177                 return ADS_SUCCESS;
178         }
179
180         /* Otherwise, set the schema model */
181
182         if ( (ctx->ad_map_type ==  WB_POSIX_MAP_SFU) ||
183              (ctx->ad_map_type ==  WB_POSIX_MAP_SFU20) ||
184              (ctx->ad_map_type ==  WB_POSIX_MAP_RFC2307) )
185         {
186                 status = ads_check_posix_schema_mapping(NULL, ctx->ads, ctx->ad_map_type, &ctx->ad_schema);
187                 if ( !ADS_ERR_OK(status) ) {
188                         DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
189                 }
190         }
191         
192         return status;
193 }
194
195 /************************************************************************
196  ***********************************************************************/
197
198 static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom,
199                                     const char *params)
200 {
201         struct idmap_ad_context *ctx;
202         char *config_option;
203         const char *range = NULL;
204         const char *schema_mode = NULL; 
205
206         if ( (ctx = TALLOC_ZERO_P(dom, struct idmap_ad_context)) == NULL ) {
207                 DEBUG(0, ("Out of memory!\n"));
208                 return NT_STATUS_NO_MEMORY;
209         }
210
211         if ( (config_option = talloc_asprintf(ctx, "idmap config %s", dom->name)) == NULL ) {
212                 DEBUG(0, ("Out of memory!\n"));
213                 talloc_free(ctx);
214                 return NT_STATUS_NO_MEMORY;
215         }
216
217         /* load ranges */
218         range = lp_parm_const_string(-1, config_option, "range", NULL);
219         if (range && range[0]) {
220                 if ((sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) ||
221                     (ctx->filter_low_id > ctx->filter_high_id)) {
222                         DEBUG(1, ("ERROR: invalid filter range [%s]", range));
223                         ctx->filter_low_id = 0;
224                         ctx->filter_high_id = 0;
225                 }
226         }
227
228         /* default map type */
229         ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
230
231         /* schema mode */
232         schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL);
233         if ( schema_mode && schema_mode[0] ) {
234                 if ( strequal(schema_mode, "sfu") )
235                         ctx->ad_map_type = WB_POSIX_MAP_SFU;
236                 else if ( strequal(schema_mode, "sfu20" ) )
237                         ctx->ad_map_type = WB_POSIX_MAP_SFU20;
238                 else if ( strequal(schema_mode, "rfc2307" ) )
239                         ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
240                 else
241                         DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n",
242                                  schema_mode));
243         }
244
245         dom->private_data = ctx;
246
247         talloc_free(config_option);
248
249         return NT_STATUS_OK;
250 }
251
252 /************************************************************************
253  Search up to IDMAP_AD_MAX_IDS entries in maps for a match.
254  ***********************************************************************/
255
256 static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id)
257 {
258         int i;
259
260         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
261                 if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
262                         return maps[i];
263                 }
264         }
265
266         return NULL;    
267 }
268
269 /************************************************************************
270  Search up to IDMAP_AD_MAX_IDS entries in maps for a match
271  ***********************************************************************/
272
273 static struct id_map *find_map_by_sid(struct id_map **maps, struct dom_sid *sid)
274 {
275         int i;
276
277         for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
278                 if (sid_equal(maps[i]->sid, sid)) {
279                         return maps[i];
280                 }
281         }
282
283         return NULL;    
284 }
285
286 /************************************************************************
287  ***********************************************************************/
288
289 static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
290 {
291         NTSTATUS ret;
292         TALLOC_CTX *memctx;
293         struct idmap_ad_context *ctx;
294         ADS_STATUS rc;
295         const char *attrs[] = { "sAMAccountType", 
296                                 "objectSid",
297                                 NULL, /* uidnumber */
298                                 NULL, /* gidnumber */
299                                 NULL };
300         LDAPMessage *res = NULL;
301         LDAPMessage *entry = NULL;
302         char *filter = NULL;
303         int idx = 0;
304         int bidx = 0;
305         int count;
306         int i;
307         char *u_filter = NULL;
308         char *g_filter = NULL;
309
310         /* initialize the status to avoid suprise */
311         for (i = 0; ids[i]; i++) {
312                 ids[i]->status = ID_UNKNOWN;
313         }
314         
315         /* Only do query if we are online */
316         if (idmap_is_offline()) {
317                 return NT_STATUS_FILE_IS_OFFLINE;
318         }
319
320         ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
321
322         if ( (memctx = talloc_new(ctx)) == NULL ) {
323                 DEBUG(0, ("Out of memory!\n"));
324                 return NT_STATUS_NO_MEMORY;
325         }
326
327         rc = ad_idmap_cached_connection(dom);
328         if (!ADS_ERR_OK(rc)) {
329                 DEBUG(1, ("ADS uninitialized: %s\n", ads_errstr(rc)));
330                 ret = NT_STATUS_UNSUCCESSFUL;
331                 /* ret = ads_ntstatus(rc); */
332                 goto done;
333         }
334
335         attrs[2] = ctx->ad_schema->posix_uidnumber_attr;
336         attrs[3] = ctx->ad_schema->posix_gidnumber_attr;
337
338 again:
339         bidx = idx;
340         for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
341                 switch (ids[idx]->xid.type) {
342                 case ID_TYPE_UID:     
343                         if ( ! u_filter) {
344                                 u_filter = talloc_asprintf(memctx, "(&(|"
345                                                            "(sAMAccountType=%d)"
346                                                            "(sAMAccountType=%d)"
347                                                            "(sAMAccountType=%d))(|",
348                                                            ATYPE_NORMAL_ACCOUNT,
349                                                            ATYPE_WORKSTATION_TRUST,
350                                                            ATYPE_INTERDOMAIN_TRUST);
351                         }
352                         u_filter = talloc_asprintf_append_buffer(u_filter, "(%s=%lu)",
353                                                           ctx->ad_schema->posix_uidnumber_attr,
354                                                           (unsigned long)ids[idx]->xid.id);
355                         CHECK_ALLOC_DONE(u_filter);
356                         break;
357                                 
358                 case ID_TYPE_GID:
359                         if ( ! g_filter) {
360                                 g_filter = talloc_asprintf(memctx, "(&(|"
361                                                            "(sAMAccountType=%d)"
362                                                            "(sAMAccountType=%d))(|",
363                                                            ATYPE_SECURITY_GLOBAL_GROUP,
364                                                            ATYPE_SECURITY_LOCAL_GROUP);
365                         }
366                         g_filter = talloc_asprintf_append_buffer(g_filter, "(%s=%lu)",
367                                                           ctx->ad_schema->posix_gidnumber_attr,
368                                                           (unsigned long)ids[idx]->xid.id);
369                         CHECK_ALLOC_DONE(g_filter);
370                         break;
371
372                 default:
373                         DEBUG(3, ("Error: mapping requested but Unknown ID type\n"));
374                         ids[idx]->status = ID_UNKNOWN;
375                         continue;
376                 }
377         }
378         filter = talloc_asprintf(memctx, "(|");
379         CHECK_ALLOC_DONE(filter);
380         if ( u_filter) {
381                 filter = talloc_asprintf_append_buffer(filter, "%s))", u_filter);
382                 CHECK_ALLOC_DONE(filter);
383                         TALLOC_FREE(u_filter);
384         }
385         if ( g_filter) {
386                 filter = talloc_asprintf_append_buffer(filter, "%s))", g_filter);
387                 CHECK_ALLOC_DONE(filter);
388                 TALLOC_FREE(g_filter);
389         }
390         filter = talloc_asprintf_append_buffer(filter, ")");
391         CHECK_ALLOC_DONE(filter);
392
393         rc = ads_search_retry(ctx->ads, &res, filter, attrs);
394         if (!ADS_ERR_OK(rc)) {
395                 DEBUG(1, ("ERROR: ads search returned: %s\n", ads_errstr(rc)));
396                 ret = NT_STATUS_UNSUCCESSFUL;
397                 goto done;
398         }
399
400         if ( (count = ads_count_replies(ctx->ads, res)) == 0 ) {
401                 DEBUG(10, ("No IDs found\n"));
402         }
403
404         entry = res;
405         for (i = 0; (i < count) && entry; i++) {
406                 struct dom_sid sid;
407                 enum id_type type;
408                 struct id_map *map;
409                 uint32_t id;
410                 uint32_t atype;
411
412                 if (i == 0) { /* first entry */
413                         entry = ads_first_entry(ctx->ads, entry);
414                 } else { /* following ones */
415                         entry = ads_next_entry(ctx->ads, entry);
416                 }
417
418                 if ( !entry ) {
419                         DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
420                         break;
421                 }
422
423                 /* first check if the SID is present */
424                 if (!ads_pull_sid(ctx->ads, entry, "objectSid", &sid)) {
425                         DEBUG(2, ("Could not retrieve SID from entry\n"));
426                         continue;
427                 }
428
429                 /* get type */
430                 if (!ads_pull_uint32(ctx->ads, entry, "sAMAccountType", &atype)) {
431                         DEBUG(1, ("could not get SAM account type\n"));
432                         continue;
433                 }
434
435                 switch (atype & 0xF0000000) {
436                 case ATYPE_SECURITY_GLOBAL_GROUP:
437                 case ATYPE_SECURITY_LOCAL_GROUP:
438                         type = ID_TYPE_GID;
439                         break;
440                 case ATYPE_NORMAL_ACCOUNT:
441                 case ATYPE_WORKSTATION_TRUST:
442                 case ATYPE_INTERDOMAIN_TRUST:
443                         type = ID_TYPE_UID;
444                         break;
445                 default:
446                         DEBUG(1, ("unrecognized SAM account type %08x\n", atype));
447                         continue;
448                 }
449
450                 if (!ads_pull_uint32(ctx->ads, entry, (type==ID_TYPE_UID) ?
451                                                  ctx->ad_schema->posix_uidnumber_attr :
452                                                  ctx->ad_schema->posix_gidnumber_attr,
453                                      &id)) 
454                 {
455                         DEBUG(1, ("Could not get unix ID\n"));
456                         continue;
457                 }
458
459                 if ((id == 0) ||
460                     (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
461                     (ctx->filter_high_id && (id > ctx->filter_high_id))) {
462                         DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
463                                 id, ctx->filter_low_id, ctx->filter_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