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