lib/ldb Add checks for overflow during ldb pack and parse
[obnox/samba/samba-obnox.git] / lib / ldb / common / ldb_pack.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /*
25  *  Name: ldb
26  *
27  *  Component: ldb pack/unpack
28  *
29  *  Description: pack/unpack routines for ldb messages as key/value blobs
30  *
31  *  Author: Andrew Tridgell
32  */
33
34 #include "ldb_private.h"
35
36 /* change this if the data format ever changes */
37 #define LDB_PACKING_FORMAT 0x26011967
38
39 /* old packing formats */
40 #define LDB_PACKING_FORMAT_NODN 0x26011966
41
42 /* use a portable integer format */
43 static void put_uint32(uint8_t *p, int ofs, unsigned int val)
44 {
45         p += ofs;
46         p[0] = val&0xFF;
47         p[1] = (val>>8)  & 0xFF;
48         p[2] = (val>>16) & 0xFF;
49         p[3] = (val>>24) & 0xFF;
50 }
51
52 static unsigned int pull_uint32(uint8_t *p, int ofs)
53 {
54         p += ofs;
55         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
56 }
57
58 static int attribute_storable_values(const struct ldb_message_element *el)
59 {
60         if (el->num_values == 0) return 0;
61
62         if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
63
64         return el->num_values;
65 }
66
67 /*
68   pack a ldb message into a linear buffer in a ldb_val
69
70   note that this routine avoids saving elements with zero values,
71   as these are equivalent to having no element
72
73   caller frees the data buffer after use
74 */
75 int ldb_pack_data(struct ldb_context *ldb,
76                   const struct ldb_message *message,
77                   struct ldb_val *data)
78 {
79         unsigned int i, j, real_elements=0;
80         size_t size, dn_len, attr_len, value_len;
81         const char *dn;
82         uint8_t *p;
83         size_t len;
84
85         dn = ldb_dn_get_linearized(message->dn);
86         if (dn == NULL) {
87                 errno = ENOMEM;
88                 return -1;
89         }
90
91         /* work out how big it needs to be */
92         size = 8;
93
94         size += 1;
95
96         dn_len = strlen(dn);
97         if (size + dn_len < size) {
98                 errno = ENOMEM;
99                 return -1;
100         }
101         size += dn_len;
102
103         /*
104          * First calcuate the buffer size we need, and check for
105          * overflows
106          */
107         for (i=0;i<message->num_elements;i++) {
108                 if (attribute_storable_values(&message->elements[i]) == 0) {
109                         continue;
110                 }
111
112                 real_elements++;
113
114                 if (size + 5 < size) {
115                         errno = ENOMEM;
116                         return -1;
117                 }
118                 size += 5;
119
120                 attr_len = strlen(message->elements[i].name);
121                 if (size + attr_len < size) {
122                         errno = ENOMEM;
123                         return -1;
124                 }
125                 size += attr_len;
126
127                 for (j=0;j<message->elements[i].num_values;j++) {
128                         if (size + 5 < size) {
129                                 errno = ENOMEM;
130                                 return -1;
131                         }
132                         size += 5;
133
134                         value_len = message->elements[i].values[j].length;
135                         if (size + value_len < size) {
136                                 errno = ENOMEM;
137                                 return -1;
138                         }
139                         size += value_len;
140                 }
141         }
142
143         /* allocate it */
144         data->data = talloc_array(ldb, uint8_t, size);
145         if (!data->data) {
146                 errno = ENOMEM;
147                 return -1;
148         }
149         data->length = size;
150
151         p = data->data;
152         put_uint32(p, 0, LDB_PACKING_FORMAT);
153         put_uint32(p, 4, real_elements);
154         p += 8;
155
156         /* the dn needs to be packed so we can be case preserving
157            while hashing on a case folded dn */
158         len = dn_len;
159         memcpy(p, dn, len+1);
160         p += len + 1;
161
162         for (i=0;i<message->num_elements;i++) {
163                 if (attribute_storable_values(&message->elements[i]) == 0) {
164                         continue;
165                 }
166                 len = strlen(message->elements[i].name);
167                 memcpy(p, message->elements[i].name, len+1);
168                 p += len + 1;
169                 put_uint32(p, 0, message->elements[i].num_values);
170                 p += 4;
171                 for (j=0;j<message->elements[i].num_values;j++) {
172                         put_uint32(p, 0, message->elements[i].values[j].length);
173                         memcpy(p+4, message->elements[i].values[j].data,
174                                message->elements[i].values[j].length);
175                         p[4+message->elements[i].values[j].length] = 0;
176                         p += 4 + message->elements[i].values[j].length + 1;
177                 }
178         }
179
180         return 0;
181 }
182
183 static bool ldb_consume_element_data(uint8_t **pp, size_t *premaining)
184 {
185         unsigned int remaining = *premaining;
186         uint8_t *p = *pp;
187         uint32_t num_values = pull_uint32(p, 0);
188         uint32_t len;
189         int j;
190
191         p += 4;
192         if (remaining < 4) {
193                 return false;
194         }
195         remaining -= 4;
196         for (j = 0; j < num_values; j++) {
197                 len = pull_uint32(p, 0);
198                 if (remaining < 5) {
199                         return false;
200                 }
201                 remaining -= 5;
202                 if (len > remaining) {
203                         return false;
204                 }
205                 remaining -= len;
206                 p += len + 4 + 1;
207         }
208
209         *premaining = remaining;
210         *pp = p;
211         return true;
212 }
213
214 /*
215  * Unpack a ldb message from a linear buffer in ldb_val
216  *
217  * Providing a list of attributes to this function allows selective unpacking.
218  * Giving a NULL list (or a list_size of 0) unpacks all the attributes.
219  *
220  * Free with ldb_unpack_data_free()
221  */
222 int ldb_unpack_data_only_attr_list(struct ldb_context *ldb,
223                                    const struct ldb_val *data,
224                                    struct ldb_message *message,
225                                    const char * const *list,
226                                    unsigned int list_size,
227                                    unsigned int *nb_elements_in_db)
228 {
229         uint8_t *p;
230         size_t remaining;
231         size_t dn_len;
232         unsigned int i, j;
233         unsigned format;
234         unsigned int nelem = 0;
235         size_t len;
236         unsigned int found = 0;
237
238         if (list == NULL) {
239                 list_size = 0;
240         }
241
242         message->elements = NULL;
243
244         p = data->data;
245         if (data->length < 8) {
246                 errno = EIO;
247                 goto failed;
248         }
249
250         format = pull_uint32(p, 0);
251         message->num_elements = pull_uint32(p, 4);
252         p += 8;
253         if (nb_elements_in_db) {
254                 *nb_elements_in_db = message->num_elements;
255         }
256
257         remaining = data->length - 8;
258
259         switch (format) {
260         case LDB_PACKING_FORMAT_NODN:
261                 message->dn = NULL;
262                 break;
263
264         case LDB_PACKING_FORMAT:
265                 /*
266                  * With this check, we know that the DN at p is \0
267                  * terminated.
268                  */
269                 dn_len = strnlen((char *)p, remaining);
270                 if (dn_len == remaining) {
271                         errno = EIO;
272                         goto failed;
273                 }
274                 message->dn = ldb_dn_new(message, ldb, (char *)p);
275                 if (message->dn == NULL) {
276                         errno = ENOMEM;
277                         goto failed;
278                 }
279                 /*
280                  * Redundant: by definition, remaining must be more
281                  * than one less than dn_len, as otherwise it would be
282                  * == dn_len
283                  */
284                 if (remaining < dn_len + 1) {
285                         errno = EIO;
286                         goto failed;
287                 }
288                 remaining -= dn_len + 1;
289                 p += dn_len + 1;
290                 break;
291
292         default:
293                 errno = EIO;
294                 goto failed;
295         }
296
297         if (message->num_elements == 0) {
298                 return 0;
299         }
300
301         if (message->num_elements > remaining / 6) {
302                 errno = EIO;
303                 goto failed;
304         }
305
306         message->elements = talloc_zero_array(message, struct ldb_message_element,
307                                               message->num_elements);
308         if (!message->elements) {
309                 errno = ENOMEM;
310                 goto failed;
311         }
312
313         for (i=0;i<message->num_elements;i++) {
314                 const char *attr = NULL;
315                 size_t attr_len;
316                 struct ldb_message_element *element = NULL;
317
318                 if (remaining < 10) {
319                         errno = EIO;
320                         goto failed;
321                 }
322                 /*
323                  * With this check, we know that the attribute name at
324                  * p is \0 terminated.
325                  */
326                 attr_len = strnlen((char *)p, remaining-6);
327                 if (attr_len == remaining-6) {
328                         errno = EIO;
329                         goto failed;
330                 }
331                 if (attr_len == 0) {
332                         errno = EIO;
333                         goto failed;
334                 }
335                 attr = (char *)p;
336
337                 /*
338                  * The general idea is to reduce allocations by skipping over
339                  * attributes that we do not actually care about.
340                  *
341                  * This is a bit expensive but normally the list is pretty small
342                  * also the cost of freeing unused attributes is quite important
343                  * and can dwarf the cost of looping.
344                  */
345                 if (list_size != 0) {
346                         bool keep = false;
347                         int h;
348
349                         /*
350                          * We know that p has a \0 terminator before the
351                          * end of the buffer due to the check above.
352                          */
353                         for (h = 0; h < list_size && found < list_size; h++) {
354                                 if (ldb_attr_cmp(attr, list[h]) == 0) {
355                                         keep = true;
356                                         found++;
357                                         break;
358                                 }
359                         }
360
361                         if (!keep) {
362                                 if (remaining < (attr_len + 1)) {
363                                         errno = EIO;
364                                         goto failed;
365                                 }
366                                 remaining -= attr_len + 1;
367                                 p += attr_len + 1;
368                                 if (!ldb_consume_element_data(&p, &remaining)) {
369                                         errno = EIO;
370                                         goto failed;
371                                 }
372                                 continue;
373                         }
374                 }
375                 element = &message->elements[nelem];
376                 element->name = talloc_strndup(message->elements, (char *)p, attr_len);
377                 if (element->name == NULL) {
378                         errno = ENOMEM;
379                         goto failed;
380                 }
381                 element->flags = 0;
382
383                 if (remaining < (attr_len + 1)) {
384                         errno = EIO;
385                         goto failed;
386                 }
387                 remaining -= attr_len + 1;
388                 p += attr_len + 1;
389                 element->num_values = pull_uint32(p, 0);
390                 element->values = NULL;
391                 if (element->num_values != 0) {
392                         element->values = talloc_array(message->elements,
393                                                        struct ldb_val,
394                                                        element->num_values);
395                         if (!element->values) {
396                                 errno = ENOMEM;
397                                 goto failed;
398                         }
399                 }
400                 p += 4;
401                 if (remaining < 4) {
402                         errno = EIO;
403                         goto failed;
404                 }
405                 remaining -= 4;
406                 for (j = 0; j < element->num_values; j++) {
407                         if (remaining < 5) {
408                                 errno = EIO;
409                                 goto failed;
410                         }
411                         remaining -= 5;
412
413                         len = pull_uint32(p, 0);
414                         if (remaining < len) {
415                                 errno = EIO;
416                                 goto failed;
417                         }
418                         if (len + 1 < len) {
419                                 errno = EIO;
420                                 goto failed;
421                         }
422
423                         element->values[j].length = len;
424                         element->values[j].data = talloc_size(element->values, len+1);
425                         if (element->values[j].data == NULL) {
426                                 errno = ENOMEM;
427                                 goto failed;
428                         }
429                         memcpy(element->values[j].data, p + 4,
430                                len);
431                         element->values[j].data[len] = 0;
432
433                         remaining -= len;
434                         p += len+4+1;
435                 }
436                 nelem++;
437         }
438         /*
439          * Adapt the number of elements to the real number of unpacked elements,
440          * it means that we overallocated elements array.
441          */
442         message->num_elements = nelem;
443
444         /*
445          * Shrink the allocated size.  On current talloc behaviour
446          * this will help if we skipped 32 or more attributes.
447          */
448         message->elements = talloc_realloc(message, message->elements,
449                                            struct ldb_message_element,
450                                            message->num_elements);
451
452         if (remaining != 0) {
453                 ldb_debug(ldb, LDB_DEBUG_ERROR,
454                           "Error: %zu bytes unread in ldb_unpack_data_only_attr_list",
455                           remaining);
456         }
457
458         return 0;
459
460 failed:
461         talloc_free(message->elements);
462         return -1;
463 }
464
465 int ldb_unpack_data(struct ldb_context *ldb,
466                     const struct ldb_val *data,
467                     struct ldb_message *message)
468 {
469         return ldb_unpack_data_only_attr_list(ldb, data, message, NULL, 0, NULL);
470 }