+ } else {
+ c_write_str(ssh, "Further authentication required\r\n");
+ logevent("Further authentication required");
+ }
+
+ s->can_pubkey =
+ in_commasep_string("publickey", methods, methlen);
+ s->can_passwd =
+ in_commasep_string("password", methods, methlen);
+ s->can_keyb_inter = conf_get_int(ssh->conf, CONF_try_ki_auth) &&
+ in_commasep_string("keyboard-interactive", methods, methlen);
+#ifndef NO_GSSAPI
+ if (!ssh->gsslibs)
+ ssh->gsslibs = ssh_gss_setup(ssh->conf);
+ s->can_gssapi = conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
+ in_commasep_string("gssapi-with-mic", methods, methlen) &&
+ ssh->gsslibs->nlibraries > 0;
+#endif
+ }
+
+ ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
+
+ if (s->can_pubkey && !s->done_agent && s->nkeys) {
+
+ /*
+ * Attempt public-key authentication using a key from Pageant.
+ */
+
+ ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
+
+ logeventf(ssh, "Trying Pageant key #%d", s->keyi);
+
+ /* Unpack key from agent response */
+ s->pklen = GET_32BIT(s->agentp);
+ s->agentp += 4;
+ s->pkblob = (char *)s->agentp;
+ s->agentp += s->pklen;
+ s->alglen = GET_32BIT(s->pkblob);
+ s->alg = s->pkblob + 4;
+ s->commentlen = GET_32BIT(s->agentp);
+ s->agentp += 4;
+ s->commentp = (char *)s->agentp;
+ s->agentp += s->commentlen;
+ /* s->agentp now points at next key, if any */
+
+ /* See if server will accept it */
+ 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, "publickey");
+ /* method */
+ ssh2_pkt_addbool(s->pktout, FALSE); /* no signature included */
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
+ ssh2_pkt_send(ssh, s->pktout);
+ s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
+
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
+
+ /* Offer of key refused. */
+ s->gotit = TRUE;
+
+ } else {
+
+ void *vret;
+
+ if (flags & FLAG_VERBOSE) {
+ c_write_str(ssh, "Authenticating with "
+ "public key \"");
+ c_write(ssh, s->commentp, s->commentlen);
+ c_write_str(ssh, "\" from agent\r\n");
+ }
+
+ /*
+ * Server is willing to accept the key.
+ * Construct a SIGN_REQUEST.
+ */
+ 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, "publickey");
+ /* method */
+ ssh2_pkt_addbool(s->pktout, TRUE); /* signature included */
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
+
+ /* Ask agent for signature. */
+ s->siglen = s->pktout->length - 5 + 4 +
+ ssh->v2_session_id_len;
+ if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+ s->siglen -= 4;
+ s->len = 1; /* message type */
+ s->len += 4 + s->pklen; /* key blob */
+ s->len += 4 + s->siglen; /* data to sign */
+ s->len += 4; /* flags */
+ s->agentreq = snewn(4 + s->len, char);
+ PUT_32BIT(s->agentreq, s->len);
+ s->q = s->agentreq + 4;
+ *s->q++ = SSH2_AGENTC_SIGN_REQUEST;
+ PUT_32BIT(s->q, s->pklen);
+ s->q += 4;
+ memcpy(s->q, s->pkblob, s->pklen);
+ s->q += s->pklen;
+ PUT_32BIT(s->q, s->siglen);
+ s->q += 4;
+ /* Now the data to be signed... */
+ if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
+ PUT_32BIT(s->q, ssh->v2_session_id_len);
+ s->q += 4;
+ }
+ memcpy(s->q, ssh->v2_session_id,
+ ssh->v2_session_id_len);
+ s->q += ssh->v2_session_id_len;
+ memcpy(s->q, s->pktout->data + 5,
+ s->pktout->length - 5);
+ s->q += s->pktout->length - 5;
+ /* And finally the (zero) flags word. */
+ PUT_32BIT(s->q, 0);
+ if (!agent_query(s->agentreq, s->len + 4,
+ &vret, &s->retlen,
+ ssh_agent_callback, ssh)) {
+ do {
+ crReturnV;
+ if (pktin) {
+ bombout(("Unexpected data from server"
+ " while waiting for agent"
+ " response"));
+ crStopV;
+ }
+ } while (pktin || inlen > 0);
+ vret = ssh->agent_response;
+ s->retlen = ssh->agent_response_len;
+ }
+ s->ret = vret;
+ sfree(s->agentreq);
+ if (s->ret) {
+ if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
+ logevent("Sending Pageant's response");
+ ssh2_add_sigblob(ssh, s->pktout,
+ s->pkblob, s->pklen,
+ s->ret + 9,
+ GET_32BIT(s->ret + 5));
+ ssh2_pkt_send(ssh, s->pktout);
+ s->type = AUTH_TYPE_PUBLICKEY;
+ } else {
+ /* FIXME: less drastic response */
+ bombout(("Pageant failed to answer challenge"));
+ crStopV;
+ }
+ }
+ }
+
+ /* Do we have any keys left to try? */
+ if (s->pkblob_in_agent) {
+ s->done_agent = TRUE;
+ s->tried_pubkey_config = TRUE;
+ } else {
+ s->keyi++;
+ if (s->keyi >= s->nkeys)
+ s->done_agent = TRUE;
+ }
+
+ } else if (s->can_pubkey && s->publickey_blob &&
+ !s->tried_pubkey_config) {
+
+ struct ssh2_userkey *key; /* not live over crReturn */
+ char *passphrase; /* not live over crReturn */
+
+ ssh->pkt_actx = SSH2_PKTCTX_PUBLICKEY;
+
+ s->tried_pubkey_config = TRUE;
+
+ /*
+ * Try the public key supplied in the configuration.
+ *
+ * First, offer the public blob to see if the server is
+ * willing to accept it.
+ */
+ 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, "publickey"); /* method */
+ ssh2_pkt_addbool(s->pktout, FALSE);
+ /* no signature included */
+ ssh2_pkt_addstring(s->pktout, s->publickey_algorithm);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout,
+ (char *)s->publickey_blob,
+ s->publickey_bloblen);
+ ssh2_pkt_send(ssh, s->pktout);
+ logevent("Offered public key");
+
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
+ /* Key refused. Give up. */
+ s->gotit = TRUE; /* reconsider message next loop */
+ s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;
+ continue; /* process this new message */
+ }
+ logevent("Offer of public key accepted");
+
+ /*
+ * Actually attempt a serious authentication using
+ * the key.
+ */
+ if (flags & FLAG_VERBOSE) {
+ c_write_str(ssh, "Authenticating with public key \"");
+ c_write_str(ssh, s->publickey_comment);
+ c_write_str(ssh, "\"\r\n");
+ }
+ key = NULL;
+ while (!key) {
+ const char *error; /* not live over crReturn */
+ if (s->publickey_encrypted) {
+ /*
+ * Get a passphrase from the user.
+ */
+ int ret; /* need not be kept over crReturn */
+ s->cur_prompt = new_prompts(ssh->frontend);
+ s->cur_prompt->to_server = FALSE;
+ s->cur_prompt->name = dupstr("SSH key passphrase");
+ add_prompt(s->cur_prompt,
+ dupprintf("Passphrase for key \"%.100s\": ",
+ s->publickey_comment),
+ 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 a passphrase. Terminate. */
+ free_prompts(s->cur_prompt);
+ ssh_disconnect(ssh, NULL,
+ "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ TRUE);
+ crStopV;
+ }
+ passphrase =
+ dupstr(s->cur_prompt->prompts[0]->result);
+ free_prompts(s->cur_prompt);
+ } else {
+ passphrase = NULL; /* no passphrase needed */
+ }
+
+ /*
+ * Try decrypting the key.
+ */
+ s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
+ key = ssh2_load_userkey(s->keyfile, passphrase, &error);
+ if (passphrase) {
+ /* burn the evidence */
+ smemclr(passphrase, strlen(passphrase));
+ sfree(passphrase);
+ }
+ if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
+ if (passphrase &&
+ (key == SSH2_WRONG_PASSPHRASE)) {
+ c_write_str(ssh, "Wrong passphrase\r\n");
+ key = NULL;
+ /* and loop again */
+ } else {
+ c_write_str(ssh, "Unable to load private key (");
+ c_write_str(ssh, error);
+ c_write_str(ssh, ")\r\n");
+ key = NULL;
+ break; /* try something else */
+ }
+ }
+ }
+
+ if (key) {
+ unsigned char *pkblob, *sigblob, *sigdata;
+ int pkblob_len, sigblob_len, sigdata_len;
+ int p;
+
+ /*
+ * We have loaded the private key and the server
+ * has announced that it's willing to accept it.
+ * Hallelujah. Generate a signature and send it.
+ */
+ 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, "publickey");
+ /* method */
+ ssh2_pkt_addbool(s->pktout, TRUE);
+ /* signature follows */
+ ssh2_pkt_addstring(s->pktout, key->alg->name);
+ pkblob = key->alg->public_blob(key->data,
+ &pkblob_len);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, (char *)pkblob,
+ pkblob_len);
+
+ /*
+ * The data to be signed is:
+ *
+ * string session-id
+ *
+ * followed by everything so far placed in the
+ * outgoing packet.
+ */
+ sigdata_len = s->pktout->length - 5 + 4 +
+ ssh->v2_session_id_len;
+ if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+ sigdata_len -= 4;
+ sigdata = snewn(sigdata_len, unsigned char);
+ p = 0;
+ if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
+ PUT_32BIT(sigdata+p, ssh->v2_session_id_len);
+ p += 4;
+ }
+ memcpy(sigdata+p, ssh->v2_session_id,
+ ssh->v2_session_id_len);
+ p += ssh->v2_session_id_len;
+ memcpy(sigdata+p, s->pktout->data + 5,
+ s->pktout->length - 5);
+ p += s->pktout->length - 5;
+ assert(p == sigdata_len);
+ sigblob = key->alg->sign(key->data, (char *)sigdata,
+ sigdata_len, &sigblob_len);
+ ssh2_add_sigblob(ssh, s->pktout, pkblob, pkblob_len,
+ sigblob, sigblob_len);
+ sfree(pkblob);
+ sfree(sigblob);
+ sfree(sigdata);
+
+ ssh2_pkt_send(ssh, s->pktout);
+ logevent("Sent public key signature");
+ s->type = AUTH_TYPE_PUBLICKEY;
+ key->alg->freekey(key->data);