idmap_tdb: Remove a variable never used
[samba.git] / source3 / winbindd / idmap_tdb_common.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    common functions for TDB based idmapping backends
5
6    Copyright (C) Christian Ambach 2012
7
8    These functions were initially copied over from idmap_tdb.c and idmap_tdb2.c
9    which are:
10
11    Copyright (C) Tim Potter 2000
12    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
13    Copyright (C) Jeremy Allison 2006
14    Copyright (C) Simo Sorce 2003-2006
15    Copyright (C) Michael Adam 2009-2010
16    Copyright (C) Andrew Tridgell 2007
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 "idmap_tdb_common.h"
35 #include "dbwrap/dbwrap.h"
36 #include "util_tdb.h"
37 #include "idmap_rw.h"
38 #include "../libcli/security/dom_sid.h"
39
40 #undef DBGC_CLASS
41 #define DBGC_CLASS DBGC_IDMAP
42
43 struct idmap_tdb_common_allocate_id_context {
44         const char *hwmkey;
45         const char *hwmtype;
46         uint32_t high_hwm;
47         uint32_t hwm;
48 };
49
50 static NTSTATUS idmap_tdb_common_allocate_id_action(struct db_context *db,
51                                                     void *private_data)
52 {
53         NTSTATUS ret;
54         struct idmap_tdb_common_allocate_id_context *state = private_data;
55         uint32_t hwm;
56
57         ret = dbwrap_fetch_uint32_bystring(db, state->hwmkey, &hwm);
58         if (!NT_STATUS_IS_OK(ret)) {
59                 ret = NT_STATUS_INTERNAL_DB_ERROR;
60                 goto done;
61         }
62
63         /* check it is in the range */
64         if (hwm > state->high_hwm) {
65                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
66                           state->hwmtype, (unsigned long)state->high_hwm));
67                 ret = NT_STATUS_UNSUCCESSFUL;
68                 goto done;
69         }
70
71         /* fetch a new id and increment it */
72         ret = dbwrap_change_uint32_atomic_bystring(db, state->hwmkey, &hwm, 1);
73         if (!NT_STATUS_IS_OK(ret)) {
74                 DEBUG(1, ("Fatal error while fetching a new %s value\n!",
75                           state->hwmtype));
76                 goto done;
77         }
78
79         /* recheck it is in the range */
80         if (hwm > state->high_hwm) {
81                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
82                           state->hwmtype, (unsigned long)state->high_hwm));
83                 ret = NT_STATUS_UNSUCCESSFUL;
84                 goto done;
85         }
86
87         ret = NT_STATUS_OK;
88         state->hwm = hwm;
89
90       done:
91         return ret;
92 }
93
94 static NTSTATUS idmap_tdb_common_allocate_id(struct idmap_domain *dom,
95                                              struct unixid *xid)
96 {
97         const char *hwmkey;
98         const char *hwmtype;
99         uint32_t hwm = 0;
100         NTSTATUS status;
101         struct idmap_tdb_common_allocate_id_context state;
102         struct idmap_tdb_common_context *ctx;
103
104         ctx =
105             talloc_get_type_abort(dom->private_data,
106                                   struct idmap_tdb_common_context);
107
108         /* Get current high water mark */
109         switch (xid->type) {
110
111         case ID_TYPE_UID:
112                 hwmkey = ctx->hwmkey_uid;
113                 hwmtype = "UID";
114                 break;
115
116         case ID_TYPE_GID:
117                 hwmkey = ctx->hwmkey_gid;
118                 hwmtype = "GID";
119                 break;
120
121         case ID_TYPE_BOTH:
122                 /*
123                  * This is not supported here yet and
124                  * already handled in idmap_rw_new_mapping()
125                  */
126                 FALL_THROUGH;
127         case ID_TYPE_NOT_SPECIFIED:
128                 /*
129                  * This is handled in idmap_rw_new_mapping()
130                  */
131                 FALL_THROUGH;
132         default:
133                 DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
134                 return NT_STATUS_INVALID_PARAMETER;
135         }
136
137         state.hwm = hwm;
138         state.high_hwm = ctx->max_id;
139         state.hwmtype = hwmtype;
140         state.hwmkey = hwmkey;
141
142         status = dbwrap_trans_do(ctx->db, idmap_tdb_common_allocate_id_action,
143                                  &state);
144
145         if (NT_STATUS_IS_OK(status)) {
146                 xid->id = state.hwm;
147                 DEBUG(10, ("New %s = %d\n", hwmtype, state.hwm));
148         } else {
149                 DEBUG(1, ("Error allocating a new %s\n", hwmtype));
150         }
151
152         return status;
153 }
154
155 /**
156  * Allocate a new unix-ID.
157  * For now this is for the default idmap domain only.
158  * Should be extended later on.
159  */
160 NTSTATUS idmap_tdb_common_get_new_id(struct idmap_domain * dom,
161                                      struct unixid * id)
162 {
163         NTSTATUS ret;
164
165         if (!strequal(dom->name, "*")) {
166                 DEBUG(3, ("idmap_tdb_common_get_new_id: "
167                           "Refusing allocation of a new unixid for domain'%s'. "
168                           "Currently only supported for the default "
169                           "domain \"*\".\n", dom->name));
170                 return NT_STATUS_NOT_IMPLEMENTED;
171         }
172
173         ret = idmap_tdb_common_allocate_id(dom, id);
174
175         return ret;
176 }
177
178 /**
179  * store a mapping in the database.
180  */
181
182 struct idmap_tdb_common_set_mapping_context {
183         const char *ksidstr;
184         const char *kidstr;
185 };
186
187 static NTSTATUS idmap_tdb_common_set_mapping_action(struct db_context *db,
188                                                     void *private_data)
189 {
190         TDB_DATA data;
191         NTSTATUS ret;
192         struct idmap_tdb_common_set_mapping_context *state = private_data;
193         TALLOC_CTX *tmp_ctx = talloc_stackframe();
194
195         DEBUG(10, ("Storing %s <-> %s map\n", state->ksidstr, state->kidstr));
196
197         /* check whether sid mapping is already present in db */
198         ret = dbwrap_fetch_bystring(db, tmp_ctx, state->ksidstr, &data);
199         if (NT_STATUS_IS_OK(ret)) {
200                 ret = NT_STATUS_OBJECT_NAME_COLLISION;
201                 goto done;
202         }
203
204         ret = dbwrap_store_bystring(db, state->ksidstr,
205                                     string_term_tdb_data(state->kidstr),
206                                     TDB_INSERT);
207         if (!NT_STATUS_IS_OK(ret)) {
208                 DEBUG(0, ("Error storing SID -> ID: %s\n", nt_errstr(ret)));
209                 goto done;
210         }
211
212         ret = dbwrap_store_bystring(db, state->kidstr,
213                                     string_term_tdb_data(state->ksidstr),
214                                     TDB_INSERT);
215         if (!NT_STATUS_IS_OK(ret)) {
216                 DEBUG(0, ("Error storing ID -> SID: %s\n", nt_errstr(ret)));
217                 /* try to remove the previous stored SID -> ID map */
218                 dbwrap_delete_bystring(db, state->ksidstr);
219                 goto done;
220         }
221
222         DEBUG(10, ("Stored %s <-> %s\n", state->ksidstr, state->kidstr));
223
224       done:
225         talloc_free(tmp_ctx);
226         return ret;
227 }
228
229 NTSTATUS idmap_tdb_common_set_mapping(struct idmap_domain * dom,
230                                       const struct id_map * map)
231 {
232         struct idmap_tdb_common_context *ctx;
233         struct idmap_tdb_common_set_mapping_context state;
234         NTSTATUS ret;
235         struct dom_sid_buf ksidstr;
236         char *kidstr = NULL;
237
238         if (!map || !map->sid) {
239                 return NT_STATUS_INVALID_PARAMETER;
240         }
241
242         /* TODO: should we filter a set_mapping using low/high filters ? */
243
244         ctx =
245             talloc_get_type_abort(dom->private_data,
246                                   struct idmap_tdb_common_context);
247
248         switch (map->xid.type) {
249
250         case ID_TYPE_UID:
251                 kidstr =
252                     talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
253                 break;
254
255         case ID_TYPE_GID:
256                 kidstr =
257                     talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
258                 break;
259
260         default:
261                 DEBUG(2, ("INVALID unix ID type: 0x%02x\n", map->xid.type));
262                 return NT_STATUS_INVALID_PARAMETER;
263         }
264
265         if (kidstr == NULL) {
266                 DEBUG(0, ("ERROR: Out of memory!\n"));
267                 ret = NT_STATUS_NO_MEMORY;
268                 goto done;
269         }
270
271         state.ksidstr = dom_sid_str_buf(map->sid, &ksidstr);
272         state.kidstr = kidstr;
273
274         ret = dbwrap_trans_do(ctx->db, idmap_tdb_common_set_mapping_action,
275                               &state);
276
277       done:
278         talloc_free(kidstr);
279         return ret;
280 }
281
282 /*
283  * Create a new mapping for an unmapped SID, also allocating a new ID.
284  * This should be run inside a transaction.
285  *
286  * TODO:
287  *  Properly integrate this with multi domain idmap config:
288  *  Currently, the allocator is default-config only.
289  */
290 NTSTATUS idmap_tdb_common_new_mapping(struct idmap_domain * dom,
291                                       struct id_map * map)
292 {
293         NTSTATUS ret;
294         struct idmap_tdb_common_context *ctx;
295
296         ctx =
297             talloc_get_type_abort(dom->private_data,
298                                   struct idmap_tdb_common_context);
299
300         ret = idmap_rw_new_mapping(dom, ctx->rw_ops, map);
301
302         return ret;
303 }
304
305 /*
306   lookup a set of unix ids
307 */
308 NTSTATUS idmap_tdb_common_unixids_to_sids(struct idmap_domain * dom,
309                                           struct id_map ** ids)
310 {
311         NTSTATUS ret;
312         size_t i, num_mapped = 0;
313         struct idmap_tdb_common_context *ctx;
314
315         NTSTATUS(*unixid_to_sid_fn) (struct idmap_domain * dom,
316                                      struct id_map * map);
317         ctx =
318             talloc_get_type_abort(dom->private_data,
319                                   struct idmap_tdb_common_context);
320
321         if (ctx->unixid_to_sid_fn == NULL) {
322                 unixid_to_sid_fn = idmap_tdb_common_unixid_to_sid;
323         } else {
324                 unixid_to_sid_fn = ctx->unixid_to_sid_fn;
325         }
326
327         /* initialize the status to avoid surprise */
328         for (i = 0; ids[i]; i++) {
329                 ids[i]->status = ID_UNKNOWN;
330         }
331
332         for (i = 0; ids[i]; i++) {
333                 ret = unixid_to_sid_fn(dom, ids[i]);
334                 if (!NT_STATUS_IS_OK(ret)) {
335
336                         /* if it is just a failed mapping continue */
337                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
338
339                                 /* make sure it is marked as unmapped */
340                                 ids[i]->status = ID_UNMAPPED;
341                                 continue;
342                         }
343
344                         /* some fatal error occurred, return immediately */
345                         goto done;
346                 }
347
348                 /* all ok, id is mapped */
349                 ids[i]->status = ID_MAPPED;
350                 num_mapped += 1;
351         }
352
353         ret = NT_STATUS_OK;
354
355 done:
356
357         if (NT_STATUS_IS_OK(ret)) {
358                 if (i == 0 || num_mapped == 0) {
359                         ret = NT_STATUS_NONE_MAPPED;
360                 } else if (num_mapped < i) {
361                         ret = STATUS_SOME_UNMAPPED;
362                 } else {
363                         ret = NT_STATUS_OK;
364                 }
365         }
366
367         return ret;
368 }
369
370 /*
371   default single id to sid lookup function
372 */
373 NTSTATUS idmap_tdb_common_unixid_to_sid(struct idmap_domain * dom,
374                                         struct id_map * map)
375 {
376         NTSTATUS ret;
377         TDB_DATA data;
378         char *keystr;
379         struct idmap_tdb_common_context *ctx;
380
381         if (!dom || !map) {
382                 return NT_STATUS_INVALID_PARAMETER;
383         }
384
385         ctx =
386             talloc_get_type_abort(dom->private_data,
387                                   struct idmap_tdb_common_context);
388
389         /* apply filters before checking */
390         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
391                 DEBUG(5,
392                       ("Requested id (%u) out of range (%u - %u). Filtered!\n",
393                        map->xid.id, dom->low_id, dom->high_id));
394                 return NT_STATUS_NONE_MAPPED;
395         }
396
397         switch (map->xid.type) {
398
399         case ID_TYPE_UID:
400                 keystr =
401                     talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
402                 break;
403
404         case ID_TYPE_GID:
405                 keystr =
406                     talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
407                 break;
408
409         default:
410                 DEBUG(2, ("INVALID unix ID type: 0x%02x\n", map->xid.type));
411                 return NT_STATUS_INVALID_PARAMETER;
412         }
413
414         if (keystr == NULL) {
415                 DEBUG(0, ("Out of memory!\n"));
416                 ret = NT_STATUS_NO_MEMORY;
417                 goto done;
418         }
419
420         DEBUG(10, ("Fetching record %s\n", keystr));
421
422         /* Check if the mapping exists */
423         ret = dbwrap_fetch_bystring(ctx->db, keystr, keystr, &data);
424
425         if (!NT_STATUS_IS_OK(ret)) {
426                 DEBUG(10, ("Record %s not found\n", keystr));
427                 ret = NT_STATUS_NONE_MAPPED;
428                 goto done;
429         }
430
431         if ((data.dsize == 0) || (data.dptr[data.dsize-1] != '\0')) {
432                 DBG_DEBUG("Invalid record length %zu\n", data.dsize);
433                 ret = NT_STATUS_INTERNAL_DB_ERROR;
434                 goto done;
435         }
436
437         if (!string_to_sid(map->sid, (const char *)data.dptr)) {
438                 DEBUG(10, ("INVALID SID (%s) in record %s\n",
439                            (const char *)data.dptr, keystr));
440                 ret = NT_STATUS_INTERNAL_DB_ERROR;
441                 goto done;
442         }
443
444         DEBUG(10, ("Found record %s -> %s\n", keystr, (const char *)data.dptr));
445         ret = NT_STATUS_OK;
446
447       done:
448         talloc_free(keystr);
449         return ret;
450 }
451
452 /**********************************
453  Single sid to id lookup function.
454 **********************************/
455
456 NTSTATUS idmap_tdb_common_sid_to_unixid(struct idmap_domain * dom,
457                                         struct id_map * map)
458 {
459         NTSTATUS ret;
460         TDB_DATA data;
461         struct dom_sid_buf keystr;
462         unsigned long rec_id = 0;
463         struct idmap_tdb_common_context *ctx;
464         TALLOC_CTX *tmp_ctx = talloc_stackframe();
465
466         if (!dom || !map) {
467                 talloc_free(tmp_ctx);
468                 return NT_STATUS_INVALID_PARAMETER;
469         }
470
471         ctx =
472             talloc_get_type_abort(dom->private_data,
473                                   struct idmap_tdb_common_context);
474
475         dom_sid_str_buf(map->sid, &keystr);
476
477         DEBUG(10, ("Fetching record %s\n", keystr.buf));
478
479         /* Check if sid is present in database */
480         ret = dbwrap_fetch_bystring(ctx->db, tmp_ctx, keystr.buf, &data);
481         if (!NT_STATUS_IS_OK(ret)) {
482                 DEBUG(10, ("Record %s not found\n", keystr.buf));
483                 ret = NT_STATUS_NONE_MAPPED;
484                 goto done;
485         }
486
487         /* What type of record is this ? */
488         if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) {
489                 /* Try a UID record. */
490                 map->xid.id = rec_id;
491                 map->xid.type = ID_TYPE_UID;
492                 DEBUG(10,
493                       ("Found uid record %s -> %s \n", keystr.buf,
494                        (const char *)data.dptr));
495                 ret = NT_STATUS_OK;
496
497         } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) {
498                 /* Try a GID record. */
499                 map->xid.id = rec_id;
500                 map->xid.type = ID_TYPE_GID;
501                 DEBUG(10,
502                       ("Found gid record %s -> %s \n", keystr.buf,
503                        (const char *)data.dptr));
504                 ret = NT_STATUS_OK;
505
506         } else {                /* Unknown record type ! */
507                 DEBUG(2,
508                       ("Found INVALID record %s -> %s\n", keystr.buf,
509                        (const char *)data.dptr));
510                 ret = NT_STATUS_INTERNAL_DB_ERROR;
511                 goto done;
512         }
513
514         /* apply filters before returning result */
515         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
516                 DEBUG(5,
517                       ("Requested id (%u) out of range (%u - %u). Filtered!\n",
518                        map->xid.id, dom->low_id, dom->high_id));
519                 ret = NT_STATUS_NONE_MAPPED;
520         }
521
522       done:
523         talloc_free(tmp_ctx);
524         return ret;
525 }
526
527 /**********************************
528  lookup a set of sids
529 **********************************/
530
531 struct idmap_tdb_common_sids_to_unixids_context {
532         struct idmap_domain *dom;
533         struct id_map **ids;
534         bool allocate_unmapped;
535          NTSTATUS(*sid_to_unixid_fn) (struct idmap_domain * dom,
536                                       struct id_map * map);
537 };
538
539 static NTSTATUS idmap_tdb_common_sids_to_unixids_action(struct db_context *db,
540                                                         void *private_data)
541 {
542         struct idmap_tdb_common_sids_to_unixids_context *state = private_data;
543         size_t i, num_mapped = 0, num_required = 0;
544         NTSTATUS ret = NT_STATUS_OK;
545
546         DEBUG(10, ("idmap_tdb_common_sids_to_unixids: "
547                    " domain: [%s], allocate: %s\n",
548                    state->dom->name, state->allocate_unmapped ? "yes" : "no"));
549
550         for (i = 0; state->ids[i]; i++) {
551                 if ((state->ids[i]->status == ID_UNKNOWN) ||
552                     /* retry if we could not map in previous run: */
553                     (state->ids[i]->status == ID_UNMAPPED)) {
554                         NTSTATUS ret2;
555
556                         ret2 = state->sid_to_unixid_fn(state->dom,
557                                         state->ids[i]);
558
559                         if (!NT_STATUS_IS_OK(ret2)) {
560
561                                 /* if it is just a failed mapping, continue */
562                                 if (NT_STATUS_EQUAL
563                                     (ret2, NT_STATUS_NONE_MAPPED)) {
564
565                                         /* make sure it is marked as unmapped */
566                                         state->ids[i]->status = ID_UNMAPPED;
567                                         ret = STATUS_SOME_UNMAPPED;
568                                 } else {
569                                         /*
570                                          * some fatal error occurred,
571                                          * return immediately
572                                          */
573                                         ret = ret2;
574                                         goto done;
575                                 }
576                         } else {
577                                 /* all ok, id is mapped */
578                                 state->ids[i]->status = ID_MAPPED;
579                         }
580                 }
581
582                 if (state->ids[i]->status == ID_MAPPED) {
583                         num_mapped += 1;
584                 }
585
586                 if ((state->ids[i]->status == ID_UNMAPPED) &&
587                     state->allocate_unmapped) {
588                         ret =
589                             idmap_tdb_common_new_mapping(state->dom,
590                                                          state->ids[i]);
591                         DBG_DEBUG("idmap_tdb_common_new_mapping returned %s\n",
592                                   nt_errstr(ret));
593                         if (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED)) {
594                                 if (state->ids[i]->status == ID_REQUIRE_TYPE) {
595                                         num_required += 1;
596                                         continue;
597                                 }
598                         }
599                         if (!NT_STATUS_IS_OK(ret)) {
600                                 ret = STATUS_SOME_UNMAPPED;
601                                 continue;
602                         }
603                         num_mapped += 1;
604                 }
605         }
606
607 done:
608
609         if (NT_STATUS_IS_OK(ret) ||
610             NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED)) {
611                 if (i == 0 || num_mapped == 0) {
612                         ret = NT_STATUS_NONE_MAPPED;
613                 } else if (num_mapped < i) {
614                         ret = STATUS_SOME_UNMAPPED;
615                 } else {
616                         ret = NT_STATUS_OK;
617                 }
618                 if (num_required > 0) {
619                         ret = STATUS_SOME_UNMAPPED;
620                 }
621         }
622
623         return ret;
624 }
625
626 NTSTATUS idmap_tdb_common_sids_to_unixids(struct idmap_domain * dom,
627                                           struct id_map ** ids)
628 {
629         NTSTATUS ret;
630         int i;
631         struct idmap_tdb_common_sids_to_unixids_context state;
632         struct idmap_tdb_common_context *ctx;
633
634         ctx =
635             talloc_get_type_abort(dom->private_data,
636                                   struct idmap_tdb_common_context);
637
638         /* initialize the status to avoid surprise */
639         for (i = 0; ids[i]; i++) {
640                 ids[i]->status = ID_UNKNOWN;
641         }
642
643         state.dom = dom;
644         state.ids = ids;
645         state.allocate_unmapped = false;
646         if (ctx->sid_to_unixid_fn == NULL) {
647                 state.sid_to_unixid_fn = idmap_tdb_common_sid_to_unixid;
648         } else {
649                 state.sid_to_unixid_fn = ctx->sid_to_unixid_fn;
650         }
651
652         ret = idmap_tdb_common_sids_to_unixids_action(ctx->db, &state);
653
654         if ( (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED) ||
655               NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) &&
656              !dom->read_only) {
657                 state.allocate_unmapped = true;
658                 ret = dbwrap_trans_do(ctx->db,
659                                       idmap_tdb_common_sids_to_unixids_action,
660                                       &state);
661         }
662
663         return ret;
664 }