457440804bc320f33b39a5b711fcc32e784ea8c2
[samba.git] / source / dsdb / samdb / ldb_modules / objectguid.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2004-2006
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6    Copyright (C) Andrew Tridgell 2005
7
8      ** NOTE! The following LGPL license applies to the ldb
9      ** library. This does NOT imply that all of Samba is released
10      ** under the LGPL
11    
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 2 of the License, or (at your option) any later version.
16
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Lesser General Public License for more details.
21
22    You should have received a copy of the GNU Lesser General Public
23    License along with this library; if not, write to the Free Software
24    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 */
26
27 /*
28  *  Name: ldb
29  *
30  *  Component: ldb objectguid module
31  *
32  *  Description: add a unique objectGUID onto every new record
33  *
34  *  Author: Simo Sorce
35  */
36
37 #include "includes.h"
38 #include "ldb/include/ldb_includes.h"
39 #include "librpc/gen_ndr/ndr_misc.h"
40
41 static struct ldb_message_element *objectguid_find_attribute(const struct ldb_message *msg, const char *name)
42 {
43         int i;
44
45         for (i = 0; i < msg->num_elements; i++) {
46                 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
47                         return &msg->elements[i];
48                 }
49         }
50
51         return NULL;
52 }
53
54 /*
55   add a time element to a record
56 */
57 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
58 {
59         struct ldb_message_element *el;
60         char *s;
61
62         if (ldb_msg_find_element(msg, attr) != NULL) {
63                 return 0;
64         }
65
66         s = ldb_timestring(msg, t);
67         if (s == NULL) {
68                 return -1;
69         }
70
71         if (ldb_msg_add_string(msg, attr, s) != 0) {
72                 return -1;
73         }
74
75         el = ldb_msg_find_element(msg, attr);
76         /* always set as replace. This works because on add ops, the flag
77            is ignored */
78         el->flags = LDB_FLAG_MOD_REPLACE;
79
80         return 0;
81 }
82
83 /*
84   add a uint64_t element to a record
85 */
86 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
87 {
88         struct ldb_message_element *el;
89
90         if (ldb_msg_find_element(msg, attr) != NULL) {
91                 return 0;
92         }
93
94         if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
95                 return -1;
96         }
97
98         el = ldb_msg_find_element(msg, attr);
99         /* always set as replace. This works because on add ops, the flag
100            is ignored */
101         el->flags = LDB_FLAG_MOD_REPLACE;
102
103         return 0;
104 }
105
106 /* add_record: add objectGUID attribute */
107 static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
108 {
109         struct ldb_request *down_req;
110         struct ldb_message_element *attribute;
111         struct ldb_message *msg;
112         struct ldb_val v;
113         struct GUID guid;
114         uint64_t seq_num;
115         NTSTATUS nt_status;
116         int ret;
117         time_t t = time(NULL);
118
119         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
120
121         /* do not manipulate our control entries */
122         if (ldb_dn_is_special(req->op.add.message->dn)) {
123                 return ldb_next_request(module, req);
124         }
125
126         if ((attribute = objectguid_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
127                 return ldb_next_request(module, req);
128         }
129
130         down_req = talloc(req, struct ldb_request);
131         if (down_req == NULL) {
132                 return LDB_ERR_OPERATIONS_ERROR;
133         }
134
135         *down_req = *req;
136
137         /* we have to copy the message as the caller might have it as a const */
138         down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
139         if (msg == NULL) {
140                 talloc_free(down_req);
141                 return LDB_ERR_OPERATIONS_ERROR;
142         }
143
144         /* a new GUID */
145         guid = GUID_random();
146
147         nt_status = ndr_push_struct_blob(&v, msg, &guid, 
148                                          (ndr_push_flags_fn_t)ndr_push_GUID);
149         if (!NT_STATUS_IS_OK(nt_status)) {
150                 talloc_free(down_req);
151                 return LDB_ERR_OPERATIONS_ERROR;
152         }
153
154         ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
155         if (ret) {
156                 talloc_free(down_req);
157                 return ret;
158         }
159         
160         if (add_time_element(msg, "whenCreated", t) != 0 ||
161             add_time_element(msg, "whenChanged", t) != 0) {
162                 talloc_free(down_req);
163                 return LDB_ERR_OPERATIONS_ERROR;
164         }
165
166         /* Get a sequence number from the backend */
167         ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
168         if (ret == LDB_SUCCESS) {
169                 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
170                     add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
171                         talloc_free(down_req);
172                         return LDB_ERR_OPERATIONS_ERROR;
173                 }
174         }
175
176         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
177
178         /* go on with the call chain */
179         ret = ldb_next_request(module, down_req);
180
181         /* do not free down_req as the call results may be linked to it,
182          * it will be freed when the upper level request get freed */
183         if (ret == LDB_SUCCESS) {
184                 req->handle = down_req->handle;
185         }
186
187         return ret;
188 }
189
190 /* modify_record: update timestamps */
191 static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
192 {
193         struct ldb_request *down_req;
194         struct ldb_message *msg;
195         int ret;
196         time_t t = time(NULL);
197         uint64_t seq_num;
198
199         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
200
201         /* do not manipulate our control entries */
202         if (ldb_dn_is_special(req->op.add.message->dn)) {
203                 return ldb_next_request(module, req);
204         }
205
206         down_req = talloc(req, struct ldb_request);
207         if (down_req == NULL) {
208                 return LDB_ERR_OPERATIONS_ERROR;
209         }
210
211         *down_req = *req;
212
213         /* we have to copy the message as the caller might have it as a const */
214         down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
215         if (msg == NULL) {
216                 talloc_free(down_req);
217                 return LDB_ERR_OPERATIONS_ERROR;
218         }
219
220         if (add_time_element(msg, "whenChanged", t) != 0) {
221                 talloc_free(down_req);
222                 return LDB_ERR_OPERATIONS_ERROR;
223         }
224
225         /* Get a sequence number from the backend */
226         ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
227         if (ret == LDB_SUCCESS) {
228                 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
229                         talloc_free(down_req);
230                         return LDB_ERR_OPERATIONS_ERROR;
231                 }
232         }
233
234         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
235
236         /* go on with the call chain */
237         ret = ldb_next_request(module, down_req);
238
239         /* do not free down_req as the call results may be linked to it,
240          * it will be freed when the upper level request get freed */
241         if (ret == LDB_SUCCESS) {
242                 req->handle = down_req->handle;
243         }
244
245         return ret;
246 }
247
248 static const struct ldb_module_ops objectguid_ops = {
249         .name          = "objectguid",
250         .add           = objectguid_add,
251         .modify        = objectguid_modify,
252 };
253
254
255 int objectguid_module_init(void)
256 {
257         return ldb_register_module(&objectguid_ops);
258 }