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