s3:idmap: remove the alloc methods list from idmap.c
[samba.git] / source3 / winbindd / idmap.c
1 /*
2    Unix SMB/CIFS implementation.
3    ID Mapping
4    Copyright (C) Tim Potter 2000
5    Copyright (C) Jim McDonough <jmcd@us.ibm.com>        2003
6    Copyright (C) Simo Sorce 2003-2007
7    Copyright (C) Jeremy Allison 2006
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "winbindd.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_IDMAP
28
29 static_decl_idmap;
30
31 static void idmap_init(void)
32 {
33         static bool initialized;
34
35         if (initialized) {
36                 return;
37         }
38
39         DEBUG(10, ("idmap_init(): calling static_init_idmap\n"));
40
41         static_init_idmap;
42
43         initialized = true;
44 }
45
46 /**
47  * Pointer to the backend methods. Modules register themselves here via
48  * smb_register_idmap.
49  */
50
51 struct idmap_backend {
52         const char *name;
53         struct idmap_methods *methods;
54         struct idmap_backend *prev, *next;
55 };
56 static struct idmap_backend *backends = NULL;
57
58 /**
59  * The idmap alloc context that is configured via "idmap alloc
60  * backend". Defaults to "idmap backend" in case the module (tdb, ldap) also
61  * provides alloc methods.
62  */
63 struct idmap_alloc_context {
64         struct idmap_alloc_methods *methods;
65 };
66 static struct idmap_alloc_context *idmap_alloc_ctx = NULL;
67
68 /**
69  * Default idmap domain configured via "idmap backend".
70  */
71 static struct idmap_domain *default_idmap_domain;
72
73 /**
74  * Passdb idmap domain, not configurable. winbind must always give passdb a
75  * chance to map ids.
76  */
77 static struct idmap_domain *passdb_idmap_domain;
78
79 /**
80  * List of specially configured idmap domains. This list is filled on demand
81  * in the winbind idmap child when the parent winbind figures out via the
82  * special range parameter or via the domain SID that a special "idmap config
83  * domain" configuration is present.
84  */
85 static struct idmap_domain **idmap_domains = NULL;
86 static int num_domains = 0;
87
88 static struct idmap_methods *get_methods(const char *name)
89 {
90         struct idmap_backend *b;
91
92         for (b = backends; b; b = b->next) {
93                 if (strequal(b->name, name)) {
94                         return b->methods;
95                 }
96         }
97
98         return NULL;
99 }
100
101 bool idmap_is_offline(void)
102 {
103         return ( lp_winbind_offline_logon() &&
104              get_global_winbindd_state_offline() );
105 }
106
107 bool idmap_is_online(void)
108 {
109         return !idmap_is_offline();
110 }
111
112 /**********************************************************************
113  Allow a module to register itself as a method.
114 **********************************************************************/
115
116 NTSTATUS smb_register_idmap(int version, const char *name,
117                             struct idmap_methods *methods)
118 {
119         struct idmap_backend *entry;
120
121         if ((version != SMB_IDMAP_INTERFACE_VERSION)) {
122                 DEBUG(0, ("Failed to register idmap module.\n"
123                           "The module was compiled against "
124                           "SMB_IDMAP_INTERFACE_VERSION %d,\n"
125                           "current SMB_IDMAP_INTERFACE_VERSION is %d.\n"
126                           "Please recompile against the current version "
127                           "of samba!\n",
128                           version, SMB_IDMAP_INTERFACE_VERSION));
129                 return NT_STATUS_OBJECT_TYPE_MISMATCH;
130         }
131
132         if (!name || !name[0] || !methods) {
133                 DEBUG(0,("Called with NULL pointer or empty name!\n"));
134                 return NT_STATUS_INVALID_PARAMETER;
135         }
136
137         for (entry = backends; entry != NULL; entry = entry->next) {
138                 if (strequal(entry->name, name)) {
139                         DEBUG(0,("Idmap module %s already registered!\n",
140                                  name));
141                         return NT_STATUS_OBJECT_NAME_COLLISION;
142                 }
143         }
144
145         entry = talloc(NULL, struct idmap_backend);
146         if ( ! entry) {
147                 DEBUG(0,("Out of memory!\n"));
148                 TALLOC_FREE(entry);
149                 return NT_STATUS_NO_MEMORY;
150         }
151         entry->name = talloc_strdup(entry, name);
152         if ( ! entry->name) {
153                 DEBUG(0,("Out of memory!\n"));
154                 TALLOC_FREE(entry);
155                 return NT_STATUS_NO_MEMORY;
156         }
157         entry->methods = methods;
158
159         DLIST_ADD(backends, entry);
160         DEBUG(5, ("Successfully added idmap backend '%s'\n", name));
161         return NT_STATUS_OK;
162 }
163
164 static int close_domain_destructor(struct idmap_domain *dom)
165 {
166         NTSTATUS ret;
167
168         ret = dom->methods->close_fn(dom);
169         if (!NT_STATUS_IS_OK(ret)) {
170                 DEBUG(3, ("Failed to close idmap domain [%s]!\n", dom->name));
171         }
172
173         return 0;
174 }
175
176 static bool parse_idmap_module(TALLOC_CTX *mem_ctx, const char *param,
177                                char **pmodulename, char **pargs)
178 {
179         char *modulename;
180         char *args;
181
182         if (strncmp(param, "idmap_", 6) == 0) {
183                 param += 6;
184                 DEBUG(1, ("idmap_init: idmap backend uses deprecated "
185                           "'idmap_' prefix.  Please replace 'idmap_%s' by "
186                           "'%s'\n", param, param));
187         }
188
189         modulename = talloc_strdup(mem_ctx, param);
190         if (modulename == NULL) {
191                 return false;
192         }
193
194         args = strchr(modulename, ':');
195         if (args == NULL) {
196                 *pmodulename = modulename;
197                 *pargs = NULL;
198                 return true;
199         }
200
201         *args = '\0';
202
203         args = talloc_strdup(mem_ctx, args+1);
204         if (args == NULL) {
205                 TALLOC_FREE(modulename);
206                 return false;
207         }
208
209         *pmodulename = modulename;
210         *pargs = args;
211         return true;
212 }
213
214 /**
215  * Initialize a domain structure
216  * @param[in] mem_ctx           memory context for the result
217  * @param[in] domainname        which domain is this for
218  * @param[in] modulename        which backend module
219  * @param[in] params            parameter to pass to the init function
220  * @result The initialized structure
221  */
222 static struct idmap_domain *idmap_init_domain(TALLOC_CTX *mem_ctx,
223                                               const char *domainname,
224                                               const char *modulename,
225                                               const char *params)
226 {
227         struct idmap_domain *result;
228         NTSTATUS status;
229
230         result = talloc_zero(mem_ctx, struct idmap_domain);
231         if (result == NULL) {
232                 DEBUG(0, ("talloc failed\n"));
233                 return NULL;
234         }
235
236         result->name = talloc_strdup(result, domainname);
237         if (result->name == NULL) {
238                 DEBUG(0, ("talloc failed\n"));
239                 goto fail;
240         }
241
242         result->methods = get_methods(modulename);
243         if (result->methods == NULL) {
244                 DEBUG(3, ("idmap backend %s not found\n", modulename));
245
246                 status = smb_probe_module("idmap", modulename);
247                 if (!NT_STATUS_IS_OK(status)) {
248                         DEBUG(3, ("Could not probe idmap module %s\n",
249                                   modulename));
250                         goto fail;
251                 }
252
253                 result->methods = get_methods(modulename);
254         }
255         if (result->methods == NULL) {
256                 DEBUG(1, ("idmap backend %s not found\n", modulename));
257                 goto fail;
258         }
259
260         status = result->methods->init(result, params);
261         if (!NT_STATUS_IS_OK(status)) {
262                 DEBUG(1, ("idmap initialization returned %s\n",
263                           nt_errstr(status)));
264                 goto fail;
265         }
266
267         talloc_set_destructor(result, close_domain_destructor);
268
269         return result;
270
271 fail:
272         TALLOC_FREE(result);
273         return NULL;
274 }
275
276 /**
277  * Initialize the default domain structure
278  * @param[in] mem_ctx           memory context for the result
279  * @result The default domain structure
280  *
281  * This routine takes the module name from the "idmap backend" parameter,
282  * passing a possible parameter like ldap:ldap://ldap-url/ to the module.
283  */
284
285 static struct idmap_domain *idmap_init_default_domain(TALLOC_CTX *mem_ctx)
286 {
287         struct idmap_domain *result;
288         char *modulename;
289         char *params;
290
291         idmap_init();
292
293         if (!parse_idmap_module(talloc_tos(), lp_idmap_backend(), &modulename,
294                                 &params)) {
295                 DEBUG(1, ("parse_idmap_module failed\n"));
296                 return NULL;
297         }
298
299         DEBUG(3, ("idmap_init: using '%s' as remote backend\n", modulename));
300
301         result = idmap_init_domain(mem_ctx, "*", modulename, params);
302         if (result == NULL) {
303                 goto fail;
304         }
305
306         TALLOC_FREE(modulename);
307         TALLOC_FREE(params);
308         return result;
309
310 fail:
311         TALLOC_FREE(modulename);
312         TALLOC_FREE(params);
313         TALLOC_FREE(result);
314         return NULL;
315 }
316
317 /**
318  * Initialize a named domain structure
319  * @param[in] mem_ctx           memory context for the result
320  * @param[in] domname           the domain name
321  * @result The default domain structure
322  *
323  * This routine looks at the "idmap config <domname>" parameters to figure out
324  * the configuration.
325  */
326
327 static struct idmap_domain *idmap_init_named_domain(TALLOC_CTX *mem_ctx,
328                                                     const char *domname)
329 {
330         struct idmap_domain *result = NULL;
331         char *config_option;
332         const char *backend;
333
334         config_option = talloc_asprintf(talloc_tos(), "idmap config %s",
335                                         domname);
336         if (config_option == NULL) {
337                 DEBUG(0, ("talloc failed\n"));
338                 goto fail;
339         }
340
341         backend = lp_parm_const_string(-1, config_option, "backend", NULL);
342         if (backend == NULL) {
343                 DEBUG(1, ("no backend defined for %s\n", config_option));
344                 goto fail;
345         }
346
347         result = idmap_init_domain(mem_ctx, domname, backend, NULL);
348         if (result == NULL) {
349                 goto fail;
350         }
351
352         TALLOC_FREE(config_option);
353         return result;
354
355 fail:
356         TALLOC_FREE(config_option);
357         TALLOC_FREE(result);
358         return NULL;
359 }
360
361 /**
362  * Initialize the passdb domain structure
363  * @param[in] mem_ctx           memory context for the result
364  * @result The default domain structure
365  *
366  * No config, passdb has its own configuration.
367  */
368
369 static struct idmap_domain *idmap_init_passdb_domain(TALLOC_CTX *mem_ctx)
370 {
371         idmap_init();
372
373         if (passdb_idmap_domain != NULL) {
374                 return passdb_idmap_domain;
375         }
376
377         passdb_idmap_domain = idmap_init_domain(NULL, get_global_sam_name(),
378                                                 "passdb", NULL);
379         if (passdb_idmap_domain == NULL) {
380                 DEBUG(1, ("Could not init passdb idmap domain\n"));
381         }
382
383         return passdb_idmap_domain;
384 }
385
386 /**
387  * Find a domain struct according to a domain name
388  * @param[in] domname           Domain name to get the config for
389  * @result The default domain structure that fits
390  *
391  * This is the central routine in the winbindd-idmap child to pick the correct
392  * domain for looking up IDs. If domname is NULL or empty, we use the default
393  * domain. If it contains something, we try to use idmap_init_named_domain()
394  * to fetch the correct backend.
395  *
396  * The choice about "domname" is being made by the winbind parent, look at the
397  * "have_idmap_config" of "struct winbindd_domain" which is set in
398  * add_trusted_domain.
399  */
400
401 static struct idmap_domain *idmap_find_domain(const char *domname)
402 {
403         struct idmap_domain *result;
404         int i;
405
406         DEBUG(10, ("idmap_find_domain called for domain '%s'\n",
407                    domname?domname:"NULL"));
408
409         /*
410          * Always init the default domain, we can't go without one
411          */
412         if (default_idmap_domain == NULL) {
413                 default_idmap_domain = idmap_init_default_domain(NULL);
414         }
415         if (default_idmap_domain == NULL) {
416                 return NULL;
417         }
418
419         if ((domname == NULL) || (domname[0] == '\0')) {
420                 return default_idmap_domain;
421         }
422
423         for (i=0; i<num_domains; i++) {
424                 if (strequal(idmap_domains[i]->name, domname)) {
425                         return idmap_domains[i];
426                 }
427         }
428
429         if (idmap_domains == NULL) {
430                 /*
431                  * talloc context for all idmap domains
432                  */
433                 idmap_domains = TALLOC_ARRAY(NULL, struct idmap_domain *, 1);
434         }
435
436         if (idmap_domains == NULL) {
437                 DEBUG(0, ("talloc failed\n"));
438                 return NULL;
439         }
440
441         result = idmap_init_named_domain(idmap_domains, domname);
442         if (result == NULL) {
443                 /*
444                  * Could not init that domain -- try the default one
445                  */
446                 return default_idmap_domain;
447         }
448
449         ADD_TO_ARRAY(idmap_domains, struct idmap_domain *, result,
450                      &idmap_domains, &num_domains);
451         return result;
452 }
453
454 void idmap_close(void)
455 {
456         if (idmap_alloc_ctx) {
457                 idmap_alloc_ctx->methods->close_fn();
458                 idmap_alloc_ctx->methods = NULL;
459         }
460         TALLOC_FREE(default_idmap_domain);
461         TALLOC_FREE(passdb_idmap_domain);
462         TALLOC_FREE(idmap_domains);
463         num_domains = 0;
464 }
465
466 /**************************************************************************
467  idmap allocator interface functions
468 **************************************************************************/
469
470 static NTSTATUS idmap_allocate_unixid(struct unixid *id)
471 {
472         struct idmap_domain *dom;
473         NTSTATUS ret;
474
475         dom = idmap_find_domain(NULL);
476
477         if (dom == NULL) {
478                 return NT_STATUS_UNSUCCESSFUL;
479         }
480
481         if (dom->methods->allocate_id == NULL) {
482                 return NT_STATUS_NOT_IMPLEMENTED;
483         }
484
485         ret = dom->methods->allocate_id(dom, id);
486
487         return ret;
488 }
489
490
491 NTSTATUS idmap_allocate_uid(struct unixid *id)
492 {
493         id->type = ID_TYPE_UID;
494         return idmap_allocate_unixid(id);
495 }
496
497 NTSTATUS idmap_allocate_gid(struct unixid *id)
498 {
499         id->type = ID_TYPE_GID;
500         return idmap_allocate_unixid(id);
501 }
502
503 NTSTATUS idmap_backends_unixid_to_sid(const char *domname, struct id_map *id)
504 {
505         struct idmap_domain *dom;
506         struct id_map *maps[2];
507
508          DEBUG(10, ("idmap_backend_unixid_to_sid: domain = '%s', xid = %d "
509                     "(type %d)\n",
510                     domname?domname:"NULL", id->xid.id, id->xid.type));
511
512         maps[0] = id;
513         maps[1] = NULL;
514
515         /*
516          * Always give passdb a chance first
517          */
518
519         dom = idmap_init_passdb_domain(NULL);
520         if ((dom != NULL)
521             && NT_STATUS_IS_OK(dom->methods->unixids_to_sids(dom, maps))
522             && id->status == ID_MAPPED) {
523                 return NT_STATUS_OK;
524         }
525
526         dom = idmap_find_domain(domname);
527         if (dom == NULL) {
528                 return NT_STATUS_NONE_MAPPED;
529         }
530
531         return dom->methods->unixids_to_sids(dom, maps);
532 }
533
534 NTSTATUS idmap_backends_sid_to_unixid(const char *domain, struct id_map *id)
535 {
536         struct idmap_domain *dom;
537         struct id_map *maps[2];
538
539          DEBUG(10, ("idmap_backends_sid_to_unixid: domain = '%s', sid = [%s]\n",
540                     domain?domain:"NULL", sid_string_dbg(id->sid)));
541
542         maps[0] = id;
543         maps[1] = NULL;
544
545         if (sid_check_is_in_builtin(id->sid)
546             || (sid_check_is_in_our_domain(id->sid))) {
547
548                 dom = idmap_init_passdb_domain(NULL);
549                 if (dom == NULL) {
550                         return NT_STATUS_NONE_MAPPED;
551                 }
552                 return dom->methods->sids_to_unixids(dom, maps);
553         }
554
555         dom = idmap_find_domain(domain);
556         if (dom == NULL) {
557                 return NT_STATUS_NONE_MAPPED;
558         }
559
560         return dom->methods->sids_to_unixids(dom, maps);
561 }