#include "gssapiP_krb5.h"
#include <assert.h>
static krb5_error_code
make_seal_token_v1 (krb5_context context,
krb5_keyblock *enc,
krb5_keyblock *seq,
gssint_uint64 *seqnum,
int direction,
gss_buffer_t text,
gss_buffer_t token,
int signalg,
size_t cksum_size,
int sealalg,
int do_encrypt,
int toktype,
int bigend,
gss_OID oid)
{
krb5_error_code code;
size_t sumlen;
char *data_ptr;
krb5_data plaind;
krb5_checksum md5cksum;
krb5_checksum cksum;
unsigned conflen=0, tmsglen, tlen, msglen;
unsigned char *t, *ptr;
unsigned char *plain;
unsigned char pad;
krb5_keyusage sign_usage = KG_USAGE_SIGN;
assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG));
if (do_encrypt || (!bigend && (toktype == KG_TOK_SEAL_MSG)))
conflen = kg_confounder_size(context, enc);
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((gss_OID) oid, 14+cksum_size+tmsglen);
if ((t = (unsigned char *) xmalloc(tlen)) == NULL)
return(ENOMEM);
ptr = t;
g_make_token_header(oid, 14+cksum_size+tmsglen, &ptr, toktype);
ptr[0] = signalg & 0xff;
ptr[1] = (signalg >> 8) & 0xff;
if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) {
ptr[2] = sealalg & 0xff;
ptr[3] = (sealalg >> 8) & 0xff;
} else {
ptr[2] = 0xff;
ptr[3] = 0xff;
}
ptr[4] = 0xff;
ptr[5] = 0xff;
switch (signalg) {
case SGN_ALG_DES_MAC_MD5:
case SGN_ALG_MD2_5:
md5cksum.checksum_type = CKSUMTYPE_RSA_MD5;
break;
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:
case SGN_ALG_DES_MAC:
abort ();
}
code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen);
if (code) {
xfree(t);
return(code);
}
md5cksum.length = sumlen;
if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) {
xfree(t);
return(ENOMEM);
}
if (conflen) {
if ((code = kg_make_confounder(context, enc, plain))) {
xfree(plain);
xfree(t);
return(code);
}
}
memcpy(plain+conflen, text->value, text->length);
if (pad) memset(plain+conflen+text->length, pad, pad);
if (! (data_ptr =
(char *) xmalloc(8 + (bigend ? text->length : msglen)))) {
xfree(plain);
xfree(t);
return(ENOMEM);
}
(void) memcpy(data_ptr, ptr-2, 8);
if (bigend)
(void) memcpy(data_ptr+8, text->value, text->length);
else
(void) memcpy(data_ptr+8, plain, msglen);
plaind.length = 8 + (bigend ? text->length : msglen);
plaind.data = data_ptr;
code = krb5_c_make_checksum(context, md5cksum.checksum_type, seq,
sign_usage, &plaind, &md5cksum);
xfree(data_ptr);
if (code) {
xfree(plain);
xfree(t);
return(code);
}
switch(signalg) {
case SGN_ALG_DES_MAC_MD5:
case 3:
if ((code = kg_encrypt(context, seq, KG_USAGE_SEAL,
(g_OID_equal(oid, gss_mech_krb5_old) ?
seq->contents : NULL),
md5cksum.contents, md5cksum.contents, 16))) {
krb5_free_checksum_contents(context, &md5cksum);
xfree (plain);
xfree(t);
return code;
}
cksum.length = cksum_size;
cksum.contents = md5cksum.contents + 16 - cksum.length;
memcpy(ptr+14, cksum.contents, cksum.length);
break;
case SGN_ALG_HMAC_SHA1_DES3_KD:
if (md5cksum.length != cksum_size)
abort ();
memcpy (ptr+14, md5cksum.contents, md5cksum.length);
break;
case SGN_ALG_HMAC_MD5:
memcpy (ptr+14, md5cksum.contents, cksum_size);
break;
}
krb5_free_checksum_contents(context, &md5cksum);
if ((code = kg_make_seq_num(context, seq, direction?0:0xff, *seqnum,
ptr+14, ptr+6))) {
xfree (plain);
xfree(t);
return(code);
}
if (do_encrypt) {
switch(sealalg) {
case SEAL_ALG_MICROSOFT_RC4:
{
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, enc, &enc_key);
if (code)
{
xfree(plain);
xfree(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,
ptr+14+cksum_size);
krb5_free_keyblock (context, enc_key);
if (code)
{
xfree(plain);
xfree(t);
return(code);
}
}
break;
default:
if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL,
(krb5_pointer) plain,
(krb5_pointer) (ptr+cksum_size+14),
tmsglen))) {
xfree(plain);
xfree(t);
return(code);
}
}
}else {
if (tmsglen)
memcpy(ptr+14+cksum_size, plain, tmsglen);
}
xfree(plain);
(*seqnum)++;
*seqnum &= 0xffffffffL;
token->length = tlen;
token->value = (void *) t;
return(0);
}
OM_uint32
kg_seal(minor_status, context_handle, conf_req_flag, qop_req,
input_message_buffer, conf_state, output_message_buffer, toktype)
OM_uint32 *minor_status;
gss_ctx_id_t context_handle;
int conf_req_flag;
int 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_timestamp now;
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_FAILURE;
}
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);
}
context = ctx->k5_context;
if ((code = krb5_timeofday(context, &now))) {
*minor_status = code;
save_error_info(*minor_status, context);
return(GSS_S_FAILURE);
}
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->big_endian,
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((ctx->endtime < now)?GSS_S_CONTEXT_EXPIRED:GSS_S_COMPLETE);
}