summaryrefslogtreecommitdiff
path: root/clientloop.c
diff options
context:
space:
mode:
authorEd Maste <emaste@FreeBSD.org>2021-04-23 19:10:38 +0000
committerEd Maste <emaste@FreeBSD.org>2021-04-23 19:10:38 +0000
commit206be79acbdeb88ea254ac622a60a4ee8015c5f6 (patch)
treea2b2db1cfcb757172a91d03ca0c8805185a76e1b /clientloop.c
parent3bbd8dc96b4466d8e4f850fc0adf7d02e1df2dc7 (diff)
Diffstat (limited to 'clientloop.c')
-rw-r--r--clientloop.c563
1 files changed, 356 insertions, 207 deletions
diff --git a/clientloop.c b/clientloop.c
index 60b46d1616c8..70f492f89371 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.346 2020/09/16 03:07:31 dtucker Exp $ */
+/* $OpenBSD: clientloop.c,v 1.358 2021/01/27 10:05:28 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -233,13 +233,13 @@ set_control_persist_exit_time(struct ssh *ssh)
} else if (channel_still_open(ssh)) {
/* some client connections are still open */
if (control_persist_exit_time > 0)
- debug2("%s: cancel scheduled exit", __func__);
+ debug2_f("cancel scheduled exit");
control_persist_exit_time = 0;
} else if (control_persist_exit_time <= 0) {
/* a client connection has recently closed */
control_persist_exit_time = monotime() +
(time_t)options.control_persist_timeout;
- debug2("%s: schedule exit in %d seconds", __func__,
+ debug2_f("schedule exit in %d seconds",
options.control_persist_timeout);
}
/* else we are already counting down to the timeout */
@@ -307,7 +307,7 @@ client_x11_get_proto(struct ssh *ssh, const char *display,
if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s",
display + 10)) < 0 ||
(size_t)r >= sizeof(xdisplay)) {
- error("%s: display name too long", __func__);
+ error_f("display name too long");
return -1;
}
display = xdisplay;
@@ -322,15 +322,14 @@ client_x11_get_proto(struct ssh *ssh, const char *display,
*/
mktemp_proto(xauthdir, sizeof(xauthdir));
if (mkdtemp(xauthdir) == NULL) {
- error("%s: mkdtemp: %s",
- __func__, strerror(errno));
+ error_f("mkdtemp: %s", strerror(errno));
return -1;
}
do_unlink = 1;
if ((r = snprintf(xauthfile, sizeof(xauthfile),
"%s/xauthfile", xauthdir)) < 0 ||
(size_t)r >= sizeof(xauthfile)) {
- error("%s: xauthfile path too long", __func__);
+ error_f("xauthfile path too long");
rmdir(xauthdir);
return -1;
}
@@ -356,7 +355,7 @@ client_x11_get_proto(struct ssh *ssh, const char *display,
SSH_X11_PROTO, x11_timeout_real,
_PATH_DEVNULL);
}
- debug2("%s: xauth command: %s", __func__, cmd);
+ debug2_f("xauth command: %s", cmd);
if (timeout != 0 && x11_refuse_time == 0) {
now = monotime() + 1;
@@ -445,7 +444,7 @@ client_check_window_change(struct ssh *ssh)
if (!received_window_change_signal)
return;
received_window_change_signal = 0;
- debug2("%s: changed", __func__);
+ debug2_f("changed");
channel_send_window_changes(ssh);
}
@@ -487,7 +486,7 @@ server_alive_check(struct ssh *ssh)
(r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) != 0 ||
(r = sshpkt_put_u8(ssh, 1)) != 0 || /* boolean: want reply */
(r = sshpkt_send(ssh)) != 0)
- fatal("%s: send packet: %s", __func__, ssh_err(r));
+ fatal_fr(r, "send packet");
/* Insert an empty placeholder to maintain ordering */
client_register_global_confirm(NULL, NULL);
schedule_server_alive_check();
@@ -569,7 +568,7 @@ client_wait_until_can_do_something(struct ssh *ssh,
/* Note: we might still have data in the buffers. */
if ((r = sshbuf_putf(stderr_buffer,
"select: %s\r\n", strerror(errno))) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
quit_pending = 1;
} else if (options.server_alive_interval > 0 && !FD_ISSET(connection_in,
*readsetp) && monotime() >= server_alive_time)
@@ -629,8 +628,7 @@ client_process_net_input(struct ssh *ssh, fd_set *readset)
if ((r = sshbuf_putf(stderr_buffer,
"Connection to %.300s closed by remote host.\r\n",
host)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
quit_pending = 1;
return;
}
@@ -650,8 +648,7 @@ client_process_net_input(struct ssh *ssh, fd_set *readset)
if ((r = sshbuf_putf(stderr_buffer,
"Read from remote host %.300s: %.100s\r\n",
host, strerror(errno))) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
quit_pending = 1;
return;
}
@@ -701,8 +698,7 @@ client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx)
if (tochan) {
if ((r = sshbuf_put(c->extended, errmsg,
strlen(errmsg))) != 0)
- fatal("%s: buffer error %s", __func__,
- ssh_err(r));
+ fatal_fr(r, "sshbuf_put");
} else
error("%s", errmsg);
if (cr->action == CONFIRM_TTY) {
@@ -750,8 +746,8 @@ client_register_global_confirm(global_confirm_cb *cb, void *ctx)
last_gc = TAILQ_LAST(&global_confirms, global_confirms);
if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) {
if (++last_gc->ref_count >= INT_MAX)
- fatal("%s: last_gc->ref_count = %d",
- __func__, last_gc->ref_count);
+ fatal_f("last_gc->ref_count = %d",
+ last_gc->ref_count);
return;
}
@@ -914,7 +910,7 @@ print_escape_help(struct sshbuf *b, int escape_char, int mux_client,
if ((r = sshbuf_putf(b,
"%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
suppress_flags =
(mux_client ? SUPPRESS_MUXCLIENT : 0) |
@@ -926,14 +922,14 @@ print_escape_help(struct sshbuf *b, int escape_char, int mux_client,
continue;
if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n",
escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
}
if ((r = sshbuf_putf(b,
" %c%c - send the escape character by typing it twice\r\n"
"(Note that escapes are only recognized immediately after "
"newline.)\r\n", escape_char, escape_char)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
}
/*
@@ -973,8 +969,7 @@ process_escapes(struct ssh *ssh, Channel *c,
/* Terminate the connection. */
if ((r = sshbuf_putf(berr, "%c.\r\n",
efc->escape_char)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
if (c && c->ctl_chan != -1) {
chan_read_failed(ssh, c);
chan_write_failed(ssh, c);
@@ -1003,16 +998,14 @@ process_escapes(struct ssh *ssh, Channel *c,
"%c%s escape not available to "
"multiplexed sessions\r\n",
efc->escape_char, b)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
continue;
}
/* Suspend the program. Inform the user */
if ((r = sshbuf_putf(berr,
"%c^Z [suspend ssh]\r\n",
efc->escape_char)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
/* Restore terminal modes and suspend. */
client_suspend_self(bin, bout, berr);
@@ -1023,17 +1016,15 @@ process_escapes(struct ssh *ssh, Channel *c,
case 'B':
if ((r = sshbuf_putf(berr,
"%cB\r\n", efc->escape_char)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
channel_request_start(ssh, c->self, "break", 0);
if ((r = sshpkt_put_u32(ssh, 1000)) != 0 ||
(r = sshpkt_send(ssh)) != 0)
- fatal("%s: send packet: %s", __func__,
- ssh_err(r));
+ fatal_fr(r, "send packet");
continue;
case 'R':
- if (datafellows & SSH_BUG_NOREKEY)
+ if (ssh->compat & SSH_BUG_NOREKEY)
logit("Server does not "
"support re-keying");
else
@@ -1049,8 +1040,7 @@ process_escapes(struct ssh *ssh, Channel *c,
if ((r = sshbuf_putf(berr,
"%c%c [Logging to syslog]\r\n",
efc->escape_char, ch)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
continue;
}
if (ch == 'V' && options.log_level >
@@ -1063,8 +1053,7 @@ process_escapes(struct ssh *ssh, Channel *c,
"%c%c [LogLevel %s]\r\n",
efc->escape_char, ch,
log_level_name(options.log_level))) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
continue;
case '&':
@@ -1082,11 +1071,9 @@ process_escapes(struct ssh *ssh, Channel *c,
/* Stop listening for new connections. */
channel_stop_listening(ssh);
- if ((r = sshbuf_putf(berr,
- "%c& [backgrounded]\n", efc->escape_char))
- != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ if ((r = sshbuf_putf(berr, "%c& "
+ "[backgrounded]\n", efc->escape_char)) != 0)
+ fatal_fr(r, "sshbuf_putf");
/* Fork into background. */
pid = fork();
@@ -1101,8 +1088,7 @@ process_escapes(struct ssh *ssh, Channel *c,
/* The child continues serving connections. */
/* fake EOF on stdin */
if ((r = sshbuf_put_u8(bin, 4)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_put_u8");
return -1;
case '?':
print_escape_help(berr, efc->escape_char,
@@ -1113,12 +1099,10 @@ process_escapes(struct ssh *ssh, Channel *c,
case '#':
if ((r = sshbuf_putf(berr, "%c#\r\n",
efc->escape_char)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
s = channel_open_message(ssh);
if ((r = sshbuf_put(berr, s, strlen(s))) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_put");
free(s);
continue;
@@ -1132,8 +1116,7 @@ process_escapes(struct ssh *ssh, Channel *c,
if (ch != efc->escape_char) {
if ((r = sshbuf_put_u8(bin,
efc->escape_char)) != 0)
- fatal("%s: buffer error: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_put_u8");
bytes++;
}
/* Escaped characters fall through here */
@@ -1160,7 +1143,7 @@ process_escapes(struct ssh *ssh, Channel *c,
*/
last_was_cr = (ch == '\r' || ch == '\n');
if ((r = sshbuf_put_u8(bin, ch)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_put_u8");
bytes++;
}
return bytes;
@@ -1246,30 +1229,30 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
debug("pledge: id");
if (pledge("stdio rpath wpath cpath unix inet dns recvfd sendfd proc exec id tty",
NULL) == -1)
- fatal("%s pledge(): %s", __func__, strerror(errno));
+ fatal_f("pledge(): %s", strerror(errno));
} else if (options.forward_x11 || options.permit_local_command) {
debug("pledge: exec");
if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty",
NULL) == -1)
- fatal("%s pledge(): %s", __func__, strerror(errno));
+ fatal_f("pledge(): %s", strerror(errno));
} else if (options.update_hostkeys) {
debug("pledge: filesystem full");
if (pledge("stdio rpath wpath cpath unix inet dns proc tty",
NULL) == -1)
- fatal("%s pledge(): %s", __func__, strerror(errno));
+ fatal_f("pledge(): %s", strerror(errno));
} else if (!option_clear_or_none(options.proxy_command) ||
fork_after_authentication_flag) {
debug("pledge: proc");
if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1)
- fatal("%s pledge(): %s", __func__, strerror(errno));
+ fatal_f("pledge(): %s", strerror(errno));
} else {
debug("pledge: network");
if (pledge("stdio unix inet dns proc tty", NULL) == -1)
- fatal("%s pledge(): %s", __func__, strerror(errno));
+ fatal_f("pledge(): %s", strerror(errno));
}
start_time = monotime_double();
@@ -1285,7 +1268,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
/* Initialize buffer. */
if ((stderr_buffer = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
+ fatal_f("sshbuf_new failed");
client_init_dispatch(ssh);
@@ -1336,8 +1319,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
/* manual rekey request */
debug("need rekeying");
if ((r = kex_start_rekex(ssh)) != 0)
- fatal("%s: kex_start_rekex: %s", __func__,
- ssh_err(r));
+ fatal_fr(r, "kex_start_rekex");
need_rekeying = 0;
} else {
/*
@@ -1414,7 +1396,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
(r = sshpkt_put_cstring(ssh, "")) != 0 || /* language tag */
(r = sshpkt_send(ssh)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
- fatal("%s: send disconnect: %s", __func__, ssh_err(r));
+ fatal_fr(r, "send disconnect");
channel_free_all(ssh);
@@ -1451,7 +1433,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) {
if ((r = sshbuf_putf(stderr_buffer,
"Connection to %.64s closed.\r\n", host)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_putf");
}
/* Output any buffered data for stderr. */
@@ -1462,7 +1444,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer))
error("Write failed flushing stderr buffer.");
else if ((r = sshbuf_consume(stderr_buffer, len)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ fatal_fr(r, "sshbuf_consume");
}
/* Clear and free any buffers. */
@@ -1499,15 +1481,15 @@ client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type,
(r = sshpkt_get_cstring(ssh, &originator_address, NULL)) != 0 ||
(r = sshpkt_get_u32(ssh, &originator_port)) != 0 ||
(r = sshpkt_get_end(ssh)) != 0)
- fatal("%s: parse packet: %s", __func__, ssh_err(r));
+ fatal_fr(r, "parse packet");
- debug("%s: listen %s port %d, originator %s port %d", __func__,
+ debug_f("listen %s port %d, originator %s port %d",
listen_address, listen_port, originator_address, originator_port);
if (listen_port > 0xffff)
- error("%s: invalid listen port", __func__);
+ error_f("invalid listen port");
else if (originator_port > 0xffff)
- error("%s: invalid originator port", __func__);
+ error_f("invalid originator port");
else {
c = channel_connect_by_listen_address(ssh,
listen_address, listen_port, "forwarded-tcpip",
@@ -1516,7 +1498,7 @@ client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type,
if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) {
if ((b = sshbuf_new()) == NULL) {
- error("%s: alloc reply", __func__);
+ error_f("alloc reply");
goto out;
}
/* reconstruct and send to muxclient */
@@ -1531,8 +1513,7 @@ client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type,
(r = sshbuf_put_cstring(b, originator_address)) != 0 ||
(r = sshbuf_put_u32(b, originator_port)) != 0 ||
(r = sshbuf_put_stringb(c->output, b)) != 0) {
- error("%s: compose for muxclient %s", __func__,
- ssh_err(r));
+ error_fr(r, "compose for muxclient");
goto out;
}
}
@@ -1556,9 +1537,9 @@ client_request_forwarded_streamlocal(struct ssh *ssh,
if ((r = sshpkt_get_cstring(ssh, &listen_path, NULL)) != 0 ||
(r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* reserved */
(r = sshpkt_get_end(ssh)) != 0)
- fatal("%s: parse packet: %s", __func__, ssh_err(r));
+ fatal_fr(r, "parse packet");
- debug("%s: request: %s", __func__, listen_path);
+ debug_f("request: %s", listen_path);
c = channel_connect_by_listen_path(ssh, listen_path,
"forwarded-streamlocal@openssh.com", "forwarded-streamlocal");
@@ -1588,7 +1569,7 @@ client_request_x11(struct ssh *ssh, const char *request_type, int rchan)
if ((r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 ||
(r = sshpkt_get_u32(ssh, &originator_port)) != 0 ||
(r = sshpkt_get_end(ssh)) != 0)
- fatal("%s: parse packet: %s", __func__, ssh_err(r));
+ fatal_fr(r, "parse packet");
/* XXX check permission */
/* XXX range check originator port? */
debug("client_request_x11: request from %s %u", originator,
@@ -1623,8 +1604,7 @@ client_request_agent(struct ssh *ssh, const char *request_type, int rchan)
}
if (r != 0) {
if (r != SSH_ERR_AGENT_NOT_PRESENT)
- debug("%s: ssh_get_authentication_socket: %s",
- __func__, ssh_err(r));
+ debug_fr(r, "ssh_get_authentication_socket");
return NULL;
}
c = channel_new(ssh, "authentication agent connection",
@@ -1785,16 +1765,15 @@ client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh)
exit_status = exitval;
} else {
/* Probably for a mux channel that has already closed */
- debug("%s: no sink for exit-status on channel %d",
- __func__, id);
+ debug_f("no sink for exit-status on channel %d",
+ id);
}
if ((r = sshpkt_get_end(ssh)) != 0)
goto out;
}
if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) {
if (!c->have_remote_id)
- fatal("%s: channel %d: no remote_id",
- __func__, c->self);
+ fatal_f("channel %d: no remote_id", c->self);
if ((r = sshpkt_start(ssh, success ?
SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 ||
(r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
@@ -1814,12 +1793,13 @@ struct hostkeys_update_ctx {
/*
* Keys received from the server and a flag for each indicating
* whether they already exist in known_hosts.
- * keys_seen is filled in by hostkeys_find() and later (for new
+ * keys_match is filled in by hostkeys_find() and later (for new
* keys) by client_global_hostkeys_private_confirm().
*/
struct sshkey **keys;
- int *keys_seen;
- size_t nkeys, nnew;
+ u_int *keys_match; /* mask of HKF_MATCH_* from hostfile.h */
+ int *keys_verified; /* flag for new keys verified by server */
+ size_t nkeys, nnew, nincomplete; /* total, new keys, incomplete match */
/*
* Keys that are in known_hosts, but were not present in the update
@@ -1828,6 +1808,12 @@ struct hostkeys_update_ctx {
*/
struct sshkey **old_keys;
size_t nold;
+
+ /* Various special cases. */
+ int complex_hostspec; /* wildcard or manual pattern-list host name */
+ int ca_available; /* saw CA key for this host */
+ int old_key_seen; /* saw old key with other name/addr */
+ int other_name_seen; /* saw key with other name/addr */
};
static void
@@ -1840,7 +1826,8 @@ hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx)
for (i = 0; i < ctx->nkeys; i++)
sshkey_free(ctx->keys[i]);
free(ctx->keys);
- free(ctx->keys_seen);
+ free(ctx->keys_match);
+ free(ctx->keys_verified);
for (i = 0; i < ctx->nold; i++)
sshkey_free(ctx->old_keys[i]);
free(ctx->old_keys);
@@ -1849,6 +1836,30 @@ hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx)
free(ctx);
}
+/*
+ * Returns non-zero if a known_hosts hostname list is not of a form that
+ * can be handled by UpdateHostkeys. These include wildcard hostnames and
+ * hostnames lists that do not follow the form host[,ip].
+ */
+static int
+hostspec_is_complex(const char *hosts)
+{
+ char *cp;
+
+ /* wildcard */
+ if (strchr(hosts, '*') != NULL || strchr(hosts, '?') != NULL)
+ return 1;
+ /* single host/ip = ok */
+ if ((cp = strchr(hosts, ',')) == NULL)
+ return 0;
+ /* more than two entries on the line */
+ if (strchr(cp + 1, ',') != NULL)
+ return 1;
+ /* XXX maybe parse cp+1 and ensure it is an IP? */
+ return 0;
+}
+
+/* callback to search for ctx->keys in known_hosts */
static int
hostkeys_find(struct hostkey_foreach_line *l, void *_ctx)
{
@@ -1856,25 +1867,73 @@ hostkeys_find(struct hostkey_foreach_line *l, void *_ctx)
size_t i;
struct sshkey **tmp;
- if (l->status != HKF_STATUS_MATCHED || l->key == NULL)
+ if (l->key == NULL)
return 0;
+ if (l->status != HKF_STATUS_MATCHED) {
+ /* Record if one of the keys appears on a non-matching line */
+ for (i = 0; i < ctx->nkeys; i++) {
+ if (sshkey_equal(l->key, ctx->keys[i])) {
+ ctx->other_name_seen = 1;
+ debug3_f("found %s key under different "
+ "name/addr at %s:%ld",
+ sshkey_ssh_name(ctx->keys[i]),
+ l->path, l->linenum);
+ return 0;
+ }
+ }
+ return 0;
+ }
+ /* Don't proceed if revocation or CA markers are present */
+ /* XXX relax this */
+ if (l->marker != MRK_NONE) {
+ debug3_f("hostkeys file %s:%ld has CA/revocation marker",
+ l->path, l->linenum);
+ ctx->complex_hostspec = 1;
+ return 0;
+ }
- /* Mark off keys we've already seen for this host */
- for (i = 0; i < ctx->nkeys; i++) {
- if (sshkey_equal(l->key, ctx->keys[i])) {
- debug3("%s: found %s key at %s:%ld", __func__,
- sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum);
- ctx->keys_seen[i] = 1;
+ /* If CheckHostIP is enabled, then check for mismatched hostname/addr */
+ if (ctx->ip_str != NULL && strchr(l->hosts, ',') != NULL) {
+ if ((l->match & HKF_MATCH_HOST) == 0) {
+ /* Record if address matched a different hostname. */
+ ctx->other_name_seen = 1;
+ debug3_f("found address %s against different hostname "
+ "at %s:%ld", ctx->ip_str, l->path, l->linenum);
return 0;
+ } else if ((l->match & HKF_MATCH_IP) == 0) {
+ /* Record if hostname matched a different address. */
+ ctx->other_name_seen = 1;
+ debug3_f("found hostname %s against different address "
+ "at %s:%ld", ctx->host_str, l->path, l->linenum);
}
}
+
+ /*
+ * UpdateHostkeys is skipped for wildcard host names and hostnames
+ * that contain more than two entries (ssh never writes these).
+ */
+ if (hostspec_is_complex(l->hosts)) {
+ debug3_f("hostkeys file %s:%ld complex host specification",
+ l->path, l->linenum);
+ ctx->complex_hostspec = 1;
+ return 0;
+ }
+
+ /* Mark off keys we've already seen for this host */
+ for (i = 0; i < ctx->nkeys; i++) {
+ if (!sshkey_equal(l->key, ctx->keys[i]))
+ continue;
+ debug3_f("found %s key at %s:%ld",
+ sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum);
+ ctx->keys_match[i] |= l->match;
+ return 0;
+ }
/* This line contained a key that not offered by the server */
- debug3("%s: deprecated %s key at %s:%ld", __func__,
- sshkey_ssh_name(l->key), l->path, l->linenum);
+ debug3_f("deprecated %s key at %s:%ld", sshkey_ssh_name(l->key),
+ l->path, l->linenum);
if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1,
sizeof(*ctx->old_keys))) == NULL)
- fatal("%s: recallocarray failed nold = %zu",
- __func__, ctx->nold);
+ fatal_f("recallocarray failed nold = %zu", ctx->nold);
ctx->old_keys = tmp;
ctx->old_keys[ctx->nold++] = l->key;
l->key = NULL;
@@ -1882,6 +1941,63 @@ hostkeys_find(struct hostkey_foreach_line *l, void *_ctx)
return 0;
}
+/* callback to search for ctx->old_keys in known_hosts under other names */
+static int
+hostkeys_check_old(struct hostkey_foreach_line *l, void *_ctx)
+{
+ struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx;
+ size_t i;
+ int hashed;
+
+ /* only care about lines that *don't* match the active host spec */
+ if (l->status == HKF_STATUS_MATCHED || l->key == NULL)
+ return 0;
+
+ hashed = l->match & (HKF_MATCH_HOST_HASHED|HKF_MATCH_IP_HASHED);
+ for (i = 0; i < ctx->nold; i++) {
+ if (!sshkey_equal(l->key, ctx->old_keys[i]))
+ continue;
+ debug3_f("found deprecated %s key at %s:%ld as %s",
+ sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum,
+ hashed ? "[HASHED]" : l->hosts);
+ ctx->old_key_seen = 1;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Check known_hosts files for deprecated keys under other names. Returns 0
+ * on success or -1 on failure. Updates ctx->old_key_seen if deprecated keys
+ * exist under names other than the active hostname/IP.
+ */
+static int
+check_old_keys_othernames(struct hostkeys_update_ctx *ctx)
+{
+ size_t i;
+ int r;
+
+ debug2_f("checking for %zu deprecated keys", ctx->nold);
+ for (i = 0; i < options.num_user_hostfiles; i++) {
+ debug3_f("searching %s for %s / %s",
+ options.user_hostfiles[i], ctx->host_str,
+ ctx->ip_str ? ctx->ip_str : "(none)");
+ if ((r = hostkeys_foreach(options.user_hostfiles[i],
+ hostkeys_check_old, ctx, ctx->host_str, ctx->ip_str,
+ HKF_WANT_PARSE_KEY, 0)) != 0) {
+ if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) {
+ debug_f("hostkeys file %s does not exist",
+ options.user_hostfiles[i]);
+ continue;
+ }
+ error_fr(r, "hostkeys_foreach failed for %s",
+ options.user_hostfiles[i]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
static void
hostkey_change_preamble(LogLevel loglevel)
{
@@ -1901,11 +2017,11 @@ update_known_hosts(struct hostkeys_update_ctx *ctx)
struct stat sb;
for (i = 0; i < ctx->nkeys; i++) {
- if (ctx->keys_seen[i] != 2)
+ if (!ctx->keys_verified[i])
continue;
if ((fp = sshkey_fingerprint(ctx->keys[i],
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
- fatal("%s: sshkey_fingerprint failed", __func__);
+ fatal_f("sshkey_fingerprint failed");
if (first && asking)
hostkey_change_preamble(loglevel);
do_log2(loglevel, "Learned new hostkey: %s %s",
@@ -1916,7 +2032,7 @@ update_known_hosts(struct hostkeys_update_ctx *ctx)
for (i = 0; i < ctx->nold; i++) {
if ((fp = sshkey_fingerprint(ctx->old_keys[i],
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
- fatal("%s: sshkey_fingerprint failed", __func__);
+ fatal_f("sshkey_fingerprint failed");
if (first && asking)
hostkey_change_preamble(loglevel);
do_log2(loglevel, "Deprecating obsolete hostkey: %s %s",
@@ -1965,11 +2081,12 @@ update_known_hosts(struct hostkeys_update_ctx *ctx)
*/
if (stat(options.user_hostfiles[i], &sb) != 0) {
if (errno == ENOENT) {
- debug("%s: known hosts file %s does not exist",
- __func__, strerror(errno));
+ debug_f("known hosts file %s does not "
+ "exist", options.user_hostfiles[i]);
} else {
- error("%s: known hosts file %s inaccessible",
- __func__, strerror(errno));
+ error_f("known hosts file %s "
+ "inaccessible: %s",
+ options.user_hostfiles[i], strerror(errno));
}
continue;
}
@@ -1978,8 +2095,8 @@ update_known_hosts(struct hostkeys_update_ctx *ctx)
i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0,
options.hash_known_hosts, 0,
options.fingerprint_hash)) != 0) {
- error("%s: hostfile_replace_entries failed for %s: %s",
- __func__, options.user_hostfiles[i], ssh_err(r));
+ error_fr(r, "hostfile_replace_entries failed for %s",
+ options.user_hostfiles[i]);
}
}
}
@@ -1996,7 +2113,7 @@ client_global_hostkeys_private_confirm(struct ssh *ssh, int type,
size_t siglen;
if (ctx->nnew == 0)
- fatal("%s: ctx->nnew == 0", __func__); /* sanity */
+ fatal_f("ctx->nnew == 0"); /* sanity */
if (type != SSH2_MSG_REQUEST_SUCCESS) {
error("Server failed to confirm ownership of "
"private host keys");
@@ -2007,31 +2124,26 @@ client_global_hostkeys_private_confirm(struct ssh *ssh, int type,
sshkey_type_from_name(ssh->kex->hostkey_alg));
if ((signdata = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- /* Don't want to accidentally accept an unbound signature */
- if (ssh->kex->session_id_len == 0)
- fatal("%s: ssh->kex->session_id_len == 0", __func__);
+ fatal_f("sshbuf_new failed");
/*
* Expect a signature for each of the ctx->nnew private keys we
* haven't seen before. They will be in the same order as the
- * ctx->keys where the corresponding ctx->keys_seen[i] == 0.
+ * ctx->keys where the corresponding ctx->keys_match[i] == 0.
*/
for (ndone = i = 0; i < ctx->nkeys; i++) {
- if (ctx->keys_seen[i])
+ if (ctx->keys_match[i])
continue;
/* Prepare data to be signed: session ID, unique string, key */
sshbuf_reset(signdata);
if ( (r = sshbuf_put_cstring(signdata,
"hostkeys-prove-00@openssh.com")) != 0 ||
- (r = sshbuf_put_string(signdata, ssh->kex->session_id,
- ssh->kex->session_id_len)) != 0 ||
+ (r = sshbuf_put_stringb(signdata,
+ ssh->kex->session_id)) != 0 ||
(r = sshkey_puts(ctx->keys[i], signdata)) != 0)
- fatal("%s: failed to prepare signature: %s",
- __func__, ssh_err(r));
+ fatal_fr(r, "compose signdata");
/* Extract and verify signature */
if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) {
- error("%s: couldn't parse message: %s",
- __func__, ssh_err(r));
+ error_fr(r, "parse sig");
goto out;
}
/*
@@ -2044,19 +2156,19 @@ client_global_hostkeys_private_confirm(struct ssh *ssh, int type,
sshbuf_ptr(signdata), sshbuf_len(signdata),
use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0,
NULL)) != 0) {
- error("%s: server gave bad signature for %s key %zu",
- __func__, sshkey_type(ctx->keys[i]), i);
+ error_f("server gave bad signature for %s key %zu",
+ sshkey_type(ctx->keys[i]), i);
goto out;
}
/* Key is good. Mark it as 'seen' */
- ctx->keys_seen[i] = 2;
+ ctx->keys_verified[i] = 1;
ndone++;
}
+ /* Shouldn't happen */
if (ndone != ctx->nnew)
- fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__,
- ndone, ctx->nnew); /* Shouldn't happen */
+ fatal_f("ndone != ctx->nnew (%zu / %zu)", ndone, ctx->nnew);
if ((r = sshpkt_get_end(ssh)) != 0) {
- error("%s: protocol error", __func__);
+ error_f("protocol error");
goto out;
}
@@ -2102,9 +2214,10 @@ client_input_hostkeys(struct ssh *ssh)
static int hostkeys_seen = 0; /* XXX use struct ssh */
extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */
struct hostkeys_update_ctx *ctx = NULL;
+ u_int want;
if (hostkeys_seen)
- fatal("%s: server already sent hostkeys", __func__);
+ fatal_f("server already sent hostkeys");
if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK &&
options.batch_mode)
return 1; /* won't ask in batchmode, so don't even try */
@@ -2116,59 +2229,59 @@ client_input_hostkeys(struct ssh *ssh)
sshkey_free(key);
key = NULL;
if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) {
- error("%s: couldn't parse message: %s",
- __func__, ssh_err(r));
+ error_fr(r, "parse key");
goto out;
}
if ((r = sshkey_from_blob(blob, len, &key)) != 0) {
- do_log2(r == SSH_ERR_KEY_TYPE_UNKNOWN ?
+ do_log2_fr(r, r == SSH_ERR_KEY_TYPE_UNKNOWN ?
SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR,
- "%s: parse key: %s", __func__, ssh_err(r));
+ "convert key");
continue;
}
fp = sshkey_fingerprint(key, options.fingerprint_hash,
SSH_FP_DEFAULT);
- debug3("%s: received %s key %s", __func__,
- sshkey_type(key), fp);
+ debug3_f("received %s key %s", sshkey_type(key), fp);
free(fp);
if (!key_accepted_by_hostkeyalgs(key)) {
- debug3("%s: %s key not permitted by HostkeyAlgorithms",
- __func__, sshkey_ssh_name(key));
+ debug3_f("%s key not permitted by "
+ "HostkeyAlgorithms", sshkey_ssh_name(key));
continue;
}
/* Skip certs */
if (sshkey_is_cert(key)) {
- debug3("%s: %s key is a certificate; skipping",
- __func__, sshkey_ssh_name(key));
+ debug3_f("%s key is a certificate; skipping",
+ sshkey_ssh_name(key));
continue;
}
/* Ensure keys are unique */
for (i = 0; i < ctx->nkeys; i++) {
if (sshkey_equal(key, ctx->keys[i])) {
- error("%s: received duplicated %s host key",
- __func__, sshkey_ssh_name(key));
+ error_f("received duplicated %s host key",
+ sshkey_ssh_name(key));
goto out;
}
}
/* Key is good, record it */
if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1,
sizeof(*ctx->keys))) == NULL)
- fatal("%s: recallocarray failed nkeys = %zu",
- __func__, ctx->nkeys);
+ fatal_f("recallocarray failed nkeys = %zu",
+ ctx->nkeys);
ctx->keys = tmp;
ctx->keys[ctx->nkeys++] = key;
key = NULL;
}
if (ctx->nkeys == 0) {
- debug("%s: server sent no hostkeys", __func__);
+ debug_f("server sent no hostkeys");
goto out;
}
- if ((ctx->keys_seen = calloc(ctx->nkeys,
- sizeof(*ctx->keys_seen))) == NULL)
- fatal("%s: calloc failed", __func__);
+ if ((ctx->keys_match = calloc(ctx->nkeys,
+ sizeof(*ctx->keys_match))) == NULL ||
+ (ctx->keys_verified = calloc(ctx->nkeys,
+ sizeof(*ctx->keys_verified))) == NULL)
+ fatal_f("calloc failed");
get_hostfile_hostname_ipaddr(host,
options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL,
@@ -2177,68 +2290,107 @@ client_input_hostkeys(struct ssh *ssh)
/* Find which keys we already know about. */
for (i = 0; i < options.num_user_hostfiles; i++) {
- debug("%s: searching %s for %s / %s", __func__,
+ debug_f("searching %s for %s / %s",
options.user_hostfiles[i], ctx->host_str,
ctx->ip_str ? ctx->ip_str : "(none)");
if ((r = hostkeys_foreach(options.user_hostfiles[i],
hostkeys_find, ctx, ctx->host_str, ctx->ip_str,
- HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) {
+ HKF_WANT_PARSE_KEY, 0)) != 0) {
if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) {
- debug("%s: hostkeys file %s does not exist",
- __func__, options.user_hostfiles[i]);
+ debug_f("hostkeys file %s does not exist",
+ options.user_hostfiles[i]);
continue;
}
- error("%s: hostkeys_foreach failed for %s: %s",
- __func__, options.user_hostfiles[i], ssh_err(r));
+ error_fr(r, "hostkeys_foreach failed for %s",
+ options.user_hostfiles[i]);
goto out;
}
}
/* Figure out if we have any new keys to add */
- ctx->nnew = 0;
+ ctx->nnew = ctx->nincomplete = 0;
+ want = HKF_MATCH_HOST | ( options.check_host_ip ? HKF_MATCH_IP : 0);
for (i = 0; i < ctx->nkeys; i++) {
- if (!ctx->keys_seen[i])
+ if (ctx->keys_match[i] == 0)
ctx->nnew++;
+ if ((ctx->keys_match[i] & want) != want)
+ ctx->nincomplete++;
}
- debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove",
- __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold);
+ debug3_f("%zu server keys: %zu new, %zu retained, "
+ "%zu incomplete match. %zu to remove", ctx->nkeys, ctx->nnew,
+ ctx->nkeys - ctx->nnew - ctx->nincomplete,
+ ctx->nincomplete, ctx->nold);
- if (ctx->nnew == 0 && ctx->nold != 0) {
- /* We have some keys to remove. Just do it. */
- update_known_hosts(ctx);
- } else if (ctx->nnew != 0) {
+ if (ctx->nnew == 0 && ctx->nold == 0) {
+ debug_f("no new or deprecated keys from server");
+ goto out;
+ }
+
+ /* Various reasons why we cannot proceed with the update */
+ if (ctx->complex_hostspec) {
+ debug_f("CA/revocation marker, manual host list or wildcard "
+ "host pattern found, skipping UserKnownHostsFile update");
+ goto out;
+ }
+ if (ctx->other_name_seen) {
+ debug_f("host key found matching a different name/address, "
+ "skipping UserKnownHostsFile update");
+ goto out;
+ }
+ /*
+ * If removing keys, check whether they appear under different
+ * names/addresses and refuse to proceed if they do. This avoids
+ * cases such as hosts with multiple names becoming inconsistent
+ * with regards to CheckHostIP entries.
+ * XXX UpdateHostkeys=force to override this (and other) checks?
+ */
+ if (ctx->nold != 0) {
+ if (check_old_keys_othernames(ctx) != 0)
+ goto out; /* error already logged */
+ if (ctx->old_key_seen) {
+ debug_f("key(s) for %s%s%s exist under other names; "
+ "skipping UserKnownHostsFile update",
+ ctx->host_str, ctx->ip_str == NULL ? "" : ",",
+ ctx->ip_str == NULL ? "" : ctx->ip_str);
+ goto out;
+ }
+ }
+
+ if (ctx->nnew == 0) {
/*
- * We have received hitherto-unseen keys from the server.
- * Ask the server to confirm ownership of the private halves.
+ * We have some keys to remove or fix matching for.
+ * We can proceed to do this without requiring a fresh proof
+ * from the server.
*/
- debug3("%s: asking server to prove ownership for %zu keys",
- __func__, ctx->nnew);
- if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
- (r = sshpkt_put_cstring(ssh,
- "hostkeys-prove-00@openssh.com")) != 0 ||
- (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */
- fatal("%s: cannot prepare packet: %s",
- __func__, ssh_err(r));
- if ((buf = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new", __func__);
- for (i = 0; i < ctx->nkeys; i++) {
- if (ctx->keys_seen[i])
- continue;
- sshbuf_reset(buf);
- if ((r = sshkey_putb(ctx->keys[i], buf)) != 0)
- fatal("%s: sshkey_putb: %s",
- __func__, ssh_err(r));
- if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
- fatal("%s: sshpkt_put_string: %s",
- __func__, ssh_err(r));
- }
- if ((r = sshpkt_send(ssh)) != 0)
- fatal("%s: sshpkt_send: %s", __func__, ssh_err(r));
- client_register_global_confirm(
- client_global_hostkeys_private_confirm, ctx);
- ctx = NULL; /* will be freed in callback */
+ update_known_hosts(ctx);
+ goto out;
+ }
+ /*
+ * We have received previously-unseen keys from the server.
+ * Ask the server to confirm ownership of the private halves.
+ */
+ debug3_f("asking server to prove ownership for %zu keys", ctx->nnew);
+ if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+ (r = sshpkt_put_cstring(ssh,
+ "hostkeys-prove-00@openssh.com")) != 0 ||
+ (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */
+ fatal_fr(r, "prepare hostkeys-prove");
+ if ((buf = sshbuf_new()) == NULL)
+ fatal_f("sshbuf_new");
+ for (i = 0; i < ctx->nkeys; i++) {
+ if (ctx->keys_match[i])
+ continue;
+ sshbuf_reset(buf);
+ if ((r = sshkey_putb(ctx->keys[i], buf)) != 0 ||
+ (r = sshpkt_put_stringb(ssh, buf)) != 0)
+ fatal_fr(r, "assemble hostkeys-prove");
}
+ if ((r = sshpkt_send(ssh)) != 0)
+ fatal_fr(r, "send hostkeys-prove");
+ client_register_global_confirm(
+ client_global_hostkeys_private_confirm, ctx);
+ ctx = NULL; /* will be freed in callback */
/* Success */
out:
@@ -2279,6 +2431,19 @@ client_input_global_request(int type, u_int32_t seq, struct ssh *ssh)
return r;
}
+static void
+client_send_env(struct ssh *ssh, int id, const char *name, const char *val)
+{
+ int r;
+
+ debug("channel %d: setting env %s = \"%s\"", id, name, val);
+ channel_request_start(ssh, id, "env", 0);
+ if ((r = sshpkt_put_cstring(ssh, name)) != 0 ||
+ (r = sshpkt_put_cstring(ssh, val)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ fatal_fr(r, "send setenv");
+}
+
void
client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem,
const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd,
@@ -2288,10 +2453,10 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem,
char *name, *val;
Channel *c = NULL;
- debug2("%s: id %d", __func__, id);
+ debug2_f("id %d", id);
if ((c = channel_lookup(ssh, id)) == NULL)
- fatal("%s: channel %d: unknown channel", __func__, id);
+ fatal_f("channel %d: unknown channel", id);
ssh_packet_set_interactive(ssh, want_tty,
options.ip_qos_interactive, options.ip_qos_bulk);
@@ -2311,12 +2476,12 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem,
(r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
(r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
(r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0)
- fatal("%s: build packet: %s", __func__, ssh_err(r));
+ fatal_fr(r, "build pty-req");
if (tiop == NULL)
tiop = get_saved_tio();
ssh_tty_make_modes(ssh, -1, tiop);
if ((r = sshpkt_send(ssh)) != 0)
- fatal("%s: send packet: %s", __func__, ssh_err(r));
+ fatal_fr(r, "send pty-req");
/* XXX wait for reply */
c->client_tty = 1;
}
@@ -2345,15 +2510,7 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem,
free(name);
continue;
}
-
- debug("Sending env %s = %s", name, val);
- channel_request_start(ssh, id, "env", 0);
- if ((r = sshpkt_put_cstring(ssh, name)) != 0 ||
- (r = sshpkt_put_cstring(ssh, val)) != 0 ||
- (r = sshpkt_send(ssh)) != 0) {
- fatal("%s: send packet: %s",
- __func__, ssh_err(r));
- }
+ client_send_env(ssh, id, name, val);
free(name);
}
}
@@ -2365,13 +2522,7 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem,
continue;
}
*val++ = '\0';
-
- debug("Setting env %s = %s", name, val);
- channel_request_start(ssh, id, "env", 0);
- if ((r = sshpkt_put_cstring(ssh, name)) != 0 ||
- (r = sshpkt_put_cstring(ssh, val)) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- fatal("%s: send packet: %s", __func__, ssh_err(r));
+ client_send_env(ssh, id, name, val);
free(name);
}
@@ -2393,14 +2544,12 @@ client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem,
}
if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 ||
(r = sshpkt_send(ssh)) != 0)
- fatal("%s: send command: %s", __func__, ssh_err(r));
+ fatal_fr(r, "send command");
} else {
channel_request_start(ssh, id, "shell", 1);
client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE);
- if ((r = sshpkt_send(ssh)) != 0) {
- fatal("%s: send shell request: %s",
- __func__, ssh_err(r));
- }
+ if ((r = sshpkt_send(ssh)) != 0)
+ fatal_fr(r, "send shell");
}
}