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