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