2001-11-24 17:16:41 +03:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2001-11-24 17:16:41 +03:00
krb5 set password implementation
Copyright ( C ) Andrew Tridgell 2001
2001-12-20 06:54:52 +03:00
Copyright ( C ) Remus Koos 2001 ( remuskoos @ yahoo . com )
2001-11-24 17:16:41 +03:00
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
2001-11-24 17:16:41 +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/>.
2001-11-24 17:16:41 +03:00
*/
# include "includes.h"
2009-11-27 17:52:57 +03:00
# include "smb_krb5.h"
2010-07-02 02:32:52 +04:00
# include "libads/kerberos_proto.h"
2011-02-24 14:27:29 +03:00
# include "../lib/util/asn1.h"
2001-11-24 17:16:41 +03:00
2001-11-29 02:54:07 +03:00
# ifdef HAVE_KRB5
2001-11-24 17:16:41 +03:00
2014-08-02 18:31:20 +04:00
/* Those are defined by kerberos-set-passwd-02.txt and are probably
2003-02-24 05:55:00 +03:00
* not supported by M $ implementation */
# define KRB5_KPASSWD_POLICY_REJECT 8
# define KRB5_KPASSWD_BAD_PRINCIPAL 9
# define KRB5_KPASSWD_ETYPE_NOSUPP 10
2006-06-16 01:25:57 +04:00
/*
* we ' ve got to be able to distinguish KRB_ERRORs from other
* requests - valid response for CHPW v2 replies .
*/
2014-08-02 18:31:20 +04:00
static krb5_error_code kpasswd_err_to_krb5_err ( krb5_error_code res_code )
2006-02-04 01:19:41 +03:00
{
2019-09-17 02:50:33 +03:00
switch ( res_code ) {
case KRB5_KPASSWD_ACCESSDENIED :
return KRB5KDC_ERR_BADOPTION ;
case KRB5_KPASSWD_INITIAL_FLAG_NEEDED :
return KRB5KDC_ERR_BADOPTION ;
/* return KV5M_ALT_METHOD; MIT-only define */
case KRB5_KPASSWD_ETYPE_NOSUPP :
return KRB5KDC_ERR_ETYPE_NOSUPP ;
case KRB5_KPASSWD_BAD_PRINCIPAL :
return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN ;
case KRB5_KPASSWD_POLICY_REJECT :
case KRB5_KPASSWD_SOFTERROR :
return KRB5KDC_ERR_POLICY ;
default :
return KRB5KRB_ERR_GENERIC ;
2006-02-04 01:19:41 +03:00
}
}
2001-11-24 17:16:41 +03:00
2014-08-02 18:31:20 +04:00
ADS_STATUS ads_krb5_set_password ( const char * kdc_host , const char * principal ,
2003-05-30 23:51:09 +04:00
const char * newpw , int time_offset )
2003-02-24 05:55:00 +03:00
{
ADS_STATUS aret ;
2004-06-23 04:20:31 +04:00
krb5_error_code ret = 0 ;
2004-05-07 06:48:03 +04:00
krb5_context context = NULL ;
2014-08-02 18:31:20 +04:00
krb5_principal princ = NULL ;
2004-05-07 06:48:03 +04:00
krb5_ccache ccache = NULL ;
2014-08-02 18:31:20 +04:00
int result_code ;
krb5_data result_code_string = { 0 } ;
krb5_data result_string = { 0 } ;
2003-02-24 05:55:00 +03:00
2018-12-05 13:16:42 +03:00
ret = smb_krb5_init_context_common ( & context ) ;
2003-02-24 05:55:00 +03:00
if ( ret ) {
2018-12-05 13:16:42 +03:00
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
2003-02-24 05:55:00 +03:00
return ADS_ERROR_KRB5 ( ret ) ;
}
2014-08-02 18:31:20 +04:00
if ( principal ) {
ret = smb_krb5_parse_name ( context , principal , & princ ) ;
if ( ret ) {
krb5_free_context ( context ) ;
DEBUG ( 1 , ( " Failed to parse %s (%s) \n " , principal ,
error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
}
2003-02-24 05:55:00 +03:00
if ( time_offset ! = 0 ) {
krb5_set_real_time ( context , time ( NULL ) + time_offset , 0 ) ;
}
ret = krb5_cc_default ( context , & ccache ) ;
if ( ret ) {
2014-08-02 18:31:20 +04:00
krb5_free_principal ( context , princ ) ;
2019-09-17 02:50:33 +03:00
krb5_free_context ( context ) ;
2003-02-24 05:55:00 +03:00
DEBUG ( 1 , ( " Failed to get default creds (%s) \n " , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
2015-10-06 15:05:15 +03:00
ret = krb5_set_password_using_ccache ( context ,
ccache ,
discard_const_p ( char , newpw ) ,
princ ,
2014-08-02 18:31:20 +04:00
& result_code ,
& result_code_string ,
& result_string ) ;
2003-02-24 05:55:00 +03:00
if ( ret ) {
2014-08-02 18:31:20 +04:00
DEBUG ( 1 , ( " krb5_set_password failed (%s) \n " , error_message ( ret ) ) ) ;
aret = ADS_ERROR_KRB5 ( ret ) ;
goto done ;
2003-02-24 05:55:00 +03:00
}
2014-08-02 18:31:20 +04:00
if ( result_code ! = KRB5_KPASSWD_SUCCESS ) {
ret = kpasswd_err_to_krb5_err ( result_code ) ;
DEBUG ( 1 , ( " krb5_set_password failed (%s) \n " , error_message ( ret ) ) ) ;
aret = ADS_ERROR_KRB5 ( ret ) ;
goto done ;
2003-02-24 05:55:00 +03:00
}
2014-08-02 18:31:20 +04:00
aret = ADS_SUCCESS ;
2003-02-24 05:55:00 +03:00
2019-09-17 02:50:33 +03:00
done :
2016-08-26 12:51:52 +03:00
smb_krb5_free_data_contents ( context , & result_code_string ) ;
smb_krb5_free_data_contents ( context , & result_string ) ;
2014-08-02 18:31:20 +04:00
krb5_free_principal ( context , princ ) ;
2004-06-23 04:20:31 +04:00
krb5_cc_close ( context , ccache ) ;
2001-12-20 06:54:52 +03:00
krb5_free_context ( context ) ;
2003-02-24 05:55:00 +03:00
return aret ;
}
/*
we use a prompter to avoid a crash bug in the kerberos libs when
dealing with empty passwords
this prompter is just a string copy . . .
*/
static krb5_error_code
kerb_prompter ( krb5_context ctx , void * data ,
const char * name ,
const char * banner ,
int num_prompts ,
krb5_prompt prompts [ ] )
{
if ( num_prompts = = 0 ) return 0 ;
memset ( prompts [ 0 ] . reply - > data , 0 , prompts [ 0 ] . reply - > length ) ;
if ( prompts [ 0 ] . reply - > length > 0 ) {
if ( data ) {
2008-10-09 13:05:42 +04:00
strncpy ( ( char * ) prompts [ 0 ] . reply - > data ,
2006-08-20 21:55:06 +04:00
( const char * ) data ,
prompts [ 0 ] . reply - > length - 1 ) ;
2008-10-09 13:05:42 +04:00
prompts [ 0 ] . reply - > length = strlen ( ( const char * ) prompts [ 0 ] . reply - > data ) ;
2003-02-24 05:55:00 +03:00
} else {
prompts [ 0 ] . reply - > length = 0 ;
}
}
return 0 ;
}
2003-05-31 00:03:18 +04:00
static ADS_STATUS ads_krb5_chg_password ( const char * kdc_host ,
const char * principal ,
2019-09-17 02:50:33 +03:00
const char * oldpw ,
const char * newpw ,
2003-05-31 00:03:18 +04:00
int time_offset )
2003-02-24 05:55:00 +03:00
{
2019-09-17 02:50:33 +03:00
ADS_STATUS aret ;
krb5_error_code ret ;
krb5_context context = NULL ;
krb5_principal princ ;
krb5_get_init_creds_opt * opts = NULL ;
krb5_creds creds ;
char * chpw_princ = NULL , * password ;
char * realm = NULL ;
int result_code ;
krb5_data result_code_string = { 0 } ;
krb5_data result_string = { 0 } ;
smb_krb5_addresses * addr = NULL ;
ret = smb_krb5_init_context_common ( & context ) ;
if ( ret ) {
DBG_ERR ( " kerberos init context failed (%s) \n " ,
error_message ( ret ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
if ( ( ret = smb_krb5_parse_name ( context , principal , & princ ) ) ) {
krb5_free_context ( context ) ;
DEBUG ( 1 , ( " Failed to parse %s (%s) \n " , principal , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
2003-02-24 05:55:00 +03:00
2016-08-26 18:08:57 +03:00
ret = krb5_get_init_creds_opt_alloc ( context , & opts ) ;
if ( ret ! = 0 ) {
krb5_free_context ( context ) ;
DBG_WARNING ( " krb5_get_init_creds_opt_alloc failed: %s \n " ,
error_message ( ret ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
2014-08-26 16:28:01 +04:00
2019-09-17 02:50:33 +03:00
krb5_get_init_creds_opt_set_tkt_life ( opts , 5 * 60 ) ;
2016-08-26 18:08:57 +03:00
krb5_get_init_creds_opt_set_renew_life ( opts , 0 ) ;
krb5_get_init_creds_opt_set_forwardable ( opts , 0 ) ;
krb5_get_init_creds_opt_set_proxiable ( opts , 0 ) ;
2019-09-13 17:04:30 +03:00
# ifdef SAMBA4_USES_HEIMDAL
krb5_get_init_creds_opt_set_win2k ( context , opts , true ) ;
krb5_get_init_creds_opt_set_canonicalize ( context , opts , true ) ;
# else /* MIT */
2019-10-09 17:32:47 +03:00
#if 0
/*
* FIXME
*
* Due to an upstream MIT Kerberos bug , this feature is not
* not working . Affection versions ( 2019 - 10 - 09 ) : < = 1.17
*
* Reproducer :
* kinit - C aDmInIsTrAtOr @ ACME . COM - S kadmin / changepw @ ACME . COM
*
* This is NOT a problem if the service is a krbtgt .
*
* https : //bugzilla.samba.org/show_bug.cgi?id=14155
*/
2019-09-13 17:04:30 +03:00
krb5_get_init_creds_opt_set_canonicalize ( opts , true ) ;
2019-10-09 17:32:47 +03:00
# endif
2019-09-13 17:04:30 +03:00
# endif /* MIT */
2003-02-24 05:55:00 +03:00
2019-09-17 02:50:33 +03:00
/* note that heimdal will fill in the local addresses if the addresses
* in the creds_init_opt are all empty and then later fail with invalid
* address , sending our local netbios krb5 address - just like windows
* - avoids this - gd */
ret = smb_krb5_gen_netbios_krb5_address ( & addr , lp_netbios_name ( ) ) ;
if ( ret ) {
krb5_free_principal ( context , princ ) ;
krb5_get_init_creds_opt_free ( context , opts ) ;
krb5_free_context ( context ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
2016-08-26 18:08:57 +03:00
krb5_get_init_creds_opt_set_address_list ( opts , addr - > addrs ) ;
2014-08-26 16:28:01 +04:00
2019-09-17 02:50:33 +03:00
realm = smb_krb5_principal_get_realm ( NULL , context , princ ) ;
/* We have to obtain an INITIAL changepw ticket for changing password */
if ( asprintf ( & chpw_princ , " kadmin/changepw@%s " , realm ) = = - 1 ) {
krb5_free_principal ( context , princ ) ;
krb5_get_init_creds_opt_free ( context , opts ) ;
smb_krb5_free_addresses ( context , addr ) ;
krb5_free_context ( context ) ;
TALLOC_FREE ( realm ) ;
DEBUG ( 1 , ( " ads_krb5_chg_password: asprintf fail \n " ) ) ;
return ADS_ERROR_NT ( NT_STATUS_NO_MEMORY ) ;
}
2011-02-15 08:34:02 +03:00
2018-11-20 19:45:11 +03:00
TALLOC_FREE ( realm ) ;
2019-09-17 02:50:33 +03:00
password = SMB_STRDUP ( oldpw ) ;
ret = krb5_get_init_creds_password ( context , & creds , princ , password ,
kerb_prompter , NULL ,
2016-08-26 18:08:57 +03:00
0 , chpw_princ , opts ) ;
krb5_get_init_creds_opt_free ( context , opts ) ;
2018-08-09 17:02:16 +03:00
smb_krb5_free_addresses ( context , addr ) ;
2019-09-17 02:50:33 +03:00
SAFE_FREE ( chpw_princ ) ;
SAFE_FREE ( password ) ;
2003-02-24 05:55:00 +03:00
2019-09-17 02:50:33 +03:00
if ( ret ) {
if ( ret = = KRB5KRB_AP_ERR_BAD_INTEGRITY ) {
2023-08-07 07:36:27 +03:00
DEBUG ( 1 , ( " Password incorrect while getting initial ticket \n " ) ) ;
2019-09-17 02:50:33 +03:00
} else {
DEBUG ( 1 , ( " krb5_get_init_creds_password failed (%s) \n " , error_message ( ret ) ) ) ;
}
krb5_free_principal ( context , princ ) ;
krb5_free_context ( context ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
2003-02-24 05:55:00 +03:00
2016-08-30 13:48:09 +03:00
ret = krb5_set_password ( context ,
& creds ,
discard_const_p ( char , newpw ) ,
2017-08-09 19:14:23 +03:00
NULL ,
2016-08-30 13:48:09 +03:00
& result_code ,
& result_code_string ,
& result_string ) ;
2019-09-17 02:50:33 +03:00
if ( ret ) {
DEBUG ( 1 , ( " krb5_change_password failed (%s) \n " , error_message ( ret ) ) ) ;
aret = ADS_ERROR_KRB5 ( ret ) ;
goto done ;
}
2014-08-02 18:31:20 +04:00
2019-09-17 02:50:33 +03:00
if ( result_code ! = KRB5_KPASSWD_SUCCESS ) {
ret = kpasswd_err_to_krb5_err ( result_code ) ;
DEBUG ( 1 , ( " krb5_change_password failed (%s) \n " , error_message ( ret ) ) ) ;
aret = ADS_ERROR_KRB5 ( ret ) ;
goto done ;
}
2014-08-02 18:31:20 +04:00
2019-09-17 02:50:33 +03:00
aret = ADS_SUCCESS ;
2003-02-24 05:55:00 +03:00
2019-09-17 02:50:33 +03:00
done :
smb_krb5_free_data_contents ( context , & result_code_string ) ;
smb_krb5_free_data_contents ( context , & result_string ) ;
krb5_free_principal ( context , princ ) ;
krb5_free_context ( context ) ;
2003-02-24 05:55:00 +03:00
2019-09-17 02:50:33 +03:00
return aret ;
2001-11-24 17:16:41 +03:00
}
2019-09-17 02:50:33 +03:00
ADS_STATUS kerberos_set_password ( const char * kpasswd_server ,
const char * auth_principal ,
const char * auth_password ,
const char * target_principal ,
const char * new_password , int time_offset )
2001-12-20 06:54:52 +03:00
{
2019-09-17 02:50:33 +03:00
int ret ;
if ( ( ret = kerberos_kinit_password ( auth_principal , auth_password , time_offset , NULL ) ) ) {
DEBUG ( 1 , ( " Failed kinit for principal %s (%s) \n " , auth_principal , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
if ( ! strcmp ( auth_principal , target_principal ) ) {
return ads_krb5_chg_password ( kpasswd_server , target_principal ,
auth_password , new_password ,
time_offset ) ;
} else {
return ads_krb5_set_password ( kpasswd_server , target_principal ,
new_password , time_offset ) ;
}
2001-12-20 06:54:52 +03:00
}
2001-11-24 17:16:41 +03:00
# endif