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