Merge branch 'master' of ctdb into 'master' of samba
[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, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "passdb.h"
26 #include "groupdb/mapping.h"
27 #include "dbwrap/dbwrap.h"
28 #include "dbwrap/dbwrap_open.h"
29 #include "util_tdb.h"
30 #include "../libcli/security/security.h"
31 #include "groupdb/mapping_tdb.h"
32
33 static struct db_context *db; /* used for driver files */
34
35 static bool enum_group_mapping(const struct dom_sid *domsid,
36                                enum lsa_SidType sid_name_use,
37                                GROUP_MAP ***pp_rmap,
38                                size_t *p_num_entries,
39                                bool unix_only);
40 static bool group_map_remove(const struct dom_sid *sid);
41
42 static bool mapping_switch(const char *ldb_path);
43
44 /****************************************************************************
45  Open the group mapping tdb.
46 ****************************************************************************/
47 static bool init_group_mapping(void)
48 {
49         const char *ldb_path;
50
51         if (db != NULL) {
52                 return true;
53         }
54
55         db = db_open(NULL, state_path("group_mapping.tdb"), 0,
56                      TDB_DEFAULT, O_RDWR|O_CREAT, 0600,
57                      DBWRAP_LOCK_ORDER_1);
58         if (db == NULL) {
59                 DEBUG(0, ("Failed to open group mapping database: %s\n",
60                           strerror(errno)));
61                 return false;
62         }
63
64         ldb_path = state_path("group_mapping.ldb");
65         if (file_exist(ldb_path) && !mapping_switch(ldb_path)) {
66                 unlink(state_path("group_mapping.tdb"));
67                 return false;
68
69         } else {
70                 /* handle upgrade from old versions of the database */
71 #if 0 /* -- Needs conversion to dbwrap -- */
72                 const char *vstring = "INFO/version";
73                 int32 vers_id;
74                 GROUP_MAP *map_table = NULL;
75                 size_t num_entries = 0;
76
77                 /* handle a Samba upgrade */
78                 tdb_lock_bystring(tdb, vstring);
79
80                 /* Cope with byte-reversed older versions of the db. */
81                 vers_id = tdb_fetch_int32(tdb, vstring);
82                 if ((vers_id == DATABASE_VERSION_V1)
83                     || (IREV(vers_id) == DATABASE_VERSION_V1)) {
84                         /*
85                          * Written on a bigendian machine with old fetch_int
86                          * code. Save as le.
87                          */
88                         tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
89                         vers_id = DATABASE_VERSION_V2;
90                 }
91
92                 /* if its an unknown version we remove everthing in the db */
93
94                 if (vers_id != DATABASE_VERSION_V2) {
95                         tdb_wipe_all(tdb);
96                         tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
97                 }
98
99                 tdb_unlock_bystring(tdb, vstring);
100
101                 /* cleanup any map entries with a gid == -1 */
102
103                 if ( enum_group_mapping( NULL, SID_NAME_UNKNOWN, &map_table,
104                                          &num_entries, False ) ) {
105                         int i;
106
107                         for ( i=0; i<num_entries; i++ ) {
108                                 if ( map_table[i].gid == -1 ) {
109                                         group_map_remove( &map_table[i].sid );
110                                 }
111                         }
112
113                         SAFE_FREE( map_table );
114                 }
115 #endif
116         }
117         return true;
118 }
119
120 static char *group_mapping_key(TALLOC_CTX *mem_ctx, const struct dom_sid *sid)
121 {
122         char *sidstr, *result;
123
124         sidstr = sid_string_talloc(talloc_tos(), sid);
125         if (sidstr == NULL) {
126                 return NULL;
127         }
128
129         result = talloc_asprintf(mem_ctx, "%s%s", GROUP_PREFIX, sidstr);
130
131         TALLOC_FREE(sidstr);
132         return result;
133 }
134
135 /****************************************************************************
136 ****************************************************************************/
137 static bool add_mapping_entry(GROUP_MAP *map, int flag)
138 {
139         char *key, *buf;
140         int len;
141         NTSTATUS status;
142
143         key = group_mapping_key(talloc_tos(), &map->sid);
144         if (key == NULL) {
145                 return false;
146         }
147
148         len = tdb_pack(NULL, 0, "ddff",
149                 map->gid, map->sid_name_use, map->nt_name, map->comment);
150
151         buf = talloc_array(key, char, len);
152         if (!buf) {
153                 TALLOC_FREE(key);
154                 return false;
155         }
156         len = tdb_pack((uint8 *)buf, len, "ddff", map->gid,
157                        map->sid_name_use, map->nt_name, map->comment);
158
159         status = dbwrap_trans_store(
160                 db, string_term_tdb_data(key),
161                 make_tdb_data((uint8_t *)buf, len), TDB_REPLACE);
162
163         TALLOC_FREE(key);
164
165         return NT_STATUS_IS_OK(status);
166 }
167
168
169 /****************************************************************************
170  Return the sid and the type of the unix group.
171 ****************************************************************************/
172
173 static bool get_group_map_from_sid(struct dom_sid sid, GROUP_MAP *map)
174 {
175         TDB_DATA dbuf;
176         char *key;
177         int ret = 0;
178         NTSTATUS status;
179         fstring nt_name;
180         fstring comment;
181
182         /* the key is the SID, retrieving is direct */
183
184         key = group_mapping_key(talloc_tos(), &sid);
185         if (key == NULL) {
186                 return false;
187         }
188
189         status = dbwrap_fetch_bystring(db, key, key, &dbuf);
190         if (!NT_STATUS_IS_OK(status)) {
191                 TALLOC_FREE(key);
192                 return false;
193         }
194
195         ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
196                         &map->gid, &map->sid_name_use,
197                         &nt_name, &comment);
198
199         TALLOC_FREE(key);
200
201         if ( ret == -1 ) {
202                 DEBUG(3,("get_group_map_from_sid: tdb_unpack failure\n"));
203                 return false;
204         }
205
206         sid_copy(&map->sid, &sid);
207
208         map->nt_name = talloc_strdup(map, nt_name);
209         if (!map->nt_name) {
210                 return false;
211         }
212         map->comment = talloc_strdup(map, comment);
213         if (!map->comment) {
214                 return false;
215         }
216
217         return true;
218 }
219
220 static bool dbrec2map(const struct db_record *rec, GROUP_MAP *map)
221 {
222         TDB_DATA key = dbwrap_record_get_key(rec);
223         TDB_DATA value = dbwrap_record_get_value(rec);
224         int ret = 0;
225         fstring nt_name;
226         fstring comment;
227
228         if ((key.dsize < strlen(GROUP_PREFIX))
229             || (strncmp((char *)key.dptr, GROUP_PREFIX,
230                         GROUP_PREFIX_LEN) != 0)) {
231                 return False;
232         }
233
234         if (!string_to_sid(&map->sid, (const char *)key.dptr
235                            + GROUP_PREFIX_LEN)) {
236                 return False;
237         }
238
239         ret = tdb_unpack(value.dptr, value.dsize, "ddff",
240                           &map->gid, &map->sid_name_use,
241                           &nt_name, &comment);
242
243         if (ret == -1) {
244                 DEBUG(3, ("dbrec2map: tdb_unpack failure\n"));
245                 return false;
246         }
247
248         map->nt_name = talloc_strdup(map, nt_name);
249         if (!map->nt_name) {
250                 return false;
251         }
252         map->comment = talloc_strdup(map, comment);
253         if (!map->comment) {
254                 return false;
255         }
256
257         return true;
258 }
259
260 struct find_map_state {
261         bool found;
262         const char *name;       /* If != NULL, look for name */
263         gid_t gid;              /* valid iff name == NULL */
264         GROUP_MAP *map;
265 };
266
267 static int find_map(struct db_record *rec, void *private_data)
268 {
269         struct find_map_state *state = (struct find_map_state *)private_data;
270
271         if (!dbrec2map(rec, state->map)) {
272                 DEBUG(10, ("failed to unpack map\n"));
273                 return 0;
274         }
275
276         if (state->name != NULL) {
277                 if (strequal(state->name, state->map->nt_name)) {
278                         state->found = true;
279                         return 1;
280                 }
281         }
282         else {
283                 if (state->map->gid == state->gid) {
284                         state->found = true;
285                         return 1;
286                 }
287         }
288
289         return 0;
290 }
291
292 /****************************************************************************
293  Return the sid and the type of the unix group.
294 ****************************************************************************/
295
296 static bool get_group_map_from_gid(gid_t gid, GROUP_MAP *map)
297 {
298         struct find_map_state state;
299
300         state.found = false;
301         state.name = NULL;      /* Indicate we're looking for gid */
302         state.gid = gid;
303         state.map = map;
304
305         dbwrap_traverse_read(db, find_map, (void *)&state, NULL);
306
307         return state.found;
308 }
309
310 /****************************************************************************
311  Return the sid and the type of the unix group.
312 ****************************************************************************/
313
314 static bool get_group_map_from_ntname(const char *name, GROUP_MAP *map)
315 {
316         struct find_map_state state;
317
318         state.found = false;
319         state.name = name;
320         state.map = map;
321
322         dbwrap_traverse_read(db, find_map, (void *)&state, NULL);
323
324         return state.found;
325 }
326
327 /****************************************************************************
328  Remove a group mapping entry.
329 ****************************************************************************/
330
331 static bool group_map_remove(const struct dom_sid *sid)
332 {
333         char *key;
334         NTSTATUS status;
335
336         key = group_mapping_key(talloc_tos(), sid);
337         if (key == NULL) {
338                 return false;
339         }
340
341         status = dbwrap_trans_delete(db, string_term_tdb_data(key));
342
343         TALLOC_FREE(key);
344         return NT_STATUS_IS_OK(status);
345 }
346
347 /****************************************************************************
348  Enumerate the group mapping.
349 ****************************************************************************/
350
351 struct enum_map_state {
352         const struct dom_sid *domsid;
353         enum lsa_SidType sid_name_use;
354         bool unix_only;
355
356         size_t num_maps;
357         GROUP_MAP **maps;
358 };
359
360 static int collect_map(struct db_record *rec, void *private_data)
361 {
362         struct enum_map_state *state = (struct enum_map_state *)private_data;
363         GROUP_MAP *map;
364         GROUP_MAP **tmp;
365
366         map = talloc_zero(NULL, GROUP_MAP);
367         if (!map) {
368                 DEBUG(0, ("Unable to allocate group map!\n"));
369                 return 1;
370         }
371
372         if (!dbrec2map(rec, map)) {
373                 TALLOC_FREE(map);
374                 return 0;
375         }
376         /* list only the type or everything if UNKNOWN */
377         if (state->sid_name_use != SID_NAME_UNKNOWN
378             && state->sid_name_use != map->sid_name_use) {
379                 DEBUG(11,("enum_group_mapping: group %s is not of the "
380                           "requested type\n", map->nt_name));
381                 TALLOC_FREE(map);
382                 return 0;
383         }
384
385         if ((state->unix_only == ENUM_ONLY_MAPPED) && (map->gid == -1)) {
386                 DEBUG(11,("enum_group_mapping: group %s is non mapped\n",
387                           map->nt_name));
388                 TALLOC_FREE(map);
389                 return 0;
390         }
391
392         if ((state->domsid != NULL) &&
393             (dom_sid_compare_domain(state->domsid, &map->sid) != 0)) {
394                 DEBUG(11,("enum_group_mapping: group %s is not in domain\n",
395                           sid_string_dbg(&map->sid)));
396                 TALLOC_FREE(map);
397                 return 0;
398         }
399
400         tmp = talloc_realloc(NULL, state->maps, GROUP_MAP *,
401                                         state->num_maps + 1);
402         if (!tmp) {
403                 DEBUG(0,("enum_group_mapping: Unable to enlarge group "
404                          "map!\n"));
405                 TALLOC_FREE(map);
406                 return 1;
407         }
408
409         state->maps = tmp;
410         state->maps[state->num_maps] = talloc_move(state->maps, &map);
411         state->num_maps++;
412         return 0;
413 }
414
415 static bool enum_group_mapping(const struct dom_sid *domsid,
416                                enum lsa_SidType sid_name_use,
417                                GROUP_MAP ***pp_rmap,
418                                size_t *p_num_entries, bool unix_only)
419 {
420         struct enum_map_state state;
421         NTSTATUS status;
422
423         state.domsid = domsid;
424         state.sid_name_use = sid_name_use;
425         state.unix_only = unix_only;
426         state.num_maps = 0;
427         state.maps = NULL;
428
429         status = dbwrap_traverse_read(db, collect_map, (void *)&state, NULL);
430         if (!NT_STATUS_IS_OK(status)) {
431                 TALLOC_FREE(state.maps);
432                 return false;
433         }
434
435         *pp_rmap = state.maps;
436         *p_num_entries = state.num_maps;
437
438         return true;
439 }
440
441 /* This operation happens on session setup, so it should better be fast. We
442  * store a list of aliases a SID is member of hanging off MEMBEROF/SID. */
443
444 static NTSTATUS one_alias_membership(const struct dom_sid *member,
445                                struct dom_sid **sids, size_t *num)
446 {
447         fstring tmp;
448         fstring key;
449         char *string_sid;
450         TDB_DATA dbuf;
451         const char *p;
452         NTSTATUS status = NT_STATUS_OK;
453         TALLOC_CTX *frame = talloc_stackframe();
454
455         slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX,
456                  sid_to_fstring(tmp, member));
457
458         status = dbwrap_fetch_bystring(db, frame, key, &dbuf);
459         if (!NT_STATUS_IS_OK(status)) {
460                 TALLOC_FREE(frame);
461                 return NT_STATUS_OK;
462         }
463
464         p = (const char *)dbuf.dptr;
465
466         while (next_token_talloc(frame, &p, &string_sid, " ")) {
467                 struct dom_sid alias;
468                 uint32_t num_sids;
469
470                 if (!string_to_sid(&alias, string_sid))
471                         continue;
472
473                 num_sids = *num;
474                 status= add_sid_to_array_unique(NULL, &alias, sids, &num_sids);
475                 if (!NT_STATUS_IS_OK(status)) {
476                         goto done;
477                 }
478                 *num = num_sids;
479         }
480
481 done:
482         TALLOC_FREE(frame);
483         return status;
484 }
485
486 static NTSTATUS alias_memberships(const struct dom_sid *members, size_t num_members,
487                                   struct dom_sid **sids, size_t *num)
488 {
489         size_t i;
490
491         *num = 0;
492         *sids = NULL;
493
494         for (i=0; i<num_members; i++) {
495                 NTSTATUS status = one_alias_membership(&members[i], sids, num);
496                 if (!NT_STATUS_IS_OK(status))
497                         return status;
498         }
499         return NT_STATUS_OK;
500 }
501
502 static bool is_aliasmem(const struct dom_sid *alias, const struct dom_sid *member)
503 {
504         struct dom_sid *sids;
505         size_t i;
506         size_t num;
507
508         /* This feels the wrong way round, but the on-disk data structure
509          * dictates it this way. */
510         if (!NT_STATUS_IS_OK(alias_memberships(member, 1, &sids, &num)))
511                 return False;
512
513         for (i=0; i<num; i++) {
514                 if (dom_sid_compare(alias, &sids[i]) == 0) {
515                         TALLOC_FREE(sids);
516                         return True;
517                 }
518         }
519         TALLOC_FREE(sids);
520         return False;
521 }
522
523
524 static NTSTATUS add_aliasmem(const struct dom_sid *alias, const struct dom_sid *member)
525 {
526         GROUP_MAP *map;
527         char *key;
528         fstring string_sid;
529         char *new_memberstring;
530         struct db_record *rec;
531         NTSTATUS status;
532         TDB_DATA value;
533
534         map = talloc_zero(talloc_tos(), GROUP_MAP);
535         if (!map) {
536                 return NT_STATUS_NO_MEMORY;
537         }
538
539         if (!get_group_map_from_sid(*alias, map)) {
540                 TALLOC_FREE(map);
541                 return NT_STATUS_NO_SUCH_ALIAS;
542         }
543
544         if ((map->sid_name_use != SID_NAME_ALIAS) &&
545             (map->sid_name_use != SID_NAME_WKN_GRP)) {
546                 TALLOC_FREE(map);
547                 return NT_STATUS_NO_SUCH_ALIAS;
548         }
549
550         TALLOC_FREE(map);
551
552         if (is_aliasmem(alias, member))
553                 return NT_STATUS_MEMBER_IN_ALIAS;
554
555         sid_to_fstring(string_sid, member);
556
557         key = talloc_asprintf(talloc_tos(), "%s%s", MEMBEROF_PREFIX,
558                               string_sid);
559         if (key == NULL) {
560                 return NT_STATUS_NO_MEMORY;
561         }
562
563         if (dbwrap_transaction_start(db) != 0) {
564                 DEBUG(0, ("transaction_start failed\n"));
565                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
566         }
567
568         rec = dbwrap_fetch_locked(db, key, string_term_tdb_data(key));
569
570         if (rec == NULL) {
571                 DEBUG(10, ("fetch_lock failed\n"));
572                 TALLOC_FREE(key);
573                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
574                 goto cancel;
575         }
576
577         value = dbwrap_record_get_value(rec);
578
579         sid_to_fstring(string_sid, alias);
580
581         if (value.dptr != NULL) {
582                 new_memberstring = talloc_asprintf(
583                         key, "%s %s", (char *)(value.dptr), string_sid);
584         } else {
585                 new_memberstring = talloc_strdup(key, string_sid);
586         }
587
588         if (new_memberstring == NULL) {
589                 TALLOC_FREE(key);
590                 status = NT_STATUS_NO_MEMORY;
591                 goto cancel;
592         }
593
594         status = dbwrap_record_store(rec, string_term_tdb_data(new_memberstring), 0);
595
596         TALLOC_FREE(key);
597
598         if (!NT_STATUS_IS_OK(status)) {
599                 DEBUG(10, ("Could not store record: %s\n", nt_errstr(status)));
600                 goto cancel;
601         }
602
603         if (dbwrap_transaction_commit(db) != 0) {
604                 DEBUG(0, ("transaction_commit failed\n"));
605                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
606                 return status;
607         }
608
609         return NT_STATUS_OK;
610
611  cancel:
612         if (dbwrap_transaction_cancel(db) != 0) {
613                 smb_panic("transaction_cancel failed");
614         }
615
616         return status;
617 }
618
619 struct aliasmem_state {
620         TALLOC_CTX *mem_ctx;
621         const struct dom_sid *alias;
622         struct dom_sid **sids;
623         size_t *num;
624 };
625
626 static int collect_aliasmem(struct db_record *rec, void *priv)
627 {
628         struct aliasmem_state *state = (struct aliasmem_state *)priv;
629         const char *p;
630         char *alias_string;
631         TALLOC_CTX *frame;
632         TDB_DATA key = dbwrap_record_get_key(rec);
633         TDB_DATA value = dbwrap_record_get_value(rec);
634
635         if (strncmp((const char *)key.dptr, MEMBEROF_PREFIX,
636                     MEMBEROF_PREFIX_LEN) != 0)
637                 return 0;
638
639         p = (const char *)value.dptr;
640
641         frame = talloc_stackframe();
642
643         while (next_token_talloc(frame, &p, &alias_string, " ")) {
644                 struct dom_sid alias, member;
645                 const char *member_string;
646                 uint32_t num_sids;
647
648                 if (!string_to_sid(&alias, alias_string))
649                         continue;
650
651                 if (dom_sid_compare(state->alias, &alias) != 0)
652                         continue;
653
654                 /* Ok, we found the alias we're looking for in the membership
655                  * list currently scanned. The key represents the alias
656                  * member. Add that. */
657
658                 member_string = strchr((const char *)key.dptr, '/');
659
660                 /* Above we tested for MEMBEROF_PREFIX which includes the
661                  * slash. */
662
663                 SMB_ASSERT(member_string != NULL);
664                 member_string += 1;
665
666                 if (!string_to_sid(&member, member_string))
667                         continue;
668
669                 num_sids = *state->num;
670                 if (!NT_STATUS_IS_OK(add_sid_to_array(state->mem_ctx, &member,
671                                                       state->sids,
672                                                       &num_sids)))
673                 {
674                         /* talloc fail. */
675                         break;
676                 }
677                 *state->num = num_sids;
678         }
679
680         TALLOC_FREE(frame);
681         return 0;
682 }
683
684 static NTSTATUS enum_aliasmem(const struct dom_sid *alias, TALLOC_CTX *mem_ctx,
685                               struct dom_sid **sids, size_t *num)
686 {
687         GROUP_MAP *map;
688         struct aliasmem_state state;
689
690         map = talloc_zero(talloc_tos(), GROUP_MAP);
691         if (!map) {
692                 return NT_STATUS_NO_MEMORY;
693         }
694
695         if (!get_group_map_from_sid(*alias, map)) {
696                 TALLOC_FREE(map);
697                 return NT_STATUS_NO_SUCH_ALIAS;
698         }
699
700         if ((map->sid_name_use != SID_NAME_ALIAS) &&
701             (map->sid_name_use != SID_NAME_WKN_GRP)) {
702                 TALLOC_FREE(map);
703                 return NT_STATUS_NO_SUCH_ALIAS;
704         }
705
706         TALLOC_FREE(map);
707
708         *sids = NULL;
709         *num = 0;
710
711         state.alias = alias;
712         state.sids = sids;
713         state.num = num;
714         state.mem_ctx = mem_ctx;
715
716         dbwrap_traverse_read(db, collect_aliasmem, &state, NULL);
717         return NT_STATUS_OK;
718 }
719
720 static NTSTATUS del_aliasmem(const struct dom_sid *alias, const struct dom_sid *member)
721 {
722         NTSTATUS status;
723         struct dom_sid *sids;
724         size_t i, num;
725         bool found = False;
726         char *member_string;
727         char *key;
728         fstring sid_string;
729
730         if (dbwrap_transaction_start(db) != 0) {
731                 DEBUG(0, ("transaction_start failed\n"));
732                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
733         }
734
735         status = alias_memberships(member, 1, &sids, &num);
736
737         if (!NT_STATUS_IS_OK(status)) {
738                 goto cancel;
739         }
740
741         for (i=0; i<num; i++) {
742                 if (dom_sid_compare(&sids[i], alias) == 0) {
743                         found = True;
744                         break;
745                 }
746         }
747
748         if (!found) {
749                 TALLOC_FREE(sids);
750                 status = NT_STATUS_MEMBER_NOT_IN_ALIAS;
751                 goto cancel;
752         }
753
754         if (i < num)
755                 sids[i] = sids[num-1];
756
757         num -= 1;
758
759         sid_to_fstring(sid_string, member);
760
761         key = talloc_asprintf(sids, "%s%s", MEMBEROF_PREFIX, sid_string);
762         if (key == NULL) {
763                 TALLOC_FREE(sids);
764                 status = NT_STATUS_NO_MEMORY;
765                 goto cancel;
766         }
767
768         if (num == 0) {
769                 status = dbwrap_delete_bystring(db, key);
770                 goto commit;
771         }
772
773         member_string = talloc_strdup(sids, "");
774         if (member_string == NULL) {
775                 TALLOC_FREE(sids);
776                 status = NT_STATUS_NO_MEMORY;
777                 goto cancel;
778         }
779
780         for (i=0; i<num; i++) {
781
782                 sid_to_fstring(sid_string, &sids[i]);
783
784                 member_string = talloc_asprintf_append_buffer(
785                         member_string, " %s", sid_string);
786
787                 if (member_string == NULL) {
788                         TALLOC_FREE(sids);
789                         status = NT_STATUS_NO_MEMORY;
790                         goto cancel;
791                 }
792         }
793
794         status = dbwrap_store_bystring(
795                 db, key, string_term_tdb_data(member_string), 0);
796  commit:
797         TALLOC_FREE(sids);
798
799         if (!NT_STATUS_IS_OK(status)) {
800                 DEBUG(10, ("dbwrap_store_bystring failed: %s\n",
801                            nt_errstr(status)));
802                 goto cancel;
803         }
804
805         if (dbwrap_transaction_commit(db) != 0) {
806                 DEBUG(0, ("transaction_commit failed\n"));
807                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
808                 return status;
809         }
810
811         return NT_STATUS_OK;
812
813  cancel:
814         if (dbwrap_transaction_cancel(db) != 0) {
815                 smb_panic("transaction_cancel failed");
816         }
817         return status;
818 }
819
820
821 /* -- ldb->tdb switching code -------------------------------------------- */
822
823 /* change this if the data format ever changes */
824 #define LTDB_PACKING_FORMAT 0x26011967
825
826 /* old packing formats (not supported for now,
827  * it was never used for group mapping AFAIK) */
828 #define LTDB_PACKING_FORMAT_NODN 0x26011966
829
830 static unsigned int pull_uint32(uint8_t *p, int ofs)
831 {
832         p += ofs;
833         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
834 }
835
836 /*
837   unpack a ldb message from a linear buffer in TDB_DATA
838 */
839 static int convert_ldb_record(TDB_CONTEXT *ltdb, TDB_DATA key,
840                               TDB_DATA data, void *ptr)
841 {
842         TALLOC_CTX *tmp_ctx = talloc_tos();
843         GROUP_MAP *map = NULL;
844         uint8_t *p;
845         uint32_t format;
846         uint32_t num_el;
847         unsigned int remaining;
848         unsigned int i, j;
849         size_t len;
850         char *name;
851         char *val;
852         char *q;
853         uint32_t num_mem = 0;
854         struct dom_sid *members = NULL;
855
856         p = (uint8_t *)data.dptr;
857         if (data.dsize < 8) {
858                 errno = EIO;
859                 goto failed;
860         }
861
862         format = pull_uint32(p, 0);
863         num_el = pull_uint32(p, 4);
864         p += 8;
865
866         remaining = data.dsize - 8;
867
868         switch (format) {
869         case LTDB_PACKING_FORMAT:
870                 len = strnlen((char *)p, remaining);
871                 if (len == remaining) {
872                         errno = EIO;
873                         goto failed;
874                 }
875
876                 if (*p == '@') {
877                         /* ignore special LDB attributes */
878                         return 0;
879                 }
880
881                 if (strncmp((char *)p, "rid=", 4)) {
882                         /* unknown entry, ignore */
883                         DEBUG(3, ("Found unknown entry in group mapping "
884                                   "database named [%s]\n", (char *)p));
885                         return 0;
886                 }
887
888                 remaining -= len + 1;
889                 p += len + 1;
890                 break;
891
892         case LTDB_PACKING_FORMAT_NODN:
893         default:
894                 errno = EIO;
895                 goto failed;
896         }
897
898         if (num_el == 0) {
899                 /* bad entry, ignore */
900                 return 0;
901         }
902
903         if (num_el > remaining / 6) {
904                 errno = EIO;
905                 goto failed;
906         }
907
908         map = talloc_zero(NULL, GROUP_MAP);
909         if (!map) {
910                 errno = ENOMEM;
911                 goto failed;
912         }
913
914         for (i = 0; i < num_el; i++) {
915                 uint32_t num_vals;
916
917                 if (remaining < 10) {
918                         errno = EIO;
919                         goto failed;
920                 }
921                 len = strnlen((char *)p, remaining - 6);
922                 if (len == remaining - 6) {
923                         errno = EIO;
924                         goto failed;
925                 }
926                 name = talloc_strndup(tmp_ctx, (char *)p, len);
927                 if (name == NULL) {
928                         errno = ENOMEM;
929                         goto failed;
930                 }
931                 remaining -= len + 1;
932                 p += len + 1;
933
934                 num_vals = pull_uint32(p, 0);
935                 if (strcasecmp_m(name, "member") == 0) {
936                         num_mem = num_vals;
937                         members = talloc_array(tmp_ctx, struct dom_sid, num_mem);
938                         if (members == NULL) {
939                                 errno = ENOMEM;
940                                 goto failed;
941                         }
942                 } else if (num_vals != 1) {
943                         errno = EIO;
944                         goto failed;
945                 }
946
947                 p += 4;
948                 remaining -= 4;
949
950                 for (j = 0; j < num_vals; j++) {
951                         len = pull_uint32(p, 0);
952                         if (len > remaining-5) {
953                                 errno = EIO;
954                                 goto failed;
955                         }
956
957                         val = talloc_strndup(tmp_ctx, (char *)(p + 4), len);
958                         if (val == NULL) {
959                                 errno = ENOMEM;
960                                 goto failed;
961                         }
962
963                         remaining -= len+4+1;
964                         p += len+4+1;
965
966                         /* we ignore unknown or uninteresting attributes
967                          * (objectclass, etc.) */
968                         if (strcasecmp_m(name, "gidNumber") == 0) {
969                                 map->gid = strtoul(val, &q, 10);
970                                 if (*q) {
971                                         errno = EIO;
972                                         goto failed;
973                                 }
974                         } else if (strcasecmp_m(name, "sid") == 0) {
975                                 if (!string_to_sid(&map->sid, val)) {
976                                         errno = EIO;
977                                         goto failed;
978                                 }
979                         } else if (strcasecmp_m(name, "sidNameUse") == 0) {
980                                 map->sid_name_use = strtoul(val, &q, 10);
981                                 if (*q) {
982                                         errno = EIO;
983                                         goto failed;
984                                 }
985                         } else if (strcasecmp_m(name, "ntname") == 0) {
986                                 map->nt_name = talloc_strdup(map, val);
987                                 if (!map->nt_name) {
988                                         errno = ENOMEM;
989                                         goto failed;
990                                 }
991                         } else if (strcasecmp_m(name, "comment") == 0) {
992                                 map->comment = talloc_strdup(map, val);
993                                 if (!map->comment) {
994                                         errno = ENOMEM;
995                                         goto failed;
996                                 }
997                         } else if (strcasecmp_m(name, "member") == 0) {
998                                 if (!string_to_sid(&members[j], val)) {
999                                         errno = EIO;
1000                                         goto failed;
1001                                 }
1002                         }
1003
1004                         TALLOC_FREE(val);
1005                 }
1006
1007                 TALLOC_FREE(name);
1008         }
1009
1010         if (map->nt_name == NULL) {
1011                 errno = EIO;
1012                 goto failed;
1013         }
1014
1015         if (map->comment == NULL) {
1016                 map->comment = talloc_strdup(map, "");
1017         }
1018         if (map->comment == NULL) {
1019                 errno = ENOMEM;
1020                 goto failed;
1021         }
1022
1023         if (!add_mapping_entry(map, 0)) {
1024                 errno = EIO;
1025                 goto failed;
1026         }
1027
1028         if (num_mem) {
1029                 for (j = 0; j < num_mem; j++) {
1030                         NTSTATUS status;
1031                         status = add_aliasmem(&map->sid, &members[j]);
1032                         if (!NT_STATUS_IS_OK(status)) {
1033                                 errno = EIO;
1034                                 goto failed;
1035                         }
1036                 }
1037         }
1038
1039         if (remaining != 0) {
1040                 DEBUG(0, ("Errror: %d bytes unread in ltdb_unpack_data\n",
1041                           remaining));
1042         }
1043
1044         TALLOC_FREE(map);
1045         return 0;
1046
1047 failed:
1048         TALLOC_FREE(map);
1049         return -1;
1050 }
1051
1052 static bool mapping_switch(const char *ldb_path)
1053 {
1054         TDB_CONTEXT *ltdb;
1055         TALLOC_CTX *frame;
1056         char *new_path;
1057         int ret;
1058
1059         frame = talloc_stackframe();
1060
1061         ltdb = tdb_open_log(ldb_path, 0, TDB_DEFAULT, O_RDONLY, 0600);
1062         if (ltdb == NULL) goto failed;
1063
1064         /* ldb is just a very fancy tdb, read out raw data and perform
1065          * conversion */
1066         ret = tdb_traverse(ltdb, convert_ldb_record, NULL);
1067         if (ret < 0) goto failed;
1068
1069         if (ltdb) {
1070                 tdb_close(ltdb);
1071                 ltdb = NULL;
1072         }
1073
1074         /* now rename the old db out of the way */
1075         new_path = state_path("group_mapping.ldb.replaced");
1076         if (!new_path) {
1077                 goto failed;
1078         }
1079         if (rename(ldb_path, new_path) != 0) {
1080                 DEBUG(0,("Failed to rename old group mapping database\n"));
1081                 goto failed;
1082         }
1083         TALLOC_FREE(frame);
1084         return True;
1085
1086 failed:
1087         DEBUG(0, ("Failed to switch to tdb group mapping database\n"));
1088         if (ltdb) tdb_close(ltdb);
1089         TALLOC_FREE(frame);
1090         return False;
1091 }
1092
1093 static const struct mapping_backend tdb_backend = {
1094         .add_mapping_entry         = add_mapping_entry,
1095         .get_group_map_from_sid    = get_group_map_from_sid,
1096         .get_group_map_from_gid    = get_group_map_from_gid,
1097         .get_group_map_from_ntname = get_group_map_from_ntname,
1098         .group_map_remove          = group_map_remove,
1099         .enum_group_mapping        = enum_group_mapping,
1100         .one_alias_membership      = one_alias_membership,
1101         .add_aliasmem              = add_aliasmem,
1102         .del_aliasmem              = del_aliasmem,
1103         .enum_aliasmem             = enum_aliasmem      
1104 };
1105
1106 /*
1107   initialise the tdb mapping backend
1108  */
1109 const struct mapping_backend *groupdb_tdb_init(void)
1110 {
1111         if (!init_group_mapping()) {
1112                 DEBUG(0,("Failed to initialise tdb mapping backend\n"));
1113                 return NULL;
1114         }
1115
1116         return &tdb_backend;
1117 }