Introduce system MIT krb5 build with --with-system-mitkrb5 option.
[sfrench/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / objectguid.c
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 "dsdb/samdb/samdb.h"
35 #include "dsdb/samdb/ldb_modules/util.h"
36 #include "librpc/gen_ndr/ndr_misc.h"
37 #include "param/param.h"
38
39 /*
40   add a time element to a record
41 */
42 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
43 {
44         struct ldb_message_element *el;
45         char *s;
46         int ret;
47
48         if (ldb_msg_find_element(msg, attr) != NULL) {
49                 return LDB_SUCCESS;
50         }
51
52         s = ldb_timestring(msg, t);
53         if (s == NULL) {
54                 return LDB_ERR_OPERATIONS_ERROR;
55         }
56
57         ret = ldb_msg_add_string(msg, attr, s);
58         if (ret != LDB_SUCCESS) {
59                 return ret;
60         }
61
62         el = ldb_msg_find_element(msg, attr);
63         /* always set as replace. This works because on add ops, the flag
64            is ignored */
65         el->flags = LDB_FLAG_MOD_REPLACE;
66
67         return LDB_SUCCESS;
68 }
69
70 /*
71   add a uint64_t element to a record
72 */
73 static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
74                               const char *attr, uint64_t v)
75 {
76         struct ldb_message_element *el;
77         int ret;
78
79         if (ldb_msg_find_element(msg, attr) != NULL) {
80                 return LDB_SUCCESS;
81         }
82
83         ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
84         if (ret != LDB_SUCCESS) {
85                 return ret;
86         }
87
88         el = ldb_msg_find_element(msg, attr);
89         /* always set as replace. This works because on add ops, the flag
90            is ignored */
91         el->flags = LDB_FLAG_MOD_REPLACE;
92
93         return LDB_SUCCESS;
94 }
95
96 struct og_context {
97         struct ldb_module *module;
98         struct ldb_request *req;
99 };
100
101 /* add_record: add objectGUID and timestamp attributes */
102 static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
103 {
104         struct ldb_context *ldb;
105         struct ldb_request *down_req;
106         struct ldb_message *msg;
107         struct ldb_message_element *el;
108         struct GUID guid;
109         uint64_t seq_num;
110         int ret;
111         time_t t = time(NULL);
112         struct og_context *ac;
113
114         ldb = ldb_module_get_ctx(module);
115
116         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
117
118         /* do not manipulate our control entries */
119         if (ldb_dn_is_special(req->op.add.message->dn)) {
120                 return ldb_next_request(module, req);
121         }
122
123         el = ldb_msg_find_element(req->op.add.message, "objectGUID");
124         if (el != NULL) {
125                 ldb_set_errstring(ldb,
126                                   "objectguid: objectGUID must not be specified!");
127                 return LDB_ERR_UNWILLING_TO_PERFORM;
128         }
129
130         ac = talloc(req, struct og_context);
131         if (ac == NULL) {
132                 return ldb_oom(ldb);
133         }
134         ac->module = module;
135         ac->req = req;
136
137         /* we have to copy the message as the caller might have it as a const */
138         msg = ldb_msg_copy_shallow(ac, req->op.add.message);
139         if (msg == NULL) {
140                 talloc_free(ac);
141                 return ldb_operr(ldb);
142         }
143
144         /* a new GUID */
145         guid = GUID_random();
146
147         ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
148         if (ret != LDB_SUCCESS) {
149                 return ret;
150         }
151         
152         if (add_time_element(msg, "whenCreated", t) != LDB_SUCCESS ||
153             add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
154                 return ldb_operr(ldb);
155         }
156
157         /* Get a sequence number from the backend */
158         ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
159         if (ret == LDB_SUCCESS) {
160                 if (add_uint64_element(ldb, msg, "uSNCreated",
161                                        seq_num) != LDB_SUCCESS ||
162                     add_uint64_element(ldb, msg, "uSNChanged",
163                                        seq_num) != LDB_SUCCESS) {
164                         return ldb_operr(ldb);
165                 }
166         }
167
168         ret = ldb_build_add_req(&down_req, ldb, ac,
169                                 msg,
170                                 req->controls,
171                                 req, dsdb_next_callback,
172                                 req);
173         LDB_REQ_SET_LOCATION(down_req);
174         if (ret != LDB_SUCCESS) {
175                 return ret;
176         }
177
178         /* go on with the call chain */
179         return ldb_next_request(module, down_req);
180 }
181
182 /* modify_record: update timestamps */
183 static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
184 {
185         struct ldb_context *ldb;
186         struct ldb_request *down_req;
187         struct ldb_message *msg;
188         struct ldb_message_element *el;
189         int ret;
190         time_t t = time(NULL);
191         uint64_t seq_num;
192         struct og_context *ac;
193
194         ldb = ldb_module_get_ctx(module);
195
196         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_modify_record\n");
197
198         /* do not manipulate our control entries */
199         if (ldb_dn_is_special(req->op.add.message->dn)) {
200                 return ldb_next_request(module, req);
201         }
202
203         el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
204         if (el != NULL) {
205                 ldb_set_errstring(ldb,
206                                   "objectguid: objectGUID must not be specified!");
207                 return LDB_ERR_CONSTRAINT_VIOLATION;
208         }
209
210         ac = talloc(req, struct og_context);
211         if (ac == NULL) {
212                 return ldb_oom(ldb);
213         }
214         ac->module = module;
215         ac->req = req;
216
217         /* we have to copy the message as the caller might have it as a const */
218         msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
219         if (msg == NULL) {
220                 return ldb_operr(ldb);
221         }
222
223         if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
224                 return ldb_operr(ldb);
225         }
226
227         /* Get a sequence number from the backend */
228         ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
229         if (ret == LDB_SUCCESS) {
230                 if (add_uint64_element(ldb, msg, "uSNChanged",
231                                        seq_num) != LDB_SUCCESS) {
232                         return ldb_operr(ldb);
233                 }
234         }
235
236         ret = ldb_build_mod_req(&down_req, ldb, ac,
237                                 msg,
238                                 req->controls,
239                                 req, dsdb_next_callback,
240                                 req);
241         LDB_REQ_SET_LOCATION(down_req);
242         if (ret != LDB_SUCCESS) {
243                 return ret;
244         }
245
246         /* go on with the call chain */
247         return ldb_next_request(module, down_req);
248 }
249
250 static const struct ldb_module_ops ldb_objectguid_module_ops = {
251         .name          = "objectguid",
252         .add           = objectguid_add,
253         .modify        = objectguid_modify
254 };
255
256 int ldb_objectguid_module_init(const char *version)
257 {
258         LDB_MODULE_CHECK_VERSION(version);
259         return ldb_register_module(&ldb_objectguid_module_ops);
260 }