2009-12-23 23:17:16 +03:00
/*
2005-10-21 05:25:55 +04:00
Unix SMB / CIFS implementation .
kpasswd Server implementation
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2005
Copyright ( C ) Andrew Tridgell 2005
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-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-10-21 05:25:55 +04:00
( at your option ) any later version .
2009-12-23 23:17:16 +03:00
2005-10-21 05:25:55 +04:00
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 .
2009-12-23 23:17:16 +03:00
2005-10-21 05:25:55 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-10-21 05:25:55 +04:00
*/
# include "includes.h"
# include "smbd/service_task.h"
2006-11-07 03:48:36 +03:00
# include "auth/gensec/gensec.h"
# include "auth/credentials/credentials.h"
2005-10-21 05:25:55 +04:00
# include "auth/auth.h"
2005-12-28 18:38:36 +03:00
# include "dsdb/samdb/samdb.h"
2010-10-17 16:27:18 +04:00
# include "../lib/util/util_ldb.h"
2006-04-29 21:34:49 +04:00
# include "libcli/security/security.h"
2007-09-28 05:17:46 +04:00
# include "param/param.h"
2016-06-10 12:10:50 +03:00
# include "kdc/kdc-server.h"
2010-11-11 06:09:41 +03:00
# include "kdc/kdc-glue.h"
2011-09-28 23:23:38 +04:00
# include "dsdb/common/util.h"
2014-08-05 19:49:55 +04:00
# include "kdc/kpasswd_glue.h"
2005-10-21 05:25:55 +04:00
/* Return true if there is a valid error packet formed in the error_blob */
2009-12-23 23:17:16 +03:00
static bool kpasswdd_make_error_reply ( struct kdc_server * kdc ,
TALLOC_CTX * mem_ctx ,
uint16_t result_code ,
const char * error_string ,
DATA_BLOB * error_blob )
2005-10-21 05:25:55 +04:00
{
char * error_string_utf8 ;
2009-03-02 00:24:34 +03:00
size_t len ;
2009-12-23 23:17:16 +03:00
2005-10-21 05:25:55 +04:00
DEBUG ( result_code ? 3 : 10 , ( " kpasswdd: %s \n " , error_string ) ) ;
2009-03-02 00:24:34 +03:00
if ( ! push_utf8_talloc ( mem_ctx , & error_string_utf8 , error_string , & len ) ) {
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
* error_blob = data_blob_talloc ( mem_ctx , NULL , 2 + len + 1 ) ;
if ( ! error_blob - > data ) {
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
RSSVAL ( error_blob - > data , 0 , result_code ) ;
memcpy ( error_blob - > data + 2 , error_string_utf8 , len + 1 ) ;
2007-10-07 01:42:58 +04:00
return true ;
2005-10-21 05:25:55 +04:00
}
/* Return true if there is a valid error packet formed in the error_blob */
2009-12-23 23:17:16 +03:00
static bool kpasswdd_make_unauth_error_reply ( struct kdc_server * kdc ,
TALLOC_CTX * mem_ctx ,
uint16_t result_code ,
const char * error_string ,
DATA_BLOB * error_blob )
2005-10-21 05:25:55 +04:00
{
2007-10-07 01:42:58 +04:00
bool ret ;
2005-10-21 05:25:55 +04:00
int kret ;
DATA_BLOB error_bytes ;
krb5_data k5_error_bytes , k5_error_blob ;
2009-12-23 23:17:16 +03:00
ret = kpasswdd_make_error_reply ( kdc , mem_ctx , result_code , error_string ,
2005-10-21 05:25:55 +04:00
& error_bytes ) ;
if ( ! ret ) {
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
k5_error_bytes . data = error_bytes . data ;
k5_error_bytes . length = error_bytes . length ;
2016-06-14 17:31:32 +03:00
kret = smb_krb5_mk_error ( kdc - > smb_krb5_context - > krb5_context ,
result_code ,
NULL ,
& k5_error_bytes ,
& k5_error_blob ) ;
2005-10-21 05:25:55 +04:00
if ( kret ) {
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
* error_blob = data_blob_talloc ( mem_ctx , k5_error_blob . data , k5_error_blob . length ) ;
2016-08-26 12:51:52 +03:00
smb_krb5_free_data_contents ( kdc - > smb_krb5_context - > krb5_context ,
2016-06-14 17:31:32 +03:00
& k5_error_blob ) ;
2005-10-21 05:25:55 +04:00
if ( ! error_blob - > data ) {
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
2007-10-07 01:42:58 +04:00
return true ;
2005-10-21 05:25:55 +04:00
}
2009-12-23 23:17:16 +03:00
static bool kpasswd_make_pwchange_reply ( struct kdc_server * kdc ,
TALLOC_CTX * mem_ctx ,
NTSTATUS status ,
2009-09-26 00:44:00 +04:00
enum samPwdChangeReason reject_reason ,
2005-10-21 05:25:55 +04:00
struct samr_DomInfo1 * dominfo ,
2009-12-23 23:17:16 +03:00
DATA_BLOB * error_blob )
2005-10-21 05:25:55 +04:00
{
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NO_SUCH_USER ) ) {
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_ACCESSDENIED ,
" No such user when changing password " ,
error_blob ) ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) ) {
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_ACCESSDENIED ,
" Not permitted to change password " ,
error_blob ) ;
}
2006-04-02 15:15:59 +04:00
if ( dominfo & & NT_STATUS_EQUAL ( status , NT_STATUS_PASSWORD_RESTRICTION ) ) {
2005-10-21 05:25:55 +04:00
const char * reject_string ;
switch ( reject_reason ) {
2009-09-26 00:44:00 +04:00
case SAM_PWD_CHANGE_PASSWORD_TOO_SHORT :
2012-09-01 05:34:33 +04:00
reject_string = talloc_asprintf ( mem_ctx , " Password too short, password must be at least %d characters long. " ,
2005-10-21 05:25:55 +04:00
dominfo - > min_password_length ) ;
break ;
2009-09-26 00:44:00 +04:00
case SAM_PWD_CHANGE_NOT_COMPLEX :
2005-10-21 05:25:55 +04:00
reject_string = " Password does not meet complexity requirements " ;
break ;
2009-09-26 00:44:00 +04:00
case SAM_PWD_CHANGE_PWD_IN_HISTORY :
2012-09-01 05:34:33 +04:00
reject_string = talloc_asprintf ( mem_ctx , " Password is already in password history. New password must not match any of your %d previous passwords. " ,
2012-08-31 08:02:28 +04:00
dominfo - > password_history_length ) ;
2006-09-19 01:00:00 +04:00
break ;
2005-11-01 16:29:22 +03:00
default :
2012-09-01 05:34:33 +04:00
reject_string = " Password change rejected, password changes may not be permitted on this account, or the minimum password age may not have elapsed. " ;
2005-10-21 05:25:55 +04:00
break ;
}
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_SOFTERROR ,
reject_string ,
error_blob ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_HARDERROR ,
talloc_asprintf ( mem_ctx , " failed to set password: %s " , nt_errstr ( status ) ) ,
error_blob ) ;
2009-12-23 23:17:16 +03:00
2005-10-21 05:25:55 +04:00
}
return kpasswdd_make_error_reply ( kdc , mem_ctx , KRB5_KPASSWD_SUCCESS ,
" Password changed " ,
error_blob ) ;
}
2009-12-23 23:17:16 +03:00
/*
2005-10-21 05:25:55 +04:00
A user password change
2009-12-23 23:17:16 +03:00
2010-02-21 09:46:46 +03:00
Return true if there is a valid error packet ( or success ) formed in
2005-10-21 05:25:55 +04:00
the error_blob
*/
2007-10-07 01:42:58 +04:00
static bool kpasswdd_change_password ( struct kdc_server * kdc ,
2009-12-23 23:17:16 +03:00
TALLOC_CTX * mem_ctx ,
2005-10-21 05:25:55 +04:00
struct auth_session_info * session_info ,
2008-10-16 05:48:16 +04:00
const DATA_BLOB * password ,
2005-10-21 05:25:55 +04:00
DATA_BLOB * reply )
{
NTSTATUS status ;
2014-08-05 19:49:55 +04:00
NTSTATUS result = NT_STATUS_UNSUCCESSFUL ;
2009-09-26 00:44:00 +04:00
enum samPwdChangeReason reject_reason ;
2005-10-21 05:25:55 +04:00
struct samr_DomInfo1 * dominfo ;
2014-08-05 19:49:55 +04:00
const char * error_string ;
status = samdb_kpasswd_change_password ( mem_ctx ,
kdc - > task - > lp_ctx ,
kdc - > task - > event_ctx ,
kdc - > samdb ,
session_info ,
password ,
& reject_reason ,
& dominfo ,
& error_string ,
& result ) ;
2010-07-06 20:16:32 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-08-05 19:49:55 +04:00
return kpasswdd_make_error_reply ( kdc ,
mem_ctx ,
KRB5_KPASSWD_ACCESSDENIED ,
error_string ,
reply ) ;
2005-10-21 05:25:55 +04:00
}
2009-12-23 23:17:16 +03:00
2014-08-05 19:49:55 +04:00
return kpasswd_make_pwchange_reply ( kdc ,
mem_ctx ,
result ,
2005-10-21 05:25:55 +04:00
reject_reason ,
2009-12-23 23:17:16 +03:00
dominfo ,
2005-10-21 05:25:55 +04:00
reply ) ;
}
2007-10-07 01:42:58 +04:00
static bool kpasswd_process_request ( struct kdc_server * kdc ,
2009-12-23 23:17:16 +03:00
TALLOC_CTX * mem_ctx ,
2005-10-21 05:25:55 +04:00
struct gensec_security * gensec_security ,
uint16_t version ,
2009-12-23 23:17:16 +03:00
DATA_BLOB * input ,
2005-10-21 05:25:55 +04:00
DATA_BLOB * reply )
{
struct auth_session_info * session_info ;
2009-03-01 21:55:46 +03:00
size_t pw_len ;
2008-10-16 05:48:16 +04:00
2009-12-23 23:17:16 +03:00
if ( ! NT_STATUS_IS_OK ( gensec_session_info ( gensec_security ,
2011-08-01 09:39:01 +04:00
mem_ctx ,
2005-10-21 05:25:55 +04:00
& session_info ) ) ) {
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_HARDERROR ,
" gensec_session_info failed! " ,
reply ) ;
}
switch ( version ) {
case KRB5_KPASSWD_VERS_CHANGEPW :
{
2008-10-16 05:48:16 +04:00
DATA_BLOB password ;
2011-03-25 00:37:00 +03:00
if ( ! convert_string_talloc_handle ( mem_ctx , lpcfg_iconv_handle ( kdc - > task - > lp_ctx ) ,
2009-12-23 23:17:16 +03:00
CH_UTF8 , CH_UTF16 ,
( const char * ) input - > data ,
2008-10-16 05:48:16 +04:00
input - > length ,
2011-03-24 02:59:41 +03:00
( void * * ) & password . data , & pw_len ) ) {
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
2008-10-16 05:48:16 +04:00
password . length = pw_len ;
2009-12-23 23:17:16 +03:00
return kpasswdd_change_password ( kdc , mem_ctx , session_info ,
2008-10-16 05:48:16 +04:00
& password , reply ) ;
2005-10-21 05:25:55 +04:00
}
case KRB5_KPASSWD_VERS_SETPW :
{
2006-01-03 01:00:40 +03:00
NTSTATUS status ;
2009-09-26 00:44:00 +04:00
enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR ;
2006-04-02 15:15:59 +04:00
struct samr_DomInfo1 * dominfo = NULL ;
2006-01-03 01:00:40 +03:00
struct ldb_context * samdb ;
krb5_context context = kdc - > smb_krb5_context - > krb5_context ;
2005-10-21 05:25:55 +04:00
ChangePasswdDataMS chpw ;
2008-10-16 05:48:16 +04:00
DATA_BLOB password ;
2006-01-03 01:00:40 +03:00
2005-10-21 05:25:55 +04:00
krb5_principal principal ;
char * set_password_on_princ ;
struct ldb_dn * set_password_on_dn ;
2010-03-25 08:27:40 +03:00
bool service_principal_name = false ;
2005-10-21 05:25:55 +04:00
2006-01-03 01:00:40 +03:00
size_t len ;
int ret ;
2005-10-21 05:25:55 +04:00
ret = decode_ChangePasswdDataMS ( input - > data , input - > length ,
& chpw , & len ) ;
if ( ret ) {
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_MALFORMED ,
" failed to decode password change structure " ,
reply ) ;
}
2009-12-23 23:17:16 +03:00
2011-03-25 00:37:00 +03:00
if ( ! convert_string_talloc_handle ( mem_ctx , lpcfg_iconv_handle ( kdc - > task - > lp_ctx ) ,
2009-12-23 23:17:16 +03:00
CH_UTF8 , CH_UTF16 ,
( const char * ) chpw . newpasswd . data ,
2008-10-16 05:48:16 +04:00
chpw . newpasswd . length ,
2011-03-24 02:59:41 +03:00
( void * * ) & password . data , & pw_len ) ) {
2005-10-21 05:25:55 +04:00
free_ChangePasswdDataMS ( & chpw ) ;
2007-10-07 01:42:58 +04:00
return false ;
2005-10-21 05:25:55 +04:00
}
2009-12-23 23:17:16 +03:00
2008-10-16 05:48:16 +04:00
password . length = pw_len ;
2009-12-23 23:17:16 +03:00
if ( ( chpw . targname & & ! chpw . targrealm )
2005-10-21 05:25:55 +04:00
| | ( ! chpw . targname & & chpw . targrealm ) ) {
2010-12-04 18:38:45 +03:00
free_ChangePasswdDataMS ( & chpw ) ;
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_MALFORMED ,
" Realm and principal must be both present, or neither present " ,
reply ) ;
}
if ( chpw . targname & & chpw . targrealm ) {
2010-12-04 18:38:45 +03:00
ret = krb5_build_principal_ext ( kdc - > smb_krb5_context - > krb5_context ,
& principal ,
strlen ( * chpw . targrealm ) ,
* chpw . targrealm , 0 ) ;
if ( ret ) {
free_ChangePasswdDataMS ( & chpw ) ;
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" failed to get principal " ,
reply ) ;
}
2010-11-12 07:37:07 +03:00
if ( copy_PrincipalName ( chpw . targname , & principal - > name ) ) {
2005-10-21 05:25:55 +04:00
free_ChangePasswdDataMS ( & chpw ) ;
2010-12-04 18:38:45 +03:00
krb5_free_principal ( context , principal ) ;
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_MALFORMED ,
" failed to extract principal to set " ,
reply ) ;
}
} else {
free_ChangePasswdDataMS ( & chpw ) ;
2009-12-23 23:17:16 +03:00
return kpasswdd_change_password ( kdc , mem_ctx , session_info ,
2008-10-16 05:48:16 +04:00
& password , reply ) ;
2005-10-21 05:25:55 +04:00
}
free_ChangePasswdDataMS ( & chpw ) ;
2010-03-25 08:27:40 +03:00
if ( principal - > name . name_string . len > = 2 ) {
service_principal_name = true ;
2009-12-23 23:17:16 +03:00
2010-03-25 08:27:40 +03:00
/* We use this, rather than 'no realm' flag,
* as we don ' t want to accept a password
* change on a principal from another realm */
if ( krb5_unparse_name_short ( context , principal , & set_password_on_princ ) ! = 0 ) {
krb5_free_principal ( context , principal ) ;
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" krb5_unparse_name failed! " ,
reply ) ;
}
} else {
if ( krb5_unparse_name ( context , principal , & set_password_on_princ ) ! = 0 ) {
krb5_free_principal ( context , principal ) ;
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
KRB5_KPASSWD_MALFORMED ,
" krb5_unparse_name failed! " ,
reply ) ;
}
}
2005-10-21 05:25:55 +04:00
krb5_free_principal ( context , principal ) ;
2009-12-23 23:17:16 +03:00
2010-10-10 19:00:45 +04:00
samdb = samdb_connect ( mem_ctx , kdc - > task - > event_ctx , kdc - > task - > lp_ctx , session_info , 0 ) ;
2006-01-03 01:00:40 +03:00
if ( ! samdb ) {
2010-12-04 18:38:45 +03:00
free ( set_password_on_princ ) ;
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2006-01-03 01:00:40 +03:00
KRB5_KPASSWD_HARDERROR ,
" Unable to open database! " ,
reply ) ;
}
2009-12-23 23:17:16 +03:00
DEBUG ( 3 , ( " %s \\ %s (%s) is changing password of %s \n " ,
2011-02-08 08:53:13 +03:00
session_info - > info - > domain_name ,
session_info - > info - > account_name ,
2010-08-20 06:15:15 +04:00
dom_sid_string ( mem_ctx , & session_info - > security_token - > sids [ PRIMARY_USER_SID_INDEX ] ) ,
2006-01-03 01:00:40 +03:00
set_password_on_princ ) ) ;
ret = ldb_transaction_start ( samdb ) ;
2009-09-26 14:09:07 +04:00
if ( ret ! = LDB_SUCCESS ) {
2010-12-04 18:38:45 +03:00
free ( set_password_on_princ ) ;
2006-01-03 01:00:40 +03:00
status = NT_STATUS_TRANSACTION_ABORTED ;
2009-12-23 23:17:16 +03:00
return kpasswd_make_pwchange_reply ( kdc , mem_ctx ,
2006-01-03 01:00:40 +03:00
status ,
2009-09-26 00:44:00 +04:00
SAM_PWD_CHANGE_NO_ERROR ,
2009-12-23 23:17:16 +03:00
NULL ,
2006-01-03 01:00:40 +03:00
reply ) ;
}
2010-03-25 08:27:40 +03:00
if ( service_principal_name ) {
status = crack_service_principal_name ( samdb , mem_ctx ,
set_password_on_princ ,
& set_password_on_dn , NULL ) ;
} else {
status = crack_user_principal_name ( samdb , mem_ctx ,
set_password_on_princ ,
& set_password_on_dn , NULL ) ;
}
2005-10-21 05:25:55 +04:00
free ( set_password_on_princ ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2006-01-03 01:00:40 +03:00
ldb_transaction_cancel ( samdb ) ;
2009-12-23 23:17:16 +03:00
return kpasswd_make_pwchange_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
status ,
2009-09-26 00:44:00 +04:00
SAM_PWD_CHANGE_NO_ERROR ,
2009-12-23 23:17:16 +03:00
NULL ,
2005-10-21 05:25:55 +04:00
reply ) ;
}
2006-01-03 01:00:40 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
/* Admin password set */
status = samdb_set_password ( samdb , mem_ctx ,
set_password_on_dn , NULL ,
2009-09-26 14:09:07 +04:00
& password , NULL , NULL ,
2010-08-15 23:06:11 +04:00
NULL , NULL , /* this is not a user password change */
2006-01-03 01:00:40 +03:00
& reject_reason , & dominfo ) ;
}
2005-10-21 05:25:55 +04:00
2006-01-03 01:00:40 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
ret = ldb_transaction_commit ( samdb ) ;
2009-09-26 14:09:07 +04:00
if ( ret ! = LDB_SUCCESS ) {
2006-01-03 01:00:40 +03:00
DEBUG ( 1 , ( " Failed to commit transaction to set password on %s: %s \n " ,
2009-09-26 14:09:07 +04:00
ldb_dn_get_linearized ( set_password_on_dn ) ,
2006-01-03 01:00:40 +03:00
ldb_errstring ( samdb ) ) ) ;
status = NT_STATUS_TRANSACTION_ABORTED ;
}
} else {
ldb_transaction_cancel ( samdb ) ;
}
2009-12-23 23:17:16 +03:00
return kpasswd_make_pwchange_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
status ,
2009-12-23 23:17:16 +03:00
reject_reason ,
dominfo ,
2005-10-21 05:25:55 +04:00
reply ) ;
}
default :
2009-12-23 23:17:16 +03:00
return kpasswdd_make_error_reply ( kdc , mem_ctx ,
2006-01-03 01:00:40 +03:00
KRB5_KPASSWD_BAD_VERSION ,
2009-12-23 23:17:16 +03:00
talloc_asprintf ( mem_ctx ,
" Protocol version %u not supported " ,
2006-01-03 01:00:40 +03:00
version ) ,
reply ) ;
2005-10-21 05:25:55 +04:00
}
}
2016-06-10 10:50:45 +03:00
kdc_code kpasswdd_process ( struct kdc_server * kdc ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * input ,
DATA_BLOB * reply ,
struct tsocket_address * peer_addr ,
struct tsocket_address * my_addr ,
int datagram_reply )
2005-10-21 05:25:55 +04:00
{
2007-10-07 01:42:58 +04:00
bool ret ;
2005-10-21 05:25:55 +04:00
const uint16_t header_len = 6 ;
uint16_t len ;
uint16_t ap_req_len ;
uint16_t krb_priv_len ;
uint16_t version ;
NTSTATUS nt_status ;
2006-04-02 15:15:59 +04:00
DATA_BLOB ap_req , krb_priv_req ;
DATA_BLOB krb_priv_rep = data_blob ( NULL , 0 ) ;
DATA_BLOB ap_rep = data_blob ( NULL , 0 ) ;
2005-10-21 05:25:55 +04:00
DATA_BLOB kpasswd_req , kpasswd_rep ;
struct cli_credentials * server_credentials ;
struct gensec_security * gensec_security ;
TALLOC_CTX * tmp_ctx = talloc_new ( mem_ctx ) ;
2009-07-27 10:09:25 +04:00
char * keytab_name ;
2005-10-21 05:25:55 +04:00
if ( ! tmp_ctx ) {
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2010-11-12 09:23:34 +03:00
if ( kdc - > am_rodc ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_PROXY_REQUEST ;
2010-11-12 09:23:34 +03:00
}
2006-01-03 01:00:40 +03:00
/* Be parinoid. We need to ensure we don't just let the
* caller lead us into a buffer overflow */
2005-10-21 05:25:55 +04:00
if ( input - > length < = header_len ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
len = RSVAL ( input - > data , 0 ) ;
if ( input - > length ! = len ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2006-01-03 01:00:40 +03:00
/* There are two different versions of this protocol so far,
* plus others in the standards pipe . Fortunetly they all
* take a very similar framing */
2005-10-21 05:25:55 +04:00
version = RSVAL ( input - > data , 2 ) ;
ap_req_len = RSVAL ( input - > data , 4 ) ;
if ( ( ap_req_len > = len ) | | ( ap_req_len + header_len ) > = len ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2009-12-23 23:17:16 +03:00
2005-10-21 05:25:55 +04:00
krb_priv_len = len - ap_req_len ;
ap_req = data_blob_const ( & input - > data [ header_len ] , ap_req_len ) ;
krb_priv_req = data_blob_const ( & input - > data [ header_len + ap_req_len ] , krb_priv_len ) ;
2009-12-23 23:17:16 +03:00
2006-07-31 18:05:08 +04:00
server_credentials = cli_credentials_init ( tmp_ctx ) ;
2005-10-21 05:25:55 +04:00
if ( ! server_credentials ) {
DEBUG ( 1 , ( " Failed to init server credentials \n " ) ) ;
2010-11-12 09:23:34 +03:00
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2006-01-24 08:31:08 +03:00
/* We want the credentials subsystem to use the krb5 context
2009-12-23 23:17:16 +03:00
* we already have , rather than a new context */
2006-01-24 08:31:08 +03:00
cli_credentials_set_krb5_context ( server_credentials , kdc - > smb_krb5_context ) ;
2007-12-03 17:53:28 +03:00
cli_credentials_set_conf ( server_credentials , kdc - > task - > lp_ctx ) ;
2009-07-27 10:09:25 +04:00
2010-01-28 08:08:36 +03:00
keytab_name = talloc_asprintf ( server_credentials , " HDB:samba4&%p " , kdc - > base_ctx ) ;
2009-07-27 10:09:25 +04:00
cli_credentials_set_username ( server_credentials , " kadmin/changepw " , CRED_SPECIFIED ) ;
2010-10-11 09:53:08 +04:00
ret = cli_credentials_set_keytab_name ( server_credentials , kdc - > task - > lp_ctx , keytab_name , CRED_SPECIFIED ) ;
2009-07-27 10:09:25 +04:00
if ( ret ! = 0 ) {
2009-12-23 23:17:16 +03:00
ret = kpasswdd_make_unauth_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_HARDERROR ,
2009-12-23 23:17:16 +03:00
talloc_asprintf ( mem_ctx ,
2010-12-12 13:58:59 +03:00
" Failed to obtain server credentials for kadmin/changepw! " ) ,
2005-10-21 05:25:55 +04:00
& krb_priv_rep ) ;
ap_rep . length = 0 ;
if ( ret ) {
goto reply ;
}
talloc_free ( tmp_ctx ) ;
return ret ;
}
2009-12-23 23:17:16 +03:00
2009-02-13 02:24:16 +03:00
/* We don't strictly need to call this wrapper, and could call
* gensec_server_start directly , as we have no need for NTLM
* and we have a PAC , but this ensures that the wrapper can be
* safely extended for other helpful things in future */
2009-12-23 23:17:16 +03:00
nt_status = samba_server_gensec_start ( tmp_ctx , kdc - > task - > event_ctx ,
2009-02-13 02:24:16 +03:00
kdc - > task - > msg_ctx ,
kdc - > task - > lp_ctx ,
server_credentials ,
2009-12-23 23:17:16 +03:00
" kpasswd " ,
2009-02-13 02:24:16 +03:00
& gensec_security ) ;
2006-01-03 01:00:40 +03:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2006-01-03 01:00:40 +03:00
}
/* The kerberos PRIV packets include these addresses. MIT
* clients check that they are present */
2009-09-16 09:02:36 +04:00
#if 0
/* Skip this part for now, it breaks with a NetAPP filer and
* in any case where the client address is behind NAT . If
* older MIT clients need this , we might have to insert more
* complex code */
2014-09-22 10:38:10 +04:00
nt_status = gensec_set_remote_address ( gensec_security , peer_addr ) ;
2006-01-03 01:00:40 +03:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2006-01-03 01:00:40 +03:00
}
2009-09-16 09:02:36 +04:00
# endif
2009-12-16 17:52:30 +03:00
nt_status = gensec_set_local_address ( gensec_security , my_addr ) ;
2006-01-03 01:00:40 +03:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2006-01-03 01:00:40 +03:00
}
/* We want the GENSEC wrap calls to generate PRIV tokens */
2005-10-21 05:25:55 +04:00
gensec_want_feature ( gensec_security , GENSEC_FEATURE_SEAL ) ;
nt_status = gensec_start_mech_by_name ( gensec_security , " krb5 " ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2006-01-03 01:00:40 +03:00
/* Accept the AP-REQ and generate teh AP-REP we need for the reply */
2013-12-13 22:35:34 +04:00
nt_status = gensec_update_ev ( gensec_security , tmp_ctx , kdc - > task - > event_ctx , ap_req , & ap_rep ) ;
2005-10-21 05:25:55 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) & & ! NT_STATUS_EQUAL ( nt_status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
2009-12-23 23:17:16 +03:00
ret = kpasswdd_make_unauth_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_HARDERROR ,
2009-12-23 23:17:16 +03:00
talloc_asprintf ( mem_ctx ,
" gensec_update failed: %s " ,
2005-10-21 05:25:55 +04:00
nt_errstr ( nt_status ) ) ,
& krb_priv_rep ) ;
ap_rep . length = 0 ;
if ( ret ) {
goto reply ;
}
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2006-01-03 01:00:40 +03:00
/* Extract the data from the KRB-PRIV half of the message */
2005-10-21 05:25:55 +04:00
nt_status = gensec_unwrap ( gensec_security , tmp_ctx , & krb_priv_req , & kpasswd_req ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2009-12-23 23:17:16 +03:00
ret = kpasswdd_make_unauth_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_HARDERROR ,
2009-12-23 23:17:16 +03:00
talloc_asprintf ( mem_ctx ,
" gensec_unwrap failed: %s " ,
2005-10-21 05:25:55 +04:00
nt_errstr ( nt_status ) ) ,
& krb_priv_rep ) ;
ap_rep . length = 0 ;
if ( ret ) {
goto reply ;
}
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2006-01-03 01:00:40 +03:00
/* Figure out something to do with it (probably changing a password...) */
2009-12-23 23:17:16 +03:00
ret = kpasswd_process_request ( kdc , tmp_ctx ,
gensec_security ,
version ,
& kpasswd_req , & kpasswd_rep ) ;
2005-10-21 05:25:55 +04:00
if ( ! ret ) {
/* Argh! */
2010-11-12 09:23:34 +03:00
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2006-01-03 01:00:40 +03:00
/* And wrap up the reply: This ensures that the error message
* or success can be verified by the client */
2009-12-23 23:17:16 +03:00
nt_status = gensec_wrap ( gensec_security , tmp_ctx ,
2005-10-21 05:25:55 +04:00
& kpasswd_rep , & krb_priv_rep ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2009-12-23 23:17:16 +03:00
ret = kpasswdd_make_unauth_error_reply ( kdc , mem_ctx ,
2005-10-21 05:25:55 +04:00
KRB5_KPASSWD_HARDERROR ,
2009-12-23 23:17:16 +03:00
talloc_asprintf ( mem_ctx ,
" gensec_wrap failed: %s " ,
2005-10-21 05:25:55 +04:00
nt_errstr ( nt_status ) ) ,
& krb_priv_rep ) ;
ap_rep . length = 0 ;
if ( ret ) {
goto reply ;
}
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
2009-12-23 23:17:16 +03:00
2005-10-21 05:25:55 +04:00
reply :
* reply = data_blob_talloc ( mem_ctx , NULL , krb_priv_rep . length + ap_rep . length + header_len ) ;
if ( ! reply - > data ) {
2010-11-12 09:23:34 +03:00
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_ERROR ;
2005-10-21 05:25:55 +04:00
}
RSSVAL ( reply - > data , 0 , reply - > length ) ;
RSSVAL ( reply - > data , 2 , 1 ) ; /* This is a version 1 reply, MS change/set or otherwise */
RSSVAL ( reply - > data , 4 , ap_rep . length ) ;
2009-12-23 23:17:16 +03:00
memcpy ( reply - > data + header_len ,
ap_rep . data ,
2005-10-21 05:25:55 +04:00
ap_rep . length ) ;
2009-12-23 23:17:16 +03:00
memcpy ( reply - > data + header_len + ap_rep . length ,
krb_priv_rep . data ,
2005-10-21 05:25:55 +04:00
krb_priv_rep . length ) ;
talloc_free ( tmp_ctx ) ;
2016-06-10 10:50:45 +03:00
return KDC_OK ;
2005-10-21 05:25:55 +04:00
}