root/source4/dsdb/samdb/ldb_modules/extended_dn_store.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. extended_dn_context_init
  2. extended_final_callback
  3. extended_replace_dn
  4. extended_store_replace
  5. extended_dn_add
  6. extended_dn_modify

   1 /* 
   2    ldb database library
   3 
   4    Copyright (C) Simo Sorce 2005-2008
   5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
   6 
   7    This program is free software; you can redistribute it and/or modify
   8    it under the terms of the GNU General Public License as published by
   9    the Free Software Foundation; either version 3 of the License, or
  10    (at your option) any later version.
  11    
  12    This program is distributed in the hope that it will be useful,
  13    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15    GNU General Public License for more details.
  16    
  17    You should have received a copy of the GNU General Public License
  18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20 
  21 /*
  22  *  Name: ldb
  23  *
  24  *  Component: ldb extended dn control module
  25  *
  26  *  Description: this module builds a special dn for returned search
  27  *  results nad creates the special DN in the backend store for new
  28  *  values.
  29  *
  30  *  This also has the curious result that we convert <SID=S-1-2-345>
  31  *  in an attribute value into a normal DN for the rest of the stack
  32  *  to process
  33  *
  34  *  Authors: Simo Sorce
  35  *           Andrew Bartlett
  36  */
  37 
  38 #include "includes.h"
  39 #include "ldb/include/ldb.h"
  40 #include "ldb/include/ldb_errors.h"
  41 #include "ldb/include/ldb_private.h"
  42 #include "librpc/gen_ndr/ndr_misc.h"
  43 #include "dsdb/samdb/samdb.h"
  44 #include "libcli/security/security.h"
  45 
  46 #include <time.h>
  47 
  48 struct extended_dn_replace_list {
  49         struct extended_dn_replace_list *next;
  50         struct ldb_dn *dn;
  51         TALLOC_CTX *mem_ctx;
  52         struct ldb_val *replace_dn;
  53         struct extended_dn_context *ac;
  54         struct ldb_request *search_req;
  55 };
  56 
  57 
  58 struct extended_dn_context {
  59         const struct dsdb_schema *schema;
  60         struct ldb_module *module;
  61         struct ldb_request *req;
  62         struct ldb_request *new_req;
  63 
  64         struct extended_dn_replace_list *ops;
  65         struct extended_dn_replace_list *cur;
  66 };
  67 
  68 
  69 static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
     /* [<][>][^][v][top][bottom][index][help] */
  70                                                             struct ldb_request *req)
  71 {
  72         struct extended_dn_context *ac;
  73 
  74         ac = talloc_zero(req, struct extended_dn_context);
  75         if (ac == NULL) {
  76                 ldb_oom(module->ldb);
  77                 return NULL;
  78         }
  79 
  80         ac->schema = dsdb_get_schema(module->ldb);
  81         ac->module = module;
  82         ac->req = req;
  83 
  84         return ac;
  85 }
  86 
  87 /* An extra layer of indirection because LDB does not allow the original request to be altered */
  88 
  89 static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
     /* [<][>][^][v][top][bottom][index][help] */
  90 {
  91         int ret = LDB_ERR_OPERATIONS_ERROR;
  92         struct extended_dn_context *ac;
  93         ac = talloc_get_type(req->context, struct extended_dn_context);
  94 
  95         if (ares->error != LDB_SUCCESS) {
  96                 ret = ldb_module_done(ac->req, ares->controls,
  97                                       ares->response, ares->error);
  98         } else {
  99                 switch (ares->type) {
 100                 case LDB_REPLY_ENTRY:
 101                         
 102                         ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
 103                         break;
 104                 case LDB_REPLY_REFERRAL:
 105                         
 106                         ret = ldb_module_send_referral(ac->req, ares->referral);
 107                         break;
 108                 case LDB_REPLY_DONE:
 109                         
 110                         ret = ldb_module_done(ac->req, ares->controls,
 111                                               ares->response, ares->error);
 112                         break;
 113                 }
 114         }
 115         return ret;
 116 }
 117 
 118 static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
     /* [<][>][^][v][top][bottom][index][help] */
 119 {
 120         struct extended_dn_replace_list *os = talloc_get_type(req->context, 
 121                                                            struct extended_dn_replace_list);
 122 
 123         if (!ares) {
 124                 return ldb_module_done(os->ac->req, NULL, NULL,
 125                                         LDB_ERR_OPERATIONS_ERROR);
 126         }
 127         if (ares->error == LDB_ERR_NO_SUCH_OBJECT) {
 128                 /* Don't worry too much about dangling references */
 129 
 130                 ldb_reset_err_string(os->ac->module->ldb);
 131                 if (os->next) {
 132                         struct extended_dn_replace_list *next;
 133 
 134                         next = os->next;
 135 
 136                         talloc_free(os);
 137 
 138                         os = next;
 139                         return ldb_next_request(os->ac->module, next->search_req);
 140                 } else {
 141                         /* Otherwise, we are done - let's run the
 142                          * request now we have swapped the DNs for the
 143                          * full versions */
 144                         return ldb_next_request(os->ac->module, os->ac->req);
 145                 }
 146         }
 147         if (ares->error != LDB_SUCCESS) {
 148                 return ldb_module_done(os->ac->req, ares->controls,
 149                                         ares->response, ares->error);
 150         }
 151 
 152         /* Only entries are interesting, and we only want the olddn */
 153         switch (ares->type) {
 154         case LDB_REPLY_ENTRY:
 155         {
 156                 /* This *must* be the right DN, as this is a base
 157                  * search.  We can't check, as it could be an extended
 158                  * DN, so a module below will resolve it */
 159                 struct ldb_dn *dn = ares->message->dn;
 160 
 161                 /* Replace the DN with the extended version of the DN
 162                  * (ie, add SID and GUID) */
 163                 *os->replace_dn = data_blob_string_const(
 164                         ldb_dn_get_extended_linearized(os->mem_ctx, 
 165                                                        dn, 1));
 166                 if (os->replace_dn->data == NULL) {
 167                         return ldb_module_done(os->ac->req, NULL, NULL,
 168                                                 LDB_ERR_OPERATIONS_ERROR);
 169                 }
 170                 break;
 171         }
 172         case LDB_REPLY_REFERRAL:
 173                 /* ignore */
 174                 break;
 175 
 176         case LDB_REPLY_DONE:
 177 
 178                 talloc_free(ares);
 179                 
 180                 /* Run the next search */
 181 
 182                 if (os->next) {
 183                         struct extended_dn_replace_list *next;
 184 
 185                         next = os->next;
 186 
 187                         talloc_free(os);
 188 
 189                         os = next;
 190                         return ldb_next_request(os->ac->module, next->search_req);
 191                 } else {
 192                         /* Otherwise, we are done - let's run the
 193                          * request now we have swapped the DNs for the
 194                          * full versions */
 195                         return ldb_next_request(os->ac->module, os->ac->new_req);
 196                 }
 197         }
 198 
 199         talloc_free(ares);
 200         return LDB_SUCCESS;
 201 }
 202 
 203 /* We have a 'normal' DN in the inbound request.  We need to find out
 204  * what the GUID and SID are on the DN it points to, so we can
 205  * construct an extended DN for storage.
 206  *
 207  * This creates a list of DNs to look up, and the plain DN to replace
 208  */
 209 
 210 static int extended_store_replace(struct extended_dn_context *ac,
     /* [<][>][^][v][top][bottom][index][help] */
 211                                   TALLOC_CTX *callback_mem_ctx,
 212                                   struct ldb_val *plain_dn)
 213 {
 214         int ret;
 215         struct extended_dn_replace_list *os;
 216         static const char *attrs[] = {
 217                 "objectSid",
 218                 "objectGUID",
 219                 NULL
 220         };
 221 
 222         os = talloc_zero(ac, struct extended_dn_replace_list);
 223         if (!os) {
 224                 return LDB_ERR_OPERATIONS_ERROR;
 225         }
 226 
 227         os->ac = ac;
 228         
 229         os->mem_ctx = callback_mem_ctx;
 230 
 231         os->dn = ldb_dn_from_ldb_val(os, ac->module->ldb, plain_dn);
 232         if (!os->dn || !ldb_dn_validate(os->dn)) {
 233                 talloc_free(os);
 234                 ldb_asprintf_errstring(ac->module->ldb, 
 235                                        "could not parse %.*s as a DN", (int)plain_dn->length, plain_dn->data);
 236                 return LDB_ERR_INVALID_DN_SYNTAX;
 237         }
 238 
 239         os->replace_dn = plain_dn;
 240 
 241         /* The search request here might happen to be for an
 242          * 'extended' style DN, such as <GUID=abced...>.  The next
 243          * module in the stack will convert this into a normal DN for
 244          * processing */
 245         ret = ldb_build_search_req(&os->search_req,
 246                                    ac->module->ldb, os, os->dn, LDB_SCOPE_BASE, NULL, 
 247                                    attrs, NULL, os, extended_replace_dn,
 248                                    ac->req);
 249 
 250         if (ret != LDB_SUCCESS) {
 251                 talloc_free(os);
 252                 return ret;
 253         }
 254 
 255         ret = ldb_request_add_control(os->search_req,
 256                                       DSDB_CONTROL_DN_STORAGE_FORMAT_OID,
 257                                       true, NULL);
 258         if (ret != LDB_SUCCESS) {
 259                 talloc_free(os);
 260                 return ret;
 261         }
 262 
 263         if (ac->ops) {
 264                 ac->cur->next = os;
 265         } else {
 266                 ac->ops = os;
 267         }
 268         ac->cur = os;
 269 
 270         return LDB_SUCCESS;
 271 }
 272 
 273 
 274 /* add */
 275 static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 276 {
 277         struct extended_dn_context *ac;
 278         int ret;
 279         int i, j;
 280 
 281         if (ldb_dn_is_special(req->op.add.message->dn)) {
 282                 /* do not manipulate our control entries */
 283                 return ldb_next_request(module, req);
 284         }
 285 
 286         ac = extended_dn_context_init(module, req);
 287         if (!ac) {
 288                 return LDB_ERR_OPERATIONS_ERROR;
 289         }
 290 
 291         if (!ac->schema) {
 292                 /* without schema, this doesn't make any sense */
 293                 talloc_free(ac);
 294                 return ldb_next_request(module, req);
 295         }
 296 
 297         for (i=0; i < req->op.add.message->num_elements; i++) {
 298                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
 299                 const struct dsdb_attribute *schema_attr
 300                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
 301                 if (!schema_attr) {
 302                         continue;
 303                 }
 304 
 305                 /* We only setup an extended DN GUID on these particular DN objects */
 306                 if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
 307                         continue;
 308                 }
 309 
 310                 /* Before we setup a procedure to modify the incoming message, we must copy it */
 311                 if (!ac->new_req) {
 312                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
 313                         if (!msg) {
 314                                 ldb_oom(module->ldb);
 315                                 return LDB_ERR_OPERATIONS_ERROR;
 316                         }
 317                    
 318                         ret = ldb_build_add_req(&ac->new_req, module->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
 319                         if (ret != LDB_SUCCESS) {
 320                                 return ret;
 321                         }
 322                 }
 323                 /* Re-calculate el */
 324                 el = &ac->new_req->op.add.message->elements[i];
 325                 for (j = 0; j < el->num_values; j++) {
 326                         ret = extended_store_replace(ac, ac->new_req->op.add.message->elements, &el->values[j]);
 327                         if (ret != LDB_SUCCESS) {
 328                                 return ret;
 329                         }
 330                 }
 331         }
 332 
 333         /* if DNs were set continue */
 334         if (ac->ops == NULL) {
 335                 talloc_free(ac);
 336                 return ldb_next_request(module, req);
 337         }
 338 
 339         /* start with the searches */
 340         return ldb_next_request(module, ac->ops->search_req);
 341 }
 342 
 343 /* modify */
 344 static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 345 {
 346         /* Look over list of modifications */
 347         /* Find if any are for linked attributes */
 348         /* Determine the effect of the modification */
 349         /* Apply the modify to the linked entry */
 350 
 351         int i, j;
 352         struct extended_dn_context *ac;
 353         int ret;
 354 
 355         if (ldb_dn_is_special(req->op.mod.message->dn)) {
 356                 /* do not manipulate our control entries */
 357                 return ldb_next_request(module, req);
 358         }
 359 
 360         ac = extended_dn_context_init(module, req);
 361         if (!ac) {
 362                 return LDB_ERR_OPERATIONS_ERROR;
 363         }
 364 
 365         if (!ac->schema) {
 366                 /* without schema, this doesn't make any sense */
 367                 return ldb_next_request(module, req);
 368         }
 369 
 370         for (i=0; i < req->op.mod.message->num_elements; i++) {
 371                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
 372                 const struct dsdb_attribute *schema_attr
 373                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
 374                 if (!schema_attr) {
 375                         continue;
 376                 }
 377 
 378                 /* We only setup an extended DN GUID on these particular DN objects */
 379                 if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
 380                         continue;
 381                 }
 382                 
 383                 /* Before we setup a procedure to modify the incoming message, we must copy it */
 384                 if (!ac->new_req) {
 385                         struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
 386                         if (!msg) {
 387                                 ldb_oom(module->ldb);
 388                                 return LDB_ERR_OPERATIONS_ERROR;
 389                         }
 390                    
 391                         ret = ldb_build_mod_req(&ac->new_req, module->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
 392                         if (ret != LDB_SUCCESS) {
 393                                 return ret;
 394                         }
 395                 }
 396                 /* Re-calculate el */
 397                 el = &ac->new_req->op.mod.message->elements[i];
 398                 /* For each value being added, we need to setup the lookups to fill in the extended DN */
 399                 for (j = 0; j < el->num_values; j++) {
 400                         struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, module->ldb, &el->values[j]);
 401                         if (!dn || !ldb_dn_validate(dn)) {
 402                                 ldb_asprintf_errstring(module->ldb, 
 403                                                        "could not parse attribute %s as a DN", el->name);
 404                                 return LDB_ERR_INVALID_DN_SYNTAX;
 405                         }
 406                         if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE) && !ldb_dn_has_extended(dn)) {
 407                                 /* NO need to figure this DN out, it's going to be deleted anyway */
 408                                 continue;
 409                         }
 410                         ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j]);
 411                         if (ret != LDB_SUCCESS) {
 412                                 return ret;
 413                         }
 414                 }
 415         }
 416 
 417         /* if DNs were set continue */
 418         if (ac->ops == NULL) {
 419                 talloc_free(ac);
 420                 return ldb_next_request(module, req);
 421         }
 422 
 423         /* start with the searches */
 424         return ldb_next_request(module, ac->ops->search_req);
 425 }
 426 
 427 _PUBLIC_ const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
 428         .name              = "extended_dn_store",
 429         .add               = extended_dn_add,
 430         .modify            = extended_dn_modify,
 431 };

/* [<][>][^][v][top][bottom][index][help] */