From 8180bca1d4d08ad9b71e7f1d0cec36bebe7f16e5 Mon Sep 17 00:00:00 2001 From: Assar Westerlund Date: Sun, 27 Aug 2000 04:26:12 +0000 Subject: [PATCH] re-organize and add 3DES code git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@9004 ec53bebd-3082-4978-b11e-865c3cabbd6b --- lib/gssapi/krb5/unwrap.c | 242 ++++++++++++++++++++++++++++-- lib/gssapi/krb5/verify_mic.c | 173 +++++++++++++++++++-- lib/gssapi/krb5/wrap.c | 282 ++++++++++++++++++++++++++++++++--- lib/gssapi/unwrap.c | 242 ++++++++++++++++++++++++++++-- lib/gssapi/verify_mic.c | 173 +++++++++++++++++++-- lib/gssapi/wrap.c | 282 ++++++++++++++++++++++++++++++++--- 6 files changed, 1288 insertions(+), 106 deletions(-) diff --git a/lib/gssapi/krb5/unwrap.c b/lib/gssapi/krb5/unwrap.c index 2bee77f0d..d1efed77b 100644 --- a/lib/gssapi/krb5/unwrap.c +++ b/lib/gssapi/krb5/unwrap.c @@ -37,7 +37,7 @@ RCSID("$Id$"); OM_uint32 gss_krb5_getsomekey(const gss_ctx_id_t context_handle, - des_cblock *key) + krb5_keyblock **key) { /* XXX this is ugly, and probably incorrect... */ krb5_keyblock *skey; @@ -54,18 +54,19 @@ gss_krb5_getsomekey(const gss_ctx_id_t context_handle, &skey); if(skey == NULL) return GSS_S_FAILURE; - memcpy(key, skey->keyvalue.data, sizeof(*key)); - krb5_free_keyblock(gssapi_krb5_context, skey); + *key = skey; return 0; } -OM_uint32 gss_unwrap +static OM_uint32 +unwrap_des (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, - gss_qop_t * qop_state + gss_qop_t * qop_state, + krb5_keyblock *key ) { u_char *p, *pad; @@ -73,7 +74,7 @@ OM_uint32 gss_unwrap MD5_CTX md5; u_char hash[16], seq_data[8]; des_key_schedule schedule; - des_cblock key; + des_cblock deskey; des_cblock zero; int i; int32_t seq_number; @@ -109,10 +110,11 @@ OM_uint32 gss_unwrap if(cstate) { /* decrypt data */ - gss_krb5_getsomekey(context_handle, &key); - for (i = 0; i < sizeof(key); ++i) - key[i] ^= 0xf0; - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + + for (i = 0; i < sizeof(deskey); ++i) + deskey[i] ^= 0xf0; + des_set_key (&deskey, schedule); memset (&zero, 0, sizeof(zero)); des_cbc_encrypt ((void *)p, (void *)p, @@ -121,7 +123,7 @@ OM_uint32 gss_unwrap &zero, DES_DECRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); } /* check pad */ @@ -140,8 +142,8 @@ OM_uint32 gss_unwrap MD5Final (hash, &md5); memset (&zero, 0, sizeof(zero)); - gss_krb5_getsomekey(context_handle, &key); - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + des_set_key (&deskey, schedule); des_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), schedule, &zero); if (memcmp (p - 8, hash, 8) != 0) @@ -161,11 +163,11 @@ OM_uint32 gss_unwrap 4); p -= 16; - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_encrypt ((void *)p, (void *)p, 8, schedule, (des_cblock *)hash, DES_DECRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); if (memcmp (p, seq_data, 8) != 0) { @@ -179,7 +181,7 @@ OM_uint32 gss_unwrap /* copy out data */ output_message_buffer->length = input_message_buffer->length - - len - 8 - padlength; + - len - padlength - 8; output_message_buffer->value = malloc(output_message_buffer->length); if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) return GSS_S_FAILURE; @@ -188,3 +190,211 @@ OM_uint32 gss_unwrap output_message_buffer->length); return GSS_S_COMPLETE; } + +static OM_uint32 +unwrap_des3 + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int * conf_state, + gss_qop_t * qop_state, + krb5_keyblock *key + ) +{ + u_char *p, *pad; + size_t len; + u_char seq[8]; + krb5_data seq_data; + u_char cksum[20]; + int i; + int32_t seq_number; + size_t padlength; + OM_uint32 ret; + int cstate; + krb5_crypto crypto; + Checksum csum; + int cmp; + + p = input_message_buffer->value; + ret = gssapi_krb5_verify_header (&p, + input_message_buffer->length, + "\x02\x01"); + if (ret) + return ret; + + if (memcmp (p, "\x04\x00", 2) != 0) /* HMAC SHA1 DES3_KD */ + return GSS_S_BAD_SIG; + p += 2; + if (memcmp (p, "\x02\x00", 2) == 0) { + cstate = 1; + } else if (memcmp (p, "\xff\xff", 2) == 0) { + cstate = 0; + } else + return GSS_S_BAD_MIC; + p += 2; + if(conf_state != NULL) + *conf_state = cstate; + if (memcmp (p, "\xff\xff", 2) != 0) + return GSS_S_DEFECTIVE_TOKEN; + p += 2; + p += 28; + + len = p - (u_char *)input_message_buffer->value; + + if(cstate) { + /* decrypt data */ + krb5_data tmp; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + ret = krb5_decrypt(gssapi_krb5_context, crypto, KRB5_KU_USAGE_SEAL, + p, input_message_buffer->length - len, &tmp); + krb5_crypto_destroy(gssapi_krb5_context, crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + assert (tmp.length == input_message_buffer->length - len); + + memcpy (p, tmp.data, tmp.length); + krb5_data_free(&tmp); + } + /* check pad */ + + pad = (u_char *)input_message_buffer->value + input_message_buffer->length - 1; + padlength = *pad; + + for (i = padlength; i > 0 && *pad == padlength; i--, pad--) + ; + if (i != 0) + return GSS_S_BAD_MIC; + + /* verify sequence number */ + + krb5_auth_getremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + &seq_number); + seq[0] = (seq_number >> 0) & 0xFF; + seq[1] = (seq_number >> 8) & 0xFF; + seq[2] = (seq_number >> 16) & 0xFF; + seq[3] = (seq_number >> 24) & 0xFF; + memset (seq + 4, + (context_handle->more_flags & LOCAL) ? 0xFF : 0, + 4); + + p -= 28; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + ret = krb5_decrypt (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SEQ, + p, 8, &seq_data); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + if (seq_data.length != 8) { + krb5_data_free (&seq_data); + return GSS_S_BAD_MIC; + } + + cmp = memcmp (seq, seq_data.data, seq_data.length); + krb5_data_free (&seq_data); + if (cmp != 0) { + return GSS_S_BAD_MIC; + } + + krb5_auth_setremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + ++seq_number); + + /* verify checksum */ + + memcpy (cksum, p + 8, 20); + + memcpy (p + 20, p - 8, 8); + + csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; + csum.checksum.length = 20; + csum.checksum.data = cksum; + + ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = krb5_verify_checksum (gssapi_krb5_context, crypto, + KRB5_KU_USAGE_SIGN, + p + 20, + input_message_buffer->length - len + 8, + &csum); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* copy out data */ + + output_message_buffer->length = input_message_buffer->length + - len - padlength - 8; + output_message_buffer->value = malloc(output_message_buffer->length); + if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) + return GSS_S_FAILURE; + memcpy (output_message_buffer->value, + p + 36, + output_message_buffer->length); + return GSS_S_COMPLETE; +} + +OM_uint32 gss_unwrap + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int * conf_state, + gss_qop_t * qop_state + ) +{ + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = gss_krb5_getsomekey(context_handle, &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + + switch (keytype) { + case KEYTYPE_DES : + ret = unwrap_des (minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state, key); + break; + case KEYTYPE_DES3 : + ret = unwrap_des3 (minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state, key); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; +} diff --git a/lib/gssapi/krb5/verify_mic.c b/lib/gssapi/krb5/verify_mic.c index 2a7e15062..2776ad46e 100644 --- a/lib/gssapi/krb5/verify_mic.c +++ b/lib/gssapi/krb5/verify_mic.c @@ -35,20 +35,22 @@ RCSID("$Id$"); -OM_uint32 gss_verify_mic +static OM_uint32 +verify_mic_des (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, - gss_qop_t * qop_state + gss_qop_t * qop_state, + krb5_keyblock *key ) { u_char *p; MD5_CTX md5; u_char hash[16], seq_data[8]; des_key_schedule schedule; - des_cblock key; des_cblock zero; + des_cblock deskey; int32_t seq_number; OM_uint32 ret; @@ -75,18 +77,13 @@ OM_uint32 gss_verify_mic MD5Final (hash, &md5); memset (&zero, 0, sizeof(zero)); -#if 0 - memcpy (&key, context_handle->auth_context->key.keyvalue.data, - sizeof(key)); -#endif - memcpy (&key, context_handle->auth_context->remote_subkey->keyvalue.data, - sizeof(key)); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), schedule, &zero); if (memcmp (p - 8, hash, 8) != 0) { - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); return GSS_S_BAD_MIC; } @@ -105,11 +102,11 @@ OM_uint32 gss_verify_mic 4); p -= 16; - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_encrypt ((void *)p, (void *)p, 8, schedule, (des_cblock *)hash, DES_DECRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); if (memcmp (p, seq_data, 8) != 0) { @@ -122,3 +119,153 @@ OM_uint32 gss_verify_mic return GSS_S_COMPLETE; } + +static OM_uint32 +verify_mic_des3 + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t message_buffer, + const gss_buffer_t token_buffer, + gss_qop_t * qop_state, + krb5_keyblock *key + ) +{ + u_char *p; + u_char seq[8]; + int32_t seq_number; + OM_uint32 ret; + krb5_crypto crypto; + krb5_data seq_data; + int cmp; + Checksum csum; + char *tmp; + + p = token_buffer->value; + ret = gssapi_krb5_verify_header (&p, + token_buffer->length, + "\x01\x01"); + if (ret) + return ret; + + if (memcmp(p, "\x04\x00", 2) != 0) /* SGN_ALG = HMAC SHA1 DES3-KD */ + return GSS_S_BAD_SIG; + p += 2; + if (memcmp (p, "\xff\xff\xff\xff", 4) != 0) + return GSS_S_BAD_MIC; + p += 4; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret){ + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* verify sequence number */ + + ret = krb5_decrypt (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SEQ, + p, 8, &seq_data); + if (ret) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + *minor_status = ret; + return GSS_S_FAILURE; + } + + if (seq_data.length != 8) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + krb5_data_free (&seq_data); + return GSS_S_BAD_MIC; + } + + krb5_auth_getremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + &seq_number); + seq[0] = (seq_number >> 0) & 0xFF; + seq[1] = (seq_number >> 8) & 0xFF; + seq[2] = (seq_number >> 16) & 0xFF; + seq[3] = (seq_number >> 24) & 0xFF; + memset (seq + 4, + (context_handle->more_flags & LOCAL) ? 0xFF : 0, + 4); + cmp = memcmp (seq, seq_data.data, seq_data.length); + krb5_data_free (&seq_data); + if (cmp != 0) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + return GSS_S_BAD_MIC; + } + + /* verify checksum */ + + tmp = malloc (message_buffer->length + 8); + if (tmp == NULL) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + memcpy (tmp, p - 8, 8); + memcpy (tmp + 8, message_buffer->value, message_buffer->length); + + csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; + csum.checksum.length = 20; + csum.checksum.data = p + 8; + + ret = krb5_verify_checksum (gssapi_krb5_context, crypto, + KRB5_KU_USAGE_SIGN, + tmp, message_buffer->length + 8, + &csum); + free (tmp); + if (ret) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + *minor_status = ret; + return GSS_S_BAD_MIC; + } + + krb5_auth_setremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + ++seq_number); + + krb5_crypto_destroy (gssapi_krb5_context, crypto); + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_verify_mic + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t message_buffer, + const gss_buffer_t token_buffer, + gss_qop_t * qop_state + ) +{ + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = krb5_auth_con_getremotesubkey (gssapi_krb5_context, + context_handle->auth_context, + &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + switch (keytype) { + case KEYTYPE_DES : + ret = verify_mic_des (minor_status, context_handle, + message_buffer, token_buffer, qop_state, key); + break; + case KEYTYPE_DES3 : + ret = verify_mic_des3 (minor_status, context_handle, + message_buffer, token_buffer, qop_state, key); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; +} diff --git a/lib/gssapi/krb5/wrap.c b/lib/gssapi/krb5/wrap.c index 43f4d72e1..3816d4620 100644 --- a/lib/gssapi/krb5/wrap.c +++ b/lib/gssapi/krb5/wrap.c @@ -35,7 +35,24 @@ RCSID("$Id$"); -OM_uint32 gss_wrap_size_limit ( +static OM_uint32 +sub_wrap_size ( + OM_uint32 req_output_size, + OM_uint32 * max_input_size, + int blocksize, + int extrasize + ) +{ + size_t len, total_len, padlength; + padlength = blocksize - (req_output_size % blocksize); + len = req_output_size + 8 + padlength + extrasize; + gssapi_krb5_encap_length(len, &len, &total_len); + *max_input_size = (OM_uint32)total_len; + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_wrap_size_limit ( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, @@ -44,36 +61,58 @@ OM_uint32 gss_wrap_size_limit ( OM_uint32 * max_input_size ) { - size_t len, total_len, padlength; - padlength = 8 - (req_output_size % 8); - len = req_output_size + 8 + padlength + 22; - gssapi_krb5_encap_length(len, &len, &total_len); - *max_input_size = (OM_uint32)total_len; - return GSS_S_COMPLETE; + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = gss_krb5_getsomekey(context_handle, &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + + switch (keytype) { + case KEYTYPE_DES : + ret = sub_wrap_size(req_output_size, max_input_size, 8, 22); + break; + case KEYTYPE_DES3 : + ret = sub_wrap_size(req_output_size, max_input_size, 8, 34); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; } -OM_uint32 gss_wrap +static OM_uint32 +wrap_des (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, - gss_buffer_t output_message_buffer + gss_buffer_t output_message_buffer, + krb5_keyblock *key ) { u_char *p; MD5_CTX md5; u_char hash[16]; des_key_schedule schedule; - des_cblock key; + des_cblock deskey; des_cblock zero; int i; int32_t seq_number; - size_t len, total_len, padlength; + size_t len, total_len, padlength, datalen; padlength = 8 - (input_message_buffer->length % 8); - len = input_message_buffer->length + 8 + padlength + 22; + datalen = input_message_buffer->length + padlength + 8; + len = datalen + 22; gssapi_krb5_encap_length (len, &len, &total_len); output_message_buffer->length = total_len; @@ -83,8 +122,7 @@ OM_uint32 gss_wrap p = gssapi_krb5_make_header(output_message_buffer->value, len, - "\x02\x01"); - + "\x02\x01"); /* TOK_ID */ /* SGN_ALG */ memcpy (p, "\x00\x00", 2); @@ -112,12 +150,12 @@ OM_uint32 gss_wrap /* checksum */ MD5Init (&md5); MD5Update (&md5, p - 24, 8); - MD5Update (&md5, p, input_message_buffer->length + padlength + 8); + MD5Update (&md5, p, datalen); MD5Final (hash, &md5); memset (&zero, 0, sizeof(zero)); - gss_krb5_getsomekey(context_handle, &key); - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + des_set_key (&deskey, schedule); des_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), schedule, &zero); memcpy (p - 8, hash, 8); @@ -136,7 +174,7 @@ OM_uint32 gss_wrap (context_handle->more_flags & LOCAL) ? 0 : 0xFF, 4); - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_encrypt ((void *)p, (void *)p, 8, schedule, (des_cblock *)(p + 8), DES_ENCRYPT); @@ -148,22 +186,218 @@ OM_uint32 gss_wrap p += 16; if(conf_req_flag) { - gss_krb5_getsomekey(context_handle, &key); - for (i = 0; i < sizeof(key); ++i) - key[i] ^= 0xf0; - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + + for (i = 0; i < sizeof(deskey); ++i) + deskey[i] ^= 0xf0; + des_set_key (&deskey, schedule); memset (&zero, 0, sizeof(zero)); des_cbc_encrypt ((void *)p, (void *)p, - 8 + input_message_buffer->length + padlength, + datalen, schedule, &zero, DES_ENCRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); } if(conf_state != NULL) *conf_state = conf_req_flag; return GSS_S_COMPLETE; } + +static OM_uint32 +wrap_des3 + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + const gss_buffer_t input_message_buffer, + int * conf_state, + gss_buffer_t output_message_buffer, + krb5_keyblock *key + ) +{ + u_char *p; + u_char seq[8]; + int32_t seq_number; + size_t len, total_len, padlength, datalen; + u_int32_t ret; + krb5_crypto crypto; + Checksum cksum; + krb5_data encdata; + + padlength = 8 - (input_message_buffer->length % 8); + datalen = input_message_buffer->length + padlength + 8; + len = datalen + 34; + gssapi_krb5_encap_length (len, &len, &total_len); + + output_message_buffer->length = total_len; + output_message_buffer->value = malloc (total_len); + if (output_message_buffer->value == NULL) + return GSS_S_FAILURE; + + p = gssapi_krb5_make_header(output_message_buffer->value, + len, + "\x02\x01"); /* TOK_ID */ + + /* SGN_ALG */ + memcpy (p, "\x04\x00", 2); /* HMAC SHA1 DES3-KD */ + p += 2; + /* SEAL_ALG */ + if(conf_req_flag) + memcpy (p, "\x02\x00", 2); /* DES3-KD */ + else + memcpy (p, "\xff\xff", 2); + p += 2; + /* Filler */ + memcpy (p, "\xff\xff", 2); + p += 2; + + /* calculate checksum (the above + confounder + data + pad) */ + + memcpy (p + 20, p - 8, 8); + des_new_random_key((des_cblock*)(p + 28)); + memcpy (p + 28 + 8, input_message_buffer->value, + input_message_buffer->length); + memset (p + 28 + 8 + input_message_buffer->length, padlength, padlength); + + ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = krb5_create_checksum (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SIGN, + p + 20, + datalen + 8, + &cksum); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* zero out SND_SEQ + SGN_CKSUM in case */ + memset (p, 0, 28); + + memcpy (p + 8, cksum.checksum.data, cksum.checksum.length); + free_Checksum (&cksum); + + /* sequence number */ + krb5_auth_getlocalseqnumber (gssapi_krb5_context, + context_handle->auth_context, + &seq_number); + + seq[0] = (seq_number >> 0) & 0xFF; + seq[1] = (seq_number >> 8) & 0xFF; + seq[2] = (seq_number >> 16) & 0xFF; + seq[3] = (seq_number >> 24) & 0xFF; + memset (seq + 4, + (context_handle->more_flags & LOCAL) ? 0 : 0xFF, + 4); + + ret = krb5_crypto_init(gssapi_krb5_context, key, ETYPE_DES3_CBC_NONE, + &crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = krb5_encrypt (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SEQ, + seq, 8, &encdata); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + assert (encdata.length == 8); + + memcpy (p, encdata.data, encdata.length); + krb5_data_free (&encdata); + + krb5_auth_setlocalseqnumber (gssapi_krb5_context, + context_handle->auth_context, + ++seq_number); + + /* encrypt the data */ + p += 28; + + if(conf_req_flag) { + krb5_data tmp; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + ret = krb5_encrypt(gssapi_krb5_context, crypto, KRB5_KU_USAGE_SEAL, + p, datalen, &tmp); + krb5_crypto_destroy(gssapi_krb5_context, crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + assert (tmp.length == datalen); + + memcpy (p, tmp.data, datalen); + krb5_data_free(&tmp); + } + if(conf_state != NULL) + *conf_state = conf_req_flag; + return GSS_S_COMPLETE; +} + +OM_uint32 gss_wrap + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + const gss_buffer_t input_message_buffer, + int * conf_state, + gss_buffer_t output_message_buffer + ) +{ + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = gss_krb5_getsomekey(context_handle, &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + + switch (keytype) { + case KEYTYPE_DES : + ret = wrap_des (minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer, key); + break; + case KEYTYPE_DES3 : + ret = wrap_des3 (minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer, key); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; +} diff --git a/lib/gssapi/unwrap.c b/lib/gssapi/unwrap.c index 2bee77f0d..d1efed77b 100644 --- a/lib/gssapi/unwrap.c +++ b/lib/gssapi/unwrap.c @@ -37,7 +37,7 @@ RCSID("$Id$"); OM_uint32 gss_krb5_getsomekey(const gss_ctx_id_t context_handle, - des_cblock *key) + krb5_keyblock **key) { /* XXX this is ugly, and probably incorrect... */ krb5_keyblock *skey; @@ -54,18 +54,19 @@ gss_krb5_getsomekey(const gss_ctx_id_t context_handle, &skey); if(skey == NULL) return GSS_S_FAILURE; - memcpy(key, skey->keyvalue.data, sizeof(*key)); - krb5_free_keyblock(gssapi_krb5_context, skey); + *key = skey; return 0; } -OM_uint32 gss_unwrap +static OM_uint32 +unwrap_des (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, - gss_qop_t * qop_state + gss_qop_t * qop_state, + krb5_keyblock *key ) { u_char *p, *pad; @@ -73,7 +74,7 @@ OM_uint32 gss_unwrap MD5_CTX md5; u_char hash[16], seq_data[8]; des_key_schedule schedule; - des_cblock key; + des_cblock deskey; des_cblock zero; int i; int32_t seq_number; @@ -109,10 +110,11 @@ OM_uint32 gss_unwrap if(cstate) { /* decrypt data */ - gss_krb5_getsomekey(context_handle, &key); - for (i = 0; i < sizeof(key); ++i) - key[i] ^= 0xf0; - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + + for (i = 0; i < sizeof(deskey); ++i) + deskey[i] ^= 0xf0; + des_set_key (&deskey, schedule); memset (&zero, 0, sizeof(zero)); des_cbc_encrypt ((void *)p, (void *)p, @@ -121,7 +123,7 @@ OM_uint32 gss_unwrap &zero, DES_DECRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); } /* check pad */ @@ -140,8 +142,8 @@ OM_uint32 gss_unwrap MD5Final (hash, &md5); memset (&zero, 0, sizeof(zero)); - gss_krb5_getsomekey(context_handle, &key); - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + des_set_key (&deskey, schedule); des_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), schedule, &zero); if (memcmp (p - 8, hash, 8) != 0) @@ -161,11 +163,11 @@ OM_uint32 gss_unwrap 4); p -= 16; - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_encrypt ((void *)p, (void *)p, 8, schedule, (des_cblock *)hash, DES_DECRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); if (memcmp (p, seq_data, 8) != 0) { @@ -179,7 +181,7 @@ OM_uint32 gss_unwrap /* copy out data */ output_message_buffer->length = input_message_buffer->length - - len - 8 - padlength; + - len - padlength - 8; output_message_buffer->value = malloc(output_message_buffer->length); if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) return GSS_S_FAILURE; @@ -188,3 +190,211 @@ OM_uint32 gss_unwrap output_message_buffer->length); return GSS_S_COMPLETE; } + +static OM_uint32 +unwrap_des3 + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int * conf_state, + gss_qop_t * qop_state, + krb5_keyblock *key + ) +{ + u_char *p, *pad; + size_t len; + u_char seq[8]; + krb5_data seq_data; + u_char cksum[20]; + int i; + int32_t seq_number; + size_t padlength; + OM_uint32 ret; + int cstate; + krb5_crypto crypto; + Checksum csum; + int cmp; + + p = input_message_buffer->value; + ret = gssapi_krb5_verify_header (&p, + input_message_buffer->length, + "\x02\x01"); + if (ret) + return ret; + + if (memcmp (p, "\x04\x00", 2) != 0) /* HMAC SHA1 DES3_KD */ + return GSS_S_BAD_SIG; + p += 2; + if (memcmp (p, "\x02\x00", 2) == 0) { + cstate = 1; + } else if (memcmp (p, "\xff\xff", 2) == 0) { + cstate = 0; + } else + return GSS_S_BAD_MIC; + p += 2; + if(conf_state != NULL) + *conf_state = cstate; + if (memcmp (p, "\xff\xff", 2) != 0) + return GSS_S_DEFECTIVE_TOKEN; + p += 2; + p += 28; + + len = p - (u_char *)input_message_buffer->value; + + if(cstate) { + /* decrypt data */ + krb5_data tmp; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + ret = krb5_decrypt(gssapi_krb5_context, crypto, KRB5_KU_USAGE_SEAL, + p, input_message_buffer->length - len, &tmp); + krb5_crypto_destroy(gssapi_krb5_context, crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + assert (tmp.length == input_message_buffer->length - len); + + memcpy (p, tmp.data, tmp.length); + krb5_data_free(&tmp); + } + /* check pad */ + + pad = (u_char *)input_message_buffer->value + input_message_buffer->length - 1; + padlength = *pad; + + for (i = padlength; i > 0 && *pad == padlength; i--, pad--) + ; + if (i != 0) + return GSS_S_BAD_MIC; + + /* verify sequence number */ + + krb5_auth_getremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + &seq_number); + seq[0] = (seq_number >> 0) & 0xFF; + seq[1] = (seq_number >> 8) & 0xFF; + seq[2] = (seq_number >> 16) & 0xFF; + seq[3] = (seq_number >> 24) & 0xFF; + memset (seq + 4, + (context_handle->more_flags & LOCAL) ? 0xFF : 0, + 4); + + p -= 28; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + ret = krb5_decrypt (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SEQ, + p, 8, &seq_data); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + if (seq_data.length != 8) { + krb5_data_free (&seq_data); + return GSS_S_BAD_MIC; + } + + cmp = memcmp (seq, seq_data.data, seq_data.length); + krb5_data_free (&seq_data); + if (cmp != 0) { + return GSS_S_BAD_MIC; + } + + krb5_auth_setremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + ++seq_number); + + /* verify checksum */ + + memcpy (cksum, p + 8, 20); + + memcpy (p + 20, p - 8, 8); + + csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; + csum.checksum.length = 20; + csum.checksum.data = cksum; + + ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = krb5_verify_checksum (gssapi_krb5_context, crypto, + KRB5_KU_USAGE_SIGN, + p + 20, + input_message_buffer->length - len + 8, + &csum); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* copy out data */ + + output_message_buffer->length = input_message_buffer->length + - len - padlength - 8; + output_message_buffer->value = malloc(output_message_buffer->length); + if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) + return GSS_S_FAILURE; + memcpy (output_message_buffer->value, + p + 36, + output_message_buffer->length); + return GSS_S_COMPLETE; +} + +OM_uint32 gss_unwrap + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int * conf_state, + gss_qop_t * qop_state + ) +{ + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = gss_krb5_getsomekey(context_handle, &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + + switch (keytype) { + case KEYTYPE_DES : + ret = unwrap_des (minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state, key); + break; + case KEYTYPE_DES3 : + ret = unwrap_des3 (minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state, key); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; +} diff --git a/lib/gssapi/verify_mic.c b/lib/gssapi/verify_mic.c index 2a7e15062..2776ad46e 100644 --- a/lib/gssapi/verify_mic.c +++ b/lib/gssapi/verify_mic.c @@ -35,20 +35,22 @@ RCSID("$Id$"); -OM_uint32 gss_verify_mic +static OM_uint32 +verify_mic_des (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, - gss_qop_t * qop_state + gss_qop_t * qop_state, + krb5_keyblock *key ) { u_char *p; MD5_CTX md5; u_char hash[16], seq_data[8]; des_key_schedule schedule; - des_cblock key; des_cblock zero; + des_cblock deskey; int32_t seq_number; OM_uint32 ret; @@ -75,18 +77,13 @@ OM_uint32 gss_verify_mic MD5Final (hash, &md5); memset (&zero, 0, sizeof(zero)); -#if 0 - memcpy (&key, context_handle->auth_context->key.keyvalue.data, - sizeof(key)); -#endif - memcpy (&key, context_handle->auth_context->remote_subkey->keyvalue.data, - sizeof(key)); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), schedule, &zero); if (memcmp (p - 8, hash, 8) != 0) { - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); return GSS_S_BAD_MIC; } @@ -105,11 +102,11 @@ OM_uint32 gss_verify_mic 4); p -= 16; - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_encrypt ((void *)p, (void *)p, 8, schedule, (des_cblock *)hash, DES_DECRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); if (memcmp (p, seq_data, 8) != 0) { @@ -122,3 +119,153 @@ OM_uint32 gss_verify_mic return GSS_S_COMPLETE; } + +static OM_uint32 +verify_mic_des3 + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t message_buffer, + const gss_buffer_t token_buffer, + gss_qop_t * qop_state, + krb5_keyblock *key + ) +{ + u_char *p; + u_char seq[8]; + int32_t seq_number; + OM_uint32 ret; + krb5_crypto crypto; + krb5_data seq_data; + int cmp; + Checksum csum; + char *tmp; + + p = token_buffer->value; + ret = gssapi_krb5_verify_header (&p, + token_buffer->length, + "\x01\x01"); + if (ret) + return ret; + + if (memcmp(p, "\x04\x00", 2) != 0) /* SGN_ALG = HMAC SHA1 DES3-KD */ + return GSS_S_BAD_SIG; + p += 2; + if (memcmp (p, "\xff\xff\xff\xff", 4) != 0) + return GSS_S_BAD_MIC; + p += 4; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret){ + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* verify sequence number */ + + ret = krb5_decrypt (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SEQ, + p, 8, &seq_data); + if (ret) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + *minor_status = ret; + return GSS_S_FAILURE; + } + + if (seq_data.length != 8) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + krb5_data_free (&seq_data); + return GSS_S_BAD_MIC; + } + + krb5_auth_getremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + &seq_number); + seq[0] = (seq_number >> 0) & 0xFF; + seq[1] = (seq_number >> 8) & 0xFF; + seq[2] = (seq_number >> 16) & 0xFF; + seq[3] = (seq_number >> 24) & 0xFF; + memset (seq + 4, + (context_handle->more_flags & LOCAL) ? 0xFF : 0, + 4); + cmp = memcmp (seq, seq_data.data, seq_data.length); + krb5_data_free (&seq_data); + if (cmp != 0) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + return GSS_S_BAD_MIC; + } + + /* verify checksum */ + + tmp = malloc (message_buffer->length + 8); + if (tmp == NULL) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + memcpy (tmp, p - 8, 8); + memcpy (tmp + 8, message_buffer->value, message_buffer->length); + + csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; + csum.checksum.length = 20; + csum.checksum.data = p + 8; + + ret = krb5_verify_checksum (gssapi_krb5_context, crypto, + KRB5_KU_USAGE_SIGN, + tmp, message_buffer->length + 8, + &csum); + free (tmp); + if (ret) { + krb5_crypto_destroy (gssapi_krb5_context, crypto); + *minor_status = ret; + return GSS_S_BAD_MIC; + } + + krb5_auth_setremoteseqnumber (gssapi_krb5_context, + context_handle->auth_context, + ++seq_number); + + krb5_crypto_destroy (gssapi_krb5_context, crypto); + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_verify_mic + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t message_buffer, + const gss_buffer_t token_buffer, + gss_qop_t * qop_state + ) +{ + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = krb5_auth_con_getremotesubkey (gssapi_krb5_context, + context_handle->auth_context, + &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + switch (keytype) { + case KEYTYPE_DES : + ret = verify_mic_des (minor_status, context_handle, + message_buffer, token_buffer, qop_state, key); + break; + case KEYTYPE_DES3 : + ret = verify_mic_des3 (minor_status, context_handle, + message_buffer, token_buffer, qop_state, key); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; +} diff --git a/lib/gssapi/wrap.c b/lib/gssapi/wrap.c index 43f4d72e1..3816d4620 100644 --- a/lib/gssapi/wrap.c +++ b/lib/gssapi/wrap.c @@ -35,7 +35,24 @@ RCSID("$Id$"); -OM_uint32 gss_wrap_size_limit ( +static OM_uint32 +sub_wrap_size ( + OM_uint32 req_output_size, + OM_uint32 * max_input_size, + int blocksize, + int extrasize + ) +{ + size_t len, total_len, padlength; + padlength = blocksize - (req_output_size % blocksize); + len = req_output_size + 8 + padlength + extrasize; + gssapi_krb5_encap_length(len, &len, &total_len); + *max_input_size = (OM_uint32)total_len; + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_wrap_size_limit ( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, @@ -44,36 +61,58 @@ OM_uint32 gss_wrap_size_limit ( OM_uint32 * max_input_size ) { - size_t len, total_len, padlength; - padlength = 8 - (req_output_size % 8); - len = req_output_size + 8 + padlength + 22; - gssapi_krb5_encap_length(len, &len, &total_len); - *max_input_size = (OM_uint32)total_len; - return GSS_S_COMPLETE; + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = gss_krb5_getsomekey(context_handle, &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + + switch (keytype) { + case KEYTYPE_DES : + ret = sub_wrap_size(req_output_size, max_input_size, 8, 22); + break; + case KEYTYPE_DES3 : + ret = sub_wrap_size(req_output_size, max_input_size, 8, 34); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; } -OM_uint32 gss_wrap +static OM_uint32 +wrap_des (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, - gss_buffer_t output_message_buffer + gss_buffer_t output_message_buffer, + krb5_keyblock *key ) { u_char *p; MD5_CTX md5; u_char hash[16]; des_key_schedule schedule; - des_cblock key; + des_cblock deskey; des_cblock zero; int i; int32_t seq_number; - size_t len, total_len, padlength; + size_t len, total_len, padlength, datalen; padlength = 8 - (input_message_buffer->length % 8); - len = input_message_buffer->length + 8 + padlength + 22; + datalen = input_message_buffer->length + padlength + 8; + len = datalen + 22; gssapi_krb5_encap_length (len, &len, &total_len); output_message_buffer->length = total_len; @@ -83,8 +122,7 @@ OM_uint32 gss_wrap p = gssapi_krb5_make_header(output_message_buffer->value, len, - "\x02\x01"); - + "\x02\x01"); /* TOK_ID */ /* SGN_ALG */ memcpy (p, "\x00\x00", 2); @@ -112,12 +150,12 @@ OM_uint32 gss_wrap /* checksum */ MD5Init (&md5); MD5Update (&md5, p - 24, 8); - MD5Update (&md5, p, input_message_buffer->length + padlength + 8); + MD5Update (&md5, p, datalen); MD5Final (hash, &md5); memset (&zero, 0, sizeof(zero)); - gss_krb5_getsomekey(context_handle, &key); - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + des_set_key (&deskey, schedule); des_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), schedule, &zero); memcpy (p - 8, hash, 8); @@ -136,7 +174,7 @@ OM_uint32 gss_wrap (context_handle->more_flags & LOCAL) ? 0 : 0xFF, 4); - des_set_key (&key, schedule); + des_set_key (&deskey, schedule); des_cbc_encrypt ((void *)p, (void *)p, 8, schedule, (des_cblock *)(p + 8), DES_ENCRYPT); @@ -148,22 +186,218 @@ OM_uint32 gss_wrap p += 16; if(conf_req_flag) { - gss_krb5_getsomekey(context_handle, &key); - for (i = 0; i < sizeof(key); ++i) - key[i] ^= 0xf0; - des_set_key (&key, schedule); + memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); + + for (i = 0; i < sizeof(deskey); ++i) + deskey[i] ^= 0xf0; + des_set_key (&deskey, schedule); memset (&zero, 0, sizeof(zero)); des_cbc_encrypt ((void *)p, (void *)p, - 8 + input_message_buffer->length + padlength, + datalen, schedule, &zero, DES_ENCRYPT); - memset (key, 0, sizeof(key)); + memset (deskey, 0, sizeof(deskey)); memset (schedule, 0, sizeof(schedule)); } if(conf_state != NULL) *conf_state = conf_req_flag; return GSS_S_COMPLETE; } + +static OM_uint32 +wrap_des3 + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + const gss_buffer_t input_message_buffer, + int * conf_state, + gss_buffer_t output_message_buffer, + krb5_keyblock *key + ) +{ + u_char *p; + u_char seq[8]; + int32_t seq_number; + size_t len, total_len, padlength, datalen; + u_int32_t ret; + krb5_crypto crypto; + Checksum cksum; + krb5_data encdata; + + padlength = 8 - (input_message_buffer->length % 8); + datalen = input_message_buffer->length + padlength + 8; + len = datalen + 34; + gssapi_krb5_encap_length (len, &len, &total_len); + + output_message_buffer->length = total_len; + output_message_buffer->value = malloc (total_len); + if (output_message_buffer->value == NULL) + return GSS_S_FAILURE; + + p = gssapi_krb5_make_header(output_message_buffer->value, + len, + "\x02\x01"); /* TOK_ID */ + + /* SGN_ALG */ + memcpy (p, "\x04\x00", 2); /* HMAC SHA1 DES3-KD */ + p += 2; + /* SEAL_ALG */ + if(conf_req_flag) + memcpy (p, "\x02\x00", 2); /* DES3-KD */ + else + memcpy (p, "\xff\xff", 2); + p += 2; + /* Filler */ + memcpy (p, "\xff\xff", 2); + p += 2; + + /* calculate checksum (the above + confounder + data + pad) */ + + memcpy (p + 20, p - 8, 8); + des_new_random_key((des_cblock*)(p + 28)); + memcpy (p + 28 + 8, input_message_buffer->value, + input_message_buffer->length); + memset (p + 28 + 8 + input_message_buffer->length, padlength, padlength); + + ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = krb5_create_checksum (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SIGN, + p + 20, + datalen + 8, + &cksum); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* zero out SND_SEQ + SGN_CKSUM in case */ + memset (p, 0, 28); + + memcpy (p + 8, cksum.checksum.data, cksum.checksum.length); + free_Checksum (&cksum); + + /* sequence number */ + krb5_auth_getlocalseqnumber (gssapi_krb5_context, + context_handle->auth_context, + &seq_number); + + seq[0] = (seq_number >> 0) & 0xFF; + seq[1] = (seq_number >> 8) & 0xFF; + seq[2] = (seq_number >> 16) & 0xFF; + seq[3] = (seq_number >> 24) & 0xFF; + memset (seq + 4, + (context_handle->more_flags & LOCAL) ? 0 : 0xFF, + 4); + + ret = krb5_crypto_init(gssapi_krb5_context, key, ETYPE_DES3_CBC_NONE, + &crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = krb5_encrypt (gssapi_krb5_context, + crypto, + KRB5_KU_USAGE_SEQ, + seq, 8, &encdata); + krb5_crypto_destroy (gssapi_krb5_context, crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + + assert (encdata.length == 8); + + memcpy (p, encdata.data, encdata.length); + krb5_data_free (&encdata); + + krb5_auth_setlocalseqnumber (gssapi_krb5_context, + context_handle->auth_context, + ++seq_number); + + /* encrypt the data */ + p += 28; + + if(conf_req_flag) { + krb5_data tmp; + + ret = krb5_crypto_init(gssapi_krb5_context, key, + ETYPE_DES3_CBC_NONE, &crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + ret = krb5_encrypt(gssapi_krb5_context, crypto, KRB5_KU_USAGE_SEAL, + p, datalen, &tmp); + krb5_crypto_destroy(gssapi_krb5_context, crypto); + if (ret) { + free (output_message_buffer->value); + *minor_status = ret; + return GSS_S_FAILURE; + } + assert (tmp.length == datalen); + + memcpy (p, tmp.data, datalen); + krb5_data_free(&tmp); + } + if(conf_state != NULL) + *conf_state = conf_req_flag; + return GSS_S_COMPLETE; +} + +OM_uint32 gss_wrap + (OM_uint32 * minor_status, + const gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + const gss_buffer_t input_message_buffer, + int * conf_state, + gss_buffer_t output_message_buffer + ) +{ + krb5_keyblock *key; + OM_uint32 ret; + krb5_keytype keytype; + + ret = gss_krb5_getsomekey(context_handle, &key); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype); + + switch (keytype) { + case KEYTYPE_DES : + ret = wrap_des (minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer, key); + break; + case KEYTYPE_DES3 : + ret = wrap_des3 (minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer, key); + break; + default : + *minor_status = KRB5_PROG_ETYPE_NOSUPP; + ret = GSS_S_FAILURE; + break; + } + krb5_free_keyblock (gssapi_krb5_context, key); + return ret; +}