1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/k5sealiov.c */
3 /*
4  * Copyright 2008, 2009 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 static krb5_error_code
make_seal_token_v1_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 make_seal_token_v1_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     krb5_checksum md5cksum;
44     krb5_checksum cksum;
45     size_t k5_headerlen = 0, k5_trailerlen = 0;
46     size_t data_length = 0, assoc_data_length = 0;
47     size_t tmsglen = 0, tlen;
48     unsigned char *ptr;
49     krb5_keyusage sign_usage = KG_USAGE_SIGN;
50 
51     md5cksum.length = cksum.length = 0;
52     md5cksum.contents = cksum.contents = NULL;
53 
54     header = kg_locate_header_iov(iov, iov_count, toktype);
55     if (header == NULL)
56         return EINVAL;
57 
58     padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
59     if (padding == NULL && toktype == KG_TOK_WRAP_MSG &&
60         (ctx->gss_flags & GSS_C_DCE_STYLE) == 0)
61         return EINVAL;
62 
63     trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
64     if (trailer != NULL)
65         trailer->buffer.length = 0;
66 
67     /* Determine confounder length */
68     if (toktype == KG_TOK_WRAP_MSG || conf_req_flag)
69         k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype);
70 
71     /* Check padding length */
72     if (toktype == KG_TOK_WRAP_MSG) {
73         size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8;
74         size_t gss_padlen;
75         size_t conf_data_length;
76 
77         kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
78         conf_data_length = k5_headerlen + data_length - assoc_data_length;
79 
80         if (k5_padlen == 1)
81             gss_padlen = 1; /* one byte to indicate one byte of padding */
82         else
83             gss_padlen = k5_padlen - (conf_data_length % k5_padlen);
84 
85         if (ctx->gss_flags & GSS_C_DCE_STYLE) {
86             /* DCE will pad the actual data itself; padding buffer optional and will be zeroed */
87             gss_padlen = 0;
88 
89             if (conf_data_length % k5_padlen)
90                 code = KRB5_BAD_MSIZE;
91         } else if (padding->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
92             code = kg_allocate_iov(padding, gss_padlen);
93         } else if (padding->buffer.length < gss_padlen) {
94             code = KRB5_BAD_MSIZE;
95         }
96         if (code != 0)
97             goto cleanup;
98 
99         /* Initialize padding buffer to pad itself */
100         if (padding != NULL) {
101             padding->buffer.length = gss_padlen;
102             memset(padding->buffer.value, (int)gss_padlen, gss_padlen);
103         }
104 
105         if (ctx->gss_flags & GSS_C_DCE_STYLE)
106             tmsglen = k5_headerlen; /* confounder length */
107         else
108             tmsglen = conf_data_length + padding->buffer.length;
109     }
110 
111     /* Determine token size */
112     tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen);
113 
114     k5_headerlen += tlen - tmsglen;
115 
116     if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
117         code = kg_allocate_iov(header, k5_headerlen);
118     else if (header->buffer.length < k5_headerlen)
119         code = KRB5_BAD_MSIZE;
120     if (code != 0)
121         goto cleanup;
122 
123     header->buffer.length = k5_headerlen;
124 
125     ptr = (unsigned char *)header->buffer.value;
126     g_make_token_header(ctx->mech_used, 14 + ctx->cksum_size + tmsglen, &ptr, toktype);
127 
128     /* 0..1 SIGN_ALG */
129     store_16_le(ctx->signalg, &ptr[0]);
130 
131     /* 2..3 SEAL_ALG or Filler */
132     if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
133         store_16_le(ctx->sealalg, &ptr[2]);
134     } else {
135         /* No seal */
136         ptr[2] = 0xFF;
137         ptr[3] = 0xFF;
138     }
139 
140     /* 4..5 Filler */
141     ptr[4] = 0xFF;
142     ptr[5] = 0xFF;
143 
144     /* pad the plaintext, encrypt if needed, and stick it in the token */
145 
146     /* initialize the checksum */
147     switch (ctx->signalg) {
148     case SGN_ALG_HMAC_SHA1_DES3_KD:
149         md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3;
150         break;
151     case SGN_ALG_HMAC_MD5:
152         md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR;
153         if (toktype != KG_TOK_WRAP_MSG)
154             sign_usage = 15;
155         break;
156     default:
157         abort ();
158     }
159 
160     code = krb5_c_checksum_length(context, md5cksum.checksum_type, &k5_trailerlen);
161     if (code != 0)
162         goto cleanup;
163     md5cksum.length = k5_trailerlen;
164 
165     if (k5_headerlen != 0 && toktype == KG_TOK_WRAP_MSG) {
166         code = kg_make_confounder(context, ctx->enc->keyblock.enctype,
167                                   ptr + 14 + ctx->cksum_size);
168         if (code != 0)
169             goto cleanup;
170     }
171 
172     /* compute the checksum */
173     code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type,
174                                    ctx->cksum_size, ctx->seq, ctx->enc,
175                                    sign_usage, iov, iov_count, toktype,
176                                    &md5cksum);
177     if (code != 0)
178         goto cleanup;
179 
180     switch (ctx->signalg) {
181     case SGN_ALG_HMAC_SHA1_DES3_KD:
182         assert(md5cksum.length == ctx->cksum_size);
183         memcpy(ptr + 14, md5cksum.contents, md5cksum.length);
184         break;
185     case SGN_ALG_HMAC_MD5:
186         memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size);
187         break;
188     }
189 
190     /* create the seq_num */
191     code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF,
192                            (OM_uint32)ctx->seq_send, ptr + 14, ptr + 6);
193     if (code != 0)
194         goto cleanup;
195 
196     if (conf_req_flag) {
197         if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) {
198             unsigned char bigend_seqnum[4];
199             krb5_keyblock *enc_key;
200             size_t i;
201 
202             store_32_be(ctx->seq_send, bigend_seqnum);
203 
204             code = krb5_k_key_keyblock(context, ctx->enc, &enc_key);
205             if (code != 0)
206                 goto cleanup;
207 
208             assert(enc_key->length == 16);
209 
210             for (i = 0; i < enc_key->length; i++)
211                 ((char *)enc_key->contents)[i] ^= 0xF0;
212 
213             code = kg_arcfour_docrypt_iov(context, enc_key, 0,
214                                           bigend_seqnum, 4,
215                                           iov, iov_count);
216             krb5_free_keyblock(context, enc_key);
217         } else {
218             code = kg_encrypt_iov(context, ctx->proto,
219                                   ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
220                                   0 /*EC*/, 0 /*RRC*/,
221                                   ctx->enc, KG_USAGE_SEAL, NULL,
222                                   iov, iov_count);
223         }
224         if (code != 0)
225             goto cleanup;
226     }
227 
228     ctx->seq_send++;
229     ctx->seq_send &= 0xFFFFFFFFL;
230 
231     code = 0;
232 
233     if (conf_state != NULL)
234         *conf_state = conf_req_flag;
235 
236 cleanup:
237     if (code != 0)
238         kg_release_iov(iov, iov_count);
239     krb5_free_checksum_contents(context, &md5cksum);
240 
241     return code;
242 }
243 
244 OM_uint32
kg_seal_iov(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,int toktype)245 kg_seal_iov(OM_uint32 *minor_status,
246             gss_ctx_id_t context_handle,
247             int conf_req_flag,
248             gss_qop_t qop_req,
249             int *conf_state,
250             gss_iov_buffer_desc *iov,
251             int iov_count,
252             int toktype)
253 {
254     krb5_gss_ctx_id_rec *ctx;
255     krb5_error_code code;
256     krb5_context context;
257 
258     if (qop_req != 0) {
259         *minor_status = (OM_uint32)G_UNKNOWN_QOP;
260         return GSS_S_BAD_QOP;
261     }
262 
263     ctx = (krb5_gss_ctx_id_rec *)context_handle;
264     if (ctx->terminated || !ctx->established) {
265         *minor_status = KG_CTX_INCOMPLETE;
266         return GSS_S_NO_CONTEXT;
267     }
268 
269     if (conf_req_flag && kg_integ_only_iov(iov, iov_count)) {
270         /* may be more sensible to return an error here */
271         conf_req_flag = FALSE;
272     }
273 
274     context = ctx->k5_context;
275     switch (ctx->proto) {
276     case 0:
277         code = make_seal_token_v1_iov(context, ctx, conf_req_flag,
278                                       conf_state, iov, iov_count, toktype);
279         break;
280     case 1:
281         code = gss_krb5int_make_seal_token_v3_iov(context, ctx, conf_req_flag,
282                                                   conf_state, iov, iov_count, toktype);
283         break;
284     default:
285         code = G_UNKNOWN_QOP;
286         break;
287     }
288 
289     if (code != 0) {
290         *minor_status = code;
291         save_error_info(*minor_status, context);
292         return GSS_S_FAILURE;
293     }
294 
295     *minor_status = 0;
296 
297     return GSS_S_COMPLETE;
298 }
299 
300 #define INIT_IOV_DATA(_iov)     do { (_iov)->buffer.value = NULL;       \
301         (_iov)->buffer.length = 0; }                                    \
302     while (0)
303 
304 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,int toktype)305 kg_seal_iov_length(OM_uint32 *minor_status,
306                    gss_ctx_id_t context_handle,
307                    int conf_req_flag,
308                    gss_qop_t qop_req,
309                    int *conf_state,
310                    gss_iov_buffer_desc *iov,
311                    int iov_count,
312                    int toktype)
313 {
314     krb5_gss_ctx_id_rec *ctx;
315     gss_iov_buffer_t header, trailer, padding;
316     size_t data_length, assoc_data_length;
317     size_t gss_headerlen, gss_padlen, gss_trailerlen;
318     unsigned int k5_headerlen = 0, k5_trailerlen = 0, k5_padlen = 0;
319     krb5_error_code code;
320     krb5_context context;
321     int dce_or_mic;
322 
323     if (qop_req != GSS_C_QOP_DEFAULT) {
324         *minor_status = (OM_uint32)G_UNKNOWN_QOP;
325         return GSS_S_BAD_QOP;
326     }
327 
328     ctx = (krb5_gss_ctx_id_rec *)context_handle;
329     if (!ctx->established) {
330         *minor_status = KG_CTX_INCOMPLETE;
331         return GSS_S_NO_CONTEXT;
332     }
333 
334     header = kg_locate_header_iov(iov, iov_count, toktype);
335     if (header == NULL) {
336         *minor_status = EINVAL;
337         return GSS_S_FAILURE;
338     }
339     INIT_IOV_DATA(header);
340 
341     trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
342     if (trailer != NULL) {
343         INIT_IOV_DATA(trailer);
344     }
345 
346     /* MIC tokens and DCE-style wrap tokens have similar length considerations:
347      * no padding, and the framing surrounds the header only, not the data. */
348     dce_or_mic = ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0 ||
349                   toktype == KG_TOK_MIC_MSG);
350 
351     /* For CFX, EC is used instead of padding, and is placed in header or trailer */
352     padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
353     if (padding == NULL) {
354         if (conf_req_flag && ctx->proto == 0 && !dce_or_mic) {
355             *minor_status = EINVAL;
356             return GSS_S_FAILURE;
357         }
358     } else {
359         INIT_IOV_DATA(padding);
360     }
361 
362     kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
363 
364     if (conf_req_flag && kg_integ_only_iov(iov, iov_count))
365         conf_req_flag = FALSE;
366 
367     context = ctx->k5_context;
368 
369     gss_headerlen = gss_padlen = gss_trailerlen = 0;
370 
371     if (ctx->proto == 1) {
372         krb5_key key;
373         krb5_enctype enctype;
374         size_t ec;
375 
376         key = (ctx->have_acceptor_subkey) ? ctx->acceptor_subkey : ctx->subkey;
377         enctype = key->keyblock.enctype;
378 
379         code = krb5_c_crypto_length(context, enctype,
380                                     conf_req_flag ?
381                                     KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM,
382                                     &k5_trailerlen);
383         if (code != 0) {
384             *minor_status = code;
385             return GSS_S_FAILURE;
386         }
387 
388         if (conf_req_flag) {
389             code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
390             if (code != 0) {
391                 *minor_status = code;
392                 return GSS_S_FAILURE;
393             }
394         }
395 
396         gss_headerlen = 16; /* Header */
397         if (conf_req_flag) {
398             gss_headerlen += k5_headerlen; /* Kerb-Header */
399             gss_trailerlen = 16 /* E(Header) */ + k5_trailerlen; /* Kerb-Trailer */
400 
401             code = krb5_c_padding_length(context, enctype,
402                                          data_length - assoc_data_length + 16 /* E(Header) */, &k5_padlen);
403             if (code != 0) {
404                 *minor_status = code;
405                 return GSS_S_FAILURE;
406             }
407 
408             if (k5_padlen == 0 && dce_or_mic) {
409                 /* Windows rejects AEAD tokens with non-zero EC */
410                 code = krb5_c_block_size(context, enctype, &ec);
411                 if (code != 0) {
412                     *minor_status = code;
413                     return GSS_S_FAILURE;
414                 }
415             } else
416                 ec = k5_padlen;
417 
418             gss_trailerlen += ec;
419         } else {
420             gss_trailerlen = k5_trailerlen; /* Kerb-Checksum */
421         }
422     } else if (!dce_or_mic) {
423         k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8;
424 
425         if (k5_padlen == 1)
426             gss_padlen = 1;
427         else
428             gss_padlen = k5_padlen - ((data_length - assoc_data_length) % k5_padlen);
429     }
430 
431     data_length += gss_padlen;
432 
433     if (ctx->proto == 0) {
434         /* Header | Checksum | Confounder | Data | Pad */
435         size_t data_size;
436 
437         k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype);
438 
439         data_size = 14 /* Header */ + ctx->cksum_size + k5_headerlen;
440 
441         if (!dce_or_mic)
442             data_size += data_length;
443 
444         gss_headerlen = g_token_size(ctx->mech_used, data_size);
445 
446         /* g_token_size() will include data_size as well as the overhead, so
447          * subtract data_length just to get the overhead (ie. token size) */
448         if (!dce_or_mic)
449             gss_headerlen -= data_length;
450     }
451 
452     if (minor_status != NULL)
453         *minor_status = 0;
454 
455     if (trailer == NULL)
456         gss_headerlen += gss_trailerlen;
457     else
458         trailer->buffer.length = gss_trailerlen;
459 
460     assert(gss_padlen == 0 || padding != NULL);
461 
462     if (padding != NULL)
463         padding->buffer.length = gss_padlen;
464 
465     header->buffer.length = gss_headerlen;
466 
467     if (conf_state != NULL)
468         *conf_state = conf_req_flag;
469 
470     return GSS_S_COMPLETE;
471 }
472 
473 OM_uint32 KRB5_CALLCONV
krb5_gss_wrap_iov(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)474 krb5_gss_wrap_iov(OM_uint32 *minor_status,
475                   gss_ctx_id_t context_handle,
476                   int conf_req_flag,
477                   gss_qop_t qop_req,
478                   int *conf_state,
479                   gss_iov_buffer_desc *iov,
480                   int iov_count)
481 {
482     OM_uint32 major_status;
483 
484     major_status = kg_seal_iov(minor_status, context_handle, conf_req_flag,
485                                qop_req, conf_state,
486                                iov, iov_count, KG_TOK_WRAP_MSG);
487 
488     return major_status;
489 }
490 
491 OM_uint32 KRB5_CALLCONV
krb5_gss_wrap_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)492 krb5_gss_wrap_iov_length(OM_uint32 *minor_status,
493                          gss_ctx_id_t context_handle,
494                          int conf_req_flag,
495                          gss_qop_t qop_req,
496                          int *conf_state,
497                          gss_iov_buffer_desc *iov,
498                          int iov_count)
499 {
500     OM_uint32 major_status;
501 
502     major_status = kg_seal_iov_length(minor_status, context_handle,
503                                       conf_req_flag, qop_req, conf_state, iov,
504                                       iov_count, KG_TOK_WRAP_MSG);
505     return major_status;
506 }
507 
508 OM_uint32 KRB5_CALLCONV
krb5_gss_get_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)509 krb5_gss_get_mic_iov(OM_uint32 *minor_status,
510                      gss_ctx_id_t context_handle,
511                      gss_qop_t qop_req,
512                      gss_iov_buffer_desc *iov,
513                      int iov_count)
514 {
515     OM_uint32 major_status;
516 
517     major_status = kg_seal_iov(minor_status, context_handle, FALSE,
518                                qop_req, NULL,
519                                iov, iov_count, KG_TOK_MIC_MSG);
520 
521     return major_status;
522 }
523 
524 OM_uint32 KRB5_CALLCONV
krb5_gss_get_mic_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)525 krb5_gss_get_mic_iov_length(OM_uint32 *minor_status,
526                             gss_ctx_id_t context_handle,
527                             gss_qop_t qop_req,
528                             gss_iov_buffer_desc *iov,
529                             int iov_count)
530 {
531     OM_uint32 major_status;
532 
533     major_status = kg_seal_iov_length(minor_status, context_handle, FALSE,
534                                       qop_req, NULL, iov, iov_count,
535                                       KG_TOK_MIC_MSG);
536     return major_status;
537 }
538