mirror of
https://github.com/clearml/dropbear
synced 2025-04-10 07:25:49 +00:00
propagate from branch 'au.asn.ucc.matt.dropbear' (head 899a8851a5edf840b2f7925bcc26ffe99dcac54d)
to branch 'au.asn.ucc.matt.dropbear.cli-agent' (head 6bbab8364de17bd9ecb1dee5ffb796e48c0380d2) --HG-- branch : agent-client extra : convert_revision : d39a49137cc36b624768d4e79e564141dde8d355
This commit is contained in:
commit
709a3e75cf
53
CHANGES
53
CHANGES
@ -1,3 +1,56 @@
|
||||
0.52 - Wed 12 November 2008
|
||||
|
||||
- Add "netcat-alike" option (-B) to dbclient, allowing Dropbear to tunnel
|
||||
standard input/output to a TCP port-forwarded remote host.
|
||||
|
||||
- Add "proxy command" support to dbclient, to allow using a spawned process for
|
||||
IO rather than a direct TCP connection. eg
|
||||
dbclient remotehost
|
||||
is equivalent to
|
||||
dbclient -J 'nc remotehost 22' remotehost
|
||||
(the hostname is still provided purely for looking up saved host keys)
|
||||
|
||||
- Combine netcat-alike and proxy support to allow "multihop" connections, with
|
||||
comma-separated host syntax. Allows running
|
||||
|
||||
dbclient user1@host1,user2@host2,user3@host3
|
||||
|
||||
to end up at host3 via the other two, using SSH TCP forwarding. It's a bit
|
||||
like onion-routing. All connections are established from the local machine.
|
||||
The comma-separated syntax can also be used for scp/rsync, eg
|
||||
|
||||
rsync -a -e dbclient m@gateway,m2@host,martello:/home/matt/ ~/backup/
|
||||
|
||||
to bounce through a few hosts.
|
||||
|
||||
- Add -I "idle timeout" option (contributed by Farrell Aultman)
|
||||
|
||||
- Allow restrictions on authorized_keys logins such as restricting commands
|
||||
to be run etc. This is a subset of those allowed by OpenSSH, doesn't
|
||||
yet allow restricting source host.
|
||||
|
||||
- Use vfork() for scp on uClinux
|
||||
|
||||
- Default to PATH=/usr/bin:/bin for shells.
|
||||
|
||||
- Report errors if -R forwarding fails
|
||||
|
||||
- Add counter mode cipher support, which avoids some security problems with the
|
||||
standard CBC mode.
|
||||
|
||||
- Support zlib@openssh.com delayed compression for client/server. It can be
|
||||
required for the Dropbear server with the '-Z' option. This is useful for
|
||||
security as it avoids exposing the server to attacks on zlib by
|
||||
unauthenticated remote users, though requires client side support.
|
||||
|
||||
- options.h has been split into options.h (user-changable) and sysoptions.h
|
||||
(less commonly changed)
|
||||
|
||||
- Support "dbclient -s sftp" to specify a subsystem
|
||||
|
||||
- Fix a bug in replies to channel requests that could be triggered by recent
|
||||
versions of PuTTY
|
||||
|
||||
0.51 - Thu 27 March 2008
|
||||
|
||||
- Make a copy of password fields rather erroneously relying on getwpnam()
|
||||
|
2
LICENSE
2
LICENSE
@ -8,7 +8,7 @@ The majority of code is written by Matt Johnston, under the license below.
|
||||
Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the
|
||||
same license:
|
||||
|
||||
Copyright (c) 2002-2006 Matt Johnston
|
||||
Copyright (c) 2002-2008 Matt Johnston
|
||||
Portions copyright (c) 2004 Mihnea Stoenescu
|
||||
All rights reserved.
|
||||
|
||||
|
@ -25,7 +25,7 @@ COMMONOBJS=dbutil.o buffer.o \
|
||||
SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
|
||||
svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \
|
||||
svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
|
||||
svr-tcpfwd.o svr-authpam.o
|
||||
svr-tcpfwd.o svr-authpam.o @CRYPTLIB@
|
||||
|
||||
CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
|
||||
cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
|
||||
|
22
algo.h
22
algo.h
@ -29,13 +29,18 @@
|
||||
#include "includes.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#define DROPBEAR_MODE_UNUSED 0
|
||||
#define DROPBEAR_MODE_CBC 1
|
||||
#define DROPBEAR_MODE_CTR 2
|
||||
|
||||
struct Algo_Type {
|
||||
|
||||
unsigned char *name; /* identifying name */
|
||||
char val; /* a value for this cipher, or -1 for invalid */
|
||||
void *data; /* algorithm specific data */
|
||||
unsigned usable : 1; /* whether we can use this algorithm */
|
||||
|
||||
const void *data; /* algorithm specific data */
|
||||
char usable; /* whether we can use this algorithm */
|
||||
const void *mode; /* the mode, currently only used for ciphers,
|
||||
points to a 'struct dropbear_cipher_mode' */
|
||||
};
|
||||
|
||||
typedef struct Algo_Type algo_type;
|
||||
@ -48,6 +53,7 @@ extern algo_type sshhashes[];
|
||||
extern algo_type sshcompress[];
|
||||
|
||||
extern const struct dropbear_cipher dropbear_nocipher;
|
||||
extern const struct dropbear_cipher_mode dropbear_mode_none;
|
||||
extern const struct dropbear_hash dropbear_nohash;
|
||||
|
||||
struct dropbear_cipher {
|
||||
@ -56,6 +62,16 @@ struct dropbear_cipher {
|
||||
unsigned char blocksize;
|
||||
};
|
||||
|
||||
struct dropbear_cipher_mode {
|
||||
int (*start)(int cipher, const unsigned char *IV,
|
||||
const unsigned char *key,
|
||||
int keylen, int num_rounds, void *cipher_state);
|
||||
int (*encrypt)(const unsigned char *pt, unsigned char *ct,
|
||||
unsigned long len, void *cipher_state);
|
||||
int (*decrypt)(const unsigned char *ct, unsigned char *pt,
|
||||
unsigned long len, void *cipher_state);
|
||||
};
|
||||
|
||||
struct dropbear_hash {
|
||||
const struct ltc_hash_descriptor *hashdesc;
|
||||
unsigned long keysize;
|
||||
|
3
auth.h
3
auth.h
@ -135,7 +135,8 @@ struct SignKeyList {
|
||||
int type; /* The type of key */
|
||||
struct SignKeyList *next;
|
||||
int source;
|
||||
/* filename? or the buffer? for encrypted keys, so we can later get
|
||||
char *filename;
|
||||
/* the buffer? for encrypted keys, so we can later get
|
||||
* the private key portion */
|
||||
|
||||
};
|
||||
|
@ -91,7 +91,7 @@ void recv_msg_userauth_banner() {
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s\n", banner);
|
||||
fprintf(stderr, "%s\n", banner);
|
||||
|
||||
out:
|
||||
m_free(banner);
|
||||
@ -229,6 +229,8 @@ void recv_msg_userauth_failure() {
|
||||
|
||||
void recv_msg_userauth_success() {
|
||||
TRACE(("received msg_userauth_success"))
|
||||
/* Note: in delayed-zlib mode, setting authdone here
|
||||
* will enable compression in the transport layer */
|
||||
ses.authstate.authdone = 1;
|
||||
cli_ses.state = USERAUTH_SUCCESS_RCVD;
|
||||
cli_ses.lastauthtype = AUTH_TYPE_NONE;
|
||||
|
@ -53,6 +53,7 @@ void cli_pubkeyfail() {
|
||||
}
|
||||
|
||||
sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */
|
||||
m_free(cli_ses.lastprivkey->filename);
|
||||
m_free(cli_ses.lastprivkey);
|
||||
|
||||
TRACE(("leave cli_pubkeyfail"))
|
||||
|
@ -327,4 +327,5 @@ out:
|
||||
if (line != NULL) {
|
||||
buf_free(line);
|
||||
}
|
||||
m_free(fingerprint);
|
||||
}
|
||||
|
@ -32,7 +32,9 @@
|
||||
static void cli_dropbear_exit(int exitcode, const char* format, va_list param);
|
||||
static void cli_dropbear_log(int priority, const char* format, va_list param);
|
||||
|
||||
#ifdef ENABLE_CLI_PROXYCMD
|
||||
static void cli_proxy_cmd(int *sock_in, int *sock_out);
|
||||
#endif
|
||||
|
||||
#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
|
||||
#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
|
||||
@ -63,6 +65,7 @@ int main(int argc, char ** argv) {
|
||||
#ifdef ENABLE_CLI_PROXYCMD
|
||||
if (cli_opts.proxycmd) {
|
||||
cli_proxy_cmd(&sock_in, &sock_out);
|
||||
m_free(cli_opts.proxycmd);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@ -132,6 +135,7 @@ static void exec_proxy_cmd(void *user_data_cmd) {
|
||||
dropbear_exit("Failed to run '%s'\n", cmd);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CLI_PROXYCMD
|
||||
static void cli_proxy_cmd(int *sock_in, int *sock_out) {
|
||||
int ret;
|
||||
|
||||
@ -144,3 +148,4 @@ static void cli_proxy_cmd(int *sock_in, int *sock_out) {
|
||||
*sock_in = *sock_out = -1;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_CLI_PROXYCMD
|
||||
|
104
cli-runopts.c
104
cli-runopts.c
@ -49,7 +49,11 @@ static void add_netcat(const char *str);
|
||||
static void printhelp() {
|
||||
|
||||
fprintf(stderr, "Dropbear client v%s\n"
|
||||
#ifdef ENABLE_CLI_MULTIHOP
|
||||
"Usage: %s [options] [user@]host[/port][,[user@]host/port],...] [command]\n"
|
||||
#else
|
||||
"Usage: %s [options] [user@]host[/port] [command]\n"
|
||||
#endif
|
||||
"Options are:\n"
|
||||
"-p <remoteport>\n"
|
||||
"-l <username>\n"
|
||||
@ -74,22 +78,22 @@ static void printhelp() {
|
||||
#endif
|
||||
"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
|
||||
"-K <keepalive> (0 is never, default %d)\n"
|
||||
"-I <idle_timeout> (0 is never, default %d)\n"
|
||||
#ifdef ENABLE_CLI_NETCAT
|
||||
"-B <endhost:endport> Netcat-alike bouncing\n"
|
||||
"-B <endhost:endport> Netcat-alike forwarding\n"
|
||||
#endif
|
||||
#ifdef ENABLE_CLI_PROXYCMD
|
||||
"-J <proxy_program> Use program rather than tcp connection\n"
|
||||
"-J <proxy_program> Use program pipe rather than TCP connection\n"
|
||||
#endif
|
||||
#ifdef DEBUG_TRACE
|
||||
"-v verbose\n"
|
||||
"-v verbose (compiled with DEBUG_TRACE)\n"
|
||||
#endif
|
||||
,DROPBEAR_VERSION, cli_opts.progname,
|
||||
DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE);
|
||||
DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
|
||||
|
||||
}
|
||||
|
||||
void cli_getopts(int argc, char ** argv) {
|
||||
|
||||
unsigned int i, j;
|
||||
char ** next = 0;
|
||||
unsigned int cmdlen;
|
||||
@ -109,6 +113,8 @@ void cli_getopts(int argc, char ** argv) {
|
||||
|
||||
char* recv_window_arg = NULL;
|
||||
char* keepalive_arg = NULL;
|
||||
char* idle_timeout_arg = NULL;
|
||||
char *host_arg = NULL;
|
||||
|
||||
/* see printhelp() for options */
|
||||
cli_opts.progname = argv[0];
|
||||
@ -264,6 +270,9 @@ void cli_getopts(int argc, char ** argv) {
|
||||
case 'K':
|
||||
next = &keepalive_arg;
|
||||
break;
|
||||
case 'I':
|
||||
next = &idle_timeout_arg;
|
||||
break;
|
||||
#ifdef ENABLE_CLI_AGENTFWD
|
||||
case 'A':
|
||||
cli_opts.agent_fwd = 1;
|
||||
@ -307,12 +316,8 @@ void cli_getopts(int argc, char ** argv) {
|
||||
|
||||
/* Either the hostname or commands */
|
||||
|
||||
if (cli_opts.remotehost == NULL) {
|
||||
#ifdef ENABLE_CLI_MULTIHOP
|
||||
parse_multihop_hostname(argv[i], argv[0]);
|
||||
#else
|
||||
parse_hostname(argv[i]);
|
||||
#endif
|
||||
if (host_arg == NULL) {
|
||||
host_arg = argv[i];
|
||||
} else {
|
||||
|
||||
/* this is part of the commands to send - after this we
|
||||
@ -341,7 +346,7 @@ void cli_getopts(int argc, char ** argv) {
|
||||
|
||||
/* And now a few sanity checks and setup */
|
||||
|
||||
if (cli_opts.remotehost == NULL) {
|
||||
if (host_arg == NULL) {
|
||||
printhelp();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@ -377,12 +382,26 @@ void cli_getopts(int argc, char ** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (idle_timeout_arg) {
|
||||
if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CLI_NETCAT
|
||||
if (cli_opts.cmd && cli_opts.netcat_host) {
|
||||
dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* The hostname gets set up last, since
|
||||
* in multi-hop mode it will require knowledge
|
||||
* of other flags such as -i */
|
||||
#ifdef ENABLE_CLI_MULTIHOP
|
||||
parse_multihop_hostname(host_arg, argv[0]);
|
||||
#else
|
||||
parse_hostname(host_arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
||||
@ -395,14 +414,12 @@ static void loadidentityfile(const char* filename) {
|
||||
key = new_sign_key();
|
||||
keytype = DROPBEAR_SIGNKEY_ANY;
|
||||
if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
|
||||
|
||||
fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
|
||||
sign_key_free(key);
|
||||
|
||||
} else {
|
||||
|
||||
nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList));
|
||||
nextkey->key = key;
|
||||
nextkey->filename = m_strdup(filename);
|
||||
nextkey->next = cli_opts.privkeys;
|
||||
nextkey->type = keytype;
|
||||
nextkey->source = SIGNKEY_SOURCE_RAW_FILE;
|
||||
@ -413,6 +430,39 @@ static void loadidentityfile(const char* filename) {
|
||||
|
||||
#ifdef ENABLE_CLI_MULTIHOP
|
||||
|
||||
static char*
|
||||
multihop_passthrough_args() {
|
||||
char *ret;
|
||||
int total;
|
||||
unsigned int len = 0;
|
||||
struct SignKeyList *nextkey;
|
||||
/* Fill out -i and -W options that make sense for all
|
||||
* the intermediate processes */
|
||||
for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next)
|
||||
{
|
||||
len += 3 + strlen(nextkey->filename);
|
||||
}
|
||||
len += 20; // space for -W <size>, terminator.
|
||||
ret = m_malloc(len);
|
||||
total = 0;
|
||||
|
||||
if (opts.recv_window != DEFAULT_RECV_WINDOW)
|
||||
{
|
||||
int written = snprintf(ret+total, len-total, "-W %d", opts.recv_window);
|
||||
total += written;
|
||||
}
|
||||
|
||||
for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next)
|
||||
{
|
||||
const size_t size = len - total;
|
||||
int written = snprintf(ret+total, size, "-i %s", nextkey->filename);
|
||||
dropbear_assert(written < size);
|
||||
total += written;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sets up 'onion-forwarding' connections. This will spawn
|
||||
* a separate dbclient process for each hop.
|
||||
* As an example, if the cmdline is
|
||||
@ -427,6 +477,7 @@ static void loadidentityfile(const char* filename) {
|
||||
*/
|
||||
static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
|
||||
char *userhostarg = NULL;
|
||||
char *hostbuf = NULL;
|
||||
char *last_hop = NULL;;
|
||||
char *remainder = NULL;
|
||||
|
||||
@ -439,11 +490,12 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
|
||||
&& strchr(cli_opts.username, ',')
|
||||
&& strchr(cli_opts.username, '@')) {
|
||||
unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2;
|
||||
userhostarg = m_malloc(len);
|
||||
snprintf(userhostarg, len, "%s@%s", cli_opts.username, orighostarg);
|
||||
hostbuf = m_malloc(len);
|
||||
snprintf(hostbuf, len, "%s@%s", cli_opts.username, orighostarg);
|
||||
} else {
|
||||
userhostarg = m_strdup(orighostarg);
|
||||
hostbuf = m_strdup(orighostarg);
|
||||
}
|
||||
userhostarg = hostbuf;
|
||||
|
||||
last_hop = strrchr(userhostarg, ',');
|
||||
if (last_hop) {
|
||||
@ -461,19 +513,24 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
|
||||
if (last_hop) {
|
||||
/* Set up the proxycmd */
|
||||
unsigned int cmd_len = 0;
|
||||
char *passthrough_args = multihop_passthrough_args();
|
||||
if (cli_opts.proxycmd) {
|
||||
dropbear_exit("-J can't be used with multihop mode");
|
||||
}
|
||||
if (cli_opts.remoteport == NULL) {
|
||||
cli_opts.remoteport = "22";
|
||||
}
|
||||
cmd_len = strlen(remainder)
|
||||
cmd_len = strlen(argv0) + strlen(remainder)
|
||||
+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
|
||||
+ strlen(argv0) + 30;
|
||||
+ strlen(passthrough_args)
|
||||
+ 30;
|
||||
cli_opts.proxycmd = m_malloc(cmd_len);
|
||||
snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s",
|
||||
argv0, cli_opts.remotehost, cli_opts.remoteport, remainder);
|
||||
snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s",
|
||||
argv0, cli_opts.remotehost, cli_opts.remoteport,
|
||||
passthrough_args, remainder);
|
||||
m_free(passthrough_args);
|
||||
}
|
||||
m_free(hostbuf);
|
||||
}
|
||||
#endif /* !ENABLE_CLI_MULTIHOP */
|
||||
|
||||
@ -622,6 +679,7 @@ static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
|
||||
goto badport;
|
||||
}
|
||||
|
||||
newfwd->have_reply = 0;
|
||||
newfwd->next = *fwdlist;
|
||||
*fwdlist = newfwd;
|
||||
|
||||
|
@ -64,6 +64,10 @@ 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 */
|
||||
#ifdef ENABLE_CLI_REMOTETCPFWD
|
||||
{SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
|
||||
{SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
|
||||
#endif
|
||||
{0, 0} /* End */
|
||||
};
|
||||
|
||||
|
38
cli-tcpfwd.c
38
cli-tcpfwd.c
@ -128,7 +128,7 @@ static void send_msg_global_request_remotetcp(int port) {
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
|
||||
buf_putstring(ses.writepayload, "tcpip-forward", 13);
|
||||
buf_putbyte(ses.writepayload, 0);
|
||||
buf_putbyte(ses.writepayload, 1); /* want_reply */
|
||||
if (opts.listen_fwd_all) {
|
||||
listenspec = "";
|
||||
} else {
|
||||
@ -143,6 +143,42 @@ static void send_msg_global_request_remotetcp(int port) {
|
||||
TRACE(("leave send_msg_global_request_remotetcp"))
|
||||
}
|
||||
|
||||
/* The only global success/failure messages are for remotetcp.
|
||||
* Since there isn't any identifier in these messages, we have to rely on them
|
||||
* being in the same order as we sent the requests. This is the ordering
|
||||
* of the cli_opts.remotefwds list */
|
||||
void cli_recv_msg_request_success() {
|
||||
|
||||
/* Nothing in the packet. We just mark off that we have received the reply,
|
||||
* so that we can report failure for later ones. */
|
||||
struct TCPFwdList * iter = NULL;
|
||||
|
||||
iter = cli_opts.remotefwds;
|
||||
while (iter != NULL) {
|
||||
if (!iter->have_reply)
|
||||
{
|
||||
iter->have_reply = 1;
|
||||
return;
|
||||
}
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
|
||||
void cli_recv_msg_request_failure() {
|
||||
struct TCPFwdList * iter = NULL;
|
||||
|
||||
iter = cli_opts.remotefwds;
|
||||
while (iter != NULL) {
|
||||
if (!iter->have_reply)
|
||||
{
|
||||
iter->have_reply = 1;
|
||||
dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", iter->listenport, iter->connectaddr, iter->connectport);
|
||||
return;
|
||||
}
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
|
||||
void setup_remotetcp() {
|
||||
|
||||
struct TCPFwdList * iter = NULL;
|
||||
|
124
common-algo.c
124
common-algo.c
@ -29,32 +29,46 @@
|
||||
/* This file (algo.c) organises the ciphers which can be used, and is used to
|
||||
* decide which ciphers/hashes/compression/signing to use during key exchange*/
|
||||
|
||||
static int void_cipher(const unsigned char* in, unsigned char* out,
|
||||
unsigned long len, void *cipher_state) {
|
||||
if (in != out) {
|
||||
memmove(out, in, len);
|
||||
}
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
static int void_start(int cipher, const unsigned char *IV,
|
||||
const unsigned char *key,
|
||||
int keylen, int num_rounds, void *cipher_state) {
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
/* Mappings for ciphers, parameters are
|
||||
{&cipher_desc, keysize, blocksize} */
|
||||
/* NOTE: if keysize > 2*SHA1_HASH_SIZE, code such as hashkeys()
|
||||
needs revisiting */
|
||||
|
||||
#ifdef DROPBEAR_AES256_CBC
|
||||
#ifdef DROPBEAR_AES256
|
||||
static const struct dropbear_cipher dropbear_aes256 =
|
||||
{&aes_desc, 32, 16};
|
||||
#endif
|
||||
#ifdef DROPBEAR_AES128_CBC
|
||||
#ifdef DROPBEAR_AES128
|
||||
static const struct dropbear_cipher dropbear_aes128 =
|
||||
{&aes_desc, 16, 16};
|
||||
#endif
|
||||
#ifdef DROPBEAR_BLOWFISH_CBC
|
||||
#ifdef DROPBEAR_BLOWFISH
|
||||
static const struct dropbear_cipher dropbear_blowfish =
|
||||
{&blowfish_desc, 16, 8};
|
||||
#endif
|
||||
#ifdef DROPBEAR_TWOFISH256_CBC
|
||||
#ifdef DROPBEAR_TWOFISH256
|
||||
static const struct dropbear_cipher dropbear_twofish256 =
|
||||
{&twofish_desc, 32, 16};
|
||||
#endif
|
||||
#ifdef DROPBEAR_TWOFISH128_CBC
|
||||
#ifdef DROPBEAR_TWOFISH128
|
||||
static const struct dropbear_cipher dropbear_twofish128 =
|
||||
{&twofish_desc, 16, 16};
|
||||
#endif
|
||||
#ifdef DROPBEAR_3DES_CBC
|
||||
#ifdef DROPBEAR_3DES
|
||||
static const struct dropbear_cipher dropbear_3des =
|
||||
{&des3_desc, 24, 8};
|
||||
#endif
|
||||
@ -63,6 +77,24 @@ static const struct dropbear_cipher dropbear_3des =
|
||||
const struct dropbear_cipher dropbear_nocipher =
|
||||
{NULL, 16, 8};
|
||||
|
||||
/* A few void* s are required to silence warnings
|
||||
* about the symmetric_CBC vs symmetric_CTR cipher_state pointer */
|
||||
const struct dropbear_cipher_mode dropbear_mode_cbc =
|
||||
{(void*)cbc_start, (void*)cbc_encrypt, (void*)cbc_decrypt};
|
||||
const struct dropbear_cipher_mode dropbear_mode_none =
|
||||
{void_start, void_cipher, void_cipher};
|
||||
#ifdef DROPBEAR_ENABLE_CTR_MODE
|
||||
/* a wrapper to make ctr_start and cbc_start look the same */
|
||||
static int dropbear_big_endian_ctr_start(int cipher,
|
||||
const unsigned char *IV,
|
||||
const unsigned char *key, int keylen,
|
||||
int num_rounds, symmetric_CTR *ctr) {
|
||||
return ctr_start(cipher, IV, key, keylen, num_rounds, CTR_COUNTER_BIG_ENDIAN, ctr);
|
||||
}
|
||||
const struct dropbear_cipher_mode dropbear_mode_ctr =
|
||||
{(void*)dropbear_big_endian_ctr_start, (void*)ctr_encrypt, (void*)ctr_decrypt};
|
||||
#endif
|
||||
|
||||
/* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc.
|
||||
{&hash_desc, keysize, hashsize} */
|
||||
|
||||
@ -83,65 +115,81 @@ const struct dropbear_hash dropbear_nohash =
|
||||
{NULL, 16, 0}; /* used initially */
|
||||
|
||||
|
||||
/* The following map ssh names to internal values */
|
||||
/* The following map ssh names to internal values.
|
||||
* The ordering here is important for the client - the first mode
|
||||
* that is also supported by the server will get used. */
|
||||
|
||||
algo_type sshciphers[] = {
|
||||
#ifdef DROPBEAR_AES128_CBC
|
||||
{"aes128-cbc", 0, (void*)&dropbear_aes128, 1},
|
||||
#ifdef DROPBEAR_ENABLE_CTR_MODE
|
||||
#ifdef DROPBEAR_AES128
|
||||
{"aes128-ctr", 0, &dropbear_aes128, 1, &dropbear_mode_ctr},
|
||||
#endif
|
||||
#ifdef DROPBEAR_3DES_CBC
|
||||
{"3des-cbc", 0, (void*)&dropbear_3des, 1},
|
||||
#ifdef DROPBEAR_3DES
|
||||
{"3des-ctr", 0, &dropbear_3des, 1, &dropbear_mode_ctr},
|
||||
#endif
|
||||
#ifdef DROPBEAR_AES256_CBC
|
||||
{"aes256-cbc", 0, (void*)&dropbear_aes256, 1},
|
||||
#ifdef DROPBEAR_AES256
|
||||
{"aes256-ctr", 0, &dropbear_aes256, 1, &dropbear_mode_ctr},
|
||||
#endif
|
||||
#ifdef DROPBEAR_TWOFISH256_CBC
|
||||
{"twofish256-cbc", 0, (void*)&dropbear_twofish256, 1},
|
||||
{"twofish-cbc", 0, (void*)&dropbear_twofish256, 1},
|
||||
#endif /* DROPBEAR_ENABLE_CTR_MODE */
|
||||
|
||||
/* CBC modes are always enabled */
|
||||
#ifdef DROPBEAR_AES128
|
||||
{"aes128-cbc", 0, &dropbear_aes128, 1, &dropbear_mode_cbc},
|
||||
#endif
|
||||
#ifdef DROPBEAR_TWOFISH128_CBC
|
||||
{"twofish128-cbc", 0, (void*)&dropbear_twofish128, 1},
|
||||
#ifdef DROPBEAR_3DES
|
||||
{"3des-cbc", 0, &dropbear_3des, 1, &dropbear_mode_cbc},
|
||||
#endif
|
||||
#ifdef DROPBEAR_BLOWFISH_CBC
|
||||
{"blowfish-cbc", 0, (void*)&dropbear_blowfish, 1},
|
||||
#ifdef DROPBEAR_AES256
|
||||
{"aes256-cbc", 0, &dropbear_aes256, 1, &dropbear_mode_cbc},
|
||||
#endif
|
||||
{NULL, 0, NULL, 0}
|
||||
#ifdef DROPBEAR_TWOFISH256
|
||||
{"twofish256-cbc", 0, &dropbear_twofish256, 1, &dropbear_mode_cbc},
|
||||
{"twofish-cbc", 0, &dropbear_twofish256, 1, &dropbear_mode_cbc},
|
||||
#endif
|
||||
#ifdef DROPBEAR_TWOFISH128
|
||||
{"twofish128-cbc", 0, &dropbear_twofish128, 1, &dropbear_mode_cbc},
|
||||
#endif
|
||||
#ifdef DROPBEAR_BLOWFISH
|
||||
{"blowfish-cbc", 0, &dropbear_blowfish, 1, &dropbear_mode_cbc},
|
||||
#endif
|
||||
{NULL, 0, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
algo_type sshhashes[] = {
|
||||
#ifdef DROPBEAR_SHA1_96_HMAC
|
||||
{"hmac-sha1-96", 0, (void*)&dropbear_sha1_96, 1},
|
||||
{"hmac-sha1-96", 0, &dropbear_sha1_96, 1, NULL},
|
||||
#endif
|
||||
#ifdef DROPBEAR_SHA1_HMAC
|
||||
{"hmac-sha1", 0, (void*)&dropbear_sha1, 1},
|
||||
{"hmac-sha1", 0, &dropbear_sha1, 1, NULL},
|
||||
#endif
|
||||
#ifdef DROPBEAR_MD5_HMAC
|
||||
{"hmac-md5", 0, (void*)&dropbear_md5, 1},
|
||||
{"hmac-md5", 0, &dropbear_md5, 1, NULL},
|
||||
#endif
|
||||
{NULL, 0, NULL, 0}
|
||||
{NULL, 0, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
algo_type sshcompress[] = {
|
||||
#ifndef DISABLE_ZLIB
|
||||
{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1},
|
||||
{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL},
|
||||
{"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL},
|
||||
#endif
|
||||
{"none", DROPBEAR_COMP_NONE, NULL, 1},
|
||||
{NULL, 0, NULL, 0}
|
||||
{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
|
||||
{NULL, 0, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
algo_type sshhostkey[] = {
|
||||
#ifdef DROPBEAR_RSA
|
||||
{"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1},
|
||||
{"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL},
|
||||
#endif
|
||||
#ifdef DROPBEAR_DSS
|
||||
{"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1},
|
||||
{"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL},
|
||||
#endif
|
||||
{NULL, 0, NULL, 0}
|
||||
{NULL, 0, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
algo_type sshkex[] = {
|
||||
{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1},
|
||||
{NULL, 0, NULL, 0}
|
||||
{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL},
|
||||
{NULL, 0, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
||||
@ -150,16 +198,16 @@ algo_type sshkex[] = {
|
||||
void crypto_init() {
|
||||
|
||||
const struct ltc_cipher_descriptor *regciphers[] = {
|
||||
#ifdef DROPBEAR_AES_CBC
|
||||
#ifdef DROPBEAR_AES
|
||||
&aes_desc,
|
||||
#endif
|
||||
#ifdef DROPBEAR_BLOWFISH_CBC
|
||||
#ifdef DROPBEAR_BLOWFISH
|
||||
&blowfish_desc,
|
||||
#endif
|
||||
#ifdef DROPBEAR_TWOFISH_CBC
|
||||
#ifdef DROPBEAR_TWOFISH
|
||||
&twofish_desc,
|
||||
#endif
|
||||
#ifdef DROPBEAR_3DES_CBC
|
||||
#ifdef DROPBEAR_3DES
|
||||
&des3_desc,
|
||||
#endif
|
||||
NULL
|
||||
@ -215,7 +263,7 @@ void buf_put_algolist(buffer * buf, algo_type localalgos[]) {
|
||||
unsigned int donefirst = 0;
|
||||
buffer *algolist = NULL;
|
||||
|
||||
algolist = buf_new(100);
|
||||
algolist = buf_new(160);
|
||||
for (i = 0; localalgos[i].name != NULL; i++) {
|
||||
if (localalgos[i].usable) {
|
||||
if (donefirst)
|
||||
|
@ -276,10 +276,10 @@ static void check_close(struct Channel *channel) {
|
||||
channel->flushing = 1;
|
||||
}
|
||||
|
||||
// if a type-specific check_close is defined we will only exit
|
||||
// once that has been triggered. this is only used for a server "session"
|
||||
// channel, to ensure that the shell has exited (and the exit status
|
||||
// retrieved) before we close things up.
|
||||
/* if a type-specific check_close is defined we will only exit
|
||||
once that has been triggered. this is only used for a server "session"
|
||||
channel, to ensure that the shell has exited (and the exit status
|
||||
retrieved) before we close things up. */
|
||||
if (!channel->type->check_close
|
||||
|| channel->type->check_close(channel)) {
|
||||
close_allowed = 1;
|
||||
@ -691,7 +691,7 @@ void common_recv_msg_channel_data(struct Channel *channel, int fd,
|
||||
dropbear_exit("received data after eof");
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
if (fd < 0) {
|
||||
/* If we have encountered failed write, the far side might still
|
||||
* be sending data without having yet received our close notification.
|
||||
* We just drop the data. */
|
||||
|
126
common-kex.c
126
common-kex.c
@ -272,8 +272,8 @@ void gen_new_keys() {
|
||||
recv_IV = S2C_IV;
|
||||
trans_key = C2S_key;
|
||||
recv_key = S2C_key;
|
||||
C2S_keysize = ses.newkeys->trans_algo_crypt->keysize;
|
||||
S2C_keysize = ses.newkeys->recv_algo_crypt->keysize;
|
||||
C2S_keysize = ses.newkeys->trans.algo_crypt->keysize;
|
||||
S2C_keysize = ses.newkeys->recv.algo_crypt->keysize;
|
||||
mactransletter = 'E';
|
||||
macrecvletter = 'F';
|
||||
} else {
|
||||
@ -281,8 +281,8 @@ void gen_new_keys() {
|
||||
recv_IV = C2S_IV;
|
||||
trans_key = S2C_key;
|
||||
recv_key = C2S_key;
|
||||
C2S_keysize = ses.newkeys->recv_algo_crypt->keysize;
|
||||
S2C_keysize = ses.newkeys->trans_algo_crypt->keysize;
|
||||
C2S_keysize = ses.newkeys->recv.algo_crypt->keysize;
|
||||
S2C_keysize = ses.newkeys->trans.algo_crypt->keysize;
|
||||
mactransletter = 'F';
|
||||
macrecvletter = 'E';
|
||||
}
|
||||
@ -292,30 +292,33 @@ void gen_new_keys() {
|
||||
hashkeys(C2S_key, C2S_keysize, &hs, 'C');
|
||||
hashkeys(S2C_key, S2C_keysize, &hs, 'D');
|
||||
|
||||
recv_cipher = find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name);
|
||||
recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
|
||||
if (recv_cipher < 0)
|
||||
dropbear_exit("crypto error");
|
||||
|
||||
if (cbc_start(recv_cipher, recv_IV, recv_key,
|
||||
ses.newkeys->recv_algo_crypt->keysize, 0,
|
||||
&ses.newkeys->recv_symmetric_struct) != CRYPT_OK) {
|
||||
if (ses.newkeys->recv.crypt_mode->start(recv_cipher,
|
||||
recv_IV, recv_key,
|
||||
ses.newkeys->recv.algo_crypt->keysize, 0,
|
||||
&ses.newkeys->recv.cipher_state) != CRYPT_OK) {
|
||||
dropbear_exit("crypto error");
|
||||
}
|
||||
trans_cipher = find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name);
|
||||
|
||||
trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
|
||||
if (trans_cipher < 0)
|
||||
dropbear_exit("crypto error");
|
||||
|
||||
if (cbc_start(trans_cipher, trans_IV, trans_key,
|
||||
ses.newkeys->trans_algo_crypt->keysize, 0,
|
||||
&ses.newkeys->trans_symmetric_struct) != CRYPT_OK) {
|
||||
if (ses.newkeys->trans.crypt_mode->start(trans_cipher,
|
||||
trans_IV, trans_key,
|
||||
ses.newkeys->trans.algo_crypt->keysize, 0,
|
||||
&ses.newkeys->trans.cipher_state) != CRYPT_OK) {
|
||||
dropbear_exit("crypto error");
|
||||
}
|
||||
|
||||
/* MAC keys */
|
||||
hashkeys(ses.newkeys->transmackey,
|
||||
ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter);
|
||||
hashkeys(ses.newkeys->recvmackey,
|
||||
ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter);
|
||||
hashkeys(ses.newkeys->trans.mackey,
|
||||
ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter);
|
||||
hashkeys(ses.newkeys->recv.mackey,
|
||||
ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter);
|
||||
ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hashdesc->name),
|
||||
ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name),
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
gen_new_zstreams();
|
||||
@ -331,53 +334,68 @@ void gen_new_keys() {
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
|
||||
int is_compress_trans() {
|
||||
return ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB
|
||||
|| (ses.authstate.authdone
|
||||
&& ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
|
||||
}
|
||||
|
||||
int is_compress_recv() {
|
||||
return ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB
|
||||
|| (ses.authstate.authdone
|
||||
&& ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
|
||||
}
|
||||
|
||||
/* Set up new zlib compression streams, close the old ones. Only
|
||||
* called from gen_new_keys() */
|
||||
static void gen_new_zstreams() {
|
||||
|
||||
/* create new zstreams */
|
||||
if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
|
||||
ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream));
|
||||
ses.newkeys->recv_zstream->zalloc = Z_NULL;
|
||||
ses.newkeys->recv_zstream->zfree = Z_NULL;
|
||||
if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB
|
||||
|| ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
|
||||
ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream));
|
||||
ses.newkeys->recv.zstream->zalloc = Z_NULL;
|
||||
ses.newkeys->recv.zstream->zfree = Z_NULL;
|
||||
|
||||
if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) {
|
||||
if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) {
|
||||
dropbear_exit("zlib error");
|
||||
}
|
||||
} else {
|
||||
ses.newkeys->recv_zstream = NULL;
|
||||
ses.newkeys->recv.zstream = NULL;
|
||||
}
|
||||
|
||||
if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
|
||||
ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream));
|
||||
ses.newkeys->trans_zstream->zalloc = Z_NULL;
|
||||
ses.newkeys->trans_zstream->zfree = Z_NULL;
|
||||
if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB
|
||||
|| ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
|
||||
ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream));
|
||||
ses.newkeys->trans.zstream->zalloc = Z_NULL;
|
||||
ses.newkeys->trans.zstream->zfree = Z_NULL;
|
||||
|
||||
if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION)
|
||||
if (deflateInit(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION)
|
||||
!= Z_OK) {
|
||||
dropbear_exit("zlib error");
|
||||
}
|
||||
} else {
|
||||
ses.newkeys->trans_zstream = NULL;
|
||||
ses.newkeys->trans.zstream = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* clean up old keys */
|
||||
if (ses.keys->recv_zstream != NULL) {
|
||||
if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) {
|
||||
if (ses.keys->recv.zstream != NULL) {
|
||||
if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
|
||||
/* Z_DATA_ERROR is ok, just means that stream isn't ended */
|
||||
dropbear_exit("crypto error");
|
||||
}
|
||||
m_free(ses.keys->recv_zstream);
|
||||
m_free(ses.keys->recv.zstream);
|
||||
}
|
||||
if (ses.keys->trans_zstream != NULL) {
|
||||
if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) {
|
||||
if (ses.keys->trans.zstream != NULL) {
|
||||
if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) {
|
||||
/* Z_DATA_ERROR is ok, just means that stream isn't ended */
|
||||
dropbear_exit("crypto error");
|
||||
}
|
||||
m_free(ses.keys->trans_zstream);
|
||||
m_free(ses.keys->trans.zstream);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* DISABLE_ZLIB */
|
||||
|
||||
|
||||
/* Executed upon receiving a kexinit message from the client to initiate
|
||||
@ -682,28 +700,36 @@ static void read_kex_algos() {
|
||||
|
||||
/* Handle the asymmetry */
|
||||
if (IS_DROPBEAR_CLIENT) {
|
||||
ses.newkeys->recv_algo_crypt =
|
||||
ses.newkeys->recv.algo_crypt =
|
||||
(struct dropbear_cipher*)s2c_cipher_algo->data;
|
||||
ses.newkeys->trans_algo_crypt =
|
||||
ses.newkeys->trans.algo_crypt =
|
||||
(struct dropbear_cipher*)c2s_cipher_algo->data;
|
||||
ses.newkeys->recv_algo_mac =
|
||||
ses.newkeys->recv.crypt_mode =
|
||||
(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
|
||||
ses.newkeys->trans.crypt_mode =
|
||||
(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
|
||||
ses.newkeys->recv.algo_mac =
|
||||
(struct dropbear_hash*)s2c_hash_algo->data;
|
||||
ses.newkeys->trans_algo_mac =
|
||||
ses.newkeys->trans.algo_mac =
|
||||
(struct dropbear_hash*)c2s_hash_algo->data;
|
||||
ses.newkeys->recv_algo_comp = s2c_comp_algo->val;
|
||||
ses.newkeys->trans_algo_comp = c2s_comp_algo->val;
|
||||
ses.newkeys->recv.algo_comp = s2c_comp_algo->val;
|
||||
ses.newkeys->trans.algo_comp = c2s_comp_algo->val;
|
||||
} else {
|
||||
/* SERVER */
|
||||
ses.newkeys->recv_algo_crypt =
|
||||
ses.newkeys->recv.algo_crypt =
|
||||
(struct dropbear_cipher*)c2s_cipher_algo->data;
|
||||
ses.newkeys->trans_algo_crypt =
|
||||
ses.newkeys->trans.algo_crypt =
|
||||
(struct dropbear_cipher*)s2c_cipher_algo->data;
|
||||
ses.newkeys->recv_algo_mac =
|
||||
ses.newkeys->recv.crypt_mode =
|
||||
(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
|
||||
ses.newkeys->trans.crypt_mode =
|
||||
(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
|
||||
ses.newkeys->recv.algo_mac =
|
||||
(struct dropbear_hash*)c2s_hash_algo->data;
|
||||
ses.newkeys->trans_algo_mac =
|
||||
ses.newkeys->trans.algo_mac =
|
||||
(struct dropbear_hash*)s2c_hash_algo->data;
|
||||
ses.newkeys->recv_algo_comp = c2s_comp_algo->val;
|
||||
ses.newkeys->trans_algo_comp = s2c_comp_algo->val;
|
||||
ses.newkeys->recv.algo_comp = c2s_comp_algo->val;
|
||||
ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
|
||||
}
|
||||
|
||||
/* reserved for future extensions */
|
||||
|
@ -63,6 +63,7 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
|
||||
ses.maxfd = MAX(sock_in, sock_out);
|
||||
|
||||
ses.connect_time = 0;
|
||||
ses.last_trx_packet_time = 0;
|
||||
ses.last_packet_time = 0;
|
||||
|
||||
if (pipe(ses.signal_pipe) < 0) {
|
||||
@ -70,6 +71,9 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
|
||||
}
|
||||
setnonblocking(ses.signal_pipe[0]);
|
||||
setnonblocking(ses.signal_pipe[1]);
|
||||
|
||||
ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]);
|
||||
ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]);
|
||||
|
||||
kexfirstinitialise(); /* initialise the kex state */
|
||||
|
||||
@ -77,7 +81,6 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
|
||||
ses.transseq = 0;
|
||||
|
||||
ses.readbuf = NULL;
|
||||
ses.decryptreadbuf = NULL;
|
||||
ses.payload = NULL;
|
||||
ses.recvseq = 0;
|
||||
|
||||
@ -94,20 +97,22 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
|
||||
/* set all the algos to none */
|
||||
ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
|
||||
ses.newkeys = NULL;
|
||||
ses.keys->recv_algo_crypt = &dropbear_nocipher;
|
||||
ses.keys->trans_algo_crypt = &dropbear_nocipher;
|
||||
ses.keys->recv.algo_crypt = &dropbear_nocipher;
|
||||
ses.keys->trans.algo_crypt = &dropbear_nocipher;
|
||||
ses.keys->recv.crypt_mode = &dropbear_mode_none;
|
||||
ses.keys->trans.crypt_mode = &dropbear_mode_none;
|
||||
|
||||
ses.keys->recv_algo_mac = &dropbear_nohash;
|
||||
ses.keys->trans_algo_mac = &dropbear_nohash;
|
||||
ses.keys->recv.algo_mac = &dropbear_nohash;
|
||||
ses.keys->trans.algo_mac = &dropbear_nohash;
|
||||
|
||||
ses.keys->algo_kex = -1;
|
||||
ses.keys->algo_hostkey = -1;
|
||||
ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE;
|
||||
ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE;
|
||||
ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE;
|
||||
ses.keys->trans.algo_comp = DROPBEAR_COMP_NONE;
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
ses.keys->recv_zstream = NULL;
|
||||
ses.keys->trans_zstream = NULL;
|
||||
ses.keys->recv.zstream = NULL;
|
||||
ses.keys->trans.zstream = NULL;
|
||||
#endif
|
||||
|
||||
/* key exchange buffers */
|
||||
@ -256,7 +261,7 @@ void session_identification() {
|
||||
ses.remoteclosed();
|
||||
}
|
||||
|
||||
/* If they send more than 50 lines, something is wrong */
|
||||
/* If they send more than 50 lines, something is wrong */
|
||||
for (i = 0; i < 50; i++) {
|
||||
len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf));
|
||||
|
||||
@ -281,11 +286,11 @@ void session_identification() {
|
||||
memcpy(ses.remoteident, linebuf, len);
|
||||
}
|
||||
|
||||
/* Shall assume that 2.x will be backwards compatible. */
|
||||
if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
|
||||
&& strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
|
||||
dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
|
||||
}
|
||||
/* Shall assume that 2.x will be backwards compatible. */
|
||||
if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
|
||||
&& strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
|
||||
dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
|
||||
}
|
||||
|
||||
TRACE(("remoteident: %s", ses.remoteident))
|
||||
|
||||
@ -397,9 +402,14 @@ static void checktimeouts() {
|
||||
}
|
||||
|
||||
if (opts.keepalive_secs > 0
|
||||
&& now - ses.last_packet_time >= opts.keepalive_secs) {
|
||||
&& now - ses.last_trx_packet_time >= opts.keepalive_secs) {
|
||||
send_msg_ignore();
|
||||
}
|
||||
|
||||
if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0
|
||||
&& now - ses.last_packet_time >= opts.idle_timeout_secs) {
|
||||
dropbear_close("Idle timeout");
|
||||
}
|
||||
}
|
||||
|
||||
static long select_timeout() {
|
||||
@ -412,6 +422,8 @@ static long select_timeout() {
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,8 @@ AC_CHECK_DECL(__UCLIBC__,
|
||||
],,,)
|
||||
|
||||
# Checks for libraries.
|
||||
AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt")
|
||||
AC_CHECK_LIB(crypt, crypt, CRYPTLIB="-lcrypt")
|
||||
AC_SUBST(CRYPTLIB)
|
||||
|
||||
# Check if zlib is needed
|
||||
AC_ARG_WITH(zlib,
|
||||
|
47
dbclient.1
47
dbclient.1
@ -10,6 +10,13 @@ dbclient \- lightweight SSH2 client
|
||||
.I l\fR:\fIh\fR:\fIr\fR] [\-l
|
||||
.IR user ]
|
||||
.I host
|
||||
.RI [ command ]
|
||||
|
||||
.B dbclient
|
||||
[
|
||||
.I args ]
|
||||
.I [user1]@host1[/port1],[user2]@host2[/port2],...
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B dbclient
|
||||
is a SSH 2 client designed to be small enough to be used in small memory
|
||||
@ -86,22 +93,52 @@ useful for working around firewalls or routers that drop connections after
|
||||
a certain period of inactivity. The trade-off is that a session may be
|
||||
closed if there is a temporary lapse of network connectivity. A setting
|
||||
if 0 disables keepalives.
|
||||
.TP
|
||||
.B \-I \fIidle_timeout
|
||||
Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
|
||||
.TP
|
||||
.B \-J \fIproxy_command
|
||||
Use the standard input/output of the program \fIproxy_command\fR rather than using
|
||||
a normal TCP connection. A hostname should be still be provided, as this is used for
|
||||
comparing saved hostkeys.
|
||||
.TP
|
||||
.B \-B \fIendhost:endport
|
||||
"Netcat-alike" mode, where Dropbear will connect to the given host, then create a
|
||||
forwarded connection to \fIendhost\fR. This will then be presented as dbclient's
|
||||
standard input/output.
|
||||
|
||||
Dropbear will also allow multiple "hops" to be specified, separated by commas. In
|
||||
this case a connection will be made to the first host, then a TCP forwarded
|
||||
connection will be made through that to the second host, and so on. Hosts other than
|
||||
the final destination will not see anything other than the encrypted SSH stream.
|
||||
A port for a host can be specified with a slash (eg matt@martello/44 ).
|
||||
This syntax can also be used with scp or rsync (specifying dbclient as the
|
||||
ssh/rsh command). A file can be "bounced" through multiple SSH hops, eg
|
||||
|
||||
scp -S dbclient matt@martello,root@wrt,canyons:/tmp/dump .
|
||||
|
||||
Note that hostnames are resolved by the prior hop (so "canyons" would be resolved by the host "wrt")
|
||||
in the example above, the same way as other -L TCP forwarded hosts are. Host keys are
|
||||
checked locally based on the given hostname.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B DROPBEAR_PASSWORD
|
||||
A password to use for remote authentication can be specified in the environment
|
||||
variable DROPBEAR_PASSWORD. Care should be taken that the password is not
|
||||
exposed to other users on a multi-user system, or stored in accessible files.
|
||||
.TP
|
||||
.B SSH_ASKPASS
|
||||
dbclient can use an external program to request a password from a user.
|
||||
SSH_ASKPASS should be set to the path of a program that will return a password
|
||||
on standard output. This program will only be used if either DISPLAY is set and
|
||||
standard input is not a TTY, or the environment variable SSH_ASKPASS_ALWAYS is
|
||||
set.
|
||||
.TP
|
||||
.B DROPBEAR_PASSWORD
|
||||
A password to use for remote authentication can be specified in the environment
|
||||
variable DROPBEAR_PASSWORD. Care should be taken that the password is not
|
||||
exposed to other users on a multi-user system, or stored in accessible files.
|
||||
.SH AUTHOR
|
||||
Matt Johnston (matt@ucc.asn.au).
|
||||
.br
|
||||
Mihnea Stoenescu wrote initial Dropbear client support
|
||||
.br
|
||||
Gerrit Pape (pape@smarden.org) wrote this manual page.
|
||||
.SH SEE ALSO
|
||||
dropbear(8), dropbearkey(8)
|
||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
||||
dropbear (0.52-0.1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Wed, 12 Nov 2008 22:54:00 +0900
|
||||
|
||||
dropbear (0.51-0.1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
2
debian/dropbear.postinst
vendored
2
debian/dropbear.postinst
vendored
@ -71,7 +71,7 @@ if test -x /etc/init.d/dropbear; then
|
||||
fi
|
||||
|
||||
if test -n "$2" && dpkg --compare-versions "$2" lt '0.50-4' &&
|
||||
update-service --check dropbear; then
|
||||
update-service --check dropbear 2>/dev/null; then
|
||||
update-service --remove /etc/dropbear 2>/dev/null || :
|
||||
sleep 6
|
||||
rm -rf /var/run/dropbear /var/run/dropbear.log
|
||||
|
2
debug.h
2
debug.h
@ -39,7 +39,7 @@
|
||||
* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
|
||||
* since the printing may not sanitise strings etc. This will add a reasonable
|
||||
* amount to your executable size. */
|
||||
#define DEBUG_TRACE
|
||||
/*#define DEBUG_TRACE*/
|
||||
|
||||
/* All functions writing to the cleartext payload buffer call
|
||||
* CHECKCLEARTOWRITE() before writing. This is only really useful if you're
|
||||
|
62
dropbear.8
62
dropbear.8
@ -24,7 +24,10 @@ before user login (default: none).
|
||||
dsskeyfile.
|
||||
Use the contents of the file
|
||||
.I dsskey
|
||||
for the dss host key (default: /etc/dropbear/dropbear_dss_host_key).
|
||||
for the DSS host key (default: /etc/dropbear/dropbear_dss_host_key).
|
||||
Note that
|
||||
some SSH implementations
|
||||
use the term "DSA" rather than "DSS", they mean the same thing.
|
||||
This file is generated with
|
||||
.BR dropbearkey (8).
|
||||
.TP
|
||||
@ -94,6 +97,63 @@ useful for working around firewalls or routers that drop connections after
|
||||
a certain period of inactivity. The trade-off is that a session may be
|
||||
closed if there is a temporary lapse of network connectivity. A setting
|
||||
if 0 disables keepalives.
|
||||
.TP
|
||||
.B \-I \fIidle_timeout
|
||||
Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
|
||||
.SH FILES
|
||||
|
||||
.TP
|
||||
Authorized Keys
|
||||
|
||||
~/.ssh/authorized_keys can be set up to allow remote login with a RSA or DSS
|
||||
key. Each line is of the form
|
||||
.TP
|
||||
[restrictions] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIgAsp... [comment]
|
||||
|
||||
and can be extracted from a Dropbear private host key with "dropbearkey -y". This is the same format as used by OpenSSH, though the restrictions are a subset (keys with unknown restrictions are ignored).
|
||||
Restrictions are comma separated, with double quotes around spaces in arguments.
|
||||
Available restrictions are:
|
||||
|
||||
.TP
|
||||
.B no-port-forwarding
|
||||
Don't allow port forwarding for this connection
|
||||
|
||||
.TP
|
||||
.B no-agent-forwarding
|
||||
Don't allow agent forwarding for this connection
|
||||
|
||||
.TP
|
||||
.B no-X11-forwarding
|
||||
Don't allow X11 forwarding for this connection
|
||||
|
||||
.TP
|
||||
.B no-pty
|
||||
Disable PTY allocation. Note that a user can still obtain most of the
|
||||
same functionality with other means even if no-pty is set.
|
||||
|
||||
.TP
|
||||
.B command="\fIforced_command\fR"
|
||||
Disregard the command provided by the user and always run \fIforced_command\fR.
|
||||
|
||||
The authorized_keys file and its containing ~/.ssh directory must only be
|
||||
writable by the user, otherwise Dropbear will not allow a login using public
|
||||
key authentication.
|
||||
|
||||
.TP
|
||||
Host Key Files
|
||||
|
||||
Host key files are read at startup from a standard location, by default
|
||||
/etc/dropbear/dropbear_dss_host_key and /etc/dropbear/dropbear_rsa_host_key
|
||||
or specified on the commandline with -d or -r. These are of the form generated
|
||||
by dropbearkey.
|
||||
|
||||
.TP
|
||||
Message Of The Day
|
||||
|
||||
By default the file /etc/motd will be printed for any login shell (unless
|
||||
disabled at compile-time). This can also be disabled per-user
|
||||
by creating a file ~/.hushlogin .
|
||||
|
||||
.SH AUTHOR
|
||||
Matt Johnston (matt@ucc.asn.au).
|
||||
.br
|
||||
|
@ -11,13 +11,16 @@ dropbearkey \- create private keys for the use with dropbear(8)
|
||||
.IR bits ]
|
||||
.SH DESCRIPTION
|
||||
.B dropbearkey
|
||||
generates a type
|
||||
.I rsa
|
||||
generates a
|
||||
.I RSA
|
||||
or
|
||||
.I dss
|
||||
SSH private key, and saves it to a file for the use with the
|
||||
.I DSS
|
||||
format SSH private key, and saves it to a file for the use with the
|
||||
.BR dropbear (8)
|
||||
SSH 2 server.
|
||||
Note that
|
||||
some SSH implementations
|
||||
use the term "DSA" rather than "DSS", they mean the same thing.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-t \fItype
|
||||
|
5
kex.h
5
kex.h
@ -37,6 +37,11 @@ void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
|
||||
void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
|
||||
sign_key *hostkey);
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
int is_compress_trans();
|
||||
int is_compress_recv();
|
||||
#endif
|
||||
|
||||
void recv_msg_kexdh_init(); /* server */
|
||||
|
||||
void send_msg_kexdh_init(); /* client */
|
||||
|
@ -90,15 +90,15 @@
|
||||
/* #define LTC_NO_BSWAP */
|
||||
|
||||
|
||||
#ifdef DROPBEAR_BLOWFISH_CBC
|
||||
#ifdef DROPBEAR_BLOWFISH
|
||||
#define BLOWFISH
|
||||
#endif
|
||||
|
||||
#ifdef DROPBEAR_AES_CBC
|
||||
#ifdef DROPBEAR_AES
|
||||
#define RIJNDAEL
|
||||
#endif
|
||||
|
||||
#ifdef DROPBEAR_TWOFISH_CBC
|
||||
#ifdef DROPBEAR_TWOFISH
|
||||
#define TWOFISH
|
||||
|
||||
/* enabling just TWOFISH_SMALL will make the binary ~1kB smaller, turning on
|
||||
@ -108,12 +108,16 @@
|
||||
/*#define TWOFISH_TABLES*/
|
||||
#endif
|
||||
|
||||
#ifdef DROPBEAR_3DES_CBC
|
||||
#ifdef DROPBEAR_3DES
|
||||
#define DES
|
||||
#endif
|
||||
|
||||
#define LTC_CBC_MODE
|
||||
|
||||
#ifdef DROPBEAR_ENABLE_CTR_MODE
|
||||
#define LTC_CTR_MODE
|
||||
#endif
|
||||
|
||||
#if defined(DROPBEAR_DSS) && defined(DSS_PROTOK)
|
||||
#define SHA512
|
||||
#endif
|
||||
|
@ -1334,7 +1334,7 @@ lastlog_openseek(struct logininfo *li, int *fd, int filemode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
*fd = open(lastlog_file, filemode);
|
||||
*fd = open(lastlog_file, filemode, 0600);
|
||||
if ( *fd < 0) {
|
||||
dropbear_log(LOG_INFO, "lastlog_openseek: Couldn't open %s: %s",
|
||||
lastlog_file, strerror(errno));
|
||||
|
53
options.h
53
options.h
@ -46,9 +46,10 @@
|
||||
/*#define NO_FAST_EXPTMOD*/
|
||||
|
||||
/* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save
|
||||
several kB in binary size, however will make the symmetrical ciphers (AES, DES
|
||||
etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
#define DROPBEAR_SMALL_CODE
|
||||
several kB in binary size however will make the symmetrical ciphers and hashes
|
||||
slower, perhaps by 50%. Recommended for small systems that aren't doing
|
||||
much traffic. */
|
||||
/*#define DROPBEAR_SMALL_CODE*/
|
||||
|
||||
/* Enable X11 Forwarding - server only */
|
||||
#define ENABLE_X11FWD
|
||||
@ -60,10 +61,6 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
#define ENABLE_CLI_LOCALTCPFWD
|
||||
#define ENABLE_CLI_REMOTETCPFWD
|
||||
|
||||
/* Allow using -J <proxycommand> to run the connection through a
|
||||
pipe to a program, rather the normal TCP connection */
|
||||
#define ENABLE_CLI_PROXYCMD
|
||||
|
||||
#define ENABLE_SVR_LOCALTCPFWD
|
||||
#define ENABLE_SVR_REMOTETCPFWD
|
||||
|
||||
@ -71,23 +68,36 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
#define ENABLE_SVR_AGENTFWD
|
||||
#define ENABLE_CLI_AGENTFWD
|
||||
|
||||
/* Enable "Netcat mode". TODO describe here. */
|
||||
|
||||
/* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to
|
||||
* allow multihop dbclient connections */
|
||||
|
||||
/* Allow using -J <proxycommand> to run the connection through a
|
||||
pipe to a program, rather the normal TCP connection */
|
||||
#define ENABLE_CLI_PROXYCMD
|
||||
|
||||
/* Enable "Netcat mode" option. This will forward standard input/output
|
||||
* to a remote TCP-forwarded connection */
|
||||
#define ENABLE_CLI_NETCAT
|
||||
|
||||
|
||||
/* Encryption - at least one required.
|
||||
* RFC Draft requires 3DES and recommends AES128 for interoperability.
|
||||
* Protocol RFC requires 3DES and recommends AES128 for interoperability.
|
||||
* Including multiple keysize variants the same cipher
|
||||
* (eg AES256 as well as AES128) will result in a minimal size increase.*/
|
||||
#define DROPBEAR_AES128_CBC
|
||||
#define DROPBEAR_3DES_CBC
|
||||
#define DROPBEAR_AES256_CBC
|
||||
#define DROPBEAR_BLOWFISH_CBC
|
||||
#define DROPBEAR_TWOFISH256_CBC
|
||||
#define DROPBEAR_TWOFISH128_CBC
|
||||
#define DROPBEAR_AES128
|
||||
#define DROPBEAR_3DES
|
||||
#define DROPBEAR_AES256
|
||||
#define DROPBEAR_BLOWFISH
|
||||
#define DROPBEAR_TWOFISH256
|
||||
#define DROPBEAR_TWOFISH128
|
||||
|
||||
/* Enable "Counter Mode" for ciphers. This is more secure than normal
|
||||
* CBC mode against certain attacks. This adds around 1kB to binary
|
||||
* size and is recommended for most cases */
|
||||
#define DROPBEAR_ENABLE_CTR_MODE
|
||||
|
||||
/* Message Integrity - at least one required.
|
||||
* RFC Draft requires sha1 and recommends sha1-96.
|
||||
* Protocol RFC requires sha1 and recommends sha1-96.
|
||||
* sha1-96 may be of use for slow links, as it has a smaller overhead.
|
||||
*
|
||||
* Note: there's no point disabling sha1 to save space, since it's used
|
||||
@ -143,7 +153,7 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
|
||||
#define ENABLE_SVR_PASSWORD_AUTH
|
||||
/* PAM requires ./configure --enable-pam */
|
||||
/* #define ENABLE_SVR_PAM_AUTH */
|
||||
/*#define ENABLE_SVR_PAM_AUTH*/
|
||||
#define ENABLE_SVR_PUBKEY_AUTH
|
||||
|
||||
/* Wether to ake public key options in authorized_keys file into account */
|
||||
@ -250,6 +260,13 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
|
||||
be overridden at runtime with -K. 0 disables keepalives */
|
||||
#define DEFAULT_KEEPALIVE 0
|
||||
|
||||
/* 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
|
||||
|
||||
/* The default path. This will often get replaced by the shell */
|
||||
#define DEFAULT_PATH "/usr/bin:/bin"
|
||||
|
||||
/* Some other defines (that mostly should be left alone) are defined
|
||||
* in sysoptions.h */
|
||||
#include "sysoptions.h"
|
||||
|
323
packet.c
323
packet.c
@ -35,9 +35,11 @@
|
||||
#include "auth.h"
|
||||
#include "channel.h"
|
||||
|
||||
static void read_packet_init();
|
||||
static void writemac(buffer * outputbuffer, buffer * clearwritebuf);
|
||||
static int checkmac(buffer* hashbuf, buffer* readbuf);
|
||||
static int read_packet_init();
|
||||
static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
|
||||
buffer * clear_buf, unsigned int clear_len,
|
||||
unsigned char *output_mac);
|
||||
static int checkmac();
|
||||
|
||||
#define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */
|
||||
#define ZLIB_DECOMPRESS_INCR 100
|
||||
@ -72,6 +74,7 @@ void write_packet() {
|
||||
}
|
||||
}
|
||||
|
||||
ses.last_trx_packet_time = time(NULL);
|
||||
ses.last_packet_time = time(NULL);
|
||||
|
||||
if (written == 0) {
|
||||
@ -101,18 +104,18 @@ void read_packet() {
|
||||
unsigned char blocksize;
|
||||
|
||||
TRACE(("enter read_packet"))
|
||||
blocksize = ses.keys->recv_algo_crypt->blocksize;
|
||||
blocksize = ses.keys->recv.algo_crypt->blocksize;
|
||||
|
||||
if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
|
||||
int ret;
|
||||
/* In the first blocksize of a packet */
|
||||
|
||||
/* Read the first blocksize of the packet, so we can decrypt it and
|
||||
* find the length of the whole packet */
|
||||
read_packet_init();
|
||||
ret = read_packet_init();
|
||||
|
||||
/* If we don't have the length of decryptreadbuf, we didn't read
|
||||
* a whole blocksize and should exit */
|
||||
if (ses.decryptreadbuf->len == 0) {
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
/* didn't read enough to determine the length */
|
||||
TRACE(("leave read_packet: packetinit done"))
|
||||
return;
|
||||
}
|
||||
@ -120,7 +123,6 @@ void read_packet() {
|
||||
|
||||
/* Attempt to read the remainder of the packet, note that there
|
||||
* mightn't be any available (EAGAIN) */
|
||||
dropbear_assert(ses.readbuf != NULL);
|
||||
maxlen = ses.readbuf->len - ses.readbuf->pos;
|
||||
len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen);
|
||||
|
||||
@ -150,7 +152,9 @@ void read_packet() {
|
||||
|
||||
/* Function used to read the initial portion of a packet, and determine the
|
||||
* length. Only called during the first BLOCKSIZE of a packet. */
|
||||
static void read_packet_init() {
|
||||
/* Returns DROPBEAR_SUCCESS if the length is determined,
|
||||
* DROPBEAR_FAILURE otherwise */
|
||||
static int read_packet_init() {
|
||||
|
||||
unsigned int maxlen;
|
||||
int len;
|
||||
@ -158,14 +162,12 @@ static void read_packet_init() {
|
||||
unsigned char macsize;
|
||||
|
||||
|
||||
blocksize = ses.keys->recv_algo_crypt->blocksize;
|
||||
macsize = ses.keys->recv_algo_mac->hashsize;
|
||||
blocksize = ses.keys->recv.algo_crypt->blocksize;
|
||||
macsize = ses.keys->recv.algo_mac->hashsize;
|
||||
|
||||
if (ses.readbuf == NULL) {
|
||||
/* start of a new packet */
|
||||
ses.readbuf = buf_new(INIT_READBUF);
|
||||
dropbear_assert(ses.decryptreadbuf == NULL);
|
||||
ses.decryptreadbuf = buf_new(blocksize);
|
||||
}
|
||||
|
||||
maxlen = blocksize - ses.readbuf->pos;
|
||||
@ -179,7 +181,7 @@ static void read_packet_init() {
|
||||
if (len < 0) {
|
||||
if (errno == EINTR) {
|
||||
TRACE(("leave read_packet_init: EINTR"))
|
||||
return;
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
dropbear_exit("error reading: %s", strerror(errno));
|
||||
}
|
||||
@ -188,30 +190,22 @@ static void read_packet_init() {
|
||||
|
||||
if ((unsigned int)len != maxlen) {
|
||||
/* don't have enough bytes to determine length, get next time */
|
||||
return;
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
/* now we have the first block, need to get packet length, so we decrypt
|
||||
* the first block (only need first 4 bytes) */
|
||||
buf_setpos(ses.readbuf, 0);
|
||||
if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
|
||||
/* copy it */
|
||||
memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize),
|
||||
buf_getptr(ses.readbuf, blocksize),
|
||||
blocksize);
|
||||
} else {
|
||||
/* decrypt it */
|
||||
if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize),
|
||||
buf_getwriteptr(ses.decryptreadbuf,blocksize),
|
||||
blocksize,
|
||||
&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
|
||||
dropbear_exit("error decrypting");
|
||||
}
|
||||
if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize),
|
||||
buf_getwriteptr(ses.readbuf, blocksize),
|
||||
blocksize,
|
||||
&ses.keys->recv.cipher_state) != CRYPT_OK) {
|
||||
dropbear_exit("error decrypting");
|
||||
}
|
||||
buf_setlen(ses.decryptreadbuf, blocksize);
|
||||
len = buf_getint(ses.decryptreadbuf) + 4 + macsize;
|
||||
len = buf_getint(ses.readbuf) + 4 + macsize;
|
||||
|
||||
TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize))
|
||||
|
||||
buf_setpos(ses.readbuf, blocksize);
|
||||
|
||||
/* check packet length */
|
||||
if ((len > RECV_MAX_PACKET_LEN) ||
|
||||
@ -220,9 +214,12 @@ static void read_packet_init() {
|
||||
dropbear_exit("bad packet size %d", len);
|
||||
}
|
||||
|
||||
buf_resize(ses.readbuf, len);
|
||||
if (len > ses.readbuf->size) {
|
||||
buf_resize(ses.readbuf, len);
|
||||
}
|
||||
buf_setlen(ses.readbuf, len);
|
||||
|
||||
buf_setpos(ses.readbuf, blocksize);
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
/* handle the received packet */
|
||||
@ -234,77 +231,58 @@ void decrypt_packet() {
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter decrypt_packet"))
|
||||
blocksize = ses.keys->recv_algo_crypt->blocksize;
|
||||
macsize = ses.keys->recv_algo_mac->hashsize;
|
||||
blocksize = ses.keys->recv.algo_crypt->blocksize;
|
||||
macsize = ses.keys->recv.algo_mac->hashsize;
|
||||
|
||||
ses.kexstate.datarecv += ses.readbuf->len;
|
||||
|
||||
/* we've already decrypted the first blocksize in read_packet_init */
|
||||
buf_setpos(ses.readbuf, blocksize);
|
||||
|
||||
buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize);
|
||||
buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size);
|
||||
buf_setpos(ses.decryptreadbuf, blocksize);
|
||||
|
||||
/* decrypt if encryption is set, memcpy otherwise */
|
||||
if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
|
||||
/* copy it */
|
||||
len = ses.readbuf->len - macsize - blocksize;
|
||||
memcpy(buf_getwriteptr(ses.decryptreadbuf, len),
|
||||
buf_getptr(ses.readbuf, len), len);
|
||||
} else {
|
||||
/* decrypt */
|
||||
while (ses.readbuf->pos < ses.readbuf->len - macsize) {
|
||||
if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize),
|
||||
buf_getwriteptr(ses.decryptreadbuf, blocksize),
|
||||
blocksize,
|
||||
&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
|
||||
dropbear_exit("error decrypting");
|
||||
}
|
||||
buf_incrpos(ses.readbuf, blocksize);
|
||||
buf_incrwritepos(ses.decryptreadbuf, blocksize);
|
||||
}
|
||||
/* decrypt it in-place */
|
||||
len = ses.readbuf->len - macsize - ses.readbuf->pos;
|
||||
if (ses.keys->recv.crypt_mode->decrypt(
|
||||
buf_getptr(ses.readbuf, len),
|
||||
buf_getwriteptr(ses.readbuf, len),
|
||||
len,
|
||||
&ses.keys->recv.cipher_state) != CRYPT_OK) {
|
||||
dropbear_exit("error decrypting");
|
||||
}
|
||||
buf_incrpos(ses.readbuf, len);
|
||||
|
||||
/* check the hmac */
|
||||
buf_setpos(ses.readbuf, ses.readbuf->len - macsize);
|
||||
if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) {
|
||||
if (checkmac() != DROPBEAR_SUCCESS) {
|
||||
dropbear_exit("Integrity error");
|
||||
}
|
||||
|
||||
/* readbuf no longer required */
|
||||
buf_free(ses.readbuf);
|
||||
ses.readbuf = NULL;
|
||||
|
||||
/* get padding length */
|
||||
buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF);
|
||||
padlen = buf_getbyte(ses.decryptreadbuf);
|
||||
buf_setpos(ses.readbuf, PACKET_PADDING_OFF);
|
||||
padlen = buf_getbyte(ses.readbuf);
|
||||
|
||||
/* payload length */
|
||||
/* - 4 - 1 is for LEN and PADLEN values */
|
||||
len = ses.decryptreadbuf->len - padlen - 4 - 1;
|
||||
len = ses.readbuf->len - padlen - 4 - 1;
|
||||
if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) {
|
||||
dropbear_exit("bad packet size");
|
||||
}
|
||||
|
||||
buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF);
|
||||
buf_setpos(ses.readbuf, PACKET_PAYLOAD_OFF);
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
|
||||
if (is_compress_recv()) {
|
||||
/* decompress */
|
||||
ses.payload = buf_decompress(ses.decryptreadbuf, len);
|
||||
|
||||
ses.payload = buf_decompress(ses.readbuf, len);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* copy payload */
|
||||
ses.payload = buf_new(len);
|
||||
memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len);
|
||||
memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len);
|
||||
buf_incrlen(ses.payload, len);
|
||||
}
|
||||
|
||||
buf_free(ses.decryptreadbuf);
|
||||
ses.decryptreadbuf = NULL;
|
||||
buf_free(ses.readbuf);
|
||||
ses.readbuf = NULL;
|
||||
buf_setpos(ses.payload, 0);
|
||||
|
||||
ses.recvseq++;
|
||||
@ -312,49 +290,22 @@ void decrypt_packet() {
|
||||
TRACE(("leave decrypt_packet"))
|
||||
}
|
||||
|
||||
/* Checks the mac in hashbuf, for the data in readbuf.
|
||||
/* Checks the mac at the end of a decrypted readbuf.
|
||||
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
static int checkmac(buffer* macbuf, buffer* sourcebuf) {
|
||||
static int checkmac() {
|
||||
|
||||
unsigned int macsize;
|
||||
hmac_state hmac;
|
||||
unsigned char tempbuf[MAX_MAC_LEN];
|
||||
unsigned long bufsize;
|
||||
unsigned int len;
|
||||
|
||||
macsize = ses.keys->recv_algo_mac->hashsize;
|
||||
if (macsize == 0) {
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
/* calculate the mac */
|
||||
if (hmac_init(&hmac,
|
||||
find_hash(ses.keys->recv_algo_mac->hashdesc->name),
|
||||
ses.keys->recvmackey,
|
||||
ses.keys->recv_algo_mac->keysize)
|
||||
!= CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
unsigned char mac_bytes[MAX_MAC_LEN];
|
||||
unsigned int mac_size, contents_len;
|
||||
|
||||
/* sequence number */
|
||||
STORE32H(ses.recvseq, tempbuf);
|
||||
if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
mac_size = ses.keys->trans.algo_mac->hashsize;
|
||||
contents_len = ses.readbuf->len - mac_size;
|
||||
|
||||
buf_setpos(sourcebuf, 0);
|
||||
len = sourcebuf->len;
|
||||
if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
|
||||
bufsize = sizeof(tempbuf);
|
||||
if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
buf_setpos(ses.readbuf, 0);
|
||||
make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
|
||||
|
||||
/* compare the hash */
|
||||
if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) {
|
||||
buf_setpos(ses.readbuf, contents_len);
|
||||
if (memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) {
|
||||
return DROPBEAR_FAILURE;
|
||||
} else {
|
||||
return DROPBEAR_SUCCESS;
|
||||
@ -369,7 +320,7 @@ static buffer* buf_decompress(buffer* buf, unsigned int len) {
|
||||
buffer * ret;
|
||||
z_streamp zstream;
|
||||
|
||||
zstream = ses.keys->recv_zstream;
|
||||
zstream = ses.keys->recv.zstream;
|
||||
ret = buf_new(len);
|
||||
|
||||
zstream->avail_in = len;
|
||||
@ -465,10 +416,12 @@ void maybe_flush_reply_queue() {
|
||||
void encrypt_packet() {
|
||||
|
||||
unsigned char padlen;
|
||||
unsigned char blocksize, macsize;
|
||||
buffer * writebuf; /* the packet which will go on the wire */
|
||||
buffer * clearwritebuf; /* unencrypted, possibly compressed */
|
||||
unsigned char blocksize, mac_size;
|
||||
buffer * writebuf; /* the packet which will go on the wire. This is
|
||||
encrypted in-place. */
|
||||
unsigned char type;
|
||||
unsigned int len, encrypt_buf_size;
|
||||
unsigned char mac_bytes[MAX_MAC_LEN];
|
||||
|
||||
type = ses.writepayload->data[0];
|
||||
TRACE(("enter encrypt_packet()"))
|
||||
@ -482,33 +435,36 @@ void encrypt_packet() {
|
||||
return;
|
||||
}
|
||||
|
||||
blocksize = ses.keys->trans_algo_crypt->blocksize;
|
||||
macsize = ses.keys->trans_algo_mac->hashsize;
|
||||
blocksize = ses.keys->trans.algo_crypt->blocksize;
|
||||
mac_size = ses.keys->trans.algo_mac->hashsize;
|
||||
|
||||
/* Encrypted packet len is payload+5, then worst case is if we are 3 away
|
||||
* from a blocksize multiple. In which case we need to pad to the
|
||||
* multiple, then add another blocksize (or MIN_PACKET_LEN) */
|
||||
clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3
|
||||
encrypt_buf_size = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3;
|
||||
/* add space for the MAC at the end */
|
||||
encrypt_buf_size += mac_size;
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
+ ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/
|
||||
encrypt_buf_size += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/
|
||||
#endif
|
||||
);
|
||||
buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF);
|
||||
buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF);
|
||||
writebuf = buf_new(encrypt_buf_size);
|
||||
buf_setlen(writebuf, PACKET_PAYLOAD_OFF);
|
||||
buf_setpos(writebuf, PACKET_PAYLOAD_OFF);
|
||||
|
||||
buf_setpos(ses.writepayload, 0);
|
||||
|
||||
#ifndef DISABLE_ZLIB
|
||||
/* compression */
|
||||
if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
|
||||
buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
|
||||
if (is_compress_trans()) {
|
||||
buf_compress(writebuf, ses.writepayload, ses.writepayload->len);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len),
|
||||
memcpy(buf_getwriteptr(writebuf, ses.writepayload->len),
|
||||
buf_getptr(ses.writepayload, ses.writepayload->len),
|
||||
ses.writepayload->len);
|
||||
buf_incrwritepos(clearwritebuf, ses.writepayload->len);
|
||||
buf_incrwritepos(writebuf, ses.writepayload->len);
|
||||
}
|
||||
|
||||
/* finished with payload */
|
||||
@ -517,60 +473,45 @@ void encrypt_packet() {
|
||||
|
||||
/* length of padding - packet length must be a multiple of blocksize,
|
||||
* with a minimum of 4 bytes of padding */
|
||||
padlen = blocksize - (clearwritebuf->len) % blocksize;
|
||||
padlen = blocksize - (writebuf->len) % blocksize;
|
||||
if (padlen < 4) {
|
||||
padlen += blocksize;
|
||||
}
|
||||
/* check for min packet length */
|
||||
if (clearwritebuf->len + padlen < MIN_PACKET_LEN) {
|
||||
if (writebuf->len + padlen < MIN_PACKET_LEN) {
|
||||
padlen += blocksize;
|
||||
}
|
||||
|
||||
buf_setpos(clearwritebuf, 0);
|
||||
buf_setpos(writebuf, 0);
|
||||
/* packet length excluding the packetlength uint32 */
|
||||
buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4);
|
||||
buf_putint(writebuf, writebuf->len + padlen - 4);
|
||||
|
||||
/* padding len */
|
||||
buf_putbyte(clearwritebuf, padlen);
|
||||
buf_putbyte(writebuf, padlen);
|
||||
/* actual padding */
|
||||
buf_setpos(clearwritebuf, clearwritebuf->len);
|
||||
buf_incrlen(clearwritebuf, padlen);
|
||||
genrandom(buf_getptr(clearwritebuf, padlen), padlen);
|
||||
buf_setpos(writebuf, writebuf->len);
|
||||
buf_incrlen(writebuf, padlen);
|
||||
genrandom(buf_getptr(writebuf, padlen), padlen);
|
||||
|
||||
/* do the actual encryption */
|
||||
buf_setpos(clearwritebuf, 0);
|
||||
/* create a new writebuffer, this is freed when it has been put on the
|
||||
* wire by writepacket() */
|
||||
writebuf = buf_new(clearwritebuf->len + macsize);
|
||||
make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes);
|
||||
|
||||
if (ses.keys->trans_algo_crypt->cipherdesc == NULL) {
|
||||
/* copy it */
|
||||
memcpy(buf_getwriteptr(writebuf, clearwritebuf->len),
|
||||
buf_getptr(clearwritebuf, clearwritebuf->len),
|
||||
clearwritebuf->len);
|
||||
buf_incrwritepos(writebuf, clearwritebuf->len);
|
||||
} else {
|
||||
/* encrypt it */
|
||||
while (clearwritebuf->pos < clearwritebuf->len) {
|
||||
if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize),
|
||||
buf_getwriteptr(writebuf, blocksize),
|
||||
blocksize,
|
||||
&ses.keys->trans_symmetric_struct) != CRYPT_OK) {
|
||||
dropbear_exit("error encrypting");
|
||||
}
|
||||
buf_incrpos(clearwritebuf, blocksize);
|
||||
buf_incrwritepos(writebuf, blocksize);
|
||||
}
|
||||
/* do the actual encryption, in-place */
|
||||
buf_setpos(writebuf, 0);
|
||||
/* encrypt it in-place*/
|
||||
len = writebuf->len;
|
||||
if (ses.keys->trans.crypt_mode->encrypt(
|
||||
buf_getptr(writebuf, len),
|
||||
buf_getwriteptr(writebuf, len),
|
||||
len,
|
||||
&ses.keys->trans.cipher_state) != CRYPT_OK) {
|
||||
dropbear_exit("error encrypting");
|
||||
}
|
||||
buf_incrpos(writebuf, len);
|
||||
|
||||
/* now add a hmac and we're done */
|
||||
writemac(writebuf, clearwritebuf);
|
||||
/* stick the MAC on it */
|
||||
buf_putbytes(writebuf, mac_bytes, mac_size);
|
||||
|
||||
/* clearwritebuf is finished with */
|
||||
buf_free(clearwritebuf);
|
||||
clearwritebuf = NULL;
|
||||
|
||||
/* enqueue the packet for sending */
|
||||
/* enqueue the packet for sending. It will get freed after transmission. */
|
||||
buf_setpos(writebuf, 0);
|
||||
enqueue(&ses.writequeue, (void*)writebuf);
|
||||
|
||||
@ -583,47 +524,43 @@ void encrypt_packet() {
|
||||
|
||||
|
||||
/* Create the packet mac, and append H(seqno|clearbuf) to the output */
|
||||
static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {
|
||||
|
||||
unsigned int macsize;
|
||||
/* output_mac must have ses.keys->trans.algo_mac->hashsize bytes. */
|
||||
static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
|
||||
buffer * clear_buf, unsigned int clear_len,
|
||||
unsigned char *output_mac) {
|
||||
unsigned char seqbuf[4];
|
||||
unsigned char tempbuf[MAX_MAC_LEN];
|
||||
unsigned long bufsize;
|
||||
hmac_state hmac;
|
||||
|
||||
TRACE(("enter writemac"))
|
||||
|
||||
macsize = ses.keys->trans_algo_mac->hashsize;
|
||||
if (macsize > 0) {
|
||||
if (key_state->algo_mac->hashsize > 0) {
|
||||
/* calculate the mac */
|
||||
if (hmac_init(&hmac,
|
||||
find_hash(ses.keys->trans_algo_mac->hashdesc->name),
|
||||
ses.keys->transmackey,
|
||||
ses.keys->trans_algo_mac->keysize) != CRYPT_OK) {
|
||||
key_state->hash_index,
|
||||
key_state->mackey,
|
||||
key_state->algo_mac->keysize) != CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
|
||||
/* sequence number */
|
||||
STORE32H(ses.transseq, seqbuf);
|
||||
STORE32H(seqno, seqbuf);
|
||||
if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
|
||||
/* the actual contents */
|
||||
buf_setpos(clearwritebuf, 0);
|
||||
buf_setpos(clear_buf, 0);
|
||||
if (hmac_process(&hmac,
|
||||
buf_getptr(clearwritebuf,
|
||||
clearwritebuf->len),
|
||||
clearwritebuf->len) != CRYPT_OK) {
|
||||
buf_getptr(clear_buf, clear_len),
|
||||
clear_len) != CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
|
||||
bufsize = sizeof(tempbuf);
|
||||
if (hmac_done(&hmac, tempbuf, &bufsize)
|
||||
!= CRYPT_OK) {
|
||||
bufsize = MAX_MAC_LEN;
|
||||
if (hmac_done(&hmac, output_mac, &bufsize) != CRYPT_OK) {
|
||||
dropbear_exit("HMAC error");
|
||||
}
|
||||
buf_putbytes(outputbuffer, tempbuf, macsize);
|
||||
}
|
||||
TRACE(("leave writemac"))
|
||||
}
|
||||
@ -640,29 +577,29 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
|
||||
|
||||
while (1) {
|
||||
|
||||
ses.keys->trans_zstream->avail_in = endpos - src->pos;
|
||||
ses.keys->trans_zstream->next_in =
|
||||
buf_getptr(src, ses.keys->trans_zstream->avail_in);
|
||||
ses.keys->trans.zstream->avail_in = endpos - src->pos;
|
||||
ses.keys->trans.zstream->next_in =
|
||||
buf_getptr(src, ses.keys->trans.zstream->avail_in);
|
||||
|
||||
ses.keys->trans_zstream->avail_out = dest->size - dest->pos;
|
||||
ses.keys->trans_zstream->next_out =
|
||||
buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out);
|
||||
ses.keys->trans.zstream->avail_out = dest->size - dest->pos;
|
||||
ses.keys->trans.zstream->next_out =
|
||||
buf_getwriteptr(dest, ses.keys->trans.zstream->avail_out);
|
||||
|
||||
result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH);
|
||||
result = deflate(ses.keys->trans.zstream, Z_SYNC_FLUSH);
|
||||
|
||||
buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in);
|
||||
buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out);
|
||||
buf_setpos(src, endpos - ses.keys->trans.zstream->avail_in);
|
||||
buf_setlen(dest, dest->size - ses.keys->trans.zstream->avail_out);
|
||||
buf_setpos(dest, dest->len);
|
||||
|
||||
if (result != Z_OK) {
|
||||
dropbear_exit("zlib error");
|
||||
}
|
||||
|
||||
if (ses.keys->trans_zstream->avail_in == 0) {
|
||||
if (ses.keys->trans.zstream->avail_in == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
dropbear_assert(ses.keys->trans_zstream->avail_out == 0);
|
||||
dropbear_assert(ses.keys->trans.zstream->avail_out == 0);
|
||||
|
||||
/* the buffer has been filled, we must extend. This only happens in
|
||||
* unusual circumstances where the data grows in size after deflate(),
|
||||
|
2
packet.h
2
packet.h
@ -44,6 +44,6 @@ typedef struct PacketType {
|
||||
#define PACKET_PADDING_OFF 4
|
||||
#define PACKET_PAYLOAD_OFF 5
|
||||
|
||||
#define INIT_READBUF 200
|
||||
#define INIT_READBUF 128
|
||||
|
||||
#endif /* _PACKET_H_ */
|
||||
|
@ -70,6 +70,7 @@ void process_packet() {
|
||||
dropbear_close("Disconnect received");
|
||||
}
|
||||
|
||||
ses.last_packet_time = time(NULL);
|
||||
|
||||
/* This applies for KEX, where the spec says the next packet MUST be
|
||||
* NEWKEYS */
|
||||
|
@ -38,6 +38,7 @@ typedef struct runopts {
|
||||
#endif
|
||||
unsigned int recv_window;
|
||||
unsigned int keepalive_secs;
|
||||
unsigned int idle_timeout_secs;
|
||||
|
||||
} runopts;
|
||||
|
||||
|
15
scp.c
15
scp.c
@ -130,13 +130,22 @@ do_local_cmd(arglist *a)
|
||||
fprintf(stderr, " %s", a->list[i]);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
if ((pid = fork()) == -1)
|
||||
#ifdef __uClinux__
|
||||
pid = vfork();
|
||||
#else
|
||||
pid = fork();
|
||||
#endif /* __uClinux__ */
|
||||
if (pid == -1)
|
||||
fatal("do_local_cmd: fork: %s", strerror(errno));
|
||||
|
||||
if (pid == 0) {
|
||||
execvp(a->list[0], a->list);
|
||||
perror(a->list[0]);
|
||||
#ifdef __uClinux__
|
||||
_exit(1);
|
||||
#else
|
||||
exit(1);
|
||||
#endif /* __uClinux__ */
|
||||
}
|
||||
|
||||
do_cmd_pid = pid;
|
||||
@ -225,7 +234,11 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
|
||||
|
||||
execvp(ssh_program, args.list);
|
||||
perror(ssh_program);
|
||||
#ifndef __uClinux__
|
||||
exit(1);
|
||||
#else
|
||||
_exit(1);
|
||||
#endif /* __uClinux__ */
|
||||
} else if (do_cmd_pid == -1) {
|
||||
fatal("fork: %s", strerror(errno));
|
||||
}
|
||||
|
53
session.h
53
session.h
@ -60,28 +60,36 @@ void cli_session(int sock_in, int sock_out, char *remotehost);
|
||||
void cli_session_cleanup();
|
||||
void cleantext(unsigned char* dirtytext);
|
||||
|
||||
/* crypto parameters that are stored individually for transmit and receive */
|
||||
struct key_context_directional {
|
||||
const struct dropbear_cipher *algo_crypt; /* NULL for none */
|
||||
const struct dropbear_cipher_mode *crypt_mode;
|
||||
const struct dropbear_hash *algo_mac; /* NULL for none */
|
||||
int hash_index; /* lookup for libtomcrypt */
|
||||
char algo_comp; /* compression */
|
||||
#ifndef DISABLE_ZLIB
|
||||
z_streamp zstream;
|
||||
#endif
|
||||
/* actual keys */
|
||||
union {
|
||||
symmetric_CBC cbc;
|
||||
#ifdef DROPBEAR_ENABLE_CTR_MODE
|
||||
symmetric_CTR ctr;
|
||||
#endif
|
||||
} cipher_state;
|
||||
unsigned char mackey[MAX_MAC_KEY];
|
||||
};
|
||||
|
||||
struct key_context {
|
||||
|
||||
const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */
|
||||
const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */
|
||||
const struct dropbear_hash *recv_algo_mac; /* NULL for none */
|
||||
const struct dropbear_hash *trans_algo_mac; /* NULL for none */
|
||||
struct key_context_directional recv;
|
||||
struct key_context_directional trans;
|
||||
|
||||
char algo_kex;
|
||||
char algo_hostkey;
|
||||
|
||||
char recv_algo_comp; /* compression */
|
||||
char trans_algo_comp;
|
||||
#ifndef DISABLE_ZLIB
|
||||
z_streamp recv_zstream;
|
||||
z_streamp trans_zstream;
|
||||
#endif
|
||||
|
||||
/* actual keys */
|
||||
symmetric_CBC recv_symmetric_struct;
|
||||
symmetric_CBC trans_symmetric_struct;
|
||||
unsigned char recvmackey[MAX_MAC_KEY];
|
||||
unsigned char transmackey[MAX_MAC_KEY];
|
||||
|
||||
int allow_compress; /* whether compression has started (useful in
|
||||
zlib@openssh.com delayed compression case) */
|
||||
};
|
||||
|
||||
struct packetlist;
|
||||
@ -114,8 +122,7 @@ struct sshsession {
|
||||
throughout the code, as handlers fill out this
|
||||
buffer with the packet to send. */
|
||||
struct Queue writequeue; /* A queue of encrypted packets to send */
|
||||
buffer *readbuf; /* Encrypted */
|
||||
buffer *decryptreadbuf; /* Post-decryption */
|
||||
buffer *readbuf; /* From the wire, decrypted in-place */
|
||||
buffer *payload; /* Post-decompression, the actual SSH packet */
|
||||
unsigned int transseq, recvseq; /* Sequence IDs */
|
||||
|
||||
@ -134,12 +141,16 @@ struct sshsession {
|
||||
|
||||
unsigned char lastpacket; /* What the last received packet type was */
|
||||
|
||||
int signal_pipe[2]; /* stores endpoints of a self-pipe used for
|
||||
int signal_pipe[2]; /* stores endpoints of a self-pipe used for
|
||||
race-free signal handling */
|
||||
|
||||
time_t last_packet_time; /* time of the last packet transmission, for
|
||||
time_t last_trx_packet_time; /* time of the last packet transmission, for
|
||||
keepalive purposes */
|
||||
|
||||
time_t last_packet_time; /* time of the last packet transmission or receive, for
|
||||
idle timeout purposes */
|
||||
|
||||
|
||||
/* KEX/encryption related */
|
||||
struct KEXState kexstate;
|
||||
struct key_context *keys;
|
||||
|
@ -368,6 +368,8 @@ void send_msg_userauth_success() {
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS);
|
||||
encrypt_packet();
|
||||
|
||||
/* authdone must be set after encrypt_packet() for
|
||||
* delayed-zlib mode */
|
||||
ses.authstate.authdone = 1;
|
||||
ses.connect_time = 0;
|
||||
|
||||
|
@ -105,7 +105,7 @@ void svr_pubkey_options_cleanup() {
|
||||
/* helper for svr_add_pubkey_options. returns DROPBEAR_SUCCESS if the option is matched,
|
||||
and increments the options_buf */
|
||||
static int match_option(buffer *options_buf, const char *opt_name) {
|
||||
const int len = strlen(opt_name);
|
||||
const unsigned int len = strlen(opt_name);
|
||||
if (options_buf->len - options_buf->pos < len) {
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
@ -663,11 +663,11 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
|
||||
addchildpid(chansess, chansess->pid);
|
||||
|
||||
if (svr_ses.lastexit.exitpid != -1) {
|
||||
unsigned int i;
|
||||
TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
|
||||
/* The child probably exited and the signal handler triggered
|
||||
* possibly before we got around to adding the childpid. So we fill
|
||||
* out its data manually */
|
||||
int i;
|
||||
for (i = 0; i < svr_ses.childpidsize; i++) {
|
||||
if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
|
||||
TRACE(("found match for lastexitpid"))
|
||||
@ -878,6 +878,7 @@ static void execchild(void *user_data) {
|
||||
addnewvar("LOGNAME", ses.authstate.pw_name);
|
||||
addnewvar("HOME", ses.authstate.pw_dir);
|
||||
addnewvar("SHELL", get_user_shell());
|
||||
addnewvar("PATH", DEFAULT_PATH);
|
||||
if (chansess->term != NULL) {
|
||||
addnewvar("TERM", chansess->term);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ void main_noinetd() {
|
||||
for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
|
||||
childpipes[i] = -1;
|
||||
}
|
||||
bzero(preauth_addrs, sizeof(preauth_addrs));
|
||||
memset(preauth_addrs, 0x0, sizeof(preauth_addrs));
|
||||
|
||||
/* Set up the listening sockets */
|
||||
listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
|
||||
|
@ -82,8 +82,9 @@ static void printhelp(const char * progname) {
|
||||
#endif
|
||||
"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
|
||||
"-K <keepalive> (0 is never, default %d)\n"
|
||||
"-I <idle_timeout> (0 is never, default %d)\n"
|
||||
#ifdef DEBUG_TRACE
|
||||
"-v verbose\n"
|
||||
"-v verbose (compiled with DEBUG_TRACE)\n"
|
||||
#endif
|
||||
,DROPBEAR_VERSION, progname,
|
||||
#ifdef DROPBEAR_DSS
|
||||
@ -93,7 +94,7 @@ static void printhelp(const char * progname) {
|
||||
RSA_PRIV_FILENAME,
|
||||
#endif
|
||||
DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE,
|
||||
DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE);
|
||||
DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
|
||||
}
|
||||
|
||||
void svr_getopts(int argc, char ** argv) {
|
||||
@ -103,6 +104,7 @@ void svr_getopts(int argc, char ** argv) {
|
||||
int nextisport = 0;
|
||||
char* recv_window_arg = NULL;
|
||||
char* keepalive_arg = NULL;
|
||||
char* idle_timeout_arg = NULL;
|
||||
|
||||
/* see printhelp() for options */
|
||||
svr_opts.rsakeyfile = NULL;
|
||||
@ -134,7 +136,8 @@ void svr_getopts(int argc, char ** argv) {
|
||||
svr_opts.usingsyslog = 1;
|
||||
#endif
|
||||
opts.recv_window = DEFAULT_RECV_WINDOW;
|
||||
opts.keepalive_secs = DEFAULT_KEEPALIVE;
|
||||
opts.keepalive_secs = DEFAULT_KEEPALIVE;
|
||||
opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
|
||||
|
||||
#ifdef ENABLE_SVR_REMOTETCPFWD
|
||||
opts.listen_fwd_all = 0;
|
||||
@ -218,6 +221,9 @@ void svr_getopts(int argc, char ** argv) {
|
||||
case 'K':
|
||||
next = &keepalive_arg;
|
||||
break;
|
||||
case 'I':
|
||||
next = &idle_timeout_arg;
|
||||
break;
|
||||
#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
|
||||
case 's':
|
||||
svr_opts.noauthpass = 1;
|
||||
@ -253,7 +259,7 @@ void svr_getopts(int argc, char ** argv) {
|
||||
svr_opts.addresses[0] = m_strdup(DROPBEAR_DEFADDRESS);
|
||||
svr_opts.portcount = 1;
|
||||
}
|
||||
|
||||
|
||||
if (svr_opts.dsskeyfile == NULL) {
|
||||
svr_opts.dsskeyfile = DSS_PRIV_FILENAME;
|
||||
}
|
||||
@ -294,6 +300,12 @@ void svr_getopts(int argc, char ** argv) {
|
||||
dropbear_exit("Bad keepalive '%s'", keepalive_arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (idle_timeout_arg) {
|
||||
if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addportandaddress(char* spec) {
|
||||
|
16
sysoptions.h
16
sysoptions.h
@ -4,7 +4,7 @@
|
||||
*******************************************************************/
|
||||
|
||||
#ifndef DROPBEAR_VERSION
|
||||
#define DROPBEAR_VERSION "0.51"
|
||||
#define DROPBEAR_VERSION "0.52"
|
||||
#endif
|
||||
|
||||
#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
|
||||
@ -68,6 +68,7 @@
|
||||
|
||||
#define DROPBEAR_COMP_NONE 0
|
||||
#define DROPBEAR_COMP_ZLIB 1
|
||||
#define DROPBEAR_COMP_ZLIB_DELAY 2
|
||||
|
||||
/* Required for pubkey auth */
|
||||
#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
|
||||
@ -133,12 +134,12 @@
|
||||
accept for keyb-interactive
|
||||
auth */
|
||||
|
||||
#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
|
||||
#define DROPBEAR_AES_CBC
|
||||
#if defined(DROPBEAR_AES256) || defined(DROPBEAR_AES128)
|
||||
#define DROPBEAR_AES
|
||||
#endif
|
||||
|
||||
#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
|
||||
#define DROPBEAR_TWOFISH_CBC
|
||||
#if defined(DROPBEAR_TWOFISH256) || defined(DROPBEAR_TWOFISH128)
|
||||
#define DROPBEAR_TWOFISH
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_X11FWD
|
||||
@ -201,5 +202,8 @@
|
||||
#define IS_DROPBEAR_CLIENT 1
|
||||
|
||||
#else
|
||||
#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
|
||||
/* Just building key utils? */
|
||||
#define IS_DROPBEAR_SERVER 0
|
||||
#define IS_DROPBEAR_CLIENT 0
|
||||
|
||||
#endif
|
||||
|
4
tcpfwd.h
4
tcpfwd.h
@ -49,6 +49,8 @@ struct TCPFwdList {
|
||||
const unsigned char* connectaddr;
|
||||
unsigned int connectport;
|
||||
unsigned int listenport;
|
||||
unsigned int have_reply; /* is set to 1 after a reply has been received
|
||||
when setting up the forwarding */
|
||||
struct TCPFwdList * next;
|
||||
|
||||
};
|
||||
@ -62,6 +64,8 @@ extern const struct ChanType svr_chan_tcpdirect;
|
||||
void setup_localtcp();
|
||||
void setup_remotetcp();
|
||||
extern const struct ChanType cli_chan_tcpremote;
|
||||
void cli_recv_msg_request_success();
|
||||
void cli_recv_msg_request_failure();
|
||||
|
||||
/* Common */
|
||||
int listen_tcpfwd(struct TCPListener* tcpinfo);
|
||||
|
Loading…
Reference in New Issue
Block a user