upstream commit

avoid more fatal/exit in the packet.c paths that
 ssh-keyscan uses; feedback and "looks good" markus@
This commit is contained in:
djm@openbsd.org 2015-01-30 01:13:33 +00:00 committed by Damien Miller
parent 669aee9943
commit 4509b5d4a4
9 changed files with 204 additions and 114 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: dispatch.c,v 1.24 2015/01/28 22:05:31 djm Exp $ */
/* $OpenBSD: dispatch.c,v 1.25 2015/01/30 01:13:33 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -49,9 +49,9 @@ dispatch_protocol_error(int type, u_int32_t seq, void *ctx)
fatal("protocol error");
if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
(r = sshpkt_put_u32(ssh, seq)) != 0 ||
(r = sshpkt_send(ssh)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
ssh_packet_write_wait(ssh);
(r = sshpkt_send(ssh)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
sshpkt_fatal(ssh, __func__, r);
return 0;
}

View File

@ -223,6 +223,8 @@ void
packet_set_connection(int fd_in, int fd_out)
{
active_state = ssh_packet_set_connection(active_state, fd_in, fd_out);
if (active_state == NULL)
fatal("%s: ssh_packet_set_connection failed", __func__);
}
void
@ -255,20 +257,8 @@ packet_read_seqnr(u_int32_t *seqnr)
u_char type;
int r;
if ((r = ssh_packet_read_seqnr(active_state, &type, seqnr)) != 0) {
switch (r) {
case SSH_ERR_CONN_CLOSED:
logit("Connection closed by %.200s",
ssh_remote_ipaddr(active_state));
cleanup_exit(255);
case SSH_ERR_CONN_TIMEOUT:
logit("Connection to %.200s timed out while "
"waiting to read", ssh_remote_ipaddr(active_state));
cleanup_exit(255);
default:
fatal("%s: %s", __func__, ssh_err(r));
}
}
if ((r = ssh_packet_read_seqnr(active_state, &type, seqnr)) != 0)
sshpkt_fatal(active_state, __func__, r);
return type;
}
@ -279,7 +269,7 @@ packet_read_poll_seqnr(u_int32_t *seqnr)
int r;
if ((r = ssh_packet_read_poll_seqnr(active_state, &type, seqnr)))
fatal("%s: %s", __func__, ssh_err(r));
sshpkt_fatal(active_state, __func__, r);
return type;
}
@ -296,5 +286,32 @@ packet_process_incoming(const char *buf, u_int len)
int r;
if ((r = ssh_packet_process_incoming(active_state, buf, len)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
sshpkt_fatal(active_state, __func__, r);
}
void
packet_write_wait(void)
{
int r;
if ((r = ssh_packet_write_wait(active_state)) != 0)
sshpkt_fatal(active_state, __func__, r);
}
void
packet_write_poll(void)
{
int r;
if ((r = ssh_packet_write_poll(active_state)) != 0)
sshpkt_fatal(active_state, __func__, r);
}
void
packet_read_expect(int expected_type)
{
int r;
if ((r = ssh_packet_read_expect(active_state, expected_type)) != 0)
sshpkt_fatal(active_state, __func__, r);
}

View File

@ -45,6 +45,9 @@ void packet_set_connection(int, int);
int packet_read_seqnr(u_int32_t *);
int packet_read_poll_seqnr(u_int32_t *);
void packet_process_incoming(const char *buf, u_int len);
void packet_write_wait(void);
void packet_write_poll(void);
void packet_read_expect(int expected_type);
#define packet_set_timeout(timeout, count) \
ssh_packet_set_timeout(active_state, (timeout), (count))
#define packet_connection_is_on_socket() \
@ -85,8 +88,6 @@ void packet_process_incoming(const char *buf, u_int len);
ssh_packet_send(active_state)
#define packet_read() \
ssh_packet_read(active_state)
#define packet_read_expect(expected_type) \
ssh_packet_read_expect(active_state, (expected_type))
#define packet_get_int64() \
ssh_packet_get_int64(active_state)
#define packet_get_bignum(value) \
@ -105,10 +106,6 @@ void packet_process_incoming(const char *buf, u_int len);
ssh_packet_send_debug(active_state, (fmt), ##args)
#define packet_disconnect(fmt, args...) \
ssh_packet_disconnect(active_state, (fmt), ##args)
#define packet_write_poll() \
ssh_packet_write_poll(active_state)
#define packet_write_wait() \
ssh_packet_write_wait(active_state)
#define packet_have_data_to_write() \
ssh_packet_have_data_to_write(active_state)
#define packet_not_very_much_data_to_write() \

220
packet.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.204 2015/01/28 21:15:47 djm Exp $ */
/* $OpenBSD: packet.c,v 1.205 2015/01/30 01:13:33 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -272,20 +272,26 @@ ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out)
const struct sshcipher *none = cipher_by_name("none");
int r;
if (none == NULL)
fatal("%s: cannot load cipher 'none'", __func__);
if (none == NULL) {
error("%s: cannot load cipher 'none'", __func__);
return NULL;
}
if (ssh == NULL)
ssh = ssh_alloc_session_state();
if (ssh == NULL)
fatal("%s: cound not allocate state", __func__);
if (ssh == NULL) {
error("%s: cound not allocate state", __func__);
return NULL;
}
state = ssh->state;
state->connection_in = fd_in;
state->connection_out = fd_out;
if ((r = cipher_init(&state->send_context, none,
(const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 ||
(r = cipher_init(&state->receive_context, none,
(const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0)
fatal("%s: cipher_init failed: %s", __func__, ssh_err(r));
(const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) {
error("%s: cipher_init failed: %s", __func__, ssh_err(r));
return NULL;
}
state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL;
deattack_init(&state->deattack);
return ssh;
@ -893,8 +899,8 @@ ssh_packet_send1(struct ssh *ssh)
/*
* Note that the packet is now only buffered in output. It won't be
* actually sent until packet_write_wait or packet_write_poll is
* called.
* actually sent until ssh_packet_write_wait or ssh_packet_write_poll
* is called.
*/
r = 0;
out:
@ -1263,8 +1269,12 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if (setp == NULL)
return SSH_ERR_ALLOC_FAIL;
/* Since we are blocking, ensure that all written packets have been sent. */
ssh_packet_write_wait(ssh);
/*
* Since we are blocking, ensure that all written packets have
* been sent.
*/
if ((r = ssh_packet_write_wait(ssh)) != 0)
return r;
/* Stay in the loop until we have received a complete packet. */
for (;;) {
@ -1351,16 +1361,22 @@ ssh_packet_read(struct ssh *ssh)
* that given, and gives a fatal error and exits if there is a mismatch.
*/
void
ssh_packet_read_expect(struct ssh *ssh, int expected_type)
int
ssh_packet_read_expect(struct ssh *ssh, u_int expected_type)
{
int type;
int r;
u_char type;
type = ssh_packet_read(ssh);
if (type != expected_type)
ssh_packet_disconnect(ssh,
if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0)
return r;
if (type != expected_type) {
if ((r = sshpkt_disconnect(ssh,
"Protocol error: expected packet type %d, got %d",
expected_type, type);
expected_type, type)) != 0)
return r;
return SSH_ERR_PROTOCOL_ERROR;
}
return 0;
}
/* Checks if a full packet is available in the data received so far via
@ -1377,6 +1393,7 @@ ssh_packet_read_poll1(struct ssh *ssh, u_char *typep)
{
struct session_state *state = ssh->state;
u_int len, padded_len;
const char *emsg;
const u_char *cp;
u_char *p;
u_int checksum, stored_checksum;
@ -1389,9 +1406,12 @@ ssh_packet_read_poll1(struct ssh *ssh, u_char *typep)
return 0;
/* Get length of incoming packet. */
len = PEEK_U32(sshbuf_ptr(state->input));
if (len < 1 + 2 + 2 || len > 256 * 1024)
ssh_packet_disconnect(ssh, "Bad packet length %u.",
len);
if (len < 1 + 2 + 2 || len > 256 * 1024) {
if ((r = sshpkt_disconnect(ssh, "Bad packet length %u",
len)) != 0)
return r;
return SSH_ERR_CONN_CORRUPT;
}
padded_len = (len + 8) & ~7;
/* Check if the packet has been entirely received. */
@ -1410,19 +1430,27 @@ ssh_packet_read_poll1(struct ssh *ssh, u_char *typep)
* Ariel Futoransky(futo@core-sdi.com)
*/
if (!state->receive_context.plaintext) {
emsg = NULL;
switch (detect_attack(&state->deattack,
sshbuf_ptr(state->input), padded_len)) {
case DEATTACK_OK:
break;
case DEATTACK_DETECTED:
ssh_packet_disconnect(ssh,
"crc32 compensation attack: network attack detected"
);
emsg = "crc32 compensation attack detected";
break;
case DEATTACK_DOS_DETECTED:
ssh_packet_disconnect(ssh,
"deattack denial of service detected");
emsg = "deattack denial of service detected";
break;
default:
ssh_packet_disconnect(ssh, "deattack error");
emsg = "deattack error";
break;
}
if (emsg != NULL) {
error("%s", emsg);
if ((r = sshpkt_disconnect(ssh, "%s", emsg)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
return r;
return SSH_ERR_CONN_CORRUPT;
}
}
@ -1451,16 +1479,24 @@ ssh_packet_read_poll1(struct ssh *ssh, u_char *typep)
goto out;
/* Test check bytes. */
if (len != sshbuf_len(state->incoming_packet))
ssh_packet_disconnect(ssh,
"packet_read_poll1: len %d != sshbuf_len %zd.",
if (len != sshbuf_len(state->incoming_packet)) {
error("%s: len %d != sshbuf_len %zd", __func__,
len, sshbuf_len(state->incoming_packet));
if ((r = sshpkt_disconnect(ssh, "invalid packet length")) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
return r;
return SSH_ERR_CONN_CORRUPT;
}
cp = sshbuf_ptr(state->incoming_packet) + len - 4;
stored_checksum = PEEK_U32(cp);
if (checksum != stored_checksum)
ssh_packet_disconnect(ssh,
"Corrupted check bytes on input.");
if (checksum != stored_checksum) {
error("Corrupted check bytes on input");
if ((r = sshpkt_disconnect(ssh, "connection corrupted")) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
return r;
return SSH_ERR_CONN_CORRUPT;
}
if ((r = sshbuf_consume_end(state->incoming_packet, 4)) < 0)
goto out;
@ -1478,9 +1514,13 @@ ssh_packet_read_poll1(struct ssh *ssh, u_char *typep)
state->p_read.bytes += padded_len + 4;
if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0)
goto out;
if (*typep < SSH_MSG_MIN || *typep > SSH_MSG_MAX)
ssh_packet_disconnect(ssh,
"Invalid ssh1 packet type: %d", *typep);
if (*typep < SSH_MSG_MIN || *typep > SSH_MSG_MAX) {
error("Invalid ssh1 packet type: %d", *typep);
if ((r = sshpkt_disconnect(ssh, "invalid packet type")) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
return r;
return SSH_ERR_PROTOCOL_ERROR;
}
r = 0;
out:
return r;
@ -1634,7 +1674,6 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0)
goto out;
}
/* XXX now it's safe to use fatal/packet_disconnect */
if (seqnr_p != NULL)
*seqnr_p = state->p_read.seqnr;
if (++state->p_read.seqnr == 0)
@ -1648,9 +1687,13 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
/* get padlen */
padlen = sshbuf_ptr(state->incoming_packet)[4];
DBG(debug("input: padlen %d", padlen));
if (padlen < 4)
ssh_packet_disconnect(ssh,
"Corrupted padlen %d on input.", padlen);
if (padlen < 4) {
if ((r = sshpkt_disconnect(ssh,
"Corrupted padlen %d on input.", padlen)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
return r;
return SSH_ERR_CONN_CORRUPT;
}
/* skip packet size + padlen, discard padding */
if ((r = sshbuf_consume(state->incoming_packet, 4 + 1)) != 0 ||
@ -1677,9 +1720,13 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
*/
if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0)
goto out;
if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN)
ssh_packet_disconnect(ssh,
"Invalid ssh2 packet type: %d", *typep);
if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) {
if ((r = sshpkt_disconnect(ssh,
"Invalid ssh2 packet type: %d", *typep)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
return r;
return SSH_ERR_PROTOCOL_ERROR;
}
if (*typep == SSH2_MSG_NEWKEYS)
r = ssh_set_newkeys(ssh, MODE_IN);
else if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side)
@ -1816,9 +1863,8 @@ ssh_packet_remaining(struct ssh *ssh)
* message is printed immediately, but only if the client is being executed
* in verbose mode. These messages are primarily intended to ease debugging
* authentication problems. The length of the formatted message must not
* exceed 1024 bytes. This will automatically call packet_write_wait.
* exceed 1024 bytes. This will automatically call ssh_packet_write_wait.
*/
void
ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...)
{
@ -1846,7 +1892,29 @@ ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...)
(r = sshpkt_send(ssh)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
}
ssh_packet_write_wait(ssh);
if ((r = ssh_packet_write_wait(ssh)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
}
/*
* Pretty-print connection-terminating errors and exit.
*/
void
sshpkt_fatal(struct ssh *ssh, const char *tag, int r)
{
switch (r) {
case SSH_ERR_CONN_CLOSED:
logit("Connection closed by %.200s", ssh_remote_ipaddr(ssh));
cleanup_exit(255);
case SSH_ERR_CONN_TIMEOUT:
logit("Connection to %.200s timed out while "
"waiting to write", ssh_remote_ipaddr(ssh));
cleanup_exit(255);
default:
fatal("%s%sConnection to %.200s: %s",
tag != NULL ? tag : "", tag != NULL ? ": " : "",
ssh_remote_ipaddr(ssh), ssh_err(r));
}
}
/*
@ -1855,7 +1923,6 @@ ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...)
* should not contain a newline. The length of the formatted message must
* not exceed 1024 bytes.
*/
void
ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...)
{
@ -1879,30 +1946,26 @@ ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...)
/* Display the error locally */
logit("Disconnecting: %.100s", buf);
/* Send the disconnect message to the other side, and wait for it to get sent. */
if (compat20) {
if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 ||
(r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 ||
(r = sshpkt_put_cstring(ssh, buf)) != 0 ||
(r = sshpkt_put_cstring(ssh, "")) != 0 ||
(r = sshpkt_send(ssh)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
} else {
if ((r = sshpkt_start(ssh, SSH_MSG_DISCONNECT)) != 0 ||
(r = sshpkt_put_cstring(ssh, buf)) != 0 ||
(r = sshpkt_send(ssh)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
}
ssh_packet_write_wait(ssh);
/*
* Send the disconnect message to the other side, and wait
* for it to get sent.
*/
if ((r = sshpkt_disconnect(ssh, "%s", buf)) != 0)
sshpkt_fatal(ssh, __func__, r);
if ((r = ssh_packet_write_wait(ssh)) != 0)
sshpkt_fatal(ssh, __func__, r);
/* Close the connection. */
ssh_packet_close(ssh);
cleanup_exit(255);
}
/* Checks if there is any buffered output, and tries to write some of the output. */
void
/*
* Checks if there is any buffered output, and tries to write some of
* the output.
*/
int
ssh_packet_write_poll(struct ssh *ssh)
{
struct session_state *state = ssh->state;
@ -1916,33 +1979,33 @@ ssh_packet_write_poll(struct ssh *ssh)
if (len == -1) {
if (errno == EINTR || errno == EAGAIN ||
errno == EWOULDBLOCK)
return;
fatal("Write failed: %.100s", strerror(errno));
return 0;
return SSH_ERR_SYSTEM_ERROR;
}
if (len == 0 && !cont)
fatal("Write connection closed");
return SSH_ERR_CONN_CLOSED;
if ((r = sshbuf_consume(state->output, len)) != 0)
fatal("%s: %s", __func__, ssh_err(r));
return r;
}
return 0;
}
/*
* Calls packet_write_poll repeatedly until all pending output data has been
* written.
*/
void
int
ssh_packet_write_wait(struct ssh *ssh)
{
fd_set *setp;
int ret, ms_remain = 0;
int ret, r, ms_remain = 0;
struct timeval start, timeout, *timeoutp = NULL;
struct session_state *state = ssh->state;
setp = (fd_set *)calloc(howmany(state->connection_out + 1,
NFDBITS), sizeof(fd_mask));
if (setp == NULL)
fatal("%s: calloc failed", __func__);
return SSH_ERR_ALLOC_FAIL;
ssh_packet_write_poll(ssh);
while (ssh_packet_have_data_to_write(ssh)) {
memset(setp, 0, howmany(state->connection_out + 1,
@ -1973,13 +2036,16 @@ ssh_packet_write_wait(struct ssh *ssh)
}
}
if (ret == 0) {
logit("Connection to %.200s timed out while "
"waiting to write", ssh_remote_ipaddr(ssh));
cleanup_exit(255);
free(setp);
return SSH_ERR_CONN_TIMEOUT;
}
if ((r = ssh_packet_write_poll(ssh)) != 0) {
free(setp);
return r;
}
ssh_packet_write_poll(ssh);
}
free(setp);
return 0;
}
/* Returns true if there is buffered data to write to the connection. */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.h,v 1.65 2015/01/28 21:15:47 djm Exp $ */
/* $OpenBSD: packet.h,v 1.66 2015/01/30 01:13:33 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -90,7 +90,7 @@ int ssh_packet_send2_wrapped(struct ssh *);
int ssh_packet_send2(struct ssh *);
int ssh_packet_read(struct ssh *);
void ssh_packet_read_expect(struct ssh *, int type);
int ssh_packet_read_expect(struct ssh *, u_int type);
int ssh_packet_read_poll(struct ssh *);
int ssh_packet_read_poll1(struct ssh *, u_char *);
int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p);
@ -112,8 +112,8 @@ typedef void (ssh_packet_comp_free_func)(void *, void *);
void ssh_packet_set_compress_hooks(struct ssh *, void *,
ssh_packet_comp_alloc_func *, ssh_packet_comp_free_func *);
void ssh_packet_write_poll(struct ssh *);
void ssh_packet_write_wait(struct ssh *);
int ssh_packet_write_poll(struct ssh *);
int ssh_packet_write_wait(struct ssh *);
int ssh_packet_have_data_to_write(struct ssh *);
int ssh_packet_not_very_much_data_to_write(struct ssh *);
@ -148,8 +148,10 @@ void *ssh_packet_get_output(struct ssh *);
/* new API */
int sshpkt_start(struct ssh *ssh, u_char type);
int sshpkt_send(struct ssh *ssh);
int sshpkt_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
int sshpkt_disconnect(struct ssh *, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
int sshpkt_add_padding(struct ssh *, u_char);
void sshpkt_fatal(struct ssh *ssh, const char *tag, int r);
int sshpkt_put(struct ssh *ssh, const void *v, size_t len);
int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keyscan.c,v 1.97 2015/01/28 21:15:47 djm Exp $ */
/* $OpenBSD: ssh-keyscan.c,v 1.98 2015/01/30 01:13:33 djm Exp $ */
/*
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
*
@ -466,7 +466,8 @@ congreet(int s)
return;
}
*cp = '\0';
c->c_ssh = ssh_packet_set_connection(NULL, s, s);
if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL)
fatal("ssh_packet_set_connection failed");
ssh_set_app_data(c->c_ssh, c); /* back link */
if (sscanf(buf, "SSH-%d.%d-%[^\n]\n",
&remote_major, &remote_minor, remote_version) == 3)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh_api.c,v 1.2 2015/01/26 06:10:03 djm Exp $ */
/* $OpenBSD: ssh_api.c,v 1.3 2015/01/30 01:13:33 djm Exp $ */
/*
* Copyright (c) 2012 Markus Friedl. All rights reserved.
*
@ -85,7 +85,8 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
called = 1;
}
ssh = ssh_packet_set_connection(NULL, -1, -1);
if ((ssh = ssh_packet_set_connection(NULL, -1, -1)) == NULL)
return SSH_ERR_ALLOC_FAIL;
if (is_server)
ssh_packet_set_server(ssh);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssherr.c,v 1.2 2015/01/28 21:15:47 djm Exp $ */
/* $OpenBSD: ssherr.c,v 1.3 2015/01/30 01:13:33 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@ -129,6 +129,10 @@ ssh_err(int n)
return "Connection closed";
case SSH_ERR_CONN_TIMEOUT:
return "Connection timed out";
case SSH_ERR_CONN_CORRUPT:
return "Connection corrupted";
case SSH_ERR_PROTOCOL_ERROR:
return "Protocol error";
default:
return "unknown error";
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssherr.h,v 1.2 2015/01/28 21:15:47 djm Exp $ */
/* $OpenBSD: ssherr.h,v 1.3 2015/01/30 01:13:33 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@ -75,6 +75,8 @@
#define SSH_ERR_KEY_REVOKED -51
#define SSH_ERR_CONN_CLOSED -52
#define SSH_ERR_CONN_TIMEOUT -53
#define SSH_ERR_CONN_CORRUPT -54
#define SSH_ERR_PROTOCOL_ERROR -55
/* Translate a numeric error code to a human-readable error string */
const char *ssh_err(int n);