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:
Matt Johnston 2009-07-01 04:16:32 +00:00
commit 709a3e75cf
40 changed files with 787 additions and 405 deletions

53
CHANGES
View File

@ -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()

View File

@ -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.

View File

@ -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
View File

@ -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
View File

@ -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 */
};

View File

@ -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;

View File

@ -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"))

View File

@ -327,4 +327,5 @@ out:
if (line != NULL) {
buf_free(line);
}
m_free(fingerprint);
}

View File

@ -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

View File

@ -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;

View File

@ -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 */
};

View File

@ -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;

View File

@ -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)

View File

@ -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. */

View File

@ -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 */

View File

@ -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;
}

View File

@ -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,

View File

@ -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
View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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 */

View File

@ -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

View File

@ -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));

View File

@ -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
View File

@ -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(),

View File

@ -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_ */

View File

@ -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 */

View File

@ -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
View File

@ -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));
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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) {

View File

@ -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

View File

@ -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);