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