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