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