2005-12-30 08:40:16 +00:00
/*
ldb database module
2006-06-02 02:54:24 +00:00
Copyright ( C ) Simo Sorce 2004 - 2006
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2005 - 2006
2005-12-30 08:40:16 +00:00
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 .
*/
/*
* Name : ldb
*
* Component : ldb password_hash module
*
2006-01-05 06:53:39 +00:00
* Description : correctly update hash values based on changes to sambaPassword and friends
2005-12-30 08:40:16 +00:00
*
* Author : Andrew Bartlett
*/
# include "includes.h"
# include "libcli/ldap/ldap.h"
# include "ldb/include/ldb_errors.h"
# include "ldb/include/ldb_private.h"
2006-03-16 00:23:11 +00:00
# include "librpc/gen_ndr/misc.h"
# include "librpc/gen_ndr/samr.h"
2006-03-14 15:03:25 +00:00
# include "libcli/auth/libcli_auth.h"
2006-04-29 17:34:49 +00:00
# include "libcli/security/security.h"
2005-12-30 08:40:16 +00:00
# include "system/kerberos.h"
# include "auth/kerberos/kerberos.h"
# include "system/time.h"
# include "dsdb/samdb/samdb.h"
2006-08-30 11:29:34 +00:00
# include "dsdb/common/flags.h"
2005-12-30 08:40:16 +00:00
# include "hdb.h"
2006-07-28 06:30:03 +00:00
# include "dsdb/samdb/ldb_modules/password_modules.h"
2005-12-30 08:40:16 +00:00
/* If we have decided there is reason to work on this request, then
* setup all the password hash types correctly .
*
2006-01-05 06:53:39 +00:00
* If the administrator doesn ' t want the sambaPassword stored ( set in the
2005-12-30 08:40:16 +00:00
* domain and per - account policies ) then we must strip that out before
* we do the first operation .
*
* Once this is done ( which could update anything at all ) , we
* calculate the password hashes .
*
* This function must not only update the ntPwdHash , lmPwdHash and
* krb5Key fields , it must also atomicly increment the
* msDS - KeyVersionNumber . We should be in a transaction , so all this
* should be quite safe . . .
*
* Finally , if the administrator has requested that a password history
* be maintained , then this should also be written out .
*
*/
2006-07-22 16:56:33 +00:00
struct ph_context {
2006-05-19 21:12:26 +00:00
2006-05-29 23:46:43 +00:00
enum ph_type { PH_ADD , PH_MOD } type ;
enum ph_step { PH_ADD_SEARCH_DOM , PH_ADD_DO_ADD , PH_MOD_DO_REQ , PH_MOD_SEARCH_SELF , PH_MOD_SEARCH_DOM , PH_MOD_DO_MOD } step ;
2006-05-19 21:12:26 +00:00
struct ldb_module * module ;
struct ldb_request * orig_req ;
struct ldb_request * dom_req ;
2006-07-22 16:56:33 +00:00
struct ldb_reply * dom_res ;
2006-05-19 21:12:26 +00:00
struct ldb_request * down_req ;
struct ldb_request * search_req ;
2006-07-22 16:56:33 +00:00
struct ldb_reply * search_res ;
2006-05-19 21:12:26 +00:00
struct ldb_request * mod_req ;
2006-07-03 03:37:55 +00:00
struct dom_sid * domain_sid ;
2006-05-19 21:12:26 +00:00
} ;
struct domain_data {
2006-09-08 00:23:21 +00:00
BOOL store_cleartext ;
2006-05-19 21:12:26 +00:00
uint_t pwdProperties ;
uint_t pwdHistoryLength ;
2006-08-31 08:17:09 +00:00
char * dns_domain ;
2006-05-19 21:12:26 +00:00
char * realm ;
} ;
static int add_password_hashes ( struct ldb_module * module , struct ldb_message * msg , int is_mod )
{
const char * sambaPassword ;
struct samr_Password tmp_hash ;
2006-08-13 08:00:36 +00:00
sambaPassword = ldb_msg_find_attr_as_string ( msg , " sambaPassword " , NULL ) ;
2006-05-19 21:12:26 +00:00
if ( sambaPassword = = NULL ) { /* impossible, what happened ?! */
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( is_mod ) {
if ( ldb_msg_add_empty ( msg , " ntPwdHash " , LDB_FLAG_MOD_REPLACE ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-05-22 15:17:12 +00:00
if ( ldb_msg_add_empty ( msg , " lmPwdHash " , LDB_FLAG_MOD_REPLACE ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-05-19 21:12:26 +00:00
}
2006-05-22 15:17:12 +00:00
/* compute the new nt and lm hashes */
2006-05-19 21:12:26 +00:00
E_md4hash ( sambaPassword , tmp_hash . hash ) ;
if ( samdb_msg_add_hash ( module - > ldb , msg , msg , " ntPwdHash " , & tmp_hash ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( E_deshash ( sambaPassword , tmp_hash . hash ) ) {
if ( samdb_msg_add_hash ( module - > ldb , msg , msg , " lmPwdHash " , & tmp_hash ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
}
return LDB_SUCCESS ;
}
static int add_krb5_keys_from_password ( struct ldb_module * module , struct ldb_message * msg ,
struct smb_krb5_context * smb_krb5_context ,
struct domain_data * domain ,
const char * samAccountName ,
const char * user_principal_name ,
int is_computer )
{
const char * sambaPassword ;
Principal * salt_principal ;
krb5_error_code krb5_ret ;
size_t num_keys ;
Key * keys ;
int i ;
/* Many, many thanks to lukeh@padl.com for this
* algorithm , described in his Nov 10 2004 mail to
* samba - technical @ samba . org */
2006-08-13 08:00:36 +00:00
sambaPassword = ldb_msg_find_attr_as_string ( msg , " sambaPassword " , NULL ) ;
2006-05-19 21:12:26 +00:00
if ( sambaPassword = = NULL ) { /* impossible, what happened ?! */
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( is_computer ) {
/* Determine a salting principal */
char * name = talloc_strdup ( msg , samAccountName ) ;
char * saltbody ;
if ( name = = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_asprintf_errstring ( module - > ldb ,
" password_hash_handle: "
" generation of new kerberos keys failed: %s is a computer without a samAccountName " ,
ldb_dn_linearize ( msg , msg - > dn ) ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( name [ strlen ( name ) - 1 ] = = ' $ ' ) {
name [ strlen ( name ) - 1 ] = ' \0 ' ;
}
2006-08-31 08:17:09 +00:00
saltbody = talloc_asprintf ( msg , " %s.%s " , name , domain - > dns_domain ) ;
2006-05-19 21:12:26 +00:00
krb5_ret = krb5_make_principal ( smb_krb5_context - > krb5_context ,
& salt_principal ,
domain - > realm , " host " ,
saltbody , NULL ) ;
} else if ( user_principal_name ) {
char * p ;
user_principal_name = talloc_strdup ( msg , user_principal_name ) ;
if ( user_principal_name = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
} else {
p = strchr ( user_principal_name , ' @ ' ) ;
if ( p ) {
p [ 0 ] = ' \0 ' ;
}
krb5_ret = krb5_make_principal ( smb_krb5_context - > krb5_context ,
& salt_principal ,
domain - > realm , user_principal_name , NULL ) ;
}
} else {
if ( ! samAccountName ) {
2006-08-13 07:33:57 +00:00
ldb_asprintf_errstring ( module - > ldb ,
" password_hash_handle: "
" generation of new kerberos keys failed: %s has no samAccountName " ,
ldb_dn_linearize ( msg , msg - > dn ) ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
krb5_ret = krb5_make_principal ( smb_krb5_context - > krb5_context ,
& salt_principal ,
domain - > realm , samAccountName ,
NULL ) ;
}
if ( krb5_ret ) {
2006-08-13 07:33:57 +00:00
ldb_asprintf_errstring ( module - > ldb ,
" password_hash_handle: "
" generation of a saltking principal failed: %s " ,
smb_get_krb5_error_message ( smb_krb5_context - > krb5_context , krb5_ret , msg ) ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
/* TODO: We may wish to control the encryption types chosen in future */
krb5_ret = hdb_generate_key_set_password ( smb_krb5_context - > krb5_context ,
salt_principal , sambaPassword , & keys , & num_keys ) ;
krb5_free_principal ( smb_krb5_context - > krb5_context , salt_principal ) ;
if ( krb5_ret ) {
2006-08-13 07:33:57 +00:00
ldb_asprintf_errstring ( module - > ldb ,
" password_hash_handle: "
" generation of new kerberos keys failed: %s " ,
smb_get_krb5_error_message ( smb_krb5_context - > krb5_context , krb5_ret , msg ) ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
/* Walking all the key types generated, transform each
* key into an ASN .1 blob
*/
for ( i = 0 ; i < num_keys ; i + + ) {
unsigned char * buf ;
size_t buf_size ;
size_t len ;
struct ldb_val val ;
int ret ;
2006-06-02 02:54:24 +00:00
if ( keys [ i ] . key . keytype = = ETYPE_ARCFOUR_HMAC_MD5 ) {
2006-05-19 21:12:26 +00:00
/* We might end up doing this below:
* This ensures we get the unicode
* conversion right . This should also
* be fixed in the Heimdal libs */
continue ;
}
ASN1_MALLOC_ENCODE ( Key , buf , buf_size , & keys [ i ] , & len , krb5_ret ) ;
if ( krb5_ret ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
val . data = talloc_memdup ( msg , buf , len ) ;
val . length = len ;
free ( buf ) ;
if ( ! val . data | | krb5_ret ) {
hdb_free_keys ( smb_krb5_context - > krb5_context , num_keys , keys ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
ret = ldb_msg_add_value ( msg , " krb5Key " , & val ) ;
if ( ret ! = LDB_SUCCESS ) {
hdb_free_keys ( smb_krb5_context - > krb5_context , num_keys , keys ) ;
return ret ;
}
}
hdb_free_keys ( smb_krb5_context - > krb5_context , num_keys , keys ) ;
return LDB_SUCCESS ;
}
static int add_krb5_keys_from_NThash ( struct ldb_module * module , struct ldb_message * msg ,
struct smb_krb5_context * smb_krb5_context )
{
struct samr_Password * ntPwdHash ;
krb5_error_code krb5_ret ;
unsigned char * buf ;
size_t buf_size ;
size_t len ;
struct ldb_val val ;
Key key ;
key . mkvno = 0 ;
key . salt = NULL ; /* No salt for this enc type */
ntPwdHash = samdb_result_hash ( msg , msg , " ntPwdHash " ) ;
if ( ntPwdHash = = NULL ) { /* what happened ?! */
return LDB_ERR_OPERATIONS_ERROR ;
}
krb5_ret = krb5_keyblock_init ( smb_krb5_context - > krb5_context ,
2006-06-02 02:54:24 +00:00
ETYPE_ARCFOUR_HMAC_MD5 ,
ntPwdHash - > hash , sizeof ( ntPwdHash - > hash ) ,
& key . key ) ;
2006-05-19 21:12:26 +00:00
if ( krb5_ret ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ASN1_MALLOC_ENCODE ( Key , buf , buf_size , & key , & len , krb5_ret ) ;
if ( krb5_ret ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
krb5_free_keyblock_contents ( smb_krb5_context - > krb5_context ,
& key . key ) ;
val . data = talloc_memdup ( msg , buf , len ) ;
val . length = len ;
free ( buf ) ;
if ( ! val . data ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( ldb_msg_add_value ( msg , " krb5Key " , & val ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
return LDB_SUCCESS ;
}
2006-05-22 03:55:01 +00:00
static int set_pwdLastSet ( struct ldb_module * module , struct ldb_message * msg , int is_mod )
2006-05-19 21:12:26 +00:00
{
NTTIME now_nt ;
/* set it as now */
unix_to_nt_time ( & now_nt , time ( NULL ) ) ;
2006-05-22 03:55:01 +00:00
if ( ! is_mod ) {
/* be sure there isn't a 0 value set (eg. coming from the template) */
ldb_msg_remove_attr ( msg , " pwdLastSet " ) ;
/* add */
if ( ldb_msg_add_empty ( msg , " pwdLastSet " , LDB_FLAG_MOD_ADD ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
} else {
/* replace */
if ( ldb_msg_add_empty ( msg , " pwdLastSet " , LDB_FLAG_MOD_REPLACE ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-05-19 21:12:26 +00:00
}
if ( samdb_msg_add_uint64 ( module - > ldb , msg , msg , " pwdLastSet " , now_nt ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
return LDB_SUCCESS ;
}
static int add_keyVersionNumber ( struct ldb_module * module , struct ldb_message * msg , int previous )
{
/* replace or add */
if ( ldb_msg_add_empty ( msg , " msDS-KeyVersionNumber " , LDB_FLAG_MOD_REPLACE ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( samdb_msg_add_uint ( module - > ldb , msg , msg , " msDS-KeyVersionNumber " , previous + 1 ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
return LDB_SUCCESS ;
}
static int setPwdHistory ( struct ldb_module * module , struct ldb_message * msg , struct ldb_message * old_msg , int hlen )
{
struct samr_Password * nt_hash ;
struct samr_Password * lm_hash ;
struct samr_Password * nt_history ;
struct samr_Password * lm_history ;
struct samr_Password * new_nt_history ;
struct samr_Password * new_lm_history ;
int nt_hist_len ;
int lm_hist_len ;
int i ;
nt_hash = samdb_result_hash ( msg , old_msg , " ntPwdHash " ) ;
lm_hash = samdb_result_hash ( msg , old_msg , " lmPwdHash " ) ;
/* if no previous passwords just return */
if ( nt_hash = = NULL & & lm_hash = = NULL ) return LDB_SUCCESS ;
nt_hist_len = samdb_result_hashes ( msg , old_msg , " sambaNTPwdHistory " , & nt_history ) ;
lm_hist_len = samdb_result_hashes ( msg , old_msg , " sambaLMPwdHistory " , & lm_history ) ;
/* We might not have an old NT password */
new_nt_history = talloc_array ( msg , struct samr_Password , hlen ) ;
if ( new_nt_history = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
for ( i = 0 ; i < MIN ( hlen - 1 , nt_hist_len ) ; i + + ) {
new_nt_history [ i + 1 ] = nt_history [ i ] ;
}
nt_hist_len = i + 1 ;
if ( nt_hash ) {
new_nt_history [ 0 ] = * nt_hash ;
} else {
ZERO_STRUCT ( new_nt_history [ 0 ] ) ;
}
if ( ldb_msg_add_empty ( msg , " sambaNTPwdHistory " , LDB_FLAG_MOD_REPLACE ) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( samdb_msg_add_hashes ( msg , msg , " sambaNTPwdHistory " , new_nt_history , nt_hist_len ) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
/* Don't store 'long' passwords in the LM history,
but make sure to ' expire ' one password off the other end */
new_lm_history = talloc_array ( msg , struct samr_Password , hlen ) ;
if ( new_lm_history = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
for ( i = 0 ; i < MIN ( hlen - 1 , lm_hist_len ) ; i + + ) {
new_lm_history [ i + 1 ] = lm_history [ i ] ;
}
lm_hist_len = i + 1 ;
if ( lm_hash ) {
new_lm_history [ 0 ] = * lm_hash ;
} else {
ZERO_STRUCT ( new_lm_history [ 0 ] ) ;
}
if ( ldb_msg_add_empty ( msg , " sambaLMPwdHistory " , LDB_FLAG_MOD_REPLACE ) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( samdb_msg_add_hashes ( msg , msg , " sambaLMPwdHistory " , new_lm_history , lm_hist_len ) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
return LDB_SUCCESS ;
}
2006-07-22 16:56:33 +00:00
static struct ldb_handle * ph_init_handle ( struct ldb_request * req , struct ldb_module * module , enum ph_type type )
2006-05-19 21:12:26 +00:00
{
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
struct ldb_handle * h ;
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
h = talloc_zero ( req , struct ldb_handle ) ;
2006-05-19 21:12:26 +00:00
if ( h = = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( module - > ldb , " Out of Memory " ) ;
2006-05-19 21:12:26 +00:00
return NULL ;
}
h - > module = module ;
2006-07-22 16:56:33 +00:00
ac = talloc_zero ( h , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
if ( ac = = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( module - > ldb , " Out of Memory " ) ;
2006-05-19 21:12:26 +00:00
talloc_free ( h ) ;
return NULL ;
}
h - > private_data = ( void * ) ac ;
h - > state = LDB_ASYNC_INIT ;
h - > status = LDB_SUCCESS ;
ac - > type = type ;
ac - > module = module ;
ac - > orig_req = req ;
return h ;
}
2006-07-22 16:56:33 +00:00
static int get_domain_data_callback ( struct ldb_context * ldb , void * context , struct ldb_reply * ares )
2006-05-19 21:12:26 +00:00
{
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
2006-05-19 21:12:26 +00:00
if ( ! context | | ! ares ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( ldb , " NULL Context or Result in callback " ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( context , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
/* we are interested only in the single reply (base search) we receive here */
if ( ares - > type = = LDB_REPLY_ENTRY ) {
if ( ac - > dom_res ! = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( ldb , " Too many results " ) ;
2006-05-19 21:12:26 +00:00
talloc_free ( ares ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > dom_res = talloc_steal ( ac , ares ) ;
} else {
talloc_free ( ares ) ;
}
return LDB_SUCCESS ;
}
2006-07-22 16:56:33 +00:00
static int build_domain_data_request ( struct ph_context * ac )
2006-05-19 21:12:26 +00:00
{
2006-05-24 12:33:06 +00:00
/* attrs[] is returned from this function in
ac - > dom_req - > op . search . attrs , so it must be static , as
otherwise the compiler can put it on the stack */
2006-08-31 08:17:09 +00:00
static const char * const attrs [ ] = { " pwdProperties " , " pwdHistoryLength " , NULL } ;
2006-05-19 21:12:26 +00:00
char * filter ;
ac - > dom_req = talloc_zero ( ac , struct ldb_request ) ;
if ( ac - > dom_req = = NULL ) {
ldb_debug ( ac - > module - > ldb , LDB_DEBUG_ERROR , " Out of Memory! \n " ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-05-30 00:33:52 +00:00
ac - > dom_req - > operation = LDB_SEARCH ;
2006-08-25 12:59:03 +00:00
ac - > dom_req - > op . search . base = ldb_get_default_basedn ( ac - > module - > ldb ) ;
2006-05-19 21:12:26 +00:00
ac - > dom_req - > op . search . scope = LDB_SCOPE_SUBTREE ;
2006-07-03 03:37:55 +00:00
filter = talloc_asprintf ( ac - > dom_req , " (&(objectSid=%s)(|(objectClass=domain)(objectClass=builtinDomain))) " ,
ldap_encode_ndr_dom_sid ( ac - > dom_req , ac - > domain_sid ) ) ;
2006-05-19 21:12:26 +00:00
if ( filter = = NULL ) {
ldb_debug ( ac - > module - > ldb , LDB_DEBUG_ERROR , " Out of Memory! \n " ) ;
talloc_free ( ac - > dom_req ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > dom_req - > op . search . tree = ldb_parse_tree ( ac - > module - > ldb , filter ) ;
if ( ac - > dom_req - > op . search . tree = = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( ac - > module - > ldb , " Invalid search filter " ) ;
2006-05-19 21:12:26 +00:00
talloc_free ( ac - > dom_req ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > dom_req - > op . search . attrs = attrs ;
ac - > dom_req - > controls = NULL ;
2006-07-22 17:21:59 +00:00
ac - > dom_req - > context = ac ;
ac - > dom_req - > callback = get_domain_data_callback ;
2006-06-04 05:28:13 +00:00
ldb_set_timeout_from_prev_req ( ac - > module - > ldb , ac - > orig_req , ac - > dom_req ) ;
2006-05-19 21:12:26 +00:00
return LDB_SUCCESS ;
}
2006-07-22 16:56:33 +00:00
static struct domain_data * get_domain_data ( struct ldb_module * module , void * ctx , struct ldb_reply * res )
2006-05-19 21:12:26 +00:00
{
struct domain_data * data ;
const char * tmp ;
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
2006-08-31 08:17:09 +00:00
char * p ;
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( ctx , struct ph_context ) ;
2006-07-03 03:37:55 +00:00
data = talloc_zero ( ac , struct domain_data ) ;
2006-05-19 21:12:26 +00:00
if ( data = = NULL ) {
return NULL ;
}
2006-06-15 00:29:11 +00:00
if ( res = = NULL ) {
2006-07-03 03:37:55 +00:00
ldb_debug ( module - > ldb , LDB_DEBUG_ERROR , " Could not find this user's domain: %s! \n " , dom_sid_string ( data , ac - > domain_sid ) ) ;
2006-06-15 00:29:11 +00:00
talloc_free ( data ) ;
return NULL ;
}
2006-09-08 00:23:21 +00:00
data - > pwdProperties = samdb_result_uint ( res - > message , " pwdProperties " , 0 ) ;
data - > store_cleartext = data - > pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT ;
2006-05-19 21:12:26 +00:00
data - > pwdHistoryLength = samdb_result_uint ( res - > message , " pwdHistoryLength " , 0 ) ;
2006-08-31 08:17:09 +00:00
/* For a domain DN, this puts things in dotted notation */
/* For builtin domains, this will give details for the host,
* but that doesn ' t really matter , as it ' s just used for salt
* and kerberos principals , which don ' t exist here */
tmp = ldb_dn_canonical_string ( ctx , res - > message - > dn ) ;
if ( ! tmp ) {
return NULL ;
}
/* But it puts a trailing (or just before 'builtin') / on things, so kill that */
p = strchr ( tmp , ' / ' ) ;
if ( p ) {
p [ 0 ] = ' \0 ' ;
}
2006-05-19 21:12:26 +00:00
if ( tmp ! = NULL ) {
2006-08-31 08:17:09 +00:00
data - > dns_domain = strlower_talloc ( data , tmp ) ;
if ( data - > dns_domain = = NULL ) {
2006-05-19 21:12:26 +00:00
ldb_debug ( module - > ldb , LDB_DEBUG_ERROR , " Out of memory! \n " ) ;
return NULL ;
}
2006-07-03 03:37:55 +00:00
data - > realm = strupper_talloc ( data , tmp ) ;
2006-05-19 21:12:26 +00:00
if ( data - > realm = = NULL ) {
ldb_debug ( module - > ldb , LDB_DEBUG_ERROR , " Out of memory! \n " ) ;
return NULL ;
}
}
return data ;
}
2006-05-29 23:46:43 +00:00
static int password_hash_add ( struct ldb_module * module , struct ldb_request * req )
2006-05-19 21:12:26 +00:00
{
2006-07-22 16:56:33 +00:00
struct ldb_handle * h ;
struct ph_context * ac ;
2006-07-03 03:37:55 +00:00
struct ldb_message_element * sambaAttr ;
struct ldb_message_element * ntAttr ;
struct ldb_message_element * lmAttr ;
2006-05-19 21:12:26 +00:00
int ret ;
2006-05-29 23:46:43 +00:00
ldb_debug ( module - > ldb , LDB_DEBUG_TRACE , " password_hash_add \n " ) ;
2006-05-19 21:12:26 +00:00
if ( ldb_dn_is_special ( req - > op . add . message - > dn ) ) { /* do not manipulate our control entries */
return ldb_next_request ( module , req ) ;
}
2006-07-28 06:30:03 +00:00
/* If the caller is manipulating the local passwords directly, let them pass */
if ( ldb_dn_compare_base ( module - > ldb ,
ldb_dn_explode ( req , LOCAL_BASE ) ,
req - > op . add . message - > dn ) = = 0 ) {
return ldb_next_request ( module , req ) ;
}
2006-05-19 21:12:26 +00:00
/* nobody must touch password Histories */
if ( ldb_msg_find_element ( req - > op . add . message , " sambaNTPwdHistory " ) | |
ldb_msg_find_element ( req - > op . add . message , " sambaLMPwdHistory " ) ) {
return LDB_ERR_UNWILLING_TO_PERFORM ;
}
2006-07-03 03:37:55 +00:00
/* If no part of this ADD touches the sambaPassword, or the NT
* or LM hashes , then we don ' t need to make any changes . */
sambaAttr = ldb_msg_find_element ( req - > op . mod . message , " sambaPassword " ) ;
ntAttr = ldb_msg_find_element ( req - > op . mod . message , " ntPwdHash " ) ;
lmAttr = ldb_msg_find_element ( req - > op . mod . message , " lmPwdHash " ) ;
if ( ( ! sambaAttr ) & & ( ! ntAttr ) & & ( ! lmAttr ) ) {
2006-05-19 21:12:26 +00:00
return ldb_next_request ( module , req ) ;
}
/* if it is not an entry of type person its an error */
/* TODO: remove this when sambaPassword will be in schema */
if ( ! ldb_msg_check_string_attribute ( req - > op . add . message , " objectClass " , " person " ) ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( module - > ldb , " Cannot set a password on entry that does not have objectClass 'person' " ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OBJECT_CLASS_VIOLATION ;
}
/* check sambaPassword is single valued here */
/* TODO: remove this when sambaPassword will be single valued in schema */
2006-07-06 05:51:39 +00:00
if ( sambaAttr & & sambaAttr - > num_values > 1 ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( module - > ldb , " mupltiple values for sambaPassword not allowed! \n " ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_CONSTRAINT_VIOLATION ;
}
2006-07-03 03:37:55 +00:00
if ( ntAttr & & ( ntAttr - > num_values > 1 ) ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( module - > ldb , " mupltiple values for lmPwdHash not allowed! \n " ) ;
2006-07-03 03:37:55 +00:00
return LDB_ERR_CONSTRAINT_VIOLATION ;
}
if ( lmAttr & & ( lmAttr - > num_values > 1 ) ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( module - > ldb , " mupltiple values for lmPwdHash not allowed! \n " ) ;
2006-07-03 03:37:55 +00:00
return LDB_ERR_CONSTRAINT_VIOLATION ;
}
2006-07-03 15:49:23 +00:00
h = ph_init_handle ( req , module , PH_ADD ) ;
if ( ! h ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( h - > private_data , struct ph_context ) ;
2006-07-03 03:37:55 +00:00
2006-05-19 21:12:26 +00:00
/* get user domain data */
2006-07-03 03:37:55 +00:00
ac - > domain_sid = samdb_result_sid_prefix ( ac , req - > op . add . message , " objectSid " ) ;
if ( ac - > domain_sid = = NULL ) {
2006-05-19 21:12:26 +00:00
ldb_debug ( module - > ldb , LDB_DEBUG_ERROR , " can't handle entry with missing objectSid! \n " ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-03 03:37:55 +00:00
ret = build_domain_data_request ( ac ) ;
2006-05-19 21:12:26 +00:00
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
ac - > step = PH_ADD_SEARCH_DOM ;
2006-07-22 17:21:59 +00:00
req - > handle = h ;
2006-05-19 21:12:26 +00:00
return ldb_next_request ( module , ac - > dom_req ) ;
}
2006-07-22 16:56:33 +00:00
static int password_hash_add_do_add ( struct ldb_handle * h ) {
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
2006-05-19 21:12:26 +00:00
struct domain_data * domain ;
struct smb_krb5_context * smb_krb5_context ;
2006-07-03 03:37:55 +00:00
struct ldb_message_element * sambaAttr ;
2006-05-19 21:12:26 +00:00
struct ldb_message * msg ;
2006-07-03 03:37:55 +00:00
int ret ;
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( h - > private_data , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
2006-05-21 20:06:01 +00:00
domain = get_domain_data ( ac - > module , ac , ac - > dom_res ) ;
2006-05-19 21:12:26 +00:00
if ( domain = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > down_req = talloc ( ac , struct ldb_request ) ;
if ( ac - > down_req = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
* ( ac - > down_req ) = * ( ac - > orig_req ) ;
ac - > down_req - > op . add . message = msg = ldb_msg_copy_shallow ( ac - > down_req , ac - > orig_req - > op . add . message ) ;
if ( ac - > down_req - > op . add . message = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-06-04 05:28:13 +00:00
2006-05-19 21:12:26 +00:00
/* Some operations below require kerberos contexts */
if ( smb_krb5_init_context ( ac - > down_req , & smb_krb5_context ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-03 03:37:55 +00:00
/* if we have sambaPassword in the original message add the operatio on it here */
sambaAttr = ldb_msg_find_element ( msg , " sambaPassword " ) ;
if ( sambaAttr ) {
2006-09-08 00:23:21 +00:00
unsigned int user_account_control ;
2006-07-03 03:37:55 +00:00
ret = add_password_hashes ( ac - > module , msg , 0 ) ;
/* we can compute new password hashes from the unicode password */
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
/* now add krb5 keys based on unicode password */
ret = add_krb5_keys_from_password ( ac - > module , msg , smb_krb5_context , domain ,
2006-08-13 08:00:36 +00:00
ldb_msg_find_attr_as_string ( msg , " samAccountName " , NULL ) ,
ldb_msg_find_attr_as_string ( msg , " userPrincipalName " , NULL ) ,
2006-07-03 03:37:55 +00:00
ldb_msg_check_string_attribute ( msg , " objectClass " , " computer " ) ) ;
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
/* add also kr5 keys based on NT the hash */
ret = add_krb5_keys_from_NThash ( ac - > module , msg , smb_krb5_context ) ;
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
/* if both the domain properties and the user account controls do not permit
* clear text passwords then wipe out the sambaPassword */
2006-09-08 00:23:21 +00:00
user_account_control = ldb_msg_find_attr_as_uint ( msg , " userAccountControl " , 0 ) ;
if ( domain - > store_cleartext & & ( user_account_control & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED ) ) {
/* Keep sambaPassword attribute */
} else {
2006-07-03 03:37:55 +00:00
ldb_msg_remove_attr ( msg , " sambaPassword " ) ;
}
2006-05-19 21:12:26 +00:00
}
/* don't touch it if a value is set. It could be an incoming samsync */
2006-08-13 08:00:36 +00:00
if ( ldb_msg_find_attr_as_uint64 ( msg , " pwdLastSet " , 0 ) = = 0 ) {
2006-05-22 03:55:01 +00:00
if ( set_pwdLastSet ( ac - > module , msg , 0 ) ! = LDB_SUCCESS ) {
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
}
/* don't touch it if a value is set. It could be an incoming samsync */
if ( ! ldb_msg_find_element ( msg , " msDS-KeyVersionNumber " ) ) {
if ( add_keyVersionNumber ( ac - > module , msg , 0 ) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
}
h - > state = LDB_ASYNC_INIT ;
h - > status = LDB_SUCCESS ;
ac - > step = PH_ADD_DO_ADD ;
2006-06-04 05:28:13 +00:00
ldb_set_timeout_from_prev_req ( ac - > module - > ldb , ac - > orig_req , ac - > down_req ) ;
2006-05-19 21:12:26 +00:00
/* perform the operation */
return ldb_next_request ( ac - > module , ac - > down_req ) ;
}
2006-07-22 16:56:33 +00:00
static int password_hash_mod_search_self ( struct ldb_handle * h ) ;
2006-05-19 21:12:26 +00:00
2006-05-29 23:46:43 +00:00
static int password_hash_modify ( struct ldb_module * module , struct ldb_request * req )
2006-05-19 21:12:26 +00:00
{
2006-07-22 16:56:33 +00:00
struct ldb_handle * h ;
struct ph_context * ac ;
2006-05-19 21:12:26 +00:00
struct ldb_message_element * sambaAttr ;
struct ldb_message_element * ntAttr ;
struct ldb_message_element * lmAttr ;
2006-07-28 06:30:03 +00:00
struct ldb_message * msg ;
2006-05-19 21:12:26 +00:00
2006-05-29 23:46:43 +00:00
ldb_debug ( module - > ldb , LDB_DEBUG_TRACE , " password_hash_modify \n " ) ;
2006-05-19 21:12:26 +00:00
if ( ldb_dn_is_special ( req - > op . mod . message - > dn ) ) { /* do not manipulate our control entries */
return ldb_next_request ( module , req ) ;
}
2006-07-28 06:30:03 +00:00
/* If the caller is manipulating the local passwords directly, let them pass */
if ( ldb_dn_compare_base ( module - > ldb ,
ldb_dn_explode ( req , LOCAL_BASE ) ,
req - > op . mod . message - > dn ) = = 0 ) {
return ldb_next_request ( module , req ) ;
}
2006-05-19 21:12:26 +00:00
/* nobody must touch password Histories */
if ( ldb_msg_find_element ( req - > op . mod . message , " sambaNTPwdHistory " ) | |
ldb_msg_find_element ( req - > op . mod . message , " sambaLMPwdHistory " ) ) {
return LDB_ERR_UNWILLING_TO_PERFORM ;
}
sambaAttr = ldb_msg_find_element ( req - > op . mod . message , " sambaPassword " ) ;
ntAttr = ldb_msg_find_element ( req - > op . mod . message , " ntPwdHash " ) ;
lmAttr = ldb_msg_find_element ( req - > op . mod . message , " lmPwdHash " ) ;
/* check passwords are single valued here */
/* TODO: remove this when passwords will be single valued in schema */
if ( sambaAttr & & ( sambaAttr - > num_values > 1 ) ) {
return LDB_ERR_CONSTRAINT_VIOLATION ;
}
if ( ntAttr & & ( ntAttr - > num_values > 1 ) ) {
return LDB_ERR_CONSTRAINT_VIOLATION ;
}
if ( lmAttr & & ( lmAttr - > num_values > 1 ) ) {
return LDB_ERR_CONSTRAINT_VIOLATION ;
}
/* If no part of this touches the sambaPassword OR ntPwdHash and/or lmPwdHash, then we don't
* need to make any changes . For password changes / set there should
* be a ' delete ' or a ' modify ' on this attribute . */
/* If the only operation is the deletion of the passwords then go on */
if ( ( ( ! sambaAttr ) | | ( ( sambaAttr - > flags & LDB_FLAG_MOD_MASK ) = = LDB_FLAG_MOD_DELETE ) )
& & ( ( ! ntAttr ) | | ( ( ntAttr - > flags & LDB_FLAG_MOD_MASK ) = = LDB_FLAG_MOD_DELETE ) )
& & ( ( ! lmAttr ) | | ( ( lmAttr - > flags & LDB_FLAG_MOD_MASK ) = = LDB_FLAG_MOD_DELETE ) ) ) {
return ldb_next_request ( module , req ) ;
}
h = ph_init_handle ( req , module , PH_MOD ) ;
if ( ! h ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( h - > private_data , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
/* return or own handle to deal with this call */
2006-07-22 17:21:59 +00:00
req - > handle = h ;
2006-05-19 21:12:26 +00:00
/* prepare the first operation */
ac - > down_req = talloc_zero ( ac , struct ldb_request ) ;
if ( ac - > down_req = = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( module - > ldb , " Out of memory! " ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
* ( ac - > down_req ) = * req ; /* copy the request */
/* use a new message structure so that we can modify it */
2006-07-28 06:30:03 +00:00
ac - > down_req - > op . mod . message = msg = ldb_msg_copy_shallow ( ac - > down_req , req - > op . mod . message ) ;
2006-05-19 21:12:26 +00:00
/* - remove any imodification to the password from the first commit
* we will make the real modification later */
2006-07-28 06:30:03 +00:00
if ( sambaAttr ) ldb_msg_remove_attr ( msg , " sambaPassword " ) ;
if ( ntAttr ) ldb_msg_remove_attr ( msg , " ntPwdHash " ) ;
if ( lmAttr ) ldb_msg_remove_attr ( msg , " lmPwdHash " ) ;
2006-05-19 21:12:26 +00:00
/* if there was nothing else to be modify skip to next step */
2006-07-28 06:30:03 +00:00
if ( msg - > num_elements = = 0 ) {
2006-05-19 21:12:26 +00:00
talloc_free ( ac - > down_req ) ;
ac - > down_req = NULL ;
2006-05-29 23:46:43 +00:00
return password_hash_mod_search_self ( h ) ;
2006-05-19 21:12:26 +00:00
}
2006-07-22 17:21:59 +00:00
ac - > down_req - > context = NULL ;
ac - > down_req - > callback = NULL ;
2006-05-19 21:12:26 +00:00
ac - > step = PH_MOD_DO_REQ ;
2006-06-04 05:28:13 +00:00
ldb_set_timeout_from_prev_req ( module - > ldb , req , ac - > down_req ) ;
2006-05-19 21:12:26 +00:00
return ldb_next_request ( module , ac - > down_req ) ;
}
2006-07-22 16:56:33 +00:00
static int get_self_callback ( struct ldb_context * ldb , void * context , struct ldb_reply * ares )
2006-05-19 21:12:26 +00:00
{
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
2006-05-19 21:12:26 +00:00
if ( ! context | | ! ares ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( ldb , " NULL Context or Result in callback " ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( context , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
/* we are interested only in the single reply (base search) we receive here */
if ( ares - > type = = LDB_REPLY_ENTRY ) {
if ( ac - > search_res ! = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( ldb , " Too many results " ) ;
2006-05-19 21:12:26 +00:00
talloc_free ( ares ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
/* if it is not an entry of type person this is an error */
/* TODO: remove this when sambaPassword will be in schema */
if ( ! ldb_msg_check_string_attribute ( ares - > message , " objectClass " , " person " ) ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( ldb , " Object class violation " ) ;
2006-05-19 21:12:26 +00:00
talloc_free ( ares ) ;
return LDB_ERR_OBJECT_CLASS_VIOLATION ;
}
ac - > search_res = talloc_steal ( ac , ares ) ;
} else {
talloc_free ( ares ) ;
}
return LDB_SUCCESS ;
}
2006-07-22 16:56:33 +00:00
static int password_hash_mod_search_self ( struct ldb_handle * h ) {
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
2006-06-02 02:54:24 +00:00
static const char * const attrs [ ] = { " userAccountControl " , " sambaLMPwdHistory " ,
" sambaNTPwdHistory " ,
" objectSid " , " msDS-KeyVersionNumber " ,
" objectClass " , " userPrincipalName " ,
" samAccountName " ,
" lmPwdHash " , " ntPwdHash " ,
NULL } ;
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( h - > private_data , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
/* prepare the search operation */
ac - > search_req = talloc_zero ( ac , struct ldb_request ) ;
if ( ac - > search_req = = NULL ) {
ldb_debug ( ac - > module - > ldb , LDB_DEBUG_ERROR , " Out of Memory! \n " ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-05-30 00:33:52 +00:00
ac - > search_req - > operation = LDB_SEARCH ;
2006-05-19 21:12:26 +00:00
ac - > search_req - > op . search . base = ac - > orig_req - > op . mod . message - > dn ;
ac - > search_req - > op . search . scope = LDB_SCOPE_BASE ;
ac - > search_req - > op . search . tree = ldb_parse_tree ( ac - > module - > ldb , NULL ) ;
if ( ac - > search_req - > op . search . tree = = NULL ) {
2006-08-13 07:33:57 +00:00
ldb_set_errstring ( ac - > module - > ldb , " Invalid search filter " ) ;
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-06-02 02:54:24 +00:00
ac - > search_req - > op . search . attrs = attrs ;
2006-05-19 21:12:26 +00:00
ac - > search_req - > controls = NULL ;
2006-07-22 17:21:59 +00:00
ac - > search_req - > context = ac ;
ac - > search_req - > callback = get_self_callback ;
2006-06-04 05:28:13 +00:00
ldb_set_timeout_from_prev_req ( ac - > module - > ldb , ac - > orig_req , ac - > search_req ) ;
2006-05-19 21:12:26 +00:00
ac - > step = PH_MOD_SEARCH_SELF ;
return ldb_next_request ( ac - > module , ac - > search_req ) ;
}
2006-07-22 16:56:33 +00:00
static int password_hash_mod_search_dom ( struct ldb_handle * h ) {
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
2006-05-19 21:12:26 +00:00
int ret ;
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( h - > private_data , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
/* get object domain sid */
2006-07-03 03:37:55 +00:00
ac - > domain_sid = samdb_result_sid_prefix ( ac , ac - > search_res - > message , " objectSid " ) ;
if ( ac - > domain_sid = = NULL ) {
2006-05-19 21:12:26 +00:00
ldb_debug ( ac - > module - > ldb , LDB_DEBUG_ERROR , " can't handle entry with missing objectSid! \n " ) ;
return LDB_ERR_OPERATIONS_ERROR ;
}
/* get user domain data */
2006-07-03 03:37:55 +00:00
ret = build_domain_data_request ( ac ) ;
2006-05-19 21:12:26 +00:00
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
ac - > step = PH_MOD_SEARCH_DOM ;
return ldb_next_request ( ac - > module , ac - > dom_req ) ;
}
2006-07-22 16:56:33 +00:00
static int password_hash_mod_do_mod ( struct ldb_handle * h ) {
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
struct ph_context * ac ;
2006-05-19 21:12:26 +00:00
struct domain_data * domain ;
struct smb_krb5_context * smb_krb5_context ;
struct ldb_message_element * sambaAttr ;
struct ldb_message * msg ;
int phlen ;
2006-07-03 03:37:55 +00:00
int ret ;
BOOL added_hashes = False ;
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( h - > private_data , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
domain = get_domain_data ( ac - > module , ac , ac - > dom_res ) ;
if ( domain = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
ac - > mod_req = talloc ( ac , struct ldb_request ) ;
if ( ac - > mod_req = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
* ( ac - > mod_req ) = * ( ac - > orig_req ) ;
/* use a new message structure so that we can modify it */
ac - > mod_req - > op . mod . message = msg = ldb_msg_new ( ac - > mod_req ) ;
if ( msg = = NULL ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
/* modify dn */
msg - > dn = ac - > orig_req - > op . mod . message - > dn ;
/* Some operations below require kerberos contexts */
if ( smb_krb5_init_context ( ac - > mod_req , & smb_krb5_context ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
/* we are going to replace the existing krb5key or delete it */
if ( ldb_msg_add_empty ( msg , " krb5key " , LDB_FLAG_MOD_REPLACE ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-03 03:37:55 +00:00
/* if we have sambaPassword in the original message add the operation on it here */
2006-05-19 21:12:26 +00:00
sambaAttr = ldb_msg_find_element ( ac - > orig_req - > op . mod . message , " sambaPassword " ) ;
if ( sambaAttr ) {
if ( ldb_msg_add ( msg , sambaAttr , sambaAttr - > flags ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-07-03 03:37:55 +00:00
/* if we are actually settting a new unicode password,
* use it to generate the password hashes */
if ( ( ( sambaAttr - > flags & LDB_FLAG_MOD_MASK ) ! = LDB_FLAG_MOD_DELETE )
& & ( sambaAttr - > num_values = = 1 ) ) {
2006-05-19 21:12:26 +00:00
/* we can compute new password hashes from the unicode password */
2006-07-03 03:37:55 +00:00
ret = add_password_hashes ( ac - > module , msg , 1 ) ;
if ( ret ! = LDB_SUCCESS ) {
return ret ;
2006-05-19 21:12:26 +00:00
}
2006-07-03 03:37:55 +00:00
added_hashes = True ;
2006-05-19 21:12:26 +00:00
/* now add krb5 keys based on unicode password */
2006-07-03 03:37:55 +00:00
ret = add_krb5_keys_from_password ( ac - > module , msg , smb_krb5_context , domain ,
2006-08-13 08:00:36 +00:00
ldb_msg_find_attr_as_string ( ac - > search_res - > message , " samAccountName " , NULL ) ,
ldb_msg_find_attr_as_string ( ac - > search_res - > message , " userPrincipalName " , NULL ) ,
2006-07-03 03:37:55 +00:00
ldb_msg_check_string_attribute ( ac - > search_res - > message , " objectClass " , " computer " ) ) ;
if ( ret ! = LDB_SUCCESS ) {
return ret ;
2006-05-19 21:12:26 +00:00
}
/* if the domain properties or the user account controls do not permit
* clear text passwords then wipe out the sambaPassword */
2006-09-08 00:23:21 +00:00
if ( domain - > store_cleartext & &
( ldb_msg_find_attr_as_uint ( ac - > search_res - > message , " userAccountControl " , 0 ) & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED ) ) {
/* Keep sambaPassword attribute */
} else {
2006-05-19 21:12:26 +00:00
ldb_msg_remove_attr ( msg , " sambaPassword " ) ;
}
}
}
2006-07-03 03:37:55 +00:00
/* if we didn't create the hashes above, try using values supplied directly */
if ( ! added_hashes ) {
2006-05-19 21:12:26 +00:00
struct ldb_message_element * el ;
el = ldb_msg_find_element ( ac - > orig_req - > op . mod . message , " ntPwdHash " ) ;
if ( ldb_msg_add ( msg , el , el - > flags ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
el = ldb_msg_find_element ( ac - > orig_req - > op . mod . message , " lmPwdHash " ) ;
if ( ldb_msg_add ( msg , el , el - > flags ) ! = 0 ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
}
2006-06-02 02:54:24 +00:00
/* add also krb5 keys based on NT the hash */
2006-05-19 21:12:26 +00:00
if ( add_krb5_keys_from_NThash ( ac - > module , msg , smb_krb5_context ) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
/* set change time */
2006-05-22 03:55:01 +00:00
if ( set_pwdLastSet ( ac - > module , msg , 1 ) ! = LDB_SUCCESS ) {
2006-05-19 21:12:26 +00:00
return LDB_ERR_OPERATIONS_ERROR ;
}
/* don't touch it if a value is set. It could be an incoming samsync */
2006-07-03 03:37:55 +00:00
if ( ! ldb_msg_find_element ( ac - > orig_req - > op . mod . message ,
" msDS-KeyVersionNumber " ) ) {
if ( add_keyVersionNumber ( ac - > module , msg ,
2006-08-13 08:00:36 +00:00
ldb_msg_find_attr_as_uint ( ac - > search_res - > message ,
2006-07-03 03:37:55 +00:00
" msDS-KeyVersionNumber " , 0 )
) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
2006-05-19 21:12:26 +00:00
}
if ( ( phlen = samdb_result_uint ( ac - > dom_res - > message , " pwdHistoryLength " , 0 ) ) > 0 ) {
if ( setPwdHistory ( ac - > module , msg , ac - > search_res - > message , phlen ) ! = LDB_SUCCESS ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
}
h - > state = LDB_ASYNC_INIT ;
h - > status = LDB_SUCCESS ;
ac - > step = PH_MOD_DO_MOD ;
2006-06-04 05:28:13 +00:00
ldb_set_timeout_from_prev_req ( ac - > module - > ldb , ac - > orig_req , ac - > mod_req ) ;
2006-05-19 21:12:26 +00:00
/* perform the search */
return ldb_next_request ( ac - > module , ac - > mod_req ) ;
}
2006-07-22 16:56:33 +00:00
static int ph_wait ( struct ldb_handle * handle ) {
struct ph_context * ac ;
2006-05-19 21:12:26 +00:00
int ret ;
if ( ! handle | | ! handle - > private_data ) {
return LDB_ERR_OPERATIONS_ERROR ;
}
if ( handle - > state = = LDB_ASYNC_DONE ) {
return handle - > status ;
}
handle - > state = LDB_ASYNC_PENDING ;
2006-05-28 02:10:44 +00:00
handle - > status = LDB_SUCCESS ;
2006-05-19 21:12:26 +00:00
2006-07-22 16:56:33 +00:00
ac = talloc_get_type ( handle - > private_data , struct ph_context ) ;
2006-05-19 21:12:26 +00:00
switch ( ac - > step ) {
case PH_ADD_SEARCH_DOM :
2006-07-22 17:21:59 +00:00
ret = ldb_wait ( ac - > dom_req - > handle , LDB_WAIT_NONE ) ;
2006-05-19 21:12:26 +00:00
2006-05-28 02:10:44 +00:00
if ( ret ! = LDB_SUCCESS ) {
handle - > status = ret ;
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > dom_req - > handle - > status ! = LDB_SUCCESS ) {
handle - > status = ac - > dom_req - > handle - > status ;
2006-05-28 02:10:44 +00:00
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > dom_req - > handle - > state ! = LDB_ASYNC_DONE ) {
2006-05-28 02:10:44 +00:00
return LDB_SUCCESS ;
2006-05-19 21:12:26 +00:00
}
/* domain search done, go on */
2006-05-29 23:46:43 +00:00
return password_hash_add_do_add ( handle ) ;
2006-05-19 21:12:26 +00:00
case PH_ADD_DO_ADD :
2006-07-22 17:21:59 +00:00
ret = ldb_wait ( ac - > down_req - > handle , LDB_WAIT_NONE ) ;
2006-05-19 21:12:26 +00:00
2006-05-28 02:10:44 +00:00
if ( ret ! = LDB_SUCCESS ) {
handle - > status = ret ;
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > down_req - > handle - > status ! = LDB_SUCCESS ) {
handle - > status = ac - > down_req - > handle - > status ;
2006-05-28 02:10:44 +00:00
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > down_req - > handle - > state ! = LDB_ASYNC_DONE ) {
2006-05-28 02:10:44 +00:00
return LDB_SUCCESS ;
2006-05-19 21:12:26 +00:00
}
2006-05-21 20:06:01 +00:00
break ;
2006-05-19 21:12:26 +00:00
case PH_MOD_DO_REQ :
2006-07-22 17:21:59 +00:00
ret = ldb_wait ( ac - > down_req - > handle , LDB_WAIT_NONE ) ;
2006-05-19 21:12:26 +00:00
2006-05-28 02:10:44 +00:00
if ( ret ! = LDB_SUCCESS ) {
handle - > status = ret ;
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > down_req - > handle - > status ! = LDB_SUCCESS ) {
handle - > status = ac - > down_req - > handle - > status ;
2006-05-28 02:10:44 +00:00
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > down_req - > handle - > state ! = LDB_ASYNC_DONE ) {
2006-05-28 02:10:44 +00:00
return LDB_SUCCESS ;
2006-05-19 21:12:26 +00:00
}
/* non-password mods done, go on */
2006-05-29 23:46:43 +00:00
return password_hash_mod_search_self ( handle ) ;
2006-05-19 21:12:26 +00:00
case PH_MOD_SEARCH_SELF :
2006-07-22 17:21:59 +00:00
ret = ldb_wait ( ac - > search_req - > handle , LDB_WAIT_NONE ) ;
2006-05-19 21:12:26 +00:00
2006-05-28 02:10:44 +00:00
if ( ret ! = LDB_SUCCESS ) {
handle - > status = ret ;
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > search_req - > handle - > status ! = LDB_SUCCESS ) {
handle - > status = ac - > search_req - > handle - > status ;
2006-05-28 02:10:44 +00:00
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > search_req - > handle - > state ! = LDB_ASYNC_DONE ) {
2006-05-28 02:10:44 +00:00
return LDB_SUCCESS ;
2006-05-19 21:12:26 +00:00
}
/* self search done, go on */
2006-05-29 23:46:43 +00:00
return password_hash_mod_search_dom ( handle ) ;
2006-05-19 21:12:26 +00:00
case PH_MOD_SEARCH_DOM :
2006-07-22 17:21:59 +00:00
ret = ldb_wait ( ac - > dom_req - > handle , LDB_WAIT_NONE ) ;
2006-05-19 21:12:26 +00:00
2006-05-28 02:10:44 +00:00
if ( ret ! = LDB_SUCCESS ) {
handle - > status = ret ;
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > dom_req - > handle - > status ! = LDB_SUCCESS ) {
handle - > status = ac - > dom_req - > handle - > status ;
2006-05-28 02:10:44 +00:00
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > dom_req - > handle - > state ! = LDB_ASYNC_DONE ) {
2006-05-28 02:10:44 +00:00
return LDB_SUCCESS ;
2006-05-19 21:12:26 +00:00
}
/* domain search done, go on */
2006-05-29 23:46:43 +00:00
return password_hash_mod_do_mod ( handle ) ;
2006-05-19 21:12:26 +00:00
case PH_MOD_DO_MOD :
2006-07-22 17:21:59 +00:00
ret = ldb_wait ( ac - > mod_req - > handle , LDB_WAIT_NONE ) ;
2006-05-19 21:12:26 +00:00
2006-05-28 02:10:44 +00:00
if ( ret ! = LDB_SUCCESS ) {
handle - > status = ret ;
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > mod_req - > handle - > status ! = LDB_SUCCESS ) {
handle - > status = ac - > mod_req - > handle - > status ;
2006-05-28 02:10:44 +00:00
goto done ;
}
2006-07-22 17:21:59 +00:00
if ( ac - > mod_req - > handle - > state ! = LDB_ASYNC_DONE ) {
2006-05-28 02:10:44 +00:00
return LDB_SUCCESS ;
2006-05-19 21:12:26 +00:00
}
2006-05-20 19:37:21 +00:00
break ;
2006-05-19 21:12:26 +00:00
default :
ret = LDB_ERR_OPERATIONS_ERROR ;
2006-05-20 19:37:21 +00:00
goto done ;
2006-05-19 21:12:26 +00:00
}
2006-05-20 19:37:21 +00:00
ret = LDB_SUCCESS ;
done :
2006-05-19 21:12:26 +00:00
handle - > state = LDB_ASYNC_DONE ;
return ret ;
}
2006-07-22 16:56:33 +00:00
static int ph_wait_all ( struct ldb_handle * handle ) {
2006-05-19 21:12:26 +00:00
int ret ;
while ( handle - > state ! = LDB_ASYNC_DONE ) {
2006-07-22 16:56:33 +00:00
ret = ph_wait ( handle ) ;
2006-05-19 21:12:26 +00:00
if ( ret ! = LDB_SUCCESS ) {
return ret ;
}
}
return handle - > status ;
}
2006-07-22 16:56:33 +00:00
static int password_hash_wait ( struct ldb_handle * handle , enum ldb_wait_type type )
2006-05-19 21:12:26 +00:00
{
if ( type = = LDB_WAIT_ALL ) {
2006-07-22 16:56:33 +00:00
return ph_wait_all ( handle ) ;
2006-05-19 21:12:26 +00:00
} else {
2006-07-22 16:56:33 +00:00
return ph_wait ( handle ) ;
2006-05-19 21:12:26 +00:00
}
}
2005-12-30 08:40:16 +00:00
static const struct ldb_module_ops password_hash_ops = {
. name = " password_hash " ,
2006-05-29 23:46:43 +00:00
. add = password_hash_add ,
. modify = password_hash_modify ,
2006-07-22 16:56:33 +00:00
. wait = password_hash_wait
2005-12-30 08:40:16 +00:00
} ;
2006-03-02 16:32:53 +00:00
int password_hash_module_init ( void )
2005-12-30 08:40:16 +00:00
{
2006-03-02 16:32:53 +00:00
return ldb_register_module ( & password_hash_ops ) ;
2005-12-30 08:40:16 +00:00
}