Back-port of Volkers fix.
[samba.git] / source / nsswitch / 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    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_IDMAP
31
32 /* High water mark keys */
33 #define HWM_GROUP  "GROUP HWM"
34 #define HWM_USER   "USER HWM"
35
36 static struct idmap_tdb_state {
37
38         /* User and group id pool */
39         uid_t low_uid, high_uid;               /* Range of uids to allocate */
40         gid_t low_gid, high_gid;               /* Range of gids to allocate */
41
42 } idmap_tdb_state;
43
44 /*****************************************************************************
45  For idmap conversion: convert one record to new format
46  Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
47  instead of the SID.
48 *****************************************************************************/
49 static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state)
50 {
51         struct winbindd_domain *domain;
52         char *p;
53         DOM_SID sid;
54         uint32 rid;
55         fstring keystr;
56         fstring dom_name;
57         TDB_DATA key2;
58         BOOL *failed = (BOOL *)state;
59
60         DEBUG(10,("Converting %s\n", key.dptr));
61
62         p = strchr(key.dptr, '/');
63         if (!p)
64                 return 0;
65
66         *p = 0;
67         fstrcpy(dom_name, key.dptr);
68         *p++ = '/';
69
70         domain = find_domain_from_name(dom_name);
71         if (domain == NULL) {
72                 /* We must delete the old record. */
73                 DEBUG(0,("Unable to find domain %s\n", dom_name ));
74                 DEBUG(0,("deleting record %s\n", key.dptr ));
75
76                 if (tdb_delete(tdb, key) != 0) {
77                         DEBUG(0, ("Unable to delete record %s\n", key.dptr));
78                         *failed = True;
79                         return -1;
80                 }
81
82                 return 0;
83         }
84
85         rid = atoi(p);
86
87         sid_copy(&sid, &domain->sid);
88         sid_append_rid(&sid, rid);
89
90         sid_to_string(keystr, &sid);
91         key2.dptr = keystr;
92         key2.dsize = strlen(keystr) + 1;
93
94         if (tdb_store(tdb, key2, data, TDB_INSERT) != 0) {
95                 DEBUG(0,("Unable to add record %s\n", key2.dptr ));
96                 *failed = True;
97                 return -1;
98         }
99
100         if (tdb_store(tdb, data, key2, TDB_REPLACE) != 0) {
101                 DEBUG(0,("Unable to update record %s\n", data.dptr ));
102                 *failed = True;
103                 return -1;
104         }
105
106         if (tdb_delete(tdb, key) != 0) {
107                 DEBUG(0,("Unable to delete record %s\n", key.dptr ));
108                 *failed = True;
109                 return -1;
110         }
111
112         return 0;
113 }
114
115 /*****************************************************************************
116  Convert the idmap database from an older version.
117 *****************************************************************************/
118
119 static BOOL idmap_tdb_convert(const char *idmap_name)
120 {
121         int32 vers;
122         BOOL bigendianheader;
123         BOOL failed = False;
124         TDB_CONTEXT *idmap_tdb;
125
126         if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
127                                         TDB_DEFAULT, O_RDWR,
128                                         0600))) {
129                 DEBUG(0, ("Unable to open idmap database\n"));
130                 return False;
131         }
132
133         bigendianheader = (tdb_get_flags(idmap_tdb) & TDB_BIGENDIAN) ? True : False;
134
135         vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
136
137         if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
138                 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
139                 /*
140                  * high and low records were created on a
141                  * big endian machine and will need byte-reversing.
142                  */
143
144                 int32 wm;
145
146                 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
147
148                 if (wm != -1) {
149                         wm = IREV(wm);
150                 }  else {
151                         wm = idmap_tdb_state.low_uid;
152                 }
153
154                 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
155                         DEBUG(0, ("Unable to byteswap user hwm in idmap database\n"));
156                         tdb_close(idmap_tdb);
157                         return False;
158                 }
159
160                 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
161                 if (wm != -1) {
162                         wm = IREV(wm);
163                 } else {
164                         wm = idmap_tdb_state.low_gid;
165                 }
166
167                 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
168                         DEBUG(0, ("Unable to byteswap group hwm in idmap database\n"));
169                         tdb_close(idmap_tdb);
170                         return False;
171                 }
172         }
173
174         /* the old format stored as DOMAIN/rid - now we store the SID direct */
175         tdb_traverse(idmap_tdb, convert_fn, &failed);
176
177         if (failed) {
178                 DEBUG(0, ("Problem during conversion\n"));
179                 tdb_close(idmap_tdb);
180                 return False;
181         }
182
183         if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
184                 DEBUG(0, ("Unable to dtore idmap version in databse\n"));
185                 tdb_close(idmap_tdb);
186                 return False;
187         }
188
189         tdb_close(idmap_tdb);
190         return True;
191 }
192
193 /*****************************************************************************
194  Convert the idmap database from an older version if necessary
195 *****************************************************************************/
196
197 BOOL idmap_tdb_upgrade(TALLOC_CTX *ctx, const char *tdbfile)
198 {
199         char *backup_name;
200
201         DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
202
203         backup_name = talloc_asprintf(ctx, "%s.bak", tdbfile);
204         if ( ! backup_name) {
205                 DEBUG(0, ("Out of memory!\n"));
206                 return False;
207         }
208
209         if (backup_tdb(tdbfile, backup_name, 0) != 0) {
210                 DEBUG(0, ("Could not backup idmap database\n"));
211                 talloc_free(backup_name);
212                 return False;
213         }
214
215         talloc_free(backup_name);
216         return idmap_tdb_convert(tdbfile);
217 }
218
219 /* WARNING: We can't open a tdb twice inthe same process, for that reason
220  * I'm going to use a hack with open ref counts to open the winbindd_idmap.tdb
221  * only once. We will later decide whether to split the db in multiple files
222  * or come up with a better solution to share them. */
223
224 static TDB_CONTEXT *idmap_tdb_common_ctx;
225 static int idmap_tdb_open_ref_count = 0;
226
227 static NTSTATUS idmap_tdb_open_db(TALLOC_CTX *memctx, TDB_CONTEXT **tdbctx)
228 {
229         NTSTATUS ret;
230         TALLOC_CTX *ctx;
231         SMB_STRUCT_STAT stbuf;
232         char *tdbfile = NULL;
233         int32 version;
234         BOOL tdb_is_new = False;
235
236         if (idmap_tdb_open_ref_count) { /* the tdb has already been opened */
237                 idmap_tdb_open_ref_count++;
238                 *tdbctx = idmap_tdb_common_ctx;
239                 return NT_STATUS_OK;
240         }
241
242         /* use our own context here */
243         ctx = talloc_new(memctx);
244         if (!ctx) {
245                 DEBUG(0, ("Out of memory!\n"));
246                 return NT_STATUS_NO_MEMORY;
247         }
248
249         /* use the old database if present */
250         tdbfile = talloc_strdup(ctx, lock_path("winbindd_idmap.tdb"));
251         if (!tdbfile) {
252                 DEBUG(0, ("Out of memory!\n"));
253                 ret = NT_STATUS_NO_MEMORY;
254                 goto done;
255         }
256
257         if (!file_exist(tdbfile, &stbuf)) {
258                 tdb_is_new = True;
259         }
260
261         DEBUG(10,("Opening tdbfile %s\n", tdbfile ));
262
263         /* Open idmap repository */
264         if (!(idmap_tdb_common_ctx = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644))) {
265                 DEBUG(0, ("Unable to open idmap database\n"));
266                 ret = NT_STATUS_UNSUCCESSFUL;
267                 goto done;
268         }
269
270         if (tdb_is_new) {
271                 /* the file didn't existed before opening it, let's
272                  * store idmap version as nobody else yet opened and
273                  * stored it. I do not like this method but didn't
274                  * found a way to understand if an opened tdb have
275                  * been just created or not --- SSS */
276                 tdb_store_int32(idmap_tdb_common_ctx, "IDMAP_VERSION", IDMAP_VERSION);
277         }
278
279         /* check against earlier versions */
280         version = tdb_fetch_int32(idmap_tdb_common_ctx, "IDMAP_VERSION");
281         if (version != IDMAP_VERSION) {
282                 
283                 /* backup_tdb expects the tdb not to be open */
284                 tdb_close(idmap_tdb_common_ctx);
285
286                 if ( ! idmap_tdb_upgrade(ctx, tdbfile)) {
287                 
288                         DEBUG(0, ("Unable to open idmap database, it's in an old formati, and upgrade failed!\n"));
289                         ret = NT_STATUS_INTERNAL_DB_ERROR;
290                         goto done;
291                 }
292
293                 /* Re-Open idmap repository */
294                 if (!(idmap_tdb_common_ctx = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644))) {
295                         DEBUG(0, ("Unable to open idmap database\n"));
296                         ret = NT_STATUS_UNSUCCESSFUL;
297                         goto done;
298                 }
299         }
300
301         *tdbctx = idmap_tdb_common_ctx;
302         idmap_tdb_open_ref_count++;
303         ret = NT_STATUS_OK;
304
305 done:
306         talloc_free(ctx);
307         return ret;
308 }
309
310  /* NEVER use tdb_close() except for the conversion routines that are guaranteed
311  * to run only when the database is opened the first time, always use this function. */ 
312
313 BOOL idmap_tdb_tdb_close(TDB_CONTEXT *tdbctx)
314 {
315         if (tdbctx != idmap_tdb_common_ctx) {
316                 DEBUG(0, ("ERROR: Invalid tdb context!"));
317                 return False;
318         }
319
320         idmap_tdb_open_ref_count--;
321         if (idmap_tdb_open_ref_count) {
322                 return True;
323         }
324
325         return tdb_close(idmap_tdb_common_ctx);
326 }
327
328 /**********************************************************************
329  IDMAP ALLOC TDB BACKEND
330 **********************************************************************/
331  
332 static TDB_CONTEXT *idmap_alloc_tdb;
333
334 /**********************************
335  Initialise idmap alloc database. 
336 **********************************/
337
338 static NTSTATUS idmap_tdb_alloc_init( const char *params )
339 {
340         NTSTATUS ret;
341         TALLOC_CTX *ctx;
342         const char *range;
343         uid_t low_uid = 0;
344         uid_t high_uid = 0;
345         gid_t low_gid = 0;
346         gid_t high_gid = 0;
347
348         /* use our own context here */
349         ctx = talloc_new(NULL);
350         if (!ctx) {
351                 DEBUG(0, ("Out of memory!\n"));
352                 return NT_STATUS_NO_MEMORY;
353         }
354
355         ret = idmap_tdb_open_db(ctx, &idmap_alloc_tdb);
356         if ( ! NT_STATUS_IS_OK(ret)) {
357                 talloc_free(ctx);
358                 return ret;
359         }
360
361         talloc_free(ctx);
362
363         /* load ranges */
364         idmap_tdb_state.low_uid = 0;
365         idmap_tdb_state.high_uid = 0;
366         idmap_tdb_state.low_gid = 0;
367         idmap_tdb_state.high_gid = 0;
368
369         range = lp_parm_const_string(-1, "idmap alloc config", "range", NULL);
370         if (range && range[0]) {
371                 unsigned low_id, high_id;
372
373                 if (sscanf(range, "%u - %u", &low_id, &high_id) == 2) {
374                         if (low_id < high_id) {
375                                 idmap_tdb_state.low_gid = idmap_tdb_state.low_uid = low_id;
376                                 idmap_tdb_state.high_gid = idmap_tdb_state.high_uid = high_id;
377                         } else {
378                                 DEBUG(1, ("ERROR: invalid idmap alloc range [%s]", range));
379                         }
380                 } else {
381                         DEBUG(1, ("ERROR: invalid syntax for idmap alloc config:range [%s]", range));
382                 }
383         }
384
385         /* Create high water marks for group and user id */
386         if (lp_idmap_uid(&low_uid, &high_uid)) {
387                 idmap_tdb_state.low_uid = low_uid;
388                 idmap_tdb_state.high_uid = high_uid;
389         }
390
391         if (lp_idmap_gid(&low_gid, &high_gid)) {
392                 idmap_tdb_state.low_gid = low_gid;
393                 idmap_tdb_state.high_gid = high_gid;
394         }
395
396         if (idmap_tdb_state.high_uid <= idmap_tdb_state.low_uid) {
397                 DEBUG(1, ("idmap uid range missing or invalid\n"));
398                 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
399                 return NT_STATUS_UNSUCCESSFUL;
400         } else {
401                 uint32 low_id;
402
403                 if (((low_id = tdb_fetch_int32(idmap_alloc_tdb, HWM_USER)) == -1) ||
404                     (low_id < idmap_tdb_state.low_uid)) {
405                         if (tdb_store_int32(idmap_alloc_tdb, HWM_USER, idmap_tdb_state.low_uid) == -1) {
406                                 DEBUG(0, ("Unable to initialise user hwm in idmap database\n"));
407                                 return NT_STATUS_INTERNAL_DB_ERROR;
408                         }
409                 }
410         }
411
412         if (idmap_tdb_state.high_gid <= idmap_tdb_state.low_gid) {
413                 DEBUG(1, ("idmap gid range missing or invalid\n"));
414                 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
415                 return NT_STATUS_UNSUCCESSFUL;
416         } else {
417                 uint32 low_id;
418
419                 if (((low_id = tdb_fetch_int32(idmap_alloc_tdb, HWM_GROUP)) == -1) ||
420                     (low_id < idmap_tdb_state.low_gid)) {
421                         if (tdb_store_int32(idmap_alloc_tdb, HWM_GROUP, idmap_tdb_state.low_gid) == -1) {
422                                 DEBUG(0, ("Unable to initialise group hwm in idmap database\n"));
423                                 return NT_STATUS_INTERNAL_DB_ERROR;
424                         }
425                 }
426         }
427
428         return NT_STATUS_OK;
429 }
430
431 /**********************************
432  Allocate a new id. 
433 **********************************/
434
435 static NTSTATUS idmap_tdb_allocate_id(struct unixid *xid)
436 {
437         BOOL ret;
438         const char *hwmkey;
439         const char *hwmtype;
440         uint32_t high_hwm;
441         uint32_t hwm;
442
443         /* Get current high water mark */
444         switch (xid->type) {
445
446         case ID_TYPE_UID:
447                 hwmkey = HWM_USER;
448                 hwmtype = "UID";
449                 high_hwm = idmap_tdb_state.high_uid;
450                 break;
451
452         case ID_TYPE_GID:
453                 hwmkey = HWM_GROUP;
454                 hwmtype = "GID";
455                 high_hwm = idmap_tdb_state.high_gid;
456                 break;
457
458         default:
459                 DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
460                 return NT_STATUS_INVALID_PARAMETER;
461         }
462
463         if ((hwm = tdb_fetch_int32(idmap_alloc_tdb, hwmkey)) == -1) {
464                 return NT_STATUS_INTERNAL_DB_ERROR;
465         }
466
467         /* check it is in the range */
468         if (hwm > high_hwm) {
469                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", 
470                           hwmtype, (unsigned long)high_hwm));
471                 return NT_STATUS_UNSUCCESSFUL;
472         }
473
474         /* fetch a new id and increment it */
475         ret = tdb_change_uint32_atomic(idmap_alloc_tdb, hwmkey, &hwm, 1);
476         if (!ret) {
477                 DEBUG(1, ("Fatal error while fetching a new %s value\n!", hwmtype));
478                 return NT_STATUS_UNSUCCESSFUL;
479         }
480
481         /* recheck it is in the range */
482         if (hwm > high_hwm) {
483                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", 
484                           hwmtype, (unsigned long)high_hwm));
485                 return NT_STATUS_UNSUCCESSFUL;
486         }
487         
488         xid->id = hwm;
489         DEBUG(10,("New %s = %d\n", hwmtype, hwm));
490
491         return NT_STATUS_OK;
492 }
493
494 /**********************************
495  Get current highest id. 
496 **********************************/
497
498 static NTSTATUS idmap_tdb_get_hwm(struct unixid *xid)
499 {
500         const char *hwmkey;
501         const char *hwmtype;
502         uint32_t hwm;
503         uint32_t high_hwm;
504
505         /* Get current high water mark */
506         switch (xid->type) {
507
508         case ID_TYPE_UID:
509                 hwmkey = HWM_USER;
510                 hwmtype = "UID";
511                 high_hwm = idmap_tdb_state.high_uid;
512                 break;
513
514         case ID_TYPE_GID:
515                 hwmkey = HWM_GROUP;
516                 hwmtype = "GID";
517                 high_hwm = idmap_tdb_state.high_gid;
518                 break;
519
520         default:
521                 return NT_STATUS_INVALID_PARAMETER;
522         }
523
524         if ((hwm = tdb_fetch_int32(idmap_alloc_tdb, hwmkey)) == -1) {
525                 return NT_STATUS_INTERNAL_DB_ERROR;
526         }
527
528         xid->id = hwm;
529
530         /* Warn if it is out of range */
531         if (hwm >= high_hwm) {
532                 DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", 
533                           hwmtype, (unsigned long)high_hwm));
534         }
535
536         return NT_STATUS_OK;
537 }
538
539 /**********************************
540  Set high id. 
541 **********************************/
542
543 static NTSTATUS idmap_tdb_set_hwm(struct unixid *xid)
544 {
545         const char *hwmkey;
546         const char *hwmtype;
547         uint32_t hwm;
548         uint32_t high_hwm;
549
550         /* Get current high water mark */
551         switch (xid->type) {
552
553         case ID_TYPE_UID:
554                 hwmkey = HWM_USER;
555                 hwmtype = "UID";
556                 high_hwm = idmap_tdb_state.high_uid;
557                 break;
558
559         case ID_TYPE_GID:
560                 hwmkey = HWM_GROUP;
561                 hwmtype = "GID";
562                 high_hwm = idmap_tdb_state.high_gid;
563                 break;
564
565         default:
566                 return NT_STATUS_INVALID_PARAMETER;
567         }
568
569         hwm = xid->id;
570
571         if ((hwm = tdb_store_int32(idmap_alloc_tdb, hwmkey, hwm)) == -1) {
572                 return NT_STATUS_INTERNAL_DB_ERROR;
573         }
574
575         /* Warn if it is out of range */
576         if (hwm >= high_hwm) {
577                 DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", 
578                           hwmtype, (unsigned long)high_hwm));
579         }
580
581         return NT_STATUS_OK;
582 }
583
584 /**********************************
585  Close the alloc tdb 
586 **********************************/
587
588 static NTSTATUS idmap_tdb_alloc_close(void)
589 {
590         if (idmap_alloc_tdb) {
591                 if (idmap_tdb_tdb_close(idmap_alloc_tdb) == 0) {
592                         return NT_STATUS_OK;
593                 } else {
594                         return NT_STATUS_UNSUCCESSFUL;
595                 }
596         }
597         return NT_STATUS_OK;
598 }
599
600 /**********************************************************************
601  IDMAP MAPPING TDB BACKEND
602 **********************************************************************/
603  
604 struct idmap_tdb_context {
605         TDB_CONTEXT *tdb;
606         uint32_t filter_low_id;
607         uint32_t filter_high_id;
608 };
609
610 /*****************************
611  Initialise idmap database. 
612 *****************************/
613
614 static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom)
615 {
616         NTSTATUS ret;
617         struct idmap_tdb_context *ctx;
618         char *config_option = NULL;
619         const char *range;
620
621         ctx = talloc(dom, struct idmap_tdb_context);
622         if ( ! ctx) {
623                 DEBUG(0, ("Out of memory!\n"));
624                 return NT_STATUS_NO_MEMORY;
625         }
626
627         config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
628         if ( ! config_option) {
629                 DEBUG(0, ("Out of memory!\n"));
630                 ret = NT_STATUS_NO_MEMORY;
631                 goto failed;
632         }
633
634         ret = idmap_tdb_open_db(ctx, &ctx->tdb);
635         if ( ! NT_STATUS_IS_OK(ret)) {
636                 goto failed;
637         }
638
639         range = lp_parm_const_string(-1, config_option, "range", NULL);
640         if (( ! range) ||
641             (sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) ||
642             (ctx->filter_low_id > ctx->filter_high_id)) {
643                 ctx->filter_low_id = 0;
644                 ctx->filter_high_id = 0;
645         }
646
647         dom->private_data = ctx;
648         dom->initialized = True;
649
650         talloc_free(config_option);
651         return NT_STATUS_OK;
652
653 failed:
654         talloc_free(ctx);
655         return ret;
656 }
657
658 /**********************************
659  Single id to sid lookup function. 
660 **********************************/
661
662 static NTSTATUS idmap_tdb_id_to_sid(struct idmap_tdb_context *ctx, struct id_map *map)
663 {
664         NTSTATUS ret;
665         TDB_DATA key, data;
666
667         if (!ctx || !map) {
668                 return NT_STATUS_INVALID_PARAMETER;
669         }
670
671         /* apply filters before checking */
672         if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
673             (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
674                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
675                                 map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
676                 return NT_STATUS_NONE_MAPPED;
677         }
678
679         switch (map->xid.type) {
680
681         case ID_TYPE_UID:
682                 key.dptr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
683                 break;
684                 
685         case ID_TYPE_GID:
686                 key.dptr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
687                 break;
688
689         default:
690                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
691                 return NT_STATUS_INVALID_PARAMETER;
692         }
693
694         /* final SAFE_FREE safe */
695         data.dptr = NULL;
696
697         if (key.dptr == NULL) {
698                 DEBUG(0, ("Out of memory!\n"));
699                 ret = NT_STATUS_NO_MEMORY;
700                 goto done;
701         }
702
703         key.dsize = strlen(key.dptr) + 1;
704
705         DEBUG(10,("Fetching record %s\n", key.dptr));
706
707         /* Check if the mapping exists */
708         data = tdb_fetch(ctx->tdb, key);
709
710         if (!data.dptr) {
711                 DEBUG(10,("Record %s not found\n", key.dptr));
712                 ret = NT_STATUS_NONE_MAPPED;
713                 goto done;
714         }
715                 
716         if (!string_to_sid(map->sid, data.dptr)) {
717                 DEBUG(10,("INVALID SID (%s) in record %s\n",
718                                 data.dptr, key.dptr));
719                 ret = NT_STATUS_INTERNAL_DB_ERROR;
720                 goto done;
721         }
722
723         DEBUG(10,("Found record %s -> %s\n", key.dptr, data.dptr));
724         ret = NT_STATUS_OK;
725
726 done:
727         SAFE_FREE(data.dptr);
728         talloc_free(key.dptr);
729         return ret;
730 }
731
732 /**********************************
733  Single sid to id lookup function. 
734 **********************************/
735
736 static NTSTATUS idmap_tdb_sid_to_id(struct idmap_tdb_context *ctx, struct id_map *map)
737 {
738         NTSTATUS ret;
739         TDB_DATA key, data;
740         unsigned long rec_id = 0;
741
742         if ((key.dptr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) {
743                 DEBUG(0, ("Out of memory!\n"));
744                 ret = NT_STATUS_NO_MEMORY;
745                 goto done;
746         }
747
748         key.dsize = strlen(key.dptr) + 1;
749
750         DEBUG(10,("Fetching record %s\n", key.dptr));
751
752         /* Check if sid is present in database */
753         data = tdb_fetch(ctx->tdb, key);
754         if (!data.dptr) {
755                 DEBUG(10,("Record %s not found\n", key.dptr));
756                 ret = NT_STATUS_NONE_MAPPED;
757                 goto done;
758         }
759
760         /* What type of record is this ? */
761         if (sscanf(data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
762                 map->xid.id = rec_id;
763                 map->xid.type = ID_TYPE_UID;
764                 DEBUG(10,("Found uid record %s -> %s \n", key.dptr, data.dptr ));
765                 ret = NT_STATUS_OK;
766
767         } else if (sscanf(data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
768                 map->xid.id = rec_id;
769                 map->xid.type = ID_TYPE_GID;
770                 DEBUG(10,("Found gid record %s -> %s \n", key.dptr, data.dptr ));
771                 ret = NT_STATUS_OK;
772
773         } else { /* Unknown record type ! */
774                 DEBUG(2, ("Found INVALID record %s -> %s\n", key.dptr, data.dptr));
775                 ret = NT_STATUS_INTERNAL_DB_ERROR;
776         }
777         
778         SAFE_FREE(data.dptr);
779
780         /* apply filters before returning result */
781         if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
782             (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
783                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
784                                 map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
785                 ret = NT_STATUS_NONE_MAPPED;
786         }
787
788 done:
789         talloc_free(key.dptr);
790         return ret;
791 }
792
793 /**********************************
794  lookup a set of unix ids. 
795 **********************************/
796
797 static NTSTATUS idmap_tdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
798 {
799         struct idmap_tdb_context *ctx;
800         NTSTATUS ret;
801         int i;
802
803         /* make sure we initialized */
804         if ( ! dom->initialized) {
805                 ret = idmap_tdb_db_init(dom);
806                 if ( ! NT_STATUS_IS_OK(ret)) {
807                         return ret;
808                 }
809         }
810
811         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
812
813         for (i = 0; ids[i]; i++) {
814                 ret = idmap_tdb_id_to_sid(ctx, ids[i]);
815                 if ( ! NT_STATUS_IS_OK(ret)) {
816
817                         /* if it is just a failed mapping continue */
818                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
819
820                                 /* make sure it is marked as unmapped */
821                                 ids[i]->status = ID_UNMAPPED;
822                                 continue;
823                         }
824                         
825                         /* some fatal error occurred, return immediately */
826                         goto done;
827                 }
828
829                 /* all ok, id is mapped */
830                 ids[i]->status = ID_MAPPED;
831         }
832
833         ret = NT_STATUS_OK;
834
835 done:
836         return ret;
837 }
838
839 /**********************************
840  lookup a set of sids. 
841 **********************************/
842
843 static NTSTATUS idmap_tdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
844 {
845         struct idmap_tdb_context *ctx;
846         NTSTATUS ret;
847         int i;
848
849         /* make sure we initialized */
850         if ( ! dom->initialized) {
851                 ret = idmap_tdb_db_init(dom);
852                 if ( ! NT_STATUS_IS_OK(ret)) {
853                         return ret;
854                 }
855         }
856
857         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
858
859         for (i = 0; ids[i]; i++) {
860                 ret = idmap_tdb_sid_to_id(ctx, ids[i]);
861                 if ( ! NT_STATUS_IS_OK(ret)) {
862
863                         /* if it is just a failed mapping continue */
864                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
865
866                                 /* make sure it is marked as unmapped */
867                                 ids[i]->status = ID_UNMAPPED;
868                                 continue;
869                         }
870                         
871                         /* some fatal error occurred, return immediately */
872                         goto done;
873                 }
874
875                 /* all ok, id is mapped */
876                 ids[i]->status = ID_MAPPED;
877         }
878
879         ret = NT_STATUS_OK;
880
881 done:
882         return ret;
883 }
884
885 /**********************************
886  set a mapping. 
887 **********************************/
888
889 static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom, const struct id_map *map)
890 {
891         struct idmap_tdb_context *ctx;
892         NTSTATUS ret;
893         TDB_DATA ksid, kid, data;
894
895         /* make sure we initialized */
896         if ( ! dom->initialized) {
897                 ret = idmap_tdb_db_init(dom);
898                 if ( ! NT_STATUS_IS_OK(ret)) {
899                         return ret;
900                 }
901         }
902
903         if (!map || !map->sid) {
904                 return NT_STATUS_INVALID_PARAMETER;
905         }
906
907         ksid.dptr = kid.dptr = data.dptr = NULL;
908
909         /* TODO: should we filter a set_mapping using low/high filters ? */
910         
911         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
912
913         switch (map->xid.type) {
914
915         case ID_TYPE_UID:
916                 kid.dptr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
917                 break;
918                 
919         case ID_TYPE_GID:
920                 kid.dptr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
921                 break;
922
923         default:
924                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
925                 return NT_STATUS_INVALID_PARAMETER;
926         }
927
928         if (kid.dptr == NULL) {
929                 DEBUG(0, ("ERROR: Out of memory!\n"));
930                 ret = NT_STATUS_NO_MEMORY;
931                 goto done;
932         }
933         kid.dsize = strlen(kid.dptr) + 1;
934
935         if ((ksid.dptr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) {
936                 DEBUG(0, ("Out of memory!\n"));
937                 ret = NT_STATUS_NO_MEMORY;
938                 goto done;
939         }
940         ksid.dsize = strlen(ksid.dptr) + 1;
941
942         DEBUG(10, ("Storing %s <-> %s map\n", ksid.dptr, kid.dptr));
943
944         /* *DELETE* previous mappings if any.
945          * This is done both SID and [U|G]ID passed in */
946         
947         /* Lock the record for this SID. */
948         if (tdb_chainlock(ctx->tdb, ksid) != 0) {
949                 DEBUG(10,("Failed to lock record %s. Error %s\n",
950                                 ksid.dptr, tdb_errorstr(ctx->tdb) ));
951                 return NT_STATUS_UNSUCCESSFUL;
952         }
953
954         data = tdb_fetch(ctx->tdb, ksid);
955         if (data.dptr) {
956                 DEBUG(10, ("Deleting existing mapping %s <-> %s\n", data.dptr, ksid.dptr ));
957                 tdb_delete(ctx->tdb, data);
958                 tdb_delete(ctx->tdb, ksid);
959                 SAFE_FREE(data.dptr);
960         }
961
962         data = tdb_fetch(ctx->tdb, kid);
963         if (data.dptr) {
964                 DEBUG(10,("Deleting existing mapping %s <-> %s\n", data.dptr, kid.dptr ));
965                 tdb_delete(ctx->tdb, data);
966                 tdb_delete(ctx->tdb, kid);
967                 SAFE_FREE(data.dptr);
968         }
969
970         if (tdb_store(ctx->tdb, ksid, kid, TDB_INSERT) == -1) {
971                 DEBUG(0, ("Error storing SID -> ID: %s\n", tdb_errorstr(ctx->tdb)));
972                 tdb_chainunlock(ctx->tdb, ksid);
973                 ret = NT_STATUS_UNSUCCESSFUL;
974                 goto done;
975         }
976         if (tdb_store(ctx->tdb, kid, ksid, TDB_INSERT) == -1) {
977                 DEBUG(0, ("Error stroing ID -> SID: %s\n", tdb_errorstr(ctx->tdb)));
978                 /* try to remove the previous stored SID -> ID map */
979                 tdb_delete(ctx->tdb, ksid);
980                 tdb_chainunlock(ctx->tdb, ksid);
981                 ret = NT_STATUS_UNSUCCESSFUL;
982                 goto done;
983         }
984
985         tdb_chainunlock(ctx->tdb, ksid);
986         DEBUG(10,("Stored %s <-> %s\n", ksid.dptr, kid.dptr));
987         ret = NT_STATUS_OK;
988
989 done:
990         talloc_free(ksid.dptr);
991         talloc_free(kid.dptr);
992         SAFE_FREE(data.dptr);
993         return ret;
994 }
995
996 /**********************************
997  remove a mapping. 
998 **********************************/
999
1000 static NTSTATUS idmap_tdb_remove_mapping(struct idmap_domain *dom, const struct id_map *map)
1001 {
1002         struct idmap_tdb_context *ctx;
1003         NTSTATUS ret;
1004         TDB_DATA ksid, kid, data;
1005
1006         /* make sure we initialized */
1007         if ( ! dom->initialized) {
1008                 ret = idmap_tdb_db_init(dom);
1009                 if ( ! NT_STATUS_IS_OK(ret)) {
1010                         return ret;
1011                 }
1012         }
1013
1014         if (!map || !map->sid) {
1015                 return NT_STATUS_INVALID_PARAMETER;
1016         }
1017
1018         ksid.dptr = kid.dptr = data.dptr = NULL;
1019
1020         /* TODO: should we filter a remove_mapping using low/high filters ? */
1021         
1022         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
1023
1024         switch (map->xid.type) {
1025
1026         case ID_TYPE_UID:
1027                 kid.dptr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
1028                 break;
1029                 
1030         case ID_TYPE_GID:
1031                 kid.dptr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
1032                 break;
1033
1034         default:
1035                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
1036                 return NT_STATUS_INVALID_PARAMETER;
1037         }
1038
1039         if (kid.dptr == NULL) {
1040                 DEBUG(0, ("ERROR: Out of memory!\n"));
1041                 ret = NT_STATUS_NO_MEMORY;
1042                 goto done;
1043         }
1044         kid.dsize = strlen(kid.dptr) + 1;
1045
1046         if ((ksid.dptr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) {
1047                 DEBUG(0, ("Out of memory!\n"));
1048                 ret = NT_STATUS_NO_MEMORY;
1049                 goto done;
1050         }
1051         ksid.dsize = strlen(ksid.dptr) + 1;
1052
1053         DEBUG(10, ("Checking %s <-> %s map\n", ksid.dptr, kid.dptr));
1054
1055         /* Lock the record for this SID. */
1056         if (tdb_chainlock(ctx->tdb, ksid) != 0) {
1057                 DEBUG(10,("Failed to lock record %s. Error %s\n",
1058                                 ksid.dptr, tdb_errorstr(ctx->tdb) ));
1059                 return NT_STATUS_UNSUCCESSFUL;
1060         }
1061
1062         /* Check if sid is present in database */
1063         data = tdb_fetch(ctx->tdb, ksid);
1064         if (!data.dptr) {
1065                 DEBUG(10,("Record %s not found\n", ksid.dptr));
1066                 tdb_chainunlock(ctx->tdb, ksid);
1067                 ret = NT_STATUS_NONE_MAPPED;
1068                 goto done;
1069         }
1070
1071         /* Check if sid is mapped to the specified ID */
1072         if ((data.dsize != kid.dsize) ||
1073             (memcmp(data.dptr, kid.dptr, data.dsize) != 0)) {
1074                 DEBUG(10,("Specified SID does not map to specified ID\n"));
1075                 DEBUGADD(10,("Actual mapping is %s -> %s\n", ksid.dptr, data.dptr));
1076                 tdb_chainunlock(ctx->tdb, ksid);
1077                 ret = NT_STATUS_NONE_MAPPED;
1078                 goto done;
1079         }
1080         
1081         DEBUG(10, ("Removing %s <-> %s map\n", ksid.dptr, kid.dptr));
1082
1083         /* Delete previous mappings. */
1084         
1085         DEBUG(10, ("Deleting existing mapping %s -> %s\n", ksid.dptr, kid.dptr ));
1086         tdb_delete(ctx->tdb, ksid);
1087
1088         DEBUG(10,("Deleting existing mapping %s -> %s\n", kid.dptr, ksid.dptr ));
1089         tdb_delete(ctx->tdb, kid);
1090
1091         tdb_chainunlock(ctx->tdb, ksid);
1092         ret = NT_STATUS_OK;
1093
1094 done:
1095         talloc_free(ksid.dptr);
1096         talloc_free(kid.dptr);
1097         SAFE_FREE(data.dptr);
1098         return ret;
1099 }
1100
1101 /**********************************
1102  Close the idmap tdb instance
1103 **********************************/
1104
1105 static NTSTATUS idmap_tdb_close(struct idmap_domain *dom)
1106 {
1107         struct idmap_tdb_context *ctx;
1108
1109         if (dom->private_data) {
1110                 ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
1111
1112                 if (idmap_tdb_tdb_close(ctx->tdb) == 0) {
1113                         return NT_STATUS_OK;
1114                 } else {
1115                         return NT_STATUS_UNSUCCESSFUL;
1116                 }
1117         }
1118         return NT_STATUS_OK;
1119 }
1120
1121 struct dump_data {
1122         TALLOC_CTX *memctx;
1123         struct id_map **maps;
1124         int *num_maps;
1125         NTSTATUS ret;
1126 };
1127
1128 static int idmap_tdb_dump_one_entry(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value, void *pdata)
1129 {
1130         struct dump_data *data = talloc_get_type(pdata, struct dump_data);
1131         struct id_map *maps;
1132         int num_maps = *data->num_maps;
1133
1134         /* ignore any record but the ones with a SID as key */
1135         if (strncmp(key.dptr, "S-", 2) == 0) {
1136
1137                 maps = talloc_realloc(NULL, *data->maps, struct id_map, num_maps+1);
1138                 if ( ! maps) {
1139                         DEBUG(0, ("Out of memory!\n"));
1140                         data->ret = NT_STATUS_NO_MEMORY;
1141                         return -1;
1142                 }
1143                 *data->maps = maps;
1144                 maps[num_maps].sid = talloc(maps, DOM_SID);
1145                 if ( ! maps[num_maps].sid) {
1146                         DEBUG(0, ("Out of memory!\n"));
1147                         data->ret = NT_STATUS_NO_MEMORY;
1148                         return -1;
1149                 }
1150
1151                 if (!string_to_sid(maps[num_maps].sid, key.dptr)) {
1152                         DEBUG(10,("INVALID record %s\n", key.dptr));
1153                         /* continue even with errors */
1154                         return 0;
1155                 }
1156
1157                 /* Try a UID record. */
1158                 if (sscanf(value.dptr, "UID %u", &(maps[num_maps].xid.id)) == 1) {
1159                         maps[num_maps].xid.type = ID_TYPE_UID;
1160                         maps[num_maps].status = ID_MAPPED;
1161                         *data->num_maps = num_maps + 1;
1162
1163                 /* Try a GID record. */
1164                 } else
1165                 if (sscanf(value.dptr, "GID %u", &(maps[num_maps].xid.id)) == 1) {
1166                         maps[num_maps].xid.type = ID_TYPE_GID;
1167                         maps[num_maps].status = ID_MAPPED;
1168                         *data->num_maps = num_maps + 1;
1169
1170                 /* Unknown record type ! */
1171                 } else {
1172                         maps[num_maps].status = ID_UNKNOWN;
1173                         DEBUG(2, ("Found INVALID record %s -> %s\n", key.dptr, value.dptr));
1174                         /* do not increment num_maps */
1175                 }
1176         }
1177
1178         return 0;
1179 }
1180
1181 /**********************************
1182  Dump all mappings out
1183 **********************************/
1184
1185 static NTSTATUS idmap_tdb_dump_data(struct idmap_domain *dom, struct id_map **maps, int *num_maps)
1186 {
1187         struct idmap_tdb_context *ctx;
1188         struct dump_data *data;
1189         NTSTATUS ret = NT_STATUS_OK;
1190
1191         /* make sure we initialized */
1192         if ( ! dom->initialized) {
1193                 ret = idmap_tdb_db_init(dom);
1194                 if ( ! NT_STATUS_IS_OK(ret)) {
1195                         return ret;
1196                 }
1197         }
1198
1199         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
1200
1201         data = TALLOC_ZERO_P(ctx, struct dump_data);
1202         if ( ! data) {
1203                 DEBUG(0, ("Out of memory!\n"));
1204                 return NT_STATUS_NO_MEMORY;
1205         }
1206         data->maps = maps;
1207         data->num_maps = num_maps;
1208         data->ret = NT_STATUS_OK;
1209
1210         tdb_traverse(ctx->tdb, idmap_tdb_dump_one_entry, data);
1211
1212         if ( ! NT_STATUS_IS_OK(data->ret)) {
1213                 ret = data->ret;
1214         }
1215
1216         talloc_free(data);
1217         return ret;
1218 }
1219
1220 static struct idmap_methods db_methods = {
1221
1222         .init = idmap_tdb_db_init,
1223         .unixids_to_sids = idmap_tdb_unixids_to_sids,
1224         .sids_to_unixids = idmap_tdb_sids_to_unixids,
1225         .set_mapping = idmap_tdb_set_mapping,
1226         .remove_mapping = idmap_tdb_remove_mapping,
1227         .dump_data = idmap_tdb_dump_data,
1228         .close_fn = idmap_tdb_close
1229 };
1230
1231 static struct idmap_alloc_methods db_alloc_methods = {
1232
1233         .init = idmap_tdb_alloc_init,
1234         .allocate_id = idmap_tdb_allocate_id,
1235         .get_id_hwm = idmap_tdb_get_hwm,
1236         .set_id_hwm = idmap_tdb_set_hwm,
1237         .close_fn = idmap_tdb_alloc_close
1238 };
1239
1240 NTSTATUS idmap_alloc_tdb_init(void)
1241 {
1242         return smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_alloc_methods);
1243 }
1244
1245 NTSTATUS idmap_tdb_init(void)
1246 {
1247         NTSTATUS ret;
1248
1249         /* FIXME: bad hack to actually register also the alloc_tdb module without changining configure.in */
1250         ret = idmap_alloc_tdb_init();
1251         if (! NT_STATUS_IS_OK(ret)) {
1252                 return ret;
1253         }
1254         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
1255 }