--HG--
branch : coverity
This commit is contained in:
Matt Johnston
2014-07-25 22:23:50 +08:00
29 changed files with 406 additions and 129 deletions

26
CHANGES
View File

@@ -1,3 +1,29 @@
2014.64 pending
- Fix compiling with ECDSA and DSS disabled
- Don't exit abruptly if too many outgoing packets are queued for writev(). Patch
thanks to Ronny Meeus
- The -K keepalive option now behaves more like OpenSSH's "ServerAliveInterval".
If no response is received after 3 keepalives then the session is terminated. This
will close connections faster than waiting for a TCP timeout.
- Rework TCP priority setting. New settings are
if (connecting || ptys || x11) tos = LOWDELAY
else if (tcp_forwards) tos = 0
else tos = BULK
Thanks to Catalin Patulea for the suggestion.
- Improve handling of many concurrent new TCP forwarded connections, should now
be able to handle as many as MAX_CHANNELS. Thanks to Eduardo Silva for reporting
and investigating it.
- Make sure that exit messages from the client are printed, regression in 2013.57
- Use monotonic clock where available, timeouts won't be affected by system time
changes
2014.63 - Wednesday 19 February 2014
- Fix ~. to terminate a client interactive session after waking a laptop

View File

@@ -29,14 +29,6 @@
#include "buffer.h"
#include "circbuffer.h"
/* channel->type values */
#define CHANNEL_ID_NONE 0
#define CHANNEL_ID_SESSION 1
#define CHANNEL_ID_X11 2
#define CHANNEL_ID_AGENT 3
#define CHANNEL_ID_TCPDIRECT 4
#define CHANNEL_ID_TCPFORWARDED 5
#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1
#define SSH_OPEN_CONNECT_FAILED 2
#define SSH_OPEN_UNKNOWN_CHANNEL_TYPE 3
@@ -49,6 +41,13 @@
struct ChanType;
enum dropbear_channel_prio {
DROPBEAR_CHANNEL_PRIO_INTERACTIVE, /* pty shell, x11 */
DROPBEAR_CHANNEL_PRIO_UNKNOWABLE, /* tcp - can't know what's being forwarded */
DROPBEAR_CHANNEL_PRIO_BULK, /* the rest - probably scp or something */
DROPBEAR_CHANNEL_PRIO_EARLY, /* channel is still being set up */
};
struct Channel {
unsigned int index; /* the local channel index */
@@ -87,6 +86,8 @@ struct Channel {
void (*read_mangler)(struct Channel*, unsigned char* bytes, int *len);
const struct ChanType* type;
enum dropbear_channel_prio prio;
};
struct ChanType {
@@ -97,7 +98,6 @@ struct ChanType {
int (*check_close)(struct Channel*);
void (*reqhandler)(struct Channel*);
void (*closehandler)(struct Channel*);
};
void chaninitialise(const struct ChanType *chantypes[]);
@@ -129,4 +129,7 @@ void recv_msg_channel_open_confirmation();
void recv_msg_channel_open_failure();
#endif
void send_msg_request_success();
void send_msg_request_failure();
#endif /* _CHANNEL_H_ */

View File

@@ -174,11 +174,11 @@ void recv_msg_userauth_failure() {
the "none" auth request, and then a response to the immediate auth request.
We need to be careful handling them. */
if (cli_ses.ignore_next_auth_response) {
TRACE(("ignore next response, state set to USERAUTH_REQ_SENT"))
cli_ses.state = USERAUTH_REQ_SENT;
cli_ses.ignore_next_auth_response = 0;
TRACE(("leave recv_msg_userauth_failure, ignored response, state set to USERAUTH_REQ_SENT"));
return;
} else {
cli_ses.state = USERAUTH_FAIL_RCVD;
cli_ses.lastauthtype = AUTH_TYPE_NONE;
#ifdef ENABLE_CLI_PUBKEY_AUTH
/* If it was a pubkey auth request, we should cross that key
* off the list. */
@@ -197,10 +197,10 @@ void recv_msg_userauth_failure() {
cli_ses.auth_interact_failed = 1;
}
#endif
cli_ses.state = USERAUTH_FAIL_RCVD;
cli_ses.lastauthtype = AUTH_TYPE_NONE;
}
cli_ses.ignore_next_auth_response = 0;
methods = buf_getstring(ses.payload, &methlen);
partial = buf_getbool(ses.payload);

View File

@@ -41,7 +41,7 @@ static void cli_chansessreq(struct Channel *channel);
static void send_chansess_pty_req(struct Channel *channel);
static void send_chansess_shell_req(struct Channel *channel);
static void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len);
static int cli_init_netcat(struct Channel *channel);
static void cli_tty_setup();
@@ -357,6 +357,11 @@ static int cli_init_stdpipe_sess(struct Channel *channel) {
return 0;
}
static int cli_init_netcat(struct Channel *channel) {
channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
return cli_init_stdpipe_sess(channel);
}
static int cli_initchansess(struct Channel *channel) {
cli_init_stdpipe_sess(channel);
@@ -369,8 +374,9 @@ static int cli_initchansess(struct Channel *channel) {
if (cli_opts.wantpty) {
send_chansess_pty_req(channel);
channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
} else {
set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK);
channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
}
send_chansess_shell_req(channel);
@@ -389,7 +395,7 @@ static int cli_initchansess(struct Channel *channel) {
static const struct ChanType cli_chan_netcat = {
0, /* sepfds */
"direct-tcpip",
cli_init_stdpipe_sess, /* inithandler */
cli_init_netcat, /* inithandler */
NULL,
NULL,
cli_closechansess

View File

@@ -75,9 +75,6 @@ int main(int argc, char ** argv) {
int sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport,
0, &error);
sock_in = sock_out = sock;
if (cli_opts.wantpty) {
set_sock_priority(sock, DROPBEAR_PRIO_LOWDELAY);
}
}
if (sock_in < 0) {
@@ -107,9 +104,10 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
/* Do the cleanup first, since then the terminal will be reset */
session_cleanup();
/* Avoid printing onwards from terminal cruft */
fprintf(stderr, "\n");
_dropbear_log(LOG_INFO, fmtbuf, param);
exit(exitcode);
}
@@ -121,7 +119,7 @@ static void cli_dropbear_log(int UNUSED(priority),
vsnprintf(printbuf, sizeof(printbuf), format, param);
fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
fflush(stderr);
}
static void exec_proxy_cmd(void *user_data_cmd) {

View File

@@ -163,6 +163,8 @@ void cli_getopts(int argc, char ** argv) {
opts.ipv6 = 1;
*/
opts.recv_window = DEFAULT_RECV_WINDOW;
opts.keepalive_secs = DEFAULT_KEEPALIVE;
opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
fill_own_user();

View File

@@ -44,6 +44,7 @@ static void cli_session_init();
static void cli_finished();
static void recv_msg_service_accept(void);
static void cli_session_cleanup(void);
static void recv_msg_global_request_cli(void);
struct clientsession cli_ses; /* GLOBAL */
@@ -68,6 +69,7 @@ static const packettype cli_packettypes[] = {
{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
{SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
{SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_cli},
#ifdef ENABLE_CLI_REMOTETCPFWD
{SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
{SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
@@ -366,3 +368,9 @@ void cleantext(unsigned char* dirtytext) {
/* Null terminate */
dirtytext[j] = '\0';
}
static void recv_msg_global_request_cli(void) {
TRACE(("recv_msg_global_request_cli"))
/* Send a proper rejection */
send_msg_request_failure();
}

View File

@@ -52,7 +52,7 @@ static int cli_localtcp(const char* listenaddr,
static const struct ChanType cli_chan_tcplocal = {
1, /* sepfds */
"direct-tcpip",
NULL,
tcp_prio_inithandler,
NULL,
NULL,
NULL
@@ -267,6 +267,8 @@ static int newtcpforwarded(struct Channel * channel) {
* progress succeeds */
channel->writefd = sock;
channel->initconn = 1;
channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
err = SSH_OPEN_IN_PROGRESS;

View File

@@ -174,6 +174,8 @@ static struct Channel* newchannel(unsigned int remotechan,
newchan->recvdonelen = 0;
newchan->recvmaxpacket = RECV_MAX_CHANNEL_DATA_LEN;
newchan->prio = DROPBEAR_CHANNEL_PRIO_EARLY; /* inithandler sets it */
ses.channels[i] = newchan;
ses.chancount++;
@@ -208,11 +210,14 @@ struct Channel* getchannel() {
/* Iterate through the channels, performing IO if available */
void channelio(fd_set *readfds, fd_set *writefds) {
/* Listeners such as TCP, X11, agent-auth */
struct Channel *channel;
unsigned int i;
/* foreach channel */
for (i = 0; i < ses.chansize; i++) {
/* Close checking only needs to occur for channels that had IO events */
int do_check_close = 0;
channel = ses.channels[i];
if (channel == NULL) {
@@ -224,6 +229,7 @@ void channelio(fd_set *readfds, fd_set *writefds) {
if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
TRACE(("send normal readfd"))
send_msg_channel_data(channel, 0);
do_check_close = 1;
}
/* read stderr data and send it over the wire */
@@ -231,6 +237,7 @@ void channelio(fd_set *readfds, fd_set *writefds) {
&& FD_ISSET(channel->errfd, readfds)) {
TRACE(("send normal errfd"))
send_msg_channel_data(channel, 1);
do_check_close = 1;
}
/* write to program/pipe stdin */
@@ -242,20 +249,22 @@ void channelio(fd_set *readfds, fd_set *writefds) {
check_in_progress(), as it may be NULL */
}
writechannel(channel, channel->writefd, channel->writebuf);
do_check_close = 1;
}
/* stderr for client mode */
if (ERRFD_IS_WRITE(channel)
&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
writechannel(channel, channel->errfd, channel->extrabuf);
do_check_close = 1;
}
/* handle any channel closing etc */
check_close(channel);
if (do_check_close) {
check_close(channel);
}
}
/* Listeners such as TCP, X11, agent-auth */
#ifdef USING_LISTENERS
handle_listeners(readfds);
#endif
@@ -567,14 +576,16 @@ static void remove_channel(struct Channel * channel) {
}
/* close the FDs in case they haven't been done
* yet (they might have been shutdown etc) */
TRACE(("CLOSE writefd %d", channel->writefd))
close(channel->writefd);
TRACE(("CLOSE readfd %d", channel->readfd))
close(channel->readfd);
TRACE(("CLOSE errfd %d", channel->errfd))
close(channel->errfd);
if (IS_DROPBEAR_SERVER || (channel->writefd != STDOUT_FILENO)) {
/* close the FDs in case they haven't been done
* yet (they might have been shutdown etc) */
TRACE(("CLOSE writefd %d", channel->writefd))
close(channel->writefd);
TRACE(("CLOSE readfd %d", channel->readfd))
close(channel->readfd);
TRACE(("CLOSE errfd %d", channel->errfd))
close(channel->errfd);
}
if (!channel->close_handler_done
&& channel->type->closehandler) {
@@ -586,6 +597,8 @@ static void remove_channel(struct Channel * channel) {
m_free(channel);
ses.chancount--;
update_channel_prio();
TRACE(("leave remove_channel"))
}
@@ -876,6 +889,10 @@ void recv_msg_channel_open() {
}
}
if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
}
chan_initwritebuf(channel);
/* success */
@@ -889,6 +906,8 @@ failure:
cleanup:
m_free(type);
update_channel_prio();
TRACE(("leave recv_msg_channel_open"))
}
@@ -1004,7 +1023,7 @@ static void close_chan_fd(struct Channel *channel, int fd, int how) {
* for X11, agent, tcp forwarding, and should be filled with channel-specific
* options, with the calling function calling encrypt_packet() after
* completion. It is mandatory for the caller to encrypt_packet() if
* DROPBEAR_SUCCESS is returned */
* a channel is returned. NULL is returned on failure. */
int send_msg_channel_open_init(int fd, const struct ChanType *type) {
struct Channel* chan;
@@ -1073,6 +1092,10 @@ void recv_msg_channel_open_confirmation() {
}
}
if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
}
update_channel_prio();
TRACE(("leave recv_msg_channel_open_confirmation"))
}
@@ -1092,3 +1115,15 @@ void recv_msg_channel_open_failure() {
remove_channel(channel);
}
#endif /* USING_LISTENERS */
void send_msg_request_success() {
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
encrypt_packet();
}
void send_msg_request_failure() {
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
encrypt_packet();
}

View File

@@ -270,7 +270,7 @@ static void kexinitialise() {
ses.kexstate.our_first_follows_matches = 0;
ses.kexstate.lastkextime = time(NULL);
ses.kexstate.lastkextime = monotonic_now();
}

View File

@@ -51,6 +51,7 @@ int exitflag = 0; /* GLOBAL */
/* called only at the start of a session, set up initial state */
void common_session_init(int sock_in, int sock_out) {
time_t now;
TRACE(("enter session_init"))
@@ -58,9 +59,15 @@ void common_session_init(int sock_in, int sock_out) {
ses.sock_out = sock_out;
ses.maxfd = MAX(sock_in, sock_out);
ses.connect_time = 0;
ses.last_trx_packet_time = 0;
ses.last_packet_time = 0;
ses.socket_prio = DROPBEAR_PRIO_DEFAULT;
/* Sets it to lowdelay */
update_channel_prio();
now = monotonic_now();
ses.last_packet_time_keepalive_recv = now;
ses.last_packet_time_idle = now;
ses.last_packet_time_any_sent = 0;
ses.last_packet_time_keepalive_sent = 0;
if (pipe(ses.signal_pipe) < 0) {
dropbear_exit("Signal pipe failed");
@@ -186,13 +193,7 @@ void session_loop(void(*loophandler)()) {
/* check for auth timeout, rekeying required etc */
checktimeouts();
/* process session socket's incoming/outgoing data */
if (ses.sock_out != -1) {
if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) {
write_packet();
}
}
/* process session socket's incoming data */
if (ses.sock_in != -1) {
if (FD_ISSET(ses.sock_in, &readfd)) {
if (!ses.remoteident) {
@@ -218,6 +219,14 @@ void session_loop(void(*loophandler)()) {
* during rekeying ) */
channelio(&readfd, &writefd);
/* process session socket's outgoing data */
if (ses.sock_out != -1) {
if (!isempty(&ses.writequeue)) {
write_packet();
}
}
if (loophandler) {
loophandler();
}
@@ -385,11 +394,21 @@ static int ident_readln(int fd, char* buf, int count) {
return pos+1;
}
void send_msg_ignore() {
static void send_msg_keepalive() {
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_IGNORE);
buf_putstring(ses.writepayload, "", 0);
time_t old_time_idle = ses.last_packet_time_idle;
/* Try to force a response from the other end. Some peers will
reply with SSH_MSG_REQUEST_FAILURE, some will reply with SSH_MSG_UNIMPLEMENTED */
buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
/* A short string */
buf_putstring(ses.writepayload, "k@dropbear.nl", 0);
buf_putbyte(ses.writepayload, 1); /* want_reply */
encrypt_packet();
ses.last_packet_time_keepalive_sent = monotonic_now();
/* keepalives shouldn't update idle timeout, reset it back */
ses.last_packet_time_idle = old_time_idle;
}
/* Check all timeouts which are required. Currently these are the time for
@@ -397,13 +416,8 @@ void send_msg_ignore() {
static void checktimeouts() {
time_t now;
now = time(NULL);
now = monotonic_now();
if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) {
dropbear_close("Timeout before auth");
}
/* we can't rekey if we haven't done remote ident exchange yet */
if (ses.remoteident == NULL) {
return;
@@ -416,13 +430,27 @@ static void checktimeouts() {
send_msg_kexinit();
}
if (opts.keepalive_secs > 0
&& now - ses.last_trx_packet_time >= opts.keepalive_secs) {
send_msg_ignore();
if (opts.keepalive_secs > 0) {
/* Send keepalives if we've been idle */
if (now - ses.last_packet_time_any_sent >= opts.keepalive_secs) {
send_msg_keepalive();
}
/* Also send an explicit keepalive message to trigger a response
if the remote end hasn't sent us anything */
if (now - ses.last_packet_time_keepalive_recv >= opts.keepalive_secs
&& now - ses.last_packet_time_keepalive_sent >= opts.keepalive_secs) {
send_msg_keepalive();
}
if (now - ses.last_packet_time_keepalive_recv
>= opts.keepalive_secs * DEFAULT_KEEPALIVE_LIMIT) {
dropbear_exit("Keepalive timeout");
}
}
if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0
&& now - ses.last_packet_time >= opts.idle_timeout_secs) {
if (opts.idle_timeout_secs > 0
&& now - ses.last_packet_time_idle >= opts.idle_timeout_secs) {
dropbear_close("Idle timeout");
}
}
@@ -433,12 +461,13 @@ static long select_timeout() {
long ret = LONG_MAX;
if (KEX_REKEY_TIMEOUT > 0)
ret = MIN(KEX_REKEY_TIMEOUT, ret);
if (AUTH_TIMEOUT > 0)
/* AUTH_TIMEOUT is only relevant before authdone */
if (ses.authstate.authdone != 1 && AUTH_TIMEOUT > 0)
ret = MIN(AUTH_TIMEOUT, ret);
if (opts.keepalive_secs > 0)
ret = MIN(opts.keepalive_secs, ret);
if (opts.idle_timeout_secs > 0)
ret = MIN(opts.idle_timeout_secs, ret);
if (opts.idle_timeout_secs > 0)
ret = MIN(opts.idle_timeout_secs, ret);
return ret;
}
@@ -487,3 +516,47 @@ void fill_passwd(const char* username) {
}
}
/* Called when channels are modified */
void update_channel_prio() {
enum dropbear_prio new_prio;
int any = 0;
unsigned int i;
TRACE(("update_channel_prio"))
new_prio = DROPBEAR_PRIO_BULK;
for (i = 0; i < ses.chansize; i++) {
struct Channel *channel = ses.channels[i];
if (!channel || channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
if (channel && channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) {
TRACE(("update_channel_prio: early %d", channel->index))
}
continue;
}
any = 1;
if (channel->prio == DROPBEAR_CHANNEL_PRIO_INTERACTIVE)
{
TRACE(("update_channel_prio: lowdelay %d", channel->index))
new_prio = DROPBEAR_PRIO_LOWDELAY;
break;
} else if (channel->prio == DROPBEAR_CHANNEL_PRIO_UNKNOWABLE
&& new_prio == DROPBEAR_PRIO_BULK)
{
TRACE(("update_channel_prio: unknowable %d", channel->index))
new_prio = DROPBEAR_PRIO_DEFAULT;
}
}
if (any == 0) {
/* lowdelay during setup */
TRACE(("update_channel_prio: not any"))
new_prio = DROPBEAR_PRIO_LOWDELAY;
}
if (new_prio != ses.socket_prio) {
TRACE(("Dropbear priority transitioning %4.4s -> %4.4s", (char*)&ses.socket_prio, (char*)&new_prio))
set_sock_priority(ses.sock_out, new_prio);
ses.socket_prio = new_prio;
}
}

View File

@@ -361,6 +361,10 @@ AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline )
AC_CHECK_FUNCS(setutxent utmpxname)
AC_CHECK_FUNCS(logout updwtmp logwtmp)
# OS X monotonic time
AC_CHECK_HEADERS([mach/mach_time.h])
AC_CHECK_FUNCS(mach_absolute_time)
AC_ARG_ENABLE(bundled-libtom,
[ --enable-bundled-libtom Force using bundled libtomcrypt/libtommath even if a system version exists.
--disable-bundled-libtom Force using system libtomcrypt/libtommath, fail if it does not exist.

View File

@@ -48,6 +48,19 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "config.h"
#ifdef __linux__
#define _GNU_SOURCE
/* To call clock_gettime() directly */
#include <sys/syscall.h>
#endif /* __linux */
#ifdef HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#include <mach/mach.h>
#endif
#include "includes.h"
#include "dbutil.h"
#include "buffer.h"
@@ -319,7 +332,7 @@ int dropbear_listen(const char* address, const char* port,
continue;
}
if (listen(sock, 20) < 0) {
if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) {
err = errno;
close(sock);
TRACE(("listen() failed"))
@@ -932,3 +945,33 @@ int constant_time_memcmp(const void* a, const void *b, size_t n)
return c;
}
time_t monotonic_now() {
#if defined(__linux__) && defined(SYS_clock_gettime)
/* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32. Probably cheaper. */
#ifndef CLOCK_MONOTONIC_COARSE
#define CLOCK_MONOTONIC_COARSE 6
#endif
static clockid_t clock_source = CLOCK_MONOTONIC_COARSE;
struct timespec ts;
if (syscall(SYS_clock_gettime, clock_source, &ts) == EINVAL) {
clock_source = CLOCK_MONOTONIC;
syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts);
}
return ts.tv_sec;
#elif defined(HAVE_MACH_ABSOLUTE_TIME)
/* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
static mach_timebase_info_data_t timebase_info;
if (timebase_info.denom == 0) {
mach_timebase_info(&timebase_info);
}
return mach_absolute_time() * timebase_info.numer / timebase_info.denom
/ 1e9;
#else
/* Fallback for everything else - this will sometimes go backwards */
return time(NULL);
#endif
}

View File

@@ -62,9 +62,9 @@ extern int debug_trace;
#endif
enum dropbear_prio {
DROPBEAR_PRIO_DEFAULT,
DROPBEAR_PRIO_LOWDELAY,
DROPBEAR_PRIO_BULK,
DROPBEAR_PRIO_DEFAULT = 'dffd',
DROPBEAR_PRIO_LOWDELAY = 'lddl',
DROPBEAR_PRIO_BULK = 'bllb',
};
char * stripcontrol(const char * text);
@@ -106,4 +106,9 @@ int m_str_to_uint(const char* str, unsigned int *val);
/* Returns 0 if a and b have the same contents */
int constant_time_memcmp(const void* a, const void *b, size_t n);
/* Returns a time in seconds that doesn't go backwards - does not correspond to
a real-world clock */
time_t monotonic_now();
#endif /* _DBUTIL_H_ */

View File

@@ -602,13 +602,18 @@ static sign_key *openssh_read(const char *filename, char * UNUSED(passphrase))
*/
blobbuf = buf_new(3000);
#ifdef DROPBEAR_DSS
if (key->type == OSSH_DSA) {
buf_putstring(blobbuf, "ssh-dss", 7);
retkey->type = DROPBEAR_SIGNKEY_DSS;
} else if (key->type == OSSH_RSA) {
}
#endif
#ifdef DROPBEAR_RSA
if (key->type == OSSH_RSA) {
buf_putstring(blobbuf, "ssh-rsa", 7);
retkey->type = DROPBEAR_SIGNKEY_RSA;
}
#endif
for (i = 0; i < num_integers; i++) {
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
@@ -831,7 +836,14 @@ static int openssh_write(const char *filename, sign_key *key,
mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */
#endif
if (key->type == DROPBEAR_SIGNKEY_RSA || key->type == DROPBEAR_SIGNKEY_DSS)
if (
#ifdef DROPBEAR_RSA
key->type == DROPBEAR_SIGNKEY_RSA ||
#endif
#ifdef DROPBEAR_DSS
key->type == DROPBEAR_SIGNKEY_DSS ||
#endif
0)
{
/*
* Fetch the key blobs.

View File

@@ -308,6 +308,11 @@ much traffic. */
be overridden at runtime with -K. 0 disables keepalives */
#define DEFAULT_KEEPALIVE 0
/* If this many KEEPALIVES are sent with no packets received from the
other side, exit. Not run-time configurable - if you have a need
for runtime configuration please mail the Dropbear list */
#define DEFAULT_KEEPALIVE_LIMIT 3
/* Ensure that data is received within IDLE_TIMEOUT seconds. This can
be overridden at runtime with -I. 0 disables idle timeouts */
#define DEFAULT_IDLE_TIMEOUT 0

View File

@@ -57,42 +57,52 @@ void write_packet() {
int len, written;
buffer * writebuf = NULL;
time_t now;
unsigned packet_type;
int all_ignore = 1;
#ifdef HAVE_WRITEV
struct iovec *iov = NULL;
int i;
struct Link *l;
int iov_max_count;
#endif
TRACE2(("enter write_packet"))
dropbear_assert(!isempty(&ses.writequeue));
#ifdef HAVE_WRITEV
iov = m_malloc(sizeof(*iov) * ses.writequeue.count);
#if defined(HAVE_WRITEV) && (defined(IOV_MAX) || defined(UIO_MAXIOV))
#ifndef IOV_MAX
#define IOV_MAX UIO_MAXIOV
#endif
/* Make sure the size of the iov is below the maximum allowed by the OS. */
iov_max_count = ses.writequeue.count;
if (iov_max_count > IOV_MAX)
{
iov_max_count = IOV_MAX;
}
iov = m_malloc(sizeof(*iov) * iov_max_count);
for (l = ses.writequeue.head, i = 0; l; l = l->link, i++)
{
writebuf = (buffer*)l->item;
packet_type = writebuf->data[writebuf->len-1];
len = writebuf->len - 1 - writebuf->pos;
dropbear_assert(len > 0);
all_ignore &= (packet_type == SSH_MSG_IGNORE);
TRACE2(("write_packet writev #%d type %d len %d/%d", i, packet_type,
len, writebuf->len-1))
iov[i].iov_base = buf_getptr(writebuf, len);
iov[i].iov_len = len;
}
written = writev(ses.sock_out, iov, ses.writequeue.count);
written = writev(ses.sock_out, iov, iov_max_count);
if (written < 0) {
if (errno == EINTR) {
m_free(iov);
TRACE2(("leave writepacket: EINTR"))
TRACE2(("leave write_packet: EINTR"))
return;
} else {
dropbear_exit("Error writing");
dropbear_exit("Error writing: %s", strerror(errno));
}
}
}
if (written == 0) {
ses.remoteclosed();
@@ -113,8 +123,7 @@ void write_packet() {
}
m_free(iov);
#else
#else /* No writev () */
/* Get the next buffer in the queue of encrypted packets to write*/
writebuf = (buffer*)examine(&ses.writequeue);
@@ -131,10 +140,9 @@ void write_packet() {
TRACE2(("leave writepacket: EINTR"))
return;
} else {
dropbear_exit("Error writing");
dropbear_exit("Error writing: %s", strerror(errno));
}
}
all_ignore = (packet_type == SSH_MSG_IGNORE);
if (written == 0) {
ses.remoteclosed();
@@ -149,14 +157,7 @@ void write_packet() {
/* More packet left to write, leave it in the queue for later */
buf_incrpos(writebuf, written);
}
#endif
now = time(NULL);
ses.last_trx_packet_time = now;
if (!all_ignore) {
ses.last_packet_time = now;
}
#endif /* writev */
TRACE2(("leave write_packet"))
}
@@ -503,6 +504,8 @@ void encrypt_packet() {
unsigned char packet_type;
unsigned int len, encrypt_buf_size;
unsigned char mac_bytes[MAX_MAC_LEN];
time_t now;
TRACE2(("enter encrypt_packet()"))
@@ -610,6 +613,18 @@ void encrypt_packet() {
ses.kexstate.datatrans += writebuf->len;
ses.transseq++;
now = monotonic_now();
ses.last_packet_time_any_sent = now;
/* idle timeout shouldn't be affected by responses to keepalives.
send_msg_keepalive() itself also does tricks with
ses.last_packet_idle_time - read that if modifying this code */
if (packet_type != SSH_MSG_REQUEST_FAILURE
&& packet_type != SSH_MSG_UNIMPLEMENTED
&& packet_type != SSH_MSG_IGNORE) {
ses.last_packet_time_idle = now;
}
TRACE2(("leave encrypt_packet()"))
}

View File

@@ -44,6 +44,7 @@ void process_packet() {
unsigned char type;
unsigned int i;
time_t now;
TRACE2(("enter process_packet"))
@@ -52,7 +53,8 @@ void process_packet() {
ses.lastpacket = type;
ses.last_packet_time = time(NULL);
now = monotonic_now();
ses.last_packet_time_keepalive_recv = now;
/* These packets we can receive at any time */
switch(type) {
@@ -65,13 +67,21 @@ void process_packet() {
case SSH_MSG_UNIMPLEMENTED:
/* debugging XXX */
TRACE(("SSH_MSG_UNIMPLEMENTED"))
dropbear_exit("Received SSH_MSG_UNIMPLEMENTED");
goto out;
case SSH_MSG_DISCONNECT:
/* TODO cleanup? */
dropbear_close("Disconnect received");
}
/* Ignore these packet types so that keepalives don't interfere with
idle detection. This is slightly incorrect since a tcp forwarded
global request with failure won't trigger the idle timeout,
but that's probably acceptable */
if (!(type == SSH_MSG_GLOBAL_REQUEST || type == SSH_MSG_REQUEST_FAILURE)) {
ses.last_packet_time_idle = now;
}
/* This applies for KEX, where the spec says the next packet MUST be
* NEWKEYS */
if (ses.requirenext != 0) {

View File

@@ -37,8 +37,8 @@ typedef struct runopts {
int listen_fwd_all;
#endif
unsigned int recv_window;
time_t keepalive_secs;
time_t idle_timeout_secs;
time_t keepalive_secs; /* Time between sending keepalives. 0 is off */
time_t idle_timeout_secs; /* Exit if no traffic is sent/received in this time */
#ifndef DISABLE_ZLIB
/* TODO: add a commandline flag. Currently this is on by default if compression

View File

@@ -48,6 +48,8 @@ void session_cleanup();
void send_session_identification();
void send_msg_ignore();
void update_channel_prio();
const char* get_user_shell();
void fill_passwd(const char* username);
@@ -104,10 +106,6 @@ struct sshsession {
/* Is it a client or server? */
unsigned char isserver;
time_t connect_time; /* time the connection was established
(cleared after auth once we're not
respecting AUTH_TIMEOUT any more) */
int sock_in;
int sock_out;
@@ -146,11 +144,14 @@ struct sshsession {
int signal_pipe[2]; /* stores endpoints of a self-pipe used for
race-free signal handling */
time_t last_trx_packet_time; /* time of the last packet transmission, for
keepalive purposes */
/* time of the last packet send/receive, for keepalive. Not real-world clock */
time_t last_packet_time_keepalive_sent;
time_t last_packet_time_keepalive_recv;
time_t last_packet_time_any_sent;
time_t last_packet_time; /* time of the last packet transmission or receive, for
idle timeout purposes */
time_t last_packet_time_idle; /* time of the last packet transmission or receive, for
idle timeout purposes so ignores SSH_MSG_IGNORE
or responses to keepalives. Not real-world clock */
/* KEX/encryption related */
@@ -187,7 +188,9 @@ struct sshsession {
unsigned int chancount; /* the number of Channel*s in use */
const struct ChanType **chantypes; /* The valid channel types */
/* TCP priority level for the main "port 22" tcp socket */
enum dropbear_prio socket_prio;
/* TCP forwarding - where manage listeners */
struct Listener ** listeners;
unsigned int listensize;
@@ -217,6 +220,11 @@ struct serversession {
/* The resolved remote address, used for lastlog etc */
char *remotehost;
time_t connect_time; /* time the connection was established
(cleared after auth once we're not
respecting AUTH_TIMEOUT any more).
A monotonic time, not realworld */
#ifdef USE_VFORK
pid_t server_pid;
#endif

View File

@@ -106,6 +106,7 @@ enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen)
void **
signkey_key_ptr(sign_key *key, enum signkey_type type) {
switch (type) {
#ifdef DROPBEAR_ECDSA
#ifdef DROPBEAR_ECC_256
case DROPBEAR_SIGNKEY_ECDSA_NISTP256:
return (void**)&key->ecckey256;
@@ -118,6 +119,7 @@ signkey_key_ptr(sign_key *key, enum signkey_type type) {
case DROPBEAR_SIGNKEY_ECDSA_NISTP521:
return (void**)&key->ecckey521;
#endif
#endif /* DROPBEAR_ECDSA */
#ifdef DROPBEAR_RSA
case DROPBEAR_SIGNKEY_RSA:
return (void**)&key->rsakey;

View File

@@ -392,8 +392,7 @@ void send_msg_userauth_success() {
/* authdone must be set after encrypt_packet() for
* delayed-zlib mode */
ses.authstate.authdone = 1;
ses.connect_time = 0;
svr_ses.connect_time = 0;
if (ses.authstate.pw_uid == 0) {
ses.allowprivport = 1;

View File

@@ -253,6 +253,8 @@ static int newchansess(struct Channel *channel) {
chansess->agentdir = NULL;
#endif
channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
return 0;
}
@@ -668,8 +670,11 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
if (chansess->term == NULL) {
/* no pty */
set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK);
ret = noptycommand(channel, chansess);
if (ret == DROPBEAR_SUCCESS) {
channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
update_channel_prio();
}
} else {
/* want pty */
ret = ptycommand(channel, chansess);

View File

@@ -80,12 +80,22 @@ svr_session_cleanup(void)
svr_pubkey_options_cleanup();
}
static void
svr_sessionloop() {
if (svr_ses.connect_time != 0
&& monotonic_now() - svr_ses.connect_time >= AUTH_TIMEOUT) {
dropbear_close("Timeout before auth");
}
}
void svr_session(int sock, int childpipe) {
char *host, *port;
size_t len;
common_session_init(sock, sock);
svr_ses.connect_time = monotonic_now();;
/* Initialise server specific parts of the session */
svr_ses.childpipe = childpipe;
#ifdef USE_VFORK
@@ -95,8 +105,6 @@ void svr_session(int sock, int childpipe) {
chaninitialise(svr_chantypes);
svr_chansessinitialise();
ses.connect_time = time(NULL);
/* for logging the remote address */
get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0);
len = strlen(host) + strlen(port) + 2;
@@ -128,7 +136,7 @@ void svr_session(int sock, int childpipe) {
/* Run the main for loop. NULL is for the dispatcher - only the client
* code makes use of it */
session_loop(NULL);
session_loop(svr_sessionloop);
/* Not reached */

View File

@@ -34,14 +34,6 @@
#include "runopts.h"
#include "auth.h"
static void send_msg_request_failure();
static void send_msg_request_failure() {
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
encrypt_packet();
}
#ifndef ENABLE_SVR_REMOTETCPFWD
/* This is better than SSH_MSG_UNIMPLEMENTED */
@@ -53,7 +45,6 @@ void recv_msg_global_request_remotetcp() {
/* */
#endif /* !ENABLE_SVR_REMOTETCPFWD */
static void send_msg_request_success();
static int svr_cancelremotetcp();
static int svr_remotetcpreq();
static int newtcpdirect(struct Channel * channel);
@@ -62,7 +53,7 @@ static int newtcpdirect(struct Channel * channel);
static const struct ChanType svr_chan_tcpremote = {
1, /* sepfds */
"forwarded-tcpip",
NULL,
tcp_prio_inithandler,
NULL,
NULL,
NULL
@@ -115,15 +106,6 @@ out:
TRACE(("leave recv_msg_global_request"))
}
static void send_msg_request_success() {
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
encrypt_packet();
}
static int matchtcp(void* typedata1, void* typedata2) {
const struct TCPListener *info1 = (struct TCPListener*)typedata1;
@@ -258,6 +240,8 @@ static int newtcpdirect(struct Channel * channel) {
int len;
int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
TRACE(("newtcpdirect channel %d", channel->index))
if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
goto out;
@@ -299,6 +283,8 @@ static int newtcpdirect(struct Channel * channel) {
* progress succeeds */
channel->writefd = sock;
channel->initconn = 1;
channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
err = SSH_OPEN_IN_PROGRESS;

View File

@@ -182,10 +182,15 @@ void x11cleanup(struct ChanSess *chansess) {
}
}
static int x11_inithandler(struct Channel *channel) {
channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
return 0;
}
static const struct ChanType chan_x11 = {
0, /* sepfds */
"x11",
NULL, /* inithandler */
x11_inithandler, /* inithandler */
NULL, /* checkclose */
NULL, /* reqhandler */
NULL /* closehandler */

View File

@@ -251,4 +251,10 @@
#define USE_VFORK
#endif /* don't HAVE_FORK */
#if MAX_UNAUTH_CLIENTS > MAX_CHANNELS
#define DROPBEAR_LISTEN_BACKLOG MAX_UNAUTH_CLIENTS
#else
#define DROPBEAR_LISTEN_BACKLOG MAX_CHANNELS
#endif
/* no include guard for this file */

View File

@@ -30,6 +30,7 @@
#include "buffer.h"
#include "packet.h"
#include "listener.h"
#include "listener.h"
#include "runopts.h"
#ifdef DROPBEAR_TCP_ACCEPT
@@ -44,6 +45,13 @@ static void cleanup_tcp(struct Listener *listener) {
m_free(tcpinfo);
}
int tcp_prio_inithandler(struct Channel* channel)
{
TRACE(("tcp_prio_inithandler channel %d", channel->index))
channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE;
return 0;
}
static void tcp_acceptor(struct Listener *listener, int sock) {
int fd;

View File

@@ -70,5 +70,8 @@ void cli_recv_msg_request_failure();
/* Common */
int listen_tcpfwd(struct TCPListener* tcpinfo);
int tcp_prio_inithandler(struct Channel* chan);
#define CHANNEL_ID_TCPFORWARDED 'tcpf'
#endif