1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/k5sealv3iov.c */
3 /*
4 * Copyright 2008 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 #include "k5-int.h"
28 #include "gssapiP_krb5.h"
29
30 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)31 gss_krb5int_make_seal_token_v3_iov(krb5_context context,
32 krb5_gss_ctx_id_rec *ctx,
33 int conf_req_flag,
34 int *conf_state,
35 gss_iov_buffer_desc *iov,
36 int iov_count,
37 int toktype)
38 {
39 krb5_error_code code = 0;
40 gss_iov_buffer_t header;
41 gss_iov_buffer_t padding;
42 gss_iov_buffer_t trailer;
43 unsigned char acceptor_flag;
44 unsigned short tok_id;
45 unsigned char *outbuf = NULL;
46 unsigned char *tbuf = NULL;
47 int key_usage;
48 size_t rrc = 0;
49 unsigned int gss_headerlen, gss_trailerlen;
50 krb5_key key;
51 krb5_cksumtype cksumtype;
52 size_t data_length, assoc_data_length;
53
54 assert(ctx->proto == 1);
55
56 acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
57 key_usage = (toktype == KG_TOK_WRAP_MSG
58 ? (ctx->initiate
59 ? KG_USAGE_INITIATOR_SEAL
60 : KG_USAGE_ACCEPTOR_SEAL)
61 : (ctx->initiate
62 ? KG_USAGE_INITIATOR_SIGN
63 : KG_USAGE_ACCEPTOR_SIGN));
64 if (ctx->have_acceptor_subkey) {
65 key = ctx->acceptor_subkey;
66 cksumtype = ctx->acceptor_subkey_cksumtype;
67 } else {
68 key = ctx->subkey;
69 cksumtype = ctx->cksumtype;
70 }
71 assert(key != NULL);
72 assert(cksumtype != 0);
73
74 kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
75
76 header = kg_locate_header_iov(iov, iov_count, toktype);
77 if (header == NULL)
78 return EINVAL;
79
80 padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
81 if (padding != NULL)
82 padding->buffer.length = 0;
83
84 trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
85
86 if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
87 unsigned int k5_headerlen, k5_trailerlen, k5_padlen;
88 size_t ec = 0;
89 size_t conf_data_length = data_length - assoc_data_length;
90
91 code = krb5_c_crypto_length(context, key->keyblock.enctype,
92 KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
93 if (code != 0)
94 goto cleanup;
95
96 code = krb5_c_padding_length(context, key->keyblock.enctype,
97 conf_data_length + 16 /* E(Header) */, &k5_padlen);
98 if (code != 0)
99 goto cleanup;
100
101 if (k5_padlen == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
102 /* Windows rejects AEAD tokens with non-zero EC */
103 code = krb5_c_block_size(context, key->keyblock.enctype, &ec);
104 if (code != 0)
105 goto cleanup;
106 } else
107 ec = k5_padlen;
108
109 code = krb5_c_crypto_length(context, key->keyblock.enctype,
110 KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen);
111 if (code != 0)
112 goto cleanup;
113
114 gss_headerlen = 16 /* Header */ + k5_headerlen;
115 gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen;
116
117 if (trailer == NULL) {
118 rrc = gss_trailerlen;
119 /* Workaround for Windows bug where it rotates by EC + RRC */
120 if (ctx->gss_flags & GSS_C_DCE_STYLE)
121 rrc -= ec;
122 gss_headerlen += gss_trailerlen;
123 }
124
125 if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
126 code = kg_allocate_iov(header, (size_t) gss_headerlen);
127 } else if (header->buffer.length < gss_headerlen)
128 code = KRB5_BAD_MSIZE;
129 if (code != 0)
130 goto cleanup;
131 outbuf = (unsigned char *)header->buffer.value;
132 header->buffer.length = (size_t) gss_headerlen;
133
134 if (trailer != NULL) {
135 if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
136 code = kg_allocate_iov(trailer, (size_t) gss_trailerlen);
137 else if (trailer->buffer.length < gss_trailerlen)
138 code = KRB5_BAD_MSIZE;
139 if (code != 0)
140 goto cleanup;
141 trailer->buffer.length = (size_t) gss_trailerlen;
142 }
143
144 /* TOK_ID */
145 store_16_be(KG2_TOK_WRAP_MSG, outbuf);
146 /* flags */
147 outbuf[2] = (acceptor_flag | FLAG_WRAP_CONFIDENTIAL |
148 (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
149 /* filler */
150 outbuf[3] = 0xFF;
151 /* EC */
152 store_16_be(ec, outbuf + 4);
153 /* RRC */
154 store_16_be(0, outbuf + 6);
155 store_64_be(ctx->seq_send, outbuf + 8);
156
157 /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */
158 if (trailer == NULL)
159 tbuf = (unsigned char *)header->buffer.value + 16; /* Header */
160 else
161 tbuf = (unsigned char *)trailer->buffer.value;
162
163 memset(tbuf, 0xFF, ec);
164 memcpy(tbuf + ec, header->buffer.value, 16);
165
166 code = kg_encrypt_iov(context, ctx->proto,
167 ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
168 ec, rrc, key, key_usage, 0, iov, iov_count);
169 if (code != 0)
170 goto cleanup;
171
172 /* RRC */
173 store_16_be(rrc, outbuf + 6);
174
175 ctx->seq_send++;
176 } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
177 tok_id = KG2_TOK_WRAP_MSG;
178
179 wrap_with_checksum:
180
181 gss_headerlen = 16;
182
183 code = krb5_c_crypto_length(context, key->keyblock.enctype,
184 KRB5_CRYPTO_TYPE_CHECKSUM,
185 &gss_trailerlen);
186 if (code != 0)
187 goto cleanup;
188
189 assert(gss_trailerlen <= 0xFFFF);
190
191 if (trailer == NULL) {
192 rrc = gss_trailerlen;
193 gss_headerlen += gss_trailerlen;
194 }
195
196 if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
197 code = kg_allocate_iov(header, (size_t) gss_headerlen);
198 else if (header->buffer.length < gss_headerlen)
199 code = KRB5_BAD_MSIZE;
200 if (code != 0)
201 goto cleanup;
202 outbuf = (unsigned char *)header->buffer.value;
203 header->buffer.length = (size_t) gss_headerlen;
204
205 if (trailer != NULL) {
206 if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
207 code = kg_allocate_iov(trailer, (size_t) gss_trailerlen);
208 else if (trailer->buffer.length < gss_trailerlen)
209 code = KRB5_BAD_MSIZE;
210 if (code != 0)
211 goto cleanup;
212 trailer->buffer.length = (size_t) gss_trailerlen;
213 }
214
215 /* TOK_ID */
216 store_16_be(tok_id, outbuf);
217 /* flags */
218 outbuf[2] = (acceptor_flag
219 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
220 /* filler */
221 outbuf[3] = 0xFF;
222 if (toktype == KG_TOK_WRAP_MSG) {
223 /* Use 0 for checksum calculation, substitute
224 * checksum length later.
225 */
226 /* EC */
227 store_16_be(0, outbuf + 4);
228 /* RRC */
229 store_16_be(0, outbuf + 6);
230 } else {
231 /* MIC and DEL store 0xFF in EC and RRC */
232 store_16_be(0xFFFF, outbuf + 4);
233 store_16_be(0xFFFF, outbuf + 6);
234 }
235 store_64_be(ctx->seq_send, outbuf + 8);
236
237 code = kg_make_checksum_iov_v3(context, cksumtype,
238 rrc, key, key_usage,
239 iov, iov_count, toktype);
240 if (code != 0)
241 goto cleanup;
242
243 ctx->seq_send++;
244
245 if (toktype == KG_TOK_WRAP_MSG) {
246 /* Fix up EC field */
247 store_16_be(gss_trailerlen, outbuf + 4);
248 /* Fix up RRC field */
249 store_16_be(rrc, outbuf + 6);
250 }
251 } else if (toktype == KG_TOK_MIC_MSG) {
252 tok_id = KG2_TOK_MIC_MSG;
253 trailer = NULL;
254 goto wrap_with_checksum;
255 } else if (toktype == KG_TOK_DEL_CTX) {
256 tok_id = KG2_TOK_DEL_CTX;
257 goto wrap_with_checksum;
258 } else {
259 abort();
260 }
261
262 code = 0;
263 if (conf_state != NULL)
264 *conf_state = conf_req_flag;
265
266 cleanup:
267 if (code != 0)
268 kg_release_iov(iov, iov_count);
269
270 return code;
271 }
272
273 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)274 gss_krb5int_unseal_v3_iov(krb5_context context,
275 OM_uint32 *minor_status,
276 krb5_gss_ctx_id_rec *ctx,
277 gss_iov_buffer_desc *iov,
278 int iov_count,
279 int *conf_state,
280 gss_qop_t *qop_state,
281 int toktype)
282 {
283 OM_uint32 code;
284 gss_iov_buffer_t header;
285 gss_iov_buffer_t padding;
286 gss_iov_buffer_t trailer;
287 unsigned char acceptor_flag;
288 unsigned char *ptr = NULL;
289 int key_usage;
290 size_t rrc, ec;
291 size_t data_length, assoc_data_length;
292 krb5_key key;
293 uint64_t seqnum;
294 krb5_boolean valid;
295 krb5_cksumtype cksumtype;
296 int conf_flag = 0;
297
298 if (qop_state != NULL)
299 *qop_state = GSS_C_QOP_DEFAULT;
300
301 header = kg_locate_header_iov(iov, iov_count, toktype);
302 assert(header != NULL);
303
304 padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
305 if (padding != NULL && padding->buffer.length != 0)
306 return GSS_S_DEFECTIVE_TOKEN;
307
308 trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
309
310 acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
311 key_usage = (toktype == KG_TOK_WRAP_MSG
312 ? (!ctx->initiate
313 ? KG_USAGE_INITIATOR_SEAL
314 : KG_USAGE_ACCEPTOR_SEAL)
315 : (!ctx->initiate
316 ? KG_USAGE_INITIATOR_SIGN
317 : KG_USAGE_ACCEPTOR_SIGN));
318
319 kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
320
321 ptr = (unsigned char *)header->buffer.value;
322
323 if (header->buffer.length < 16) {
324 *minor_status = 0;
325 return GSS_S_DEFECTIVE_TOKEN;
326 }
327
328 if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
329 *minor_status = (OM_uint32)G_BAD_DIRECTION;
330 return GSS_S_BAD_SIG;
331 }
332
333 if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
334 key = ctx->acceptor_subkey;
335 cksumtype = ctx->acceptor_subkey_cksumtype;
336 } else {
337 key = ctx->subkey;
338 cksumtype = ctx->cksumtype;
339 }
340 assert(key != NULL);
341
342
343 if (toktype == KG_TOK_WRAP_MSG) {
344 unsigned int k5_trailerlen;
345
346 if (load_16_be(ptr) != KG2_TOK_WRAP_MSG)
347 goto defective;
348 conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0);
349 if (ptr[3] != 0xFF)
350 goto defective;
351 ec = load_16_be(ptr + 4);
352 rrc = load_16_be(ptr + 6);
353 seqnum = load_64_be(ptr + 8);
354
355 code = krb5_c_crypto_length(context, key->keyblock.enctype,
356 conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
357 KRB5_CRYPTO_TYPE_CHECKSUM,
358 &k5_trailerlen);
359 if (code != 0) {
360 *minor_status = code;
361 return GSS_S_FAILURE;
362 }
363
364 /* Deal with RRC */
365 if (trailer == NULL) {
366 size_t desired_rrc = k5_trailerlen;
367
368 if (conf_flag) {
369 desired_rrc += 16; /* E(Header) */
370
371 if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0)
372 desired_rrc += ec;
373 }
374
375 /* According to MS, we only need to deal with a fixed RRC for DCE */
376 if (rrc != desired_rrc)
377 goto defective;
378 } else if (rrc != 0) {
379 /* Should have been rotated by kg_unseal_stream_iov() */
380 goto defective;
381 }
382
383 if (conf_flag) {
384 unsigned char *althdr;
385
386 /* Decrypt */
387 code = kg_decrypt_iov(context, ctx->proto,
388 ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
389 ec, rrc,
390 key, key_usage, 0, iov, iov_count);
391 if (code != 0) {
392 *minor_status = code;
393 return GSS_S_BAD_SIG;
394 }
395
396 /* Validate header integrity */
397 if (trailer == NULL)
398 althdr = (unsigned char *)header->buffer.value + 16 + ec;
399 else
400 althdr = (unsigned char *)trailer->buffer.value + ec;
401
402 if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
403 || althdr[2] != ptr[2]
404 || althdr[3] != ptr[3]
405 || memcmp(althdr + 8, ptr + 8, 8) != 0) {
406 *minor_status = 0;
407 return GSS_S_BAD_SIG;
408 }
409 } else {
410 /* Verify checksum: note EC is checksum size here, not padding */
411 if (ec != k5_trailerlen)
412 goto defective;
413
414 /* Zero EC, RRC before computing checksum */
415 store_16_be(0, ptr + 4);
416 store_16_be(0, ptr + 6);
417
418 code = kg_verify_checksum_iov_v3(context, cksumtype, rrc,
419 key, key_usage,
420 iov, iov_count, toktype, &valid);
421 if (code != 0 || valid == FALSE) {
422 *minor_status = code;
423 return GSS_S_BAD_SIG;
424 }
425 }
426
427 code = g_seqstate_check(ctx->seqstate, seqnum);
428 } else if (toktype == KG_TOK_MIC_MSG) {
429 if (load_16_be(ptr) != KG2_TOK_MIC_MSG)
430 goto defective;
431
432 verify_mic_1:
433 if (ptr[3] != 0xFF)
434 goto defective;
435 seqnum = load_64_be(ptr + 8);
436
437 /* For MIC tokens, the GSS header and checksum are in the same buffer.
438 * Fake up an RRC so that the checksum is expected in the header. */
439 rrc = (trailer != NULL) ? 0 : header->buffer.length - 16;
440 code = kg_verify_checksum_iov_v3(context, cksumtype, rrc,
441 key, key_usage,
442 iov, iov_count, toktype, &valid);
443 if (code != 0 || valid == FALSE) {
444 *minor_status = code;
445 return GSS_S_BAD_SIG;
446 }
447 code = g_seqstate_check(ctx->seqstate, seqnum);
448 } else if (toktype == KG_TOK_DEL_CTX) {
449 if (load_16_be(ptr) != KG2_TOK_DEL_CTX)
450 goto defective;
451 goto verify_mic_1;
452 } else {
453 goto defective;
454 }
455
456 *minor_status = 0;
457
458 if (conf_state != NULL)
459 *conf_state = conf_flag;
460
461 return code;
462
463 defective:
464 *minor_status = 0;
465
466 return GSS_S_DEFECTIVE_TOKEN;
467 }
468