r20218: Same fix as r20217 -- can we consolidate that duplicated code a bit?
[sfrench/samba-autobuild/.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, const char *compat_params)
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
649         talloc_free(config_option);
650         return NT_STATUS_OK;
651
652 failed:
653         talloc_free(ctx);
654         return ret;
655 }
656
657 /**********************************
658  Single id to sid lookup function. 
659 **********************************/
660
661 static NTSTATUS idmap_tdb_id_to_sid(struct idmap_tdb_context *ctx, struct id_map *map)
662 {
663         NTSTATUS ret;
664         TDB_DATA key, data;
665
666         if (!ctx || !map) {
667                 return NT_STATUS_INVALID_PARAMETER;
668         }
669
670         /* apply filters before checking */
671         if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
672             (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
673                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
674                                 map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
675                 return NT_STATUS_NONE_MAPPED;
676         }
677
678         switch (map->xid.type) {
679
680         case ID_TYPE_UID:
681                 key.dptr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
682                 break;
683                 
684         case ID_TYPE_GID:
685                 key.dptr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
686                 break;
687
688         default:
689                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
690                 return NT_STATUS_INVALID_PARAMETER;
691         }
692
693         /* final SAFE_FREE safe */
694         data.dptr = NULL;
695
696         if (key.dptr == NULL) {
697                 DEBUG(0, ("Out of memory!\n"));
698                 ret = NT_STATUS_NO_MEMORY;
699                 goto done;
700         }
701
702         key.dsize = strlen(key.dptr) + 1;
703
704         DEBUG(10,("Fetching record %s\n", key.dptr));
705
706         /* Check if the mapping exists */
707         data = tdb_fetch(ctx->tdb, key);
708
709         if (!data.dptr) {
710                 DEBUG(10,("Record %s not found\n", key.dptr));
711                 ret = NT_STATUS_NONE_MAPPED;
712                 goto done;
713         }
714                 
715         if (!string_to_sid(map->sid, data.dptr)) {
716                 DEBUG(10,("INVALID SID (%s) in record %s\n",
717                                 data.dptr, key.dptr));
718                 ret = NT_STATUS_INTERNAL_DB_ERROR;
719                 goto done;
720         }
721
722         DEBUG(10,("Found record %s -> %s\n", key.dptr, data.dptr));
723         ret = NT_STATUS_OK;
724
725 done:
726         SAFE_FREE(data.dptr);
727         talloc_free(key.dptr);
728         return ret;
729 }
730
731 /**********************************
732  Single sid to id lookup function. 
733 **********************************/
734
735 static NTSTATUS idmap_tdb_sid_to_id(struct idmap_tdb_context *ctx, struct id_map *map)
736 {
737         NTSTATUS ret;
738         TDB_DATA key, data;
739         unsigned long rec_id = 0;
740
741         if ((key.dptr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) {
742                 DEBUG(0, ("Out of memory!\n"));
743                 ret = NT_STATUS_NO_MEMORY;
744                 goto done;
745         }
746
747         key.dsize = strlen(key.dptr) + 1;
748
749         DEBUG(10,("Fetching record %s\n", key.dptr));
750
751         /* Check if sid is present in database */
752         data = tdb_fetch(ctx->tdb, key);
753         if (!data.dptr) {
754                 DEBUG(10,("Record %s not found\n", key.dptr));
755                 ret = NT_STATUS_NONE_MAPPED;
756                 goto done;
757         }
758
759         /* What type of record is this ? */
760         if (sscanf(data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
761                 map->xid.id = rec_id;
762                 map->xid.type = ID_TYPE_UID;
763                 DEBUG(10,("Found uid record %s -> %s \n", key.dptr, data.dptr ));
764                 ret = NT_STATUS_OK;
765
766         } else if (sscanf(data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
767                 map->xid.id = rec_id;
768                 map->xid.type = ID_TYPE_GID;
769                 DEBUG(10,("Found gid record %s -> %s \n", key.dptr, data.dptr ));
770                 ret = NT_STATUS_OK;
771
772         } else { /* Unknown record type ! */
773                 DEBUG(2, ("Found INVALID record %s -> %s\n", key.dptr, data.dptr));
774                 ret = NT_STATUS_INTERNAL_DB_ERROR;
775         }
776         
777         SAFE_FREE(data.dptr);
778
779         /* apply filters before returning result */
780         if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
781             (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
782                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
783                                 map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
784                 ret = NT_STATUS_NONE_MAPPED;
785         }
786
787 done:
788         talloc_free(key.dptr);
789         return ret;
790 }
791
792 /**********************************
793  lookup a set of unix ids. 
794 **********************************/
795
796 static NTSTATUS idmap_tdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
797 {
798         struct idmap_tdb_context *ctx;
799         NTSTATUS ret;
800         int i;
801
802         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
803
804         for (i = 0; ids[i]; i++) {
805                 ret = idmap_tdb_id_to_sid(ctx, ids[i]);
806                 if ( ! NT_STATUS_IS_OK(ret)) {
807
808                         /* if it is just a failed mapping continue */
809                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
810
811                                 /* make sure it is marked as unmapped */
812                                 ids[i]->mapped = False;
813                                 continue;
814                         }
815                         
816                         /* some fatal error occurred, return immediately */
817                         goto done;
818                 }
819
820                 /* all ok, id is mapped */
821                 ids[i]->mapped = True;
822         }
823
824         ret = NT_STATUS_OK;
825
826 done:
827         return ret;
828 }
829
830 /**********************************
831  lookup a set of sids. 
832 **********************************/
833
834 static NTSTATUS idmap_tdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
835 {
836         struct idmap_tdb_context *ctx;
837         NTSTATUS ret;
838         int i;
839
840         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
841
842         for (i = 0; ids[i]; i++) {
843                 ret = idmap_tdb_sid_to_id(ctx, ids[i]);
844                 if ( ! NT_STATUS_IS_OK(ret)) {
845
846                         /* if it is just a failed mapping continue */
847                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
848
849                                 /* make sure it is marked as unmapped */
850                                 ids[i]->mapped = False;
851                                 continue;
852                         }
853                         
854                         /* some fatal error occurred, return immediately */
855                         goto done;
856                 }
857
858                 /* all ok, id is mapped */
859                 ids[i]->mapped = True;
860         }
861
862         ret = NT_STATUS_OK;
863
864 done:
865         return ret;
866 }
867
868 /**********************************
869  set a mapping. 
870 **********************************/
871
872 static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom, const struct id_map *map)
873 {
874         struct idmap_tdb_context *ctx;
875         NTSTATUS ret;
876         TDB_DATA ksid, kid, data;
877
878         if (!map || !map->sid) {
879                 return NT_STATUS_INVALID_PARAMETER;
880         }
881
882         ksid.dptr = kid.dptr = data.dptr = NULL;
883
884         /* TODO: should we filter a set_mapping using low/high filters ? */
885         
886         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
887
888         switch (map->xid.type) {
889
890         case ID_TYPE_UID:
891                 kid.dptr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
892                 break;
893                 
894         case ID_TYPE_GID:
895                 kid.dptr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
896                 break;
897
898         default:
899                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
900                 return NT_STATUS_INVALID_PARAMETER;
901         }
902
903         if (kid.dptr == NULL) {
904                 DEBUG(0, ("ERROR: Out of memory!\n"));
905                 ret = NT_STATUS_NO_MEMORY;
906                 goto done;
907         }
908         kid.dsize = strlen(kid.dptr) + 1;
909
910         if ((ksid.dptr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) {
911                 DEBUG(0, ("Out of memory!\n"));
912                 ret = NT_STATUS_NO_MEMORY;
913                 goto done;
914         }
915         ksid.dsize = strlen(ksid.dptr) + 1;
916
917         DEBUG(10, ("Storing %s <-> %s map\n", ksid.dptr, kid.dptr));
918
919         /* *DELETE* previous mappings if any.
920          * This is done both SID and [U|G]ID passed in */
921         
922         /* Lock the record for this SID. */
923         if (tdb_chainlock(ctx->tdb, ksid) != 0) {
924                 DEBUG(10,("Failed to lock record %s. Error %s\n",
925                                 ksid.dptr, tdb_errorstr(ctx->tdb) ));
926                 return NT_STATUS_UNSUCCESSFUL;
927         }
928
929         data = tdb_fetch(ctx->tdb, ksid);
930         if (data.dptr) {
931                 DEBUG(10, ("Deleting existing mapping %s <-> %s\n", data.dptr, ksid.dptr ));
932                 tdb_delete(ctx->tdb, data);
933                 tdb_delete(ctx->tdb, ksid);
934                 SAFE_FREE(data.dptr);
935         }
936
937         data = tdb_fetch(ctx->tdb, kid);
938         if (data.dptr) {
939                 DEBUG(10,("Deleting existing mapping %s <-> %s\n", data.dptr, kid.dptr ));
940                 tdb_delete(ctx->tdb, data);
941                 tdb_delete(ctx->tdb, kid);
942                 SAFE_FREE(data.dptr);
943         }
944
945         if (tdb_store(ctx->tdb, ksid, kid, TDB_INSERT) == -1) {
946                 DEBUG(0, ("Error storing SID -> ID: %s\n", tdb_errorstr(ctx->tdb)));
947                 tdb_chainunlock(ctx->tdb, ksid);
948                 ret = NT_STATUS_UNSUCCESSFUL;
949                 goto done;
950         }
951         if (tdb_store(ctx->tdb, kid, ksid, TDB_INSERT) == -1) {
952                 DEBUG(0, ("Error stroing ID -> SID: %s\n", tdb_errorstr(ctx->tdb)));
953                 /* try to remove the previous stored SID -> ID map */
954                 tdb_delete(ctx->tdb, ksid);
955                 tdb_chainunlock(ctx->tdb, ksid);
956                 ret = NT_STATUS_UNSUCCESSFUL;
957                 goto done;
958         }
959
960         tdb_chainunlock(ctx->tdb, ksid);
961         DEBUG(10,("Stored %s <-> %s\n", ksid.dptr, kid.dptr));
962         ret = NT_STATUS_OK;
963
964 done:
965         talloc_free(ksid.dptr);
966         talloc_free(kid.dptr);
967         SAFE_FREE(data.dptr);
968         return ret;
969 }
970
971 /**********************************
972  remove a mapping. 
973 **********************************/
974
975 static NTSTATUS idmap_tdb_remove_mapping(struct idmap_domain *dom, const struct id_map *map)
976 {
977         struct idmap_tdb_context *ctx;
978         NTSTATUS ret;
979         TDB_DATA ksid, kid, data;
980
981         if (!map || !map->sid) {
982                 return NT_STATUS_INVALID_PARAMETER;
983         }
984
985         ksid.dptr = kid.dptr = data.dptr = NULL;
986
987         /* TODO: should we filter a remove_mapping using low/high filters ? */
988         
989         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
990
991         switch (map->xid.type) {
992
993         case ID_TYPE_UID:
994                 kid.dptr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
995                 break;
996                 
997         case ID_TYPE_GID:
998                 kid.dptr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
999                 break;
1000
1001         default:
1002                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
1003                 return NT_STATUS_INVALID_PARAMETER;
1004         }
1005
1006         if (kid.dptr == NULL) {
1007                 DEBUG(0, ("ERROR: Out of memory!\n"));
1008                 ret = NT_STATUS_NO_MEMORY;
1009                 goto done;
1010         }
1011         kid.dsize = strlen(kid.dptr) + 1;
1012
1013         if ((ksid.dptr = talloc_asprintf(ctx, "%s", sid_string_static(map->sid))) == NULL) {
1014                 DEBUG(0, ("Out of memory!\n"));
1015                 ret = NT_STATUS_NO_MEMORY;
1016                 goto done;
1017         }
1018         ksid.dsize = strlen(ksid.dptr) + 1;
1019
1020         DEBUG(10, ("Checking %s <-> %s map\n", ksid.dptr, kid.dptr));
1021
1022         /* Lock the record for this SID. */
1023         if (tdb_chainlock(ctx->tdb, ksid) != 0) {
1024                 DEBUG(10,("Failed to lock record %s. Error %s\n",
1025                                 ksid.dptr, tdb_errorstr(ctx->tdb) ));
1026                 return NT_STATUS_UNSUCCESSFUL;
1027         }
1028
1029         /* Check if sid is present in database */
1030         data = tdb_fetch(ctx->tdb, ksid);
1031         if (!data.dptr) {
1032                 DEBUG(10,("Record %s not found\n", ksid.dptr));
1033                 tdb_chainunlock(ctx->tdb, ksid);
1034                 ret = NT_STATUS_NONE_MAPPED;
1035                 goto done;
1036         }
1037
1038         /* Check if sid is mapped to the specified ID */
1039         if ((data.dsize != kid.dsize) ||
1040             (memcmp(data.dptr, kid.dptr, data.dsize) != 0)) {
1041                 DEBUG(10,("Specified SID does not map to specified ID\n"));
1042                 DEBUGADD(10,("Actual mapping is %s -> %s\n", ksid.dptr, data.dptr));
1043                 tdb_chainunlock(ctx->tdb, ksid);
1044                 ret = NT_STATUS_NONE_MAPPED;
1045                 goto done;
1046         }
1047         
1048         DEBUG(10, ("Removing %s <-> %s map\n", ksid.dptr, kid.dptr));
1049
1050         /* Delete previous mappings. */
1051         
1052         data = tdb_fetch(ctx->tdb, ksid);
1053         if (data.dptr) {
1054                 DEBUG(10, ("Deleting existing mapping %s -> %s\n", ksid.dptr, kid.dptr ));
1055                 tdb_delete(ctx->tdb, ksid);
1056                 SAFE_FREE(data.dptr);
1057         }
1058
1059         data = tdb_fetch(ctx->tdb, kid);
1060         if (data.dptr) {
1061                 DEBUG(10,("Deleting existing mapping %s -> %s\n", kid.dptr, ksid.dptr ));
1062                 tdb_delete(ctx->tdb, kid);
1063                 SAFE_FREE(data.dptr);
1064         }
1065
1066         tdb_chainunlock(ctx->tdb, ksid);
1067         ret = NT_STATUS_OK;
1068
1069 done:
1070         talloc_free(ksid.dptr);
1071         talloc_free(kid.dptr);
1072         SAFE_FREE(data.dptr);
1073         return ret;
1074 }
1075
1076 /**********************************
1077  Close the idmap tdb instance
1078 **********************************/
1079
1080 static NTSTATUS idmap_tdb_close(struct idmap_domain *dom)
1081 {
1082         struct idmap_tdb_context *ctx;
1083
1084         if (dom->private_data) {
1085                 ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
1086
1087                 if (idmap_tdb_tdb_close(ctx->tdb) == 0) {
1088                         return NT_STATUS_OK;
1089                 } else {
1090                         return NT_STATUS_UNSUCCESSFUL;
1091                 }
1092         }
1093         return NT_STATUS_OK;
1094 }
1095
1096 struct dump_data {
1097         TALLOC_CTX *memctx;
1098         struct id_map **maps;
1099         int *num_maps;
1100         NTSTATUS ret;
1101 };
1102
1103 static int idmap_tdb_dump_one_entry(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value, void *pdata)
1104 {
1105         struct dump_data *data = talloc_get_type(pdata, struct dump_data);
1106         struct id_map *maps;
1107         int num_maps = *data->num_maps;
1108
1109         /* ignore any record but the ones with a SID as key */
1110         if (strncmp(key.dptr, "S-", 2) == 0) {
1111
1112                 maps = talloc_realloc(NULL, *data->maps, struct id_map, num_maps+1);
1113                 if ( ! maps) {
1114                         DEBUG(0, ("Out of memory!\n"));
1115                         data->ret = NT_STATUS_NO_MEMORY;
1116                         return -1;
1117                 }
1118                 *data->maps = maps;
1119                 maps[num_maps].sid = talloc(maps, DOM_SID);
1120                 if ( ! maps[num_maps].sid) {
1121                         DEBUG(0, ("Out of memory!\n"));
1122                         data->ret = NT_STATUS_NO_MEMORY;
1123                         return -1;
1124                 }
1125
1126                 if (!string_to_sid(maps[num_maps].sid, key.dptr)) {
1127                         DEBUG(10,("INVALID record %s\n", key.dptr));
1128                         /* continue even with errors */
1129                         return 0;
1130                 }
1131
1132                 /* Try a UID record. */
1133                 if (sscanf(value.dptr, "UID %u", &(maps[num_maps].xid.id)) == 1) {
1134                         maps[num_maps].xid.type = ID_TYPE_UID;
1135                         maps[num_maps].mapped = True;
1136                         *data->num_maps = num_maps + 1;
1137
1138                 /* Try a GID record. */
1139                 } else
1140                 if (sscanf(value.dptr, "GID %u", &(maps[num_maps].xid.id)) == 1) {
1141                         maps[num_maps].xid.type = ID_TYPE_GID;
1142                         maps[num_maps].mapped = True;
1143                         *data->num_maps = num_maps + 1;
1144
1145                 /* Unknown record type ! */
1146                 } else {
1147                         DEBUG(2, ("Found INVALID record %s -> %s\n", key.dptr, value.dptr));
1148                         /* do not increment num_maps */
1149                 }
1150         }
1151
1152         return 0;
1153 }
1154
1155 /**********************************
1156  Dump all mappings out
1157 **********************************/
1158
1159 static NTSTATUS idmap_tdb_dump_data(struct idmap_domain *dom, struct id_map **maps, int *num_maps)
1160 {
1161         struct idmap_tdb_context *ctx;
1162         struct dump_data *data;
1163         NTSTATUS ret = NT_STATUS_OK;
1164
1165         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
1166
1167         data = talloc_zero(ctx, struct dump_data);
1168         if ( ! data) {
1169                 DEBUG(0, ("Out of memory!\n"));
1170                 return NT_STATUS_NO_MEMORY;
1171         }
1172         data->maps = maps;
1173         data->num_maps = num_maps;
1174         data->ret = NT_STATUS_OK;
1175
1176         tdb_traverse(ctx->tdb, idmap_tdb_dump_one_entry, data);
1177
1178         if ( ! NT_STATUS_IS_OK(data->ret)) {
1179                 ret = data->ret;
1180         }
1181
1182         talloc_free(data);
1183         return ret;
1184 }
1185
1186 static struct idmap_methods db_methods = {
1187
1188         .init = idmap_tdb_db_init,
1189         .unixids_to_sids = idmap_tdb_unixids_to_sids,
1190         .sids_to_unixids = idmap_tdb_sids_to_unixids,
1191         .set_mapping = idmap_tdb_set_mapping,
1192         .remove_mapping = idmap_tdb_remove_mapping,
1193         .dump_data = idmap_tdb_dump_data,
1194         .close_fn = idmap_tdb_close
1195 };
1196
1197 static struct idmap_alloc_methods db_alloc_methods = {
1198
1199         .init = idmap_tdb_alloc_init,
1200         .allocate_id = idmap_tdb_allocate_id,
1201         .get_id_hwm = idmap_tdb_get_hwm,
1202         .set_id_hwm = idmap_tdb_set_hwm,
1203         .close_fn = idmap_tdb_alloc_close
1204 };
1205
1206 NTSTATUS idmap_alloc_tdb_init(void)
1207 {
1208         return smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_alloc_methods);
1209 }
1210
1211 NTSTATUS idmap_tdb_init(void)
1212 {
1213         NTSTATUS ret;
1214
1215         /* FIXME: bad hack to actually register also the alloc_tdb module without changining configure.in */
1216         ret = idmap_alloc_tdb_init();
1217         if (! NT_STATUS_IS_OK(ret)) {
1218                 return ret;
1219         }
1220         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
1221 }