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