1/* 2 ldb database library 3 4 Copyright (C) Andrew Tridgell 2005 5 Copyright (C) Simo Sorce 2006-2008 6 Copyright (C) Matthias Dieter Wallnöfer 2009 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22/* 23 handle operational attributes 24 */ 25 26/* 27 createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated 28 modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged 29 30 for the above two, we do the search as normal, and if 31 createTimestamp or modifyTimestamp is asked for, then do 32 additional searches for whenCreated and whenChanged and fill in 33 the resulting values 34 35 we also need to replace these with the whenCreated/whenChanged 36 equivalent in the search expression trees 37 38 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE 39 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE 40 41 on init we need to setup attribute handlers for these so 42 comparisons are done correctly. The resolution is 1 second. 43 44 on add we need to add both the above, for current time 45 46 on modify we need to change whenChanged 47 48 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass? 49 50 for this one we do the search as normal, then if requested ask 51 for objectclass, change the attribute name, and add it 52 53 primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE 54 55 contains the RID of a certain group object 56 57 58 attributeTypes: in schema only 59 objectClasses: in schema only 60 matchingRules: in schema only 61 matchingRuleUse: in schema only 62 creatorsName: not supported by w2k3? 63 modifiersName: not supported by w2k3? 64*/ 65 66#include "ldb_includes.h" 67#include "ldb_module.h" 68 69#include "includes.h" 70#include "dsdb/samdb/samdb.h" 71 72#ifndef ARRAY_SIZE 73#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 74#endif 75 76/* 77 construct a canonical name from a message 78*/ 79static int construct_canonical_name(struct ldb_module *module, 80 struct ldb_message *msg) 81{ 82 char *canonicalName; 83 canonicalName = ldb_dn_canonical_string(msg, msg->dn); 84 if (canonicalName == NULL) { 85 return -1; 86 } 87 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName); 88} 89 90/* 91 construct a primary group token for groups from a message 92*/ 93static int construct_primary_group_token(struct ldb_module *module, 94 struct ldb_message *msg) 95{ 96 struct ldb_context *ldb; 97 uint32_t primary_group_token; 98 99 ldb = ldb_module_get_ctx(module); 100 101 if (samdb_search_count(ldb, ldb, msg->dn, "(objectclass=group)") == 1) { 102 primary_group_token 103 = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0); 104 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken", 105 primary_group_token); 106 } else { 107 return LDB_SUCCESS; 108 } 109} 110 111 112/* 113 a list of attribute names that should be substituted in the parse 114 tree before the search is done 115*/ 116static const struct { 117 const char *attr; 118 const char *replace; 119} parse_tree_sub[] = { 120 { "createTimestamp", "whenCreated" }, 121 { "modifyTimestamp", "whenChanged" } 122}; 123 124 125/* 126 a list of attribute names that are hidden, but can be searched for 127 using another (non-hidden) name to produce the correct result 128*/ 129static const struct { 130 const char *attr; 131 const char *replace; 132 int (*constructor)(struct ldb_module *, struct ldb_message *); 133} search_sub[] = { 134 { "createTimestamp", "whenCreated", NULL }, 135 { "modifyTimestamp", "whenChanged", NULL }, 136 { "structuralObjectClass", "objectClass", NULL }, 137 { "canonicalName", "distinguishedName", construct_canonical_name }, 138 { "primaryGroupToken", "objectSid", construct_primary_group_token } 139}; 140 141/* 142 post process a search result record. For any search_sub[] attributes that were 143 asked for, we need to call the appropriate copy routine to copy the result 144 into the message, then remove any attributes that we added to the search but 145 were not asked for by the user 146*/ 147static int operational_search_post_process(struct ldb_module *module, 148 struct ldb_message *msg, 149 const char * const *attrs) 150{ 151 struct ldb_context *ldb; 152 int i, a=0; 153 154 ldb = ldb_module_get_ctx(module); 155 156 for (a=0;attrs && attrs[a];a++) { 157 for (i=0;i<ARRAY_SIZE(search_sub);i++) { 158 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) { 159 continue; 160 } 161 162 /* construct the new attribute, using either a supplied 163 constructor or a simple copy */ 164 if (search_sub[i].constructor) { 165 if (search_sub[i].constructor(module, msg) != 0) { 166 goto failed; 167 } 168 } else if (ldb_msg_copy_attr(msg, 169 search_sub[i].replace, 170 search_sub[i].attr) != 0) { 171 goto failed; 172 } 173 174 /* remove the added search attribute, unless it was 175 asked for by the user */ 176 if (search_sub[i].replace == NULL || 177 ldb_attr_in_list(attrs, search_sub[i].replace) || 178 ldb_attr_in_list(attrs, "*")) { 179 continue; 180 } 181 182 ldb_msg_remove_attr(msg, search_sub[i].replace); 183 } 184 } 185 186 return 0; 187 188failed: 189 ldb_debug_set(ldb, LDB_DEBUG_WARNING, 190 "operational_search_post_process failed for attribute '%s'", 191 attrs[a]); 192 return -1; 193} 194 195 196/* 197 hook search operations 198*/ 199 200struct operational_context { 201 struct ldb_module *module; 202 struct ldb_request *req; 203 204 const char * const *attrs; 205}; 206 207static int operational_callback(struct ldb_request *req, struct ldb_reply *ares) 208{ 209 struct operational_context *ac; 210 int ret; 211 212 ac = talloc_get_type(req->context, struct operational_context); 213 214 if (!ares) { 215 return ldb_module_done(ac->req, NULL, NULL, 216 LDB_ERR_OPERATIONS_ERROR); 217 } 218 if (ares->error != LDB_SUCCESS) { 219 return ldb_module_done(ac->req, ares->controls, 220 ares->response, ares->error); 221 } 222 223 switch (ares->type) { 224 case LDB_REPLY_ENTRY: 225 /* for each record returned post-process to add any derived 226 attributes that have been asked for */ 227 ret = operational_search_post_process(ac->module, 228 ares->message, 229 ac->attrs); 230 if (ret != 0) { 231 return ldb_module_done(ac->req, NULL, NULL, 232 LDB_ERR_OPERATIONS_ERROR); 233 } 234 return ldb_module_send_entry(ac->req, ares->message, ares->controls); 235 236 case LDB_REPLY_REFERRAL: 237 /* ignore referrals */ 238 break; 239 240 case LDB_REPLY_DONE: 241 242 return ldb_module_done(ac->req, ares->controls, 243 ares->response, LDB_SUCCESS); 244 } 245 246 talloc_free(ares); 247 return LDB_SUCCESS; 248} 249 250static int operational_search(struct ldb_module *module, struct ldb_request *req) 251{ 252 struct ldb_context *ldb; 253 struct operational_context *ac; 254 struct ldb_request *down_req; 255 const char **search_attrs = NULL; 256 int i, a; 257 int ret; 258 259 ldb = ldb_module_get_ctx(module); 260 261 ac = talloc(req, struct operational_context); 262 if (ac == NULL) { 263 return LDB_ERR_OPERATIONS_ERROR; 264 } 265 266 ac->module = module; 267 ac->req = req; 268 ac->attrs = req->op.search.attrs; 269 270 /* FIXME: We must copy the tree and keep the original 271 * unmodified. SSS */ 272 /* replace any attributes in the parse tree that are 273 searchable, but are stored using a different name in the 274 backend */ 275 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) { 276 ldb_parse_tree_attr_replace(req->op.search.tree, 277 parse_tree_sub[i].attr, 278 parse_tree_sub[i].replace); 279 } 280 281 /* in the list of attributes we are looking for, rename any 282 attributes to the alias for any hidden attributes that can 283 be fetched directly using non-hidden names */ 284 for (a=0;ac->attrs && ac->attrs[a];a++) { 285 for (i=0;i<ARRAY_SIZE(search_sub);i++) { 286 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 && 287 search_sub[i].replace) { 288 if (!search_attrs) { 289 search_attrs = ldb_attr_list_copy(req, ac->attrs); 290 if (search_attrs == NULL) { 291 return LDB_ERR_OPERATIONS_ERROR; 292 } 293 } 294 search_attrs[a] = search_sub[i].replace; 295 } 296 } 297 } 298 299 ret = ldb_build_search_req_ex(&down_req, ldb, ac, 300 req->op.search.base, 301 req->op.search.scope, 302 req->op.search.tree, 303 /* use new set of attrs if any */ 304 search_attrs == NULL?req->op.search.attrs:search_attrs, 305 req->controls, 306 ac, operational_callback, 307 req); 308 if (ret != LDB_SUCCESS) { 309 return LDB_ERR_OPERATIONS_ERROR; 310 } 311 312 /* perform the search */ 313 return ldb_next_request(module, down_req); 314} 315 316static int operational_init(struct ldb_module *ctx) 317{ 318 int ret = 0; 319 320 if (ret != 0) { 321 return ret; 322 } 323 324 return ldb_next_init(ctx); 325} 326 327const struct ldb_module_ops ldb_operational_module_ops = { 328 .name = "operational", 329 .search = operational_search, 330 .init_context = operational_init 331}; 332