From: Luke Howard Date: Tue, 7 Sep 2010 21:41:05 +0000 (+0200) Subject: More work on initial implementation X-Git-Tag: vm/20110310~397 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.orig;a=commitdiff_plain;h=6800e2157097202a9e1e8ff414fb484ef4607bde More work on initial implementation --- diff --git a/.gitignore b/.gitignore index fbcaac4..5be0c7e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ Makefile .deps .libs +.a.out.dSYM +.dSYM diff --git a/Makefile.am b/Makefile.am index 479c57a..3a86451 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,9 +4,9 @@ gssdir = $(libdir)/gss gss_LTLIBRARIES = libmech_eap.la -libmech_eap_la_CFLAGS = -g -Wall -fno-strict-aliasing @KRB5_CFLAGS@ @TARGET_CFLAGS@ -libmech_eap_la_LDFLAGS = -export-symbols mech_eap.exports -version-info 0:0:0 @KRB5_LDFLAGS@ @TARGET_LDFLAGS@ -libmech_eap_la_LIBADD = @KRB5_LIBS@ +libmech_eap_la_CFLAGS = -DBUILD_GSSEAP_LIB -g -Wall -fno-strict-aliasing @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@ +libmech_eap_la_LDFLAGS = -export-symbols mech_eap.exports -version-info 0:0:0 @EAP_LDFLAGS@ @KRB5_LDFLAGS@ @TARGET_LDFLAGS@ +libmech_eap_la_LIBADD = @EAP_LIBS@ @KRB5_LIBS@ libmech_eap_la_SOURCES = \ accept_sec_context.c \ @@ -32,8 +32,10 @@ libmech_eap_la_SOURCES = \ init_sec_context.c \ inquire_context.c \ inquire_cred.c \ + inquire_cred_by_oid.c \ inquire_mechs_for_name.c \ inquire_names_for_mech.c \ + inquire_sec_context_by_oid.c \ map_name_to_any.c \ process_context_token.c \ pseudo_random.c \ @@ -41,9 +43,16 @@ libmech_eap_la_SOURCES = \ release_cred.c \ release_name.c \ set_name_attribute.c \ + set_cred_option.c \ + set_sec_context_option.c \ store_cred.c \ unwrap.c \ unwrap_iov.c \ + util_context.c \ + util_cksum.c \ + util_cred.c \ + util_crypt.c \ + util_name.c \ verify_mic.c \ wrap.c \ wrap_iov.c \ diff --git a/acinclude.m4 b/acinclude.m4 index c376194..cd51aab 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1,7 +1,7 @@ dnl Based on the one from the Boinc project by Reinhard AC_DEFUN([AX_CHECK_KRB5], -[AC_MSG_CHECKING(for Kerberos) +[AC_MSG_CHECKING(for GSS-API and Kerberos implementation) KRB5_DIR= found_krb5="no" AC_ARG_WITH(krb5, @@ -13,22 +13,16 @@ for dir in $check_krb5_dir /usr /usr/local ; do krb5dir="$dir" if test -f "$dir/include/krb5.h"; then found_krb5="yes"; - krb5_DIR="${krb5dir}" - krb5_CFLAGS="-I$krb5dir/include"; + KRB5_DIR="${krb5dir}" + KRB5_CFLAGS="-I$krb5dir/include"; break; fi - if test -f "$dir/include/krb5.h"; then - found_krb5="yes"; - krb5_DIR="${krb5dir}" - krb5_CFLAGS="-I$krb5dir/include/"; - break - fi done AC_MSG_RESULT($found_krb5) if test x_$found_krb5 != x_yes; then AC_MSG_ERROR([ ---------------------------------------------------------------------- - Cannot find krb5 libraries. + Cannot find GSS-API/Kerberos libraries. Please install MIT or Heimdal or specify installation directory with --with-krb5=(dir). @@ -36,10 +30,49 @@ if test x_$found_krb5 != x_yes; then ]) else printf "Kerberos found in $krb5dir\n"; - krb5_LIBS="-lgssapi_krb5 -lkrb5"; - krb5_LDFLAGS="-L$krb5dir/lib"; + KRB5_LIBS="-lgssapi_krb5 -lkrb5"; + KRB5_LDFLAGS="-L$krb5dir/lib"; AC_SUBST(KRB5_CFLAGS) AC_SUBST(KRB5_LDFLAGS) AC_SUBST(KRB5_LIBS) fi ])dnl + +AC_DEFUN([AX_CHECK_EAP], +[AC_MSG_CHECKING(for EAP implementation) +EAP_DIR= +found_eap="no" +AC_ARG_WITH(eap, + AC_HELP_STRING([--with-eap], + [Use eap (in specified installation directory)]), + [check_eap_dir="$withval"], + [check_eap_dir=]) +for dir in $check_eap_dir /usr /usr/local ; do + eapdir="$dir" + if test -f "$dir/src/eap_peer/eap.h"; then + found_eap="yes"; + EAP_DIR="${eapdir}" + EAP_CFLAGS="-I$eapdir/src/common -I$eapdir/src -I$eapdir/src/utils"; + break; + fi +done +AC_MSG_RESULT($found_eap) +if test x_$found_eap != x_yes; then + AC_MSG_ERROR([ +---------------------------------------------------------------------- + Cannot find EAP libraries. + + Please install wpa_supplicant or specify installation directory with + --with-eap=(dir). +---------------------------------------------------------------------- +]) +else + printf "EAP found in $eapdir\n"; + EAP_LIBS="-leap"; + EAP_LDFLAGS="-L$eapdir/eap_example"; + AC_SUBST(EAP_CFLAGS) + AC_SUBST(EAP_LDFLAGS) + AC_SUBST(EAP_LIBS) +fi +])dnl + diff --git a/configure.ac b/configure.ac index 79826d3..ee0ae6b 100644 --- a/configure.ac +++ b/configure.ac @@ -27,5 +27,6 @@ dnl esac AC_SUBST(TARGET_CFLAGS) AC_SUBST(TARGET_LDFLAGS) AX_CHECK_KRB5 +AX_CHECK_EAP AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/gssapiP_eap.h b/gssapiP_eap.h index 6415371..128b0fe 100644 --- a/gssapiP_eap.h +++ b/gssapiP_eap.h @@ -33,14 +33,30 @@ #ifndef _GSSAPIP_EAP_H_ #define _GSSAPIP_EAP_H_ 1 -#include -#include +#include +#include +#include +#include +/* GSS includes */ +#include +#include #include "gssapi_eap.h" +/* EAP includes */ +#define IEEE8021X_EAPOL 1 + +#include +#include +#include +#include + +/* Kerberos includes */ +#include + struct gss_name_struct { OM_uint32 flags; - krb5_principal principal; + krb5_principal kerberosName; void *aaa; void *assertion; }; @@ -52,13 +68,15 @@ struct gss_name_struct { struct gss_cred_id_struct { OM_uint32 flags; - gss_name_t initiatorName; - gss_name_t acceptorName; + gss_name_t name; gss_buffer_desc password; + time_t expiryTime; }; #define CTX_FLAG_INITIATOR 0x00000001 +#define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0) + enum eap_gss_state { EAP_STATE_AUTHENTICATE = 1, EAP_STATE_KEY_TRANSPORT, @@ -67,18 +85,168 @@ enum eap_gss_state { EAP_STATE_ESTABLISHED }; +#define CTX_IS_ESTABLISHED(ctx) ((ctx)->state == EAP_STATE_ESTABLISHED) + +/* Initiator context flags */ +#define CTX_FLAG_EAP_SUCCESS 0x00010000 +#define CTX_FLAG_EAP_RESTART 0x00020000 +#define CTX_FLAG_EAP_FAIL 0x00040000 +#define CTX_FLAG_EAP_RESP 0x00080000 +#define CTX_FLAG_EAP_NO_RESP 0x00100000 +#define CTX_FLAG_EAP_REQ 0x00200000 +#define CTX_FLAG_EAP_PORT_ENABLED 0x00400000 +#define CTX_FLAG_EAP_ALT_ACCEPT 0x00800000 +#define CTX_FLAG_EAP_ALT_REJECT 0x01000000 + +struct eap_gss_initiator_ctx { + struct wpabuf *eapReqData; + unsigned int idleWhile; + struct eap_peer_config eapConfig; + struct eap_sm *eap; +}; + +/* Acceptor context flags */ +struct eap_gss_acceptor_ctx { +}; + struct gss_ctx_id_struct { enum eap_gss_state state; OM_uint32 flags; OM_uint32 gssFlags; krb5_context kerberosCtx; gss_OID mechanismUsed; + krb5_enctype encryptionType; krb5_cksumtype checksumType; krb5_keyblock *encryptionKey; gss_name_t initiatorName; gss_name_t acceptorName; - OM_uint32 lifetime; + time_t expiryTime; + union { + struct eap_gss_initiator_ctx initiator; + #define initiatorCtx ctxU.initiator + struct eap_gss_acceptor_ctx acceptor; + #define acceptorCtx ctxU.acceptor + } ctxU; + uint64_t sendSeq, recvSeq; + void *seqState; +}; + +#define TOK_FLAG_SENDER_IS_ACCEPTOR 0x01 +#define TOK_FLAG_WRAP_CONFIDENTIAL 0x02 +#define TOK_FLAG_ACCEPTOR_SUBKEY 0x04 + +enum gss_eap_token_type { + TOK_TYPE_MIC = 0x0404, + TOK_TYPE_WRAP = 0x0504, + TOK_TYPE_DELETE = 0x0405 }; +/* Helper APIs */ +OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); +OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); + +OM_uint32 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName); +OM_uint32 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName); + +OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred); +OM_uint32 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred); + +/* Kerberos token services */ +#define KRB_USAGE_ACCEPTOR_SEAL 22 +#define KRB_USAGE_ACCEPTOR_SIGN 23 +#define KRB_USAGE_INITIATOR_SEAL 24 +#define KRB_USAGE_INITIATOR_SIGN 25 + +#if 0 +#define KRB_KEYTYPE(key) ((key)->keytype) +#else +#define KRB_KEYTYPE(key) ((key)->enctype) +#endif + +/* util_crypt.c */ +int +gssEapEncrypt(krb5_context context, int dce_style, size_t ec, + size_t rrc, krb5_keyblock *key, int usage, krb5_pointer iv, + gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapDecrypt(krb5_context context, int dce_style, size_t ec, + size_t rrc, krb5_keyblock *key, int usage, krb5_pointer iv, + gss_iov_buffer_desc *iov, int iov_count); + +krb5_cryptotype +gssEapTranslateCryptoFlag(OM_uint32 type); + +gss_iov_buffer_t +gssEapLocateIov(gss_iov_buffer_desc *iov, + int iov_count, + OM_uint32 type); + +void +gssEapIovMessageLength(gss_iov_buffer_desc *iov, + int iov_count, + size_t *data_length, + size_t *assoc_data_length); + +void +gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapAllocIov(gss_iov_buffer_t iov, size_t size); + +/* util_cksum.c */ +int +gssEapSign(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_keyblock *key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count); + +int +gssEapVerify(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_keyblock *key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int *valid); + +/* wrap_iov.c */ +OM_uint32 +gssEapWrapOrGetMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype); + +OM_uint32 +gssEapUnwrapOrVerifyMIC(OM_uint32 *minor_status, + gss_ctx_id_t ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype); + +/* Helper macros */ +#define GSSEAP_CALLOC(count, size) (calloc((count), (size))) +#define GSSEAP_FREE(ptr) (free((ptr))) +#define GSSEAP_MALLOC(size) (malloc((size))) +#define GSSEAP_REALLOC(ptr, size) (realloc((ptr), (size))) + +#define GSSEAP_NOT_IMPLEMENTED do { \ + assert(0 && "not implemented"); \ + *minor = ENOSYS; \ + return GSS_S_FAILURE; \ + } while (0) + #endif /* _GSSAPIP_EAP_H_ */ diff --git a/unwrap_iov.c b/unwrap_iov.c index 264e137..3cec4f0 100644 --- a/unwrap_iov.c +++ b/unwrap_iov.c @@ -29,10 +29,226 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +/* + * lib/gssapi/krb5/k5sealv3iov.c + * + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + */ #include "gssapiP_eap.h" OM_uint32 +gssEapUnwrapOrVerifyMIC(OM_uint32 *minor_status, + gss_ctx_id_t ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype) +{ + OM_uint32 code; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char acceptor_flag; + unsigned char *ptr = NULL; + int key_usage; + size_t rrc, ec; + size_t data_length, assoc_data_length; + uint64_t seqnum; + krb5_boolean valid; + krb5_cksumtype cksumtype; + int conf_flag = 0; + + *minor_status = 0; + + if (qop_state != NULL) + *qop_state = GSS_C_QOP_DEFAULT; + + if (!CTX_IS_ESTABLISHED(ctx)) + return GSS_S_NO_CONTEXT; + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL && padding->buffer.length != 0) + return GSS_S_DEFECTIVE_TOKEN; + + trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + acceptor_flag = CTX_IS_INITIATOR(ctx) ? TOK_FLAG_SENDER_IS_ACCEPTOR : 0; + key_usage = (toktype == TOK_TYPE_WRAP + ? (!CTX_IS_INITIATOR(ctx) + ? KRB_USAGE_INITIATOR_SEAL + : KRB_USAGE_ACCEPTOR_SEAL) + : (!CTX_IS_INITIATOR(ctx) + ? KRB_USAGE_INITIATOR_SIGN + : KRB_USAGE_ACCEPTOR_SIGN)); + + gssEapIovMessageLength(iov, iov_count, &data_length, &assoc_data_length); + + ptr = (unsigned char *)header->buffer.value; + + if (header->buffer.length < 16) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((ptr[2] & TOK_FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { + return GSS_S_BAD_SIG; + } + + if (ptr[2] & TOK_FLAG_ACCEPTOR_SUBKEY) { + return GSS_S_BAD_SIG; + } + + if (toktype == TOK_TYPE_WRAP) { + unsigned int k5_trailerlen; + + if (load_16_be(ptr) != TOK_TYPE_WRAP) + goto defective; + conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0); + if (ptr[3] != 0xFF) + goto defective; + ec = load_16_be(ptr + 4); + rrc = load_16_be(ptr + 6); + seqnum = load_64_be(ptr + 8); + + code = krb5_c_crypto_length(ctx->kerberosCtx, + KRB_KEYTYPE(ctx->encryptionKey), + conf_flag ? KRB5_CRYPTO_TYPE_TRAILER : + KRB5_CRYPTO_TYPE_CHECKSUM, + &k5_trailerlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + /* Deal with RRC */ + if (trailer == NULL) { + size_t desired_rrc = k5_trailerlen; + + if (conf_flag) { + desired_rrc += 16; /* E(Header) */ + + if ((ctx->gssFlags & GSS_C_DCE_STYLE) == 0) + desired_rrc += ec; + } + + /* According to MS, we only need to deal with a fixed RRC for DCE */ + if (rrc != desired_rrc) + goto defective; + } else if (rrc != 0) { + goto defective; + } + + if (conf_flag) { + unsigned char *althdr; + + /* Decrypt */ + code = gssEapDecrypt(ctx->kerberosCtx, + ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0), + ec, rrc, ctx->encryptionKey, + key_usage, 0, iov, iov_count); + if (code != 0) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + + /* Validate header integrity */ + if (trailer == NULL) + althdr = (unsigned char *)header->buffer.value + 16 + ec; + else + althdr = (unsigned char *)trailer->buffer.value + ec; + + if (load_16_be(althdr) != TOK_TYPE_WRAP + || althdr[2] != ptr[2] + || althdr[3] != ptr[3] + || memcmp(althdr + 8, ptr + 8, 8) != 0) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + } else { + /* Verify checksum: note EC is checksum size here, not padding */ + if (ec != k5_trailerlen) + goto defective; + + /* Zero EC, RRC before computing checksum */ + store_16_be(0, ptr + 4); + store_16_be(0, ptr + 6); + + code = gssEapVerify(ctx->kerberosCtx, cksumtype, rrc, + ctx->encryptionKey, key_usage, + iov, iov_count, &valid); + if (code != 0 || valid == FALSE) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + } + + code = g_order_check(&ctx->seqState, seqnum); + } else if (toktype == TOK_TYPE_MIC) { + if (load_16_be(ptr) != TOK_TYPE_MIC) + goto defective; + + verify_mic_1: + if (ptr[3] != 0xFF) + goto defective; + seqnum = load_64_be(ptr + 8); + + code = gssEapVerify(ctx->kerberosCtx, cksumtype, 0, + ctx->encryptionKey, key_usage, + iov, iov_count, &valid); + if (code != 0 || valid == FALSE) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + code = g_order_check(&ctx->seqState, seqnum); + } else if (toktype == TOK_TYPE_DELETE) { + if (load_16_be(ptr) != TOK_TYPE_DELETE) + goto defective; + goto verify_mic_1; + } else { + goto defective; + } + + *minor_status = 0; + + if (conf_state != NULL) + *conf_state = conf_flag; + + return code; + +defective: + *minor_status = 0; + + return GSS_S_DEFECTIVE_TOKEN; +} + +OM_uint32 gss_unwrap_iov(OM_uint32 *minor, gss_ctx_id_t ctx, int *conf_state, @@ -40,5 +256,8 @@ gss_unwrap_iov(OM_uint32 *minor, gss_iov_buffer_desc *iov, int iov_count) { - GSSEAP_NOT_IMPLEMENTED; + return gssEapUnwrapOrVerifyMIC(minor, ctx, + iov, iov_count, conf_state, + qop_state, TOK_TYPE_WRAP); + } diff --git a/util_context.c b/util_context.c index daf65d6..7121d7f 100644 --- a/util_context.c +++ b/util_context.c @@ -36,6 +36,7 @@ OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx) { + OM_uint32 tmpMinor; gss_ctx_id_t ctx; assert(*pCtx == GSS_C_NO_CONTEXT); @@ -46,6 +47,12 @@ gssEapAllocContext(OM_uint32 *minor, return GSS_S_FAILURE; } + *minor = krb5_init_context(&ctx->kerberosCtx); + if (*minor != 0) { + gssEapReleaseContext(&tmpMinor, &ctx); + return GSS_S_FAILURE; + } + *pCtx = ctx; return GSS_S_COMPLETE; diff --git a/wrap_iov.c b/wrap_iov.c index ccc9b16..7bf7f94 100644 --- a/wrap_iov.c +++ b/wrap_iov.c @@ -29,10 +29,270 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +/* + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ #include "gssapiP_eap.h" OM_uint32 +gssEapWrapOrGetMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype) +{ + krb5_error_code code = 0; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char acceptor_flag; + unsigned char *outbuf = NULL; + unsigned char *tbuf = NULL; + int key_usage; + size_t rrc = 0; + unsigned int gss_headerlen, gss_trailerlen; + size_t data_length, assoc_data_length; + + if (!CTX_IS_ESTABLISHED(ctx)) { + return GSS_S_NO_CONTEXT; + } + + acceptor_flag = CTX_IS_INITIATOR(ctx) ? 0 : TOK_FLAG_SENDER_IS_ACCEPTOR; + key_usage = ((toktype == TOK_TYPE_WRAP) + ? (CTX_IS_INITIATOR(ctx) + ? KRB_USAGE_INITIATOR_SEAL + : KRB_USAGE_ACCEPTOR_SEAL) + : (CTX_IS_INITIATOR(ctx) + ? KRB_USAGE_INITIATOR_SIGN + : KRB_USAGE_ACCEPTOR_SIGN)); + + gssEapIovMessageLength(iov, iov_count, &data_length, &assoc_data_length); + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + if (header == NULL) { + *minor = EINVAL; + return GSS_S_FAILURE; + } + + padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL) + padding->buffer.length = 0; + + trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + if (toktype == TOK_TYPE_WRAP && conf_req_flag) { + unsigned int k5_headerlen, k5_trailerlen, k5_padlen; + size_t ec = 0; + size_t conf_data_length = data_length - assoc_data_length; + + code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType, + KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) + goto cleanup; + + code = krb5_c_padding_length(ctx->kerberosCtx, ctx->encryptionType, + conf_data_length + 16 /* E(Header) */, + &k5_padlen); + if (code != 0) + goto cleanup; + + if (k5_padlen == 0 && (ctx->gssFlags & GSS_C_DCE_STYLE)) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krb5_c_block_size(ctx->kerberosCtx, ctx->encryptionType, &ec); + if (code != 0) + goto cleanup; + } else + ec = k5_padlen; + + code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType, + KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen); + if (code != 0) + goto cleanup; + + gss_headerlen = 16 /* Header */ + k5_headerlen; + gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen; + + if (trailer == NULL) { + rrc = gss_trailerlen; + /* Workaround for Windows bug where it rotates by EC + RRC */ + if (ctx->gssFlags & GSS_C_DCE_STYLE) + rrc -= ec; + gss_headerlen += gss_trailerlen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = gssEapAllocIov(header, (size_t) gss_headerlen); + } else if (header->buffer.length < gss_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t) gss_headerlen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(trailer, (size_t) gss_trailerlen); + else if (trailer->buffer.length < gss_trailerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t) gss_trailerlen; + } + + /* TOK_ID */ + store_16_be((uint16_t)toktype, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0) + | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xFF; + /* EC */ + store_16_be(ec, outbuf + 4); + /* RRC */ + store_16_be(0, outbuf + 6); + store_64_be(ctx->sendSeq, outbuf + 8); + + /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */ + if (trailer == NULL) + tbuf = (unsigned char *)header->buffer.value + 16; /* Header */ + else + tbuf = (unsigned char *)trailer->buffer.value; + + memset(tbuf, 0xFF, ec); + memcpy(tbuf + ec, header->buffer.value, 16); + + code = gssEapEncrypt(ctx->kerberosCtx, + ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0), + ec, rrc, ctx->encryptionKey, + key_usage, 0, iov, iov_count); + if (code != 0) + goto cleanup; + + /* RRC */ + store_16_be(rrc, outbuf + 6); + + ctx->sendSeq++; + } else if (toktype == TOK_TYPE_WRAP && !conf_req_flag) { + wrap_with_checksum: + + gss_headerlen = 16; + + code = krb5_c_crypto_length(ctx->kerberosCtx, ctx->encryptionType, + KRB5_CRYPTO_TYPE_CHECKSUM, + &gss_trailerlen); + if (code != 0) + goto cleanup; + + assert(gss_trailerlen <= 0xFFFF); + + if (trailer == NULL) { + rrc = gss_trailerlen; + gss_headerlen += gss_trailerlen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(header, (size_t) gss_headerlen); + else if (header->buffer.length < gss_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t) gss_headerlen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(trailer, (size_t) gss_trailerlen); + else if (trailer->buffer.length < gss_trailerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t) gss_trailerlen; + } + + /* TOK_ID */ + store_16_be((uint16_t)toktype, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (0 ? TOK_FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xFF; + if (toktype == TOK_TYPE_WRAP) { + /* Use 0 for checksum calculation, substitute + * checksum length later. + */ + /* EC */ + store_16_be(0, outbuf + 4); + /* RRC */ + store_16_be(0, outbuf + 6); + } else { + /* MIC and DEL store 0xFF in EC and RRC */ + store_16_be(0xFFFF, outbuf + 4); + store_16_be(0xFFFF, outbuf + 6); + } + store_64_be(ctx->sendSeq, outbuf + 8); + + code = gssEapSign(ctx->kerberosCtx, ctx->checksumType, + rrc, ctx->encryptionKey, key_usage, + iov, iov_count); + if (code != 0) + goto cleanup; + + ctx->sendSeq++; + + if (toktype == TOK_TYPE_WRAP) { + /* Fix up EC field */ + store_16_be(gss_trailerlen, outbuf + 4); + /* Fix up RRC field */ + store_16_be(rrc, outbuf + 6); + } + } else if (toktype == TOK_TYPE_MIC) { + trailer = NULL; + goto wrap_with_checksum; + } else if (toktype == TOK_TYPE_DELETE) { + goto wrap_with_checksum; + } else { + abort(); + } + + code = 0; + +cleanup: + if (code != 0) + gssEapReleaseIov(iov, iov_count); + + *minor = code; + + if (code == 0) + return GSS_S_FAILURE; + else + return GSS_S_COMPLETE; +} + +OM_uint32 gss_wrap_iov(OM_uint32 *minor, gss_ctx_id_t ctx, int conf_req_flag, @@ -41,5 +301,6 @@ gss_wrap_iov(OM_uint32 *minor, gss_iov_buffer_desc *iov, int iov_count) { - GSSEAP_NOT_IMPLEMENTED; + return gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state, + iov, iov_count, TOK_TYPE_WRAP); }