r23779: Change from v2 or later to v3 or later.
[samba.git] / source3 / groupdb / mapping_tdb.c
1 /* 
2  *  Unix SMB/CIFS implementation.
3  *  RPC Pipe client / server routines
4  *  Copyright (C) Andrew Tridgell              1992-2006,
5  *  Copyright (C) Jean Fran├žois Micouleau      1998-2001.
6  *  Copyright (C) Volker Lendecke              2006.
7  *  Copyright (C) Gerald Carter                2006.
8  *  
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 3 of the License, or
12  *  (at your option) any later version.
13  *  
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *  
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include "includes.h"
25 #include "groupdb/mapping.h"
26
27 static TDB_CONTEXT *tdb; /* used for driver files */
28
29 static BOOL enum_group_mapping(const DOM_SID *domsid, enum lsa_SidType sid_name_use, GROUP_MAP **pp_rmap,
30                                size_t *p_num_entries, BOOL unix_only);
31 static BOOL group_map_remove(const DOM_SID *sid);
32         
33 /****************************************************************************
34  Open the group mapping tdb.
35 ****************************************************************************/
36 static BOOL init_group_mapping(void)
37 {
38         const char *vstring = "INFO/version";
39         int32 vers_id;
40         GROUP_MAP *map_table = NULL;
41         size_t num_entries = 0;
42         
43         if (tdb)
44                 return True;
45                 
46         tdb = tdb_open_log(lock_path("group_mapping.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
47         if (!tdb) {
48                 DEBUG(0,("Failed to open group mapping database\n"));
49                 return False;
50         }
51
52         /* handle a Samba upgrade */
53         tdb_lock_bystring(tdb, vstring);
54
55         /* Cope with byte-reversed older versions of the db. */
56         vers_id = tdb_fetch_int32(tdb, vstring);
57         if ((vers_id == DATABASE_VERSION_V1) || (IREV(vers_id) == DATABASE_VERSION_V1)) {
58                 /* Written on a bigendian machine with old fetch_int code. Save as le. */
59                 tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
60                 vers_id = DATABASE_VERSION_V2;
61         }
62
63         /* if its an unknown version we remove everthing in the db */
64         
65         if (vers_id != DATABASE_VERSION_V2) {
66                 tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
67                 tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
68         }
69
70         tdb_unlock_bystring(tdb, vstring);
71
72         /* cleanup any map entries with a gid == -1 */
73         
74         if ( enum_group_mapping( NULL, SID_NAME_UNKNOWN, &map_table, &num_entries, False ) ) {
75                 int i;
76                 
77                 for ( i=0; i<num_entries; i++ ) {
78                         if ( map_table[i].gid == -1 ) {
79                                 group_map_remove( &map_table[i].sid );
80                         }
81                 }
82                 
83                 SAFE_FREE( map_table );
84         }
85
86
87         return True;
88 }
89
90 /****************************************************************************
91 ****************************************************************************/
92 static BOOL add_mapping_entry(GROUP_MAP *map, int flag)
93 {
94         TDB_DATA dbuf;
95         pstring key, buf;
96         fstring string_sid="";
97         int len;
98
99         sid_to_string(string_sid, &map->sid);
100
101         len = tdb_pack((uint8 *)buf, sizeof(buf), "ddff",
102                         map->gid, map->sid_name_use, map->nt_name, map->comment);
103
104         if (len > sizeof(buf))
105                 return False;
106
107         slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
108
109         dbuf.dsize = len;
110         dbuf.dptr = (uint8 *)buf;
111         if (tdb_store_bystring(tdb, key, dbuf, flag) != 0) return False;
112
113         return True;
114 }
115
116
117 /****************************************************************************
118  Return the sid and the type of the unix group.
119 ****************************************************************************/
120
121 static BOOL get_group_map_from_sid(DOM_SID sid, GROUP_MAP *map)
122 {
123         TDB_DATA dbuf;
124         pstring key;
125         fstring string_sid;
126         int ret = 0;
127         
128         /* the key is the SID, retrieving is direct */
129
130         sid_to_string(string_sid, &sid);
131         slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
132
133         dbuf = tdb_fetch_bystring(tdb, key);
134         if (!dbuf.dptr)
135                 return False;
136
137         ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
138                                 &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
139
140         SAFE_FREE(dbuf.dptr);
141         
142         if ( ret == -1 ) {
143                 DEBUG(3,("get_group_map_from_sid: tdb_unpack failure\n"));
144                 return False;
145         }
146
147         sid_copy(&map->sid, &sid);
148         
149         return True;
150 }
151
152 /****************************************************************************
153  Return the sid and the type of the unix group.
154 ****************************************************************************/
155
156 static BOOL get_group_map_from_gid(gid_t gid, GROUP_MAP *map)
157 {
158         TDB_DATA kbuf, dbuf, newkey;
159         fstring string_sid;
160         int ret;
161
162         /* we need to enumerate the TDB to find the GID */
163
164         for (kbuf = tdb_firstkey(tdb); 
165              kbuf.dptr; 
166              newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
167
168                 if (strncmp((const char *)kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
169                 
170                 dbuf = tdb_fetch(tdb, kbuf);
171                 if (!dbuf.dptr)
172                         continue;
173
174                 fstrcpy(string_sid, (const char *)kbuf.dptr+strlen(GROUP_PREFIX));
175
176                 string_to_sid(&map->sid, string_sid);
177                 
178                 ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
179                                  &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
180
181                 SAFE_FREE(dbuf.dptr);
182
183                 if ( ret == -1 ) {
184                         DEBUG(3,("get_group_map_from_gid: tdb_unpack failure\n"));
185                         return False;
186                 }
187         
188                 if (gid==map->gid) {
189                         SAFE_FREE(kbuf.dptr);
190                         return True;
191                 }
192         }
193
194         return False;
195 }
196
197 /****************************************************************************
198  Return the sid and the type of the unix group.
199 ****************************************************************************/
200
201 static BOOL get_group_map_from_ntname(const char *name, GROUP_MAP *map)
202 {
203         TDB_DATA kbuf, dbuf, newkey;
204         fstring string_sid;
205         int ret;
206
207         /* we need to enumerate the TDB to find the name */
208
209         for (kbuf = tdb_firstkey(tdb); 
210              kbuf.dptr; 
211              newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
212
213                 if (strncmp((const char *)kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
214                 
215                 dbuf = tdb_fetch(tdb, kbuf);
216                 if (!dbuf.dptr)
217                         continue;
218
219                 fstrcpy(string_sid, (const char *)kbuf.dptr+strlen(GROUP_PREFIX));
220
221                 string_to_sid(&map->sid, string_sid);
222                 
223                 ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
224                                  &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
225
226                 SAFE_FREE(dbuf.dptr);
227                 
228                 if ( ret == -1 ) {
229                         DEBUG(3,("get_group_map_from_ntname: tdb_unpack failure\n"));
230                         return False;
231                 }
232
233                 if ( strequal(name, map->nt_name) ) {
234                         SAFE_FREE(kbuf.dptr);
235                         return True;
236                 }
237         }
238
239         return False;
240 }
241
242 /****************************************************************************
243  Remove a group mapping entry.
244 ****************************************************************************/
245
246 static BOOL group_map_remove(const DOM_SID *sid)
247 {
248         TDB_DATA dbuf;
249         pstring key;
250         fstring string_sid;
251         
252         /* the key is the SID, retrieving is direct */
253
254         sid_to_string(string_sid, sid);
255         slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
256
257         dbuf = tdb_fetch_bystring(tdb, key);
258         if (!dbuf.dptr)
259                 return False;
260         
261         SAFE_FREE(dbuf.dptr);
262
263         if(tdb_delete_bystring(tdb, key) != TDB_SUCCESS)
264                 return False;
265
266         return True;
267 }
268
269 /****************************************************************************
270  Enumerate the group mapping.
271 ****************************************************************************/
272
273 static BOOL enum_group_mapping(const DOM_SID *domsid, enum lsa_SidType sid_name_use, GROUP_MAP **pp_rmap,
274                         size_t *p_num_entries, BOOL unix_only)
275 {
276         TDB_DATA kbuf, dbuf, newkey;
277         fstring string_sid;
278         GROUP_MAP map;
279         GROUP_MAP *mapt;
280         int ret;
281         size_t entries=0;
282         DOM_SID grpsid;
283         uint32 rid;
284
285         *p_num_entries=0;
286         *pp_rmap=NULL;
287
288         for (kbuf = tdb_firstkey(tdb); 
289              kbuf.dptr; 
290              newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
291
292                 if (strncmp((const char *)kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0)
293                         continue;
294
295                 dbuf = tdb_fetch(tdb, kbuf);
296                 if (!dbuf.dptr)
297                         continue;
298
299                 fstrcpy(string_sid, (const char *)kbuf.dptr+strlen(GROUP_PREFIX));
300                                 
301                 ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
302                                  &map.gid, &map.sid_name_use, &map.nt_name, &map.comment);
303
304                 SAFE_FREE(dbuf.dptr);
305
306                 if ( ret == -1 ) {
307                         DEBUG(3,("enum_group_mapping: tdb_unpack failure\n"));
308                         continue;
309                 }
310         
311                 /* list only the type or everything if UNKNOWN */
312                 if (sid_name_use!=SID_NAME_UNKNOWN  && sid_name_use!=map.sid_name_use) {
313                         DEBUG(11,("enum_group_mapping: group %s is not of the requested type\n", map.nt_name));
314                         continue;
315                 }
316
317                 if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
318                         DEBUG(11,("enum_group_mapping: group %s is non mapped\n", map.nt_name));
319                         continue;
320                 }
321
322                 string_to_sid(&grpsid, string_sid);
323                 sid_copy( &map.sid, &grpsid );
324                 
325                 sid_split_rid( &grpsid, &rid );
326
327                 /* Only check the domain if we were given one */
328
329                 if ( domsid && !sid_equal( domsid, &grpsid ) ) {
330                         DEBUG(11,("enum_group_mapping: group %s is not in domain %s\n", 
331                                 string_sid, sid_string_static(domsid)));
332                         continue;
333                 }
334
335                 DEBUG(11,("enum_group_mapping: returning group %s of "
336                           "type %s\n", map.nt_name,
337                           sid_type_lookup(map.sid_name_use)));
338
339                 (*pp_rmap) = SMB_REALLOC_ARRAY((*pp_rmap), GROUP_MAP, entries+1);
340                 if (!(*pp_rmap)) {
341                         DEBUG(0,("enum_group_mapping: Unable to enlarge group map!\n"));
342                         return False;
343                 }
344
345                 mapt = (*pp_rmap);
346
347                 mapt[entries].gid = map.gid;
348                 sid_copy( &mapt[entries].sid, &map.sid);
349                 mapt[entries].sid_name_use = map.sid_name_use;
350                 fstrcpy(mapt[entries].nt_name, map.nt_name);
351                 fstrcpy(mapt[entries].comment, map.comment);
352
353                 entries++;
354
355         }
356
357         *p_num_entries=entries;
358
359         return True;
360 }
361
362 /* This operation happens on session setup, so it should better be fast. We
363  * store a list of aliases a SID is member of hanging off MEMBEROF/SID. */
364
365 static NTSTATUS one_alias_membership(const DOM_SID *member,
366                                DOM_SID **sids, size_t *num)
367 {
368         fstring key, string_sid;
369         TDB_DATA dbuf;
370         const char *p;
371
372         sid_to_string(string_sid, member);
373         slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, string_sid);
374
375         dbuf = tdb_fetch_bystring(tdb, key);
376
377         if (dbuf.dptr == NULL) {
378                 return NT_STATUS_OK;
379         }
380
381         p = (const char *)dbuf.dptr;
382
383         while (next_token(&p, string_sid, " ", sizeof(string_sid))) {
384
385                 DOM_SID alias;
386
387                 if (!string_to_sid(&alias, string_sid))
388                         continue;
389
390                 if (!add_sid_to_array_unique(NULL, &alias, sids, num)) {
391                         return NT_STATUS_NO_MEMORY;
392                 }
393         }
394
395         SAFE_FREE(dbuf.dptr);
396         return NT_STATUS_OK;
397 }
398
399 static NTSTATUS alias_memberships(const DOM_SID *members, size_t num_members,
400                                   DOM_SID **sids, size_t *num)
401 {
402         size_t i;
403
404         *num = 0;
405         *sids = NULL;
406
407         for (i=0; i<num_members; i++) {
408                 NTSTATUS status = one_alias_membership(&members[i], sids, num);
409                 if (!NT_STATUS_IS_OK(status))
410                         return status;
411         }
412         return NT_STATUS_OK;
413 }
414
415 static BOOL is_aliasmem(const DOM_SID *alias, const DOM_SID *member)
416 {
417         DOM_SID *sids;
418         size_t i, num;
419
420         /* This feels the wrong way round, but the on-disk data structure
421          * dictates it this way. */
422         if (!NT_STATUS_IS_OK(alias_memberships(member, 1, &sids, &num)))
423                 return False;
424
425         for (i=0; i<num; i++) {
426                 if (sid_compare(alias, &sids[i]) == 0) {
427                         TALLOC_FREE(sids);
428                         return True;
429                 }
430         }
431         TALLOC_FREE(sids);
432         return False;
433 }
434
435
436 static NTSTATUS add_aliasmem(const DOM_SID *alias, const DOM_SID *member)
437 {
438         GROUP_MAP map;
439         TDB_DATA dbuf;
440         pstring key;
441         fstring string_sid;
442         char *new_memberstring;
443         int result;
444
445         if (!get_group_map_from_sid(*alias, &map))
446                 return NT_STATUS_NO_SUCH_ALIAS;
447
448         if ( (map.sid_name_use != SID_NAME_ALIAS) &&
449              (map.sid_name_use != SID_NAME_WKN_GRP) )
450                 return NT_STATUS_NO_SUCH_ALIAS;
451
452         if (is_aliasmem(alias, member))
453                 return NT_STATUS_MEMBER_IN_ALIAS;
454
455         sid_to_string(string_sid, member);
456         slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, string_sid);
457
458         dbuf = tdb_fetch_bystring(tdb, key);
459
460         sid_to_string(string_sid, alias);
461
462         if (dbuf.dptr != NULL) {
463                 asprintf(&new_memberstring, "%s %s", (char *)(dbuf.dptr),
464                          string_sid);
465         } else {
466                 new_memberstring = SMB_STRDUP(string_sid);
467         }
468
469         if (new_memberstring == NULL)
470                 return NT_STATUS_NO_MEMORY;
471
472         SAFE_FREE(dbuf.dptr);
473         dbuf = string_term_tdb_data(new_memberstring);
474
475         result = tdb_store_bystring(tdb, key, dbuf, 0);
476
477         SAFE_FREE(new_memberstring);
478
479         return (result == 0 ? NT_STATUS_OK : NT_STATUS_ACCESS_DENIED);
480 }
481
482 struct aliasmem_closure {
483         const DOM_SID *alias;
484         DOM_SID **sids;
485         size_t *num;
486 };
487
488 static int collect_aliasmem(TDB_CONTEXT *tdb_ctx, TDB_DATA key, TDB_DATA data,
489                             void *state)
490 {
491         struct aliasmem_closure *closure = (struct aliasmem_closure *)state;
492         const char *p;
493         fstring alias_string;
494
495         if (strncmp((const char *)key.dptr, MEMBEROF_PREFIX,
496                     strlen(MEMBEROF_PREFIX)) != 0)
497                 return 0;
498
499         p = (const char *)data.dptr;
500
501         while (next_token(&p, alias_string, " ", sizeof(alias_string))) {
502
503                 DOM_SID alias, member;
504                 const char *member_string;
505                 
506
507                 if (!string_to_sid(&alias, alias_string))
508                         continue;
509
510                 if (sid_compare(closure->alias, &alias) != 0)
511                         continue;
512
513                 /* Ok, we found the alias we're looking for in the membership
514                  * list currently scanned. The key represents the alias
515                  * member. Add that. */
516
517                 member_string = strchr((const char *)key.dptr, '/');
518
519                 /* Above we tested for MEMBEROF_PREFIX which includes the
520                  * slash. */
521
522                 SMB_ASSERT(member_string != NULL);
523                 member_string += 1;
524
525                 if (!string_to_sid(&member, member_string))
526                         continue;
527                 
528                 if (!add_sid_to_array(NULL, &member, closure->sids, closure->num)) {
529                         /* talloc fail. */
530                         break;
531                 }
532         }
533
534         return 0;
535 }
536
537 static NTSTATUS enum_aliasmem(const DOM_SID *alias, DOM_SID **sids, size_t *num)
538 {
539         GROUP_MAP map;
540         struct aliasmem_closure closure;
541
542         if (!get_group_map_from_sid(*alias, &map))
543                 return NT_STATUS_NO_SUCH_ALIAS;
544
545         if ( (map.sid_name_use != SID_NAME_ALIAS) &&
546              (map.sid_name_use != SID_NAME_WKN_GRP) )
547                 return NT_STATUS_NO_SUCH_ALIAS;
548
549         *sids = NULL;
550         *num = 0;
551
552         closure.alias = alias;
553         closure.sids = sids;
554         closure.num = num;
555
556         tdb_traverse(tdb, collect_aliasmem, &closure);
557         return NT_STATUS_OK;
558 }
559
560 static NTSTATUS del_aliasmem(const DOM_SID *alias, const DOM_SID *member)
561 {
562         NTSTATUS result;
563         DOM_SID *sids;
564         size_t i, num;
565         BOOL found = False;
566         char *member_string;
567         TDB_DATA dbuf;
568         pstring key;
569         fstring sid_string;
570
571         result = alias_memberships(member, 1, &sids, &num);
572
573         if (!NT_STATUS_IS_OK(result))
574                 return result;
575
576         for (i=0; i<num; i++) {
577                 if (sid_compare(&sids[i], alias) == 0) {
578                         found = True;
579                         break;
580                 }
581         }
582
583         if (!found) {
584                 TALLOC_FREE(sids);
585                 return NT_STATUS_MEMBER_NOT_IN_ALIAS;
586         }
587
588         if (i < num)
589                 sids[i] = sids[num-1];
590
591         num -= 1;
592
593         sid_to_string(sid_string, member);
594         slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, sid_string);
595
596         if (num == 0)
597                 return tdb_delete_bystring(tdb, key) == 0 ?
598                         NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
599
600         member_string = SMB_STRDUP("");
601
602         if (member_string == NULL) {
603                 TALLOC_FREE(sids);
604                 return NT_STATUS_NO_MEMORY;
605         }
606
607         for (i=0; i<num; i++) {
608                 char *s = member_string;
609
610                 sid_to_string(sid_string, &sids[i]);
611                 asprintf(&member_string, "%s %s", s, sid_string);
612
613                 SAFE_FREE(s);
614                 if (member_string == NULL) {
615                         TALLOC_FREE(sids);
616                         return NT_STATUS_NO_MEMORY;
617                 }
618         }
619
620         dbuf = string_term_tdb_data(member_string);
621
622         result = tdb_store_bystring(tdb, key, dbuf, 0) == 0 ?
623                 NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
624
625         TALLOC_FREE(sids);
626         SAFE_FREE(member_string);
627
628         return result;
629 }
630
631
632 static const struct mapping_backend tdb_backend = {
633         .add_mapping_entry         = add_mapping_entry,
634         .get_group_map_from_sid    = get_group_map_from_sid,
635         .get_group_map_from_gid    = get_group_map_from_gid,
636         .get_group_map_from_ntname = get_group_map_from_ntname,
637         .group_map_remove          = group_map_remove,
638         .enum_group_mapping        = enum_group_mapping,
639         .one_alias_membership      = one_alias_membership,
640         .add_aliasmem              = add_aliasmem,
641         .del_aliasmem              = del_aliasmem,
642         .enum_aliasmem             = enum_aliasmem      
643 };
644
645 /*
646   initialise the tdb mapping backend
647  */
648 const struct mapping_backend *groupdb_tdb_init(void)
649 {
650         if (!init_group_mapping()) {
651                 DEBUG(0,("Failed to initialise tdb mapping backend\n"));
652                 return NULL;
653         }
654
655         return &tdb_backend;
656 }