diff options
Diffstat (limited to 'src/lib/gssapi/krb5/k5unseal.c')
-rw-r--r-- | src/lib/gssapi/krb5/k5unseal.c | 585 |
1 files changed, 525 insertions, 60 deletions
diff --git a/src/lib/gssapi/krb5/k5unseal.c b/src/lib/gssapi/krb5/k5unseal.c index 041cae06a..c32e3255d 100644 --- a/src/lib/gssapi/krb5/k5unseal.c +++ b/src/lib/gssapi/krb5/k5unseal.c @@ -20,6 +20,32 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + #include "gssapiP_krb5.h" #include <memory.h> @@ -27,34 +53,417 @@ * $Id$ */ +static OM_uint32 +kg2_verify_mic(context, minor_status, ctx, ptr, bodysize, + text, qop_state) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t text; + gss_qop_t *qop_state; +{ + size_t cksumlen; + krb5_error_code code; + krb5_data plain; + krb5_cksumtype tctype; + krb5_ui_4 tseqnum; + int tdirection; + krb5_checksum cksum; + krb5_boolean ckvalid; + krb5_timestamp now; + OM_uint32 retval; + + plain.data = 0; + cksum.contents = 0; + + /* verify the header */ + + if (bodysize < 11) { + free(plain.data); + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + /* allocate the checksum buffer */ + + plain.length = 7+text->length; + + if ((plain.data = (char *) malloc(plain.length)) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + /* suck out the body parts from the token */ + + tctype = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) | + (ptr[2]<<8) | ptr[3]); + ptr += 4; + + memcpy(plain.data, ptr, 5); + tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); + ptr += 4; + tdirection = ptr[0]; + ptr += 1; + + cksum.length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 11; + + if (cksum.length != bodysize) { + free(plain.data); + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + cksum.contents = ptr; + cksum.checksum_type = tctype; + + /* finish assembling the checksum buffer and compute the checksum */ + + plain.data[5] = (text->length >> 8) & 0xff; + plain.data[6] = text->length & 0xff; + + memcpy(plain.data+7, text->value, text->length); + + if (code = krb5_c_verify_checksum(context, ctx->subkey, + KRB5_KEYUSAGE_GSS_TOK_MIC, + &plain, &cksum, &ckvalid)) { + free(plain.data); + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (!ckvalid) { + free(plain.data); + *minor_status = 0; + return(GSS_S_BAD_SIG); + } + + /* check context expiry */ + + if ((code = krb5_timeofday(context, &now))) { + free(plain.data); + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (now > ctx->endtime) { + free(plain.data); + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } + + /* do sequencing checks */ + + if ((ctx->initiate && tdirection != 0xff) || + (!ctx->initiate && tdirection != 0)) { + free(plain.data); + *minor_status = G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + retval = g_order_check(&(ctx->seqstate), tseqnum); + + free(plain.data); + + if (retval) { + *minor_status = 0; + return(retval); + } + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +static OM_uint32 +kg2_unwrap_integ(context, minor_status, ctx, ptr, bodysize, output, qop_state) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t output; + gss_qop_t *qop_state; +{ + krb5_error_code code; + OM_uint32 retval; + krb5_ui_4 tseqnum; + int tdirection; + int tmsglen; + unsigned char *tmsg; + krb5_data plain; + krb5_checksum tcksum; + krb5_boolean ckvalid; + krb5_timestamp now; + + output->length = 0; + output->value = NULL; + + /* read the body parts out of the message */ + + if (bodysize < 11) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tcksum.checksum_type = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) | + (ptr[2]<<8) | ptr[3]); + ptr += 4; + + plain.data = ptr; + + tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); + ptr += 4; + tdirection = ptr[0]; + ptr += 1; + + tmsglen = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 11; + + if (bodysize < tmsglen) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tmsg = ptr; + ptr += tmsglen; + bodysize -= tmsglen; + + plain.length = ((char*)ptr) - ((char *)plain.data); + + tcksum.length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 2; + + if (bodysize != tcksum.length) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tcksum.contents = ptr; + + /* verify the MIC */ + + if (code = krb5_c_verify_checksum(context, ctx->subkey, + KRB5_KEYUSAGE_GSS_TOK_WRAP_INTEG, + &plain, &tcksum, &ckvalid)) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (!ckvalid) { + *minor_status = 0; + return(GSS_S_BAD_SIG); + } + + /* check context expiry */ + + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (now > ctx->endtime) { + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } + + /* do sequencing checks */ + + if ((ctx->initiate && tdirection != 0xff) || + (!ctx->initiate && tdirection != 0)) { + *minor_status = G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + if (retval = g_order_check(&(ctx->seqstate), tseqnum)) { + *minor_status = 0; + return(retval); + } + + if ((output->value = (void *) malloc(tmsglen)) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + memcpy(output->value, tmsg, tmsglen); + output->length = tmsglen; + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +static OM_uint32 +kg2_unwrap_priv(context, minor_status, ctx, ptr, bodysize, output, qop_state) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t output; + gss_qop_t *qop_state; +{ + krb5_error_code code; + OM_uint32 retval; + krb5_enc_data cipher; + krb5_data plain; + krb5_ui_4 tseqnum; + int tdirection; + int tmsglen; + unsigned char *tmsg; + krb5_timestamp now; + + output->length = 0; + output->value = NULL; + + /* read the body parts out of the message */ + + if (bodysize < 2) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + cipher.ciphertext.length = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 2; + + if (bodysize != cipher.ciphertext.length) { + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + cipher.ciphertext.data = ptr; + cipher.enctype = ENCTYPE_UNKNOWN; + + plain.length = cipher.ciphertext.length; + if ((plain.data = (char *) malloc(plain.length)) == NULL) { + *minor_status = 0; + return(GSS_S_FAILURE); + } + + /* decrypt (and implicitly verify) the encrypted data */ + + if (code = krb5_c_decrypt(context, ctx->subkey, + KRB5_KEYUSAGE_GSS_TOK_WRAP_PRIV, + 0, &cipher, &plain)) { + free(plain.data); + *minor_status = code; + return(GSS_S_FAILURE); + } + + /* parse out the encrypted fields */ + + ptr = plain.data; + bodysize = plain.length; + + if (bodysize < 7) { + free(plain.data); + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tseqnum = ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); + ptr += 4; + tdirection = ptr[0]; + ptr += 1; + + tmsglen = (ptr[0]<<8) | ptr[1]; + ptr += 2; + bodysize -= 7; + + /* check context expiry */ + + if ((code = krb5_timeofday(context, &now))) { + free(plain.data); + *minor_status = code; + return(GSS_S_FAILURE); + } + + if (now > ctx->endtime) { + free(plain.data); + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } + + /* do sequencing checks */ + + if ((ctx->initiate && tdirection != 0xff) || + (!ctx->initiate && tdirection != 0)) { + free(plain.data); + *minor_status = G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + if (retval = g_order_check(&(ctx->seqstate), tseqnum)) { + free(plain.data); + *minor_status = 0; + return(retval); + } + + /* now copy out the data. can't do a strict equality check here, + since the output could be padded. */ + + if (bodysize < tmsglen) { + free(plain.data); + *minor_status = G_TOK_TRUNC; + return(GSS_S_DEFECTIVE_TOKEN); + } + + tmsg = ptr; + + if ((output->value = (void *) malloc(tmsglen)) == NULL) { + free(plain.data); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + memcpy(output->value, tmsg, tmsglen); + output->length = tmsglen; + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + free(plain.data); + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX - conf_state is only valid if SEAL. - */ + conf_state is only valid if SEAL. */ OM_uint32 -kg_unseal(context, minor_status, context_handle, input_token_buffer, - message_buffer, conf_state, qop_state, toktype) +kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + conf_state, qop_state, toktype) krb5_context context; OM_uint32 *minor_status; - gss_ctx_id_t context_handle; - gss_buffer_t input_token_buffer; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; gss_buffer_t message_buffer; int *conf_state; int *qop_state; int toktype; { - krb5_gss_ctx_id_rec *ctx; krb5_error_code code; - int bodysize; int tmsglen; int conflen = 0; int signalg; int sealalg; gss_buffer_desc token; - unsigned char *ptr; krb5_checksum cksum; krb5_checksum desmac; krb5_checksum md5cksum; + krb5_data plaind; char *data_ptr; krb5_timestamp now; unsigned char *plain; @@ -64,38 +473,13 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, int direction; krb5_int32 seqnum; OM_uint32 retval; + size_t sumlen; if (toktype == KG_TOK_SEAL_MSG) { message_buffer->length = 0; message_buffer->value = NULL; } - /* validate the context handle */ - if (! kg_validate_ctx_id(context_handle)) { - *minor_status = (OM_uint32) G_VALIDATE_FAILED; - return(GSS_S_NO_CONTEXT); - } - - ctx = (krb5_gss_ctx_id_rec *) context_handle; - - if (! ctx->established) { - *minor_status = KG_CTX_INCOMPLETE; - return(GSS_S_NO_CONTEXT); - } - - /* parse the token, leave the data in message_buffer, setting conf_state */ - - /* verify the header */ - - ptr = (unsigned char *) input_token_buffer->value; - - if ((err = g_verify_token_header((gss_OID) ctx->mech_used, &bodysize, - &ptr, toktype, - input_token_buffer->length))) { - *minor_status = err; - return(GSS_S_DEFECTIVE_TOKEN); - } - /* get the sign and seal algorithms */ signalg = ptr[0] + (ptr[1]<<8); @@ -159,7 +543,7 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, return(GSS_S_FAILURE); } - if ((code = kg_decrypt(context, &ctx->enc, NULL, + if ((code = kg_decrypt(context, ctx->enc, NULL, ptr+14+cksum_len, plain, tmsglen))) { xfree(plain); *minor_status = code; @@ -174,7 +558,7 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, if ((sealalg == 0xffff) && ctx->big_endian) { token.length = tmsglen; } else { - conflen = kg_confounder_size(&ctx->enc); + conflen = kg_confounder_size(context, ctx->enc); token.length = tmsglen - conflen - plain[tmsglen-1]; } @@ -200,15 +584,12 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, /* compute the checksum of the message */ - /* initialize the the cksum and allocate the contents buffer */ + /* initialize the the cksum */ + if (code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &sumlen)) + return(code); + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; - md5cksum.length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5); - if ((md5cksum.contents = (krb5_octet *) xmalloc(md5cksum.length)) == NULL) { - if (sealalg != 0xffff) - xfree(plain); - *minor_status = ENOMEM; - return(GSS_S_FAILURE); - } + md5cksum.length = sumlen; switch (signalg) { case 0: @@ -219,7 +600,6 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, if (! (data_ptr = (void *) xmalloc(8 + (ctx->big_endian ? token.length : plainlen)))) { - xfree(md5cksum.contents); if (sealalg != 0xffff) xfree(plain); if (toktype == KG_TOK_SEAL_MSG) @@ -235,14 +615,13 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, else (void) memcpy(data_ptr+8, plain, plainlen); - code = krb5_calculate_checksum(context, md5cksum.checksum_type, - data_ptr, 8 + - (ctx->big_endian ? token.length : - plainlen), 0, 0, &md5cksum); + plaind.length = 8 + (ctx->big_endian ? token.length : plainlen); + plaind.data = data_ptr; + code = krb5_c_make_checksum(context, md5cksum.checksum_type, 0, 0, + &plaind, &md5cksum); xfree(data_ptr); if (code) { - xfree(md5cksum.contents); if (toktype == KG_TOK_SEAL_MSG) xfree(token.value); *minor_status = code; @@ -264,6 +643,7 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, return(GSS_S_FAILURE); } + /* XXX not converted to new api since it's inside an #if 0 */ if (code = krb5_calculate_checksum(context, cksum.checksum_type, md5cksum.contents, 16, ctx->seq.key->contents, @@ -281,9 +661,9 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, xfree(cksum.contents); #else - if ((code = kg_encrypt(context, &ctx->seq, + if ((code = kg_encrypt(context, ctx->seq, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? - ctx->seq.key->contents : NULL), + ctx->seq->contents : NULL), md5cksum.contents, md5cksum.contents, 16))) { xfree(md5cksum.contents); if (toktype == KG_TOK_SEAL_MSG) @@ -333,15 +713,15 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, else (void) memcpy(data_ptr+8+sizeof(ctx->seed), plain, plainlen); - code = krb5_calculate_checksum(context, md5cksum.checksum_type, - data_ptr, 8 + sizeof(ctx->seed) + - (ctx->big_endian ? token.length : - plainlen), 0, 0, &md5cksum); - + plaind.length = 8 + sizeof(ctx->seed) + + (ctx->big_endian ? token.length : plainlen); + plaind.data = data_ptr; + xfree(md5cksum.contents); + code = krb5_c_make_checksum(context, md5cksum.checksum_type, 0, 0, + &plaind, &md5cksum); xfree(data_ptr); if (code) { - xfree(md5cksum.contents); if (sealalg == 0) xfree(plain); if (toktype == KG_TOK_SEAL_MSG) @@ -394,7 +774,7 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, /* do sequencing checks */ - if ((code = kg_get_seq_num(context, &(ctx->seq), ptr+14, ptr+6, &direction, + if ((code = kg_get_seq_num(context, ctx->seq, ptr+14, ptr+6, &direction, &seqnum))) { if (toktype == KG_TOK_SEAL_MSG) xfree(token.value); @@ -417,3 +797,88 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, *minor_status = 0; return(retval); } + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +OM_uint32 +kg_unseal(context, minor_status, context_handle, input_token_buffer, + message_buffer, conf_state, qop_state, toktype) + krb5_context context; + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_token_buffer; + gss_buffer_t message_buffer; + int *conf_state; + int *qop_state; + int toktype; +{ + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + int err; + OM_uint32 retval; + + /* validate the context handle */ + if (! kg_validate_ctx_id(context_handle)) { + *minor_status = (OM_uint32) G_VALIDATE_FAILED; + return(GSS_S_NO_CONTEXT); + } + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (! ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + /* parse the token, leave the data in message_buffer, setting conf_state */ + + /* verify the header */ + + ptr = (unsigned char *) input_token_buffer->value; + + if (ctx->gsskrb5_version == 2000) { + if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, KG2_TOK_MIC, + input_token_buffer->length))) { + return(kg2_verify_mic(context, minor_status, ctx, ptr, bodysize, + message_buffer, qop_state)); + } else if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, + KG2_TOK_WRAP_INTEG, + input_token_buffer->length))) { + if (GSS_ERROR(retval = kg2_unwrap_integ(context, minor_status, + ctx, ptr, bodysize, + message_buffer, qop_state))) + return(retval); + + if (conf_state) + *conf_state = 0; + return(GSS_S_COMPLETE); + } else if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, + KG2_TOK_WRAP_PRIV, + input_token_buffer->length))) { + if (GSS_ERROR(retval = kg2_unwrap_priv(context, minor_status, + ctx, ptr, bodysize, + message_buffer, qop_state))) + return(retval); + + if (conf_state) + *conf_state = 1; + return(GSS_S_COMPLETE); + } + } else { + if (!(err = g_verify_token_header((gss_OID) ctx->mech_used, + &bodysize, &ptr, toktype, + input_token_buffer->length))) { + return(kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, + message_buffer, conf_state, qop_state, + toktype)); + } + } + + *minor_status = err; + return(GSS_S_DEFECTIVE_TOKEN); +} |