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