2012-10-31 15:39:09 +11:00
/*
2004-03-31 06:45:39 +00:00
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
2012-10-31 15:39:09 +11:00
2004-03-31 06:45:39 +00:00
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
2007-07-10 02:46:15 +00:00
version 3 of the License , or ( at your option ) any later version .
2004-03-31 06:45:39 +00:00
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
2007-07-10 03:42:26 +00:00
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2004-03-31 06:45:39 +00:00
*/
/*
* Name : ldb
*
* Component : ldb pack / unpack
*
* Description : pack / unpack routines for ldb messages as key / value blobs
*
* Author : Andrew Tridgell
*/
2012-10-31 16:06:03 +11:00
# include "ldb_private.h"
2004-03-31 06:45:39 +00:00
/* change this if the data format ever changes */
2012-10-31 16:06:03 +11:00
# define LDB_PACKING_FORMAT 0x26011967
2004-05-01 09:45:56 +00:00
/* old packing formats */
2012-10-31 16:06:03 +11:00
# define LDB_PACKING_FORMAT_NODN 0x26011966
2004-03-31 06:45:39 +00:00
2004-05-14 00:26:37 +00:00
/* use a portable integer format */
2004-12-02 10:35:25 +00:00
static void put_uint32 ( uint8_t * p , int ofs , unsigned int val )
2004-05-14 00:26:37 +00:00
{
p + = ofs ;
p [ 0 ] = val & 0xFF ;
p [ 1 ] = ( val > > 8 ) & 0xFF ;
p [ 2 ] = ( val > > 16 ) & 0xFF ;
p [ 3 ] = ( val > > 24 ) & 0xFF ;
}
2004-12-02 10:35:25 +00:00
static unsigned int pull_uint32 ( uint8_t * p , int ofs )
2004-05-14 00:26:37 +00:00
{
p + = ofs ;
return p [ 0 ] | ( p [ 1 ] < < 8 ) | ( p [ 2 ] < < 16 ) | ( p [ 3 ] < < 24 ) ;
}
2005-12-21 20:25:43 +00:00
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 ;
}
2004-03-31 06:45:39 +00:00
/*
2012-10-31 16:06:03 +11:00
pack a ldb message into a linear buffer in a ldb_val
2004-03-31 06:45:39 +00:00
2004-04-03 12:29:21 +00:00
note that this routine avoids saving elements with zero values ,
as these are equivalent to having no element
2004-03-31 06:45:39 +00:00
caller frees the data buffer after use
*/
2012-10-31 16:06:03 +11:00
int ldb_pack_data ( struct ldb_context * ldb ,
const struct ldb_message * message ,
struct ldb_val * data )
2004-03-31 06:45:39 +00:00
{
2004-07-07 01:02:54 +00:00
unsigned int i , j , real_elements = 0 ;
2015-11-13 18:45:23 +13:00
size_t size , dn_len , attr_len , value_len ;
2006-11-22 02:05:19 +00:00
const char * dn ;
2005-10-12 06:10:23 +00:00
uint8_t * p ;
2004-05-01 09:45:56 +00:00
size_t len ;
2004-03-31 06:45:39 +00:00
2006-11-22 02:05:19 +00:00
dn = ldb_dn_get_linearized ( message - > dn ) ;
2005-08-18 15:02:01 +00:00
if ( dn = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
2004-03-31 06:45:39 +00:00
/* work out how big it needs to be */
size = 8 ;
2015-11-13 18:45:23 +13:00
size + = 1 ;
2004-05-01 09:45:56 +00:00
2015-11-13 18:45:23 +13:00
dn_len = strlen ( dn ) ;
if ( size + dn_len < size ) {
errno = ENOMEM ;
return - 1 ;
}
size + = dn_len ;
/*
* First calcuate the buffer size we need , and check for
* overflows
*/
2004-03-31 06:45:39 +00:00
for ( i = 0 ; i < message - > num_elements ; i + + ) {
2005-12-21 20:25:43 +00:00
if ( attribute_storable_values ( & message - > elements [ i ] ) = = 0 ) {
2004-04-03 12:29:21 +00:00
continue ;
}
2005-12-21 20:25:43 +00:00
real_elements + + ;
2015-11-13 18:45:23 +13:00
if ( size + 5 < size ) {
errno = ENOMEM ;
return - 1 ;
}
size + = 5 ;
attr_len = strlen ( message - > elements [ i ] . name ) ;
if ( size + attr_len < size ) {
errno = ENOMEM ;
return - 1 ;
}
size + = attr_len ;
2004-04-03 12:29:21 +00:00
for ( j = 0 ; j < message - > elements [ i ] . num_values ; j + + ) {
2015-11-13 18:45:23 +13:00
if ( size + 5 < size ) {
errno = ENOMEM ;
return - 1 ;
}
size + = 5 ;
value_len = message - > elements [ i ] . values [ j ] . length ;
if ( size + value_len < size ) {
errno = ENOMEM ;
return - 1 ;
}
size + = value_len ;
2004-04-03 12:29:21 +00:00
}
2004-03-31 06:45:39 +00:00
}
/* allocate it */
2012-10-31 16:06:03 +11:00
data - > data = talloc_array ( ldb , uint8_t , size ) ;
if ( ! data - > data ) {
2004-03-31 06:45:39 +00:00
errno = ENOMEM ;
return - 1 ;
}
2012-10-31 16:06:03 +11:00
data - > length = size ;
2004-03-31 06:45:39 +00:00
2012-10-31 16:06:03 +11:00
p = data - > data ;
put_uint32 ( p , 0 , LDB_PACKING_FORMAT ) ;
2012-10-31 15:39:09 +11:00
put_uint32 ( p , 4 , real_elements ) ;
2004-03-31 06:45:39 +00:00
p + = 8 ;
2004-05-01 09:45:56 +00:00
/* the dn needs to be packed so we can be case preserving
while hashing on a case folded dn */
2015-11-13 18:45:23 +13:00
len = dn_len ;
2005-08-18 15:02:01 +00:00
memcpy ( p , dn , len + 1 ) ;
2004-05-01 09:45:56 +00:00
p + = len + 1 ;
2012-10-31 15:39:09 +11:00
2004-03-31 06:45:39 +00:00
for ( i = 0 ; i < message - > num_elements ; i + + ) {
2005-12-21 20:25:43 +00:00
if ( attribute_storable_values ( & message - > elements [ i ] ) = = 0 ) {
2004-04-03 12:29:21 +00:00
continue ;
}
len = strlen ( message - > elements [ i ] . name ) ;
2004-03-31 06:45:39 +00:00
memcpy ( p , message - > elements [ i ] . name , len + 1 ) ;
p + = len + 1 ;
2004-05-14 00:26:37 +00:00
put_uint32 ( p , 0 , message - > elements [ i ] . num_values ) ;
2004-04-03 12:29:21 +00:00
p + = 4 ;
for ( j = 0 ; j < message - > elements [ i ] . num_values ; j + + ) {
2004-05-14 00:26:37 +00:00
put_uint32 ( p , 0 , message - > elements [ i ] . values [ j ] . length ) ;
2012-10-31 15:39:09 +11:00
memcpy ( p + 4 , message - > elements [ i ] . values [ j ] . data ,
2004-04-03 12:29:21 +00:00
message - > elements [ i ] . values [ j ] . length ) ;
p [ 4 + message - > elements [ i ] . values [ j ] . length ] = 0 ;
p + = 4 + message - > elements [ i ] . values [ j ] . length + 1 ;
}
2004-03-31 06:45:39 +00:00
}
return 0 ;
}
2015-11-13 18:45:23 +13:00
static bool ldb_consume_element_data ( uint8_t * * pp , size_t * premaining )
2012-12-27 21:38:29 -08:00
{
unsigned int remaining = * premaining ;
uint8_t * p = * pp ;
uint32_t num_values = pull_uint32 ( p , 0 ) ;
2016-08-11 15:46:49 +02:00
uint32_t j , len ;
2012-12-27 21:38:29 -08:00
p + = 4 ;
2015-11-13 18:45:23 +13:00
if ( remaining < 4 ) {
return false ;
}
2012-12-27 21:38:29 -08:00
remaining - = 4 ;
for ( j = 0 ; j < num_values ; j + + ) {
len = pull_uint32 ( p , 0 ) ;
2015-11-13 18:45:23 +13:00
if ( remaining < 5 ) {
return false ;
}
remaining - = 5 ;
if ( len > remaining ) {
2012-12-27 21:38:29 -08:00
return false ;
}
2015-11-13 18:45:23 +13:00
remaining - = len ;
2012-12-27 21:38:29 -08:00
p + = len + 4 + 1 ;
}
* premaining = remaining ;
* pp = p ;
return true ;
}
2004-03-31 06:45:39 +00:00
2016-07-27 00:17:36 +12:00
2015-12-17 11:24:44 +13:00
/*
* Unpack a ldb message from a linear buffer in ldb_val
*
* Providing a list of attributes to this function allows selective unpacking .
* Giving a NULL list ( or a list_size of 0 ) unpacks all the attributes .
*/
2016-07-27 00:17:36 +12:00
int ldb_unpack_data_only_attr_list_flags ( struct ldb_context * ldb ,
const struct ldb_val * data ,
struct ldb_message * message ,
const char * const * list ,
unsigned int list_size ,
unsigned int flags ,
unsigned int * nb_elements_in_db )
2004-03-31 06:45:39 +00:00
{
2005-10-12 06:10:23 +00:00
uint8_t * p ;
2015-11-13 18:45:23 +13:00
size_t remaining ;
size_t dn_len ;
2004-07-07 01:02:54 +00:00
unsigned int i , j ;
2004-05-01 09:45:56 +00:00
unsigned format ;
2012-12-27 21:38:29 -08:00
unsigned int nelem = 0 ;
2004-05-01 09:45:56 +00:00
size_t len ;
2012-12-27 21:38:29 -08:00
unsigned int found = 0 ;
if ( list = = NULL ) {
list_size = 0 ;
}
2004-03-31 06:45:39 +00:00
message - > elements = NULL ;
2012-10-31 16:06:03 +11:00
p = data - > data ;
if ( data - > length < 8 ) {
2004-03-31 06:45:39 +00:00
errno = EIO ;
goto failed ;
}
2004-05-14 00:26:37 +00:00
format = pull_uint32 ( p , 0 ) ;
message - > num_elements = pull_uint32 ( p , 4 ) ;
2004-05-01 09:45:56 +00:00
p + = 8 ;
2012-12-27 21:38:29 -08:00
if ( nb_elements_in_db ) {
* nb_elements_in_db = message - > num_elements ;
}
2004-05-01 09:45:56 +00:00
2012-10-31 16:06:03 +11:00
remaining = data - > length - 8 ;
2004-05-01 09:45:56 +00:00
switch ( format ) {
2012-10-31 16:06:03 +11:00
case LDB_PACKING_FORMAT_NODN :
2004-05-01 09:45:56 +00:00
message - > dn = NULL ;
break ;
2012-10-31 16:06:03 +11:00
case LDB_PACKING_FORMAT :
2015-11-13 18:45:23 +13:00
/*
* With this check , we know that the DN at p is \ 0
* terminated .
*/
dn_len = strnlen ( ( char * ) p , remaining ) ;
if ( dn_len = = remaining ) {
2004-05-01 09:45:56 +00:00
errno = EIO ;
goto failed ;
}
2016-07-27 00:17:36 +12:00
if ( flags & LDB_UNPACK_DATA_FLAG_NO_DN ) {
message - > dn = NULL ;
} else {
message - > dn = ldb_dn_new ( message , ldb , ( char * ) p ) ;
if ( message - > dn = = NULL ) {
errno = ENOMEM ;
goto failed ;
}
2005-08-18 15:02:01 +00:00
}
2015-11-13 18:45:23 +13:00
/*
* Redundant : by definition , remaining must be more
* than one less than dn_len , as otherwise it would be
* = = dn_len
*/
if ( remaining < dn_len + 1 ) {
errno = EIO ;
goto failed ;
}
remaining - = dn_len + 1 ;
p + = dn_len + 1 ;
2004-05-01 09:45:56 +00:00
break ;
default :
2004-03-31 06:45:39 +00:00
errno = EIO ;
goto failed ;
}
if ( message - > num_elements = = 0 ) {
return 0 ;
}
2012-10-31 15:39:09 +11:00
2004-03-31 06:45:39 +00:00
if ( message - > num_elements > remaining / 6 ) {
errno = EIO ;
goto failed ;
}
2015-11-13 18:45:23 +13:00
message - > elements = talloc_zero_array ( message , struct ldb_message_element ,
message - > num_elements ) ;
2004-03-31 06:45:39 +00:00
if ( ! message - > elements ) {
errno = ENOMEM ;
goto failed ;
}
for ( i = 0 ; i < message - > num_elements ; i + + ) {
2012-12-27 21:38:29 -08:00
const char * attr = NULL ;
2015-09-01 13:27:52 +12:00
size_t attr_len ;
2012-12-27 21:38:29 -08:00
struct ldb_message_element * element = NULL ;
2004-04-03 12:29:21 +00:00
if ( remaining < 10 ) {
2004-03-31 06:45:39 +00:00
errno = EIO ;
goto failed ;
}
2015-09-01 13:27:52 +12:00
/*
* 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 ) {
2004-05-01 09:45:56 +00:00
errno = EIO ;
goto failed ;
}
2015-09-01 13:27:52 +12:00
if ( attr_len = = 0 ) {
2009-09-15 10:00:24 -07:00
errno = EIO ;
goto failed ;
}
2012-12-27 21:38:29 -08:00
attr = ( char * ) p ;
2015-12-17 11:24:44 +13:00
2012-12-27 21:38:29 -08:00
/*
2015-12-17 11:24:44 +13:00
* The general idea is to reduce allocations by skipping over
* attributes that we do not actually care about .
*
2012-12-27 21:38:29 -08:00
* This is a bit expensive but normally the list is pretty small
* also the cost of freeing unused attributes is quite important
2015-12-17 11:24:44 +13:00
* and can dwarf the cost of looping .
2012-12-27 21:38:29 -08:00
*/
if ( list_size ! = 0 ) {
bool keep = false ;
2016-08-11 15:46:49 +02:00
unsigned int h ;
2012-12-27 21:38:29 -08:00
/*
* We know that p has a \ 0 terminator before the
* end of the buffer due to the check above .
*/
for ( h = 0 ; h < list_size & & found < list_size ; h + + ) {
if ( ldb_attr_cmp ( attr , list [ h ] ) = = 0 ) {
keep = true ;
found + + ;
break ;
}
}
if ( ! keep ) {
2015-11-13 18:45:23 +13:00
if ( remaining < ( attr_len + 1 ) ) {
errno = EIO ;
goto failed ;
}
2015-09-01 13:27:52 +12:00
remaining - = attr_len + 1 ;
p + = attr_len + 1 ;
2012-12-27 21:38:29 -08:00
if ( ! ldb_consume_element_data ( & p , & remaining ) ) {
errno = EIO ;
goto failed ;
}
continue ;
}
}
element = & message - > elements [ nelem ] ;
2016-07-27 00:17:36 +12:00
if ( flags & LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC ) {
element - > name = attr ;
} else {
element - > name = talloc_memdup ( message - > elements , attr , attr_len + 1 ) ;
2015-12-17 11:41:13 +13:00
2016-07-27 00:17:36 +12:00
if ( element - > name = = NULL ) {
errno = ENOMEM ;
goto failed ;
}
2006-03-03 17:44:03 +00:00
}
2012-12-27 21:38:29 -08:00
element - > flags = 0 ;
2015-11-13 18:45:23 +13:00
if ( remaining < ( attr_len + 1 ) ) {
errno = EIO ;
goto failed ;
}
2015-09-01 13:27:52 +12:00
remaining - = attr_len + 1 ;
p + = attr_len + 1 ;
2012-12-27 21:38:29 -08:00
element - > num_values = pull_uint32 ( p , 0 ) ;
element - > values = NULL ;
if ( element - > num_values ! = 0 ) {
element - > values = talloc_array ( message - > elements ,
struct ldb_val ,
element - > num_values ) ;
if ( ! element - > values ) {
2004-04-03 12:29:21 +00:00
errno = ENOMEM ;
goto failed ;
}
}
p + = 4 ;
2015-11-13 18:45:23 +13:00
if ( remaining < 4 ) {
errno = EIO ;
goto failed ;
}
2004-05-20 13:25:06 +00:00
remaining - = 4 ;
2012-12-27 21:38:29 -08:00
for ( j = 0 ; j < element - > num_values ; j + + ) {
2015-11-13 18:45:23 +13:00
if ( remaining < 5 ) {
errno = EIO ;
goto failed ;
}
remaining - = 5 ;
2004-05-14 00:26:37 +00:00
len = pull_uint32 ( p , 0 ) ;
2015-11-13 18:45:23 +13:00
if ( remaining < len ) {
errno = EIO ;
goto failed ;
}
if ( len + 1 < len ) {
2004-04-03 12:29:21 +00:00
errno = EIO ;
goto failed ;
}
2012-12-27 21:38:29 -08:00
element - > values [ j ] . length = len ;
2016-07-27 00:17:36 +12:00
if ( flags & LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC ) {
element - > values [ j ] . data = p + 4 ;
} else {
element - > values [ j ] . data = talloc_size ( element - > values , len + 1 ) ;
if ( element - > values [ j ] . data = = NULL ) {
errno = ENOMEM ;
goto failed ;
}
memcpy ( element - > values [ j ] . data , p + 4 ,
len ) ;
element - > values [ j ] . data [ len ] = 0 ;
2006-03-03 17:44:03 +00:00
}
2015-11-13 18:45:23 +13:00
remaining - = len ;
2004-04-03 12:29:21 +00:00
p + = len + 4 + 1 ;
2004-03-31 06:45:39 +00:00
}
2012-12-27 21:38:29 -08:00
nelem + + ;
2004-03-31 06:45:39 +00:00
}
2012-12-27 21:38:29 -08:00
/*
* Adapt the number of elements to the real number of unpacked elements ,
2015-11-13 18:45:23 +13:00
* it means that we overallocated elements array .
2012-12-27 21:38:29 -08:00
*/
message - > num_elements = nelem ;
2004-03-31 06:45:39 +00:00
2015-11-13 18:45:23 +13:00
/*
* 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 ) ;
2004-05-20 13:25:06 +00:00
if ( remaining ! = 0 ) {
2012-10-31 15:39:09 +11:00
ldb_debug ( ldb , LDB_DEBUG_ERROR ,
2015-11-13 18:45:23 +13:00
" Error: %zu bytes unread in ldb_unpack_data_only_attr_list " ,
2012-12-27 21:38:29 -08:00
remaining ) ;
2004-05-20 13:25:06 +00:00
}
2004-03-31 06:45:39 +00:00
return 0 ;
failed :
2005-01-02 09:46:59 +00:00
talloc_free ( message - > elements ) ;
2004-03-31 06:45:39 +00:00
return - 1 ;
}
2012-12-27 21:38:29 -08:00
2016-07-27 00:17:36 +12:00
/*
* Unpack a ldb message from a linear buffer in ldb_val
*
* Providing a list of attributes to this function allows selective unpacking .
* Giving a NULL list ( or a list_size of 0 ) unpacks all the attributes .
*
* Free with ldb_unpack_data_free ( )
*/
int ldb_unpack_data_only_attr_list ( struct ldb_context * ldb ,
const struct ldb_val * data ,
struct ldb_message * message ,
const char * const * list ,
unsigned int list_size ,
unsigned int * nb_elements_in_db )
{
return ldb_unpack_data_only_attr_list_flags ( ldb ,
data ,
message ,
list ,
list_size ,
0 ,
nb_elements_in_db ) ;
}
2012-12-27 21:38:29 -08:00
int ldb_unpack_data ( struct ldb_context * ldb ,
const struct ldb_val * data ,
struct ldb_message * message )
{
2015-12-17 11:24:44 +13:00
return ldb_unpack_data_only_attr_list ( ldb , data , message , NULL , 0 , NULL ) ;
2012-12-27 21:38:29 -08:00
}