e90fc77068f0424e4882d1478fb2f2a0b251b22d
[nivanova/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / secrets_tdb_sync.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2012
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  *  Name: ldb
22  *
23  *  Component: ldb secrets_tdb_sync module
24  *
25  *  Description: Update secrets.tdb whenever the matching secret record changes
26  *
27  *  Author: Andrew Bartlett
28  */
29
30 #include "includes.h"
31 #include "ldb_module.h"
32 #include "lib/util/dlinklist.h"
33 #include "auth/credentials/credentials.h"
34 #include "auth/credentials/credentials_krb5.h"
35 #include "system/kerberos.h"
36 #include "auth/kerberos/kerberos.h"
37 #include "auth/kerberos/kerberos_srv_keytab.h"
38 #include "dsdb/samdb/ldb_modules/util.h"
39 #include "param/secrets.h"
40 #include "source3/include/secrets.h"
41 #include "lib/dbwrap/dbwrap.h"
42 #include "dsdb/samdb/samdb.h"
43
44 struct dn_list {
45         struct ldb_message *msg;
46         bool do_delete;
47         struct dn_list *prev, *next;
48 };
49
50 struct secrets_tdb_sync_private {
51         struct dn_list *changed_dns;
52         struct db_context *secrets_tdb;
53 };
54
55 struct secrets_tdb_sync_ctx {
56         struct ldb_module *module;
57         struct ldb_request *req;
58
59         struct ldb_dn *dn;
60         bool do_delete;
61
62         struct ldb_reply *op_reply;
63         bool found;
64 };
65
66 static struct secrets_tdb_sync_ctx *secrets_tdb_sync_ctx_init(struct ldb_module *module,
67                                                 struct ldb_request *req)
68 {
69         struct secrets_tdb_sync_ctx *ac;
70
71         ac = talloc_zero(req, struct secrets_tdb_sync_ctx);
72         if (ac == NULL) {
73                 ldb_oom(ldb_module_get_ctx(module));
74                 return NULL;
75         }
76
77         ac->module = module;
78         ac->req = req;
79
80         return ac;
81 }
82
83 /* FIXME: too many semi-async searches here for my taste, direct and indirect as
84  * cli_credentials_set_secrets() performs a sync ldb search.
85  * Just hope we are lucky and nothing breaks (using the tdb backend masks a lot
86  * of async issues). -SSS
87  */
88 static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool do_delete,
89                         struct ldb_request *parent)
90 {
91         struct ldb_context *ldb = ldb_module_get_ctx(module);
92         struct secrets_tdb_sync_private *data = talloc_get_type(ldb_module_get_private(module), struct secrets_tdb_sync_private);
93         struct dn_list *item;
94         char *filter;
95         struct ldb_result *res;
96         int ret;
97
98         filter = talloc_asprintf(data,
99                                  "(&(objectClass=primaryDomain)(flatname=*))");
100         if (!filter) {
101                 return ldb_oom(ldb);
102         }
103
104         ret = dsdb_module_search(module, data, &res,
105                                  dn, LDB_SCOPE_BASE, NULL,
106                                  DSDB_FLAG_NEXT_MODULE, parent,
107                                  "%s", filter);
108         talloc_free(filter);
109         if (ret != LDB_SUCCESS) {
110                 return ret;
111         }
112
113         if (res->count != 1) {
114                 /* if it's not a primaryDomain then we don't have anything to update */
115                 talloc_free(res);
116                 return LDB_SUCCESS;
117         }
118
119         item = talloc(data->changed_dns? (void *)data->changed_dns: (void *)data, struct dn_list);
120         if (!item) {
121                 talloc_free(res);
122                 return ldb_oom(ldb);
123         }
124
125         item->msg = talloc_steal(item, res->msgs[0]);
126         item->do_delete = do_delete;
127         talloc_free(res);
128
129         DLIST_ADD_END(data->changed_dns, item, struct dn_list *);
130         return LDB_SUCCESS;
131 }
132
133 static int ust_search_modified(struct secrets_tdb_sync_ctx *ac);
134
135 static int secrets_tdb_sync_op_callback(struct ldb_request *req,
136                                  struct ldb_reply *ares)
137 {
138         struct ldb_context *ldb;
139         struct secrets_tdb_sync_ctx *ac;
140         int ret;
141
142         ac = talloc_get_type(req->context, struct secrets_tdb_sync_ctx);
143         ldb = ldb_module_get_ctx(ac->module);
144
145         if (!ares) {
146                 return ldb_module_done(ac->req, NULL, NULL,
147                                         LDB_ERR_OPERATIONS_ERROR);
148         }
149         if (ares->error != LDB_SUCCESS) {
150                 return ldb_module_done(ac->req, ares->controls,
151                                         ares->response, ares->error);
152         }
153
154         if (ares->type != LDB_REPLY_DONE) {
155                 ldb_set_errstring(ldb, "Invalid request type!\n");
156                 return ldb_module_done(ac->req, NULL, NULL,
157                                         LDB_ERR_OPERATIONS_ERROR);
158         }
159
160         if (ac->do_delete) {
161                 return ldb_module_done(ac->req, ares->controls,
162                                         ares->response, LDB_SUCCESS);
163         }
164
165         ac->op_reply = talloc_steal(ac, ares);
166
167         ret = ust_search_modified(ac);
168         if (ret != LDB_SUCCESS) {
169                 return ldb_module_done(ac->req, NULL, NULL, ret);
170         }
171
172         return LDB_SUCCESS;
173 }
174
175 static int ust_del_op(struct secrets_tdb_sync_ctx *ac)
176 {
177         struct ldb_context *ldb;
178         struct ldb_request *down_req;
179         int ret;
180
181         ldb = ldb_module_get_ctx(ac->module);
182
183         ret = ldb_build_del_req(&down_req, ldb, ac,
184                                 ac->dn,
185                                 ac->req->controls,
186                                 ac, secrets_tdb_sync_op_callback,
187                                 ac->req);
188         LDB_REQ_SET_LOCATION(down_req);
189         if (ret != LDB_SUCCESS) {
190                 return ret;
191         }
192         return ldb_next_request(ac->module, down_req);
193 }
194
195 static int ust_search_modified_callback(struct ldb_request *req,
196                                         struct ldb_reply *ares)
197 {
198         struct secrets_tdb_sync_ctx *ac;
199         int ret;
200
201         ac = talloc_get_type(req->context, struct secrets_tdb_sync_ctx);
202
203         if (!ares) {
204                 return ldb_module_done(ac->req, NULL, NULL,
205                                         LDB_ERR_OPERATIONS_ERROR);
206         }
207         if (ares->error != LDB_SUCCESS) {
208                 return ldb_module_done(ac->req, ares->controls,
209                                         ares->response, ares->error);
210         }
211
212         switch (ares->type) {
213         case LDB_REPLY_ENTRY:
214
215                 ac->found = true;
216                 break;
217
218         case LDB_REPLY_REFERRAL:
219                 /* ignore */
220                 break;
221
222         case LDB_REPLY_DONE:
223
224                 if (ac->found) {
225                         /* do the dirty sync job here :/ */
226                         ret = add_modified(ac->module, ac->dn, ac->do_delete, ac->req);
227                 }
228
229                 if (ac->do_delete) {
230                         ret = ust_del_op(ac);
231                         if (ret != LDB_SUCCESS) {
232                                 return ldb_module_done(ac->req,
233                                                         NULL, NULL, ret);
234                         }
235                         break;
236                 }
237
238                 return ldb_module_done(ac->req, ac->op_reply->controls,
239                                         ac->op_reply->response, LDB_SUCCESS);
240         }
241
242         talloc_free(ares);
243         return LDB_SUCCESS;
244 }
245
246 static int ust_search_modified(struct secrets_tdb_sync_ctx *ac)
247 {
248         struct ldb_context *ldb;
249         static const char * const no_attrs[] = { NULL };
250         struct ldb_request *search_req;
251         int ret;
252
253         ldb = ldb_module_get_ctx(ac->module);
254
255         ret = ldb_build_search_req(&search_req, ldb, ac,
256                                    ac->dn, LDB_SCOPE_BASE,
257                                    "(&(objectClass=kerberosSecret)"
258                                      "(privateKeytab=*))", no_attrs,
259                                    NULL,
260                                    ac, ust_search_modified_callback,
261                                    ac->req);
262         LDB_REQ_SET_LOCATION(search_req);
263         if (ret != LDB_SUCCESS) {
264                 return ret;
265         }
266         return ldb_next_request(ac->module, search_req);
267 }
268
269
270 /* add */
271 static int secrets_tdb_sync_add(struct ldb_module *module, struct ldb_request *req)
272 {
273         struct ldb_context *ldb;
274         struct secrets_tdb_sync_ctx *ac;
275         struct ldb_request *down_req;
276         int ret;
277
278         ldb = ldb_module_get_ctx(module);
279
280         ac = secrets_tdb_sync_ctx_init(module, req);
281         if (ac == NULL) {
282                 return ldb_operr(ldb);
283         }
284
285         ac->dn = req->op.add.message->dn;
286
287         ret = ldb_build_add_req(&down_req, ldb, ac,
288                                 req->op.add.message,
289                                 req->controls,
290                                 ac, secrets_tdb_sync_op_callback,
291                                 req);
292         LDB_REQ_SET_LOCATION(down_req);
293         if (ret != LDB_SUCCESS) {
294                 return ret;
295         }
296
297         return ldb_next_request(module, down_req);
298 }
299
300 /* modify */
301 static int secrets_tdb_sync_modify(struct ldb_module *module, struct ldb_request *req)
302 {
303         struct ldb_context *ldb;
304         struct secrets_tdb_sync_ctx *ac;
305         struct ldb_request *down_req;
306         int ret;
307
308         ldb = ldb_module_get_ctx(module);
309
310         ac = secrets_tdb_sync_ctx_init(module, req);
311         if (ac == NULL) {
312                 return ldb_operr(ldb);
313         }
314
315         ac->dn = req->op.mod.message->dn;
316
317         ret = ldb_build_mod_req(&down_req, ldb, ac,
318                                 req->op.mod.message,
319                                 req->controls,
320                                 ac, secrets_tdb_sync_op_callback,
321                                 req);
322         LDB_REQ_SET_LOCATION(down_req);
323         if (ret != LDB_SUCCESS) {
324                 return ret;
325         }
326
327         return ldb_next_request(module, down_req);
328 }
329
330 /* delete */
331 static int secrets_tdb_sync_delete(struct ldb_module *module, struct ldb_request *req)
332 {
333         struct secrets_tdb_sync_ctx *ac;
334
335         ac = secrets_tdb_sync_ctx_init(module, req);
336         if (ac == NULL) {
337                 return ldb_operr(ldb_module_get_ctx(module));
338         }
339
340         ac->dn = req->op.del.dn;
341         ac->do_delete = true;
342
343         return ust_search_modified(ac);
344 }
345
346 /* rename */
347 static int secrets_tdb_sync_rename(struct ldb_module *module, struct ldb_request *req)
348 {
349         struct ldb_context *ldb;
350         struct secrets_tdb_sync_ctx *ac;
351         struct ldb_request *down_req;
352         int ret;
353
354         ldb = ldb_module_get_ctx(module);
355
356         ac = secrets_tdb_sync_ctx_init(module, req);
357         if (ac == NULL) {
358                 return ldb_operr(ldb);
359         }
360
361         ac->dn = req->op.rename.newdn;
362
363         ret = ldb_build_rename_req(&down_req, ldb, ac,
364                                 req->op.rename.olddn,
365                                 req->op.rename.newdn,
366                                 req->controls,
367                                 ac, secrets_tdb_sync_op_callback,
368                                 req);
369         LDB_REQ_SET_LOCATION(down_req);
370         if (ret != LDB_SUCCESS) {
371                 return ret;
372         }
373
374         return ldb_next_request(module, down_req);
375 }
376
377 /* prepare for a commit */
378 static int secrets_tdb_sync_prepare_commit(struct ldb_module *module)
379 {
380         struct ldb_context *ldb = ldb_module_get_ctx(module);
381         struct secrets_tdb_sync_private *data = talloc_get_type(ldb_module_get_private(module),
382                                                                 struct secrets_tdb_sync_private);
383         struct dn_list *p;
384         TALLOC_CTX *tmp_ctx;
385
386         tmp_ctx = talloc_new(data);
387         if (!tmp_ctx) {
388                 ldb_oom(ldb);
389                 goto fail;
390         }
391
392         for (p=data->changed_dns; p; p = p->next) {
393                 const struct ldb_val *whenChanged = ldb_msg_find_ldb_val(p->msg, "whenChanged");
394                 time_t lct = 0;
395                 bool ret;
396
397                 if (whenChanged) {
398                         ldb_val_to_time(whenChanged, &lct);
399                 }
400
401                 ret = secrets_store_machine_pw_sync(ldb_msg_find_attr_as_string(p->msg, "secret", NULL),
402                                                     ldb_msg_find_attr_as_string(p->msg, "priorSecret", NULL),
403
404                                                     ldb_msg_find_attr_as_string(p->msg, "flatname", NULL),
405                                                     ldb_msg_find_attr_as_string(p->msg, "realm", NULL),
406                                                     ldb_msg_find_attr_as_string(p->msg, "saltPrincipal", NULL),
407                                                     (uint32_t)ldb_msg_find_attr_as_int(p->msg, "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES),
408                                                     samdb_result_dom_sid(tmp_ctx, p->msg, "objectSid"),
409
410                                                     lct,
411                                                     p->do_delete);
412                 if (ret == false) {
413                         ldb_asprintf_errstring(ldb, "Failed to update secrets.tdb from entry %s in %s",
414                                                ldb_dn_get_linearized(p->msg->dn),
415                                                (const char *)ldb_get_opaque(ldb, "ldb_url"));
416                         goto fail;
417                 }
418         }
419
420         talloc_free(data->changed_dns);
421         data->changed_dns = NULL;
422         talloc_free(tmp_ctx);
423
424         return ldb_next_prepare_commit(module);
425
426 fail:
427         dbwrap_transaction_cancel(data->secrets_tdb);
428         talloc_free(data->changed_dns);
429         data->changed_dns = NULL;
430         talloc_free(tmp_ctx);
431         return LDB_ERR_OPERATIONS_ERROR;
432 }
433
434 /* start a transaction */
435 static int secrets_tdb_sync_start_transaction(struct ldb_module *module)
436 {
437         struct secrets_tdb_sync_private *data = talloc_get_type(ldb_module_get_private(module), struct secrets_tdb_sync_private);
438
439         if (dbwrap_transaction_start(data->secrets_tdb) != 0) {
440                 return ldb_module_operr(module);
441         }
442
443         return ldb_next_start_trans(module);
444 }
445
446 /* end a transaction */
447 static int secrets_tdb_sync_end_transaction(struct ldb_module *module)
448 {
449         struct secrets_tdb_sync_private *data = talloc_get_type(ldb_module_get_private(module), struct secrets_tdb_sync_private);
450
451         if (dbwrap_transaction_commit(data->secrets_tdb) != 0) {
452                 return ldb_module_operr(module);
453         }
454
455         return ldb_next_end_trans(module);
456 }
457
458 /* abandon a transaction */
459 static int secrets_tdb_sync_del_transaction(struct ldb_module *module)
460 {
461         struct secrets_tdb_sync_private *data = talloc_get_type(ldb_module_get_private(module), struct secrets_tdb_sync_private);
462
463         talloc_free(data->changed_dns);
464         data->changed_dns = NULL;
465         if (dbwrap_transaction_cancel(data->secrets_tdb) != 0) {
466                 return ldb_module_operr(module);
467         }
468
469         return ldb_next_del_trans(module);
470 }
471
472 static int secrets_tdb_sync_init(struct ldb_module *module)
473 {
474         struct ldb_context *ldb;
475         struct secrets_tdb_sync_private *data;
476         char *private_dir, *p;
477         const char *secrets_ldb;
478
479         ldb = ldb_module_get_ctx(module);
480
481         data = talloc(module, struct secrets_tdb_sync_private);
482         if (data == NULL) {
483                 return ldb_oom(ldb);
484         }
485
486         data->changed_dns = NULL;
487
488         ldb_module_set_private(module, data);
489
490         secrets_ldb = (const char *)ldb_get_opaque(ldb, "ldb_url");
491         if (strncmp("tdb://", secrets_ldb, 6) == 0) {
492                 secrets_ldb += 6;
493         }
494         if (!secrets_ldb) {
495                 return ldb_operr(ldb);
496         }
497         private_dir = talloc_strdup(data, secrets_ldb);
498         p = strrchr(private_dir, '/');
499         if (p) {
500                 *p = '\0';
501                 secrets_init_path(private_dir);
502         } else {
503                 secrets_init_path(".");
504         }
505
506         TALLOC_FREE(private_dir);
507
508         data->secrets_tdb = secrets_db_ctx();
509
510         return ldb_next_init(module);
511 }
512
513 static const struct ldb_module_ops ldb_secrets_tdb_sync_module_ops = {
514         .name              = "secrets_tdb_sync",
515         .init_context      = secrets_tdb_sync_init,
516         .add               = secrets_tdb_sync_add,
517         .modify            = secrets_tdb_sync_modify,
518         .rename            = secrets_tdb_sync_rename,
519         .del               = secrets_tdb_sync_delete,
520         .start_transaction = secrets_tdb_sync_start_transaction,
521         .prepare_commit    = secrets_tdb_sync_prepare_commit,
522         .end_transaction   = secrets_tdb_sync_end_transaction,
523         .del_transaction   = secrets_tdb_sync_del_transaction,
524 };
525
526 int ldb_secrets_tdb_sync_module_init(const char *version)
527 {
528         LDB_MODULE_CHECK_VERSION(version);
529         return ldb_register_module(&ldb_secrets_tdb_sync_module_ops);
530 }