s3:idmap_tdb2: untangle assignment and check in idmap_tdb2_alloc_load()
[sfrench/samba-autobuild/.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 static NTSTATUS idmap_tdb2_allocate_id(struct unixid *xid)
197 {
198         bool ret;
199         const char *hwmkey;
200         const char *hwmtype;
201         uint32_t high_hwm;
202         uint32_t hwm;
203         int res;
204         NTSTATUS status;
205
206         status = idmap_tdb2_open_db();
207         NT_STATUS_NOT_OK_RETURN(status);
208
209         /* Get current high water mark */
210         switch (xid->type) {
211
212         case ID_TYPE_UID:
213                 hwmkey = HWM_USER;
214                 hwmtype = "UID";
215                 high_hwm = idmap_tdb2_state.high_uid;
216                 break;
217
218         case ID_TYPE_GID:
219                 hwmkey = HWM_GROUP;
220                 hwmtype = "GID";
221                 high_hwm = idmap_tdb2_state.high_gid;
222                 break;
223
224         default:
225                 DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
226                 return NT_STATUS_INVALID_PARAMETER;
227         }
228
229         res = idmap_tdb2->transaction_start(idmap_tdb2);
230         if (res != 0) {
231                 DEBUG(1,(__location__ " Failed to start transaction\n"));
232                 return NT_STATUS_UNSUCCESSFUL;
233         }
234
235         if ((hwm = dbwrap_fetch_int32(idmap_tdb2, hwmkey)) == -1) {
236                 idmap_tdb2->transaction_cancel(idmap_tdb2);
237                 return NT_STATUS_INTERNAL_DB_ERROR;
238         }
239
240         /* check it is in the range */
241         if (hwm > high_hwm) {
242                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", 
243                           hwmtype, (unsigned long)high_hwm));
244                 idmap_tdb2->transaction_cancel(idmap_tdb2);
245                 return NT_STATUS_UNSUCCESSFUL;
246         }
247
248         /* fetch a new id and increment it */
249         ret = dbwrap_change_uint32_atomic(idmap_tdb2, hwmkey, &hwm, 1);
250         if (ret == -1) {
251                 DEBUG(1, ("Fatal error while fetching a new %s value\n!", hwmtype));
252                 idmap_tdb2->transaction_cancel(idmap_tdb2);
253                 return NT_STATUS_UNSUCCESSFUL;
254         }
255
256         /* recheck it is in the range */
257         if (hwm > high_hwm) {
258                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n", 
259                           hwmtype, (unsigned long)high_hwm));
260                 idmap_tdb2->transaction_cancel(idmap_tdb2);
261                 return NT_STATUS_UNSUCCESSFUL;
262         }
263
264         res = idmap_tdb2->transaction_commit(idmap_tdb2);
265         if (res != 0) {
266                 DEBUG(1,(__location__ " Failed to commit transaction\n"));
267                 return NT_STATUS_UNSUCCESSFUL;
268         }
269         
270         xid->id = hwm;
271         DEBUG(10,("New %s = %d\n", hwmtype, hwm));
272
273         return NT_STATUS_OK;
274 }
275
276 /*
277   Get current highest id. 
278 */
279 static NTSTATUS idmap_tdb2_get_hwm(struct unixid *xid)
280 {
281         const char *hwmkey;
282         const char *hwmtype;
283         uint32_t hwm;
284         uint32_t high_hwm;
285         NTSTATUS status;
286
287         status = idmap_tdb2_open_db();
288         NT_STATUS_NOT_OK_RETURN(status);
289
290         /* Get current high water mark */
291         switch (xid->type) {
292
293         case ID_TYPE_UID:
294                 hwmkey = HWM_USER;
295                 hwmtype = "UID";
296                 high_hwm = idmap_tdb2_state.high_uid;
297                 break;
298
299         case ID_TYPE_GID:
300                 hwmkey = HWM_GROUP;
301                 hwmtype = "GID";
302                 high_hwm = idmap_tdb2_state.high_gid;
303                 break;
304
305         default:
306                 return NT_STATUS_INVALID_PARAMETER;
307         }
308
309         if ((hwm = dbwrap_fetch_int32(idmap_tdb2, hwmkey)) == -1) {
310                 return NT_STATUS_INTERNAL_DB_ERROR;
311         }
312
313         xid->id = hwm;
314
315         /* Warn if it is out of range */
316         if (hwm >= high_hwm) {
317                 DEBUG(0, ("Warning: %s range full!! (max: %lu)\n", 
318                           hwmtype, (unsigned long)high_hwm));
319         }
320
321         return NT_STATUS_OK;
322 }
323
324 /*
325   Set high id. 
326 */
327 static NTSTATUS idmap_tdb2_set_hwm(struct unixid *xid)
328 {
329         /* not supported, or we would invalidate the cache tdb on
330            other nodes */
331         DEBUG(0,("idmap_tdb2_set_hwm not supported\n"));
332         return NT_STATUS_NOT_SUPPORTED;
333 }
334
335 /*
336   Close the alloc tdb 
337 */
338 static NTSTATUS idmap_tdb2_alloc_close(void)
339 {
340         /* don't actually close it */
341         return NT_STATUS_OK;
342 }
343
344 /*
345   IDMAP MAPPING TDB BACKEND
346 */
347 struct idmap_tdb2_context {
348         uint32_t filter_low_id;
349         uint32_t filter_high_id;
350 };
351
352 /*
353   Initialise idmap database. 
354 */
355 static NTSTATUS idmap_tdb2_db_init(struct idmap_domain *dom,
356                                    const char *params)
357 {
358         NTSTATUS ret;
359         struct idmap_tdb2_context *ctx;
360         char *config_option = NULL;
361         const char *range;
362         NTSTATUS status;
363
364         status = idmap_tdb2_open_db();
365         NT_STATUS_NOT_OK_RETURN(status);
366
367         ctx = talloc(dom, struct idmap_tdb2_context);
368         if ( ! ctx) {
369                 DEBUG(0, ("Out of memory!\n"));
370                 return NT_STATUS_NO_MEMORY;
371         }
372
373         config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
374         if ( ! config_option) {
375                 DEBUG(0, ("Out of memory!\n"));
376                 ret = NT_STATUS_NO_MEMORY;
377                 goto failed;
378         }
379
380         range = lp_parm_const_string(-1, config_option, "range", NULL);
381         if (( ! range) ||
382             (sscanf(range, "%u - %u", &ctx->filter_low_id, &ctx->filter_high_id) != 2) ||
383             (ctx->filter_low_id > ctx->filter_high_id)) {
384                 ctx->filter_low_id = 0;
385                 ctx->filter_high_id = 0;
386         }
387
388         dom->private_data = ctx;
389
390         talloc_free(config_option);
391         return NT_STATUS_OK;
392
393 failed:
394         talloc_free(ctx);
395         return ret;
396 }
397
398
399 /*
400   run a script to perform a mapping
401
402   The script should the following command lines:
403
404       SIDTOID S-1-xxxx
405       IDTOSID UID xxxx
406       IDTOSID GID xxxx
407
408   and should return one of the following as a single line of text
409      UID:xxxx
410      GID:xxxx
411      SID:xxxx
412      ERR:xxxx
413  */
414 static NTSTATUS idmap_tdb2_script(struct idmap_tdb2_context *ctx, struct id_map *map,
415                                   const char *fmt, ...)
416 {
417         va_list ap;
418         char *cmd;
419         FILE *p;
420         char line[64];
421         unsigned long v;
422
423         cmd = talloc_asprintf(ctx, "%s ", idmap_tdb2_state.idmap_script);
424         NT_STATUS_HAVE_NO_MEMORY(cmd);  
425
426         va_start(ap, fmt);
427         cmd = talloc_vasprintf_append(cmd, fmt, ap);
428         va_end(ap);
429         NT_STATUS_HAVE_NO_MEMORY(cmd);
430
431         p = popen(cmd, "r");
432         talloc_free(cmd);
433         if (p == NULL) {
434                 return NT_STATUS_NONE_MAPPED;
435         }
436
437         if (fgets(line, sizeof(line)-1, p) == NULL) {
438                 pclose(p);
439                 return NT_STATUS_NONE_MAPPED;
440         }
441         pclose(p);
442
443         DEBUG(10,("idmap script gave: %s\n", line));
444
445         if (sscanf(line, "UID:%lu", &v) == 1) {
446                 map->xid.id   = v;
447                 map->xid.type = ID_TYPE_UID;
448         } else if (sscanf(line, "GID:%lu", &v) == 1) {
449                 map->xid.id   = v;
450                 map->xid.type = ID_TYPE_GID;            
451         } else if (strncmp(line, "SID:S-", 6) == 0) {
452                 if (!string_to_sid(map->sid, &line[4])) {
453                         DEBUG(0,("Bad SID in '%s' from idmap script %s\n",
454                                  line, idmap_tdb2_state.idmap_script));
455                         return NT_STATUS_NONE_MAPPED;                   
456                 }
457         } else {
458                 DEBUG(0,("Bad reply '%s' from idmap script %s\n",
459                          line, idmap_tdb2_state.idmap_script));
460                 return NT_STATUS_NONE_MAPPED;
461         }
462
463         return NT_STATUS_OK;
464 }
465
466
467
468 /*
469   Single id to sid lookup function. 
470 */
471 static NTSTATUS idmap_tdb2_id_to_sid(struct idmap_tdb2_context *ctx, struct id_map *map)
472 {
473         NTSTATUS ret;
474         TDB_DATA data;
475         char *keystr;
476         NTSTATUS status;
477
478         status = idmap_tdb2_open_db();
479         NT_STATUS_NOT_OK_RETURN(status);
480
481         if (!ctx || !map) {
482                 return NT_STATUS_INVALID_PARAMETER;
483         }
484
485         /* apply filters before checking */
486         if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
487             (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
488                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
489                                 map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
490                 return NT_STATUS_NONE_MAPPED;
491         }
492
493         switch (map->xid.type) {
494
495         case ID_TYPE_UID:
496                 keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
497                 break;
498                 
499         case ID_TYPE_GID:
500                 keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
501                 break;
502
503         default:
504                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
505                 return NT_STATUS_INVALID_PARAMETER;
506         }
507
508         /* final SAFE_FREE safe */
509         data.dptr = NULL;
510
511         if (keystr == NULL) {
512                 DEBUG(0, ("Out of memory!\n"));
513                 ret = NT_STATUS_NO_MEMORY;
514                 goto done;
515         }
516
517         DEBUG(10,("Fetching record %s\n", keystr));
518
519         /* Check if the mapping exists */
520         data = dbwrap_fetch_bystring(idmap_tdb2, keystr, keystr);
521
522         if (!data.dptr) {
523                 fstring sidstr;
524
525                 DEBUG(10,("Record %s not found\n", keystr));
526                 if (idmap_tdb2_state.idmap_script == NULL) {
527                         ret = NT_STATUS_NONE_MAPPED;
528                         goto done;
529                 }
530
531                 ret = idmap_tdb2_script(ctx, map, "IDTOSID %s", keystr);
532
533                 /* store it on shared storage */
534                 if (!NT_STATUS_IS_OK(ret)) {
535                         goto done;
536                 }
537
538                 if (sid_to_fstring(sidstr, map->sid)) {
539                         /* both forward and reverse mappings */
540                         dbwrap_store_bystring(idmap_tdb2, keystr,
541                                             string_term_tdb_data(sidstr), 
542                                             TDB_REPLACE);
543                         dbwrap_store_bystring(idmap_tdb2, sidstr,
544                                             string_term_tdb_data(keystr), 
545                                             TDB_REPLACE);
546                 }
547                 goto done;
548         }
549                 
550         if (!string_to_sid(map->sid, (const char *)data.dptr)) {
551                 DEBUG(10,("INVALID SID (%s) in record %s\n",
552                         (const char *)data.dptr, keystr));
553                 ret = NT_STATUS_INTERNAL_DB_ERROR;
554                 goto done;
555         }
556
557         DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr));
558         ret = NT_STATUS_OK;
559
560 done:
561         talloc_free(keystr);
562         return ret;
563 }
564
565
566 /*
567  Single sid to id lookup function. 
568 */
569 static NTSTATUS idmap_tdb2_sid_to_id(struct idmap_tdb2_context *ctx, struct id_map *map)
570 {
571         NTSTATUS ret;
572         TDB_DATA data;
573         char *keystr;
574         unsigned long rec_id = 0;
575         TALLOC_CTX *tmp_ctx = talloc_stackframe();
576
577         ret = idmap_tdb2_open_db();
578         NT_STATUS_NOT_OK_RETURN(ret);
579
580         keystr = sid_string_talloc(tmp_ctx, map->sid);
581         if (keystr == NULL) {
582                 DEBUG(0, ("Out of memory!\n"));
583                 ret = NT_STATUS_NO_MEMORY;
584                 goto done;
585         }
586
587         DEBUG(10,("Fetching record %s\n", keystr));
588
589         /* Check if sid is present in database */
590         data = dbwrap_fetch_bystring(idmap_tdb2, tmp_ctx, keystr);
591         if (!data.dptr) {
592                 fstring idstr;
593
594                 DEBUG(10,(__location__ " Record %s not found\n", keystr));
595
596                 if (idmap_tdb2_state.idmap_script == NULL) {
597                         ret = NT_STATUS_NONE_MAPPED;
598                         goto done;
599                 }
600                         
601                 ret = idmap_tdb2_script(ctx, map, "SIDTOID %s", keystr);
602                 /* store it on shared storage */
603                 if (!NT_STATUS_IS_OK(ret)) {
604                         goto done;
605                 }
606
607                 snprintf(idstr, sizeof(idstr), "%cID %lu", 
608                          map->xid.type == ID_TYPE_UID?'U':'G',
609                          (unsigned long)map->xid.id);
610                 /* store both forward and reverse mappings */
611                 dbwrap_store_bystring(idmap_tdb2, keystr, string_term_tdb_data(idstr),
612                                     TDB_REPLACE);
613                 dbwrap_store_bystring(idmap_tdb2, idstr, string_term_tdb_data(keystr),
614                                     TDB_REPLACE);
615                 goto done;
616         }
617
618         /* What type of record is this ? */
619         if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
620                 map->xid.id = rec_id;
621                 map->xid.type = ID_TYPE_UID;
622                 DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr ));
623                 ret = NT_STATUS_OK;
624
625         } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
626                 map->xid.id = rec_id;
627                 map->xid.type = ID_TYPE_GID;
628                 DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr ));
629                 ret = NT_STATUS_OK;
630
631         } else { /* Unknown record type ! */
632                 DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr));
633                 ret = NT_STATUS_INTERNAL_DB_ERROR;
634         }
635         
636         /* apply filters before returning result */
637         if ((ctx->filter_low_id && (map->xid.id < ctx->filter_low_id)) ||
638             (ctx->filter_high_id && (map->xid.id > ctx->filter_high_id))) {
639                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
640                                 map->xid.id, ctx->filter_low_id, ctx->filter_high_id));
641                 ret = NT_STATUS_NONE_MAPPED;
642         }
643
644 done:
645         talloc_free(tmp_ctx);
646         return ret;
647 }
648
649 /*
650   lookup a set of unix ids. 
651 */
652 static NTSTATUS idmap_tdb2_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
653 {
654         struct idmap_tdb2_context *ctx;
655         NTSTATUS ret;
656         int i;
657
658         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
659
660         for (i = 0; ids[i]; i++) {
661                 ret = idmap_tdb2_id_to_sid(ctx, ids[i]);
662                 if ( ! NT_STATUS_IS_OK(ret)) {
663
664                         /* if it is just a failed mapping continue */
665                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
666
667                                 /* make sure it is marked as unmapped */
668                                 ids[i]->status = ID_UNMAPPED;
669                                 continue;
670                         }
671                         
672                         /* some fatal error occurred, return immediately */
673                         goto done;
674                 }
675
676                 /* all ok, id is mapped */
677                 ids[i]->status = ID_MAPPED;
678         }
679
680         ret = NT_STATUS_OK;
681
682 done:
683         return ret;
684 }
685
686 /*
687   lookup a set of sids. 
688 */
689 static NTSTATUS idmap_tdb2_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
690 {
691         struct idmap_tdb2_context *ctx;
692         NTSTATUS ret;
693         int i;
694
695         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
696
697         for (i = 0; ids[i]; i++) {
698                 ret = idmap_tdb2_sid_to_id(ctx, ids[i]);
699                 if ( ! NT_STATUS_IS_OK(ret)) {
700
701                         /* if it is just a failed mapping continue */
702                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
703
704                                 /* make sure it is marked as unmapped */
705                                 ids[i]->status = ID_UNMAPPED;
706                                 continue;
707                         }
708                         
709                         /* some fatal error occurred, return immediately */
710                         goto done;
711                 }
712
713                 /* all ok, id is mapped */
714                 ids[i]->status = ID_MAPPED;
715         }
716
717         ret = NT_STATUS_OK;
718
719 done:
720         return ret;
721 }
722
723
724 /*
725   set a mapping. 
726 */
727 static NTSTATUS idmap_tdb2_set_mapping(struct idmap_domain *dom, const struct id_map *map)
728 {
729         struct idmap_tdb2_context *ctx;
730         NTSTATUS ret;
731         TDB_DATA data;
732         char *ksidstr, *kidstr;
733         int res;
734         bool started_transaction = false;
735
736         if (!map || !map->sid) {
737                 return NT_STATUS_INVALID_PARAMETER;
738         }
739
740         ksidstr = kidstr = NULL;
741
742         /* TODO: should we filter a set_mapping using low/high filters ? */
743         
744         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
745
746         switch (map->xid.type) {
747
748         case ID_TYPE_UID:
749                 kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
750                 break;
751                 
752         case ID_TYPE_GID:
753                 kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
754                 break;
755
756         default:
757                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
758                 return NT_STATUS_INVALID_PARAMETER;
759         }
760
761         if (kidstr == NULL) {
762                 DEBUG(0, ("ERROR: Out of memory!\n"));
763                 ret = NT_STATUS_NO_MEMORY;
764                 goto done;
765         }
766
767         if (!(ksidstr = sid_string_talloc(ctx, map->sid))) {
768                 DEBUG(0, ("Out of memory!\n"));
769                 ret = NT_STATUS_NO_MEMORY;
770                 goto done;
771         }
772
773         DEBUG(10, ("Storing %s <-> %s map\n", ksidstr, kidstr));
774
775         res = idmap_tdb2->transaction_start(idmap_tdb2);
776         if (res != 0) {
777                 DEBUG(1,(__location__ " Failed to start transaction\n"));
778                 ret = NT_STATUS_UNSUCCESSFUL;
779                 goto done;
780         }
781
782         started_transaction = true;
783         
784         /* check wheter sid mapping is already present in db */
785         data = dbwrap_fetch_bystring(idmap_tdb2, ksidstr, ksidstr);
786         if (data.dptr) {
787                 ret = NT_STATUS_OBJECT_NAME_COLLISION;
788                 goto done;
789         }
790
791         ret = dbwrap_store_bystring(idmap_tdb2, ksidstr, string_term_tdb_data(kidstr),
792                                   TDB_INSERT);
793         if (!NT_STATUS_IS_OK(ret)) {
794                 DEBUG(0, ("Error storing SID -> ID: %s\n", nt_errstr(ret)));
795                 goto done;
796         }
797         ret = dbwrap_store_bystring(idmap_tdb2, kidstr, string_term_tdb_data(ksidstr),
798                                   TDB_INSERT);
799         if (!NT_STATUS_IS_OK(ret)) {
800                 DEBUG(0, ("Error storing ID -> SID: %s\n", nt_errstr(ret)));
801                 /* try to remove the previous stored SID -> ID map */
802                 dbwrap_delete_bystring(idmap_tdb2, ksidstr);
803                 goto done;
804         }
805
806         started_transaction = false;
807
808         res = idmap_tdb2->transaction_commit(idmap_tdb2);
809         if (res != 0) {
810                 DEBUG(1,(__location__ " Failed to commit transaction\n"));
811                 ret = NT_STATUS_UNSUCCESSFUL;
812                 goto done;
813         }
814
815         DEBUG(10,("Stored %s <-> %s\n", ksidstr, kidstr));
816         ret = NT_STATUS_OK;
817
818 done:
819         if (started_transaction) {
820                 idmap_tdb2->transaction_cancel(idmap_tdb2);
821         }
822         talloc_free(ksidstr);
823         talloc_free(kidstr);
824         return ret;
825 }
826
827 /*
828   remove a mapping. 
829 */
830 static NTSTATUS idmap_tdb2_remove_mapping(struct idmap_domain *dom, const struct id_map *map)
831 {
832         /* not supported as it would invalidate the cache tdb on other
833            nodes */
834         DEBUG(0,("idmap_tdb2_remove_mapping not supported\n"));
835         return NT_STATUS_NOT_SUPPORTED;
836 }
837
838 /*
839   Close the idmap tdb instance
840 */
841 static NTSTATUS idmap_tdb2_close(struct idmap_domain *dom)
842 {
843         /* don't do anything */
844         return NT_STATUS_OK;
845 }
846
847
848 /*
849   Dump all mappings out
850 */
851 static NTSTATUS idmap_tdb2_dump_data(struct idmap_domain *dom, struct id_map **maps, int *num_maps)
852 {
853         DEBUG(0,("idmap_tdb2_dump_data not supported\n"));
854         return NT_STATUS_NOT_SUPPORTED;
855 }
856
857 static struct idmap_methods db_methods = {
858         .init            = idmap_tdb2_db_init,
859         .unixids_to_sids = idmap_tdb2_unixids_to_sids,
860         .sids_to_unixids = idmap_tdb2_sids_to_unixids,
861         .set_mapping     = idmap_tdb2_set_mapping,
862         .remove_mapping  = idmap_tdb2_remove_mapping,
863         .dump_data       = idmap_tdb2_dump_data,
864         .close_fn        = idmap_tdb2_close
865 };
866
867 static struct idmap_alloc_methods db_alloc_methods = {
868         .init        = idmap_tdb2_alloc_init,
869         .allocate_id = idmap_tdb2_allocate_id,
870         .get_id_hwm  = idmap_tdb2_get_hwm,
871         .set_id_hwm  = idmap_tdb2_set_hwm,
872         .close_fn    = idmap_tdb2_alloc_close
873 };
874
875 NTSTATUS idmap_tdb2_init(void)
876 {
877         NTSTATUS ret;
878
879         /* register both backends */
880         ret = smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION, "tdb2", &db_alloc_methods);
881         if (! NT_STATUS_IS_OK(ret)) {
882                 DEBUG(0, ("Unable to register idmap alloc tdb2 module: %s\n", get_friendly_nt_error_msg(ret)));
883                 return ret;
884         }
885
886         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb2", &db_methods);
887 }