1 /* -*- mode: c; indent-tabs-mode: nil -*- */
3 * lib/gssapi/krb5/k5sealv3iov.c
5 * Copyright 2008 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
31 #include "k5-platform.h" /* for 64-bit support */
32 #include "k5-int.h" /* for zap() */
33 #include "gssapiP_krb5.h"
37 gss_krb5int_make_seal_token_v3_iov(krb5_context context,
38 krb5_gss_ctx_id_rec *ctx,
41 gss_iov_buffer_desc *iov,
45 krb5_error_code code = 0;
46 gss_iov_buffer_t header;
47 gss_iov_buffer_t padding;
48 gss_iov_buffer_t trailer;
49 unsigned char acceptor_flag;
50 unsigned short tok_id;
51 unsigned char *outbuf = NULL;
52 unsigned char *tbuf = NULL;
55 size_t gss_headerlen, gss_trailerlen;
57 krb5_cksumtype cksumtype;
58 size_t data_length, assoc_data_length;
60 assert(ctx->big_endian == 0);
61 assert(ctx->proto == 1);
63 acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
64 key_usage = (toktype == KG_TOK_WRAP_MSG
66 ? KG_USAGE_INITIATOR_SEAL
67 : KG_USAGE_ACCEPTOR_SEAL)
69 ? KG_USAGE_INITIATOR_SIGN
70 : KG_USAGE_ACCEPTOR_SIGN));
71 if (ctx->have_acceptor_subkey) {
72 key = ctx->acceptor_subkey;
73 cksumtype = ctx->acceptor_subkey_cksumtype;
76 cksumtype = ctx->cksumtype;
79 assert(cksumtype != 0);
81 kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
83 header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
87 padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
89 padding->buffer.length = 0;
91 trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
93 outbuf = (unsigned char *)header->buffer.value;
95 if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
96 unsigned int k5_headerlen, k5_trailerlen, k5_padlen;
98 size_t conf_data_length = data_length - assoc_data_length;
100 code = krb5_c_crypto_length(context, key->enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
104 code = krb5_c_padding_length(context, key->enctype,
105 conf_data_length + 16 /* E(Header) */, &k5_padlen);
109 if (k5_padlen == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
110 /* Windows rejects AEAD tokens with non-zero EC */
111 code = krb5_c_block_size(context, key->enctype, &ec);
117 code = krb5_c_crypto_length(context, key->enctype, KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen);
121 gss_headerlen = 16 /* Header */ + k5_headerlen;
122 gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen;
124 if (trailer == NULL) {
125 rrc = gss_trailerlen;
126 /* Workaround for Windows bug where it rotates by EC + RRC */
127 if (ctx->gss_flags & GSS_C_DCE_STYLE)
129 gss_headerlen += gss_trailerlen;
132 if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
133 code = kg_allocate_iov(header, gss_headerlen);
134 else if (header->buffer.length < gss_headerlen)
135 code = KRB5_BAD_MSIZE;
138 header->buffer.length = gss_headerlen;
140 if (trailer != NULL) {
141 if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
142 code = kg_allocate_iov(trailer, gss_trailerlen);
143 else if (trailer->buffer.length < gss_trailerlen)
144 code = KRB5_BAD_MSIZE;
147 trailer->buffer.length = gss_trailerlen;
151 store_16_be(KG2_TOK_WRAP_MSG, outbuf);
153 outbuf[2] = (acceptor_flag
154 | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
155 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
159 store_16_be(ec, outbuf + 4);
161 store_16_be(0, outbuf + 6);
162 store_64_be(ctx->seq_send, outbuf + 8);
164 /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */
166 tbuf = (unsigned char *)header->buffer.value + 16; /* Header */
168 tbuf = (unsigned char *)trailer->buffer.value;
170 memset(tbuf, 0xFF, ec);
171 memcpy(tbuf + ec, header->buffer.value, 16);
173 code = kg_encrypt_iov(context, ctx->proto,
174 ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
175 ec, rrc, key, key_usage, 0, iov, iov_count);
180 store_16_be(rrc, outbuf + 6);
183 } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
184 tok_id = KG2_TOK_WRAP_MSG;
190 code = krb5_c_crypto_length(context, key->enctype, KRB5_CRYPTO_TYPE_CHECKSUM, &gss_trailerlen);
194 assert(gss_trailerlen <= 0xFFFF);
196 if (trailer == NULL) {
197 rrc = gss_trailerlen;
198 gss_headerlen += gss_trailerlen;
201 if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
202 code = kg_allocate_iov(header, gss_headerlen);
203 else if (header->buffer.length < gss_headerlen)
204 code = KRB5_BAD_MSIZE;
207 header->buffer.length = gss_headerlen;
209 if (trailer != NULL) {
210 if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
211 code = kg_allocate_iov(trailer, gss_trailerlen);
212 else if (trailer->buffer.length < gss_trailerlen)
213 code = KRB5_BAD_MSIZE;
216 trailer->buffer.length = gss_trailerlen;
220 store_16_be(tok_id, outbuf);
222 outbuf[2] = (acceptor_flag
223 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
226 if (toktype == KG_TOK_WRAP_MSG) {
227 /* Use 0 for checksum calculation, substitute
228 * checksum length later.
231 store_16_be(0, outbuf + 4);
233 store_16_be(0, outbuf + 6);
235 /* MIC and DEL store 0xFF in EC and RRC */
236 store_16_be(0xFFFF, outbuf + 4);
237 store_16_be(0xFFFF, outbuf + 6);
239 store_64_be(ctx->seq_send, outbuf + 8);
241 code = kg_make_checksum_iov_v3(context, cksumtype,
249 if (toktype == KG_TOK_WRAP_MSG) {
250 /* Fix up EC field */
251 store_16_be(gss_trailerlen, outbuf + 4);
252 /* Fix up RRC field */
253 store_16_be(rrc, outbuf + 6);
255 } else if (toktype == KG_TOK_MIC_MSG) {
256 tok_id = KG2_TOK_MIC_MSG;
258 goto wrap_with_checksum;
259 } else if (toktype == KG_TOK_DEL_CTX) {
260 tok_id = KG2_TOK_DEL_CTX;
261 goto wrap_with_checksum;
270 kg_release_iov(iov, iov_count);
276 gss_krb5int_unseal_v3_iov(krb5_context context,
277 OM_uint32 *minor_status,
278 krb5_gss_ctx_id_rec *ctx,
279 gss_iov_buffer_desc *iov,
282 gss_qop_t *qop_state,
286 gss_iov_buffer_t header;
287 gss_iov_buffer_t padding;
288 gss_iov_buffer_t trailer;
289 unsigned char acceptor_flag;
290 unsigned char *ptr = NULL;
293 size_t data_length, assoc_data_length;
295 gssint_uint64 seqnum;
297 krb5_cksumtype cksumtype;
300 assert(ctx->big_endian == 0);
301 assert(ctx->proto == 1);
303 if (qop_state != NULL)
304 *qop_state = GSS_C_QOP_DEFAULT;
306 header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
307 assert(header != NULL);
309 padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
310 if (padding != NULL && padding->buffer.length != 0)
311 return GSS_S_DEFECTIVE_TOKEN;
313 trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
315 acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
316 key_usage = (toktype == KG_TOK_WRAP_MSG
318 ? KG_USAGE_INITIATOR_SEAL
319 : KG_USAGE_ACCEPTOR_SEAL)
321 ? KG_USAGE_INITIATOR_SIGN
322 : KG_USAGE_ACCEPTOR_SIGN));
324 kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
326 ptr = (unsigned char *)header->buffer.value;
328 if (header->buffer.length < 16) {
330 return GSS_S_DEFECTIVE_TOKEN;
333 if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
334 *minor_status = (OM_uint32)G_BAD_DIRECTION;
335 return GSS_S_BAD_SIG;
338 if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
339 key = ctx->acceptor_subkey;
340 cksumtype = ctx->acceptor_subkey_cksumtype;
343 cksumtype = ctx->cksumtype;
348 if (toktype == KG_TOK_WRAP_MSG) {
349 unsigned int k5_trailerlen;
351 if (load_16_be(ptr) != KG2_TOK_WRAP_MSG)
353 conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0);
356 ec = load_16_be(ptr + 4);
357 rrc = load_16_be(ptr + 6);
358 seqnum = load_64_be(ptr + 8);
360 code = krb5_c_crypto_length(context, key->enctype,
361 conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
362 KRB5_CRYPTO_TYPE_CHECKSUM,
365 *minor_status = code;
366 return GSS_S_FAILURE;
370 if (trailer == NULL) {
371 size_t desired_rrc = k5_trailerlen;
374 desired_rrc += 16; /* E(Header) */
376 if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0)
380 /* According to MS, we only need to deal with a fixed RRC for DCE */
381 if (rrc != desired_rrc)
383 } else if (rrc != 0) {
384 /* Should have been rotated by kg_unseal_stream_iov() */
389 unsigned char *althdr;
392 code = kg_decrypt_iov(context, ctx->proto,
393 ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
395 key, key_usage, 0, iov, iov_count);
397 *minor_status = code;
398 return GSS_S_BAD_SIG;
401 /* Validate header integrity */
403 althdr = (unsigned char *)header->buffer.value + 16 + ec;
405 althdr = (unsigned char *)trailer->buffer.value + ec;
407 if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
408 || althdr[2] != ptr[2]
409 || althdr[3] != ptr[3]
410 || memcmp(althdr + 8, ptr + 8, 8) != 0) {
412 return GSS_S_BAD_SIG;
415 /* Verify checksum: note EC is checksum size here, not padding */
416 if (ec != k5_trailerlen)
419 /* Zero EC, RRC before computing checksum */
420 store_16_be(0, ptr + 4);
421 store_16_be(0, ptr + 6);
423 code = kg_verify_checksum_iov_v3(context, cksumtype, rrc,
425 iov, iov_count, &valid);
426 if (code != 0 || valid == FALSE) {
427 *minor_status = code;
428 return GSS_S_BAD_SIG;
432 code = g_order_check(&ctx->seqstate, seqnum);
433 } else if (toktype == KG_TOK_MIC_MSG) {
434 if (load_16_be(ptr) != KG2_TOK_MIC_MSG)
440 seqnum = load_64_be(ptr + 8);
442 code = kg_verify_checksum_iov_v3(context, cksumtype, 0,
444 iov, iov_count, &valid);
445 if (code != 0 || valid == FALSE) {
446 *minor_status = code;
447 return GSS_S_BAD_SIG;
449 code = g_order_check(&ctx->seqstate, seqnum);
450 } else if (toktype == KG_TOK_DEL_CTX) {
451 if (load_16_be(ptr) != KG2_TOK_DEL_CTX)
460 if (conf_state != NULL)
461 *conf_state = conf_flag;
468 return GSS_S_DEFECTIVE_TOKEN;