s4-prefixmap: Use WERR_NOT_FOUND when OID is not found in current prefixMap
[kamenim/samba.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  * Reference: [MS-DRSR] section 5.12.2
263  */
264 WERROR dsdb_schema_pfm_make_attid(struct dsdb_schema_prefixmap *pfm, const char *oid, uint32_t *attid)
265 {
266         WERROR werr;
267         uint32_t idx;
268         uint32_t lo_word, hi_word;
269         uint32_t last_subid;
270         DATA_BLOB bin_oid;
271
272         if (!pfm) {
273                 return WERR_INVALID_PARAMETER;
274         }
275         if (!oid) {
276                 return WERR_INVALID_PARAMETER;
277         }
278
279         werr = _dsdb_pfm_make_binary_oid(oid, pfm, &bin_oid, &last_subid);
280         W_ERROR_NOT_OK_RETURN(werr);
281
282         /* search the prefix in the prefix table, if none found, add
283          * one entry for new prefix.
284          */
285         werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, &idx);
286         if (W_ERROR_IS_OK(werr)) {
287                 /* free memory allocated for bin_oid */
288                 data_blob_free(&bin_oid);
289         } else {
290                 /* entry does not exists, add it */
291                 werr = _dsdb_schema_pfm_add_entry(pfm, bin_oid, &idx);
292                 W_ERROR_NOT_OK_RETURN(werr);
293         }
294
295         /* compose the attid */
296         lo_word = last_subid % 16384;   /* actually get lower 14 bits: lo_word & 0x3FFF */
297         if (last_subid >= 16384) {
298                 /* mark it so that it is known to not be the whole lastValue
299                  * This will raise 16-th bit*/
300                 lo_word += 32768;
301         }
302         hi_word = pfm->prefixes[idx].id;
303
304         /* make ATTID:
305          * HIWORD is prefixMap id
306          * LOWORD is truncated binary-oid */
307         *attid = (hi_word * 65536) + lo_word;
308
309         return WERR_OK;
310 }
311
312
313 /**
314  * Make OID for given ATTID.
315  * Reference: [MS-DRSR] section 5.12.2
316  */
317 WERROR dsdb_schema_pfm_oid_from_attid(struct dsdb_schema_prefixmap *pfm, uint32_t attid,
318                                       TALLOC_CTX *mem_ctx, const char **_oid)
319 {
320         uint32_t i;
321         uint32_t hi_word, lo_word;
322         DATA_BLOB bin_oid = {NULL, 0};
323         struct dsdb_schema_prefixmap_oid *pfm_entry;
324         WERROR werr = WERR_OK;
325
326         /* sanity check for attid requested */
327         if (dsdb_pfm_get_attid_type(attid) != dsdb_attid_type_pfm) {
328                 return WERR_INVALID_PARAMETER;
329         }
330
331         /* crack attid value */
332         hi_word = attid >> 16;
333         lo_word = attid & 0xFFFF;
334
335         /* locate corRespoNding prefixMap entry */
336         pfm_entry = NULL;
337         for (i = 0; i < pfm->length; i++) {
338                 if (hi_word == pfm->prefixes[i].id) {
339                         pfm_entry = &pfm->prefixes[i];
340                         break;
341                 }
342         }
343
344         if (!pfm_entry) {
345                 DEBUG(1,("Failed to find prefixMap entry for ATTID = 0x%08X (%d)\n",
346                          attid, attid));
347                 return WERR_DS_NO_ATTRIBUTE_OR_VALUE;
348         }
349
350         /* copy oid prefix making enough room */
351         bin_oid.length = pfm_entry->bin_oid.length + 2;
352         bin_oid.data = talloc_array(mem_ctx, uint8_t, bin_oid.length);
353         W_ERROR_HAVE_NO_MEMORY(bin_oid.data);
354         memcpy(bin_oid.data, pfm_entry->bin_oid.data, pfm_entry->bin_oid.length);
355
356         if (lo_word < 128) {
357                 bin_oid.length = bin_oid.length - 1;
358                 bin_oid.data[bin_oid.length-1] = lo_word;
359         }
360         else {
361                 if (lo_word >= 32768) {
362                         lo_word -= 32768;
363                 }
364                 bin_oid.data[bin_oid.length-2] = (0x80 | ((lo_word>>7) & 0x7f));
365                 bin_oid.data[bin_oid.length-1] = lo_word & 0x7f;
366         }
367
368         if (!ber_read_OID_String(mem_ctx, bin_oid, _oid)) {
369                 DEBUG(0,("ber_read_OID_String() failed for %s\n",
370                          hex_encode_talloc(bin_oid.data, bin_oid.data, bin_oid.length)));
371                 werr = WERR_INTERNAL_ERROR;
372         }
373
374         /* free locally allocated memory */
375         talloc_free(bin_oid.data);
376
377         return werr;
378 }
379
380
381 /**
382  * Verifies drsuapi mappings.
383  */
384 static WERROR _dsdb_drsuapi_pfm_verify(const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr,
385                                        bool have_schema_info)
386 {
387         uint32_t i;
388         uint32_t num_mappings;
389         struct drsuapi_DsReplicaOIDMapping *mapping;
390
391         /* check input params */
392         if (!ctr) {
393                 return WERR_INVALID_PARAMETER;
394         }
395         if (!ctr->mappings) {
396                 return WERR_INVALID_PARAMETER;
397         }
398         num_mappings = ctr->num_mappings;
399
400         if (have_schema_info) {
401                 DATA_BLOB blob;
402
403                 if (ctr->num_mappings < 2) {
404                         return WERR_INVALID_PARAMETER;
405                 }
406
407                 /* check last entry for being special */
408                 mapping = &ctr->mappings[ctr->num_mappings - 1];
409                 if (mapping->id_prefix != 0) {
410                         return WERR_INVALID_PARAMETER;
411                 }
412
413                 /* verify schemaInfo blob is valid one */
414                 blob = data_blob_const(mapping->oid.binary_oid, mapping->oid.length);
415                 if (!dsdb_schema_info_blob_is_valid(&blob)) {
416                         return WERR_INVALID_PARAMETER;
417                 }
418
419                 /* get number of read mappings in the map */
420                 num_mappings--;
421         }
422
423         /* now, verify rest of entries for being at least not null */
424         for (i = 0; i < num_mappings; i++) {
425                 mapping = &ctr->mappings[i];
426                 if (!mapping->oid.length) {
427                         return WERR_INVALID_PARAMETER;
428                 }
429                 if (!mapping->oid.binary_oid) {
430                         return WERR_INVALID_PARAMETER;
431                 }
432                 /* check it is not the special entry */
433                 if (*mapping->oid.binary_oid == 0xFF) {
434                         return WERR_INVALID_PARAMETER;
435                 }
436         }
437
438         return WERR_OK;
439 }
440
441 /**
442  * Convert drsuapi_ prefix map to prefixMap internal presentation.
443  *
444  * \param ctr Pointer to drsuapi_DsReplicaOIDMapping_Ctr which represents drsuapi_ prefixMap
445  * \param have_schema_info if drsuapi_prefixMap have schem_info in it or not
446  * \param mem_ctx TALLOC_CTX to make allocations in
447  * \param _pfm Out pointer to hold newly created prefixMap
448  * \param _schema_info Out param to store schema_info to. If NULL, schema_info is not decoded
449  */
450 WERROR dsdb_schema_pfm_from_drsuapi_pfm(const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr,
451                                         bool have_schema_info,
452                                         TALLOC_CTX *mem_ctx,
453                                         struct dsdb_schema_prefixmap **_pfm,
454                                         const char **_schema_info)
455 {
456         WERROR werr;
457         uint32_t i;
458         DATA_BLOB blob;
459         uint32_t num_mappings;
460         struct dsdb_schema_prefixmap *pfm;
461
462         if (!_pfm) {
463                 return WERR_INVALID_PARAMETER;
464         }
465
466         /*
467          * error out if schema_info is requested
468          * but it is not in the drsuapi_prefixMap
469          */
470         if (_schema_info && !have_schema_info) {
471                 return WERR_INVALID_PARAMETER;
472         }
473
474         /* verify drsuapi_pefixMap */
475         werr =_dsdb_drsuapi_pfm_verify(ctr, have_schema_info);
476         W_ERROR_NOT_OK_RETURN(werr);
477
478         /* allocate mem for prefix map */
479         num_mappings = ctr->num_mappings;
480         if (have_schema_info) {
481                 num_mappings--;
482         }
483         pfm = _dsdb_schema_prefixmap_talloc(mem_ctx, num_mappings);
484         W_ERROR_HAVE_NO_MEMORY(pfm);
485
486         /* copy entries from drsuapi_prefixMap */
487         for (i = 0; i < pfm->length; i++) {
488                 blob = data_blob_talloc(pfm,
489                                         ctr->mappings[i].oid.binary_oid,
490                                         ctr->mappings[i].oid.length);
491                 if (!blob.data) {
492                         talloc_free(pfm);
493                         return WERR_NOMEM;
494                 }
495                 pfm->prefixes[i].id = ctr->mappings[i].id_prefix;
496                 pfm->prefixes[i].bin_oid = blob;
497         }
498
499         /* fetch schema_info if requested */
500         if (_schema_info) {
501                 /* by this time, i should have this value,
502                  *  but set it here for clarity */
503                 i = ctr->num_mappings - 1;
504
505                 *_schema_info = hex_encode_talloc(mem_ctx,
506                                                   ctr->mappings[i].oid.binary_oid,
507                                                   ctr->mappings[i].oid.length);
508                 if (!*_schema_info) {
509                         talloc_free(pfm);
510                         return WERR_NOMEM;
511                 }
512         }
513
514         /* schema_prefixMap created successfully */
515         *_pfm = pfm;
516
517         return WERR_OK;
518 }
519
520 /**
521  * Convert drsuapi_ prefix map to prefixMap internal presentation.
522  *
523  * \param pfm Schema prefixMap to be converted
524  * \param schema_info schema_info string - if NULL, we don't need it
525  * \param mem_ctx TALLOC_CTX to make allocations in
526  * \param _ctr Out pointer to drsuapi_DsReplicaOIDMapping_Ctr prefix map structure
527  */
528 WERROR dsdb_drsuapi_pfm_from_schema_pfm(const struct dsdb_schema_prefixmap *pfm,
529                                         const char *schema_info,
530                                         TALLOC_CTX *mem_ctx,
531                                         struct drsuapi_DsReplicaOIDMapping_Ctr **_ctr)
532 {
533         uint32_t i;
534         DATA_BLOB blob;
535         struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
536
537         if (!_ctr) {
538                 return WERR_INVALID_PARAMETER;
539         }
540         if (!pfm) {
541                 return WERR_INVALID_PARAMETER;
542         }
543         if (pfm->length == 0) {
544                 return WERR_INVALID_PARAMETER;
545         }
546
547         /* allocate memory for the structure */
548         ctr = talloc_zero(mem_ctx, struct drsuapi_DsReplicaOIDMapping_Ctr);
549         W_ERROR_HAVE_NO_MEMORY(ctr);
550
551         ctr->num_mappings = (schema_info ? pfm->length + 1 : pfm->length);
552         ctr->mappings = talloc_array(ctr, struct drsuapi_DsReplicaOIDMapping, ctr->num_mappings);
553         if (!ctr->mappings) {
554                 talloc_free(ctr);
555                 return WERR_NOMEM;
556         }
557
558         /* copy entries from schema_prefixMap */
559         for (i = 0; i < pfm->length; i++) {
560                 blob = data_blob_dup_talloc(ctr, &pfm->prefixes[i].bin_oid);
561                 if (!blob.data) {
562                         talloc_free(ctr);
563                         return WERR_NOMEM;
564                 }
565                 ctr->mappings[i].id_prefix = pfm->prefixes[i].id;
566                 ctr->mappings[i].oid.length = blob.length;
567                 ctr->mappings[i].oid.binary_oid = blob.data;
568         }
569
570         /* make schema_info entry if needed */
571         if (schema_info) {
572                 /* by this time, i should have this value,
573                  *  but set it here for clarity */
574                 i = ctr->num_mappings - 1;
575
576                 blob = strhex_to_data_blob(ctr, schema_info);
577                 if (!blob.data) {
578                         talloc_free(ctr);
579                         return WERR_NOMEM;
580                 }
581
582                 ctr->mappings[i].id_prefix = 0;
583                 ctr->mappings[i].oid.length = blob.length;
584                 ctr->mappings[i].oid.binary_oid = blob.data;
585         }
586
587         /* drsuapi_prefixMap constructed successfully */
588         *_ctr = ctr;
589
590         return WERR_OK;
591 }
592
593 /**
594  * Verifies schema prefixMap and drsuapi prefixMap are same.
595  * Note that we just need to verify pfm contains prefixes
596  * from ctr, not that those prefixes has same id_prefix.
597  */
598 WERROR dsdb_schema_pfm_contains_drsuapi_pfm(const struct dsdb_schema_prefixmap *pfm,
599                                             const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
600 {
601         WERROR werr;
602         uint32_t i;
603         uint32_t idx;
604         DATA_BLOB bin_oid;
605
606         /* verify drsuapi_pefixMap */
607         werr = _dsdb_drsuapi_pfm_verify(ctr, true);
608         W_ERROR_NOT_OK_RETURN(werr);
609
610         /* check pfm contains every entry from ctr, except the last one */
611         for (i = 0; i < ctr->num_mappings - 1; i++) {
612                 bin_oid.length = ctr->mappings[i].oid.length;
613                 bin_oid.data   = ctr->mappings[i].oid.binary_oid;
614
615                 werr = dsdb_schema_pfm_find_binary_oid(pfm, bin_oid, &idx);
616                 if (!W_ERROR_IS_OK(werr)) {
617                         return WERR_DS_DRA_SCHEMA_MISMATCH;
618                 }
619         }
620
621         return WERR_OK;
622 }