return GSS_S_COMPLETE;
}
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_create_integrity_checksum(krb5_context context,
+ krb5_crypto crypto,
+ krb5_key_usage usage,
+ int type,
+ void *data,
+ size_t len,
+ Checksum *result);
+
+OM_uint32 _gssapi_wrap_ex_cfx(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ int *conf_state,
+ krb5_keyblock *key,
+ const gss_buffer_t associated_data_buffer,
+ gss_buffer_t message_buffer,
+ gss_buffer_t output_token_buffer)
+{
+ krb5_crypto crypto;
+ gss_cfx_wrap_token token;
+ krb5_error_code ret;
+ unsigned usage;
+ krb5_data cipher;
+ size_t wrapped_len, cksumsize;
+ uint16_t padlength, rrc = 0;
+ int32_t seq_number;
+ u_char *p;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = _gsskrb5cfx_wrap_length_cfx(context_handle, context,
+ crypto, conf_req_flag,
+ message_buffer->length,
+ &wrapped_len, &cksumsize, &padlength);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Always rotate encrypted token (if any) and checksum to header */
+ rrc = (conf_req_flag ? sizeof(*token) : 0) + (uint16_t)cksumsize;
+
+ output_token_buffer->length = wrapped_len - message_buffer->length;
+ output_token_buffer->value = malloc(output_token_buffer->length);
+ if (output_token_buffer->value == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ p = output_token_buffer->value;
+ token = (gss_cfx_wrap_token)p;
+ token->TOK_ID[0] = 0x05;
+ token->TOK_ID[1] = 0x04;
+ token->Flags = 0;
+ token->Filler = 0xFF;
+ if ((context_handle->more_flags & LOCAL) == 0)
+ token->Flags |= CFXSentByAcceptor;
+ if (context_handle->more_flags & ACCEPTOR_SUBKEY)
+ token->Flags |= CFXAcceptorSubkey;
+ if (conf_req_flag) {
+ /*
+ * In Wrap tokens with confidentiality, the EC field is
+ * used to encode the size (in bytes) of the random filler.
+ */
+ token->Flags |= CFXSealed;
+ token->EC[0] = (padlength >> 8) & 0xFF;
+ token->EC[1] = (padlength >> 0) & 0xFF;
+ } else {
+ /*
+ * In Wrap tokens without confidentiality, the EC field is
+ * used to encode the size (in bytes) of the trailing
+ * checksum.
+ *
+ * This is not used in the checksum calcuation itself,
+ * because the checksum length could potentially vary
+ * depending on the data length.
+ */
+ token->EC[0] = 0;
+ token->EC[1] = 0;
+ }
+
+ /*
+ * In Wrap tokens that provide for confidentiality, the RRC
+ * field in the header contains the hex value 00 00 before
+ * encryption.
+ *
+ * In Wrap tokens that do not provide for confidentiality,
+ * both the EC and RRC fields in the appended checksum
+ * contain the hex value 00 00 for the purpose of calculating
+ * the checksum.
+ */
+ token->RRC[0] = 0;
+ token->RRC[1] = 0;
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ krb5_auth_con_getlocalseqnumber(context,
+ context_handle->auth_context,
+ &seq_number);
+ _gsskrb5_encode_be_om_uint32(0, &token->SND_SEQ[0]);
+ _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
+ krb5_auth_con_setlocalseqnumber(context,
+ context_handle->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ /*
+ * If confidentiality is requested, the token header is
+ * appended to the plaintext before encryption; the resulting
+ * token is {"header" | encrypt(plaintext | pad | "header")}.
+ *
+ * If no confidentiality is requested, the checksum is
+ * calculated over the plaintext concatenated with the
+ * token header.
+ */
+ if (context_handle->more_flags & LOCAL) {
+ usage = KRB5_KU_USAGE_INITIATOR_SEAL;
+ } else {
+ usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
+ }
+
+ if (conf_req_flag) {
+ Checksum cksum;
+ size_t cf_len = 0;
+ uint8_t *cf;
+ size_t sign_len = 0;
+ size_t sign_idx = 0;
+ uint8_t *sign;
+ size_t seal_len = 0;
+ size_t seal_idx = 0;
+ uint8_t *seal;
+
+ krb5_crypto_getconfoundersize(context, crypto, &cf_len);
+
+ cf = (uint8_t *)malloc(cf_len);
+ if (cf == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_token_buffer);
+ return GSS_S_FAILURE;
+ }
+
+ krb5_generate_random_block(cf, cf_len);
+
+ /*
+ * Any necessary padding is added here to ensure that the
+ * encrypted token header is always at the end of the
+ * ciphertext.
+ *
+ * The specification does not require that the padding
+ * bytes are initialized.
+ */
+ sign_len += cf_len;
+ sign_len += associated_data_buffer->length;
+ sign_len += padlength;
+ sign_len += sizeof(*token);
+
+ sign = (uint8_t *)malloc(sign_len);
+ if (sign == NULL) {
+ *minor_status = ENOMEM;
+ free(cf);
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_token_buffer);
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(sign + sign_idx, cf, cf_len);
+ sign_idx += cf_len;
+ memcpy(sign + sign_idx, associated_data_buffer->value, associated_data_buffer->length);
+ sign_idx += associated_data_buffer->length;
+ memset(sign + sign_idx, 0xFF, padlength);
+ sign_idx += padlength;
+ memcpy(sign + sign_idx, token, sizeof(*token));
+ sign_idx += sizeof(*token);
+
+ ret = krb5_create_integrity_checksum(context, crypto, usage, 0,
+ sign, sign_len, &cksum);
+ free(sign);
+ if (ret != 0) {
+ *minor_status = ret;
+ free(cf);
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_token_buffer);
+ return GSS_S_FAILURE;
+ }
+
+ seal_len += cf_len;
+ seal_len += message_buffer->length;
+ seal_len += padlength;
+ seal_len += sizeof(*token);
+
+ seal = (uint8_t *)malloc(seal_len);
+ if (seal == NULL) {
+ *minor_status = ENOMEM;
+ free(cf);
+ free_Checksum(&cksum);
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_token_buffer);
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(seal + seal_idx, cf, cf_len);
+ seal_idx += cf_len;
+ memcpy(seal + seal_idx, message_buffer->value, message_buffer->length);
+ seal_idx += message_buffer->length;
+ memset(seal + seal_idx, 0xFF, padlength);
+ seal_idx += padlength;
+ memcpy(seal + seal_idx, token, sizeof(*token));
+ seal_idx += sizeof(*token);
+
+ ret = krb5_encrypt_inplace_ivec(context, crypto, usage,
+ seal, seal_len, NULL);
+ free(cf);
+ if (ret != 0) {
+ *minor_status = ret;
+ free(seal);
+ free_Checksum(&cksum);
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_token_buffer);
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(message_buffer->value, seal + cf_len, message_buffer->length);
+
+ token->RRC[0] = (rrc >> 8) & 0xFF;
+ token->RRC[1] = (rrc >> 0) & 0xFF;
+
+ p += sizeof(*token);
+
+ /* rotated by padlength+sizeof(token)+checksum.length */
+ seal_idx = cf_len + message_buffer->length;
+ memcpy(p, seal + seal_idx, padlength);
+ p += padlength;
+
+ seal_idx += padlength;
+ memcpy(p, seal + seal_idx, sizeof(*token));
+ p += sizeof(*token);
+
+ assert(cksum.checksum.length == cksumsize);
+ memcpy(p, cksum.checksum.data, cksum.checksum.length);
+ p += cksum.checksum.length;
+
+ memcpy(p, seal, cf_len);
+
+ free_Checksum(&cksum);
+ free(seal);
+
+ } else {
+ char *buf;
+ Checksum cksum;
+
+ buf = malloc(associated_data_buffer->length + sizeof(*token));
+ if (buf == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_token_buffer);
+ return GSS_S_FAILURE;
+ }
+ memcpy(buf, associated_data_buffer->value, associated_data_buffer->length);
+ memcpy(buf + associated_data_buffer->length, token, sizeof(*token));
+
+ ret = krb5_create_checksum(context, crypto,
+ usage, 0, buf,
+ associated_data_buffer->length +
+ sizeof(*token),
+ &cksum);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_token_buffer);
+ free(buf);
+ return GSS_S_FAILURE;
+ }
+
+ free(buf);
+
+ assert(cksum.checksum.length == cksumsize);
+ token->EC[0] = (cksum.checksum.length >> 8) & 0xFF;
+ token->EC[1] = (cksum.checksum.length >> 0) & 0xFF;
+ token->RRC[0] = (rrc >> 8) & 0xFF;
+ token->RRC[1] = (rrc >> 0) & 0xFF;
+
+ p += sizeof(*token);
+ memcpy(p, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum(&cksum);
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ if (conf_state != NULL) {
+ *conf_state = conf_req_flag;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
OM_uint32 _gssapi_unwrap_cfx(OM_uint32 *minor_status,
const gsskrb5_ctx context_handle,
krb5_context context,
return GSS_S_COMPLETE;
}
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_integrity_checksum(krb5_context context,
+ krb5_crypto crypto,
+ krb5_key_usage usage,
+ void *data,
+ size_t len,
+ Checksum *cksum);
+
+OM_uint32 _gssapi_unwrap_ex_cfx(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ int *conf_state,
+ gss_qop_t *qop_state,
+ krb5_keyblock *key,
+ const gss_buffer_t token_header_buffer,
+ const gss_buffer_t associated_data_buffer,
+ gss_buffer_t message_buffer)
+{
+ krb5_crypto crypto;
+ gss_cfx_wrap_token token;
+ u_char token_flags;
+ krb5_error_code ret;
+ unsigned usage;
+ krb5_data data;
+ uint16_t ec, rrc;
+ OM_uint32 seq_number_lo, seq_number_hi;
+ size_t len;
+ u_char *p;
+
+ *minor_status = 0;
+
+ if (token_header_buffer->length < sizeof(*token)) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ p = token_header_buffer->value;
+
+ token = (gss_cfx_wrap_token)p;
+
+ if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ /* Ignore unknown flags */
+ token_flags = token->Flags &
+ (CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
+
+ if (token_flags & CFXSentByAcceptor) {
+ if ((context_handle->more_flags & LOCAL) == 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (context_handle->more_flags & ACCEPTOR_SUBKEY) {
+ if ((token_flags & CFXAcceptorSubkey) == 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ } else {
+ if (token_flags & CFXAcceptorSubkey)
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (token->Filler != 0xFF) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (conf_state != NULL) {
+ *conf_state = (token_flags & CFXSealed) ? 1 : 0;
+ }
+
+ ec = (token->EC[0] << 8) | token->EC[1];
+ rrc = (token->RRC[0] << 8) | token->RRC[1];
+
+ /*
+ * Check sequence number
+ */
+ _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
+ _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
+ if (seq_number_hi) {
+ /* no support for 64-bit sequence numbers */
+ *minor_status = ERANGE;
+ return GSS_S_UNSEQ_TOKEN;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ ret = _gssapi_msg_order_check(context_handle->order, seq_number_lo);
+ if (ret != 0) {
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return ret;
+ }
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ /*
+ * Decrypt and/or verify checksum
+ */
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ if (context_handle->more_flags & LOCAL) {
+ usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
+ } else {
+ usage = KRB5_KU_USAGE_INITIATOR_SEAL;
+ }
+
+ p += sizeof(*token);
+
+ if (token_flags & CFXSealed) {
+ Checksum cksum;
+ size_t overhead;
+ size_t cf_len = 0;
+ uint8_t *cf;
+ size_t rot_len = 0;
+ size_t rot_idx = 0;
+ uint8_t *rot;
+ size_t seal_len = 0;
+ uint8_t *seal;
+ size_t sign_len = 0;
+ size_t sign_idx = 0;
+ uint8_t *sign;
+ uint8_t *pad;
+ gss_cfx_wrap_token token2;
+
+ overhead = token_header_buffer->length - sizeof(*token);
+
+ /* Determine checksum type */
+ ret = krb5_crypto_get_checksum_type(context,
+ crypto, &cksum.cksumtype);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Determine checksum size */
+ ret = krb5_checksumsize(context,
+ cksum.cksumtype, &cksum.checksum.length);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Determine confounder size */
+ ret = krb5_crypto_getconfoundersize(context, crypto, &cf_len);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ if (overhead != (ec + sizeof(*token) + cksum.checksum.length + cf_len)) {
+ *minor_status = ERANGE;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_BAD_MIC;
+ }
+
+ rot_len += overhead;
+ rot_len += message_buffer->length;
+
+ rot = (uint8_t *)malloc(rot_len);
+ if (rot == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(rot + rot_idx, p, token_header_buffer->length - sizeof(*token));
+ rot_idx += (token_header_buffer->length - sizeof(*token));
+
+ memcpy(rot + rot_idx, message_buffer->value, message_buffer->length);
+ rot_idx += message_buffer->length;
+
+ *minor_status = rrc_rotate(rot, rot_len, rrc+ec, TRUE);
+ if (*minor_status != 0) {
+ free(rot);
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ seal = rot;
+ seal_len = rot_len - cksum.checksum.length;
+
+ cksum.checksum.data = seal + seal_len;
+
+ ret = krb5_decrypt_inplace_ivec(context, crypto, usage,
+ seal, seal_len, NULL);
+ if (ret != 0) {
+ *minor_status = ret;
+ free(rot);
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ cf = seal;
+ memcpy(message_buffer->value, seal + cf_len, message_buffer->length);
+ pad = seal + cf_len + message_buffer->length;
+ token2 = (gss_cfx_wrap_token)((uint8_t *)seal + cf_len + message_buffer->length + ec);
+
+ sign_len += cf_len;
+ sign_len += associated_data_buffer->length;
+ sign_len += ec;
+ sign_len += sizeof(*token2);
+
+ sign = (uint8_t *)malloc(sign_len);
+ if (sign == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(sign + sign_idx, cf, cf_len);
+ sign_idx += cf_len;
+ memcpy(sign + sign_idx, associated_data_buffer->value, associated_data_buffer->length);
+ sign_idx += associated_data_buffer->length;
+ memcpy(sign + sign_idx, pad, ec);
+ sign_idx += ec;
+ memcpy(sign + sign_idx, token2, sizeof(*token2));
+ sign_idx += sizeof(*token2);
+
+ ret = krb5_verify_integrity_checksum(context, crypto, usage,
+ sign, sign_len, &cksum);
+ free(sign);
+ if (ret != 0) {
+ *minor_status = ret;
+ free(rot);
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* RRC is unprotected; don't modify input buffer */
+ token2->RRC[0] = token->RRC[0];
+ token2->RRC[1] = token->RRC[1];
+
+ /* Check the integrity of the header */
+ if (memcmp(token2, token, sizeof(*token)) != 0) {
+ free(rot);
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_BAD_MIC;
+ }
+
+ free(rot);
+ } else {
+ Checksum cksum;
+#if 0
+ gssoutput_message;
+
+ /* Rotate by RRC; bogus to do this in-place XXX */
+ *minor_status = rrc_rotate(p, len, rrc, TRUE);
+ if (*minor_status != 0) {
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Determine checksum type */
+ ret = krb5_crypto_get_checksum_type(context,
+ crypto, &cksum.cksumtype);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ cksum.checksum.length = ec;
+
+ /* Check we have at least as much data as the checksum */
+ if (len < cksum.checksum.length) {
+ *minor_status = ERANGE;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_BAD_MIC;
+ }
+
+ /* Length now is of the plaintext only, no checksum */
+ len -= cksum.checksum.length;
+ cksum.checksum.data = p + len;
+
+ output_message_buffer->length = len; /* for later */
+ output_message_buffer->value = malloc(len + sizeof(*token));
+ if (output_message_buffer->value == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Checksum is over (plaintext-data | "header") */
+ memcpy(output_message_buffer->value, p, len);
+ memcpy((u_char *)output_message_buffer->value + len,
+ token, sizeof(*token));
+
+ /* EC is not included in checksum calculation */
+ token = (gss_cfx_wrap_token)((u_char *)output_message_buffer->value +
+ len);
+ token->EC[0] = 0;
+ token->EC[1] = 0;
+ token->RRC[0] = 0;
+ token->RRC[1] = 0;
+
+ ret = krb5_verify_checksum(context, crypto,
+ usage,
+ output_message_buffer->value,
+ len + sizeof(*token),
+ &cksum);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_BAD_MIC;
+ }
+#endif
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ if (qop_state != NULL) {
+ *qop_state = GSS_C_QOP_DEFAULT;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status,
const gsskrb5_ctx context_handle,
krb5_context context,