static void test_enctype(krb5_enctype enctype) { krb5_error_code ret; krb5_keyblock keyblock; krb5_enc_data input; krb5_data output; krb5_crypto_iov iov[2]; unsigned int dummy; size_t min_len, len; printf("Testing enctype %d\n", (int) enctype); x(krb5_c_encrypt_length(NULL, enctype, 0, &min_len)); x(krb5_c_make_random_key(NULL, enctype, &keyblock)); input.enctype = enctype; /* Try each length up to the minimum length. */ for (len = 0; len <= min_len; len++) { input.ciphertext.data = calloc(len, 1); input.ciphertext.length = len; output.data = calloc(len, 1); output.length = len; /* Attempt a normal decryption. */ ret = krb5_c_decrypt(NULL, &keyblock, 0, NULL, &input, &output); check_decrypt_result(ret, len, min_len); if (krb5_c_crypto_length(NULL, enctype, KRB5_CRYPTO_TYPE_HEADER, &dummy) == 0) { /* Attempt an IOV stream decryption. */ iov[0].flags = KRB5_CRYPTO_TYPE_STREAM; iov[0].data = input.ciphertext; iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data.data = NULL; iov[1].data.length = 0; ret = krb5_c_decrypt_iov(NULL, &keyblock, 0, NULL, iov, 2); check_decrypt_result(ret, len, min_len); } free(input.ciphertext.data); free(output.data); } krb5int_c_free_keyblock_contents (NULL, &keyblock); }
OM_uint32 kg_seal_iov_length(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, int *conf_state, gss_iov_buffer_desc *iov, int iov_count) { krb5_gss_ctx_id_rec *ctx; gss_iov_buffer_t header, trailer, padding; size_t data_length, assoc_data_length; size_t gss_headerlen, gss_padlen, gss_trailerlen; unsigned int k5_headerlen = 0, k5_trailerlen = 0, k5_padlen = 0; krb5_error_code code; krb5_context context; int dce_style; if (qop_req != GSS_C_QOP_DEFAULT) { *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; } header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); if (header == NULL) { *minor_status = EINVAL; return GSS_S_FAILURE; } INIT_IOV_DATA(header); trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (trailer != NULL) { INIT_IOV_DATA(trailer); } dce_style = ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0); /* For CFX, EC is used instead of padding, and is placed in header or trailer */ padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); if (padding == NULL) { if (conf_req_flag && ctx->proto == 0 && !dce_style) { *minor_status = EINVAL; return GSS_S_FAILURE; } } else { INIT_IOV_DATA(padding); } kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); if (conf_req_flag && kg_integ_only_iov(iov, iov_count)) conf_req_flag = FALSE; context = ctx->k5_context; gss_headerlen = gss_padlen = gss_trailerlen = 0; if (ctx->proto == 1) { krb5_enctype enctype; size_t ec; if (ctx->have_acceptor_subkey) enctype = ctx->acceptor_subkey->enctype; else enctype = ctx->subkey->enctype; code = krb5_c_crypto_length(context, enctype, conf_req_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, &k5_trailerlen); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (conf_req_flag) { code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } } gss_headerlen = 16; /* Header */ if (conf_req_flag) { gss_headerlen += k5_headerlen; /* Kerb-Header */ gss_trailerlen = 16 /* E(Header) */ + k5_trailerlen; /* Kerb-Trailer */ code = krb5_c_padding_length(context, enctype, data_length - assoc_data_length + 16 /* E(Header) */, &k5_padlen); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (k5_padlen == 0 && dce_style) { /* Windows rejects AEAD tokens with non-zero EC */ code = krb5_c_block_size(context, enctype, &ec); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } } else ec = k5_padlen; gss_trailerlen += ec; } else { gss_trailerlen = k5_trailerlen; /* Kerb-Checksum */ } } else if (!dce_style) { k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; if (k5_padlen == 1) gss_padlen = 1; else gss_padlen = k5_padlen - ((data_length - assoc_data_length) % k5_padlen); } data_length += gss_padlen; if (ctx->proto == 0) { /* Header | Checksum | Confounder | Data | Pad */ size_t data_size; k5_headerlen = kg_confounder_size(context, ctx->enc); data_size = 14 /* Header */ + ctx->cksum_size + k5_headerlen; if (!dce_style) data_size += data_length; gss_headerlen = g_token_size(ctx->mech_used, data_size); /* g_token_size() will include data_size as well as the overhead, so * subtract data_length just to get the overhead (ie. token size) */ if (!dce_style) gss_headerlen -= data_length; } if (minor_status != NULL) *minor_status = 0; if (trailer == NULL) gss_headerlen += gss_trailerlen; else trailer->buffer.length = gss_trailerlen; assert(gss_padlen == 0 || padding != NULL); if (padding != NULL) padding->buffer.length = gss_padlen; header->buffer.length = gss_headerlen; if (conf_state != NULL) *conf_state = conf_req_flag; return GSS_S_COMPLETE; }
/* * Split a STREAM | SIGN_DATA | DATA into * HEADER | SIGN_DATA | DATA | PADDING | TRAILER */ static OM_uint32 kg_unseal_stream_iov(OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, int *conf_state, gss_qop_t *qop_state, gss_iov_buffer_desc *iov, int iov_count, int toktype) { unsigned char *ptr; unsigned int bodysize; OM_uint32 code = 0, major_status = GSS_S_FAILURE; krb5_context context = ctx->k5_context; int conf_req_flag, toktype2; int i = 0, j; gss_iov_buffer_desc *tiov = NULL; gss_iov_buffer_t stream, data = NULL; gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer; assert(toktype == KG_TOK_WRAP_MSG); if (toktype != KG_TOK_WRAP_MSG || (ctx->gss_flags & GSS_C_DCE_STYLE)) { code = EINVAL; goto cleanup; } stream = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM); assert(stream != NULL); ptr = (unsigned char *)stream->buffer.value; code = g_verify_token_header(ctx->mech_used, &bodysize, &ptr, -1, stream->buffer.length, 0); if (code != 0) { major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } if (bodysize < 2) { *minor_status = (OM_uint32)G_BAD_TOK_HEADER; return GSS_S_DEFECTIVE_TOKEN; } toktype2 = load_16_be(ptr); ptr += 2; bodysize -= 2; tiov = (gss_iov_buffer_desc *)calloc((size_t)iov_count + 2, sizeof(gss_iov_buffer_desc)); if (tiov == NULL) { code = ENOMEM; goto cleanup; } /* HEADER */ theader = &tiov[i++]; theader->type = GSS_IOV_BUFFER_TYPE_HEADER; theader->buffer.value = stream->buffer.value; theader->buffer.length = ptr - (unsigned char *)stream->buffer.value; if (bodysize < 14 || stream->buffer.length != theader->buffer.length + bodysize) { major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } theader->buffer.length += 14; /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */ for (j = 0; j < iov_count; j++) { OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type); if (type == GSS_IOV_BUFFER_TYPE_DATA) { if (data != NULL) { /* only a single DATA buffer can appear */ code = EINVAL; goto cleanup; } data = &iov[j]; tdata = &tiov[i]; } if (type == GSS_IOV_BUFFER_TYPE_DATA || type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) tiov[i++] = iov[j]; } if (data == NULL) { /* a single DATA buffer must be present */ code = EINVAL; goto cleanup; } /* PADDING | TRAILER */ tpadding = &tiov[i++]; tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING; tpadding->buffer.length = 0; tpadding->buffer.value = NULL; ttrailer = &tiov[i++]; ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER; switch (toktype2) { case KG2_TOK_MIC_MSG: case KG2_TOK_WRAP_MSG: case KG2_TOK_DEL_CTX: { size_t ec, rrc; krb5_enctype enctype; unsigned int k5_headerlen = 0; unsigned int k5_trailerlen = 0; if (ctx->have_acceptor_subkey) enctype = ctx->acceptor_subkey->keyblock.enctype; else enctype = ctx->subkey->keyblock.enctype; conf_req_flag = ((ptr[0] & FLAG_WRAP_CONFIDENTIAL) != 0); ec = conf_req_flag ? load_16_be(ptr + 2) : 0; rrc = load_16_be(ptr + 4); if (rrc != 0) { if (!gss_krb5int_rotate_left((unsigned char *)stream->buffer.value + 16, stream->buffer.length - 16, rrc)) { code = ENOMEM; goto cleanup; } store_16_be(0, ptr + 4); /* set RRC to zero */ } if (conf_req_flag) { code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); if (code != 0) goto cleanup; theader->buffer.length += k5_headerlen; /* length validated later */ } /* no PADDING for CFX, EC is used instead */ code = krb5_c_crypto_length(context, enctype, conf_req_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, &k5_trailerlen); if (code != 0) goto cleanup; ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) + k5_trailerlen; ttrailer->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - ttrailer->buffer.length; break; } case KG_TOK_MIC_MSG: case KG_TOK_WRAP_MSG: case KG_TOK_DEL_CTX: theader->buffer.length += ctx->cksum_size + kg_confounder_size(context, ctx->enc->keyblock.enctype); /* * we can't set the padding accurately until decryption; * kg_fixup_padding_iov() will take care of this */ tpadding->buffer.length = 1; tpadding->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - 1; /* no TRAILER for pre-CFX */ ttrailer->buffer.length = 0; ttrailer->buffer.value = NULL; break; default: code = (OM_uint32)G_BAD_TOK_HEADER; major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; break; } /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/ /* Old: GSS-Header | Conf | Data | Pad | */ /* CFX: GSS-Header | Kerb-Header | Data | | EC | E(Header) | Kerb-Trailer */ /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/ /* validate lengths */ if (stream->buffer.length < theader->buffer.length + tpadding->buffer.length + ttrailer->buffer.length) { code = (OM_uint32)KRB5_BAD_MSIZE; major_status = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } /* setup data */ tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length - tpadding->buffer.length - theader->buffer.length; assert(data != NULL); if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { code = kg_allocate_iov(tdata, tdata->buffer.length); if (code != 0) goto cleanup; memcpy(tdata->buffer.value, (unsigned char *)stream->buffer.value + theader->buffer.length, tdata->buffer.length); } else tdata->buffer.value = (unsigned char *)stream->buffer.value + theader->buffer.length; assert(i <= iov_count + 2); major_status = kg_unseal_iov_token(&code, ctx, conf_state, qop_state, tiov, i, toktype); if (major_status == GSS_S_COMPLETE) *data = *tdata; else kg_release_iov(tdata, 1); cleanup: if (tiov != NULL) free(tiov); *minor_status = code; return major_status; }
int main () { krb5_context context = 0; krb5_data in, in2, out, out2, check, check2, state, signdata; krb5_crypto_iov iov[5]; int i, j, pos; unsigned int dummy; size_t len; krb5_enc_data enc_out, enc_out2; krb5_keyblock *keyblock; krb5_key key; memset(iov, 0, sizeof(iov)); in.data = "This is a test.\n"; in.length = strlen (in.data); in2.data = "This is another test.\n"; in2.length = strlen (in2.data); test ("Seeding random number generator", krb5_c_random_seed (context, &in)); /* Set up output buffers. */ out.data = malloc(2048); out2.data = malloc(2048); check.data = malloc(2048); check2.data = malloc(2048); if (out.data == NULL || out2.data == NULL || check.data == NULL || check2.data == NULL) abort(); out.magic = KV5M_DATA; out.length = 2048; out2.magic = KV5M_DATA; out2.length = 2048; check.length = 2048; check2.length = 2048; for (i = 0; interesting_enctypes[i]; i++) { krb5_enctype enctype = interesting_enctypes [i]; printf ("Testing enctype %d\n", enctype); test ("Initializing a keyblock", krb5_init_keyblock (context, enctype, 0, &keyblock)); test ("Generating random keyblock", krb5_c_make_random_key (context, enctype, keyblock)); test ("Creating opaque key from keyblock", krb5_k_create_key (context, keyblock, &key)); enc_out.ciphertext = out; enc_out2.ciphertext = out2; /* We use an intermediate `len' because size_t may be different size than `int' */ krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len); enc_out.ciphertext.length = len; /* Encrypt, decrypt, and see if we got the plaintext back again. */ test ("Encrypting (c)", krb5_c_encrypt (context, keyblock, 7, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting", krb5_c_decrypt (context, keyblock, 7, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); /* Try again with the opaque-key-using variants. */ memset(out.data, 0, out.length); test ("Encrypting (k)", krb5_k_encrypt (context, key, 7, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting", krb5_k_decrypt (context, key, 7, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); /* Check if this enctype supports IOV encryption. */ if ( krb5_c_crypto_length(context, keyblock->enctype, KRB5_CRYPTO_TYPE_HEADER, &dummy) == 0 ){ /* Set up iovecs for stream decryption. */ memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length); iov[0].flags= KRB5_CRYPTO_TYPE_STREAM; iov[0].data.data = out2.data; iov[0].data.length = enc_out.ciphertext.length; iov[1].flags = KRB5_CRYPTO_TYPE_DATA; /* Decrypt the encrypted data from above and check it. */ test("IOV stream decrypting (c)", krb5_c_decrypt_iov( context, keyblock, 7, 0, iov, 2)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Try again with the opaque-key-using variant. */ memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length); test("IOV stream decrypting (k)", krb5_k_decrypt_iov( context, key, 7, 0, iov, 2)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Set up iovecs for AEAD encryption. */ signdata.magic = KV5M_DATA; signdata.data = (char *) "This should be signed"; signdata.length = strlen(signdata.data); iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = in; /*We'll need to copy memory before encrypt*/ iov[2].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; iov[2].data = signdata; iov[3].flags = KRB5_CRYPTO_TYPE_PADDING; iov[4].flags = KRB5_CRYPTO_TYPE_TRAILER; /* "Allocate" data for the iovec buffers from the "out" buffer. */ test("Setting up iov lengths", krb5_c_crypto_length_iov(context, keyblock->enctype, iov, 5)); for (j=0,pos=0; j <= 4; j++ ){ if (iov[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) continue; iov[j].data.data = &out.data[pos]; pos += iov[j].data.length; } assert (iov[1].data.length == in.length); memcpy(iov[1].data.data, in.data, in.length); /* Encrypt and decrypt in place, and check the result. */ test("iov encrypting (c)", krb5_c_encrypt_iov(context, keyblock, 7, 0, iov, 5)); assert(iov[1].data.length == in.length); display("Header", &iov[0].data); display("Data", &iov[1].data); display("Padding", &iov[3].data); display("Trailer", &iov[4].data); test("iov decrypting", krb5_c_decrypt_iov(context, keyblock, 7, 0, iov, 5)); test("Comparing results", compare_results(&in, &iov[1].data)); /* Try again with opaque-key-using variants. */ test("iov encrypting (k)", krb5_k_encrypt_iov(context, key, 7, 0, iov, 5)); assert(iov[1].data.length == in.length); display("Header", &iov[0].data); display("Data", &iov[1].data); display("Padding", &iov[3].data); display("Trailer", &iov[4].data); test("iov decrypting", krb5_k_decrypt_iov(context, key, 7, 0, iov, 5)); test("Comparing results", compare_results(&in, &iov[1].data)); } enc_out.ciphertext.length = out.length; check.length = 2048; test ("init_state", krb5_c_init_state (context, keyblock, 7, &state)); test ("Encrypting with state", krb5_c_encrypt (context, keyblock, 7, &state, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Encrypting again with state", krb5_c_encrypt (context, keyblock, 7, &state, &in2, &enc_out2)); display ("Enc output", &enc_out2.ciphertext); test ("free_state", krb5_c_free_state (context, keyblock, &state)); test ("init_state", krb5_c_init_state (context, keyblock, 7, &state)); test ("Decrypting with state", krb5_c_decrypt (context, keyblock, 7, &state, &enc_out, &check)); test ("Decrypting again with state", krb5_c_decrypt (context, keyblock, 7, &state, &enc_out2, &check2)); test ("free_state", krb5_c_free_state (context, keyblock, &state)); test ("Comparing", compare_results (&in, &check)); test ("Comparing", compare_results (&in2, &check2)); krb5_free_keyblock (context, keyblock); krb5_k_free_key (context, key); } /* Test the RC4 decrypt fallback from key usage 9 to 8. */ test ("Initializing an RC4 keyblock", krb5_init_keyblock (context, ENCTYPE_ARCFOUR_HMAC, 0, &keyblock)); test ("Generating random RC4 key", krb5_c_make_random_key (context, ENCTYPE_ARCFOUR_HMAC, keyblock)); enc_out.ciphertext = out; krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len); enc_out.ciphertext.length = len; check.length = 2048; test ("Encrypting with RC4 key usage 8", krb5_c_encrypt (context, keyblock, 8, 0, &in, &enc_out)); display ("Enc output", &enc_out.ciphertext); test ("Decrypting with RC4 key usage 9", krb5_c_decrypt (context, keyblock, 9, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); krb5_free_keyblock (context, keyblock); free(out.data); free(out2.data); free(check.data); free(check2.data); return 0; }
/* * DCE_STYLE indicates actual RRC is EC + RRC * EC is extra rotate count for DCE_STYLE, pad length otherwise * RRC is rotate count. */ static krb5_error_code kg_translate_iov_v3(krb5_context context, int dce_style, size_t ec, size_t rrc, krb5_enctype enctype, gss_iov_buffer_desc *iov, int iov_count, krb5_crypto_iov **pkiov, size_t *pkiov_count) { gss_iov_buffer_t header; gss_iov_buffer_t trailer; int i = 0, j; size_t kiov_count; krb5_crypto_iov *kiov; unsigned int k5_headerlen = 0, k5_trailerlen = 0; size_t gss_headerlen, gss_trailerlen; krb5_error_code code; *pkiov = NULL; *pkiov_count = 0; header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); assert(header != NULL); trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); assert(trailer == NULL || rrc == 0); code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); if (code != 0) return code; code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen); if (code != 0) return code; /* Check header and trailer sizes */ gss_headerlen = 16 /* GSS-Header */ + k5_headerlen; /* Kerb-Header */ gss_trailerlen = ec + 16 /* E(GSS-Header) */ + k5_trailerlen; /* Kerb-Trailer */ /* If we're caller without a trailer, we must rotate by trailer length */ if (trailer == NULL) { size_t actual_rrc = rrc; if (dce_style) actual_rrc += ec; /* compensate for Windows bug */ if (actual_rrc != gss_trailerlen) return KRB5_BAD_MSIZE; gss_headerlen += gss_trailerlen; gss_trailerlen = 0; } else { if (trailer->buffer.length != gss_trailerlen) return KRB5_BAD_MSIZE; } if (header->buffer.length != gss_headerlen) return KRB5_BAD_MSIZE; kiov_count = 3 + iov_count; kiov = (krb5_crypto_iov *)malloc(kiov_count * sizeof(krb5_crypto_iov)); if (kiov == NULL) return ENOMEM; /* * The krb5 header is located at the end of the GSS header. */ kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER; kiov[i].data.length = k5_headerlen; kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - k5_headerlen; i++; for (j = 0; j < iov_count; j++) { kiov[i].flags = kg_translate_flag_iov(iov[j].type); if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY) continue; kiov[i].data.length = iov[j].buffer.length; kiov[i].data.data = (char *)iov[j].buffer.value; i++; } /* * The EC and encrypted GSS header are placed in the trailer, which may * be rotated directly after the plaintext header if no trailer buffer * is provided. */ kiov[i].flags = KRB5_CRYPTO_TYPE_DATA; kiov[i].data.length = ec + 16; /* E(Header) */ if (trailer == NULL) kiov[i].data.data = (char *)header->buffer.value + 16; else kiov[i].data.data = (char *)trailer->buffer.value; i++; /* * The krb5 trailer is placed after the encrypted copy of the * krb5 header (which may be in the GSS header or trailer). */ kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; kiov[i].data.length = k5_trailerlen; kiov[i].data.data = kiov[i - 1].data.data + ec + 16; /* E(Header) */ i++; *pkiov = kiov; *pkiov_count = i; return 0; }
krb5_error_code gss_krb5int_make_seal_token_v3_iov(krb5_context context, krb5_gss_ctx_id_rec *ctx, int conf_req_flag, int *conf_state, gss_iov_buffer_desc *iov, int iov_count, int toktype) { krb5_error_code code = 0; gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; unsigned char acceptor_flag; unsigned short tok_id; unsigned char *outbuf = NULL; unsigned char *tbuf = NULL; int key_usage; size_t rrc = 0; unsigned int gss_headerlen, gss_trailerlen; krb5_key key; krb5_cksumtype cksumtype; size_t data_length, assoc_data_length; assert(ctx->big_endian == 0); assert(ctx->proto == 1); 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; cksumtype = ctx->acceptor_subkey_cksumtype; } else { key = ctx->subkey; cksumtype = ctx->cksumtype; } assert(key != NULL); assert(cksumtype != 0); kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); if (header == NULL) return EINVAL; padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); if (padding != NULL) padding->buffer.length = 0; trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { unsigned int k5_headerlen, k5_trailerlen, k5_padlen; size_t ec = 0; size_t conf_data_length = data_length - assoc_data_length; code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); if (code != 0) goto cleanup; code = krb5_c_padding_length(context, key->keyblock.enctype, conf_data_length + 16 /* E(Header) */, &k5_padlen); if (code != 0) goto cleanup; if (k5_padlen == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) { /* Windows rejects AEAD tokens with non-zero EC */ code = krb5_c_block_size(context, key->keyblock.enctype, &ec); if (code != 0) goto cleanup; } else ec = k5_padlen; code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen); if (code != 0) goto cleanup; gss_headerlen = 16 /* Header */ + k5_headerlen; gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen; if (trailer == NULL) { rrc = gss_trailerlen; /* Workaround for Windows bug where it rotates by EC + RRC */ if (ctx->gss_flags & GSS_C_DCE_STYLE) rrc -= ec; gss_headerlen += gss_trailerlen; } if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { code = kg_allocate_iov(header, (size_t) gss_headerlen); } else if (header->buffer.length < gss_headerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; outbuf = (unsigned char *)header->buffer.value; header->buffer.length = (size_t) gss_headerlen; if (trailer != NULL) { if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); else if (trailer->buffer.length < gss_trailerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; trailer->buffer.length = (size_t) gss_trailerlen; } /* TOK_ID */ store_16_be(KG2_TOK_WRAP_MSG, outbuf); /* flags */ outbuf[2] = (acceptor_flag | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); /* filler */ outbuf[3] = 0xFF; /* EC */ store_16_be(ec, outbuf + 4); /* RRC */ store_16_be(0, outbuf + 6); store_64_be(ctx->seq_send, outbuf + 8); /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */ if (trailer == NULL) tbuf = (unsigned char *)header->buffer.value + 16; /* Header */ else tbuf = (unsigned char *)trailer->buffer.value; memset(tbuf, 0xFF, ec); memcpy(tbuf + ec, header->buffer.value, 16); code = kg_encrypt_iov(context, ctx->proto, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), ec, rrc, key, key_usage, 0, iov, iov_count); if (code != 0) goto cleanup; /* RRC */ store_16_be(rrc, outbuf + 6); ctx->seq_send++; } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { tok_id = KG2_TOK_WRAP_MSG; wrap_with_checksum: gss_headerlen = 16; code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_CHECKSUM, &gss_trailerlen); if (code != 0) goto cleanup; assert(gss_trailerlen <= 0xFFFF); if (trailer == NULL) { rrc = gss_trailerlen; gss_headerlen += gss_trailerlen; } if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(header, (size_t) gss_headerlen); else if (header->buffer.length < gss_headerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; outbuf = (unsigned char *)header->buffer.value; header->buffer.length = (size_t) gss_headerlen; if (trailer != NULL) { if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); else if (trailer->buffer.length < gss_trailerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; trailer->buffer.length = (size_t) gss_trailerlen; } /* TOK_ID */ store_16_be(tok_id, outbuf); /* flags */ outbuf[2] = (acceptor_flag | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); /* filler */ outbuf[3] = 0xFF; if (toktype == KG_TOK_WRAP_MSG) { /* Use 0 for checksum calculation, substitute * checksum length later. */ /* EC */ store_16_be(0, outbuf + 4); /* RRC */ store_16_be(0, outbuf + 6); } else { /* MIC and DEL store 0xFF in EC and RRC */ store_16_be(0xFFFF, outbuf + 4); store_16_be(0xFFFF, outbuf + 6); } store_64_be(ctx->seq_send, outbuf + 8); code = kg_make_checksum_iov_v3(context, cksumtype, rrc, key, key_usage, iov, iov_count); if (code != 0) goto cleanup; ctx->seq_send++; if (toktype == KG_TOK_WRAP_MSG) { /* Fix up EC field */ store_16_be(gss_trailerlen, outbuf + 4); /* Fix up RRC field */ store_16_be(rrc, outbuf + 6); } } else if (toktype == KG_TOK_MIC_MSG) { tok_id = KG2_TOK_MIC_MSG; trailer = NULL; goto wrap_with_checksum; } else if (toktype == KG_TOK_DEL_CTX) { tok_id = KG2_TOK_DEL_CTX; goto wrap_with_checksum; } else { abort(); } code = 0; if (conf_state != NULL) *conf_state = conf_req_flag; cleanup: if (code != 0) kg_release_iov(iov, iov_count); return code; }
OM_uint32 gss_krb5int_unseal_v3_iov(krb5_context context, OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, gss_iov_buffer_desc *iov, int iov_count, int *conf_state, gss_qop_t *qop_state, int toktype) { OM_uint32 code; gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; unsigned char acceptor_flag; unsigned char *ptr = NULL; int key_usage; size_t rrc, ec; size_t data_length, assoc_data_length; krb5_key key; gssint_uint64 seqnum; krb5_boolean valid; krb5_cksumtype cksumtype; int conf_flag = 0; if (ctx->big_endian != 0) return GSS_S_DEFECTIVE_TOKEN; if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); assert(header != NULL); padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); if (padding != NULL && padding->buffer.length != 0) return GSS_S_DEFECTIVE_TOKEN; trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); 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)); kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); ptr = (unsigned char *)header->buffer.value; if (header->buffer.length < 16) { *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; cksumtype = ctx->acceptor_subkey_cksumtype; } else { key = ctx->subkey; cksumtype = ctx->cksumtype; } assert(key != NULL); if (toktype == KG_TOK_WRAP_MSG) { unsigned int k5_trailerlen; if (load_16_be(ptr) != KG2_TOK_WRAP_MSG) goto defective; conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0); if (ptr[3] != 0xFF) goto defective; ec = load_16_be(ptr + 4); rrc = load_16_be(ptr + 6); seqnum = load_64_be(ptr + 8); code = krb5_c_crypto_length(context, key->keyblock.enctype, conf_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, &k5_trailerlen); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } /* Deal with RRC */ if (trailer == NULL) { size_t desired_rrc = k5_trailerlen; if (conf_flag) { desired_rrc += 16; /* E(Header) */ if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) desired_rrc += ec; } /* According to MS, we only need to deal with a fixed RRC for DCE */ if (rrc != desired_rrc) goto defective; } else if (rrc != 0) { /* Should have been rotated by kg_unseal_stream_iov() */ goto defective; } if (conf_flag) { unsigned char *althdr; /* Decrypt */ code = kg_decrypt_iov(context, ctx->proto, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), ec, rrc, key, key_usage, 0, iov, iov_count); if (code != 0) { *minor_status = code; return GSS_S_BAD_SIG; } /* Validate header integrity */ if (trailer == NULL) althdr = (unsigned char *)header->buffer.value + 16 + ec; else althdr = (unsigned char *)trailer->buffer.value + ec; if (load_16_be(althdr) != KG2_TOK_WRAP_MSG || althdr[2] != ptr[2] || althdr[3] != ptr[3] || memcmp(althdr + 8, ptr + 8, 8) != 0) { *minor_status = 0; return GSS_S_BAD_SIG; } } else { /* Verify checksum: note EC is checksum size here, not padding */ if (ec != k5_trailerlen) goto defective; /* Zero EC, RRC before computing checksum */ store_16_be(0, ptr + 4); store_16_be(0, ptr + 6); code = kg_verify_checksum_iov_v3(context, cksumtype, rrc, key, key_usage, iov, iov_count, &valid); if (code != 0 || valid == FALSE) { *minor_status = code; return GSS_S_BAD_SIG; } } code = g_order_check(&ctx->seqstate, seqnum); } else if (toktype == KG_TOK_MIC_MSG) { if (load_16_be(ptr) != KG2_TOK_MIC_MSG) goto defective; verify_mic_1: if (ptr[3] != 0xFF) goto defective; seqnum = load_64_be(ptr + 8); code = kg_verify_checksum_iov_v3(context, cksumtype, 0, key, key_usage, iov, iov_count, &valid); if (code != 0 || valid == FALSE) { *minor_status = code; return GSS_S_BAD_SIG; } code = g_order_check(&ctx->seqstate, seqnum); } else if (toktype == KG_TOK_DEL_CTX) { if (load_16_be(ptr) != KG2_TOK_DEL_CTX) goto defective; goto verify_mic_1; } else { goto defective; } *minor_status = 0; if (conf_state != NULL) *conf_state = conf_flag; return code; defective: *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; }