From 5a6288f45891be30bd8e22978f61faf487214de6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 13 Nov 2006 03:19:59 +0000 Subject: r19681: Update to current lorikeet-heimdal. I'm looking at using the realm lookup plugin, the new PAC validation code as well as Heimdal's SPNEGO implementation. Andrew Bartlett (This used to be commit 05421f45ed7811697ea491e26c9d991a7faa1a64) --- source4/heimdal/lib/asn1/asn1_err.et | 3 +- source4/heimdal/lib/gssapi/gssapi/gssapi.h | 53 +--- source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h | 8 +- source4/heimdal/lib/gssapi/krb5/gkrb5_err.et | 30 ++ source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h | 3 +- .../lib/gssapi/mech/gss_accept_sec_context.c | 163 +++++++---- source4/heimdal/lib/gssapi/mech/gss_krb5.c | 38 ++- source4/heimdal/lib/krb5/krb5-private.h | 54 ++++ source4/heimdal/lib/krb5/krb5-protos.h | 27 +- source4/heimdal/lib/krb5/krb5.h | 4 +- source4/heimdal/lib/krb5/krb5_locl.h | 6 + source4/heimdal/lib/krb5/krbhst.c | 97 ++++++- source4/heimdal/lib/krb5/locate_plugin.h | 64 +++++ source4/heimdal/lib/krb5/mit_glue.c | 10 +- source4/heimdal/lib/krb5/plugin.c | 242 +++++++++++++++++ source4/heimdal/lib/roken/socket.c | 302 +++++++++++++++++++++ 16 files changed, 974 insertions(+), 130 deletions(-) create mode 100644 source4/heimdal/lib/gssapi/krb5/gkrb5_err.et create mode 100644 source4/heimdal/lib/krb5/locate_plugin.h create mode 100644 source4/heimdal/lib/krb5/plugin.c create mode 100644 source4/heimdal/lib/roken/socket.c (limited to 'source4/heimdal/lib') diff --git a/source4/heimdal/lib/asn1/asn1_err.et b/source4/heimdal/lib/asn1/asn1_err.et index 8f1f272ccc..938b8eb988 100644 --- a/source4/heimdal/lib/asn1/asn1_err.et +++ b/source4/heimdal/lib/asn1/asn1_err.et @@ -3,7 +3,7 @@ # # This might look like a com_err file, but is not # -id "$Id: asn1_err.et,v 1.5 1998/02/16 16:17:17 joda Exp $" +id "$Id: asn1_err.et,v 1.6 2006/10/24 14:11:20 lha Exp $" error_table asn1 prefix ASN1 @@ -17,4 +17,5 @@ error_code BAD_ID, "ASN.1 identifier doesn't match expected value" error_code BAD_LENGTH, "ASN.1 length doesn't match expected value" error_code BAD_FORMAT, "ASN.1 badly-formatted encoding" error_code PARSE_ERROR, "ASN.1 parse error" +error_code EXTRA_DATA, "ASN.1 extra data past end of end structure" end diff --git a/source4/heimdal/lib/gssapi/gssapi/gssapi.h b/source4/heimdal/lib/gssapi/gssapi/gssapi.h index 238907653e..f89e5dfbee 100644 --- a/source4/heimdal/lib/gssapi/gssapi/gssapi.h +++ b/source4/heimdal/lib/gssapi/gssapi/gssapi.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: gssapi.h,v 1.5 2006/10/19 07:11:14 lha Exp $ */ +/* $Id: gssapi.h,v 1.6 2006/11/10 00:39:50 lha Exp $ */ #ifndef GSSAPI_GSSAPI_H_ #define GSSAPI_GSSAPI_H_ @@ -377,57 +377,6 @@ extern gss_OID GSS_SASL_DIGEST_MD5_MECHANISM; #define GSS_S_UNSEQ_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3)) #define GSS_S_GAP_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4)) -/* - * From RFC1964: - * - * 4.1.1. Non-Kerberos-specific codes - */ - -#define GSS_KRB5_S_G_BAD_SERVICE_NAME 1 - /* "No @ in SERVICE-NAME name string" */ -#define GSS_KRB5_S_G_BAD_STRING_UID 2 - /* "STRING-UID-NAME contains nondigits" */ -#define GSS_KRB5_S_G_NOUSER 3 - /* "UID does not resolve to username" */ -#define GSS_KRB5_S_G_VALIDATE_FAILED 4 - /* "Validation error" */ -#define GSS_KRB5_S_G_BUFFER_ALLOC 5 - /* "Couldn't allocate gss_buffer_t data" */ -#define GSS_KRB5_S_G_BAD_MSG_CTX 6 - /* "Message context invalid" */ -#define GSS_KRB5_S_G_WRONG_SIZE 7 - /* "Buffer is the wrong size" */ -#define GSS_KRB5_S_G_BAD_USAGE 8 - /* "Credential usage type is unknown" */ -#define GSS_KRB5_S_G_UNKNOWN_QOP 9 - /* "Unknown quality of protection specified" */ - - /* - * 4.1.2. Kerberos-specific-codes - */ - -#define GSS_KRB5_S_KG_CCACHE_NOMATCH 10 - /* "Principal in credential cache does not match desired name" */ -#define GSS_KRB5_S_KG_KEYTAB_NOMATCH 11 - /* "No principal in keytab matches desired name" */ -#define GSS_KRB5_S_KG_TGT_MISSING 12 - /* "Credential cache has no TGT" */ -#define GSS_KRB5_S_KG_NO_SUBKEY 13 - /* "Authenticator has no subkey" */ -#define GSS_KRB5_S_KG_CONTEXT_ESTABLISHED 14 - /* "Context is already fully established" */ -#define GSS_KRB5_S_KG_BAD_SIGN_TYPE 15 - /* "Unknown signature type in token" */ -#define GSS_KRB5_S_KG_BAD_LENGTH 16 - /* "Invalid field length in token" */ -#define GSS_KRB5_S_KG_CTX_INCOMPLETE 17 - /* "Attempt to use incomplete security context" */ - -/* - * This is used to make sure mechs that don't want to have external - * references don't get any prototypes, and thus can get warnings. - */ - /* * Finally, function prototypes for the GSS-API routines. */ diff --git a/source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h b/source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h index f06a994008..ecd90a6656 100644 --- a/source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h +++ b/source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: gssapi_krb5.h,v 1.14 2006/11/08 23:01:01 lha Exp $ */ +/* $Id: gssapi_krb5.h,v 1.17 2006/11/10 01:05:34 lha Exp $ */ #ifndef GSSAPI_KRB5_H_ #define GSSAPI_KRB5_H_ @@ -78,6 +78,7 @@ extern gss_OID GSS_KRB5_GET_AUTHTIME_X; extern gss_OID GSS_KRB5_GET_SERVICE_KEYBLOCK_X; /* Extensions creds */ extern gss_OID GSS_KRB5_IMPORT_CRED_X; +extern gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X; /* * kerberos mechanism specific functions @@ -205,6 +206,11 @@ gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *kctx); +OM_uint32 +gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t cred, + OM_uint32 num_enctypes, + int32_t *enctypes); #ifdef __cplusplus } diff --git a/source4/heimdal/lib/gssapi/krb5/gkrb5_err.et b/source4/heimdal/lib/gssapi/krb5/gkrb5_err.et new file mode 100644 index 0000000000..97e98c5e1e --- /dev/null +++ b/source4/heimdal/lib/gssapi/krb5/gkrb5_err.et @@ -0,0 +1,30 @@ +# +# extended gss krb5 error messages +# + +id "$Id: gkrb5_err.et,v 1.1 2006/11/09 23:52:17 lha Exp $" + +error_table gk5 + +prefix GSS_KRB5_S + +error_code G_BAD_SERVICE_NAME, "No @ in SERVICE-NAME name string" +error_code G_BAD_STRING_UID, "STRING-UID-NAME contains nondigits" +error_code G_NOUSER, "UID does not resolve to username" +error_code G_VALIDATE_FAILED, "Validation error" +error_code G_BUFFER_ALLOC, "Couldn't allocate gss_buffer_t data" +error_code G_BAD_MSG_CTX, "Message context invalid" +error_code G_WRONG_SIZE, "Buffer is the wrong size" +error_code G_BAD_USAGE, "Credential usage type is unknown" +error_code G_UNKNOWN_QOP, "Unknown quality of protection specified" + +index 128 + +error_code KG_CCACHE_NOMATCH, "Principal in credential cache does not match desired name" +error_code KG_KEYTAB_NOMATCH, "No principal in keytab matches desired name" +error_code KG_TGT_MISSING, "Credential cache has no TGT" +error_code KG_NO_SUBKEY, "Authenticator has no subkey" +error_code KG_CONTEXT_ESTABLISHED, "Context is already fully established" +error_code KG_BAD_SIGN_TYPE, "Unknown signature type in token" +error_code KG_BAD_LENGTH, "Invalid field length in token" +error_code KG_CTX_INCOMPLETE, "Attempt to use incomplete security context" diff --git a/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h index ea7a561b5b..39c800bf31 100644 --- a/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h +++ b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: gsskrb5_locl.h,v 1.7 2006/11/07 17:57:43 lha Exp $ */ +/* $Id: gsskrb5_locl.h,v 1.8 2006/11/10 00:36:40 lha Exp $ */ #ifndef GSSKRB5_LOCL_H #define GSSKRB5_LOCL_H @@ -41,6 +41,7 @@ #endif #include +#include #include #include #include diff --git a/source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c b/source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c index d3a21464da..73207806a0 100644 --- a/source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c +++ b/source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c @@ -27,7 +27,108 @@ */ #include "mech_locl.h" -RCSID("$Id: gss_accept_sec_context.c,v 1.6 2006/10/25 00:45:12 lha Exp $"); +RCSID("$Id: gss_accept_sec_context.c,v 1.7 2006/11/10 03:30:12 lha Exp $"); + +static OM_uint32 +parse_header(const gss_buffer_t input_token, gss_OID mech_oid) +{ + unsigned char *p = input_token->value; + size_t len = input_token->length; + size_t a, b; + + /* + * Token must start with [APPLICATION 0] SEQUENCE. + * But if it doesn't assume its DCE-STYLE Kerberos! + */ + if (len == 0) + return (GSS_S_DEFECTIVE_TOKEN); + + p++; + len--; + + /* + * Decode the length and make sure it agrees with the + * token length. + */ + if (len == 0) + return (GSS_S_DEFECTIVE_TOKEN); + if ((*p & 0x80) == 0) { + a = *p; + p++; + len--; + } else { + b = *p & 0x7f; + p++; + len--; + if (len < b) + return (GSS_S_DEFECTIVE_TOKEN); + a = 0; + while (b) { + a = (a << 8) | *p; + p++; + len--; + b--; + } + } + if (a != len) + return (GSS_S_DEFECTIVE_TOKEN); + + /* + * Decode the OID for the mechanism. Simplify life by + * assuming that the OID length is less than 128 bytes. + */ + if (len < 2 || *p != 0x06) + return (GSS_S_DEFECTIVE_TOKEN); + if ((p[1] & 0x80) || p[1] > (len - 2)) + return (GSS_S_DEFECTIVE_TOKEN); + mech_oid->length = p[1]; + p += 2; + len -= 2; + mech_oid->elements = p; + + return GSS_S_COMPLETE; +} + +static gss_OID_desc krb5_mechanism = + {9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")}; +static gss_OID_desc spnego_mechanism = + {6, rk_UNCONST("\x2b\x06\x01\x05\x05\x02")}; + +static OM_uint32 +choose_mech(const gss_buffer_t input, gss_OID mech_oid) +{ + OM_uint32 status; + + /* + * First try to parse the gssapi token header and see if its a + * correct header, use that in the first hand. + */ + + status = parse_header(input, mech_oid); + if (status == GSS_S_COMPLETE) + return GSS_S_COMPLETE; + + /* + * Lets guess what mech is really is, callback function to mech ?? + */ + + if (input->length != 0 && ((const char *)input->value)[0] == 0x6E) { + /* Could be a raw AP-REQ (check for APPLICATION tag) */ + *mech_oid = krb5_mechanism; + return GSS_S_COMPLETE; + } else if (input->length == 0) { + /* + * There is the a wiered mode of SPNEGO (in CIFS and + * SASL GSS-SPENGO where the first token is zero + * length and the acceptor returns a mech_list, lets + * home that is what is happening now. + */ + *mech_oid = spnego_mechanism; + return GSS_S_COMPLETE; + } + return status; +} + OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, @@ -64,64 +165,12 @@ OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status, * parse the input token to figure out the mechanism to use. */ if (*context_handle == GSS_C_NO_CONTEXT) { - unsigned char *p = input_token->value; - size_t len = input_token->length; - size_t a, b; gss_OID_desc mech_oid; - /* - * Token must start with [APPLICATION 0] SEQUENCE. - * But if it doesn't assume its DCE-STYLE Kerberos! - * And if it's not there at all, then we are requesting a mech list from SPNEGO - */ - if (len == 0) { - mech_oid = *GSS_SPNEGO_MECHANISM; - } else if (*p != 0x60) { - mech_oid = *GSS_KRB5_MECHANISM; - } else { - p++; - len--; - - /* - * Decode the length and make sure it agrees with the - * token length. - */ - if (len == 0) - return (GSS_S_DEFECTIVE_TOKEN); - if ((*p & 0x80) == 0) { - a = *p; - p++; - len--; - } else { - b = *p & 0x7f; - p++; - len--; - if (len < b) - return (GSS_S_DEFECTIVE_TOKEN); - a = 0; - while (b) { - a = (a << 8) | *p; - p++; - len--; - b--; - } - } - if (a != len) - return (GSS_S_DEFECTIVE_TOKEN); - - /* - * Decode the OID for the mechanism. Simplify life by - * assuming that the OID length is less than 128 bytes. - */ - if (len < 2 || *p != 0x06) - return (GSS_S_DEFECTIVE_TOKEN); - if ((p[1] & 0x80) || p[1] > (len - 2)) - return (GSS_S_DEFECTIVE_TOKEN); - mech_oid.length = p[1]; - p += 2; - len -= 2; - mech_oid.elements = p; - } + major_status = choose_mech(input_token, &mech_oid); + if (major_status != GSS_S_COMPLETE) + return major_status; + /* * Now that we have a mechanism, we can find the * implementation. diff --git a/source4/heimdal/lib/gssapi/mech/gss_krb5.c b/source4/heimdal/lib/gssapi/mech/gss_krb5.c index 34cdbeb3c1..76a2c2b637 100644 --- a/source4/heimdal/lib/gssapi/mech/gss_krb5.c +++ b/source4/heimdal/lib/gssapi/mech/gss_krb5.c @@ -27,7 +27,7 @@ */ #include "mech_locl.h" -RCSID("$Id: gss_krb5.c,v 1.20 2006/11/08 23:11:03 lha Exp $"); +RCSID("$Id: gss_krb5.c,v 1.21 2006/11/10 00:57:27 lha Exp $"); #include #include @@ -421,13 +421,41 @@ gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c) */ OM_uint32 -gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, +gss_krb5_set_allowable_enctypes(OM_uint32 *min_status, gss_cred_id_t cred, OM_uint32 num_enctypes, - krb5_enctype *enctypes) + int32_t *enctypes) { - *minor_status = 0; - return GSS_S_COMPLETE; + OM_uint32 maj_status; + gss_buffer_desc buffer; + krb5_storage *sp; + krb5_data data; + + sp = krb5_storage_emem(); + if (sp == NULL) { + *min_status = ENOMEM; + maj_status = GSS_S_FAILURE; + goto out; + } + + while(*enctypes) { + krb5_store_int32(sp, *enctypes); + enctypes++; + } + + krb5_storage_to_data(sp, &data); + + buffer.value = data.data; + buffer.length = data.length; + + maj_status = gss_set_cred_option(min_status, + &cred, + GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, + &buffer); +out: + if (sp) + krb5_storage_free(sp); + return maj_status; } /* diff --git a/source4/heimdal/lib/krb5/krb5-private.h b/source4/heimdal/lib/krb5/krb5-private.h index 0bf184a530..ba2f75ad22 100644 --- a/source4/heimdal/lib/krb5/krb5-private.h +++ b/source4/heimdal/lib/krb5/krb5-private.h @@ -299,6 +299,37 @@ _krb5_oid_to_enctype ( const heim_oid */*oid*/, krb5_enctype */*etype*/); +void +_krb5_pac_free ( + krb5_context /*context*/, + struct krb5_pac */*pac*/); + +krb5_error_code +_krb5_pac_parse ( + krb5_context /*context*/, + const void */*ptr*/, + size_t /*len*/, + struct krb5_pac **/*pac*/); + +krb5_error_code +_krb5_pac_sign ( + krb5_context /*context*/, + struct krb5_pac */*p*/, + time_t /*authtime*/, + krb5_principal /*principal*/, + krb5_keyblock */*server_key*/, + krb5_keyblock */*priv_key*/, + krb5_data */*data*/); + +krb5_error_code +_krb5_pac_verify ( + krb5_context /*context*/, + struct krb5_pac */*pac*/, + time_t /*authtime*/, + krb5_principal /*principal*/, + krb5_keyblock */*server*/, + krb5_keyblock */*privsvr*/); + krb5_error_code _krb5_parse_moduli ( krb5_context /*context*/, @@ -380,6 +411,29 @@ _krb5_pk_verify_sign ( krb5_data */*content*/, struct krb5_pk_cert **/*signer*/); +krb5_error_code +_krb5_plugin_find ( + krb5_context /*context*/, + enum plugin_type /*type*/, + const char */*name*/, + struct krb5_plugin **/*list*/); + +void +_krb5_plugin_free (struct krb5_plugin */*list*/); + +struct krb5_plugin * +_krb5_plugin_get_next (struct krb5_plugin */*p*/); + +void * +_krb5_plugin_get_symbol (struct krb5_plugin */*p*/); + +krb5_error_code +_krb5_plugin_register ( + krb5_context /*context*/, + enum plugin_type /*type*/, + const char */*name*/, + void */*symbol*/); + krb5_error_code KRB5_LIB_FUNCTION _krb5_principal2principalname ( PrincipalName */*p*/, diff --git a/source4/heimdal/lib/krb5/krb5-protos.h b/source4/heimdal/lib/krb5/krb5-protos.h index 104f10bdf2..8b61e8d7d2 100644 --- a/source4/heimdal/lib/krb5/krb5-protos.h +++ b/source4/heimdal/lib/krb5/krb5-protos.h @@ -498,6 +498,12 @@ krb5_c_is_coll_proof_cksum (krb5_cksumtype /*ctype*/); krb5_boolean KRB5_LIB_FUNCTION krb5_c_is_keyed_cksum (krb5_cksumtype /*ctype*/); +krb5_error_code KRB5_LIB_FUNCTION +krb5_c_keylength ( + krb5_context /*context*/, + krb5_enctype /*enctype*/, + size_t */*len*/); + krb5_error_code KRB5_LIB_FUNCTION krb5_c_make_checksum ( krb5_context /*context*/, @@ -2165,6 +2171,9 @@ krb5_get_krbhst ( const krb5_realm */*realm*/, char ***/*hostlist*/); +time_t KRB5_LIB_FUNCTION +krb5_get_max_time_skew (krb5_context /*context*/); + krb5_error_code KRB5_LIB_FUNCTION krb5_get_pw_salt ( krb5_context /*context*/, @@ -2177,9 +2186,6 @@ krb5_get_server_rcache ( const krb5_data */*piece*/, krb5_rcache */*id*/); -time_t KRB5_LIB_FUNCTION -krb5_get_time_wrap (krb5_context /*context*/); - krb5_boolean KRB5_LIB_FUNCTION krb5_get_use_admin_kdc (krb5_context /*context*/); @@ -3165,6 +3171,11 @@ krb5_set_ignore_addresses ( krb5_context /*context*/, const krb5_addresses */*addresses*/); +void KRB5_LIB_FUNCTION +krb5_set_max_time_skew ( + krb5_context /*context*/, + time_t /*t*/); + krb5_error_code KRB5_LIB_FUNCTION krb5_set_password ( krb5_context /*context*/, @@ -3197,11 +3208,6 @@ krb5_set_send_to_kdc_func ( krb5_send_to_kdc_func /*func*/, void */*data*/); -void KRB5_LIB_FUNCTION -krb5_set_time_wrap ( - krb5_context /*context*/, - time_t /*t*/); - void KRB5_LIB_FUNCTION krb5_set_use_admin_kdc ( krb5_context /*context*/, @@ -3271,6 +3277,11 @@ krb5_storage_from_mem ( void */*buf*/, size_t /*len*/); +krb5_storage * KRB5_LIB_FUNCTION +krb5_storage_from_readonly_mem ( + const void */*buf*/, + size_t /*len*/); + krb5_flags KRB5_LIB_FUNCTION krb5_storage_get_byteorder ( krb5_storage */*sp*/, diff --git a/source4/heimdal/lib/krb5/krb5.h b/source4/heimdal/lib/krb5/krb5.h index f5c8b069de..1b26e8b3e7 100644 --- a/source4/heimdal/lib/krb5/krb5.h +++ b/source4/heimdal/lib/krb5/krb5.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: krb5.h,v 1.254 2006/11/07 00:17:42 lha Exp $ */ +/* $Id: krb5.h,v 1.255 2006/11/12 08:33:07 lha Exp $ */ #ifndef __KRB5_H__ #define __KRB5_H__ @@ -78,6 +78,8 @@ typedef struct krb5_get_creds_opt_data *krb5_get_creds_opt; struct krb5_digest; typedef struct krb5_digest *krb5_digest; +struct krb5_pac; + typedef struct krb5_rd_req_in_ctx *krb5_rd_req_in_ctx; typedef struct krb5_rd_req_out_ctx *krb5_rd_req_out_ctx; diff --git a/source4/heimdal/lib/krb5/krb5_locl.h b/source4/heimdal/lib/krb5/krb5_locl.h index 89b3c6ad40..3fb5461b3c 100644 --- a/source4/heimdal/lib/krb5/krb5_locl.h +++ b/source4/heimdal/lib/krb5/krb5_locl.h @@ -148,6 +148,12 @@ struct krb5_dh_moduli; /* v4 glue */ struct _krb5_krb_auth_data; +struct krb5_plugin; +enum plugin_type { + PLUGIN_TYPE_DATA = 1, + PLUGIN_TYPE_FUNC +}; + #include #include diff --git a/source4/heimdal/lib/krb5/krbhst.c b/source4/heimdal/lib/krb5/krbhst.c index e7b2579229..f395f0d0c3 100644 --- a/source4/heimdal/lib/krb5/krbhst.c +++ b/source4/heimdal/lib/krb5/krbhst.c @@ -33,8 +33,9 @@ #include "krb5_locl.h" #include +#include "locate_plugin.h" -RCSID("$Id: krbhst.c,v 1.57 2006/10/06 17:11:02 lha Exp $"); +RCSID("$Id: krbhst.c,v 1.58 2006/11/12 20:05:20 lha Exp $"); static int string_to_proto(const char *string) @@ -147,6 +148,7 @@ struct krb5_krbhst_data { #define KD_FALLBACK 16 #define KD_CONFIG_EXISTS 32 #define KD_LARGE_MSG 64 +#define KD_PLUGIN 128 krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *, krb5_krbhst_info**); @@ -460,8 +462,8 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, hi->proto = proto; hi->port = hi->def_port = port; hi->ai = ai; - memmove(hi->hostname, host, hostlen - 1); - hi->hostname[hostlen - 1] = '\0'; + memmove(hi->hostname, host, hostlen); + hi->hostname[hostlen] = '\0'; free(host); append_host_hostinfo(kd, hi); kd->fallback_count++; @@ -469,6 +471,88 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, return 0; } +/* + * Fetch hosts from plugin + */ + +static krb5_error_code +add_locate(void *ctx, int type, struct sockaddr *addr) +{ + struct krb5_krbhst_info *hi; + struct krb5_krbhst_data *kd = ctx; + char host[NI_MAXHOST], port[NI_MAXSERV]; + struct addrinfo hints, *ai; + socklen_t socklen; + size_t hostlen; + int ret; + + socklen = socket_sockaddr_size(addr); + + ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV); + if (ret != 0) + return 0; + + memset(&hints, 0, sizeof(hints)); + ret = getaddrinfo(host, port, &hints, &ai); + if (ret) + return 0; + + hostlen = strlen(host); + + hi = calloc(1, sizeof(*hi) + hostlen); + if(hi == NULL) { + free(host); + return ENOMEM; + } + + hi->proto = krbhst_get_default_proto(kd); + hi->port = hi->def_port = socket_get_port(addr); + hi->ai = ai; + memmove(hi->hostname, host, hostlen); + hi->hostname[hostlen] = '\0'; + append_host_hostinfo(kd, hi); + + return 0; +} + +static void +plugin_get_hosts(krb5_context context, + struct krb5_krbhst_data *kd, + enum locate_service_type type) +{ + struct krb5_plugin *list, *e; + krb5_error_code ret; + + ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "resolve", &list); + if(ret != 0 || list == NULL) + return; + + kd->flags |= KD_CONFIG_EXISTS; + + for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) { + krb5plugin_service_locate_ftable *service; + void *ctx; + + service = _krb5_plugin_get_symbol(e); + if (service->minor_version != 0) + continue; + + (*service->init)(context, &ctx); + ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd); + (*service->fini)(ctx); + if (ret) { + krb5_set_error_string(context, "Plugin failed to lookup"); + break; + } + } + _krb5_plugin_free(list); +} + +/* + * + */ + static krb5_error_code kdc_get_next(krb5_context context, struct krb5_krbhst_data *kd, @@ -476,6 +560,13 @@ kdc_get_next(krb5_context context, { krb5_error_code ret; + if ((kd->flags & KD_PLUGIN) == 0) { + plugin_get_hosts(context, kd, locate_service_kdc); + kd->flags |= KD_PLUGIN; + if(get_next(kd, host)) + return 0; + } + if((kd->flags & KD_CONFIG) == 0) { config_get_hosts(context, kd, "kdc"); kd->flags |= KD_CONFIG; diff --git a/source4/heimdal/lib/krb5/locate_plugin.h b/source4/heimdal/lib/krb5/locate_plugin.h new file mode 100644 index 0000000000..ec06d362cf --- /dev/null +++ b/source4/heimdal/lib/krb5/locate_plugin.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006 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. + */ + +/* $Id: locate_plugin.h,v 1.1 2006/11/12 19:00:03 lha Exp $ */ + +#ifndef HEIMDAL_KRB5_LOCATE_PLUGIN_H +#define HEIMDAL_KRB5_LOCATE_PLUGIN_H 1 + +#include + +enum locate_service_type { + locate_service_kdc = 1, + locate_service_master_kdc, + locate_service_kadmin, + locate_service_krb524, + locate_service_kpasswd +}; + +typedef krb5_error_code +(*krb5plugin_service_locate_lookup) (void *, enum locate_service_type, + const char *, int, int, + int (*)(void *,int,struct sockaddr *), + void *); + + +typedef struct krb5plugin_service_locate_ftable { + int minor_version; + krb5_error_code (*init)(krb5_context, void **); + void (*fini)(void *); + krb5plugin_service_locate_lookup lookup; +} krb5plugin_service_locate_ftable; + +#endif /* HEIMDAL_KRB5_LOCATE_PLUGIN_H */ + diff --git a/source4/heimdal/lib/krb5/mit_glue.c b/source4/heimdal/lib/krb5/mit_glue.c index b9075b3079..493c4cd845 100755 --- a/source4/heimdal/lib/krb5/mit_glue.c +++ b/source4/heimdal/lib/krb5/mit_glue.c @@ -32,7 +32,7 @@ */ #include "krb5_locl.h" -RCSID("$Id: mit_glue.c,v 1.8 2006/10/14 09:51:02 lha Exp $"); +RCSID("$Id: mit_glue.c,v 1.9 2006/11/09 21:24:16 lha Exp $"); /* * Glue for MIT API @@ -325,3 +325,11 @@ krb5_c_make_random_key(krb5_context context, { return krb5_generate_random_keyblock(context, enctype, random_key); } + +krb5_error_code KRB5_LIB_FUNCTION +krb5_c_keylength(krb5_context context, + krb5_enctype enctype, + size_t *len) +{ + return krb5_enctype_keysize(context, enctype, len); +} diff --git a/source4/heimdal/lib/krb5/plugin.c b/source4/heimdal/lib/krb5/plugin.c new file mode 100644 index 0000000000..294807faab --- /dev/null +++ b/source4/heimdal/lib/krb5/plugin.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2006 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 "krb5_locl.h" +RCSID("$Id: plugin.c,v 1.2 2006/11/12 21:39:43 lha Exp $"); +#ifdef HAVE_DLFCN_H +#include +#endif +#include + +struct krb5_plugin { + void *symbol; + void *dsohandle; + struct krb5_plugin *next; +}; + +struct plugin { + enum plugin_type type; + void *name; + void *symbol; + struct plugin *next; +}; + +static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER; +static struct plugin *registered = NULL; + +static const char *plugin_dir = LIBDIR "/plugin/krb5"; + +/* + * + */ + +void * +_krb5_plugin_get_symbol(struct krb5_plugin *p) +{ + return p->symbol; +} + +struct krb5_plugin * +_krb5_plugin_get_next(struct krb5_plugin *p) +{ + return p->next; +} + +/* + * + */ + +static krb5_error_code +loadlib(krb5_context context, + enum plugin_type type, + const char *name, + const char *lib, + struct krb5_plugin **e) +{ + *e = calloc(1, sizeof(**e)); + if (*e == NULL) { + krb5_set_error_string(context, "out of memory"); + return ENOMEM; + } + + (*e)->dsohandle = dlopen(lib, 0); + if ((*e)->dsohandle == NULL) { + free(*e); + krb5_set_error_string(context, "Failed to load %s: %s", + lib, dlerror()); + return ENOMEM; + } + + /* dlsym doesn't care about the type */ + (*e)->symbol = dlsym((*e)->dsohandle, name); + if ((*e)->symbol == NULL) { + dlclose((*e)->dsohandle); + free(*e); + krb5_clear_error_string(context); + return ENOMEM; + } + + return 0; +} + +krb5_error_code +_krb5_plugin_register(krb5_context context, + enum plugin_type type, + const char *name, + void *symbol) +{ + struct plugin *e; + + e = calloc(1, sizeof(*e)); + if (e == NULL) { + krb5_set_error_string(context, "out of memory"); + return ENOMEM; + } + e->type = type; + e->name = strdup(name); + if (e->name == NULL) { + free(e); + krb5_set_error_string(context, "out of memory"); + return ENOMEM; + } + e->symbol = symbol; + + HEIMDAL_MUTEX_lock(&plugin_mutex); + e->next = registered; + registered = e; + HEIMDAL_MUTEX_unlock(&plugin_mutex); + + return 0; +} + +krb5_error_code +_krb5_plugin_find(krb5_context context, + enum plugin_type type, + const char *name, + struct krb5_plugin **list) +{ + struct krb5_plugin *e; + struct plugin *p; + krb5_error_code ret; + char *sysdirs[2] = { NULL, NULL }; + char **dirs = NULL, **di; + struct dirent *entry; + char *path; + DIR *d = NULL; + + *list = NULL; + + HEIMDAL_MUTEX_lock(&plugin_mutex); + + for (p = registered; p != NULL; p = p->next) { + if (p->type != type || strcmp(p->name, name) != 0) + continue; + + e = calloc(1, sizeof(*e)); + if (e == NULL) { + HEIMDAL_MUTEX_unlock(&plugin_mutex); + krb5_set_error_string(context, "out of memory"); + ret = ENOMEM; + goto out; + } + e->symbol = p->symbol; + e->dsohandle = NULL; + e->next = *list; + *list = e; + } + HEIMDAL_MUTEX_unlock(&plugin_mutex); + + dirs = krb5_config_get_strings(context, NULL, "libdefaults", + "plugin_dir", NULL); + if (dirs == NULL) { + sysdirs[0] = rk_UNCONST(plugin_dir); + dirs = sysdirs; + } + + for (di = dirs; *di != NULL; di++) { + + d = opendir(*di); + if (d == NULL) + continue; + + while ((entry = readdir(d)) != NULL) { + asprintf(&path, "%s/%s", *di, entry->d_name); + if (path == NULL) { + krb5_set_error_string(context, "out of memory"); + ret = ENOMEM; + goto out; + } + ret = loadlib(context, type, name, path, &e); + free(path); + if (ret) + continue; + + e->next = *list; + *list = e; + } + closedir(d); + } + if (dirs != sysdirs) + krb5_config_free_strings(dirs); + + if (*list == NULL) { + krb5_set_error_string(context, "Did not find a plugin for %s", name); + return ENOENT; + } + + return 0; + +out: + if (dirs && dirs != sysdirs) + krb5_config_free_strings(dirs); + if (d) + closedir(d); + _krb5_plugin_free(*list); + *list = NULL; + + return ret; +} + +void +_krb5_plugin_free(struct krb5_plugin *list) +{ + struct krb5_plugin *next; + while (list) { + next = list->next; + if (list->dsohandle) + dlclose(list->dsohandle); + free(list); + list = next; + } +} + diff --git a/source4/heimdal/lib/roken/socket.c b/source4/heimdal/lib/roken/socket.c new file mode 100644 index 0000000000..5f77aacf43 --- /dev/null +++ b/source4/heimdal/lib/roken/socket.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 1999 - 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. + */ + +#ifdef HAVE_CONFIG_H +#include +RCSID("$Id: socket.c,v 1.11 2005/09/01 18:48:17 lha Exp $"); +#endif + +#include +#include + +/* + * Set `sa' to the unitialized address of address family `af' + */ + +void ROKEN_LIB_FUNCTION +socket_set_any (struct sockaddr *sa, int af) +{ + switch (af) { + case AF_INET : { + struct sockaddr_in *sin4 = (struct sockaddr_in *)sa; + + memset (sin4, 0, sizeof(*sin4)); + sin4->sin_family = AF_INET; + sin4->sin_port = 0; + sin4->sin_addr.s_addr = INADDR_ANY; + break; + } +#ifdef HAVE_IPV6 + case AF_INET6 : { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + + memset (sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_addr = in6addr_any; + break; + } +#endif + default : + errx (1, "unknown address family %d", sa->sa_family); + break; + } +} + +/* + * set `sa' to (`ptr', `port') + */ + +void ROKEN_LIB_FUNCTION +socket_set_address_and_port (struct sockaddr *sa, const void *ptr, int port) +{ + switch (sa->sa_family) { + case AF_INET : { + struct sockaddr_in *sin4 = (struct sockaddr_in *)sa; + + memset (sin4, 0, sizeof(*sin4)); + sin4->sin_family = AF_INET; + sin4->sin_port = port; + memcpy (&sin4->sin_addr, ptr, sizeof(struct in_addr)); + break; + } +#ifdef HAVE_IPV6 + case AF_INET6 : { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + + memset (sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = port; + memcpy (&sin6->sin6_addr, ptr, sizeof(struct in6_addr)); + break; + } +#endif + default : + errx (1, "unknown address family %d", sa->sa_family); + break; + } +} + +/* + * Return the size of an address of the type in `sa' + */ + +size_t ROKEN_LIB_FUNCTION +socket_addr_size (const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET : + return sizeof(struct in_addr); +#ifdef HAVE_IPV6 + case AF_INET6 : + return sizeof(struct in6_addr); +#endif + default : + errx (1, "unknown address family %d", sa->sa_family); + break; + } +} + +/* + * Return the size of a `struct sockaddr' in `sa'. + */ + +size_t ROKEN_LIB_FUNCTION +socket_sockaddr_size (const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET : + return sizeof(struct sockaddr_in); +#ifdef HAVE_IPV6 + case AF_INET6 : + return sizeof(struct sockaddr_in6); +#endif + default : + errx (1, "unknown address family %d", sa->sa_family); + break; + } +} + +/* + * Return the binary address of `sa'. + */ + +void * ROKEN_LIB_FUNCTION +socket_get_address (struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET : { + struct sockaddr_in *sin4 = (struct sockaddr_in *)sa; + return &sin4->sin_addr; + } +#ifdef HAVE_IPV6 + case AF_INET6 : { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + return &sin6->sin6_addr; + } +#endif + default : + errx (1, "unknown address family %d", sa->sa_family); + break; + } +} + +/* + * Return the port number from `sa'. + */ + +int ROKEN_LIB_FUNCTION +socket_get_port (const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET : { + const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; + return sin4->sin_port; + } +#ifdef HAVE_IPV6 + case AF_INET6 : { + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + return sin6->sin6_port; + } +#endif + default : + errx (1, "unknown address family %d", sa->sa_family); + break; + } +} + +/* + * Set the port in `sa' to `port'. + */ + +void ROKEN_LIB_FUNCTION +socket_set_port (struct sockaddr *sa, int port) +{ + switch (sa->sa_family) { + case AF_INET : { + struct sockaddr_in *sin4 = (struct sockaddr_in *)sa; + sin4->sin_port = port; + break; + } +#ifdef HAVE_IPV6 + case AF_INET6 : { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + sin6->sin6_port = port; + break; + } +#endif + default : + errx (1, "unknown address family %d", sa->sa_family); + break; + } +} + +/* + * Set the range of ports to use when binding with port = 0. + */ +void ROKEN_LIB_FUNCTION +socket_set_portrange (int sock, int restr, int af) +{ +#if defined(IP_PORTRANGE) + if (af == AF_INET) { + int on = restr ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; + if (setsockopt (sock, IPPROTO_IP, IP_PORTRANGE, &on, + sizeof(on)) < 0) + warn ("setsockopt IP_PORTRANGE (ignored)"); + } +#endif +#if defined(IPV6_PORTRANGE) + if (af == AF_INET6) { + int on = restr ? IPV6_PORTRANGE_HIGH : + IPV6_PORTRANGE_DEFAULT; + if (setsockopt (sock, IPPROTO_IPV6, IPV6_PORTRANGE, &on, + sizeof(on)) < 0) + warn ("setsockopt IPV6_PORTRANGE (ignored)"); + } +#endif +} + +/* + * Enable debug on `sock'. + */ + +void ROKEN_LIB_FUNCTION +socket_set_debug (int sock) +{ +#if defined(SO_DEBUG) && defined(HAVE_SETSOCKOPT) + int on = 1; + + if (setsockopt (sock, SOL_SOCKET, SO_DEBUG, (void *) &on, sizeof (on)) < 0) + warn ("setsockopt SO_DEBUG (ignored)"); +#endif +} + +/* + * Set the type-of-service of `sock' to `tos'. + */ + +void ROKEN_LIB_FUNCTION +socket_set_tos (int sock, int tos) +{ +#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) + if (setsockopt (sock, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof (int)) < 0) + if (errno != EINVAL) + warn ("setsockopt TOS (ignored)"); +#endif +} + +/* + * set the reuse of addresses on `sock' to `val'. + */ + +void ROKEN_LIB_FUNCTION +socket_set_reuseaddr (int sock, int val) +{ +#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT) + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&val, + sizeof(val)) < 0) + err (1, "setsockopt SO_REUSEADDR"); +#endif +} + +/* + * Set the that the `sock' should bind to only IPv6 addresses. + */ + +void ROKEN_LIB_FUNCTION +socket_set_ipv6only (int sock, int val) +{ +#if defined(IPV6_V6ONLY) && defined(HAVE_SETSOCKOPT) + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val, sizeof(val)); +#endif +} -- cgit