1/* 2 ldb database library 3 4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 5 Copyright (C) Andrew Tridgell 2005 6 Copyright (C) Simo Sorce 2004-2008 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 * Name: ldb 24 * 25 * Component: ldb objectguid module 26 * 27 * Description: add a unique objectGUID onto every new record 28 * 29 * Author: Simo Sorce 30 */ 31 32#include "includes.h" 33#include "ldb_module.h" 34#include "librpc/gen_ndr/ndr_misc.h" 35#include "param/param.h" 36 37static struct ldb_message_element *objectguid_find_attribute(const struct ldb_message *msg, const char *name) 38{ 39 int i; 40 41 for (i = 0; i < msg->num_elements; i++) { 42 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) { 43 return &msg->elements[i]; 44 } 45 } 46 47 return NULL; 48} 49 50/* 51 add a time element to a record 52*/ 53static int add_time_element(struct ldb_message *msg, const char *attr, time_t t) 54{ 55 struct ldb_message_element *el; 56 char *s; 57 58 if (ldb_msg_find_element(msg, attr) != NULL) { 59 return 0; 60 } 61 62 s = ldb_timestring(msg, t); 63 if (s == NULL) { 64 return -1; 65 } 66 67 if (ldb_msg_add_string(msg, attr, s) != 0) { 68 return -1; 69 } 70 71 el = ldb_msg_find_element(msg, attr); 72 /* always set as replace. This works because on add ops, the flag 73 is ignored */ 74 el->flags = LDB_FLAG_MOD_REPLACE; 75 76 return 0; 77} 78 79/* 80 add a uint64_t element to a record 81*/ 82static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v) 83{ 84 struct ldb_message_element *el; 85 86 if (ldb_msg_find_element(msg, attr) != NULL) { 87 return 0; 88 } 89 90 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) { 91 return -1; 92 } 93 94 el = ldb_msg_find_element(msg, attr); 95 /* always set as replace. This works because on add ops, the flag 96 is ignored */ 97 el->flags = LDB_FLAG_MOD_REPLACE; 98 99 return 0; 100} 101 102struct og_context { 103 struct ldb_module *module; 104 struct ldb_request *req; 105}; 106 107static int og_op_callback(struct ldb_request *req, struct ldb_reply *ares) 108{ 109 struct og_context *ac; 110 111 ac = talloc_get_type(req->context, struct og_context); 112 113 if (!ares) { 114 return ldb_module_done(ac->req, NULL, NULL, 115 LDB_ERR_OPERATIONS_ERROR); 116 } 117 if (ares->error != LDB_SUCCESS) { 118 return ldb_module_done(ac->req, ares->controls, 119 ares->response, ares->error); 120 } 121 122 if (ares->type != LDB_REPLY_DONE) { 123 talloc_free(ares); 124 return ldb_module_done(ac->req, NULL, NULL, 125 LDB_ERR_OPERATIONS_ERROR); 126 } 127 128 return ldb_module_done(ac->req, ares->controls, 129 ares->response, ares->error); 130} 131 132/* add_record: add objectGUID attribute */ 133static int objectguid_add(struct ldb_module *module, struct ldb_request *req) 134{ 135 struct ldb_context *ldb; 136 struct ldb_request *down_req; 137 struct ldb_message_element *attribute; 138 struct ldb_message *msg; 139 struct ldb_val v; 140 struct GUID guid; 141 uint64_t seq_num; 142 enum ndr_err_code ndr_err; 143 int ret; 144 time_t t = time(NULL); 145 struct og_context *ac; 146 147 ldb = ldb_module_get_ctx(module); 148 149 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n"); 150 151 /* do not manipulate our control entries */ 152 if (ldb_dn_is_special(req->op.add.message->dn)) { 153 return ldb_next_request(module, req); 154 } 155 156 if ((attribute = objectguid_find_attribute(req->op.add.message, "objectGUID")) != NULL ) { 157 return ldb_next_request(module, req); 158 } 159 160 ac = talloc(req, struct og_context); 161 if (ac == NULL) { 162 return LDB_ERR_OPERATIONS_ERROR; 163 } 164 ac->module = module; 165 ac->req = req; 166 167 /* we have to copy the message as the caller might have it as a const */ 168 msg = ldb_msg_copy_shallow(ac, req->op.add.message); 169 if (msg == NULL) { 170 talloc_free(down_req); 171 return LDB_ERR_OPERATIONS_ERROR; 172 } 173 174 /* a new GUID */ 175 guid = GUID_random(); 176 177 ndr_err = ndr_push_struct_blob(&v, msg, 178 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), 179 &guid, 180 (ndr_push_flags_fn_t)ndr_push_GUID); 181 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 182 return LDB_ERR_OPERATIONS_ERROR; 183 } 184 185 ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL); 186 if (ret) { 187 return ret; 188 } 189 190 if (add_time_element(msg, "whenCreated", t) != 0 || 191 add_time_element(msg, "whenChanged", t) != 0) { 192 return LDB_ERR_OPERATIONS_ERROR; 193 } 194 195 /* Get a sequence number from the backend */ 196 /* FIXME: ldb_sequence_number is a semi-async call, 197 * make sure this function is split and a callback is used */ 198 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num); 199 if (ret == LDB_SUCCESS) { 200 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 || 201 add_uint64_element(msg, "uSNChanged", seq_num) != 0) { 202 return LDB_ERR_OPERATIONS_ERROR; 203 } 204 } 205 206 ret = ldb_build_add_req(&down_req, ldb, ac, 207 msg, 208 req->controls, 209 ac, og_op_callback, 210 req); 211 if (ret != LDB_SUCCESS) { 212 return LDB_ERR_OPERATIONS_ERROR; 213 } 214 215 /* go on with the call chain */ 216 return ldb_next_request(module, down_req); 217} 218 219/* modify_record: update timestamps */ 220static int objectguid_modify(struct ldb_module *module, struct ldb_request *req) 221{ 222 struct ldb_context *ldb; 223 struct ldb_request *down_req; 224 struct ldb_message *msg; 225 int ret; 226 time_t t = time(NULL); 227 uint64_t seq_num; 228 struct og_context *ac; 229 230 ldb = ldb_module_get_ctx(module); 231 232 ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n"); 233 234 /* do not manipulate our control entries */ 235 if (ldb_dn_is_special(req->op.add.message->dn)) { 236 return ldb_next_request(module, req); 237 } 238 239 ac = talloc(req, struct og_context); 240 if (ac == NULL) { 241 return LDB_ERR_OPERATIONS_ERROR; 242 } 243 ac->module = module; 244 ac->req = req; 245 246 /* we have to copy the message as the caller might have it as a const */ 247 msg = ldb_msg_copy_shallow(ac, req->op.mod.message); 248 if (msg == NULL) { 249 return LDB_ERR_OPERATIONS_ERROR; 250 } 251 252 if (add_time_element(msg, "whenChanged", t) != 0) { 253 return LDB_ERR_OPERATIONS_ERROR; 254 } 255 256 /* Get a sequence number from the backend */ 257 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num); 258 if (ret == LDB_SUCCESS) { 259 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) { 260 return LDB_ERR_OPERATIONS_ERROR; 261 } 262 } 263 264 ret = ldb_build_mod_req(&down_req, ldb, ac, 265 msg, 266 req->controls, 267 ac, og_op_callback, 268 req); 269 if (ret != LDB_SUCCESS) { 270 return LDB_ERR_OPERATIONS_ERROR; 271 } 272 273 /* go on with the call chain */ 274 return ldb_next_request(module, down_req); 275} 276 277_PUBLIC_ const struct ldb_module_ops ldb_objectguid_module_ops = { 278 .name = "objectguid", 279 .add = objectguid_add, 280 .modify = objectguid_modify, 281}; 282