#ifndef _KERNEL
#include <assert.h>
#include <stdarg.h>
#define ASSERT assert
#endif
#include "k5-int.h"
#include "k5-platform.h"
#include "k5-platform-store_16.h"
#include "k5-platform-store_64.h"
#include "k5-platform-load_16.h"
#include "k5-platform-load_64.h"
#include "gssapiP_krb5.h"
#include <sys/int_limits.h>
static int
rotate_left (void *ptr, size_t bufsiz, size_t rc)
{
void *tbuf;
if (bufsiz == 0)
return 1;
rc = rc % bufsiz;
if (rc == 0)
return 1;
tbuf = MALLOC(rc);
if (tbuf == 0)
return 0;
(void) memcpy(tbuf, ptr, rc);
(void) memmove(ptr, (char *)ptr + rc, bufsiz - rc);
(void) memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
FREE(tbuf, rc);
return 1;
}
static const gss_buffer_desc empty_message = { 0, 0 };
#define FLAG_SENDER_IS_ACCEPTOR 0x01
#define FLAG_WRAP_CONFIDENTIAL 0x02
#define FLAG_ACCEPTOR_SUBKEY 0x04
krb5_error_code
gss_krb5int_make_seal_token_v3 (krb5_context context,
krb5_gss_ctx_id_rec *ctx,
const gss_buffer_desc * message,
gss_buffer_t token,
int conf_req_flag, int toktype)
{
size_t bufsize = 16;
unsigned char *outbuf = 0;
krb5_error_code err;
int key_usage;
unsigned char acceptor_flag;
const gss_buffer_desc *message2 = message;
#ifdef CFX_EXERCISE
size_t rrc;
#endif
size_t ec;
unsigned short tok_id;
krb5_checksum sum;
krb5_keyblock *key;
ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
ASSERT(ctx->big_endian == 0);
acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
key_usage = (toktype == KG_TOK_WRAP_MSG
? (ctx->initiate
? KG_USAGE_INITIATOR_SEAL
: KG_USAGE_ACCEPTOR_SEAL)
: (ctx->initiate
? KG_USAGE_INITIATOR_SIGN
: KG_USAGE_ACCEPTOR_SIGN));
if (ctx->have_acceptor_subkey) {
key = ctx->acceptor_subkey;
} else {
key = ctx->enc;
}
#ifdef _KERNEL
context->kef_cipher_mt = get_cipher_mech_type(context, key);
context->kef_hash_mt = get_hash_mech_type(context, key);
if ((err = init_key_kef(context->kef_cipher_mt, key))) {
return (GSS_S_FAILURE);
}
#endif
#ifdef CFX_EXERCISE
{
static int initialized = 0;
if (!initialized) {
srand(time(0));
initialized = 1;
}
}
#endif
if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
krb5_data plain;
krb5_enc_data cipher;
size_t ec_max;
size_t tlen;
if (SIZE_MAX - 300 < message->length)
return ENOMEM;
ec_max = SIZE_MAX - message->length - 300;
if (ec_max > 0xffff)
ec_max = 0xffff;
ec = 0;
plain.length = message->length + 16 + ec;
plain.data = MALLOC(plain.length);
if (plain.data == NULL)
return ENOMEM;
if ((err = krb5_c_encrypt_length(context,
ctx->enc->enctype, plain.length, &tlen))) {
FREE(plain.data, plain.length);
return (err);
}
bufsize = 16 + tlen;
outbuf = MALLOC(bufsize);
if (outbuf == NULL) {
FREE(plain.data, plain.length);
return ENOMEM;
}
store_16_be(0x0504, outbuf);
outbuf[2] = (acceptor_flag
| (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
| (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
outbuf[3] = 0xff;
store_16_be(ec, outbuf+4);
store_16_be(0, outbuf+6);
store_64_be(ctx->seq_send, outbuf+8);
(void) memcpy(plain.data, message->value, message->length);
(void) memset(plain.data + message->length, 'x', ec);
(void) memcpy(plain.data + message->length + ec, outbuf, 16);
cipher.ciphertext.data = (char *)outbuf + 16;
cipher.ciphertext.length = bufsize - 16;
cipher.enctype = key->enctype;
err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
(void) bzero(plain.data, plain.length);
FREE(plain.data, plain.length);
plain.data = 0;
if (err)
goto error;
ctx->seq_send++;
#ifdef CFX_EXERCISE
rrc = rand() & 0xffff;
if (rotate_left(outbuf+16, bufsize-16,
(bufsize-16) - (rrc % (bufsize - 16))))
store_16_be(rrc, outbuf+6);
#endif
} else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
krb5_data plain;
tok_id = 0x0504;
wrap_with_checksum:
plain.length = message->length + 16;
plain.data = MALLOC(message->length + 16);
if (plain.data == NULL)
return ENOMEM;
if (ctx->cksum_size > 0xffff) {
FREE(plain.data, plain.length);
return EINVAL;
}
bufsize = 16 + message2->length + ctx->cksum_size;
outbuf = MALLOC(bufsize);
if (outbuf == NULL) {
FREE(plain.data, plain.length);
plain.data = 0;
err = ENOMEM;
goto error;
}
store_16_be(tok_id, outbuf);
outbuf[2] = (acceptor_flag
| (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
outbuf[3] = 0xff;
if (toktype == KG_TOK_WRAP_MSG) {
store_16_be(0, outbuf+4);
store_16_be(0, outbuf+6);
} else {
store_16_be(0xffff, outbuf+4);
store_16_be(0xffff, outbuf+6);
}
store_64_be(ctx->seq_send, outbuf+8);
(void) memcpy(plain.data, message->value, message->length);
(void) memcpy(plain.data + message->length, outbuf, 16);
if (message2->length)
(void) memcpy(outbuf + 16, message2->value, message2->length);
sum.contents = outbuf + 16 + message2->length;
sum.length = ctx->cksum_size;
err = krb5_c_make_checksum(context, ctx->cksumtype, key,
key_usage, &plain, &sum);
bzero(plain.data, plain.length);
FREE(plain.data, plain.length);
plain.data = 0;
if (err) {
bzero(outbuf,bufsize);
err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
goto error;
}
if (sum.length != ctx->cksum_size) {
err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
goto error;
}
(void) memcpy(outbuf + 16 + message2->length, sum.contents,
ctx->cksum_size);
krb5_free_checksum_contents(context, &sum);
sum.contents = 0;
ctx->seq_send++;
if (toktype == KG_TOK_WRAP_MSG) {
#ifdef CFX_EXERCISE
rrc = rand() & 0xffff;
if (rotate_left(outbuf+16, bufsize-16,
(bufsize-16) - (rrc % (bufsize - 16))))
store_16_be(rrc, outbuf+6);
#endif
store_16_be(ctx->cksum_size, outbuf+4);
} else {
store_16_be(0xffff, outbuf+6);
}
} else if (toktype == KG_TOK_MIC_MSG) {
tok_id = 0x0404;
message2 = &empty_message;
goto wrap_with_checksum;
} else if (toktype == KG_TOK_DEL_CTX) {
return 0;
} else {
err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
goto error;
}
token->value = outbuf;
token->length = bufsize;
return 0;
error:
FREE(outbuf, bufsize);
token->value = NULL;
token->length = 0;
return err;
}
OM_uint32
gss_krb5int_unseal_token_v3(krb5_context *contextptr,
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_context context = *contextptr;
krb5_data plain;
gssint_uint64 seqnum;
size_t ec, rrc;
int key_usage;
unsigned char acceptor_flag;
krb5_checksum sum;
krb5_error_code err;
krb5_boolean valid;
krb5_keyblock *key;
ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
ASSERT(ctx->big_endian == 0);
ASSERT(ctx->proto == 1);
if (qop_state)
*qop_state = GSS_C_QOP_DEFAULT;
acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
key_usage = (toktype == KG_TOK_WRAP_MSG
? (!ctx->initiate
? KG_USAGE_INITIATOR_SEAL
: KG_USAGE_ACCEPTOR_SEAL)
: (!ctx->initiate
? KG_USAGE_INITIATOR_SIGN
: KG_USAGE_ACCEPTOR_SIGN));
ptr -= 2;
bodysize += 2;
if (bodysize < 16) {
defective:
*minor_status = 0;
return GSS_S_DEFECTIVE_TOKEN;
}
if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
*minor_status = (OM_uint32)G_BAD_DIRECTION;
return GSS_S_BAD_SIG;
}
if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
key = ctx->acceptor_subkey;
} else {
key = ctx->enc;
}
#ifdef _KERNEL
context->kef_cipher_mt = get_cipher_mech_type(context, key);
context->kef_hash_mt = get_hash_mech_type(context, key);
if ((err = init_key_kef(context->kef_cipher_mt, key))) {
return (GSS_S_FAILURE);
}
#endif
if (toktype == KG_TOK_WRAP_MSG) {
if (load_16_be(ptr) != 0x0504)
goto defective;
if (ptr[3] != 0xff)
goto defective;
ec = load_16_be(ptr+4);
rrc = load_16_be(ptr+6);
seqnum = load_64_be(ptr+8);
if (!rotate_left(ptr+16, bodysize-16, rrc)) {
no_mem:
*minor_status = ENOMEM;
return GSS_S_FAILURE;
}
if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
krb5_enc_data cipher;
unsigned char *althdr;
size_t plainlen;
if (conf_state)
*conf_state = 1;
cipher.enctype = key->enctype;
cipher.ciphertext.length = bodysize - 16;
cipher.ciphertext.data = (char *)ptr + 16;
plain.length = plainlen = bodysize - 16;
plain.data = MALLOC(plain.length);
if (plain.data == NULL)
goto no_mem;
err = krb5_c_decrypt(context, key, key_usage, 0,
&cipher, &plain);
if (err) {
goto error;
}
althdr = (uchar_t *)plain.data + plain.length - 16;
if (load_16_be(althdr) != 0x0504
|| althdr[2] != ptr[2]
|| althdr[3] != ptr[3]
|| memcmp(althdr+8, ptr+8, 8)) {
FREE(plain.data, plainlen);
goto defective;
}
message_buffer->length = plain.length - ec - 16;
message_buffer->value = MALLOC(message_buffer->length);
if (message_buffer->value == NULL) {
FREE(plain.data, plainlen);
goto no_mem;
}
(void) memcpy(message_buffer->value, plain.data,
message_buffer->length);
FREE(plain.data, plainlen);
} else {
if (conf_state)
*conf_state = 0;
if (ec + 16 < ec)
goto defective;
if (ec + 16 > bodysize)
goto defective;
store_16_be(0, ptr+4);
store_16_be(0, ptr+6);
plain.length = bodysize-ec;
plain.data = (char *)ptr;
if (!rotate_left(ptr, bodysize-ec, 16))
goto no_mem;
sum.length = ec;
if (sum.length != ctx->cksum_size) {
*minor_status = 0;
return GSS_S_BAD_SIG;
}
sum.contents = ptr+bodysize-ec;
sum.checksum_type = ctx->cksumtype;
err = krb5_c_verify_checksum(context, key, key_usage,
&plain, &sum, &valid);
if (err) {
*minor_status = err;
return GSS_S_BAD_SIG;
}
if (!valid) {
*minor_status = 0;
return GSS_S_BAD_SIG;
}
message_buffer->length = plain.length - 16;
message_buffer->value = MALLOC(message_buffer->length);
if (message_buffer->value == NULL)
goto no_mem;
(void) memcpy(message_buffer->value,
plain.data, message_buffer->length);
if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
goto no_mem;
store_16_be(ec, ptr+4);
store_16_be(rrc, ptr+6);
}
err = g_order_check(&ctx->seqstate, seqnum);
*minor_status = 0;
return err;
} else if (toktype == KG_TOK_MIC_MSG) {
if (load_16_be(ptr) != 0x0404)
goto defective;
verify_mic_1:
if (ptr[3] != 0xff)
goto defective;
if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
goto defective;
seqnum = load_64_be(ptr+8);
plain.length = message_buffer->length + 16;
plain.data = MALLOC(plain.length);
if (plain.data == NULL)
goto no_mem;
if (message_buffer->length)
(void) memcpy(plain.data,
message_buffer->value, message_buffer->length);
(void) memcpy(plain.data + message_buffer->length, ptr, 16);
sum.length = bodysize - 16;
sum.contents = ptr + 16;
sum.checksum_type = ctx->cksumtype;
err = krb5_c_verify_checksum(context, key, key_usage,
&plain, &sum, &valid);
if (err) {
error:
FREE(plain.data, plain.length);
*minor_status = err;
save_error_info(*minor_status, context);
return GSS_S_BAD_SIG;
}
FREE(plain.data, plain.length);
if (!valid) {
*minor_status = 0;
return GSS_S_BAD_SIG;
}
err = g_order_check(&ctx->seqstate, seqnum);
*minor_status = 0;
return err;
} else if (toktype == KG_TOK_DEL_CTX) {
if (load_16_be(ptr) != 0x0405)
goto defective;
message_buffer = (gss_buffer_t)&empty_message;
goto verify_mic_1;
} else {
goto defective;
}
}