s4:dsdb Rework modules create new partitions at runtime
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / partition_init.c
1 /* 
2    Partitions ldb module
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
5    Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22  *  Name: ldb
23  *
24  *  Component: ldb partitions module
25  *
26  *  Description: Implement LDAP partitions
27  *
28  *  Author: Andrew Bartlett
29  *  Author: Stefan Metzmacher
30  */
31
32 #include "dsdb/samdb/ldb_modules/partition.h"
33
34 static int partition_sort_compare(const void *v1, const void *v2)
35 {
36         const struct dsdb_partition *p1;
37         const struct dsdb_partition *p2;
38
39         p1 = *((struct dsdb_partition * const*)v1);
40         p2 = *((struct dsdb_partition * const*)v2);
41
42         return ldb_dn_compare(p1->ctrl->dn, p2->ctrl->dn);
43 }
44
45 /* Load the list of DNs that we must replicate to all partitions */
46 static int partition_load_replicate_dns(struct ldb_context *ldb, struct partition_private_data *data, struct ldb_message *msg) 
47 {
48         struct ldb_message_element *replicate_attributes = ldb_msg_find_element(msg, "replicateEntries");
49
50         talloc_free(data->replicate);
51         if (!replicate_attributes) {
52                 data->replicate = NULL;
53         } else {
54                 int i;
55                 data->replicate = talloc_array(data, struct ldb_dn *, replicate_attributes->num_values + 1);
56                 if (!data->replicate) {
57                         return LDB_ERR_OPERATIONS_ERROR;
58                 }
59
60                 for (i=0; i < replicate_attributes->num_values; i++) {
61                         data->replicate[i] = ldb_dn_from_ldb_val(data->replicate, ldb, &replicate_attributes->values[i]);
62                         if (!ldb_dn_validate(data->replicate[i])) {
63                                 ldb_asprintf_errstring(ldb,
64                                                         "partition_init: "
65                                                         "invalid DN in partition replicate record: %s", 
66                                                         replicate_attributes->values[i].data);
67                                 return LDB_ERR_CONSTRAINT_VIOLATION;
68                         }
69                 }
70                 data->replicate[i] = NULL;
71         }
72         return LDB_SUCCESS;
73 }
74
75 /* Load the list of modules for the partitions */
76 static int partition_load_modules(struct ldb_context *ldb, 
77                                   struct partition_private_data *data, struct ldb_message *msg) 
78 {
79         int i;
80         struct ldb_message_element *modules_attributes = ldb_msg_find_element(msg, "modules");
81         talloc_free(data->modules);
82         if (!modules_attributes) {
83                 return LDB_SUCCESS;
84         }
85         
86         data->modules = talloc_array(data, struct partition_module *, modules_attributes->num_values + 1);
87         if (!data->modules) {
88                 ldb_oom(ldb);
89                 return LDB_ERR_OPERATIONS_ERROR;
90         }
91         
92         for (i=0; i < modules_attributes->num_values; i++) {
93                 char *base;
94                 char *p;
95
96                 data->modules[i] = talloc(data->modules, struct partition_module);
97                 if (!data->modules[i]) {
98                         ldb_oom(ldb);
99                         return LDB_ERR_OPERATIONS_ERROR;
100                 }
101
102                 base = talloc_strdup(data->partitions, (char *)modules_attributes->values[i].data);
103                 p = strchr(base, ':');
104                 if (!p) {
105                         ldb_asprintf_errstring(ldb, 
106                                                "partition_load_modules: "
107                                                "invalid form for partition module record (missing ':'): %s", base);
108                         return LDB_ERR_CONSTRAINT_VIOLATION;
109                 }
110                 p[0] = '\0';
111                 p++;
112                 data->modules[i]->modules = ldb_modules_list_from_string(ldb, data->modules[i],
113                                                                          p);
114                 
115                 if (strcmp(base, "*") == 0) {
116                         data->modules[i]->dn = NULL;
117                 } else {
118                         data->modules[i]->dn = ldb_dn_new(data->modules[i], ldb, base);
119                         if (!data->modules[i]->dn || !ldb_dn_validate(data->modules[i]->dn)) {
120                                 return LDB_ERR_OPERATIONS_ERROR;
121                         }
122                 }
123         }
124         return LDB_SUCCESS;
125 }
126
127 static int partition_reload_metadata(struct ldb_module *module, struct partition_private_data *data, TALLOC_CTX *mem_ctx, struct ldb_message **_msg) 
128 {
129         int ret;
130         struct ldb_message *msg;
131         struct ldb_result *res;
132         struct ldb_context *ldb = ldb_module_get_ctx(module);
133         const char *attrs[] = { "partition", "replicateEntries", "modules", NULL };
134         /* perform search for @PARTITION, looking for module, replicateEntries and ldapBackend */
135         ret = dsdb_module_search_dn(module, mem_ctx, &res, 
136                                     ldb_dn_new(mem_ctx, ldb, DSDB_PARTITION_DN),
137                                     attrs);
138         if (ret != LDB_SUCCESS) {
139                 return ret;
140         }
141
142         msg = res->msgs[0];
143
144         ret = partition_load_replicate_dns(ldb, data, msg);
145         if (ret != LDB_SUCCESS) {
146                 return ret;
147         }
148
149         ret = partition_load_modules(ldb, data, msg);                   
150         if (ret != LDB_SUCCESS) {
151                 return ret;
152         }
153
154         data->ldapBackend = talloc_steal(data, ldb_msg_find_attr_as_string(msg, "ldapBackend", NULL));
155         if (_msg) {
156                 *_msg = msg;
157         } else {
158                 talloc_free(msg);
159         }
160         return LDB_SUCCESS;
161 }
162
163 static const char **find_modules_for_dn(struct partition_private_data *data, struct ldb_dn *dn) 
164 {
165         int i;
166         struct partition_module *default_mod = NULL;
167         for (i=0; data->modules && data->modules[i]; i++) {
168                 if (!data->modules[i]->dn) {
169                         default_mod = data->modules[i];
170                 } else if (ldb_dn_compare(dn, data->modules[i]->dn) == 0) {
171                         return data->modules[i]->modules;
172                 }
173         }
174         if (default_mod) {
175                 return default_mod->modules;
176         } else {
177                 return NULL;
178         }
179 }
180
181 static int new_partition_from_dn(struct ldb_context *ldb, struct partition_private_data *data, 
182                           TALLOC_CTX *mem_ctx, 
183                           struct ldb_dn *dn,
184                           struct dsdb_partition **partition) {
185         const char *backend_name;
186         const char *full_backend;
187         struct dsdb_control_current_partition *ctrl;
188         struct ldb_module *module;
189         const char **modules;
190         int ret;
191
192         (*partition) = talloc(mem_ctx, struct dsdb_partition);
193         if (!*partition) {
194                 return LDB_ERR_OPERATIONS_ERROR;
195         }
196
197         (*partition)->ctrl = ctrl = talloc((*partition), struct dsdb_control_current_partition);
198         if (!ctrl) {
199                 talloc_free(*partition);
200                 ldb_oom(ldb);
201                 return LDB_ERR_OPERATIONS_ERROR;
202         }
203
204         /* See if an LDAP backend has been specified */
205         if (data->ldapBackend) {
206                 backend_name = data->ldapBackend;
207         } else {
208                 /* If we don't have a : in the record, then the partition name is the name of the LDB */
209                 backend_name = talloc_asprintf(data, "%s.ldb", ldb_dn_get_casefold(dn)); 
210         }
211
212         ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
213         ctrl->dn = talloc_steal(ctrl, dn);
214         
215         full_backend = samdb_relative_path(ldb, 
216                                            *partition, 
217                                            backend_name);
218         if (!full_backend) {
219                 ldb_asprintf_errstring(ldb_module_get_ctx(module), 
220                                        "partition_init: unable to determine an relative path for partition: %s", backend_name);
221                 talloc_free(*partition);
222                 return LDB_ERR_OPERATIONS_ERROR;                
223         }
224
225         ret = ldb_connect_backend(ldb, full_backend, NULL, &module);
226         if (ret != LDB_SUCCESS) {
227                 return ret;
228         }
229
230         modules = find_modules_for_dn(data, dn);
231
232         ret = ldb_load_modules_list(ldb, modules, module, &(*partition)->module);
233         if (ret != LDB_SUCCESS) {
234                 ldb_asprintf_errstring(ldb, 
235                                        "partition_init: "
236                                        "loading backend for %s failed: %s", 
237                                        ldb_dn_get_linearized(dn), ldb_errstring(ldb));
238                 talloc_free(*partition);
239                 return ret;
240         }
241         ret = ldb_init_module_chain(ldb, (*partition)->module);
242         if (ret != LDB_SUCCESS) {
243                 ldb_asprintf_errstring(ldb,
244                                        "partition_init: "
245                                        "initialising backend for %s failed: %s", 
246                                        ldb_dn_get_linearized(dn), ldb_errstring(ldb));
247                 talloc_free(*partition);
248                 return ret;
249         }
250
251         talloc_steal((*partition), (*partition)->module);
252
253         return ret;
254 }
255
256 static int partition_register(struct ldb_context *ldb, struct dsdb_control_current_partition *ctrl, TALLOC_CTX *mem_ctx) 
257 {
258         struct ldb_request *req;
259         int ret;
260
261         req = talloc_zero(mem_ctx, struct ldb_request);
262         if (req == NULL) {
263                 ldb_oom(ldb);
264                 return LDB_ERR_OPERATIONS_ERROR;
265         }
266                 
267         req->operation = LDB_REQ_REGISTER_PARTITION;
268         req->op.reg_partition.dn = ctrl->dn;
269         req->callback = ldb_op_default_callback;
270
271         ldb_set_timeout(ldb, req, 0);
272         
273         req->handle = ldb_handle_new(req, ldb);
274         if (req->handle == NULL) {
275                 return LDB_ERR_OPERATIONS_ERROR;
276         }
277         
278         ret = ldb_request(ldb, req);
279         if (ret == LDB_SUCCESS) {
280                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
281         }
282         if (ret != LDB_SUCCESS) {
283                 ldb_debug(ldb, LDB_DEBUG_ERROR, "partition: Unable to register partition with rootdse!\n");
284                 talloc_free(mem_ctx);
285                 return LDB_ERR_OTHER;
286         }
287         talloc_free(req);
288
289         return LDB_SUCCESS;
290 }
291
292 int partition_create(struct ldb_module *module, struct ldb_request *req)
293 {
294         int i, ret;
295         struct ldb_context *ldb = ldb_module_get_ctx(module);
296         struct ldb_request *mod_req;
297         struct ldb_message *mod_msg;
298         struct partition_private_data *data;
299         struct dsdb_partition *partition;
300
301         /* Check if this is already a partition */
302
303         struct dsdb_create_partition_exop *ex_op = talloc_get_type(req->op.extended.data, struct dsdb_create_partition_exop);
304         struct ldb_dn *dn = ex_op->new_dn;
305
306         data = talloc_get_type(module->private_data, struct partition_private_data);
307         if (!data) {
308                 return LDB_ERR_OPERATIONS_ERROR;
309         }
310         
311         if (!data) {
312                 /* We are not going to create a partition before we are even set up */
313                 return LDB_ERR_UNWILLING_TO_PERFORM;
314         }
315
316         for (i=0; data->partitions && data->partitions[i]; i++) {
317                 if (ldb_dn_compare(data->partitions[i]->ctrl->dn, dn) == 0) {
318                         return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
319                 }
320         }
321
322         mod_msg = ldb_msg_new(req);
323         if (!mod_msg) {
324                 ldb_oom(ldb);
325                 return LDB_ERR_OPERATIONS_ERROR;
326         }
327
328         mod_msg->dn = ldb_dn_new(mod_msg, ldb, DSDB_PARTITION_DN);
329         ret = ldb_msg_add_empty(mod_msg, DSDB_PARTITION_ATTR, LDB_FLAG_MOD_ADD, NULL);
330         if (ret != LDB_SUCCESS) {
331                 return ret;
332         }
333         ret = ldb_msg_add_string(mod_msg, DSDB_PARTITION_ATTR, ldb_dn_get_casefold(dn));
334         if (ret != LDB_SUCCESS) {
335                 return ret;
336         }
337
338         /* Perform modify on @PARTITION record */
339         ret = ldb_build_mod_req(&mod_req, ldb, req, mod_msg, NULL, NULL, 
340                                 ldb_op_default_callback, req);
341
342         if (ret != LDB_SUCCESS) {
343                 return ret;
344         }
345                 
346         ret = ldb_next_request(module, mod_req);
347         if (ret == LDB_SUCCESS) {
348                 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
349         }
350
351         if (ret != LDB_SUCCESS) {
352                 return ret;
353         }
354
355         ret = partition_reload_metadata(module, data, req, NULL);
356         if (ret != LDB_SUCCESS) {
357                 return ret;
358         }
359
360         /* Make a partition structure for this new partition, so we can copy in the template structure */ 
361         ret = new_partition_from_dn(ldb, data, req, ldb_dn_copy(req, dn), &partition);
362         if (ret != LDB_SUCCESS) {
363                 return ret;
364         }
365
366         /* Start a transaction on the DB (as it won't be in one being brand new) */
367         {
368                 struct ldb_module *next = partition->module;
369                 PARTITION_FIND_OP(next, start_transaction);
370
371                 ret = next->ops->start_transaction(next);
372                 if (ret != LDB_SUCCESS) {
373                         return ret;
374                 }
375         }
376         
377         /* otherwise, for each replicate, copy from main partition.  If we get an error, we report it up the chain */
378
379         for (i=0; data->replicate && data->replicate[i]; i++) {
380                 struct ldb_result *replicate_res;
381                 struct ldb_request *add_req;
382                 ret = dsdb_module_search_dn(module, req, &replicate_res, 
383                                             data->replicate[i],
384                                             NULL);
385                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
386                         continue;
387                 }
388                 if (ret != LDB_SUCCESS) {
389                         ldb_asprintf_errstring(ldb,
390                                                "Failed to search for %s from " DSDB_PARTITION_DN 
391                                                " replicateEntries for new partition at %s: %s", 
392                                                ldb_dn_get_linearized(data->replicate[i]), 
393                                                ldb_dn_get_linearized(partition->ctrl->dn), 
394                                                ldb_errstring(ldb));
395                         return ret;
396                 }
397
398                 /* Build add request */
399                 ret = ldb_build_add_req(&add_req, ldb, replicate_res, replicate_res->msgs[0], NULL, NULL, 
400                                         ldb_op_default_callback, mod_req);
401                 if (ret != LDB_SUCCESS) {
402                         return ret;
403                 }
404                 /* do request */
405                 ret = ldb_next_request(partition->module, add_req);
406                 
407                 /* wait */
408                 if (ret == LDB_SUCCESS) {
409                         ret = ldb_wait(add_req->handle, LDB_WAIT_ALL);
410                 }
411                 talloc_free(replicate_res);
412                 if (ret != LDB_SUCCESS) {
413                         ldb_asprintf_errstring(ldb,
414                                                "Failed to add %s from " DSDB_PARTITION_DN 
415                                                " replicateEntries to new partition at %s: %s", 
416                                                ldb_dn_get_linearized(data->replicate[i]), 
417                                                ldb_dn_get_linearized(partition->ctrl->dn), 
418                                                ldb_errstring(ldb));
419                         return ret;
420                 }               
421                 /* And around again, for the next thing we must merge */
422         }
423
424         /* Count the partitions */
425         for (i=0; data->partitions && data->partitions[i]; i++) { /* noop */};
426         
427         /* Add partition to list of partitions */
428         data->partitions = talloc_realloc(data, data->partitions, struct dsdb_partition *, i + 2);
429         if (!data->partitions) {
430                 ldb_oom(ldb);
431                 return LDB_ERR_OPERATIONS_ERROR;
432         }
433         data->partitions[i] = talloc_steal(data->partitions, partition);
434         data->partitions[i+1] = NULL;
435                 
436         /* Sort again (should use binary insert) */
437         qsort(data->partitions, i+1,
438               sizeof(*data->partitions), partition_sort_compare);
439
440         ret = partition_register(ldb, partition->ctrl, req);
441         if (ret != LDB_SUCCESS) {
442                 return ret;
443         }               
444
445         /* send request done */
446         return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
447 }
448
449
450 int partition_init(struct ldb_module *module)
451 {
452         int ret, i;
453         TALLOC_CTX *mem_ctx = talloc_new(module);
454         struct ldb_context *ldb = ldb_module_get_ctx(module);
455         struct ldb_message *msg;
456         struct ldb_message_element *partition_attributes;
457
458         struct partition_private_data *data;
459
460         if (!mem_ctx) {
461                 return LDB_ERR_OPERATIONS_ERROR;
462         }
463
464         data = talloc_zero(mem_ctx, struct partition_private_data);
465         if (data == NULL) {
466                 return LDB_ERR_OPERATIONS_ERROR;
467         }
468
469         ret = partition_reload_metadata(module, data, mem_ctx, &msg);
470         if (ret != LDB_SUCCESS) {
471                 return ret;
472         }
473
474         partition_attributes = ldb_msg_find_element(msg, "partition");
475         if (!partition_attributes) {
476                 data->partitions = NULL;
477         } else {
478                 data->partitions = talloc_array(data, struct dsdb_partition *, partition_attributes->num_values + 1);
479                 if (!data->partitions) {
480                         ldb_oom(ldb_module_get_ctx(module));
481                         talloc_free(mem_ctx);
482                         return LDB_ERR_OPERATIONS_ERROR;
483                 }
484         }
485         for (i=0; partition_attributes && i < partition_attributes->num_values; i++) {
486                 struct ldb_dn *dn = ldb_dn_from_ldb_val(mem_ctx, ldb, &partition_attributes->values[i]);
487                 if (!dn) {
488                         ldb_asprintf_errstring(ldb_module_get_ctx(module), 
489                                                "partition_init: invalid DN in partition record: %s", (const char *)partition_attributes->values[i].data);
490                         talloc_free(mem_ctx);
491                         return LDB_ERR_CONSTRAINT_VIOLATION;
492                 }
493         
494                 ret = new_partition_from_dn(ldb, data, data->partitions, dn, &data->partitions[i]);
495                 if (ret != LDB_SUCCESS) {
496                         talloc_free(mem_ctx);
497                         return ret;
498                 }
499         }
500
501         if (data->partitions) {
502                 data->partitions[i] = NULL;
503
504                 /* sort these into order, most to least specific */
505                 qsort(data->partitions, partition_attributes->num_values,
506                       sizeof(*data->partitions), partition_sort_compare);
507         }
508
509         for (i=0; data->partitions && data->partitions[i]; i++) {
510                 ret = partition_register(ldb, data->partitions[i]->ctrl, mem_ctx);
511                 if (ret != LDB_SUCCESS) {
512                         return ret;
513                 }
514         }
515
516         ret = ldb_mod_register_control(module, LDB_CONTROL_DOMAIN_SCOPE_OID);
517         if (ret != LDB_SUCCESS) {
518                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
519                         "partition: Unable to register control with rootdse!\n");
520                 return LDB_ERR_OPERATIONS_ERROR;
521         }
522
523         ret = ldb_mod_register_control(module, LDB_CONTROL_SEARCH_OPTIONS_OID);
524         if (ret != LDB_SUCCESS) {
525                 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
526                         "partition: Unable to register control with rootdse!\n");
527                 return LDB_ERR_OPERATIONS_ERROR;
528         }
529
530         module->private_data = talloc_steal(module, data);
531
532         talloc_free(mem_ctx);
533         return ldb_next_init(module);
534 }