+ sfree(key->comment);
+ sfree(key);
+ }
+
+#ifndef NO_GSSAPI
+ } else if (s->can_gssapi && !s->tried_gssapi) {
+
+ /* GSSAPI Authentication */
+
+ int micoffset, len;
+ char *data;
+ Ssh_gss_buf mic;
+ s->type = AUTH_TYPE_GSSAPI;
+ s->tried_gssapi = TRUE;
+ s->gotit = TRUE;
+ ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
+
+ /*
+ * Pick the highest GSS library on the preference
+ * list.
+ */
+ {
+ int i, j;
+ s->gsslib = NULL;
+ for (i = 0; i < ngsslibs; i++) {
+ int want_id = conf_get_int_int(ssh->conf,
+ CONF_ssh_gsslist, i);
+ for (j = 0; j < ssh->gsslibs->nlibraries; j++)
+ if (ssh->gsslibs->libraries[j].id == want_id) {
+ s->gsslib = &ssh->gsslibs->libraries[j];
+ goto got_gsslib; /* double break */
+ }
+ }
+ got_gsslib:
+ /*
+ * We always expect to have found something in
+ * the above loop: we only came here if there
+ * was at least one viable GSS library, and the
+ * preference list should always mention
+ * everything and only change the order.
+ */
+ assert(s->gsslib);
+ }
+
+ if (s->gsslib->gsslogmsg)
+ logevent(s->gsslib->gsslogmsg);
+
+ /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(s->pktout, ssh->username);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
+ logevent("Attempting GSSAPI authentication");
+
+ /* add mechanism info */
+ s->gsslib->indicate_mech(s->gsslib, &s->gss_buf);
+
+ /* number of GSSAPI mechanisms */
+ ssh2_pkt_adduint32(s->pktout,1);
+
+ /* length of OID + 2 */
+ ssh2_pkt_adduint32(s->pktout, s->gss_buf.length + 2);
+ ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE);
+
+ /* length of OID */
+ ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.length);
+
+ ssh_pkt_adddata(s->pktout, s->gss_buf.value,
+ s->gss_buf.length);
+ ssh2_pkt_send(ssh, s->pktout);
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {
+ logevent("GSSAPI authentication request refused");
+ continue;
+ }
+
+ /* check returned packet ... */
+
+ ssh_pkt_getstring(pktin, &data, &len);
+ s->gss_rcvtok.value = data;
+ s->gss_rcvtok.length = len;
+ if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
+ ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
+ ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
+ memcmp((char *)s->gss_rcvtok.value + 2,
+ s->gss_buf.value,s->gss_buf.length) ) {
+ logevent("GSSAPI authentication - wrong response from server");
+ continue;
+ }
+
+ /* now start running */
+ s->gss_stat = s->gsslib->import_name(s->gsslib,
+ ssh->fullhostname,
+ &s->gss_srv_name);
+ if (s->gss_stat != SSH_GSS_OK) {
+ if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
+ logevent("GSSAPI import name failed - Bad service name");
+ else
+ logevent("GSSAPI import name failed");
+ continue;
+ }
+
+ /* fetch TGT into GSS engine */
+ s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &s->gss_ctx);
+
+ if (s->gss_stat != SSH_GSS_OK) {
+ logevent("GSSAPI authentication failed to get credentials");
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ continue;
+ }
+
+ /* initial tokens are empty */
+ SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
+ SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
+
+ /* now enter the loop */
+ do {
+ s->gss_stat = s->gsslib->init_sec_context
+ (s->gsslib,
+ &s->gss_ctx,
+ s->gss_srv_name,
+ conf_get_int(ssh->conf, CONF_gssapifwd),
+ &s->gss_rcvtok,
+ &s->gss_sndtok);
+
+ if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
+ s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
+ logevent("GSSAPI authentication initialisation failed");
+
+ if (s->gsslib->display_status(s->gsslib, s->gss_ctx,
+ &s->gss_buf) == SSH_GSS_OK) {
+ logevent(s->gss_buf.value);
+ sfree(s->gss_buf.value);
+ }
+
+ break;
+ }
+ logevent("GSSAPI authentication initialised");
+
+ /* Client and server now exchange tokens until GSSAPI
+ * no longer says CONTINUE_NEEDED */
+
+ if (s->gss_sndtok.length != 0) {
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+ ssh_pkt_addstring_start(s->pktout);
+ ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length);
+ ssh2_pkt_send(ssh, s->pktout);
+ s->gsslib->free_tok(s->gsslib, &s->gss_sndtok);
+ }
+
+ if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) {
+ logevent("GSSAPI authentication - bad server response");
+ s->gss_stat = SSH_GSS_FAILURE;
+ break;
+ }
+ ssh_pkt_getstring(pktin, &data, &len);
+ s->gss_rcvtok.value = data;
+ s->gss_rcvtok.length = len;
+ }
+ } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
+
+ if (s->gss_stat != SSH_GSS_OK) {
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
+ continue;
+ }
+ logevent("GSSAPI authentication loop finished OK");
+
+ /* Now send the MIC */
+
+ s->pktout = ssh2_pkt_init(0);
+ micoffset = s->pktout->length;
+ ssh_pkt_addstring_start(s->pktout);
+ ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len);
+ ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST);
+ ssh_pkt_addstring(s->pktout, ssh->username);
+ ssh_pkt_addstring(s->pktout, "ssh-connection");
+ ssh_pkt_addstring(s->pktout, "gssapi-with-mic");
+
+ s->gss_buf.value = (char *)s->pktout->data + micoffset;
+ s->gss_buf.length = s->pktout->length - micoffset;
+
+ s->gsslib->get_mic(s->gsslib, s->gss_ctx, &s->gss_buf, &mic);
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC);
+ ssh_pkt_addstring_start(s->pktout);
+ ssh_pkt_addstring_data(s->pktout, mic.value, mic.length);
+ ssh2_pkt_send(ssh, s->pktout);
+ s->gsslib->free_mic(s->gsslib, &mic);
+
+ s->gotit = FALSE;
+
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
+ continue;
+#endif
+ } else if (s->can_keyb_inter && !s->kbd_inter_refused) {
+
+ /*
+ * Keyboard-interactive authentication.
+ */
+
+ s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+
+ ssh->pkt_actx = SSH2_PKTCTX_KBDINTER;
+
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(s->pktout, ssh->username);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ /* service requested */
+ ssh2_pkt_addstring(s->pktout, "keyboard-interactive");
+ /* method */
+ ssh2_pkt_addstring(s->pktout, ""); /* lang */
+ ssh2_pkt_addstring(s->pktout, ""); /* submethods */
+ ssh2_pkt_send(ssh, s->pktout);
+
+ logevent("Attempting keyboard-interactive authentication");
+
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
+ /* Server is not willing to do keyboard-interactive
+ * at all (or, bizarrely but legally, accepts the
+ * user without actually issuing any prompts).
+ * Give up on it entirely. */
+ s->gotit = TRUE;
+ s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
+ s->kbd_inter_refused = TRUE; /* don't try it again */
+ continue;
+ }
+
+ /*
+ * Loop while the server continues to send INFO_REQUESTs.
+ */
+ while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
+
+ char *name, *inst, *lang;
+ int name_len, inst_len, lang_len;
+ int i;
+
+ /*
+ * We've got a fresh USERAUTH_INFO_REQUEST.
+ * Get the preamble and start building a prompt.
+ */
+ ssh_pkt_getstring(pktin, &name, &name_len);
+ ssh_pkt_getstring(pktin, &inst, &inst_len);
+ ssh_pkt_getstring(pktin, &lang, &lang_len);
+ s->cur_prompt = new_prompts(ssh->frontend);
+ s->cur_prompt->to_server = TRUE;
+
+ /*
+ * Get any prompt(s) from the packet.
+ */
+ s->num_prompts = ssh_pkt_getuint32(pktin);
+ for (i = 0; i < s->num_prompts; i++) {
+ char *prompt;
+ int prompt_len;
+ int echo;
+ static char noprompt[] =
+ "<server failed to send prompt>: ";
+
+ ssh_pkt_getstring(pktin, &prompt, &prompt_len);
+ echo = ssh2_pkt_getbool(pktin);
+ if (!prompt_len) {
+ prompt = noprompt;
+ prompt_len = lenof(noprompt)-1;
+ }
+ add_prompt(s->cur_prompt,
+ dupprintf("%.*s", prompt_len, prompt),
+ echo);
+ }
+
+ if (name_len) {
+ /* FIXME: better prefix to distinguish from
+ * local prompts? */
+ s->cur_prompt->name =
+ dupprintf("SSH server: %.*s", name_len, name);
+ s->cur_prompt->name_reqd = TRUE;
+ } else {
+ s->cur_prompt->name =
+ dupstr("SSH server authentication");
+ s->cur_prompt->name_reqd = FALSE;
+ }
+ /* We add a prefix to try to make it clear that a prompt
+ * has come from the server.
+ * FIXME: ugly to print "Using..." in prompt _every_
+ * time round. Can this be done more subtly? */
+ /* Special case: for reasons best known to themselves,
+ * some servers send k-i requests with no prompts and
+ * nothing to display. Keep quiet in this case. */
+ if (s->num_prompts || name_len || inst_len) {
+ s->cur_prompt->instruction =
+ dupprintf("Using keyboard-interactive authentication.%s%.*s",
+ inst_len ? "\n" : "", inst_len, inst);
+ s->cur_prompt->instr_reqd = TRUE;
+ } else {
+ s->cur_prompt->instr_reqd = FALSE;
+ }
+
+ /*
+ * Display any instructions, and get the user's
+ * response(s).
+ */
+ {
+ int ret; /* not live over crReturn */
+ ret = get_userpass_input(s->cur_prompt, NULL, 0);
+ while (ret < 0) {
+ ssh->send_ok = 1;
+ crWaitUntilV(!pktin);
+ ret = get_userpass_input(s->cur_prompt, in, inlen);
+ ssh->send_ok = 0;
+ }
+ if (!ret) {
+ /*
+ * Failed to get responses. Terminate.
+ */
+ free_prompts(s->cur_prompt);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ TRUE);
+ crStopV;
+ }
+ }
+
+ /*
+ * Send the response(s) to the server.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+ ssh2_pkt_adduint32(s->pktout, s->num_prompts);
+ for (i=0; i < s->num_prompts; i++) {
+ ssh2_pkt_addstring(s->pktout,
+ s->cur_prompt->prompts[i]->result);
+ }
+ ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
+
+ /*
+ * Free the prompts structure from this iteration.
+ * If there's another, a new one will be allocated
+ * when we return to the top of this while loop.
+ */
+ free_prompts(s->cur_prompt);
+
+ /*
+ * Get the next packet in case it's another
+ * INFO_REQUEST.
+ */
+ crWaitUntilV(pktin);
+
+ }
+
+ /*
+ * We should have SUCCESS or FAILURE now.
+ */
+ s->gotit = TRUE;
+
+ } else if (s->can_passwd) {
+
+ /*
+ * Plain old password authentication.
+ */
+ int ret; /* not live over crReturn */
+ int changereq_first_time; /* not live over crReturn */
+
+ ssh->pkt_actx = SSH2_PKTCTX_PASSWORD;
+
+ s->cur_prompt = new_prompts(ssh->frontend);
+ s->cur_prompt->to_server = TRUE;
+ s->cur_prompt->name = dupstr("SSH password");
+ add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
+ ssh->username,
+ ssh->savedhost),
+ FALSE);
+
+ ret = get_userpass_input(s->cur_prompt, NULL, 0);
+ while (ret < 0) {
+ ssh->send_ok = 1;
+ crWaitUntilV(!pktin);
+ ret = get_userpass_input(s->cur_prompt, in, inlen);
+ ssh->send_ok = 0;
+ }
+ if (!ret) {
+ /*
+ * Failed to get responses. Terminate.
+ */
+ free_prompts(s->cur_prompt);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ TRUE);
+ crStopV;