#include "gssapiP_krb5.h"
#include <assert.h>
static krb5_error_code
make_seal_token_v1 (krb5_context context,
krb5_key enc,
krb5_key seq,
uint64_t *seqnum,
int direction,
gss_buffer_t text,
gss_buffer_t token,
int signalg,
size_t cksum_size,
int sealalg,
int do_encrypt,
int toktype,
gss_OID oid)
{
krb5_error_code code;
size_t sumlen;
char *data_ptr;
krb5_data plaind;
krb5_checksum md5cksum;
unsigned int conflen=0, tmsglen, tlen, msglen;
unsigned char *t, *metadata, *checksum, *payload;
unsigned char *plain;
unsigned char pad;
krb5_keyusage sign_usage = KG_USAGE_SIGN;
struct k5buf buf;
assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG));
if (do_encrypt || toktype == KG_TOK_SEAL_MSG)
conflen = kg_confounder_size(context, enc->keyblock.enctype);
else conflen = 0;
if (toktype == KG_TOK_SEAL_MSG) {
switch (sealalg) {
case SEAL_ALG_MICROSOFT_RC4:
msglen = conflen + text->length+1;
pad = 1;
break;
default:
msglen = (conflen+text->length+8)&(~7);
pad = 8-(text->length%8);
}
tmsglen = msglen;
} else {
tmsglen = 0;
msglen = text->length;
pad = 0;
}
tlen = g_token_size(oid, 14 + cksum_size + tmsglen);
t = gssalloc_malloc(tlen);
if (t == NULL)
return(ENOMEM);
k5_buf_init_fixed(&buf, t, tlen);
g_make_token_header(&buf, oid, 14 + cksum_size + tmsglen, toktype);
metadata = k5_buf_get_space(&buf, 14);
checksum = k5_buf_get_space(&buf, cksum_size);
payload = k5_buf_get_space(&buf, tmsglen);
assert(metadata != NULL && checksum != NULL && payload != NULL);
assert(buf.len == tlen);
store_16_le(signalg, &metadata[0]);
if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) {
store_16_le(sealalg, &metadata[2]);
} else {
metadata[2] = 0xFF;
metadata[3] = 0xFF;
}
metadata[4] = 0xFF;
metadata[5] = 0xFF;
switch (signalg) {
case SGN_ALG_HMAC_SHA1_DES3_KD:
md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3;
break;
case SGN_ALG_HMAC_MD5:
md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR;
if (toktype != KG_TOK_SEAL_MSG)
sign_usage = 15;
break;
default:
abort ();
}
code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen);
if (code) {
gssalloc_free(t);
return(code);
}
md5cksum.length = sumlen;
if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) {
gssalloc_free(t);
return(ENOMEM);
}
if (conflen) {
if ((code = kg_make_confounder(context, enc->keyblock.enctype,
plain))) {
xfree(plain);
gssalloc_free(t);
return(code);
}
}
memcpy(plain+conflen, text->value, text->length);
if (pad) memset(plain+conflen+text->length, pad, pad);
if (! (data_ptr = xmalloc(8 + msglen))) {
xfree(plain);
gssalloc_free(t);
return(ENOMEM);
}
memcpy(data_ptr, metadata - 2, 8);
memcpy(data_ptr + 8, plain, msglen);
plaind.length = 8 + msglen;
plaind.data = data_ptr;
code = krb5_k_make_checksum(context, md5cksum.checksum_type, seq,
sign_usage, &plaind, &md5cksum);
xfree(data_ptr);
if (code) {
xfree(plain);
gssalloc_free(t);
return(code);
}
switch(signalg) {
case SGN_ALG_HMAC_SHA1_DES3_KD:
if (md5cksum.length != cksum_size)
abort ();
memcpy(checksum, md5cksum.contents, md5cksum.length);
break;
case SGN_ALG_HMAC_MD5:
memcpy(checksum, md5cksum.contents, cksum_size);
break;
}
krb5_free_checksum_contents(context, &md5cksum);
code = kg_make_seq_num(context, seq, direction?0:0xff,
(krb5_ui_4)*seqnum, checksum, metadata + 6);
if (code) {
xfree (plain);
gssalloc_free(t);
return(code);
}
if (do_encrypt) {
switch(sealalg) {
case SEAL_ALG_MICROSOFT_RC4:
{
unsigned char bigend_seqnum[4];
krb5_keyblock *enc_key;
int i;
store_32_be(*seqnum, bigend_seqnum);
code = krb5_k_key_keyblock(context, enc, &enc_key);
if (code)
{
xfree(plain);
gssalloc_free(t);
return(code);
}
assert (enc_key->length == 16);
for (i = 0; i <= 15; i++)
((char *) enc_key->contents)[i] ^=0xf0;
code = kg_arcfour_docrypt(enc_key, 0, bigend_seqnum, 4, plain,
tmsglen, payload);
krb5_free_keyblock (context, enc_key);
if (code)
{
xfree(plain);
gssalloc_free(t);
return(code);
}
}
break;
default:
code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL, plain,
payload, tmsglen);
if (code) {
xfree(plain);
gssalloc_free(t);
return(code);
}
}
}else {
if (tmsglen)
memcpy(payload, plain, tmsglen);
}
xfree(plain);
(*seqnum)++;
*seqnum &= 0xffffffffL;
token->length = tlen;
token->value = (void *) t;
return(0);
}
OM_uint32
kg_seal(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
int conf_req_flag, gss_qop_t qop_req,
gss_buffer_t input_message_buffer, int *conf_state,
gss_buffer_t output_message_buffer, int toktype)
{
krb5_gss_ctx_id_rec *ctx;
krb5_error_code code;
krb5_context context;
output_message_buffer->length = 0;
output_message_buffer->value = NULL;
if (qop_req != 0) {
*minor_status = (OM_uint32) G_UNKNOWN_QOP;
return GSS_S_BAD_QOP;
}
ctx = (krb5_gss_ctx_id_rec *) context_handle;
if (ctx->terminated || !ctx->established) {
*minor_status = KG_CTX_INCOMPLETE;
return(GSS_S_NO_CONTEXT);
}
context = ctx->k5_context;
switch (ctx->proto)
{
case 0:
code = make_seal_token_v1(context, ctx->enc, ctx->seq,
&ctx->seq_send, ctx->initiate,
input_message_buffer, output_message_buffer,
ctx->signalg, ctx->cksum_size, ctx->sealalg,
conf_req_flag, toktype, ctx->mech_used);
break;
case 1:
code = gss_krb5int_make_seal_token_v3(context, ctx,
input_message_buffer,
output_message_buffer,
conf_req_flag, toktype);
break;
default:
code = G_UNKNOWN_QOP;
break;
}
if (code) {
*minor_status = code;
save_error_info(*minor_status, context);
return(GSS_S_FAILURE);
}
if (conf_state)
*conf_state = conf_req_flag;
*minor_status = 0;
return(GSS_S_COMPLETE);
}
OM_uint32 KRB5_CALLCONV
krb5_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
int conf_req_flag, gss_qop_t qop_req,
gss_buffer_t input_message_buffer, int *conf_state,
gss_buffer_t output_message_buffer)
{
return(kg_seal(minor_status, context_handle, conf_req_flag,
qop_req, input_message_buffer, conf_state,
output_message_buffer, KG_TOK_WRAP_MSG));
}
OM_uint32 KRB5_CALLCONV
krb5_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
gss_qop_t qop_req, gss_buffer_t message_buffer,
gss_buffer_t message_token)
{
return(kg_seal(minor_status, context_handle, 0,
qop_req, message_buffer, NULL,
message_token, KG_TOK_MIC_MSG));
}