Line data Source code
1 : /*
2 : ldb database library
3 :
4 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
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 update_keytabs module
24 : *
25 : * Description: Update keytabs whenever their 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 :
41 : struct dn_list {
42 : struct ldb_message *msg;
43 : bool do_delete;
44 : struct dn_list *prev, *next;
45 : };
46 :
47 : struct update_kt_private {
48 : struct dn_list *changed_dns;
49 : };
50 :
51 : struct update_kt_ctx {
52 : struct ldb_module *module;
53 : struct ldb_request *req;
54 :
55 : struct ldb_dn *dn;
56 : bool do_delete;
57 :
58 : struct ldb_reply *op_reply;
59 : bool found;
60 : };
61 :
62 5181 : static struct update_kt_ctx *update_kt_ctx_init(struct ldb_module *module,
63 : struct ldb_request *req)
64 : {
65 83 : struct update_kt_ctx *ac;
66 :
67 5181 : ac = talloc_zero(req, struct update_kt_ctx);
68 5181 : if (ac == NULL) {
69 0 : ldb_oom(ldb_module_get_ctx(module));
70 0 : return NULL;
71 : }
72 :
73 5181 : ac->module = module;
74 5181 : ac->req = req;
75 :
76 5181 : return ac;
77 : }
78 :
79 : /* FIXME: too many semi-async searches here for my taste, direct and indirect as
80 : * cli_credentials_set_secrets() performs a sync ldb search.
81 : * Just hope we are lucky and nothing breaks (using the tdb backend masks a lot
82 : * of async issues). -SSS
83 : */
84 251 : static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool do_delete,
85 : struct ldb_request *parent)
86 : {
87 251 : struct ldb_context *ldb = ldb_module_get_ctx(module);
88 251 : struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
89 26 : struct dn_list *item;
90 26 : char *filter;
91 26 : struct ldb_result *res;
92 26 : int ret;
93 :
94 251 : filter = talloc_asprintf(data,
95 : "(&(objectClass=kerberosSecret)(privateKeytab=*))");
96 251 : if (!filter) {
97 0 : return ldb_oom(ldb);
98 : }
99 :
100 251 : ret = dsdb_module_search(module, data, &res,
101 : dn, LDB_SCOPE_BASE, NULL,
102 : DSDB_FLAG_NEXT_MODULE, parent,
103 : "%s", filter);
104 251 : talloc_free(filter);
105 251 : if (ret != LDB_SUCCESS) {
106 0 : return ret;
107 : }
108 :
109 251 : if (res->count != 1) {
110 : /* if it's not a kerberosSecret then we don't have anything to update */
111 0 : talloc_free(res);
112 0 : return LDB_SUCCESS;
113 : }
114 :
115 253 : item = talloc(data->changed_dns? (void *)data->changed_dns: (void *)data, struct dn_list);
116 251 : if (!item) {
117 0 : talloc_free(res);
118 0 : return ldb_oom(ldb);
119 : }
120 :
121 251 : item->msg = talloc_steal(item, res->msgs[0]);
122 251 : item->do_delete = do_delete;
123 251 : talloc_free(res);
124 :
125 251 : DLIST_ADD_END(data->changed_dns, item);
126 225 : return LDB_SUCCESS;
127 : }
128 :
129 : static int ukt_search_modified(struct update_kt_ctx *ac);
130 :
131 5181 : static int update_kt_op_callback(struct ldb_request *req,
132 : struct ldb_reply *ares)
133 : {
134 83 : struct ldb_context *ldb;
135 83 : struct update_kt_ctx *ac;
136 83 : int ret;
137 :
138 5181 : ac = talloc_get_type(req->context, struct update_kt_ctx);
139 5181 : ldb = ldb_module_get_ctx(ac->module);
140 :
141 5181 : if (!ares) {
142 0 : return ldb_module_done(ac->req, NULL, NULL,
143 : LDB_ERR_OPERATIONS_ERROR);
144 : }
145 5181 : if (ares->error != LDB_SUCCESS) {
146 9 : return ldb_module_done(ac->req, ares->controls,
147 : ares->response, ares->error);
148 : }
149 :
150 5172 : if (ares->type != LDB_REPLY_DONE) {
151 0 : ldb_set_errstring(ldb, "Invalid request type!\n");
152 0 : return ldb_module_done(ac->req, NULL, NULL,
153 : LDB_ERR_OPERATIONS_ERROR);
154 : }
155 :
156 5172 : if (ac->do_delete) {
157 1643 : return ldb_module_done(ac->req, ares->controls,
158 : ares->response, LDB_SUCCESS);
159 : }
160 :
161 3529 : ac->op_reply = talloc_steal(ac, ares);
162 :
163 3529 : ret = ukt_search_modified(ac);
164 3529 : if (ret != LDB_SUCCESS) {
165 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
166 : }
167 :
168 3446 : return LDB_SUCCESS;
169 : }
170 :
171 1652 : static int ukt_del_op(struct update_kt_ctx *ac)
172 : {
173 0 : struct ldb_context *ldb;
174 0 : struct ldb_request *down_req;
175 0 : int ret;
176 :
177 1652 : ldb = ldb_module_get_ctx(ac->module);
178 :
179 1652 : ret = ldb_build_del_req(&down_req, ldb, ac,
180 : ac->dn,
181 1652 : ac->req->controls,
182 : ac, update_kt_op_callback,
183 : ac->req);
184 1652 : LDB_REQ_SET_LOCATION(down_req);
185 1652 : if (ret != LDB_SUCCESS) {
186 0 : return ret;
187 : }
188 1652 : return ldb_next_request(ac->module, down_req);
189 : }
190 :
191 5432 : static int ukt_search_modified_callback(struct ldb_request *req,
192 : struct ldb_reply *ares)
193 : {
194 109 : struct update_kt_ctx *ac;
195 109 : int ret;
196 :
197 5432 : ac = talloc_get_type(req->context, struct update_kt_ctx);
198 :
199 5432 : if (!ares) {
200 0 : return ldb_module_done(ac->req, NULL, NULL,
201 : LDB_ERR_OPERATIONS_ERROR);
202 : }
203 5432 : if (ares->error != LDB_SUCCESS) {
204 0 : return ldb_module_done(ac->req, ares->controls,
205 : ares->response, ares->error);
206 : }
207 :
208 5432 : switch (ares->type) {
209 251 : case LDB_REPLY_ENTRY:
210 :
211 251 : ac->found = true;
212 251 : break;
213 :
214 0 : case LDB_REPLY_REFERRAL:
215 : /* ignore */
216 0 : break;
217 :
218 5181 : case LDB_REPLY_DONE:
219 :
220 5181 : if (ac->found) {
221 : /* do the dirty sync job here :/ */
222 251 : ret = add_modified(ac->module, ac->dn, ac->do_delete, ac->req);
223 : }
224 :
225 5181 : if (ac->do_delete) {
226 1652 : ret = ukt_del_op(ac);
227 1652 : if (ret != LDB_SUCCESS) {
228 0 : return ldb_module_done(ac->req,
229 : NULL, NULL, ret);
230 : }
231 1652 : break;
232 : }
233 :
234 3529 : return ldb_module_done(ac->req, ac->op_reply->controls,
235 3529 : ac->op_reply->response, LDB_SUCCESS);
236 : }
237 :
238 1903 : talloc_free(ares);
239 1903 : return LDB_SUCCESS;
240 : }
241 :
242 5181 : static int ukt_search_modified(struct update_kt_ctx *ac)
243 : {
244 83 : struct ldb_context *ldb;
245 83 : static const char * const no_attrs[] = { NULL };
246 83 : struct ldb_request *search_req;
247 83 : int ret;
248 :
249 5181 : ldb = ldb_module_get_ctx(ac->module);
250 :
251 5181 : ret = ldb_build_search_req(&search_req, ldb, ac,
252 : ac->dn, LDB_SCOPE_BASE,
253 : "(&(objectClass=kerberosSecret)"
254 : "(privateKeytab=*))", no_attrs,
255 : NULL,
256 : ac, ukt_search_modified_callback,
257 : ac->req);
258 5181 : LDB_REQ_SET_LOCATION(search_req);
259 5181 : if (ret != LDB_SUCCESS) {
260 0 : return ret;
261 : }
262 5181 : return ldb_next_request(ac->module, search_req);
263 : }
264 :
265 :
266 : /* add */
267 2272 : static int update_kt_add(struct ldb_module *module, struct ldb_request *req)
268 : {
269 76 : struct ldb_context *ldb;
270 76 : struct update_kt_ctx *ac;
271 76 : struct ldb_request *down_req;
272 76 : int ret;
273 :
274 2272 : ldb = ldb_module_get_ctx(module);
275 :
276 2272 : ac = update_kt_ctx_init(module, req);
277 2272 : if (ac == NULL) {
278 0 : return ldb_operr(ldb);
279 : }
280 :
281 2272 : ac->dn = req->op.add.message->dn;
282 :
283 2272 : ret = ldb_build_add_req(&down_req, ldb, ac,
284 : req->op.add.message,
285 : req->controls,
286 : ac, update_kt_op_callback,
287 : req);
288 2272 : LDB_REQ_SET_LOCATION(down_req);
289 2272 : if (ret != LDB_SUCCESS) {
290 0 : return ret;
291 : }
292 :
293 2272 : return ldb_next_request(module, down_req);
294 : }
295 :
296 : /* modify */
297 1232 : static int update_kt_modify(struct ldb_module *module, struct ldb_request *req)
298 : {
299 5 : struct ldb_context *ldb;
300 5 : struct update_kt_ctx *ac;
301 5 : struct ldb_request *down_req;
302 5 : int ret;
303 :
304 1232 : ldb = ldb_module_get_ctx(module);
305 :
306 1232 : ac = update_kt_ctx_init(module, req);
307 1232 : if (ac == NULL) {
308 0 : return ldb_operr(ldb);
309 : }
310 :
311 1232 : ac->dn = req->op.mod.message->dn;
312 :
313 1232 : ret = ldb_build_mod_req(&down_req, ldb, ac,
314 : req->op.mod.message,
315 : req->controls,
316 : ac, update_kt_op_callback,
317 : req);
318 1232 : LDB_REQ_SET_LOCATION(down_req);
319 1232 : if (ret != LDB_SUCCESS) {
320 0 : return ret;
321 : }
322 :
323 1232 : return ldb_next_request(module, down_req);
324 : }
325 :
326 : /* delete */
327 1652 : static int update_kt_delete(struct ldb_module *module, struct ldb_request *req)
328 : {
329 0 : struct update_kt_ctx *ac;
330 :
331 1652 : ac = update_kt_ctx_init(module, req);
332 1652 : if (ac == NULL) {
333 0 : return ldb_operr(ldb_module_get_ctx(module));
334 : }
335 :
336 1652 : ac->dn = req->op.del.dn;
337 1652 : ac->do_delete = true;
338 :
339 1652 : return ukt_search_modified(ac);
340 : }
341 :
342 : /* rename */
343 25 : static int update_kt_rename(struct ldb_module *module, struct ldb_request *req)
344 : {
345 2 : struct ldb_context *ldb;
346 2 : struct update_kt_ctx *ac;
347 2 : struct ldb_request *down_req;
348 2 : int ret;
349 :
350 25 : ldb = ldb_module_get_ctx(module);
351 :
352 25 : ac = update_kt_ctx_init(module, req);
353 25 : if (ac == NULL) {
354 0 : return ldb_operr(ldb);
355 : }
356 :
357 25 : ac->dn = req->op.rename.newdn;
358 :
359 25 : ret = ldb_build_rename_req(&down_req, ldb, ac,
360 : req->op.rename.olddn,
361 : req->op.rename.newdn,
362 : req->controls,
363 : ac, update_kt_op_callback,
364 : req);
365 25 : LDB_REQ_SET_LOCATION(down_req);
366 25 : if (ret != LDB_SUCCESS) {
367 0 : return ret;
368 : }
369 :
370 25 : return ldb_next_request(module, down_req);
371 : }
372 :
373 : /* prepare for a commit */
374 4815 : static int update_kt_prepare_commit(struct ldb_module *module)
375 : {
376 4815 : struct ldb_context *ldb = ldb_module_get_ctx(module);
377 4815 : struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
378 32 : struct dn_list *p;
379 32 : struct smb_krb5_context *smb_krb5_context;
380 4815 : int krb5_ret = smb_krb5_init_context(data,
381 4815 : ldb_get_opaque(ldb, "loadparm"),
382 : &smb_krb5_context);
383 4815 : TALLOC_CTX *tmp_ctx = NULL;
384 :
385 4815 : if (krb5_ret != 0) {
386 0 : ldb_asprintf_errstring(ldb, "Failed to setup krb5_context: %s", error_message(krb5_ret));
387 0 : goto fail;
388 : }
389 :
390 4815 : tmp_ctx = talloc_new(data);
391 4815 : if (!tmp_ctx) {
392 0 : ldb_oom(ldb);
393 0 : goto fail;
394 : }
395 :
396 5066 : for (p=data->changed_dns; p; p = p->next) {
397 26 : const char *error_string;
398 26 : const char *realm;
399 26 : char *upper_realm;
400 251 : struct ldb_message_element *spn_el = ldb_msg_find_element(p->msg, "servicePrincipalName");
401 251 : const char **SPNs = NULL;
402 251 : int num_SPNs = 0;
403 26 : int i;
404 :
405 251 : realm = ldb_msg_find_attr_as_string(p->msg, "realm", NULL);
406 :
407 251 : if (spn_el) {
408 251 : upper_realm = strupper_talloc(tmp_ctx, realm);
409 251 : if (!upper_realm) {
410 0 : ldb_oom(ldb);
411 0 : goto fail;
412 : }
413 :
414 251 : num_SPNs = spn_el->num_values;
415 251 : SPNs = talloc_array(tmp_ctx, const char *, num_SPNs);
416 251 : if (!SPNs) {
417 0 : ldb_oom(ldb);
418 0 : goto fail;
419 : }
420 733 : for (i = 0; i < num_SPNs; i++) {
421 964 : SPNs[i] = talloc_asprintf(SPNs, "%*.*s@%s",
422 431 : (int)spn_el->values[i].length,
423 482 : (int)spn_el->values[i].length,
424 482 : (const char *)spn_el->values[i].data,
425 : upper_realm);
426 482 : if (!SPNs[i]) {
427 0 : ldb_oom(ldb);
428 0 : goto fail;
429 : }
430 : }
431 : }
432 :
433 251 : krb5_ret = smb_krb5_update_keytab(tmp_ctx, smb_krb5_context->krb5_context,
434 251 : keytab_name_from_msg(tmp_ctx, ldb, p->msg),
435 251 : ldb_msg_find_attr_as_string(p->msg, "samAccountName", NULL),
436 : realm, SPNs, num_SPNs,
437 251 : ldb_msg_find_attr_as_string(p->msg, "saltPrincipal", NULL),
438 251 : ldb_msg_find_attr_as_string(p->msg, "secret", NULL),
439 251 : ldb_msg_find_attr_as_string(p->msg, "priorSecret", NULL),
440 251 : ldb_msg_find_attr_as_int(p->msg, "msDS-KeyVersionNumber", 0),
441 251 : (uint32_t)ldb_msg_find_attr_as_int(p->msg, "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES),
442 251 : p->do_delete, NULL, &error_string);
443 251 : if (krb5_ret != 0) {
444 0 : ldb_asprintf_errstring(ldb, "Failed to update keytab from entry %s in %s: %s",
445 0 : ldb_dn_get_linearized(p->msg->dn),
446 0 : (const char *)ldb_get_opaque(ldb, "ldb_url"),
447 : error_string);
448 0 : goto fail;
449 : }
450 : }
451 :
452 4815 : talloc_free(data->changed_dns);
453 4815 : data->changed_dns = NULL;
454 4815 : talloc_free(tmp_ctx);
455 :
456 4815 : return ldb_next_prepare_commit(module);
457 :
458 0 : fail:
459 0 : talloc_free(data->changed_dns);
460 0 : data->changed_dns = NULL;
461 0 : talloc_free(tmp_ctx);
462 0 : return LDB_ERR_OPERATIONS_ERROR;
463 : }
464 :
465 : /* end a transaction */
466 38 : static int update_kt_del_trans(struct ldb_module *module)
467 : {
468 38 : struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
469 :
470 38 : talloc_free(data->changed_dns);
471 38 : data->changed_dns = NULL;
472 :
473 38 : return ldb_next_del_trans(module);
474 : }
475 :
476 69623 : static int update_kt_init(struct ldb_module *module)
477 : {
478 2687 : struct ldb_context *ldb;
479 2687 : struct update_kt_private *data;
480 :
481 69623 : ldb = ldb_module_get_ctx(module);
482 :
483 69623 : data = talloc(module, struct update_kt_private);
484 69623 : if (data == NULL) {
485 0 : return ldb_oom(ldb);
486 : }
487 :
488 69623 : data->changed_dns = NULL;
489 :
490 69623 : ldb_module_set_private(module, data);
491 :
492 69623 : return ldb_next_init(module);
493 : }
494 :
495 : static const struct ldb_module_ops ldb_update_keytab_module_ops = {
496 : .name = "update_keytab",
497 : .init_context = update_kt_init,
498 : .add = update_kt_add,
499 : .modify = update_kt_modify,
500 : .rename = update_kt_rename,
501 : .del = update_kt_delete,
502 : .prepare_commit = update_kt_prepare_commit,
503 : .del_transaction = update_kt_del_trans,
504 : };
505 :
506 6040 : int ldb_update_keytab_module_init(const char *version)
507 : {
508 6040 : LDB_MODULE_CHECK_VERSION(version);
509 6040 : return ldb_register_module(&ldb_update_keytab_module_ops);
510 : }
|