break; /* go and try something else */
} else if (ret == -1) {
c_write_str(ssh, "Wrong passphrase.\r\n"); /* FIXME */
- s->tried_publickey = 0;
got_passphrase = FALSE;
/* and try again */
+ } else {
+ assert(0 && "unexpected return from loadrsakey()");
}
}
crWaitUntil(pktin);
if (pktin->type == SSH1_SMSG_FAILURE) {
c_write_str(ssh, "Server refused our public key.\r\n");
- continue; /* go and try password */
+ continue; /* go and try something else */
}
if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
bombout(("Bizarre response to offer of public key"));
if (flags & FLAG_VERBOSE)
c_write_str(ssh, "Failed to authenticate with"
" our public key.\r\n");
- continue; /* go and try password */
+ continue; /* go and try something else */
} else if (pktin->type != SSH1_SMSG_SUCCESS) {
bombout(("Bizarre response to RSA authentication response"));
crStop(0);
{
struct do_ssh2_authconn_state {
enum {
- AUTH_INVALID, AUTH_PUBLICKEY_AGENT, AUTH_PUBLICKEY_FILE,
- AUTH_PASSWORD,
- AUTH_KEYBOARD_INTERACTIVE
- } method;
- enum {
AUTH_TYPE_NONE,
AUTH_TYPE_PUBLICKEY,
AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
prompts_t *cur_prompt;
int num_prompts;
char username[100];
+ char *password;
int got_username;
void *publickey_blob;
int publickey_bloblen;
break;
}
- if (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {
- /* FIXME: perhaps we should support this? */
- bombout(("PASSWD_CHANGEREQ not yet supported"));
- crStopV;
- } else if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {
- bombout(("Strange packet received during authentication: type %d",
- pktin->type));
+ if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {
+ bombout(("Strange packet received during authentication: "
+ "type %d", pktin->type));
crStopV;
}
s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;
continue; /* process this new message */
}
- s->method = AUTH_PUBLICKEY_FILE;
logevent("Offer of public key accepted");
/*
/*
* Keyboard-interactive authentication.
*/
- char *name, *inst, *lang;
- int name_len, inst_len, lang_len;
- int i;
- s->method = AUTH_KEYBOARD_INTERACTIVE;
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
crWaitUntilV(pktin);
if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
/* Server is not willing to do keyboard-interactive
- * at all. Give up on it entirely. */
+ * at all (or, bizarrely but legally, accepts the
+ * user without actually issuing any prompts).
+ * Give up on it entirely. */
s->gotit = TRUE;
if (pktin->type == SSH2_MSG_USERAUTH_FAILURE)
logevent("Keyboard-interactive authentication refused");
}
/*
- * We've got a fresh USERAUTH_INFO_REQUEST.
- * Get the preamble and start building a prompt.
+ * Loop while the server continues to send INFO_REQUESTs.
*/
- 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;
- 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;
- }
- s->cur_prompt->instruction =
- dupprintf("Using keyboard-interactive authentication.%s%.*s",
- inst_len ? "\n" : "", inst_len, inst);
- s->cur_prompt->instr_reqd = TRUE;
+ while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
- /*
- * Get the prompts 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>: ";
+ char *name, *inst, *lang;
+ int name_len, inst_len, lang_len;
+ int i;
- ssh_pkt_getstring(pktin, &prompt, &prompt_len);
- echo = ssh2_pkt_getbool(pktin);
- if (!prompt_len) {
- prompt = noprompt;
- prompt_len = lenof(noprompt)-1;
+ /*
+ * 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;
+ 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;
}
- add_prompt(s->cur_prompt,
- dupprintf("%.*s", prompt_len, prompt),
- echo, SSH_MAX_PASSWORD_LEN);
- }
+ /* FIXME: ugly to print "Using..." in prompt _every_
+ * time round. Can this be done more subtly? */
+ s->cur_prompt->instruction =
+ dupprintf("Using keyboard-interactive authentication.%s%.*s",
+ inst_len ? "\n" : "", inst_len, inst);
+ s->cur_prompt->instr_reqd = TRUE;
- /*
- * Get the user's responses.
- */
- if (s->num_prompts) {
- 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;
+ /*
+ * Get the prompts 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, SSH_MAX_PASSWORD_LEN);
}
- 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;
+
+ /*
+ * Get the user's responses.
+ */
+ if (s->num_prompts) {
+ 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 responses to the server.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+ s->pktout->forcepad = 256;
+ ssh2_pkt_adduint32(s->pktout, s->num_prompts);
+ for (i=0; i < s->num_prompts; i++) {
+ dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
+ ssh2_pkt_addstring(s->pktout,
+ s->cur_prompt->prompts[i]->result);
+ end_log_omission(ssh, s->pktout);
}
+ ssh2_pkt_send(ssh, s->pktout);
+
+ /*
+ * Get the next packet in case it's another
+ * INFO_REQUEST.
+ */
+ crWaitUntilV(pktin);
+
}
/*
- * Send the responses to the server.
+ * We should have SUCCESS or FAILURE now.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
- s->pktout->forcepad = 256;
- ssh2_pkt_adduint32(s->pktout, s->num_prompts);
- for (i=0; i < s->num_prompts; i++) {
- dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
- ssh2_pkt_addstring(s->pktout,
- s->cur_prompt->prompts[i]->result);
- end_log_omission(ssh, s->pktout);
- }
- ssh2_pkt_send(ssh, s->pktout);
- s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE; /*FIXME?*/
+ 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 */
- s->method = AUTH_PASSWORD;
ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh->pkt_ctx |= SSH2_PKTCTX_PASSWORD;
TRUE);
crStopV;
}
+ /*
+ * Squirrel away the password. (We may need it later if
+ * asked to change it.)
+ */
+ s->password = dupstr(s->cur_prompt->prompts[0]->result);
+ free_prompts(s->cur_prompt);
/*
* Send the password packet.
ssh2_pkt_addstring(s->pktout, "password");
ssh2_pkt_addbool(s->pktout, FALSE);
dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
- ssh2_pkt_addstring(s->pktout,
- s->cur_prompt->prompts[0]->result);
- free_prompts(s->cur_prompt);
+ ssh2_pkt_addstring(s->pktout, s->password);
end_log_omission(ssh, s->pktout);
ssh2_pkt_send(ssh, s->pktout);
logevent("Sent password");
- s->type = AUTH_TYPE_PASSWORD; /*FIXME?*/
+ s->type = AUTH_TYPE_PASSWORD;
+
+ /*
+ * Wait for next packet, in case it's a password change
+ * request.
+ */
+ crWaitUntilV(pktin);
+ changereq_first_time = TRUE;
+
+ while (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {
+
+ /*
+ * We're being asked for a new password
+ * (perhaps not for the first time).
+ * Loop until the server accepts it.
+ */
+
+ int got_new = FALSE; /* not live over crReturn */
+ char *prompt; /* not live over crReturn */
+ int prompt_len; /* not live over crReturn */
+
+ {
+ char *msg;
+ if (changereq_first_time)
+ msg = "Server requested password change";
+ else
+ msg = "Server rejected new password";
+ logevent(msg);
+ c_write_str(ssh, msg);
+ c_write_str(ssh, "\r\n");
+ }
+
+ ssh_pkt_getstring(pktin, &prompt, &prompt_len);
+
+ s->cur_prompt = new_prompts(ssh->frontend);
+ s->cur_prompt->to_server = TRUE;
+ s->cur_prompt->name = dupstr("New SSH password");
+ s->cur_prompt->instruction =
+ dupprintf("%.*s", prompt_len, prompt);
+ s->cur_prompt->instr_reqd = TRUE;
+ add_prompt(s->cur_prompt, dupstr("Enter new password: "),
+ FALSE, SSH_MAX_PASSWORD_LEN);
+ add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
+ FALSE, SSH_MAX_PASSWORD_LEN);
+
+ /*
+ * Loop until the user manages to enter the same
+ * password twice.
+ */
+ while (!got_new) {
+
+ 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.
+ */
+ /* burn the evidence */
+ free_prompts(s->cur_prompt);
+ memset(s->password, 0, strlen(s->password));
+ sfree(s->password);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ TRUE);
+ crStopV;
+ }
+
+ /*
+ * Check the two passwords match.
+ */
+ got_new = (strcmp(s->cur_prompt->prompts[0]->result,
+ s->cur_prompt->prompts[1]->result)
+ == 0);
+ if (!got_new)
+ /* They don't. Silly user. */
+ c_write_str(ssh, "Passwords do not match\r\n");
+
+ }
+
+ /*
+ * Send the new password (along with the old one).
+ * (see above for padding rationale)
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ s->pktout->forcepad = 256;
+ ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ /* service requested */
+ ssh2_pkt_addstring(s->pktout, "password");
+ ssh2_pkt_addbool(s->pktout, TRUE);
+ dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
+ ssh2_pkt_addstring(s->pktout, s->password);
+ ssh2_pkt_addstring(s->pktout,
+ s->cur_prompt->prompts[0]->result);
+ free_prompts(s->cur_prompt);
+ end_log_omission(ssh, s->pktout);
+ ssh2_pkt_send(ssh, s->pktout);
+ logevent("Sent new password");
+
+ /*
+ * Now see what the server has to say about it.
+ * (If it's CHANGEREQ again, it's not happy with the
+ * new password.)
+ */
+ crWaitUntilV(pktin);
+ changereq_first_time = FALSE;
+
+ }
+
+ /*
+ * We need to reexamine the current pktin at the top
+ * of the loop. Either:
+ * - we weren't asked to change password at all, in
+ * which case it's a SUCCESS or FAILURE with the
+ * usual meaning
+ * - we sent a new password, and the server was
+ * either OK with it (SUCCESS or FAILURE w/partial
+ * success) or unhappy with the _old_ password
+ * (FAILURE w/o partial success)
+ * In any of these cases, we go back to the top of
+ * the loop and start again.
+ */
+ s->gotit = TRUE;
+
+ /*
+ * We don't need the old password any more, in any
+ * case. Burn the evidence.
+ */
+ memset(s->password, 0, strlen(s->password));
+ sfree(s->password);
} else {