From 7d7194da08dfe86d2ea52cc07f42a17ae0f3ed0b Mon Sep 17 00:00:00 2001 From: Assar Westerlund Date: Wed, 21 Jun 2000 02:32:38 +0000 Subject: [PATCH] code for token delegation. From Daniel Kouril and Miroslav Ruda git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@8429 ec53bebd-3082-4978-b11e-865c3cabbd6b --- lib/gssapi/8003.c | 59 +++++++- lib/gssapi/Makefile.am | 3 +- lib/gssapi/accept_sec_context.c | 114 +++++++++++++++- lib/gssapi/address_to_krb5addr.c | 73 ++++++++++ lib/gssapi/gssapi.h | 3 + lib/gssapi/gssapi_locl.h | 10 +- lib/gssapi/init_sec_context.c | 185 ++++++++++++++++++++++---- lib/gssapi/krb5/8003.c | 59 +++++++- lib/gssapi/krb5/Makefile.am | 3 +- lib/gssapi/krb5/accept_sec_context.c | 114 +++++++++++++++- lib/gssapi/krb5/address_to_krb5addr.c | 73 ++++++++++ lib/gssapi/krb5/gssapi.h | 3 + lib/gssapi/krb5/gssapi_locl.h | 10 +- lib/gssapi/krb5/init_sec_context.c | 185 ++++++++++++++++++++++---- 14 files changed, 828 insertions(+), 66 deletions(-) create mode 100644 lib/gssapi/address_to_krb5addr.c create mode 100644 lib/gssapi/krb5/address_to_krb5addr.c diff --git a/lib/gssapi/8003.c b/lib/gssapi/8003.c index d6714ca56..dea596f9a 100644 --- a/lib/gssapi/8003.c +++ b/lib/gssapi/8003.c @@ -90,12 +90,20 @@ krb5_error_code gssapi_krb5_create_8003_checksum ( const gss_channel_bindings_t input_chan_bindings, OM_uint32 flags, + krb5_data *fwd_data, Checksum *result) { u_char *p; + /* + * see rfc1964 (section 1.1.1 (Initial Token), and the checksum value + * field's format) + */ result->cksumtype = 0x8003; - result->checksum.length = 24; + if (fwd_data->length > 0 && (flags & GSS_C_DELEG_FLAG)) + result->checksum.length = 24 + 4 + fwd_data->length; + else + result->checksum.length = 24; result->checksum.data = malloc (result->checksum.length); if (result->checksum.data == NULL) return ENOMEM; @@ -111,8 +119,31 @@ gssapi_krb5_create_8003_checksum ( p += 16; encode_om_uint32 (flags, p); p += 4; + + if (fwd_data->length > 0 && (flags & GSS_C_DELEG_FLAG)) { +#if 0 + u_char *tmp; + + result->checksum.length = 28 + fwd_data->length; + tmp = realloc(result->checksum.data, result->checksum.length); + if (tmp == NULL) + return ENOMEM; + result->checksum.data = tmp; + + p = (u_char*)result->checksum.data + 24; +#endif + *p++ = (1 >> 0) & 0xFF; /* DlgOpt */ /* == 1 */ + *p++ = (1 >> 8) & 0xFF; /* DlgOpt */ /* == 0 */ + *p++ = (fwd_data->length >> 0) & 0xFF; /* Dlgth */ + *p++ = (fwd_data->length >> 8) & 0xFF; /* Dlgth */ + memcpy(p, (unsigned char *) fwd_data->data, fwd_data->length); + + p += fwd_data->length; + if (p - (u_char *)result->checksum.data != result->checksum.length) - abort (); + abort(); + } + return 0; } @@ -120,14 +151,16 @@ krb5_error_code gssapi_krb5_verify_8003_checksum( const gss_channel_bindings_t input_chan_bindings, Checksum *cksum, - OM_uint32 *flags) + OM_uint32 *flags, + krb5_data *fwd_data) { unsigned char hash[16]; unsigned char *p; OM_uint32 length; + int DlgOpt; /* XXX should handle checksums > 24 bytes */ - if(cksum->cksumtype != 0x8003 || cksum->checksum.length != 24) + if(cksum->cksumtype != 0x8003) return GSS_S_BAD_BINDINGS; p = cksum->checksum.data; @@ -147,6 +180,24 @@ gssapi_krb5_verify_8003_checksum( p += sizeof(hash); decode_om_uint32(p, flags); + + if (cksum->checksum.length > 24 && (*flags & GSS_C_DELEG_FLAG)) { + + p += 4; + + DlgOpt = (p[0] << 0) | (p[1] << 8 ); + if (DlgOpt != 1) + return GSS_S_BAD_BINDINGS; + + p += 2; + fwd_data->length = (p[0] << 0) | (p[1] << 8); + fwd_data->data = malloc(fwd_data->length); + if (fwd_data->data == NULL) + return ENOMEM; + + p += 2; + memcpy(fwd_data->data, p, fwd_data->length); + } return 0; } diff --git a/lib/gssapi/Makefile.am b/lib/gssapi/Makefile.am index b1191e3d2..3d4481241 100644 --- a/lib/gssapi/Makefile.am +++ b/lib/gssapi/Makefile.am @@ -45,4 +45,5 @@ libgssapi_la_SOURCES = \ unwrap.c \ v1.c \ verify_mic.c \ - wrap.c + wrap.c \ + address_to_krb5addr.c diff --git a/lib/gssapi/accept_sec_context.c b/lib/gssapi/accept_sec_context.c index 8cae517b1..b335f2211 100644 --- a/lib/gssapi/accept_sec_context.c +++ b/lib/gssapi/accept_sec_context.c @@ -75,9 +75,11 @@ gss_accept_sec_context OM_uint32 flags; krb5_ticket *ticket = NULL; krb5_keytab keytab = NULL; + krb5_data fwd_data; gssapi_krb5_init (); + krb5_data_zero (&fwd_data); output_token->length = 0; output_token->value = NULL; @@ -103,6 +105,70 @@ gss_accept_sec_context goto failure; } + if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS + && input_chan_bindings->application_data.length == + 2 * sizeof((*context_handle)->auth_context->local_port) + ) { + + /* Port numbers are expected to be in application_data.value, + * initator's port first */ + + krb5_address initiator_addr, acceptor_addr; + + memset(&initiator_addr, 0, sizeof(initiator_addr)); + memset(&acceptor_addr, 0, sizeof(acceptor_addr)); + + (*context_handle)->auth_context->remote_port = + *(int16_t *) input_chan_bindings->application_data.value; + + (*context_handle)->auth_context->local_port = + *((int16_t *) input_chan_bindings->application_data.value + 1); + + + kret = gss_address_to_krb5addr(input_chan_bindings->acceptor_addrtype, + &input_chan_bindings->acceptor_address, + (*context_handle)->auth_context->local_port, + &acceptor_addr); + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = gss_address_to_krb5addr(input_chan_bindings->initiator_addrtype, + &input_chan_bindings->initiator_address, + (*context_handle)->auth_context->remote_port, + &initiator_addr); + if (kret) { + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = krb5_auth_con_setaddrs(gssapi_krb5_context, + (*context_handle)->auth_context, + &acceptor_addr, /* local address */ + &initiator_addr); /* remote address */ + + krb5_free_address (gssapi_krb5_context, &initiator_addr); + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + +#if 0 + free(input_chan_bindings->application_data.value); + input_chan_bindings->application_data.value = NULL; + input_chan_bindings->application_data.length = 0; +#endif + + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + } + + + { int32_t tmp; @@ -183,7 +249,8 @@ gss_accept_sec_context kret = gssapi_krb5_verify_8003_checksum(input_chan_bindings, authenticator->cksum, - &flags); + &flags, + &fwd_data); krb5_free_authenticator(gssapi_krb5_context, &authenticator); if (kret) { ret = GSS_S_FAILURE; @@ -191,6 +258,49 @@ gss_accept_sec_context } } + if (fwd_data.length > 0 && (flags & GSS_C_DELEG_FLAG)) { + + krb5_ccache ccache; + + if (delegated_cred_handle == NULL || *delegated_cred_handle == NULL) + /* XXX Create a new delegated_cred_handle? */ + kret = krb5_cc_default (gssapi_krb5_context, &ccache); + + else { + if ((*delegated_cred_handle)->ccache == NULL) + kret = krb5_cc_gen_new (gssapi_krb5_context, + &krb5_mcc_ops, + &(*delegated_cred_handle)->ccache); + ccache = (*delegated_cred_handle)->ccache; + } + + if (kret) { + flags &= ~GSS_C_DELEG_FLAG; + goto end_fwd; + } + + kret = krb5_cc_initialize(gssapi_krb5_context, + ccache, + *src_name); + if (kret) { + flags &= ~GSS_C_DELEG_FLAG; + goto end_fwd; + } + + kret = krb5_rd_cred(gssapi_krb5_context, + (*context_handle)->auth_context, + ccache, + &fwd_data); + if (kret) { + flags &= ~GSS_C_DELEG_FLAG; + goto end_fwd; + } + +end_fwd: + free(fwd_data.data); + } + + flags |= GSS_C_TRANS_FLAG; if (ret_flags) @@ -236,6 +346,8 @@ gss_accept_sec_context return GSS_S_COMPLETE; failure: + if (fwd_data.length > 0) + free(fwd_data.data); if (ticket != NULL) krb5_free_ticket (gssapi_krb5_context, ticket); krb5_auth_con_free (gssapi_krb5_context, diff --git a/lib/gssapi/address_to_krb5addr.c b/lib/gssapi/address_to_krb5addr.c new file mode 100644 index 000000000..a36e5f53e --- /dev/null +++ b/lib/gssapi/address_to_krb5addr.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2000 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "gssapi_locl.h" + +krb5_error_code +gss_address_to_krb5addr(OM_uint32 gss_addr_type, + gss_buffer_desc *gss_addr, + int16_t port, + krb5_address *address) +{ + sa_family_t addr_type; + struct sockaddr sa; + int sa_len = sizeof(sa); + krb5_error_code problem; + + if (gss_addr == NULL) + return GSS_S_FAILURE; + + switch (gss_addr_type) { +#ifdef HAVE_IPV6 + case GSS_C_AF_INET6: addr_type = AF_INET6; + break; +#endif /* HAVE_IPV6 */ + + case GSS_C_AF_INET: addr_type = AF_INET; + break; + default: + return GSS_S_FAILURE; + } + + problem = krb5_h_addr2sockaddr (addr_type, + gss_addr->value, + &sa, + &sa_len, + port); + if (problem) + return GSS_S_FAILURE; + + problem = krb5_sockaddr2address (&sa, address); + + return problem; +} diff --git a/lib/gssapi/gssapi.h b/lib/gssapi/gssapi.h index 95bcbf5a8..be0f4f8e0 100644 --- a/lib/gssapi/gssapi.h +++ b/lib/gssapi/gssapi.h @@ -89,6 +89,8 @@ typedef struct gss_OID_set_desc_struct { struct krb5_keytab_data; +struct krb5_ccache_data; + typedef int gss_cred_usage_t; typedef struct gss_cred_id_t_desc_struct { @@ -97,6 +99,7 @@ typedef struct gss_cred_id_t_desc_struct { OM_uint32 lifetime; gss_cred_usage_t usage; gss_OID_set mechanisms; + struct krb5_ccache_data *ccache; } gss_cred_id_t_desc; typedef gss_cred_id_t_desc *gss_cred_id_t; diff --git a/lib/gssapi/gssapi_locl.h b/lib/gssapi/gssapi_locl.h index 13a957939..3ddbf5369 100644 --- a/lib/gssapi/gssapi_locl.h +++ b/lib/gssapi/gssapi_locl.h @@ -47,13 +47,15 @@ krb5_error_code gssapi_krb5_create_8003_checksum ( const gss_channel_bindings_t input_chan_bindings, OM_uint32 flags, + krb5_data *fwd_data, Checksum *result); krb5_error_code gssapi_krb5_verify_8003_checksum ( const gss_channel_bindings_t input_chan_bindings, Checksum *cksum, - OM_uint32 *flags); + OM_uint32 *flags, + krb5_data *fwd_data); OM_uint32 gssapi_krb5_encapsulate( @@ -86,6 +88,12 @@ OM_uint32 gss_krb5_getsomekey(const gss_ctx_id_t context_handle, des_cblock *key); +krb5_error_code +gss_address_to_krb5addr(OM_uint32 gss_addr_type, + gss_buffer_desc *gss_addr, + int16_t port, + krb5_address *address); + /* sec_context flags */ #define SC_LOCAL_ADDRESS 0x01 diff --git a/lib/gssapi/init_sec_context.c b/lib/gssapi/init_sec_context.c index 12a329266..97973ef05 100644 --- a/lib/gssapi/init_sec_context.c +++ b/lib/gssapi/init_sec_context.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -63,7 +63,9 @@ init_auth krb5_data authenticator; Checksum cksum; krb5_enctype enctype; + krb5_data fwd_data; + krb5_data_zero (&fwd_data); output_token->length = 0; output_token->value = NULL; @@ -93,7 +95,66 @@ init_auth goto failure; } - { + if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS && + input_chan_bindings->application_data.length == + 2 * sizeof((*context_handle)->auth_context->local_port)) { + /* Port numbers are expected to be in application_data.value, + * initator's port first */ + + krb5_address initiator_addr, acceptor_addr; + + memset(&initiator_addr, 0, sizeof(initiator_addr)); + memset(&acceptor_addr, 0, sizeof(acceptor_addr)); + + (*context_handle)->auth_context->local_port = + *(int16_t *) input_chan_bindings->application_data.value; + + (*context_handle)->auth_context->remote_port = + *((int16_t *) input_chan_bindings->application_data.value + 1); + + kret = gss_address_to_krb5addr(input_chan_bindings->acceptor_addrtype, + &input_chan_bindings->acceptor_address, + (*context_handle)->auth_context->remote_port, + &acceptor_addr); + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = gss_address_to_krb5addr(input_chan_bindings->initiator_addrtype, + &input_chan_bindings->initiator_address, + (*context_handle)->auth_context->local_port, + &initiator_addr); + if (kret) { + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = krb5_auth_con_setaddrs(gssapi_krb5_context, + (*context_handle)->auth_context, + &initiator_addr, /* local address */ + &acceptor_addr); /* remote address */ + + krb5_free_address (gssapi_krb5_context, &initiator_addr); + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + +#if 0 + free(input_chan_bindings->application_data.value); + input_chan_bindings->application_data.value = NULL; + input_chan_bindings->application_data.length = 0; +#endif + + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + } + + { int32_t tmp; krb5_auth_con_getflags(gssapi_krb5_context, @@ -108,30 +169,6 @@ init_auth if (actual_mech_type) *actual_mech_type = GSS_KRB5_MECHANISM; - flags = 0; - ap_options = 0; - if (req_flags & GSS_C_DELEG_FLAG) - ; /* XXX */ - if (req_flags & GSS_C_MUTUAL_FLAG) { - flags |= GSS_C_MUTUAL_FLAG; - ap_options |= AP_OPTS_MUTUAL_REQUIRED; - } - if (req_flags & GSS_C_REPLAY_FLAG) - ; /* XXX */ - if (req_flags & GSS_C_SEQUENCE_FLAG) - ; /* XXX */ - if (req_flags & GSS_C_ANON_FLAG) - ; /* XXX */ - flags |= GSS_C_CONF_FLAG; - flags |= GSS_C_INTEG_FLAG; - flags |= GSS_C_SEQUENCE_FLAG; - flags |= GSS_C_TRANS_FLAG; - - if (ret_flags) - *ret_flags = flags; - (*context_handle)->flags = flags; - (*context_handle)->more_flags = LOCAL; - kret = krb5_cc_default (gssapi_krb5_context, &ccache); if (kret) { *minor_status = kret; @@ -179,8 +216,104 @@ init_auth (*context_handle)->auth_context, &cred->session); + flags = 0; + ap_options = 0; + if (req_flags & GSS_C_DELEG_FLAG) { + krb5_creds creds; + krb5_kdc_flags fwd_flags; + krb5_keyblock *subkey; + + memset ((char *)&creds, 0, sizeof(creds)); + + subkey = (krb5_keyblock *) malloc(sizeof(subkey)); + if (subkey == NULL) { + *minor_status = ENOMEM; + ret = GSS_S_FAILURE; + goto failure; + } + + krb5_generate_subkey (gssapi_krb5_context, + &cred->session, + &subkey); + if (kret) + goto end_fwd; + + kret = krb5_auth_con_setlocalsubkey(gssapi_krb5_context, + (*context_handle)->auth_context, + subkey); + if (kret) + goto end_fwd; + + kret = krb5_cc_get_principal(gssapi_krb5_context, + ccache, + &creds.client); + if (kret) + goto end_fwd; + + kret = krb5_build_principal(gssapi_krb5_context, + &creds.server, + strlen(creds.client->realm), + creds.client->realm, + KRB5_TGS_NAME, + creds.client->realm, + NULL); + if (kret) + goto end_fwd; + + creds.times.endtime = 0; + + fwd_flags.i = 0; + fwd_flags.b.forwarded = 1; + fwd_flags.b.forwardable = 1; + + if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/ + target_name->name.name_string.len < 2) + goto end_fwd; + + kret = krb5_get_forwarded_creds(gssapi_krb5_context, + (*context_handle)->auth_context, + ccache, + fwd_flags.i, + target_name->name.name_string.val[1], + &creds, + &fwd_data); + +end_fwd: + if (kret) + flags &= ~GSS_C_DELEG_FLAG; + else + flags |= GSS_C_DELEG_FLAG; + + if (creds.client) + krb5_free_principal(gssapi_krb5_context, creds.client); + if (creds.server) + krb5_free_principal(gssapi_krb5_context, creds.server); + } + + if (req_flags & GSS_C_MUTUAL_FLAG) { + flags |= GSS_C_MUTUAL_FLAG; + ap_options |= AP_OPTS_MUTUAL_REQUIRED; + } + + if (req_flags & GSS_C_REPLAY_FLAG) + ; /* XXX */ + if (req_flags & GSS_C_SEQUENCE_FLAG) + ; /* XXX */ + if (req_flags & GSS_C_ANON_FLAG) + ; /* XXX */ + flags |= GSS_C_CONF_FLAG; + flags |= GSS_C_INTEG_FLAG; + flags |= GSS_C_SEQUENCE_FLAG; + flags |= GSS_C_TRANS_FLAG; + + if (ret_flags) + *ret_flags = flags; + (*context_handle)->flags = flags; + (*context_handle)->more_flags = LOCAL; + kret = gssapi_krb5_create_8003_checksum (input_chan_bindings, flags, + &fwd_data, &cksum); if (kret) { *minor_status = kret; diff --git a/lib/gssapi/krb5/8003.c b/lib/gssapi/krb5/8003.c index d6714ca56..dea596f9a 100644 --- a/lib/gssapi/krb5/8003.c +++ b/lib/gssapi/krb5/8003.c @@ -90,12 +90,20 @@ krb5_error_code gssapi_krb5_create_8003_checksum ( const gss_channel_bindings_t input_chan_bindings, OM_uint32 flags, + krb5_data *fwd_data, Checksum *result) { u_char *p; + /* + * see rfc1964 (section 1.1.1 (Initial Token), and the checksum value + * field's format) + */ result->cksumtype = 0x8003; - result->checksum.length = 24; + if (fwd_data->length > 0 && (flags & GSS_C_DELEG_FLAG)) + result->checksum.length = 24 + 4 + fwd_data->length; + else + result->checksum.length = 24; result->checksum.data = malloc (result->checksum.length); if (result->checksum.data == NULL) return ENOMEM; @@ -111,8 +119,31 @@ gssapi_krb5_create_8003_checksum ( p += 16; encode_om_uint32 (flags, p); p += 4; + + if (fwd_data->length > 0 && (flags & GSS_C_DELEG_FLAG)) { +#if 0 + u_char *tmp; + + result->checksum.length = 28 + fwd_data->length; + tmp = realloc(result->checksum.data, result->checksum.length); + if (tmp == NULL) + return ENOMEM; + result->checksum.data = tmp; + + p = (u_char*)result->checksum.data + 24; +#endif + *p++ = (1 >> 0) & 0xFF; /* DlgOpt */ /* == 1 */ + *p++ = (1 >> 8) & 0xFF; /* DlgOpt */ /* == 0 */ + *p++ = (fwd_data->length >> 0) & 0xFF; /* Dlgth */ + *p++ = (fwd_data->length >> 8) & 0xFF; /* Dlgth */ + memcpy(p, (unsigned char *) fwd_data->data, fwd_data->length); + + p += fwd_data->length; + if (p - (u_char *)result->checksum.data != result->checksum.length) - abort (); + abort(); + } + return 0; } @@ -120,14 +151,16 @@ krb5_error_code gssapi_krb5_verify_8003_checksum( const gss_channel_bindings_t input_chan_bindings, Checksum *cksum, - OM_uint32 *flags) + OM_uint32 *flags, + krb5_data *fwd_data) { unsigned char hash[16]; unsigned char *p; OM_uint32 length; + int DlgOpt; /* XXX should handle checksums > 24 bytes */ - if(cksum->cksumtype != 0x8003 || cksum->checksum.length != 24) + if(cksum->cksumtype != 0x8003) return GSS_S_BAD_BINDINGS; p = cksum->checksum.data; @@ -147,6 +180,24 @@ gssapi_krb5_verify_8003_checksum( p += sizeof(hash); decode_om_uint32(p, flags); + + if (cksum->checksum.length > 24 && (*flags & GSS_C_DELEG_FLAG)) { + + p += 4; + + DlgOpt = (p[0] << 0) | (p[1] << 8 ); + if (DlgOpt != 1) + return GSS_S_BAD_BINDINGS; + + p += 2; + fwd_data->length = (p[0] << 0) | (p[1] << 8); + fwd_data->data = malloc(fwd_data->length); + if (fwd_data->data == NULL) + return ENOMEM; + + p += 2; + memcpy(fwd_data->data, p, fwd_data->length); + } return 0; } diff --git a/lib/gssapi/krb5/Makefile.am b/lib/gssapi/krb5/Makefile.am index b1191e3d2..3d4481241 100644 --- a/lib/gssapi/krb5/Makefile.am +++ b/lib/gssapi/krb5/Makefile.am @@ -45,4 +45,5 @@ libgssapi_la_SOURCES = \ unwrap.c \ v1.c \ verify_mic.c \ - wrap.c + wrap.c \ + address_to_krb5addr.c diff --git a/lib/gssapi/krb5/accept_sec_context.c b/lib/gssapi/krb5/accept_sec_context.c index 8cae517b1..b335f2211 100644 --- a/lib/gssapi/krb5/accept_sec_context.c +++ b/lib/gssapi/krb5/accept_sec_context.c @@ -75,9 +75,11 @@ gss_accept_sec_context OM_uint32 flags; krb5_ticket *ticket = NULL; krb5_keytab keytab = NULL; + krb5_data fwd_data; gssapi_krb5_init (); + krb5_data_zero (&fwd_data); output_token->length = 0; output_token->value = NULL; @@ -103,6 +105,70 @@ gss_accept_sec_context goto failure; } + if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS + && input_chan_bindings->application_data.length == + 2 * sizeof((*context_handle)->auth_context->local_port) + ) { + + /* Port numbers are expected to be in application_data.value, + * initator's port first */ + + krb5_address initiator_addr, acceptor_addr; + + memset(&initiator_addr, 0, sizeof(initiator_addr)); + memset(&acceptor_addr, 0, sizeof(acceptor_addr)); + + (*context_handle)->auth_context->remote_port = + *(int16_t *) input_chan_bindings->application_data.value; + + (*context_handle)->auth_context->local_port = + *((int16_t *) input_chan_bindings->application_data.value + 1); + + + kret = gss_address_to_krb5addr(input_chan_bindings->acceptor_addrtype, + &input_chan_bindings->acceptor_address, + (*context_handle)->auth_context->local_port, + &acceptor_addr); + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = gss_address_to_krb5addr(input_chan_bindings->initiator_addrtype, + &input_chan_bindings->initiator_address, + (*context_handle)->auth_context->remote_port, + &initiator_addr); + if (kret) { + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = krb5_auth_con_setaddrs(gssapi_krb5_context, + (*context_handle)->auth_context, + &acceptor_addr, /* local address */ + &initiator_addr); /* remote address */ + + krb5_free_address (gssapi_krb5_context, &initiator_addr); + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + +#if 0 + free(input_chan_bindings->application_data.value); + input_chan_bindings->application_data.value = NULL; + input_chan_bindings->application_data.length = 0; +#endif + + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + } + + + { int32_t tmp; @@ -183,7 +249,8 @@ gss_accept_sec_context kret = gssapi_krb5_verify_8003_checksum(input_chan_bindings, authenticator->cksum, - &flags); + &flags, + &fwd_data); krb5_free_authenticator(gssapi_krb5_context, &authenticator); if (kret) { ret = GSS_S_FAILURE; @@ -191,6 +258,49 @@ gss_accept_sec_context } } + if (fwd_data.length > 0 && (flags & GSS_C_DELEG_FLAG)) { + + krb5_ccache ccache; + + if (delegated_cred_handle == NULL || *delegated_cred_handle == NULL) + /* XXX Create a new delegated_cred_handle? */ + kret = krb5_cc_default (gssapi_krb5_context, &ccache); + + else { + if ((*delegated_cred_handle)->ccache == NULL) + kret = krb5_cc_gen_new (gssapi_krb5_context, + &krb5_mcc_ops, + &(*delegated_cred_handle)->ccache); + ccache = (*delegated_cred_handle)->ccache; + } + + if (kret) { + flags &= ~GSS_C_DELEG_FLAG; + goto end_fwd; + } + + kret = krb5_cc_initialize(gssapi_krb5_context, + ccache, + *src_name); + if (kret) { + flags &= ~GSS_C_DELEG_FLAG; + goto end_fwd; + } + + kret = krb5_rd_cred(gssapi_krb5_context, + (*context_handle)->auth_context, + ccache, + &fwd_data); + if (kret) { + flags &= ~GSS_C_DELEG_FLAG; + goto end_fwd; + } + +end_fwd: + free(fwd_data.data); + } + + flags |= GSS_C_TRANS_FLAG; if (ret_flags) @@ -236,6 +346,8 @@ gss_accept_sec_context return GSS_S_COMPLETE; failure: + if (fwd_data.length > 0) + free(fwd_data.data); if (ticket != NULL) krb5_free_ticket (gssapi_krb5_context, ticket); krb5_auth_con_free (gssapi_krb5_context, diff --git a/lib/gssapi/krb5/address_to_krb5addr.c b/lib/gssapi/krb5/address_to_krb5addr.c new file mode 100644 index 000000000..a36e5f53e --- /dev/null +++ b/lib/gssapi/krb5/address_to_krb5addr.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2000 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "gssapi_locl.h" + +krb5_error_code +gss_address_to_krb5addr(OM_uint32 gss_addr_type, + gss_buffer_desc *gss_addr, + int16_t port, + krb5_address *address) +{ + sa_family_t addr_type; + struct sockaddr sa; + int sa_len = sizeof(sa); + krb5_error_code problem; + + if (gss_addr == NULL) + return GSS_S_FAILURE; + + switch (gss_addr_type) { +#ifdef HAVE_IPV6 + case GSS_C_AF_INET6: addr_type = AF_INET6; + break; +#endif /* HAVE_IPV6 */ + + case GSS_C_AF_INET: addr_type = AF_INET; + break; + default: + return GSS_S_FAILURE; + } + + problem = krb5_h_addr2sockaddr (addr_type, + gss_addr->value, + &sa, + &sa_len, + port); + if (problem) + return GSS_S_FAILURE; + + problem = krb5_sockaddr2address (&sa, address); + + return problem; +} diff --git a/lib/gssapi/krb5/gssapi.h b/lib/gssapi/krb5/gssapi.h index 95bcbf5a8..be0f4f8e0 100644 --- a/lib/gssapi/krb5/gssapi.h +++ b/lib/gssapi/krb5/gssapi.h @@ -89,6 +89,8 @@ typedef struct gss_OID_set_desc_struct { struct krb5_keytab_data; +struct krb5_ccache_data; + typedef int gss_cred_usage_t; typedef struct gss_cred_id_t_desc_struct { @@ -97,6 +99,7 @@ typedef struct gss_cred_id_t_desc_struct { OM_uint32 lifetime; gss_cred_usage_t usage; gss_OID_set mechanisms; + struct krb5_ccache_data *ccache; } gss_cred_id_t_desc; typedef gss_cred_id_t_desc *gss_cred_id_t; diff --git a/lib/gssapi/krb5/gssapi_locl.h b/lib/gssapi/krb5/gssapi_locl.h index 13a957939..3ddbf5369 100644 --- a/lib/gssapi/krb5/gssapi_locl.h +++ b/lib/gssapi/krb5/gssapi_locl.h @@ -47,13 +47,15 @@ krb5_error_code gssapi_krb5_create_8003_checksum ( const gss_channel_bindings_t input_chan_bindings, OM_uint32 flags, + krb5_data *fwd_data, Checksum *result); krb5_error_code gssapi_krb5_verify_8003_checksum ( const gss_channel_bindings_t input_chan_bindings, Checksum *cksum, - OM_uint32 *flags); + OM_uint32 *flags, + krb5_data *fwd_data); OM_uint32 gssapi_krb5_encapsulate( @@ -86,6 +88,12 @@ OM_uint32 gss_krb5_getsomekey(const gss_ctx_id_t context_handle, des_cblock *key); +krb5_error_code +gss_address_to_krb5addr(OM_uint32 gss_addr_type, + gss_buffer_desc *gss_addr, + int16_t port, + krb5_address *address); + /* sec_context flags */ #define SC_LOCAL_ADDRESS 0x01 diff --git a/lib/gssapi/krb5/init_sec_context.c b/lib/gssapi/krb5/init_sec_context.c index 12a329266..97973ef05 100644 --- a/lib/gssapi/krb5/init_sec_context.c +++ b/lib/gssapi/krb5/init_sec_context.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -63,7 +63,9 @@ init_auth krb5_data authenticator; Checksum cksum; krb5_enctype enctype; + krb5_data fwd_data; + krb5_data_zero (&fwd_data); output_token->length = 0; output_token->value = NULL; @@ -93,7 +95,66 @@ init_auth goto failure; } - { + if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS && + input_chan_bindings->application_data.length == + 2 * sizeof((*context_handle)->auth_context->local_port)) { + /* Port numbers are expected to be in application_data.value, + * initator's port first */ + + krb5_address initiator_addr, acceptor_addr; + + memset(&initiator_addr, 0, sizeof(initiator_addr)); + memset(&acceptor_addr, 0, sizeof(acceptor_addr)); + + (*context_handle)->auth_context->local_port = + *(int16_t *) input_chan_bindings->application_data.value; + + (*context_handle)->auth_context->remote_port = + *((int16_t *) input_chan_bindings->application_data.value + 1); + + kret = gss_address_to_krb5addr(input_chan_bindings->acceptor_addrtype, + &input_chan_bindings->acceptor_address, + (*context_handle)->auth_context->remote_port, + &acceptor_addr); + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = gss_address_to_krb5addr(input_chan_bindings->initiator_addrtype, + &input_chan_bindings->initiator_address, + (*context_handle)->auth_context->local_port, + &initiator_addr); + if (kret) { + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + + kret = krb5_auth_con_setaddrs(gssapi_krb5_context, + (*context_handle)->auth_context, + &initiator_addr, /* local address */ + &acceptor_addr); /* remote address */ + + krb5_free_address (gssapi_krb5_context, &initiator_addr); + krb5_free_address (gssapi_krb5_context, &acceptor_addr); + +#if 0 + free(input_chan_bindings->application_data.value); + input_chan_bindings->application_data.value = NULL; + input_chan_bindings->application_data.length = 0; +#endif + + if (kret) { + *minor_status = kret; + ret = GSS_S_BAD_BINDINGS; + goto failure; + } + } + + { int32_t tmp; krb5_auth_con_getflags(gssapi_krb5_context, @@ -108,30 +169,6 @@ init_auth if (actual_mech_type) *actual_mech_type = GSS_KRB5_MECHANISM; - flags = 0; - ap_options = 0; - if (req_flags & GSS_C_DELEG_FLAG) - ; /* XXX */ - if (req_flags & GSS_C_MUTUAL_FLAG) { - flags |= GSS_C_MUTUAL_FLAG; - ap_options |= AP_OPTS_MUTUAL_REQUIRED; - } - if (req_flags & GSS_C_REPLAY_FLAG) - ; /* XXX */ - if (req_flags & GSS_C_SEQUENCE_FLAG) - ; /* XXX */ - if (req_flags & GSS_C_ANON_FLAG) - ; /* XXX */ - flags |= GSS_C_CONF_FLAG; - flags |= GSS_C_INTEG_FLAG; - flags |= GSS_C_SEQUENCE_FLAG; - flags |= GSS_C_TRANS_FLAG; - - if (ret_flags) - *ret_flags = flags; - (*context_handle)->flags = flags; - (*context_handle)->more_flags = LOCAL; - kret = krb5_cc_default (gssapi_krb5_context, &ccache); if (kret) { *minor_status = kret; @@ -179,8 +216,104 @@ init_auth (*context_handle)->auth_context, &cred->session); + flags = 0; + ap_options = 0; + if (req_flags & GSS_C_DELEG_FLAG) { + krb5_creds creds; + krb5_kdc_flags fwd_flags; + krb5_keyblock *subkey; + + memset ((char *)&creds, 0, sizeof(creds)); + + subkey = (krb5_keyblock *) malloc(sizeof(subkey)); + if (subkey == NULL) { + *minor_status = ENOMEM; + ret = GSS_S_FAILURE; + goto failure; + } + + krb5_generate_subkey (gssapi_krb5_context, + &cred->session, + &subkey); + if (kret) + goto end_fwd; + + kret = krb5_auth_con_setlocalsubkey(gssapi_krb5_context, + (*context_handle)->auth_context, + subkey); + if (kret) + goto end_fwd; + + kret = krb5_cc_get_principal(gssapi_krb5_context, + ccache, + &creds.client); + if (kret) + goto end_fwd; + + kret = krb5_build_principal(gssapi_krb5_context, + &creds.server, + strlen(creds.client->realm), + creds.client->realm, + KRB5_TGS_NAME, + creds.client->realm, + NULL); + if (kret) + goto end_fwd; + + creds.times.endtime = 0; + + fwd_flags.i = 0; + fwd_flags.b.forwarded = 1; + fwd_flags.b.forwardable = 1; + + if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/ + target_name->name.name_string.len < 2) + goto end_fwd; + + kret = krb5_get_forwarded_creds(gssapi_krb5_context, + (*context_handle)->auth_context, + ccache, + fwd_flags.i, + target_name->name.name_string.val[1], + &creds, + &fwd_data); + +end_fwd: + if (kret) + flags &= ~GSS_C_DELEG_FLAG; + else + flags |= GSS_C_DELEG_FLAG; + + if (creds.client) + krb5_free_principal(gssapi_krb5_context, creds.client); + if (creds.server) + krb5_free_principal(gssapi_krb5_context, creds.server); + } + + if (req_flags & GSS_C_MUTUAL_FLAG) { + flags |= GSS_C_MUTUAL_FLAG; + ap_options |= AP_OPTS_MUTUAL_REQUIRED; + } + + if (req_flags & GSS_C_REPLAY_FLAG) + ; /* XXX */ + if (req_flags & GSS_C_SEQUENCE_FLAG) + ; /* XXX */ + if (req_flags & GSS_C_ANON_FLAG) + ; /* XXX */ + flags |= GSS_C_CONF_FLAG; + flags |= GSS_C_INTEG_FLAG; + flags |= GSS_C_SEQUENCE_FLAG; + flags |= GSS_C_TRANS_FLAG; + + if (ret_flags) + *ret_flags = flags; + (*context_handle)->flags = flags; + (*context_handle)->more_flags = LOCAL; + kret = gssapi_krb5_create_8003_checksum (input_chan_bindings, flags, + &fwd_data, &cksum); if (kret) { *minor_status = kret;