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