1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00
samba-mirror/lib/ldb/common/ldb_pack.c
Joseph Sutton 6ef23456c3 ldb: Avoid undefined pointer arithmetic
Computing a pointer that points outside of an array, and not to one past
the last element, is undefined behaviour. To avoid this, do our
comparisons in terms of lengths, not pointers.

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andreas Schneider <asn@samba.org>
2023-04-12 13:52:31 +00:00

1363 lines
33 KiB
C

/*
ldb database library
Copyright (C) Andrew Tridgell 2004
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Name: ldb
*
* Component: ldb pack/unpack
*
* Description: pack/unpack routines for ldb messages as key/value blobs
*
* Author: Andrew Tridgell
*/
#include "ldb_private.h"
/*
* These macros are from byte_array.h via libssh
* TODO: This will be replaced with use of the byte_array.h header when it
* becomes available.
*
* Macros for handling integer types in byte arrays
*
* This file is originally from the libssh.org project
*
* Copyright (c) 2018 Andreas Schneider <asn@cryptomilk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _DATA_BYTE_CONST(data, pos) \
((uint8_t)(((const uint8_t *)(data))[(pos)]))
#define PULL_LE_U8(data, pos) \
(_DATA_BYTE_CONST(data, pos))
#define PULL_LE_U16(data, pos) \
((uint16_t)PULL_LE_U8(data, pos) |\
((uint16_t)(PULL_LE_U8(data, (pos) + 1))) << 8)
#define PULL_LE_U32(data, pos) \
((uint32_t)(PULL_LE_U16(data, pos) |\
((uint32_t)PULL_LE_U16(data, (pos) + 2)) << 16))
#define _DATA_BYTE(data, pos) \
(((uint8_t *)(data))[(pos)])
#define PUSH_LE_U8(data, pos, val) \
(_DATA_BYTE(data, pos) = ((uint8_t)(val)))
#define PUSH_LE_U16(data, pos, val) \
(PUSH_LE_U8((data), (pos), (uint8_t)((uint16_t)(val) & 0xff)),\
PUSH_LE_U8((data), (pos) + 1,\
(uint8_t)((uint16_t)(val) >> 8)))
#define PUSH_LE_U32(data, pos, val) \
(PUSH_LE_U16((data), (pos), (uint16_t)((uint32_t)(val) & 0xffff)),\
PUSH_LE_U16((data), (pos) + 2, (uint16_t)((uint32_t)(val) >> 16)))
#define U32_LEN 4
#define U16_LEN 2
#define U8_LEN 1
#define NULL_PAD_BYTE_LEN 1
static int attribute_storable_values(const struct ldb_message_element *el)
{
if (el->num_values == 0) return 0;
if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
return el->num_values;
}
static int ldb_pack_data_v1(struct ldb_context *ldb,
const struct ldb_message *message,
struct ldb_val *data)
{
unsigned int i, j, real_elements=0;
size_t size, dn_len, attr_len, value_len;
const char *dn;
uint8_t *p;
size_t len;
dn = ldb_dn_get_linearized(message->dn);
if (dn == NULL) {
errno = ENOMEM;
return -1;
}
/* work out how big it needs to be */
size = U32_LEN * 2 + NULL_PAD_BYTE_LEN;
dn_len = strlen(dn);
if (size + dn_len < size) {
errno = ENOMEM;
return -1;
}
size += dn_len;
/*
* First calculate the buffer size we need, and check for
* overflows
*/
for (i=0;i<message->num_elements;i++) {
if (attribute_storable_values(&message->elements[i]) == 0) {
continue;
}
real_elements++;
if (size + U32_LEN + NULL_PAD_BYTE_LEN < size) {
errno = ENOMEM;
return -1;
}
size += U32_LEN + NULL_PAD_BYTE_LEN;
attr_len = strlen(message->elements[i].name);
if (size + attr_len < size) {
errno = ENOMEM;
return -1;
}
size += attr_len;
for (j=0;j<message->elements[i].num_values;j++) {
if (size + U32_LEN + NULL_PAD_BYTE_LEN < size) {
errno = ENOMEM;
return -1;
}
size += U32_LEN + NULL_PAD_BYTE_LEN;
value_len = message->elements[i].values[j].length;
if (size + value_len < size) {
errno = ENOMEM;
return -1;
}
size += value_len;
}
}
/* allocate it */
data->data = talloc_array(ldb, uint8_t, size);
if (!data->data) {
errno = ENOMEM;
return -1;
}
data->length = size;
p = data->data;
PUSH_LE_U32(p, 0, LDB_PACKING_FORMAT);
p += U32_LEN;
PUSH_LE_U32(p, 0, real_elements);
p += U32_LEN;
/* the dn needs to be packed so we can be case preserving
while hashing on a case folded dn */
len = dn_len;
memcpy(p, dn, len+NULL_PAD_BYTE_LEN);
p += len + NULL_PAD_BYTE_LEN;
for (i=0;i<message->num_elements;i++) {
if (attribute_storable_values(&message->elements[i]) == 0) {
continue;
}
len = strlen(message->elements[i].name);
memcpy(p, message->elements[i].name, len+NULL_PAD_BYTE_LEN);
p += len + NULL_PAD_BYTE_LEN;
PUSH_LE_U32(p, 0, message->elements[i].num_values);
p += U32_LEN;
for (j=0;j<message->elements[i].num_values;j++) {
PUSH_LE_U32(p, 0,
message->elements[i].values[j].length);
p += U32_LEN;
memcpy(p, message->elements[i].values[j].data,
message->elements[i].values[j].length);
p[message->elements[i].values[j].length] = 0;
p += message->elements[i].values[j].length +
NULL_PAD_BYTE_LEN;
}
}
return 0;
}
/*
* New pack version designed based on performance profiling of version 1.
* The approach is to separate value data from the rest of the record's data.
* This improves performance because value data is not needed during unpacking
* or filtering of the message's attribute list. During filtering we only copy
* attributes which are present in the attribute list, however at the parse
* stage we need to point to all attributes as they may be referenced in the
* search expression.
* With this new format, we don't lose time loading data (eg via
* talloc_memdup()) that is never needed (for the vast majority of attributes
* are are never found in either the search expression or attribute list).
* Additional changes include adding a canonicalized DN (for later
* optimizations) and variable width length fields for faster unpacking.
* The pack and unpack performance improvement is tested in the torture
* test torture_ldb_pack_format_perf.
*
* Layout:
*
* Version (4 bytes)
* Number of Elements (4 bytes)
* DN length (4 bytes)
* DN with null terminator (DN length + 1 bytes)
* Canonicalized DN length (4 bytes)
* Canonicalized DN with null terminator (Canonicalized DN length + 1 bytes)
* Number of bytes from here to value data section (4 bytes)
* # For each element:
* Element name length (4 bytes)
* Element name with null terminator (Element name length + 1 bytes)
* Number of values (4 bytes)
* Width of value lengths
* # For each value:
* Value data length (#bytes given by width field above)
* # For each element:
* # For each value:
* Value data (#bytes given by corresponding length above)
*/
static int ldb_pack_data_v2(struct ldb_context *ldb,
const struct ldb_message *message,
struct ldb_val *data)
{
unsigned int i, j, real_elements=0;
size_t size, dn_len, dn_canon_len, attr_len, value_len;
const char *dn, *dn_canon;
uint8_t *p, *q;
size_t len;
size_t max_val_len;
uint8_t val_len_width;
/*
* First half of this function will calculate required size for
* packed data. Initial size is 20 = 5 * 4. 5 fixed fields are:
* version, num elements, dn len, canon dn len, attr section len
*/
size = U32_LEN * 5;
/*
* Get linearized and canonicalized form of the DN and add the lengths
* of each to size, plus 1 for null terminator.
*/
dn = ldb_dn_get_linearized(message->dn);
if (dn == NULL) {
errno = ENOMEM;
return -1;
}
dn_len = strlen(dn) + NULL_PAD_BYTE_LEN;
if (size + dn_len < size) {
errno = ENOMEM;
return -1;
}
size += dn_len;
if (ldb_dn_is_special(message->dn)) {
dn_canon_len = NULL_PAD_BYTE_LEN;
dn_canon = discard_const_p(char, "\0");
} else {
dn_canon = ldb_dn_canonical_string(message->dn, message->dn);
if (dn_canon == NULL) {
errno = ENOMEM;
return -1;
}
dn_canon_len = strlen(dn_canon) + NULL_PAD_BYTE_LEN;
if (size + dn_canon_len < size) {
errno = ENOMEM;
return -1;
}
}
size += dn_canon_len;
/* Add the size required by each element */
for (i=0;i<message->num_elements;i++) {
if (attribute_storable_values(&message->elements[i]) == 0) {
continue;
}
real_elements++;
/*
* Add length of element name + 9 for:
* 1 for null terminator
* 4 for element name length field
* 4 for number of values field
*/
attr_len = strlen(message->elements[i].name);
if (size + attr_len + U32_LEN * 2 + NULL_PAD_BYTE_LEN < size) {
errno = ENOMEM;
return -1;
}
size += attr_len + U32_LEN * 2 + NULL_PAD_BYTE_LEN;
/*
* Find the max value length, so we can calculate the width
* required for the value length fields.
*/
max_val_len = 0;
for (j=0;j<message->elements[i].num_values;j++) {
value_len = message->elements[i].values[j].length;
if (value_len > max_val_len) {
max_val_len = value_len;
}
if (size + value_len + NULL_PAD_BYTE_LEN < size) {
errno = ENOMEM;
return -1;
}
size += value_len + NULL_PAD_BYTE_LEN;
}
if (max_val_len <= UCHAR_MAX) {
val_len_width = U8_LEN;
} else if (max_val_len <= USHRT_MAX) {
val_len_width = U16_LEN;
} else if (max_val_len <= UINT_MAX) {
val_len_width = U32_LEN;
} else {
errno = EMSGSIZE;
return -1;
}
/* Total size required for val lengths (re-using variable) */
max_val_len = (val_len_width*message->elements[i].num_values);
/* Add one for storing the width */
max_val_len += U8_LEN;
if (size + max_val_len < size) {
errno = ENOMEM;
return -1;
}
size += max_val_len;
}
/* Allocate */
data->data = talloc_array(ldb, uint8_t, size);
if (!data->data) {
errno = ENOMEM;
return -1;
}
data->length = size;
/* Packing format version and number of element */
p = data->data;
PUSH_LE_U32(p, 0, LDB_PACKING_FORMAT_V2);
p += U32_LEN;
PUSH_LE_U32(p, 0, real_elements);
p += U32_LEN;
/* Pack DN and Canonicalized DN */
PUSH_LE_U32(p, 0, dn_len-NULL_PAD_BYTE_LEN);
p += U32_LEN;
memcpy(p, dn, dn_len);
p += dn_len;
PUSH_LE_U32(p, 0, dn_canon_len-NULL_PAD_BYTE_LEN);
p += U32_LEN;
memcpy(p, dn_canon, dn_canon_len);
p += dn_canon_len;
/*
* Save pointer at this point and leave a U32_LEN gap for
* storing the size of the attribute names and value lengths
* section
*/
q = p;
p += U32_LEN;
for (i=0;i<message->num_elements;i++) {
if (attribute_storable_values(&message->elements[i]) == 0) {
continue;
}
/* Length of el name */
len = strlen(message->elements[i].name);
PUSH_LE_U32(p, 0, len);
p += U32_LEN;
/*
* Even though we have the element name's length, put a null
* terminator at the end so if any code uses the name
* directly, it'll be safe to do things requiring null
* termination like strlen
*/
memcpy(p, message->elements[i].name, len+NULL_PAD_BYTE_LEN);
p += len + NULL_PAD_BYTE_LEN;
/* Num values */
PUSH_LE_U32(p, 0, message->elements[i].num_values);
p += U32_LEN;
/*
* Calculate value length width again. It's faster to
* calculate it again than do the array management to
* store the result during size calculation.
*/
max_val_len = 0;
for (j=0;j<message->elements[i].num_values;j++) {
value_len = message->elements[i].values[j].length;
if (value_len > max_val_len) {
max_val_len = value_len;
}
}
if (max_val_len <= UCHAR_MAX) {
val_len_width = U8_LEN;
} else if (max_val_len <= USHRT_MAX) {
val_len_width = U16_LEN;
} else if (max_val_len <= UINT_MAX) {
val_len_width = U32_LEN;
} else {
errno = EMSGSIZE;
return -1;
}
/* Pack the width */
*p = val_len_width & 0xFF;
p += U8_LEN;
/*
* Pack each value's length using the minimum number of bytes
* required, which we just calculated. We repeat the loop
* for each case here so the compiler can inline code.
*/
if (val_len_width == U8_LEN) {
for (j=0;j<message->elements[i].num_values;j++) {
PUSH_LE_U8(p, 0,
message->elements[i].values[j].length);
p += U8_LEN;
}
} else if (val_len_width == U16_LEN) {
for (j=0;j<message->elements[i].num_values;j++) {
PUSH_LE_U16(p, 0,
message->elements[i].values[j].length);
p += U16_LEN;
}
} else if (val_len_width == U32_LEN) {
for (j=0;j<message->elements[i].num_values;j++) {
PUSH_LE_U32(p, 0,
message->elements[i].values[j].length);
p += U32_LEN;
}
}
}
/*
* We've finished packing the attr names and value lengths
* section, so store the size in the U32_LEN gap we left
* earlier
*/
PUSH_LE_U32(q, 0, p-q);
/* Now pack the values */
for (i=0;i<message->num_elements;i++) {
if (attribute_storable_values(&message->elements[i]) == 0) {
continue;
}
for (j=0;j<message->elements[i].num_values;j++) {
memcpy(p, message->elements[i].values[j].data,
message->elements[i].values[j].length);
/*
* Even though we have the data length, put a null
* terminator at the end of each value's data so if
* any code uses the data directly, it'll be safe to
* do things requiring null termination like strlen.
*/
p[message->elements[i].values[j].length] = 0;
p += message->elements[i].values[j].length +
NULL_PAD_BYTE_LEN;
}
}
/*
* If we didn't end up at the end of the data here, something has
* gone very wrong.
*/
if (p != data->data + size) {
errno = ENOMEM;
return -1;
}
return 0;
}
/*
pack a ldb message into a linear buffer in a ldb_val
note that this routine avoids saving elements with zero values,
as these are equivalent to having no element
caller frees the data buffer after use
*/
int ldb_pack_data(struct ldb_context *ldb,
const struct ldb_message *message,
struct ldb_val *data,
uint32_t pack_format_version) {
if (pack_format_version == LDB_PACKING_FORMAT) {
return ldb_pack_data_v1(ldb, message, data);
} else if (pack_format_version == LDB_PACKING_FORMAT_V2) {
return ldb_pack_data_v2(ldb, message, data);
} else {
errno = EINVAL;
return -1;
}
}
/*
* Unpack a ldb message from a linear buffer in ldb_val
*/
static int ldb_unpack_data_flags_v1(struct ldb_context *ldb,
const struct ldb_val *data,
struct ldb_message *message,
unsigned int flags,
unsigned format)
{
uint8_t *p;
size_t remaining;
size_t dn_len;
unsigned int i, j;
unsigned int nelem = 0;
size_t len;
struct ldb_val *ldb_val_single_array = NULL;
message->elements = NULL;
p = data->data;
/* Format (U32, already read) + U32 for num_elements */
if (data->length < U32_LEN * 2) {
errno = EIO;
goto failed;
}
/* Skip first 4 bytes, format already read */
p += U32_LEN;
message->num_elements = PULL_LE_U32(p, 0);
p += U32_LEN;
remaining = data->length - U32_LEN * 2;
switch (format) {
case LDB_PACKING_FORMAT_NODN:
message->dn = NULL;
break;
case LDB_PACKING_FORMAT:
/*
* With this check, we know that the DN at p is \0
* terminated.
*/
dn_len = strnlen((char *)p, remaining);
if (dn_len == remaining) {
errno = EIO;
goto failed;
}
if (flags & LDB_UNPACK_DATA_FLAG_NO_DN) {
message->dn = NULL;
} else {
struct ldb_val blob;
blob.data = discard_const_p(uint8_t, p);
blob.length = dn_len;
message->dn = ldb_dn_from_ldb_val(message, ldb, &blob);
if (message->dn == NULL) {
errno = ENOMEM;
goto failed;
}
}
/*
* Redundant: by definition, remaining must be more
* than one less than dn_len, as otherwise it would be
* == dn_len
*/
if (remaining < dn_len + NULL_PAD_BYTE_LEN) {
errno = EIO;
goto failed;
}
remaining -= dn_len + NULL_PAD_BYTE_LEN;
p += dn_len + NULL_PAD_BYTE_LEN;
break;
default:
errno = EIO;
goto failed;
}
if (flags & LDB_UNPACK_DATA_FLAG_NO_ATTRS) {
message->num_elements = 0;
return 0;
}
if (message->num_elements == 0) {
return 0;
}
if (message->num_elements > remaining / 6) {
errno = EIO;
goto failed;
}
message->elements = talloc_zero_array(message, struct ldb_message_element,
message->num_elements);
if (!message->elements) {
errno = ENOMEM;
goto failed;
}
/*
* In typical use, most values are single-valued. This makes
* it quite expensive to allocate an array of ldb_val for each
* of these, just to then hold the pointer to the data buffer
* So with LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC we allocate this
* ahead of time and use it for the single values where possible.
* (This is used the the normal search case, but not in the
* index case because of caller requirements).
*/
if (flags & LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC) {
ldb_val_single_array = talloc_array(message->elements, struct ldb_val,
message->num_elements);
if (ldb_val_single_array == NULL) {
errno = ENOMEM;
goto failed;
}
}
for (i=0;i<message->num_elements;i++) {
const char *attr = NULL;
size_t attr_len;
struct ldb_message_element *element = NULL;
/*
* Sanity check: Element must be at least the size of empty
* attr name and value and NULL terms for each.
*/
if (remaining < U32_LEN * 2 + NULL_PAD_BYTE_LEN * 2) {
errno = EIO;
goto failed;
}
/*
* With this check, we know that the attribute name at
* p is \0 terminated.
*/
attr_len = strnlen((char *)p, remaining-6);
if (attr_len == remaining-6) {
errno = EIO;
goto failed;
}
if (attr_len == 0) {
errno = EIO;
goto failed;
}
attr = (char *)p;
element = &message->elements[nelem];
element->name = attr;
element->flags = 0;
if (remaining < (attr_len + NULL_PAD_BYTE_LEN)) {
errno = EIO;
goto failed;
}
remaining -= attr_len + NULL_PAD_BYTE_LEN;
p += attr_len + NULL_PAD_BYTE_LEN;
element->num_values = PULL_LE_U32(p, 0);
element->values = NULL;
if ((flags & LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC) && element->num_values == 1) {
element->values = &ldb_val_single_array[nelem];
element->flags |= LDB_FLAG_INTERNAL_SHARED_VALUES;
} else if (element->num_values != 0) {
element->values = talloc_array(message->elements,
struct ldb_val,
element->num_values);
if (!element->values) {
errno = ENOMEM;
goto failed;
}
}
p += U32_LEN;
if (remaining < U32_LEN) {
errno = EIO;
goto failed;
}
remaining -= U32_LEN;
for (j = 0; j < element->num_values; j++) {
/*
* Sanity check: Value must be at least the size of
* empty val and NULL terminator.
*/
if (remaining < U32_LEN + NULL_PAD_BYTE_LEN) {
errno = EIO;
goto failed;
}
remaining -= U32_LEN + NULL_PAD_BYTE_LEN;
len = PULL_LE_U32(p, 0);
if (remaining < len) {
errno = EIO;
goto failed;
}
if (len + NULL_PAD_BYTE_LEN < len) {
errno = EIO;
goto failed;
}
element->values[j].length = len;
element->values[j].data = p + U32_LEN;
remaining -= len;
p += len + U32_LEN + NULL_PAD_BYTE_LEN;
}
nelem++;
}
/*
* Adapt the number of elements to the real number of unpacked elements,
* it means that we overallocated elements array.
*/
message->num_elements = nelem;
/*
* Shrink the allocated size. On current talloc behaviour
* this will help if we skipped 32 or more attributes.
*/
message->elements = talloc_realloc(message, message->elements,
struct ldb_message_element,
message->num_elements);
if (remaining != 0) {
ldb_debug(ldb, LDB_DEBUG_ERROR,
"Error: %zu bytes unread in ldb_unpack_data_flags",
remaining);
}
return 0;
failed:
talloc_free(message->elements);
return -1;
}
/*
* Unpack a ldb message from a linear buffer in ldb_val
*/
static int ldb_unpack_data_flags_v2(struct ldb_context *ldb,
const struct ldb_val *data,
struct ldb_message *message,
unsigned int flags)
{
uint8_t *p, *q, *end_p, *value_section_p;
unsigned int i, j;
unsigned int nelem = 0;
size_t len;
struct ldb_val *ldb_val_single_array = NULL;
uint8_t val_len_width;
message->elements = NULL;
p = data->data;
end_p = p + data->length;
/* Skip first 4 bytes, format already read */
p += U32_LEN;
/* First fields are fixed: num_elements, DN length */
if (U32_LEN * 2 > end_p - p) {
errno = EIO;
goto failed;
}
message->num_elements = PULL_LE_U32(p, 0);
p += U32_LEN;
len = PULL_LE_U32(p, 0);
p += U32_LEN;
if (len + NULL_PAD_BYTE_LEN > end_p - p) {
errno = EIO;
goto failed;
}
if (flags & LDB_UNPACK_DATA_FLAG_NO_DN) {
message->dn = NULL;
} else {
struct ldb_val blob;
blob.data = discard_const_p(uint8_t, p);
blob.length = len;
message->dn = ldb_dn_from_ldb_val(message, ldb, &blob);
if (message->dn == NULL) {
errno = ENOMEM;
goto failed;
}
}
p += len + NULL_PAD_BYTE_LEN;
if (*(p-NULL_PAD_BYTE_LEN) != '\0') {
errno = EINVAL;
goto failed;
}
/* Now skip the canonicalized DN and its length */
len = PULL_LE_U32(p, 0) + NULL_PAD_BYTE_LEN;
p += U32_LEN;
if (len > end_p - p) {
errno = EIO;
goto failed;
}
p += len;
if (*(p-NULL_PAD_BYTE_LEN) != '\0') {
errno = EINVAL;
goto failed;
}
if (flags & LDB_UNPACK_DATA_FLAG_NO_ATTRS) {
message->num_elements = 0;
return 0;
}
if (message->num_elements == 0) {
return 0;
}
/*
* Sanity check (17 bytes is the minimum element size)
*/
if (message->num_elements > (end_p - p) / 17) {
errno = EIO;
goto failed;
}
message->elements = talloc_zero_array(message,
struct ldb_message_element,
message->num_elements);
if (!message->elements) {
errno = ENOMEM;
goto failed;
}
/*
* In typical use, most values are single-valued. This makes
* it quite expensive to allocate an array of ldb_val for each
* of these, just to then hold the pointer to the data buffer.
* So with LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC we allocate this
* ahead of time and use it for the single values where possible.
* (This is used the the normal search case, but not in the
* index case because of caller requirements).
*/
if (flags & LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC) {
ldb_val_single_array = talloc_array(message->elements,
struct ldb_val,
message->num_elements);
if (ldb_val_single_array == NULL) {
errno = ENOMEM;
goto failed;
}
}
q = p + PULL_LE_U32(p, 0);
value_section_p = q;
p += U32_LEN;
for (i=0;i<message->num_elements;i++) {
const char *attr = NULL;
size_t attr_len;
struct ldb_message_element *element = NULL;
/* Sanity check: minimum element size */
if ((U32_LEN * 2) + /* attr name len, num values */
(U8_LEN * 2) + /* value length width, one val length */
(NULL_PAD_BYTE_LEN * 2) /* null for attr name + val */
> value_section_p - p) {
errno = EIO;
goto failed;
}
attr_len = PULL_LE_U32(p, 0);
p += U32_LEN;
if (attr_len == 0) {
errno = EIO;
goto failed;
}
attr = (char *)p;
p += attr_len + NULL_PAD_BYTE_LEN;
/*
* num_values, val_len_width
*
* val_len_width is the width specifier
* for the variable length encoding
*/
if (U32_LEN + U8_LEN > value_section_p - p) {
errno = EIO;
goto failed;
}
if (*(p-NULL_PAD_BYTE_LEN) != '\0') {
errno = EINVAL;
goto failed;
}
element = &message->elements[nelem];
element->name = attr;
element->flags = 0;
element->num_values = PULL_LE_U32(p, 0);
element->values = NULL;
if ((flags & LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC) &&
element->num_values == 1) {
element->values = &ldb_val_single_array[nelem];
element->flags |= LDB_FLAG_INTERNAL_SHARED_VALUES;
} else if (element->num_values != 0) {
element->values = talloc_array(message->elements,
struct ldb_val,
element->num_values);
if (!element->values) {
errno = ENOMEM;
goto failed;
}
}
p += U32_LEN;
/*
* Here we read how wide the remaining lengths are
* which avoids storing and parsing a lot of leading
* 0s
*/
val_len_width = *p;
p += U8_LEN;
if (val_len_width * element->num_values >
value_section_p - p) {
errno = EIO;
goto failed;
}
/*
* This is structured weird for compiler optimization
* purposes, but we need to pull the array of widths
* with different macros depending on how wide the
* biggest one is (specified by val_len_width)
*/
if (val_len_width == U8_LEN) {
for (j = 0; j < element->num_values; j++) {
element->values[j].length = PULL_LE_U8(p, 0);
p += U8_LEN;
}
} else if (val_len_width == U16_LEN) {
for (j = 0; j < element->num_values; j++) {
element->values[j].length = PULL_LE_U16(p, 0);
p += U16_LEN;
}
} else if (val_len_width == U32_LEN) {
for (j = 0; j < element->num_values; j++) {
element->values[j].length = PULL_LE_U32(p, 0);
p += U32_LEN;
}
} else {
errno = ERANGE;
goto failed;
}
for (j = 0; j < element->num_values; j++) {
len = element->values[j].length;
if (len + NULL_PAD_BYTE_LEN < len) {
errno = EIO;
goto failed;
}
if (len + NULL_PAD_BYTE_LEN > end_p - q) {
errno = EIO;
goto failed;
}
element->values[j].data = q;
q += len + NULL_PAD_BYTE_LEN;
}
nelem++;
}
/*
* If p isn't now pointing at the beginning of the value section,
* something went very wrong.
*/
if (p != value_section_p) {
ldb_debug(ldb, LDB_DEBUG_ERROR,
"Error: Data corruption in ldb_unpack_data_flags");
errno = EIO;
goto failed;
}
/*
* Adapt the number of elements to the real number of unpacked
* elements it means that we overallocated elements array.
*/
message->num_elements = nelem;
/*
* Shrink the allocated size. On current talloc behaviour
* this will help if we skipped 32 or more attributes.
*/
message->elements = talloc_realloc(message, message->elements,
struct ldb_message_element,
message->num_elements);
if (q != end_p) {
ldb_debug(ldb, LDB_DEBUG_ERROR,
"Error: %zu bytes unread in ldb_unpack_data_flags",
end_p - q);
errno = EIO;
goto failed;
}
return 0;
failed:
talloc_free(message->elements);
return -1;
}
int ldb_unpack_get_format(const struct ldb_val *data,
uint32_t *pack_format_version)
{
if (data->length < U32_LEN) {
return LDB_ERR_OPERATIONS_ERROR;
}
*pack_format_version = PULL_LE_U32(data->data, 0);
return LDB_SUCCESS;
}
/*
* Unpack a ldb message from a linear buffer in ldb_val
*/
int ldb_unpack_data_flags(struct ldb_context *ldb,
const struct ldb_val *data,
struct ldb_message *message,
unsigned int flags)
{
unsigned format;
if (data->length < U32_LEN) {
errno = EIO;
return -1;
}
format = PULL_LE_U32(data->data, 0);
if (format == LDB_PACKING_FORMAT_V2) {
return ldb_unpack_data_flags_v2(ldb, data, message, flags);
}
/*
* The v1 function we're about to call takes either LDB_PACKING_FORMAT
* or LDB_PACKING_FORMAT_NODN packing format versions, and will error
* if given some other version, so we don't need to do any further
* checks on 'format'.
*/
return ldb_unpack_data_flags_v1(ldb, data, message, flags, format);
}
/*
* Unpack a ldb message from a linear buffer in ldb_val
*
* Free with ldb_unpack_data_free()
*/
int ldb_unpack_data(struct ldb_context *ldb,
const struct ldb_val *data,
struct ldb_message *message)
{
return ldb_unpack_data_flags(ldb, data, message, 0);
}
/*
add the special distinguishedName element
*/
int ldb_msg_add_distinguished_name(struct ldb_message *msg)
{
const char *dn_attr = "distinguishedName";
char *dn = NULL;
if (ldb_msg_find_element(msg, dn_attr)) {
/*
* This should not happen, but this is
* existing behaviour...
*/
return LDB_SUCCESS;
}
dn = ldb_dn_alloc_linearized(msg, msg->dn);
if (dn == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
return ldb_msg_add_steal_string(msg, dn_attr, dn);
}
/*
* filter the specified list of attributes from msg,
* adding requested attributes, and perhaps all for *,
* but not the DN to filtered_msg.
*/
int ldb_filter_attrs(struct ldb_context *ldb,
const struct ldb_message *msg,
const char *const *attrs,
struct ldb_message *filtered_msg)
{
unsigned int i;
bool keep_all = false;
bool add_dn = false;
uint32_t num_elements;
uint32_t elements_size;
if (attrs) {
/* check for special attrs */
for (i = 0; attrs[i]; i++) {
int cmp = strcmp(attrs[i], "*");
if (cmp == 0) {
keep_all = true;
break;
}
cmp = ldb_attr_cmp(attrs[i], "distinguishedName");
if (cmp == 0) {
add_dn = true;
}
}
} else {
keep_all = true;
}
if (keep_all) {
add_dn = true;
elements_size = msg->num_elements + 1;
/* Shortcuts for the simple cases */
} else if (add_dn && i == 1) {
if (ldb_msg_add_distinguished_name(filtered_msg) != 0) {
goto failed;
}
return 0;
} else if (i == 0) {
return 0;
/*
* Otherwise we are copying at most as many elements as we
* have attributes
*/
} else {
elements_size = i;
}
filtered_msg->elements = talloc_array(filtered_msg,
struct ldb_message_element,
elements_size);
if (filtered_msg->elements == NULL) goto failed;
num_elements = 0;
for (i = 0; i < msg->num_elements; i++) {
struct ldb_message_element *el = &msg->elements[i];
/*
* el2 is assigned after the Pigeonhole principle
* check below for clarity
*/
struct ldb_message_element *el2 = NULL;
unsigned int j;
if (keep_all == false) {
bool found = false;
for (j = 0; attrs[j]; j++) {
int cmp = ldb_attr_cmp(el->name, attrs[j]);
if (cmp == 0) {
found = true;
break;
}
}
if (found == false) {
continue;
}
}
/*
* Pigeonhole principle: we can't have more elements
* than the number of attributes if they are unique in
* the DB.
*/
if (num_elements >= elements_size) {
goto failed;
}
el2 = &filtered_msg->elements[num_elements];
*el2 = *el;
el2->name = talloc_strdup(filtered_msg->elements,
el->name);
if (el2->name == NULL) {
goto failed;
}
el2->values = talloc_array(filtered_msg->elements,
struct ldb_val, el->num_values);
if (el2->values == NULL) {
goto failed;
}
for (j=0;j<el->num_values;j++) {
el2->values[j] = ldb_val_dup(el2->values, &el->values[j]);
if (el2->values[j].data == NULL && el->values[j].length != 0) {
goto failed;
}
}
num_elements++;
}
filtered_msg->num_elements = num_elements;
if (add_dn) {
if (ldb_msg_add_distinguished_name(filtered_msg) != 0) {
goto failed;
}
}
if (filtered_msg->num_elements > 0) {
filtered_msg->elements
= talloc_realloc(filtered_msg,
filtered_msg->elements,
struct ldb_message_element,
filtered_msg->num_elements);
if (filtered_msg->elements == NULL) {
goto failed;
}
} else {
TALLOC_FREE(filtered_msg->elements);
}
return 0;
failed:
TALLOC_FREE(filtered_msg->elements);
return -1;
}
/*
* filter the specified list of attributes from msg,
* adding requested attributes, and perhaps all for *.
* Unlike ldb_filter_attrs(), the DN will not be added
* if it is missing.
*/
int ldb_filter_attrs_in_place(struct ldb_message *msg,
const char *const *attrs)
{
unsigned int i = 0;
bool keep_all = false;
unsigned int num_del = 0;
if (attrs) {
/* check for special attrs */
for (i = 0; attrs[i]; i++) {
int cmp = strcmp(attrs[i], "*");
if (cmp == 0) {
keep_all = true;
break;
}
}
if (!keep_all && i == 0) {
msg->num_elements = 0;
return LDB_SUCCESS;
}
} else {
keep_all = true;
}
for (i = 0; i < msg->num_elements; i++) {
bool found = false;
unsigned int j;
if (keep_all) {
found = true;
} else {
for (j = 0; attrs[j]; j++) {
int cmp = ldb_attr_cmp(msg->elements[i].name, attrs[j]);
if (cmp == 0) {
found = true;
break;
}
}
}
if (!found) {
++num_del;
} else if (num_del != 0) {
msg->elements[i - num_del] = msg->elements[i];
}
}
msg->num_elements -= num_del;
return LDB_SUCCESS;
}
/* Have an unpacked ldb message take talloc ownership of its elements. */
int ldb_msg_elements_take_ownership(struct ldb_message *msg)
{
unsigned int i = 0;
for (i = 0; i < msg->num_elements; i++) {
struct ldb_message_element *el = &msg->elements[i];
const char *name;
unsigned int j;
name = talloc_strdup(msg->elements,
el->name);
if (name == NULL) {
return -1;
}
el->name = name;
if (el->flags & LDB_FLAG_INTERNAL_SHARED_VALUES) {
struct ldb_val *values = talloc_memdup(msg->elements, el->values,
sizeof(struct ldb_val) * el->num_values);
if (values == NULL) {
return -1;
}
el->values = values;
el->flags &= ~LDB_FLAG_INTERNAL_SHARED_VALUES;
}
for (j = 0; j < el->num_values; j++) {
struct ldb_val val = ldb_val_dup(el->values, &el->values[j]);
if (val.data == NULL && el->values[j].length != 0) {
return -1;
}
el->values[j] = val;
}
}
return LDB_SUCCESS;
}