+}
+
+#ifdef GSSAPI
+int
+userauth_gssapi(Authctxt *authctxt)
+{
+ Gssctxt *gssctxt = NULL;
+ static gss_OID_set gss_supported = NULL;
+ static u_int mech = 0;
+ OM_uint32 min;
+ int ok = 0;
+ char *gss_host = NULL;
+
+ if (!options.gss_authentication) {
+ verbose("GSSAPI authentication disabled.");
+ return 0;
+ }
+
+ if (options.gss_trust_dns)
+ gss_host = (char *)get_canonical_hostname(1);
+ else
+ gss_host = (char *)authctxt->host;
+
+ /* Try one GSSAPI method at a time, rather than sending them all at
+ * once. */
+
+ if (gss_supported == NULL)
+ gss_indicate_mechs(&min, &gss_supported);
+
+ /* Check to see if the mechanism is usable before we offer it */
+ while (mech < gss_supported->count && !ok) {
+ /* My DER encoding requires length<128 */
+ if (gss_supported->elements[mech].length < 128 &&
+ ssh_gssapi_check_mechanism(&gssctxt,
+ &gss_supported->elements[mech], gss_host)) {
+ ok = 1; /* Mechanism works */
+ } else {
+ mech++;
+ }
+ }
+
+ if (!ok)
+ return 0;
+
+ authctxt->methoddata=(void *)gssctxt;
+
+ packet_start(SSH2_MSG_USERAUTH_REQUEST);
+ packet_put_cstring(authctxt->server_user);
+ packet_put_cstring(authctxt->service);
+ packet_put_cstring(authctxt->method->name);
+
+ packet_put_int(1);
+
+ packet_put_int((gss_supported->elements[mech].length) + 2);
+ packet_put_char(SSH_GSS_OIDTYPE);
+ packet_put_char(gss_supported->elements[mech].length);
+ packet_put_raw(gss_supported->elements[mech].elements,
+ gss_supported->elements[mech].length);
+
+ packet_send();
+
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error);
+ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
+
+ mech++; /* Move along to next candidate */
+
+ return 1;
+}
+
+static OM_uint32
+process_gssapi_token(void *ctxt, gss_buffer_t recv_tok)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt = authctxt->methoddata;
+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gssbuf;
+ OM_uint32 status, ms, flags;
+ Buffer b;
+
+ status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds,
+ recv_tok, &send_tok, &flags);
+
+ if (send_tok.length > 0) {
+ if (GSS_ERROR(status))
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+ else
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+
+ packet_put_string(send_tok.value, send_tok.length);
+ packet_send();
+ gss_release_buffer(&ms, &send_tok);
+ }
+
+ if (status == GSS_S_COMPLETE) {
+ /* send either complete or MIC, depending on mechanism */
+ if (strcmp(authctxt->method->name,"gssapi")==0 ||
+ (!(flags & GSS_C_INTEG_FLAG))) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE);
+ packet_send();
+ } else {
+ ssh_gssapi_buildmic(&b, authctxt->server_user,
+ authctxt->service, "gssapi-with-mic");
+
+ gssbuf.value = buffer_ptr(&b);
+ gssbuf.length = buffer_len(&b);
+
+ status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic);
+
+ if (!GSS_ERROR(status)) {
+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC);
+ packet_put_string(mic.value, mic.length);
+
+ packet_send();
+ }
+
+ buffer_free(&b);
+ gss_release_buffer(&ms, &mic);
+ }
+ }
+
+ return status;
+}
+
+void
+input_gssapi_response(int type, u_int32_t plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Gssctxt *gssctxt;
+ u_int oidlen;
+ u_char *oidv;
+
+ if (authctxt == NULL)
+ fatal("input_gssapi_response: no authentication context");
+ gssctxt = authctxt->methoddata;
+
+ /* Setup our OID */
+ oidv = packet_get_string(&oidlen);
+
+ if (oidlen <= 2 ||
+ oidv[0] != SSH_GSS_OIDTYPE ||
+ oidv[1] != oidlen - 2) {
+ xfree(oidv);
+ debug("Badly encoded mechanism OID received");
+ userauth(authctxt, NULL);
+ return;
+ }