2006-12-12 17:52:13 +03:00
/*
Unix SMB / CIFS implementation .
ID Mapping Cache
based on gencache
Copyright ( C ) Simo Sorce 2006
Copyright ( C ) Rafal Szczesniak 2002
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2006-12-12 17:52:13 +03:00
( at your option ) any later version .
This program 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 General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.*/
2006-12-12 17:52:13 +03:00
# include "includes.h"
2007-03-01 06:16:38 +03:00
# include "winbindd.h"
2006-12-12 17:52:13 +03:00
# define TIMEOUT_LEN 12
# define IDMAP_CACHE_DATA_FMT "%12u / %s"
# define IDMAP_READ_CACHE_DATA_FMT_TEMPLATE "%%12u / %%%us"
struct idmap_cache_ctx {
TDB_CONTEXT * tdb ;
} ;
static int idmap_cache_destructor ( struct idmap_cache_ctx * cache )
{
int ret = 0 ;
if ( cache & & cache - > tdb ) {
ret = tdb_close ( cache - > tdb ) ;
cache - > tdb = NULL ;
}
return ret ;
}
struct idmap_cache_ctx * idmap_cache_init ( TALLOC_CTX * memctx )
{
struct idmap_cache_ctx * cache ;
char * cache_fname = NULL ;
cache = talloc ( memctx , struct idmap_cache_ctx ) ;
if ( ! cache ) {
DEBUG ( 0 , ( " Out of memory! \n " ) ) ;
return NULL ;
}
cache_fname = lock_path ( " idmap_cache.tdb " ) ;
DEBUG ( 10 , ( " Opening cache file at %s \n " , cache_fname ) ) ;
cache - > tdb = tdb_open_log ( cache_fname , 0 , TDB_DEFAULT , O_RDWR | O_CREAT , 0600 ) ;
if ( ! cache - > tdb ) {
DEBUG ( 5 , ( " Attempt to open %s has failed. \n " , cache_fname ) ) ;
return NULL ;
}
talloc_set_destructor ( cache , idmap_cache_destructor ) ;
return cache ;
}
void idmap_cache_shutdown ( struct idmap_cache_ctx * cache )
{
talloc_free ( cache ) ;
}
NTSTATUS idmap_cache_build_sidkey ( TALLOC_CTX * ctx , char * * sidkey , const struct id_map * id )
{
* sidkey = talloc_asprintf ( ctx , " IDMAP/SID/%s " , sid_string_static ( id - > sid ) ) ;
if ( ! * sidkey ) {
DEBUG ( 1 , ( " failed to build sidkey, OOM? \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
}
NTSTATUS idmap_cache_build_idkey ( TALLOC_CTX * ctx , char * * idkey , const struct id_map * id )
{
* idkey = talloc_asprintf ( ctx , " IDMAP/%s/%lu " ,
( id - > xid . type = = ID_TYPE_UID ) ? " UID " : " GID " ,
( unsigned long ) id - > xid . id ) ;
if ( ! * idkey ) {
DEBUG ( 1 , ( " failed to build idkey, OOM? \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
}
NTSTATUS idmap_cache_set ( struct idmap_cache_ctx * cache , const struct id_map * id )
{
NTSTATUS ret ;
2007-03-21 18:21:28 +03:00
time_t timeout = time ( NULL ) + lp_idmap_cache_time ( ) ;
2007-03-27 14:50:32 +04:00
TDB_DATA databuf ;
2006-12-12 17:52:13 +03:00
char * sidkey ;
char * idkey ;
char * valstr ;
2007-04-07 01:05:55 +04:00
/* Don't cache lookups in the S-1-22-{1,2} domain */
if ( ( id - > xid . type = = ID_TYPE_UID ) & &
sid_check_is_in_unix_users ( id - > sid ) )
{
return NT_STATUS_OK ;
}
if ( ( id - > xid . type = = ID_TYPE_GID ) & &
sid_check_is_in_unix_groups ( id - > sid ) )
{
return NT_STATUS_OK ;
}
2006-12-12 17:52:13 +03:00
ret = idmap_cache_build_sidkey ( cache , & sidkey , id ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) return ret ;
/* use sidkey as the local memory ctx */
ret = idmap_cache_build_idkey ( sidkey , & idkey , id ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
goto done ;
}
/* save SID -> ID */
/* use sidkey as the local memory ctx */
valstr = talloc_asprintf ( sidkey , IDMAP_CACHE_DATA_FMT , ( int ) timeout , idkey ) ;
if ( ! valstr ) {
DEBUG ( 0 , ( " Out of memory! \n " ) ) ;
ret = NT_STATUS_NO_MEMORY ;
goto done ;
}
2007-03-27 14:50:32 +04:00
databuf = string_term_tdb_data ( valstr ) ;
2006-12-12 17:52:13 +03:00
DEBUG ( 10 , ( " Adding cache entry with key = %s; value = %s and timeout = "
2007-03-27 14:50:32 +04:00
" %s (%d seconds %s) \n " , sidkey , valstr , ctime ( & timeout ) ,
2006-12-12 17:52:13 +03:00
( int ) ( timeout - time ( NULL ) ) ,
timeout > time ( NULL ) ? " ahead " : " in the past " ) ) ;
2007-03-27 14:50:32 +04:00
if ( tdb_store_bystring ( cache - > tdb , sidkey , databuf , TDB_REPLACE ) ! = 0 ) {
2006-12-12 17:52:13 +03:00
DEBUG ( 3 , ( " Failed to store cache entry! \n " ) ) ;
ret = NT_STATUS_UNSUCCESSFUL ;
goto done ;
}
/* save ID -> SID */
/* use sidkey as the local memory ctx */
valstr = talloc_asprintf ( sidkey , IDMAP_CACHE_DATA_FMT , ( int ) timeout , sidkey ) ;
if ( ! valstr ) {
DEBUG ( 0 , ( " Out of memory! \n " ) ) ;
ret = NT_STATUS_NO_MEMORY ;
goto done ;
}
2007-03-27 14:50:32 +04:00
databuf = string_term_tdb_data ( valstr ) ;
2006-12-12 17:52:13 +03:00
DEBUG ( 10 , ( " Adding cache entry with key = %s; value = %s and timeout = "
2007-03-27 14:50:32 +04:00
" %s (%d seconds %s) \n " , idkey , valstr , ctime ( & timeout ) ,
2006-12-12 17:52:13 +03:00
( int ) ( timeout - time ( NULL ) ) ,
timeout > time ( NULL ) ? " ahead " : " in the past " ) ) ;
2007-03-27 14:50:32 +04:00
if ( tdb_store_bystring ( cache - > tdb , idkey , databuf , TDB_REPLACE ) ! = 0 ) {
2006-12-12 17:52:13 +03:00
DEBUG ( 3 , ( " Failed to store cache entry! \n " ) ) ;
ret = NT_STATUS_UNSUCCESSFUL ;
goto done ;
}
ret = NT_STATUS_OK ;
done :
talloc_free ( sidkey ) ;
return ret ;
}
NTSTATUS idmap_cache_set_negative_sid ( struct idmap_cache_ctx * cache , const struct id_map * id )
{
NTSTATUS ret ;
2007-03-21 18:21:28 +03:00
time_t timeout = time ( NULL ) + lp_idmap_negative_cache_time ( ) ;
2007-03-27 14:50:32 +04:00
TDB_DATA databuf ;
2006-12-12 17:52:13 +03:00
char * sidkey ;
char * valstr ;
ret = idmap_cache_build_sidkey ( cache , & sidkey , id ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) return ret ;
/* use sidkey as the local memory ctx */
valstr = talloc_asprintf ( sidkey , IDMAP_CACHE_DATA_FMT , ( int ) timeout , " IDMAP/NEGATIVE " ) ;
if ( ! valstr ) {
DEBUG ( 0 , ( " Out of memory! \n " ) ) ;
ret = NT_STATUS_NO_MEMORY ;
goto done ;
}
2007-03-27 14:50:32 +04:00
databuf = string_term_tdb_data ( valstr ) ;
2006-12-12 17:52:13 +03:00
DEBUG ( 10 , ( " Adding cache entry with key = %s; value = %s and timeout = "
2007-03-27 14:50:32 +04:00
" %s (%d seconds %s) \n " , sidkey , valstr , ctime ( & timeout ) ,
2006-12-12 17:52:13 +03:00
( int ) ( timeout - time ( NULL ) ) ,
timeout > time ( NULL ) ? " ahead " : " in the past " ) ) ;
2007-03-27 14:50:32 +04:00
if ( tdb_store_bystring ( cache - > tdb , sidkey , databuf , TDB_REPLACE ) ! = 0 ) {
2006-12-12 17:52:13 +03:00
DEBUG ( 3 , ( " Failed to store cache entry! \n " ) ) ;
ret = NT_STATUS_UNSUCCESSFUL ;
goto done ;
}
done :
talloc_free ( sidkey ) ;
return ret ;
}
NTSTATUS idmap_cache_set_negative_id ( struct idmap_cache_ctx * cache , const struct id_map * id )
{
NTSTATUS ret ;
2007-03-21 18:21:28 +03:00
time_t timeout = time ( NULL ) + lp_idmap_negative_cache_time ( ) ;
2007-03-27 14:50:32 +04:00
TDB_DATA databuf ;
2006-12-12 17:52:13 +03:00
char * idkey ;
char * valstr ;
ret = idmap_cache_build_idkey ( cache , & idkey , id ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) return ret ;
/* use idkey as the local memory ctx */
valstr = talloc_asprintf ( idkey , IDMAP_CACHE_DATA_FMT , ( int ) timeout , " IDMAP/NEGATIVE " ) ;
if ( ! valstr ) {
DEBUG ( 0 , ( " Out of memory! \n " ) ) ;
ret = NT_STATUS_NO_MEMORY ;
goto done ;
}
2007-03-27 14:50:32 +04:00
databuf = string_term_tdb_data ( valstr ) ;
2006-12-12 17:52:13 +03:00
DEBUG ( 10 , ( " Adding cache entry with key = %s; value = %s and timeout = "
2007-03-27 14:50:32 +04:00
" %s (%d seconds %s) \n " , idkey , valstr , ctime ( & timeout ) ,
2006-12-12 17:52:13 +03:00
( int ) ( timeout - time ( NULL ) ) ,
timeout > time ( NULL ) ? " ahead " : " in the past " ) ) ;
2007-03-27 14:50:32 +04:00
if ( tdb_store_bystring ( cache - > tdb , idkey , databuf , TDB_REPLACE ) ! = 0 ) {
2006-12-12 17:52:13 +03:00
DEBUG ( 3 , ( " Failed to store cache entry! \n " ) ) ;
ret = NT_STATUS_UNSUCCESSFUL ;
goto done ;
}
done :
talloc_free ( idkey ) ;
return ret ;
}
NTSTATUS idmap_cache_fill_map ( struct id_map * id , const char * value )
{
char * rem ;
/* see if it is a sid */
if ( ! strncmp ( " IDMAP/SID/ " , value , 10 ) ) {
if ( ! string_to_sid ( id - > sid , & value [ 10 ] ) ) {
goto failed ;
}
2007-01-14 20:58:24 +03:00
id - > status = ID_MAPPED ;
2006-12-12 17:52:13 +03:00
return NT_STATUS_OK ;
}
/* not a SID see if it is an UID or a GID */
if ( ! strncmp ( " IDMAP/UID/ " , value , 10 ) ) {
/* a uid */
id - > xid . type = ID_TYPE_UID ;
} else if ( ! strncmp ( " IDMAP/GID/ " , value , 10 ) ) {
/* a gid */
id - > xid . type = ID_TYPE_GID ;
} else {
/* a completely bogus value bail out */
goto failed ;
}
id - > xid . id = strtol ( & value [ 10 ] , & rem , 0 ) ;
if ( * rem ! = ' \0 ' ) {
goto failed ;
}
2007-01-14 20:58:24 +03:00
id - > status = ID_MAPPED ;
2006-12-12 17:52:13 +03:00
return NT_STATUS_OK ;
failed :
DEBUG ( 1 , ( " invalid value: %s \n " , value ) ) ;
2007-01-14 20:58:24 +03:00
id - > status = ID_UNKNOWN ;
2006-12-12 17:52:13 +03:00
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
BOOL idmap_cache_is_negative ( const char * val )
{
if ( ! strcmp ( " IDMAP/NEGATIVE " , val ) ) {
return True ;
}
return False ;
}
/* search the cahce for the SID an return a mapping if found *
*
2007-04-19 01:10:37 +04:00
* 4 cases are possible
2006-12-12 17:52:13 +03:00
*
* 1 map found
2007-01-14 20:58:24 +03:00
* in this case id - > status = ID_MAPPED and NT_STATUS_OK is returned
2006-12-12 17:52:13 +03:00
* 2 map not found
2007-01-14 20:58:24 +03:00
* in this case id - > status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned
2006-12-12 17:52:13 +03:00
* 3 negative cache found
2007-01-14 20:58:24 +03:00
* in this case id - > status = ID_UNMAPPED and NT_STATUS_OK is returned
2007-04-19 01:10:37 +04:00
* 4 map found but timer expired
* in this case id - > status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED
* is returned . In this case revalidation of the cache is needed .
2006-12-12 17:52:13 +03:00
*/
NTSTATUS idmap_cache_map_sid ( struct idmap_cache_ctx * cache , struct id_map * id )
{
NTSTATUS ret ;
2007-03-27 14:50:32 +04:00
TDB_DATA databuf ;
2007-05-07 01:04:30 +04:00
time_t t ;
2006-12-12 17:52:13 +03:00
char * sidkey ;
char * endptr ;
2007-05-07 01:04:30 +04:00
struct winbindd_domain * our_domain = find_our_domain ( ) ;
time_t now = time ( NULL ) ;
2006-12-12 17:52:13 +03:00
/* make sure it is marked as not mapped by default */
2007-01-14 20:58:24 +03:00
id - > status = ID_UNKNOWN ;
2006-12-12 17:52:13 +03:00
ret = idmap_cache_build_sidkey ( cache , & sidkey , id ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) return ret ;
2007-03-27 14:50:32 +04:00
databuf = tdb_fetch_bystring ( cache - > tdb , sidkey ) ;
2006-12-12 17:52:13 +03:00
if ( databuf . dptr = = NULL ) {
DEBUG ( 10 , ( " Cache entry with key = %s couldn't be found \n " , sidkey ) ) ;
2007-08-31 13:39:11 +04:00
ret = NT_STATUS_NONE_MAPPED ;
goto done ;
2006-12-12 17:52:13 +03:00
}
2007-03-29 13:35:51 +04:00
t = strtol ( ( const char * ) databuf . dptr , & endptr , 10 ) ;
2006-12-12 17:52:13 +03:00
if ( ( endptr = = NULL ) | | ( * endptr ! = ' / ' ) ) {
2007-03-29 13:35:51 +04:00
DEBUG ( 2 , ( " Invalid gencache data format: %s \n " , ( const char * ) databuf . dptr ) ) ;
2006-12-12 17:52:13 +03:00
/* remove the entry */
2007-03-27 14:50:32 +04:00
tdb_delete_bystring ( cache - > tdb , sidkey ) ;
2006-12-12 17:52:13 +03:00
ret = NT_STATUS_NONE_MAPPED ;
goto done ;
}
/* check it is not negative */
if ( strcmp ( " IDMAP/NEGATIVE " , endptr + 1 ) ! = 0 ) {
2007-04-19 01:10:37 +04:00
2006-12-12 17:52:13 +03:00
DEBUG ( 10 , ( " Returning %s cache entry: key = %s, value = %s, "
2007-04-19 01:10:37 +04:00
" timeout = %s " , t > now ? " valid " :
2006-12-12 17:52:13 +03:00
" expired " , sidkey , endptr + 1 , ctime ( & t ) ) ) ;
/* this call if successful will also mark the entry as mapped */
ret = idmap_cache_fill_map ( id , endptr + 1 ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
/* if not valid form delete the entry */
2007-03-27 14:50:32 +04:00
tdb_delete_bystring ( cache - > tdb , sidkey ) ;
2006-12-12 17:52:13 +03:00
ret = NT_STATUS_NONE_MAPPED ;
goto done ;
}
2007-01-14 20:58:24 +03:00
/* here ret == NT_STATUS_OK and id->status = ID_MAPPED */
2006-12-12 17:52:13 +03:00
2007-04-19 01:10:37 +04:00
if ( t < = now ) {
2007-05-07 01:04:30 +04:00
/* If we've been told to be offline - stay in
that state . . . */
if ( IS_DOMAIN_OFFLINE ( our_domain ) ) {
DEBUG ( 10 , ( " idmap_cache_map_sid: idmap is offline \n " ) ) ;
goto done ;
}
2007-04-19 01:10:37 +04:00
/* We're expired, set an error code
for upper layer */
ret = NT_STATUS_SYNCHRONIZATION_REQUIRED ;
2006-12-12 17:52:13 +03:00
}
2007-05-07 01:04:30 +04:00
goto done ;
}
/* Was a negative cache hit */
/* Ignore the negative cache when offline */
if ( IS_DOMAIN_OFFLINE ( our_domain ) ) {
DEBUG ( 10 , ( " idmap_cache_map_sid: idmap is offline \n " ) ) ;
goto done ;
}
/* Check for valid or expired cache hits */
2007-04-19 01:10:37 +04:00
if ( t < = now ) {
2007-05-07 01:04:30 +04:00
/* We're expired. Return not mapped */
2007-04-19 01:10:37 +04:00
ret = NT_STATUS_NONE_MAPPED ;
2007-01-14 20:58:24 +03:00
} else {
/* this is not mapped as it was a negative cache hit */
id - > status = ID_UNMAPPED ;
ret = NT_STATUS_OK ;
2006-12-12 17:52:13 +03:00
}
done :
SAFE_FREE ( databuf . dptr ) ;
talloc_free ( sidkey ) ;
return ret ;
}
/* search the cahce for the ID an return a mapping if found *
*
2007-04-19 01:10:37 +04:00
* 4 cases are possible
2006-12-12 17:52:13 +03:00
*
* 1 map found
2007-01-14 20:58:24 +03:00
* in this case id - > status = ID_MAPPED and NT_STATUS_OK is returned
2006-12-12 17:52:13 +03:00
* 2 map not found
2007-01-14 20:58:24 +03:00
* in this case id - > status = ID_UNKNOWN and NT_STATUS_NONE_MAPPED is returned
2006-12-12 17:52:13 +03:00
* 3 negative cache found
2007-01-14 20:58:24 +03:00
* in this case id - > status = ID_UNMAPPED and NT_STATUS_OK is returned
2007-04-19 01:10:37 +04:00
* 4 map found but timer expired
* in this case id - > status = ID_EXPIRED and NT_STATUS_SYNCHRONIZATION_REQUIRED
* is returned . In this case revalidation of the cache is needed .
2006-12-12 17:52:13 +03:00
*/
NTSTATUS idmap_cache_map_id ( struct idmap_cache_ctx * cache , struct id_map * id )
{
NTSTATUS ret ;
2007-03-27 14:50:32 +04:00
TDB_DATA databuf ;
2007-05-07 01:04:30 +04:00
time_t t ;
2006-12-12 17:52:13 +03:00
char * idkey ;
char * endptr ;
2007-05-07 01:04:30 +04:00
struct winbindd_domain * our_domain = find_our_domain ( ) ;
time_t now = time ( NULL ) ;
2006-12-12 17:52:13 +03:00
2007-04-19 01:10:37 +04:00
/* make sure it is marked as unknown by default */
2007-01-14 20:58:24 +03:00
id - > status = ID_UNKNOWN ;
2006-12-12 17:52:13 +03:00
ret = idmap_cache_build_idkey ( cache , & idkey , id ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) return ret ;
2007-03-27 14:50:32 +04:00
databuf = tdb_fetch_bystring ( cache - > tdb , idkey ) ;
2006-12-12 17:52:13 +03:00
if ( databuf . dptr = = NULL ) {
DEBUG ( 10 , ( " Cache entry with key = %s couldn't be found \n " , idkey ) ) ;
2007-08-31 13:39:11 +04:00
ret = NT_STATUS_NONE_MAPPED ;
goto done ;
2006-12-12 17:52:13 +03:00
}
2007-03-29 13:35:51 +04:00
t = strtol ( ( const char * ) databuf . dptr , & endptr , 10 ) ;
2006-12-12 17:52:13 +03:00
if ( ( endptr = = NULL ) | | ( * endptr ! = ' / ' ) ) {
2007-03-29 13:35:51 +04:00
DEBUG ( 2 , ( " Invalid gencache data format: %s \n " , ( const char * ) databuf . dptr ) ) ;
2006-12-12 17:52:13 +03:00
/* remove the entry */
2007-03-27 14:50:32 +04:00
tdb_delete_bystring ( cache - > tdb , idkey ) ;
2006-12-12 17:52:13 +03:00
ret = NT_STATUS_NONE_MAPPED ;
goto done ;
}
/* check it is not negative */
if ( strcmp ( " IDMAP/NEGATIVE " , endptr + 1 ) ! = 0 ) {
DEBUG ( 10 , ( " Returning %s cache entry: key = %s, value = %s, "
2007-04-19 01:10:37 +04:00
" timeout = %s " , t > now ? " valid " :
2006-12-12 17:52:13 +03:00
" expired " , idkey , endptr + 1 , ctime ( & t ) ) ) ;
/* this call if successful will also mark the entry as mapped */
ret = idmap_cache_fill_map ( id , endptr + 1 ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
/* if not valid form delete the entry */
2007-03-27 14:50:32 +04:00
tdb_delete_bystring ( cache - > tdb , idkey ) ;
2006-12-12 17:52:13 +03:00
ret = NT_STATUS_NONE_MAPPED ;
goto done ;
}
2007-04-19 01:10:37 +04:00
/* here ret == NT_STATUS_OK and id->mapped = ID_MAPPED */
if ( t < = now ) {
2007-05-07 01:04:30 +04:00
/* If we've been told to be offline - stay in
that state . . . */
if ( IS_DOMAIN_OFFLINE ( our_domain ) ) {
DEBUG ( 10 , ( " idmap_cache_map_sid: idmap is offline \n " ) ) ;
goto done ;
}
2007-04-19 01:10:37 +04:00
/* We're expired, set an error code
for upper layer */
ret = NT_STATUS_SYNCHRONIZATION_REQUIRED ;
2006-12-12 17:52:13 +03:00
}
2007-05-07 01:04:30 +04:00
goto done ;
}
/* Was a negative cache hit */
/* Ignore the negative cache when offline */
if ( IS_DOMAIN_OFFLINE ( our_domain ) ) {
DEBUG ( 10 , ( " idmap_cache_map_sid: idmap is offline \n " ) ) ;
ret = NT_STATUS_NONE_MAPPED ;
goto done ;
}
/* Process the negative cache hit */
2007-04-19 01:10:37 +04:00
if ( t < = now ) {
2007-05-07 01:04:30 +04:00
/* We're expired. Return not mapped */
2007-04-19 01:10:37 +04:00
ret = NT_STATUS_NONE_MAPPED ;
2007-01-14 20:58:24 +03:00
} else {
2007-05-07 01:04:30 +04:00
/* this is not mapped is it was a negative cache hit */
2007-01-14 20:58:24 +03:00
id - > status = ID_UNMAPPED ;
ret = NT_STATUS_OK ;
2006-12-12 17:52:13 +03:00
}
2007-05-07 01:04:30 +04:00
2006-12-12 17:52:13 +03:00
done :
SAFE_FREE ( databuf . dptr ) ;
talloc_free ( idkey ) ;
return ret ;
}