0b5d50b529e3aaa14f9b2ccc0236083a4a2db63e
[abartlet/samba.git/.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  * Creates and initializes new dsdb_schema_info value.
34  * Initial schemaInfo values is with:
35  *   revision = 0
36  *   invocationId = GUID_ZERO
37  */
38 WERROR dsdb_schema_info_new(TALLOC_CTX *mem_ctx, struct dsdb_schema_info **_schema_info)
39 {
40         struct dsdb_schema_info *schema_info;
41
42         schema_info = talloc_zero(mem_ctx, struct dsdb_schema_info);
43         W_ERROR_HAVE_NO_MEMORY(schema_info);
44
45         *_schema_info = schema_info;
46
47         return WERR_OK;
48 }
49
50 /**
51  * Creates and initializes new dsdb_schema_info blob value.
52  * Initial schemaInfo values is with:
53  *   revision = 0
54  *   invocationId = GUID_ZERO
55  */
56 WERROR dsdb_schema_info_blob_new(TALLOC_CTX *mem_ctx, DATA_BLOB *_schema_info_blob)
57 {
58         DATA_BLOB blob;
59
60         blob = data_blob_talloc_zero(mem_ctx, 21);
61         W_ERROR_HAVE_NO_MEMORY(blob.data);
62
63         /* Set the schemaInfo marker to 0xFF */
64         blob.data[0] = 0xFF;
65
66         *_schema_info_blob = blob;
67
68         return WERR_OK;
69 }
70
71
72 /**
73  * Verify the 'blob' is a valid schemaInfo blob
74  */
75 bool dsdb_schema_info_blob_is_valid(const DATA_BLOB *blob)
76 {
77         if (!blob || !blob->data) {
78                 return false;
79         }
80
81         /* schemaInfo blob must be 21 bytes long */
82         if (blob->length != 21) {
83                 return false;
84         }
85
86         /* schemaInfo blob should start with 0xFF */
87         if (blob->data[0] != 0xFF) {
88                 return false;
89         }
90
91         return true;
92 }
93
94 /**
95  * Parse schemaInfo structure from a data_blob
96  * (DATA_BLOB or ldb_val).
97  * Suitable for parsing blobs that comes from
98  * DRS interface of from LDB database
99  */
100 WERROR dsdb_schema_info_from_blob(const DATA_BLOB *blob,
101                                   TALLOC_CTX *mem_ctx, struct dsdb_schema_info **_schema_info)
102 {
103         TALLOC_CTX *temp_ctx;
104         enum ndr_err_code ndr_err;
105         struct dsdb_schema_info *schema_info;
106         struct schemaInfoBlob schema_info_blob;
107
108         /* verify schemaInfo blob is valid */
109         if (!dsdb_schema_info_blob_is_valid(blob)) {
110                 return WERR_INVALID_PARAMETER;
111         }
112
113         temp_ctx = talloc_new(mem_ctx);
114         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
115
116         ndr_err = ndr_pull_struct_blob_all(blob, temp_ctx,
117                                            &schema_info_blob,
118                                            (ndr_pull_flags_fn_t)ndr_pull_schemaInfoBlob);
119         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
120                 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
121                 talloc_free(temp_ctx);
122                 return ntstatus_to_werror(nt_status);
123         }
124
125         schema_info = talloc(mem_ctx, struct dsdb_schema_info);
126         if (!schema_info) {
127                 talloc_free(temp_ctx);
128                 return WERR_NOMEM;
129         }
130
131         /* note that we accept revision numbers of zero now - w2k8r2
132            sends a revision of zero on initial vampire */
133         schema_info->revision      = schema_info_blob.revision;
134         schema_info->invocation_id = schema_info_blob.invocation_id;
135         *_schema_info = schema_info;
136
137         talloc_free(temp_ctx);
138         return WERR_OK;
139 }
140
141 /**
142  * Creates a blob from schemaInfo structure
143  * Suitable for packing schemaInfo into a blob
144  * which is to be used in DRS interface of LDB database
145  */
146 WERROR dsdb_blob_from_schema_info(const struct dsdb_schema_info *schema_info,
147                                   TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
148 {
149         enum ndr_err_code ndr_err;
150         struct schemaInfoBlob schema_info_blob;
151
152         schema_info_blob.marker         = 0xFF;
153         schema_info_blob.revision       = schema_info->revision;
154         schema_info_blob.invocation_id  = schema_info->invocation_id;
155
156         ndr_err = ndr_push_struct_blob(blob, mem_ctx,
157                                        &schema_info_blob,
158                                        (ndr_push_flags_fn_t)ndr_push_schemaInfoBlob);
159         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
160                 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
161                 return ntstatus_to_werror(nt_status);
162         }
163
164         return WERR_OK;
165 }
166
167 /**
168  * Compares schemaInfo signatures in dsdb_schema and prefixMap.
169  * NOTE: At present function compares schemaInfo values
170  * as string without taking into account schemVersion field
171  *
172  * @return WERR_OK if schemaInfos are equal
173  *         WERR_DS_DRA_SCHEMA_MISMATCH if schemaInfos are different
174  */
175 WERROR dsdb_schema_info_cmp(const struct dsdb_schema *schema,
176                             const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
177 {
178         bool bres;
179         DATA_BLOB blob;
180         char *schema_info_str;
181         struct drsuapi_DsReplicaOIDMapping *mapping;
182
183         /* we should have at least schemaInfo element */
184         if (ctr->num_mappings < 1) {
185                 return WERR_INVALID_PARAMETER;
186         }
187
188         /* verify schemaInfo element is valid */
189         mapping = &ctr->mappings[ctr->num_mappings - 1];
190         if (mapping->id_prefix != 0) {
191                 return WERR_INVALID_PARAMETER;
192         }
193
194         blob = data_blob_const(mapping->oid.binary_oid, mapping->oid.length);
195         if (!dsdb_schema_info_blob_is_valid(&blob)) {
196                 return WERR_INVALID_PARAMETER;
197         }
198
199         schema_info_str = hex_encode_talloc(NULL, blob.data, blob.length);
200         W_ERROR_HAVE_NO_MEMORY(schema_info_str);
201
202         bres = strequal(schema->schema_info, schema_info_str);
203         talloc_free(schema_info_str);
204
205         return bres ? WERR_OK : WERR_DS_DRA_SCHEMA_MISMATCH;
206 }
207
208
209 /**
210  * Reads schema_info structure from schemaInfo
211  * attribute on SCHEMA partition
212  *
213  * @param dsdb_flags    DSDB_FLAG_... flag of 0
214  */
215 WERROR dsdb_module_schema_info_blob_read(struct ldb_module *ldb_module,
216                                          uint32_t dsdb_flags,
217                                          TALLOC_CTX *mem_ctx, DATA_BLOB *schema_info_blob)
218 {
219         int ldb_err;
220         const struct ldb_val *blob_val;
221         struct ldb_dn *schema_dn;
222         struct ldb_result *schema_res = NULL;
223         static const char *schema_attrs[] = {
224                 "schemaInfo",
225                 NULL
226         };
227
228         schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ldb_module));
229         if (!schema_dn) {
230                 DEBUG(0,("dsdb_module_schema_info_blob_read: no schema dn present!\n"));
231                 return WERR_INTERNAL_DB_CORRUPTION;
232         }
233
234         ldb_err = dsdb_module_search(ldb_module, mem_ctx, &schema_res, schema_dn,
235                                      LDB_SCOPE_BASE, schema_attrs, dsdb_flags, NULL);
236         if (ldb_err == LDB_ERR_NO_SUCH_OBJECT) {
237                 DEBUG(0,("dsdb_module_schema_info_blob_read: Schema DN not found!\n"));
238                 talloc_free(schema_res);
239                 return WERR_INTERNAL_DB_CORRUPTION;
240         } else if (ldb_err != LDB_SUCCESS) {
241                 DEBUG(0,("dsdb_module_schema_info_blob_read: failed to find schemaInfo attribute\n"));
242                 talloc_free(schema_res);
243                 return WERR_INTERNAL_DB_CORRUPTION;
244         }
245
246         blob_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "schemaInfo");
247         if (!blob_val) {
248                 DEBUG(0,("dsdb_module_schema_info_blob_read: no schemaInfo attribute found\n"));
249                 talloc_free(schema_res);
250                 return WERR_DS_NO_ATTRIBUTE_OR_VALUE;
251         }
252
253         /* transfer .data ownership to mem_ctx */
254         schema_info_blob->length = blob_val->length;
255         schema_info_blob->data = talloc_steal(mem_ctx, blob_val->data);
256
257         talloc_free(schema_res);
258
259         return WERR_OK;
260 }
261
262 /**
263  * Prepares ldb_msg to be used for updating schemaInfo value in DB
264  */
265 static WERROR _dsdb_schema_info_write_prepare(struct ldb_context *ldb,
266                                               DATA_BLOB *schema_info_blob,
267                                               TALLOC_CTX *mem_ctx,
268                                               struct ldb_message **_msg)
269 {
270         int ldb_err;
271         struct ldb_message *msg;
272         struct ldb_dn *schema_dn;
273         struct ldb_message_element *return_el;
274
275         schema_dn = ldb_get_schema_basedn(ldb);
276         if (!schema_dn) {
277                 DEBUG(0,("_dsdb_schema_info_write_prepare: no schema dn present\n"));
278                 return WERR_INTERNAL_DB_CORRUPTION;
279         }
280
281         /* prepare ldb_msg to update schemaInfo */
282         msg = ldb_msg_new(mem_ctx);
283         W_ERROR_HAVE_NO_MEMORY(msg);
284
285         msg->dn = schema_dn;
286         ldb_err = ldb_msg_add_value(msg, "schemaInfo", schema_info_blob, &return_el);
287         if (ldb_err != 0) {
288                 DEBUG(0,("_dsdb_schema_info_write_prepare: ldb_msg_add_value failed - %s\n",
289                          ldb_strerror(ldb_err)));
290                 talloc_free(msg);
291                 return WERR_INTERNAL_ERROR;
292         }
293
294         /* mark schemaInfo element for replacement */
295         return_el->flags = LDB_FLAG_MOD_REPLACE;
296
297         *_msg = msg;
298
299         return WERR_OK;
300 }
301
302 /**
303  * Writes schema_info structure into schemaInfo
304  * attribute on SCHEMA partition
305  *
306  * @param dsdb_flags    DSDB_FLAG_... flag of 0
307  */
308 WERROR dsdb_module_schema_info_blob_write(struct ldb_module *ldb_module,
309                                           uint32_t dsdb_flags,
310                                           DATA_BLOB *schema_info_blob)
311 {
312         int ldb_err;
313         WERROR werr;
314         struct ldb_message *msg;
315         TALLOC_CTX *temp_ctx;
316
317         temp_ctx = talloc_new(ldb_module);
318         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
319
320         /* write serialized schemaInfo into LDB */
321         werr = _dsdb_schema_info_write_prepare(ldb_module_get_ctx(ldb_module),
322                                                schema_info_blob,
323                                                temp_ctx, &msg);
324         if (!W_ERROR_IS_OK(werr)) {
325                 talloc_free(temp_ctx);
326                 return werr;
327         }
328
329
330         ldb_err = dsdb_module_modify(ldb_module, msg, dsdb_flags);
331
332         talloc_free(temp_ctx);
333
334         if (ldb_err != 0) {
335                 DEBUG(0,("dsdb_module_schema_info_blob_write: dsdb_replace failed: %s (%s)\n",
336                          ldb_strerror(ldb_err),
337                          ldb_errstring(ldb_module_get_ctx(ldb_module))));
338                 return WERR_INTERNAL_DB_ERROR;
339         }
340
341         return WERR_OK;
342 }
343
344
345 /**
346  * Reads schema_info structure from schemaInfo
347  * attribute on SCHEMA partition
348  */
349 static WERROR dsdb_module_schema_info_read(struct ldb_module *ldb_module,
350                                            uint32_t dsdb_flags,
351                                            TALLOC_CTX *mem_ctx,
352                                            struct dsdb_schema_info **_schema_info)
353 {
354         WERROR werr;
355         DATA_BLOB ndr_blob;
356         TALLOC_CTX *temp_ctx;
357
358         temp_ctx = talloc_new(mem_ctx);
359         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
360
361         /* read serialized schemaInfo from LDB  */
362         werr = dsdb_module_schema_info_blob_read(ldb_module, dsdb_flags, temp_ctx, &ndr_blob);
363         if (!W_ERROR_IS_OK(werr)) {
364                 talloc_free(temp_ctx);
365                 return werr;
366         }
367
368         /* convert NDR blob to dsdb_schema_info object */
369         werr = dsdb_schema_info_from_blob(&ndr_blob,
370                                           mem_ctx,
371                                           _schema_info);
372         talloc_free(temp_ctx);
373
374         return werr;
375 }
376
377 /**
378  * Writes schema_info structure into schemaInfo
379  * attribute on SCHEMA partition
380  *
381  * @param dsdb_flags    DSDB_FLAG_... flag of 0
382  */
383 static WERROR dsdb_module_schema_info_write(struct ldb_module *ldb_module,
384                                             uint32_t dsdb_flags,
385                                             const struct dsdb_schema_info *schema_info)
386 {
387         WERROR werr;
388         DATA_BLOB ndr_blob;
389         TALLOC_CTX *temp_ctx;
390
391         temp_ctx = talloc_new(ldb_module);
392         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
393
394         /* convert schema_info to a blob */
395         werr = dsdb_blob_from_schema_info(schema_info, temp_ctx, &ndr_blob);
396         if (!W_ERROR_IS_OK(werr)) {
397                 talloc_free(temp_ctx);
398                 return werr;
399         }
400
401         /* write serialized schemaInfo into LDB */
402         werr = dsdb_module_schema_info_blob_write(ldb_module, dsdb_flags, &ndr_blob);
403
404         talloc_free(temp_ctx);
405
406         return werr;
407 }
408
409
410 /**
411  * Increments schemaInfo revision and save it to DB
412  * setting our invocationID in the process
413  * NOTE: this function should be called in a transaction
414  * much in the same way prefixMap update function is called
415  *
416  * @param ldb_module    current module
417  * @param schema        schema cache
418  * @param dsdb_flags    DSDB_FLAG_... flag of 0
419  */
420 WERROR dsdb_module_schema_info_update(struct ldb_module *ldb_module,
421                                       struct dsdb_schema *schema,
422                                       int dsdb_flags)
423 {
424         WERROR werr;
425         const struct GUID *invocation_id;
426         DATA_BLOB ndr_blob;
427         struct dsdb_schema_info *schema_info;
428         const char *schema_info_str;
429
430         TALLOC_CTX *temp_ctx = talloc_new(schema);
431         W_ERROR_HAVE_NO_MEMORY(temp_ctx);
432
433         invocation_id = samdb_ntds_invocation_id(ldb_module_get_ctx(ldb_module));
434         if (!invocation_id) {
435                 return WERR_INTERNAL_DB_CORRUPTION;
436         }
437
438         /* read serialized schemaInfo from LDB  */
439         werr = dsdb_module_schema_info_read(ldb_module, dsdb_flags, temp_ctx, &schema_info);
440         if (W_ERROR_EQUAL(werr, WERR_DS_NO_ATTRIBUTE_OR_VALUE)) {
441                 /* make default value in case
442                  * we have no schemaInfo value yet */
443                 werr = dsdb_schema_info_new(temp_ctx, &schema_info);
444         }
445         if (!W_ERROR_IS_OK(werr)) {
446                 DEBUG(0,("dsdb_module_schema_info_update: failed to reload schemaInfo - %s\n",
447                          win_errstr(werr)));
448                 talloc_free(temp_ctx);
449                 return werr;
450         }
451
452         /* update schemaInfo */
453         schema_info->revision++;
454         schema_info->invocation_id = *invocation_id;
455
456         werr = dsdb_module_schema_info_write(ldb_module, dsdb_flags, schema_info);
457         if (!W_ERROR_IS_OK(werr)) {
458                 DEBUG(0,("dsdb_module_schema_info_update: failed to save schemaInfo - %s\n",
459                          win_errstr(werr)));
460                 talloc_free(temp_ctx);
461                 return werr;
462         }
463
464         /* finally, update schema_info in the cache */
465         werr = dsdb_blob_from_schema_info(schema_info, temp_ctx, &ndr_blob);
466         W_ERROR_NOT_OK_RETURN(werr);
467
468         schema_info_str = hex_encode_talloc(schema, ndr_blob.data, ndr_blob.length);
469         W_ERROR_HAVE_NO_MEMORY(schema_info_str);
470
471         talloc_unlink(schema, discard_const(schema->schema_info));
472         schema->schema_info = schema_info_str;
473
474         talloc_free(temp_ctx);
475         return WERR_OK;
476 }