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