Merge branch 'master' of ssh://git.samba.org/data/git/samba
[amitay/samba.git] / source4 / dsdb / samdb / ldb_modules / update_keytab.c
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 "auth/credentials/credentials.h"
33 #include "auth/credentials/credentials_krb5.h"
34 #include "system/kerberos.h"
35
36 struct dn_list {
37         struct cli_credentials *creds;
38         struct dn_list *prev, *next;
39 };
40
41 struct update_kt_private {
42         struct dn_list *changed_dns;
43 };
44
45 struct update_kt_ctx {
46         struct ldb_module *module;
47         struct ldb_request *req;
48
49         struct ldb_dn *dn;
50         bool do_delete;
51
52         struct ldb_reply *op_reply;
53         bool found;
54 };
55
56 static struct update_kt_ctx *update_kt_ctx_init(struct ldb_module *module,
57                                                 struct ldb_request *req)
58 {
59         struct update_kt_ctx *ac;
60
61         ac = talloc_zero(req, struct update_kt_ctx);
62         if (ac == NULL) {
63                 ldb_oom(ldb_module_get_ctx(module));
64                 return NULL;
65         }
66
67         ac->module = module;
68         ac->req = req;
69
70         return ac;
71 }
72
73 /* FIXME: too many semi-async searches here for my taste, direct and indirect as
74  * cli_credentials_set_secrets() performs a sync ldb search.
75  * Just hope we are lucky and nothing breaks (using the tdb backend masks a lot
76  * of async issues). -SSS
77  */
78 static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool do_delete) {
79         struct ldb_context *ldb = ldb_module_get_ctx(module);
80         struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
81         struct dn_list *item;
82         char *filter;
83         struct ldb_result *res;
84         const char *attrs[] = { NULL };
85         int ret;
86         NTSTATUS status;
87
88         filter = talloc_asprintf(data, "(&(dn=%s)(&(objectClass=kerberosSecret)(privateKeytab=*)))",
89                                  ldb_dn_get_linearized(dn));
90         if (!filter) {
91                 ldb_oom(ldb);
92                 return LDB_ERR_OPERATIONS_ERROR;
93         }
94
95         ret = ldb_search(ldb, data, &res,
96                          dn, LDB_SCOPE_BASE, attrs, "%s", filter);
97         if (ret != LDB_SUCCESS) {
98                 talloc_free(filter);
99                 return ret;
100         }
101
102         if (res->count != 1) {
103                 /* if it's not a kerberosSecret then we don't have anything to update */
104                 talloc_free(res);
105                 talloc_free(filter);
106                 return LDB_SUCCESS;
107         }
108         talloc_free(res);
109
110         item = talloc(data->changed_dns? (void *)data->changed_dns: (void *)data, struct dn_list);
111         if (!item) {
112                 talloc_free(filter);
113                 ldb_oom(ldb);
114                 return LDB_ERR_OPERATIONS_ERROR;
115         }
116
117         item->creds = cli_credentials_init(item);
118         if (!item->creds) {
119                 DEBUG(1, ("cli_credentials_init failed!"));
120                 talloc_free(filter);
121                 ldb_oom(ldb);
122                 return LDB_ERR_OPERATIONS_ERROR;
123         }
124
125         cli_credentials_set_conf(item->creds, ldb_get_opaque(ldb, "loadparm"));
126         status = cli_credentials_set_secrets(item->creds, ldb_get_event_context(ldb), ldb_get_opaque(ldb, "loadparm"), ldb, NULL, filter);
127         talloc_free(filter);
128         if (NT_STATUS_IS_OK(status)) {
129                 if (do_delete) {
130                         /* Ensure we don't helpfully keep an old keytab entry */
131                         cli_credentials_set_kvno(item->creds, cli_credentials_get_kvno(item->creds)+2); 
132                         /* Wipe passwords */
133                         cli_credentials_set_nt_hash(item->creds, NULL, 
134                                                     CRED_SPECIFIED);
135                 }
136                 DLIST_ADD_END(data->changed_dns, item, struct dn_list *);
137         }
138         return LDB_SUCCESS;
139 }
140
141 static int ukt_search_modified(struct update_kt_ctx *ac);
142
143 static int update_kt_op_callback(struct ldb_request *req,
144                                  struct ldb_reply *ares)
145 {
146         struct ldb_context *ldb;
147         struct update_kt_ctx *ac;
148         int ret;
149
150         ac = talloc_get_type(req->context, struct update_kt_ctx);
151         ldb = ldb_module_get_ctx(ac->module);
152
153         if (!ares) {
154                 return ldb_module_done(ac->req, NULL, NULL,
155                                         LDB_ERR_OPERATIONS_ERROR);
156         }
157         if (ares->error != LDB_SUCCESS) {
158                 return ldb_module_done(ac->req, ares->controls,
159                                         ares->response, ares->error);
160         }
161
162         if (ares->type != LDB_REPLY_DONE) {
163                 ldb_set_errstring(ldb, "Invalid request type!\n");
164                 return ldb_module_done(ac->req, NULL, NULL,
165                                         LDB_ERR_OPERATIONS_ERROR);
166         }
167
168         if (ac->do_delete) {
169                 return ldb_module_done(ac->req, ares->controls,
170                                         ares->response, LDB_SUCCESS);
171         }
172
173         ac->op_reply = talloc_steal(ac, ares);
174
175         ret = ukt_search_modified(ac);
176         if (ret != LDB_SUCCESS) {
177                 return ldb_module_done(ac->req, NULL, NULL, ret);
178         }
179
180         return LDB_SUCCESS;
181 }
182
183 static int ukt_del_op(struct update_kt_ctx *ac)
184 {
185         struct ldb_context *ldb;
186         struct ldb_request *down_req;
187         int ret;
188
189         ldb = ldb_module_get_ctx(ac->module);
190
191         ret = ldb_build_del_req(&down_req, ldb, ac,
192                                 ac->dn,
193                                 ac->req->controls,
194                                 ac, update_kt_op_callback,
195                                 ac->req);
196         if (ret != LDB_SUCCESS) {
197                 return ret;
198         }
199         return ldb_next_request(ac->module, down_req);
200 }
201
202 static int ukt_search_modified_callback(struct ldb_request *req,
203                                         struct ldb_reply *ares)
204 {
205         struct update_kt_ctx *ac;
206         int ret;
207
208         ac = talloc_get_type(req->context, struct update_kt_ctx);
209
210         if (!ares) {
211                 return ldb_module_done(ac->req, NULL, NULL,
212                                         LDB_ERR_OPERATIONS_ERROR);
213         }
214         if (ares->error != LDB_SUCCESS) {
215                 return ldb_module_done(ac->req, ares->controls,
216                                         ares->response, ares->error);
217         }
218
219         switch (ares->type) {
220         case LDB_REPLY_ENTRY:
221
222                 ac->found = true;
223                 break;
224
225         case LDB_REPLY_REFERRAL:
226                 /* ignore */
227                 break;
228
229         case LDB_REPLY_DONE:
230
231                 if (ac->found) {
232                         /* do the dirty sync job here :/ */
233                         ret = add_modified(ac->module, ac->dn, ac->do_delete);
234                 }
235
236                 if (ac->do_delete) {
237                         ret = ukt_del_op(ac);
238                         if (ret != LDB_SUCCESS) {
239                                 return ldb_module_done(ac->req,
240                                                         NULL, NULL, ret);
241                         }
242                         break;
243                 }
244
245                 return ldb_module_done(ac->req, ac->op_reply->controls,
246                                         ac->op_reply->response, LDB_SUCCESS);
247         }
248
249         talloc_free(ares);
250         return LDB_SUCCESS;
251 }
252
253 static int ukt_search_modified(struct update_kt_ctx *ac)
254 {
255         struct ldb_context *ldb;
256         static const char * const attrs[] = { "distinguishedName", NULL };
257         struct ldb_request *search_req;
258         int ret;
259
260         ldb = ldb_module_get_ctx(ac->module);
261
262         ret = ldb_build_search_req(&search_req, ldb, ac,
263                                    ac->dn, LDB_SCOPE_BASE,
264                                    "(&(objectClass=kerberosSecret)"
265                                      "(privateKeytab=*))", attrs,
266                                    NULL,
267                                    ac, ukt_search_modified_callback,
268                                    ac->req);
269         if (ret != LDB_SUCCESS) {
270                 return ret;
271         }
272         return ldb_next_request(ac->module, search_req);
273 }
274
275
276 /* add */
277 static int update_kt_add(struct ldb_module *module, struct ldb_request *req)
278 {
279         struct ldb_context *ldb;
280         struct update_kt_ctx *ac;
281         struct ldb_request *down_req;
282         int ret;
283
284         ldb = ldb_module_get_ctx(module);
285
286         ac = update_kt_ctx_init(module, req);
287         if (ac == NULL) {
288                 return LDB_ERR_OPERATIONS_ERROR;
289         }
290
291         ac->dn = req->op.add.message->dn;
292
293         ret = ldb_build_add_req(&down_req, ldb, ac,
294                                 req->op.add.message,
295                                 req->controls,
296                                 ac, update_kt_op_callback,
297                                 req);
298         if (ret != LDB_SUCCESS) {
299                 return ret;
300         }
301
302         return ldb_next_request(module, down_req);
303 }
304
305 /* modify */
306 static int update_kt_modify(struct ldb_module *module, struct ldb_request *req)
307 {
308         struct ldb_context *ldb;
309         struct update_kt_ctx *ac;
310         struct ldb_request *down_req;
311         int ret;
312
313         ldb = ldb_module_get_ctx(module);
314
315         ac = update_kt_ctx_init(module, req);
316         if (ac == NULL) {
317                 return LDB_ERR_OPERATIONS_ERROR;
318         }
319
320         ac->dn = req->op.mod.message->dn;
321
322         ret = ldb_build_mod_req(&down_req, ldb, ac,
323                                 req->op.mod.message,
324                                 req->controls,
325                                 ac, update_kt_op_callback,
326                                 req);
327         if (ret != LDB_SUCCESS) {
328                 return ret;
329         }
330
331         return ldb_next_request(module, down_req);
332 }
333
334 /* delete */
335 static int update_kt_delete(struct ldb_module *module, struct ldb_request *req)
336 {
337         struct update_kt_ctx *ac;
338
339         ac = update_kt_ctx_init(module, req);
340         if (ac == NULL) {
341                 return LDB_ERR_OPERATIONS_ERROR;
342         }
343
344         ac->dn = req->op.del.dn;
345         ac->do_delete = true;
346
347         return ukt_search_modified(ac);
348 }
349
350 /* rename */
351 static int update_kt_rename(struct ldb_module *module, struct ldb_request *req)
352 {
353         struct ldb_context *ldb;
354         struct update_kt_ctx *ac;
355         struct ldb_request *down_req;
356         int ret;
357
358         ldb = ldb_module_get_ctx(module);
359
360         ac = update_kt_ctx_init(module, req);
361         if (ac == NULL) {
362                 return LDB_ERR_OPERATIONS_ERROR;
363         }
364
365         ac->dn = req->op.rename.newdn;
366
367         ret = ldb_build_rename_req(&down_req, ldb, ac,
368                                 req->op.rename.olddn,
369                                 req->op.rename.newdn,
370                                 req->controls,
371                                 ac, update_kt_op_callback,
372                                 req);
373         if (ret != LDB_SUCCESS) {
374                 return ret;
375         }
376
377         return ldb_next_request(module, down_req);
378 }
379
380 /* end a transaction */
381 static int update_kt_end_trans(struct ldb_module *module)
382 {
383         struct ldb_context *ldb;
384         struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
385         struct dn_list *p;
386
387         ldb = ldb_module_get_ctx(module);
388
389         for (p=data->changed_dns; p; p = p->next) {
390                 int kret;
391                 kret = cli_credentials_update_keytab(p->creds, ldb_get_event_context(ldb), ldb_get_opaque(ldb, "loadparm"));
392                 if (kret != 0) {
393                         talloc_free(data->changed_dns);
394                         data->changed_dns = NULL;
395                         ldb_asprintf_errstring(ldb, "Failed to update keytab: %s", error_message(kret));
396                         return LDB_ERR_OPERATIONS_ERROR;
397                 }
398         }
399
400         talloc_free(data->changed_dns);
401         data->changed_dns = NULL;
402
403         return ldb_next_end_trans(module);
404 }
405
406 /* end a transaction */
407 static int update_kt_del_trans(struct ldb_module *module)
408 {
409         struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
410
411         talloc_free(data->changed_dns);
412         data->changed_dns = NULL;
413
414         return ldb_next_del_trans(module);
415 }
416
417 static int update_kt_init(struct ldb_module *module)
418 {
419         struct ldb_context *ldb;
420         struct update_kt_private *data;
421
422         ldb = ldb_module_get_ctx(module);
423
424         data = talloc(module, struct update_kt_private);
425         if (data == NULL) {
426                 ldb_oom(ldb);
427                 return LDB_ERR_OPERATIONS_ERROR;
428         }
429
430         data->changed_dns = NULL;
431
432         ldb_module_set_private(module, data);
433
434         return ldb_next_init(module);
435 }
436
437 _PUBLIC_ const struct ldb_module_ops ldb_update_keytab_module_ops = {
438         .name              = "update_keytab",
439         .init_context      = update_kt_init,
440         .add               = update_kt_add,
441         .modify            = update_kt_modify,
442         .rename            = update_kt_rename,
443         .del               = update_kt_delete,
444         .end_transaction   = update_kt_end_trans,
445         .del_transaction   = update_kt_del_trans,
446 };