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 4960 : static struct update_kt_ctx *update_kt_ctx_init(struct ldb_module *module,
63 : struct ldb_request *req)
64 : {
65 : struct update_kt_ctx *ac;
66 :
67 4960 : ac = talloc_zero(req, struct update_kt_ctx);
68 4960 : if (ac == NULL) {
69 0 : ldb_oom(ldb_module_get_ctx(module));
70 0 : return NULL;
71 : }
72 :
73 4960 : ac->module = module;
74 4960 : ac->req = req;
75 :
76 4960 : 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 174 : static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool do_delete,
85 : struct ldb_request *parent)
86 : {
87 174 : struct ldb_context *ldb = ldb_module_get_ctx(module);
88 174 : struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
89 : struct dn_list *item;
90 : char *filter;
91 : struct ldb_result *res;
92 : int ret;
93 :
94 174 : filter = talloc_asprintf(data,
95 : "(&(objectClass=kerberosSecret)(privateKeytab=*))");
96 174 : if (!filter) {
97 0 : return ldb_oom(ldb);
98 : }
99 :
100 174 : ret = dsdb_module_search(module, data, &res,
101 : dn, LDB_SCOPE_BASE, NULL,
102 : DSDB_FLAG_NEXT_MODULE, parent,
103 : "%s", filter);
104 174 : talloc_free(filter);
105 174 : if (ret != LDB_SUCCESS) {
106 0 : return ret;
107 : }
108 :
109 174 : 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 174 : item = talloc(data->changed_dns? (void *)data->changed_dns: (void *)data, struct dn_list);
116 174 : if (!item) {
117 0 : talloc_free(res);
118 0 : return ldb_oom(ldb);
119 : }
120 :
121 174 : item->msg = talloc_steal(item, res->msgs[0]);
122 174 : item->do_delete = do_delete;
123 174 : talloc_free(res);
124 :
125 174 : DLIST_ADD_END(data->changed_dns, item);
126 174 : return LDB_SUCCESS;
127 : }
128 :
129 : static int ukt_search_modified(struct update_kt_ctx *ac);
130 :
131 4960 : static int update_kt_op_callback(struct ldb_request *req,
132 : struct ldb_reply *ares)
133 : {
134 : struct ldb_context *ldb;
135 : struct update_kt_ctx *ac;
136 : int ret;
137 :
138 4960 : ac = talloc_get_type(req->context, struct update_kt_ctx);
139 4960 : ldb = ldb_module_get_ctx(ac->module);
140 :
141 4960 : if (!ares) {
142 0 : return ldb_module_done(ac->req, NULL, NULL,
143 : LDB_ERR_OPERATIONS_ERROR);
144 : }
145 4960 : if (ares->error != LDB_SUCCESS) {
146 5 : return ldb_module_done(ac->req, ares->controls,
147 : ares->response, ares->error);
148 : }
149 :
150 4955 : 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 4955 : if (ac->do_delete) {
157 1639 : return ldb_module_done(ac->req, ares->controls,
158 : ares->response, LDB_SUCCESS);
159 : }
160 :
161 3316 : ac->op_reply = talloc_steal(ac, ares);
162 :
163 3316 : ret = ukt_search_modified(ac);
164 3316 : if (ret != LDB_SUCCESS) {
165 0 : return ldb_module_done(ac->req, NULL, NULL, ret);
166 : }
167 :
168 3316 : return LDB_SUCCESS;
169 : }
170 :
171 1644 : static int ukt_del_op(struct update_kt_ctx *ac)
172 : {
173 : struct ldb_context *ldb;
174 : struct ldb_request *down_req;
175 : int ret;
176 :
177 1644 : ldb = ldb_module_get_ctx(ac->module);
178 :
179 2616 : ret = ldb_build_del_req(&down_req, ldb, ac,
180 : ac->dn,
181 1644 : ac->req->controls,
182 : ac, update_kt_op_callback,
183 : ac->req);
184 1644 : LDB_REQ_SET_LOCATION(down_req);
185 1644 : if (ret != LDB_SUCCESS) {
186 0 : return ret;
187 : }
188 1644 : return ldb_next_request(ac->module, down_req);
189 : }
190 :
191 5134 : static int ukt_search_modified_callback(struct ldb_request *req,
192 : struct ldb_reply *ares)
193 : {
194 : struct update_kt_ctx *ac;
195 : int ret;
196 :
197 5134 : ac = talloc_get_type(req->context, struct update_kt_ctx);
198 :
199 5134 : if (!ares) {
200 0 : return ldb_module_done(ac->req, NULL, NULL,
201 : LDB_ERR_OPERATIONS_ERROR);
202 : }
203 5134 : if (ares->error != LDB_SUCCESS) {
204 0 : return ldb_module_done(ac->req, ares->controls,
205 : ares->response, ares->error);
206 : }
207 :
208 5134 : switch (ares->type) {
209 174 : case LDB_REPLY_ENTRY:
210 :
211 174 : ac->found = true;
212 174 : break;
213 :
214 0 : case LDB_REPLY_REFERRAL:
215 : /* ignore */
216 0 : break;
217 :
218 4960 : case LDB_REPLY_DONE:
219 :
220 4960 : if (ac->found) {
221 : /* do the dirty sync job here :/ */
222 174 : ret = add_modified(ac->module, ac->dn, ac->do_delete, ac->req);
223 : }
224 :
225 4960 : if (ac->do_delete) {
226 1644 : ret = ukt_del_op(ac);
227 1644 : if (ret != LDB_SUCCESS) {
228 0 : return ldb_module_done(ac->req,
229 : NULL, NULL, ret);
230 : }
231 1644 : break;
232 : }
233 :
234 3316 : return ldb_module_done(ac->req, ac->op_reply->controls,
235 3316 : ac->op_reply->response, LDB_SUCCESS);
236 : }
237 :
238 1818 : talloc_free(ares);
239 1818 : return LDB_SUCCESS;
240 : }
241 :
242 4960 : static int ukt_search_modified(struct update_kt_ctx *ac)
243 : {
244 : struct ldb_context *ldb;
245 : static const char * const no_attrs[] = { NULL };
246 : struct ldb_request *search_req;
247 : int ret;
248 :
249 4960 : ldb = ldb_module_get_ctx(ac->module);
250 :
251 4960 : 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 4960 : LDB_REQ_SET_LOCATION(search_req);
259 4960 : if (ret != LDB_SUCCESS) {
260 0 : return ret;
261 : }
262 4960 : return ldb_next_request(ac->module, search_req);
263 : }
264 :
265 :
266 : /* add */
267 2094 : static int update_kt_add(struct ldb_module *module, struct ldb_request *req)
268 : {
269 : struct ldb_context *ldb;
270 : struct update_kt_ctx *ac;
271 : struct ldb_request *down_req;
272 : int ret;
273 :
274 2094 : ldb = ldb_module_get_ctx(module);
275 :
276 2094 : ac = update_kt_ctx_init(module, req);
277 2094 : if (ac == NULL) {
278 0 : return ldb_operr(ldb);
279 : }
280 :
281 2094 : ac->dn = req->op.add.message->dn;
282 :
283 2094 : 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 2094 : LDB_REQ_SET_LOCATION(down_req);
289 2094 : if (ret != LDB_SUCCESS) {
290 0 : return ret;
291 : }
292 :
293 2094 : return ldb_next_request(module, down_req);
294 : }
295 :
296 : /* modify */
297 1207 : static int update_kt_modify(struct ldb_module *module, struct ldb_request *req)
298 : {
299 : struct ldb_context *ldb;
300 : struct update_kt_ctx *ac;
301 : struct ldb_request *down_req;
302 : int ret;
303 :
304 1207 : ldb = ldb_module_get_ctx(module);
305 :
306 1207 : ac = update_kt_ctx_init(module, req);
307 1207 : if (ac == NULL) {
308 0 : return ldb_operr(ldb);
309 : }
310 :
311 1207 : ac->dn = req->op.mod.message->dn;
312 :
313 1207 : 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 1207 : LDB_REQ_SET_LOCATION(down_req);
319 1207 : if (ret != LDB_SUCCESS) {
320 0 : return ret;
321 : }
322 :
323 1207 : return ldb_next_request(module, down_req);
324 : }
325 :
326 : /* delete */
327 1644 : static int update_kt_delete(struct ldb_module *module, struct ldb_request *req)
328 : {
329 : struct update_kt_ctx *ac;
330 :
331 1644 : ac = update_kt_ctx_init(module, req);
332 1644 : if (ac == NULL) {
333 0 : return ldb_operr(ldb_module_get_ctx(module));
334 : }
335 :
336 1644 : ac->dn = req->op.del.dn;
337 1644 : ac->do_delete = true;
338 :
339 1644 : return ukt_search_modified(ac);
340 : }
341 :
342 : /* rename */
343 15 : static int update_kt_rename(struct ldb_module *module, struct ldb_request *req)
344 : {
345 : struct ldb_context *ldb;
346 : struct update_kt_ctx *ac;
347 : struct ldb_request *down_req;
348 : int ret;
349 :
350 15 : ldb = ldb_module_get_ctx(module);
351 :
352 15 : ac = update_kt_ctx_init(module, req);
353 15 : if (ac == NULL) {
354 0 : return ldb_operr(ldb);
355 : }
356 :
357 15 : ac->dn = req->op.rename.newdn;
358 :
359 15 : 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 15 : LDB_REQ_SET_LOCATION(down_req);
366 15 : if (ret != LDB_SUCCESS) {
367 0 : return ret;
368 : }
369 :
370 15 : return ldb_next_request(module, down_req);
371 : }
372 :
373 : /* prepare for a commit */
374 4713 : static int update_kt_prepare_commit(struct ldb_module *module)
375 : {
376 4713 : struct ldb_context *ldb = ldb_module_get_ctx(module);
377 4713 : struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
378 : struct dn_list *p;
379 : struct smb_krb5_context *smb_krb5_context;
380 4713 : int krb5_ret = smb_krb5_init_context(data,
381 4713 : ldb_get_opaque(ldb, "loadparm"),
382 : &smb_krb5_context);
383 4713 : TALLOC_CTX *tmp_ctx = NULL;
384 :
385 4713 : 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 4713 : tmp_ctx = talloc_new(data);
391 4713 : if (!tmp_ctx) {
392 0 : ldb_oom(ldb);
393 0 : goto fail;
394 : }
395 :
396 7915 : for (p=data->changed_dns; p; p = p->next) {
397 : const char *error_string;
398 : const char *realm;
399 : char *upper_realm;
400 174 : struct ldb_message_element *spn_el = ldb_msg_find_element(p->msg, "servicePrincipalName");
401 174 : const char **SPNs = NULL;
402 174 : int num_SPNs = 0;
403 : int i;
404 :
405 174 : realm = ldb_msg_find_attr_as_string(p->msg, "realm", NULL);
406 :
407 174 : if (spn_el) {
408 174 : upper_realm = strupper_talloc(tmp_ctx, realm);
409 174 : if (!upper_realm) {
410 0 : ldb_oom(ldb);
411 0 : goto fail;
412 : }
413 :
414 174 : num_SPNs = spn_el->num_values;
415 174 : SPNs = talloc_array(tmp_ctx, const char *, num_SPNs);
416 174 : if (!SPNs) {
417 0 : ldb_oom(ldb);
418 0 : goto fail;
419 : }
420 503 : for (i = 0; i < num_SPNs; i++) {
421 658 : SPNs[i] = talloc_asprintf(SPNs, "%*.*s@%s",
422 329 : (int)spn_el->values[i].length,
423 329 : (int)spn_el->values[i].length,
424 329 : (const char *)spn_el->values[i].data,
425 : upper_realm);
426 329 : if (!SPNs[i]) {
427 0 : ldb_oom(ldb);
428 0 : goto fail;
429 : }
430 : }
431 : }
432 :
433 1287 : krb5_ret = smb_krb5_update_keytab(tmp_ctx, smb_krb5_context->krb5_context,
434 174 : keytab_name_from_msg(tmp_ctx, ldb, p->msg),
435 174 : ldb_msg_find_attr_as_string(p->msg, "samAccountName", NULL),
436 : realm, SPNs, num_SPNs,
437 174 : ldb_msg_find_attr_as_string(p->msg, "saltPrincipal", NULL),
438 174 : ldb_msg_find_attr_as_string(p->msg, "secret", NULL),
439 174 : ldb_msg_find_attr_as_string(p->msg, "priorSecret", NULL),
440 174 : ldb_msg_find_attr_as_int(p->msg, "msDS-KeyVersionNumber", 0),
441 174 : (uint32_t)ldb_msg_find_attr_as_int(p->msg, "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES),
442 174 : p->do_delete, NULL, &error_string);
443 174 : 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 4713 : talloc_free(data->changed_dns);
453 4713 : data->changed_dns = NULL;
454 4713 : talloc_free(tmp_ctx);
455 :
456 4713 : 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 14 : static int update_kt_del_trans(struct ldb_module *module)
467 : {
468 14 : struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
469 :
470 14 : talloc_free(data->changed_dns);
471 14 : data->changed_dns = NULL;
472 :
473 14 : return ldb_next_del_trans(module);
474 : }
475 :
476 43095 : static int update_kt_init(struct ldb_module *module)
477 : {
478 : struct ldb_context *ldb;
479 : struct update_kt_private *data;
480 :
481 43095 : ldb = ldb_module_get_ctx(module);
482 :
483 43095 : data = talloc(module, struct update_kt_private);
484 43095 : if (data == NULL) {
485 0 : return ldb_oom(ldb);
486 : }
487 :
488 43095 : data->changed_dns = NULL;
489 :
490 43095 : ldb_module_set_private(module, data);
491 :
492 43095 : 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 4310 : int ldb_update_keytab_module_init(const char *version)
507 : {
508 4310 : LDB_MODULE_CHECK_VERSION(version);
509 4310 : return ldb_register_module(&ldb_update_keytab_module_ops);
510 : }
|