41e6132cd915643e57a233836ba80ec80f3d222d
[krb5.git] / src / lib / gssapi / krb5 / k5sealv3iov.c
1 /* -*- mode: c; indent-tabs-mode: nil -*- */
2 /*
3  * lib/gssapi/krb5/k5sealv3iov.c
4  *
5  * Copyright 2008 by the Massachusetts Institute of Technology.
6  * All Rights Reserved.
7  *
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.
12  *
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.
26  *
27  *
28  */
29
30 #include <assert.h>
31 #include "k5-platform.h"        /* for 64-bit support */
32 #include "k5-int.h"          /* for zap() */
33 #include "gssapiP_krb5.h"
34 #include <stdarg.h>
35
36 krb5_error_code
37 gss_krb5int_make_seal_token_v3_iov(krb5_context context,
38                                    krb5_gss_ctx_id_rec *ctx,
39                                    int conf_req_flag,
40                                    int *conf_state,
41                                    gss_iov_buffer_desc *iov,
42                                    int iov_count,
43                                    int toktype)
44 {
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;
53     int key_usage;
54     size_t rrc = 0;
55     size_t gss_headerlen, gss_trailerlen;
56     krb5_keyblock *key;
57     krb5_cksumtype cksumtype;
58     size_t data_length, assoc_data_length;
59
60     assert(ctx->big_endian == 0);
61     assert(ctx->proto == 1);
62
63     acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
64     key_usage = (toktype == KG_TOK_WRAP_MSG
65                  ? (ctx->initiate
66                     ? KG_USAGE_INITIATOR_SEAL
67                     : KG_USAGE_ACCEPTOR_SEAL)
68                  : (ctx->initiate
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;
74     } else {
75         key = ctx->subkey;
76         cksumtype = ctx->cksumtype;
77     }
78     assert(key != NULL);
79     assert(cksumtype != 0);
80
81     kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
82
83     header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
84     if (header == NULL)
85         return EINVAL;
86
87     padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
88     if (padding != NULL)
89         padding->buffer.length = 0;
90
91     trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
92
93     outbuf = (unsigned char *)header->buffer.value;
94
95     if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
96         unsigned int k5_headerlen, k5_trailerlen, k5_padlen;
97         size_t ec = 0;
98         size_t conf_data_length = data_length - assoc_data_length;
99
100         code = krb5_c_crypto_length(context, key->enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
101         if (code != 0)
102             goto cleanup;
103
104         code = krb5_c_padding_length(context, key->enctype,
105                                      conf_data_length + 16 /* E(Header) */, &k5_padlen);
106         if (code != 0)
107             goto cleanup;
108
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);
112             if (code != 0)
113                 goto cleanup;
114         } else
115             ec = k5_padlen;
116
117         code = krb5_c_crypto_length(context, key->enctype, KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen);
118         if (code != 0)
119             goto cleanup;
120
121         gss_headerlen = 16 /* Header */ + k5_headerlen;
122         gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen;
123
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)
128                 rrc -= ec;
129             gss_headerlen += gss_trailerlen;
130         }
131
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;
136         if (code != 0)
137             goto cleanup;
138         header->buffer.length = gss_headerlen;
139
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;
145             if (code != 0)
146                 goto cleanup;
147             trailer->buffer.length = gss_trailerlen;
148         }
149
150         /* TOK_ID */
151         store_16_be(KG2_TOK_WRAP_MSG, outbuf);
152         /* flags */
153         outbuf[2] = (acceptor_flag
154                      | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
155                      | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
156         /* filler */
157         outbuf[3] = 0xFF;
158         /* EC */
159         store_16_be(ec, outbuf + 4);
160         /* RRC */
161         store_16_be(0, outbuf + 6);
162         store_64_be(ctx->seq_send, outbuf + 8);
163
164         /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */
165         if (trailer == NULL)
166             tbuf = (unsigned char *)header->buffer.value + 16; /* Header */
167         else
168             tbuf = (unsigned char *)trailer->buffer.value;
169
170         memset(tbuf, 0xFF, ec);
171         memcpy(tbuf + ec, header->buffer.value, 16);
172
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);
176         if (code != 0)
177             goto cleanup;
178
179         /* RRC */
180         store_16_be(rrc, outbuf + 6);
181
182         ctx->seq_send++;
183     } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
184         tok_id = KG2_TOK_WRAP_MSG;
185
186     wrap_with_checksum:
187
188         gss_headerlen = 16;
189
190         code = krb5_c_crypto_length(context, key->enctype, KRB5_CRYPTO_TYPE_CHECKSUM, &gss_trailerlen);
191         if (code != 0)
192             goto cleanup;
193
194         assert(gss_trailerlen <= 0xFFFF);
195
196         if (trailer == NULL) {
197             rrc = gss_trailerlen;
198             gss_headerlen += gss_trailerlen;
199         }
200
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;
205         if (code != 0)
206             goto cleanup;
207         header->buffer.length = gss_headerlen;
208
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;
214             if (code != 0)
215                 goto cleanup;
216             trailer->buffer.length = gss_trailerlen;
217         }
218
219         /* TOK_ID */
220         store_16_be(tok_id, outbuf);
221         /* flags */
222         outbuf[2] = (acceptor_flag
223                      | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
224         /* filler */
225         outbuf[3] = 0xFF;
226         if (toktype == KG_TOK_WRAP_MSG) {
227             /* Use 0 for checksum calculation, substitute
228              * checksum length later.
229              */
230             /* EC */
231             store_16_be(0, outbuf + 4);
232             /* RRC */
233             store_16_be(0, outbuf + 6);
234         } else {
235             /* MIC and DEL store 0xFF in EC and RRC */
236             store_16_be(0xFFFF, outbuf + 4);
237             store_16_be(0xFFFF, outbuf + 6);
238         }
239         store_64_be(ctx->seq_send, outbuf + 8);
240
241         code = kg_make_checksum_iov_v3(context, cksumtype,
242                                        rrc, key, key_usage,
243                                        iov, iov_count);
244         if (code != 0)
245             goto cleanup;
246
247         ctx->seq_send++;
248
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);
254         }
255     } else if (toktype == KG_TOK_MIC_MSG) {
256         tok_id = KG2_TOK_MIC_MSG;
257         trailer = NULL;
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;
262     } else {
263         abort();
264     }
265
266     code = 0;
267
268 cleanup:
269     if (code != 0)
270         kg_release_iov(iov, iov_count);
271
272     return code;
273 }
274
275 OM_uint32
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,
280                           int iov_count,
281                           int *conf_state,
282                           gss_qop_t *qop_state,
283                           int toktype)
284 {
285     OM_uint32 code;
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;
291     int key_usage;
292     size_t rrc, ec;
293     size_t data_length, assoc_data_length;
294     krb5_keyblock *key;
295     gssint_uint64 seqnum;
296     krb5_boolean valid;
297     krb5_cksumtype cksumtype;
298     int conf_flag = 0;
299
300     assert(ctx->big_endian == 0);
301     assert(ctx->proto == 1);
302
303     if (qop_state != NULL)
304         *qop_state = GSS_C_QOP_DEFAULT;
305
306     header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
307     assert(header != NULL);
308
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;
312
313     trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
314
315     acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
316     key_usage = (toktype == KG_TOK_WRAP_MSG
317                  ? (!ctx->initiate 
318                     ? KG_USAGE_INITIATOR_SEAL
319                     : KG_USAGE_ACCEPTOR_SEAL)
320                  : (!ctx->initiate
321                     ? KG_USAGE_INITIATOR_SIGN 
322                     : KG_USAGE_ACCEPTOR_SIGN));
323
324     kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
325
326     ptr = (unsigned char *)header->buffer.value;
327
328     if (header->buffer.length < 16) {
329         *minor_status = 0;
330         return GSS_S_DEFECTIVE_TOKEN;
331     }
332
333     if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
334         *minor_status = (OM_uint32)G_BAD_DIRECTION;
335         return GSS_S_BAD_SIG;
336     }
337
338     if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
339         key = ctx->acceptor_subkey;
340         cksumtype = ctx->acceptor_subkey_cksumtype;
341     } else {
342         key = ctx->subkey;
343         cksumtype = ctx->cksumtype;
344     }
345     assert(key != NULL);
346
347
348     if (toktype == KG_TOK_WRAP_MSG) {
349         unsigned int k5_trailerlen;
350
351         if (load_16_be(ptr) != KG2_TOK_WRAP_MSG)
352             goto defective;
353         conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0);
354         if (ptr[3] != 0xFF)
355             goto defective;
356         ec = load_16_be(ptr + 4);
357         rrc = load_16_be(ptr + 6);
358         seqnum = load_64_be(ptr + 8);
359
360         code = krb5_c_crypto_length(context, key->enctype,
361                                     conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
362                                                 KRB5_CRYPTO_TYPE_CHECKSUM,
363                                     &k5_trailerlen);
364         if (code != 0) {
365             *minor_status = code;
366             return GSS_S_FAILURE;
367         }
368
369         /* Deal with RRC */
370         if (trailer == NULL) {
371             size_t desired_rrc = k5_trailerlen;
372
373             if (conf_flag) {
374                 desired_rrc += 16; /* E(Header) */
375
376                 if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0)
377                     desired_rrc += ec;
378             }
379
380             /* According to MS, we only need to deal with a fixed RRC for DCE */
381             if (rrc != desired_rrc)
382                 goto defective;
383         } else if (rrc != 0) {
384             /* Should have been rotated by kg_unseal_stream_iov() */
385             goto defective;
386         }
387
388         if (conf_flag) {
389             unsigned char *althdr;
390
391             /* Decrypt */
392             code = kg_decrypt_iov(context, ctx->proto,
393                                   ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
394                                   ec, rrc,
395                                   key, key_usage, 0, iov, iov_count);
396             if (code != 0) {
397                 *minor_status = code;
398                 return GSS_S_BAD_SIG;
399             }
400
401             /* Validate header integrity */
402             if (trailer == NULL)
403                 althdr = (unsigned char *)header->buffer.value + 16 + ec;
404             else
405                 althdr = (unsigned char *)trailer->buffer.value + ec;
406
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) {
411                 *minor_status = 0;
412                 return GSS_S_BAD_SIG;
413             }
414         } else {
415             /* Verify checksum: note EC is checksum size here, not padding */
416             if (ec != k5_trailerlen)
417                 goto defective;
418
419             /* Zero EC, RRC before computing checksum */
420             store_16_be(0, ptr + 4);
421             store_16_be(0, ptr + 6);
422
423             code = kg_verify_checksum_iov_v3(context, cksumtype, rrc,
424                                              key, key_usage,
425                                              iov, iov_count, &valid);
426             if (code != 0 || valid == FALSE) {
427                 *minor_status = code;
428                 return GSS_S_BAD_SIG;
429             }
430         }
431
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)
435             goto defective;
436
437     verify_mic_1:
438         if (ptr[3] != 0xFF)
439             goto defective;
440         seqnum = load_64_be(ptr + 8);
441
442         code = kg_verify_checksum_iov_v3(context, cksumtype, 0,
443                                          key, key_usage,
444                                          iov, iov_count, &valid);
445         if (code != 0 || valid == FALSE) {
446             *minor_status = code;
447             return GSS_S_BAD_SIG;
448         }
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)
452             goto defective;
453         goto verify_mic_1;
454     } else {
455         goto defective;
456     }
457
458     *minor_status = 0;
459
460     if (conf_state != NULL)
461         *conf_state = conf_flag;
462
463     return code;
464
465 defective:
466     *minor_status = 0;
467
468     return GSS_S_DEFECTIVE_TOKEN;
469 }