s3:idmap_tdb: fix hwm-handling to use uint32 consistently
[obnox/samba/samba-obnox.git] / source3 / winbindd / idmap_tdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    idmap TDB backend
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8    Copyright (C) Jeremy Allison 2006
9    Copyright (C) Simo Sorce 2003-2006
10    Copyright (C) Michael Adam 2009-2010
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "idmap.h"
30 #include "idmap_rw.h"
31 #include "dbwrap/dbwrap.h"
32 #include "dbwrap/dbwrap_open.h"
33 #include "../libcli/security/security.h"
34 #include "util_tdb.h"
35
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_IDMAP
38
39 /* idmap version determines auto-conversion - this is the database
40    structure version specifier. */
41
42 #define IDMAP_VERSION 2
43
44 struct idmap_tdb_context {
45         struct db_context *db;
46         struct idmap_rw_ops *rw_ops;
47 };
48
49 /* High water mark keys */
50 #define HWM_GROUP  "GROUP HWM"
51 #define HWM_USER   "USER HWM"
52
53 struct convert_fn_state {
54         struct db_context *db;
55         bool failed;
56 };
57
58 /*****************************************************************************
59  For idmap conversion: convert one record to new format
60  Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
61  instead of the SID.
62 *****************************************************************************/
63 static int convert_fn(struct db_record *rec, void *private_data)
64 {
65         struct winbindd_domain *domain;
66         char *p;
67         NTSTATUS status;
68         struct dom_sid sid;
69         uint32 rid;
70         fstring keystr;
71         fstring dom_name;
72         TDB_DATA key;
73         TDB_DATA key2;
74         TDB_DATA value;
75         struct convert_fn_state *s = (struct convert_fn_state *)private_data;
76
77         key = dbwrap_record_get_key(rec);
78
79         DEBUG(10,("Converting %s\n", (const char *)key.dptr));
80
81         p = strchr((const char *)key.dptr, '/');
82         if (!p)
83                 return 0;
84
85         *p = 0;
86         fstrcpy(dom_name, (const char *)key.dptr);
87         *p++ = '/';
88
89         domain = find_domain_from_name(dom_name);
90         if (domain == NULL) {
91                 /* We must delete the old record. */
92                 DEBUG(0,("Unable to find domain %s\n", dom_name ));
93                 DEBUG(0,("deleting record %s\n", (const char *)key.dptr ));
94
95                 status = dbwrap_record_delete(rec);
96                 if (!NT_STATUS_IS_OK(status)) {
97                         DEBUG(0, ("Unable to delete record %s:%s\n",
98                                 (const char *)key.dptr,
99                                 nt_errstr(status)));
100                         s->failed = true;
101                         return -1;
102                 }
103
104                 return 0;
105         }
106
107         rid = atoi(p);
108
109         sid_compose(&sid, &domain->sid, rid);
110
111         sid_to_fstring(keystr, &sid);
112         key2 = string_term_tdb_data(keystr);
113
114         value = dbwrap_record_get_value(rec);
115
116         status = dbwrap_store(s->db, key2, value, TDB_INSERT);
117         if (!NT_STATUS_IS_OK(status)) {
118                 DEBUG(0,("Unable to add record %s:%s\n",
119                         (const char *)key2.dptr,
120                         nt_errstr(status)));
121                 s->failed = true;
122                 return -1;
123         }
124
125         status = dbwrap_store(s->db, value, key2, TDB_REPLACE);
126         if (!NT_STATUS_IS_OK(status)) {
127                 DEBUG(0,("Unable to update record %s:%s\n",
128                         (const char *)value.dptr,
129                         nt_errstr(status)));
130                 s->failed = true;
131                 return -1;
132         }
133
134         status = dbwrap_record_delete(rec);
135         if (!NT_STATUS_IS_OK(status)) {
136                 DEBUG(0,("Unable to delete record %s:%s\n",
137                         (const char *)key.dptr,
138                         nt_errstr(status)));
139                 s->failed = true;
140                 return -1;
141         }
142
143         return 0;
144 }
145
146 /*****************************************************************************
147  Convert the idmap database from an older version.
148 *****************************************************************************/
149
150 static bool idmap_tdb_upgrade(struct idmap_domain *dom, struct db_context *db)
151 {
152         int32 vers;
153         bool bigendianheader;
154         struct convert_fn_state s;
155         NTSTATUS status;
156
157 #if BUILD_TDB2
158         /* If we are bigendian, tdb is bigendian if NOT converted. */
159         union {
160                 uint16 large;
161                 unsigned char small[2];
162         } u;
163         u.large = 0x0102;
164         if (u.small[0] == 0x01)
165                 bigendianheader = !(dbwrap_get_flags(db) & TDB_CONVERT);
166         else {
167                 assert(u.small[0] == 0x02);
168                 bigendianheader = (dbwrap_get_flags(db) & TDB_CONVERT);
169         }
170 #else
171         bigendianheader = (dbwrap_get_flags(db) & TDB_BIGENDIAN) ? True : False;
172 #endif
173         DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
174
175         vers = dbwrap_fetch_int32(db, "IDMAP_VERSION");
176
177         if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
178                 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
179                 /*
180                  * high and low records were created on a
181                  * big endian machine and will need byte-reversing.
182                  */
183
184                 int32 wm;
185
186                 wm = dbwrap_fetch_int32(db, HWM_USER);
187
188                 if (wm != -1) {
189                         wm = IREV(wm);
190                 }  else {
191                         wm = dom->low_id;
192                 }
193
194                 if (dbwrap_store_int32(db, HWM_USER, wm) == -1) {
195                         DEBUG(0, ("Unable to byteswap user hwm in idmap database\n"));
196                         return False;
197                 }
198
199                 wm = dbwrap_fetch_int32(db, HWM_GROUP);
200                 if (wm != -1) {
201                         wm = IREV(wm);
202                 } else {
203                         wm = dom->low_id;
204                 }
205
206                 if (dbwrap_store_int32(db, HWM_GROUP, wm) == -1) {
207                         DEBUG(0, ("Unable to byteswap group hwm in idmap database\n"));
208                         return False;
209                 }
210         }
211
212         s.db = db;
213         s.failed = false;
214
215         /* the old format stored as DOMAIN/rid - now we store the SID direct */
216         status = dbwrap_traverse(db, convert_fn, &s, NULL);
217
218         if (!NT_STATUS_IS_OK(status)) {
219                 DEBUG(0, ("Database traverse failed during conversion\n"));
220                 return false;
221         }
222
223         if (s.failed) {
224                 DEBUG(0, ("Problem during conversion\n"));
225                 return False;
226         }
227
228         if (dbwrap_store_int32(db, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
229                 DEBUG(0, ("Unable to store idmap version in database\n"));
230                 return False;
231         }
232
233         return True;
234 }
235
236 static NTSTATUS idmap_tdb_init_hwm(struct idmap_domain *dom)
237 {
238         int ret;
239         uint32_t low_uid;
240         uint32_t low_gid;
241         bool update_uid = false;
242         bool update_gid = false;
243         struct idmap_tdb_context *ctx;
244         bool status;
245
246         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
247
248         status = dbwrap_fetch_uint32(ctx->db, HWM_USER, &low_uid);
249         if (!status || low_uid < dom->low_id) {
250                 update_uid = true;
251         }
252
253         status = dbwrap_fetch_uint32(ctx->db, HWM_GROUP, &low_gid);
254         if (!status || low_gid < dom->low_id) {
255                 update_gid = true;
256         }
257
258         if (!update_uid && !update_gid) {
259                 return NT_STATUS_OK;
260         }
261
262         if (dbwrap_transaction_start(ctx->db) != 0) {
263                 DEBUG(0, ("Unable to start upgrade transaction!\n"));
264                 return NT_STATUS_INTERNAL_DB_ERROR;
265         }
266
267         if (update_uid) {
268                 ret = dbwrap_store_uint32(ctx->db, HWM_USER, dom->low_id);
269                 if (ret == -1) {
270                         dbwrap_transaction_cancel(ctx->db);
271                         DEBUG(0, ("Unable to initialise user hwm in idmap "
272                                   "database\n"));
273                         return NT_STATUS_INTERNAL_DB_ERROR;
274                 }
275         }
276
277         if (update_gid) {
278                 ret = dbwrap_store_uint32(ctx->db, HWM_GROUP, dom->low_id);
279                 if (ret == -1) {
280                         dbwrap_transaction_cancel(ctx->db);
281                         DEBUG(0, ("Unable to initialise group hwm in idmap "
282                                   "database\n"));
283                         return NT_STATUS_INTERNAL_DB_ERROR;
284                 }
285         }
286
287         if (dbwrap_transaction_commit(ctx->db) != 0) {
288                 DEBUG(0, ("Unable to commit upgrade transaction!\n"));
289                 return NT_STATUS_INTERNAL_DB_ERROR;
290         }
291
292         return NT_STATUS_OK;
293 }
294
295 static NTSTATUS idmap_tdb_open_db(struct idmap_domain *dom)
296 {
297         NTSTATUS ret;
298         TALLOC_CTX *mem_ctx;
299         char *tdbfile = NULL;
300         struct db_context *db = NULL;
301         int32_t version;
302         bool config_error = false;
303         struct idmap_tdb_context *ctx;
304
305         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
306
307         if (ctx->db) {
308                 /* it is already open */
309                 return NT_STATUS_OK;
310         }
311
312         /* use our own context here */
313         mem_ctx = talloc_stackframe();
314
315         /* use the old database if present */
316         tdbfile = state_path("winbindd_idmap.tdb");
317         if (!tdbfile) {
318                 DEBUG(0, ("Out of memory!\n"));
319                 ret = NT_STATUS_NO_MEMORY;
320                 goto done;
321         }
322
323         DEBUG(10,("Opening tdbfile %s\n", tdbfile ));
324
325         /* Open idmap repository */
326         db = db_open(mem_ctx, tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644);
327         if (!db) {
328                 DEBUG(0, ("Unable to open idmap database\n"));
329                 ret = NT_STATUS_UNSUCCESSFUL;
330                 goto done;
331         }
332
333         /* check against earlier versions */
334         version = dbwrap_fetch_int32(db, "IDMAP_VERSION");
335         if (version != IDMAP_VERSION) {
336                 if (config_error) {
337                         DEBUG(0,("Upgrade of IDMAP_VERSION from %d to %d is not "
338                                  "possible with incomplete configuration\n",
339                                  version, IDMAP_VERSION));
340                         ret = NT_STATUS_UNSUCCESSFUL;
341                         goto done;
342                 }
343                 if (dbwrap_transaction_start(db) != 0) {
344                         DEBUG(0, ("Unable to start upgrade transaction!\n"));
345                         ret = NT_STATUS_INTERNAL_DB_ERROR;
346                         goto done;
347                 }
348
349                 if (!idmap_tdb_upgrade(dom, db)) {
350                         dbwrap_transaction_cancel(db);
351                         DEBUG(0, ("Unable to open idmap database, it's in an old format, and upgrade failed!\n"));
352                         ret = NT_STATUS_INTERNAL_DB_ERROR;
353                         goto done;
354                 }
355
356                 if (dbwrap_transaction_commit(db) != 0) {
357                         DEBUG(0, ("Unable to commit upgrade transaction!\n"));
358                         ret = NT_STATUS_INTERNAL_DB_ERROR;
359                         goto done;
360                 }
361         }
362
363         ctx->db = talloc_move(ctx, &db);
364
365         ret = idmap_tdb_init_hwm(dom);
366
367 done:
368         talloc_free(mem_ctx);
369         return ret;
370 }
371
372 /**********************************************************************
373  IDMAP ALLOC TDB BACKEND
374 **********************************************************************/
375
376 /**********************************
377  Allocate a new id. 
378 **********************************/
379
380 struct idmap_tdb_allocate_id_context {
381         const char *hwmkey;
382         const char *hwmtype;
383         uint32_t high_hwm;
384         uint32_t hwm;
385 };
386
387 static NTSTATUS idmap_tdb_allocate_id_action(struct db_context *db,
388                                              void *private_data)
389 {
390         NTSTATUS ret;
391         struct idmap_tdb_allocate_id_context *state;
392         uint32_t hwm;
393         bool ret2;
394
395         state = (struct idmap_tdb_allocate_id_context *)private_data;
396
397         ret2 = dbwrap_fetch_uint32(db, state->hwmkey, &hwm);
398         if (!ret2) {
399                 ret = NT_STATUS_INTERNAL_DB_ERROR;
400                 goto done;
401         }
402
403         /* check it is in the range */
404         if (hwm > state->high_hwm) {
405                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
406                           state->hwmtype, (unsigned long)state->high_hwm));
407                 ret = NT_STATUS_UNSUCCESSFUL;
408                 goto done;
409         }
410
411         /* fetch a new id and increment it */
412         ret = dbwrap_trans_change_uint32_atomic(db, state->hwmkey, &hwm, 1);
413         if (!NT_STATUS_IS_OK(ret)) {
414                 DEBUG(0, ("Fatal error while fetching a new %s value: %s\n!",
415                           state->hwmtype, nt_errstr(ret)));
416                 goto done;
417         }
418
419         /* recheck it is in the range */
420         if (hwm > state->high_hwm) {
421                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
422                           state->hwmtype, (unsigned long)state->high_hwm));
423                 ret = NT_STATUS_UNSUCCESSFUL;
424                 goto done;
425         }
426
427         ret = NT_STATUS_OK;
428         state->hwm = hwm;
429
430 done:
431         return ret;
432 }
433
434 static NTSTATUS idmap_tdb_allocate_id(struct idmap_domain *dom,
435                                       struct unixid *xid)
436 {
437         const char *hwmkey;
438         const char *hwmtype;
439         uint32_t high_hwm;
440         uint32_t hwm = 0;
441         NTSTATUS status;
442         struct idmap_tdb_allocate_id_context state;
443         struct idmap_tdb_context *ctx;
444
445         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
446
447         /* Get current high water mark */
448         switch (xid->type) {
449
450         case ID_TYPE_UID:
451                 hwmkey = HWM_USER;
452                 hwmtype = "UID";
453                 break;
454
455         case ID_TYPE_GID:
456                 hwmkey = HWM_GROUP;
457                 hwmtype = "GID";
458                 break;
459
460         default:
461                 DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
462                 return NT_STATUS_INVALID_PARAMETER;
463         }
464
465         high_hwm = dom->high_id;
466
467         state.hwm = hwm;
468         state.high_hwm = high_hwm;
469         state.hwmtype = hwmtype;
470         state.hwmkey = hwmkey;
471
472         status = dbwrap_trans_do(ctx->db, idmap_tdb_allocate_id_action,
473                                  &state);
474
475         if (NT_STATUS_IS_OK(status)) {
476                 xid->id = state.hwm;
477                 DEBUG(10,("New %s = %d\n", hwmtype, state.hwm));
478         } else {
479                 DEBUG(1, ("Error allocating a new %s\n", hwmtype));
480         }
481
482         return status;
483 }
484
485 /**
486  * Allocate a new unix-ID.
487  * For now this is for the default idmap domain only.
488  * Should be extended later on.
489  */
490 static NTSTATUS idmap_tdb_get_new_id(struct idmap_domain *dom,
491                                      struct unixid *id)
492 {
493         NTSTATUS ret;
494
495         if (!strequal(dom->name, "*")) {
496                 DEBUG(3, ("idmap_tdb_get_new_id: "
497                           "Refusing allocation of a new unixid for domain'%s'. "
498                           "Currently only supported for the default "
499                           "domain \"*\".\n",
500                            dom->name));
501                 return NT_STATUS_NOT_IMPLEMENTED;
502         }
503
504         ret = idmap_tdb_allocate_id(dom, id);
505
506         return ret;
507 }
508
509 /**********************************************************************
510  IDMAP MAPPING TDB BACKEND
511 **********************************************************************/
512
513 /*****************************
514  Initialise idmap database. 
515 *****************************/
516
517 static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom,
518                                       const struct id_map *map);
519
520 static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom)
521 {
522         NTSTATUS ret;
523         struct idmap_tdb_context *ctx;
524
525         DEBUG(10, ("idmap_tdb_db_init called for domain '%s'\n", dom->name));
526
527         ctx = talloc_zero(dom, struct idmap_tdb_context);
528         if ( ! ctx) {
529                 DEBUG(0, ("Out of memory!\n"));
530                 return NT_STATUS_NO_MEMORY;
531         }
532
533         /* load backend specific configuration here: */
534 #if 0
535         if (strequal(dom->name, "*")) {
536         } else {
537         }
538 #endif
539
540         ctx->rw_ops = talloc_zero(ctx, struct idmap_rw_ops);
541         if (ctx->rw_ops == NULL) {
542                 DEBUG(0, ("Out of memory!\n"));
543                 ret = NT_STATUS_NO_MEMORY;
544                 goto failed;
545         }
546
547         ctx->rw_ops->get_new_id = idmap_tdb_get_new_id;
548         ctx->rw_ops->set_mapping = idmap_tdb_set_mapping;
549
550         dom->private_data = ctx;
551
552         ret = idmap_tdb_open_db(dom);
553         if ( ! NT_STATUS_IS_OK(ret)) {
554                 goto failed;
555         }
556
557         return NT_STATUS_OK;
558
559 failed:
560         talloc_free(ctx);
561         return ret;
562 }
563
564
565 /**
566  * store a mapping in the database
567  */
568
569 struct idmap_tdb_set_mapping_context {
570         const char *ksidstr;
571         const char *kidstr;
572 };
573
574 static NTSTATUS idmap_tdb_set_mapping_action(struct db_context *db,
575                                              void *private_data)
576 {
577         NTSTATUS ret;
578         struct idmap_tdb_set_mapping_context *state;
579
580         state = (struct idmap_tdb_set_mapping_context *)private_data;
581
582         DEBUG(10, ("Storing %s <-> %s map\n", state->ksidstr, state->kidstr));
583
584         ret = dbwrap_store_bystring(db, state->ksidstr,
585                                     string_term_tdb_data(state->kidstr),
586                                     TDB_REPLACE);
587         if (!NT_STATUS_IS_OK(ret)) {
588                 DEBUG(0, ("Error storing SID -> ID (%s -> %s): %s\n",
589                           state->ksidstr, state->kidstr, nt_errstr(ret)));
590                 goto done;
591         }
592
593         ret = dbwrap_store_bystring(db, state->kidstr,
594                                     string_term_tdb_data(state->ksidstr),
595                                     TDB_REPLACE);
596         if (!NT_STATUS_IS_OK(ret)) {
597                 DEBUG(0, ("Error storing ID -> SID (%s -> %s): %s\n",
598                           state->kidstr, state->ksidstr, nt_errstr(ret)));
599                 goto done;
600         }
601
602         DEBUG(10,("Stored %s <-> %s\n", state->ksidstr, state->kidstr));
603         ret = NT_STATUS_OK;
604
605 done:
606         return ret;
607 }
608
609 static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom,
610                                       const struct id_map *map)
611 {
612         struct idmap_tdb_context *ctx;
613         NTSTATUS ret;
614         char *ksidstr, *kidstr;
615         struct idmap_tdb_set_mapping_context state;
616
617         if (!map || !map->sid) {
618                 return NT_STATUS_INVALID_PARAMETER;
619         }
620
621         ksidstr = kidstr = NULL;
622
623         /* TODO: should we filter a set_mapping using low/high filters ? */
624
625         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
626
627         switch (map->xid.type) {
628
629         case ID_TYPE_UID:
630                 kidstr = talloc_asprintf(ctx, "UID %lu",
631                                          (unsigned long)map->xid.id);
632                 break;
633
634         case ID_TYPE_GID:
635                 kidstr = talloc_asprintf(ctx, "GID %lu",
636                                          (unsigned long)map->xid.id);
637                 break;
638
639         default:
640                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
641                 return NT_STATUS_INVALID_PARAMETER;
642         }
643
644         if (kidstr == NULL) {
645                 DEBUG(0, ("ERROR: Out of memory!\n"));
646                 ret = NT_STATUS_NO_MEMORY;
647                 goto done;
648         }
649
650         ksidstr = sid_string_talloc(ctx, map->sid);
651         if (ksidstr == NULL) {
652                 DEBUG(0, ("Out of memory!\n"));
653                 ret = NT_STATUS_NO_MEMORY;
654                 goto done;
655         }
656
657         state.ksidstr = ksidstr;
658         state.kidstr = kidstr;
659
660         ret = dbwrap_trans_do(ctx->db, idmap_tdb_set_mapping_action, &state);
661
662 done:
663         talloc_free(ksidstr);
664         talloc_free(kidstr);
665         return ret;
666 }
667
668 /**
669  * Create a new mapping for an unmapped SID, also allocating a new ID.
670  * This should be run inside a transaction.
671  *
672  * TODO:
673  * Properly integrate this with multi domain idmap config:
674  * Currently, the allocator is default-config only.
675  */
676 static NTSTATUS idmap_tdb_new_mapping(struct idmap_domain *dom, struct id_map *map)
677 {
678         NTSTATUS ret;
679         struct idmap_tdb_context *ctx;
680
681         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
682
683         ret = idmap_rw_new_mapping(dom, ctx->rw_ops, map);
684
685         return ret;
686 }
687
688
689 /**********************************
690  Single id to sid lookup function. 
691 **********************************/
692
693 static NTSTATUS idmap_tdb_id_to_sid(struct idmap_domain *dom, struct id_map *map)
694 {
695         NTSTATUS ret;
696         TDB_DATA data;
697         char *keystr;
698         struct idmap_tdb_context *ctx;
699
700         if (!dom || !map) {
701                 return NT_STATUS_INVALID_PARAMETER;
702         }
703
704         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
705
706         /* apply filters before checking */
707         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
708                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
709                                 map->xid.id, dom->low_id, dom->high_id));
710                 return NT_STATUS_NONE_MAPPED;
711         }
712
713         switch (map->xid.type) {
714
715         case ID_TYPE_UID:
716                 keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
717                 break;
718
719         case ID_TYPE_GID:
720                 keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
721                 break;
722
723         default:
724                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
725                 return NT_STATUS_INVALID_PARAMETER;
726         }
727
728         /* final SAFE_FREE safe */
729         data.dptr = NULL;
730
731         if (keystr == NULL) {
732                 DEBUG(0, ("Out of memory!\n"));
733                 ret = NT_STATUS_NO_MEMORY;
734                 goto done;
735         }
736
737         DEBUG(10,("Fetching record %s\n", keystr));
738
739         /* Check if the mapping exists */
740         ret = dbwrap_fetch_bystring(ctx->db, NULL, keystr, &data);
741
742         if (!NT_STATUS_IS_OK(ret)) {
743                 DEBUG(10,("Record %s not found\n", keystr));
744                 ret = NT_STATUS_NONE_MAPPED;
745                 goto done;
746         }
747
748         if (!string_to_sid(map->sid, (const char *)data.dptr)) {
749                 DEBUG(10,("INVALID SID (%s) in record %s\n",
750                         (const char *)data.dptr, keystr));
751                 ret = NT_STATUS_INTERNAL_DB_ERROR;
752                 goto done;
753         }
754
755         DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr));
756         ret = NT_STATUS_OK;
757
758 done:
759         talloc_free(data.dptr);
760         talloc_free(keystr);
761         return ret;
762 }
763
764 /**********************************
765  Single sid to id lookup function. 
766 **********************************/
767
768 static NTSTATUS idmap_tdb_sid_to_id(struct idmap_domain *dom, struct id_map *map)
769 {
770         NTSTATUS ret;
771         TDB_DATA data;
772         char *keystr;
773         unsigned long rec_id = 0;
774         struct idmap_tdb_context *ctx;
775         TALLOC_CTX *tmp_ctx = talloc_stackframe();
776
777         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
778
779         keystr = sid_string_talloc(tmp_ctx, map->sid);
780         if (keystr == NULL) {
781                 DEBUG(0, ("Out of memory!\n"));
782                 ret = NT_STATUS_NO_MEMORY;
783                 goto done;
784         }
785
786         DEBUG(10,("Fetching record %s\n", keystr));
787
788         /* Check if sid is present in database */
789         ret = dbwrap_fetch_bystring(ctx->db, tmp_ctx, keystr, &data);
790         if (!NT_STATUS_IS_OK(ret)) {
791                 DEBUG(10,("Record %s not found\n", keystr));
792                 ret = NT_STATUS_NONE_MAPPED;
793                 goto done;
794         }
795
796         /* What type of record is this ? */
797         if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
798                 map->xid.id = rec_id;
799                 map->xid.type = ID_TYPE_UID;
800                 DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr ));
801                 ret = NT_STATUS_OK;
802
803         } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
804                 map->xid.id = rec_id;
805                 map->xid.type = ID_TYPE_GID;
806                 DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr ));
807                 ret = NT_STATUS_OK;
808
809         } else { /* Unknown record type ! */
810                 DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr));
811                 ret = NT_STATUS_INTERNAL_DB_ERROR;
812                 goto done;
813         }
814
815         /* apply filters before returning result */
816         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
817                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
818                                 map->xid.id, dom->low_id, dom->high_id));
819                 ret = NT_STATUS_NONE_MAPPED;
820         }
821
822 done:
823         talloc_free(tmp_ctx);
824         return ret;
825 }
826
827 /**********************************
828  lookup a set of unix ids. 
829 **********************************/
830
831 static NTSTATUS idmap_tdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
832 {
833         NTSTATUS ret;
834         int i;
835
836         /* initialize the status to avoid suprise */
837         for (i = 0; ids[i]; i++) {
838                 ids[i]->status = ID_UNKNOWN;
839         }
840
841         for (i = 0; ids[i]; i++) {
842                 ret = idmap_tdb_id_to_sid(dom, ids[i]);
843                 if ( ! NT_STATUS_IS_OK(ret)) {
844
845                         /* if it is just a failed mapping continue */
846                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
847
848                                 /* make sure it is marked as unmapped */
849                                 ids[i]->status = ID_UNMAPPED;
850                                 continue;
851                         }
852
853                         /* some fatal error occurred, return immediately */
854                         goto done;
855                 }
856
857                 /* all ok, id is mapped */
858                 ids[i]->status = ID_MAPPED;
859         }
860
861         ret = NT_STATUS_OK;
862
863 done:
864         return ret;
865 }
866
867 /**********************************
868  lookup a set of sids. 
869 **********************************/
870
871 struct idmap_tdb_sids_to_unixids_context {
872         struct idmap_domain *dom;
873         struct id_map **ids;
874         bool allocate_unmapped;
875 };
876
877 static NTSTATUS idmap_tdb_sids_to_unixids_action(struct db_context *db,
878                                                  void *private_data)
879 {
880         struct idmap_tdb_sids_to_unixids_context *state;
881         int i;
882         NTSTATUS ret = NT_STATUS_OK;
883
884         state = (struct idmap_tdb_sids_to_unixids_context *)private_data;
885
886         DEBUG(10, ("idmap_tdb_sids_to_unixids_action: "
887                    " domain: [%s], allocate: %s\n",
888                    state->dom->name,
889                    state->allocate_unmapped ? "yes" : "no"));
890
891         for (i = 0; state->ids[i]; i++) {
892                 if ((state->ids[i]->status == ID_UNKNOWN) ||
893                     /* retry if we could not map in previous run: */
894                     (state->ids[i]->status == ID_UNMAPPED))
895                 {
896                         NTSTATUS ret2;
897
898                         ret2 = idmap_tdb_sid_to_id(state->dom, state->ids[i]);
899                         if (!NT_STATUS_IS_OK(ret2)) {
900
901                                 /* if it is just a failed mapping, continue */
902                                 if (NT_STATUS_EQUAL(ret2, NT_STATUS_NONE_MAPPED)) {
903
904                                         /* make sure it is marked as unmapped */
905                                         state->ids[i]->status = ID_UNMAPPED;
906                                         ret = STATUS_SOME_UNMAPPED;
907                                 } else {
908                                         /* some fatal error occurred, return immediately */
909                                         ret = ret2;
910                                         goto done;
911                                 }
912                         } else {
913                                 /* all ok, id is mapped */
914                                 state->ids[i]->status = ID_MAPPED;
915                         }
916                 }
917
918                 if ((state->ids[i]->status == ID_UNMAPPED) &&
919                     state->allocate_unmapped)
920                 {
921                         ret = idmap_tdb_new_mapping(state->dom, state->ids[i]);
922                         if (!NT_STATUS_IS_OK(ret)) {
923                                 goto done;
924                         }
925                 }
926         }
927
928 done:
929         return ret;
930 }
931
932 static NTSTATUS idmap_tdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
933 {
934         struct idmap_tdb_context *ctx;
935         NTSTATUS ret;
936         int i;
937         struct idmap_tdb_sids_to_unixids_context state;
938
939         /* initialize the status to avoid suprise */
940         for (i = 0; ids[i]; i++) {
941                 ids[i]->status = ID_UNKNOWN;
942         }
943
944         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
945
946         state.dom = dom;
947         state.ids = ids;
948         state.allocate_unmapped = false;
949
950         ret = idmap_tdb_sids_to_unixids_action(ctx->db, &state);
951
952         if (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED) && !dom->read_only) {
953                 state.allocate_unmapped = true;
954                 ret = dbwrap_trans_do(ctx->db,
955                                       idmap_tdb_sids_to_unixids_action,
956                                       &state);
957         }
958
959         return ret;
960 }
961
962
963 /**********************************
964  Close the idmap tdb instance
965 **********************************/
966
967 static struct idmap_methods db_methods = {
968         .init = idmap_tdb_db_init,
969         .unixids_to_sids = idmap_tdb_unixids_to_sids,
970         .sids_to_unixids = idmap_tdb_sids_to_unixids,
971         .allocate_id = idmap_tdb_get_new_id,
972 };
973
974 NTSTATUS idmap_tdb_init(void)
975 {
976         DEBUG(10, ("calling idmap_tdb_init\n"));
977
978         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
979 }