2005-06-29 17:55:09 +04:00
/*
Unix SMB / CIFS implementation .
PAC Glue between Samba and the KDC
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 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-06-29 17:55:09 +04: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 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-06-29 17:55:09 +04:00
*/
# include "includes.h"
# include "kdc/kdc.h"
2006-08-30 15:29:34 +04:00
# include "dsdb/common/flags.h"
2005-11-07 05:29:37 +03:00
# include "lib/ldb/include/ldb.h"
2007-01-10 04:51:35 +03:00
# include "librpc/gen_ndr/ndr_krb5pac.h"
2005-11-07 05:29:37 +03:00
# include "librpc/gen_ndr/krb5pac.h"
# include "auth/auth.h"
2006-03-07 14:07:23 +03:00
# include "auth/auth_sam.h"
2007-12-10 06:33:29 +03:00
# include "auth/auth_sam_reply.h"
2007-12-03 17:53:28 +03:00
# include "param/param.h"
2005-11-07 05:29:37 +03:00
2006-03-11 12:31:59 +03:00
struct krb5_dh_moduli ;
struct _krb5_krb_auth_data ;
2007-01-10 04:51:35 +03:00
krb5_error_code samba_kdc_plugin_init ( krb5_context context , void * * ptr )
{
* ptr = NULL ;
return 0 ;
}
2006-03-11 12:31:59 +03:00
2007-01-10 04:51:35 +03:00
void samba_kdc_plugin_fini ( void * ptr )
2005-06-29 17:55:09 +04:00
{
2007-01-10 04:51:35 +03:00
return ;
}
static krb5_error_code make_pac ( krb5_context context ,
TALLOC_CTX * mem_ctx ,
2008-02-21 16:50:57 +03:00
struct smb_iconv_convenience * iconv_convenience ,
2007-01-10 04:51:35 +03:00
struct auth_serversupplied_info * server_info ,
krb5_pac * pac )
{
struct PAC_LOGON_INFO_CTR logon_info ;
struct netr_SamInfo3 * info3 ;
krb5_data pac_data ;
2005-06-29 17:55:09 +04:00
NTSTATUS nt_status ;
2007-11-09 21:24:51 +03:00
enum ndr_err_code ndr_err ;
2007-01-10 04:51:35 +03:00
DATA_BLOB pac_out ;
krb5_error_code ret ;
2005-11-07 05:29:37 +03:00
2007-01-10 04:51:35 +03:00
ZERO_STRUCT ( logon_info ) ;
nt_status = auth_convert_server_info_saminfo3 ( mem_ctx , server_info , & info3 ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
DEBUG ( 1 , ( " Getting Samba info failed: %s \n " , nt_errstr ( nt_status ) ) ) ;
return EINVAL ;
}
logon_info . info = talloc_zero ( mem_ctx , struct PAC_LOGON_INFO ) ;
2005-06-29 17:55:09 +04:00
if ( ! mem_ctx ) {
return ENOMEM ;
}
2007-01-10 04:51:35 +03:00
logon_info . info - > info3 = * info3 ;
2008-02-21 16:50:57 +03:00
ndr_err = ndr_push_struct_blob ( & pac_out , mem_ctx , iconv_convenience , & logon_info ,
2007-11-09 21:24:51 +03:00
( ndr_push_flags_fn_t ) ndr_push_PAC_LOGON_INFO_CTR ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
nt_status = ndr_map_error2ntstatus ( ndr_err ) ;
2007-01-10 04:51:35 +03:00
DEBUG ( 1 , ( " PAC (presig) push failed: %s \n " , nt_errstr ( nt_status ) ) ) ;
return EINVAL ;
2005-06-29 17:55:09 +04:00
}
2007-01-10 04:51:35 +03:00
ret = krb5_data_copy ( & pac_data , pac_out . data , pac_out . length ) ;
if ( ret ! = 0 ) {
2005-07-04 06:36:16 +04:00
return ret ;
}
2007-01-10 04:51:35 +03:00
ret = krb5_pac_init ( context , pac ) ;
if ( ret ! = 0 ) {
krb5_data_free ( & pac_data ) ;
return ret ;
2005-11-07 05:29:37 +03:00
}
2007-01-10 04:51:35 +03:00
ret = krb5_pac_add_buffer ( context , * pac , PAC_TYPE_LOGON_INFO , & pac_data ) ;
krb5_data_free ( & pac_data ) ;
if ( ret ! = 0 ) {
2005-11-07 05:29:37 +03:00
return ret ;
}
2007-01-10 04:51:35 +03:00
return ret ;
2005-11-07 05:29:37 +03:00
}
2007-01-10 04:51:35 +03:00
/* Given the right private pointer from hdb_ldb, get a PAC from the attached ldb messages */
krb5_error_code samba_kdc_get_pac ( void * priv ,
krb5_context context ,
struct hdb_entry_ex * client ,
krb5_pac * pac )
2005-11-07 05:29:37 +03:00
{
krb5_error_code ret ;
2007-01-10 04:51:35 +03:00
NTSTATUS nt_status ;
struct auth_serversupplied_info * server_info ;
struct hdb_ldb_private * private = talloc_get_type ( client - > ctx , struct hdb_ldb_private ) ;
TALLOC_CTX * mem_ctx = talloc_named ( private , 0 , " samba_get_pac context " ) ;
2005-11-07 05:29:37 +03:00
unsigned int userAccountControl ;
2008-02-21 16:50:57 +03:00
struct smb_iconv_convenience * iconv_convenience = lp_iconv_convenience ( global_loadparm ) ;
2007-01-10 04:51:35 +03:00
if ( ! mem_ctx ) {
return ENOMEM ;
}
2005-11-07 05:29:37 +03:00
/* The user account may be set not to want the PAC */
2006-08-13 12:00:36 +04:00
userAccountControl = ldb_msg_find_attr_as_uint ( private - > msg , " userAccountControl " , 0 ) ;
2005-11-07 05:29:37 +03:00
if ( userAccountControl & UF_NO_AUTH_DATA_REQUIRED ) {
2007-01-10 04:51:35 +03:00
* pac = NULL ;
2005-11-07 05:29:37 +03:00
return 0 ;
}
2007-01-10 04:51:35 +03:00
nt_status = authsam_make_server_info ( mem_ctx , private - > samdb ,
2007-12-03 17:53:28 +03:00
lp_netbios_name ( global_loadparm ) ,
2007-01-10 04:51:35 +03:00
private - > msg ,
private - > realm_ref_msg ,
data_blob ( NULL , 0 ) ,
data_blob ( NULL , 0 ) ,
& server_info ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
DEBUG ( 0 , ( " Getting user info for PAC failed: %s \n " ,
nt_errstr ( nt_status ) ) ) ;
return ENOMEM ;
2005-11-07 05:29:37 +03:00
}
2008-02-21 16:50:57 +03:00
ret = make_pac ( context , mem_ctx , iconv_convenience , server_info , pac ) ;
2007-01-10 04:51:35 +03:00
talloc_free ( mem_ctx ) ;
return ret ;
2005-11-07 05:29:37 +03:00
}
/* Resign (and reform, including possibly new groups) a PAC */
2007-01-10 04:51:35 +03:00
krb5_error_code samba_kdc_reget_pac ( void * priv , krb5_context context ,
const krb5_principal client_principal ,
struct hdb_entry_ex * client ,
struct hdb_entry_ex * server , krb5_pac * pac )
2005-11-07 05:29:37 +03:00
{
NTSTATUS nt_status ;
2007-11-09 21:24:51 +03:00
enum ndr_err_code ndr_err ;
2005-11-07 05:29:37 +03:00
krb5_error_code ret ;
unsigned int userAccountControl ;
2007-01-10 04:51:35 +03:00
struct hdb_ldb_private * private = talloc_get_type ( server - > ctx , struct hdb_ldb_private ) ;
krb5_data k5pac_in ;
DATA_BLOB pac_in ;
2005-11-07 05:29:37 +03:00
2007-01-10 04:51:35 +03:00
struct PAC_LOGON_INFO_CTR logon_info ;
2005-11-07 05:29:37 +03:00
union netr_Validation validation ;
struct auth_serversupplied_info * server_info_out ;
2008-02-21 16:50:57 +03:00
struct smb_iconv_convenience * iconv_convenience = lp_iconv_convenience ( global_loadparm ) ;
2005-11-07 05:29:37 +03:00
2007-01-10 04:51:35 +03:00
TALLOC_CTX * mem_ctx = talloc_named ( private , 0 , " samba_get_pac context " ) ;
2005-11-07 05:29:37 +03:00
2007-01-10 04:51:35 +03:00
if ( ! mem_ctx ) {
return ENOMEM ;
}
2005-11-07 05:29:37 +03:00
/* The service account may be set not to want the PAC */
2006-08-13 12:00:36 +04:00
userAccountControl = ldb_msg_find_attr_as_uint ( private - > msg , " userAccountControl " , 0 ) ;
2005-11-07 05:29:37 +03:00
if ( userAccountControl & UF_NO_AUTH_DATA_REQUIRED ) {
2007-01-10 04:51:35 +03:00
* pac = NULL ;
2005-11-07 05:29:37 +03:00
return 0 ;
}
2007-01-10 04:51:35 +03:00
ret = krb5_pac_get_buffer ( context , * pac , PAC_TYPE_LOGON_INFO , & k5pac_in ) ;
if ( ret ! = 0 ) {
return ret ;
2005-11-07 05:29:37 +03:00
}
pac_in = data_blob_talloc ( mem_ctx , k5pac_in . data , k5pac_in . length ) ;
krb5_data_free ( & k5pac_in ) ;
if ( ! pac_in . data ) {
talloc_free ( mem_ctx ) ;
return ENOMEM ;
}
2008-02-21 16:50:57 +03:00
ndr_err = ndr_pull_struct_blob ( & pac_in , mem_ctx , iconv_convenience , & logon_info ,
2007-11-09 21:24:51 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_PAC_LOGON_INFO_CTR ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) | | ! logon_info . info ) {
nt_status = ndr_map_error2ntstatus ( ndr_err ) ;
2007-11-02 10:16:32 +03:00
DEBUG ( 0 , ( " can't parse the PAC LOGON_INFO: %s \n " , nt_errstr ( nt_status ) ) ) ;
2005-11-07 05:29:37 +03:00
talloc_free ( mem_ctx ) ;
2007-01-10 04:51:35 +03:00
return EINVAL ;
2005-11-07 05:29:37 +03:00
}
/* Pull this right into the normal auth sysstem structures */
2007-01-10 04:51:35 +03:00
validation . sam3 = & logon_info . info - > info3 ;
2005-11-07 05:29:37 +03:00
nt_status = make_server_info_netlogon_validation ( mem_ctx ,
" " ,
3 , & validation ,
& server_info_out ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( mem_ctx ) ;
return ENOMEM ;
}
2007-01-10 04:51:35 +03:00
/* We will compleatly regenerate this pac */
krb5_pac_free ( context , * pac ) ;
2005-11-07 05:29:37 +03:00
2008-02-21 16:50:57 +03:00
ret = make_pac ( context , mem_ctx , iconv_convenience , server_info_out , pac ) ;
2005-11-07 05:29:37 +03:00
2007-01-10 04:51:35 +03:00
talloc_free ( mem_ctx ) ;
return ret ;
2005-11-07 05:29:37 +03:00
}
/* Given an hdb entry (and in particular it's private member), consult
* the account_ok routine in auth / auth_sam . c for consistancy */
2007-01-10 04:51:35 +03:00
krb5_error_code samba_kdc_check_client_access ( void * priv ,
krb5_context context , hdb_entry_ex * entry_ex ,
KDC_REQ * req )
2005-11-07 05:29:37 +03:00
{
krb5_error_code ret ;
NTSTATUS nt_status ;
2005-12-15 23:38:24 +03:00
TALLOC_CTX * tmp_ctx = talloc_new ( entry_ex - > ctx ) ;
struct hdb_ldb_private * private = talloc_get_type ( entry_ex - > ctx , struct hdb_ldb_private ) ;
2005-11-07 05:29:37 +03:00
char * name , * workstation = NULL ;
2007-01-10 04:51:35 +03:00
HostAddresses * addresses = req - > req_body . addresses ;
2005-11-27 05:02:44 +03:00
int i ;
2005-11-07 05:29:37 +03:00
if ( ! tmp_ctx ) {
return ENOMEM ;
}
ret = krb5_unparse_name ( context , entry_ex - > entry . principal , & name ) ;
if ( ret ! = 0 ) {
talloc_free ( tmp_ctx ) ;
2005-11-27 05:02:44 +03:00
return ret ;
2005-11-07 05:29:37 +03:00
}
2005-12-20 03:00:48 +03:00
if ( addresses ) {
for ( i = 0 ; i < addresses - > len ; i + + ) {
if ( addresses - > val - > addr_type = = KRB5_ADDRESS_NETBIOS ) {
workstation = talloc_strndup ( tmp_ctx , addresses - > val - > address . data , MIN ( addresses - > val - > address . length , 15 ) ) ;
if ( workstation ) {
break ;
}
2005-11-27 05:02:44 +03:00
}
}
}
/* Strip space padding */
if ( workstation ) {
i = MIN ( strlen ( workstation ) , 15 ) ;
for ( ; i > 0 & & workstation [ i - 1 ] = = ' ' ; i - - ) {
workstation [ i - 1 ] = ' \0 ' ;
}
}
2005-11-07 05:29:37 +03:00
nt_status = authsam_account_ok ( tmp_ctx ,
private - > samdb ,
MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT ,
private - > msg ,
private - > realm_ref_msg ,
workstation ,
name ) ;
free ( name ) ;
/* TODO: Need a more complete mapping of NTSTATUS to krb5kdc errors */
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
return KRB5KDC_ERR_POLICY ;
}
return 0 ;
}