s4/dsdb: split writing of schemaInfo blob in two parts
[kamenim/samba.git] / source4 / dsdb / schema / schema_info_attr.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    SCHEMA::schemaInfo implementation
5
6    Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2010
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 #include "includes.h"
23 #include "dsdb/common/util.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "dsdb/samdb/ldb_modules/util.h"
26 #include "lib/ldb/include/ldb_module.h"
27 #include "librpc/gen_ndr/ndr_drsuapi.h"
28 #include "librpc/gen_ndr/ndr_drsblobs.h"
29 #include "param/param.h"
30
31
32 /**
33  * Parse schemaInfo structure from a data_blob
34  * (DATA_BLOB or ldb_val).
35  * Suitable for parsing blobs that comes from
36  * DRS interface of from LDB database
37  */
38 WERROR dsdb_schema_info_from_blob(const DATA_BLOB *blob,
39                                   TALLOC_CTX *mem_ctx, struct dsdb_schema_info **_schema_info)
40 {
41         TALLOC_CTX *temp_ctx;
42         enum ndr_err_code ndr_err;
43         struct dsdb_schema_info *schema_info;
44         struct schemaInfoBlob schema_info_blob;
45
46         if (!blob || !blob->data) {
47                 return WERR_INVALID_PARAMETER;
48         }
49
50         if (blob->length != 21) {
51                 return WERR_INVALID_PARAMETER;
52         }
53
54         /* schemaInfo blob should start with 0xFF */
55         if (blob->data[0] != 0xFF) {
56                 return WERR_INVALID_PARAMETER;
57         }
58
59         temp_ctx = talloc_new(mem_ctx);
60         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
61
62         ndr_err = ndr_pull_struct_blob_all(blob, temp_ctx,
63                                            lp_iconv_convenience(NULL), &schema_info_blob,
64                                            (ndr_pull_flags_fn_t)ndr_pull_schemaInfoBlob);
65         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
66                 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
67                 talloc_free(temp_ctx);
68                 return ntstatus_to_werror(nt_status);
69         }
70
71         /* revision must be not less than 1 */
72         if (schema_info_blob.revision < 1) {
73                 talloc_free(temp_ctx);
74                 return WERR_INVALID_PARAMETER;
75         }
76
77         schema_info = talloc(mem_ctx, struct dsdb_schema_info);
78         if (!schema_info) {
79                 talloc_free(temp_ctx);
80                 return WERR_NOMEM;
81         }
82         schema_info->revision      = schema_info_blob.revision;
83         schema_info->invocation_id = schema_info_blob.invocation_id;
84         *_schema_info = schema_info;
85
86         talloc_free(temp_ctx);
87         return WERR_OK;
88 }
89
90 /**
91  * Creates a blob from schemaInfo structure
92  * Suitable for packing schemaInfo into a blob
93  * which is to be used in DRS interface of LDB database
94  */
95 WERROR dsdb_blob_from_schema_info(const struct dsdb_schema_info *schema_info,
96                                   TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
97 {
98         enum ndr_err_code ndr_err;
99         struct schemaInfoBlob schema_info_blob;
100
101         if (schema_info->revision < 1) {
102                 return WERR_INVALID_PARAMETER;
103         }
104
105         schema_info_blob.marker         = 0xFF;
106         schema_info_blob.revision       = schema_info->revision;
107         schema_info_blob.invocation_id  = schema_info->invocation_id;
108
109         ndr_err = ndr_push_struct_blob(blob, mem_ctx,
110                                        lp_iconv_convenience(NULL), &schema_info_blob,
111                                        (ndr_push_flags_fn_t)ndr_push_schemaInfoBlob);
112         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
113                 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
114                 return ntstatus_to_werror(nt_status);
115         }
116
117         return WERR_OK;
118 }
119
120
121 /**
122  * Reads schema_info structure from schemaInfo
123  * attribute on SCHEMA partition
124  *
125  * @param dsdb_flags    DSDB_FLAG_... flag of 0
126  */
127 WERROR dsdb_module_schema_info_blob_read(struct ldb_module *ldb_module,
128                                          uint32_t dsdb_flags,
129                                          TALLOC_CTX *mem_ctx, DATA_BLOB *schema_info_blob)
130 {
131         int ldb_err;
132         const struct ldb_val *blob_val;
133         struct ldb_dn *schema_dn;
134         struct ldb_result *schema_res = NULL;
135         static const char *schema_attrs[] = {
136                 "schemaInfo",
137                 NULL
138         };
139
140         schema_dn = samdb_schema_dn(ldb_module_get_ctx(ldb_module));
141         if (!schema_dn) {
142                 DEBUG(0,("dsdb_module_schema_info_blob_read: no schema dn present!\n"));
143                 return WERR_INTERNAL_DB_CORRUPTION;
144         }
145
146         ldb_err = dsdb_module_search(ldb_module, mem_ctx, &schema_res, schema_dn,
147                                      LDB_SCOPE_BASE, schema_attrs, dsdb_flags, NULL);
148         if (ldb_err == LDB_ERR_NO_SUCH_OBJECT) {
149                 DEBUG(0,("dsdb_module_schema_info_blob_read: Schema DN not found!\n"));
150                 talloc_free(schema_res);
151                 return WERR_INTERNAL_DB_CORRUPTION;
152         } else if (ldb_err != LDB_SUCCESS) {
153                 DEBUG(0,("dsdb_module_schema_info_blob_read: failed to find schemaInfo attribute\n"));
154                 talloc_free(schema_res);
155                 return WERR_INTERNAL_DB_CORRUPTION;
156         }
157
158         blob_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "schemaInfo");
159         if (!blob_val) {
160                 DEBUG(0,("dsdb_module_schema_info_blob_read: no schemaInfo attribute found\n"));
161                 talloc_free(schema_res);
162                 return WERR_DS_NO_ATTRIBUTE_OR_VALUE;
163         }
164
165         /* transfer .data ownership to mem_ctx */
166         schema_info_blob->length = blob_val->length;
167         schema_info_blob->data = talloc_steal(mem_ctx, blob_val->data);
168
169         talloc_free(schema_res);
170
171         return WERR_OK;
172 }
173
174 /**
175  * Pepares ldb_msg to be used for updating schemaInfo value in DB
176  */
177 static WERROR _dsdb_schema_info_write_prepare(struct ldb_context *ldb,
178                                               DATA_BLOB *schema_info_blob,
179                                               TALLOC_CTX *mem_ctx,
180                                               struct ldb_message **_msg)
181 {
182         int ldb_err;
183         struct ldb_message *msg;
184         struct ldb_dn *schema_dn;
185         struct ldb_message_element *return_el;
186
187         schema_dn = samdb_schema_dn(ldb);
188         if (!schema_dn) {
189                 DEBUG(0,("_dsdb_schema_info_write_prepare: no schema dn present\n"));
190                 return WERR_INTERNAL_DB_CORRUPTION;
191         }
192
193         /* prepare ldb_msg to update schemaInfo */
194         msg = ldb_msg_new(mem_ctx);
195         W_ERROR_HAVE_NO_MEMORY(msg);
196
197         msg->dn = schema_dn;
198         ldb_err = ldb_msg_add_value(msg, "schemaInfo", schema_info_blob, &return_el);
199         if (ldb_err != 0) {
200                 DEBUG(0,("_dsdb_schema_info_write_prepare: ldb_msg_add_value failed - %s\n",
201                          ldb_strerror(ldb_err)));
202                 talloc_free(msg);
203                 return WERR_INTERNAL_ERROR;
204         }
205
206         /* mark schemaInfo element for replacement */
207         return_el->flags = LDB_FLAG_MOD_REPLACE;
208
209         *_msg = msg;
210
211         return WERR_OK;
212 }
213
214 /**
215  * Writes schema_info structure into schemaInfo
216  * attribute on SCHEMA partition
217  *
218  * @param dsdb_flags    DSDB_FLAG_... flag of 0
219  */
220 WERROR dsdb_module_schema_info_blob_write(struct ldb_module *ldb_module,
221                                           uint32_t dsdb_flags,
222                                           DATA_BLOB *schema_info_blob)
223 {
224         int ldb_err;
225         WERROR werr;
226         struct ldb_message *msg;
227         TALLOC_CTX *temp_ctx;
228
229         temp_ctx = talloc_new(ldb_module);
230         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
231
232         /* write serialized schemaInfo into LDB */
233         werr = _dsdb_schema_info_write_prepare(ldb_module_get_ctx(ldb_module),
234                                                schema_info_blob,
235                                                temp_ctx, &msg);
236         if (!W_ERROR_IS_OK(werr)) {
237                 talloc_free(temp_ctx);
238                 return werr;
239         }
240
241
242         ldb_err = dsdb_module_modify(ldb_module, msg, dsdb_flags | DSDB_MODIFY_PERMISSIVE);
243
244         talloc_free(temp_ctx);
245
246         if (ldb_err != 0) {
247                 DEBUG(0,("dsdb_module_schema_info_blob_write: dsdb_replace failed: %s (%s)\n",
248                          ldb_strerror(ldb_err),
249                          ldb_errstring(ldb_module_get_ctx(ldb_module))));
250                 return WERR_INTERNAL_DB_ERROR;
251         }
252
253         return WERR_OK;
254 }
255
256
257 /**
258  * Reads schema_info structure from schemaInfo
259  * attribute on SCHEMA partition
260  */
261 static WERROR dsdb_module_schema_info_read(struct ldb_module *ldb_module,
262                                            uint32_t dsdb_flags,
263                                            TALLOC_CTX *mem_ctx,
264                                            struct dsdb_schema_info **_schema_info)
265 {
266         WERROR werr;
267         DATA_BLOB ndr_blob;
268         TALLOC_CTX *temp_ctx;
269
270         temp_ctx = talloc_new(mem_ctx);
271         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
272
273         /* read serialized schemaInfo from LDB  */
274         werr = dsdb_module_schema_info_blob_read(ldb_module, dsdb_flags, temp_ctx, &ndr_blob);
275         if (!W_ERROR_IS_OK(werr)) {
276                 talloc_free(temp_ctx);
277                 return werr;
278         }
279
280         /* convert NDR blob to dsdb_schema_info object */
281         werr = dsdb_schema_info_from_blob(&ndr_blob,
282                                           mem_ctx,
283                                           _schema_info);
284         talloc_free(temp_ctx);
285
286         return werr;
287 }
288
289 /**
290  * Writes schema_info structure into schemaInfo
291  * attribute on SCHEMA partition
292  *
293  * @param dsdb_flags    DSDB_FLAG_... flag of 0
294  */
295 static WERROR dsdb_module_schema_info_write(struct ldb_module *ldb_module,
296                                             uint32_t dsdb_flags,
297                                             const struct dsdb_schema_info *schema_info)
298 {
299         WERROR werr;
300         DATA_BLOB ndr_blob;
301         TALLOC_CTX *temp_ctx;
302
303         temp_ctx = talloc_new(ldb_module);
304         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
305
306         /* convert schema_info to a blob */
307         werr = dsdb_blob_from_schema_info(schema_info, temp_ctx, &ndr_blob);
308         if (!W_ERROR_IS_OK(werr)) {
309                 talloc_free(temp_ctx);
310                 return werr;
311         }
312
313         /* write serialized schemaInfo into LDB */
314         werr = dsdb_module_schema_info_blob_write(ldb_module, dsdb_flags, &ndr_blob);
315
316         talloc_free(temp_ctx);
317
318         return werr;
319 }
320
321
322 /**
323  * Creates new dsdb_schema_info object using
324  * invocationId from supplied ldb
325  * @param check_invocation_id Error out if invocationId is not yet set
326  */
327 WERROR dsdb_schema_info_create(struct ldb_context *ldb, bool check_invocation_id,
328                                TALLOC_CTX *mem_ctx, struct dsdb_schema_info **_schema_info)
329 {
330         const struct GUID *invocation_id;
331         struct dsdb_schema_info *schema_info;
332
333         /* try to determine invocationId from ldb */
334         invocation_id = samdb_ntds_invocation_id(ldb);
335         if (check_invocation_id && !invocation_id) {
336                 return WERR_INTERNAL_DB_CORRUPTION;
337         }
338
339         schema_info = talloc(mem_ctx, struct dsdb_schema_info);
340         if (!schema_info) {
341                 return WERR_NOMEM;
342         }
343
344         schema_info->revision = 1;
345         if (invocation_id) {
346                 schema_info->invocation_id = *invocation_id;
347         } else {
348                 schema_info->invocation_id = GUID_zero();
349         }
350
351         *_schema_info = schema_info;
352
353         return WERR_OK;
354 }
355
356
357 /**
358  * Increments schemaInfo revision and save it to DB
359  * setting our invocationID in the process
360  * NOTE: this function should be called in a transaction
361  * much in the same way prefixMap update function is called
362  *
363  * @param ldb_module    current module
364  * @param schema        schema cache
365  * @param dsdb_flags    DSDB_FLAG_... flag of 0
366  */
367 WERROR dsdb_module_schema_info_update(struct ldb_module *ldb_module,
368                                       struct dsdb_schema *schema,
369                                       int dsdb_flags)
370 {
371         WERROR werr;
372         const struct GUID *invocation_id;
373         struct dsdb_schema_info *schema_info;
374
375         TALLOC_CTX *mem_ctx = talloc_new(schema);
376         W_ERROR_HAVE_NO_MEMORY(mem_ctx);
377
378         invocation_id = samdb_ntds_invocation_id(ldb_module_get_ctx(ldb_module));
379         if (!invocation_id) {
380                 return WERR_INTERNAL_DB_CORRUPTION;
381         }
382
383         werr = dsdb_module_schema_info_read(ldb_module, dsdb_flags,
384                                             mem_ctx, &schema_info);
385         if (!W_ERROR_IS_OK(werr)) {
386                 DEBUG(0,("dsdb_module_schema_info_update: failed to reload schemaInfo - %s\n",
387                          win_errstr(werr)));
388                 talloc_free(mem_ctx);
389                 return werr;
390         }
391
392         /* update schemaInfo */
393         schema_info->revision++;
394         schema_info->invocation_id = *invocation_id;
395
396         werr = dsdb_module_schema_info_write(ldb_module, dsdb_flags, schema_info);
397         if (!W_ERROR_IS_OK(werr)) {
398                 DEBUG(0,("dsdb_module_schema_info_update: failed to save schemaInfo - %s\n",
399                          win_errstr(werr)));
400                 talloc_free(mem_ctx);
401                 return werr;
402         }
403
404         /* finally, update schema_info in the cache */
405         /* TODO: update schema_info in dsdb_schema cache */
406 /*
407         talloc_unlink(schema, discard_const(schema->schema_info));
408         schema->schema_info = talloc_steal(schema, schema_info);
409 */
410
411         talloc_free(mem_ctx);
412         return WERR_OK;
413 }