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