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