xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/k5unsealiov.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/k5unsealiov.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 "k5-der.h"
29 #include "gssapiP_krb5.h"
30 
31 static OM_uint32
kg_unseal_v1_iov(krb5_context context,OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,gss_iov_buffer_desc * iov,int iov_count,size_t token_wrapper_len,int * conf_state,gss_qop_t * qop_state,int toktype)32 kg_unseal_v1_iov(krb5_context context,
33                  OM_uint32 *minor_status,
34                  krb5_gss_ctx_id_rec *ctx,
35                  gss_iov_buffer_desc *iov,
36                  int iov_count,
37                  size_t token_wrapper_len,
38                  int *conf_state,
39                  gss_qop_t *qop_state,
40                  int toktype)
41 {
42     OM_uint32 code;
43     gss_iov_buffer_t header;
44     gss_iov_buffer_t trailer;
45     unsigned char *ptr;
46     int sealalg;
47     int signalg;
48     krb5_checksum md5cksum;
49     size_t cksum_len = 0;
50     size_t conflen = 0;
51     int direction;
52     krb5_ui_4 seqnum;
53     OM_uint32 retval;
54     size_t sumlen;
55     krb5_keyusage sign_usage = KG_USAGE_SIGN;
56 
57     md5cksum.length = 0;
58     md5cksum.contents = NULL;
59 
60     header = kg_locate_header_iov(iov, iov_count, toktype);
61     assert(header != NULL);
62 
63     trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
64     if (trailer != NULL && trailer->buffer.length != 0) {
65         *minor_status = (OM_uint32)KRB5_BAD_MSIZE;
66         return GSS_S_DEFECTIVE_TOKEN;
67     }
68 
69     if (ctx->seq == NULL) {
70         /* ctx was established using a newer enctype, and cannot process RFC
71          * 1964 tokens. */
72         *minor_status = 0;
73         return GSS_S_DEFECTIVE_TOKEN;
74     }
75 
76     if (header->buffer.length < token_wrapper_len + 22) {
77         *minor_status = 0;
78         return GSS_S_DEFECTIVE_TOKEN;
79     }
80 
81     ptr = (unsigned char *)header->buffer.value + token_wrapper_len;
82 
83     signalg  = ptr[0];
84     signalg |= ptr[1] << 8;
85 
86     sealalg  = ptr[2];
87     sealalg |= ptr[3] << 8;
88 
89     if (ptr[4] != 0xFF || ptr[5] != 0xFF) {
90         *minor_status = 0;
91         return GSS_S_DEFECTIVE_TOKEN;
92     }
93 
94     if (toktype != KG_TOK_WRAP_MSG && sealalg != 0xFFFF) {
95         *minor_status = 0;
96         return GSS_S_DEFECTIVE_TOKEN;
97     }
98 
99     if (toktype == KG_TOK_WRAP_MSG &&
100         !(sealalg == 0xFFFF || sealalg == ctx->sealalg)) {
101         *minor_status = 0;
102         return GSS_S_DEFECTIVE_TOKEN;
103     }
104 
105     if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) ||
106         (ctx->sealalg == SEAL_ALG_DES3KD &&
107          signalg != SGN_ALG_HMAC_SHA1_DES3_KD)||
108         (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 &&
109          signalg != SGN_ALG_HMAC_MD5)) {
110         *minor_status = 0;
111         return GSS_S_DEFECTIVE_TOKEN;
112     }
113 
114     switch (signalg) {
115     case SGN_ALG_HMAC_MD5:
116         cksum_len = 8;
117         if (toktype != KG_TOK_WRAP_MSG)
118             sign_usage = 15;
119         break;
120     case SGN_ALG_HMAC_SHA1_DES3_KD:
121         cksum_len = 20;
122         break;
123     default:
124         *minor_status = 0;
125         return GSS_S_DEFECTIVE_TOKEN;
126     }
127 
128     /* get the token parameters */
129     code = kg_get_seq_num(context, ctx->seq, ptr + 14, ptr + 6, &direction,
130                           &seqnum);
131     if (code != 0) {
132         *minor_status = code;
133         return GSS_S_BAD_SIG;
134     }
135 
136     /* decode the message, if SEAL */
137     if (toktype == KG_TOK_WRAP_MSG) {
138         if (sealalg != 0xFFFF) {
139             if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) {
140                 unsigned char bigend_seqnum[4];
141                 krb5_keyblock *enc_key;
142                 size_t i;
143 
144                 store_32_be(seqnum, bigend_seqnum);
145 
146                 code = krb5_k_key_keyblock(context, ctx->enc, &enc_key);
147                 if (code != 0) {
148                     retval = GSS_S_FAILURE;
149                     goto cleanup;
150                 }
151 
152                 assert(enc_key->length == 16);
153 
154                 for (i = 0; i < enc_key->length; i++)
155                     ((char *)enc_key->contents)[i] ^= 0xF0;
156 
157                 code = kg_arcfour_docrypt_iov(context, enc_key, 0,
158                                               &bigend_seqnum[0], 4,
159                                               iov, iov_count);
160                 krb5_free_keyblock(context, enc_key);
161             } else {
162                 code = kg_decrypt_iov(context, 0,
163                                       ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
164                                       0 /*EC*/, 0 /*RRC*/,
165                                       ctx->enc, KG_USAGE_SEAL, NULL,
166                                       iov, iov_count);
167             }
168             if (code != 0) {
169                 retval = GSS_S_FAILURE;
170                 goto cleanup;
171             }
172         }
173         conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype);
174     }
175 
176     if (header->buffer.length != token_wrapper_len + 14 + cksum_len + conflen) {
177         retval = GSS_S_DEFECTIVE_TOKEN;
178         goto cleanup;
179     }
180 
181     /* compute the checksum of the message */
182 
183     /* initialize the checksum */
184 
185     switch (signalg) {
186     case SGN_ALG_HMAC_MD5:
187         md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR;
188         break;
189     case SGN_ALG_HMAC_SHA1_DES3_KD:
190         md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3;
191         break;
192     default:
193         abort();
194     }
195 
196     code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen);
197     if (code != 0) {
198         retval = GSS_S_FAILURE;
199         goto cleanup;
200     }
201     md5cksum.length = sumlen;
202 
203     /* compute the checksum of the message */
204     code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type,
205                                    cksum_len, ctx->seq, ctx->enc,
206                                    sign_usage, iov, iov_count, toktype,
207                                    &md5cksum);
208     if (code != 0) {
209         retval = GSS_S_FAILURE;
210         goto cleanup;
211     }
212 
213     switch (signalg) {
214     case SGN_ALG_HMAC_SHA1_DES3_KD:
215     case SGN_ALG_HMAC_MD5:
216         code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len);
217         break;
218     default:
219         code = 0;
220         retval = GSS_S_DEFECTIVE_TOKEN;
221         goto cleanup;
222         break;
223     }
224 
225     if (code != 0) {
226         code = 0;
227         retval = GSS_S_BAD_SIG;
228         goto cleanup;
229     }
230 
231     /*
232      * For GSS_C_DCE_STYLE, the caller manages the padding, because the
233      * pad length is in the RPC PDU. The value of the padding may be
234      * uninitialized. For normal GSS, the last bytes of the decrypted
235      * data contain the pad length. kg_fixup_padding_iov() will find
236      * this and fixup the last data IOV appropriately.
237      */
238     if (toktype == KG_TOK_WRAP_MSG &&
239         (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) {
240         retval = kg_fixup_padding_iov(&code, iov, iov_count);
241         if (retval != GSS_S_COMPLETE)
242             goto cleanup;
243     }
244 
245     if (conf_state != NULL)
246         *conf_state = (sealalg != 0xFFFF);
247 
248     if (qop_state != NULL)
249         *qop_state = GSS_C_QOP_DEFAULT;
250 
251     if ((ctx->initiate && direction != 0xff) ||
252         (!ctx->initiate && direction != 0)) {
253         *minor_status = (OM_uint32)G_BAD_DIRECTION;
254         retval = GSS_S_BAD_SIG;
255         goto cleanup;
256     }
257 
258     code = 0;
259     retval = g_seqstate_check(ctx->seqstate, (uint64_t)seqnum);
260 
261 cleanup:
262     krb5_free_checksum_contents(context, &md5cksum);
263 
264     *minor_status = code;
265 
266     return retval;
267 }
268 
269 /*
270  * Caller must provide TOKEN | DATA | PADDING | TRAILER, except
271  * for DCE in which case it can just provide TOKEN | DATA (must
272  * guarantee that DATA is padded)
273  */
274 static OM_uint32
kg_unseal_iov_token(OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count,int toktype)275 kg_unseal_iov_token(OM_uint32 *minor_status,
276                     krb5_gss_ctx_id_rec *ctx,
277                     int *conf_state,
278                     gss_qop_t *qop_state,
279                     gss_iov_buffer_desc *iov,
280                     int iov_count,
281                     int toktype)
282 {
283     krb5_error_code code;
284     krb5_context context = ctx->k5_context;
285     struct k5input in;
286     gss_OID_desc mech;
287     size_t tlen, header_tlen;
288     gss_iov_buffer_t header;
289     gss_iov_buffer_t padding;
290     gss_iov_buffer_t trailer;
291     int toktype2;
292 
293     header = kg_locate_header_iov(iov, iov_count, toktype);
294     if (header == NULL) {
295         *minor_status = EINVAL;
296         return GSS_S_FAILURE;
297     }
298 
299     padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
300     trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
301 
302     tlen = header->buffer.length;
303     if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
304         toktype == KG_TOK_WRAP_MSG) {
305         size_t data_length, assoc_data_length;
306 
307         kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
308 
309         tlen += data_length - assoc_data_length;
310 
311         if (padding != NULL)
312             tlen += padding->buffer.length;
313 
314         if (trailer != NULL)
315             tlen += trailer->buffer.length;
316     }
317 
318     /* If there is a token header, advance past it and verify its mech and
319      * token length. */
320     k5_input_init(&in, header->buffer.value, header->buffer.length);
321     if (g_get_token_header(&in, &mech, &header_tlen)) {
322         if (!g_OID_equal(&mech, ctx->mech_used) || header_tlen != tlen) {
323             *minor_status = G_BAD_TOK_HEADER;
324             return GSS_S_DEFECTIVE_TOKEN;
325         }
326     }
327     toktype2 = k5_input_get_uint16_be(&in);
328 
329     switch (toktype2) {
330     case KG2_TOK_MIC_MSG:
331     case KG2_TOK_WRAP_MSG:
332     case KG2_TOK_DEL_CTX:
333         code = gss_krb5int_unseal_v3_iov(context, minor_status, ctx, iov, iov_count,
334                                          conf_state, qop_state, toktype);
335         break;
336     case KG_TOK_MIC_MSG:
337     case KG_TOK_WRAP_MSG:
338     case KG_TOK_DEL_CTX:
339         code = kg_unseal_v1_iov(context, minor_status, ctx, iov, iov_count,
340                                 (size_t)(in.ptr - (unsigned char *)header->buffer.value),
341                                 conf_state, qop_state, toktype);
342         break;
343     default:
344         *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
345         code = GSS_S_DEFECTIVE_TOKEN;
346         break;
347     }
348 
349     if (code != 0)
350         save_error_info(*minor_status, context);
351 
352     return code;
353 }
354 
355 /*
356  * Split a STREAM | SIGN_DATA | DATA into
357  *         HEADER | SIGN_DATA | DATA | PADDING | TRAILER
358  */
359 static OM_uint32
kg_unseal_stream_iov(OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count,int toktype)360 kg_unseal_stream_iov(OM_uint32 *minor_status,
361                      krb5_gss_ctx_id_rec *ctx,
362                      int *conf_state,
363                      gss_qop_t *qop_state,
364                      gss_iov_buffer_desc *iov,
365                      int iov_count,
366                      int toktype)
367 {
368     struct k5input in;
369     unsigned char *ptr;
370     unsigned int bodysize;
371     OM_uint32 code = 0, major_status = GSS_S_FAILURE;
372     krb5_context context = ctx->k5_context;
373     int conf_req_flag, toktype2;
374     int i = 0, j;
375     gss_iov_buffer_desc *tiov = NULL;
376     gss_iov_buffer_t stream, data = NULL;
377     gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
378 
379     assert(toktype == KG_TOK_WRAP_MSG);
380 
381     if (toktype != KG_TOK_WRAP_MSG || (ctx->gss_flags & GSS_C_DCE_STYLE)) {
382         code = EINVAL;
383         goto cleanup;
384     }
385 
386     stream = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
387     assert(stream != NULL);
388 
389     ptr = (unsigned char *)stream->buffer.value;
390 
391     k5_input_init(&in, stream->buffer.value, stream->buffer.length);
392     (void)g_verify_token_header(&in, ctx->mech_used);
393     toktype2 = k5_input_get_uint16_be(&in);
394     if (in.status) {
395         *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
396         return GSS_S_DEFECTIVE_TOKEN;
397     }
398 
399     ptr = (uint8_t *)in.ptr;
400     bodysize = in.len;
401 
402     tiov = (gss_iov_buffer_desc *)calloc((size_t)iov_count + 2, sizeof(gss_iov_buffer_desc));
403     if (tiov == NULL) {
404         code = ENOMEM;
405         goto cleanup;
406     }
407 
408     /* HEADER */
409     theader = &tiov[i++];
410     theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
411     theader->buffer.value = stream->buffer.value;
412     theader->buffer.length = ptr - (unsigned char *)stream->buffer.value;
413     if (bodysize < 14 ||
414         stream->buffer.length != theader->buffer.length + bodysize) {
415         major_status = GSS_S_DEFECTIVE_TOKEN;
416         goto cleanup;
417     }
418     theader->buffer.length += 14;
419 
420     /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
421     for (j = 0; j < iov_count; j++) {
422         OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);
423 
424         if (type == GSS_IOV_BUFFER_TYPE_DATA) {
425             if (data != NULL) {
426                 /* only a single DATA buffer can appear */
427                 code = EINVAL;
428                 goto cleanup;
429             }
430 
431             data = &iov[j];
432             tdata = &tiov[i];
433         }
434         if (type == GSS_IOV_BUFFER_TYPE_DATA ||
435             type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
436             tiov[i++] = iov[j];
437     }
438 
439     if (data == NULL) {
440         /* a single DATA buffer must be present */
441         code = EINVAL;
442         goto cleanup;
443     }
444 
445     /* PADDING | TRAILER */
446     tpadding = &tiov[i++];
447     tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
448     tpadding->buffer.length = 0;
449     tpadding->buffer.value = NULL;
450 
451     ttrailer = &tiov[i++];
452     ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;
453 
454     switch (toktype2) {
455     case KG2_TOK_MIC_MSG:
456     case KG2_TOK_WRAP_MSG:
457     case KG2_TOK_DEL_CTX: {
458         size_t ec, rrc;
459         krb5_enctype enctype;
460         unsigned int k5_headerlen = 0;
461         unsigned int k5_trailerlen = 0;
462 
463         if (ctx->have_acceptor_subkey)
464             enctype = ctx->acceptor_subkey->keyblock.enctype;
465         else
466             enctype = ctx->subkey->keyblock.enctype;
467         conf_req_flag = ((ptr[0] & FLAG_WRAP_CONFIDENTIAL) != 0);
468         ec = conf_req_flag ? load_16_be(ptr + 2) : 0;
469         rrc = load_16_be(ptr + 4);
470 
471         if (rrc != 0) {
472             if (!gss_krb5int_rotate_left((unsigned char *)stream->buffer.value + 16,
473                                          stream->buffer.length - 16, rrc)) {
474                 code = ENOMEM;
475                 goto cleanup;
476             }
477             store_16_be(0, ptr + 4); /* set RRC to zero */
478         }
479 
480         if (conf_req_flag) {
481             code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
482             if (code != 0)
483                 goto cleanup;
484             theader->buffer.length += k5_headerlen; /* length validated later */
485         }
486 
487         /* no PADDING for CFX, EC is used instead */
488         code = krb5_c_crypto_length(context, enctype,
489                                     conf_req_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM,
490                                     &k5_trailerlen);
491         if (code != 0)
492             goto cleanup;
493 
494         ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) + k5_trailerlen;
495         ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
496             stream->buffer.length - ttrailer->buffer.length;
497         break;
498     }
499     case KG_TOK_MIC_MSG:
500     case KG_TOK_WRAP_MSG:
501     case KG_TOK_DEL_CTX:
502         theader->buffer.length += ctx->cksum_size +
503             kg_confounder_size(context, ctx->enc->keyblock.enctype);
504 
505         /*
506          * we can't set the padding accurately until decryption;
507          * kg_fixup_padding_iov() will take care of this
508          */
509         tpadding->buffer.length = 1;
510         tpadding->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - 1;
511 
512         /* no TRAILER for pre-CFX */
513         ttrailer->buffer.length = 0;
514         ttrailer->buffer.value = NULL;
515 
516         break;
517     default:
518         code = (OM_uint32)G_BAD_TOK_HEADER;
519         major_status = GSS_S_DEFECTIVE_TOKEN;
520         goto cleanup;
521         break;
522     }
523 
524     /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
525     /* Old: GSS-Header | Conf        | Data  | Pad |                               */
526     /* CFX: GSS-Header | Kerb-Header | Data  |     | EC | E(Header) | Kerb-Trailer */
527     /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/
528 
529     /* validate lengths */
530     if (stream->buffer.length < theader->buffer.length +
531         tpadding->buffer.length +
532         ttrailer->buffer.length)
533     {
534         code = (OM_uint32)KRB5_BAD_MSIZE;
535         major_status = GSS_S_DEFECTIVE_TOKEN;
536         goto cleanup;
537     }
538 
539     /* setup data */
540     tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
541         tpadding->buffer.length - theader->buffer.length;
542 
543     assert(data != NULL);
544 
545     if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
546         code = kg_allocate_iov(tdata, tdata->buffer.length);
547         if (code != 0)
548             goto cleanup;
549         memcpy(tdata->buffer.value,
550                (unsigned char *)stream->buffer.value + theader->buffer.length, tdata->buffer.length);
551     } else
552         tdata->buffer.value = (unsigned char *)stream->buffer.value + theader->buffer.length;
553 
554     assert(i <= iov_count + 2);
555 
556     major_status = kg_unseal_iov_token(&code, ctx, conf_state, qop_state,
557                                        tiov, i, toktype);
558     if (major_status == GSS_S_COMPLETE)
559         *data = *tdata;
560     else
561         kg_release_iov(tdata, 1);
562 
563 cleanup:
564     if (tiov != NULL)
565         free(tiov);
566 
567     *minor_status = code;
568 
569     return major_status;
570 }
571 
572 OM_uint32
kg_unseal_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count,int toktype)573 kg_unseal_iov(OM_uint32 *minor_status,
574               gss_ctx_id_t context_handle,
575               int *conf_state,
576               gss_qop_t *qop_state,
577               gss_iov_buffer_desc *iov,
578               int iov_count,
579               int toktype)
580 {
581     krb5_gss_ctx_id_rec *ctx;
582     OM_uint32 code;
583 
584     ctx = (krb5_gss_ctx_id_rec *)context_handle;
585     if (ctx->terminated || !ctx->established) {
586         *minor_status = KG_CTX_INCOMPLETE;
587         return GSS_S_NO_CONTEXT;
588     }
589 
590     if (kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) {
591         code = kg_unseal_stream_iov(minor_status, ctx, conf_state, qop_state,
592                                     iov, iov_count, toktype);
593     } else {
594         code = kg_unseal_iov_token(minor_status, ctx, conf_state, qop_state,
595                                    iov, iov_count, toktype);
596     }
597 
598     return code;
599 }
600 
601 OM_uint32 KRB5_CALLCONV
krb5_gss_unwrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)602 krb5_gss_unwrap_iov(OM_uint32 *minor_status,
603                     gss_ctx_id_t context_handle,
604                     int *conf_state,
605                     gss_qop_t *qop_state,
606                     gss_iov_buffer_desc *iov,
607                     int iov_count)
608 {
609     OM_uint32 major_status;
610 
611     major_status = kg_unseal_iov(minor_status, context_handle,
612                                  conf_state, qop_state,
613                                  iov, iov_count, KG_TOK_WRAP_MSG);
614 
615     return major_status;
616 }
617 
618 OM_uint32 KRB5_CALLCONV
krb5_gss_verify_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)619 krb5_gss_verify_mic_iov(OM_uint32 *minor_status,
620                         gss_ctx_id_t context_handle,
621                         gss_qop_t *qop_state,
622                         gss_iov_buffer_desc *iov,
623                         int iov_count)
624 {
625     OM_uint32 major_status;
626 
627     major_status = kg_unseal_iov(minor_status, context_handle,
628                                  NULL, qop_state,
629                                  iov, iov_count, KG_TOK_MIC_MSG);
630 
631     return major_status;
632 }
633