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