/* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 2001 by the Massachusetts Institute of Technology. * Copyright 1993 by OpenVision Technologies, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of OpenVision not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. OpenVision makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * 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 "k5-int.h" /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX conf_state is only valid if SEAL. */ static OM_uint32 kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, conf_state, qop_state, toktype) krb5_context context; OM_uint32 *minor_status; 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_error_code code; int conflen = 0; int signalg; int sealalg; gss_buffer_desc token; krb5_checksum cksum; krb5_checksum md5cksum; krb5_data plaind; char *data_ptr; krb5_timestamp now; unsigned char *plain; unsigned int cksum_len = 0; size_t plainlen; int direction; krb5_ui_4 seqnum; OM_uint32 retval; size_t sumlen, blocksize; int tmsglen; krb5_keyusage sign_usage = KG_USAGE_SIGN; KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() start\n"); /* Solaris Kerberos: make sure this is initialized */ *minor_status = 0; if (toktype == KG_TOK_SEAL_MSG) { message_buffer->length = 0; message_buffer->value = NULL; } /* get the sign and seal algorithms */ signalg = ptr[0] + (ptr[1]<<8); sealalg = ptr[2] + (ptr[3]<<8); /* Sanity checks */ if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) { *minor_status = 0; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_DEFECTIVE_TOKEN\n"); return GSS_S_DEFECTIVE_TOKEN; } if ((toktype != KG_TOK_SEAL_MSG) && (sealalg != 0xffff)) { *minor_status = 0; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error2 GSS_S_DEFECTIVE_TOKEN\n"); return GSS_S_DEFECTIVE_TOKEN; } /* in the current spec, there is only one valid seal algorithm per key type, so a simple comparison is ok */ if ((toktype == KG_TOK_SEAL_MSG) && !((sealalg == 0xffff) || (sealalg == ctx->sealalg))) { *minor_status = 0; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error3 GSS_S_DEFECTIVE_TOKEN\n"); return GSS_S_DEFECTIVE_TOKEN; } /* there are several mappings of seal algorithms to sign algorithms, but few enough that we can try them all. */ if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || (ctx->sealalg == SEAL_ALG_DES3KD && signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && signalg != SGN_ALG_HMAC_MD5)) { *minor_status = 0; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error4 GSS_S_DEFECTIVE_TOKEN\n"); return GSS_S_DEFECTIVE_TOKEN; } KRB5_LOG(KRB5_INFO, "kg_unseal_v1() signalg = %d\n", signalg); switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_HMAC_MD5: cksum_len = 8; if (toktype != KG_TOK_SEAL_MSG) sign_usage = 15; break; case SGN_ALG_3: cksum_len = 16; break; case SGN_ALG_HMAC_SHA1_DES3_KD: cksum_len = 20; break; default: *minor_status = 0; KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, error signalg=%d\n", signalg); return GSS_S_DEFECTIVE_TOKEN; } #ifdef _KERNEL /* * Because the ARCFOUR code bypasses the standard * crypto interfaces, we must make sure the kernel * crypto framework mechanism types are properly * initialized here. */ context->kef_cipher_mt = get_cipher_mech_type(context, ctx->seq); context->kef_hash_mt = get_hash_mech_type(context, ctx->seq); if ((code = init_key_kef(context->kef_cipher_mt, ctx->seq))) { *minor_status = code; return (GSS_S_FAILURE); } if ((code = init_key_kef(context->kef_cipher_mt, ctx->enc))) { *minor_status = code; return (GSS_S_FAILURE); } #endif /* _KERNEL */ /* get the token parameters */ if ((code = kg_get_seq_num(context, ctx->seq, ptr+14, ptr+6, &direction, &seqnum))) { *minor_status = code; return(GSS_S_BAD_SIG); } /* decode the message, if SEAL */ if (toktype == KG_TOK_SEAL_MSG) { tmsglen = bodysize-(14+cksum_len); KRB5_LOG1(KRB5_INFO, "kg_unseal_v1() tmsglen = %d cksum_len = %d", tmsglen, cksum_len); KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() toktype == KG_TOK_SEAL_MSG\n"); if (sealalg != 0xffff) { if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) { *minor_status = ENOMEM; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error ENOMEM\n"); return(GSS_S_FAILURE); } if (ctx->enc->enctype == ENCTYPE_ARCFOUR_HMAC) { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; int i; bigend_seqnum[0] = (seqnum>>24) & 0xff; bigend_seqnum[1] = (seqnum>>16) & 0xff; bigend_seqnum[2] = (seqnum>>8) & 0xff; bigend_seqnum[3] = seqnum & 0xff; code = krb5_copy_keyblock (context, ctx->enc, &enc_key); if (code) { xfree_wrap(plain, tmsglen); *minor_status = code; return(GSS_S_FAILURE); } for (i = 0; i <= 15; i++) ((char *) enc_key->contents)[i] ^=0xf0; #ifndef _KERNEL /* * The enc_key contents were modified, delete the * key object so it doesn't get used later. */ if (enc_key->hKey != CK_INVALID_HANDLE) { (void)C_DestroyObject(krb_ctx_hSession(context), enc_key->hKey); enc_key->hKey = CK_INVALID_HANDLE; } #endif KRB5_LOG(KRB5_INFO, "kg_unseal_v1() enc_key->enctype = %d", enc_key->enctype); code = kg_arcfour_docrypt (context, enc_key, 0, &bigend_seqnum[0], 4, ptr+14+cksum_len, tmsglen, plain); krb5_free_keyblock (context, enc_key); } else { code = kg_decrypt(context, ctx->enc, KG_USAGE_SEAL, NULL, ptr+14+cksum_len, plain, tmsglen); } if (code) { xfree_wrap(plain, tmsglen); *minor_status = code; return(GSS_S_FAILURE); } } else { plain = ptr+14+cksum_len; } plainlen = tmsglen; if ((sealalg == 0xffff) && ctx->big_endian) { token.length = tmsglen; } else { conflen = kg_confounder_size(context, ctx->enc); /* * Solaris Kerberos: we want to perform a sanity check on the * pad length, so we know it can not be more than the blocksize. */ code = krb5_c_block_size(context, ctx->enc->enctype, &blocksize); if (code != 0) { if (sealalg != 0xffff) xfree_wrap(plain, tmsglen); *minor_status = code; return(GSS_S_FAILURE); } if (plain[tmsglen-1] > blocksize) { if (sealalg != 0xffff) xfree_wrap(plain, tmsglen); *minor_status = KG_BAD_LENGTH; return(GSS_S_FAILURE); } token.length = tmsglen - conflen - plain[tmsglen-1]; } if (token.length) { if ((token.value = (void *) xmalloc(token.length)) == NULL) { if (sealalg != 0xffff) xfree_wrap(plain, tmsglen); *minor_status = ENOMEM; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error2 ENOMEM\n"); return(GSS_S_FAILURE); } (void) memcpy(token.value, plain+conflen, token.length); } else { token.value = NULL; } } else if (toktype == KG_TOK_SIGN_MSG) { KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() toktype == KG_TOK_SIGN_MSG\n"); token = *message_buffer; plain = token.value; plainlen = token.length; } else { KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() toktype == NULL\n"); token.length = 0; token.value = NULL; plain = token.value; plainlen = token.length; } /* compute the checksum of the message */ /* initialize the the cksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_DES_MAC: case SGN_ALG_3: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; default: KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, error2 signalg=%d\n", signalg); #ifndef _KERNEL abort (); #else *minor_status = 0; return(GSS_S_DEFECTIVE_TOKEN); #endif /* _KERNEL */ } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); if (code) { KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, krb5_c_checksum_length() error " "code=%d\n", code); return(code); } md5cksum.length = (size_t)sumlen; switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_3: /* compute the checksum of the message */ /* 8 = bytes of token body to be checksummed according to spec */ if (! (data_ptr = (void *) xmalloc(8 + (ctx->big_endian ? token.length : plainlen)))) { if (sealalg != 0xffff) xfree_wrap(plain, tmsglen); if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = ENOMEM; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error3 ENOMEM\n"); return(GSS_S_FAILURE); } (void) memcpy(data_ptr, ptr-2, 8); if (ctx->big_endian) (void) memcpy(data_ptr+8, token.value, token.length); else (void) memcpy(data_ptr+8, plain, plainlen); plaind.length = 8 + (ctx->big_endian ? token.length : plainlen); plaind.data = data_ptr; code = krb5_c_make_checksum(context, md5cksum.checksum_type, ctx->seq, sign_usage, &plaind, &md5cksum); xfree_wrap(data_ptr, 8 + (ctx->big_endian ? token.length : plainlen)); if (code) { if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = code; KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, krb5_c_make_checksum() " "error code = %d\n", code); return(GSS_S_FAILURE); } if ((code = kg_encrypt(context, ctx->seq, KG_USAGE_SEAL, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? ctx->seq->contents : NULL), md5cksum.contents, md5cksum.contents, 16))) { xfree_wrap(md5cksum.contents, md5cksum.length); if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = code; KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, kg_encrypt() error" "code = %d\n", code); return GSS_S_FAILURE; } if (signalg == 0) cksum.length = 8; else cksum.length = 16; cksum.contents = md5cksum.contents + 16 - cksum.length; code = memcmp(cksum.contents, ptr+14, cksum.length); break; case SGN_ALG_MD2_5: if (!ctx->seed_init && (code = kg_make_seed(context, ctx->subkey, ctx->seed))) { xfree_wrap(md5cksum.contents, md5cksum.length); if (sealalg != 0xffff) xfree_wrap(plain, tmsglen); if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = code; return GSS_S_FAILURE; } if (! (data_ptr = (void *) xmalloc(sizeof(ctx->seed) + 8 + (ctx->big_endian ? token.length : plainlen)))) { xfree_wrap(md5cksum.contents, md5cksum.length); if (sealalg == 0) xfree_wrap(plain, tmsglen); if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = ENOMEM; return(GSS_S_FAILURE); } (void) memcpy(data_ptr, ptr-2, 8); (void) memcpy(data_ptr+8, ctx->seed, sizeof(ctx->seed)); if (ctx->big_endian) (void) memcpy(data_ptr+8+sizeof(ctx->seed), token.value, token.length); else (void) memcpy(data_ptr+8+sizeof(ctx->seed), plain, plainlen); plaind.length = 8 + sizeof(ctx->seed) + (ctx->big_endian ? token.length : plainlen); plaind.data = data_ptr; xfree_wrap(md5cksum.contents, md5cksum.length); code = krb5_c_make_checksum(context, md5cksum.checksum_type, ctx->seq, KG_USAGE_SIGN, &plaind, &md5cksum); xfree_wrap(data_ptr, 8 + sizeof(ctx->seed) + (ctx->big_endian ? token.length : plainlen)); if (code) { if (sealalg == 0) xfree_wrap(plain, tmsglen); if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = code; return(GSS_S_FAILURE); } code = memcmp(md5cksum.contents, ptr+14, 8); /* Falls through to defective-token?? */ /* FALLTHROUGH */ default: *minor_status = 0; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error SGN_ALG_MD2_5 " "GSS_S_DEFECTIVE_TOKEN\n"); return(GSS_S_DEFECTIVE_TOKEN); case SGN_ALG_HMAC_SHA1_DES3_KD: case SGN_ALG_HMAC_MD5: /* compute the checksum of the message */ /* 8 = bytes of token body to be checksummed according to spec */ if (! (data_ptr = (void *) xmalloc(8 + (ctx->big_endian ? token.length : plainlen)))) { if (sealalg != 0xffff) xfree_wrap(plain, tmsglen); if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = ENOMEM; return(GSS_S_FAILURE); } (void) memcpy(data_ptr, ptr-2, 8); if (ctx->big_endian) { KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() ctx->big_endian = 1\n"); (void) memcpy(data_ptr+8, token.value, token.length); } else { KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() ctx->big_endian = 0\n"); (void) memcpy(data_ptr+8, plain, plainlen); } plaind.length = 8 + (ctx->big_endian ? token.length : plainlen); plaind.data = data_ptr; code = krb5_c_make_checksum(context, md5cksum.checksum_type, ctx->seq, sign_usage, &plaind, &md5cksum); xfree_wrap(data_ptr, 8 + (ctx->big_endian ? token.length : plainlen)); if (code) { if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = code; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error " "SGN_ALG_HMAC_SHA1_DES3_KD GSS_S_FAILURE\n"); return(GSS_S_FAILURE); } /* compare the computed checksum against the transmitted checksum */ code = memcmp(md5cksum.contents, ptr+14, cksum_len); KRB5_LOG(KRB5_INFO, "kg_unseal_v1() memcmp %d bytes", cksum_len); break; } xfree_wrap(md5cksum.contents, md5cksum.length); if (sealalg != 0xffff) xfree_wrap(plain, tmsglen); if (code) { if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = 0; KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_BAD_SIG\n"); return(GSS_S_BAD_SIG); } if (conf_state) *conf_state = (sealalg != 0xffff); if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; if ((code = krb5_timeofday(context, &now))) { *minor_status = code; KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, krb5_timeofday()" "error code = %d\n", code); return(GSS_S_FAILURE); } if (now > ctx->endtime) { *minor_status = 0; KRB5_LOG1(KRB5_ERR, "kg_unseal_v1() end, error " "now %d > ctx->endtime %d\n", now, ctx->endtime); return(GSS_S_CONTEXT_EXPIRED); } /* do sequencing checks */ if ((ctx->initiate && direction != 0xff) || (!ctx->initiate && direction != 0)) { if (toktype == KG_TOK_SEAL_MSG) { xfree_wrap(token.value, token.length); /* Solaris Kerberos: just to be safe since token.value is an * output parameter. */ token.value = NULL; token.length = 0; } *minor_status = (OM_uint32) G_BAD_DIRECTION; KRB5_LOG1(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_BAD_SIG " "G_BAD_DIRECTION ctx->initiate = %d " "direction = %d\n", ctx->initiate, direction); return(GSS_S_BAD_SIG); } retval = g_order_check(&(ctx->seqstate), (gssint_uint64)seqnum); /* It got through unscathed, adjust the output message buffer. */ if (retval == 0 && toktype == KG_TOK_SEAL_MSG) *message_buffer = token; *minor_status = 0; KRB5_LOG(KRB5_INFO, "kg_unseal_v1() end, retval = %d\n", retval); 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(minor_status, context_handle, input_token_buffer, message_buffer, conf_state, qop_state, toktype) 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; int toktype2; KRB5_LOG0(KRB5_INFO, "kg_unseal() start \n"); /* validate the context handle */ if (! kg_validate_ctx_id(context_handle)) { *minor_status = (OM_uint32) G_VALIDATE_FAILED; KRB5_LOG0(KRB5_ERR, "kg_unseal() end, kg_validate_ctx_id() error " "G_VALIDATE_FAILED \n"); return(GSS_S_NO_CONTEXT); } ctx = (krb5_gss_ctx_id_rec *) context_handle; if (! ctx->established) { *minor_status = KG_CTX_INCOMPLETE; KRB5_LOG0(KRB5_ERR, "kg_unseal() end, error ! ctx->established \n"); 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->proto) switch (toktype) { case KG_TOK_SIGN_MSG: toktype2 = 0x0404; break; case KG_TOK_SEAL_MSG: toktype2 = 0x0504; break; case KG_TOK_DEL_CTX: toktype2 = 0x0405; break; default: toktype2 = toktype; break; } else toktype2 = toktype; err = g_verify_token_header(ctx->mech_used, (uint32_t *)&bodysize, &ptr, toktype2, input_token_buffer->length, !ctx->proto); if (err) { *minor_status = err; return GSS_S_DEFECTIVE_TOKEN; } if (ctx->proto == 0) { err = kg_unseal_v1(ctx->k5_context, minor_status, ctx, ptr, bodysize, message_buffer, conf_state, qop_state, toktype); } else { err = gss_krb5int_unseal_token_v3(&ctx->k5_context, minor_status, ctx, ptr, bodysize, message_buffer, conf_state, qop_state, toktype); } *minor_status = err; #ifndef _KERNEL if (err != 0) save_error_info (*minor_status, ctx->k5_context); #endif KRB5_LOG(KRB5_INFO, "kg_unseal() end, err = %d", err); return(err); }