#define BUG_SSH2_REKEY 64
#define BUG_SSH2_PK_SESSIONID 128
+/*
+ * Codes for terminal modes.
+ * Most of these are the same in SSH-1 and SSH-2.
+ * This list is derived from draft-ietf-secsh-connect-25 and
+ * SSH-1 RFC-1.2.31.
+ */
+static const struct {
+ const char* const mode;
+ int opcode;
+ enum { TTY_OP_CHAR, TTY_OP_BOOL } type;
+} ssh_ttymodes[] = {
+ /* "V" prefix discarded for special characters relative to SSH specs */
+ { "INTR", 1, TTY_OP_CHAR },
+ { "QUIT", 2, TTY_OP_CHAR },
+ { "ERASE", 3, TTY_OP_CHAR },
+ { "KILL", 4, TTY_OP_CHAR },
+ { "EOF", 5, TTY_OP_CHAR },
+ { "EOL", 6, TTY_OP_CHAR },
+ { "EOL2", 7, TTY_OP_CHAR },
+ { "START", 8, TTY_OP_CHAR },
+ { "STOP", 9, TTY_OP_CHAR },
+ { "SUSP", 10, TTY_OP_CHAR },
+ { "DSUSP", 11, TTY_OP_CHAR },
+ { "REPRINT", 12, TTY_OP_CHAR },
+ { "WERASE", 13, TTY_OP_CHAR },
+ { "LNEXT", 14, TTY_OP_CHAR },
+ { "FLUSH", 15, TTY_OP_CHAR },
+ { "SWTCH", 16, TTY_OP_CHAR },
+ { "STATUS", 17, TTY_OP_CHAR },
+ { "DISCARD", 18, TTY_OP_CHAR },
+ { "IGNPAR", 30, TTY_OP_BOOL },
+ { "PARMRK", 31, TTY_OP_BOOL },
+ { "INPCK", 32, TTY_OP_BOOL },
+ { "ISTRIP", 33, TTY_OP_BOOL },
+ { "INLCR", 34, TTY_OP_BOOL },
+ { "IGNCR", 35, TTY_OP_BOOL },
+ { "ICRNL", 36, TTY_OP_BOOL },
+ { "IUCLC", 37, TTY_OP_BOOL },
+ { "IXON", 38, TTY_OP_BOOL },
+ { "IXANY", 39, TTY_OP_BOOL },
+ { "IXOFF", 40, TTY_OP_BOOL },
+ { "IMAXBEL", 41, TTY_OP_BOOL },
+ { "ISIG", 50, TTY_OP_BOOL },
+ { "ICANON", 51, TTY_OP_BOOL },
+ { "XCASE", 52, TTY_OP_BOOL },
+ { "ECHO", 53, TTY_OP_BOOL },
+ { "ECHOE", 54, TTY_OP_BOOL },
+ { "ECHOK", 55, TTY_OP_BOOL },
+ { "ECHONL", 56, TTY_OP_BOOL },
+ { "NOFLSH", 57, TTY_OP_BOOL },
+ { "TOSTOP", 58, TTY_OP_BOOL },
+ { "IEXTEN", 59, TTY_OP_BOOL },
+ { "ECHOCTL", 60, TTY_OP_BOOL },
+ { "ECHOKE", 61, TTY_OP_BOOL },
+ { "PENDIN", 62, TTY_OP_BOOL }, /* XXX is this a real mode? */
+ { "OPOST", 70, TTY_OP_BOOL },
+ { "OLCUC", 71, TTY_OP_BOOL },
+ { "ONLCR", 72, TTY_OP_BOOL },
+ { "OCRNL", 73, TTY_OP_BOOL },
+ { "ONOCR", 74, TTY_OP_BOOL },
+ { "ONLRET", 75, TTY_OP_BOOL },
+ { "CS7", 90, TTY_OP_BOOL },
+ { "CS8", 91, TTY_OP_BOOL },
+ { "PARENB", 92, TTY_OP_BOOL },
+ { "PARODD", 93, TTY_OP_BOOL }
+};
+
+/* Miscellaneous other tty-related constants. */
+#define SSH_TTY_OP_END 0
+/* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */
+#define SSH1_TTY_OP_ISPEED 192
+#define SSH1_TTY_OP_OSPEED 193
+#define SSH2_TTY_OP_ISPEED 128
+#define SSH2_TTY_OP_OSPEED 129
+
+/* Helper functions for parsing tty-related config. */
+static unsigned int ssh_tty_parse_specchar(char *s)
+{
+ unsigned int ret;
+ if (*s) {
+ char *next = NULL;
+ ret = ctrlparse(s, &next);
+ if (!next) ret = s[0];
+ } else {
+ ret = 255; /* special value meaning "don't set" */
+ }
+ return ret;
+}
+static unsigned int ssh_tty_parse_boolean(char *s)
+{
+ if (stricmp(s, "yes") == 0 ||
+ stricmp(s, "on") == 0 ||
+ stricmp(s, "true") == 0 ||
+ stricmp(s, "+") == 0)
+ return 1; /* true */
+ else if (stricmp(s, "no") == 0 ||
+ stricmp(s, "off") == 0 ||
+ stricmp(s, "false") == 0 ||
+ stricmp(s, "-") == 0)
+ return 0; /* false */
+ else
+ return (atoi(s) != 0);
+}
+
#define translate(x) if (type == x) return #x
#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x
static char *ssh1_pkt_type(int type)
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
-static void *nullmac_make_context(void)
-{
- return NULL;
-}
-static void nullmac_free_context(void *handle)
-{
-}
-static void nullmac_key(void *handle, unsigned char *key)
-{
-}
-static void nullmac_generate(void *handle, unsigned char *blk, int len,
- unsigned long seq)
-{
-}
-static int nullmac_verify(void *handle, unsigned char *blk, int len,
- unsigned long seq)
-{
- return 1;
-}
-const static struct ssh_mac ssh_mac_none = {
- nullmac_make_context, nullmac_free_context, nullmac_key,
- nullmac_generate, nullmac_verify, "none", 0
-};
const static struct ssh_mac *macs[] = {
- &ssh_sha1, &ssh_md5, &ssh_mac_none
+ &ssh_sha1, &ssh_md5
};
const static struct ssh_mac *buggymacs[] = {
- &ssh_sha1_buggy, &ssh_md5, &ssh_mac_none
+ &ssh_sha1_buggy, &ssh_md5
};
static void *ssh_comp_none_init(void)
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
int close_expected;
+ int clean_exit;
tree234 *rportfwds, *portfwds;
*/
int fallback_cmd;
+ bufchain banner; /* accumulates banners during do_ssh2_authconn */
/*
* Used for username and password input.
*/
pkt->logmode = PKTLOG_EMIT;
}
+/* Helper function for common bits of parsing cfg.ttymodes. */
+static void parse_ttymodes(Ssh ssh, char *modes,
+ void (*do_mode)(void *data, char *mode, char *val),
+ void *data)
+{
+ while (*modes) {
+ char *t = strchr(modes, '\t');
+ char *m = snewn(t-modes+1, char);
+ char *val;
+ strncpy(m, modes, t-modes);
+ m[t-modes] = '\0';
+ if (*(t+1) == 'A')
+ val = get_ttymode(ssh->frontend, m);
+ else
+ val = dupstr(t+2);
+ if (val)
+ do_mode(data, m, val);
+ sfree(m);
+ sfree(val);
+ modes += strlen(modes) + 1;
+ }
+}
+
static int ssh_channelcmp(void *av, void *bv)
{
struct ssh_channel *a = (struct ssh_channel *) av;
* ssh2_pkt_send() or ssh2_pkt_defer() either go straight to one of
* these or get queued, and then when the queue is later emptied
* the packets are all passed to defer_noqueue().
+ *
+ * When using a CBC-mode cipher, it's necessary to ensure that an
+ * attacker can't provide data to be encrypted using an IV that they
+ * know. We ensure this by prefixing each packet that might contain
+ * user data with an SSH_MSG_IGNORE. This is done using the deferral
+ * mechanism, so in this case send_noqueue() ends up redirecting to
+ * defer_noqueue(). If you don't like this inefficiency, don't use
+ * CBC.
*/
+static void ssh2_pkt_defer_noqueue(Ssh, struct Packet *, int);
+static void ssh_pkt_defersend(Ssh);
+
/*
* Send an SSH-2 packet immediately, without queuing or deferring.
*/
{
int len;
int backlog;
+ if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC)) {
+ /* We need to send two packets, so use the deferral mechanism. */
+ ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
+ ssh_pkt_defersend(ssh);
+ return;
+ }
len = ssh2_pkt_construct(ssh, pkt);
backlog = sk_write(ssh->s, (char *)pkt->data, len);
if (backlog > SSH_MAX_BACKLOG)
/*
* Defer an SSH-2 packet.
*/
-static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt)
+static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
{
- int len = ssh2_pkt_construct(ssh, pkt);
+ int len;
+ if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
+ ssh->deferred_len == 0 && !noignore) {
+ /*
+ * Interpose an SSH_MSG_IGNORE to ensure that user data don't
+ * get encrypted with a known IV.
+ */
+ struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
+ }
+ len = ssh2_pkt_construct(ssh, pkt);
if (ssh->deferred_len + len > ssh->deferred_size) {
ssh->deferred_size = ssh->deferred_len + len + 128;
ssh->deferred_send_data = sresize(ssh->deferred_send_data,
if (ssh->queueing)
ssh2_pkt_queue(ssh, pkt);
else
- ssh2_pkt_defer_noqueue(ssh, pkt);
+ ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
}
#endif
assert(!ssh->queueing);
for (i = 0; i < ssh->queuelen; i++)
- ssh2_pkt_defer_noqueue(ssh, ssh->queue[i]);
+ ssh2_pkt_defer_noqueue(ssh, ssh->queue[i], FALSE);
ssh->queuelen = 0;
ssh_pkt_defersend(ssh);
strcspn(verstring, "\015\012"), verstring);
sk_write(ssh->s, verstring, strlen(verstring));
sfree(verstring);
+ if (ssh->version == 2)
+ do_ssh2_transport(ssh, NULL, -1, NULL);
}
logeventf(ssh, "Using SSH protocol version %d", ssh->version);
Ssh ssh = (Ssh) plug;
int need_notify = ssh_do_close(ssh, FALSE);
- if (!error_msg && !ssh->close_expected) {
- error_msg = "Server unexpectedly closed network connection";
+ if (!error_msg) {
+ if (!ssh->close_expected)
+ error_msg = "Server unexpectedly closed network connection";
+ else
+ error_msg = "Server closed network connection";
}
if (need_notify)
notify_remote_exit(ssh->frontend);
- if (error_msg) {
- /* A socket error has occurred. */
+ if (error_msg)
logevent(error_msg);
+ if (!ssh->close_expected || !ssh->clean_exit)
connection_fatal(ssh->frontend, "%s", error_msg);
- } else {
- logevent("Server closed network connection");
- }
return 0;
}
}
/*
+ * Client-initiated disconnection. Send a DISCONNECT if `wire_reason'
+ * non-NULL, otherwise just close the connection. `client_reason' == NULL
+ * => log `wire_reason'.
+ */
+static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason,
+ int code, int clean_exit)
+{
+ char *error;
+ if (!client_reason)
+ client_reason = wire_reason;
+ if (client_reason)
+ error = dupprintf("Disconnected: %s", client_reason);
+ else
+ error = dupstr("Disconnected");
+ if (wire_reason) {
+ if (ssh->version == 1) {
+ send_packet(ssh, SSH1_MSG_DISCONNECT, PKT_STR, wire_reason,
+ PKT_END);
+ } else if (ssh->version == 2) {
+ struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ ssh2_pkt_adduint32(pktout, code);
+ ssh2_pkt_addstring(pktout, wire_reason);
+ ssh2_pkt_addstring(pktout, "en"); /* language tag */
+ ssh2_pkt_send_noqueue(ssh, pktout);
+ }
+ }
+ ssh->close_expected = TRUE;
+ ssh->clean_exit = clean_exit;
+ ssh_closing((Plug)ssh, error, 0, 0);
+ sfree(error);
+}
+
+/*
* Handle the key exchange and user authentication phases.
*/
static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at host key verification",
+ NULL, 0, TRUE);
crStop(0);
}
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
* get_line failed to get a username.
* Terminate.
*/
- logevent("No username provided. Abandoning session.");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStop(1);
}
} else {
* because one was supplied on the command line
* which has already failed to work). Terminate.
*/
- send_packet(ssh, SSH1_MSG_DISCONNECT,
- PKT_STR, "No more passwords available to try",
- PKT_END);
- logevent("Unable to authenticate");
- connection_fatal(ssh->frontend, "Unable to authenticate");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, FALSE);
crStop(1);
}
} else {
* encrypted packet, we close the session once
* we've sent EXIT_CONFIRMATION.
*/
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, NULL, 0, TRUE);
}
+/* Helper function to deal with sending tty modes for REQUEST_PTY */
+static void ssh1_send_ttymode(void *data, char *mode, char *val)
+{
+ struct Packet *pktout = (struct Packet *)data;
+ int i = 0;
+ unsigned int arg = 0;
+ while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++;
+ if (i == lenof(ssh_ttymodes)) return;
+ switch (ssh_ttymodes[i].type) {
+ case TTY_OP_CHAR:
+ arg = ssh_tty_parse_specchar(val);
+ break;
+ case TTY_OP_BOOL:
+ arg = ssh_tty_parse_boolean(val);
+ break;
+ }
+ ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);
+ ssh2_pkt_addbyte(pktout, arg);
+}
+
+
static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
struct Packet *pktin)
{
ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
data, sizeof(data), ssh->cfg.x11_auth);
x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
+ /*
+ * Note that while we blank the X authentication data here, we don't
+ * take any special action to blank the start of an X11 channel,
+ * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection
+ * without having session blanking enabled is likely to leak your
+ * cookie into the log.
+ */
if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, proto, PKT_STR, data,
+ PKT_STR, proto,
+ PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER,
PKT_INT, x11_get_screen_number(ssh->cfg.x11_display),
PKT_END);
} else {
send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, proto, PKT_STR, data, PKT_END);
+ PKT_STR, proto,
+ PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_END);
}
do {
crReturnV;
ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
if (!ssh->cfg.nopty) {
+ struct Packet *pkt;
/* Unpick the terminal-speed string. */
/* XXX perhaps we should allow no speeds to be sent. */
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
sscanf(ssh->cfg.termspeed, "%d,%d", &ssh->ospeed, &ssh->ispeed);
/* Send the pty request. */
- send_packet(ssh, SSH1_CMSG_REQUEST_PTY,
- PKT_STR, ssh->cfg.termtype,
- PKT_INT, ssh->term_height,
- PKT_INT, ssh->term_width,
- PKT_INT, 0, PKT_INT, 0, /* width,height in pixels */
- PKT_CHAR, 192, PKT_INT, ssh->ispeed, /* TTY_OP_ISPEED */
- PKT_CHAR, 193, PKT_INT, ssh->ospeed, /* TTY_OP_OSPEED */
- PKT_CHAR, 0, PKT_END);
+ pkt = ssh1_pkt_init(SSH1_CMSG_REQUEST_PTY);
+ ssh_pkt_addstring(pkt, ssh->cfg.termtype);
+ ssh_pkt_adduint32(pkt, ssh->term_height);
+ ssh_pkt_adduint32(pkt, ssh->term_width);
+ ssh_pkt_adduint32(pkt, 0); /* width in pixels */
+ ssh_pkt_adduint32(pkt, 0); /* height in pixels */
+ parse_ttymodes(ssh, ssh->cfg.ttymodes,
+ ssh1_send_ttymode, (void *)pkt);
+ ssh_pkt_addbyte(pkt, SSH1_TTY_OP_ISPEED);
+ ssh_pkt_adduint32(pkt, ssh->ispeed);
+ ssh_pkt_addbyte(pkt, SSH1_TTY_OP_OSPEED);
+ ssh_pkt_adduint32(pkt, ssh->ospeed);
+ ssh_pkt_addbyte(pkt, SSH_TTY_OP_END);
+ s_wrpkt(ssh, pkt);
ssh->state = SSH_STATE_INTERMED;
do {
crReturnV;
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at kex warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
+ 0, TRUE);
crStop(0);
}
}
}
ssh_set_frozen(ssh, 0);
if (s->dlgret == 0) {
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "User aborted at host key verification", NULL,
+ 0, TRUE);
crStop(0);
}
if (!s->got_session_id) { /* don't bother logging this in rekeys */
* not running in -N mode.)
*/
if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {
- logevent("All channels closed. Disconnecting");
-#if 0
/*
* We used to send SSH_MSG_DISCONNECT here,
* because I'd believed that _every_ conforming
* this is more polite than sending a
* DISCONNECT. So now we don't.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "All open channels closed");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
-#endif
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
}
}
*/
c = find234(ssh->channels, &localid, ssh_channelfind);
if (!c) {
- char buf[80];
- sprintf(buf, "Received channel request for nonexistent"
- " channel %d", localid);
- logevent(buf);
- pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(pktout, buf);
- ssh2_pkt_addstring(pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, pktout);
- connection_fatal(ssh->frontend, "%s", buf);
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ char *buf = dupprintf("Received channel request for nonexistent"
+ " channel %d", localid);
+ ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+ sfree(buf);
return;
}
}
/*
+ * Buffer banner messages for later display at some convenient point.
+ */
+static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
+{
+ /* Arbitrary limit to prevent unbounded inflation of buffer */
+ if (bufchain_size(&ssh->banner) <= 131072) {
+ char *banner = NULL;
+ int size = 0;
+ ssh_pkt_getstring(pktin, &banner, &size);
+ if (banner)
+ bufchain_add(&ssh->banner, banner, size);
+ }
+}
+
+/* Helper function to deal with sending tty modes for "pty-req" */
+static void ssh2_send_ttymode(void *data, char *mode, char *val)
+{
+ struct Packet *pktout = (struct Packet *)data;
+ int i = 0;
+ unsigned int arg = 0;
+ while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++;
+ if (i == lenof(ssh_ttymodes)) return;
+ switch (ssh_ttymodes[i].type) {
+ case TTY_OP_CHAR:
+ arg = ssh_tty_parse_specchar(val);
+ break;
+ case TTY_OP_BOOL:
+ arg = ssh_tty_parse_boolean(val);
+ break;
+ }
+ ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);
+ ssh2_pkt_adduint32(pktout, arg);
+}
+
+/*
* Handle the SSH-2 userauth and connection layers.
*/
static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
AUTH_TYPE_KEYBOARD_INTERACTIVE,
AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
} type;
+ int done_service_req;
int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
int tried_pubkey_config, tried_agent;
int kbd_inter_running, kbd_inter_refused;
crBegin(ssh->do_ssh2_authconn_crstate);
- /*
- * Request userauth protocol, and await a response to it.
- */
- s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
- ssh2_pkt_addstring(s->pktout, "ssh-userauth");
- ssh2_pkt_send(ssh, s->pktout);
- crWaitUntilV(pktin);
- if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) {
- bombout(("Server refused user authentication protocol"));
- crStopV;
+ s->done_service_req = FALSE;
+ s->we_are_in = FALSE;
+ if (!ssh->cfg.ssh_no_userauth) {
+ /*
+ * Request userauth protocol, and await a response to it.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ ssh2_pkt_addstring(s->pktout, "ssh-userauth");
+ ssh2_pkt_send(ssh, s->pktout);
+ crWaitUntilV(pktin);
+ if (pktin->type == SSH2_MSG_SERVICE_ACCEPT)
+ s->done_service_req = TRUE;
+ }
+ if (!s->done_service_req) {
+ /*
+ * Request connection protocol directly, without authentication.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ ssh2_pkt_send(ssh, s->pktout);
+ crWaitUntilV(pktin);
+ if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) {
+ s->we_are_in = TRUE; /* no auth required */
+ } else {
+ bombout(("Server refused service request"));
+ crStopV;
+ }
}
/*
*/
s->username[0] = '\0';
s->got_username = FALSE;
- do {
+ bufchain_init(&ssh->banner);
+ ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =
+ ssh2_msg_userauth_banner;
+ while (!s->we_are_in) {
/*
* Get a username.
*/
* get_line failed to get a username.
* Terminate.
*/
- logevent("No username provided. Abandoning session.");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
crStopV;
}
} else {
*/
if (!s->gotit)
crWaitUntilV(pktin);
- while (pktin->type == SSH2_MSG_USERAUTH_BANNER) {
- char *banner;
- int size;
+ /*
+ * Now is a convenient point to spew any banner material
+ * that we've accumulated. (This should ensure that when
+ * we exit the auth loop, we haven't any left to deal
+ * with.)
+ */
+ {
+ int size = bufchain_size(&ssh->banner);
/*
* Don't show the banner if we're operating in
* non-verbose non-interactive mode. (It's probably
* the banner will screw up processing on the
* output of (say) plink.)
*/
- if (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE)) {
- ssh_pkt_getstring(pktin, &banner, &size);
- if (banner)
- c_write_untrusted(ssh, banner, size);
+ if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {
+ char *banner = snewn(size, char);
+ bufchain_fetch(&ssh->banner, banner, size);
+ c_write_untrusted(ssh, banner, size);
+ sfree(banner);
}
- crWaitUntilV(pktin);
+ bufchain_clear(&ssh->banner);
}
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted");
* command line which has already failed to
* work). Terminate.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout,SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "No more passwords available"
- " to try");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
- logevent("Unable to authenticate");
- connection_fatal(ssh->frontend,
- "Unable to authenticate");
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ FALSE);
crStopV;
}
} else {
}
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
} else {
- c_write_str(ssh, "No supported authentication methods"
- " left to try!\r\n");
- logevent("No supported authentications offered."
- " Disconnecting");
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "No supported authentication"
- " methods available");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
- ssh->close_expected = TRUE;
- ssh_closing((Plug)ssh, NULL, 0, 0);
+ ssh_disconnect(ssh, NULL,
+ "No supported authentication methods available",
+ SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+ FALSE);
crStopV;
}
}
- } while (!s->we_are_in);
+ }
+ ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL;
/*
- * Now we're authenticated for the connection protocol. The
- * connection protocol will automatically have started at this
- * point; there's no need to send SERVICE_REQUEST.
+ * Now the connection protocol has started, one way or another.
*/
ssh->channels = newtree234(ssh_channelcmp);
ssh2_pkt_addbool(s->pktout, 1); /* want reply */
ssh2_pkt_addbool(s->pktout, 0); /* many connections */
ssh2_pkt_addstring(s->pktout, proto);
+ /*
+ * Note that while we blank the X authentication data here, we don't
+ * take any special action to blank the start of an X11 channel,
+ * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection
+ * without having session blanking enabled is likely to leak your
+ * cookie into the log.
+ */
+ dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
ssh2_pkt_addstring(s->pktout, data);
+ end_log_omission(ssh, s->pktout);
ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display));
ssh2_pkt_send(ssh, s->pktout);
ssh2_pkt_adduint32(s->pktout, 0); /* pixel width */
ssh2_pkt_adduint32(s->pktout, 0); /* pixel height */
ssh2_pkt_addstring_start(s->pktout);
- ssh2_pkt_addbyte(s->pktout, 128); /* TTY_OP_ISPEED */
+ parse_ttymodes(ssh, ssh->cfg.ttymodes,
+ ssh2_send_ttymode, (void *)s->pktout);
+ ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_ISPEED);
ssh2_pkt_adduint32(s->pktout, ssh->ispeed);
- ssh2_pkt_addbyte(s->pktout, 129); /* TTY_OP_OSPEED */
+ ssh2_pkt_addbyte(s->pktout, SSH2_TTY_OP_OSPEED);
ssh2_pkt_adduint32(s->pktout, ssh->ospeed);
ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */
ssh2_pkt_send(ssh, s->pktout);
ssh->hostkey = NULL;
ssh->exitcode = -1;
ssh->close_expected = FALSE;
+ ssh->clean_exit = FALSE;
ssh->state = SSH_STATE_PREPACKET;
ssh->size_needed = FALSE;
ssh->eof_needed = FALSE;
unsigned long old_max_data_size;
pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
- ssh_setup_portfwd(ssh, cfg);
+ if (ssh->portfwds)
+ ssh_setup_portfwd(ssh, cfg);
if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time &&
cfg->ssh_rekey_time != 0) {
}
/*
- * Called to send data down the Telnet connection.
+ * Called to send data down the SSH connection.
*/
static int ssh_send(void *handle, char *buf, int len)
{
}
/*
- * Send Telnet special codes. TS_EOF is useful for `plink', so you
+ * Send special codes. TS_EOF is useful for `plink', so you
* can send an EOF and collect resulting output (e.g. `plink
* hostname sort').
*/