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