2004-05-03 14:58:08 +00:00
/*
Unix SMB / CIFS implementation .
interface functions for the sam database
Copyright ( C ) Andrew Tridgell 2004
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
the Free Software Foundation ; either version 2 of the License , or
( 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
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2004-05-06 12:42:42 +00:00
struct samdb_context {
struct ldb_context * ldb ;
} ;
2004-05-03 14:58:08 +00:00
2004-05-06 09:55:05 +00:00
/*
this is used to catch debug messages from ldb
*/
void samdb_debug ( void * context , enum ldb_debug_level level , const char * fmt , va_list ap )
{
char * s = NULL ;
if ( DEBUGLEVEL < 4 & & level > LDB_DEBUG_WARNING ) {
return ;
}
vasprintf ( & s , fmt , ap ) ;
if ( ! s ) return ;
DEBUG ( level , ( " samdb: %s \n " , s ) ) ;
free ( s ) ;
}
2004-05-03 14:58:08 +00:00
/*
connect to the SAM database
2004-05-06 12:42:42 +00:00
return an opaque context pointer on success , or NULL on failure
2004-05-03 14:58:08 +00:00
*/
2004-05-06 12:42:42 +00:00
void * samdb_connect ( void )
2004-05-03 14:58:08 +00:00
{
2004-05-06 12:42:42 +00:00
struct samdb_context * ctx ;
/*
the way that unix fcntl locking works forces us to have a
static ldb handle here rather than a much more sensible
approach of having the ldb handle as part of the
samr_Connect ( ) pipe state . Otherwise we would try to open
the ldb more than once , and tdb would rightly refuse the
second open due to the broken nature of unix locking .
*/
static struct ldb_context * static_sam_db ;
if ( static_sam_db = = NULL ) {
static_sam_db = ldb_connect ( lp_sam_url ( ) , 0 , NULL ) ;
if ( static_sam_db = = NULL ) {
return NULL ;
}
2004-05-03 14:58:08 +00:00
}
2004-05-06 12:42:42 +00:00
ldb_set_debug ( static_sam_db , samdb_debug , NULL ) ;
ctx = malloc_p ( struct samdb_context ) ;
if ( ! ctx ) {
errno = ENOMEM ;
return NULL ;
2004-05-03 14:58:08 +00:00
}
2004-05-06 12:42:42 +00:00
ctx - > ldb = static_sam_db ;
return ctx ;
}
2004-05-06 09:55:05 +00:00
2004-05-06 12:42:42 +00:00
/* close a connection to the sam */
void samdb_close ( void * ctx )
{
struct samdb_context * sam_ctx = ctx ;
/* we don't actually close due to broken posix locking semantics */
sam_ctx - > ldb = NULL ;
free ( sam_ctx ) ;
2004-05-03 14:58:08 +00:00
}
2004-05-06 07:32:51 +00:00
/*
a alloc function for ldb
*/
static void * samdb_alloc ( void * context , void * ptr , size_t size )
{
return talloc_realloc ( ( TALLOC_CTX * ) context , ptr , size ) ;
}
2004-05-03 14:58:08 +00:00
/*
search the sam for the specified attributes - va_list varient
*/
2004-05-06 12:42:42 +00:00
int samdb_search_v ( void * ctx ,
TALLOC_CTX * mem_ctx ,
2004-05-08 00:02:31 +00:00
const char * basedn ,
2004-05-06 07:32:51 +00:00
struct ldb_message * * * res ,
2004-05-08 00:02:31 +00:00
const char * const * attrs ,
2004-05-03 14:58:08 +00:00
const char * format ,
va_list ap )
{
2004-05-06 12:42:42 +00:00
struct samdb_context * sam_ctx = ctx ;
2004-05-03 14:58:08 +00:00
char * expr = NULL ;
int count ;
vasprintf ( & expr , format , ap ) ;
if ( expr = = NULL ) {
return - 1 ;
}
2004-05-06 12:42:42 +00:00
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
2004-05-06 07:32:51 +00:00
2004-05-08 00:02:31 +00:00
count = ldb_search ( sam_ctx - > ldb , basedn , LDB_SCOPE_SUBTREE , expr , attrs , res ) ;
2004-05-03 14:58:08 +00:00
2004-05-09 00:42:35 +00:00
DEBUG ( 4 , ( " samdb_search_v: %s %s -> %d \n " , basedn ? basedn : " NULL " , expr , count ) ) ;
2004-05-03 14:58:08 +00:00
free ( expr ) ;
return count ;
}
/*
search the sam for the specified attributes - varargs varient
*/
2004-05-06 12:42:42 +00:00
int samdb_search ( void * ctx ,
TALLOC_CTX * mem_ctx ,
2004-05-08 00:02:31 +00:00
const char * basedn ,
2004-05-06 07:32:51 +00:00
struct ldb_message * * * res ,
2004-05-08 00:02:31 +00:00
const char * const * attrs ,
2004-05-03 14:58:08 +00:00
const char * format , . . . )
{
va_list ap ;
int count ;
va_start ( ap , format ) ;
2004-05-08 00:02:31 +00:00
count = samdb_search_v ( ctx , mem_ctx , basedn , res , attrs , format , ap ) ;
2004-05-03 14:58:08 +00:00
va_end ( ap ) ;
return count ;
}
2004-05-04 07:53:06 +00:00
/*
free up a search result
*/
2004-05-06 12:42:42 +00:00
int samdb_search_free ( void * ctx ,
TALLOC_CTX * mem_ctx , struct ldb_message * * res )
2004-05-04 07:53:06 +00:00
{
2004-05-06 12:42:42 +00:00
struct samdb_context * sam_ctx = ctx ;
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_search_free ( sam_ctx - > ldb , res ) ;
2004-05-04 07:53:06 +00:00
}
2004-05-03 14:58:08 +00:00
/*
search the sam for a single string attribute in exactly 1 record
*/
2004-05-08 00:02:31 +00:00
const char * samdb_search_string_v ( void * ctx ,
TALLOC_CTX * mem_ctx ,
const char * basedn ,
const char * attr_name ,
const char * format , va_list ap )
2004-05-03 14:58:08 +00:00
{
int count ;
2004-05-08 00:02:31 +00:00
const char * const attrs [ 2 ] = { attr_name , NULL } ;
2004-05-03 14:58:08 +00:00
struct ldb_message * * res = NULL ;
2004-05-08 00:02:31 +00:00
count = samdb_search_v ( ctx , mem_ctx , basedn , & res , attrs , format , ap ) ;
if ( count > 1 ) {
DEBUG ( 1 , ( " samdb: search for %s %s not single valued (count=%d) \n " ,
attr_name , format , count ) ) ;
2004-05-03 14:58:08 +00:00
}
2004-05-08 00:02:31 +00:00
if ( count ! = 1 ) {
2004-05-06 12:42:42 +00:00
samdb_search_free ( ctx , mem_ctx , res ) ;
2004-05-03 14:58:08 +00:00
return NULL ;
}
2004-05-08 00:02:31 +00:00
return samdb_result_string ( res [ 0 ] , attr_name , NULL ) ;
}
/*
search the sam for a single string attribute in exactly 1 record
*/
const char * samdb_search_string ( void * ctx ,
TALLOC_CTX * mem_ctx ,
const char * basedn ,
const char * attr_name ,
const char * format , . . . )
{
va_list ap ;
const char * str ;
2004-05-03 14:58:08 +00:00
2004-05-08 00:02:31 +00:00
va_start ( ap , format ) ;
str = samdb_search_string_v ( ctx , mem_ctx , basedn , attr_name , format , ap ) ;
va_end ( ap ) ;
2004-05-03 14:58:08 +00:00
return str ;
}
2004-05-09 12:32:25 +00:00
/*
search the sam for a single integer attribute in exactly 1 record
*/
uint_t samdb_search_uint ( void * ctx ,
TALLOC_CTX * mem_ctx ,
uint_t default_value ,
const char * basedn ,
const char * attr_name ,
const char * format , . . . )
{
va_list ap ;
int count ;
struct ldb_message * * res ;
const char * const attrs [ 2 ] = { attr_name , NULL } ;
va_start ( ap , format ) ;
count = samdb_search_v ( ctx , mem_ctx , basedn , & res , attrs , format , ap ) ;
va_end ( ap ) ;
if ( count ! = 1 ) {
return default_value ;
}
return samdb_result_uint ( res [ 0 ] , attr_name , default_value ) ;
}
2004-05-03 14:58:08 +00:00
/*
search the sam for multipe records each giving a single string attribute
return the number of matches , or - 1 on error
*/
2004-05-06 12:42:42 +00:00
int samdb_search_string_multiple ( void * ctx ,
TALLOC_CTX * mem_ctx ,
2004-05-08 00:02:31 +00:00
const char * basedn ,
const char * * * strs ,
2004-05-03 14:58:08 +00:00
const char * attr_name ,
const char * format , . . . )
{
va_list ap ;
int count , i ;
2004-05-08 00:02:31 +00:00
const char * const attrs [ 2 ] = { attr_name , NULL } ;
2004-05-03 14:58:08 +00:00
struct ldb_message * * res = NULL ;
va_start ( ap , format ) ;
2004-05-08 00:02:31 +00:00
count = samdb_search_v ( ctx , mem_ctx , basedn , & res , attrs , format , ap ) ;
2004-05-03 14:58:08 +00:00
va_end ( ap ) ;
if ( count < = 0 ) {
return count ;
}
/* make sure its single valued */
for ( i = 0 ; i < count ; i + + ) {
2004-05-08 00:02:31 +00:00
if ( res [ i ] - > num_elements ! = 1 ) {
2004-05-03 14:58:08 +00:00
DEBUG ( 1 , ( " samdb: search for %s %s not single valued \n " ,
attr_name , format ) ) ;
2004-05-06 12:42:42 +00:00
samdb_search_free ( ctx , mem_ctx , res ) ;
2004-05-03 14:58:08 +00:00
return - 1 ;
}
}
2004-05-08 00:02:31 +00:00
* strs = talloc_array_p ( mem_ctx , const char * , count + 1 ) ;
2004-05-03 14:58:08 +00:00
if ( ! * strs ) {
2004-05-06 12:42:42 +00:00
samdb_search_free ( ctx , mem_ctx , res ) ;
2004-05-03 14:58:08 +00:00
return - 1 ;
}
for ( i = 0 ; i < count ; i + + ) {
2004-05-08 00:02:31 +00:00
( * strs ) [ i ] = samdb_result_string ( res [ i ] , attr_name , NULL ) ;
2004-05-03 14:58:08 +00:00
}
( * strs ) [ count ] = NULL ;
return count ;
}
2004-05-04 07:53:06 +00:00
/*
pull a uint from a result set .
*/
uint_t samdb_result_uint ( struct ldb_message * msg , const char * attr , uint_t default_value )
{
return ldb_msg_find_uint ( msg , attr , default_value ) ;
}
2004-05-08 00:02:31 +00:00
/*
pull a string from a result set .
*/
2004-05-08 14:42:45 +00:00
const char * samdb_result_string ( struct ldb_message * msg , const char * attr ,
const char * default_value )
2004-05-08 00:02:31 +00:00
{
return ldb_msg_find_string ( msg , attr , default_value ) ;
}
2004-05-08 14:42:45 +00:00
/*
pull a rid from a objectSid in a result set .
*/
uint32 samdb_result_rid_from_sid ( TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr , uint32 default_value )
{
struct dom_sid * sid ;
const char * sidstr = ldb_msg_find_string ( msg , attr , NULL ) ;
if ( ! sidstr ) return default_value ;
sid = dom_sid_parse_talloc ( mem_ctx , sidstr ) ;
if ( ! sid ) return default_value ;
return sid - > sub_auths [ sid - > num_auths - 1 ] ;
}
/*
2004-05-09 09:39:47 +00:00
pull a NTTIME in a result set .
2004-05-08 14:42:45 +00:00
*/
2004-05-09 09:39:47 +00:00
NTTIME samdb_result_nttime ( struct ldb_message * msg , const char * attr , const char * default_value )
2004-05-08 14:42:45 +00:00
{
const char * str = ldb_msg_find_string ( msg , attr , default_value ) ;
return nttime_from_string ( str ) ;
}
2004-05-09 09:39:47 +00:00
/*
pull a double ( really a large integer ) from a result set .
*/
double samdb_result_double ( struct ldb_message * msg , const char * attr , double default_value )
{
return ldb_msg_find_double ( msg , attr , default_value ) ;
}
/*
construct the allow_pwd_change field from the PwdLastSet attribute and the
domain password settings
*/
NTTIME samdb_result_allow_pwd_change ( void * ctx , TALLOC_CTX * mem_ctx ,
const char * domain_dn , struct ldb_message * msg , const char * attr )
{
double attr_time = samdb_result_double ( msg , attr , 0 ) ;
if ( attr_time > 0 ) {
const char * minPwdAge = samdb_search_string ( ctx , mem_ctx , NULL , " minPwdAge " ,
" dn=%s " , domain_dn ) ;
if ( minPwdAge ) {
/* yes, this is a -= not a += as minPwdAge is stored as the negative
of the number of 100 - nano - seconds */
attr_time - = strtod ( minPwdAge , NULL ) ;
}
}
return nttime_from_double_nt ( attr_time ) ;
}
/*
construct the force_pwd_change field from the PwdLastSet attribute and the
domain password settings
*/
NTTIME samdb_result_force_pwd_change ( void * ctx , TALLOC_CTX * mem_ctx ,
const char * domain_dn , struct ldb_message * msg , const char * attr )
{
double attr_time = samdb_result_double ( msg , attr , 0 ) ;
if ( attr_time > 0 ) {
const char * maxPwdAge = samdb_search_string ( ctx , mem_ctx , NULL , " maxPwdAge " ,
" dn=%s " , domain_dn ) ;
if ( ! maxPwdAge | | strcmp ( maxPwdAge , " 0 " ) = = 0 ) {
attr_time = 0 ;
} else {
attr_time - = strtod ( maxPwdAge , NULL ) ;
}
}
return nttime_from_double_nt ( attr_time ) ;
}
2004-05-10 11:23:50 +00:00
/*
pull a samr_Hash structutre from a result set .
*/
struct samr_Hash samdb_result_hash ( struct ldb_message * msg , const char * attr )
{
struct samr_Hash hash ;
const struct ldb_val * val = ldb_msg_find_ldb_val ( msg , attr ) ;
ZERO_STRUCT ( hash ) ;
if ( val ) {
memcpy ( hash . hash , val - > data , MIN ( val - > length , 16 ) ) ;
}
return hash ;
}
/*
pull an array of samr_Hash structutres from a result set .
*/
uint_t samdb_result_hashes ( TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr , struct samr_Hash * * hashes )
{
uint_t count = 0 ;
const struct ldb_val * val = ldb_msg_find_ldb_val ( msg , attr ) ;
int i ;
* hashes = NULL ;
if ( ! val ) {
return 0 ;
}
count = val - > length / 16 ;
if ( count = = 0 ) {
return 0 ;
}
* hashes = talloc_array_p ( mem_ctx , struct samr_Hash , count ) ;
if ( ! * hashes ) {
return 0 ;
}
for ( i = 0 ; i < count ; i + + ) {
memcpy ( ( * hashes ) [ i ] . hash , ( i * 16 ) + ( char * ) val - > data , 16 ) ;
}
return count ;
}
2004-05-09 09:39:47 +00:00
/*
pull a samr_LogonHours structutre from a result set .
*/
struct samr_LogonHours samdb_result_logon_hours ( TALLOC_CTX * mem_ctx , struct ldb_message * msg , const char * attr )
{
struct samr_LogonHours hours ;
const int units_per_week = 168 ;
const struct ldb_val * val = ldb_msg_find_ldb_val ( msg , attr ) ;
ZERO_STRUCT ( hours ) ;
hours . bitmap = talloc_array_p ( mem_ctx , uint8 , units_per_week ) ;
if ( ! hours . bitmap ) {
return hours ;
}
hours . units_per_week = units_per_week ;
memset ( hours . bitmap , 0xFF , units_per_week ) ;
if ( val ) {
memcpy ( hours . bitmap , val - > data , MIN ( val - > length , units_per_week ) ) ;
}
return hours ;
}
/* mapping between ADS userAccountControl and SAMR acct_flags */
static const struct {
uint32 uf , acb ;
} acct_flags_map [ ] = {
{ UF_ACCOUNTDISABLE , ACB_DISABLED } ,
{ UF_HOMEDIR_REQUIRED , ACB_HOMDIRREQ } ,
{ UF_PASSWD_NOTREQD , ACB_PWNOTREQ } ,
{ UF_TEMP_DUPLICATE_ACCOUNT , ACB_TEMPDUP } ,
{ UF_NORMAL_ACCOUNT , ACB_NORMAL } ,
{ UF_MNS_LOGON_ACCOUNT , ACB_MNS } ,
{ UF_INTERDOMAIN_TRUST_ACCOUNT , ACB_DOMTRUST } ,
{ UF_WORKSTATION_TRUST_ACCOUNT , ACB_WSTRUST } ,
{ UF_SERVER_TRUST_ACCOUNT , ACB_SVRTRUST } ,
{ UF_DONT_EXPIRE_PASSWD , ACB_PWNOEXP } ,
{ UF_LOCKOUT , ACB_AUTOLOCK }
} ;
/*
pull a set of account_flags from a result set .
*/
uint32 samdb_result_acct_flags ( struct ldb_message * msg , const char * attr )
{
uint_t userAccountControl = ldb_msg_find_uint ( msg , attr , 0 ) ;
uint32 i , ret = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( acct_flags_map ) ; i + + ) {
if ( acct_flags_map [ i ] . uf & userAccountControl ) {
ret | = acct_flags_map [ i ] . acb ;
}
}
return ret ;
}
2004-05-08 00:02:31 +00:00
/*
copy from a template record to a message
*/
int samdb_copy_template ( void * ctx , TALLOC_CTX * mem_ctx ,
struct ldb_message * msg , const char * expression )
{
struct ldb_message * * res , * t ;
int ret , i , j ;
/* pull the template record */
ret = samdb_search ( ctx , mem_ctx , NULL , & res , NULL , expression ) ;
if ( ret ! = 1 ) {
DEBUG ( 1 , ( " samdb: ERROR: template '%s' matched %d records \n " ,
expression , ret ) ) ;
return - 1 ;
}
t = res [ 0 ] ;
for ( i = 0 ; i < t - > num_elements ; i + + ) {
struct ldb_message_element * el = & t - > elements [ i ] ;
/* some elements should not be copied from the template */
if ( strcasecmp ( el - > name , " cn " ) = = 0 | |
strcasecmp ( el - > name , " name " ) = = 0 | |
strcasecmp ( el - > name , " sAMAccountName " ) = = 0 ) {
continue ;
}
for ( j = 0 ; j < el - > num_values ; j + + ) {
if ( strcasecmp ( el - > name , " objectClass " ) = = 0 & &
2004-05-10 12:05:54 +00:00
( strcasecmp ( ( char * ) el - > values [ j ] . data , " Template " ) = = 0 | |
strcasecmp ( ( char * ) el - > values [ j ] . data , " userTemplate " ) = = 0 | |
2004-05-09 12:32:25 +00:00
strcasecmp ( ( char * ) el - > values [ j ] . data , " groupTemplate " ) = = 0 ) ) {
2004-05-09 11:21:46 +00:00
continue ;
}
2004-05-08 00:02:31 +00:00
samdb_msg_add_string ( ctx , mem_ctx , msg , el - > name ,
( char * ) el - > values [ j ] . data ) ;
}
}
return 0 ;
}
/*
allocate a new id , attempting to do it atomically
return 0 on failure , the id on success
*/
static NTSTATUS _samdb_allocate_next_id ( void * ctx , TALLOC_CTX * mem_ctx , const char * dn ,
const char * attr , uint32 * id )
{
struct samdb_context * sam_ctx = ctx ;
struct ldb_message msg ;
int ret ;
const char * str ;
struct ldb_val vals [ 2 ] ;
struct ldb_message_element els [ 2 ] ;
str = samdb_search_string ( ctx , mem_ctx , NULL , attr , " dn=%s " , dn ) ;
if ( ! str ) {
DEBUG ( 1 , ( " id not found at %s %s \n " , dn , attr ) ) ;
return NT_STATUS_OBJECT_NAME_INVALID ;
}
* id = strtol ( str , NULL , 0 ) ;
if ( ( * id ) + 1 = = 0 ) {
/* out of IDs ! */
return NT_STATUS_INSUFFICIENT_RESOURCES ;
}
/* we do a delete and add as a single operation. That prevents
a race */
ZERO_STRUCT ( msg ) ;
msg . dn = talloc_strdup ( mem_ctx , dn ) ;
if ( ! msg . dn ) {
return NT_STATUS_NO_MEMORY ;
}
msg . num_elements = 2 ;
msg . elements = els ;
els [ 0 ] . num_values = 1 ;
els [ 0 ] . values = & vals [ 0 ] ;
els [ 0 ] . flags = LDB_FLAG_MOD_DELETE ;
els [ 0 ] . name = talloc_strdup ( mem_ctx , attr ) ;
if ( ! els [ 0 ] . name ) {
return NT_STATUS_NO_MEMORY ;
}
els [ 1 ] . num_values = 1 ;
els [ 1 ] . values = & vals [ 1 ] ;
els [ 1 ] . flags = LDB_FLAG_MOD_ADD ;
els [ 1 ] . name = els [ 0 ] . name ;
vals [ 0 ] . data = talloc_asprintf ( mem_ctx , " %u " , * id ) ;
if ( ! vals [ 0 ] . data ) {
return NT_STATUS_NO_MEMORY ;
}
vals [ 0 ] . length = strlen ( vals [ 0 ] . data ) ;
vals [ 1 ] . data = talloc_asprintf ( mem_ctx , " %u " , ( * id ) + 1 ) ;
if ( ! vals [ 1 ] . data ) {
return NT_STATUS_NO_MEMORY ;
}
vals [ 1 ] . length = strlen ( vals [ 1 ] . data ) ;
ret = ldb_modify ( sam_ctx - > ldb , & msg ) ;
if ( ret ! = 0 ) {
return NT_STATUS_UNEXPECTED_IO_ERROR ;
}
( * id ) + + ;
return NT_STATUS_OK ;
}
/*
allocate a new id , attempting to do it atomically
return 0 on failure , the id on success
*/
NTSTATUS samdb_allocate_next_id ( void * ctx , TALLOC_CTX * mem_ctx , const char * dn , const char * attr ,
uint32 * id )
{
int tries = 10 ;
NTSTATUS status ;
/* we need to try multiple times to cope with two account
creations at the same time */
while ( tries - - ) {
status = _samdb_allocate_next_id ( ctx , mem_ctx , dn , attr , id ) ;
if ( ! NT_STATUS_EQUAL ( NT_STATUS_UNEXPECTED_IO_ERROR , status ) ) {
break ;
}
}
if ( NT_STATUS_EQUAL ( NT_STATUS_UNEXPECTED_IO_ERROR , status ) ) {
DEBUG ( 1 , ( " Failed to increment id %s at %s \n " , attr , dn ) ) ;
}
return status ;
}
/*
add a string element to a message
*/
int samdb_msg_add_string ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , const char * str )
{
struct samdb_context * sam_ctx = ctx ;
char * s = talloc_strdup ( mem_ctx , str ) ;
char * a = talloc_strdup ( mem_ctx , attr_name ) ;
if ( s = = NULL | | a = = NULL ) {
return - 1 ;
}
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_msg_add_string ( sam_ctx - > ldb , msg , a , s ) ;
}
2004-05-10 12:05:54 +00:00
/*
add a delete element operation to a message
*/
int samdb_msg_add_delete ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name )
{
struct samdb_context * sam_ctx = ctx ;
char * a = talloc_strdup ( mem_ctx , attr_name ) ;
if ( a = = NULL ) {
return - 1 ;
}
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_msg_add_empty ( sam_ctx - > ldb , msg , a , LDB_FLAG_MOD_DELETE ) ;
}
2004-05-08 14:42:45 +00:00
/*
add a uint_t element to a message
*/
int samdb_msg_add_uint ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , uint_t v )
{
const char * s = talloc_asprintf ( mem_ctx , " %u " , v ) ;
return samdb_msg_add_string ( ctx , mem_ctx , msg , attr_name , s ) ;
}
2004-05-10 11:23:50 +00:00
/*
add a double element to a message ( actually a large integer )
*/
int samdb_msg_add_double ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , double v )
{
const char * s = talloc_asprintf ( mem_ctx , " %.0f " , v ) ;
return samdb_msg_add_string ( ctx , mem_ctx , msg , attr_name , s ) ;
}
/*
add a samr_Hash element to a message
*/
int samdb_msg_add_hash ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , struct samr_Hash hash )
{
struct samdb_context * sam_ctx = ctx ;
struct ldb_val val ;
val . data = talloc ( mem_ctx , 16 ) ;
val . length = 16 ;
if ( ! val . data ) {
return - 1 ;
}
memcpy ( val . data , hash . hash , 16 ) ;
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_msg_add_value ( sam_ctx - > ldb , msg , attr_name , & val ) ;
}
/*
add a samr_Hash array to a message
*/
int samdb_msg_add_hashes ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , struct samr_Hash * hashes , uint_t count )
{
struct samdb_context * sam_ctx = ctx ;
struct ldb_val val ;
int i ;
val . data = talloc ( mem_ctx , count * 16 ) ;
val . length = count * 16 ;
if ( ! val . data ) {
return - 1 ;
}
for ( i = 0 ; i < count ; i + + ) {
memcpy ( i * 16 + ( char * ) val . data , hashes [ i ] . hash , 16 ) ;
}
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_msg_add_value ( sam_ctx - > ldb , msg , attr_name , & val ) ;
}
2004-05-09 09:39:47 +00:00
/*
add a acct_flags element to a message
*/
int samdb_msg_add_acct_flags ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , uint32 v )
{
uint_t i , flags = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( acct_flags_map ) ; i + + ) {
if ( acct_flags_map [ i ] . acb & v ) {
flags | = acct_flags_map [ i ] . uf ;
}
}
return samdb_msg_add_uint ( ctx , mem_ctx , msg , attr_name , flags ) ;
}
/*
add a logon_hours element to a message
*/
int samdb_msg_add_logon_hours ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , struct samr_LogonHours hours )
{
struct samdb_context * sam_ctx = ctx ;
struct ldb_val val ;
val . length = hours . units_per_week / 8 ;
val . data = hours . bitmap ;
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_msg_add_value ( sam_ctx - > ldb , msg , attr_name , & val ) ;
}
2004-05-08 00:02:31 +00:00
/*
set a string element in a message
*/
int samdb_msg_set_string ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , const char * str )
{
struct samdb_context * sam_ctx = ctx ;
struct ldb_message_element * el ;
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
el = ldb_msg_find_element ( msg , attr_name ) ;
if ( el ) {
el - > num_values = 0 ;
}
return samdb_msg_add_string ( ctx , mem_ctx , msg , attr_name , str ) ;
}
/*
set a ldaptime element in a message
*/
int samdb_msg_set_ldaptime ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg ,
const char * attr_name , time_t t )
{
char * str = ldap_timestring ( mem_ctx , t ) ;
if ( ! str ) {
return - 1 ;
}
return samdb_msg_set_string ( ctx , mem_ctx , msg , attr_name , str ) ;
}
/*
add a record
*/
int samdb_add ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg )
{
struct samdb_context * sam_ctx = ctx ;
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_add ( sam_ctx - > ldb , msg ) ;
}
/*
delete a record
*/
int samdb_delete ( void * ctx , TALLOC_CTX * mem_ctx , const char * dn )
{
struct samdb_context * sam_ctx = ctx ;
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_delete ( sam_ctx - > ldb , dn ) ;
}
2004-05-08 14:42:45 +00:00
/*
modify a record
*/
int samdb_modify ( void * ctx , TALLOC_CTX * mem_ctx , struct ldb_message * msg )
{
struct samdb_context * sam_ctx = ctx ;
ldb_set_alloc ( sam_ctx - > ldb , samdb_alloc , mem_ctx ) ;
return ldb_modify ( sam_ctx - > ldb , msg ) ;
}
2004-05-10 11:23:50 +00:00
/*
check that a password is sufficiently complex
*/
static BOOL samdb_password_complexity_ok ( const char * pass )
{
return check_password_quality ( pass ) ;
}
/*
set the user password using plaintext , obeying any user or domain
password restrictions
*/
NTSTATUS samdb_set_password ( void * ctx , TALLOC_CTX * mem_ctx ,
const char * user_dn , const char * domain_dn ,
struct ldb_message * mod , const char * new_pass )
{
const char * const user_attrs [ ] = { " userAccountControl " , " lmPwdHistory " ,
" ntPwdHistory " , " unicodePwd " ,
" lmPwdHash " , " ntPwdHash " , " badPwdCount " ,
NULL } ;
const char * const domain_attrs [ ] = { " pwdProperties " , " pwdHistoryLength " ,
" maxPwdAge " , " minPwdAge " ,
" minPwdLength " , " pwdLastSet " , NULL } ;
const char * unicodePwd ;
double minPwdAge , pwdLastSet ;
uint_t minPwdLength , pwdProperties , pwdHistoryLength ;
uint_t userAccountControl , badPwdCount ;
struct samr_Hash * lmPwdHistory , * ntPwdHistory , lmPwdHash , ntPwdHash ;
struct samr_Hash * new_lmPwdHistory , * new_ntPwdHistory ;
struct samr_Hash lmNewHash , ntNewHash ;
uint_t lmPwdHistory_len , ntPwdHistory_len ;
struct ldb_message * * res ;
int count ;
time_t now = time ( NULL ) ;
NTTIME now_nt ;
double now_double ;
int i ;
2004-05-10 12:05:54 +00:00
BOOL lm_hash_ok ;
2004-05-10 11:23:50 +00:00
/* we need to know the time to compute password age */
unix_to_nt_time ( & now_nt , now ) ;
now_double = nttime_to_double_nt ( now_nt ) ;
/* pull all the user parameters */
count = samdb_search ( ctx , mem_ctx , NULL , & res , user_attrs , " dn=%s " , user_dn ) ;
if ( count ! = 1 ) {
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
unicodePwd = samdb_result_string ( res [ 0 ] , " unicodePwd " , NULL ) ;
userAccountControl = samdb_result_uint ( res [ 0 ] , " userAccountControl " , 0 ) ;
badPwdCount = samdb_result_uint ( res [ 0 ] , " badPwdCount " , 0 ) ;
lmPwdHistory_len = samdb_result_hashes ( mem_ctx , res [ 0 ] ,
" lmPwdHistory " , & lmPwdHistory ) ;
ntPwdHistory_len = samdb_result_hashes ( mem_ctx , res [ 0 ] ,
" ntPwdHistory " , & ntPwdHistory ) ;
lmPwdHash = samdb_result_hash ( res [ 0 ] , " lmPwdHash " ) ;
ntPwdHash = samdb_result_hash ( res [ 0 ] , " ntPwdHash " ) ;
pwdLastSet = samdb_result_double ( res [ 0 ] , " pwdLastSet " , 0 ) ;
/* pull the domain parameters */
count = samdb_search ( ctx , mem_ctx , NULL , & res , domain_attrs , " dn=%s " , domain_dn ) ;
if ( count ! = 1 ) {
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
pwdProperties = samdb_result_uint ( res [ 0 ] , " pwdProperties " , 0 ) ;
pwdHistoryLength = samdb_result_uint ( res [ 0 ] , " pwdHistoryLength " , 0 ) ;
minPwdLength = samdb_result_uint ( res [ 0 ] , " minPwdLength " , 0 ) ;
minPwdAge = samdb_result_double ( res [ 0 ] , " minPwdAge " , 0 ) ;
/* are all password changes disallowed? */
if ( pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
/* can this user change password? */
if ( userAccountControl & UF_PASSWD_CANT_CHANGE ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
/* check the various password restrictions */
if ( minPwdLength > str_charnum ( new_pass ) ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
/* yes, this is a minus. The ages are in negative 100nsec units! */
if ( pwdLastSet - minPwdAge > now_double ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
/* possibly check password complexity */
if ( pwdProperties & DOMAIN_PASSWORD_COMPLEX & &
! samdb_password_complexity_ok ( new_pass ) ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
/* compute the new nt and lm hashes */
2004-05-10 12:05:54 +00:00
lm_hash_ok = E_deshash ( new_pass , lmNewHash . hash ) ;
2004-05-10 11:23:50 +00:00
E_md4hash ( new_pass , ntNewHash . hash ) ;
/* check the immediately past password */
2004-05-10 12:05:54 +00:00
if ( pwdHistoryLength > 0 ) {
if ( lm_hash_ok & & memcmp ( lmNewHash . hash , lmPwdHash . hash , 16 ) = = 0 ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
if ( memcmp ( ntNewHash . hash , ntPwdHash . hash , 16 ) = = 0 ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
2004-05-10 11:23:50 +00:00
}
/* check the password history */
lmPwdHistory_len = MIN ( lmPwdHistory_len , pwdHistoryLength ) ;
ntPwdHistory_len = MIN ( ntPwdHistory_len , pwdHistoryLength ) ;
if ( pwdHistoryLength > 0 ) {
2004-05-10 12:05:54 +00:00
if ( unicodePwd & & strcmp ( unicodePwd , new_pass ) = = 0 ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
if ( lm_hash_ok & & memcmp ( lmNewHash . hash , lmPwdHash . hash , 16 ) = = 0 ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
if ( memcmp ( ntNewHash . hash , ntPwdHash . hash , 16 ) = = 0 ) {
2004-05-10 11:23:50 +00:00
return NT_STATUS_PASSWORD_RESTRICTION ;
}
}
2004-05-10 12:05:54 +00:00
for ( i = 0 ; lm_hash_ok & & i < lmPwdHistory_len ; i + + ) {
2004-05-10 11:23:50 +00:00
if ( memcmp ( lmNewHash . hash , lmPwdHistory [ i ] . hash , 16 ) = = 0 ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
}
for ( i = 0 ; i < ntPwdHistory_len ; i + + ) {
if ( memcmp ( ntNewHash . hash , ntPwdHistory [ i ] . hash , 16 ) = = 0 ) {
return NT_STATUS_PASSWORD_RESTRICTION ;
}
}
# define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
/* the password is acceptable. Start forming the new fields */
2004-05-10 12:05:54 +00:00
if ( lm_hash_ok ) {
CHECK_RET ( samdb_msg_add_hash ( ctx , mem_ctx , mod , " lmPwdHash " , lmNewHash ) ) ;
} else {
CHECK_RET ( samdb_msg_add_delete ( ctx , mem_ctx , mod , " lmPwdHash " ) ) ;
}
2004-05-10 11:23:50 +00:00
CHECK_RET ( samdb_msg_add_hash ( ctx , mem_ctx , mod , " ntPwdHash " , ntNewHash ) ) ;
if ( ( pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT ) & &
( userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED ) ) {
CHECK_RET ( samdb_msg_add_string ( ctx , mem_ctx , mod ,
" unicodePwd " , new_pass ) ) ;
2004-05-10 12:05:54 +00:00
} else {
CHECK_RET ( samdb_msg_add_delete ( ctx , mem_ctx , mod , " unicodePwd " ) ) ;
2004-05-10 11:23:50 +00:00
}
CHECK_RET ( samdb_msg_add_double ( ctx , mem_ctx , mod , " pwdLastSet " , now_double ) ) ;
2004-05-10 12:05:54 +00:00
if ( pwdHistoryLength = = 0 ) {
CHECK_RET ( samdb_msg_add_delete ( ctx , mem_ctx , mod , " lmPwdHistory " ) ) ;
CHECK_RET ( samdb_msg_add_delete ( ctx , mem_ctx , mod , " ntPwdHistory " ) ) ;
return NT_STATUS_OK ;
}
/* store the password history */
new_lmPwdHistory = talloc_array_p ( mem_ctx , struct samr_Hash ,
pwdHistoryLength ) ;
if ( ! new_lmPwdHistory ) {
return NT_STATUS_NO_MEMORY ;
}
new_ntPwdHistory = talloc_array_p ( mem_ctx , struct samr_Hash ,
pwdHistoryLength ) ;
if ( ! new_ntPwdHistory ) {
return NT_STATUS_NO_MEMORY ;
}
for ( i = 0 ; i < MIN ( pwdHistoryLength - 1 , lmPwdHistory_len ) ; i + + ) {
new_lmPwdHistory [ i + 1 ] = lmPwdHistory [ i ] ;
}
for ( i = 0 ; i < MIN ( pwdHistoryLength - 1 , ntPwdHistory_len ) ; i + + ) {
new_ntPwdHistory [ i + 1 ] = ntPwdHistory [ i ] ;
}
new_lmPwdHistory [ 0 ] = lmNewHash ;
new_ntPwdHistory [ 0 ] = ntNewHash ;
CHECK_RET ( samdb_msg_add_hashes ( ctx , mem_ctx , mod ,
" lmPwdHistory " ,
new_lmPwdHistory ,
MIN ( pwdHistoryLength , lmPwdHistory_len + 1 ) ) ) ;
CHECK_RET ( samdb_msg_add_hashes ( ctx , mem_ctx , mod ,
" ntPwdHistory " ,
new_ntPwdHistory ,
MIN ( pwdHistoryLength , ntPwdHistory_len + 1 ) ) ) ;
2004-05-10 11:23:50 +00:00
return NT_STATUS_OK ;
}