s3: Add a "lock_order" argument to db_open
[kai/samba.git] / source3 / winbindd / idmap_autorid.c
1 /*
2  *  idmap_autorid: static map between Active Directory/NT RIDs
3  *  and RFC 2307 accounts
4  *
5  *  based on the idmap_rid module, but this module defines the ranges
6  *  for the domains by automatically allocating a range for each domain
7  *
8  *  Copyright (C) Christian Ambach, 2010-2011
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 3 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "includes.h"
26 #include "system/filesys.h"
27 #include "winbindd.h"
28 #include "dbwrap/dbwrap.h"
29 #include "dbwrap/dbwrap_open.h"
30 #include "idmap.h"
31 #include "../libcli/security/dom_sid.h"
32 #include "util_tdb.h"
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_IDMAP
36
37 #define HWM "NEXT RANGE"
38 #define ALLOC_HWM_UID "NEXT ALLOC UID"
39 #define ALLOC_HWM_GID "NEXT ALLOC GID"
40 #define ALLOC_RANGE "ALLOC"
41 #define CONFIGKEY "CONFIG"
42
43 struct autorid_global_config {
44         uint32_t minvalue;
45         uint32_t rangesize;
46         uint32_t maxranges;
47 };
48
49 struct autorid_domain_config {
50         fstring sid;
51         uint32_t domainnum;
52         struct autorid_global_config *globalcfg;
53 };
54
55 /* handle to the tdb storing domain <-> range assignments */
56 static struct db_context *autorid_db;
57
58 static NTSTATUS idmap_autorid_get_domainrange_action(struct db_context *db,
59                                               void *private_data)
60 {
61         NTSTATUS ret;
62         uint32_t domainnum, hwm;
63         char *numstr;
64         struct autorid_domain_config *cfg;
65
66         cfg = (struct autorid_domain_config *)private_data;
67
68         ret = dbwrap_fetch_uint32(db, cfg->sid, &(cfg->domainnum));
69
70         if (NT_STATUS_IS_OK(ret)) {
71                 /* entry is already present*/
72                 return ret;
73         }
74
75         DEBUG(10, ("Acquiring new range for domain %s\n", cfg->sid));
76
77         /* fetch the current HWM */
78         ret = dbwrap_fetch_uint32(db, HWM, &hwm);
79         if (!NT_STATUS_IS_OK(ret)) {
80                 DEBUG(1, ("Fatal error while fetching current "
81                           "HWM value: %s\n", nt_errstr(ret)));
82                 ret = NT_STATUS_INTERNAL_ERROR;
83                 goto error;
84         }
85
86         /* do we have a range left? */
87         if (hwm >= cfg->globalcfg->maxranges) {
88                 DEBUG(1, ("No more domain ranges available!\n"));
89                 ret = NT_STATUS_NO_MEMORY;
90                 goto error;
91         }
92
93         /* increase the HWM */
94         ret = dbwrap_change_uint32_atomic(db, HWM, &domainnum, 1);
95         if (!NT_STATUS_IS_OK(ret)) {
96                 DEBUG(1, ("Fatal error while fetching a new "
97                           "domain range value!\n"));
98                 goto error;
99         }
100
101         /* store away the new mapping in both directions */
102         ret = dbwrap_store_uint32(db, cfg->sid, domainnum);
103         if (!NT_STATUS_IS_OK(ret)) {
104                 DEBUG(1, ("Fatal error while storing new "
105                           "domain->range assignment!\n"));
106                 goto error;
107         }
108
109         numstr = talloc_asprintf(db, "%u", domainnum);
110         if (!numstr) {
111                 ret = NT_STATUS_NO_MEMORY;
112                 goto error;
113         }
114
115         ret = dbwrap_store_bystring(db, numstr,
116                         string_term_tdb_data(cfg->sid), TDB_INSERT);
117
118         talloc_free(numstr);
119         if (!NT_STATUS_IS_OK(ret)) {
120                 DEBUG(1, ("Fatal error while storing "
121                           "new domain->range assignment!\n"));
122                 goto error;
123         }
124         DEBUG(5, ("Acquired new range #%d for domain %s\n",
125                   domainnum, cfg->sid));
126
127         cfg->domainnum = domainnum;
128
129         return NT_STATUS_OK;
130
131 error:
132         return ret;
133
134 }
135
136 static NTSTATUS idmap_autorid_get_domainrange(struct autorid_domain_config *dom)
137 {
138         NTSTATUS ret;
139
140         /*
141          * try to find mapping without locking the database,
142          * if it is not found create a mapping in a transaction
143          */
144         ret = dbwrap_fetch_uint32(autorid_db, dom->sid, &(dom->domainnum));
145
146         if (!NT_STATUS_IS_OK(ret)) {;
147                 ret = dbwrap_trans_do(autorid_db,
148                               idmap_autorid_get_domainrange_action, dom);
149         }
150
151         DEBUG(10, ("Using range #%d for domain %s\n", dom->domainnum,
152                    dom->sid));
153
154         return ret;
155 }
156
157 static NTSTATUS idmap_autorid_id_to_sid(struct autorid_global_config *cfg,
158                                         struct id_map *map)
159 {
160         uint32_t range;
161         TDB_DATA data;
162         char *keystr;
163         struct dom_sid sid;
164         NTSTATUS status;
165
166         /* can this be one of our ids? */
167         if (map->xid.id < cfg->minvalue) {
168                 DEBUG(10, ("id %d is lower than minimum value, "
169                            "ignoring mapping request\n", map->xid.id));
170                 map->status = ID_UNKNOWN;
171                 return NT_STATUS_OK;
172         }
173
174         if (map->xid.id > (cfg->minvalue + cfg->rangesize * cfg->maxranges)) {
175                 DEBUG(10, ("id %d is outside of maximum id value, "
176                            "ignoring mapping request\n", map->xid.id));
177                 map->status = ID_UNKNOWN;
178                 return NT_STATUS_OK;
179         }
180
181         /* determine the range of this uid */
182         range = ((map->xid.id - cfg->minvalue) / cfg->rangesize);
183
184         keystr = talloc_asprintf(talloc_tos(), "%u", range);
185         if (!keystr) {
186                 return NT_STATUS_NO_MEMORY;
187         }
188
189         status = dbwrap_fetch_bystring(autorid_db, talloc_tos(), keystr, &data);
190         TALLOC_FREE(keystr);
191
192         if (!NT_STATUS_IS_OK(status)) {
193                 DEBUG(4, ("id %d belongs to range %d which does not have "
194                           "domain mapping, ignoring mapping request\n",
195                           map->xid.id, range));
196                 TALLOC_FREE(data.dptr);
197                 map->status = ID_UNKNOWN;
198                 return NT_STATUS_OK;
199         }
200
201         if (strncmp((const char *)data.dptr,
202                     ALLOC_RANGE,
203                     strlen(ALLOC_RANGE)) == 0) {
204                 /* this is from the alloc range, there is no mapping back */
205                 DEBUG(5, ("id %d belongs to alloc range, cannot map back\n",
206                           map->xid.id));
207                 TALLOC_FREE(data.dptr);
208                 map->status = ID_UNKNOWN;
209                 return NT_STATUS_OK;
210         }
211
212         string_to_sid(&sid, (const char *)data.dptr);
213         TALLOC_FREE(data.dptr);
214
215         sid_compose(map->sid, &sid,
216                     (map->xid.id - cfg->minvalue -
217                      range * cfg->rangesize));
218
219         /* We **really** should have some way of validating
220            the SID exists and is the correct type here.  But
221            that is a deficiency in the idmap_rid design. */
222
223         map->status = ID_MAPPED;
224         return NT_STATUS_OK;
225 }
226
227 /**********************************
228  Single sid to id lookup function.
229 **********************************/
230
231 static NTSTATUS idmap_autorid_sid_to_id(struct autorid_global_config *global,
232                                         struct autorid_domain_config *domain,
233                                         struct id_map *map)
234 {
235         uint32_t rid;
236
237         sid_peek_rid(map->sid, &rid);
238
239         /* if the rid is higher than the size of the range, we cannot map it */
240         if (rid >= global->rangesize) {
241                 map->status = ID_UNKNOWN;
242                 DEBUG(2, ("RID %d is larger then size of range (%d), "
243                           "user cannot be mapped\n", rid, global->rangesize));
244                 return NT_STATUS_UNSUCCESSFUL;
245         }
246         map->xid.id = global->minvalue +
247             (global->rangesize * domain->domainnum)+rid;
248
249         /* We **really** should have some way of validating
250            the SID exists and is the correct type here.  But
251            that is a deficiency in the idmap_rid design. */
252
253         map->status = ID_MAPPED;
254
255         return NT_STATUS_OK;
256 }
257
258 /**********************************
259  lookup a set of unix ids.
260 **********************************/
261
262 static NTSTATUS idmap_autorid_unixids_to_sids(struct idmap_domain *dom,
263                                               struct id_map **ids)
264 {
265         struct autorid_global_config *globalcfg;
266         NTSTATUS ret;
267         int i;
268
269         /* initialize the status to avoid surprise */
270         for (i = 0; ids[i]; i++) {
271                 ids[i]->status = ID_UNKNOWN;
272         }
273
274         globalcfg = talloc_get_type(dom->private_data,
275                                     struct autorid_global_config);
276
277         for (i = 0; ids[i]; i++) {
278
279                 ret = idmap_autorid_id_to_sid(globalcfg, ids[i]);
280
281                 if ((!NT_STATUS_IS_OK(ret)) &&
282                     (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
283                         /* some fatal error occurred, log it */
284                         DEBUG(3, ("Unexpected error resolving an ID "
285                                   " (%d)\n", ids[i]->xid.id));
286                         goto failure;
287                 }
288         }
289         return NT_STATUS_OK;
290
291       failure:
292         return ret;
293 }
294
295 /**********************************
296  lookup a set of sids.
297 **********************************/
298
299 static NTSTATUS idmap_autorid_sids_to_unixids(struct idmap_domain *dom,
300                                               struct id_map **ids)
301 {
302         struct autorid_global_config *global;
303         NTSTATUS ret;
304         int i;
305
306         /* initialize the status to avoid surprise */
307         for (i = 0; ids[i]; i++) {
308                 ids[i]->status = ID_UNKNOWN;
309         }
310
311         global = talloc_get_type(dom->private_data,
312                                  struct autorid_global_config);
313
314         for (i = 0; ids[i]; i++) {
315                 struct winbindd_tdc_domain *domain;
316                 struct autorid_domain_config domaincfg;
317                 uint32_t rid;
318                 struct dom_sid domainsid;
319
320                 ZERO_STRUCT(domaincfg);
321
322                 sid_copy(&domainsid, ids[i]->sid);
323                 if (!sid_split_rid(&domainsid, &rid)) {
324                         DEBUG(4, ("Could not determine domain SID from %s, "
325                                   "ignoring mapping request\n",
326                                   sid_string_dbg(ids[i]->sid)));
327                         continue;
328                 }
329
330                 /*
331                  * Check if the domain is around
332                  */
333                 domain = wcache_tdc_fetch_domainbysid(talloc_tos(),
334                                                       &domainsid);
335                 if (domain == NULL) {
336                         DEBUG(10, ("Ignoring unknown domain sid %s\n",
337                                    sid_string_dbg(&domainsid)));
338                         continue;
339                 }
340                 TALLOC_FREE(domain);
341
342                 domaincfg.globalcfg = global;
343                 sid_to_fstring(domaincfg.sid, &domainsid);
344
345                 ret = idmap_autorid_get_domainrange(&domaincfg);
346
347                 if (!NT_STATUS_IS_OK(ret)) {
348                         DEBUG(3, ("Could not determine range for domain, "
349                                   "check previous messages for reason\n"));
350                         goto failure;
351                 }
352
353                 ret = idmap_autorid_sid_to_id(global, &domaincfg, ids[i]);
354
355                 if ((!NT_STATUS_IS_OK(ret)) &&
356                     (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
357                         /* some fatal error occurred, log it */
358                         DEBUG(3, ("Unexpected error resolving a SID (%s)\n",
359                                   sid_string_dbg(ids[i]->sid)));
360                         goto failure;
361                 }
362         }
363         return NT_STATUS_OK;
364
365       failure:
366         return ret;
367
368 }
369
370 /* initialize the given HWM to 0 if it does not exist yet */
371 static NTSTATUS idmap_autorid_init_hwm(const char *hwm) {
372
373         NTSTATUS status;
374         uint32_t hwmval;
375
376         status = dbwrap_fetch_uint32(autorid_db, hwm, &hwmval);
377         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND))  {
378                 status = dbwrap_trans_store_int32(autorid_db, hwm, 0);
379                 if (!NT_STATUS_IS_OK(status)) {
380                         DEBUG(0,
381                               ("Unable to initialise HWM (%s) in autorid "
382                                "database: %s\n", hwm, nt_errstr(status)));
383                         return NT_STATUS_INTERNAL_DB_ERROR;
384                 }
385         } else if (!NT_STATUS_IS_OK(status)) {
386                 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
387                           "database: %s\n", hwm,  nt_errstr(status)));
388                 return status;
389         }
390
391         return NT_STATUS_OK;
392 }
393
394 /*
395  * open and initialize the database which stores the ranges for the domains
396  */
397 static NTSTATUS idmap_autorid_db_init(void)
398 {
399         NTSTATUS status;
400
401         if (autorid_db) {
402                 /* its already open */
403                 return NT_STATUS_OK;
404         }
405
406         /* Open idmap repository */
407         autorid_db = db_open(NULL, state_path("autorid.tdb"), 0,
408                              TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
409                              DBWRAP_LOCK_ORDER_1);
410
411         if (!autorid_db) {
412                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n",
413                           state_path("autorid.tdb")));
414                 return NT_STATUS_UNSUCCESSFUL;
415         }
416
417         /* Initialize high water mark for the currently used range to 0 */
418
419         status = idmap_autorid_init_hwm(HWM);
420         NT_STATUS_NOT_OK_RETURN(status);
421
422         status = idmap_autorid_init_hwm(ALLOC_HWM_UID);
423         NT_STATUS_NOT_OK_RETURN(status);
424
425         status = idmap_autorid_init_hwm(ALLOC_HWM_GID);
426
427         return status;
428 }
429
430 static struct autorid_global_config *idmap_autorid_loadconfig(TALLOC_CTX * ctx)
431 {
432
433         TDB_DATA data;
434         struct autorid_global_config *cfg;
435         unsigned long minvalue, rangesize, maxranges;
436         NTSTATUS status;
437
438         status = dbwrap_fetch_bystring(autorid_db, ctx, CONFIGKEY, &data);
439
440         if (!NT_STATUS_IS_OK(status)) {
441                 DEBUG(10, ("No saved config found\n"));
442                 return NULL;
443         }
444
445         cfg = talloc_zero(ctx, struct autorid_global_config);
446         if (!cfg) {
447                 return NULL;
448         }
449
450         if (sscanf((char *)data.dptr,
451                    "minvalue:%lu rangesize:%lu maxranges:%lu",
452                    &minvalue, &rangesize, &maxranges) != 3) {
453                 DEBUG(1,
454                       ("Found invalid configuration data"
455                        "creating new config\n"));
456                 return NULL;
457         }
458
459         cfg->minvalue = minvalue;
460         cfg->rangesize = rangesize;
461         cfg->maxranges = maxranges;
462
463         DEBUG(10, ("Loaded previously stored configuration "
464                    "minvalue:%d rangesize:%d\n",
465                    cfg->minvalue, cfg->rangesize));
466
467         return cfg;
468
469 }
470
471 static NTSTATUS idmap_autorid_saveconfig(struct autorid_global_config *cfg)
472 {
473
474         NTSTATUS status;
475         TDB_DATA data;
476         char *cfgstr;
477
478         cfgstr =
479             talloc_asprintf(talloc_tos(),
480                             "minvalue:%u rangesize:%u maxranges:%u",
481                             cfg->minvalue, cfg->rangesize, cfg->maxranges);
482
483         if (!cfgstr) {
484                 return NT_STATUS_NO_MEMORY;
485         }
486
487         data = string_tdb_data(cfgstr);
488
489         status = dbwrap_trans_store_bystring(autorid_db, CONFIGKEY,
490                                              data, TDB_REPLACE);
491
492         talloc_free(cfgstr);
493
494         return status;
495 }
496
497 static NTSTATUS idmap_autorid_initialize(struct idmap_domain *dom)
498 {
499         struct autorid_global_config *config;
500         struct autorid_global_config *storedconfig = NULL;
501         NTSTATUS status;
502         uint32_t hwm;
503
504         if (!strequal(dom->name, "*")) {
505                 DEBUG(0, ("idmap_autorid_initialize: Error: autorid configured "
506                           "for domain '%s'. But autorid can only be used for "
507                           "the default idmap configuration.\n", dom->name));
508                 return NT_STATUS_INVALID_PARAMETER;
509         }
510
511         config = talloc_zero(dom, struct autorid_global_config);
512         if (!config) {
513                 DEBUG(0, ("Out of memory!\n"));
514                 return NT_STATUS_NO_MEMORY;
515         }
516
517         status = idmap_autorid_db_init();
518         if (!NT_STATUS_IS_OK(status)) {
519                 goto error;
520         }
521
522         config->minvalue = dom->low_id;
523         config->rangesize = lp_parm_int(-1, "idmap config *", "rangesize", 100000);
524
525         if (config->rangesize < 2000) {
526                 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
527                 status = NT_STATUS_INVALID_PARAMETER;
528                 goto error;
529         }
530
531         config->maxranges = (dom->high_id - dom->low_id + 1) /
532             config->rangesize;
533
534         if (config->maxranges == 0) {
535                 DEBUG(1, ("allowed uid range is smaller then rangesize, "
536                           "increase uid range or decrease rangesize\n"));
537                 status = NT_STATUS_INVALID_PARAMETER;
538                 goto error;
539         }
540
541         /* check if the high-low limit is a multiple of the rangesize */
542         if ((dom->high_id - dom->low_id + 1) % config->rangesize != 0) {
543                 DEBUG(5, ("High uid-low uid difference of %d "
544                           "is not a multiple of the rangesize %d, "
545                           "limiting ranges to lower boundary number of %d\n",
546                           (dom->high_id - dom->low_id + 1), config->rangesize,
547                           config->maxranges));
548         }
549
550         DEBUG(10, ("Current configuration in config is "
551                    "minvalue:%d rangesize:%d maxranges:%d\n",
552                    config->minvalue, config->rangesize, config->maxranges));
553
554         /* read previously stored config and current HWM */
555         storedconfig = idmap_autorid_loadconfig(talloc_tos());
556
557         status = dbwrap_fetch_uint32(autorid_db, HWM, &hwm);
558         if (!NT_STATUS_IS_OK(status)) {
559                 DEBUG(1, ("Fatal error while fetching current "
560                           "HWM value: %s\n", nt_errstr(status)));
561                 status = NT_STATUS_INTERNAL_ERROR;
562                 goto error;
563         }
564
565         /* did the minimum value or rangesize change? */
566         if (storedconfig &&
567             ((storedconfig->minvalue != config->minvalue) ||
568              (storedconfig->rangesize != config->rangesize))) {
569                 DEBUG(1, ("New configuration values for rangesize or "
570                           "minimum uid value conflict with previously "
571                           "used values! Aborting initialization\n"));
572                 status = NT_STATUS_INVALID_PARAMETER;
573                 goto error;
574         }
575
576         /*
577          * has the highest uid value been reduced to setting that is not
578          * sufficient any more for already existing ranges?
579          */
580         if (hwm > config->maxranges) {
581                 DEBUG(1, ("New upper uid limit is too low to cover "
582                           "existing mappings! Aborting initialization\n"));
583                 status = NT_STATUS_INVALID_PARAMETER;
584                 goto error;
585         }
586
587         status = idmap_autorid_saveconfig(config);
588
589         if (!NT_STATUS_IS_OK(status)) {
590                 DEBUG(1, ("Failed to store configuration data!\n"));
591                 goto error;
592         }
593
594         DEBUG(5, ("%d domain ranges with a size of %d are available\n",
595                   config->maxranges, config->rangesize));
596
597         dom->private_data = config;
598
599         goto done;
600
601 error:
602         talloc_free(config);
603
604 done:
605         talloc_free(storedconfig);
606
607         return status;
608 }
609
610 static NTSTATUS idmap_autorid_allocate_id(struct idmap_domain *dom,
611                                           struct unixid *xid) {
612
613         NTSTATUS ret;
614         struct autorid_global_config *globalcfg;
615         struct autorid_domain_config domaincfg;
616         uint32_t hwm;
617         const char *hwmkey;
618
619         if (!strequal(dom->name, "*")) {
620                 DEBUG(3, ("idmap_autorid_allocate_id: "
621                           "Refusing creation of mapping for domain'%s'. "
622                           "Currently only supported for the default "
623                           "domain \"*\".\n",
624                            dom->name));
625                 return NT_STATUS_NOT_IMPLEMENTED;
626         }
627
628         if ((xid->type != ID_TYPE_UID) && (xid->type != ID_TYPE_GID)) {
629                 return NT_STATUS_INVALID_PARAMETER;
630         }
631
632
633         globalcfg = talloc_get_type(dom->private_data,
634                                     struct autorid_global_config);
635
636         /* fetch the range for the allocation pool */
637
638         ZERO_STRUCT(domaincfg);
639
640         domaincfg.globalcfg = globalcfg;
641         fstrcpy(domaincfg.sid, ALLOC_RANGE);
642
643         ret = idmap_autorid_get_domainrange(&domaincfg);
644
645         if (!NT_STATUS_IS_OK(ret)) {
646                 DEBUG(3, ("Could not determine range for allocation pool, "
647                           "check previous messages for reason\n"));
648                 return ret;
649         }
650
651         /* fetch the current HWM */
652         hwmkey = (xid->type==ID_TYPE_UID)?ALLOC_HWM_UID:ALLOC_HWM_GID;
653
654         ret = dbwrap_fetch_uint32(autorid_db, hwmkey, &hwm);
655
656         if (!NT_STATUS_IS_OK(ret)) {
657                 DEBUG(1, ("Failed to fetch current allocation HWM value: %s\n",
658                           nt_errstr(ret)));
659                 return NT_STATUS_INTERNAL_ERROR;
660         }
661
662         if (hwm >= globalcfg->rangesize) {
663                 DEBUG(1, ("allocation range is depleted!\n"));
664                 return NT_STATUS_NO_MEMORY;
665         }
666
667         ret = dbwrap_change_uint32_atomic(autorid_db, hwmkey, &(xid->id), 1);
668         if (!NT_STATUS_IS_OK(ret)) {
669                 DEBUG(1, ("Fatal error while allocating new ID!\n"));
670                 return ret;
671         }
672
673         xid->id = globalcfg->minvalue +
674                   globalcfg->rangesize * domaincfg.domainnum +
675                   xid->id;
676
677         DEBUG(10, ("Returned new %s %d from allocation range\n",
678                    (xid->type==ID_TYPE_UID)?"uid":"gid", xid->id));
679
680         return ret;
681 }
682
683 /*
684   Close the idmap tdb instance
685 */
686 static struct idmap_methods autorid_methods = {
687         .init = idmap_autorid_initialize,
688         .unixids_to_sids = idmap_autorid_unixids_to_sids,
689         .sids_to_unixids = idmap_autorid_sids_to_unixids,
690         .allocate_id     = idmap_autorid_allocate_id
691 };
692
693 NTSTATUS samba_init_module(void)
694 {
695         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
696                                   "autorid", &autorid_methods);
697 }