2006-08-24 19:43:32 +04:00
/*
Public Interface file for Linux DNS client library implementation
Copyright ( C ) 2006 Krishna Ganugapati < krishnag @ centeris . com >
Copyright ( C ) 2006 Gerald Carter < jerry @ samba . org >
* * NOTE ! The following LGPL license applies to the libaddns
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 2.1 of the License , or ( at your option ) any later version .
This library 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
02110 - 1301 USA
*/
# include "dns.h"
# include <ctype.h>
2006-08-24 22:09:05 +04:00
# ifdef HAVE_GSSAPI_SUPPORT
2006-08-24 19:43:32 +04:00
/*********************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int strupr ( char * szDomainName )
{
if ( ! szDomainName ) {
return ( 0 ) ;
}
while ( * szDomainName ! = ' \0 ' ) {
* szDomainName = toupper ( * szDomainName ) ;
szDomainName + + ;
}
return ( 0 ) ;
}
2006-12-14 19:27:45 +03:00
#if 0
2006-08-24 19:43:32 +04:00
/*********************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-11-18 00:46:26 +03:00
static void display_status_1 ( const char * m , OM_uint32 code , int type )
2006-08-24 19:43:32 +04:00
{
2006-11-18 00:46:26 +03:00
OM_uint32 maj_stat , min_stat ;
gss_buffer_desc msg ;
OM_uint32 msg_ctx ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
msg_ctx = 0 ;
while ( 1 ) {
maj_stat = gss_display_status ( & min_stat , code ,
type , GSS_C_NULL_OID ,
& msg_ctx , & msg ) ;
fprintf ( stdout , " GSS-API error %s: %s \n " , m ,
( char * ) msg . value ) ;
( void ) gss_release_buffer ( & min_stat , & msg ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( ! msg_ctx )
break ;
2006-08-24 19:43:32 +04:00
}
}
/*********************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-11-18 00:46:26 +03:00
void display_status ( const char * msg , OM_uint32 maj_stat , OM_uint32 min_stat )
2006-08-24 19:43:32 +04:00
{
2006-11-18 00:46:26 +03:00
display_status_1 ( msg , maj_stat , GSS_C_GSS_CODE ) ;
display_status_1 ( msg , min_stat , GSS_C_MECH_CODE ) ;
2006-08-24 19:43:32 +04:00
}
2006-12-14 19:27:45 +03:00
# endif
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
static DNS_ERROR dns_negotiate_gss_ctx_int ( TALLOC_CTX * mem_ctx ,
struct dns_connection * conn ,
const char * keyname ,
const gss_name_t target_name ,
2006-12-14 19:27:45 +03:00
gss_ctx_id_t * ctx ,
enum dns_ServerType srv_type )
2006-08-24 19:43:32 +04:00
{
2006-11-18 00:46:26 +03:00
struct gss_buffer_desc_struct input_desc , * input_ptr , output_desc ;
OM_uint32 major , minor ;
OM_uint32 ret_flags ;
DNS_ERROR err ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
gss_OID_desc krb5_oid_desc =
{ 9 , ( char * ) " \x2a \x86 \x48 \x86 \xf7 \x12 \x01 \x02 \x02 " } ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
* ctx = GSS_C_NO_CONTEXT ;
input_ptr = NULL ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
do {
major = gss_init_sec_context (
& minor , NULL , ctx , target_name , & krb5_oid_desc ,
GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG |
GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG |
GSS_C_INTEG_FLAG | GSS_C_DELEG_FLAG ,
0 , NULL , input_ptr , NULL , & output_desc ,
& ret_flags , NULL ) ;
if ( input_ptr ! = NULL ) {
TALLOC_FREE ( input_desc . value ) ;
2006-08-24 19:43:32 +04:00
}
2006-11-18 00:46:26 +03:00
if ( output_desc . length ! = 0 ) {
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
struct dns_request * req ;
struct dns_rrec * rec ;
struct dns_buffer * buf ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
time_t t = time ( NULL ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_create_query ( mem_ctx , keyname , QTYPE_TKEY ,
DNS_CLASS_IN , & req ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_create_tkey_record (
req , keyname , " gss.microsoft.com " , t ,
t + 86400 , DNS_TKEY_MODE_GSSAPI , 0 ,
output_desc . length , ( uint8 * ) output_desc . value ,
2006-12-14 19:27:45 +03:00
& rec ) ;
2006-11-18 00:46:26 +03:00
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-12-14 19:27:45 +03:00
/* Windows 2000 DNS is broken and requires the
TKEY payload in the Answer section instead
of the Additional seciton like Windows 2003 */
if ( srv_type = = DNS_SRV_WIN2000 ) {
err = dns_add_rrec ( req , rec , & req - > num_answers ,
& req - > answers ) ;
} else {
err = dns_add_rrec ( req , rec , & req - > num_additionals ,
& req - > additionals ) ;
}
2006-11-18 00:46:26 +03:00
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_marshall_request ( req , req , & buf ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_send ( conn , buf ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
TALLOC_FREE ( req ) ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
gss_release_buffer ( & minor , & output_desc ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( ( major ! = GSS_S_COMPLETE ) & &
( major ! = GSS_S_CONTINUE_NEEDED ) ) {
return ERROR_DNS_GSS_ERROR ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( major = = GSS_S_CONTINUE_NEEDED ) {
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
struct dns_request * resp ;
struct dns_buffer * buf ;
struct dns_tkey_record * tkey ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_receive ( mem_ctx , conn , & buf ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_unmarshall_request ( buf , buf , & resp ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
/*
* TODO : Compare id and keyname
*/
if ( ( resp - > num_additionals ! = 1 ) | |
2006-12-14 19:27:45 +03:00
( resp - > num_answers = = 0 ) | |
2006-11-18 00:46:26 +03:00
( resp - > answers [ 0 ] - > type ! = QTYPE_TKEY ) ) {
err = ERROR_DNS_INVALID_MESSAGE ;
goto error ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_unmarshall_tkey_record (
mem_ctx , resp - > answers [ 0 ] , & tkey ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
input_desc . length = tkey - > key_length ;
input_desc . value = talloc_move ( mem_ctx , & tkey - > key ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
input_ptr = & input_desc ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
TALLOC_FREE ( buf ) ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
} while ( major = = GSS_S_CONTINUE_NEEDED ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
/* If we arrive here, we have a valid security context */
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = ERROR_DNS_SUCCESS ;
2006-08-24 19:43:32 +04:00
error :
2006-11-18 00:46:26 +03:00
return err ;
2006-08-24 19:43:32 +04:00
}
2006-11-18 00:46:26 +03:00
DNS_ERROR dns_negotiate_sec_ctx ( const char * target_realm ,
const char * servername ,
const char * keyname ,
2006-12-14 19:27:45 +03:00
gss_ctx_id_t * gss_ctx ,
enum dns_ServerType srv_type )
2006-08-24 19:43:32 +04:00
{
2006-11-18 00:46:26 +03:00
OM_uint32 major , minor ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
char * upcaserealm , * targetname ;
DNS_ERROR err ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
gss_buffer_desc input_name ;
struct dns_connection * conn ;
2006-08-24 19:43:32 +04:00
gss_name_t targ_name ;
krb5_principal host_principal ;
2006-11-18 00:46:26 +03:00
krb5_context krb_ctx = NULL ;
2006-08-24 19:43:32 +04:00
gss_OID_desc nt_host_oid_desc =
2006-11-18 00:46:26 +03:00
{ 10 , ( char * ) " \052 \206 \110 \206 \367 \022 \001 \002 \002 \002 " } ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
TALLOC_CTX * mem_ctx ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( ! ( mem_ctx = talloc_init ( " dns_negotiate_sec_ctx " ) ) ) {
return ERROR_DNS_NO_MEMORY ;
}
2006-08-24 19:43:32 +04:00
2006-12-31 09:45:24 +03:00
err = dns_open_connection ( servername , DNS_TCP , mem_ctx , & conn ) ;
2006-11-18 00:46:26 +03:00
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( ! ( upcaserealm = talloc_strdup ( mem_ctx , target_realm ) ) ) {
err = ERROR_DNS_NO_MEMORY ;
goto error ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
strupr ( upcaserealm ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( ! ( targetname = talloc_asprintf ( mem_ctx , " dns/%s@%s " ,
servername , upcaserealm ) ) ) {
err = ERROR_DNS_NO_MEMORY ;
goto error ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
krb5_init_context ( & krb_ctx ) ;
krb5_parse_name ( krb_ctx , targetname , & host_principal ) ;
2006-08-24 19:43:32 +04:00
input_name . value = & host_principal ;
input_name . length = sizeof ( host_principal ) ;
2006-11-18 00:46:26 +03:00
major = gss_import_name ( & minor , & input_name ,
& nt_host_oid_desc , & targ_name ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( major ) {
2006-12-01 17:18:35 +03:00
krb5_free_principal ( krb_ctx , host_principal ) ;
krb5_free_context ( krb_ctx ) ;
2006-11-18 00:46:26 +03:00
err = ERROR_DNS_GSS_ERROR ;
goto error ;
}
2006-08-24 19:43:32 +04:00
2006-12-14 19:27:45 +03:00
err = dns_negotiate_gss_ctx_int ( mem_ctx , conn , keyname ,
targ_name , gss_ctx , srv_type ) ;
2006-08-24 19:43:32 +04:00
2006-12-14 19:27:45 +03:00
gss_release_name ( & minor , & targ_name ) ;
2006-12-01 17:18:35 +03:00
krb5_free_principal ( krb_ctx , host_principal ) ;
krb5_free_context ( krb_ctx ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
error :
TALLOC_FREE ( mem_ctx ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
return err ;
2006-08-24 19:43:32 +04:00
}
2006-11-18 00:46:26 +03:00
DNS_ERROR dns_sign_update ( struct dns_update_request * req ,
gss_ctx_id_t gss_ctx ,
const char * keyname ,
const char * algorithmname ,
time_t time_signed , uint16 fudge )
2006-08-24 19:43:32 +04:00
{
2006-11-18 00:46:26 +03:00
struct dns_buffer * buf ;
DNS_ERROR err ;
struct dns_domain_name * key , * algorithm ;
struct gss_buffer_desc_struct msg , mic ;
OM_uint32 major , minor ;
struct dns_rrec * rec ;
err = dns_marshall_update_request ( req , req , & buf ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) return err ;
err = dns_domain_name_from_string ( buf , keyname , & key ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
err = dns_domain_name_from_string ( buf , algorithmname , & algorithm ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
dns_marshall_domain_name ( buf , key ) ;
dns_marshall_uint16 ( buf , DNS_CLASS_ANY ) ;
dns_marshall_uint32 ( buf , 0 ) ; /* TTL */
dns_marshall_domain_name ( buf , algorithm ) ;
dns_marshall_uint16 ( buf , 0 ) ; /* Time prefix for 48-bit time_t */
dns_marshall_uint32 ( buf , time_signed ) ;
dns_marshall_uint16 ( buf , fudge ) ;
dns_marshall_uint16 ( buf , 0 ) ; /* error */
dns_marshall_uint16 ( buf , 0 ) ; /* other len */
err = buf - > error ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) goto error ;
msg . value = ( void * ) buf - > data ;
msg . length = buf - > offset ;
major = gss_get_mic ( & minor , gss_ctx , 0 , & msg , & mic ) ;
if ( major ! = 0 ) {
err = ERROR_DNS_GSS_ERROR ;
goto error ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( mic . length > 0xffff ) {
gss_release_buffer ( & minor , & mic ) ;
err = ERROR_DNS_GSS_ERROR ;
goto error ;
2006-08-24 19:43:32 +04:00
}
2006-11-18 00:46:26 +03:00
err = dns_create_tsig_record ( buf , keyname , algorithmname , time_signed ,
fudge , mic . length , ( uint8 * ) mic . value ,
req - > id , 0 , & rec ) ;
gss_release_buffer ( & minor , & mic ) ;
if ( ! ERR_DNS_IS_OK ( err ) ) goto error ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
err = dns_add_rrec ( req , rec , & req - > num_additionals , & req - > additionals ) ;
error :
TALLOC_FREE ( buf ) ;
return err ;
2006-08-24 19:43:32 +04:00
}
2006-08-24 22:09:05 +04:00
# endif /* HAVE_GSSAPI_SUPPORT */