2008-02-17 03:57:57 +03:00
/*
2003-04-07 22:01:40 +04:00
Unix SMB / CIFS implementation .
kerberos authorization data ( PAC ) utility library
2008-02-17 03:57:57 +03:00
Copyright ( C ) Jim McDonough < jmcd @ us . ibm . com > 2003
2005-09-30 21:13:37 +04:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2004 - 2005
Copyright ( C ) Andrew Tridgell 2001
Copyright ( C ) Luke Howard 2002 - 2003
Copyright ( C ) Stefan Metzmacher 2004 - 2005
2008-02-27 21:38:48 +03:00
Copyright ( C ) Guenther Deschner 2005 , 2007 , 2008
2008-02-17 03:57:57 +03:00
2003-04-07 22:01:40 +04: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
2003-04-07 22:01:40 +04:00
( at your option ) any later version .
2008-02-17 03:57:57 +03:00
2003-04-07 22:01:40 +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 .
2008-02-17 03:57:57 +03:00
2003-04-07 22:01:40 +04:00
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/>.
2003-04-07 22:01:40 +04:00
*/
# include "includes.h"
2008-10-20 21:21:10 +04:00
# include "librpc/gen_ndr/ndr_krb5pac.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"
2012-03-12 11:21:10 +04:00
# include "auth/common_auth.h"
# include "lib/param/param.h"
# include "librpc/crypto/gse.h"
# include "auth/gensec/gensec.h"
# include "../libcli/auth/spnego.h"
2023-09-14 20:00:06 +03:00
# include "lib/util/asn1.h"
2003-04-07 22:01:40 +04:00
# ifdef HAVE_KRB5
2012-04-24 15:24:29 +04:00
# include "auth/kerberos/pac_utils.h"
2012-03-12 11:21:10 +04:00
struct smb_krb5_context ;
2023-09-14 20:00:06 +03:00
/*
generate a krb5 GSS - API wrapper packet given a ticket
*/
static DATA_BLOB spnego_gen_krb5_wrap (
TALLOC_CTX * ctx , const DATA_BLOB ticket , const uint8_t tok_id [ 2 ] )
{
ASN1_DATA * data ;
DATA_BLOB ret = data_blob_null ;
data = asn1_init ( talloc_tos ( ) , ASN1_MAX_TREE_DEPTH ) ;
if ( data = = NULL ) {
return data_blob_null ;
}
if ( ! asn1_push_tag ( data , ASN1_APPLICATION ( 0 ) ) ) goto err ;
if ( ! asn1_write_OID ( data , OID_KERBEROS5 ) ) goto err ;
if ( ! asn1_write ( data , tok_id , 2 ) ) goto err ;
if ( ! asn1_write ( data , ticket . data , ticket . length ) ) goto err ;
if ( ! asn1_pop_tag ( data ) ) goto err ;
if ( ! asn1_extract_blob ( data , ctx , & ret ) ) {
goto err ;
}
asn1_free ( data ) ;
data = NULL ;
err :
if ( data ! = NULL ) {
if ( asn1_has_error ( data ) ) {
DEBUG ( 1 , ( " Failed to build krb5 wrapper at offset %d \n " ,
( int ) asn1_current_ofs ( data ) ) ) ;
}
asn1_free ( data ) ;
}
return ret ;
}
2012-03-12 11:21:10 +04:00
/*
* Given the username / password , do a kinit , store the ticket in
* cache_name if specified , and return the PAC_LOGON_INFO ( the
* structure containing the important user information such as
* groups ) .
*/
2007-08-14 20:04:37 +04:00
NTSTATUS kerberos_return_pac ( TALLOC_CTX * mem_ctx ,
const char * name ,
const char * pass ,
time_t time_offset ,
2007-08-14 23:47:57 +04:00
time_t * expire_time ,
time_t * renew_till_time ,
const char * cache_name ,
2007-10-19 04:40:25 +04:00
bool request_pac ,
bool add_netbios_addr ,
2007-08-14 23:47:57 +04:00
time_t renewable_time ,
2008-10-13 19:27:21 +04:00
const char * impersonate_princ_s ,
2014-01-17 17:29:03 +04:00
const char * local_service ,
2022-02-22 15:08:56 +03:00
char * * _canon_principal ,
char * * _canon_realm ,
2014-03-11 21:07:11 +04:00
struct PAC_DATA_CTR * * _pac_data_ctr )
2007-07-19 17:34:45 +04:00
{
krb5_error_code ret ;
NTSTATUS status = NT_STATUS_INVALID_PARAMETER ;
2022-02-22 14:59:44 +03:00
DATA_BLOB tkt = data_blob_null ;
DATA_BLOB tkt_wrapped = data_blob_null ;
DATA_BLOB ap_rep = data_blob_null ;
DATA_BLOB sesskey1 = data_blob_null ;
2007-07-19 17:34:45 +04:00
const char * auth_princ = NULL ;
2024-02-28 19:27:39 +03:00
const char * cc = NULL ;
2012-03-12 11:21:10 +04:00
struct auth_session_info * session_info ;
struct gensec_security * gensec_server_context ;
2013-08-05 13:20:21 +04:00
const struct gensec_security_ops * * backends ;
2012-03-12 11:21:10 +04:00
struct gensec_settings * gensec_settings ;
size_t idx = 0 ;
struct auth4_context * auth_context ;
struct loadparm_context * lp_ctx ;
2014-03-11 21:07:11 +04:00
struct PAC_DATA_CTR * pac_data_ctr = NULL ;
2022-02-22 15:08:56 +03:00
char * canon_principal = NULL ;
char * canon_realm = NULL ;
2024-02-28 19:27:39 +03:00
krb5_context ctx = NULL ;
krb5_ccache ccid = NULL ;
2012-03-12 11:21:10 +04:00
TALLOC_CTX * tmp_ctx = talloc_new ( mem_ctx ) ;
NT_STATUS_HAVE_NO_MEMORY ( tmp_ctx ) ;
2007-07-19 17:34:45 +04:00
ZERO_STRUCT ( tkt ) ;
ZERO_STRUCT ( ap_rep ) ;
ZERO_STRUCT ( sesskey1 ) ;
if ( ! name | | ! pass ) {
2022-02-22 14:59:44 +03:00
status = NT_STATUS_INVALID_PARAMETER ;
goto out ;
2007-07-19 17:34:45 +04:00
}
2022-02-22 15:08:56 +03:00
if ( _canon_principal ! = NULL ) {
* _canon_principal = NULL ;
}
if ( _canon_realm ! = NULL ) {
* _canon_realm = NULL ;
}
2007-08-14 23:47:57 +04:00
if ( cache_name ) {
cc = cache_name ;
2024-02-28 19:27:39 +03:00
} else {
char * ccname = NULL ;
ret = smb_krb5_init_context_common ( & ctx ) ;
if ( ret ! = 0 ) {
status = krb5_to_nt_status ( ret ) ;
goto out ;
}
ret = smb_krb5_cc_new_unique_memory ( ctx ,
tmp_ctx ,
& ccname ,
& ccid ) ;
if ( ret ! = 0 ) {
status = krb5_to_nt_status ( ret ) ;
goto out ;
}
cc = ccname ;
2007-08-14 23:47:57 +04:00
}
2007-07-19 17:34:45 +04:00
if ( ! strchr_m ( name , ' @ ' ) ) {
auth_princ = talloc_asprintf ( mem_ctx , " %s@%s " , name ,
lp_realm ( ) ) ;
} else {
auth_princ = name ;
}
NT_STATUS_HAVE_NO_MEMORY ( auth_princ ) ;
ret = kerberos_kinit_password_ext ( auth_princ ,
pass ,
time_offset ,
2007-08-14 23:47:57 +04:00
expire_time ,
renew_till_time ,
2007-07-19 17:34:45 +04:00
cc ,
2007-08-14 23:47:57 +04:00
request_pac ,
add_netbios_addr ,
renewable_time ,
2022-02-22 15:08:56 +03:00
tmp_ctx ,
& canon_principal ,
& canon_realm ,
2007-07-19 17:34:45 +04:00
& status ) ;
if ( ret ) {
2007-08-14 23:47:57 +04:00
DEBUG ( 1 , ( " kinit failed for '%s' with: %s (%d) \n " ,
auth_princ , error_message ( ret ) , ret ) ) ;
2007-07-19 17:34:45 +04:00
/* status already set */
goto out ;
}
2007-08-14 23:47:57 +04:00
DEBUG ( 10 , ( " got TGT for %s in %s \n " , auth_princ , cc ) ) ;
if ( expire_time ) {
DEBUGADD ( 10 , ( " \t valid until: %s (%d) \n " ,
2008-10-12 01:57:44 +04:00
http_timestring ( talloc_tos ( ) , * expire_time ) ,
2007-08-14 23:47:57 +04:00
( int ) * expire_time ) ) ;
}
if ( renew_till_time ) {
DEBUGADD ( 10 , ( " \t renewable till: %s (%d) \n " ,
2008-10-12 01:57:44 +04:00
http_timestring ( talloc_tos ( ) , * renew_till_time ) ,
2007-08-14 23:47:57 +04:00
( int ) * renew_till_time ) ) ;
}
/* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
* in that case fallback to NTLM - gd */
if ( expire_time & & renew_till_time & &
( * expire_time = = 0 ) & & ( * renew_till_time = = 0 ) ) {
2022-02-22 14:59:44 +03:00
status = NT_STATUS_INVALID_LOGON_TYPE ;
goto out ;
2007-08-14 23:47:57 +04:00
}
2016-08-26 17:38:53 +03:00
ret = ads_krb5_cli_get_ticket ( mem_ctx ,
local_service ,
time_offset ,
& tkt ,
& sesskey1 ,
0 ,
cc ,
NULL ,
impersonate_princ_s ) ;
2007-07-19 17:34:45 +04:00
if ( ret ) {
2007-08-14 23:47:57 +04:00
DEBUG ( 1 , ( " failed to get ticket for %s: %s \n " ,
local_service , error_message ( ret ) ) ) ;
2009-11-12 17:42:03 +03:00
if ( impersonate_princ_s ) {
DEBUGADD ( 1 , ( " tried S4U2SELF impersonation as: %s \n " ,
impersonate_princ_s ) ) ;
}
2007-07-19 17:34:45 +04:00
status = krb5_to_nt_status ( ret ) ;
goto out ;
}
2012-03-12 11:21:10 +04:00
/* wrap that up in a nice GSS-API wrapping */
tkt_wrapped = spnego_gen_krb5_wrap ( tmp_ctx , tkt , TOK_ID_KRB_AP_REQ ) ;
if ( tkt_wrapped . data = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto out ;
}
2019-12-19 17:50:24 +03:00
auth_context = auth4_context_for_PAC_DATA_CTR ( tmp_ctx ) ;
2012-03-12 11:21:10 +04:00
if ( auth_context = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto out ;
}
2012-06-27 17:24:39 +04:00
lp_ctx = loadparm_init_s3 ( tmp_ctx , loadparm_s3_helpers ( ) ) ;
2012-03-12 11:21:10 +04:00
if ( lp_ctx = = NULL ) {
status = NT_STATUS_INVALID_SERVER_STATE ;
DEBUG ( 10 , ( " loadparm_init_s3 failed \n " ) ) ;
goto out ;
}
gensec_settings = lpcfg_gensec_settings ( tmp_ctx , lp_ctx ) ;
2015-04-15 14:04:35 +03:00
if ( gensec_settings = = NULL ) {
2012-03-12 11:21:10 +04:00
status = NT_STATUS_NO_MEMORY ;
DEBUG ( 10 , ( " lpcfg_gensec_settings failed \n " ) ) ;
goto out ;
}
2013-08-05 13:20:21 +04:00
backends = talloc_zero_array ( gensec_settings ,
const struct gensec_security_ops * , 2 ) ;
if ( backends = = NULL ) {
2012-03-12 11:21:10 +04:00
status = NT_STATUS_NO_MEMORY ;
goto out ;
}
2013-08-05 13:20:21 +04:00
gensec_settings - > backends = backends ;
2012-03-12 11:21:10 +04:00
gensec_init ( ) ;
2024-04-25 16:51:40 +03:00
backends [ idx + + ] = gensec_gse_security_by_oid ( GENSEC_OID_KERBEROS5 ) ;
2012-03-12 11:21:10 +04:00
status = gensec_server_start ( tmp_ctx , gensec_settings ,
auth_context , & gensec_server_context ) ;
2007-07-19 17:34:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2012-03-12 11:21:10 +04:00
DEBUG ( 1 , ( __location__ " Failed to start server-side GENSEC to validate a Kerberos ticket: %s \n " , nt_errstr ( status ) ) ) ;
2007-07-19 17:34:45 +04:00
goto out ;
}
2012-03-12 11:21:10 +04:00
talloc_unlink ( tmp_ctx , lp_ctx ) ;
talloc_unlink ( tmp_ctx , gensec_settings ) ;
talloc_unlink ( tmp_ctx , auth_context ) ;
2017-03-01 02:18:49 +03:00
/* Session info is not complete, do not pass to auth log */
gensec_want_feature ( gensec_server_context , GENSEC_FEATURE_NO_AUTHZ_LOG ) ;
2012-03-12 11:21:10 +04:00
status = gensec_start_mech_by_oid ( gensec_server_context , GENSEC_OID_KERBEROS5 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( __location__ " Failed to start server-side GENSEC krb5 to validate a Kerberos ticket: %s \n " , nt_errstr ( status ) ) ) ;
goto out ;
}
/* Do a client-server update dance */
2013-12-13 22:56:13 +04:00
status = gensec_update ( gensec_server_context , tmp_ctx , tkt_wrapped , & ap_rep ) ;
2012-03-12 11:21:10 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " gensec_update() failed: %s \n " , nt_errstr ( status ) ) ) ;
goto out ;
}
2023-07-05 12:21:39 +03:00
/* Now return the PAC information to the callers. We ignore
2012-03-12 11:21:10 +04:00
* the session_info and instead pick out the PAC via the
* private_data on the auth_context */
status = gensec_session_info ( gensec_server_context , tmp_ctx , & session_info ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Unable to obtain PAC via gensec_session_info \n " ) ) ;
goto out ;
}
2019-12-19 17:50:24 +03:00
pac_data_ctr = auth4_context_get_PAC_DATA_CTR ( auth_context , mem_ctx ) ;
2014-03-11 21:07:11 +04:00
if ( pac_data_ctr = = NULL ) {
2007-08-14 23:47:57 +04:00
DEBUG ( 1 , ( " no PAC \n " ) ) ;
2007-07-19 17:34:45 +04:00
status = NT_STATUS_INVALID_PARAMETER ;
goto out ;
}
2014-03-11 21:07:11 +04:00
* _pac_data_ctr = talloc_move ( mem_ctx , & pac_data_ctr ) ;
2022-02-22 15:08:56 +03:00
if ( _canon_principal ! = NULL ) {
* _canon_principal = talloc_move ( mem_ctx , & canon_principal ) ;
}
if ( _canon_realm ! = NULL ) {
* _canon_realm = talloc_move ( mem_ctx , & canon_realm ) ;
}
2012-03-12 11:21:10 +04:00
2007-07-19 17:34:45 +04:00
out :
2024-02-28 19:27:39 +03:00
if ( ccid ! = NULL ) {
krb5_cc_destroy ( ctx , ccid ) ;
ccid = NULL ;
2007-08-14 23:47:57 +04:00
}
2024-02-28 19:27:39 +03:00
if ( ctx ! = NULL ) {
krb5_free_context ( ctx ) ;
ctx = NULL ;
}
talloc_free ( tmp_ctx ) ;
2007-07-19 17:34:45 +04:00
data_blob_free ( & tkt ) ;
data_blob_free ( & ap_rep ) ;
data_blob_free ( & sesskey1 ) ;
return status ;
}
2003-04-07 22:01:40 +04:00
# endif