summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2013-04-14 22:49:19 +0800
committerMatt Johnston <matt@ucc.asn.au>2013-04-14 22:49:19 +0800
commit3bdfae61a23e597acd97db189feea74fb27e028a (patch)
tree4784b179e19f4db7b40dca3cdd7ab2741476c608
parent4404126501ba18184a8e04f815c21e7a9184414a (diff)
parentadeb372a66643000aee8445f8b2f9854fdfb4402 (diff)
merge
-rw-r--r--.hgsigs1
-rw-r--r--.hgtags1
-rw-r--r--Makefile.in6
-rw-r--r--algo.h16
-rw-r--r--auth.h2
-rw-r--r--bignum.h3
-rw-r--r--buffer.c4
-rw-r--r--channel.h4
-rw-r--r--cli-agentfwd.c3
-rw-r--r--cli-algo.c99
-rw-r--r--cli-auth.c57
-rw-r--r--cli-chansession.c67
-rw-r--r--cli-kex.c24
-rw-r--r--cli-main.c3
-rw-r--r--cli-service.c85
-rw-r--r--cli-session.c92
-rw-r--r--common-algo.c120
-rw-r--r--common-channel.c25
-rw-r--r--common-kex.c175
-rw-r--r--common-session.c39
-rw-r--r--configure.ac (renamed from configure.in)4
-rw-r--r--dbutil.c32
-rw-r--r--dbutil.h9
-rw-r--r--debug.h4
-rw-r--r--dss.c6
-rw-r--r--includes.h4
-rw-r--r--kex.h5
-rw-r--r--options.h4
-rw-r--r--packet.c115
-rw-r--r--process-packet.c6
-rw-r--r--queue.c2
-rw-r--r--queue.h2
-rw-r--r--random.c3
-rw-r--r--rsa.c6
-rw-r--r--scp.c2
-rw-r--r--scpmisc.c1
-rw-r--r--service.h2
-rw-r--r--session.h27
-rw-r--r--signkey.c18
-rw-r--r--svr-algo.c100
-rw-r--r--svr-main.c2
-rw-r--r--svr-session.c16
-rw-r--r--svr-x11fwd.c2
-rw-r--r--sysoptions.h19
-rw-r--r--termcodes.c8
45 files changed, 663 insertions, 562 deletions
diff --git a/.hgsigs b/.hgsigs
index 506e078..5dcb50e 100644
--- a/.hgsigs
+++ b/.hgsigs
@@ -1,3 +1,4 @@
aa2f51a6b81d33de5e9898a7f27c792a173d9b26 0 iD8DBQBOuADmjPn4sExkf7wRAv/fAJ9FJFvjDoF+wd1ipDx1wkzdeBQNqgCgykUrSbXv76FBbxKntVbk9oS3GjI=
3f12086c2ef2b9ffe36a822fdb3ff647fcec1831 0 iD8DBQBOuSlQjPn4sExkf7wRAvkbAKCgE1e8xEMQ16CGeoywhIQ0QR4eNgCfZdYYlzjb/+521Uvh5/7FRYEmrho=
85f835f2fe0ac2c503c50a414de127222fb0a57c 0 iD8DBQBPRkMUjPn4sExkf7wRAvM4AJ9mw2OAkyjhSbamM1MizlEJUX18HACgoFKQkYf6BnYxN34Nv2HhM0cmzUc=
+9b80981212fe6c01b7c16b3ca7c4e66af56f12f1 0 iEYEABECAAYFAlFLKKcACgkQjPn4sExkf7xK7wCfcioCmJPsysSbQO6+4qZMVe0mmLwAn2/o+wRf4MrUXlohrr7aXEF9vdSB
diff --git a/.hgtags b/.hgtags
index 761d71f..71a20fe 100644
--- a/.hgtags
+++ b/.hgtags
@@ -35,3 +35,4 @@ d354464b2aa6f6ba0bf44d43bcae5aa798435393 DROPBEAR_2012.55
0000000000000000000000000000000000000000 t:ltc-0.95-orig
d7da3b1e15401eb234ec866d5eac992fc4cd5878 t:ltc-0.95-db-merge1
0000000000000000000000000000000000000000 t:ltc-0.95-db-merge1
+1b8b2b9d6e94bc3cc5e61b620476ea36cc466e1b DROPBEAR_2013.56
diff --git a/Makefile.in b/Makefile.in
index cec35f1..4bdd845 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -28,13 +28,13 @@ COMMONOBJS=dbutil.o buffer.o \
queue.o \
atomicio.o compat.o fake-rfc2553.o
-SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
+SVROBJS=svr-kex.o svr-auth.o sshpty.o \
svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \
svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
svr-tcpfwd.o svr-authpam.o
-CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
- cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
+CLIOBJS=cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
+ cli-session.o cli-runopts.o cli-chansession.o \
cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o \
cli-agentfwd.o list.o
diff --git a/algo.h b/algo.h
index ad57037..ad40c0d 100644
--- a/algo.h
+++ b/algo.h
@@ -83,10 +83,18 @@ void crypto_init();
int have_algo(char* algo, size_t algolen, algo_type algos[]);
void buf_put_algolist(buffer * buf, algo_type localalgos[]);
-algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
- int *goodguess);
-algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
- int *goodguess);
+enum kexguess2_used {
+ KEXGUESS2_LOOK,
+ KEXGUESS2_NO,
+ KEXGUESS2_YES,
+};
+
+#define KEXGUESS2_ALGO_NAME "kexguess2@matt.ucc.asn.au"
+#define KEXGUESS2_ALGO_ID 99
+
+
+algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
+ enum kexguess2_used *kexguess2, int *goodguess);
#ifdef ENABLE_USER_ALGO_LIST
int check_user_algos(const char* user_algo_list, algo_type * algos,
diff --git a/auth.h b/auth.h
index 0fd9c73..df6634e 100644
--- a/auth.h
+++ b/auth.h
@@ -67,7 +67,7 @@ void recv_msg_userauth_pk_ok();
void recv_msg_userauth_info_request();
void cli_get_user();
void cli_auth_getmethods();
-void cli_auth_try();
+int cli_auth_try();
void recv_msg_userauth_banner();
void cli_pubkeyfail();
void cli_auth_password();
diff --git a/bignum.h b/bignum.h
index 042f811..ba98db1 100644
--- a/bignum.h
+++ b/bignum.h
@@ -26,9 +26,10 @@
#define _BIGNUM_H_
#include "includes.h"
+#include "dbutil.h"
void m_mp_init(mp_int *mp);
-void m_mp_init_multi(mp_int *mp, ...);
+void m_mp_init_multi(mp_int *mp, ...) ATTRIB_SENTINEL;
void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len);
void sha1_process_mp(hash_state *hs, mp_int *mp);
diff --git a/buffer.c b/buffer.c
index 13fa1ce..facee24 100644
--- a/buffer.c
+++ b/buffer.c
@@ -282,7 +282,7 @@ void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) {
void buf_putmpint(buffer* buf, mp_int * mp) {
unsigned int len, pad = 0;
- TRACE(("enter buf_putmpint"))
+ TRACE2(("enter buf_putmpint"))
dropbear_assert(mp != NULL);
@@ -318,7 +318,7 @@ void buf_putmpint(buffer* buf, mp_int * mp) {
buf_incrwritepos(buf, len-pad);
}
- TRACE(("leave buf_putmpint"))
+ TRACE2(("leave buf_putmpint"))
}
/* Retrieve an mp_int from the buffer.
diff --git a/channel.h b/channel.h
index 950d4b3..7146697 100644
--- a/channel.h
+++ b/channel.h
@@ -83,8 +83,10 @@ struct Channel {
int flushing;
- const struct ChanType* type;
+ /* Used by client chansession to handle ~ escaping, NULL ignored otherwise */
+ void (*read_mangler)(struct Channel*, unsigned char* bytes, int *len);
+ const struct ChanType* type;
};
struct ChanType {
diff --git a/cli-agentfwd.c b/cli-agentfwd.c
index a821305..c661455 100644
--- a/cli-agentfwd.c
+++ b/cli-agentfwd.c
@@ -156,8 +156,6 @@ static buffer * agent_request(unsigned char type, buffer *data) {
goto out;
}
- TRACE(("agent_request readlen is %d", readlen))
-
buf_resize(inbuf, readlen);
buf_setpos(inbuf, 0);
ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen);
@@ -167,7 +165,6 @@ static buffer * agent_request(unsigned char type, buffer *data) {
}
buf_incrwritepos(inbuf, readlen);
buf_setpos(inbuf, 0);
- TRACE(("agent_request success, length %d", readlen))
out:
if (payload)
diff --git a/cli-algo.c b/cli-algo.c
deleted file mode 100644
index 09da41a..0000000
--- a/cli-algo.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Dropbear - a SSH2 server
- * SSH client implementation
- *
- * Copyright (c) 2002,2003 Matt Johnston
- * Copyright (c) 2004 by Mihnea Stoenescu
- * 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. */
-
-#include "algo.h"
-#include "dbutil.h"
-
-
-/*
- * The chosen [encryption | MAC | compression] algorithm to each
- * direction MUST be the first algorithm on the client's list
- * that is also on the server's list.
- */
-algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
- int *goodguess) {
-
- unsigned char * algolist = NULL;
- unsigned char * remotealgos[MAX_PROPOSED_ALGO];
- unsigned int len;
- unsigned int count, i, j;
- algo_type * ret = NULL;
-
- *goodguess = 0;
-
- /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
- algolist = buf_getstring(buf, &len);
- TRACE(("cli_buf_match_algo: %s", algolist))
- if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
- goto out; /* just a sanity check, no other use */
- }
-
- /* remotealgos will contain a list of the strings parsed out */
- /* We will have at least one string (even if it's just "") */
- remotealgos[0] = algolist;
- count = 1;
- /* Iterate through, replacing ','s with NULs, to split it into
- * words. */
- for (i = 0; i < len; i++) {
- if (algolist[i] == '\0') {
- /* someone is trying something strange */
- goto out;
- }
- if (algolist[i] == ',') {
- algolist[i] = '\0';
- remotealgos[count] = &algolist[i+1];
- count++;
- }
- if (count >= MAX_PROPOSED_ALGO) {
- break;
- }
- }
-
- /* iterate and find the first match */
-
- for (j = 0; localalgos[j].name != NULL; j++) {
- if (localalgos[j].usable) {
- len = strlen(localalgos[j].name);
- for (i = 0; i < count; i++) {
- if (len == strlen(remotealgos[i])
- && strncmp(localalgos[j].name,
- remotealgos[i], len) == 0) {
- if (i == 0 && j == 0) {
- /* was a good guess */
- *goodguess = 1;
- }
- ret = &localalgos[j];
- goto out;
- }
- }
- }
- }
-
-out:
- m_free(algolist);
- return ret;
-}
-
diff --git a/cli-auth.c b/cli-auth.c
index 321cbf3..efa9e9b 100644
--- a/cli-auth.c
+++ b/cli-auth.c
@@ -40,11 +40,18 @@ void cli_authinitialise() {
/* Send a "none" auth request to get available methods */
void cli_auth_getmethods() {
-
TRACE(("enter cli_auth_getmethods"))
-
+#ifdef CLI_IMMEDIATE_AUTH
+ ses.authstate.authtypes = AUTH_TYPE_PUBKEY;
+ if (getenv(DROPBEAR_PASSWORD_ENV)) {
+ ses.authstate.authtypes |= AUTH_TYPE_PASSWORD | AUTH_TYPE_INTERACT;
+ }
+ if (cli_auth_try() == DROPBEAR_SUCCESS) {
+ TRACE(("skipped initial none auth query"))
+ return;
+ }
+#endif
CHECKCLEARTOWRITE();
-
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
buf_putstring(ses.writepayload, cli_opts.username,
strlen(cli_opts.username));
@@ -54,7 +61,6 @@ void cli_auth_getmethods() {
encrypt_packet();
TRACE(("leave cli_auth_getmethods"))
-
}
void recv_msg_userauth_banner() {
@@ -240,7 +246,7 @@ void recv_msg_userauth_success() {
#endif
}
-void cli_auth_try() {
+int cli_auth_try() {
int finished = 0;
TRACE(("enter cli_auth_try"))
@@ -256,37 +262,40 @@ void cli_auth_try() {
}
#endif
-#ifdef ENABLE_CLI_INTERACT_AUTH
- if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
- fprintf(stderr, "Sorry, I won't let you use interactive auth unencrypted.\n");
- } else if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) {
- if (cli_ses.auth_interact_failed) {
- finished = 0;
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+ if (!finished && (ses.authstate.authtypes & AUTH_TYPE_PASSWORD)) {
+ if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
+ fprintf(stderr, "Sorry, I won't let you use password auth unencrypted.\n");
} else {
- cli_auth_interactive();
- cli_ses.lastauthtype = AUTH_TYPE_INTERACT;
+ cli_auth_password();
finished = 1;
+ cli_ses.lastauthtype = AUTH_TYPE_PASSWORD;
}
}
#endif
-#ifdef ENABLE_CLI_PASSWORD_AUTH
- if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
- fprintf(stderr, "Sorry, I won't let you use password auth unencrypted.\n");
- } else if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
- cli_auth_password();
- finished = 1;
- cli_ses.lastauthtype = AUTH_TYPE_PASSWORD;
+#ifdef ENABLE_CLI_INTERACT_AUTH
+ if (!finished && (ses.authstate.authtypes & AUTH_TYPE_INTERACT)) {
+ if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
+ fprintf(stderr, "Sorry, I won't let you use interactive auth unencrypted.\n");
+ } else {
+ if (!cli_ses.auth_interact_failed) {
+ cli_auth_interactive();
+ cli_ses.lastauthtype = AUTH_TYPE_INTERACT;
+ finished = 1;
+ }
+ }
}
#endif
TRACE(("cli_auth_try lastauthtype %d", cli_ses.lastauthtype))
- if (!finished) {
- dropbear_exit("No auth methods could be used.");
+ if (finished) {
+ TRACE(("leave cli_auth_try success"))
+ return DROPBEAR_SUCCESS;
}
-
- TRACE(("leave cli_auth_try"))
+ TRACE(("leave cli_auth_try failure"))
+ return DROPBEAR_FAILURE;
}
/* A helper for getpass() that exits if the user cancels. The returned
diff --git a/cli-chansession.c b/cli-chansession.c
index 126c32f..17e0d53 100644
--- a/cli-chansession.c
+++ b/cli-chansession.c
@@ -38,9 +38,10 @@
static void cli_closechansess(struct Channel *channel);
static int cli_initchansess(struct Channel *channel);
static void cli_chansessreq(struct Channel *channel);
-
static void send_chansess_pty_req(struct Channel *channel);
static void send_chansess_shell_req(struct Channel *channel);
+static void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len);
+
static void cli_tty_setup();
@@ -81,14 +82,12 @@ out:
/* If the main session goes, we close it up */
static void cli_closechansess(struct Channel *UNUSED(channel)) {
+ cli_tty_cleanup(); /* Restore tty modes etc */
/* This channel hasn't gone yet, so we have > 1 */
if (ses.chancount > 1) {
dropbear_log(LOG_INFO, "Waiting for other channels to close...");
}
-
- cli_tty_cleanup(); /* Restore tty modes etc */
-
}
void cli_start_send_channel_request(struct Channel *channel,
@@ -374,7 +373,9 @@ static int cli_initchansess(struct Channel *channel) {
if (cli_opts.wantpty) {
cli_tty_setup();
- }
+ channel->read_mangler = cli_escape_handler;
+ cli_ses.last_char = '\r';
+ }
return 0; /* Success */
}
@@ -429,3 +430,59 @@ void cli_send_chansess_request() {
TRACE(("leave cli_send_chansess_request"))
}
+
+// returns 1 if the character should be consumed, 0 to pass through
+static int
+do_escape(unsigned char c) {
+ switch (c) {
+ case '.':
+ dropbear_exit("Terminated");
+ return 1;
+ break;
+ case 0x1a:
+ // ctrl-z
+ cli_tty_cleanup();
+ kill(getpid(), SIGTSTP);
+ // after continuation
+ cli_tty_setup();
+ cli_ses.winchange = 1;
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+static
+void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len) {
+ char c;
+ int skip_char = 0;
+
+ // only handle escape characters if they are read one at a time. simplifies
+ // the code and avoids nasty people putting ~. at the start of a line to paste
+ if (*len != 1) {
+ cli_ses.last_char = 0x0;
+ return;
+ }
+
+ c = buf[0];
+
+ if (cli_ses.last_char == DROPBEAR_ESCAPE_CHAR) {
+ skip_char = do_escape(c);
+ cli_ses.last_char = 0x0;
+ } else {
+ if (c == DROPBEAR_ESCAPE_CHAR) {
+ if (cli_ses.last_char == '\r') {
+ cli_ses.last_char = DROPBEAR_ESCAPE_CHAR;
+ skip_char = 1;
+ } else {
+ cli_ses.last_char = 0x0;
+ }
+ } else {
+ cli_ses.last_char = c;
+ }
+ }
+
+ if (skip_char) {
+ *len = 0;
+ }
+}
diff --git a/cli-kex.c b/cli-kex.c
index c47faae..3859109 100644
--- a/cli-kex.c
+++ b/cli-kex.c
@@ -42,18 +42,27 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen);
#define MAX_KNOWNHOSTS_LINE 4500
void send_msg_kexdh_init() {
+ TRACE(("send_msg_kexdh_init()"))
+ if ((cli_ses.dh_e && cli_ses.dh_x
+ && cli_ses.dh_val_algo == ses.newkeys->algo_kex)) {
+ TRACE(("reusing existing dh_e from first_kex_packet_follows"))
+ } else {
+ if (!cli_ses.dh_e || !cli_ses.dh_e) {
+ cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int));
+ cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int));
+ m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
+ }
- cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int));
- cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int));
- m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
-
- gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
+ gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
+ cli_ses.dh_val_algo = ses.newkeys->algo_kex;
+ }
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT);
buf_putmpint(ses.writepayload, cli_ses.dh_e);
encrypt_packet();
- ses.requirenext = SSH_MSG_KEXDH_REPLY;
+ // XXX fixme
+ //ses.requirenext = SSH_MSG_KEXDH_REPLY;
}
/* Handle a diffie-hellman key exchange reply. */
@@ -98,6 +107,7 @@ void recv_msg_kexdh_reply() {
mp_clear_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
m_free(cli_ses.dh_e);
m_free(cli_ses.dh_x);
+ cli_ses.dh_val_algo = DROPBEAR_KEX_NONE;
if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE)
!= DROPBEAR_SUCCESS) {
@@ -251,7 +261,6 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
/* Compare hostnames */
if (strncmp(cli_opts.remotehost, buf_getptr(line, hostlen),
hostlen) != 0) {
- TRACE(("hosts don't match"))
continue;
}
@@ -314,7 +323,6 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
buf_putbytes(line, algoname, algolen);
buf_putbyte(line, ' ');
len = line->size - line->pos;
- TRACE(("keybloblen %d, len %d", keybloblen, len))
/* The only failure with base64 is buffer_overflow, but buf_getwriteptr
* will die horribly in the case anyway */
base64_encode(keyblob, keybloblen, buf_getwriteptr(line, len), &len);
diff --git a/cli-main.c b/cli-main.c
index 5f72969..1a8b02e 100644
--- a/cli-main.c
+++ b/cli-main.c
@@ -98,8 +98,7 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
}
/* Do the cleanup first, since then the terminal will be reset */
- cli_session_cleanup();
- common_session_cleanup();
+ session_cleanup();
_dropbear_log(LOG_INFO, fmtbuf, param);
diff --git a/cli-service.c b/cli-service.c
deleted file mode 100644
index f763103..0000000
--- a/cli-service.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Dropbear SSH
- *
- * Copyright (c) 2002,2003 Matt Johnston
- * Copyright (c) 2004 by Mihnea Stoenescu
- * 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. */
-
-#include "includes.h"
-#include "service.h"
-#include "dbutil.h"
-#include "packet.h"
-#include "buffer.h"
-#include "session.h"
-#include "ssh.h"
-
-void send_msg_service_request(char* servicename) {
-
- TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
-
- CHECKCLEARTOWRITE();
-
- buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST);
- buf_putstring(ses.writepayload, servicename, strlen(servicename));
-
- encrypt_packet();
- TRACE(("leave send_msg_service_request"))
-}
-
-/* This just sets up the state variables right for the main client session loop
- * to deal with */
-void recv_msg_service_accept() {
-
- unsigned char* servicename;
- unsigned int len;
-
- TRACE(("enter recv_msg_service_accept"))
-
- servicename = buf_getstring(ses.payload, &len);
-
- /* ssh-userauth */
- if (cli_ses.state == SERVICE_AUTH_REQ_SENT
- && len == SSH_SERVICE_USERAUTH_LEN
- && strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) {
-
- cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD;
- m_free(servicename);
- TRACE(("leave recv_msg_service_accept: done ssh-userauth"))
- return;
- }
-
- /* ssh-connection */
- if (cli_ses.state == SERVICE_CONN_REQ_SENT
- && len == SSH_SERVICE_CONNECTION_LEN
- && strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) {
-
- if (ses.authstate.authdone != 1) {
- dropbear_exit("Request for connection before auth");
- }
-
- cli_ses.state = SERVICE_CONN_ACCEPT_RCVD;
- m_free(servicename);
- TRACE(("leave recv_msg_service_accept: done ssh-connection"))
- return;
- }
-
- dropbear_exit("Unrecognised service accept");
-}
diff --git a/cli-session.c b/cli-session.c
index e58fdbd..401c9e2 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -41,6 +41,8 @@ static void cli_remoteclosed();
static void cli_sessionloop();
static void cli_session_init();
static void cli_finished();
+static void recv_msg_service_accept(void);
+static void cli_session_cleanup(void);
struct clientsession cli_ses; /* GLOBAL */
@@ -99,7 +101,7 @@ void cli_session(int sock_in, int sock_out) {
sessinitdone = 1;
/* Exchange identification */
- session_identification();
+ send_session_identification();
send_msg_kexinit();
@@ -109,6 +111,12 @@ void cli_session(int sock_in, int sock_out) {
}
+#ifdef USE_KEX_FIRST_FOLLOWS
+static void cli_send_kex_first_guess() {
+ send_msg_kexdh_init();
+}
+#endif
+
static void cli_session_init() {
cli_ses.state = STATE_NOTHING;
@@ -142,37 +150,61 @@ static void cli_session_init() {
/* For printing "remote host closed" for the user */
ses.remoteclosed = cli_remoteclosed;
- ses.buf_match_algo = cli_buf_match_algo;
+
+ ses.extra_session_cleanup = cli_session_cleanup;
/* packet handlers */
ses.packettypes = cli_packettypes;
ses.isserver = 0;
+
+#ifdef USE_KEX_FIRST_FOLLOWS
+ ses.send_kex_first_guess = cli_send_kex_first_guess;
+#endif
+
+}
+
+static void send_msg_service_request(char* servicename) {
+
+ TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
+
+ CHECKCLEARTOWRITE();
+
+ buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST);
+ buf_putstring(ses.writepayload, servicename, strlen(servicename));
+
+ encrypt_packet();
+ TRACE(("leave send_msg_service_request"))
+}
+
+static void recv_msg_service_accept(void) {
+ // do nothing, if it failed then the server MUST have disconnected
}
/* This function drives the progress of the session - it initiates KEX,
* service, userauth and channel requests */
static void cli_sessionloop() {
- TRACE(("enter cli_sessionloop"))
+ TRACE2(("enter cli_sessionloop"))
- if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
- cli_ses.kex_state = KEXINIT_RCVD;
+ if (ses.lastpacket == 0) {
+ TRACE2(("exit cli_sessionloop: no real packets yet"))
+ return;
}
- if (cli_ses.kex_state == KEXINIT_RCVD) {
-
+ if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
/* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
* negotiation would have failed. */
- send_msg_kexdh_init();
- cli_ses.kex_state = KEXDH_INIT_SENT;
+ if (!ses.kexstate.our_first_follows_matches) {
+ send_msg_kexdh_init();
+ }
+ cli_ses.kex_state = KEXDH_INIT_SENT;
TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"))
return;
}
/* A KEX has finished, so we should go back to our KEX_NOTHING state */
- if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.recvkexinit == 0
- && ses.kexstate.sentkexinit == 0) {
+ if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.sentnewkeys) {
cli_ses.kex_state = KEX_NOTHING;
}
@@ -182,10 +214,10 @@ static void cli_sessionloop() {
return;
}
- /* We should exit if we haven't donefirstkex: we shouldn't reach here
- * in normal operation */
if (ses.kexstate.donefirstkex == 0) {
- TRACE(("XXX XXX might be bad! leave cli_sessionloop: haven't donefirstkex"))
+ /* We might reach here if we have partial packet reads or have
+ * received SSG_MSG_IGNORE etc. Just skip it */
+ TRACE2(("donefirstkex false\n"))
return;
}
@@ -195,19 +227,15 @@ static void cli_sessionloop() {
/* We've got the transport layer sorted, we now need to request
* userauth */
send_msg_service_request(SSH_SERVICE_USERAUTH);
- cli_ses.state = SERVICE_AUTH_REQ_SENT;
- TRACE(("leave cli_sessionloop: sent userauth service req"))
- return;
-
- /* userauth code */
- case SERVICE_AUTH_ACCEPT_RCVD:
cli_auth_getmethods();
cli_ses.state = USERAUTH_REQ_SENT;
TRACE(("leave cli_sessionloop: sent userauth methods req"))
return;
case USERAUTH_FAIL_RCVD:
- cli_auth_try();
+ if (cli_auth_try() == DROPBEAR_FAILURE) {
+ dropbear_exit("No auth methods could be used.");
+ }
cli_ses.state = USERAUTH_REQ_SENT;
TRACE(("leave cli_sessionloop: cli_auth_try"))
return;
@@ -238,13 +266,6 @@ static void cli_sessionloop() {
}
}
-#ifdef ENABLE_CLI_LOCALTCPFWD
- setup_localtcp();
-#endif
-#ifdef ENABLE_CLI_REMOTETCPFWD
- setup_remotetcp();
-#endif
-
#ifdef ENABLE_CLI_NETCAT
if (cli_opts.netcat_host) {
cli_send_netcat_request();
@@ -253,6 +274,14 @@ static void cli_sessionloop() {
if (!cli_opts.no_cmd) {
cli_send_chansess_request();
}
+
+#ifdef ENABLE_CLI_LOCALTCPFWD
+ setup_localtcp();
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+ setup_remotetcp();
+#endif
+
TRACE(("leave cli_sessionloop: running"))
cli_ses.state = SESSION_RUNNING;
return;
@@ -274,11 +303,11 @@ static void cli_sessionloop() {
break;
}
- TRACE(("leave cli_sessionloop: fell out"))
+ TRACE2(("leave cli_sessionloop: fell out"))
}
-void cli_session_cleanup() {
+static void cli_session_cleanup(void) {
if (!sessinitdone) {
return;
@@ -296,8 +325,7 @@ void cli_session_cleanup() {
static void cli_finished() {
- cli_session_cleanup();
- common_session_cleanup();
+ session_cleanup();
fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
cli_opts.remotehost, cli_opts.remoteport);
exit(cli_ses.retval);
diff --git a/common-algo.c b/common-algo.c
index 4a14651..14c157b 100644
--- a/common-algo.c
+++ b/common-algo.c
@@ -24,6 +24,7 @@
* SOFTWARE. */
#include "algo.h"
+#include "session.h"
#include "dbutil.h"
/* This file (algo.c) organises the ciphers which can be used, and is used to
@@ -215,6 +216,9 @@ algo_type sshhostkey[] = {
algo_type sshkex[] = {
{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL},
{"diffie-hellman-group14-sha1", DROPBEAR_KEX_DH_GROUP14, NULL, 1, NULL},
+#ifdef USE_KEXGUESS2
+ {KEXGUESS2_ALGO_NAME, KEXGUESS2_ALGO_ID, NULL, 1, NULL},
+#endif
{NULL, 0, NULL, 0, NULL}
};
@@ -307,6 +311,122 @@ void buf_put_algolist(buffer * buf, algo_type localalgos[]) {
buf_free(algolist);
}
+/* match the first algorithm in the comma-separated list in buf which is
+ * also in localalgos[], or return NULL on failure.
+ * (*goodguess) is set to 1 if the preferred client/server algos match,
+ * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
+ * guessed correctly */
+algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
+ enum kexguess2_used *kexguess2, int *goodguess)
+{
+
+ unsigned char * algolist = NULL;
+ const unsigned char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO];
+ unsigned int len;
+ unsigned int remotecount, localcount, clicount, servcount, i, j;
+ algo_type * ret = NULL;
+ const unsigned char **clinames, **servnames;
+
+ if (goodguess) {
+ *goodguess = 0;
+ }
+
+ /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
+ algolist = buf_getstring(buf, &len);
+ TRACE(("buf_match_algo: %s", algolist))
+ if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
+ goto out;
+ }
+
+ /* remotenames will contain a list of the strings parsed out */
+ /* We will have at least one string (even if it's just "") */
+ remotenames[0] = algolist;
+ remotecount = 1;
+ for (i = 0; i < len; i++) {
+ if (algolist[i] == '\0') {
+ /* someone is trying something strange */
+ goto out;
+ }
+ if (algolist[i] == ',') {
+ algolist[i] = '\0';
+ remotenames[remotecount] = &algolist[i+1];
+ remotecount++;
+ }
+ if (remotecount >= MAX_PROPOSED_ALGO) {
+ break;
+ }
+ }
+ if (kexguess2 && *kexguess2 == KEXGUESS2_LOOK) {
+ for (i = 0; i < remotecount; i++)
+ {
+ if (strcmp(remotenames[i], KEXGUESS2_ALGO_NAME) == 0) {
+ *kexguess2 = KEXGUESS2_YES;
+ break;
+ }
+ }
+ if (*kexguess2 == KEXGUESS2_LOOK) {
+ *kexguess2 = KEXGUESS2_NO;
+ }
+ }
+
+ for (i = 0; localalgos[i].name != NULL; i++) {
+ if (localalgos[i].usable) {
+ localnames[i] = localalgos[i].name;
+ } else {
+ localnames[i] = NULL;
+ }
+ }
+ localcount = i;
+
+ if (IS_DROPBEAR_SERVER) {
+ clinames = remotenames;
+ clicount = remotecount;
+ servnames = localnames;
+ servcount = localcount;
+ } else {
+ clinames = localnames;
+ clicount = localcount;
+ servnames = remotenames;
+ servcount = remotecount;
+ }
+
+ /* iterate and find the first match */
+ for (i = 0; i < clicount; i++) {
+ for (j = 0; j < servcount; j++) {
+ if (!(servnames[j] && clinames[i])) {
+ // unusable algos are NULL
+ continue;
+ }
+ if (strcmp(servnames[j], clinames[i]) == 0) {
+ /* set if it was a good guess */
+ if (goodguess && kexguess2) {
+ if (*kexguess2 == KEXGUESS2_YES) {
+ if (i == 0) {
+ *goodguess = 1;
+ }
+
+ } else {
+ if (i == 0 && j == 0) {
+ *goodguess = 1;
+ }
+ }
+ }
+ /* set the algo to return */
+ if (IS_DROPBEAR_SERVER) {
+ ret = &localalgos[j];
+ } else {
+ ret = &localalgos[i];
+ }
+ goto out;
+ }
+ }
+ }
+
+out:
+ m_free(algolist);
+ return ret;
+}
+
#ifdef DROPBEAR_NONE_CIPHER
void
diff --git a/common-channel.c b/common-channel.c
index 9328a2e..8b7369c 100644
--- a/common-channel.c
+++ b/common-channel.c
@@ -273,10 +273,10 @@ static unsigned int write_pending(struct Channel * channel) {
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",
+ TRACE2(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d",
channel->writefd, channel->readfd,
channel->errfd, channel->sent_close, channel->recv_close))
- TRACE(("writebuf size %d extrabuf size %d",
+ TRACE2(("writebuf size %d extrabuf size %d",
channel->writebuf ? cbuf_getused(channel->writebuf) : 0,
channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0))
@@ -561,7 +561,11 @@ static void remove_channel(struct Channel * channel) {
TRACE(("CLOSE errfd %d", channel->errfd))
close(channel->errfd);
- channel->typedata = NULL;
+ if (!channel->close_handler_done
+ && channel->type->closehandler) {
+ channel->type->closehandler(channel);
+ channel->close_handler_done = 1;
+ }
ses.channels[channel->index] = NULL;
m_free(channel);
@@ -625,7 +629,7 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) {
* exttype if is extended */
maxlen = MIN(maxlen,
ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0));
- TRACE(("maxlen %d", maxlen))
+ TRACE(("maxlen %zd", maxlen))
if (maxlen == 0) {
TRACE(("leave send_msg_channel_data: no window"))
return;
@@ -643,6 +647,7 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) {
/* read the data */
len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen);
+
if (len <= 0) {
if (len == 0 || errno != EINTR) {
/* This will also get hit in the case of EAGAIN. The only
@@ -650,12 +655,22 @@ static void send_msg_channel_data(struct Channel *channel, int isextended) {
in which case it can be treated the same as EOF */
close_chan_fd(channel, fd, SHUT_RD);
}
- ses.writepayload->len = ses.writepayload->pos = 0;
+ buf_setpos(ses.writepayload, 0);
+ buf_setlen(ses.writepayload, 0);
TRACE(("leave send_msg_channel_data: len %d read err %d or EOF for fd %d",
len, errno, fd))
return;
}
+ if (channel->read_mangler) {
+ channel->read_mangler(channel, buf_getwriteptr(ses.writepayload, len), &len);
+ if (len == 0) {
+ buf_setpos(ses.writepayload, 0);
+ buf_setlen(ses.writepayload, 0);
+ return;
+ }
+ }
+
TRACE(("send_msg_channel_data: len %d fd %d", len, fd))
buf_incrwritepos(ses.writepayload, len);
/* ... real size here */
diff --git a/common-kex.c b/common-kex.c
index 56b206d..530de0b 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -80,9 +80,10 @@ static const unsigned char dh_p_14[DH_P_14_LEN] = {
static const int DH_G_VAL = 2;
static void kexinitialise();
-void gen_new_keys();
+static void gen_new_keys();
#ifndef DISABLE_ZLIB
-static void gen_new_zstreams();
+static void gen_new_zstream_recv();
+static void gen_new_zstream_trans();
#endif
static void read_kex_algos();
/* helper function for gen_new_keys */
@@ -131,8 +132,8 @@ void send_msg_kexinit() {
/* languages_server_to_client */
buf_putstring(ses.writepayload, "", 0);
- /* first_kex_packet_follows - unimplemented for now */
- buf_putbyte(ses.writepayload, 0x00);
+ /* first_kex_packet_follows */
+ buf_putbyte(ses.writepayload, (ses.send_kex_first_guess != NULL));
/* reserved unit32 */
buf_putint(ses.writepayload, 0);
@@ -144,16 +145,56 @@ void send_msg_kexinit() {
encrypt_packet();
ses.dataallowed = 0; /* don't send other packets during kex */
+ ses.kexstate.sentkexinit = 1;
+
+ ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
+
+ if (ses.send_kex_first_guess) {
+ ses.newkeys->algo_kex = sshkex[0].val;
+ ses.newkeys->algo_hostkey = sshhostkey[0].val;
+ ses.send_kex_first_guess();
+ }
+
TRACE(("DATAALLOWED=0"))
TRACE(("-> KEXINIT"))
- ses.kexstate.sentkexinit = 1;
+
}
-/* *** NOTE regarding (send|recv)_msg_newkeys ***
- * Changed by mihnea from the original kex.c to set dataallowed after a
- * completed key exchange, no matter the order in which it was performed.
- * This enables client mode without affecting server functionality.
- */
+static void switch_keys() {
+ TRACE2(("enter switch_keys"))
+ if (!(ses.kexstate.sentkexinit && ses.kexstate.recvkexinit)) {
+ dropbear_exit("Unexpected newkeys message");
+ }
+
+ if (!ses.keys) {
+ ses.keys = m_malloc(sizeof(*ses.newkeys));
+ }
+ if (ses.kexstate.recvnewkeys && ses.newkeys->recv.valid) {
+ TRACE(("switch_keys recv"))
+ gen_new_zstream_recv();
+ ses.keys->recv = ses.newkeys->recv;
+ m_burn(&ses.newkeys->recv, sizeof(ses.newkeys->recv));
+ ses.newkeys->recv.valid = 0;
+ }
+ if (ses.kexstate.sentnewkeys && ses.newkeys->trans.valid) {
+ TRACE(("switch_keys trans"))
+ gen_new_zstream_trans();
+ ses.keys->trans = ses.newkeys->trans;
+ m_burn(&ses.newkeys->trans, sizeof(ses.newkeys->trans));
+ ses.newkeys->trans.valid = 0;
+ }
+ if (ses.kexstate.sentnewkeys && ses.kexstate.recvnewkeys)
+ {
+ TRACE(("switch_keys done"))
+ ses.keys->algo_kex = ses.newkeys->algo_kex;
+ ses.keys->algo_hostkey = ses.newkeys->algo_hostkey;
+ ses.keys->allow_compress = 0;
+ m_free(ses.newkeys);
+ ses.newkeys = NULL;
+ kexinitialise();
+ }
+ TRACE2(("leave switch_keys"))
+}
/* Bring new keys into use after a key exchange, and let the client know*/
void send_msg_newkeys() {
@@ -164,44 +205,25 @@ void send_msg_newkeys() {
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_NEWKEYS);
encrypt_packet();
-
+
/* set up our state */
- if (ses.kexstate.recvnewkeys) {
- TRACE(("while RECVNEWKEYS=1"))
- gen_new_keys();
- kexinitialise(); /* we've finished with this kex */
- TRACE((" -> DATAALLOWED=1"))
- ses.dataallowed = 1; /* we can send other packets again now */
- ses.kexstate.donefirstkex = 1;
- } else {
- ses.kexstate.sentnewkeys = 1;
- TRACE(("SENTNEWKEYS=1"))
- }
+ ses.kexstate.sentnewkeys = 1;
+ ses.kexstate.donefirstkex = 1;
+ ses.dataallowed = 1; /* we can send other packets again now */
+ gen_new_keys();
+ switch_keys();
- TRACE(("-> MSG_NEWKEYS"))
TRACE(("leave send_msg_newkeys"))
}
/* Bring the new keys into use after a key exchange */
void recv_msg_newkeys() {
- TRACE(("<- MSG_NEWKEYS"))
TRACE(("enter recv_msg_newkeys"))
- /* simply check if we've sent SSH_MSG_NEWKEYS, and if so,
- * switch to the new keys */
- if (ses.kexstate.sentnewkeys) {
- TRACE(("while SENTNEWKEYS=1"))
- gen_new_keys();
- kexinitialise(); /* we've finished with this kex */
- TRACE((" -> DATAALLOWED=1"))
- ses.dataallowed = 1; /* we can send other packets again now */
- ses.kexstate.donefirstkex = 1;
- } else {
- TRACE(("RECVNEWKEYS=1"))
- ses.kexstate.recvnewkeys = 1;
- }
+ ses.kexstate.recvnewkeys = 1;
+ switch_keys();
TRACE(("leave recv_msg_newkeys"))
}
@@ -236,11 +258,13 @@ static void kexinitialise() {
ses.kexstate.sentnewkeys = 0;
/* first_packet_follows */
- ses.kexstate.firstfollows = 0;
+ ses.kexstate.them_firstfollows = 0;
ses.kexstate.datatrans = 0;
ses.kexstate.datarecv = 0;
+ ses.kexstate.our_first_follows_matches = 0;
+
ses.kexstate.lastkextime = time(NULL);
}
@@ -281,8 +305,7 @@ static void hashkeys(unsigned char *out, int outlen,
* ses.newkeys is the new set of keys which are generated, these are only
* taken into use after both sides have sent a newkeys message */
-/* Originally from kex.c, generalized for cli/svr mode --mihnea */
-void gen_new_keys() {
+static void gen_new_keys() {
unsigned char C2S_IV[MAX_IV_LEN];
unsigned char C2S_key[MAX_KEY_LEN];
@@ -366,15 +389,9 @@ void gen_new_keys() {
ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name);
}
-#ifndef DISABLE_ZLIB
- gen_new_zstreams();
-#endif
-
- /* Switch over to the new keys */
- m_burn(ses.keys, sizeof(struct key_context));
- m_free(ses.keys);
- ses.keys = ses.newkeys;
- ses.newkeys = NULL;
+ /* Ready to switch over */
+ ses.newkeys->trans.valid = 1;
+ ses.newkeys->recv.valid = 1;
m_burn(C2S_IV, sizeof(C2S_IV));
m_burn(C2S_key, sizeof(C2S_key));
@@ -400,7 +417,7 @@ int is_compress_recv() {
/* Set up new zlib compression streams, close the old ones. Only
* called from gen_new_keys() */
-static void gen_new_zstreams() {
+static void gen_new_zstream_recv() {
/* create new zstreams */
if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB
@@ -415,6 +432,17 @@ static void gen_new_zstreams() {
} else {
ses.newkeys->recv.zstream = NULL;
}
+ /* clean up old keys */
+ if (ses.keys->recv.zstream != NULL) {
+ if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
+ /* Z_DATA_ERROR is ok, just means that stream isn't ended */
+ dropbear_exit("Crypto error");
+ }
+ m_free(ses.keys->recv.zstream);
+ }
+}
+
+static void gen_new_zstream_trans() {
if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB
|| ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
@@ -432,14 +460,6 @@ static void gen_new_zstreams() {
ses.newkeys->trans.zstream = NULL;
}
- /* clean up old keys */
- if (ses.keys->recv.zstream != NULL) {
- if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
- /* Z_DATA_ERROR is ok, just means that stream isn't ended */
- dropbear_exit("Crypto error");
- }
- m_free(ses.keys->recv.zstream);
- }
if (ses.keys->trans.zstream != NULL) {
if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) {
/* Z_DATA_ERROR is ok, just means that stream isn't ended */
@@ -555,7 +575,7 @@ void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
DEF_MP_INT(dh_q);
DEF_MP_INT(dh_g);
- TRACE(("enter send_msg_kexdh_reply"))
+ TRACE(("enter gen_kexdh_vals"))
m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL);
@@ -678,20 +698,27 @@ static void read_kex_algos() {
buf_incrpos(ses.payload, 16); /* start after the cookie */
- ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
+ memset(ses.newkeys, 0x0, sizeof(*ses.newkeys));
+
+#ifdef USE_KEXGUESS2
+ enum kexguess2_used kexguess2 = KEXGUESS2_LOOK;
+#else
+ enum kexguess2_used kexguess2 = KEXGUESS2_NO;
+#endif
/* kex_algorithms */
- algo = ses.buf_match_algo(ses.payload, sshkex, &goodguess);
+ algo = buf_match_algo(ses.payload, sshkex, &kexguess2, &goodguess);
allgood &= goodguess;
- if (algo == NULL) {
+ if (algo == NULL || algo->val == KEXGUESS2_ALGO_ID) {
erralgo = "kex";
goto error;
}
+ TRACE(("kexguess2 %d", kexguess2))
TRACE(("kex algo %s", algo->name))
ses.newkeys->algo_kex = algo->val;
/* server_host_key_algorithms */
- algo = ses.buf_match_algo(ses.payload, sshhostkey, &goodguess);
+ algo = buf_match_algo(ses.payload, sshhostkey, &kexguess2, &goodguess);
allgood &= goodguess;
if (algo == NULL) {
erralgo = "hostkey";
@@ -701,7 +728,7 @@ static void read_kex_algos() {
ses.newkeys->algo_hostkey = algo->val;
/* encryption_algorithms_client_to_server */
- c2s_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
+ c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL);
if (c2s_cipher_algo == NULL) {
erralgo = "enc c->s";
goto error;
@@ -709,7 +736,7 @@ static void read_kex_algos() {
TRACE(("enc c2s is %s", c2s_cipher_algo->name))
/* encryption_algorithms_server_to_client */
- s2c_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
+ s2c_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL);
if (s2c_cipher_algo == NULL) {
erralgo = "enc s->c";
goto error;
@@ -717,7 +744,7 @@ static void read_kex_algos() {
TRACE(("enc s2c is %s", s2c_cipher_algo->name))
/* mac_algorithms_client_to_server */
- c2s_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
+ c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL);
if (c2s_hash_algo == NULL) {
erralgo = "mac c->s";
goto error;
@@ -725,7 +752,7 @@ static void read_kex_algos() {
TRACE(("hash c2s is %s", c2s_hash_algo->name))
/* mac_algorithms_server_to_client */
- s2c_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
+ s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL);
if (s2c_hash_algo == NULL) {
erralgo = "mac s->c";
goto error;
@@ -733,7 +760,7 @@ static void read_kex_algos() {
TRACE(("hash s2c is %s", s2c_hash_algo->name))
/* compression_algorithms_client_to_server */
- c2s_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
+ c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL);
if (c2s_comp_algo == NULL) {
erralgo = "comp c->s";
goto error;
@@ -741,7 +768,7 @@ static void read_kex_algos() {
TRACE(("hash c2s is %s", c2s_comp_algo->name))
/* compression_algorithms_server_to_client */
- s2c_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
+ s2c_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL);
if (s2c_comp_algo == NULL) {
erralgo = "comp s->c";
goto error;
@@ -754,9 +781,10 @@ static void read_kex_algos() {
/* languages_server_to_client */
buf_eatstring(ses.payload);
- /* first_kex_packet_follows */
+ /* their first_kex_packet_follows */
if (buf_getbool(ses.payload)) {
- ses.kexstate.firstfollows = 1;
+ TRACE(("them kex firstfollows. allgood %d", allgood))
+ ses.kexstate.them_firstfollows = 1;
/* if the guess wasn't good, we ignore the packet sent */
if (!allgood) {
ses.ignorenext = 1;
@@ -799,6 +827,11 @@ static void read_kex_algos() {
/* reserved for future extensions */
buf_getint(ses.payload);
+
+ if (ses.send_kex_first_guess && allgood) {
+ TRACE(("our_first_follows_matches 1"))
+ ses.kexstate.our_first_follows_matches = 1;
+ }
return;
error:
diff --git a/common-session.c b/common-session.c
index f4fa579..6b357bf 100644
--- a/common-session.c
+++ b/common-session.c
@@ -33,12 +33,12 @@
#include "random.h"
#include "kex.h"
#include "channel.h"
-#include "atomicio.h"
#include "runopts.h"
static void checktimeouts();
static long select_timeout();
static int ident_readln(int fd, char* buf, int count);
+static void read_session_identification();
struct sshsession ses; /* GLOBAL */
@@ -49,8 +49,6 @@ int sessinitdone = 0; /* GLOBAL */
/* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
int exitflag = 0; /* GLOBAL */
-
-
/* called only at the start of a session, set up initial state */
void common_session_init(int sock_in, int sock_out) {
@@ -141,7 +139,10 @@ void session_loop(void(*loophandler)()) {
FD_ZERO(&writefd);
FD_ZERO(&readfd);
dropbear_assert(ses.payload == NULL);
- if (ses.sock_in != -1) {
+
+ /* during initial setup we flush out the KEXINIT packet before
+ * attempting to read the remote version string, which might block */
+ if (ses.sock_in != -1 && (ses.remoteident || isempty(&ses.writequeue))) {
FD_SET(ses.sock_in, &readfd);
}
if (ses.sock_out != -1 && !isempty(&ses.writequeue)) {
@@ -195,7 +196,12 @@ void session_loop(void(*loophandler)()) {
if (ses.sock_in != -1) {
if (FD_ISSET(ses.sock_in, &readfd)) {
- read_packet();
+ if (!ses.remoteident) {
+ /* blocking read of the version string */
+ read_session_identification();
+ } else {
+ read_packet();
+ }
}
/* Process the decrypted packet. After this, the read buffer
@@ -225,7 +231,7 @@ void session_loop(void(*loophandler)()) {
}
/* clean up a session on exit */
-void common_session_cleanup() {
+void session_cleanup() {
TRACE(("enter session_cleanup"))
@@ -234,6 +240,10 @@ void common_session_cleanup() {
TRACE(("leave session_cleanup: !sessinitdone"))
return;
}
+
+ if (ses.extra_session_cleanup) {
+ ses.extra_session_cleanup();
+ }
m_free(ses.session_id);
m_burn(ses.keys, sizeof(struct key_context));
@@ -244,21 +254,20 @@ void common_session_cleanup() {
TRACE(("leave session_cleanup"))
}
+void send_session_identification() {
+ buffer *writebuf = buf_new(strlen(LOCAL_IDENT "\r\n") + 1);
+ buf_putbytes(writebuf, LOCAL_IDENT "\r\n", strlen(LOCAL_IDENT "\r\n"));
+ buf_putbyte(writebuf, 0x0); // packet type
+ buf_setpos(writebuf, 0);
+ enqueue(&ses.writequeue, writebuf);
+}
-void session_identification() {
-
+static void read_session_identification() {
/* max length of 255 chars */
char linebuf[256];
int len = 0;
char done = 0;
int i;
-
- /* write our version string, this blocks */
- 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_in, linebuf, sizeof(linebuf));
diff --git a/configure.in b/configure.ac
index 3b82bb7..05461f3 100644
--- a/configure.in
+++ b/configure.ac
@@ -211,7 +211,7 @@ AC_ARG_ENABLE(shadow,
# Checks for header files.
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h security/pam_appl.h pam/pam_appl.h netinet/in_systm.h])
+AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h security/pam_appl.h pam/pam_appl.h netinet/in_systm.h, sys/uio.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@@ -616,7 +616,7 @@ AC_PROG_GCC_TRADITIONAL
AC_FUNC_MEMCMP
AC_FUNC_SELECT_ARGTYPES
AC_TYPE_SIGNAL
-AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork])
+AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev])
AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME))
diff --git a/dbutil.c b/dbutil.c
index 044388a..8ee6050 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -138,15 +138,39 @@ void dropbear_log(int priority, const char* format, ...) {
#ifdef DEBUG_TRACE
void dropbear_trace(const char* format, ...) {
-
va_list param;
+ struct timeval tv;
if (!debug_trace) {
return;
}
+ gettimeofday(&tv, NULL);
+
va_start(param, format);
- fprintf(stderr, "TRACE (%d): ", getpid());
+ fprintf(stderr, "TRACE (%d) %d.%d: ", getpid(), tv.tv_sec, tv.tv_usec);
+ vfprintf(stderr, format, param);
+ fprintf(stderr, "\n");
+ va_end(param);
+}
+
+void dropbear_trace2(const char* format, ...) {
+ static int trace_env = -1;
+ va_list param;
+ struct timeval tv;
+
+ if (trace_env == -1) {
+ trace_env = getenv("DROPBEAR_TRACE2") ? 1 : 0;
+ }
+
+ if (!(debug_trace && trace_env)) {
+ return;
+ }
+
+ gettimeofday(&tv, NULL);
+
+ va_start(param, format);
+ fprintf(stderr, "TRACE2 (%d) %d.%d: ", getpid(), tv.tv_sec, tv.tv_usec);
vfprintf(stderr, format, param);
fprintf(stderr, "\n");
va_end(param);
@@ -725,8 +749,6 @@ int buf_getline(buffer * line, FILE * authfile) {
int c = EOF;
- TRACE(("enter buf_getline"))
-
buf_setpos(line, 0);
buf_setlen(line, 0);
@@ -750,10 +772,8 @@ out:
/* if we didn't read anything before EOF or error, exit */
if (c == EOF && line->pos == 0) {
- TRACE(("leave buf_getline: failure"))
return DROPBEAR_FAILURE;
} else {
- TRACE(("leave buf_getline: success"))
buf_setpos(line, 0);
return DROPBEAR_SUCCESS;
}
diff --git a/dbutil.h b/dbutil.h
index 0f16bf3..3af79f7 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -35,14 +35,12 @@ void startsyslog();
#ifdef __GNUC__
#define ATTRIB_PRINTF(fmt,args) __attribute__((format(printf, fmt, args)))
-#else
-#define ATTRIB_PRINTF(fmt,args)
-#endif
-
-#ifdef __GNUC__
#define ATTRIB_NORETURN __attribute__((noreturn))
+#define ATTRIB_SENTINEL __attribute__((sentinel))
#else
+#define ATTRIB_PRINTF(fmt,args)
#define ATTRIB_NORETURN
+#define ATTRIB_SENTINEL
#endif
extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param) ATTRIB_NORETURN;
@@ -57,6 +55,7 @@ void fail_assert(const char* expr, const char* file, int line) ATTRIB_NORETURN;
#ifdef DEBUG_TRACE
void dropbear_trace(const char* format, ...) ATTRIB_PRINTF(1,2);
+void dropbear_trace2(const char* format, ...) ATTRIB_PRINTF(1,2);
void printhex(const char * label, const unsigned char * buf, int len);
extern int debug_trace;
#endif
diff --git a/debug.h b/debug.h
index b20e685..be09865 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
@@ -63,8 +63,10 @@
/* you don't need to touch this block */
#ifdef DEBUG_TRACE
#define TRACE(X) dropbear_trace X;
+#define TRACE2(X) dropbear_trace2 X;
#else /*DEBUG_TRACE*/
#define TRACE(X)
+#define TRACE2(X)
#endif /*DEBUG_TRACE*/
/* To debug with GDB it is easier to run with no forking of child processes.
diff --git a/dss.c b/dss.c
index d984669..75dc0d0 100644
--- a/dss.c
+++ b/dss.c
@@ -101,9 +101,9 @@ int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key) {
/* Clear and free the memory used by a public or private key */
void dss_key_free(dropbear_dss_key *key) {
- TRACE(("enter dsa_key_free"))
+ TRACE2(("enter dsa_key_free"))
if (key == NULL) {
- TRACE(("enter dsa_key_free: key == NULL"))
+ TRACE2(("enter dsa_key_free: key == NULL"))
return;
}
if (key->p) {
@@ -127,7 +127,7 @@ void dss_key_free(dropbear_dss_key *key) {
m_free(key->x);
}
m_free(key);
- TRACE(("leave dsa_key_free"))
+ TRACE2(("leave dsa_key_free"))
}
/* put the dss public key into the buffer in the required format:
diff --git a/includes.h b/includes.h
index 571a3be..203ccfd 100644
--- a/includes.h
+++ b/includes.h
@@ -120,6 +120,10 @@
#include <libgen.h>
#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
#ifdef BUNDLED_LIBTOM
#include "libtomcrypt/src/headers/tomcrypt.h"
#include "libtommath/tommath.h"
diff --git a/kex.h b/kex.h
index c89b0a3..72430e9 100644
--- a/kex.h
+++ b/kex.h
@@ -51,19 +51,22 @@ struct KEXState {
unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
unsigned recvkexinit : 1;
- unsigned firstfollows : 1; /* true when first_kex_packet_follows is set */
+ unsigned them_firstfollows : 1; /* true when first_kex_packet_follows is set */
unsigned sentnewkeys : 1; /* set once we've send MSG_NEWKEYS (will be cleared once we have also received */
unsigned recvnewkeys : 1; /* set once we've received MSG_NEWKEYS (cleared once we have also sent */
unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed,
ie the transport layer has been set up */
+ unsigned our_first_follows_matches : 1;
+
time_t lastkextime; /* time of the last kex */
unsigned int datatrans; /* data transmitted since last kex */
unsigned int datarecv; /* data received since last kex */
};
+
#define MAX_KEXHASHBUF 2000
#endif /* _KEX_H_ */
diff --git a/options.h b/options.h
index c52d6c2..88b17ca 100644
--- a/options.h
+++ b/options.h
@@ -153,7 +153,7 @@ much traffic. */
#endif
/* Whether to do reverse DNS lookups. */
-#define DO_HOST_LOOKUP
+//#define DO_HOST_LOOKUP
/* Whether to print the message of the day (MOTD). This doesn't add much code
* size */
@@ -176,7 +176,7 @@ much traffic. */
#define ENABLE_SVR_PASSWORD_AUTH
/* PAM requires ./configure --enable-pam */
-/*#define ENABLE_SVR_PAM_AUTH*/
+//#define ENABLE_SVR_PAM_AUTH
#define ENABLE_SVR_PUBKEY_AUTH
/* Whether to take public key options in
diff --git a/packet.c b/packet.c
index f979cae..09f0600 100644
--- a/packet.c
+++ b/packet.c
@@ -55,10 +55,62 @@ void write_packet() {
buffer * writebuf = NULL;
time_t now;
unsigned packet_type;
+ int all_ignore = 1;
+#ifdef HAVE_WRITEV
+ struct iovec *iov = NULL;
+ int i;
+ struct Link *l;
+#endif
- TRACE(("enter write_packet"))
+ TRACE2(("enter write_packet"))
dropbear_assert(!isempty(&ses.writequeue));
+#ifdef HAVE_WRITEV
+ iov = m_malloc(sizeof(*iov) * ses.writequeue.count);
+ for (l = ses.writequeue.head, i = 0; l; l = l->link, i++)
+ {
+ writebuf = (buffer*)l->item;
+ packet_type = writebuf->data[writebuf->len-1];
+ len = writebuf->len - 1 - writebuf->pos;
+ dropbear_assert(len > 0);
+ all_ignore &= (packet_type == SSH_MSG_IGNORE);
+ TRACE2(("write_packet writev #%d type %d len %d/%d", i, packet_type,
+ len, writebuf->len-1))
+ iov[i].iov_base = buf_getptr(writebuf, len);
+ iov[i].iov_len = len;
+ }
+ written = writev(ses.sock_out, iov, ses.writequeue.count);
+ if (written < 0) {
+ if (errno == EINTR) {
+ m_free(iov);
+ TRACE2(("leave writepacket: EINTR"))
+ return;
+ } else {
+ dropbear_exit("Error writing");
+ }
+ }
+
+ if (written == 0) {
+ ses.remoteclosed();
+ }
+
+ while (written > 0) {
+ writebuf = (buffer*)examine(&ses.writequeue);
+ len = writebuf->len - 1 - writebuf->pos;
+ if (len > written) {
+ // partial buffer write
+ buf_incrpos(writebuf, written);
+ written = 0;
+ } else {
+ written -= len;
+ dequeue(&ses.writequeue);
+ buf_free(writebuf);
+ }
+ }
+
+ m_free(iov);
+
+#else
/* Get the next buffer in the queue of encrypted packets to write*/
writebuf = (buffer*)examine(&ses.writequeue);
@@ -72,19 +124,13 @@ void write_packet() {
if (written < 0) {
if (errno == EINTR) {
- TRACE(("leave writepacket: EINTR"))
+ TRACE2(("leave writepacket: EINTR"))
return;
} else {
dropbear_exit("Error writing");
}
}
-
- now = time(NULL);
- ses.last_trx_packet_time = now;
-
- if (packet_type != SSH_MSG_IGNORE) {
- ses.last_packet_time = now;
- }
+ all_ignore = (packet_type == SSH_MSG_IGNORE);
if (written == 0) {
ses.remoteclosed();
@@ -100,7 +146,15 @@ void write_packet() {
buf_incrpos(writebuf, written);
}
- TRACE(("leave write_packet"))
+#endif
+ now = time(NULL);
+ ses.last_trx_packet_time = now;
+
+ if (!all_ignore) {
+ ses.last_packet_time = now;
+ }
+
+ TRACE2(("leave write_packet"))
}
/* Non-blocking function reading available portion of a packet into the
@@ -112,7 +166,7 @@ void read_packet() {
unsigned int maxlen;
unsigned char blocksize;
- TRACE(("enter read_packet"))
+ TRACE2(("enter read_packet"))
blocksize = ses.keys->recv.algo_crypt->blocksize;
if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
@@ -125,7 +179,7 @@ void read_packet() {
if (ret == DROPBEAR_FAILURE) {
/* didn't read enough to determine the length */
- TRACE(("leave read_packet: packetinit done"))
+ TRACE2(("leave read_packet: packetinit done"))
return;
}
}
@@ -147,7 +201,7 @@ void read_packet() {
if (len < 0) {
if (errno == EINTR || errno == EAGAIN) {
- TRACE(("leave read_packet: EINTR or EAGAIN"))
+ TRACE2(("leave read_packet: EINTR or EAGAIN"))
return;
} else {
dropbear_exit("Error reading: %s", strerror(errno));
@@ -163,7 +217,7 @@ void read_packet() {
/* The main select() loop process_packet() to
* handle the packet contents... */
}
- TRACE(("leave read_packet"))
+ TRACE2(("leave read_packet"))
}
/* Function used to read the initial portion of a packet, and determine the
@@ -197,7 +251,7 @@ static int read_packet_init() {
}
if (slen < 0) {
if (errno == EINTR) {
- TRACE(("leave read_packet_init: EINTR"))
+ TRACE2(("leave read_packet_init: EINTR"))
return DROPBEAR_FAILURE;
}
dropbear_exit("Error reading: %s", strerror(errno));
@@ -221,7 +275,7 @@ static int read_packet_init() {
}
len = buf_getint(ses.readbuf) + 4 + macsize;
- TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize))
+ TRACE2(("packet size is %d, block %d mac %d", len, blocksize, macsize))
/* check packet length */
@@ -247,7 +301,7 @@ void decrypt_packet() {
unsigned int padlen;
unsigned int len;
- TRACE(("enter decrypt_packet"))
+ TRACE2(("enter decrypt_packet"))
blocksize = ses.keys->recv.algo_crypt->blocksize;
macsize = ses.keys->recv.algo_mac->hashsize;
@@ -304,7 +358,7 @@ void decrypt_packet() {
ses.recvseq++;
- TRACE(("leave decrypt_packet"))
+ TRACE2(("leave decrypt_packet"))
}
/* Checks the mac at the end of a decrypted readbuf.
@@ -314,7 +368,7 @@ static int checkmac() {
unsigned char mac_bytes[MAX_MAC_LEN];
unsigned int mac_size, contents_len;
- mac_size = ses.keys->trans.algo_mac->hashsize;
+ mac_size = ses.keys->recv.algo_mac->hashsize;
contents_len = ses.readbuf->len - mac_size;
buf_setpos(ses.readbuf, 0);
@@ -403,7 +457,6 @@ static void enqueue_reply_packet() {
ses.reply_queue_head = new_item;
}
ses.reply_queue_tail = new_item;
- TRACE(("leave enqueue_reply_packet"))
}
void maybe_flush_reply_queue() {
@@ -440,24 +493,18 @@ void encrypt_packet() {
unsigned int len, encrypt_buf_size;
unsigned char mac_bytes[MAX_MAC_LEN];
- TRACE(("enter encrypt_packet()"))
+ TRACE2(("enter encrypt_packet()"))
buf_setpos(ses.writepayload, 0);
packet_type = buf_getbyte(ses.writepayload);
buf_setpos(ses.writepayload, 0);
- TRACE(("encrypt_packet type is %d", packet_type))
+ TRACE2(("encrypt_packet type is %d", packet_type))
- if ((!ses.dataallowed && !packet_is_okay_kex(packet_type))
- || ses.kexstate.sentnewkeys) {
+ if ((!ses.dataallowed && !packet_is_okay_kex(packet_type))) {
/* During key exchange only particular packets are allowed.
Since this packet_type isn't OK we just enqueue it to send
after the KEX, see maybe_flush_reply_queue */
-
- /* We also enqueue packets here when we have sent a MSG_NEWKEYS
- * packet but are yet to received one. For simplicity we just switch
- * over all the keys at once. This is the 'ses.kexstate.sentnewkeys'
- * case. */
enqueue_reply_packet();
return;
}
@@ -559,7 +606,7 @@ void encrypt_packet() {
ses.kexstate.datatrans += writebuf->len;
ses.transseq++;
- TRACE(("leave encrypt_packet()"))
+ TRACE2(("leave encrypt_packet()"))
}
@@ -572,8 +619,6 @@ static void make_mac(unsigned int seqno, const struct key_context_directional *
unsigned long bufsize;
hmac_state hmac;
- TRACE(("enter writemac"))
-
if (key_state->algo_mac->hashsize > 0) {
/* calculate the mac */
if (hmac_init(&hmac,
@@ -602,7 +647,7 @@ static void make_mac(unsigned int seqno, const struct key_context_directional *
dropbear_exit("HMAC error");
}
}
- TRACE(("leave writemac"))
+ TRACE2(("leave writemac"))
}
#ifndef DISABLE_ZLIB
@@ -613,7 +658,7 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
unsigned int endpos = src->pos + len;
int result;
- TRACE(("enter buf_compress"))
+ TRACE2(("enter buf_compress"))
while (1) {
@@ -647,6 +692,6 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR);
}
- TRACE(("leave buf_compress"))
+ TRACE2(("leave buf_compress"))
}
#endif
diff --git a/process-packet.c b/process-packet.c
index 2ae410d..128eb72 100644
--- a/process-packet.c
+++ b/process-packet.c
@@ -45,10 +45,10 @@ void process_packet() {
unsigned char type;
unsigned int i;
- TRACE(("enter process_packet"))
+ TRACE2(("enter process_packet"))
type = buf_getbyte(ses.payload);
- TRACE(("process_packet: packet type = %d", type))
+ TRACE(("process_packet: packet type = %d, len %d", type, ses.payload->len))
ses.lastpacket = type;
@@ -123,7 +123,7 @@ out:
buf_free(ses.payload);
ses.payload = NULL;
- TRACE(("leave process_packet"))
+ TRACE2(("leave process_packet"))
}
diff --git a/queue.c b/queue.c
index 7a80124..9d00808 100644
--- a/queue.c
+++ b/queue.c
@@ -70,7 +70,6 @@ void enqueue(struct Queue* queue, void* item) {
struct Link* newlink;
- TRACE(("enter enqueue"))
newlink = (struct Link*)m_malloc(sizeof(struct Link));
newlink->item = item;
@@ -85,5 +84,4 @@ void enqueue(struct Queue* queue, void* item) {
queue->head = newlink;
}
queue->count++;
- TRACE(("leave enqueue"))
}
diff --git a/queue.h b/queue.h
index 80fbb9d..8cffab7 100644
--- a/queue.h
+++ b/queue.h
@@ -36,7 +36,7 @@ struct Queue {
struct Link* head;
struct Link* tail;
- unsigned int count; /* safety value */
+ unsigned int count;
};
diff --git a/random.c b/random.c
index 0378e9a..1f687a2 100644
--- a/random.c
+++ b/random.c
@@ -157,6 +157,9 @@ static void write_urandom()
/* This is opportunistic, don't worry about failure */
unsigned char buf[INIT_SEED_SIZE];
FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w");
+ if (!f) {
+ return;
+ }
genrandom(buf, sizeof(buf));
fwrite(buf, sizeof(buf), 1, f);
fclose(f);
diff --git a/rsa.c b/rsa.c
index 91bf59d..520ad84 100644
--- a/rsa.c
+++ b/rsa.c
@@ -139,10 +139,10 @@ out:
/* Clear and free the memory used by a public or private key */
void rsa_key_free(dropbear_rsa_key *key) {
- TRACE(("enter rsa_key_free"))
+ TRACE2(("enter rsa_key_free"))
if (key == NULL) {
- TRACE(("leave rsa_key_free: key == NULL"))
+ TRACE2(("leave rsa_key_free: key == NULL"))
return;
}
if (key->d) {
@@ -166,7 +166,7 @@ void rsa_key_free(dropbear_rsa_key *key) {
m_free(key->q);
}
m_free(key);
- TRACE(("leave rsa_key_free"))
+ TRACE2(("leave rsa_key_free"))
}
/* Put the public rsa key into the buffer in the required format:
diff --git a/scp.c b/scp.c
index 8715726..114d095 100644
--- a/scp.c
+++ b/scp.c
@@ -230,7 +230,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
close(pin[0]);
close(pout[1]);
-#ifdef USE_VFORK
+#ifndef USE_VFORK
arg_setup(host, remuser, cmd);
#endif
diff --git a/scpmisc.c b/scpmisc.c
index c3b09a5..ec4df35 100644
--- a/scpmisc.c
+++ b/scpmisc.c
@@ -40,6 +40,7 @@
/*RCSID("OpenBSD: xmalloc.c,v 1.16 2001/07/23 18:21:46 stevesk Exp ");*/
+#define _GNU_SOURCE
#include "includes.h"
#include "scpmisc.h"
diff --git a/service.h b/service.h
index 197d8d1..9c60c09 100644
--- a/service.h
+++ b/service.h
@@ -26,7 +26,5 @@
#define _SERVICE_H_
void recv_msg_service_request(); /* Server */
-void send_msg_service_request(); /* Client */
-void recv_msg_service_accept(); /* Client */
#endif /* _SERVICE_H_ */
diff --git a/session.h b/session.h
index 158e209..4ba8ac8 100644
--- a/session.h
+++ b/session.h
@@ -44,8 +44,8 @@ extern int exitflag;
void common_session_init(int sock_in, int sock_out);
void session_loop(void(*loophandler)());
-void common_session_cleanup();
-void session_identification();
+void session_cleanup();
+void send_session_identification();
void send_msg_ignore();
const char* get_user_shell();
@@ -58,7 +58,6 @@ void svr_dropbear_log(int priority, const char* format, va_list param);
/* Client */
void cli_session(int sock_in, int sock_out);
-void cli_session_cleanup();
void cleantext(unsigned char* dirtytext);
/* crypto parameters that are stored individually for transmit and receive */
@@ -79,6 +78,7 @@ struct key_context_directional {
#endif
} cipher_state;
unsigned char mackey[MAX_MAC_LEN];
+ int valid;
};
struct key_context {
@@ -111,7 +111,10 @@ struct sshsession {
int sock_in;
int sock_out;
- unsigned char *remoteident;
+ /* remotehost will be initially NULL as we delay
+ * reading the remote version string. it will be set
+ * by the time any recv_() packet methods are called */
+ unsigned char *remoteident;
int maxfd; /* the maximum file descriptor to check with select() */
@@ -169,15 +172,11 @@ struct sshsession {
concluded (ie, while dataallowed was unset)*/
struct packetlist *reply_queue_head, *reply_queue_tail;
- algo_type*(*buf_match_algo)(buffer*buf, algo_type localalgos[],
- int *goodguess); /* The function to use to choose which algorithm
- to use from the ones presented by the remote
- side. Is specific to the client/server mode,
- hence the function-pointer callback.*/
-
void(*remoteclosed)(); /* A callback to handle closure of the
remote connection */
+ void(*extra_session_cleanup)(); /* client or server specific cleanup */
+ void(*send_kex_first_guess)();
struct AuthState authstate; /* Common amongst client and server, since most
struct elements are common */
@@ -233,10 +232,6 @@ typedef enum {
typedef enum {
STATE_NOTHING,
- SERVICE_AUTH_REQ_SENT,
- SERVICE_AUTH_ACCEPT_RCVD,
- SERVICE_CONN_REQ_SENT,
- SERVICE_CONN_ACCEPT_RCVD,
USERAUTH_REQ_SENT,
USERAUTH_FAIL_RCVD,
USERAUTH_SUCCESS_RCVD,
@@ -246,6 +241,7 @@ typedef enum {
struct clientsession {
mp_int *dh_e, *dh_x; /* Used during KEX */
+ int dh_val_algo; /* KEX algorithm corresponding to current dh_e and dh_x */
cli_kex_state kex_state; /* Used for progressing KEX */
cli_state state; /* Used to progress auth/channelsession etc */
unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
@@ -259,6 +255,9 @@ struct clientsession {
int stderrcopy;
int stderrflags;
+ /* for escape char handling */
+ int last_char;
+
int winchange; /* Set to 1 when a windowchange signal happens */
int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD,
diff --git a/signkey.c b/signkey.c
index 1d908f4..f647990 100644
--- a/signkey.c
+++ b/signkey.c
@@ -98,7 +98,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
int keytype;
int ret = DROPBEAR_FAILURE;
- TRACE(("enter buf_get_pub_key"))
+ TRACE2(("enter buf_get_pub_key"))
ident = buf_getstring(buf, &len);
keytype = signkey_type_from_name(ident, len);
@@ -109,7 +109,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
return DROPBEAR_FAILURE;
}
- TRACE(("buf_get_pub_key keytype is %d", keytype))
+ TRACE2(("buf_get_pub_key keytype is %d", keytype))
*type = keytype;
@@ -137,7 +137,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
}
#endif
- TRACE(("leave buf_get_pub_key"))
+ TRACE2(("leave buf_get_pub_key"))
return ret;
@@ -153,7 +153,7 @@ int buf_get_priv_key(buffer *buf, sign_key *key, int *type) {
int keytype;
int ret = DROPBEAR_FAILURE;
- TRACE(("enter buf_get_priv_key"))
+ TRACE2(("enter buf_get_priv_key"))
ident = buf_getstring(buf, &len);
keytype = signkey_type_from_name(ident, len);
@@ -190,7 +190,7 @@ int buf_get_priv_key(buffer *buf, sign_key *key, int *type) {
}
#endif
- TRACE(("leave buf_get_priv_key"))
+ TRACE2(("leave buf_get_priv_key"))
return ret;
@@ -201,7 +201,7 @@ void buf_put_pub_key(buffer* buf, sign_key *key, int type) {
buffer *pubkeys;
- TRACE(("enter buf_put_pub_key"))
+ TRACE2(("enter buf_put_pub_key"))
pubkeys = buf_new(MAX_PUBKEY_SIZE);
#ifdef DROPBEAR_DSS
@@ -223,7 +223,7 @@ void buf_put_pub_key(buffer* buf, sign_key *key, int type) {
pubkeys->len);
buf_free(pubkeys);
- TRACE(("leave buf_put_pub_key"))
+ TRACE2(("leave buf_put_pub_key"))
}
/* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */
@@ -251,7 +251,7 @@ void buf_put_priv_key(buffer* buf, sign_key *key, int type) {
void sign_key_free(sign_key *key) {
- TRACE(("enter sign_key_free"))
+ TRACE2(("enter sign_key_free"))
#ifdef DROPBEAR_DSS
dss_key_free(key->dsskey);
@@ -265,7 +265,7 @@ void sign_key_free(sign_key *key) {
m_free(key->filename);
m_free(key);
- TRACE(("leave sign_key_free"))
+ TRACE2(("leave sign_key_free"))
}
static char hexdig(unsigned char x) {
diff --git a/svr-algo.c b/svr-algo.c
deleted file mode 100644
index f8f9055..0000000
--- a/svr-algo.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Dropbear - a SSH2 server
- * SSH client implementation
- *
- * Copyright (c) 2002,2003 Matt Johnston
- * Copyright (c) 2004 by Mihnea Stoenescu
- * 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. */
-
-#include "algo.h"
-#include "dbutil.h"
-
-/* match the first algorithm in the comma-separated list in buf which is
- * also in localalgos[], or return NULL on failure.
- * (*goodguess) is set to 1 if the preferred client/server algos match,
- * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
- * guessed correctly */
-algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
- int *goodguess)
-{
-
- unsigned char * algolist = NULL;
- unsigned char * remotealgos[MAX_PROPOSED_ALGO];
- unsigned int len;
- unsigned int count, i, j;
- algo_type * ret = NULL;
-
- *goodguess = 0;
-
- /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
- algolist = buf_getstring(buf, &len);
- /* Debug this */
- TRACE(("buf_match_algo: %s", algolist))
- if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
- goto out; /* just a sanity check, no other use */
- }
-
- /* remotealgos will contain a list of the strings parsed out */
- /* We will have at least one string (even if it's just "") */
- remotealgos[0] = algolist;
- count = 1;
- /* Iterate through, replacing ','s with NULs, to split it into
- * words. */
- for (i = 0; i < len; i++) {
- if (algolist[i] == '\0') {
- /* someone is trying something strange */
- goto out;
- }
- if (algolist[i] == ',') {
- algolist[i] = '\0';
- remotealgos[count] = &algolist[i+1];
- count++;
- }
- if (count >= MAX_PROPOSED_ALGO) {
- break;
- }
- }
-
- /* iterate and find the first match */
- for (i = 0; i < count; i++) {
-
- len = strlen(remotealgos[i]);
-
- for (j = 0; localalgos[j].name != NULL; j++) {
- if (localalgos[j].usable) {
- if (len == strlen(localalgos[j].name) &&
- strncmp(localalgos[j].name, remotealgos[i], len) == 0) {
- /* set if it was a good guess */
- if (i == 0 && j == 0) {
- *goodguess = 1;
- }
- /* set the algo to return */
- ret = &localalgos[j];
- goto out;
- }
- }
- }
- }
-
-out:
- m_free(algolist);
- return ret;
-}
diff --git a/svr-main.c b/svr-main.c
index 43d6656..461aeaf 100644
--- a/svr-main.c
+++ b/svr-main.c
@@ -271,7 +271,7 @@ void main_noinetd() {
goto out;
}
- addrandom(&fork_ret, sizeof(fork_ret));
+ addrandom((void*)&fork_ret, sizeof(fork_ret));
if (fork_ret > 0) {
diff --git a/svr-session.c b/svr-session.c
index cf82289..3a979a1 100644
--- a/svr-session.c
+++ b/svr-session.c
@@ -72,6 +72,13 @@ static const struct ChanType *svr_chantypes[] = {
NULL /* Null termination is mandatory. */
};
+static void
+svr_session_cleanup(void)
+{
+ /* free potential public key options */
+ svr_pubkey_options_cleanup();
+}
+
void svr_session(int sock, int childpipe) {
char *host, *port;
size_t len;
@@ -103,10 +110,10 @@ void svr_session(int sock, int childpipe) {
/* set up messages etc */
ses.remoteclosed = svr_remoteclosed;
+ ses.extra_session_cleanup = svr_session_cleanup;
/* packet handlers */
ses.packettypes = svr_packettypes;
- ses.buf_match_algo = svr_buf_match_algo;
ses.isserver = 1;
@@ -114,7 +121,7 @@ void svr_session(int sock, int childpipe) {
sessinitdone = 1;
/* exchange identification, version etc */
- session_identification();
+ send_session_identification();
/* start off with key exchange */
send_msg_kexinit();
@@ -160,11 +167,8 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
if (svr_ses.server_pid == getpid())
#endif
{
- /* free potential public key options */
- svr_pubkey_options_cleanup();
-
/* must be after we've done with username etc */
- common_session_cleanup();
+ session_cleanup();
}
exit(exitcode);
diff --git a/svr-x11fwd.c b/svr-x11fwd.c
index 92dadd5..f6368d7 100644
--- a/svr-x11fwd.c
+++ b/svr-x11fwd.c
@@ -175,7 +175,7 @@ void x11cleanup(struct ChanSess *chansess) {
m_free(chansess->x11authprot);
m_free(chansess->x11authcookie);
- TRACE(("chansess %x", chansess))
+ TRACE(("chansess %p", chansess))
if (chansess->x11listener != NULL) {
remove_listener(chansess->x11listener);
chansess->x11listener = NULL;
diff --git a/sysoptions.h b/sysoptions.h
index 2d93e7b..22c2a4d 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -23,6 +23,15 @@
#define AUTH_TIMEOUT 300 /* we choose 5 minutes */
#endif
+/* A client should try and send an initial key exchange packet guessing
+ * the algorithm that will match - saves a round trip connecting, has little
+ * overhead if the guess was "wrong". */
+#define USE_KEX_FIRST_FOLLOWS
+/* Use protocol extension to allow "first follows" to succeed more frequently.
+ * This is currently Dropbear-specific but will gracefully fallback when connecting
+ * to other implementations. */
+#define USE_KEXGUESS2
+
/* Minimum key sizes for DSS and RSA */
#ifndef MIN_DSS_KEYLEN
#define MIN_DSS_KEYLEN 512
@@ -54,13 +63,16 @@
#define _PATH_CP "/bin/cp"
+#define DROPBEAR_ESCAPE_CHAR '~'
+
/* success/failure defines */
#define DROPBEAR_SUCCESS 0
#define DROPBEAR_FAILURE -1
/* various algorithm identifiers */
-#define DROPBEAR_KEX_DH_GROUP1 0
-#define DROPBEAR_KEX_DH_GROUP14 1
+#define DROPBEAR_KEX_NONE 0
+#define DROPBEAR_KEX_DH_GROUP1 1
+#define DROPBEAR_KEX_DH_GROUP14 2
#define DROPBEAR_SIGNKEY_ANY 0
#define DROPBEAR_SIGNKEY_RSA 1
@@ -186,6 +198,9 @@
#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
#endif
+/* Send an auth request straight away rather than trying "none" type to get a list */
+#define CLI_IMMEDIATE_AUTH
+
/* Changing this is inadvisable, it appears to have problems
* with flushing compressed data */
#define DROPBEAR_ZLIB_MEM_LEVEL 8
diff --git a/termcodes.c b/termcodes.c
index d59505c..490e6ce 100644
--- a/termcodes.c
+++ b/termcodes.c
@@ -107,8 +107,14 @@ const struct TermCode termcodes[MAX_TERMCODE+1] = {
#else
{0, 0},
#endif
- {0, 0}, /* 42 */
+ /* IUTF8 isn't standardised in rfc4254 but is likely soon.
+ * Implemented by linux and darwin */
+#ifdef IUTF8
+ {IUTF8, TERMCODE_INPUT},
+#else
{0, 0},
+#endif
+ {0, 0}, /* 43 */
{0, 0},
{0, 0},
{0, 0},