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