1/* 2 ldb database library 3 4 Copyright (C) Simo Sorce 2005 5 6 ** NOTE! The following LGPL license applies to the ldb 7 ** library. This does NOT imply that all of Samba is released 8 ** under the LGPL 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 3 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; if not, see <http://www.gnu.org/licenses/>. 22*/ 23 24/* 25 * Name: ldb 26 * 27 * Component: ldb attribute scoped query control module 28 * 29 * Description: this module searches all the the objects pointed 30 * by the DNs contained in the references attribute 31 * 32 * Author: Simo Sorce 33 */ 34 35#include "includes.h" 36#include "ldb/include/includes.h" 37 38struct asq_context { 39 40 enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step; 41 42 struct ldb_module *module; 43 void *up_context; 44 int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *); 45 46 const char * const *req_attrs; 47 char *req_attribute; 48 enum { 49 ASQ_CTRL_SUCCESS = 0, 50 ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX = 21, 51 ASQ_CTRL_UNWILLING_TO_PERFORM = 53, 52 ASQ_CTRL_AFFECTS_MULTIPLE_DSA = 71 53 } asq_ret; 54 55 struct ldb_request *base_req; 56 struct ldb_reply *base_res; 57 58 struct ldb_request **reqs; 59 int num_reqs; 60 int cur_req; 61 62 struct ldb_control **controls; 63}; 64 65static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module, 66 void *context, 67 int (*callback)(struct ldb_context *, void *, struct ldb_reply *)) 68{ 69 struct asq_context *ac; 70 struct ldb_handle *h; 71 72 h = talloc_zero(mem_ctx, struct ldb_handle); 73 if (h == NULL) { 74 ldb_set_errstring(module->ldb, "Out of Memory"); 75 return NULL; 76 } 77 78 h->module = module; 79 80 ac = talloc_zero(h, struct asq_context); 81 if (ac == NULL) { 82 ldb_set_errstring(module->ldb, "Out of Memory"); 83 talloc_free(h); 84 return NULL; 85 } 86 87 h->private_data = (void *)ac; 88 89 h->state = LDB_ASYNC_INIT; 90 h->status = LDB_SUCCESS; 91 92 ac->module = module; 93 ac->up_context = context; 94 ac->up_callback = callback; 95 96 return h; 97} 98 99static int asq_terminate(struct ldb_handle *handle) 100{ 101 struct asq_context *ac; 102 struct ldb_reply *ares; 103 struct ldb_asq_control *asq; 104 int i; 105 106 ac = talloc_get_type(handle->private_data, struct asq_context); 107 if (ac == NULL) { 108 return LDB_ERR_OPERATIONS_ERROR; 109 } 110 111 handle->status = LDB_SUCCESS; 112 handle->state = LDB_ASYNC_DONE; 113 114 ares = talloc_zero(ac, struct ldb_reply); 115 if (ares == NULL) 116 return LDB_ERR_OPERATIONS_ERROR; 117 118 ares->type = LDB_REPLY_DONE; 119 120 if (ac->controls) { 121 for (i = 0; ac->controls[i]; i++); 122 ares->controls = talloc_move(ares, &ac->controls); 123 } else { 124 i = 0; 125 } 126 127 ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, i + 2); 128 129 if (ares->controls == NULL) 130 return LDB_ERR_OPERATIONS_ERROR; 131 132 ares->controls[i] = talloc(ares->controls, struct ldb_control); 133 if (ares->controls[i] == NULL) 134 return LDB_ERR_OPERATIONS_ERROR; 135 136 ares->controls[i]->oid = LDB_CONTROL_ASQ_OID; 137 ares->controls[i]->critical = 0; 138 139 asq = talloc_zero(ares->controls[i], struct ldb_asq_control); 140 if (asq == NULL) 141 return LDB_ERR_OPERATIONS_ERROR; 142 143 asq->result = ac->asq_ret; 144 145 ares->controls[i]->data = asq; 146 147 ares->controls[i + 1] = NULL; 148 149 ac->up_callback(ac->module->ldb, ac->up_context, ares); 150 151 return LDB_SUCCESS; 152} 153 154static int asq_base_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 155{ 156 struct asq_context *ac; 157 158 if (!context || !ares) { 159 ldb_set_errstring(ldb, "NULL Context or Result in callback"); 160 goto error; 161 } 162 163 if (!(ac = talloc_get_type(context, struct asq_context))) { 164 goto error; 165 } 166 167 /* we are interested only in the single reply (base search) we receive here */ 168 if (ares->type == LDB_REPLY_ENTRY) { 169 ac->base_res = talloc_move(ac, &ares); 170 } else { 171 talloc_free(ares); 172 } 173 174 return LDB_SUCCESS; 175error: 176 talloc_free(ares); 177 return LDB_ERR_OPERATIONS_ERROR; 178} 179 180static int asq_reqs_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 181{ 182 struct asq_context *ac; 183 184 if (!context || !ares) { 185 ldb_set_errstring(ldb, "NULL Context or Result in callback"); 186 goto error; 187 } 188 189 if (!(ac = talloc_get_type(context, struct asq_context))) { 190 goto error; 191 } 192 193 /* we are interested only in the single reply (base search) we receive here */ 194 if (ares->type == LDB_REPLY_ENTRY) { 195 196 /* pass the message up to the original callback as we 197 * do not have to elaborate on it any further */ 198 return ac->up_callback(ac->module->ldb, ac->up_context, ares); 199 200 } else { /* ignore any REFERRAL or DONE reply */ 201 talloc_free(ares); 202 } 203 204 return LDB_SUCCESS; 205error: 206 talloc_free(ares); 207 return LDB_ERR_OPERATIONS_ERROR; 208} 209 210static int asq_search(struct ldb_module *module, struct ldb_request *req) 211{ 212 struct ldb_control *control; 213 struct ldb_asq_control *asq_ctrl; 214 struct asq_context *ac; 215 struct ldb_handle *h; 216 char **base_attrs; 217 int ret; 218 219 /* check if there's a paged request control */ 220 control = get_control_from_list(req->controls, LDB_CONTROL_ASQ_OID); 221 if (control == NULL) { 222 /* not found go on */ 223 return ldb_next_request(module, req); 224 } 225 226 req->handle = NULL; 227 228 if (!req->callback || !req->context) { 229 ldb_set_errstring(module->ldb, 230 "Async interface called with NULL callback function or NULL context"); 231 return LDB_ERR_OPERATIONS_ERROR; 232 } 233 234 asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control); 235 if (!asq_ctrl) { 236 return LDB_ERR_PROTOCOL_ERROR; 237 } 238 239 h = init_handle(req, module, req->context, req->callback); 240 if (!h) { 241 return LDB_ERR_OPERATIONS_ERROR; 242 } 243 if (!(ac = talloc_get_type(h->private_data, struct asq_context))) { 244 245 return LDB_ERR_OPERATIONS_ERROR; 246 } 247 248 req->handle = h; 249 250 /* check the search is well formed */ 251 if (req->op.search.scope != LDB_SCOPE_BASE) { 252 ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM; 253 return asq_terminate(h); 254 } 255 256 ac->req_attrs = req->op.search.attrs; 257 ac->req_attribute = talloc_strdup(ac, asq_ctrl->source_attribute); 258 if (ac->req_attribute == NULL) 259 return LDB_ERR_OPERATIONS_ERROR; 260 261 /* get the object to retrieve the DNs to search */ 262 ac->base_req = talloc_zero(req, struct ldb_request); 263 if (ac->base_req == NULL) 264 return LDB_ERR_OPERATIONS_ERROR; 265 ac->base_req->operation = req->operation; 266 ac->base_req->op.search.base = req->op.search.base; 267 ac->base_req->op.search.scope = LDB_SCOPE_BASE; 268 ac->base_req->op.search.tree = req->op.search.tree; 269 base_attrs = talloc_array(ac->base_req, char *, 2); 270 if (base_attrs == NULL) 271 return LDB_ERR_OPERATIONS_ERROR; 272 base_attrs[0] = talloc_strdup(base_attrs, asq_ctrl->source_attribute); 273 if (base_attrs[0] == NULL) 274 return LDB_ERR_OPERATIONS_ERROR; 275 base_attrs[1] = NULL; 276 ac->base_req->op.search.attrs = (const char * const *)base_attrs; 277 278 ac->base_req->context = ac; 279 ac->base_req->callback = asq_base_callback; 280 ldb_set_timeout_from_prev_req(module->ldb, req, ac->base_req); 281 282 ac->step = ASQ_SEARCH_BASE; 283 284 ret = ldb_request(module->ldb, ac->base_req); 285 286 if (ret != LDB_SUCCESS) { 287 return ret; 288 } 289 290 return LDB_SUCCESS; 291} 292 293static int asq_requests(struct ldb_handle *handle) { 294 struct asq_context *ac; 295 struct ldb_message_element *el; 296 int i; 297 298 if (!(ac = talloc_get_type(handle->private_data, 299 struct asq_context))) { 300 return LDB_ERR_OPERATIONS_ERROR; 301 } 302 303 /* look up the DNs */ 304 if (ac->base_res == NULL) { 305 return LDB_ERR_NO_SUCH_OBJECT; 306 } 307 el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute); 308 /* no values found */ 309 if (el == NULL) { 310 ac->asq_ret = ASQ_CTRL_SUCCESS; 311 return asq_terminate(handle); 312 } 313 314 /* build up the requests call chain */ 315 ac->num_reqs = el->num_values; 316 ac->cur_req = 0; 317 ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs); 318 if (ac->reqs == NULL) { 319 return LDB_ERR_OPERATIONS_ERROR; 320 } 321 322 for (i = 0; i < el->num_values; i++) { 323 324 ac->reqs[i] = talloc_zero(ac->reqs, struct ldb_request); 325 if (ac->reqs[i] == NULL) 326 return LDB_ERR_OPERATIONS_ERROR; 327 ac->reqs[i]->operation = LDB_SEARCH; 328 ac->reqs[i]->op.search.base = ldb_dn_explode(ac->reqs[i], (const char *)el->values[i].data); 329 if (ac->reqs[i]->op.search.base == NULL) { 330 ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX; 331 return asq_terminate(handle); 332 } 333 ac->reqs[i]->op.search.scope = LDB_SCOPE_BASE; 334 ac->reqs[i]->op.search.tree = ac->base_req->op.search.tree; 335 ac->reqs[i]->op.search.attrs = ac->req_attrs; 336 337 ac->reqs[i]->context = ac; 338 ac->reqs[i]->callback = asq_reqs_callback; 339 ldb_set_timeout_from_prev_req(ac->module->ldb, ac->base_req, ac->reqs[i]); 340 } 341 342 ac->step = ASQ_SEARCH_MULTI; 343 344 return LDB_SUCCESS; 345} 346 347static int asq_wait_none(struct ldb_handle *handle) 348{ 349 struct asq_context *ac; 350 int ret; 351 352 if (!handle || !handle->private_data) { 353 return LDB_ERR_OPERATIONS_ERROR; 354 } 355 356 if (handle->state == LDB_ASYNC_DONE) { 357 return handle->status; 358 } 359 360 handle->state = LDB_ASYNC_PENDING; 361 handle->status = LDB_SUCCESS; 362 363 if (!(ac = talloc_get_type(handle->private_data, 364 struct asq_context))) { 365 return LDB_ERR_OPERATIONS_ERROR; 366 } 367 368 switch (ac->step) { 369 case ASQ_SEARCH_BASE: 370 ret = ldb_wait(ac->base_req->handle, LDB_WAIT_NONE); 371 372 if (ret != LDB_SUCCESS) { 373 handle->status = ret; 374 goto done; 375 } 376 377 if (ac->base_req->handle->status != LDB_SUCCESS) { 378 handle->status = ac->base_req->handle->status; 379 goto done; 380 } 381 if (ac->base_req->handle->state != LDB_ASYNC_DONE) { 382 return LDB_SUCCESS; 383 } 384 385 ret = asq_requests(handle); 386 387 /* no break nor return, 388 * the set of requests is performed in ASQ_SEARCH_MULTI 389 */ 390 391 case ASQ_SEARCH_MULTI: 392 393 if (ac->reqs[ac->cur_req]->handle == NULL) { 394 ret = ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]); 395 if (ret != LDB_SUCCESS) { 396 return ret; 397 } 398 } 399 400 ret = ldb_wait(ac->reqs[ac->cur_req]->handle, LDB_WAIT_NONE); 401 402 if (ret != LDB_SUCCESS) { 403 handle->status = ret; 404 goto done; 405 } 406 if (ac->reqs[ac->cur_req]->handle->status != LDB_SUCCESS) { 407 handle->status = ac->reqs[ac->cur_req]->handle->status; 408 } 409 410 if (ac->reqs[ac->cur_req]->handle->state == LDB_ASYNC_DONE) { 411 ac->cur_req++; 412 } 413 414 if (ac->cur_req < ac->num_reqs) { 415 return LDB_SUCCESS; 416 } 417 418 return asq_terminate(handle); 419 420 default: 421 ret = LDB_ERR_OPERATIONS_ERROR; 422 goto done; 423 } 424 425 ret = LDB_SUCCESS; 426 427done: 428 handle->state = LDB_ASYNC_DONE; 429 return ret; 430} 431 432static int asq_wait_all(struct ldb_handle *handle) 433{ 434 int ret; 435 436 while (handle->state != LDB_ASYNC_DONE) { 437 ret = asq_wait_none(handle); 438 if (ret != LDB_SUCCESS) { 439 return ret; 440 } 441 } 442 443 return handle->status; 444} 445 446static int asq_wait(struct ldb_handle *handle, enum ldb_wait_type type) 447{ 448 if (type == LDB_WAIT_ALL) { 449 return asq_wait_all(handle); 450 } else { 451 return asq_wait_none(handle); 452 } 453} 454 455static int asq_init(struct ldb_module *module) 456{ 457 struct ldb_request *req; 458 int ret; 459 460 req = talloc_zero(module, struct ldb_request); 461 if (req == NULL) { 462 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Out of memory!\n"); 463 return LDB_ERR_OPERATIONS_ERROR; 464 } 465 466 req->operation = LDB_REQ_REGISTER_CONTROL; 467 req->op.reg_control.oid = LDB_CONTROL_ASQ_OID; 468 469 ret = ldb_request(module->ldb, req); 470 if (ret != LDB_SUCCESS) { 471 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!\n"); 472 } 473 474 return ldb_next_init(module); 475} 476 477 478static const struct ldb_module_ops asq_ops = { 479 .name = "asq", 480 .search = asq_search, 481 .wait = asq_wait, 482 .init_context = asq_init 483}; 484 485int ldb_asq_init(void) 486{ 487 return ldb_register_module(&asq_ops); 488} 489