2 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
6 * 1. Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
13 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
14 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
15 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
16 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
17 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
18 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
19 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
39 #include <openssl/evp.h>
50 #ifdef HAVE_GSS_OID_TO_MECH
51 #include <gssapi/gssapi_ext.h>
52 #endif /* HAVE_GSS_OID_TO_MECH */
57 } ssh_gss_kex_mapping
;
59 static ssh_gss_kex_mapping
**gss_enc2oid
= NULL
;
61 static void ssh_gssapi_encode_oid_for_kex(const gss_OID oid
, char **enc_name
);
62 static char *ssh_gssapi_make_kexalgs_list(gss_OID_set mechs
,
63 const char *old_kexalgs
);
66 * Populate gss_enc2oid table and return list of kexnames.
68 * If called with both mechs == GSS_C_NULL_OID_SET and kexname_list == NULL
69 * then cached gss_enc2oid table is cleaned up.
72 ssh_gssapi_mech_oids_to_kexnames(const gss_OID_set mechs
, char **kexname_list
)
74 ssh_gss_kex_mapping
**new_gss_enc2oid
, **p
;
79 if (kexname_list
!= NULL
)
80 *kexname_list
= NULL
; /* default to failed */
82 if (mechs
!= GSS_C_NULL_OID_SET
|| kexname_list
== NULL
) {
83 /* Cleanup gss_enc2oid table */
84 for (p
= gss_enc2oid
; p
!= NULL
&& *p
!= NULL
; p
++) {
87 ssh_gssapi_release_oid(&(*p
)->oid
);
94 if (mechs
== GSS_C_NULL_OID_SET
&& kexname_list
== NULL
)
95 return; /* nothing left to do */
99 /* Populate gss_enc2oid table */
100 new_gss_enc2oid
= xmalloc(sizeof (ssh_gss_kex_mapping
*) *
102 memset(new_gss_enc2oid
, 0,
103 sizeof (ssh_gss_kex_mapping
*) * (mechs
->count
+ 1));
105 for (i
= 0; i
< mechs
->count
; i
++) {
106 mech
= &mechs
->elements
[i
];
107 ssh_gssapi_encode_oid_for_kex((const gss_OID
)mech
,
114 xmalloc(sizeof (ssh_gss_kex_mapping
));
115 (new_gss_enc2oid
[i
])->encoded
= enc_name
;
116 (new_gss_enc2oid
[i
])->oid
=
117 ssh_gssapi_dup_oid(&mechs
->elements
[i
]);
120 /* Do this last to avoid run-ins with fatal_cleanups */
121 gss_enc2oid
= new_gss_enc2oid
;
125 return; /* nothing left to do */
127 /* Make kex name list */
129 for (p
= gss_enc2oid
; p
&& *p
; p
++) {
130 buffer_put_char(&buf
, ',');
131 buffer_append(&buf
, (*p
)->encoded
, strlen((*p
)->encoded
));
134 if (buffer_len(&buf
) == 0) {
139 buffer_consume(&buf
, 1); /* consume leading ',' */
140 buffer_put_char(&buf
, '\0');
142 *kexname_list
= xstrdup(buffer_ptr(&buf
));
147 ssh_gssapi_mech_oid_to_kexname(const gss_OID mech
, char **kexname
)
149 ssh_gss_kex_mapping
**p
;
151 if (mech
== GSS_C_NULL_OID
|| !kexname
)
154 *kexname
= NULL
; /* default to not found */
156 for (p
= gss_enc2oid
; p
&& *p
; p
++) {
157 if (mech
->length
== (*p
)->oid
->length
&&
158 memcmp(mech
->elements
, (*p
)->oid
->elements
,
160 *kexname
= xstrdup((*p
)->encoded
);
167 ssh_gssapi_encode_oid_for_kex(mech
, kexname
);
171 ssh_gssapi_oid_of_kexname(const char *kexname
, gss_OID
*mech
)
173 ssh_gss_kex_mapping
**p
;
175 if (!mech
|| !kexname
|| !*kexname
)
178 *mech
= GSS_C_NULL_OID
; /* default to not found */
183 for (p
= gss_enc2oid
; p
&& *p
; p
++) {
184 if (strcmp(kexname
, (*p
)->encoded
) == 0) {
193 ssh_gssapi_encode_oid_for_kex(const gss_OID oid
, char **enc_name
)
198 const EVP_MD
*evp_md
= EVP_md5();
200 uchar_t digest
[EVP_MAX_MD_SIZE
];
203 if (oid
== GSS_C_NULL_OID
|| !enc_name
)
208 oidlen
= oid
->length
;
210 /* No GSS mechs have OIDs as long as 128 -- simplify DER encoding */
212 return; /* fail gracefully */
215 * NOTE: If we need to support SSH_BUG_GSSAPI_BER this is where
218 * That means using "Se3H81ismmOC3OE+FwYCiQ==" for the Kerberos
219 * V mech and "N3+k7/4wGxHyuP8Yxi4RhA==" for the GSI mech. Ick.
224 /* UNIVERSAL class tag for OBJECT IDENTIFIER */
225 buffer_put_char(&buf
, 0x06);
226 buffer_put_char(&buf
, oidlen
); /* one octet DER length -- see above */
229 buffer_append(&buf
, oid
->elements
, oidlen
);
232 EVP_DigestInit(&md
, evp_md
);
233 EVP_DigestUpdate(&md
, buffer_ptr(&buf
), buffer_len(&buf
));
234 EVP_DigestFinal(&md
, digest
, NULL
);
237 /* Base 64 encoding */
238 encoded
= xmalloc(EVP_MD_size(evp_md
)*2);
239 enclen
= __b64_ntop(digest
, EVP_MD_size(evp_md
),
240 encoded
, EVP_MD_size(evp_md
) * 2);
242 buffer_append(&buf
, KEX_GSS_SHA1
, sizeof (KEX_GSS_SHA1
) - 1);
243 buffer_append(&buf
, encoded
, enclen
);
244 buffer_put_char(&buf
, '\0');
246 debug2("GSS-API Mechanism encoded as %s", encoded
);
249 *enc_name
= xstrdup(buffer_ptr(&buf
));
254 ssh_gssapi_make_kexalgs_list(gss_OID_set mechs
, const char *old_kexalgs
)
256 char *gss_kexalgs
, *new_kexalgs
;
259 if (mechs
== GSS_C_NULL_OID_SET
)
260 return (xstrdup(old_kexalgs
)); /* never null */
262 ssh_gssapi_mech_oids_to_kexnames(mechs
, &gss_kexalgs
);
264 if (gss_kexalgs
== NULL
|| *gss_kexalgs
== '\0')
265 return (xstrdup(old_kexalgs
)); /* never null */
267 if (old_kexalgs
== NULL
|| *old_kexalgs
== '\0')
268 return (gss_kexalgs
);
270 len
= strlen(old_kexalgs
) + strlen(gss_kexalgs
) + 2;
271 new_kexalgs
= xmalloc(len
);
272 (void) snprintf(new_kexalgs
, len
, "%s,%s", gss_kexalgs
, old_kexalgs
);
275 return (new_kexalgs
);
279 ssh_gssapi_modify_kex(Kex
*kex
, gss_OID_set mechs
, char **proposal
)
281 char *kexalgs
, *orig_kexalgs
, *p
;
282 char **hostalg
, *orig_hostalgs
, *new_hostalgs
;
284 gss_OID_set dup_mechs
;
288 if (kex
== NULL
|| proposal
== NULL
||
289 proposal
[PROPOSAL_KEX_ALGS
] == NULL
) {
290 fatal("INTERNAL ERROR (%s)", __func__
);
293 orig_hostalgs
= proposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
];
295 if (kex
->mechs
== GSS_C_NULL_OID_SET
&& mechs
== GSS_C_NULL_OID_SET
)
296 return; /* didn't offer GSS last time, not offering now */
298 if (kex
->mechs
== GSS_C_NULL_OID_SET
|| mechs
== GSS_C_NULL_OID_SET
)
299 goto mod_offer
; /* didn't offer last time or not offering now */
301 /* Check if mechs is congruent to kex->mechs (last offered) */
302 if (kex
->mechs
->count
== mechs
->count
) {
303 int present
, matches
= 0;
305 for (i
= 0; i
< mechs
->count
; i
++) {
306 maj
= gss_test_oid_set_member(&min
,
307 &kex
->mechs
->elements
[i
], mechs
, &present
);
309 if (GSS_ERROR(maj
)) {
310 mechs
= GSS_C_NULL_OID_SET
;
314 matches
+= (present
) ? 1 : 0;
317 if (matches
== kex
->mechs
->count
)
318 return; /* no change in offer from last time */
323 * Remove previously offered mechs from PROPOSAL_KEX_ALGS proposal
325 * ASSUMPTION: GSS-API kex algs always go in front, so removing
326 * them is a matter of skipping them.
328 p
= kexalgs
= orig_kexalgs
= proposal
[PROPOSAL_KEX_ALGS
];
329 while (p
!= NULL
&& *p
!= '\0' &&
330 strncmp(p
, KEX_GSS_SHA1
, strlen(KEX_GSS_SHA1
)) == 0) {
332 if ((p
= strchr(p
, ',')) == NULL
)
338 kexalgs
= proposal
[PROPOSAL_KEX_ALGS
] = xstrdup(kexalgs
);
341 (void) gss_release_oid_set(&min
, &kex
->mechs
); /* ok if !kex->mechs */
343 /* Not offering GSS kex algorithms now -> all done */
344 if (mechs
== GSS_C_NULL_OID_SET
)
347 /* Remember mechs we're offering */
348 maj
= gss_create_empty_oid_set(&min
, &dup_mechs
);
351 for (i
= 0; i
< mechs
->count
; i
++) {
352 maj
= gss_add_oid_set_member(&min
, &mechs
->elements
[i
],
355 if (GSS_ERROR(maj
)) {
356 (void) gss_release_oid_set(&min
, &dup_mechs
);
361 /* Add mechs to kex algorithms ... */
362 proposal
[PROPOSAL_KEX_ALGS
] = ssh_gssapi_make_kexalgs_list(mechs
,
365 kex
->mechs
= dup_mechs
; /* remember what we offer now */
368 * ... and add null host key alg, if it wasn't there before, but
369 * not if we're the server and we have other host key algs to
372 * NOTE: Never remove "null" host key alg once added.
374 if (orig_hostalgs
== NULL
|| *orig_hostalgs
== '\0') {
375 proposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = xstrdup("null");
376 } else if (!kex
->server
) {
377 hostalgs
= xsplit(orig_hostalgs
, ',');
378 for (hostalg
= hostalgs
; *hostalg
!= NULL
; hostalg
++) {
379 if (strcmp(*hostalg
, "null") == 0) {
380 xfree_split_list(hostalgs
);
384 xfree_split_list(hostalgs
);
386 if (kex
->mechs
!= GSS_C_NULL_OID_SET
) {
389 len
= strlen(orig_hostalgs
) + sizeof (",null");
390 new_hostalgs
= xmalloc(len
);
391 (void) snprintf(new_hostalgs
, len
, "%s,null",
393 proposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = new_hostalgs
;
396 xfree(orig_hostalgs
);
401 * Yes, we harcode OIDs for some things, for now it's all we can do.
403 * We have to reference particular mechanisms due to lack of generality
404 * in the GSS-API in several areas: authorization, mapping principal
405 * names to usernames, "storing" delegated credentials, and discovering
406 * whether a mechanism is a pseudo-mechanism that negotiates mechanisms.
408 * Even if they were in some header file or if __gss_mech_to_oid()
409 * and/or __gss_oid_to_mech() were standard we'd still have to hardcode
410 * the mechanism names, and since the mechanisms have no standard names
411 * other than their OIDs it's actually worse [less portable] to hardcode
412 * names than OIDs, so we hardcode OIDs.
414 * SPNEGO is a difficult problem though -- it MUST NOT be used in SSHv2,
415 * but that's true of all possible pseudo-mechanisms that can perform
416 * mechanism negotiation, and SPNEGO could have new OIDs in the future.
417 * Ideally we could query each mechanism for its feature set and then
418 * ignore any mechanisms that negotiate mechanisms, but, alas, there's
419 * no interface to do that.
421 * In the future, if the necessary generic GSS interfaces for the issues
422 * listed above are made available (even if they differ by platform, as
423 * we can expect authorization interfaces will), then we can stop
424 * referencing specific mechanism OIDs here.
427 ssh_gssapi_is_spnego(gss_OID oid
)
429 return (oid
->length
== 6 &&
430 memcmp("\053\006\001\005\005\002", oid
->elements
, 6) == 0);
434 ssh_gssapi_is_krb5(gss_OID oid
)
436 return (oid
->length
== 9 &&
437 memcmp("\x2A\x86\x48\x86\xF7\x12\x01\x02\x02",
438 oid
->elements
, 9) == 0);
442 ssh_gssapi_is_dh(gss_OID oid
)
444 return (oid
->length
== 9 &&
445 memcmp("\053\006\004\001\052\002\032\002\005",
446 oid
->elements
, 9) == 0);
450 ssh_gssapi_is_gsi(gss_OID oid
)
452 return (oid
->length
== 9 &&
453 memcmp("\x2B\x06\x01\x04\x01\x9B\x50\x01\x01",
454 oid
->elements
, 9) == 0);
458 ssh_gssapi_oid_to_name(gss_OID oid
)
460 #ifdef HAVE_GSS_OID_TO_MECH
461 return (__gss_oid_to_mech(oid
));
463 if (ssh_gssapi_is_krb5(oid
))
465 if (ssh_gssapi_is_gsi(oid
))
467 return ("(unknown)");
468 #endif /* HAVE_GSS_OID_TO_MECH */
472 ssh_gssapi_oid_to_str(gss_OID oid
)
474 #ifdef HAVE_GSS_OID_TO_STR
475 gss_buffer_desc str_buf
;
479 maj
= gss_oid_to_str(&min
, oid
, &str_buf
);
482 return (xstrdup("<gss_oid_to_str() failed>"));
484 str
= xmalloc(str_buf
.length
+ 1);
485 memset(str
, 0, str_buf
.length
+ 1);
486 strlcpy(str
, str_buf
.value
, str_buf
.length
+ 1);
487 (void) gss_release_buffer(&min
, &str_buf
);
491 return (xstrdup("<gss_oid_to_str() unsupported>"));
492 #endif /* HAVE_GSS_OID_TO_STR */
495 /* Check that the OID in a data stream matches that in the context */
497 ssh_gssapi_check_mech_oid(Gssctxt
*ctx
, void *data
, size_t len
)
500 return (ctx
!= NULL
&& ctx
->desired_mech
!= GSS_C_NULL_OID
&&
501 ctx
->desired_mech
->length
== len
&&
502 memcmp(ctx
->desired_mech
->elements
, data
, len
) == 0);
505 /* Set the contexts OID from a data stream */
507 ssh_gssapi_set_oid_data(Gssctxt
*ctx
, void *data
, size_t len
)
509 if (ctx
->actual_mech
!= GSS_C_NULL_OID
) {
510 xfree(ctx
->actual_mech
->elements
);
511 xfree(ctx
->actual_mech
);
513 ctx
->actual_mech
= xmalloc(sizeof (gss_OID_desc
));
514 ctx
->actual_mech
->length
= len
;
515 ctx
->actual_mech
->elements
= xmalloc(len
);
516 memcpy(ctx
->actual_mech
->elements
, data
, len
);
519 /* Set the contexts OID */
521 ssh_gssapi_set_oid(Gssctxt
*ctx
, gss_OID oid
)
523 ssh_gssapi_set_oid_data(ctx
, oid
->elements
, oid
->length
);
526 /* All this effort to report an error ... */
529 ssh_gssapi_error(Gssctxt
*ctxt
, const char *where
)
531 char *errmsg
= ssh_gssapi_last_error(ctxt
, NULL
, NULL
);
534 debug("GSS-API error while %s: %s", where
, errmsg
);
536 debug("GSS-API error: %s", errmsg
);
538 /* ssh_gssapi_last_error() can't return NULL */
543 ssh_gssapi_last_error(Gssctxt
*ctxt
, OM_uint32
*major_status
,
544 OM_uint32
*minor_status
)
546 OM_uint32 lmin
, more
;
548 gss_OID mech
= GSS_C_NULL_OID
;
556 /* Get status codes from the Gssctxt */
559 /* Output them if desired */
564 /* Get mechanism for minor status display */
565 mech
= (ctxt
->actual_mech
!= GSS_C_NULL_OID
) ?
566 ctxt
->actual_mech
: ctxt
->desired_mech
;
567 } else if (major_status
&& minor_status
) {
571 maj
= GSS_S_COMPLETE
;
576 /* The GSSAPI error */
578 gss_display_status(&lmin
, maj
, GSS_C_GSS_CODE
,
579 GSS_C_NULL_OID
, &more
, &msg
);
581 buffer_append(&b
, msg
.value
, msg
.length
);
582 buffer_put_char(&b
, '\n');
583 gss_release_buffer(&lmin
, &msg
);
586 /* The mechanism specific error */
589 * If mech == GSS_C_NULL_OID we may get the default
590 * mechanism, whatever that is, and that may not be
593 gss_display_status(&lmin
, min
, GSS_C_MECH_CODE
, mech
, &more
,
596 buffer_append(&b
, msg
.value
, msg
.length
);
597 buffer_put_char(&b
, '\n');
599 gss_release_buffer(&lmin
, &msg
);
602 buffer_put_char(&b
, '\0');
603 ret
= xstrdup(buffer_ptr(&b
));
610 * Initialise our GSSAPI context. We use this opaque structure to contain all
611 * of the data which both the client and server need to persist across
612 * {accept,init}_sec_context calls, so that when we do it from the userauth
613 * stuff life is a little easier
616 ssh_gssapi_build_ctx(Gssctxt
**ctx
, int client
, gss_OID mech
)
621 newctx
= (Gssctxt
*)xmalloc(sizeof (Gssctxt
));
622 memset(newctx
, 0, sizeof (Gssctxt
));
625 newctx
->local
= client
;
626 newctx
->desired_mech
= ssh_gssapi_dup_oid(mech
);
628 /* This happens to be redundant given the memset() above */
629 newctx
->major
= GSS_S_COMPLETE
;
630 newctx
->context
= GSS_C_NO_CONTEXT
;
631 newctx
->actual_mech
= GSS_C_NULL_OID
;
632 newctx
->desired_name
= GSS_C_NO_NAME
;
633 newctx
->src_name
= GSS_C_NO_NAME
;
634 newctx
->dst_name
= GSS_C_NO_NAME
;
635 newctx
->creds
= GSS_C_NO_CREDENTIAL
;
636 newctx
->deleg_creds
= GSS_C_NO_CREDENTIAL
;
638 newctx
->default_creds
= (*ctx
!= NULL
) ? (*ctx
)->default_creds
: 0;
640 ssh_gssapi_delete_ctx(ctx
);
646 ssh_gssapi_dup_oid(gss_OID oid
)
650 new_oid
= xmalloc(sizeof (gss_OID_desc
));
652 new_oid
->elements
= xmalloc(oid
->length
);
653 new_oid
->length
= oid
->length
;
654 memcpy(new_oid
->elements
, oid
->elements
, oid
->length
);
660 ssh_gssapi_make_oid(size_t length
, void *elements
)
665 oid
.elements
= elements
;
667 return (ssh_gssapi_dup_oid(&oid
));
671 ssh_gssapi_release_oid(gss_OID
*oid
)
675 if (oid
&& *oid
== GSS_C_NULL_OID
)
677 (void) gss_release_oid(&min
, oid
);
679 if (*oid
== GSS_C_NULL_OID
)
680 return; /* libgss did own this gss_OID and released it */
682 xfree((*oid
)->elements
);
684 *oid
= GSS_C_NULL_OID
;
689 gss_buffer_t external_name
;
694 /* Delete our context, providing it has been built correctly */
696 ssh_gssapi_delete_ctx(Gssctxt
**ctx
)
703 if ((*ctx
)->context
!= GSS_C_NO_CONTEXT
)
704 gss_delete_sec_context(&ms
, &(*ctx
)->context
, GSS_C_NO_BUFFER
);
707 if ((*ctx
)->desired_mech
!= GSS_C_NULL_OID
)
708 ssh_gssapi_release_oid(&(*ctx
)->desired_mech
);
710 if ((*ctx
)->actual_mech
!= GSS_C_NULL_OID
)
711 (void) ssh_gssapi_release_oid(&(*ctx
)->actual_mech
);
712 if ((*ctx
)->desired_name
!= GSS_C_NO_NAME
)
713 gss_release_name(&ms
, &(*ctx
)->desired_name
);
715 if ((*ctx
)->src_name
!= GSS_C_NO_NAME
)
716 gss_release_name(&ms
, &(*ctx
)->src_name
);
718 if ((*ctx
)->dst_name
!= GSS_C_NO_NAME
)
719 gss_release_name(&ms
, &(*ctx
)->dst_name
);
720 if ((*ctx
)->creds
!= GSS_C_NO_CREDENTIAL
)
721 gss_release_cred(&ms
, &(*ctx
)->creds
);
722 if ((*ctx
)->deleg_creds
!= GSS_C_NO_CREDENTIAL
)
723 gss_release_cred(&ms
, &(*ctx
)->deleg_creds
);
729 /* Create a GSS hostbased service principal name for a given server hostname */
731 ssh_gssapi_import_name(Gssctxt
*ctx
, const char *server_host
)
733 gss_buffer_desc name_buf
;
736 /* Build target principal */
737 name_buf
.length
= strlen(SSH_GSS_HOSTBASED_SERVICE
) +
738 strlen(server_host
) + 1; /* +1 for '@' */
739 name_buf
.value
= xmalloc(name_buf
.length
+ 1); /* +1 for NUL */
740 ret
= snprintf(name_buf
.value
, name_buf
.length
+ 1, "%s@%s",
741 SSH_GSS_HOSTBASED_SERVICE
, server_host
);
743 debug3("%s: snprintf() returned %d, expected %d", __func__
, ret
,
746 ctx
->major
= gss_import_name(&ctx
->minor
, &name_buf
,
747 GSS_C_NT_HOSTBASED_SERVICE
, &ctx
->desired_name
);
749 if (GSS_ERROR(ctx
->major
)) {
750 ssh_gssapi_error(ctx
, "calling GSS_Import_name()");
754 xfree(name_buf
.value
);
760 ssh_gssapi_get_mic(Gssctxt
*ctx
, gss_buffer_desc
*buffer
, gss_buffer_desc
*hash
)
763 ctx
->major
= gss_get_mic(&ctx
->minor
, ctx
->context
,
764 GSS_C_QOP_DEFAULT
, buffer
, hash
);
765 if (GSS_ERROR(ctx
->major
))
766 ssh_gssapi_error(ctx
, "while getting MIC");
771 ssh_gssapi_verify_mic(Gssctxt
*ctx
, gss_buffer_desc
*buffer
,
772 gss_buffer_desc
*hash
)
776 ctx
->major
= gss_verify_mic(&ctx
->minor
, ctx
->context
, buffer
,
778 if (GSS_ERROR(ctx
->major
))
779 ssh_gssapi_error(ctx
, "while verifying MIC");