2016-09-07 17:03:15 +03:00
/*
Unix SMB / CIFS implementation .
Samba kpasswd implementation
Copyright ( c ) 2016 Andreas Schneider < asn @ samba . org >
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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2020-11-20 17:27:17 +03:00
# include "samba/service_task.h"
2016-09-07 17:03:15 +03:00
# include "param/param.h"
# include "auth/auth.h"
# include "auth/gensec/gensec.h"
2022-05-18 07:52:41 +03:00
# include "gensec_krb5_helpers.h"
2016-09-07 17:03:15 +03:00
# include "kdc/kdc-server.h"
# include "kdc/kpasswd_glue.h"
# include "kdc/kpasswd-service.h"
# include "kdc/kpasswd-helper.h"
2022-09-09 13:32:57 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_KERBEROS
2016-09-07 17:03:15 +03:00
static krb5_error_code kpasswd_change_password ( struct kdc_server * kdc ,
TALLOC_CTX * mem_ctx ,
2022-05-18 07:52:41 +03:00
const struct gensec_security * gensec_security ,
2016-09-07 17:03:15 +03:00
struct auth_session_info * session_info ,
DATA_BLOB * password ,
DATA_BLOB * kpasswd_reply ,
const char * * error_string )
{
NTSTATUS status ;
NTSTATUS result = NT_STATUS_UNSUCCESSFUL ;
2024-05-06 03:20:44 +03:00
enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR ;
2016-09-07 17:03:15 +03:00
const char * reject_string = NULL ;
2024-05-06 03:17:20 +03:00
struct samr_DomInfo1 * dominfo = NULL ;
2016-09-07 17:03:15 +03:00
bool ok ;
2022-05-18 07:52:41 +03:00
int ret ;
/*
* We ' re doing a password change ( rather than a password set ) , so check
* that we were given an initial ticket .
*/
ret = gensec_krb5_initial_ticket ( gensec_security ) ;
if ( ret ! = 1 ) {
* error_string = " Expected an initial ticket " ;
return KRB5_KPASSWD_INITIAL_FLAG_NEEDED ;
}
2016-09-07 17:03:15 +03:00
status = samdb_kpasswd_change_password ( mem_ctx ,
kdc - > task - > lp_ctx ,
kdc - > task - > event_ctx ,
session_info ,
password ,
& reject_reason ,
& dominfo ,
& reject_string ,
& result ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ok = kpasswd_make_error_reply ( mem_ctx ,
KRB5_KPASSWD_ACCESSDENIED ,
reject_string ,
kpasswd_reply ) ;
if ( ! ok ) {
* error_string = " Failed to create reply " ;
return KRB5_KPASSWD_HARDERROR ;
}
/* We want to send an an authenticated packet. */
return 0 ;
}
ok = kpasswd_make_pwchange_reply ( mem_ctx ,
result ,
reject_reason ,
dominfo ,
kpasswd_reply ) ;
if ( ! ok ) {
* error_string = " Failed to create reply " ;
return KRB5_KPASSWD_HARDERROR ;
}
return 0 ;
}
static krb5_error_code kpasswd_set_password ( struct kdc_server * kdc ,
TALLOC_CTX * mem_ctx ,
2022-05-18 07:52:41 +03:00
const struct gensec_security * gensec_security ,
2016-09-07 17:03:15 +03:00
struct auth_session_info * session_info ,
DATA_BLOB * decoded_data ,
DATA_BLOB * kpasswd_reply ,
const char * * error_string )
{
krb5_context context = kdc - > smb_krb5_context - > krb5_context ;
krb5_error_code code ;
krb5_principal target_principal ;
ChangePasswdDataMS chpw = { } ;
size_t chpw_len = 0 ;
DATA_BLOB password = data_blob_null ;
enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR ;
struct samr_DomInfo1 * dominfo = NULL ;
char * target_principal_string = NULL ;
bool is_service_principal = false ;
NTSTATUS status ;
bool ok ;
code = decode_ChangePasswdDataMS ( decoded_data - > data ,
decoded_data - > length ,
& chpw ,
& chpw_len ) ;
if ( code ! = 0 ) {
DBG_WARNING ( " decode_ChangePasswdDataMS failed \n " ) ;
ok = kpasswd_make_error_reply ( mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" Failed to decode packet " ,
kpasswd_reply ) ;
if ( ! ok ) {
* error_string = " Failed to create reply " ;
return KRB5_KPASSWD_HARDERROR ;
}
return 0 ;
}
ok = convert_string_talloc_handle ( mem_ctx ,
lpcfg_iconv_handle ( kdc - > task - > lp_ctx ) ,
CH_UTF8 ,
CH_UTF16 ,
2023-08-09 07:54:38 +03:00
chpw . newpasswd . data ,
2016-09-07 17:03:15 +03:00
chpw . newpasswd . length ,
2023-08-09 07:54:38 +03:00
& password . data ,
2016-09-07 17:03:15 +03:00
& password . length ) ;
if ( ! ok ) {
free_ChangePasswdDataMS ( & chpw ) ;
DBG_WARNING ( " String conversion failed \n " ) ;
* error_string = " String conversion failed " ;
return KRB5_KPASSWD_HARDERROR ;
}
if ( ( chpw . targname ! = NULL & & chpw . targrealm = = NULL ) | |
( chpw . targname = = NULL & & chpw . targrealm ! = NULL ) ) {
free_ChangePasswdDataMS ( & chpw ) ;
ok = kpasswd_make_error_reply ( mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" Realm and principal must be "
" both present, or neither present " ,
kpasswd_reply ) ;
if ( ! ok ) {
* error_string = " Failed to create reply " ;
return KRB5_KPASSWD_HARDERROR ;
}
return 0 ;
}
2022-05-18 08:11:49 +03:00
if ( chpw . targname = = NULL | | chpw . targrealm = = NULL ) {
2016-09-07 17:03:15 +03:00
free_ChangePasswdDataMS ( & chpw ) ;
return kpasswd_change_password ( kdc ,
mem_ctx ,
2022-05-18 07:52:41 +03:00
gensec_security ,
2016-09-07 17:03:15 +03:00
session_info ,
& password ,
kpasswd_reply ,
error_string ) ;
}
2022-05-18 08:11:49 +03:00
code = krb5_build_principal_ext ( context ,
& target_principal ,
strlen ( * chpw . targrealm ) ,
* chpw . targrealm ,
0 ) ;
if ( code ! = 0 ) {
free_ChangePasswdDataMS ( & chpw ) ;
return kpasswd_make_error_reply ( mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" Failed to parse principal " ,
kpasswd_reply ) ;
}
code = copy_PrincipalName ( chpw . targname ,
& target_principal - > name ) ;
2016-09-07 17:03:15 +03:00
free_ChangePasswdDataMS ( & chpw ) ;
2022-05-18 08:11:49 +03:00
if ( code ! = 0 ) {
krb5_free_principal ( context , target_principal ) ;
return kpasswd_make_error_reply ( mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" Failed to parse principal " ,
kpasswd_reply ) ;
}
2016-09-07 17:03:15 +03:00
if ( target_principal - > name . name_string . len > = 2 ) {
is_service_principal = true ;
code = krb5_unparse_name_short ( context ,
target_principal ,
& target_principal_string ) ;
} else {
code = krb5_unparse_name ( context ,
target_principal ,
& target_principal_string ) ;
}
krb5_free_principal ( context , target_principal ) ;
if ( code ! = 0 ) {
ok = kpasswd_make_error_reply ( mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" Failed to parse principal " ,
kpasswd_reply ) ;
if ( ! ok ) {
2024-05-06 03:19:18 +03:00
krb5_xfree ( target_principal_string ) ;
2016-09-07 17:03:15 +03:00
* error_string = " Failed to create reply " ;
return KRB5_KPASSWD_HARDERROR ;
}
}
status = kpasswd_samdb_set_password ( mem_ctx ,
kdc - > task - > event_ctx ,
kdc - > task - > lp_ctx ,
session_info ,
is_service_principal ,
target_principal_string ,
& password ,
& reject_reason ,
& dominfo ) ;
2024-05-06 03:19:18 +03:00
krb5_xfree ( target_principal_string ) ;
2016-09-07 17:03:15 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " kpasswd_samdb_set_password failed - %s \n " ,
nt_errstr ( status ) ) ;
}
ok = kpasswd_make_pwchange_reply ( mem_ctx ,
status ,
reject_reason ,
dominfo ,
kpasswd_reply ) ;
if ( ! ok ) {
* error_string = " Failed to create reply " ;
return KRB5_KPASSWD_HARDERROR ;
}
return 0 ;
}
krb5_error_code kpasswd_handle_request ( struct kdc_server * kdc ,
TALLOC_CTX * mem_ctx ,
struct gensec_security * gensec_security ,
uint16_t verno ,
DATA_BLOB * decoded_data ,
DATA_BLOB * kpasswd_reply ,
const char * * error_string )
{
struct auth_session_info * session_info ;
NTSTATUS status ;
2022-06-10 10:18:53 +03:00
krb5_error_code code ;
2016-09-07 17:03:15 +03:00
status = gensec_session_info ( gensec_security ,
mem_ctx ,
& session_info ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
* error_string = talloc_asprintf ( mem_ctx ,
" gensec_session_info failed - %s " ,
nt_errstr ( status ) ) ;
return KRB5_KPASSWD_HARDERROR ;
}
2022-06-10 10:18:53 +03:00
/*
* Since the kpasswd service shares its keys with the krbtgt , we might
* have received a TGT rather than a kpasswd ticket . We need to check
* the ticket type to ensure that TGTs cannot be misused in this manner .
*/
code = kpasswd_check_non_tgt ( session_info ,
error_string ) ;
if ( code ! = 0 ) {
DBG_WARNING ( " %s \n " , * error_string ) ;
return code ;
}
2016-09-07 17:03:15 +03:00
switch ( verno ) {
case KRB5_KPASSWD_VERS_CHANGEPW : {
DATA_BLOB password = data_blob_null ;
bool ok ;
ok = convert_string_talloc_handle ( mem_ctx ,
lpcfg_iconv_handle ( kdc - > task - > lp_ctx ) ,
CH_UTF8 ,
CH_UTF16 ,
2023-08-09 07:54:38 +03:00
decoded_data - > data ,
2016-09-07 17:03:15 +03:00
decoded_data - > length ,
2023-08-09 07:54:38 +03:00
& password . data ,
2016-09-07 17:03:15 +03:00
& password . length ) ;
if ( ! ok ) {
* error_string = " String conversion failed! " ;
DBG_WARNING ( " %s \n " , * error_string ) ;
return KRB5_KPASSWD_HARDERROR ;
}
return kpasswd_change_password ( kdc ,
mem_ctx ,
2022-05-18 07:52:41 +03:00
gensec_security ,
2016-09-07 17:03:15 +03:00
session_info ,
& password ,
kpasswd_reply ,
error_string ) ;
}
case KRB5_KPASSWD_VERS_SETPW : {
return kpasswd_set_password ( kdc ,
mem_ctx ,
2022-05-18 07:52:41 +03:00
gensec_security ,
2016-09-07 17:03:15 +03:00
session_info ,
decoded_data ,
kpasswd_reply ,
error_string ) ;
}
default :
* error_string = talloc_asprintf ( mem_ctx ,
" Protocol version %u not supported " ,
verno ) ;
return KRB5_KPASSWD_BAD_VERSION ;
}
return 0 ;
}