s4-prefixMap: split dsdb_schema_make_attid() function into read-only and
[sfrench/samba-autobuild/.git] / source4 / dsdb / schema / schema_prefixmap.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    DRS::prefixMap implementation
5
6    Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
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/samdb/samdb.h"
24 #include "librpc/gen_ndr/ndr_drsuapi.h"
25 #include "librpc/gen_ndr/ndr_drsblobs.h"
26 #include "../lib/util/asn1.h"
27
28
29 /**
30  * Determine range type for supplied ATTID
31  */
32 enum dsdb_attid_type dsdb_pfm_get_attid_type(uint32_t attid)
33 {
34         if (attid <= 0x7FFFFFFF) {
35                 return dsdb_attid_type_pfm;
36         }
37         else if (attid <= 0xBFFFFFFF) {
38                 return dsdb_attid_type_intid;
39         }
40         else if (attid <= 0xFFFEFFFF) {
41                 return dsdb_attid_type_reserved;
42         }
43         else {
44                 return dsdb_attid_type_internal;
45         }
46 }
47
48 /**
49  * Allocates schema_prefixMap object in supplied memory context
50  */
51 static struct dsdb_schema_prefixmap *_dsdb_schema_prefixmap_talloc(TALLOC_CTX *mem_ctx,
52                                                                    uint32_t length)
53 {
54         struct dsdb_schema_prefixmap *pfm;
55
56         pfm = talloc_zero(mem_ctx, struct dsdb_schema_prefixmap);
57         if (!pfm) {
58                 return NULL;
59         }
60
61         pfm->length = length;
62         pfm->prefixes = talloc_zero_array(pfm, struct dsdb_schema_prefixmap_oid,
63                                           pfm->length);
64         if (!pfm->prefixes) {
65                 talloc_free(pfm);
66                 return NULL;
67         }
68
69         return pfm;
70 }
71
72 /**
73  * Initial prefixMap creation according to:
74  * [MS-DRSR] section 5.12.2
75  */
76 WERROR dsdb_schema_pfm_new(TALLOC_CTX *mem_ctx, struct dsdb_schema_prefixmap **_pfm)
77 {
78         uint32_t i;
79         struct dsdb_schema_prefixmap *pfm;
80         const struct {
81                 uint32_t        id;
82                 const char      *oid_prefix;
83         } pfm_init_data[] = {
84                 {.id=0x00000000, .oid_prefix="2.5.4"},
85                 {.id=0x00000001, .oid_prefix="2.5.6"},
86                 {.id=0x00000002, .oid_prefix="1.2.840.113556.1.2"},
87                 {.id=0x00000003, .oid_prefix="1.2.840.113556.1.3"},
88                 {.id=0x00000004, .oid_prefix="2.16.840.1.101.2.2.1"},
89                 {.id=0x00000005, .oid_prefix="2.16.840.1.101.2.2.3"},
90                 {.id=0x00000006, .oid_prefix="2.16.840.1.101.2.1.5"},
91                 {.id=0x00000007, .oid_prefix="2.16.840.1.101.2.1.4"},
92                 {.id=0x00000008, .oid_prefix="2.5.5"},
93                 {.id=0x00000009, .oid_prefix="1.2.840.113556.1.4"},
94                 {.id=0x0000000A, .oid_prefix="1.2.840.113556.1.5"},
95                 {.id=0x00000013, .oid_prefix="0.9.2342.19200300.100"},
96                 {.id=0x00000014, .oid_prefix="2.16.840.1.113730.3"},
97                 {.id=0x00000015, .oid_prefix="0.9.2342.19200300.100.1"},
98                 {.id=0x00000016, .oid_prefix="2.16.840.1.113730.3.1"},
99                 {.id=0x00000017, .oid_prefix="1.2.840.113556.1.5.7000"},
100                 {.id=0x00000018, .oid_prefix="2.5.21"},
101                 {.id=0x00000019, .oid_prefix="2.5.18"},
102                 {.id=0x0000001A, .oid_prefix="2.5.20"},
103         };
104
105         /* allocate mem for prefix map */
106         pfm = _dsdb_schema_prefixmap_talloc(mem_ctx, ARRAY_SIZE(pfm_init_data));
107         W_ERROR_HAVE_NO_MEMORY(pfm);
108
109         /* build prefixes */
110         for (i = 0; i < pfm->length; i++) {
111                 if (!ber_write_partial_OID_String(pfm, &pfm->prefixes[i].bin_oid, pfm_init_data[i].oid_prefix)) {
112                         talloc_free(pfm);
113                         return WERR_INTERNAL_ERROR;
114                 }
115                 pfm->prefixes[i].id = pfm_init_data[i].id;
116         }
117
118         *_pfm = pfm;
119
120         return WERR_OK;
121 }
122
123
124 /**
125  * Adds oid to prefix map.
126  * On success returns ID for newly added index
127  * or ID of existing entry that matches oid
128  * Reference: [MS-DRSR] section 5.12.2
129  *
130  * \param pfm prefixMap
131  * \param bin_oid OID prefix to be added to prefixMap
132  * \param pfm_id Location where to store prefixMap entry ID
133  */
134 static WERROR _dsdb_schema_pfm_add_entry(struct dsdb_schema_prefixmap *pfm, DATA_BLOB bin_oid, uint32_t *_idx)
135 {
136         uint32_t i;
137         struct dsdb_schema_prefixmap_oid * pfm_entry;
138         struct dsdb_schema_prefixmap_oid * prefixes_new;
139
140         /* dup memory for bin-oid prefix to be added */
141         bin_oid = data_blob_dup_talloc(pfm, &bin_oid);
142         W_ERROR_HAVE_NO_MEMORY(bin_oid.data);
143
144         /* make room for new entry */
145         prefixes_new = talloc_realloc(pfm, pfm->prefixes, struct dsdb_schema_prefixmap_oid, pfm->length + 1);
146         if (!prefixes_new) {
147                 talloc_free(bin_oid.data);
148                 return WERR_NOMEM;
149         }
150         pfm->prefixes = prefixes_new;
151
152         /* make new unique ID in prefixMap */
153         pfm_entry = &pfm->prefixes[pfm->length];
154         pfm_entry->id = 0;
155         for (i = 0; i < pfm->length; i++) {
156                 if (pfm_entry->id < pfm->prefixes[i].id)
157                         pfm_entry->id = pfm->prefixes[i].id;
158         }
159
160         /* add new bin-oid prefix */
161         pfm_entry->id++;
162         pfm_entry->bin_oid = bin_oid;
163
164         *_idx = pfm->length;
165         pfm->length++;
166
167         return WERR_OK;
168 }
169
170
171 /**
172  * Make partial binary OID for supplied OID.
173  * Reference: [MS-DRSR] section 5.12.2
174  */
175 static WERROR _dsdb_pfm_make_binary_oid(const char *full_oid, TALLOC_CTX *mem_ctx,
176                                         DATA_BLOB *_bin_oid, uint32_t *_last_subid)
177 {
178         uint32_t last_subid;
179         const char *oid_subid;
180
181         /* make last sub-identifier value */
182         oid_subid = strrchr(full_oid, '.');
183         if (!oid_subid) {
184                 return WERR_INVALID_PARAMETER;
185         }
186         oid_subid++;
187         last_subid = strtoul(oid_subid, NULL, 10);
188
189         /* encode oid in BER format */
190         if (!ber_write_OID_String(mem_ctx, _bin_oid, full_oid)) {
191                 DEBUG(0,("ber_write_OID_String() failed for %s\n", full_oid));
192                 return WERR_INTERNAL_ERROR;
193         }
194
195         /* get the prefix of the OID */
196         if (last_subid < 128) {
197                 _bin_oid->length -= 1;
198         } else {
199                 _bin_oid->length -= 2;
200         }
201
202         /* return last_value if requested */
203         if (_last_subid) {
204                 *_last_subid = last_subid;
205         }
206
207         return WERR_OK;
208 }
209
210 /**
211  * Lookup partial-binary-oid in prefixMap
212  */
213 WERROR dsdb_schema_pfm_find_binary_oid(const struct dsdb_schema_prefixmap *pfm,
214                                        DATA_BLOB bin_oid,
215                                        uint32_t *_idx)
216 {
217         uint32_t i;
218
219         for (i = 0; i < pfm->length; i++) {
220                 if (pfm->prefixes[i].bin_oid.length != bin_oid.length) {
221                         continue;
222                 }
223
224                 if (memcmp(pfm->prefixes[i].bin_oid.data, bin_oid.data, bin_oid.length) == 0) {
225                         if (_idx) {
226                                 *_idx = i;
227                         }
228                         return WERR_OK;
229                 }
230         }
231
232         return WERR_NOT_FOUND;
233 }
234
235 /**
236  * Lookup full-oid in prefixMap
237  * Note: this may be slow.
238  */
239 WERROR dsdb_schema_pfm_find_oid(const struct dsdb_schema_prefixmap *pfm,
240                                 const char *full_oid,
241                                 uint32_t *_idx)
242 {
243         WERROR werr;
244         DATA_BLOB bin_oid;
245
246         ZERO_STRUCT(bin_oid);
247
248         /* make partial-binary-oid to look for */
249         werr = _dsdb_pfm_make_binary_oid(full_oid, NULL, &bin_oid, NULL);
250         W_ERROR_NOT_OK_RETURN(werr);
251
252         /* lookup the partial-oid */
253         werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, _idx);
254
255         data_blob_free(&bin_oid);
256
257         return werr;
258 }
259
260 /**
261  * Make ATTID for given OID
262  * If OID is not in prefixMap, new prefix
263  * may be added depending on 'can_change_pfm' flag
264  * Reference: [MS-DRSR] section 5.12.2
265  */
266 static WERROR dsdb_schema_pfm_make_attid_impl(struct dsdb_schema_prefixmap *pfm,
267                                               const char *oid,
268                                               bool can_change_pfm,
269                                               uint32_t *attid)
270 {
271         WERROR werr;
272         uint32_t idx;
273         uint32_t lo_word, hi_word;
274         uint32_t last_subid;
275         DATA_BLOB bin_oid;
276
277         if (!pfm) {
278                 return WERR_INVALID_PARAMETER;
279         }
280         if (!oid) {
281                 return WERR_INVALID_PARAMETER;
282         }
283
284         werr = _dsdb_pfm_make_binary_oid(oid, pfm, &bin_oid, &last_subid);
285         W_ERROR_NOT_OK_RETURN(werr);
286
287         /* search the prefix in the prefix table, if none found, add
288          * one entry for new prefix.
289          */
290         werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, &idx);
291         if (W_ERROR_IS_OK(werr)) {
292                 /* free memory allocated for bin_oid */
293                 data_blob_free(&bin_oid);
294         } else {
295                 /* return error in read-only mode */
296                 if (!can_change_pfm) {
297                         return werr;
298                 }
299
300                 /* entry does not exists, add it */
301                 werr = _dsdb_schema_pfm_add_entry(pfm, bin_oid, &idx);
302                 W_ERROR_NOT_OK_RETURN(werr);
303         }
304
305         /* compose the attid */
306         lo_word = last_subid % 16384;   /* actually get lower 14 bits: lo_word & 0x3FFF */
307         if (last_subid >= 16384) {
308                 /* mark it so that it is known to not be the whole lastValue
309                  * This will raise 16-th bit*/
310                 lo_word += 32768;
311         }
312         hi_word = pfm->prefixes[idx].id;
313
314         /* make ATTID:
315          * HIWORD is prefixMap id
316          * LOWORD is truncated binary-oid */
317         *attid = (hi_word * 65536) + lo_word;
318
319         return WERR_OK;
320 }
321
322 /**
323  * Make ATTID for given OID
324  * Reference: [MS-DRSR] section 5.12.2
325  *
326  * Note: This function may change prefixMap if prefix
327  * for supplied 'oid' doesn't exists yet.
328  * It is recommended to be used mostly when caller
329  * want to add new prefixes.
330  * Otherwise dsdb_schema_pfm_attid_from_oid() should be used.
331  */
332 WERROR dsdb_schema_pfm_make_attid(struct dsdb_schema_prefixmap *pfm,
333                                   const char *oid,
334                                   uint32_t *attid)
335 {
336         return dsdb_schema_pfm_make_attid_impl(pfm, oid, true, attid);
337 }
338
339 /**
340  * Make ATTID for given OID
341  * Reference: [MS-DRSR] section 5.12.2
342  */
343 WERROR dsdb_schema_pfm_attid_from_oid(struct dsdb_schema_prefixmap *pfm,
344                                       const char *oid,
345                                       uint32_t *attid)
346 {
347         return dsdb_schema_pfm_make_attid_impl(pfm, oid, false, attid);
348 }
349
350 /**
351  * Make OID for given ATTID.
352  * Reference: [MS-DRSR] section 5.12.2
353  */
354 WERROR dsdb_schema_pfm_oid_from_attid(struct dsdb_schema_prefixmap *pfm, uint32_t attid,
355                                       TALLOC_CTX *mem_ctx, const char **_oid)
356 {
357         uint32_t i;
358         uint32_t hi_word, lo_word;
359         DATA_BLOB bin_oid = {NULL, 0};
360         struct dsdb_schema_prefixmap_oid *pfm_entry;
361         WERROR werr = WERR_OK;
362
363         /* sanity check for attid requested */
364         if (dsdb_pfm_get_attid_type(attid) != dsdb_attid_type_pfm) {
365                 return WERR_INVALID_PARAMETER;
366         }
367
368         /* crack attid value */
369         hi_word = attid >> 16;
370         lo_word = attid & 0xFFFF;
371
372         /* locate corRespoNding prefixMap entry */
373         pfm_entry = NULL;
374         for (i = 0; i < pfm->length; i++) {
375                 if (hi_word == pfm->prefixes[i].id) {
376                         pfm_entry = &pfm->prefixes[i];
377                         break;
378                 }
379         }
380
381         if (!pfm_entry) {
382                 DEBUG(1,("Failed to find prefixMap entry for ATTID = 0x%08X (%d)\n",
383                          attid, attid));
384                 return WERR_DS_NO_ATTRIBUTE_OR_VALUE;
385         }
386
387         /* copy oid prefix making enough room */
388         bin_oid.length = pfm_entry->bin_oid.length + 2;
389         bin_oid.data = talloc_array(mem_ctx, uint8_t, bin_oid.length);
390         W_ERROR_HAVE_NO_MEMORY(bin_oid.data);
391         memcpy(bin_oid.data, pfm_entry->bin_oid.data, pfm_entry->bin_oid.length);
392
393         if (lo_word < 128) {
394                 bin_oid.length = bin_oid.length - 1;
395                 bin_oid.data[bin_oid.length-1] = lo_word;
396         }
397         else {
398                 if (lo_word >= 32768) {
399                         lo_word -= 32768;
400                 }
401                 bin_oid.data[bin_oid.length-2] = (0x80 | ((lo_word>>7) & 0x7f));
402                 bin_oid.data[bin_oid.length-1] = lo_word & 0x7f;
403         }
404
405         if (!ber_read_OID_String(mem_ctx, bin_oid, _oid)) {
406                 DEBUG(0,("ber_read_OID_String() failed for %s\n",
407                          hex_encode_talloc(bin_oid.data, bin_oid.data, bin_oid.length)));
408                 werr = WERR_INTERNAL_ERROR;
409         }
410
411         /* free locally allocated memory */
412         talloc_free(bin_oid.data);
413
414         return werr;
415 }
416
417
418 /**
419  * Verifies drsuapi mappings.
420  */
421 static WERROR _dsdb_drsuapi_pfm_verify(const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr,
422                                        bool have_schema_info)
423 {
424         uint32_t i;
425         uint32_t num_mappings;
426         struct drsuapi_DsReplicaOIDMapping *mapping;
427
428         /* check input params */
429         if (!ctr) {
430                 return WERR_INVALID_PARAMETER;
431         }
432         if (!ctr->mappings) {
433                 return WERR_INVALID_PARAMETER;
434         }
435         num_mappings = ctr->num_mappings;
436
437         if (have_schema_info) {
438                 DATA_BLOB blob;
439
440                 if (ctr->num_mappings < 2) {
441                         return WERR_INVALID_PARAMETER;
442                 }
443
444                 /* check last entry for being special */
445                 mapping = &ctr->mappings[ctr->num_mappings - 1];
446                 if (mapping->id_prefix != 0) {
447                         return WERR_INVALID_PARAMETER;
448                 }
449
450                 /* verify schemaInfo blob is valid one */
451                 blob = data_blob_const(mapping->oid.binary_oid, mapping->oid.length);
452                 if (!dsdb_schema_info_blob_is_valid(&blob)) {
453                         return WERR_INVALID_PARAMETER;
454                 }
455
456                 /* get number of read mappings in the map */
457                 num_mappings--;
458         }
459
460         /* now, verify rest of entries for being at least not null */
461         for (i = 0; i < num_mappings; i++) {
462                 mapping = &ctr->mappings[i];
463                 if (!mapping->oid.length) {
464                         return WERR_INVALID_PARAMETER;
465                 }
466                 if (!mapping->oid.binary_oid) {
467                         return WERR_INVALID_PARAMETER;
468                 }
469                 /* check it is not the special entry */
470                 if (*mapping->oid.binary_oid == 0xFF) {
471                         return WERR_INVALID_PARAMETER;
472                 }
473         }
474
475         return WERR_OK;
476 }
477
478 /**
479  * Convert drsuapi_ prefix map to prefixMap internal presentation.
480  *
481  * \param ctr Pointer to drsuapi_DsReplicaOIDMapping_Ctr which represents drsuapi_ prefixMap
482  * \param have_schema_info if drsuapi_prefixMap have schem_info in it or not
483  * \param mem_ctx TALLOC_CTX to make allocations in
484  * \param _pfm Out pointer to hold newly created prefixMap
485  * \param _schema_info Out param to store schema_info to. If NULL, schema_info is not decoded
486  */
487 WERROR dsdb_schema_pfm_from_drsuapi_pfm(const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr,
488                                         bool have_schema_info,
489                                         TALLOC_CTX *mem_ctx,
490                                         struct dsdb_schema_prefixmap **_pfm,
491                                         const char **_schema_info)
492 {
493         WERROR werr;
494         uint32_t i;
495         DATA_BLOB blob;
496         uint32_t num_mappings;
497         struct dsdb_schema_prefixmap *pfm;
498
499         if (!_pfm) {
500                 return WERR_INVALID_PARAMETER;
501         }
502
503         /*
504          * error out if schema_info is requested
505          * but it is not in the drsuapi_prefixMap
506          */
507         if (_schema_info && !have_schema_info) {
508                 return WERR_INVALID_PARAMETER;
509         }
510
511         /* verify drsuapi_pefixMap */
512         werr =_dsdb_drsuapi_pfm_verify(ctr, have_schema_info);
513         W_ERROR_NOT_OK_RETURN(werr);
514
515         /* allocate mem for prefix map */
516         num_mappings = ctr->num_mappings;
517         if (have_schema_info) {
518                 num_mappings--;
519         }
520         pfm = _dsdb_schema_prefixmap_talloc(mem_ctx, num_mappings);
521         W_ERROR_HAVE_NO_MEMORY(pfm);
522
523         /* copy entries from drsuapi_prefixMap */
524         for (i = 0; i < pfm->length; i++) {
525                 blob = data_blob_talloc(pfm,
526                                         ctr->mappings[i].oid.binary_oid,
527                                         ctr->mappings[i].oid.length);
528                 if (!blob.data) {
529                         talloc_free(pfm);
530                         return WERR_NOMEM;
531                 }
532                 pfm->prefixes[i].id = ctr->mappings[i].id_prefix;
533                 pfm->prefixes[i].bin_oid = blob;
534         }
535
536         /* fetch schema_info if requested */
537         if (_schema_info) {
538                 /* by this time, i should have this value,
539                  *  but set it here for clarity */
540                 i = ctr->num_mappings - 1;
541
542                 *_schema_info = hex_encode_talloc(mem_ctx,
543                                                   ctr->mappings[i].oid.binary_oid,
544                                                   ctr->mappings[i].oid.length);
545                 if (!*_schema_info) {
546                         talloc_free(pfm);
547                         return WERR_NOMEM;
548                 }
549         }
550
551         /* schema_prefixMap created successfully */
552         *_pfm = pfm;
553
554         return WERR_OK;
555 }
556
557 /**
558  * Convert drsuapi_ prefix map to prefixMap internal presentation.
559  *
560  * \param pfm Schema prefixMap to be converted
561  * \param schema_info schema_info string - if NULL, we don't need it
562  * \param mem_ctx TALLOC_CTX to make allocations in
563  * \param _ctr Out pointer to drsuapi_DsReplicaOIDMapping_Ctr prefix map structure
564  */
565 WERROR dsdb_drsuapi_pfm_from_schema_pfm(const struct dsdb_schema_prefixmap *pfm,
566                                         const char *schema_info,
567                                         TALLOC_CTX *mem_ctx,
568                                         struct drsuapi_DsReplicaOIDMapping_Ctr **_ctr)
569 {
570         uint32_t i;
571         DATA_BLOB blob;
572         struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
573
574         if (!_ctr) {
575                 return WERR_INVALID_PARAMETER;
576         }
577         if (!pfm) {
578                 return WERR_INVALID_PARAMETER;
579         }
580         if (pfm->length == 0) {
581                 return WERR_INVALID_PARAMETER;
582         }
583
584         /* allocate memory for the structure */
585         ctr = talloc_zero(mem_ctx, struct drsuapi_DsReplicaOIDMapping_Ctr);
586         W_ERROR_HAVE_NO_MEMORY(ctr);
587
588         ctr->num_mappings = (schema_info ? pfm->length + 1 : pfm->length);
589         ctr->mappings = talloc_array(ctr, struct drsuapi_DsReplicaOIDMapping, ctr->num_mappings);
590         if (!ctr->mappings) {
591                 talloc_free(ctr);
592                 return WERR_NOMEM;
593         }
594
595         /* copy entries from schema_prefixMap */
596         for (i = 0; i < pfm->length; i++) {
597                 blob = data_blob_dup_talloc(ctr, &pfm->prefixes[i].bin_oid);
598                 if (!blob.data) {
599                         talloc_free(ctr);
600                         return WERR_NOMEM;
601                 }
602                 ctr->mappings[i].id_prefix = pfm->prefixes[i].id;
603                 ctr->mappings[i].oid.length = blob.length;
604                 ctr->mappings[i].oid.binary_oid = blob.data;
605         }
606
607         /* make schema_info entry if needed */
608         if (schema_info) {
609                 /* by this time, i should have this value,
610                  *  but set it here for clarity */
611                 i = ctr->num_mappings - 1;
612
613                 blob = strhex_to_data_blob(ctr, schema_info);
614                 if (!blob.data) {
615                         talloc_free(ctr);
616                         return WERR_NOMEM;
617                 }
618
619                 ctr->mappings[i].id_prefix = 0;
620                 ctr->mappings[i].oid.length = blob.length;
621                 ctr->mappings[i].oid.binary_oid = blob.data;
622         }
623
624         /* drsuapi_prefixMap constructed successfully */
625         *_ctr = ctr;
626
627         return WERR_OK;
628 }
629
630 /**
631  * Verifies schema prefixMap and drsuapi prefixMap are same.
632  * Note that we just need to verify pfm contains prefixes
633  * from ctr, not that those prefixes has same id_prefix.
634  */
635 WERROR dsdb_schema_pfm_contains_drsuapi_pfm(const struct dsdb_schema_prefixmap *pfm,
636                                             const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
637 {
638         WERROR werr;
639         uint32_t i;
640         uint32_t idx;
641         DATA_BLOB bin_oid;
642
643         /* verify drsuapi_pefixMap */
644         werr = _dsdb_drsuapi_pfm_verify(ctr, true);
645         W_ERROR_NOT_OK_RETURN(werr);
646
647         /* check pfm contains every entry from ctr, except the last one */
648         for (i = 0; i < ctr->num_mappings - 1; i++) {
649                 bin_oid.length = ctr->mappings[i].oid.length;
650                 bin_oid.data   = ctr->mappings[i].oid.binary_oid;
651
652                 werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, &idx);
653                 if (!W_ERROR_IS_OK(werr)) {
654                         return WERR_DS_DRA_SCHEMA_MISMATCH;
655                 }
656         }
657
658         return WERR_OK;
659 }