summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGES12
-rw-r--r--Makefile.in2
-rw-r--r--auth.h45
-rw-r--r--chansession.h3
-rw-r--r--cli-authinteract.c5
-rw-r--r--cli-chansession.c66
-rw-r--r--cli-main.c42
-rw-r--r--cli-runopts.c223
-rw-r--r--cli-session.c32
-rw-r--r--common-channel.c22
-rw-r--r--common-session.c61
-rw-r--r--dbclient.113
-rw-r--r--dbutil.c156
-rw-r--r--dbutil.h4
-rw-r--r--debian/README.runit16
-rw-r--r--debian/changelog71
-rw-r--r--debian/control4
-rw-r--r--debian/dropbear.README.Debian55
-rw-r--r--debian/dropbear.init11
-rw-r--r--debian/dropbear.postinst8
-rwxr-xr-xdebian/rules9
-rw-r--r--debug.h7
-rw-r--r--genrsa.c14
-rw-r--r--keyimport.c1
-rw-r--r--options.h223
-rw-r--r--packet.c6
-rw-r--r--runopts.h12
-rw-r--r--session.h9
-rw-r--r--svr-agentfwd.c13
-rw-r--r--svr-auth.c52
-rw-r--r--svr-authpam.c8
-rw-r--r--svr-authpasswd.c10
-rw-r--r--svr-authpubkey.c127
-rw-r--r--svr-authpubkeyoptions.c202
-rw-r--r--svr-chansession.c217
-rw-r--r--svr-main.c6
-rw-r--r--svr-runopts.c7
-rw-r--r--svr-session.c19
-rw-r--r--svr-tcpfwd.c5
-rw-r--r--svr-x11fwd.c5
-rw-r--r--sysoptions.h205
-rw-r--r--tcpfwd.h1
42 files changed, 1423 insertions, 586 deletions
diff --git a/CHANGES b/CHANGES
index 27aa5cf..eb6855c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,15 @@
+0.51 - Thu 27 March 2008
+
+- Make a copy of password fields rather erroneously relying on getwpnam()
+ to be safe to call multiple times
+
+- If $SSH_ASKPASS_ALWAYS environment variable is set (and $SSH_ASKPASS is
+ as well) always use that program, ignoring isatty() and $DISPLAY
+
+- Wait until a process exits before the server closes a connection, so
+ that an exit code can be sent. This fixes problems with exit codes not
+ being returned, which could cause scp to fail.
+
0.50 - Wed 8 August 2007
- Add DROPBEAR_PASSWORD environment variable to specify a dbclient password
diff --git a/Makefile.in b/Makefile.in
index f5b111f..3e6c855 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -23,7 +23,7 @@ COMMONOBJS=dbutil.o buffer.o \
atomicio.o compat.o fake-rfc2553.o
SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
- svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.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
diff --git a/auth.h b/auth.h
index e6ff478..d6fe120 100644
--- a/auth.h
+++ b/auth.h
@@ -26,6 +26,7 @@
#define _AUTH_H_
#include "includes.h"
+#include "chansession.h"
void svr_authinitialise();
void cli_authinitialise();
@@ -38,6 +39,25 @@ void svr_auth_password();
void svr_auth_pubkey();
void svr_auth_pam();
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+int svr_pubkey_allows_agentfwd();
+int svr_pubkey_allows_tcpfwd();
+int svr_pubkey_allows_x11fwd();
+int svr_pubkey_allows_pty();
+void svr_pubkey_set_forced_command(struct ChanSess *chansess);
+void svr_pubkey_options_cleanup();
+int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename);
+#else
+/* no option : success */
+#define svr_pubkey_allows_agentfwd() 1
+#define svr_pubkey_allows_tcpfwd() 1
+#define svr_pubkey_allows_x11fwd() 1
+#define svr_pubkey_allows_pty() 1
+static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) { }
+static inline void svr_pubkey_options_cleanup() { }
+#define svr_add_pubkey_options(x,y,z) DROPBEAR_SUCCESS
+#endif
+
/* Client functions */
void recv_msg_userauth_failure();
void recv_msg_userauth_success();
@@ -91,8 +111,15 @@ struct AuthState {
logged. */
/* These are only used for the server */
- char *printableuser; /* stripped of control chars, used for logs etc */
- struct passwd * pw;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ char *pw_dir;
+ char *pw_shell;
+ char *pw_name;
+ char *pw_passwd;
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+ struct PubKeyOptions* pubkey_options;
+#endif
};
@@ -113,4 +140,18 @@ struct SignKeyList {
};
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+struct PubKeyOptions;
+struct PubKeyOptions {
+ /* Flags */
+ int no_port_forwarding_flag;
+ int no_agent_forwarding_flag;
+ int no_x11_forwarding_flag;
+ int no_pty_flag;
+ /* "command=" option. */
+ unsigned char * forced_command;
+
+};
+#endif
+
#endif /* _AUTH_H_ */
diff --git a/chansession.h b/chansession.h
index 213c285..4513b1a 100644
--- a/chansession.h
+++ b/chansession.h
@@ -78,6 +78,9 @@ void addnewvar(const char* param, const char* var);
void cli_send_chansess_request();
void cli_tty_cleanup();
void cli_chansess_winchange();
+#ifdef ENABLE_CLI_NETCAT
+void cli_send_netcat_request();
+#endif
void svr_chansessinitialise();
extern const struct ChanType svrchansess;
diff --git a/cli-authinteract.c b/cli-authinteract.c
index 5fe5bf1..7851578 100644
--- a/cli-authinteract.c
+++ b/cli-authinteract.c
@@ -77,6 +77,11 @@ void recv_msg_userauth_info_request() {
TRACE(("enter recv_msg_recv_userauth_info_request"))
+ /* Let the user know what password/host they are authing for */
+ if (!cli_ses.interact_request_received) {
+ fprintf(stderr, "Login for %s@%s\n", cli_opts.username,
+ cli_opts.remotehost);
+ }
cli_ses.interact_request_received = 1;
name = buf_getstring(ses.payload, NULL);
diff --git a/cli-chansession.c b/cli-chansession.c
index c92f902..dc8e641 100644
--- a/cli-chansession.c
+++ b/cli-chansession.c
@@ -321,7 +321,11 @@ static void send_chansess_shell_req(struct Channel *channel) {
TRACE(("enter send_chansess_shell_req"))
if (cli_opts.cmd) {
- reqtype = "exec";
+ if (cli_opts.is_subsystem) {
+ reqtype = "subsystem";
+ } else {
+ reqtype = "exec";
+ }
} else {
reqtype = "shell";
}
@@ -338,9 +342,8 @@ static void send_chansess_shell_req(struct Channel *channel) {
TRACE(("leave send_chansess_shell_req"))
}
-static int cli_initchansess(struct Channel *channel) {
-
-
+/* Shared for normal client channel and netcat-alike */
+static int cli_init_stdpipe_sess(struct Channel *channel) {
channel->writefd = STDOUT_FILENO;
setnonblocking(STDOUT_FILENO);
@@ -351,6 +354,12 @@ static int cli_initchansess(struct Channel *channel) {
setnonblocking(STDERR_FILENO);
channel->extrabuf = cbuf_new(opts.recv_window);
+ return 0;
+}
+
+static int cli_initchansess(struct Channel *channel) {
+
+ cli_init_stdpipe_sess(channel);
if (cli_opts.wantpty) {
send_chansess_pty_req(channel);
@@ -363,12 +372,48 @@ static int cli_initchansess(struct Channel *channel) {
}
return 0; /* Success */
+}
+
+#ifdef ENABLE_CLI_NETCAT
+
+void cli_send_netcat_request() {
+
+ const unsigned char* source_host = "127.0.0.1";
+ const int source_port = 22;
+
+ const struct ChanType cli_chan_netcat = {
+ 0, /* sepfds */
+ "direct-tcpip",
+ cli_init_stdpipe_sess, /* inithandler */
+ NULL,
+ NULL,
+ cli_closechansess
+ };
+
+ cli_opts.wantpty = 0;
+ if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat)
+ == DROPBEAR_FAILURE) {
+ dropbear_exit("Couldn't open initial channel");
+ }
+
+ buf_putstring(ses.writepayload, cli_opts.netcat_host,
+ strlen(cli_opts.netcat_host));
+ buf_putint(ses.writepayload, cli_opts.netcat_port);
+
+ /* originator ip - localhost is accurate enough */
+ buf_putstring(ses.writepayload, source_host, strlen(source_host));
+ buf_putint(ses.writepayload, source_port);
+
+ encrypt_packet();
+ TRACE(("leave cli_send_chansess_request"))
}
+#endif
void cli_send_chansess_request() {
TRACE(("enter cli_send_chansess_request"))
+
if (send_msg_channel_open_init(STDIN_FILENO, &clichansess)
== DROPBEAR_FAILURE) {
dropbear_exit("Couldn't open initial channel");
@@ -379,3 +424,16 @@ void cli_send_chansess_request() {
TRACE(("leave cli_send_chansess_request"))
}
+
+
+#if 0
+ while (cli_opts.localfwds != NULL) {
+ ret = cli_localtcp(cli_opts.localfwds->listenport,
+ cli_opts.localfwds->connectaddr,
+ cli_opts.localfwds->connectport);
+ if (ret == DROPBEAR_FAILURE) {
+ dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
+ cli_opts.localfwds->listenport,
+ cli_opts.localfwds->connectaddr,
+ cli_opts.localfwds->connectport);
+#endif
diff --git a/cli-main.c b/cli-main.c
index 68cf023..4ba43c0 100644
--- a/cli-main.c
+++ b/cli-main.c
@@ -32,6 +32,8 @@
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);
+static void cli_proxy_cmd(int *sock_in, int *sock_out);
+
#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
int cli_main(int argc, char ** argv) {
@@ -39,7 +41,7 @@ int cli_main(int argc, char ** argv) {
int main(int argc, char ** argv) {
#endif
- int sock;
+ int sock_in, sock_out;
char* error = NULL;
char* hostandport;
int len;
@@ -58,10 +60,18 @@ int main(int argc, char ** argv) {
dropbear_exit("signal() error");
}
- sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport,
- 0, &error);
+#ifdef ENABLE_CLI_PROXYCMD
+ if (cli_opts.proxycmd) {
+ cli_proxy_cmd(&sock_in, &sock_out);
+ } else
+#endif
+ {
+ int sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport,
+ 0, &error);
+ sock_in = sock_out = sock;
+ }
- if (sock < 0) {
+ if (sock_in < 0) {
dropbear_exit("%s", error);
}
@@ -72,7 +82,7 @@ int main(int argc, char ** argv) {
snprintf(hostandport, len, "%s:%s",
cli_opts.remotehost, cli_opts.remoteport);
- cli_session(sock, hostandport);
+ cli_session(sock_in, sock_out, hostandport);
/* not reached */
return -1;
@@ -112,3 +122,25 @@ static void cli_dropbear_log(int UNUSED(priority),
fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
}
+
+static void exec_proxy_cmd(void *user_data_cmd) {
+ const char *cmd = user_data_cmd;
+ char *usershell;
+
+ usershell = m_strdup(get_user_shell());
+ run_shell_command(cmd, ses.maxfd, usershell);
+ dropbear_exit("Failed to run '%s'\n", cmd);
+}
+
+static void cli_proxy_cmd(int *sock_in, int *sock_out) {
+ int ret;
+
+ fill_passwd(cli_opts.own_user);
+
+ ret = spawn_command(exec_proxy_cmd, cli_opts.proxycmd,
+ sock_out, sock_in, NULL, NULL);
+ if (ret == DROPBEAR_FAILURE) {
+ dropbear_exit("Failed running proxy command");
+ *sock_in = *sock_out = -1;
+ }
+}
diff --git a/cli-runopts.c b/cli-runopts.c
index c7d9b8b..d49caa2 100644
--- a/cli-runopts.c
+++ b/cli-runopts.c
@@ -33,18 +33,23 @@
cli_runopts cli_opts; /* GLOBAL */
static void printhelp();
-static void parsehostname(char* userhostarg);
+static void parse_hostname(const char* orighostarg);
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0);
+static void fill_own_user();
#ifdef ENABLE_CLI_PUBKEY_AUTH
static void loadidentityfile(const char* filename);
#endif
#ifdef ENABLE_CLI_ANYTCPFWD
-static void addforward(char* str, struct TCPFwdList** fwdlist);
+static void addforward(const char* str, struct TCPFwdList** fwdlist);
+#endif
+#ifdef ENABLE_CLI_NETCAT
+static void add_netcat(const char *str);
#endif
static void printhelp() {
fprintf(stderr, "Dropbear client v%s\n"
- "Usage: %s [options] [user@]host [command]\n"
+ "Usage: %s [options] [user@]host[/port] [command]\n"
"Options are:\n"
"-p <remoteport>\n"
"-l <username>\n"
@@ -53,6 +58,7 @@ static void printhelp() {
"-N Don't run a remote command\n"
"-f Run in background after auth\n"
"-y Always accept remote host key if unknown\n"
+ "-s Request a subsystem (use for sftp)\n"
#ifdef ENABLE_CLI_PUBKEY_AUTH
"-i <identityfile> (multiple allowed)\n"
#endif
@@ -68,6 +74,12 @@ 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"
+#ifdef ENABLE_CLI_NETCAT
+ "-B <endhost:endport> Netcat-alike bouncing\n"
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+ "-J <proxy_program> Use program rather than tcp connection\n"
+#endif
#ifdef DEBUG_TRACE
"-v verbose\n"
#endif
@@ -90,6 +102,9 @@ void cli_getopts(int argc, char ** argv) {
#ifdef ENABLE_CLI_REMOTETCPFWD
int nextisremote = 0;
#endif
+#ifdef ENABLE_CLI_NETCAT
+ int nextisnetcat = 0;
+#endif
char* dummy = NULL; /* Not used for anything real */
char* recv_window_arg = NULL;
@@ -105,6 +120,7 @@ void cli_getopts(int argc, char ** argv) {
cli_opts.backgrounded = 0;
cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
cli_opts.always_accept_key = 0;
+ cli_opts.is_subsystem = 0;
#ifdef ENABLE_CLI_PUBKEY_AUTH
cli_opts.privkeys = NULL;
#endif
@@ -119,12 +135,17 @@ void cli_getopts(int argc, char ** argv) {
cli_opts.agent_fwd = 0;
cli_opts.agent_keys_loaded = 0;
#endif
+#ifdef ENABLE_CLI_PROXYCMD
+ cli_opts.proxycmd = NULL;
+#endif
/* not yet
opts.ipv4 = 1;
opts.ipv6 = 1;
*/
opts.recv_window = DEFAULT_RECV_WINDOW;
+ fill_own_user();
+
/* Iterate all the arguments */
for (i = 1; i < (unsigned int)argc; i++) {
#ifdef ENABLE_CLI_PUBKEY_AUTH
@@ -151,6 +172,14 @@ void cli_getopts(int argc, char ** argv) {
continue;
}
#endif
+#ifdef ENABLE_CLI_NETCAT
+ if (nextisnetcat) {
+ TRACE(("nextisnetcat true"))
+ add_netcat(argv[i]);
+ nextisnetcat = 0;
+ continue;
+ }
+#endif
if (next) {
/* The previous flag set a value to assign */
*next = argv[i];
@@ -193,6 +222,9 @@ void cli_getopts(int argc, char ** argv) {
case 'f':
cli_opts.backgrounded = 1;
break;
+ case 's':
+ cli_opts.is_subsystem = 1;
+ break;
#ifdef ENABLE_CLI_LOCALTCPFWD
case 'L':
nextislocal = 1;
@@ -206,6 +238,16 @@ void cli_getopts(int argc, char ** argv) {
nextisremote = 1;
break;
#endif
+#ifdef ENABLE_CLI_NETCAT
+ case 'B':
+ nextisnetcat = 1;
+ break;
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+ case 'J':
+ next = &cli_opts.proxycmd;
+ break;
+#endif
case 'l':
next = &cli_opts.username;
break;
@@ -266,9 +308,11 @@ void cli_getopts(int argc, char ** argv) {
/* Either the hostname or commands */
if (cli_opts.remotehost == NULL) {
-
- parsehostname(argv[i]);
-
+#ifdef ENABLE_CLI_MULTIHOP
+ parse_multihop_hostname(argv[i], argv[0]);
+#else
+ parse_hostname(argv[i]);
+#endif
} else {
/* this is part of the commands to send - after this we
@@ -295,6 +339,8 @@ void cli_getopts(int argc, char ** argv) {
}
}
+ /* And now a few sanity checks and setup */
+
if (cli_opts.remotehost == NULL) {
printhelp();
exit(EXIT_FAILURE);
@@ -319,21 +365,23 @@ void cli_getopts(int argc, char ** argv) {
dropbear_exit("command required for -f");
}
- if (recv_window_arg)
- {
+ if (recv_window_arg) {
opts.recv_window = atol(recv_window_arg);
- if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW)
- {
+ if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) {
dropbear_exit("Bad recv window '%s'", recv_window_arg);
}
}
if (keepalive_arg) {
- opts.keepalive_secs = strtoul(keepalive_arg, NULL, 10);
- if (opts.keepalive_secs == 0 && errno == EINVAL)
- {
+ if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
dropbear_exit("Bad keepalive '%s'", keepalive_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
}
@@ -363,16 +411,77 @@ static void loadidentityfile(const char* filename) {
}
#endif
+#ifdef ENABLE_CLI_MULTIHOP
+
+/* Sets up 'onion-forwarding' connections. This will spawn
+ * a separate dbclient process for each hop.
+ * As an example, if the cmdline is
+ * dbclient wrt,madako,canyons
+ * then we want to run:
+ * dbclient -J "dbclient -B canyons:22 wrt,madako" canyons
+ * and then the inner dbclient will recursively run:
+ * dbclient -J "dbclient -B madako:22 wrt" madako
+ * etc for as many hosts as we want.
+ *
+ * Ports for hosts can be specified as host/port.
+ */
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
+ char *userhostarg = NULL;
+ char *last_hop = NULL;;
+ char *remainder = NULL;
+
+ /* both scp and rsync parse a user@host argument
+ * and turn it into "-l user host". This breaks
+ * for our multihop syntax, so we suture it back together.
+ * This will break usernames that have both '@' and ',' in them,
+ * though that should be fairly uncommon. */
+ if (cli_opts.username
+ && 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);
+ } else {
+ userhostarg = m_strdup(orighostarg);
+ }
+
+ last_hop = strrchr(userhostarg, ',');
+ if (last_hop) {
+ if (last_hop == userhostarg) {
+ dropbear_exit("Bad multi-hop hostnames");
+ }
+ *last_hop = '\0';
+ last_hop++;
+ remainder = userhostarg;
+ userhostarg = last_hop;
+ }
-/* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
- * - note that it will be modified */
-static void parsehostname(char* orighostarg) {
+ parse_hostname(userhostarg);
- uid_t uid;
- struct passwd *pw = NULL;
+ if (last_hop) {
+ /* Set up the proxycmd */
+ unsigned int cmd_len = 0;
+ 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)
+ + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
+ + strlen(argv0) + 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);
+ }
+}
+#endif /* !ENABLE_CLI_MULTIHOP */
+
+/* Parses a [user@]hostname[/port] argument. */
+static void parse_hostname(const char* orighostarg) {
char *userhostarg = NULL;
+ char *port = NULL;
- /* We probably don't want to be editing argvs */
userhostarg = m_strdup(orighostarg);
cli_opts.remotehost = strchr(userhostarg, '@');
@@ -387,14 +496,13 @@ static void parsehostname(char* orighostarg) {
}
if (cli_opts.username == NULL) {
- uid = getuid();
-
- pw = getpwuid(uid);
- if (pw == NULL || pw->pw_name == NULL) {
- dropbear_exit("Unknown own user");
- }
+ cli_opts.username = m_strdup(cli_opts.own_user);
+ }
- cli_opts.username = m_strdup(pw->pw_name);
+ port = strchr(cli_opts.remotehost, '/');
+ if (port) {
+ *port = '\0';
+ cli_opts.remoteport = port+1;
}
if (cli_opts.remotehost[0] == '\0') {
@@ -402,10 +510,61 @@ static void parsehostname(char* orighostarg) {
}
}
+#ifdef ENABLE_CLI_NETCAT
+static void add_netcat(const char* origstr) {
+ char *portstr = NULL;
+
+ char * str = m_strdup(origstr);
+
+ portstr = strchr(str, ':');
+ if (portstr == NULL) {
+ TRACE(("No netcat port"))
+ goto fail;
+ }
+ *portstr = '\0';
+ portstr++;
+
+ if (strchr(portstr, ':')) {
+ TRACE(("Multiple netcat colons"))
+ goto fail;
+ }
+
+ if (m_str_to_uint(portstr, &cli_opts.netcat_port) == DROPBEAR_FAILURE) {
+ TRACE(("bad netcat port"))
+ goto fail;
+ }
+
+ if (cli_opts.netcat_port > 65535) {
+ TRACE(("too large netcat port"))
+ goto fail;
+ }
+
+ cli_opts.netcat_host = str;
+ return;
+
+fail:
+ dropbear_exit("Bad netcat endpoint '%s'", origstr);
+}
+#endif
+
+static void fill_own_user() {
+ uid_t uid;
+ struct passwd *pw = NULL;
+
+ uid = getuid();
+
+ pw = getpwuid(uid);
+ if (pw == NULL || pw->pw_name == NULL) {
+ dropbear_exit("Unknown own user");
+ }
+
+ cli_opts.own_user = m_strdup(pw->pw_name);
+}
+
#ifdef ENABLE_CLI_ANYTCPFWD
/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding
* set, and add it to the forwarding list */
-static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
+static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
char * listenport = NULL;
char * connectport = NULL;
@@ -441,15 +600,13 @@ static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
/* Now we check the ports - note that the port ints are unsigned,
* the check later only checks for >= MAX_PORT */
- newfwd->listenport = strtol(listenport, NULL, 10);
- if (errno != 0) {
- TRACE(("bad listenport strtol"))
+ if (m_str_to_uint(listenport, &newfwd->listenport) == DROPBEAR_FAILURE) {
+ TRACE(("bad listenport strtoul"))
goto fail;
}
- newfwd->connectport = strtol(connectport, NULL, 10);
- if (errno != 0) {
- TRACE(("bad connectport strtol"))
+ if (m_str_to_uint(connectport, &newfwd->connectport) == DROPBEAR_FAILURE) {
+ TRACE(("bad connectport strtoul"))
goto fail;
}
diff --git a/cli-session.c b/cli-session.c
index e176bd9..8c087d4 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -77,13 +77,13 @@ static const struct ChanType *cli_chantypes[] = {
NULL /* Null termination */
};
-void cli_session(int sock, char* remotehost) {
+void cli_session(int sock_in, int sock_out, char* remotehost) {
seedrandom();
crypto_init();
- common_session_init(sock, remotehost);
+ common_session_init(sock_in, sock_out, remotehost);
chaninitialise(cli_chantypes);
@@ -200,20 +200,6 @@ static void cli_sessionloop() {
TRACE(("leave cli_sessionloop: cli_auth_try"))
return;
- /*
- case USERAUTH_SUCCESS_RCVD:
- send_msg_service_request(SSH_SERVICE_CONNECTION);
- cli_ses.state = SERVICE_CONN_REQ_SENT;
- TRACE(("leave cli_sessionloop: sent ssh-connection service req"))
- return;
-
- case SERVICE_CONN_ACCEPT_RCVD:
- cli_send_chansess_request();
- TRACE(("leave cli_sessionloop: cli_send_chansess_request"))
- cli_ses.state = SESSION_RUNNING;
- return;
- */
-
case USERAUTH_SUCCESS_RCVD:
if (cli_opts.backgrounded) {
@@ -238,7 +224,13 @@ static void cli_sessionloop() {
#ifdef ENABLE_CLI_REMOTETCPFWD
setup_remotetcp();
#endif
- if (!cli_opts.no_cmd) {
+
+#ifdef ENABLE_CLI_NETCAT
+ if (cli_opts.netcat_host) {
+ cli_send_netcat_request();
+ } else
+#endif
+ if (!cli_opts.no_cmd) {
cli_send_chansess_request();
}
TRACE(("leave cli_sessionloop: running"))
@@ -297,8 +289,10 @@ static void cli_remoteclosed() {
/* XXX TODO perhaps print a friendlier message if we get this but have
* already sent/received disconnect message(s) ??? */
- close(ses.sock);
- ses.sock = -1;
+ m_close(ses.sock_in);
+ m_close(ses.sock_out);
+ ses.sock_in = -1;
+ ses.sock_out = -1;
dropbear_exit("remote closed the connection");
}
diff --git a/common-channel.c b/common-channel.c
index 97fd4a8..2105184 100644
--- a/common-channel.c
+++ b/common-channel.c
@@ -261,6 +261,7 @@ static unsigned int write_pending(struct Channel * channel) {
/* EOF/close handling */
static void check_close(struct Channel *channel) {
+ int close_allowed = 0;
TRACE(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d",
channel->writefd, channel->readfd,
@@ -274,8 +275,17 @@ 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 (!channel->type->check_close
+ || channel->type->check_close(channel)) {
+ close_allowed = 1;
+ }
- if (channel->recv_close && !write_pending(channel)) {
+ if (channel->recv_close && !write_pending(channel) && close_allowed) {
if (!channel->sent_close) {
TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
send_msg_channel_close(channel);
@@ -312,9 +322,10 @@ static void check_close(struct Channel *channel) {
}
/* And if we can't receive any more data from them either, close up */
- if (!channel->sent_close
- && channel->readfd == FD_CLOSED
+ if (channel->readfd == FD_CLOSED
&& (ERRFD_IS_WRITE(channel) || channel->errfd == FD_CLOSED)
+ && !channel->sent_close
+ && close_allowed
&& !write_pending(channel)) {
TRACE(("sending close, readfd is closed"))
send_msg_channel_close(channel);
@@ -561,6 +572,11 @@ void recv_msg_channel_request() {
channel = getchannel();
+ if (channel->sent_close) {
+ TRACE(("leave recv_msg_channel_request: already closed channel"))
+ return;
+ }
+
if (channel->type->reqhandler) {
channel->type->reqhandler(channel);
} else {
diff --git a/common-session.c b/common-session.c
index 79313f2..3d759b5 100644
--- a/common-session.c
+++ b/common-session.c
@@ -52,14 +52,15 @@ int exitflag = 0; /* GLOBAL */
/* called only at the start of a session, set up initial state */
-void common_session_init(int sock, char* remotehost) {
+void common_session_init(int sock_in, int sock_out, char* remotehost) {
TRACE(("enter session_init"))
ses.remotehost = remotehost;
- ses.sock = sock;
- ses.maxfd = sock;
+ ses.sock_in = sock_in;
+ ses.sock_out = sock_out;
+ ses.maxfd = MAX(sock_in, sock_out);
ses.connect_time = 0;
ses.last_packet_time = 0;
@@ -137,11 +138,11 @@ void session_loop(void(*loophandler)()) {
FD_ZERO(&writefd);
FD_ZERO(&readfd);
dropbear_assert(ses.payload == NULL);
- if (ses.sock != -1) {
- FD_SET(ses.sock, &readfd);
- if (!isempty(&ses.writequeue)) {
- FD_SET(ses.sock, &writefd);
- }
+ if (ses.sock_in != -1) {
+ FD_SET(ses.sock_in, &readfd);
+ }
+ if (ses.sock_out != -1 && !isempty(&ses.writequeue)) {
+ FD_SET(ses.sock_out, &writefd);
}
/* We get woken up when signal handlers write to this pipe.
@@ -183,12 +184,14 @@ void session_loop(void(*loophandler)()) {
checktimeouts();
/* process session socket's incoming/outgoing data */
- if (ses.sock != -1) {
- if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
+ if (ses.sock_out != -1) {
+ if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) {
write_packet();
}
+ }
- if (FD_ISSET(ses.sock, &readfd)) {
+ if (ses.sock_in != -1) {
+ if (FD_ISSET(ses.sock_in, &readfd)) {
read_packet();
}
@@ -248,14 +251,14 @@ void session_identification() {
int i;
/* write our version string, this blocks */
- if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
+ if (atomicio(write, ses.sock_out, LOCAL_IDENT "\r\n",
strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
ses.remoteclosed();
}
/* If they send more than 50 lines, something is wrong */
for (i = 0; i < 50; i++) {
- len = ident_readln(ses.sock, linebuf, sizeof(linebuf));
+ len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf));
if (len < 0 && errno != EINTR) {
/* It failed */
@@ -411,3 +414,35 @@ static long select_timeout() {
ret = MIN(opts.keepalive_secs, ret);
return ret;
}
+
+const char* get_user_shell() {
+ /* an empty shell should be interpreted as "/bin/sh" */
+ if (ses.authstate.pw_shell[0] == '\0') {
+ return "/bin/sh";
+ } else {
+ return ses.authstate.pw_shell;
+ }
+}
+void fill_passwd(const char* username) {
+ struct passwd *pw = NULL;
+ if (ses.authstate.pw_name)
+ m_free(ses.authstate.pw_name);
+ if (ses.authstate.pw_dir)
+ m_free(ses.authstate.pw_dir);
+ if (ses.authstate.pw_shell)
+ m_free(ses.authstate.pw_shell);
+ if (ses.authstate.pw_passwd)
+ m_free(ses.authstate.pw_passwd);
+
+ pw = getpwnam(username);
+ if (!pw) {
+ return;
+ }
+ ses.authstate.pw_uid = pw->pw_uid;
+ ses.authstate.pw_gid = pw->pw_gid;
+ ses.authstate.pw_name = m_strdup(pw->pw_name);
+ ses.authstate.pw_dir = m_strdup(pw->pw_dir);
+ ses.authstate.pw_shell = m_strdup(pw->pw_shell);
+ ses.authstate.pw_passwd = m_strdup(pw->pw_passwd);
+}
+
diff --git a/dbclient.1 b/dbclient.1
index e72f249..033672d 100644
--- a/dbclient.1
+++ b/dbclient.1
@@ -86,6 +86,19 @@ 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.
+.SH ENVIRONMENT
+.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
diff --git a/dbutil.c b/dbutil.c
index 84b29c4..0680b75 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -146,7 +146,7 @@ void dropbear_trace(const char* format, ...) {
}
va_start(param, format);
- fprintf(stderr, "TRACE: ");
+ fprintf(stderr, "TRACE (%d): ", getpid());
vfprintf(stderr, format, param);
fprintf(stderr, "\n");
va_end(param);
@@ -338,9 +338,10 @@ int connect_remote(const char* remotehost, const char* remoteport,
if (err) {
if (errstring != NULL && *errstring == NULL) {
int len;
- len = 20 + strlen(gai_strerror(err));
+ len = 100 + strlen(gai_strerror(err));
*errstring = (char*)m_malloc(len);
- snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+ snprintf(*errstring, len, "Error resolving '%s' port '%s'. %s",
+ remotehost, remoteport, gai_strerror(err));
}
TRACE(("Error resolving: %s", gai_strerror(err)))
return -1;
@@ -398,6 +399,141 @@ int connect_remote(const char* remotehost, const char* remoteport,
return sock;
}
+/* Sets up a pipe for a, returning three non-blocking file descriptors
+ * and the pid. exec_fn is the function that will actually execute the child process,
+ * it will be run after the child has fork()ed, and is passed exec_data.
+ * If ret_errfd == NULL then stderr will not be captured.
+ * ret_pid can be passed as NULL to discard the pid. */
+int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
+ int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
+ int infds[2];
+ int outfds[2];
+ int errfds[2];
+ pid_t pid;
+
+ const int FDIN = 0;
+ const int FDOUT = 1;
+
+ /* redirect stdin/stdout/stderr */
+ if (pipe(infds) != 0) {
+ return DROPBEAR_FAILURE;
+ }
+ if (pipe(outfds) != 0) {
+ return DROPBEAR_FAILURE;
+ }
+ if (ret_errfd && pipe(errfds) != 0) {
+ return DROPBEAR_FAILURE;
+ }
+
+#ifdef __uClinux__
+ pid = vfork();
+#else
+ pid = fork();
+#endif
+
+ if (pid < 0) {
+ return DROPBEAR_FAILURE;
+ }
+
+ if (!pid) {
+ /* child */
+
+ TRACE(("back to normal sigchld"))
+ /* Revert to normal sigchld handling */
+ if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
+ dropbear_exit("signal() error");
+ }
+
+ /* redirect stdin/stdout */
+
+ if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
+ (dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
+ (ret_errfd && dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
+ TRACE(("leave noptycommand: error redirecting FDs"))
+ dropbear_exit("child dup2() failure");
+ }
+
+ close(infds[FDOUT]);
+ close(infds[FDIN]);
+ close(outfds[FDIN]);
+ close(outfds[FDOUT]);
+ if (ret_errfd)
+ {
+ close(errfds[FDIN]);
+ close(errfds[FDOUT]);
+ }
+
+ exec_fn(exec_data);
+ /* not reached */
+ return DROPBEAR_FAILURE;
+ } else {
+ /* parent */
+ close(infds[FDIN]);
+ close(outfds[FDOUT]);
+
+ setnonblocking(outfds[FDIN]);
+ setnonblocking(infds[FDOUT]);
+
+ if (ret_errfd) {
+ close(errfds[FDOUT]);
+ setnonblocking(errfds[FDIN]);
+ }
+
+ if (ret_pid) {
+ *ret_pid = pid;
+ }
+
+ *ret_writefd = infds[FDOUT];
+ *ret_readfd = outfds[FDIN];
+ if (ret_errfd) {
+ *ret_errfd = errfds[FDIN];
+ }
+ return DROPBEAR_SUCCESS;
+ }
+}
+
+/* Runs a command with "sh -c". Will close FDs (except stdin/stdout/stderr) and
+ * re-enabled SIGPIPE. If cmd is NULL, will run a login shell.
+ */
+void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
+ char * argv[4];
+ char * baseshell = NULL;
+ unsigned int i;
+
+ baseshell = basename(usershell);
+
+ if (cmd != NULL) {
+ argv[0] = baseshell;
+ } else {
+ /* a login shell should be "-bash" for "/bin/bash" etc */
+ int len = strlen(baseshell) + 2; /* 2 for "-" */
+ argv[0] = (char*)m_malloc(len);
+ snprintf(argv[0], len, "-%s", baseshell);
+ }
+
+ if (cmd != NULL) {
+ argv[1] = "-c";
+ argv[2] = (char*)cmd;
+ argv[3] = NULL;
+ } else {
+ /* construct a shell of the form "-bash" etc */
+ argv[1] = NULL;
+ }
+
+ /* Re-enable SIGPIPE for the executed process */
+ if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
+ dropbear_exit("signal() error");
+ }
+
+ /* close file descriptors except stdin/stdout/stderr
+ * Need to be sure FDs are closed here to avoid reading files as root */
+ for (i = 3; i <= maxfd; i++) {
+ m_close(i);
+ }
+
+ execv(usershell, argv);
+}
+
/* Return a string representation of the socket address passed. The return
* value is allocated with malloc() */
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
@@ -708,3 +844,17 @@ void disallow_core() {
lim.rlim_cur = lim.rlim_max = 0;
setrlimit(RLIMIT_CORE, &lim);
}
+
+/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */
+int m_str_to_uint(const char* str, unsigned int *val) {
+ errno = 0;
+ *val = strtoul(str, NULL, 10);
+ /* The c99 spec doesn't actually seem to define EINVAL, but most platforms
+ * I've looked at mention it in their manpage */
+ if ((*val == 0 && errno == EINVAL)
+ || (*val == ULONG_MAX && errno == ERANGE)) {
+ return DROPBEAR_FAILURE;
+ } else {
+ return DROPBEAR_SUCCESS;
+ }
+}
diff --git a/dbutil.h b/dbutil.h
index 0b43eba..fe08f18 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -49,6 +49,9 @@ char * stripcontrol(const char * text);
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
int dropbear_listen(const char* address, const char* port,
int *socks, unsigned int sockcount, char **errstring, int *maxfd);
+int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
+ int *writefd, int *readfd, int *errfd, pid_t *pid);
+void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
#ifdef ENABLE_CONNECT_UNIX
int connect_unix(const char* addr);
#endif
@@ -67,6 +70,7 @@ void __m_free(void* ptr);
void m_burn(void* data, unsigned int len);
void setnonblocking(int fd);
void disallow_core();
+int m_str_to_uint(const char* str, unsigned int *val);
/* Used to force mp_ints to be initialised */
#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}
diff --git a/debian/README.runit b/debian/README.runit
index 4ac2814..0a32176 100644
--- a/debian/README.runit
+++ b/debian/README.runit
@@ -31,16 +31,16 @@ run script
# vi /etc/dropbear/run
-Finally enable the service by linking dropbear's service directory to
-/var/service/. The service will be started within five seconds, and
-automatically at boot time. The sysv init script is disabled; see the
-runsvctrl(8) program for information on how to control services handled by
-runit. See the svlogd(8) program on how to configure the log service.
+Finally enable the service through runit's update-service(8) program, the
+service will be started within five seconds, and automatically at boot
+time, and the sysv init script will automatically be disabled; see the
+sv(8) program for information on how to control services handled by runit.
+See the svlogd(8) program on how to configure the log service.
- # ln -s /etc/dropbear /var/service/
+ # update-service --add /etc/dropbear
Optionally check the status of the service a few seconds later
- # runsvstat -l /var/service/dropbear
+ # sv status dropbear
- -- Gerrit Pape <pape@smarden.org>, Sun, 16 May 2004 15:52:34 +0000
+ -- Gerrit Pape <pape@smarden.org>, Fri, 02 Mar 2007 20:41:08 +0000
diff --git a/debian/changelog b/debian/changelog
index e7d4141..f51769b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,14 +1,75 @@
-dropbear (0.50-0.1) unstable; urgency=low
+dropbear (0.51-0.1) unstable; urgency=low
* New upstream release.
- -- Matt Johnston <matt@ucc.asn.au> Wed, 8 Aug 2007 11:22:33 +0800
+ -- Matt Johnston <matt@ucc.asn.au> Thu, 27 Mar 2008 19:14:00 +0900
-dropbear (0.49-0.1) unstable; urgency=low
+dropbear (0.50-4) unstable; urgency=low
- * New upstream release.
+ * debian/dropbear.init: apply patch from Petter Reinholdtsen: add LSB
+ formatted dependency info in init.d script (closes: #466257).
+ * debian/rules: no longer include symlinks for ./supervise/ subdirectories.
+ * debian/dropbear.postinst: upgrade from << 0.50-4: if dropbear is managed
+ by runit, remove service, and re-add using update-service(8).
+ * debian/control: Standards-Version: 3.7.3.0.
+ * debian/rules: target clean: don't ignore errors but check for readable
+ ./Makefile.
+
+ -- Gerrit Pape <pape@smarden.org> Thu, 06 Mar 2008 19:06:58 +0000
+
+dropbear (0.50-3) unstable; urgency=low
+
+ * debian/dropbear.init: use the update-service(8) program from the runit
+ package instead of directly checking for the symlink in /var/service/.
+ * debian/README.runit: talk about update-service(8) instead of symlinks
+ in /var/service/.
+
+ -- Gerrit Pape <pape@smarden.org> Fri, 15 Feb 2008 00:32:37 +0000
+
+dropbear (0.50-2) unstable; urgency=low
+
+ * debian/dropbear.README.Debian: no longer talk about entropy from
+ /dev/random, /dev/urandom is now used by default (thx Joey Hess,
+ closes: #441515).
+
+ -- Gerrit Pape <pape@smarden.org> Mon, 24 Sep 2007 16:49:17 +0000
+
+dropbear (0.50-1) unstable; urgency=low
+
+ * debian/README.runit: minor.
+ * new upstream version.
+ * debian/diff/0001-options.h-use-dev-urandom-instead-of-dev-random-a.diff:
+ remove; fixed upstream.
+
+ -- Gerrit Pape <pape@smarden.org> Thu, 09 Aug 2007 23:01:01 +0000
+
+dropbear (0.49-2) unstable; urgency=low
+
+ * debian/rules: apply diffs from debian/diff/ with patch -p1 instead of
+ -p0.
+ * debian/diff/0001-options.h-use-dev-urandom-instead-of-dev-random-a.diff:
+ new; options.h: use /dev/urandom instead of /dev/random as
+ DROPBEAR_RANDOM_DEV (closes: #386976).
+ * debian/rules: target clean: remove libtomcrypt/Makefile,
+ libtommath/Makefile.
+
+ -- Gerrit Pape <pape@smarden.org> Sat, 09 Jun 2007 08:59:59 +0000
+
+dropbear (0.49-1) unstable; urgency=high
+
+ * new upstream release, fixes
+ * CVE-2007-1099: dropbear dbclient insufficient warning on hostkey
+ mismatch (closes: #412899).
+ * dbclient uses static "Password:" prompt instead of using the server's
+ prompt (closes: #394996).
+ * debian/control: Suggests: openssh-client, not ssh (closes: #405686);
+ Standards-Version: 3.7.2.2.
+ * debian/README.Debian: ssh -> openssh-server, openssh-client; remove
+ 'Replacing OpenSSH "sshd" with Dropbear' part, this is simply done by not
+ installing the openssh-server package.
+ * debian/README.runit: runsvstat -> sv status.
- -- Matt Johnston <matt@ucc.asn.au> Fri, 23 Feb 2007 00:44:00 +0900
+ -- Gerrit Pape <pape@smarden.org> Fri, 2 Mar 2007 20:48:18 +0000
dropbear (0.48.1-1) unstable; urgency=medium
diff --git a/debian/control b/debian/control
index 81835b3..e2731f6 100644
--- a/debian/control
+++ b/debian/control
@@ -3,12 +3,12 @@ Section: net
Priority: optional
Maintainer: Gerrit Pape <pape@smarden.org>
Build-Depends: libz-dev
-Standards-Version: 3.6.2.1
+Standards-Version: 3.7.3.0
Package: dropbear
Architecture: any
Depends: ${shlibs:Depends}
-Suggests: ssh, runit
+Suggests: openssh-client, runit
Description: lightweight SSH2 server and client
dropbear is a SSH 2 server and client designed to be small enough to
be used in small memory environments, while still being functional and
diff --git a/debian/dropbear.README.Debian b/debian/dropbear.README.Debian
index 7eec3e6..0ce1874 100644
--- a/debian/dropbear.README.Debian
+++ b/debian/dropbear.README.Debian
@@ -1,52 +1,19 @@
Dropbear for Debian
-------------------
-This package will attempt to listen on port 22. If the OpenSSH
-package ("ssh") is installed, the file /etc/default/dropbear
-will be set up so that the server does not start by default.
+This package will attempt to setup the Dropbear ssh server to listen on
+port 22. If the OpenSSH server package ("openssh-server") is installed,
+the file /etc/default/dropbear will be set up so that the server does not
+start by default.
-You can run Dropbear concurrently with OpenSSH 'sshd' by
-modifying /etc/default/dropbear so that "NO_START" is set to
-"0" and changing the port number that Dropbear runs on. Follow
-the instructions in the file.
+You can run Dropbear concurrently with OpenSSH 'sshd' by modifying
+/etc/default/dropbear so that "NO_START" is set to "0", and changing the
+port number that Dropbear runs on. Follow the instructions in the file.
-This package suggests you install the "ssh" package. This package
-provides the "ssh" client program, as well as the "/usr/bin/scp"
-binary you will need to be able to retrieve files from a server
-running Dropbear via SCP.
-
-Replacing OpenSSH "sshd" with Dropbear
---------------------------------------
-
-You will still want to have the "ssh" package installed, as it
-provides the "ssh" and "scp" binaries. When you install this
-package, it checks for existing OpenSSH host keys and if found,
-converts them to the Dropbear format.
-
-If this appears to have worked, you should be able to change over
-by following these steps:
-
-1. Stop the OpenSSH server
- % /etc/init.d/ssh stop
-2. Prevent the OpenSSH server from starting in the future
- % touch /etc/ssh/sshd_not_to_be_run
-3. Modify the Dropbear defaults file, set NO_START to 0 and
- ensure DROPBEAR_PORT is set to 22.
- % editor /etc/default/dropbear
-4. Restart the Dropbear server.
- % /etc/init.d/dropbear restart
+This package suggests you install the "openssh-client" package, which
+provides the "ssh" client program, as well as the "/usr/bin/scp" binary
+you will need to be able to retrieve files via SCP from a server running
+Dropbear.
See the Dropbear homepage for more information:
http://matt.ucc.asn.au/dropbear/dropbear.html
-
-
-Entropy from /dev/random
-------------------------
-
-The dropbear binary package is configured at compile time to read
-entropy from /dev/random. If /dev/random on a system blocks when
-reading data from it, client logins may be delayed until the client
-times out. The dropbear server writes a notice to the logs when it
-sees /dev/random blocking. A workaround for such systems is to
-re-compile the package with DROPBEAR_RANDOM_DEV set to /dev/urandom
-in options.h.
diff --git a/debian/dropbear.init b/debian/dropbear.init
index ee69076..1705330 100644
--- a/debian/dropbear.init
+++ b/debian/dropbear.init
@@ -1,4 +1,11 @@
#!/bin/sh
+### BEGIN INIT INFO
+# Provides: dropbear
+# Required-Start: $remote_fs $syslog
+# Required-Stop: $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+### END INIT INFO
#
# Do not configure this file. Edit /etc/default/dropbear instead!
#
@@ -17,8 +24,8 @@ set -e
cancel() { echo "$1" >&2; exit 0; };
test ! -r /etc/default/dropbear || . /etc/default/dropbear
test -x "$DAEMON" || cancel "$DAEMON does not exist or is not executable."
-test ! -h /var/service/dropbear || \
- cancel '/var/service/dropbear exists, service is controlled through runit.'
+test ! -x /usr/sbin/update-service || ! update-service --check dropbear ||
+ cancel 'The dropbear service is controlled through runit, use the sv(8) program'
test -z "$DROPBEAR_BANNER" || \
DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -b $DROPBEAR_BANNER"
diff --git a/debian/dropbear.postinst b/debian/dropbear.postinst
index e51e2b7..7c95cfa 100644
--- a/debian/dropbear.postinst
+++ b/debian/dropbear.postinst
@@ -69,3 +69,11 @@ if test -x /etc/init.d/dropbear; then
/etc/init.d/dropbear restart
fi
fi
+
+if test -n "$2" && dpkg --compare-versions "$2" lt '0.50-4' &&
+update-service --check dropbear; then
+ update-service --remove /etc/dropbear 2>/dev/null || :
+ sleep 6
+ rm -rf /var/run/dropbear /var/run/dropbear.log
+ update-service --add /etc/dropbear || :
+fi
diff --git a/debian/rules b/debian/rules
index 52c3ea8..605754e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -28,7 +28,7 @@ DIR =$(shell pwd)/debian/dropbear
patch: deb-checkdir patch-stamp
patch-stamp:
for i in `ls -1 debian/diff/*.diff || :`; do \
- patch -p0 <$$i || exit 1; \
+ patch -p1 <$$i || exit 1; \
done
touch patch-stamp
@@ -46,10 +46,11 @@ build-stamp: config.status
touch build-stamp
clean: deb-checkdir deb-checkuid
- -$(MAKE) distclean
+ test ! -r Makefile || $(MAKE) distclean
+ rm -f libtomcrypt/Makefile libtommath/Makefile
test ! -e patch-stamp || \
for i in `ls -1r debian/diff/*.diff || :`; do \
- patch -p0 -R <$$i; \
+ patch -p1 -R <$$i; \
done
rm -f patch-stamp build-stamp config.log config.status
rm -rf '$(DIR)'
@@ -76,8 +77,6 @@ install: deb-checkdir deb-checkuid build-stamp
install -d -m0755 '$(DIR)'/etc/dropbear/log
install -m0755 debian/service/log '$(DIR)'/etc/dropbear/log/run
ln -s /var/log/dropbear '$(DIR)'/etc/dropbear/log/main
- ln -s /var/run/dropbear '$(DIR)'/etc/dropbear/supervise
- ln -s /var/run/dropbear.log '$(DIR)'/etc/dropbear/log/supervise
# man pages
install -d -m0755 '$(DIR)'/usr/share/man/man8
for i in dropbear.8 dropbearkey.8; do \
diff --git a/debug.h b/debug.h
index 175f3fc..a9cc0bd 100644
--- a/debug.h
+++ b/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
@@ -67,6 +67,11 @@
#define TRACE(X)
#endif /*DEBUG_TRACE*/
+/* To debug with GDB it is easier to run with no forking of child processes.
+ You will need to pass "-F" as well. */
+/* #define DEBUG_NOFORK */
+
+
/* For testing as non-root on shadowed systems, include the crypt of a password
* here. You can then log in as any user with this password. Ensure that you
* make your own password, and are careful about using this. This will also
diff --git a/genrsa.c b/genrsa.c
index 73a7984..7e5ecd5 100644
--- a/genrsa.c
+++ b/genrsa.c
@@ -62,17 +62,13 @@ rsa_key * gen_rsa_priv_key(unsigned int size) {
exit(1);
}
- /* PuTTY doesn't like it if the modulus isn't a multiple of 8 bits,
- * so we just generate them until we get one which is OK */
getrsaprime(key->p, &pminus, key->e, size/2);
- do {
- getrsaprime(key->q, &qminus, key->e, size/2);
+ getrsaprime(key->q, &qminus, key->e, size/2);
- if (mp_mul(key->p, key->q, key->n) != MP_OKAY) {
- fprintf(stderr, "rsa generation failed\n");
- exit(1);
- }
- } while (mp_count_bits(key->n) % 8 != 0);
+ if (mp_mul(key->p, key->q, key->n) != MP_OKAY) {
+ fprintf(stderr, "rsa generation failed\n");
+ exit(1);
+ }
/* lcm(p-1, q-1) */
if (mp_lcm(&pminus, &qminus, &lcm) != MP_OKAY) {
diff --git a/keyimport.c b/keyimport.c
index 1d3e3f5..76b92f1 100644
--- a/keyimport.c
+++ b/keyimport.c
@@ -701,7 +701,6 @@ static int openssh_write(const char *filename, sign_key *key,
int nnumbers = -1, pos, len, seqlen, i;
char *header = NULL, *footer = NULL;
char zero[1];
- unsigned char iv[8];
int ret = 0;
FILE *fp;
int keytype = -1;
diff --git a/options.h b/options.h
index 5e4f4ff..5515628 100644
--- a/options.h
+++ b/options.h
@@ -60,6 +60,10 @@ 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
@@ -67,6 +71,10 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
#define ENABLE_SVR_AGENTFWD
#define ENABLE_CLI_AGENTFWD
+/* Enable "Netcat mode". TODO describe here. */
+#define ENABLE_CLI_NETCAT
+
+
/* Encryption - at least one required.
* RFC Draft requires 3DES and recommends AES128 for interoperability.
* Including multiple keysize variants the same cipher
@@ -134,9 +142,15 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
* You can't enable both PASSWORD and PAM. */
#define ENABLE_SVR_PASSWORD_AUTH
-/*#define ENABLE_SVR_PAM_AUTH */ /* requires ./configure --enable-pam */
+/* PAM requires ./configure --enable-pam */
+/* #define ENABLE_SVR_PAM_AUTH */
#define ENABLE_SVR_PUBKEY_AUTH
+/* Wether to ake public key options in authorized_keys file into account */
+#ifdef ENABLE_SVR_PUBKEY_AUTH
+#define ENABLE_SVR_PUBKEY_OPTIONS
+#endif
+
#define ENABLE_CLI_PASSWORD_AUTH
#define ENABLE_CLI_PUBKEY_AUTH
#define ENABLE_CLI_INTERACT_AUTH
@@ -236,209 +250,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
be overridden at runtime with -K. 0 disables keepalives */
#define DEFAULT_KEEPALIVE 0
-/*******************************************************************
- * You shouldn't edit below here unless you know you need to.
- *******************************************************************/
-
-#ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "0.50"
-#endif
-
-#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
-#define PROGNAME "dropbear"
-
-/* Spec recommends after one hour or 1 gigabyte of data. One hour
- * is a bit too verbose, so we try 8 hours */
-#ifndef KEX_REKEY_TIMEOUT
-#define KEX_REKEY_TIMEOUT (3600 * 8)
-#endif
-#ifndef KEX_REKEY_DATA
-#define KEX_REKEY_DATA (1<<30) /* 2^30 == 1GB, this value must be < INT_MAX */
-#endif
-/* Close connections to clients which haven't authorised after AUTH_TIMEOUT */
-#ifndef AUTH_TIMEOUT
-#define AUTH_TIMEOUT 300 /* we choose 5 minutes */
-#endif
-
-/* Minimum key sizes for DSS and RSA */
-#ifndef MIN_DSS_KEYLEN
-#define MIN_DSS_KEYLEN 512
-#endif
-#ifndef MIN_RSA_KEYLEN
-#define MIN_RSA_KEYLEN 512
-#endif
-
-#define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */
-#define MAX_BANNER_LINES 20 /* How many lines the client will display */
-
-/* the number of NAME=VALUE pairs to malloc for environ, if we don't have
- * the clearenv() function */
-#define ENV_SIZE 100
-
-#define MAX_CMD_LEN 1024 /* max length of a command */
-#define MAX_TERM_LEN 200 /* max length of TERM name */
-
-#define MAX_HOST_LEN 254 /* max hostname len for tcp fwding */
-#define MAX_IP_LEN 15 /* strlen("255.255.255.255") == 15 */
-
-#define DROPBEAR_MAX_PORTS 10 /* max number of ports which can be specified,
- ipv4 and ipv6 don't count twice */
-
-/* Each port might have at least a v4 and a v6 address */
-#define MAX_LISTEN_ADDR (DROPBEAR_MAX_PORTS*3)
-
-#define _PATH_TTY "/dev/tty"
-
-#define _PATH_CP "/bin/cp"
-
-/* success/failure defines */
-#define DROPBEAR_SUCCESS 0
-#define DROPBEAR_FAILURE -1
-
-/* various algorithm identifiers */
-#define DROPBEAR_KEX_DH_GROUP1 0
-
-#define DROPBEAR_SIGNKEY_ANY 0
-#define DROPBEAR_SIGNKEY_RSA 1
-#define DROPBEAR_SIGNKEY_DSS 2
-#define DROPBEAR_SIGNKEY_NONE 3
-
-#define DROPBEAR_COMP_NONE 0
-#define DROPBEAR_COMP_ZLIB 1
-
-/* Required for pubkey auth */
-#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
-#define DROPBEAR_SIGNKEY_VERIFY
-#endif
-
-/* SHA1 is 20 bytes == 160 bits */
-#define SHA1_HASH_SIZE 20
-/* SHA512 is 64 bytes == 512 bits */
-#define SHA512_HASH_SIZE 64
-/* MD5 is 16 bytes = 128 bits */
-#define MD5_HASH_SIZE 16
-
-/* largest of MD5 and SHA1 */
-#define MAX_MAC_LEN SHA1_HASH_SIZE
-
-
-#define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
-#define MAX_IV_LEN 20 /* must be same as max blocksize,
- and >= SHA1_HASH_SIZE */
-#define MAX_MAC_KEY 20
-
-#define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't
- explicitly specified for all protocols (just
- for algos) but seems valid */
-
-#define MAX_PROPOSED_ALGO 20
-
-/* size/count limits */
-#define MIN_PACKET_LEN 16
-
-#define RECV_MAX_PACKET_LEN (MAX(35000, ((RECV_MAX_PAYLOAD_LEN)+100)))
-
-/* for channel code */
-#define TRANS_MAX_WINDOW 500000000 /* 500MB is sufficient, stopping overflow */
-#define TRANS_MAX_WIN_INCR 500000000 /* overflow prevention */
-
-#define RECV_WINDOWEXTEND (opts.recv_window / 3) /* We send a "window extend" every
- RECV_WINDOWEXTEND bytes */
-#define MAX_RECV_WINDOW (1024*1024) /* 1 MB should be enough */
-
-#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
- connection, so can't be _too_ small */
-
-#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
- is the max length for a password etc */
-
-/* For a 4096 bit DSS key, empirically determined */
-#define MAX_PUBKEY_SIZE 1700
-/* For a 4096 bit DSS key, empirically determined */
-#define MAX_PRIVKEY_SIZE 1700
-
-/* The maximum size of the bignum portion of the kexhash buffer */
-/* Sect. 8 of the transport draft, K_S + e + f + K */
-#define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130)
-
-#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
- in a few years time.... */
-
-#define DROPBEAR_MAX_CLI_PASS 1024
-
-#define DROPBEAR_MAX_CLI_INTERACT_PROMPTS 80 /* The number of prompts we'll
- accept for keyb-interactive
- auth */
-
-#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
-#define DROPBEAR_AES_CBC
-#endif
-
-#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
-#define DROPBEAR_TWOFISH_CBC
-#endif
-
-#ifndef ENABLE_X11FWD
-#define DISABLE_X11FWD
-#endif
-
-#ifndef ENABLE_AGENTFWD
-#define DISABLE_AGENTFWD
-#endif
-
-#if defined(DROPBEAR_PRNGD_SOCKET) || defined(ENABLE_CLI_AGENTFWD)
-#define ENABLE_CONNECT_UNIX
-#endif
-
-#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
-#define ENABLE_CLI_ANYTCPFWD
-#endif
-
-#if defined(ENABLE_CLI_LOCALTCPFWD) || defined(ENABLE_SVR_REMOTETCPFWD)
-#define DROPBEAR_TCP_ACCEPT
-#endif
-
-#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
- defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
- defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
-#define USING_LISTENERS
-#endif
-
-#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
-#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
-#endif
-
-#if defined(ENABLE_SVR_PASSWORD_AUTH) && defined(ENABLE_SVR_PAM_AUTH)
-#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
-#endif
-
-#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET)
-#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once"
-#endif
-
-#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET)
-#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h"
-#endif
-
-/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
- * code, if we're just compiling as client or server */
-#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)
-
-#define IS_DROPBEAR_SERVER (ses.isserver == 1)
-#define IS_DROPBEAR_CLIENT (ses.isserver == 0)
-
-#elif defined(DROPBEAR_SERVER)
-
-#define IS_DROPBEAR_SERVER 1
-#define IS_DROPBEAR_CLIENT 0
-
-#elif defined(DROPBEAR_CLIENT)
-
-#define IS_DROPBEAR_SERVER 0
-#define IS_DROPBEAR_CLIENT 1
-
-#else
-#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
-#endif
+/* Some other defines (that mostly should be left alone) are defined
+ * in sysoptions.h */
+#include "sysoptions.h"
#endif /* _OPTIONS_H_ */
diff --git a/packet.c b/packet.c
index c559dab..30f4758 100644
--- a/packet.c
+++ b/packet.c
@@ -61,7 +61,7 @@ void write_packet() {
len = writebuf->len - writebuf->pos;
dropbear_assert(len > 0);
/* Try to write as much as possible */
- written = write(ses.sock, buf_getptr(writebuf, len), len);
+ written = write(ses.sock_out, buf_getptr(writebuf, len), len);
if (written < 0) {
if (errno == EINTR) {
@@ -122,7 +122,7 @@ void read_packet() {
* mightn't be any available (EAGAIN) */
dropbear_assert(ses.readbuf != NULL);
maxlen = ses.readbuf->len - ses.readbuf->pos;
- len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
+ len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen);
if (len == 0) {
ses.remoteclosed();
@@ -171,7 +171,7 @@ static void read_packet_init() {
maxlen = blocksize - ses.readbuf->pos;
/* read the rest of the packet if possible */
- len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
+ len = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
maxlen);
if (len == 0) {
ses.remoteclosed();
diff --git a/runopts.h b/runopts.h
index b79befb..6cbdd26 100644
--- a/runopts.h
+++ b/runopts.h
@@ -37,7 +37,7 @@ typedef struct runopts {
int listen_fwd_all;
#endif
unsigned int recv_window;
- time_t keepalive_secs;
+ unsigned int keepalive_secs;
} runopts;
@@ -101,6 +101,7 @@ typedef struct cli_runopts {
char *remotehost;
char *remoteport;
+ char *own_user;
char *username;
char *cmd;
@@ -108,6 +109,7 @@ typedef struct cli_runopts {
int always_accept_key;
int no_cmd;
int backgrounded;
+ int is_subsystem;
#ifdef ENABLE_CLI_PUBKEY_AUTH
struct SignKeyList *privkeys; /* Keys to use for public-key auth */
#endif
@@ -123,6 +125,14 @@ typedef struct cli_runopts {
list of keys held by the agent */
#endif
+#ifdef ENABLE_CLI_NETCAT
+ char *netcat_host;
+ unsigned int netcat_port;
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+ char *proxycmd;
+#endif
+
} cli_runopts;
extern cli_runopts cli_opts;
diff --git a/session.h b/session.h
index e11c959..b63a258 100644
--- a/session.h
+++ b/session.h
@@ -41,12 +41,14 @@
extern int sessinitdone; /* Is set to 0 somewhere */
extern int exitflag;
-void common_session_init(int sock, char* remotehost);
+void common_session_init(int sock_in, int sock_out, char* remotehost);
void session_loop(void(*loophandler)());
void common_session_cleanup();
void session_identification();
void send_msg_ignore();
+const char* get_user_shell();
+void fill_passwd(const char* username);
/* Server */
void svr_session(int sock, int childpipe, char *remotehost, char *addrstring);
@@ -54,7 +56,7 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param);
void svr_dropbear_log(int priority, const char* format, va_list param);
/* Client */
-void cli_session(int sock, char *remotehost);
+void cli_session(int sock_in, int sock_out, char *remotehost);
void cli_session_cleanup();
void cleantext(unsigned char* dirtytext);
@@ -97,7 +99,8 @@ struct sshsession {
(cleared after auth once we're not
respecting AUTH_TIMEOUT any more) */
- int sock;
+ int sock_in;
+ int sock_out;
unsigned char *remotehost; /* the peer hostname */
diff --git a/svr-agentfwd.c b/svr-agentfwd.c
index f4909d5..b86c447 100644
--- a/svr-agentfwd.c
+++ b/svr-agentfwd.c
@@ -39,6 +39,7 @@
#include "buffer.h"
#include "random.h"
#include "listener.h"
+#include "auth.h"
#define AGENTDIRPREFIX "/tmp/dropbear-"
@@ -52,6 +53,10 @@ int agentreq(struct ChanSess * chansess) {
int fd;
+ if (!svr_pubkey_allows_agentfwd()) {
+ return DROPBEAR_FAILURE;
+ }
+
if (chansess->agentlistener != NULL) {
return DROPBEAR_FAILURE;
}
@@ -150,8 +155,8 @@ void agentcleanup(struct ChanSess * chansess) {
* for themselves */
uid = getuid();
gid = getgid();
- if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
- (seteuid(ses.authstate.pw->pw_uid)) < 0) {
+ if ((setegid(ses.authstate.pw_gid)) < 0 ||
+ (seteuid(ses.authstate.pw_uid)) < 0) {
dropbear_exit("failed to set euid");
}
@@ -213,8 +218,8 @@ static int bindagent(int fd, struct ChanSess * chansess) {
/* drop to user privs to make the dir/file */
uid = getuid();
gid = getgid();
- if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
- (seteuid(ses.authstate.pw->pw_uid)) < 0) {
+ if ((setegid(ses.authstate.pw_gid)) < 0 ||
+ (seteuid(ses.authstate.pw_uid)) < 0) {
dropbear_exit("failed to set euid");
}
diff --git a/svr-auth.c b/svr-auth.c
index ea31e79..4adf809 100644
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -42,6 +42,10 @@ static void send_msg_userauth_banner();
void svr_authinitialise() {
ses.authstate.failcount = 0;
+ ses.authstate.pw_name = NULL;
+ ses.authstate.pw_dir = NULL;
+ ses.authstate.pw_shell = NULL;
+ ses.authstate.pw_passwd = NULL;
authclear();
}
@@ -60,7 +64,19 @@ static void authclear() {
ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
}
#endif
-
+ if (ses.authstate.pw_name) {
+ m_free(ses.authstate.pw_name);
+ }
+ if (ses.authstate.pw_shell) {
+ m_free(ses.authstate.pw_shell);
+ }
+ if (ses.authstate.pw_dir) {
+ m_free(ses.authstate.pw_dir);
+ }
+ if (ses.authstate.pw_passwd) {
+ m_free(ses.authstate.pw_passwd);
+ }
+
}
/* Send a banner message if specified to the client. The client might
@@ -143,7 +159,7 @@ void recv_msg_userauth_request() {
#ifdef ENABLE_SVR_PASSWORD_AUTH
if (!svr_opts.noauthpass &&
- !(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
+ !(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) {
/* user wants to try password auth */
if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
strncmp(methodname, AUTH_METHOD_PASSWORD,
@@ -156,7 +172,7 @@ void recv_msg_userauth_request() {
#ifdef ENABLE_SVR_PAM_AUTH
if (!svr_opts.noauthpass &&
- !(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
+ !(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) {
/* user wants to try password auth */
if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
strncmp(methodname, AUTH_METHOD_PASSWORD,
@@ -187,6 +203,7 @@ out:
m_free(methodname);
}
+
/* Check that the username exists, has a non-empty password, and has a valid
* shell.
* returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */
@@ -194,7 +211,6 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
char* listshell = NULL;
char* usershell = NULL;
-
TRACE(("enter checkusername"))
if (userlen > MAX_USERNAME_LEN) {
return DROPBEAR_FAILURE;
@@ -210,13 +226,12 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
m_free(ses.authstate.username);
}
authclear();
- ses.authstate.pw = getpwnam((char*)username);
+ fill_passwd(username);
ses.authstate.username = m_strdup(username);
- m_free(ses.authstate.printableuser);
}
/* check that user exists */
- if (ses.authstate.pw == NULL) {
+ if (!ses.authstate.pw_name) {
TRACE(("leave checkusername: user '%s' doesn't exist", username))
dropbear_log(LOG_WARNING,
"login attempt for nonexistent user from %s",
@@ -225,11 +240,8 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
return DROPBEAR_FAILURE;
}
- /* We can set it once we know its a real user */
- ses.authstate.printableuser = m_strdup(ses.authstate.pw->pw_name);
-
/* check for non-root if desired */
- if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) {
+ if (svr_opts.norootlogin && ses.authstate.pw_uid == 0) {
TRACE(("leave checkusername: root login disabled"))
dropbear_log(LOG_WARNING, "root login rejected");
send_msg_userauth_failure(0, 1);
@@ -237,18 +249,18 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
}
/* check for an empty password */
- if (ses.authstate.pw->pw_passwd[0] == '\0') {
+ if (ses.authstate.pw_passwd[0] == '\0') {
TRACE(("leave checkusername: empty pword"))
dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
- ses.authstate.printableuser);
+ ses.authstate.pw_name);
send_msg_userauth_failure(0, 1);
return DROPBEAR_FAILURE;
}
- TRACE(("shell is %s", ses.authstate.pw->pw_shell))
+ TRACE(("shell is %s", ses.authstate.pw_shell))
/* check that the shell is set */
- usershell = ses.authstate.pw->pw_shell;
+ usershell = ses.authstate.pw_shell;
if (usershell[0] == '\0') {
/* empty shell in /etc/passwd means /bin/sh according to passwd(5) */
usershell = "/bin/sh";
@@ -269,7 +281,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) {
endusershell();
TRACE(("no matching shell"))
dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
- ses.authstate.printableuser);
+ ses.authstate.pw_name);
send_msg_userauth_failure(0, 1);
return DROPBEAR_FAILURE;
@@ -277,7 +289,7 @@ goodshell:
endusershell();
TRACE(("matching shell"))
- TRACE(("uid = %d", ses.authstate.pw->pw_uid))
+ TRACE(("uid = %d", ses.authstate.pw_uid))
TRACE(("leave checkusername"))
return DROPBEAR_SUCCESS;
@@ -334,10 +346,10 @@ void send_msg_userauth_failure(int partial, int incrfail) {
/* XXX - send disconnect ? */
TRACE(("Max auth tries reached, exiting"))
- if (ses.authstate.printableuser == NULL) {
+ if (ses.authstate.pw_name == NULL) {
userstr = "is invalid";
} else {
- userstr = ses.authstate.printableuser;
+ userstr = ses.authstate.pw_name;
}
dropbear_exit("Max auth tries reached - user '%s' from %s",
userstr, svr_ses.addrstring);
@@ -360,7 +372,7 @@ void send_msg_userauth_success() {
ses.connect_time = 0;
- if (ses.authstate.pw->pw_uid == 0) {
+ if (ses.authstate.pw_uid == 0) {
ses.allowprivport = 1;
}
diff --git a/svr-authpam.c b/svr-authpam.c
index ee7d6ba..8d6a6e7 100644
--- a/svr-authpam.c
+++ b/svr-authpam.c
@@ -195,7 +195,7 @@ void svr_auth_pam() {
/* used to pass data to the PAM conversation function - don't bother with
* strdup() etc since these are touched only by our own conversation
* function (above) which takes care of it */
- userData.user = ses.authstate.printableuser;
+ userData.user = ses.authstate.pw_name;
userData.passwd = password;
/* Init pam */
@@ -221,7 +221,7 @@ void svr_auth_pam() {
rc, pam_strerror(pamHandlep, rc));
dropbear_log(LOG_WARNING,
"bad PAM password attempt for '%s' from %s",
- ses.authstate.printableuser,
+ ses.authstate.pw_name,
svr_ses.addrstring);
send_msg_userauth_failure(0, 1);
goto cleanup;
@@ -232,7 +232,7 @@ void svr_auth_pam() {
rc, pam_strerror(pamHandlep, rc));
dropbear_log(LOG_WARNING,
"bad PAM password attempt for '%s' from %s",
- ses.authstate.printableuser,
+ ses.authstate.pw_name,
svr_ses.addrstring);
send_msg_userauth_failure(0, 1);
goto cleanup;
@@ -240,7 +240,7 @@ void svr_auth_pam() {
/* successful authentication */
dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
- ses.authstate.printableuser,
+ ses.authstate.pw_name,
svr_ses.addrstring);
send_msg_userauth_success();
diff --git a/svr-authpasswd.c b/svr-authpasswd.c
index 5be1e2a..53550a2 100644
--- a/svr-authpasswd.c
+++ b/svr-authpasswd.c
@@ -46,10 +46,10 @@ void svr_auth_password() {
unsigned int changepw;
- passwdcrypt = ses.authstate.pw->pw_passwd;
+ passwdcrypt = ses.authstate.pw_passwd;
#ifdef HAVE_SHADOW_H
/* get the shadow password if possible */
- spasswd = getspnam(ses.authstate.printableuser);
+ spasswd = getspnam(ses.authstate.pw_name);
if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
passwdcrypt = spasswd->sp_pwdp;
}
@@ -65,7 +65,7 @@ void svr_auth_password() {
* in auth.c */
if (passwdcrypt[0] == '\0') {
dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
- ses.authstate.printableuser);
+ ses.authstate.pw_name);
send_msg_userauth_failure(0, 1);
return;
}
@@ -89,13 +89,13 @@ void svr_auth_password() {
/* successful authentication */
dropbear_log(LOG_NOTICE,
"password auth succeeded for '%s' from %s",
- ses.authstate.printableuser,
+ ses.authstate.pw_name,
svr_ses.addrstring);
send_msg_userauth_success();
} else {
dropbear_log(LOG_WARNING,
"bad password attempt for '%s' from %s",
- ses.authstate.printableuser,
+ ses.authstate.pw_name,
svr_ses.addrstring);
send_msg_userauth_failure(0, 1);
}
diff --git a/svr-authpubkey.c b/svr-authpubkey.c
index d611c89..de72766 100644
--- a/svr-authpubkey.c
+++ b/svr-authpubkey.c
@@ -21,6 +21,37 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. */
+/*
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This copyright and permission notice applies to the code parsing public keys
+ * options string which can also be found in OpenSSH auth2-pubkey.c file
+ * (user_key_allowed2). It has been adapted to work with buffers.
+ *
+ */
/* Process a pubkey auth request */
@@ -105,12 +136,12 @@ void svr_auth_pubkey() {
signbuf->len) == DROPBEAR_SUCCESS) {
dropbear_log(LOG_NOTICE,
"pubkey auth succeeded for '%s' with key %s from %s",
- ses.authstate.printableuser, fp, svr_ses.addrstring);
+ ses.authstate.pw_name, fp, svr_ses.addrstring);
send_msg_userauth_success();
} else {
dropbear_log(LOG_WARNING,
"pubkey auth bad signature for '%s' with key %s from %s",
- ses.authstate.printableuser, fp, svr_ses.addrstring);
+ ses.authstate.pw_name, fp, svr_ses.addrstring);
send_msg_userauth_failure(0, 1);
}
m_free(fp);
@@ -159,14 +190,16 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
int ret = DROPBEAR_FAILURE;
buffer * line = NULL;
unsigned int len, pos;
-
+ buffer * options_buf = NULL;
+ int line_num;
+
TRACE(("enter checkpubkey"))
/* check that we can use the algo */
if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
dropbear_log(LOG_WARNING,
"pubkey auth attempt with unknown algo for '%s' from %s",
- ses.authstate.printableuser, svr_ses.addrstring);
+ ses.authstate.pw_name, svr_ses.addrstring);
goto out;
}
@@ -178,12 +211,12 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
/* we don't need to check pw and pw_dir for validity, since
* its been done in checkpubkeyperms. */
- len = strlen(ses.authstate.pw->pw_dir);
+ len = strlen(ses.authstate.pw_dir);
/* allocate max required pathname storage,
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
filename = m_malloc(len + 22);
snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
- ses.authstate.pw->pw_dir);
+ ses.authstate.pw_dir);
/* open the file */
authfile = fopen(filename, "r");
@@ -193,25 +226,82 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
TRACE(("checkpubkey: opened authorized_keys OK"))
line = buf_new(MAX_AUTHKEYS_LINE);
+ line_num = 0;
/* iterate through the lines */
do {
+ /* new line : potentially new options */
+ if (options_buf) {
+ buf_free(options_buf);
+ options_buf = NULL;
+ }
if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
/* EOF reached */
TRACE(("checkpubkey: authorized_keys EOF reached"))
break;
}
+ line_num++;
if (line->len < MIN_AUTHKEYS_LINE) {
TRACE(("checkpubkey: line too short"))
continue; /* line is too short for it to be a valid key */
}
- /* check the key type - this also stops us from using keys
- * which have options with them */
+ /* check the key type - will fail if there are options */
+ TRACE(("a line!"))
+
if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) {
- continue;
+ int is_comment = 0;
+ char *options_start = NULL;
+ int options_len = 0;
+ int escape, quoted;
+
+ /* skip over any comments or leading whitespace */
+ while (line->pos < line->len) {
+ const char c = buf_getbyte(line);
+ if (c == ' ' || c == '\t') {
+ continue;
+ } else if (c == '#') {
+ is_comment = 1;
+ break;
+ }
+ buf_incrpos(line, -1);
+ break;
+ }
+ if (is_comment) {
+ /* next line */
+ continue;
+ }
+
+ /* remember start of options */
+ options_start = buf_getptr(line, 1);
+ quoted = 0;
+ escape = 0;
+ options_len = 0;
+
+ /* figure out where the options are */
+ while (line->pos < line->len) {
+ const char c = buf_getbyte(line);
+ if (!quoted && (c == ' ' || c == '\t')) {
+ break;
+ }
+ escape = (!escape && c == '\\');
+ if (!escape && c == '"') {
+ quoted = !quoted;
+ }
+ options_len++;
+ }
+ options_buf = buf_new(options_len);
+ buf_putbytes(options_buf, options_start, options_len);
+
+ /* compare the algorithm */
+ if (line->pos + algolen > line->len) {
+ continue;
+ }
+ if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) {
+ continue;
+ }
}
buf_incrpos(line, algolen);
@@ -232,6 +322,11 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen,
TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len))
ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line, NULL);
+
+ if (ret == DROPBEAR_SUCCESS && options_buf) {
+ ret = svr_add_pubkey_options(options_buf, line_num, filename);
+ }
+
if (ret == DROPBEAR_SUCCESS) {
break;
}
@@ -248,6 +343,9 @@ out:
buf_free(line);
}
m_free(filename);
+ if (options_buf) {
+ buf_free(options_buf);
+ }
TRACE(("leave checkpubkey: ret=%d", ret))
return ret;
}
@@ -266,18 +364,18 @@ static int checkpubkeyperms() {
TRACE(("enter checkpubkeyperms"))
- if (ses.authstate.pw->pw_dir == NULL) {
+ if (ses.authstate.pw_dir == NULL) {
goto out;
}
- if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) {
+ if ((len = strlen(ses.authstate.pw_dir)) == 0) {
goto out;
}
/* allocate max required pathname storage,
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
filename = m_malloc(len + 22);
- strncpy(filename, ses.authstate.pw->pw_dir, len+1);
+ strncpy(filename, ses.authstate.pw_dir, len+1);
/* check ~ */
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
@@ -320,7 +418,7 @@ static int checkfileperm(char * filename) {
return DROPBEAR_FAILURE;
}
/* check ownership - user or root only*/
- if (filestat.st_uid != ses.authstate.pw->pw_uid
+ if (filestat.st_uid != ses.authstate.pw_uid
&& filestat.st_uid != 0) {
badperm = 1;
TRACE(("wrong ownership"))
@@ -343,5 +441,4 @@ static int checkfileperm(char * filename) {
return DROPBEAR_SUCCESS;
}
-
-#endif
+#endif
diff --git a/svr-authpubkeyoptions.c b/svr-authpubkeyoptions.c
new file mode 100644
index 0000000..8a92d62
--- /dev/null
+++ b/svr-authpubkeyoptions.c
@@ -0,0 +1,202 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * Copyright (c) 2008 Frederic Moulins
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ *
+ * This copyright and permission notice applies to the code parsing public keys
+ * options string which can also be found in OpenSSH auth-options.c file
+ * (auth_parse_options).
+ *
+ */
+
+/* Process pubkey options during a pubkey auth request */
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "signkey.h"
+#include "auth.h"
+
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+
+/* Returns 1 if pubkey allows agent forwarding,
+ * 0 otherwise */
+int svr_pubkey_allows_agentfwd() {
+ if (ses.authstate.pubkey_options
+ && ses.authstate.pubkey_options->no_agent_forwarding_flag) {
+ return 0;
+ }
+ return 1;
+}
+
+/* Returns 1 if pubkey allows tcp forwarding,
+ * 0 otherwise */
+int svr_pubkey_allows_tcpfwd() {
+ if (ses.authstate.pubkey_options
+ && ses.authstate.pubkey_options->no_port_forwarding_flag) {
+ return 0;
+ }
+ return 1;
+}
+
+/* Returns 1 if pubkey allows x11 forwarding,
+ * 0 otherwise */
+int svr_pubkey_allows_x11fwd() {
+ if (ses.authstate.pubkey_options
+ && ses.authstate.pubkey_options->no_x11_forwarding_flag) {
+ return 0;
+ }
+ return 1;
+}
+
+/* Returns 1 if pubkey allows pty, 0 otherwise */
+int svr_pubkey_allows_pty() {
+ if (ses.authstate.pubkey_options
+ && ses.authstate.pubkey_options->no_pty_flag) {
+ return 0;
+ }
+ return 1;
+}
+
+/* Set chansession command to the one forced by 'command' public key option */
+void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
+ if (ses.authstate.pubkey_options)
+ chansess->cmd = ses.authstate.pubkey_options->forced_command;
+}
+
+/* Free potential public key options */
+void svr_pubkey_options_cleanup() {
+ if (ses.authstate.pubkey_options) {
+ m_free(ses.authstate.pubkey_options);
+ ses.authstate.pubkey_options = NULL;
+ }
+}
+
+/* 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);
+ if (options_buf->len - options_buf->pos < len) {
+ return DROPBEAR_FAILURE;
+ }
+ if (strncasecmp(buf_getptr(options_buf, len), opt_name, len) == 0) {
+ buf_incrpos(options_buf, len);
+ return DROPBEAR_SUCCESS;
+ }
+ return DROPBEAR_FAILURE;
+}
+
+/* Parse pubkey options and set ses.authstate.pubkey_options accordingly.
+ * Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
+int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
+ int ret = DROPBEAR_FAILURE;
+
+ TRACE(("enter addpubkeyoptions"))
+
+ ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
+ memset(ses.authstate.pubkey_options, '\0', sizeof(*ses.authstate.pubkey_options));
+
+ buf_setpos(options_buf, 0);
+ while (options_buf->pos < options_buf->len) {
+ if (match_option(options_buf, "no-port-forwarding") == DROPBEAR_SUCCESS) {
+ dropbear_log(LOG_WARNING, "Port forwarding disabled.");
+ ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
+ goto next_option;
+ }
+#ifdef ENABLE_AGENTFWD
+ if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) {
+ dropbear_log(LOG_WARNING, "Agent forwarding disabled.");
+ ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
+ goto next_option;
+ }
+#endif
+#ifdef ENABLE_X11FWD
+ if (match_option(options_buf, "no-X11-forwarding") == DROPBEAR_SUCCESS) {
+ dropbear_log(LOG_WARNING, "X11 forwarding disabled.");
+ ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
+ goto next_option;
+ }
+#endif
+ if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) {
+ dropbear_log(LOG_WARNING, "Pty allocation disabled.");
+ ses.authstate.pubkey_options->no_pty_flag = 1;
+ goto next_option;
+ }
+ if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) {
+ int escaped = 0;
+ const unsigned char* command_start = buf_getptr(options_buf, 0);
+ while (options_buf->pos < options_buf->len) {
+ const char c = buf_getbyte(options_buf);
+ if (!escaped && c == '"') {
+ const int command_len = buf_getptr(options_buf, 0) - command_start;
+ ses.authstate.pubkey_options->forced_command = m_malloc(command_len);
+ memcpy(ses.authstate.pubkey_options->forced_command,
+ command_start, command_len-1);
+ ses.authstate.pubkey_options->forced_command[command_len-1] = '\0';
+ dropbear_log(LOG_WARNING, "Forced command '%s'",
+ ses.authstate.pubkey_options->forced_command);
+ goto next_option;
+ }
+ escaped = (!escaped && c == '\\');
+ }
+ dropbear_log(LOG_WARNING, "Badly formatted command= authorized_keys option");
+ goto bad_option;
+ }
+
+next_option:
+ /*
+ * Skip the comma, and move to the next option
+ * (or break out if there are no more).
+ */
+ if (options_buf->pos < options_buf->len
+ && buf_getbyte(options_buf) != ',') {
+ goto bad_option;
+ }
+ /* Process the next option. */
+ }
+ /* parsed all options with no problem */
+ ret = DROPBEAR_SUCCESS;
+ goto end;
+
+bad_option:
+ ret = DROPBEAR_FAILURE;
+ m_free(ses.authstate.pubkey_options);
+ ses.authstate.pubkey_options = NULL;
+ dropbear_log(LOG_WARNING, "Bad public key options at %s:%d", filename, line_num);
+
+end:
+ TRACE(("leave addpubkeyoptions"))
+ return ret;
+}
+
+#endif
diff --git a/svr-chansession.c b/svr-chansession.c
index 619a451..9b2a412 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -37,6 +37,7 @@
#include "x11fwd.h"
#include "agentfwd.h"
#include "runopts.h"
+#include "auth.h"
/* Handles sessions (either shells or programs) requested by the client */
@@ -47,7 +48,7 @@ static int sessionsignal(struct ChanSess *chansess);
static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
static int sessionwinchange(struct ChanSess *chansess);
-static void execchild(struct ChanSess *chansess);
+static void execchild(void *user_data_chansess);
static void addchildpid(struct ChanSess *chansess, pid_t pid);
static void sesssigchild_handler(int val);
static void closechansess(struct Channel *channel);
@@ -524,8 +525,15 @@ static int sessionpty(struct ChanSess * chansess) {
unsigned int termlen;
unsigned char namebuf[65];
+ struct passwd * pw = NULL;
TRACE(("enter sessionpty"))
+
+ if (!svr_pubkey_allows_pty()) {
+ TRACE(("leave sessionpty : pty forbidden by public key option"))
+ return DROPBEAR_FAILURE;
+ }
+
chansess->term = buf_getstring(ses.payload, &termlen);
if (termlen > MAX_TERM_LEN) {
/* TODO send disconnect ? */
@@ -547,7 +555,10 @@ static int sessionpty(struct ChanSess * chansess) {
dropbear_exit("out of memory"); /* TODO disconnect */
}
- pty_setowner(ses.authstate.pw, chansess->tty);
+ pw = getpwnam(ses.authstate.pw_name);
+ if (!pw)
+ dropbear_exit("getpwnam failed after succeeding previously");
+ pty_setowner(pw, chansess->tty);
/* Set up the rows/col counts */
sessionwinchange(chansess);
@@ -578,14 +589,19 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
return DROPBEAR_FAILURE;
}
+ /* take public key option 'command' into account */
+ svr_pubkey_set_forced_command(chansess);
+
if (iscmd) {
/* "exec" */
- chansess->cmd = buf_getstring(ses.payload, &cmdlen);
+ if (chansess->cmd == NULL) {
+ chansess->cmd = buf_getstring(ses.payload, &cmdlen);
- if (cmdlen > MAX_CMD_LEN) {
- m_free(chansess->cmd);
- /* TODO - send error - too long ? */
- return DROPBEAR_FAILURE;
+ if (cmdlen > MAX_CMD_LEN) {
+ m_free(chansess->cmd);
+ /* TODO - send error - too long ? */
+ return DROPBEAR_FAILURE;
+ }
}
if (issubsys) {
#ifdef SFTPSERVER_PATH
@@ -604,10 +620,10 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
#ifdef LOG_COMMANDS
if (chansess->cmd) {
dropbear_log(LOG_INFO, "user %s executing '%s'",
- ses.authstate.printableuser, chansess->cmd);
+ ses.authstate.pw_name, chansess->cmd);
} else {
dropbear_log(LOG_INFO, "user %s executing login shell",
- ses.authstate.printableuser);
+ ses.authstate.pw_name);
}
#endif
@@ -629,100 +645,37 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
* pty.
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
-
- int infds[2];
- int outfds[2];
- int errfds[2];
- pid_t pid;
- unsigned int i;
+ int ret;
TRACE(("enter noptycommand"))
+ ret = spawn_command(execchild, chansess,
+ &channel->writefd, &channel->readfd, &channel->errfd,
+ &chansess->pid);
- /* redirect stdin/stdout/stderr */
- if (pipe(infds) != 0)
- return DROPBEAR_FAILURE;
- if (pipe(outfds) != 0)
- return DROPBEAR_FAILURE;
- if (pipe(errfds) != 0)
- return DROPBEAR_FAILURE;
-
-#ifdef __uClinux__
- pid = vfork();
-#else
- pid = fork();
-#endif
-
- if (pid < 0)
- return DROPBEAR_FAILURE;
-
- if (!pid) {
- /* child */
-
- TRACE(("back to normal sigchld"))
- /* Revert to normal sigchld handling */
- if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
- dropbear_exit("signal() error");
- }
-
- /* redirect stdin/stdout */
-#define FDIN 0
-#define FDOUT 1
- if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
- (dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
- (dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
- TRACE(("leave noptycommand: error redirecting FDs"))
- return DROPBEAR_FAILURE;
- }
-
- close(infds[FDOUT]);
- close(infds[FDIN]);
- close(outfds[FDIN]);
- close(outfds[FDOUT]);
- close(errfds[FDIN]);
- close(errfds[FDOUT]);
-
- execchild(chansess);
- /* not reached */
+ if (ret == DROPBEAR_FAILURE) {
+ return ret;
+ }
- } else {
- /* parent */
- TRACE(("continue noptycommand: parent"))
- chansess->pid = pid;
- TRACE(("child pid is %d", pid))
+ ses.maxfd = MAX(ses.maxfd, channel->writefd);
+ ses.maxfd = MAX(ses.maxfd, channel->readfd);
+ ses.maxfd = MAX(ses.maxfd, channel->errfd);
- addchildpid(chansess, pid);
+ addchildpid(chansess, chansess->pid);
- if (svr_ses.lastexit.exitpid != -1) {
- 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 */
- for (i = 0; i < svr_ses.childpidsize; i++) {
- if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
- TRACE(("found match for lastexitpid"))
- svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
- svr_ses.lastexit.exitpid = -1;
- }
+ if (svr_ses.lastexit.exitpid != -1) {
+ 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"))
+ svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
+ svr_ses.lastexit.exitpid = -1;
}
}
-
- close(infds[FDIN]);
- close(outfds[FDOUT]);
- close(errfds[FDOUT]);
- channel->writefd = infds[FDOUT];
- channel->readfd = outfds[FDIN];
- channel->errfd = errfds[FDIN];
- ses.maxfd = MAX(ses.maxfd, channel->writefd);
- ses.maxfd = MAX(ses.maxfd, channel->readfd);
- ses.maxfd = MAX(ses.maxfd, channel->errfd);
-
- setnonblocking(channel->readfd);
- setnonblocking(channel->writefd);
- setnonblocking(channel->errfd);
-
}
-#undef FDIN
-#undef FDOUT
TRACE(("leave noptycommand"))
return DROPBEAR_SUCCESS;
@@ -794,11 +747,11 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
if (svr_opts.domotd) {
/* don't show the motd if ~/.hushlogin exists */
- /* 11 == strlen("/hushlogin\0") */
- len = strlen(ses.authstate.pw->pw_dir) + 11;
+ /* 12 == strlen("/.hushlogin\0") */
+ len = strlen(ses.authstate.pw_dir) + 12;
hushpath = m_malloc(len);
- snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir);
+ snprintf(hushpath, len, "%s/.hushlogin", ses.authstate.pw_dir);
if (stat(hushpath, &sb) < 0) {
/* more than a screenful is stupid IMHO */
@@ -867,12 +820,9 @@ static void addchildpid(struct ChanSess *chansess, pid_t pid) {
/* Clean up, drop to user privileges, set up the environment and execute
* the command/shell. This function does not return. */
-static void execchild(struct ChanSess *chansess) {
-
- char *argv[4];
- char * usershell = NULL;
- char * baseshell = NULL;
- unsigned int i;
+static void execchild(void *user_data) {
+ struct ChanSess *chansess = user_data;
+ char *usershell = NULL;
/* with uClinux we'll have vfork()ed, so don't want to overwrite the
* hostkey. can't think of a workaround to clear it */
@@ -885,12 +835,6 @@ static void execchild(struct ChanSess *chansess) {
reseedrandom();
#endif
- /* close file descriptors except stdin/stdout/stderr
- * Need to be sure FDs are closed here to avoid reading files as root */
- for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
- m_close(i);
- }
-
/* clear environment */
/* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
* etc. This is hazardous, so should only be used for debugging. */
@@ -908,12 +852,12 @@ static void execchild(struct ChanSess *chansess) {
/* We can only change uid/gid as root ... */
if (getuid() == 0) {
- if ((setgid(ses.authstate.pw->pw_gid) < 0) ||
- (initgroups(ses.authstate.pw->pw_name,
- ses.authstate.pw->pw_gid) < 0)) {
+ if ((setgid(ses.authstate.pw_gid) < 0) ||
+ (initgroups(ses.authstate.pw_name,
+ ses.authstate.pw_gid) < 0)) {
dropbear_exit("error changing user group");
}
- if (setuid(ses.authstate.pw->pw_uid) < 0) {
+ if (setuid(ses.authstate.pw_uid) < 0) {
dropbear_exit("error changing user");
}
} else {
@@ -924,29 +868,22 @@ static void execchild(struct ChanSess *chansess) {
* usernames with the same uid, but differing groups, then the
* differing groups won't be set (as with initgroups()). The solution
* is for the sysadmin not to give out the UID twice */
- if (getuid() != ses.authstate.pw->pw_uid) {
+ if (getuid() != ses.authstate.pw_uid) {
dropbear_exit("couldn't change user as non-root");
}
}
- /* an empty shell should be interpreted as "/bin/sh" */
- if (ses.authstate.pw->pw_shell[0] == '\0') {
- usershell = "/bin/sh";
- } else {
- usershell = ses.authstate.pw->pw_shell;
- }
-
/* set env vars */
- addnewvar("USER", ses.authstate.pw->pw_name);
- addnewvar("LOGNAME", ses.authstate.pw->pw_name);
- addnewvar("HOME", ses.authstate.pw->pw_dir);
- addnewvar("SHELL", usershell);
+ addnewvar("USER", ses.authstate.pw_name);
+ addnewvar("LOGNAME", ses.authstate.pw_name);
+ addnewvar("HOME", ses.authstate.pw_dir);
+ addnewvar("SHELL", get_user_shell());
if (chansess->term != NULL) {
addnewvar("TERM", chansess->term);
}
/* change directory */
- if (chdir(ses.authstate.pw->pw_dir) < 0) {
+ if (chdir(ses.authstate.pw_dir) < 0) {
dropbear_exit("error changing directory");
}
@@ -959,32 +896,8 @@ static void execchild(struct ChanSess *chansess) {
agentset(chansess);
#endif
- /* Re-enable SIGPIPE for the executed process */
- if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
- dropbear_exit("signal() error");
- }
-
- baseshell = basename(usershell);
-
- if (chansess->cmd != NULL) {
- argv[0] = baseshell;
- } else {
- /* a login shell should be "-bash" for "/bin/bash" etc */
- int len = strlen(baseshell) + 2; /* 2 for "-" */
- argv[0] = (char*)m_malloc(len);
- snprintf(argv[0], len, "-%s", baseshell);
- }
-
- if (chansess->cmd != NULL) {
- argv[1] = "-c";
- argv[2] = chansess->cmd;
- argv[3] = NULL;
- } else {
- /* construct a shell of the form "-bash" etc */
- argv[1] = NULL;
- }
-
- execv(usershell, argv);
+ usershell = m_strdup(get_user_shell());
+ run_shell_command(chansess->cmd, ses.maxfd, usershell);
/* only reached on error */
dropbear_exit("child failed");
diff --git a/svr-main.c b/svr-main.c
index f7ce221..8d57084 100644
--- a/svr-main.c
+++ b/svr-main.c
@@ -266,7 +266,11 @@ void main_noinetd() {
goto out;
}
+#ifdef DEBUG_NOFORK
+ fork_ret = 0;
+#else
fork_ret = fork();
+#endif
if (fork_ret < 0) {
dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno));
goto out;
@@ -292,9 +296,11 @@ void main_noinetd() {
addrstring = getaddrstring(&remoteaddr, 1);
dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+#ifndef DEBUG_NOFORK
if (setsid() < 0) {
dropbear_exit("setsid: %s", strerror(errno));
}
+#endif
/* make sure we close sockets */
for (i = 0; i < listensockcount; i++) {
diff --git a/svr-runopts.c b/svr-runopts.c
index 83c75c3..c8b6585 100644
--- a/svr-runopts.c
+++ b/svr-runopts.c
@@ -284,16 +284,13 @@ void svr_getopts(int argc, char ** argv) {
if (recv_window_arg) {
opts.recv_window = atol(recv_window_arg);
- if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW)
- {
+ if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) {
dropbear_exit("Bad recv window '%s'", recv_window_arg);
}
}
if (keepalive_arg) {
- opts.keepalive_secs = strtoul(keepalive_arg, NULL, 10);
- if (opts.keepalive_secs == 0 && errno == EINVAL)
- {
+ if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
dropbear_exit("Bad keepalive '%s'", keepalive_arg);
}
}
diff --git a/svr-session.c b/svr-session.c
index 18fab6b..eaccfe5 100644
--- a/svr-session.c
+++ b/svr-session.c
@@ -80,7 +80,7 @@ void svr_session(int sock, int childpipe,
reseedrandom();
crypto_init();
- common_session_init(sock, remotehost);
+ common_session_init(sock, sock, remotehost);
/* Initialise server specific parts of the session */
svr_ses.childpipe = childpipe;
@@ -130,12 +130,12 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
/* user has authenticated */
snprintf(fmtbuf, sizeof(fmtbuf),
"exit after auth (%s): %s",
- ses.authstate.printableuser, format);
- } else if (ses.authstate.printableuser) {
+ ses.authstate.pw_name, format);
+ } else if (ses.authstate.pw_name) {
/* we have a potential user */
snprintf(fmtbuf, sizeof(fmtbuf),
"exit before auth (user '%s', %d fails): %s",
- ses.authstate.printableuser, ses.authstate.failcount, format);
+ ses.authstate.pw_name, ses.authstate.failcount, format);
} else {
/* before userauth */
snprintf(fmtbuf, sizeof(fmtbuf),
@@ -144,6 +144,9 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
_dropbear_log(LOG_INFO, fmtbuf, param);
+ /* free potential public key options */
+ svr_pubkey_options_cleanup();
+
/* must be after we've done with username etc */
common_session_cleanup();
@@ -183,7 +186,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param) {
localtime(&timesec)) == 0)
{
/* upon failure, just print the epoch-seconds time. */
- snprintf(datestr, sizeof(datestr), "%d", timesec);
+ snprintf(datestr, sizeof(datestr), "%d", (int)timesec);
}
fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf);
}
@@ -192,8 +195,10 @@ void svr_dropbear_log(int priority, const char* format, va_list param) {
/* called when the remote side closes the connection */
static void svr_remoteclosed() {
- close(ses.sock);
- ses.sock = -1;
+ m_close(ses.sock_in);
+ m_close(ses.sock_out);
+ ses.sock_in = -1;
+ ses.sock_out = -1;
dropbear_close("Exited normally");
}
diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c
index d4dca6b..a55361b 100644
--- a/svr-tcpfwd.c
+++ b/svr-tcpfwd.c
@@ -32,6 +32,7 @@
#include "packet.h"
#include "listener.h"
#include "runopts.h"
+#include "auth.h"
#ifdef ENABLE_SVR_REMOTETCPFWD
@@ -72,7 +73,7 @@ void recv_msg_global_request_remotetcp() {
TRACE(("enter recv_msg_global_request_remotetcp"))
- if (svr_opts.noremotetcp) {
+ if (svr_opts.noremotetcp || !svr_pubkey_allows_tcpfwd()) {
TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
goto out;
}
@@ -236,7 +237,7 @@ static int newtcpdirect(struct Channel * channel) {
int len;
int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
- if (svr_opts.nolocaltcp) {
+ if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
goto out;
}
diff --git a/svr-x11fwd.c b/svr-x11fwd.c
index cbc8a79..7618e3b 100644
--- a/svr-x11fwd.c
+++ b/svr-x11fwd.c
@@ -33,6 +33,7 @@
#include "channel.h"
#include "packet.h"
#include "buffer.h"
+#include "auth.h"
#define X11BASEPORT 6000
#define X11BINDBASE 6010
@@ -47,6 +48,10 @@ int x11req(struct ChanSess * chansess) {
int fd;
+ if (!svr_pubkey_allows_x11fwd()) {
+ return DROPBEAR_FAILURE;
+ }
+
/* we already have an x11 connection */
if (chansess->x11listener != NULL) {
return DROPBEAR_FAILURE;
diff --git a/sysoptions.h b/sysoptions.h
new file mode 100644
index 0000000..6b17151
--- /dev/null
+++ b/sysoptions.h
@@ -0,0 +1,205 @@
+/*******************************************************************
+ * You shouldn't edit this file unless you know you need to.
+ * This file is only included from options.h
+ *******************************************************************/
+
+#ifndef DROPBEAR_VERSION
+#define DROPBEAR_VERSION "0.51"
+#endif
+
+#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
+#define PROGNAME "dropbear"
+
+/* Spec recommends after one hour or 1 gigabyte of data. One hour
+ * is a bit too verbose, so we try 8 hours */
+#ifndef KEX_REKEY_TIMEOUT
+#define KEX_REKEY_TIMEOUT (3600 * 8)
+#endif
+#ifndef KEX_REKEY_DATA
+#define KEX_REKEY_DATA (1<<30) /* 2^30 == 1GB, this value must be < INT_MAX */
+#endif
+/* Close connections to clients which haven't authorised after AUTH_TIMEOUT */
+#ifndef AUTH_TIMEOUT
+#define AUTH_TIMEOUT 300 /* we choose 5 minutes */
+#endif
+
+/* Minimum key sizes for DSS and RSA */
+#ifndef MIN_DSS_KEYLEN
+#define MIN_DSS_KEYLEN 512
+#endif
+#ifndef MIN_RSA_KEYLEN
+#define MIN_RSA_KEYLEN 512
+#endif
+
+#define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */
+#define MAX_BANNER_LINES 20 /* How many lines the client will display */
+
+/* the number of NAME=VALUE pairs to malloc for environ, if we don't have
+ * the clearenv() function */
+#define ENV_SIZE 100
+
+#define MAX_CMD_LEN 1024 /* max length of a command */
+#define MAX_TERM_LEN 200 /* max length of TERM name */
+
+#define MAX_HOST_LEN 254 /* max hostname len for tcp fwding */
+#define MAX_IP_LEN 15 /* strlen("255.255.255.255") == 15 */
+
+#define DROPBEAR_MAX_PORTS 10 /* max number of ports which can be specified,
+ ipv4 and ipv6 don't count twice */
+
+/* Each port might have at least a v4 and a v6 address */
+#define MAX_LISTEN_ADDR (DROPBEAR_MAX_PORTS*3)
+
+#define _PATH_TTY "/dev/tty"
+
+#define _PATH_CP "/bin/cp"
+
+/* success/failure defines */
+#define DROPBEAR_SUCCESS 0
+#define DROPBEAR_FAILURE -1
+
+/* various algorithm identifiers */
+#define DROPBEAR_KEX_DH_GROUP1 0
+
+#define DROPBEAR_SIGNKEY_ANY 0
+#define DROPBEAR_SIGNKEY_RSA 1
+#define DROPBEAR_SIGNKEY_DSS 2
+#define DROPBEAR_SIGNKEY_NONE 3
+
+#define DROPBEAR_COMP_NONE 0
+#define DROPBEAR_COMP_ZLIB 1
+
+/* Required for pubkey auth */
+#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
+#define DROPBEAR_SIGNKEY_VERIFY
+#endif
+
+/* SHA1 is 20 bytes == 160 bits */
+#define SHA1_HASH_SIZE 20
+/* SHA512 is 64 bytes == 512 bits */
+#define SHA512_HASH_SIZE 64
+/* MD5 is 16 bytes = 128 bits */
+#define MD5_HASH_SIZE 16
+
+/* largest of MD5 and SHA1 */
+#define MAX_MAC_LEN SHA1_HASH_SIZE
+
+
+#define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
+#define MAX_IV_LEN 20 /* must be same as max blocksize,
+ and >= SHA1_HASH_SIZE */
+#define MAX_MAC_KEY 20
+
+#define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't
+ explicitly specified for all protocols (just
+ for algos) but seems valid */
+
+#define MAX_PROPOSED_ALGO 20
+
+/* size/count limits */
+#define MIN_PACKET_LEN 16
+
+#define RECV_MAX_PACKET_LEN (MAX(35000, ((RECV_MAX_PAYLOAD_LEN)+100)))
+
+/* for channel code */
+#define TRANS_MAX_WINDOW 500000000 /* 500MB is sufficient, stopping overflow */
+#define TRANS_MAX_WIN_INCR 500000000 /* overflow prevention */
+
+#define RECV_WINDOWEXTEND (opts.recv_window / 3) /* We send a "window extend" every
+ RECV_WINDOWEXTEND bytes */
+#define MAX_RECV_WINDOW (1024*1024) /* 1 MB should be enough */
+
+#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
+ connection, so can't be _too_ small */
+
+#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
+ is the max length for a password etc */
+
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PUBKEY_SIZE 1700
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PRIVKEY_SIZE 1700
+
+/* The maximum size of the bignum portion of the kexhash buffer */
+/* Sect. 8 of the transport draft, K_S + e + f + K */
+#define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130)
+
+#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
+ in a few years time.... */
+
+#define DROPBEAR_MAX_CLI_PASS 1024
+
+#define DROPBEAR_MAX_CLI_INTERACT_PROMPTS 80 /* The number of prompts we'll
+ accept for keyb-interactive
+ auth */
+
+#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
+#define DROPBEAR_AES_CBC
+#endif
+
+#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
+#define DROPBEAR_TWOFISH_CBC
+#endif
+
+#ifndef ENABLE_X11FWD
+#define DISABLE_X11FWD
+#endif
+
+#ifndef ENABLE_AGENTFWD
+#define DISABLE_AGENTFWD
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+#define ENABLE_CLI_ANYTCPFWD
+#endif
+
+#if defined(ENABLE_CLI_LOCALTCPFWD) || defined(ENABLE_SVR_REMOTETCPFWD)
+#define DROPBEAR_TCP_ACCEPT
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
+ defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
+ defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
+#define USING_LISTENERS
+#endif
+
+#if defined(ENABLE_CLI_NETCAT) && defined(ENABLE_CLI_PROXYCMD)
+#define ENABLE_CLI_MULTIHOP
+#endif
+
+#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
+#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
+#endif
+
+#if defined(ENABLE_SVR_PASSWORD_AUTH) && defined(ENABLE_SVR_PAM_AUTH)
+#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
+#endif
+
+#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET)
+#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once"
+#endif
+
+#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET)
+#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h"
+#endif
+
+/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
+ * code, if we're just compiling as client or server */
+#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)
+
+#define IS_DROPBEAR_SERVER (ses.isserver == 1)
+#define IS_DROPBEAR_CLIENT (ses.isserver == 0)
+
+#elif defined(DROPBEAR_SERVER)
+
+#define IS_DROPBEAR_SERVER 1
+#define IS_DROPBEAR_CLIENT 0
+
+#elif defined(DROPBEAR_CLIENT)
+
+#define IS_DROPBEAR_SERVER 0
+#define IS_DROPBEAR_CLIENT 1
+
+#else
+#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
+#endif
diff --git a/tcpfwd.h b/tcpfwd.h
index 28af029..f50515c 100644
--- a/tcpfwd.h
+++ b/tcpfwd.h
@@ -55,6 +55,7 @@ struct TCPFwdList {
/* Server */
void recv_msg_global_request_remotetcp();
+
extern const struct ChanType svr_chan_tcpdirect;
/* Client */