X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=wrap_iov.c;h=19f263cf4d862ad6435e77be764e7f3174ffc3cb;hb=163856b1a70d7773c46d4ea5495b85c4dce0f089;hp=3e0654bfc89622e45b276dd4605e2a0311459de1;hpb=31cef49681566dc99790812f31de834dfce02c74;p=mech_eap.orig diff --git a/wrap_iov.c b/wrap_iov.c index 3e0654b..19f263c 100644 --- a/wrap_iov.c +++ b/wrap_iov.c @@ -29,6 +29,351 @@ * 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. + */ + +/* + * Message protection services: wrap with scatter-gather API. + */ #include "gssapiP_eap.h" +unsigned char +rfc4121Flags(gss_ctx_id_t ctx, int receiving) +{ + unsigned char flags; + int isAcceptor; + + isAcceptor = !CTX_IS_INITIATOR(ctx); + if (receiving) + isAcceptor = !isAcceptor; + + flags = 0; + if (isAcceptor) + flags |= TOK_FLAG_SENDER_IS_ACCEPTOR; + + if ((ctx->flags & CTX_FLAG_KRB_REAUTH) && + (ctx->gssFlags & GSS_C_MUTUAL_FLAG)) + flags |= TOK_FLAG_ACCEPTOR_SUBKEY; + + return flags; +} + +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 flags; + unsigned char *outbuf = NULL; + unsigned char *tbuf = NULL; + int keyUsage; + size_t rrc = 0; + size_t gssHeaderLen, gssTrailerLen; + size_t dataLen, assocDataLen; + krb5_context krbContext; +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto = NULL; +#endif + + if (ctx->encryptionType == ENCTYPE_NULL) { + *minor = GSSEAP_KEY_UNAVAILABLE; + return GSS_S_UNAVAILABLE; + } + + GSSEAP_KRB_INIT(&krbContext); + + flags = rfc4121Flags(ctx, FALSE); + + if (toktype == TOK_TYPE_WRAP) { + keyUsage = CTX_IS_INITIATOR(ctx) + ? KEY_USAGE_INITIATOR_SEAL + : KEY_USAGE_ACCEPTOR_SEAL; + } else { + keyUsage = CTX_IS_INITIATOR(ctx) + ? KEY_USAGE_INITIATOR_SIGN + : KEY_USAGE_ACCEPTOR_SIGN; + } + + gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen); + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + if (header == NULL) { + *minor = GSSEAP_MISSING_IOV; + 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); + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto); + if (code != 0) + goto cleanup; +#endif + + if (toktype == TOK_TYPE_WRAP && conf_req_flag) { + size_t krbHeaderLen, krbTrailerLen, krbPadLen; + size_t ec = 0, confDataLen = dataLen - assocDataLen; + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen); + if (code != 0) + goto cleanup; + + code = krbPaddingLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + confDataLen + 16 /* E(Header) */, + &krbPadLen); + if (code != 0) + goto cleanup; + + if (krbPadLen == 0 && (ctx->gssFlags & GSS_C_DCE_STYLE)) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krbBlockSize(krbContext, KRB_CRYPTO_CONTEXT(ctx), &ec); + if (code != 0) + goto cleanup; + } else + ec = krbPadLen; + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_TRAILER, &krbTrailerLen); + if (code != 0) + goto cleanup; + + gssHeaderLen = 16 /* Header */ + krbHeaderLen; + gssTrailerLen = ec + 16 /* E(Header) */ + krbTrailerLen; + + if (trailer == NULL) { + rrc = gssTrailerLen; + /* Workaround for Windows bug where it rotates by EC + RRC */ + if (ctx->gssFlags & GSS_C_DCE_STYLE) + rrc -= ec; + gssHeaderLen += gssTrailerLen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = gssEapAllocIov(header, (size_t)gssHeaderLen); + } else if (header->buffer.length < gssHeaderLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t)gssHeaderLen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(trailer, (size_t)gssTrailerLen); + else if (trailer->buffer.length < gssTrailerLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t)gssTrailerLen; + } + + /* TOK_ID */ + store_uint16_be((uint16_t)toktype, outbuf); + /* flags */ + outbuf[2] = flags + | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0); + /* filler */ + outbuf[3] = 0xFF; + /* EC */ + store_uint16_be(ec, outbuf + 4); + /* RRC */ + store_uint16_be(0, outbuf + 6); + store_uint64_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(krbContext, + ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0), + ec, rrc, KRB_CRYPTO_CONTEXT(ctx), + keyUsage, iov, iov_count); + if (code != 0) + goto cleanup; + + /* RRC */ + store_uint16_be(rrc, outbuf + 6); + + ctx->sendSeq++; + } else if (toktype == TOK_TYPE_WRAP && !conf_req_flag) { + wrap_with_checksum: + + gssHeaderLen = 16; + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_CHECKSUM, &gssTrailerLen); + if (code != 0) + goto cleanup; + + assert(gssTrailerLen <= 0xFFFF); + + if (trailer == NULL) { + rrc = gssTrailerLen; + gssHeaderLen += gssTrailerLen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(header, (size_t)gssHeaderLen); + else if (header->buffer.length < gssHeaderLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t)gssHeaderLen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(trailer, (size_t)gssTrailerLen); + else if (trailer->buffer.length < gssTrailerLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t)gssTrailerLen; + } + + /* TOK_ID */ + store_uint16_be((uint16_t)toktype, outbuf); + /* flags */ + outbuf[2] = flags; + /* filler */ + outbuf[3] = 0xFF; + if (toktype == TOK_TYPE_WRAP) { + /* Use 0 for checksum calculation, substitute + * checksum length later. + */ + /* EC */ + store_uint16_be(0, outbuf + 4); + /* RRC */ + store_uint16_be(0, outbuf + 6); + } else { + /* MIC and DEL store 0xFF in EC and RRC */ + store_uint16_be(0xFFFF, outbuf + 4); + store_uint16_be(0xFFFF, outbuf + 6); + } + store_uint64_be(ctx->sendSeq, outbuf + 8); + + code = gssEapSign(krbContext, ctx->checksumType, rrc, + KRB_CRYPTO_CONTEXT(ctx), keyUsage, + iov, iov_count); + if (code != 0) + goto cleanup; + + ctx->sendSeq++; + + if (toktype == TOK_TYPE_WRAP) { + /* Fix up EC field */ + store_uint16_be(gssTrailerLen, outbuf + 4); + /* Fix up RRC field */ + store_uint16_be(rrc, outbuf + 6); + } + } else if (toktype == TOK_TYPE_MIC) { + trailer = NULL; + goto wrap_with_checksum; + } else if (toktype == TOK_TYPE_DELETE_CONTEXT) { + trailer = NULL; + goto wrap_with_checksum; + } else { + abort(); + } + + code = 0; + if (conf_state != NULL) + *conf_state = conf_req_flag; + +cleanup: + if (code != 0) + gssEapReleaseIov(iov, iov_count); +#ifdef HAVE_HEIMDAL_VERSION + if (krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); +#endif + + *minor = code; + + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +OM_uint32 +gss_wrap_iov(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor = GSSEAP_UNKNOWN_QOP; + return GSS_S_UNAVAILABLE; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + major = gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state, + iov, iov_count, TOK_TYPE_WRAP); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +}