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
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"
2001-11-29 02:54:07 +03:00
# ifdef HAVE_KRB5
2001-11-24 17:16:41 +03:00
# define DEFAULT_KPASSWD_PORT 464
2003-02-24 05:55:00 +03:00
# define KRB5_KPASSWD_VERS_CHANGEPW 1
# define KRB5_KPASSWD_VERS_SETPW 2
# define KRB5_KPASSWD_VERS_SETPW_MS 0xff80
# define KRB5_KPASSWD_ACCESSDENIED 5
# define KRB5_KPASSWD_BAD_VERSION 6
# define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7
/* Those are defined by kerberos-set-passwd-02.txt and are probably
* not supported by M $ implementation */
# define KRB5_KPASSWD_POLICY_REJECT 8
# define KRB5_KPASSWD_BAD_PRINCIPAL 9
# define KRB5_KPASSWD_ETYPE_NOSUPP 10
/* This implements kerberos password change protocol as specified in
* kerb - chg - password - 02. txt and kerberos - set - passwd - 02. txt
* as well as microsoft version of the protocol
* as specified in kerberos - set - passwd - 00. txt
2001-11-24 17:16:41 +03:00
*/
2001-12-20 06:54:52 +03:00
static DATA_BLOB encode_krb5_setpw ( const char * principal , const char * password )
2001-11-24 17:16:41 +03:00
{
2001-12-20 06:54:52 +03:00
char * princ_part1 = NULL ;
char * princ_part2 = NULL ;
char * realm = NULL ;
char * c ;
char * princ ;
2001-11-24 17:16:41 +03:00
ASN1_DATA req ;
DATA_BLOB ret ;
2001-12-20 06:54:52 +03:00
princ = strdup ( principal ) ;
if ( ( c = strchr ( princ , ' / ' ) ) = = NULL ) {
c = princ ;
} else {
* c = ' \0 ' ;
c + + ;
princ_part1 = princ ;
}
princ_part2 = c ;
if ( ( c = strchr ( c , ' @ ' ) ) ! = NULL ) {
* c = ' \0 ' ;
c + + ;
realm = c ;
}
2001-11-24 17:16:41 +03:00
memset ( & req , 0 , sizeof ( req ) ) ;
asn1_push_tag ( & req , ASN1_SEQUENCE ( 0 ) ) ;
asn1_push_tag ( & req , ASN1_CONTEXT ( 0 ) ) ;
asn1_write_OctetString ( & req , password , strlen ( password ) ) ;
asn1_pop_tag ( & req ) ;
asn1_push_tag ( & req , ASN1_CONTEXT ( 1 ) ) ;
asn1_push_tag ( & req , ASN1_SEQUENCE ( 0 ) ) ;
asn1_push_tag ( & req , ASN1_CONTEXT ( 0 ) ) ;
asn1_write_Integer ( & req , 1 ) ;
asn1_pop_tag ( & req ) ;
asn1_push_tag ( & req , ASN1_CONTEXT ( 1 ) ) ;
asn1_push_tag ( & req , ASN1_SEQUENCE ( 0 ) ) ;
2001-12-20 06:54:52 +03:00
if ( princ_part1 )
asn1_write_GeneralString ( & req , princ_part1 ) ;
asn1_write_GeneralString ( & req , princ_part2 ) ;
2001-11-24 17:16:41 +03:00
asn1_pop_tag ( & req ) ;
asn1_pop_tag ( & req ) ;
asn1_pop_tag ( & req ) ;
asn1_pop_tag ( & req ) ;
asn1_push_tag ( & req , ASN1_CONTEXT ( 2 ) ) ;
asn1_write_GeneralString ( & req , realm ) ;
asn1_pop_tag ( & req ) ;
asn1_pop_tag ( & req ) ;
ret = data_blob ( req . data , req . length ) ;
asn1_free ( & req ) ;
2001-12-20 06:54:52 +03:00
free ( princ ) ;
2001-11-24 17:16:41 +03:00
return ret ;
}
2003-02-24 05:55:00 +03:00
static krb5_error_code build_kpasswd_request ( uint16 pversion ,
krb5_context context ,
2001-11-24 17:16:41 +03:00
krb5_auth_context auth_context ,
krb5_data * ap_req ,
2001-12-20 06:54:52 +03:00
const char * princ ,
2001-11-24 17:16:41 +03:00
const char * passwd ,
krb5_data * packet )
{
krb5_error_code ret ;
krb5_data cipherpw ;
krb5_data encoded_setpw ;
krb5_replay_data replay ;
char * p ;
DATA_BLOB setpw ;
ret = krb5_auth_con_setflags ( context ,
auth_context , KRB5_AUTH_CONTEXT_DO_SEQUENCE ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_auth_con_setflags failed (%s) \n " ,
error_message ( ret ) ) ) ;
return ret ;
}
2003-02-24 05:55:00 +03:00
/* handle protocol differences in chpw and setpw */
if ( pversion = = KRB5_KPASSWD_VERS_CHANGEPW )
setpw = data_blob ( passwd , strlen ( passwd ) ) ;
else if ( pversion = = KRB5_KPASSWD_VERS_SETPW | |
pversion = = KRB5_KPASSWD_VERS_SETPW_MS )
setpw = encode_krb5_setpw ( princ , passwd ) ;
else
return EINVAL ;
2001-11-24 17:16:41 +03:00
encoded_setpw . data = setpw . data ;
encoded_setpw . length = setpw . length ;
ret = krb5_mk_priv ( context , auth_context ,
& encoded_setpw , & cipherpw , & replay ) ;
2001-12-20 06:54:52 +03:00
data_blob_free ( & setpw ) ; /*from 'encode_krb5_setpw(...)' */
2001-11-24 17:16:41 +03:00
if ( ret ) {
DEBUG ( 1 , ( " krb5_mk_priv failed (%s) \n " , error_message ( ret ) ) ) ;
return ret ;
}
packet - > data = ( char * ) malloc ( ap_req - > length + cipherpw . length + 6 ) ;
2003-02-05 02:44:28 +03:00
if ( ! packet - > data )
return - 1 ;
2001-11-24 17:16:41 +03:00
/* see the RFC for details */
2003-01-21 09:23:49 +03:00
p = ( ( char * ) packet - > data ) + 2 ;
2003-02-24 05:55:00 +03:00
RSSVAL ( p , 0 , pversion ) ;
2003-02-05 02:44:28 +03:00
p + = 2 ;
RSSVAL ( p , 0 , ap_req - > length ) ;
p + = 2 ;
memcpy ( p , ap_req - > data , ap_req - > length ) ;
p + = ap_req - > length ;
memcpy ( p , cipherpw . data , cipherpw . length ) ;
p + = cipherpw . length ;
2001-11-24 17:16:41 +03:00
packet - > length = PTR_DIFF ( p , packet - > data ) ;
RSSVAL ( packet - > data , 0 , packet - > length ) ;
2001-12-20 06:54:52 +03:00
free ( cipherpw . data ) ; /* from 'krb5_mk_priv(...)' */
2001-11-24 17:16:41 +03:00
return 0 ;
}
2003-02-24 05:55:00 +03:00
static krb5_error_code krb5_setpw_result_code_string ( krb5_context context ,
int result_code ,
2003-03-05 04:34:34 +03:00
const char * * code_string )
2003-02-24 05:55:00 +03:00
{
switch ( result_code ) {
case KRB5_KPASSWD_MALFORMED :
* code_string = " Malformed request error " ;
break ;
case KRB5_KPASSWD_HARDERROR :
* code_string = " Server error " ;
break ;
case KRB5_KPASSWD_AUTHERROR :
* code_string = " Authentication error " ;
break ;
case KRB5_KPASSWD_SOFTERROR :
* code_string = " Password change rejected " ;
break ;
case KRB5_KPASSWD_ACCESSDENIED :
* code_string = " Client does not have proper authorization " ;
break ;
case KRB5_KPASSWD_BAD_VERSION :
* code_string = " Protocol version not supported " ;
break ;
case KRB5_KPASSWD_INITIAL_FLAG_NEEDED :
* code_string = " Authorization ticket must have initial flag set " ;
break ;
case KRB5_KPASSWD_POLICY_REJECT :
* code_string = " Password rejected due to policy requirements " ;
break ;
case KRB5_KPASSWD_BAD_PRINCIPAL :
* code_string = " Target principal does not exist " ;
break ;
case KRB5_KPASSWD_ETYPE_NOSUPP :
* code_string = " Unsupported encryption type " ;
break ;
default :
* code_string = " Password change failed " ;
break ;
}
return ( 0 ) ;
}
2001-11-24 17:16:41 +03:00
static krb5_error_code parse_setpw_reply ( krb5_context context ,
krb5_auth_context auth_context ,
krb5_data * packet )
{
krb5_data ap_rep ;
char * p ;
int vnum , ret , res_code ;
krb5_data cipherresult ;
krb5_data clearresult ;
krb5_ap_rep_enc_part * ap_rep_enc ;
krb5_replay_data replay ;
if ( packet - > length < 4 ) {
return KRB5KRB_AP_ERR_MODIFIED ;
}
p = packet - > data ;
2003-01-21 09:23:49 +03:00
if ( ( ( char * ) packet - > data ) [ 0 ] = = 0x7e | | ( ( char * ) packet - > data ) [ 0 ] = = 0x5e ) {
2001-11-24 17:16:41 +03:00
/* it's an error packet. We should parse it ... */
DEBUG ( 1 , ( " Got error packet 0x%x from kpasswd server \n " ,
2003-01-21 09:23:49 +03:00
( ( char * ) packet - > data ) [ 0 ] ) ) ;
2001-11-24 17:16:41 +03:00
return KRB5KRB_AP_ERR_MODIFIED ;
}
if ( RSVAL ( p , 0 ) ! = packet - > length ) {
DEBUG ( 1 , ( " Bad packet length (%d/%d) from kpasswd server \n " ,
RSVAL ( p , 0 ) , packet - > length ) ) ;
return KRB5KRB_AP_ERR_MODIFIED ;
}
p + = 2 ;
vnum = RSVAL ( p , 0 ) ; p + = 2 ;
2003-02-24 05:55:00 +03:00
/* FIXME: According to standard there is only one type of reply */
if ( vnum ! = KRB5_KPASSWD_VERS_SETPW & &
vnum ! = KRB5_KPASSWD_VERS_SETPW_MS & &
vnum ! = KRB5_KPASSWD_VERS_CHANGEPW ) {
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " Bad vnum (%d) from kpasswd server \n " , vnum ) ) ;
return KRB5KDC_ERR_BAD_PVNO ;
}
ap_rep . length = RSVAL ( p , 0 ) ; p + = 2 ;
2003-01-21 09:23:49 +03:00
if ( p + ap_rep . length > = ( char * ) packet - > data + packet - > length ) {
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " ptr beyond end of packet from kpasswd server \n " ) ) ;
return KRB5KRB_AP_ERR_MODIFIED ;
}
if ( ap_rep . length = = 0 ) {
DEBUG ( 1 , ( " got unencrypted setpw result?! \n " ) ) ;
return KRB5KRB_AP_ERR_MODIFIED ;
}
/* verify ap_rep */
ap_rep . data = p ;
p + = ap_rep . length ;
ret = krb5_rd_rep ( context , auth_context , & ap_rep , & ap_rep_enc ) ;
if ( ret ) {
DEBUG ( 1 , ( " failed to rd setpw reply (%s) \n " , error_message ( ret ) ) ) ;
return KRB5KRB_AP_ERR_MODIFIED ;
}
krb5_free_ap_rep_enc_part ( context , ap_rep_enc ) ;
cipherresult . data = p ;
2003-01-21 09:23:49 +03:00
cipherresult . length = ( ( char * ) packet - > data + packet - > length ) - p ;
2001-11-24 17:16:41 +03:00
ret = krb5_rd_priv ( context , auth_context , & cipherresult , & clearresult ,
& replay ) ;
if ( ret ) {
DEBUG ( 1 , ( " failed to decrypt setpw reply (%s) \n " , error_message ( ret ) ) ) ;
return KRB5KRB_AP_ERR_MODIFIED ;
}
if ( clearresult . length < 2 ) {
2001-12-20 06:54:52 +03:00
free ( clearresult . data ) ;
2001-11-24 17:16:41 +03:00
ret = KRB5KRB_AP_ERR_MODIFIED ;
return KRB5KRB_AP_ERR_MODIFIED ;
}
p = clearresult . data ;
res_code = RSVAL ( p , 0 ) ;
2001-12-20 06:54:52 +03:00
free ( clearresult . data ) ;
2001-11-24 17:16:41 +03:00
if ( ( res_code < KRB5_KPASSWD_SUCCESS ) | |
2003-02-24 05:55:00 +03:00
( res_code > KRB5_KPASSWD_ETYPE_NOSUPP ) ) {
2001-11-24 17:16:41 +03:00
return KRB5KRB_AP_ERR_MODIFIED ;
}
2003-02-24 05:55:00 +03:00
if ( res_code = = KRB5_KPASSWD_SUCCESS )
return 0 ;
else {
2003-03-05 04:34:34 +03:00
const char * errstr ;
2003-02-24 05:55:00 +03:00
krb5_setpw_result_code_string ( context , res_code , & errstr ) ;
DEBUG ( 1 , ( " Error changing password: %s \n " , errstr ) ) ;
switch ( res_code ) {
case KRB5_KPASSWD_ACCESSDENIED :
return KRB5KDC_ERR_BADOPTION ;
break ;
case KRB5_KPASSWD_INITIAL_FLAG_NEEDED :
return KRB5KDC_ERR_BADOPTION ;
/* return KV5M_ALT_METHOD; MIT-only define */
break ;
case KRB5_KPASSWD_ETYPE_NOSUPP :
return KRB5KDC_ERR_ETYPE_NOSUPP ;
break ;
case KRB5_KPASSWD_BAD_PRINCIPAL :
return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN ;
break ;
case KRB5_KPASSWD_POLICY_REJECT :
return KRB5KDC_ERR_POLICY ;
break ;
default :
return KRB5KRB_ERR_GENERIC ;
break ;
}
}
2001-11-24 17:16:41 +03:00
}
2003-02-24 05:55:00 +03:00
static ADS_STATUS do_krb5_kpasswd_request ( krb5_context context ,
const char * kdc_host ,
uint16 pversion ,
krb5_creds * credsp ,
const char * princ ,
const char * newpw )
2001-11-24 17:16:41 +03:00
{
krb5_auth_context auth_context = NULL ;
krb5_data ap_req , chpw_req , chpw_rep ;
int ret , sock , addr_len ;
struct sockaddr remote_addr , local_addr ;
krb5_address local_kaddr , remote_kaddr ;
ret = krb5_mk_req_extended ( context , & auth_context , AP_OPTS_USE_SUBKEY ,
NULL , credsp , & ap_req ) ;
if ( ret ) {
DEBUG ( 1 , ( " krb5_mk_req_extended failed (%s) \n " , error_message ( ret ) ) ) ;
2001-12-19 15:21:12 +03:00
return ADS_ERROR_KRB5 ( ret ) ;
2001-11-24 17:16:41 +03:00
}
sock = open_udp_socket ( kdc_host , DEFAULT_KPASSWD_PORT ) ;
if ( sock = = - 1 ) {
2001-12-19 15:21:12 +03:00
int rc = errno ;
2001-12-20 06:54:52 +03:00
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " failed to open kpasswd socket to %s (%s) \n " ,
kdc_host , strerror ( errno ) ) ) ;
2001-12-19 15:21:12 +03:00
return ADS_ERROR_SYSTEM ( rc ) ;
2001-11-24 17:16:41 +03:00
}
addr_len = sizeof ( remote_addr ) ;
getpeername ( sock , & remote_addr , & addr_len ) ;
addr_len = sizeof ( local_addr ) ;
getsockname ( sock , & local_addr , & addr_len ) ;
2003-01-21 09:23:49 +03:00
setup_kaddr ( & remote_kaddr , & remote_addr ) ;
setup_kaddr ( & local_kaddr , & local_addr ) ;
2001-11-24 17:16:41 +03:00
ret = krb5_auth_con_setaddrs ( context , auth_context , & local_kaddr , NULL ) ;
if ( ret ) {
2001-12-20 06:54:52 +03:00
close ( sock ) ;
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " krb5_auth_con_setaddrs failed (%s) \n " , error_message ( ret ) ) ) ;
2001-12-19 15:21:12 +03:00
return ADS_ERROR_KRB5 ( ret ) ;
2001-11-24 17:16:41 +03:00
}
2003-02-24 05:55:00 +03:00
ret = build_kpasswd_request ( pversion , context , auth_context , & ap_req ,
2001-12-20 06:54:52 +03:00
princ , newpw , & chpw_req ) ;
2001-11-24 17:16:41 +03:00
if ( ret ) {
2001-12-20 06:54:52 +03:00
close ( sock ) ;
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " build_setpw_request failed (%s) \n " , error_message ( ret ) ) ) ;
2001-12-19 15:21:12 +03:00
return ADS_ERROR_KRB5 ( ret ) ;
2001-11-24 17:16:41 +03:00
}
if ( write ( sock , chpw_req . data , chpw_req . length ) ! = chpw_req . length ) {
2001-12-20 06:54:52 +03:00
close ( sock ) ;
free ( chpw_req . data ) ;
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " send of chpw failed (%s) \n " , strerror ( errno ) ) ) ;
2001-12-31 14:14:38 +03:00
return ADS_ERROR_SYSTEM ( errno ) ;
2001-11-24 17:16:41 +03:00
}
free ( chpw_req . data ) ;
chpw_rep . length = 1500 ;
chpw_rep . data = ( char * ) malloc ( chpw_rep . length ) ;
2003-02-05 02:44:28 +03:00
if ( ! chpw_rep . data ) {
close ( sock ) ;
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2003-02-05 02:44:28 +03:00
DEBUG ( 1 , ( " send of chpw failed (%s) \n " , strerror ( errno ) ) ) ;
errno = ENOMEM ;
return ADS_ERROR_SYSTEM ( errno ) ;
}
2001-11-24 17:16:41 +03:00
ret = read ( sock , chpw_rep . data , chpw_rep . length ) ;
if ( ret < 0 ) {
2001-12-20 06:54:52 +03:00
close ( sock ) ;
free ( chpw_rep . data ) ;
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2001-12-20 06:54:52 +03:00
DEBUG ( 1 , ( " recv of chpw reply failed (%s) \n " , strerror ( errno ) ) ) ;
2001-12-19 15:21:12 +03:00
return ADS_ERROR_SYSTEM ( errno ) ;
2001-11-24 17:16:41 +03:00
}
close ( sock ) ;
chpw_rep . length = ret ;
ret = krb5_auth_con_setaddrs ( context , auth_context , NULL , & remote_kaddr ) ;
if ( ret ) {
2001-12-20 06:54:52 +03:00
free ( chpw_rep . data ) ;
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " krb5_auth_con_setaddrs on reply failed (%s) \n " ,
error_message ( ret ) ) ) ;
2001-12-19 15:21:12 +03:00
return ADS_ERROR_KRB5 ( ret ) ;
2001-11-24 17:16:41 +03:00
}
ret = parse_setpw_reply ( context , auth_context , & chpw_rep ) ;
free ( chpw_rep . data ) ;
if ( ret ) {
2001-12-20 06:54:52 +03:00
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
2001-11-24 17:16:41 +03:00
DEBUG ( 1 , ( " parse_setpw_reply failed (%s) \n " ,
error_message ( ret ) ) ) ;
2001-12-19 15:21:12 +03:00
return ADS_ERROR_KRB5 ( ret ) ;
2001-11-24 17:16:41 +03:00
}
2001-12-20 06:54:52 +03:00
free ( ap_req . data ) ;
2003-02-24 05:55:00 +03:00
krb5_auth_con_free ( context , auth_context ) ;
return ADS_SUCCESS ;
}
ADS_STATUS krb5_set_password ( const char * kdc_host , const char * princ , const char * newpw ,
int time_offset )
{
ADS_STATUS aret ;
krb5_error_code ret ;
krb5_context context ;
krb5_principal principal ;
char * princ_name ;
char * realm ;
krb5_creds creds , * credsp ;
krb5_ccache ccache ;
ret = krb5_init_context ( & context ) ;
if ( ret ) {
DEBUG ( 1 , ( " Failed to init krb5 context (%s) \n " , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
if ( time_offset ! = 0 ) {
krb5_set_real_time ( context , time ( NULL ) + time_offset , 0 ) ;
}
ret = krb5_cc_default ( context , & ccache ) ;
if ( ret ) {
krb5_free_context ( context ) ;
DEBUG ( 1 , ( " Failed to get default creds (%s) \n " , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
ZERO_STRUCT ( creds ) ;
realm = strchr ( princ , ' @ ' ) ;
realm + + ;
asprintf ( & princ_name , " kadmin/changepw@%s " , realm ) ;
ret = krb5_parse_name ( context , princ_name , & creds . server ) ;
if ( ret ) {
krb5_free_context ( context ) ;
DEBUG ( 1 , ( " Failed to parse kadmin/changepw (%s) \n " , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
free ( princ_name ) ;
/* parse the principal we got as a function argument */
ret = krb5_parse_name ( context , princ , & principal ) ;
if ( ret ) {
krb5_free_context ( context ) ;
DEBUG ( 1 , ( " Failed to parse %s (%s) \n " , princ_name , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
krb5_princ_set_realm ( context , creds . server ,
krb5_princ_realm ( context , principal ) ) ;
ret = krb5_cc_get_principal ( context , ccache , & creds . client ) ;
if ( ret ) {
krb5_free_principal ( context , principal ) ;
krb5_free_context ( context ) ;
DEBUG ( 1 , ( " Failed to get principal from ccache (%s) \n " ,
error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
ret = krb5_get_credentials ( context , 0 , ccache , & creds , & credsp ) ;
if ( ret ) {
krb5_free_principal ( context , creds . client ) ;
krb5_free_principal ( context , principal ) ;
krb5_free_context ( context ) ;
DEBUG ( 1 , ( " krb5_get_credentials failed (%s) \n " , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
/* we might have to call krb5_free_creds(...) from now on ... */
aret = do_krb5_kpasswd_request ( context , kdc_host ,
KRB5_KPASSWD_VERS_SETPW_MS ,
credsp , princ , newpw ) ;
2001-12-20 06:54:52 +03:00
krb5_free_creds ( context , credsp ) ;
krb5_free_principal ( context , creds . client ) ;
2003-02-24 05:55:00 +03:00
krb5_free_principal ( context , creds . server ) ;
2001-12-20 06:54:52 +03:00
krb5_free_principal ( context , principal ) ;
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 ) {
strncpy ( prompts [ 0 ] . reply - > data , data , prompts [ 0 ] . reply - > length - 1 ) ;
prompts [ 0 ] . reply - > length = strlen ( prompts [ 0 ] . reply - > data ) ;
} else {
prompts [ 0 ] . reply - > length = 0 ;
}
}
return 0 ;
}
ADS_STATUS krb5_chg_password ( const char * kdc_host ,
const char * principal ,
const char * oldpw ,
const char * newpw ,
int time_offset )
{
ADS_STATUS aret ;
krb5_error_code ret ;
krb5_context context ;
krb5_principal princ ;
krb5_get_init_creds_opt opts ;
krb5_creds creds ;
char * chpw_princ = NULL , * password ;
ret = krb5_init_context ( & context ) ;
if ( ret ) {
DEBUG ( 1 , ( " Failed to init krb5 context (%s) \n " , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
if ( ( ret = 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 ) ;
}
krb5_get_init_creds_opt_init ( & opts ) ;
krb5_get_init_creds_opt_set_tkt_life ( & opts , 5 * 60 ) ;
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 ) ;
/* We have to obtain an INITIAL changepw ticket for changing password */
asprintf ( & chpw_princ , " kadmin/changepw@%s " ,
( char * ) krb5_princ_realm ( context , princ ) ) ;
password = strdup ( oldpw ) ;
ret = krb5_get_init_creds_password ( context , & creds , princ , password ,
kerb_prompter , NULL ,
0 , chpw_princ , & opts ) ;
SAFE_FREE ( chpw_princ ) ;
SAFE_FREE ( password ) ;
if ( ret ) {
if ( ret = = KRB5KRB_AP_ERR_BAD_INTEGRITY )
DEBUG ( 1 , ( " Password incorrect while getting initial ticket " ) ) ;
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 ) ;
}
aret = do_krb5_kpasswd_request ( context , kdc_host ,
KRB5_KPASSWD_VERS_CHANGEPW ,
& creds , principal , newpw ) ;
krb5_free_principal ( context , princ ) ;
krb5_free_context ( context ) ;
return aret ;
2001-11-24 17:16:41 +03:00
}
2001-12-20 06:54:52 +03:00
ADS_STATUS kerberos_set_password ( const char * kpasswd_server ,
const char * auth_principal , const char * auth_password ,
2002-09-25 19:19:00 +04:00
const char * target_principal , const char * new_password ,
int time_offset )
2001-12-20 06:54:52 +03:00
{
int ret ;
2002-09-25 19:19:00 +04:00
if ( ( ret = kerberos_kinit_password ( auth_principal , auth_password , time_offset ) ) ) {
2001-12-20 06:54:52 +03:00
DEBUG ( 1 , ( " Failed kinit for principal %s (%s) \n " , auth_principal , error_message ( ret ) ) ) ;
return ADS_ERROR_KRB5 ( ret ) ;
}
2003-02-24 05:55:00 +03:00
if ( ! strcmp ( auth_principal , target_principal ) )
return krb5_chg_password ( kpasswd_server , target_principal ,
auth_password , new_password , time_offset ) ;
else
return krb5_set_password ( kpasswd_server , target_principal ,
new_password , time_offset ) ;
2001-12-20 06:54:52 +03:00
}
2002-10-01 22:26:00 +04:00
/**
* Set the machine account password
* @ param ads connection to ads server
* @ param hostname machine whose password is being set
* @ param password new password
* @ return status of password change
* */
ADS_STATUS ads_set_machine_password ( ADS_STRUCT * ads ,
const char * hostname ,
const char * password )
{
ADS_STATUS status ;
char * host = strdup ( hostname ) ;
char * principal ;
strlower ( host ) ;
/*
we need to use the ' $ ' form of the name here , as otherwise the
server might end up setting the password for a user instead
*/
asprintf ( & principal , " %s$@%s " , host , ads - > auth . realm ) ;
status = krb5_set_password ( ads - > auth . kdc_server , principal , password , ads - > auth . time_offset ) ;
free ( host ) ;
free ( principal ) ;
return status ;
}
2003-02-24 05:55:00 +03:00
2001-11-24 17:16:41 +03:00
# endif