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