merge of '48fdaa8706d1acda35e9d564adc9a1fbc96c18c8'

and '658fd03abd21e0da7c4c89b9fff9dc693c72daae'

--HG--
extra : convert_revision : 8064882fcaa13d586651021462b9014b74332107
This commit is contained in:
Matt Johnston
2010-02-27 11:53:18 +00:00
48 changed files with 1339 additions and 663 deletions

View File

@@ -20,16 +20,17 @@ COMMONOBJS=dbutil.o buffer.o \
dss.o bignum.o \ dss.o bignum.o \
signkey.o rsa.o random.o \ signkey.o rsa.o random.o \
queue.o \ queue.o \
atomicio.o compat.o fake-rfc2553.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-algo.o svr-auth.o sshpty.o \
svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.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-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
svr-tcpfwd.o svr-authpam.o svr-tcpfwd.o svr-authpam.o @CRYPTLIB@
CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ 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 \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o \
cli-agentfwd.o list.o
CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
common-channel.o common-chansession.o termcodes.o loginrec.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \
@@ -40,7 +41,7 @@ KEYOBJS=dropbearkey.o gendss.o genrsa.o
CONVERTOBJS=dropbearconvert.o keyimport.o CONVERTOBJS=dropbearconvert.o keyimport.o
SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \ HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \ dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \

View File

@@ -23,21 +23,34 @@
* SOFTWARE. */ * SOFTWARE. */
#ifndef _AGENTFWD_H_ #ifndef _AGENTFWD_H_
#define _AGENTFWD_H_ #define _AGENTFWD_H_
#ifndef DISABLE_AGENTFWD
#include "includes.h" #include "includes.h"
#include "chansession.h" #include "chansession.h"
#include "channel.h" #include "channel.h"
#include "auth.h"
#include "list.h"
/* An agent reply can be reasonably large, as it can
* contain a list of all public keys held by the agent.
* 10000 is arbitrary */
#define MAX_AGENT_REPLY 10000
int svr_agentreq(struct ChanSess * chansess);
void svr_agentcleanup(struct ChanSess * chansess);
void svr_agentset(struct ChanSess *chansess);
/* client functions */
void cli_load_agent_keys(m_list * ret_list);
void agent_buf_sign(buffer *sigblob, sign_key *key,
const unsigned char *data, unsigned int len);
void cli_setup_agent(struct Channel *channel);
int agentreq(struct ChanSess * chansess);
void agentsetauth(struct ChanSess *chansess);
void agentcleanup(struct ChanSess * chansess);
void agentset(struct ChanSess *chansess);
#ifdef __hpux #ifdef __hpux
#define seteuid(a) setresuid(-1, (a), -1) #define seteuid(a) setresuid(-1, (a), -1)
#define setegid(a) setresgid(-1, (a), -1) #define setegid(a) setresgid(-1, (a), -1)
#endif #endif
#endif /* DROPBEAR_AGENTFWD */ extern const struct ChanType cli_chan_agent;
#endif /* _AGENTFWD_H_ */ #endif /* _AGENTFWD_H_ */

3
algo.h
View File

@@ -50,7 +50,8 @@ extern algo_type sshkex[];
extern algo_type sshhostkey[]; extern algo_type sshhostkey[];
extern algo_type sshciphers[]; extern algo_type sshciphers[];
extern algo_type sshhashes[]; extern algo_type sshhashes[];
extern algo_type sshcompress[]; extern algo_type ssh_compress[];
extern algo_type ssh_nocompress[];
extern const struct dropbear_cipher dropbear_nocipher; extern const struct dropbear_cipher dropbear_nocipher;
extern const struct dropbear_cipher_mode dropbear_mode_none; extern const struct dropbear_cipher_mode dropbear_mode_none;

18
auth.h
View File

@@ -26,6 +26,7 @@
#define _AUTH_H_ #define _AUTH_H_
#include "includes.h" #include "includes.h"
#include "signkey.h"
#include "chansession.h" #include "chansession.h"
void svr_authinitialise(); void svr_authinitialise();
@@ -73,6 +74,7 @@ void cli_auth_password();
int cli_auth_pubkey(); int cli_auth_pubkey();
void cli_auth_interactive(); void cli_auth_interactive();
char* getpass_or_cancel(char* prompt); char* getpass_or_cancel(char* prompt);
void cli_auth_pubkey_cleanup();
#define MAX_USERNAME_LEN 25 /* arbitrary for the moment */ #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
@@ -97,7 +99,6 @@ char* getpass_or_cancel(char* prompt);
* relatively little extraneous bits when used for the client rather than the * relatively little extraneous bits when used for the client rather than the
* server */ * server */
struct AuthState { struct AuthState {
char *username; /* This is the username the client presents to check. It char *username; /* This is the username the client presents to check. It
is updated each run through, used for auth checking */ is updated each run through, used for auth checking */
unsigned char authtypes; /* Flags indicating which auth types are still unsigned char authtypes; /* Flags indicating which auth types are still
@@ -120,19 +121,6 @@ struct AuthState {
#ifdef ENABLE_SVR_PUBKEY_OPTIONS #ifdef ENABLE_SVR_PUBKEY_OPTIONS
struct PubKeyOptions* pubkey_options; struct PubKeyOptions* pubkey_options;
#endif #endif
};
struct SignKeyList;
/* A singly linked list of signing keys */
struct SignKeyList {
sign_key *key;
int type; /* The type of key */
struct SignKeyList *next;
/* filename? or the buffer? for encrypted keys, so we can later get
* the private key portion */
}; };
#ifdef ENABLE_SVR_PUBKEY_OPTIONS #ifdef ENABLE_SVR_PUBKEY_OPTIONS
@@ -145,7 +133,7 @@ struct PubKeyOptions {
int no_pty_flag; int no_pty_flag;
/* "command=" option. */ /* "command=" option. */
unsigned char * forced_command; unsigned char * forced_command;
unsigned char * original_command;
}; };
#endif #endif

View File

@@ -223,6 +223,20 @@ unsigned char* buf_getstring(buffer* buf, unsigned int *retlen) {
return ret; return ret;
} }
/* Return a string as a newly allocated buffer */
buffer * buf_getstringbuf(buffer *buf) {
buffer *ret;
unsigned char* str;
unsigned int len;
str = buf_getstring(buf, &len);
ret = m_malloc(sizeof(*ret));
ret->data = str;
ret->len = len;
ret->size = len;
ret->pos = 0;
return ret;
}
/* Just increment the buffer position the same as if we'd used buf_getstring, /* Just increment the buffer position the same as if we'd used buf_getstring,
* but don't bother copying/malloc()ing for it */ * but don't bother copying/malloc()ing for it */
void buf_eatstring(buffer *buf) { void buf_eatstring(buffer *buf) {

View File

@@ -55,6 +55,7 @@ void buf_putbyte(buffer* buf, unsigned char val);
unsigned char* buf_getptr(buffer* buf, unsigned int len); unsigned char* buf_getptr(buffer* buf, unsigned int len);
unsigned char* buf_getwriteptr(buffer* buf, unsigned int len); unsigned char* buf_getwriteptr(buffer* buf, unsigned int len);
unsigned char* buf_getstring(buffer* buf, unsigned int *retlen); unsigned char* buf_getstring(buffer* buf, unsigned int *retlen);
buffer * buf_getstringbuf(buffer *buf);
void buf_eatstring(buffer *buf); void buf_eatstring(buffer *buf);
void buf_putint(buffer* buf, unsigned int val); void buf_putint(buffer* buf, unsigned int val);
void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len); void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len);

View File

@@ -58,7 +58,7 @@ struct Channel {
unsigned int recvmaxpacket, transmaxpacket; unsigned int recvmaxpacket, transmaxpacket;
void* typedata; /* a pointer to type specific data */ void* typedata; /* a pointer to type specific data */
int writefd; /* read from wire, written to insecure side */ int writefd; /* read from wire, written to insecure side */
int readfd; /* read from insecure size, written to wire */ int readfd; /* read from insecure side, written to wire */
int errfd; /* used like writefd or readfd, depending if it's client or server. int errfd; /* used like writefd or readfd, depending if it's client or server.
Doesn't exactly belong here, but is cleaner here */ Doesn't exactly belong here, but is cleaner here */
circbuffer *writebuf; /* data from the wire, for local consumption */ circbuffer *writebuf; /* data from the wire, for local consumption */

View File

@@ -50,6 +50,10 @@ struct ChanSess {
/* exit details */ /* exit details */
struct exitinfo exit; struct exitinfo exit;
/* Used to set $SSH_CONNECTION in the child session.
Is only set temporarily before forking */
char *connection_string;
#ifndef DISABLE_X11FWD #ifndef DISABLE_X11FWD
struct Listener * x11listener; struct Listener * x11listener;
@@ -60,7 +64,7 @@ struct ChanSess {
unsigned char x11singleconn; unsigned char x11singleconn;
#endif #endif
#ifndef DISABLE_AGENTFWD #ifdef ENABLE_SVR_AGENTFWD
struct Listener * agentlistener; struct Listener * agentlistener;
char * agentfile; char * agentfile;
char * agentdir; char * agentdir;
@@ -81,6 +85,7 @@ void cli_chansess_winchange();
#ifdef ENABLE_CLI_NETCAT #ifdef ENABLE_CLI_NETCAT
void cli_send_netcat_request(); void cli_send_netcat_request();
#endif #endif
void cli_start_send_channel_request(struct Channel *channel, unsigned char *type);
void svr_chansessinitialise(); void svr_chansessinitialise();
extern const struct ChanType svrchansess; extern const struct ChanType svrchansess;

311
cli-agentfwd.c Normal file
View File

@@ -0,0 +1,311 @@
/*
* Dropbear - a SSH2 server
*
* Copyright (c) 2005 Matt Johnston
* 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"
#ifdef ENABLE_CLI_AGENTFWD
#include "agentfwd.h"
#include "session.h"
#include "ssh.h"
#include "dbutil.h"
#include "chansession.h"
#include "channel.h"
#include "packet.h"
#include "buffer.h"
#include "random.h"
#include "listener.h"
#include "runopts.h"
#include "atomicio.h"
#include "signkey.h"
#include "auth.h"
/* The protocol implemented to talk to OpenSSH's SSH2 agent is documented in
PROTOCOL.agent in recent OpenSSH source distributions (5.1p1 has it). */
static int new_agent_chan(struct Channel * channel);
const struct ChanType cli_chan_agent = {
0, /* sepfds */
"auth-agent@openssh.com",
new_agent_chan,
NULL,
NULL,
NULL
};
static int connect_agent() {
int fd = -1;
char* agent_sock = NULL;
agent_sock = getenv("SSH_AUTH_SOCK");
if (agent_sock == NULL)
return -1;
fd = connect_unix(agent_sock);
if (fd < 0) {
dropbear_log(LOG_INFO, "Failed to connect to agent");
}
return fd;
}
// handle a request for a connection to the locally running ssh-agent
// or forward.
static int new_agent_chan(struct Channel * channel) {
int fd = -1;
if (!cli_opts.agent_fwd)
return SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
fd = connect_agent();
if (cli_opts.agent_fd < 0) {
return SSH_OPEN_CONNECT_FAILED;
}
setnonblocking(fd);
ses.maxfd = MAX(ses.maxfd, fd);
channel->readfd = fd;
channel->writefd = fd;
// success
return 0;
}
/* Sends a request to the agent, returning a newly allocated buffer
* with the response */
/* This function will block waiting for a response - it will
* only be used by client authentication (not for forwarded requests)
* won't cause problems for interactivity. */
/* Packet format (from draft-ylonen)
4 bytes Length, msb first. Does not include length itself.
1 byte Packet type. The value 255 is reserved for future extensions.
data Any data, depending on packet type. Encoding as in the ssh packet
protocol.
*/
static buffer * agent_request(unsigned char type, buffer *data) {
buffer * payload = NULL;
buffer * inbuf = NULL;
size_t readlen = 0;
ssize_t ret;
const int fd = cli_opts.agent_fd;
unsigned int data_len = 0;
if (data)
{
data_len = data->len;
}
payload = buf_new(4 + 1 + data_len);
buf_putint(payload, 1 + data_len);
buf_putbyte(payload, type);
if (data) {
buf_putbytes(payload, data->data, data->len);
}
buf_setpos(payload, 0);
ret = atomicio(write, fd, buf_getptr(payload, payload->len), payload->len);
if ((size_t)ret != payload->len) {
TRACE(("write failed fd %d for agent_request, %s", fd, strerror(errno)))
goto out;
}
buf_free(payload);
payload = NULL;
TRACE(("Wrote out bytes for agent_request"))
/* Now we read the response */
inbuf = buf_new(4);
ret = atomicio(read, fd, buf_getwriteptr(inbuf, 4), 4);
if (ret != 4) {
TRACE(("read of length failed for agent_request"))
goto out;
}
buf_setpos(inbuf, 0);
buf_setlen(inbuf, ret);
readlen = buf_getint(inbuf);
if (readlen > MAX_AGENT_REPLY) {
TRACE(("agent reply is too big"));
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);
if ((size_t)ret != readlen) {
TRACE(("read of data failed for agent_request"))
goto out;
}
buf_incrwritepos(inbuf, readlen);
buf_setpos(inbuf, 0);
TRACE(("agent_request success, length %d", readlen))
out:
if (payload)
buf_free(payload);
return inbuf;
}
static void agent_get_key_list(m_list * ret_list)
{
buffer * inbuf = NULL;
unsigned int num = 0;
unsigned char packet_type;
unsigned int i;
int ret;
inbuf = agent_request(SSH2_AGENTC_REQUEST_IDENTITIES, NULL);
if (!inbuf) {
TRACE(("agent_request failed returning identities"))
goto out;
}
/* The reply has a format of:
byte SSH2_AGENT_IDENTITIES_ANSWER
uint32 num_keys
Followed by zero or more consecutive keys, encoded as:
string key_blob
string key_comment
*/
packet_type = buf_getbyte(inbuf);
if (packet_type != SSH2_AGENT_IDENTITIES_ANSWER) {
goto out;
}
num = buf_getint(inbuf);
for (i = 0; i < num; i++) {
sign_key * pubkey = NULL;
int key_type = DROPBEAR_SIGNKEY_ANY;
buffer * key_buf;
/* each public key is encoded as a string */
key_buf = buf_getstringbuf(inbuf);
pubkey = new_sign_key();
ret = buf_get_pub_key(key_buf, pubkey, &key_type);
buf_free(key_buf);
if (ret != DROPBEAR_SUCCESS) {
/* This is slack, properly would cleanup vars etc */
dropbear_exit("Bad pubkey received from agent");
}
pubkey->type = key_type;
pubkey->source = SIGNKEY_SOURCE_AGENT;
list_append(ret_list, pubkey);
/* We'll ignore the comment for now. might want it later.*/
buf_eatstring(inbuf);
}
out:
if (inbuf) {
buf_free(inbuf);
inbuf = NULL;
}
}
void cli_setup_agent(struct Channel *channel) {
if (!getenv("SSH_AUTH_SOCK")) {
return;
}
cli_start_send_channel_request(channel, "auth-agent-req@openssh.com");
/* Don't want replies */
buf_putbyte(ses.writepayload, 0);
encrypt_packet();
}
/* Returned keys are prepended to ret_list, which will
be updated. */
void cli_load_agent_keys(m_list *ret_list) {
/* agent_fd will be closed after successful auth */
cli_opts.agent_fd = connect_agent();
if (cli_opts.agent_fd < 0) {
return;
}
agent_get_key_list(ret_list);
}
void agent_buf_sign(buffer *sigblob, sign_key *key,
const unsigned char *data, unsigned int len) {
buffer *request_data = buf_new(MAX_PUBKEY_SIZE + len + 12);
buffer *response;
unsigned int keylen, siglen;
int packet_type;
/* Request format
byte SSH2_AGENTC_SIGN_REQUEST
string key_blob
string data
uint32 flags
*/
/* We write the key, then figure how long it was and write that */
//buf_putint(request_data, 0);
buf_put_pub_key(request_data, key, key->type);
keylen = request_data->len - 4;
//buf_setpos(request_data, 0);
//buf_putint(request_data, keylen);
//buf_setpos(request_data, request_data->len);
buf_putstring(request_data, data, len);
buf_putint(request_data, 0);
response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data);
buf_free(request_data);
if (!response) {
goto fail;
}
packet_type = buf_getbyte(response);
if (packet_type != SSH2_AGENT_SIGN_RESPONSE) {
goto fail;
}
/* Response format
byte SSH2_AGENT_SIGN_RESPONSE
string signature_blob
*/
siglen = buf_getint(response);
buf_putbytes(sigblob, buf_getptr(response, siglen), siglen);
buf_free(response);
return;
fail:
/* XXX don't fail badly here. instead propagate a failure code back up to
the cli auth pubkey code, and just remove this key from the list of
ones to try. */
dropbear_exit("Agent failed signing key");
}
#endif

View File

@@ -91,7 +91,7 @@ void recv_msg_userauth_banner() {
} }
} }
printf("%s\n", banner); fprintf(stderr, "%s\n", banner);
out: out:
m_free(banner); m_free(banner);
@@ -234,6 +234,10 @@ void recv_msg_userauth_success() {
ses.authstate.authdone = 1; ses.authstate.authdone = 1;
cli_ses.state = USERAUTH_SUCCESS_RCVD; cli_ses.state = USERAUTH_SUCCESS_RCVD;
cli_ses.lastauthtype = AUTH_TYPE_NONE; cli_ses.lastauthtype = AUTH_TYPE_NONE;
#ifdef ENABLE_CLI_PUBKEY_AUTH
cli_auth_pubkey_cleanup();
#endif
} }
void cli_auth_try() { void cli_auth_try() {

View File

@@ -30,6 +30,7 @@
#include "ssh.h" #include "ssh.h"
#include "runopts.h" #include "runopts.h"
#include "auth.h" #include "auth.h"
#include "agentfwd.h"
#ifdef ENABLE_CLI_PUBKEY_AUTH #ifdef ENABLE_CLI_PUBKEY_AUTH
static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
@@ -37,30 +38,23 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
/* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
* We use it to remove the key we tried from the list */ * We use it to remove the key we tried from the list */
void cli_pubkeyfail() { void cli_pubkeyfail() {
m_list_elem *iter;
struct SignKeyList *keyitem; for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
struct SignKeyList **previtem; sign_key *iter_key = (sign_key*)iter->item;
TRACE(("enter cli_pubkeyfail")) if (iter_key == cli_ses.lastprivkey)
previtem = &cli_opts.privkeys; {
/* found the failing key */
/* Find the key we failed with, and remove it */ list_remove(iter);
for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) { sign_key_free(iter_key);
if (keyitem == cli_ses.lastprivkey) { cli_ses.lastprivkey = NULL;
*previtem = keyitem->next; return;
} }
previtem = &keyitem;
} }
sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */
m_free(cli_ses.lastprivkey);
TRACE(("leave cli_pubkeyfail"))
} }
void recv_msg_userauth_pk_ok() { void recv_msg_userauth_pk_ok() {
m_list_elem *iter;
struct SignKeyList *keyitem = NULL;
buffer* keybuf = NULL; buffer* keybuf = NULL;
char* algotype = NULL; char* algotype = NULL;
unsigned int algolen; unsigned int algolen;
@@ -80,9 +74,9 @@ void recv_msg_userauth_pk_ok() {
/* Iterate through our keys, find which one it was that matched, and /* Iterate through our keys, find which one it was that matched, and
* send a real request with that key */ * send a real request with that key */
for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) { for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
sign_key *key = (sign_key*)iter->item;
if (keyitem->type != keytype) { if (key->type != keytype) {
/* Types differed */ /* Types differed */
TRACE(("types differed")) TRACE(("types differed"))
continue; continue;
@@ -90,7 +84,7 @@ void recv_msg_userauth_pk_ok() {
/* Now we compare the contents of the key */ /* Now we compare the contents of the key */
keybuf->pos = keybuf->len = 0; keybuf->pos = keybuf->len = 0;
buf_put_pub_key(keybuf, keyitem->key, keytype); buf_put_pub_key(keybuf, key, keytype);
buf_setpos(keybuf, 0); buf_setpos(keybuf, 0);
buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie
remotelen) which has already been taken from remotelen) which has already been taken from
@@ -114,11 +108,11 @@ void recv_msg_userauth_pk_ok() {
} }
buf_free(keybuf); buf_free(keybuf);
if (keyitem != NULL) { if (iter != NULL) {
TRACE(("matching key")) TRACE(("matching key"))
/* XXX TODO: if it's an encrypted key, here we ask for their /* XXX TODO: if it's an encrypted key, here we ask for their
* password */ * password */
send_msg_userauth_pubkey(keyitem->key, keytype, 1); send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1);
} else { } else {
TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part")) TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
} }
@@ -126,6 +120,25 @@ void recv_msg_userauth_pk_ok() {
TRACE(("leave recv_msg_userauth_pk_ok")) TRACE(("leave recv_msg_userauth_pk_ok"))
} }
void cli_buf_put_sign(buffer* buf, sign_key *key, int type,
const unsigned char *data, unsigned int len)
{
if (key->source == SIGNKEY_SOURCE_AGENT) {
/* Format the agent signature ourselves, as buf_put_sign would. */
buffer *sigblob;
sigblob = buf_new(MAX_PUBKEY_SIZE);
agent_buf_sign(sigblob, key, data, len);
buf_setpos(sigblob, 0);
buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
sigblob->len);
buf_free(sigblob);
} else {
buf_put_sign(buf, key, type, data, len);
}
}
/* TODO: make it take an agent reference to use as well */ /* TODO: make it take an agent reference to use as well */
static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
@@ -161,7 +174,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len); sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len);
buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE); buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE);
buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len); buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len); cli_buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len);
buf_free(sigbuf); /* Nothing confidential in the buffer */ buf_free(sigbuf); /* Nothing confidential in the buffer */
} }
@@ -169,20 +182,41 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
TRACE(("leave send_msg_userauth_pubkey")) TRACE(("leave send_msg_userauth_pubkey"))
} }
/* Returns 1 if a key was tried */
int cli_auth_pubkey() { int cli_auth_pubkey() {
TRACE(("enter cli_auth_pubkey")) TRACE(("enter cli_auth_pubkey"))
if (cli_opts.privkeys != NULL) { if (!cli_opts.agent_keys_loaded) {
/* get the list of available keys from the agent */
cli_load_agent_keys(cli_opts.privkeys);
cli_opts.agent_keys_loaded = 1;
}
if (cli_opts.privkeys->first) {
sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
/* Send a trial request */ /* Send a trial request */
send_msg_userauth_pubkey(cli_opts.privkeys->key, send_msg_userauth_pubkey(key, key->type, 0);
cli_opts.privkeys->type, 0); cli_ses.lastprivkey = key;
cli_ses.lastprivkey = cli_opts.privkeys;
TRACE(("leave cli_auth_pubkey-success")) TRACE(("leave cli_auth_pubkey-success"))
return 1; return 1;
} else { } else {
/* no more keys left */
TRACE(("leave cli_auth_pubkey-failure")) TRACE(("leave cli_auth_pubkey-failure"))
return 0; return 0;
} }
} }
void cli_auth_pubkey_cleanup() {
#ifdef ENABLE_CLI_AGENTFWD
m_close(cli_opts.agent_fd);
cli_opts.agent_fd = -1;
#endif
while (cli_opts.privkeys->first) {
sign_key * key = list_remove(cli_opts.privkeys->first);
sign_key_free(key);
}
}
#endif /* Pubkey auth */ #endif /* Pubkey auth */

View File

@@ -33,13 +33,12 @@
#include "runopts.h" #include "runopts.h"
#include "termcodes.h" #include "termcodes.h"
#include "chansession.h" #include "chansession.h"
#include "agentfwd.h"
static void cli_closechansess(struct Channel *channel); static void cli_closechansess(struct Channel *channel);
static int cli_initchansess(struct Channel *channel); static int cli_initchansess(struct Channel *channel);
static void cli_chansessreq(struct Channel *channel); static void cli_chansessreq(struct Channel *channel);
static void start_channel_request(struct Channel *channel, unsigned char *type);
static void send_chansess_pty_req(struct Channel *channel); static void send_chansess_pty_req(struct Channel *channel);
static void send_chansess_shell_req(struct Channel *channel); static void send_chansess_shell_req(struct Channel *channel);
@@ -92,7 +91,7 @@ static void cli_closechansess(struct Channel *UNUSED(channel)) {
} }
static void start_channel_request(struct Channel *channel, void cli_start_send_channel_request(struct Channel *channel,
unsigned char *type) { unsigned char *type) {
CHECKCLEARTOWRITE(); CHECKCLEARTOWRITE();
@@ -287,7 +286,7 @@ static void send_chansess_pty_req(struct Channel *channel) {
TRACE(("enter send_chansess_pty_req")) TRACE(("enter send_chansess_pty_req"))
start_channel_request(channel, "pty-req"); cli_start_send_channel_request(channel, "pty-req");
/* Don't want replies */ /* Don't want replies */
buf_putbyte(ses.writepayload, 0); buf_putbyte(ses.writepayload, 0);
@@ -330,7 +329,7 @@ static void send_chansess_shell_req(struct Channel *channel) {
reqtype = "shell"; reqtype = "shell";
} }
start_channel_request(channel, reqtype); cli_start_send_channel_request(channel, reqtype);
/* XXX TODO */ /* XXX TODO */
buf_putbyte(ses.writepayload, 0); /* Don't want replies */ buf_putbyte(ses.writepayload, 0); /* Don't want replies */
@@ -361,6 +360,12 @@ static int cli_initchansess(struct Channel *channel) {
cli_init_stdpipe_sess(channel); cli_init_stdpipe_sess(channel);
#ifdef ENABLE_CLI_AGENTFWD
if (cli_opts.agent_fwd) {
cli_setup_agent(channel);
}
#endif
if (cli_opts.wantpty) { if (cli_opts.wantpty) {
send_chansess_pty_req(channel); send_chansess_pty_req(channel);
} }
@@ -376,20 +381,20 @@ static int cli_initchansess(struct Channel *channel) {
#ifdef ENABLE_CLI_NETCAT #ifdef ENABLE_CLI_NETCAT
const struct ChanType cli_chan_netcat = {
0, /* sepfds */
"direct-tcpip",
cli_init_stdpipe_sess, /* inithandler */
NULL,
NULL,
cli_closechansess
};
void cli_send_netcat_request() { void cli_send_netcat_request() {
const unsigned char* source_host = "127.0.0.1"; const unsigned char* source_host = "127.0.0.1";
const int source_port = 22; 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; cli_opts.wantpty = 0;
if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat) if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat)
@@ -424,16 +429,3 @@ void cli_send_chansess_request() {
TRACE(("leave 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

View File

@@ -304,7 +304,7 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
fseek(hostsfile, 0, SEEK_END); /* In case it wasn't opened append */ fseek(hostsfile, 0, SEEK_END); /* In case it wasn't opened append */
buf_setpos(line, 0); buf_setpos(line, 0);
buf_setlen(line, 0); buf_setlen(line, 0);
buf_putbytes(line, ses.remotehost, hostlen); buf_putbytes(line, cli_opts.remotehost, hostlen);
buf_putbyte(line, ' '); buf_putbyte(line, ' ');
buf_putbytes(line, algoname, algolen); buf_putbytes(line, algoname, algolen);
buf_putbyte(line, ' '); buf_putbyte(line, ' ');
@@ -327,4 +327,5 @@ out:
if (line != NULL) { if (line != NULL) {
buf_free(line); buf_free(line);
} }
m_free(fingerprint);
} }

View File

@@ -32,7 +32,9 @@
static void cli_dropbear_exit(int exitcode, const char* format, va_list param); static void cli_dropbear_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_dropbear_log(int priority, const char* format, va_list param);
#ifdef ENABLE_CLI_PROXYCMD
static void cli_proxy_cmd(int *sock_in, int *sock_out); static void cli_proxy_cmd(int *sock_in, int *sock_out);
#endif
#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI) #if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI) #if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
@@ -43,8 +45,6 @@ int main(int argc, char ** argv) {
int sock_in, sock_out; int sock_in, sock_out;
char* error = NULL; char* error = NULL;
char* hostandport;
int len;
_dropbear_exit = cli_dropbear_exit; _dropbear_exit = cli_dropbear_exit;
_dropbear_log = cli_dropbear_log; _dropbear_log = cli_dropbear_log;
@@ -63,6 +63,7 @@ int main(int argc, char ** argv) {
#ifdef ENABLE_CLI_PROXYCMD #ifdef ENABLE_CLI_PROXYCMD
if (cli_opts.proxycmd) { if (cli_opts.proxycmd) {
cli_proxy_cmd(&sock_in, &sock_out); cli_proxy_cmd(&sock_in, &sock_out);
m_free(cli_opts.proxycmd);
} else } else
#endif #endif
{ {
@@ -75,14 +76,7 @@ int main(int argc, char ** argv) {
dropbear_exit("%s", error); dropbear_exit("%s", error);
} }
/* Set up the host:port log */ cli_session(sock_in, sock_out);
len = strlen(cli_opts.remotehost);
len += 10; /* 16 bit port and leeway*/
hostandport = (char*)m_malloc(len);
snprintf(hostandport, len, "%s:%s",
cli_opts.remotehost, cli_opts.remoteport);
cli_session(sock_in, sock_out, hostandport);
/* not reached */ /* not reached */
return -1; return -1;
@@ -132,6 +126,7 @@ static void exec_proxy_cmd(void *user_data_cmd) {
dropbear_exit("Failed to run '%s'\n", cmd); dropbear_exit("Failed to run '%s'\n", cmd);
} }
#ifdef ENABLE_CLI_PROXYCMD
static void cli_proxy_cmd(int *sock_in, int *sock_out) { static void cli_proxy_cmd(int *sock_in, int *sock_out) {
int ret; int ret;
@@ -144,3 +139,4 @@ static void cli_proxy_cmd(int *sock_in, int *sock_out) {
*sock_in = *sock_out = -1; *sock_in = *sock_out = -1;
} }
} }
#endif // ENABLE_CLI_PROXYCMD

View File

@@ -29,6 +29,7 @@
#include "dbutil.h" #include "dbutil.h"
#include "algo.h" #include "algo.h"
#include "tcpfwd.h" #include "tcpfwd.h"
#include "list.h"
cli_runopts cli_opts; /* GLOBAL */ cli_runopts cli_opts; /* GLOBAL */
@@ -40,7 +41,7 @@ static void fill_own_user();
static void loadidentityfile(const char* filename); static void loadidentityfile(const char* filename);
#endif #endif
#ifdef ENABLE_CLI_ANYTCPFWD #ifdef ENABLE_CLI_ANYTCPFWD
static void addforward(const char* str, struct TCPFwdList** fwdlist); static void addforward(const char* str, m_list *fwdlist);
#endif #endif
#ifdef ENABLE_CLI_NETCAT #ifdef ENABLE_CLI_NETCAT
static void add_netcat(const char *str); static void add_netcat(const char *str);
@@ -66,6 +67,9 @@ static void printhelp() {
#ifdef ENABLE_CLI_PUBKEY_AUTH #ifdef ENABLE_CLI_PUBKEY_AUTH
"-i <identityfile> (multiple allowed)\n" "-i <identityfile> (multiple allowed)\n"
#endif #endif
#ifdef ENABLE_CLI_AGENTFWD
"-A Enable agent auth forwarding\n"
#endif
#ifdef ENABLE_CLI_LOCALTCPFWD #ifdef ENABLE_CLI_LOCALTCPFWD
"-L <listenport:remotehost:remoteport> Local port forwarding\n" "-L <listenport:remotehost:remoteport> Local port forwarding\n"
"-g Allow remote hosts to connect to forwarded ports\n" "-g Allow remote hosts to connect to forwarded ports\n"
@@ -91,7 +95,6 @@ static void printhelp() {
} }
void cli_getopts(int argc, char ** argv) { void cli_getopts(int argc, char ** argv) {
unsigned int i, j; unsigned int i, j;
char ** next = 0; char ** next = 0;
unsigned int cmdlen; unsigned int cmdlen;
@@ -112,6 +115,7 @@ void cli_getopts(int argc, char ** argv) {
char* recv_window_arg = NULL; char* recv_window_arg = NULL;
char* keepalive_arg = NULL; char* keepalive_arg = NULL;
char* idle_timeout_arg = NULL; char* idle_timeout_arg = NULL;
char *host_arg = NULL;
/* see printhelp() for options */ /* see printhelp() for options */
cli_opts.progname = argv[0]; cli_opts.progname = argv[0];
@@ -125,17 +129,24 @@ void cli_getopts(int argc, char ** argv) {
cli_opts.always_accept_key = 0; cli_opts.always_accept_key = 0;
cli_opts.is_subsystem = 0; cli_opts.is_subsystem = 0;
#ifdef ENABLE_CLI_PUBKEY_AUTH #ifdef ENABLE_CLI_PUBKEY_AUTH
cli_opts.privkeys = NULL; cli_opts.privkeys = list_new();
#endif #endif
#ifdef ENABLE_CLI_LOCALTCPFWD #ifdef ENABLE_CLI_LOCALTCPFWD
cli_opts.localfwds = NULL; cli_opts.localfwds = list_new();
opts.listen_fwd_all = 0; opts.listen_fwd_all = 0;
#endif #endif
#ifdef ENABLE_CLI_REMOTETCPFWD #ifdef ENABLE_CLI_REMOTETCPFWD
cli_opts.remotefwds = NULL; cli_opts.remotefwds = list_new();
#endif
#ifdef ENABLE_CLI_AGENTFWD
cli_opts.agent_fwd = 0;
cli_opts.agent_keys_loaded = 0;
#endif #endif
#ifdef ENABLE_CLI_PROXYCMD #ifdef ENABLE_CLI_PROXYCMD
cli_opts.proxycmd = NULL; cli_opts.proxycmd = NULL;
#endif
#ifndef DISABLE_ZLIB
opts.enable_compress = 1;
#endif #endif
/* not yet /* not yet
opts.ipv4 = 1; opts.ipv4 = 1;
@@ -158,7 +169,7 @@ void cli_getopts(int argc, char ** argv) {
#ifdef ENABLE_CLI_REMOTETCPFWD #ifdef ENABLE_CLI_REMOTETCPFWD
if (nextisremote) { if (nextisremote) {
TRACE(("nextisremote true")) TRACE(("nextisremote true"))
addforward(argv[i], &cli_opts.remotefwds); addforward(argv[i], cli_opts.remotefwds);
nextisremote = 0; nextisremote = 0;
continue; continue;
} }
@@ -166,7 +177,7 @@ void cli_getopts(int argc, char ** argv) {
#ifdef ENABLE_CLI_LOCALTCPFWD #ifdef ENABLE_CLI_LOCALTCPFWD
if (nextislocal) { if (nextislocal) {
TRACE(("nextislocal true")) TRACE(("nextislocal true"))
addforward(argv[i], &cli_opts.localfwds); addforward(argv[i], cli_opts.localfwds);
nextislocal = 0; nextislocal = 0;
continue; continue;
} }
@@ -266,6 +277,11 @@ void cli_getopts(int argc, char ** argv) {
case 'I': case 'I':
next = &idle_timeout_arg; next = &idle_timeout_arg;
break; break;
#ifdef ENABLE_CLI_AGENTFWD
case 'A':
cli_opts.agent_fwd = 1;
break;
#endif
#ifdef DEBUG_TRACE #ifdef DEBUG_TRACE
case 'v': case 'v':
debug_trace = 1; debug_trace = 1;
@@ -304,12 +320,8 @@ void cli_getopts(int argc, char ** argv) {
/* Either the hostname or commands */ /* Either the hostname or commands */
if (cli_opts.remotehost == NULL) { if (host_arg == NULL) {
#ifdef ENABLE_CLI_MULTIHOP host_arg = argv[i];
parse_multihop_hostname(argv[i], argv[0]);
#else
parse_hostname(argv[i]);
#endif
} else { } else {
/* this is part of the commands to send - after this we /* this is part of the commands to send - after this we
@@ -338,7 +350,7 @@ void cli_getopts(int argc, char ** argv) {
/* And now a few sanity checks and setup */ /* And now a few sanity checks and setup */
if (cli_opts.remotehost == NULL) { if (host_arg == NULL) {
printhelp(); printhelp();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -369,15 +381,19 @@ void cli_getopts(int argc, char ** argv) {
} }
} }
if (keepalive_arg) { if (keepalive_arg) {
if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) { unsigned int val;
if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
dropbear_exit("Bad keepalive '%s'", keepalive_arg); dropbear_exit("Bad keepalive '%s'", keepalive_arg);
} }
opts.keepalive_secs = val;
} }
if (idle_timeout_arg) { if (idle_timeout_arg) {
if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) { unsigned int val;
if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg); dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
} }
opts.idle_timeout_secs = val;
} }
#ifdef ENABLE_CLI_NETCAT #ifdef ENABLE_CLI_NETCAT
@@ -385,36 +401,73 @@ void cli_getopts(int argc, char ** argv) {
dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd); dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
} }
#endif #endif
/* The hostname gets set up last, since
* in multi-hop mode it will require knowledge
* of other flags such as -i */
#ifdef ENABLE_CLI_MULTIHOP
parse_multihop_hostname(host_arg, argv[0]);
#else
parse_hostname(host_arg);
#endif
} }
#ifdef ENABLE_CLI_PUBKEY_AUTH #ifdef ENABLE_CLI_PUBKEY_AUTH
static void loadidentityfile(const char* filename) { static void loadidentityfile(const char* filename) {
struct SignKeyList * nextkey;
sign_key *key; sign_key *key;
int keytype; int keytype;
key = new_sign_key(); key = new_sign_key();
keytype = DROPBEAR_SIGNKEY_ANY; keytype = DROPBEAR_SIGNKEY_ANY;
if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) { if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
fprintf(stderr, "Failed loading keyfile '%s'\n", filename); fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
sign_key_free(key); sign_key_free(key);
} else { } else {
key->type = keytype;
nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); key->source = SIGNKEY_SOURCE_RAW_FILE;
nextkey->key = key; key->filename = m_strdup(filename);
nextkey->next = cli_opts.privkeys; list_append(cli_opts.privkeys, key);
nextkey->type = keytype;
cli_opts.privkeys = nextkey;
} }
} }
#endif #endif
#ifdef ENABLE_CLI_MULTIHOP #ifdef ENABLE_CLI_MULTIHOP
static char*
multihop_passthrough_args() {
char *ret;
int total;
unsigned int len = 0;
m_list_elem *iter;
/* Fill out -i and -W options that make sense for all
* the intermediate processes */
for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
{
sign_key * key = (sign_key*)iter->item;
len += 3 + strlen(key->filename);
}
len += 20; // space for -W <size>, terminator.
ret = m_malloc(len);
total = 0;
if (opts.recv_window != DEFAULT_RECV_WINDOW)
{
int written = snprintf(ret+total, len-total, "-W %d", opts.recv_window);
total += written;
}
for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
{
sign_key * key = (sign_key*)iter->item;
const size_t size = len - total;
int written = snprintf(ret+total, size, "-i %s", key->filename);
dropbear_assert((unsigned int)written < size);
total += written;
}
return ret;
}
/* Sets up 'onion-forwarding' connections. This will spawn /* Sets up 'onion-forwarding' connections. This will spawn
* a separate dbclient process for each hop. * a separate dbclient process for each hop.
* As an example, if the cmdline is * As an example, if the cmdline is
@@ -429,7 +482,8 @@ static void loadidentityfile(const char* filename) {
*/ */
static void parse_multihop_hostname(const char* orighostarg, const char* argv0) { static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
char *userhostarg = NULL; char *userhostarg = NULL;
char *last_hop = NULL;; char *hostbuf = NULL;
char *last_hop = NULL;
char *remainder = NULL; char *remainder = NULL;
/* both scp and rsync parse a user@host argument /* both scp and rsync parse a user@host argument
@@ -441,11 +495,12 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
&& strchr(cli_opts.username, ',') && strchr(cli_opts.username, ',')
&& strchr(cli_opts.username, '@')) { && strchr(cli_opts.username, '@')) {
unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2; unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2;
userhostarg = m_malloc(len); hostbuf = m_malloc(len);
snprintf(userhostarg, len, "%s@%s", cli_opts.username, orighostarg); snprintf(hostbuf, len, "%s@%s", cli_opts.username, orighostarg);
} else { } else {
userhostarg = m_strdup(orighostarg); hostbuf = m_strdup(orighostarg);
} }
userhostarg = hostbuf;
last_hop = strrchr(userhostarg, ','); last_hop = strrchr(userhostarg, ',');
if (last_hop) { if (last_hop) {
@@ -463,19 +518,28 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0)
if (last_hop) { if (last_hop) {
/* Set up the proxycmd */ /* Set up the proxycmd */
unsigned int cmd_len = 0; unsigned int cmd_len = 0;
char *passthrough_args = multihop_passthrough_args();
if (cli_opts.proxycmd) { if (cli_opts.proxycmd) {
dropbear_exit("-J can't be used with multihop mode"); dropbear_exit("-J can't be used with multihop mode");
} }
if (cli_opts.remoteport == NULL) { if (cli_opts.remoteport == NULL) {
cli_opts.remoteport = "22"; cli_opts.remoteport = "22";
} }
cmd_len = strlen(remainder) cmd_len = strlen(argv0) + strlen(remainder)
+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport) + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
+ strlen(argv0) + 30; + strlen(passthrough_args)
+ 30;
cli_opts.proxycmd = m_malloc(cmd_len); cli_opts.proxycmd = m_malloc(cmd_len);
snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s", snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s",
argv0, cli_opts.remotehost, cli_opts.remoteport, remainder); argv0, cli_opts.remotehost, cli_opts.remoteport,
passthrough_args, remainder);
#ifndef DISABLE_ZLIB
/* The stream will be incompressible since it's encrypted. */
opts.enable_compress = 0;
#endif
m_free(passthrough_args);
} }
m_free(hostbuf);
} }
#endif /* !ENABLE_CLI_MULTIHOP */ #endif /* !ENABLE_CLI_MULTIHOP */
@@ -566,14 +630,14 @@ static void fill_own_user() {
#ifdef ENABLE_CLI_ANYTCPFWD #ifdef ENABLE_CLI_ANYTCPFWD
/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding /* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding
* set, and add it to the forwarding list */ * set, and add it to the forwarding list */
static void addforward(const char* origstr, struct TCPFwdList** fwdlist) { static void addforward(const char* origstr, m_list *fwdlist) {
char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL; char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL;
char * listenaddr = NULL; char * listenaddr = NULL;
char * listenport = NULL; char * listenport = NULL;
char * connectaddr = NULL; char * connectaddr = NULL;
char * connectport = NULL; char * connectport = NULL;
struct TCPFwdList* newfwd = NULL; struct TCPFwdEntry* newfwd = NULL;
char * str = NULL; char * str = NULL;
TRACE(("enter addforward")) TRACE(("enter addforward"))
@@ -618,7 +682,9 @@ static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
connectport = part3; connectport = part3;
} }
newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList)); }
newfwd = m_malloc(sizeof(struct TCPFwdEntry));
/* Now we check the ports - note that the port ints are unsigned, /* Now we check the ports - note that the port ints are unsigned,
* the check later only checks for >= MAX_PORT */ * the check later only checks for >= MAX_PORT */
@@ -646,8 +712,7 @@ static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
} }
newfwd->have_reply = 0; newfwd->have_reply = 0;
newfwd->next = *fwdlist; list_append(fwdlist, newfwd);
*fwdlist = newfwd;
TRACE(("leave addforward: done")) TRACE(("leave addforward: done"))
return; return;

View File

@@ -35,6 +35,7 @@
#include "service.h" #include "service.h"
#include "runopts.h" #include "runopts.h"
#include "chansession.h" #include "chansession.h"
#include "agentfwd.h"
static void cli_remoteclosed(); static void cli_remoteclosed();
static void cli_sessionloop(); static void cli_sessionloop();
@@ -74,17 +75,20 @@ static const packettype cli_packettypes[] = {
static const struct ChanType *cli_chantypes[] = { static const struct ChanType *cli_chantypes[] = {
#ifdef ENABLE_CLI_REMOTETCPFWD #ifdef ENABLE_CLI_REMOTETCPFWD
&cli_chan_tcpremote, &cli_chan_tcpremote,
#endif
#ifdef ENABLE_CLI_AGENTFWD
&cli_chan_agent,
#endif #endif
NULL /* Null termination */ NULL /* Null termination */
}; };
void cli_session(int sock_in, int sock_out, char* remotehost) { void cli_session(int sock_in, int sock_out) {
seedrandom(); seedrandom();
crypto_init(); crypto_init();
common_session_init(sock_in, sock_out, remotehost); common_session_init(sock_in, sock_out);
chaninitialise(cli_chantypes); chaninitialise(cli_chantypes);
@@ -231,7 +235,7 @@ static void cli_sessionloop() {
cli_send_netcat_request(); cli_send_netcat_request();
} else } else
#endif #endif
if (!cli_opts.no_cmd) { if (!cli_opts.no_cmd) {
cli_send_chansess_request(); cli_send_chansess_request();
} }
TRACE(("leave cli_sessionloop: running")) TRACE(("leave cli_sessionloop: running"))

View File

@@ -61,30 +61,25 @@ static const struct ChanType cli_chan_tcplocal = {
#ifdef ENABLE_CLI_LOCALTCPFWD #ifdef ENABLE_CLI_LOCALTCPFWD
void setup_localtcp() { void setup_localtcp() {
m_list_elem *iter;
int ret; int ret;
TRACE(("enter setup_localtcp")) TRACE(("enter setup_localtcp"))
if (cli_opts.localfwds == NULL) { for (iter = cli_opts.localfwds->first; iter; iter = iter->next) {
TRACE(("cli_opts.localfwds == NULL")) struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item;
}
while (cli_opts.localfwds != NULL) {
ret = cli_localtcp( ret = cli_localtcp(
cli_opts.localfwds->listenaddr, fwd->listenaddr,
cli_opts.localfwds->listenport, fwd->listenport,
cli_opts.localfwds->connectaddr, fwd->connectaddr,
cli_opts.localfwds->connectport); fwd->connectport);
if (ret == DROPBEAR_FAILURE) { if (ret == DROPBEAR_FAILURE) {
dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d", dropbear_log(LOG_WARNING, "Failed local port forward %s:%d:%s:%d",
cli_opts.localfwds->listenaddr, fwd->listenaddr,
cli_opts.localfwds->listenport, fwd->listenport,
cli_opts.localfwds->connectaddr, fwd->connectaddr,
cli_opts.localfwds->connectport); fwd->connectport);
} }
cli_opts.localfwds = cli_opts.localfwds->next;
} }
TRACE(("leave setup_localtcp")) TRACE(("leave setup_localtcp"))
@@ -156,63 +151,49 @@ static void send_msg_global_request_remotetcp(const char *addr, int port) {
* being in the same order as we sent the requests. This is the ordering * being in the same order as we sent the requests. This is the ordering
* of the cli_opts.remotefwds list */ * of the cli_opts.remotefwds list */
void cli_recv_msg_request_success() { void cli_recv_msg_request_success() {
/* Nothing in the packet. We just mark off that we have received the reply, /* Nothing in the packet. We just mark off that we have received the reply,
* so that we can report failure for later ones. */ * so that we can report failure for later ones. */
struct TCPFwdList * iter = NULL; m_list_elem * iter = NULL;
for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
iter = cli_opts.remotefwds; struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
while (iter != NULL) { if (!fwd->have_reply) {
if (!iter->have_reply) fwd->have_reply = 1;
{
iter->have_reply = 1;
return; return;
} }
iter = iter->next;
} }
} }
void cli_recv_msg_request_failure() { void cli_recv_msg_request_failure() {
struct TCPFwdList * iter = NULL; m_list_elem *iter;
for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
iter = cli_opts.remotefwds; struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
while (iter != NULL) { if (!fwd->have_reply) {
if (!iter->have_reply) fwd->have_reply = 1;
{ dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", fwd->listenport, fwd->connectaddr, fwd->connectport);
iter->have_reply = 1;
dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", iter->listenport, iter->connectaddr, iter->connectport);
return; return;
} }
iter = iter->next;
} }
} }
void setup_remotetcp() { void setup_remotetcp() {
m_list_elem *iter;
struct TCPFwdList * iter = NULL;
TRACE(("enter setup_remotetcp")) TRACE(("enter setup_remotetcp"))
if (cli_opts.remotefwds == NULL) { for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
TRACE(("cli_opts.remotefwds == NULL")) struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
} if (!fwd->listenaddr)
iter = cli_opts.remotefwds;
while (iter != NULL) {
if (!iter->listenaddr)
{ {
// we store the addresses so that we can compare them // we store the addresses so that we can compare them
// when the server sends them back // when the server sends them back
if (opts.listen_fwd_all) { if (opts.listen_fwd_all) {
iter->listenaddr = m_strdup(""); fwd->listenaddr = m_strdup("");
} else { } else {
iter->listenaddr = m_strdup("localhost"); fwd->listenaddr = m_strdup("localhost");
} }
} }
send_msg_global_request_remotetcp(iter->listenaddr, iter->listenport); send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport);
iter = iter->next;
} }
TRACE(("leave setup_remotetcp")) TRACE(("leave setup_remotetcp"))
} }
@@ -220,7 +201,8 @@ static int newtcpforwarded(struct Channel * channel) {
char *origaddr = NULL; char *origaddr = NULL;
unsigned int origport; unsigned int origport;
struct TCPFwdList * iter = NULL; m_list_elem * iter = NULL;
struct TCPFwdEntry *fwd;
char portstring[NI_MAXSERV]; char portstring[NI_MAXSERV];
int sock; int sock;
int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
@@ -229,14 +211,12 @@ static int newtcpforwarded(struct Channel * channel) {
origport = buf_getint(ses.payload); origport = buf_getint(ses.payload);
/* Find which port corresponds */ /* Find which port corresponds */
iter = cli_opts.remotefwds; for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
fwd = (struct TCPFwdEntry*)iter->item;
while (iter != NULL) { if (origport == fwd->listenport
if (origport == iter->listenport && (strcmp(origaddr, fwd->listenaddr) == 0)) {
&& (strcmp(origaddr, iter->listenaddr) == 0)) {
break; break;
} }
iter = iter->next;
} }
if (iter == NULL) { if (iter == NULL) {
@@ -247,8 +227,8 @@ static int newtcpforwarded(struct Channel * channel) {
goto out; goto out;
} }
snprintf(portstring, sizeof(portstring), "%d", iter->connectport); snprintf(portstring, sizeof(portstring), "%d", fwd->connectport);
sock = connect_remote(iter->connectaddr, portstring, 1, NULL); sock = connect_remote(fwd->connectaddr, portstring, 1, NULL);
if (sock < 0) { if (sock < 0) {
TRACE(("leave newtcpdirect: sock failed")) TRACE(("leave newtcpdirect: sock failed"))
err = SSH_OPEN_CONNECT_FAILED; err = SSH_OPEN_CONNECT_FAILED;
@@ -265,7 +245,7 @@ static int newtcpforwarded(struct Channel * channel) {
err = SSH_OPEN_IN_PROGRESS; err = SSH_OPEN_IN_PROGRESS;
out: out:
m_free(origaddr); m_free(origaddr);
TRACE(("leave newtcpdirect: err %d", err)) TRACE(("leave newtcpdirect: err %d", err))
return err; return err;
} }

View File

@@ -31,7 +31,9 @@
static int void_cipher(const unsigned char* in, unsigned char* out, static int void_cipher(const unsigned char* in, unsigned char* out,
unsigned long len, void *cipher_state) { unsigned long len, void *cipher_state) {
memcpy(out, in, len); if (in != out) {
memmove(out, in, len);
}
return CRYPT_OK; return CRYPT_OK;
} }
@@ -166,11 +168,16 @@ algo_type sshhashes[] = {
{NULL, 0, NULL, 0, NULL} {NULL, 0, NULL, 0, NULL}
}; };
algo_type sshcompress[] = {
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
algo_type ssh_compress[] = {
{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL}, {"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL},
{"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL}, {"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL},
{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
{NULL, 0, NULL, 0, NULL}
};
#endif #endif
algo_type ssh_nocompress[] = {
{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
{NULL, 0, NULL, 0, NULL} {NULL, 0, NULL, 0, NULL}
}; };

View File

@@ -33,6 +33,7 @@
#include "packet.h" #include "packet.h"
#include "bignum.h" #include "bignum.h"
#include "random.h" #include "random.h"
#include "runopts.h"
/* diffie-hellman-group1-sha1 value for p */ /* diffie-hellman-group1-sha1 value for p */
static const unsigned char dh_p_val[] = { static const unsigned char dh_p_val[] = {
@@ -91,10 +92,10 @@ void send_msg_kexinit() {
buf_put_algolist(ses.writepayload, sshhashes); buf_put_algolist(ses.writepayload, sshhashes);
/* compression_algorithms_client_to_server */ /* compression_algorithms_client_to_server */
buf_put_algolist(ses.writepayload, sshcompress); buf_put_algolist(ses.writepayload, ses.compress_algos);
/* compression_algorithms_server_to_client */ /* compression_algorithms_server_to_client */
buf_put_algolist(ses.writepayload, sshcompress); buf_put_algolist(ses.writepayload, ses.compress_algos);
/* languages_client_to_server */ /* languages_client_to_server */
buf_putstring(ses.writepayload, "", 0); buf_putstring(ses.writepayload, "", 0);
@@ -180,8 +181,16 @@ void recv_msg_newkeys() {
/* Set up the kex for the first time */ /* Set up the kex for the first time */
void kexfirstinitialise() { void kexfirstinitialise() {
ses.kexstate.donefirstkex = 0; ses.kexstate.donefirstkex = 0;
#ifndef DISABLE_ZLIB
if (opts.enable_compress) {
ses.compress_algos = ssh_compress;
} else
#endif
{
ses.compress_algos = ssh_nocompress;
}
kexinitialise(); kexinitialise();
} }
@@ -272,8 +281,8 @@ void gen_new_keys() {
recv_IV = S2C_IV; recv_IV = S2C_IV;
trans_key = C2S_key; trans_key = C2S_key;
recv_key = S2C_key; recv_key = S2C_key;
C2S_keysize = ses.newkeys->trans_algo_crypt->keysize; C2S_keysize = ses.newkeys->trans.algo_crypt->keysize;
S2C_keysize = ses.newkeys->recv_algo_crypt->keysize; S2C_keysize = ses.newkeys->recv.algo_crypt->keysize;
mactransletter = 'E'; mactransletter = 'E';
macrecvletter = 'F'; macrecvletter = 'F';
} else { } else {
@@ -281,8 +290,8 @@ void gen_new_keys() {
recv_IV = C2S_IV; recv_IV = C2S_IV;
trans_key = S2C_key; trans_key = S2C_key;
recv_key = C2S_key; recv_key = C2S_key;
C2S_keysize = ses.newkeys->recv_algo_crypt->keysize; C2S_keysize = ses.newkeys->recv.algo_crypt->keysize;
S2C_keysize = ses.newkeys->trans_algo_crypt->keysize; S2C_keysize = ses.newkeys->trans.algo_crypt->keysize;
mactransletter = 'F'; mactransletter = 'F';
macrecvletter = 'E'; macrecvletter = 'E';
} }
@@ -292,31 +301,33 @@ void gen_new_keys() {
hashkeys(C2S_key, C2S_keysize, &hs, 'C'); hashkeys(C2S_key, C2S_keysize, &hs, 'C');
hashkeys(S2C_key, S2C_keysize, &hs, 'D'); hashkeys(S2C_key, S2C_keysize, &hs, 'D');
recv_cipher = find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name); recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
if (recv_cipher < 0) if (recv_cipher < 0)
dropbear_exit("crypto error"); dropbear_exit("crypto error");
if (ses.newkeys->recv_crypt_mode->start(recv_cipher, if (ses.newkeys->recv.crypt_mode->start(recv_cipher,
recv_IV, recv_key, recv_IV, recv_key,
ses.newkeys->recv_algo_crypt->keysize, 0, ses.newkeys->recv.algo_crypt->keysize, 0,
&ses.newkeys->recv_cipher_state) != CRYPT_OK) { &ses.newkeys->recv.cipher_state) != CRYPT_OK) {
dropbear_exit("crypto error"); dropbear_exit("crypto error");
} }
trans_cipher = find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name); trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
if (trans_cipher < 0) if (trans_cipher < 0)
dropbear_exit("crypto error"); dropbear_exit("crypto error");
if (ses.newkeys->trans_crypt_mode->start(trans_cipher, if (ses.newkeys->trans.crypt_mode->start(trans_cipher,
trans_IV, trans_key, trans_IV, trans_key,
ses.newkeys->trans_algo_crypt->keysize, 0, ses.newkeys->trans.algo_crypt->keysize, 0,
&ses.newkeys->trans_cipher_state) != CRYPT_OK) { &ses.newkeys->trans.cipher_state) != CRYPT_OK) {
dropbear_exit("crypto error"); dropbear_exit("crypto error");
} }
/* MAC keys */ /* MAC keys */
hashkeys(ses.newkeys->transmackey, hashkeys(ses.newkeys->trans.mackey,
ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter); ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter);
hashkeys(ses.newkeys->recvmackey, hashkeys(ses.newkeys->recv.mackey,
ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter); ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter);
ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hashdesc->name),
ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name),
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
gen_new_zstreams(); gen_new_zstreams();
@@ -334,15 +345,15 @@ void gen_new_keys() {
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
int is_compress_trans() { int is_compress_trans() {
return ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB return ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB
|| (ses.authstate.authdone || (ses.authstate.authdone
&& ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY); && ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
} }
int is_compress_recv() { int is_compress_recv() {
return ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB return ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB
|| (ses.authstate.authdone || (ses.authstate.authdone
&& ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY); && ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
} }
/* Set up new zlib compression streams, close the old ones. Only /* Set up new zlib compression streams, close the old ones. Only
@@ -350,47 +361,49 @@ int is_compress_recv() {
static void gen_new_zstreams() { static void gen_new_zstreams() {
/* create new zstreams */ /* create new zstreams */
if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB
|| ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { || ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream)); ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream));
ses.newkeys->recv_zstream->zalloc = Z_NULL; ses.newkeys->recv.zstream->zalloc = Z_NULL;
ses.newkeys->recv_zstream->zfree = Z_NULL; ses.newkeys->recv.zstream->zfree = Z_NULL;
if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) { if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) {
dropbear_exit("zlib error"); dropbear_exit("zlib error");
} }
} else { } else {
ses.newkeys->recv_zstream = NULL; ses.newkeys->recv.zstream = NULL;
} }
if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB
|| ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { || ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream)); ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream));
ses.newkeys->trans_zstream->zalloc = Z_NULL; ses.newkeys->trans.zstream->zalloc = Z_NULL;
ses.newkeys->trans_zstream->zfree = Z_NULL; ses.newkeys->trans.zstream->zfree = Z_NULL;
if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION) if (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION,
Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS,
DROPBEAR_ZLIB_MEM_LEVEL, Z_DEFAULT_STRATEGY)
!= Z_OK) { != Z_OK) {
dropbear_exit("zlib error"); dropbear_exit("zlib error");
} }
} else { } else {
ses.newkeys->trans_zstream = NULL; ses.newkeys->trans.zstream = NULL;
} }
/* clean up old keys */ /* clean up old keys */
if (ses.keys->recv_zstream != NULL) { if (ses.keys->recv.zstream != NULL) {
if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) { if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
/* Z_DATA_ERROR is ok, just means that stream isn't ended */ /* Z_DATA_ERROR is ok, just means that stream isn't ended */
dropbear_exit("crypto error"); dropbear_exit("crypto error");
} }
m_free(ses.keys->recv_zstream); m_free(ses.keys->recv.zstream);
} }
if (ses.keys->trans_zstream != NULL) { if (ses.keys->trans.zstream != NULL) {
if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) { if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) {
/* Z_DATA_ERROR is ok, just means that stream isn't ended */ /* Z_DATA_ERROR is ok, just means that stream isn't ended */
dropbear_exit("crypto error"); dropbear_exit("crypto error");
} }
m_free(ses.keys->trans_zstream); m_free(ses.keys->trans.zstream);
} }
} }
#endif /* DISABLE_ZLIB */ #endif /* DISABLE_ZLIB */
@@ -666,7 +679,7 @@ static void read_kex_algos() {
TRACE(("hash s2c is %s", s2c_hash_algo->name)) TRACE(("hash s2c is %s", s2c_hash_algo->name))
/* compression_algorithms_client_to_server */ /* compression_algorithms_client_to_server */
c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); c2s_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
if (c2s_comp_algo == NULL) { if (c2s_comp_algo == NULL) {
erralgo = "comp c->s"; erralgo = "comp c->s";
goto error; goto error;
@@ -674,7 +687,7 @@ static void read_kex_algos() {
TRACE(("hash c2s is %s", c2s_comp_algo->name)) TRACE(("hash c2s is %s", c2s_comp_algo->name))
/* compression_algorithms_server_to_client */ /* compression_algorithms_server_to_client */
s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); s2c_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
if (s2c_comp_algo == NULL) { if (s2c_comp_algo == NULL) {
erralgo = "comp s->c"; erralgo = "comp s->c";
goto error; goto error;
@@ -698,36 +711,36 @@ static void read_kex_algos() {
/* Handle the asymmetry */ /* Handle the asymmetry */
if (IS_DROPBEAR_CLIENT) { if (IS_DROPBEAR_CLIENT) {
ses.newkeys->recv_algo_crypt = ses.newkeys->recv.algo_crypt =
(struct dropbear_cipher*)s2c_cipher_algo->data; (struct dropbear_cipher*)s2c_cipher_algo->data;
ses.newkeys->trans_algo_crypt = ses.newkeys->trans.algo_crypt =
(struct dropbear_cipher*)c2s_cipher_algo->data; (struct dropbear_cipher*)c2s_cipher_algo->data;
ses.newkeys->recv_crypt_mode = ses.newkeys->recv.crypt_mode =
(struct dropbear_cipher_mode*)s2c_cipher_algo->mode; (struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
ses.newkeys->trans_crypt_mode = ses.newkeys->trans.crypt_mode =
(struct dropbear_cipher_mode*)c2s_cipher_algo->mode; (struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
ses.newkeys->recv_algo_mac = ses.newkeys->recv.algo_mac =
(struct dropbear_hash*)s2c_hash_algo->data; (struct dropbear_hash*)s2c_hash_algo->data;
ses.newkeys->trans_algo_mac = ses.newkeys->trans.algo_mac =
(struct dropbear_hash*)c2s_hash_algo->data; (struct dropbear_hash*)c2s_hash_algo->data;
ses.newkeys->recv_algo_comp = s2c_comp_algo->val; ses.newkeys->recv.algo_comp = s2c_comp_algo->val;
ses.newkeys->trans_algo_comp = c2s_comp_algo->val; ses.newkeys->trans.algo_comp = c2s_comp_algo->val;
} else { } else {
/* SERVER */ /* SERVER */
ses.newkeys->recv_algo_crypt = ses.newkeys->recv.algo_crypt =
(struct dropbear_cipher*)c2s_cipher_algo->data; (struct dropbear_cipher*)c2s_cipher_algo->data;
ses.newkeys->trans_algo_crypt = ses.newkeys->trans.algo_crypt =
(struct dropbear_cipher*)s2c_cipher_algo->data; (struct dropbear_cipher*)s2c_cipher_algo->data;
ses.newkeys->recv_crypt_mode = ses.newkeys->recv.crypt_mode =
(struct dropbear_cipher_mode*)c2s_cipher_algo->mode; (struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
ses.newkeys->trans_crypt_mode = ses.newkeys->trans.crypt_mode =
(struct dropbear_cipher_mode*)s2c_cipher_algo->mode; (struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
ses.newkeys->recv_algo_mac = ses.newkeys->recv.algo_mac =
(struct dropbear_hash*)c2s_hash_algo->data; (struct dropbear_hash*)c2s_hash_algo->data;
ses.newkeys->trans_algo_mac = ses.newkeys->trans.algo_mac =
(struct dropbear_hash*)s2c_hash_algo->data; (struct dropbear_hash*)s2c_hash_algo->data;
ses.newkeys->recv_algo_comp = c2s_comp_algo->val; ses.newkeys->recv.algo_comp = c2s_comp_algo->val;
ses.newkeys->trans_algo_comp = s2c_comp_algo->val; ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
} }
/* reserved for future extensions */ /* reserved for future extensions */

View File

@@ -52,12 +52,10 @@ int exitflag = 0; /* GLOBAL */
/* called only at the start of a session, set up initial state */ /* called only at the start of a session, set up initial state */
void common_session_init(int sock_in, int sock_out, char* remotehost) { void common_session_init(int sock_in, int sock_out) {
TRACE(("enter session_init")) TRACE(("enter session_init"))
ses.remotehost = remotehost;
ses.sock_in = sock_in; ses.sock_in = sock_in;
ses.sock_out = sock_out; ses.sock_out = sock_out;
ses.maxfd = MAX(sock_in, sock_out); ses.maxfd = MAX(sock_in, sock_out);
@@ -71,6 +69,9 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
} }
setnonblocking(ses.signal_pipe[0]); setnonblocking(ses.signal_pipe[0]);
setnonblocking(ses.signal_pipe[1]); setnonblocking(ses.signal_pipe[1]);
ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]);
ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]);
kexfirstinitialise(); /* initialise the kex state */ kexfirstinitialise(); /* initialise the kex state */
@@ -78,7 +79,6 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
ses.transseq = 0; ses.transseq = 0;
ses.readbuf = NULL; ses.readbuf = NULL;
ses.decryptreadbuf = NULL;
ses.payload = NULL; ses.payload = NULL;
ses.recvseq = 0; ses.recvseq = 0;
@@ -95,22 +95,22 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) {
/* set all the algos to none */ /* set all the algos to none */
ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context)); ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
ses.newkeys = NULL; ses.newkeys = NULL;
ses.keys->recv_algo_crypt = &dropbear_nocipher; ses.keys->recv.algo_crypt = &dropbear_nocipher;
ses.keys->trans_algo_crypt = &dropbear_nocipher; ses.keys->trans.algo_crypt = &dropbear_nocipher;
ses.keys->recv_crypt_mode = &dropbear_mode_none; ses.keys->recv.crypt_mode = &dropbear_mode_none;
ses.keys->trans_crypt_mode = &dropbear_mode_none; ses.keys->trans.crypt_mode = &dropbear_mode_none;
ses.keys->recv_algo_mac = &dropbear_nohash; ses.keys->recv.algo_mac = &dropbear_nohash;
ses.keys->trans_algo_mac = &dropbear_nohash; ses.keys->trans.algo_mac = &dropbear_nohash;
ses.keys->algo_kex = -1; ses.keys->algo_kex = -1;
ses.keys->algo_hostkey = -1; ses.keys->algo_hostkey = -1;
ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE; ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE;
ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE; ses.keys->trans.algo_comp = DROPBEAR_COMP_NONE;
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
ses.keys->recv_zstream = NULL; ses.keys->recv.zstream = NULL;
ses.keys->trans_zstream = NULL; ses.keys->trans.zstream = NULL;
#endif #endif
/* key exchange buffers */ /* key exchange buffers */

View File

@@ -82,7 +82,8 @@ AC_CHECK_DECL(__UCLIBC__,
],,,) ],,,)
# Checks for libraries. # Checks for libraries.
AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt") AC_CHECK_LIB(crypt, crypt, CRYPTLIB="-lcrypt")
AC_SUBST(CRYPTLIB)
# Check if zlib is needed # Check if zlib is needed
AC_ARG_WITH(zlib, AC_ARG_WITH(zlib,
@@ -145,6 +146,7 @@ AC_ARG_ENABLE(pam,
if test "x$enableval" = "xyes"; then if test "x$enableval" = "xyes"; then
AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***])) AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***]))
AC_MSG_NOTICE(Enabling PAM) AC_MSG_NOTICE(Enabling PAM)
AC_CHECK_FUNCS(pam_fail_delay)
else else
AC_DEFINE(DISABLE_PAM,, Use PAM) AC_DEFINE(DISABLE_PAM,, Use PAM)
AC_MSG_NOTICE(Disabling PAM) AC_MSG_NOTICE(Disabling PAM)

View File

@@ -82,6 +82,11 @@ by the ssh server.
Always accept hostkeys if they are unknown. If a hostkey mismatch occurs the Always accept hostkeys if they are unknown. If a hostkey mismatch occurs the
connection will abort as normal. connection will abort as normal.
.TP .TP
.B \-A
Forward agent connections to the remote host. dbclient will use any
OpenSSH-style agent program if available ($SSH_AUTH_SOCK will be set) for
public key authentication. Forwarding is only enabled if -A is specified.
.TP
.B \-W \fIwindowsize .B \-W \fIwindowsize
Specify the per-channel receive window buffer size. Increasing this Specify the per-channel receive window buffer size. Increasing this
may improve network performance at the expense of memory use. Use -h to see the may improve network performance at the expense of memory use. Use -h to see the

144
dbutil.c
View File

@@ -295,6 +295,28 @@ int dropbear_listen(const char* address, const char* port,
return nsock; return nsock;
} }
/* Connect to a given unix socket. The socket is blocking */
#ifdef ENABLE_CONNECT_UNIX
int connect_unix(const char* path) {
struct sockaddr_un addr;
int fd = -1;
memset((void*)&addr, 0x0, sizeof(addr));
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
TRACE(("Failed to open unix socket"))
return -1;
}
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
TRACE(("Failed to connect to '%s' socket", path))
return -1;
}
return fd;
}
#endif
/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
* return immediately if nonblocking is set. On failure, if errstring * return immediately if nonblocking is set. On failure, if errstring
* wasn't null, it will be a newly malloced error message */ * wasn't null, it will be a newly malloced error message */
@@ -341,15 +363,7 @@ int connect_remote(const char* remotehost, const char* remoteport,
} }
if (nonblocking) { if (nonblocking) {
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { setnonblocking(sock);
close(sock);
sock = -1;
if (errstring != NULL && *errstring == NULL) {
*errstring = m_strdup("Failed non-blocking");
}
TRACE(("Failed non-blocking: %s", strerror(errno)))
continue;
}
} }
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
@@ -525,14 +539,47 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
execv(usershell, argv); execv(usershell, argv);
} }
void get_socket_address(int fd, char **local_host, char **local_port,
char **remote_host, char **remote_port, int host_lookup)
{
struct sockaddr_storage addr;
socklen_t addrlen;
if (local_host || local_port) {
addrlen = sizeof(addr);
if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
dropbear_exit("Failed socket address: %s", strerror(errno));
}
getaddrstring(&addr, local_host, local_port, host_lookup);
}
if (remote_host || remote_port) {
addrlen = sizeof(addr);
if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
dropbear_exit("Failed socket address: %s", strerror(errno));
}
getaddrstring(&addr, remote_host, remote_port, host_lookup);
}
}
/* Return a string representation of the socket address passed. The return /* Return a string representation of the socket address passed. The return
* value is allocated with malloc() */ * value is allocated with malloc() */
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) { void getaddrstring(struct sockaddr_storage* addr,
char **ret_host, char **ret_port,
int host_lookup) {
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; char host[NI_MAXHOST+1], serv[NI_MAXSERV+1];
char *retstring = NULL;
int ret;
unsigned int len; unsigned int len;
int ret;
int flags = NI_NUMERICSERV | NI_NUMERICHOST;
#ifndef DO_HOST_LOOKUP
host_lookup = 0;
#endif
if (host_lookup) {
flags = NI_NUMERICSERV;
}
len = sizeof(struct sockaddr_storage); len = sizeof(struct sockaddr_storage);
/* Some platforms such as Solaris 8 require that len is the length /* Some platforms such as Solaris 8 require that len is the length
@@ -550,67 +597,28 @@ unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
#endif #endif
#endif #endif
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1,
sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST); serv, sizeof(serv)-1, flags);
if (ret != 0) { if (ret != 0) {
/* This is a fairly bad failure - it'll fallback to IP if it if (host_lookup) {
* just can't resolve */ /* On some systems (Darwin does it) we get EINTR from getnameinfo
dropbear_exit("failed lookup (%d, %d)", ret, errno); * somehow. Eew. So we'll just return the IP, since that doesn't seem
* to exhibit that behaviour. */
getaddrstring(addr, ret_host, ret_port, 0);
return;
} else {
/* if we can't do a numeric lookup, something's gone terribly wrong */
dropbear_exit("Failed lookup: %s", gai_strerror(ret));
}
} }
if (withport) { if (ret_host) {
len = strlen(hbuf) + 2 + strlen(sbuf); *ret_host = m_strdup(host);
retstring = (char*)m_malloc(len);
snprintf(retstring, len, "%s:%s", hbuf, sbuf);
} else {
retstring = m_strdup(hbuf);
} }
if (ret_port) {
return retstring; *ret_port = m_strdup(serv);
}
/* Get the hostname corresponding to the address addr. On failure, the IP
* address is returned. The return value is allocated with strdup() */
char* getaddrhostname(struct sockaddr_storage * addr) {
char hbuf[NI_MAXHOST];
char sbuf[NI_MAXSERV];
int ret;
unsigned int len;
#ifdef DO_HOST_LOOKUP
const int flags = NI_NUMERICSERV;
#else
const int flags = NI_NUMERICHOST | NI_NUMERICSERV;
#endif
len = sizeof(struct sockaddr_storage);
/* Some platforms such as Solaris 8 require that len is the length
* of the specific structure. */
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
if (addr->ss_family == AF_INET) {
len = sizeof(struct sockaddr_in);
} }
#ifdef AF_INET6
if (addr->ss_family == AF_INET6) {
len = sizeof(struct sockaddr_in6);
}
#endif
#endif
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
sbuf, sizeof(sbuf), flags);
if (ret != 0) {
/* On some systems (Darwin does it) we get EINTR from getnameinfo
* somehow. Eew. So we'll just return the IP, since that doesn't seem
* to exhibit that behaviour. */
return getaddrstring(addr, 0);
}
return m_strdup(hbuf);
} }
#ifdef DEBUG_TRACE #ifdef DEBUG_TRACE

View File

@@ -46,15 +46,20 @@ void printhex(const char * label, const unsigned char * buf, int len);
extern int debug_trace; extern int debug_trace;
#endif #endif
char * stripcontrol(const char * text); char * stripcontrol(const char * text);
unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport); void get_socket_address(int fd, char **local_host, char **local_port,
char **remote_host, char **remote_port, int host_lookup);
void getaddrstring(struct sockaddr_storage* addr,
char **ret_host, char **ret_port, int host_lookup);
int dropbear_listen(const char* address, const char* port, int dropbear_listen(const char* address, const char* port,
int *socks, unsigned int sockcount, char **errstring, int *maxfd); int *socks, unsigned int sockcount, char **errstring, int *maxfd);
int spawn_command(void(*exec_fn)(void *user_data), void *exec_data, int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
int *writefd, int *readfd, int *errfd, pid_t *pid); int *writefd, int *readfd, int *errfd, pid_t *pid);
void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell); void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
#ifdef ENABLE_CONNECT_UNIX
int connect_unix(const char* addr);
#endif
int connect_remote(const char* remotehost, const char* remoteport, int connect_remote(const char* remotehost, const char* remoteport,
int nonblocking, char ** errstring); int nonblocking, char ** errstring);
char* getaddrhostname(struct sockaddr_storage * addr);
int buf_readfile(buffer* buf, const char* filename); int buf_readfile(buffer* buf, const char* filename);
int buf_getline(buffer * line, FILE * authfile); int buf_getline(buffer * line, FILE * authfile);

View File

@@ -39,7 +39,7 @@
* Caution: Don't use this in an unfriendly environment (ie unfirewalled), * Caution: Don't use this in an unfriendly environment (ie unfirewalled),
* since the printing may not sanitise strings etc. This will add a reasonable * since the printing may not sanitise strings etc. This will add a reasonable
* amount to your executable size. */ * amount to your executable size. */
/*#define DEBUG_TRACE*/ /*#define DEBUG_TRACE */
/* All functions writing to the cleartext payload buffer call /* All functions writing to the cleartext payload buffer call
* CHECKCLEARTOWRITE() before writing. This is only really useful if you're * CHECKCLEARTOWRITE() before writing. This is only really useful if you're

View File

@@ -7,7 +7,7 @@ dropbear \- lightweight SSH2 server
.I banner\fR] [\-d .I banner\fR] [\-d
.I dsskey\fR] [\-r .I dsskey\fR] [\-r
.I rsakey\fR] [\-p .I rsakey\fR] [\-p
.IR port ] .IR [address:]port ]
.SH DESCRIPTION .SH DESCRIPTION
.B dropbear .B dropbear
is a SSH 2 server designed to be small enough to be used in small memory is a SSH 2 server designed to be small enough to be used in small memory
@@ -154,6 +154,34 @@ By default the file /etc/motd will be printed for any login shell (unless
disabled at compile-time). This can also be disabled per-user disabled at compile-time). This can also be disabled per-user
by creating a file ~/.hushlogin . by creating a file ~/.hushlogin .
.SH ENVIRONMENT VARIABLES
Dropbear sets the standard variables USER, LOGNAME, HOME, SHELL, PATH, and TERM.
The variables below are set for sessions as appropriate.
.TP
.B SSH_TTY
This is set to the allocated TTY if a PTY was used.
.TP
.B SSH_CONNECTION
Contains "<remote_ip> <remote_port> <local_ip> <local_port>".
.TP
.B DISPLAY
Set X11 forwarding is used.
.TP
.B SSH_ORIGINAL_COMMAND
If a 'command=' authorized_keys option was used, the original command is specified
in this variable. If a shell was requested this is set to an empty value.
.TP
.B SSH_AUTH_SOCK
Set to a forwarded ssh-agent connection.
.SH AUTHOR .SH AUTHOR
Matt Johnston (matt@ucc.asn.au). Matt Johnston (matt@ucc.asn.au).
.br .br

49
list.c Normal file
View File

@@ -0,0 +1,49 @@
#include "options.h"
#include "dbutil.h"
#include "list.h"
void list_append(m_list *list, void *item) {
m_list_elem *elem;
elem = m_malloc(sizeof(*elem));
elem->item = item;
elem->list = list;
elem->next = NULL;
if (!list->first) {
list->first = elem;
elem->prev = NULL;
} else {
elem->prev = list->last;
list->last->next = elem;
}
list->last = elem;
}
m_list * list_new() {
m_list *ret = m_malloc(sizeof(m_list));
ret->first = ret->last = NULL;
return ret;
}
void * list_remove(m_list_elem *elem) {
void *item = elem->item;
m_list *list = elem->list;
if (list->first == elem)
{
list->first = elem->next;
}
if (list->last == elem)
{
list->last = elem->prev;
}
if (elem->prev)
{
elem->prev->next = elem->next;
}
if (elem->next)
{
elem->next->prev = elem->prev;
}
m_free(elem);
return item;
}

28
list.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef _DROPBEAR_LIST_H
#define _DROPBEAR_LIST_H
struct _m_list;
struct _m_list_elem {
void *item;
struct _m_list_elem *next;
struct _m_list_elem *prev;
struct _m_list *list;
};
typedef struct _m_list_elem m_list_elem;
struct _m_list {
m_list_elem *first;
m_list_elem *last;
};
typedef struct _m_list m_list;
m_list * list_new();
void list_append(m_list *list, void *item);
/* returns the item for the element removed */
void * list_remove(m_list_elem *elem);
#endif /* _DROPBEAR_LIST_H */

View File

@@ -46,9 +46,10 @@
/*#define NO_FAST_EXPTMOD*/ /*#define NO_FAST_EXPTMOD*/
/* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save /* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save
several kB in binary size, however will make the symmetrical ciphers (AES, DES several kB in binary size however will make the symmetrical ciphers and hashes
etc) slower (perhaps by 50%). Recommended for most small systems. */ slower, perhaps by 50%. Recommended for small systems that aren't doing
#define DROPBEAR_SMALL_CODE much traffic. */
/*#define DROPBEAR_SMALL_CODE*/
/* Enable X11 Forwarding - server only */ /* Enable X11 Forwarding - server only */
#define ENABLE_X11FWD #define ENABLE_X11FWD
@@ -64,7 +65,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
#define ENABLE_SVR_REMOTETCPFWD #define ENABLE_SVR_REMOTETCPFWD
/* Enable Authentication Agent Forwarding - server only for now */ /* Enable Authentication Agent Forwarding - server only for now */
#define ENABLE_AGENTFWD #define ENABLE_SVR_AGENTFWD
#define ENABLE_CLI_AGENTFWD
/* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to /* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to
@@ -85,7 +87,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
#define DROPBEAR_AES128 #define DROPBEAR_AES128
#define DROPBEAR_3DES #define DROPBEAR_3DES
#define DROPBEAR_AES256 #define DROPBEAR_AES256
#define DROPBEAR_BLOWFISH /* Compiling in Blowfish will add ~6kB to runtime heap memory usage */
/*#define DROPBEAR_BLOWFISH*/
#define DROPBEAR_TWOFISH256 #define DROPBEAR_TWOFISH256
#define DROPBEAR_TWOFISH128 #define DROPBEAR_TWOFISH128
@@ -128,6 +131,21 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
* if the random number source isn't good. In general this isn't required */ * if the random number source isn't good. In general this isn't required */
/* #define DSS_PROTOK */ /* #define DSS_PROTOK */
/* Control the memory/performance/compression tradeoff for zlib.
* Set windowBits=8, memLevel=1 for least memory usage, see your system's
* zlib.h for full details.
* Default settings (windowBits=15, memLevel=8) will use
* 256kB for compression + 32kB for decompression.
* windowBits=8, memLevel=1 will use 10kB compression + 32kB decompression.
* Note that windowBits is only set for deflate() - inflate() always uses the
* default of 15 so as to interoperate with other clients. */
#ifndef DROPBEAR_ZLIB_WINDOW_BITS
#define DROPBEAR_ZLIB_WINDOW_BITS 15
#endif
#ifndef DROPBEAR_ZLIB_MEM_LEVEL
#define DROPBEAR_ZLIB_MEM_LEVEL 8
#endif
/* Whether to do reverse DNS lookups. */ /* Whether to do reverse DNS lookups. */
#define DO_HOST_LOOKUP #define DO_HOST_LOOKUP
@@ -154,7 +172,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
/*#define ENABLE_SVR_PAM_AUTH*/ /*#define ENABLE_SVR_PAM_AUTH*/
#define ENABLE_SVR_PUBKEY_AUTH #define ENABLE_SVR_PUBKEY_AUTH
/* Wether to ake public key options in authorized_keys file into account */ /* Whether to take public key options in
* authorized_keys file into account */
#ifdef ENABLE_SVR_PUBKEY_AUTH #ifdef ENABLE_SVR_PUBKEY_AUTH
#define ENABLE_SVR_PUBKEY_OPTIONS #define ENABLE_SVR_PUBKEY_OPTIONS
#endif #endif
@@ -220,7 +239,7 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
/* The command to invoke for xauth when using X11 forwarding. /* The command to invoke for xauth when using X11 forwarding.
* "-q" for quiet */ * "-q" for quiet */
#ifndef XAUTH_COMMAND #ifndef XAUTH_COMMAND
#define XAUTH_COMMAND "/usr/X11R6/bin/xauth -q" #define XAUTH_COMMAND "/usr/bin/X11/xauth -q"
#endif #endif
/* if you want to enable running an sftp server (such as the one included with /* if you want to enable running an sftp server (such as the one included with
@@ -246,13 +265,19 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
significant difference to network performance. 24kB was empirically significant difference to network performance. 24kB was empirically
chosen for a 100mbit ethernet network. The value can be altered at chosen for a 100mbit ethernet network. The value can be altered at
runtime with the -W argument. */ runtime with the -W argument. */
#ifndef DEFAULT_RECV_WINDOW
#define DEFAULT_RECV_WINDOW 24576 #define DEFAULT_RECV_WINDOW 24576
#endif
/* Maximum size of a received SSH data packet - this _MUST_ be >= 32768 /* Maximum size of a received SSH data packet - this _MUST_ be >= 32768
in order to interoperate with other implementations */ in order to interoperate with other implementations */
#ifndef RECV_MAX_PAYLOAD_LEN
#define RECV_MAX_PAYLOAD_LEN 32768 #define RECV_MAX_PAYLOAD_LEN 32768
#endif
/* Maximum size of a transmitted data packet - this can be any value, /* Maximum size of a transmitted data packet - this can be any value,
though increasing it may not make a significant difference. */ though increasing it may not make a significant difference. */
#ifndef TRANS_MAX_PAYLOAD_LEN
#define TRANS_MAX_PAYLOAD_LEN 16384 #define TRANS_MAX_PAYLOAD_LEN 16384
#endif
/* Ensure that data is transmitted every KEEPALIVE seconds. This can /* Ensure that data is transmitted every KEEPALIVE seconds. This can
be overridden at runtime with -K. 0 disables keepalives */ be overridden at runtime with -K. 0 disables keepalives */

308
packet.c
View File

@@ -35,9 +35,11 @@
#include "auth.h" #include "auth.h"
#include "channel.h" #include "channel.h"
static void read_packet_init(); static int read_packet_init();
static void writemac(buffer * outputbuffer, buffer * clearwritebuf); static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
static int checkmac(buffer* hashbuf, buffer* readbuf); buffer * clear_buf, unsigned int clear_len,
unsigned char *output_mac);
static int checkmac();
#define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */ #define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */
#define ZLIB_DECOMPRESS_INCR 100 #define ZLIB_DECOMPRESS_INCR 100
@@ -102,18 +104,18 @@ void read_packet() {
unsigned char blocksize; unsigned char blocksize;
TRACE(("enter read_packet")) TRACE(("enter read_packet"))
blocksize = ses.keys->recv_algo_crypt->blocksize; blocksize = ses.keys->recv.algo_crypt->blocksize;
if (ses.readbuf == NULL || ses.readbuf->len < blocksize) { if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
int ret;
/* In the first blocksize of a packet */ /* In the first blocksize of a packet */
/* Read the first blocksize of the packet, so we can decrypt it and /* Read the first blocksize of the packet, so we can decrypt it and
* find the length of the whole packet */ * find the length of the whole packet */
read_packet_init(); ret = read_packet_init();
/* If we don't have the length of decryptreadbuf, we didn't read if (ret == DROPBEAR_FAILURE) {
* a whole blocksize and should exit */ /* didn't read enough to determine the length */
if (ses.decryptreadbuf->len == 0) {
TRACE(("leave read_packet: packetinit done")) TRACE(("leave read_packet: packetinit done"))
return; return;
} }
@@ -121,7 +123,6 @@ void read_packet() {
/* Attempt to read the remainder of the packet, note that there /* Attempt to read the remainder of the packet, note that there
* mightn't be any available (EAGAIN) */ * mightn't be any available (EAGAIN) */
dropbear_assert(ses.readbuf != NULL);
maxlen = ses.readbuf->len - ses.readbuf->pos; maxlen = ses.readbuf->len - ses.readbuf->pos;
len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen); len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen);
@@ -151,60 +152,61 @@ void read_packet() {
/* Function used to read the initial portion of a packet, and determine the /* Function used to read the initial portion of a packet, and determine the
* length. Only called during the first BLOCKSIZE of a packet. */ * length. Only called during the first BLOCKSIZE of a packet. */
static void read_packet_init() { /* Returns DROPBEAR_SUCCESS if the length is determined,
* DROPBEAR_FAILURE otherwise */
static int read_packet_init() {
unsigned int maxlen; unsigned int maxlen;
int len; int slen;
unsigned char blocksize; unsigned int len;
unsigned char macsize; unsigned int blocksize;
unsigned int macsize;
blocksize = ses.keys->recv_algo_crypt->blocksize; blocksize = ses.keys->recv.algo_crypt->blocksize;
macsize = ses.keys->recv_algo_mac->hashsize; macsize = ses.keys->recv.algo_mac->hashsize;
if (ses.readbuf == NULL) { if (ses.readbuf == NULL) {
/* start of a new packet */ /* start of a new packet */
ses.readbuf = buf_new(INIT_READBUF); ses.readbuf = buf_new(INIT_READBUF);
dropbear_assert(ses.decryptreadbuf == NULL);
ses.decryptreadbuf = buf_new(blocksize);
} }
maxlen = blocksize - ses.readbuf->pos; maxlen = blocksize - ses.readbuf->pos;
/* read the rest of the packet if possible */ /* read the rest of the packet if possible */
len = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen), slen = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
maxlen); maxlen);
if (len == 0) { if (slen == 0) {
ses.remoteclosed(); ses.remoteclosed();
} }
if (len < 0) { if (slen < 0) {
if (errno == EINTR) { if (errno == EINTR) {
TRACE(("leave read_packet_init: EINTR")) TRACE(("leave read_packet_init: EINTR"))
return; return DROPBEAR_FAILURE;
} }
dropbear_exit("error reading: %s", strerror(errno)); dropbear_exit("error reading: %s", strerror(errno));
} }
buf_incrwritepos(ses.readbuf, len); buf_incrwritepos(ses.readbuf, slen);
if ((unsigned int)len != maxlen) { if ((unsigned int)slen != maxlen) {
/* don't have enough bytes to determine length, get next time */ /* don't have enough bytes to determine length, get next time */
return; return DROPBEAR_FAILURE;
} }
/* now we have the first block, need to get packet length, so we decrypt /* now we have the first block, need to get packet length, so we decrypt
* the first block (only need first 4 bytes) */ * the first block (only need first 4 bytes) */
buf_setpos(ses.readbuf, 0); buf_setpos(ses.readbuf, 0);
if (ses.keys->recv_crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize),
buf_getwriteptr(ses.decryptreadbuf,blocksize), buf_getwriteptr(ses.readbuf, blocksize),
blocksize, blocksize,
&ses.keys->recv_cipher_state) != CRYPT_OK) { &ses.keys->recv.cipher_state) != CRYPT_OK) {
dropbear_exit("error decrypting"); dropbear_exit("error decrypting");
} }
buf_setlen(ses.decryptreadbuf, blocksize); len = buf_getint(ses.readbuf) + 4 + macsize;
len = buf_getint(ses.decryptreadbuf) + 4 + macsize;
TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize))
buf_setpos(ses.readbuf, blocksize);
/* check packet length */ /* check packet length */
if ((len > RECV_MAX_PACKET_LEN) || if ((len > RECV_MAX_PACKET_LEN) ||
@@ -213,9 +215,12 @@ static void read_packet_init() {
dropbear_exit("bad packet size %d", len); dropbear_exit("bad packet size %d", len);
} }
buf_resize(ses.readbuf, len); if (len > ses.readbuf->size) {
buf_resize(ses.readbuf, len);
}
buf_setlen(ses.readbuf, len); buf_setlen(ses.readbuf, len);
buf_setpos(ses.readbuf, blocksize);
return DROPBEAR_SUCCESS;
} }
/* handle the received packet */ /* handle the received packet */
@@ -227,69 +232,58 @@ void decrypt_packet() {
unsigned int len; unsigned int len;
TRACE(("enter decrypt_packet")) TRACE(("enter decrypt_packet"))
blocksize = ses.keys->recv_algo_crypt->blocksize; blocksize = ses.keys->recv.algo_crypt->blocksize;
macsize = ses.keys->recv_algo_mac->hashsize; macsize = ses.keys->recv.algo_mac->hashsize;
ses.kexstate.datarecv += ses.readbuf->len; ses.kexstate.datarecv += ses.readbuf->len;
/* we've already decrypted the first blocksize in read_packet_init */ /* we've already decrypted the first blocksize in read_packet_init */
buf_setpos(ses.readbuf, blocksize); buf_setpos(ses.readbuf, blocksize);
buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize); /* decrypt it in-place */
buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size); len = ses.readbuf->len - macsize - ses.readbuf->pos;
buf_setpos(ses.decryptreadbuf, blocksize); if (ses.keys->recv.crypt_mode->decrypt(
buf_getptr(ses.readbuf, len),
/* decrypt it */ buf_getwriteptr(ses.readbuf, len),
while (ses.readbuf->pos < ses.readbuf->len - macsize) { len,
if (ses.keys->recv_crypt_mode->decrypt( &ses.keys->recv.cipher_state) != CRYPT_OK) {
buf_getptr(ses.readbuf, blocksize), dropbear_exit("error decrypting");
buf_getwriteptr(ses.decryptreadbuf, blocksize),
blocksize,
&ses.keys->recv_cipher_state) != CRYPT_OK) {
dropbear_exit("error decrypting");
}
buf_incrpos(ses.readbuf, blocksize);
buf_incrwritepos(ses.decryptreadbuf, blocksize);
} }
buf_incrpos(ses.readbuf, len);
/* check the hmac */ /* check the hmac */
buf_setpos(ses.readbuf, ses.readbuf->len - macsize); if (checkmac() != DROPBEAR_SUCCESS) {
if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) {
dropbear_exit("Integrity error"); dropbear_exit("Integrity error");
} }
/* readbuf no longer required */
buf_free(ses.readbuf);
ses.readbuf = NULL;
/* get padding length */ /* get padding length */
buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF); buf_setpos(ses.readbuf, PACKET_PADDING_OFF);
padlen = buf_getbyte(ses.decryptreadbuf); padlen = buf_getbyte(ses.readbuf);
/* payload length */ /* payload length */
/* - 4 - 1 is for LEN and PADLEN values */ /* - 4 - 1 is for LEN and PADLEN values */
len = ses.decryptreadbuf->len - padlen - 4 - 1; len = ses.readbuf->len - padlen - 4 - 1 - macsize;
if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) { if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) {
dropbear_exit("bad packet size"); dropbear_exit("bad packet size");
} }
buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF); buf_setpos(ses.readbuf, PACKET_PAYLOAD_OFF);
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
if (is_compress_recv()) { if (is_compress_recv()) {
/* decompress */ /* decompress */
ses.payload = buf_decompress(ses.decryptreadbuf, len); ses.payload = buf_decompress(ses.readbuf, len);
} else } else
#endif #endif
{ {
/* copy payload */ /* copy payload */
ses.payload = buf_new(len); ses.payload = buf_new(len);
memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len); memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len);
buf_incrlen(ses.payload, len); buf_incrlen(ses.payload, len);
} }
buf_free(ses.decryptreadbuf); buf_free(ses.readbuf);
ses.decryptreadbuf = NULL; ses.readbuf = NULL;
buf_setpos(ses.payload, 0); buf_setpos(ses.payload, 0);
ses.recvseq++; ses.recvseq++;
@@ -297,49 +291,22 @@ void decrypt_packet() {
TRACE(("leave decrypt_packet")) TRACE(("leave decrypt_packet"))
} }
/* Checks the mac in hashbuf, for the data in readbuf. /* Checks the mac at the end of a decrypted readbuf.
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
static int checkmac(buffer* macbuf, buffer* sourcebuf) { static int checkmac() {
unsigned int macsize; unsigned char mac_bytes[MAX_MAC_LEN];
hmac_state hmac; unsigned int mac_size, contents_len;
unsigned char tempbuf[MAX_MAC_LEN];
unsigned long bufsize;
unsigned int len;
macsize = ses.keys->recv_algo_mac->hashsize;
if (macsize == 0) {
return DROPBEAR_SUCCESS;
}
/* calculate the mac */
if (hmac_init(&hmac,
find_hash(ses.keys->recv_algo_mac->hashdesc->name),
ses.keys->recvmackey,
ses.keys->recv_algo_mac->keysize)
!= CRYPT_OK) {
dropbear_exit("HMAC error");
}
/* sequence number */ mac_size = ses.keys->trans.algo_mac->hashsize;
STORE32H(ses.recvseq, tempbuf); contents_len = ses.readbuf->len - mac_size;
if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) {
dropbear_exit("HMAC error");
}
buf_setpos(sourcebuf, 0); buf_setpos(ses.readbuf, 0);
len = sourcebuf->len; make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) {
dropbear_exit("HMAC error");
}
bufsize = sizeof(tempbuf);
if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) {
dropbear_exit("HMAC error");
}
/* compare the hash */ /* compare the hash */
if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) { buf_setpos(ses.readbuf, contents_len);
if (memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) {
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
} else { } else {
return DROPBEAR_SUCCESS; return DROPBEAR_SUCCESS;
@@ -354,7 +321,7 @@ static buffer* buf_decompress(buffer* buf, unsigned int len) {
buffer * ret; buffer * ret;
z_streamp zstream; z_streamp zstream;
zstream = ses.keys->recv_zstream; zstream = ses.keys->recv.zstream;
ret = buf_new(len); ret = buf_new(len);
zstream->avail_in = len; zstream->avail_in = len;
@@ -450,11 +417,12 @@ void maybe_flush_reply_queue() {
void encrypt_packet() { void encrypt_packet() {
unsigned char padlen; unsigned char padlen;
unsigned char blocksize, macsize; unsigned char blocksize, mac_size;
buffer * writebuf; /* the packet which will go on the wire */ buffer * writebuf; /* the packet which will go on the wire. This is
buffer * clearwritebuf; /* unencrypted, possibly compressed */ encrypted in-place. */
unsigned char type; unsigned char type;
unsigned int clear_len; unsigned int len, encrypt_buf_size;
unsigned char mac_bytes[MAX_MAC_LEN];
type = ses.writepayload->data[0]; type = ses.writepayload->data[0];
TRACE(("enter encrypt_packet()")) TRACE(("enter encrypt_packet()"))
@@ -468,34 +436,36 @@ void encrypt_packet() {
return; return;
} }
blocksize = ses.keys->trans_algo_crypt->blocksize; blocksize = ses.keys->trans.algo_crypt->blocksize;
macsize = ses.keys->trans_algo_mac->hashsize; mac_size = ses.keys->trans.algo_mac->hashsize;
/* Encrypted packet len is payload+5, then worst case is if we are 3 away /* Encrypted packet len is payload+5, then worst case is if we are 3 away
* from a blocksize multiple. In which case we need to pad to the * from a blocksize multiple. In which case we need to pad to the
* multiple, then add another blocksize (or MIN_PACKET_LEN) */ * multiple, then add another blocksize (or MIN_PACKET_LEN) */
clear_len = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3; encrypt_buf_size = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3;
/* add space for the MAC at the end */
encrypt_buf_size += mac_size;
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
clear_len += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/ encrypt_buf_size += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/
#endif #endif
clearwritebuf = buf_new(clear_len); writebuf = buf_new(encrypt_buf_size);
buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF); buf_setlen(writebuf, PACKET_PAYLOAD_OFF);
buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF); buf_setpos(writebuf, PACKET_PAYLOAD_OFF);
buf_setpos(ses.writepayload, 0); buf_setpos(ses.writepayload, 0);
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
/* compression */ /* compression */
if (is_compress_trans()) { if (is_compress_trans()) {
buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len); buf_compress(writebuf, ses.writepayload, ses.writepayload->len);
} else } else
#endif #endif
{ {
memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len), memcpy(buf_getwriteptr(writebuf, ses.writepayload->len),
buf_getptr(ses.writepayload, ses.writepayload->len), buf_getptr(ses.writepayload, ses.writepayload->len),
ses.writepayload->len); ses.writepayload->len);
buf_incrwritepos(clearwritebuf, ses.writepayload->len); buf_incrwritepos(writebuf, ses.writepayload->len);
} }
/* finished with payload */ /* finished with payload */
@@ -504,53 +474,45 @@ void encrypt_packet() {
/* length of padding - packet length must be a multiple of blocksize, /* length of padding - packet length must be a multiple of blocksize,
* with a minimum of 4 bytes of padding */ * with a minimum of 4 bytes of padding */
padlen = blocksize - (clearwritebuf->len) % blocksize; padlen = blocksize - (writebuf->len) % blocksize;
if (padlen < 4) { if (padlen < 4) {
padlen += blocksize; padlen += blocksize;
} }
/* check for min packet length */ /* check for min packet length */
if (clearwritebuf->len + padlen < MIN_PACKET_LEN) { if (writebuf->len + padlen < MIN_PACKET_LEN) {
padlen += blocksize; padlen += blocksize;
} }
buf_setpos(clearwritebuf, 0); buf_setpos(writebuf, 0);
/* packet length excluding the packetlength uint32 */ /* packet length excluding the packetlength uint32 */
buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4); buf_putint(writebuf, writebuf->len + padlen - 4);
/* padding len */ /* padding len */
buf_putbyte(clearwritebuf, padlen); buf_putbyte(writebuf, padlen);
/* actual padding */ /* actual padding */
buf_setpos(clearwritebuf, clearwritebuf->len); buf_setpos(writebuf, writebuf->len);
buf_incrlen(clearwritebuf, padlen); buf_incrlen(writebuf, padlen);
genrandom(buf_getptr(clearwritebuf, padlen), padlen); genrandom(buf_getptr(writebuf, padlen), padlen);
/* do the actual encryption */ make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes);
buf_setpos(clearwritebuf, 0);
/* create a new writebuffer, this is freed when it has been put on the
* wire by writepacket() */
writebuf = buf_new(clearwritebuf->len + macsize);
/* encrypt it */ /* do the actual encryption, in-place */
while (clearwritebuf->pos < clearwritebuf->len) { buf_setpos(writebuf, 0);
if (ses.keys->trans_crypt_mode->encrypt( /* encrypt it in-place*/
buf_getptr(clearwritebuf, blocksize), len = writebuf->len;
buf_getwriteptr(writebuf, blocksize), if (ses.keys->trans.crypt_mode->encrypt(
blocksize, buf_getptr(writebuf, len),
&ses.keys->trans_cipher_state) != CRYPT_OK) { buf_getwriteptr(writebuf, len),
dropbear_exit("error encrypting"); len,
} &ses.keys->trans.cipher_state) != CRYPT_OK) {
buf_incrpos(clearwritebuf, blocksize); dropbear_exit("error encrypting");
buf_incrwritepos(writebuf, blocksize);
} }
buf_incrpos(writebuf, len);
/* now add a hmac and we're done */ /* stick the MAC on it */
writemac(writebuf, clearwritebuf); buf_putbytes(writebuf, mac_bytes, mac_size);
/* clearwritebuf is finished with */ /* enqueue the packet for sending. It will get freed after transmission. */
buf_free(clearwritebuf);
clearwritebuf = NULL;
/* enqueue the packet for sending */
buf_setpos(writebuf, 0); buf_setpos(writebuf, 0);
enqueue(&ses.writequeue, (void*)writebuf); enqueue(&ses.writequeue, (void*)writebuf);
@@ -563,47 +525,43 @@ void encrypt_packet() {
/* Create the packet mac, and append H(seqno|clearbuf) to the output */ /* Create the packet mac, and append H(seqno|clearbuf) to the output */
static void writemac(buffer * outputbuffer, buffer * clearwritebuf) { /* output_mac must have ses.keys->trans.algo_mac->hashsize bytes. */
static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
unsigned int macsize; buffer * clear_buf, unsigned int clear_len,
unsigned char *output_mac) {
unsigned char seqbuf[4]; unsigned char seqbuf[4];
unsigned char tempbuf[MAX_MAC_LEN];
unsigned long bufsize; unsigned long bufsize;
hmac_state hmac; hmac_state hmac;
TRACE(("enter writemac")) TRACE(("enter writemac"))
macsize = ses.keys->trans_algo_mac->hashsize; if (key_state->algo_mac->hashsize > 0) {
if (macsize > 0) {
/* calculate the mac */ /* calculate the mac */
if (hmac_init(&hmac, if (hmac_init(&hmac,
find_hash(ses.keys->trans_algo_mac->hashdesc->name), key_state->hash_index,
ses.keys->transmackey, key_state->mackey,
ses.keys->trans_algo_mac->keysize) != CRYPT_OK) { key_state->algo_mac->keysize) != CRYPT_OK) {
dropbear_exit("HMAC error"); dropbear_exit("HMAC error");
} }
/* sequence number */ /* sequence number */
STORE32H(ses.transseq, seqbuf); STORE32H(seqno, seqbuf);
if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) { if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) {
dropbear_exit("HMAC error"); dropbear_exit("HMAC error");
} }
/* the actual contents */ /* the actual contents */
buf_setpos(clearwritebuf, 0); buf_setpos(clear_buf, 0);
if (hmac_process(&hmac, if (hmac_process(&hmac,
buf_getptr(clearwritebuf, buf_getptr(clear_buf, clear_len),
clearwritebuf->len), clear_len) != CRYPT_OK) {
clearwritebuf->len) != CRYPT_OK) {
dropbear_exit("HMAC error"); dropbear_exit("HMAC error");
} }
bufsize = sizeof(tempbuf); bufsize = MAX_MAC_LEN;
if (hmac_done(&hmac, tempbuf, &bufsize) if (hmac_done(&hmac, output_mac, &bufsize) != CRYPT_OK) {
!= CRYPT_OK) {
dropbear_exit("HMAC error"); dropbear_exit("HMAC error");
} }
buf_putbytes(outputbuffer, tempbuf, macsize);
} }
TRACE(("leave writemac")) TRACE(("leave writemac"))
} }
@@ -620,29 +578,29 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
while (1) { while (1) {
ses.keys->trans_zstream->avail_in = endpos - src->pos; ses.keys->trans.zstream->avail_in = endpos - src->pos;
ses.keys->trans_zstream->next_in = ses.keys->trans.zstream->next_in =
buf_getptr(src, ses.keys->trans_zstream->avail_in); buf_getptr(src, ses.keys->trans.zstream->avail_in);
ses.keys->trans_zstream->avail_out = dest->size - dest->pos; ses.keys->trans.zstream->avail_out = dest->size - dest->pos;
ses.keys->trans_zstream->next_out = ses.keys->trans.zstream->next_out =
buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out); buf_getwriteptr(dest, ses.keys->trans.zstream->avail_out);
result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH); result = deflate(ses.keys->trans.zstream, Z_SYNC_FLUSH);
buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in); buf_setpos(src, endpos - ses.keys->trans.zstream->avail_in);
buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out); buf_setlen(dest, dest->size - ses.keys->trans.zstream->avail_out);
buf_setpos(dest, dest->len); buf_setpos(dest, dest->len);
if (result != Z_OK) { if (result != Z_OK) {
dropbear_exit("zlib error"); dropbear_exit("zlib error");
} }
if (ses.keys->trans_zstream->avail_in == 0) { if (ses.keys->trans.zstream->avail_in == 0) {
break; break;
} }
dropbear_assert(ses.keys->trans_zstream->avail_out == 0); dropbear_assert(ses.keys->trans.zstream->avail_out == 0);
/* the buffer has been filled, we must extend. This only happens in /* the buffer has been filled, we must extend. This only happens in
* unusual circumstances where the data grows in size after deflate(), * unusual circumstances where the data grows in size after deflate(),

View File

@@ -44,6 +44,6 @@ typedef struct PacketType {
#define PACKET_PADDING_OFF 4 #define PACKET_PADDING_OFF 4
#define PACKET_PAYLOAD_OFF 5 #define PACKET_PAYLOAD_OFF 5
#define INIT_READBUF 200 #define INIT_READBUF 128
#endif /* _PACKET_H_ */ #endif /* _PACKET_H_ */

View File

@@ -69,12 +69,8 @@ static void readrand(unsigned char* buf, unsigned int buflen) {
#endif #endif
#ifdef DROPBEAR_PRNGD_SOCKET #ifdef DROPBEAR_PRNGD_SOCKET
memset((void*)&egdsock, 0x0, sizeof(egdsock)); readfd = connect_unix(DROPBEAR_PRNGD_SOCKET);
egdsock.sun_family = AF_UNIX;
strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
sizeof(egdsock.sun_path));
readfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (readfd < 0) { if (readfd < 0) {
dropbear_exit("couldn't open random device"); dropbear_exit("couldn't open random device");
} }

View File

@@ -37,8 +37,16 @@ typedef struct runopts {
int listen_fwd_all; int listen_fwd_all;
#endif #endif
unsigned int recv_window; unsigned int recv_window;
unsigned int keepalive_secs; time_t keepalive_secs;
unsigned int idle_timeout_secs; time_t idle_timeout_secs;
#ifndef DISABLE_ZLIB
/* TODO: add a commandline flag. Currently this is on by default if compression
* is compiled in, but disabled for a client's non-final multihop stages. (The
* intermediate stages are compressed streams, so are uncompressible. */
int enable_compress;
#endif
} runopts; } runopts;
@@ -112,13 +120,20 @@ typedef struct cli_runopts {
int backgrounded; int backgrounded;
int is_subsystem; int is_subsystem;
#ifdef ENABLE_CLI_PUBKEY_AUTH #ifdef ENABLE_CLI_PUBKEY_AUTH
struct SignKeyList *privkeys; /* Keys to use for public-key auth */ m_list *privkeys; /* Keys to use for public-key auth */
#endif #endif
#ifdef ENABLE_CLI_REMOTETCPFWD #ifdef ENABLE_CLI_REMOTETCPFWD
struct TCPFwdList * remotefwds; m_list * remotefwds;
#endif #endif
#ifdef ENABLE_CLI_LOCALTCPFWD #ifdef ENABLE_CLI_LOCALTCPFWD
struct TCPFwdList * localfwds; m_list * localfwds;
#endif
#ifdef ENABLE_CLI_AGENTFWD
int agent_fwd;
int agent_keys_loaded; /* whether pubkeys has been populated with a
list of keys held by the agent */
int agent_fd; /* The agent fd is only set during authentication. Forwarded
agent sessions have their own file descriptors */
#endif #endif
#ifdef ENABLE_CLI_NETCAT #ifdef ENABLE_CLI_NETCAT
@@ -128,7 +143,6 @@ typedef struct cli_runopts {
#ifdef ENABLE_CLI_PROXYCMD #ifdef ENABLE_CLI_PROXYCMD
char *proxycmd; char *proxycmd;
#endif #endif
} cli_runopts; } cli_runopts;
extern cli_runopts cli_opts; extern cli_runopts cli_opts;

6
scp.c
View File

@@ -343,7 +343,7 @@ main(int argc, char **argv)
addargs(&args, "-p%s", optarg); addargs(&args, "-p%s", optarg);
break; break;
case 'B': case 'B':
addargs(&args, "-oBatchmode yes"); fprintf(stderr, "Note: -B option is disabled in this version of scp");
break; break;
case 'l': case 'l':
speed = strtod(optarg, &endp); speed = strtod(optarg, &endp);
@@ -492,9 +492,13 @@ toremote(char *targ, int argc, char **argv)
addargs(&alist, "%s", ssh_program); addargs(&alist, "%s", ssh_program);
if (verbose_mode) if (verbose_mode)
addargs(&alist, "-v"); addargs(&alist, "-v");
#if 0
// Disabled since dbclient won't understand them
// and scp works fine without them.
addargs(&alist, "-x"); addargs(&alist, "-x");
addargs(&alist, "-oClearAllForwardings yes"); addargs(&alist, "-oClearAllForwardings yes");
addargs(&alist, "-n"); addargs(&alist, "-n");
#endif
*src++ = 0; *src++ = 0;
if (*src == 0) if (*src == 0)

View File

@@ -41,7 +41,7 @@
extern int sessinitdone; /* Is set to 0 somewhere */ extern int sessinitdone; /* Is set to 0 somewhere */
extern int exitflag; extern int exitflag;
void common_session_init(int sock_in, int sock_out, char* remotehost); void common_session_init(int sock_in, int sock_out);
void session_loop(void(*loophandler)()); void session_loop(void(*loophandler)());
void common_session_cleanup(); void common_session_cleanup();
void session_identification(); void session_identification();
@@ -51,51 +51,45 @@ const char* get_user_shell();
void fill_passwd(const char* username); void fill_passwd(const char* username);
/* Server */ /* Server */
void svr_session(int sock, int childpipe, char *remotehost, char *addrstring); void svr_session(int sock, int childpipe);
void svr_dropbear_exit(int exitcode, const char* format, va_list param); void svr_dropbear_exit(int exitcode, const char* format, va_list param);
void svr_dropbear_log(int priority, const char* format, va_list param); void svr_dropbear_log(int priority, const char* format, va_list param);
/* Client */ /* Client */
void cli_session(int sock_in, int sock_out, char *remotehost); void cli_session(int sock_in, int sock_out);
void cli_session_cleanup(); void cli_session_cleanup();
void cleantext(unsigned char* dirtytext); void cleantext(unsigned char* dirtytext);
struct key_context { /* crypto parameters that are stored individually for transmit and receive */
struct key_context_directional {
const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */ const struct dropbear_cipher *algo_crypt; /* NULL for none */
const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */ const struct dropbear_cipher_mode *crypt_mode;
const struct dropbear_cipher_mode *recv_crypt_mode; const struct dropbear_hash *algo_mac; /* NULL for none */
const struct dropbear_cipher_mode *trans_crypt_mode; int hash_index; /* lookup for libtomcrypt */
const struct dropbear_hash *recv_algo_mac; /* NULL for none */ char algo_comp; /* compression */
const struct dropbear_hash *trans_algo_mac; /* NULL for none */
char algo_kex;
char algo_hostkey;
char recv_algo_comp; /* compression */
char trans_algo_comp;
int allow_compress; /* whether compression has started (useful in
zlib@openssh.com delayed compression case) */
#ifndef DISABLE_ZLIB #ifndef DISABLE_ZLIB
z_streamp recv_zstream; z_streamp zstream;
z_streamp trans_zstream;
#endif #endif
/* actual keys */ /* actual keys */
union { union {
symmetric_CBC cbc; symmetric_CBC cbc;
#ifdef DROPBEAR_ENABLE_CTR_MODE #ifdef DROPBEAR_ENABLE_CTR_MODE
symmetric_CTR ctr; symmetric_CTR ctr;
#endif #endif
} recv_cipher_state; } cipher_state;
union { unsigned char mackey[MAX_MAC_KEY];
symmetric_CBC cbc; };
#ifdef DROPBEAR_ENABLE_CTR_MODE
symmetric_CTR ctr;
#endif
} trans_cipher_state;
unsigned char recvmackey[MAX_MAC_KEY];
unsigned char transmackey[MAX_MAC_KEY];
struct key_context {
struct key_context_directional recv;
struct key_context_directional trans;
char algo_kex;
char algo_hostkey;
int allow_compress; /* whether compression has started (useful in
zlib@openssh.com delayed compression case) */
}; };
struct packetlist; struct packetlist;
@@ -116,8 +110,6 @@ struct sshsession {
int sock_in; int sock_in;
int sock_out; int sock_out;
unsigned char *remotehost; /* the peer hostname */
unsigned char *remoteident; unsigned char *remoteident;
int maxfd; /* the maximum file descriptor to check with select() */ int maxfd; /* the maximum file descriptor to check with select() */
@@ -128,8 +120,7 @@ struct sshsession {
throughout the code, as handlers fill out this throughout the code, as handlers fill out this
buffer with the packet to send. */ buffer with the packet to send. */
struct Queue writequeue; /* A queue of encrypted packets to send */ struct Queue writequeue; /* A queue of encrypted packets to send */
buffer *readbuf; /* Encrypted */ buffer *readbuf; /* From the wire, decrypted in-place */
buffer *decryptreadbuf; /* Post-decryption */
buffer *payload; /* Post-decompression, the actual SSH packet */ buffer *payload; /* Post-decompression, the actual SSH packet */
unsigned int transseq, recvseq; /* Sequence IDs */ unsigned int transseq, recvseq; /* Sequence IDs */
@@ -169,6 +160,9 @@ struct sshsession {
buffer* kexhashbuf; /* session hash buffer calculated from various packets*/ buffer* kexhashbuf; /* session hash buffer calculated from various packets*/
buffer* transkexinit; /* the kexinit packet we send should be kept so we buffer* transkexinit; /* the kexinit packet we send should be kept so we
can add it to the hash when generating keys */ can add it to the hash when generating keys */
/* Enables/disables compression */
algo_type *compress_algos;
/* a list of queued replies that should be sent after a KEX has /* a list of queued replies that should be sent after a KEX has
concluded (ie, while dataallowed was unset)*/ concluded (ie, while dataallowed was unset)*/
@@ -220,6 +214,13 @@ struct serversession {
/* The numeric address they connected from, used for logging */ /* The numeric address they connected from, used for logging */
char * addrstring; char * addrstring;
/* The resolved remote address, used for lastlog etc */
char *remotehost;
#ifdef __uClinux__
pid_t server_pid;
#endif
}; };
typedef enum { typedef enum {
@@ -268,7 +269,7 @@ struct clientsession {
info request from the server for info request from the server for
interactive auth.*/ interactive auth.*/
#endif #endif
struct SignKeyList *lastprivkey; sign_key *lastprivkey;
int retval; /* What the command exit status was - we emulate it */ int retval; /* What the command exit status was - we emulate it */
#if 0 #if 0

View File

@@ -40,8 +40,10 @@ sign_key * new_sign_key() {
#ifdef DROPBEAR_RSA #ifdef DROPBEAR_RSA
ret->rsakey = NULL; ret->rsakey = NULL;
#endif #endif
ret->filename = NULL;
ret->type = DROPBEAR_SIGNKEY_NONE;
ret->source = SIGNKEY_SOURCE_INVALID;
return ret; return ret;
} }
/* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally /* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally
@@ -81,6 +83,8 @@ int signkey_type_from_name(const char* name, int namelen) {
} }
#endif #endif
TRACE(("signkey_type_from_name unexpected key type."))
return DROPBEAR_SIGNKEY_NONE; return DROPBEAR_SIGNKEY_NONE;
} }
@@ -101,8 +105,11 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
m_free(ident); m_free(ident);
if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) { if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
TRACE(("buf_get_pub_key bad type - got %d, expected %d", keytype, type))
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
} }
TRACE(("buf_get_pub_key keytype is %d"))
*type = keytype; *type = keytype;
@@ -255,6 +262,8 @@ void sign_key_free(sign_key *key) {
key->rsakey = NULL; key->rsakey = NULL;
#endif #endif
m_free(key->filename);
m_free(key); m_free(key);
TRACE(("leave sign_key_free")) TRACE(("leave sign_key_free"))
} }
@@ -358,7 +367,6 @@ void buf_put_sign(buffer* buf, sign_key *key, int type,
const unsigned char *data, unsigned int len) { const unsigned char *data, unsigned int len) {
buffer *sigblob; buffer *sigblob;
sigblob = buf_new(MAX_PUBKEY_SIZE); sigblob = buf_new(MAX_PUBKEY_SIZE);
#ifdef DROPBEAR_DSS #ifdef DROPBEAR_DSS
@@ -374,7 +382,6 @@ void buf_put_sign(buffer* buf, sign_key *key, int type,
if (sigblob->len == 0) { if (sigblob->len == 0) {
dropbear_exit("non-matching signing type"); dropbear_exit("non-matching signing type");
} }
buf_setpos(sigblob, 0); buf_setpos(sigblob, 0);
buf_putstring(buf, buf_getptr(sigblob, sigblob->len), buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
sigblob->len); sigblob->len);

View File

@@ -29,8 +29,22 @@
#include "dss.h" #include "dss.h"
#include "rsa.h" #include "rsa.h"
/* Sources for signing keys */
typedef enum {
SIGNKEY_SOURCE_RAW_FILE,
SIGNKEY_SOURCE_AGENT,
SIGNKEY_SOURCE_INVALID,
} signkey_source;
struct SIGN_key { struct SIGN_key {
int type; /* The type of key (dss or rsa) */
signkey_source source;
char *filename;
/* the buffer? for encrypted keys, so we can later get
* the private key portion */
#ifdef DROPBEAR_DSS #ifdef DROPBEAR_DSS
dss_key * dsskey; dss_key * dsskey;
#endif #endif

11
ssh.h
View File

@@ -105,3 +105,14 @@
#define SSH_SIGNKEY_DSS_LEN 7 #define SSH_SIGNKEY_DSS_LEN 7
#define SSH_SIGNKEY_RSA "ssh-rsa" #define SSH_SIGNKEY_RSA "ssh-rsa"
#define SSH_SIGNKEY_RSA_LEN 7 #define SSH_SIGNKEY_RSA_LEN 7
/* Agent commands. These aren't part of the spec, and are defined
* only on the openssh implementation. */
#define SSH_AGENT_FAILURE 5
#define SSH_AGENT_SUCCESS 6
#define SSH2_AGENTC_REQUEST_IDENTITIES 11
#define SSH2_AGENT_IDENTITIES_ANSWER 12
#define SSH2_AGENTC_SIGN_REQUEST 13
#define SSH2_AGENT_SIGN_RESPONSE 14
#define SSH2_AGENT_FAILURE 30

View File

@@ -49,10 +49,12 @@ static void agentaccept(struct Listener * listener, int sock);
/* Handles client requests to start agent forwarding, sets up listening socket. /* Handles client requests to start agent forwarding, sets up listening socket.
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
int agentreq(struct ChanSess * chansess) { int svr_agentreq(struct ChanSess * chansess) {
int fd; int fd;
TRACE(("enter svr_agentreq"))
if (!svr_pubkey_allows_agentfwd()) { if (!svr_pubkey_allows_agentfwd()) {
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
} }
@@ -89,10 +91,12 @@ int agentreq(struct ChanSess * chansess) {
} }
return DROPBEAR_SUCCESS; return DROPBEAR_SUCCESS;
TRACE(("success"))
fail: fail:
TRACE(("fail"))
/* cleanup */ /* cleanup */
agentcleanup(chansess); svr_agentcleanup(chansess);
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
} }
@@ -118,7 +122,7 @@ static void agentaccept(struct Listener *UNUSED(listener), int sock) {
/* set up the environment variable pointing to the socket. This is called /* set up the environment variable pointing to the socket. This is called
* just before command/shell execution, after dropping priveleges */ * just before command/shell execution, after dropping priveleges */
void agentset(struct ChanSess * chansess) { void svr_agentset(struct ChanSess * chansess) {
char *path = NULL; char *path = NULL;
int len; int len;
@@ -137,7 +141,7 @@ void agentset(struct ChanSess * chansess) {
} }
/* close the socket, remove the socket-file */ /* close the socket, remove the socket-file */
void agentcleanup(struct ChanSess * chansess) { void svr_agentcleanup(struct ChanSess * chansess) {
char *path = NULL; char *path = NULL;
uid_t uid; uid_t uid;
@@ -181,7 +185,7 @@ void agentcleanup(struct ChanSess * chansess) {
} }
static const struct ChanType chan_agent = { static const struct ChanType chan_svr_agent = {
0, /* sepfds */ 0, /* sepfds */
"auth-agent@openssh.com", "auth-agent@openssh.com",
NULL, NULL,
@@ -194,7 +198,7 @@ static const struct ChanType chan_agent = {
/* helper for accepting an agent request */ /* helper for accepting an agent request */
static int send_msg_channel_open_agent(int fd) { static int send_msg_channel_open_agent(int fd) {
if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) { if (send_msg_channel_open_init(fd, &chan_svr_agent) == DROPBEAR_SUCCESS) {
encrypt_packet(); encrypt_packet();
return DROPBEAR_SUCCESS; return DROPBEAR_SUCCESS;
} else { } else {

View File

@@ -33,6 +33,7 @@
#include "packet.h" #include "packet.h"
#include "auth.h" #include "auth.h"
#include "runopts.h" #include "runopts.h"
#include "random.h"
static void authclear(); static void authclear();
static int checkusername(unsigned char *username, unsigned int userlen); static int checkusername(unsigned char *username, unsigned int userlen);
@@ -337,7 +338,12 @@ void send_msg_userauth_failure(int partial, int incrfail) {
encrypt_packet(); encrypt_packet();
if (incrfail) { if (incrfail) {
usleep(300000); /* XXX improve this */ unsigned int delay;
genrandom((unsigned char*)&delay, sizeof(delay));
/* We delay for 300ms +- 50ms, 0.1ms granularity */
delay = 250000 + (delay % 1000)*100;
usleep(delay);
dropbear_log(LOG_INFO, "delay is %d", delay);
ses.authstate.failcount++; ses.authstate.failcount++;
} }

View File

@@ -102,7 +102,7 @@ pamConvFunc(int num_msg,
/* We don't recognise the prompt as asking for a password, /* We don't recognise the prompt as asking for a password,
so can't handle it. Add more above as required for so can't handle it. Add more above as required for
different pam modules/implementations */ different pam modules/implementations */
dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)", dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (no echo)",
compare_message); compare_message);
rc = PAM_CONV_ERR; rc = PAM_CONV_ERR;
break; break;
@@ -123,12 +123,15 @@ pamConvFunc(int num_msg,
case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_ON:
if (!((strcmp(compare_message, "login:" ) == 0) if (!(
|| (strcmp(compare_message, "please enter username:") == 0))) { (strcmp(compare_message, "login:" ) == 0)
|| (strcmp(compare_message, "please enter username:") == 0)
|| (strcmp(compare_message, "username:") == 0)
)) {
/* We don't recognise the prompt as asking for a username, /* We don't recognise the prompt as asking for a username,
so can't handle it. Add more above as required for so can't handle it. Add more above as required for
different pam modules/implementations */ different pam modules/implementations */
dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)", dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (with echo)",
compare_message); compare_message);
rc = PAM_CONV_ERR; rc = PAM_CONV_ERR;
break; break;
@@ -212,7 +215,10 @@ void svr_auth_pam() {
goto cleanup; goto cleanup;
} }
#ifdef HAVE_PAM_FAIL_DELAY
/* We have our own random delay code already, disable PAM's */
(void) pam_fail_delay(pamHandlep, 0 /* musec_delay */); (void) pam_fail_delay(pamHandlep, 0 /* musec_delay */);
#endif
/* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */ /* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */

View File

@@ -88,10 +88,20 @@ int svr_pubkey_allows_pty() {
return 1; return 1;
} }
/* Set chansession command to the one forced by 'command' public key option */ /* Set chansession command to the one forced
* by any 'command' public key option. */
void svr_pubkey_set_forced_command(struct ChanSess *chansess) { void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
if (ses.authstate.pubkey_options) if (ses.authstate.pubkey_options) {
ses.authstate.pubkey_options->original_command = chansess->cmd;
if (!chansess->cmd)
{
ses.authstate.pubkey_options->original_command = m_strdup("");
}
chansess->cmd = ses.authstate.pubkey_options->forced_command; chansess->cmd = ses.authstate.pubkey_options->forced_command;
#ifdef LOG_COMMANDS
dropbear_log(LOG_INFO, "command forced to '%s'", ses.authstate.pubkey_options->original_command);
#endif
}
} }
/* Free potential public key options */ /* Free potential public key options */
@@ -124,7 +134,6 @@ int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filena
TRACE(("enter addpubkeyoptions")) TRACE(("enter addpubkeyoptions"))
ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions )); 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); buf_setpos(options_buf, 0);
while (options_buf->pos < options_buf->len) { while (options_buf->pos < options_buf->len) {

View File

@@ -222,6 +222,7 @@ static int newchansess(struct Channel *channel) {
chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess)); chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
chansess->cmd = NULL; chansess->cmd = NULL;
chansess->connection_string = NULL;
chansess->pid = 0; chansess->pid = 0;
/* pty details */ /* pty details */
@@ -250,6 +251,14 @@ static int newchansess(struct Channel *channel) {
} }
static struct logininfo*
chansess_login_alloc(struct ChanSess *chansess) {
struct logininfo * li;
li = login_alloc_entry(chansess->pid, ses.authstate.username,
svr_ses.remotehost, chansess->tty);
return li;
}
/* clean a session channel */ /* clean a session channel */
static void closechansess(struct Channel *channel) { static void closechansess(struct Channel *channel) {
@@ -273,8 +282,7 @@ static void closechansess(struct Channel *channel) {
if (chansess->tty) { if (chansess->tty) {
/* write the utmp/wtmp login record */ /* write the utmp/wtmp login record */
li = login_alloc_entry(chansess->pid, ses.authstate.username, li = chansess_login_alloc(chansess);
ses.remotehost, chansess->tty);
login_logout(li); login_logout(li);
login_free_entry(li); login_free_entry(li);
@@ -287,7 +295,7 @@ static void closechansess(struct Channel *channel) {
#endif #endif
#ifndef DISABLE_AGENTFWD #ifndef DISABLE_AGENTFWD
agentcleanup(chansess); svr_agentcleanup(chansess);
#endif #endif
/* clear child pid entries */ /* clear child pid entries */
@@ -346,7 +354,7 @@ static void chansessionrequest(struct Channel *channel) {
#endif #endif
#ifndef DISABLE_AGENTFWD #ifndef DISABLE_AGENTFWD
} else if (strcmp(type, "auth-agent-req@openssh.com") == 0) { } else if (strcmp(type, "auth-agent-req@openssh.com") == 0) {
ret = agentreq(chansess); ret = svr_agentreq(chansess);
#endif #endif
} else if (strcmp(type, "signal") == 0) { } else if (strcmp(type, "signal") == 0) {
ret = sessionsignal(chansess); ret = sessionsignal(chansess);
@@ -570,6 +578,21 @@ static int sessionpty(struct ChanSess * chansess) {
return DROPBEAR_SUCCESS; return DROPBEAR_SUCCESS;
} }
static char* make_connection_string() {
char *local_ip, *local_port, *remote_ip, *remote_port;
size_t len;
char *ret;
get_socket_address(ses.sock_in, &local_ip, &local_port, &remote_ip, &remote_port, 0);
len = strlen(local_ip) + strlen(local_port) + strlen(remote_ip) + strlen(remote_port) + 4;
ret = m_malloc(len);
snprintf(ret, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port);
m_free(local_ip);
m_free(local_port);
m_free(remote_ip);
m_free(remote_port);
return ret;
}
/* Handle a command request from the client. This is used for both shell /* Handle a command request from the client. This is used for both shell
* and command-execution requests, and passes the command to * and command-execution requests, and passes the command to
* noptycommand or ptycommand as appropriate. * noptycommand or ptycommand as appropriate.
@@ -589,9 +612,6 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
return DROPBEAR_FAILURE; return DROPBEAR_FAILURE;
} }
/* take public key option 'command' into account */
svr_pubkey_set_forced_command(chansess);
if (iscmd) { if (iscmd) {
/* "exec" */ /* "exec" */
if (chansess->cmd == NULL) { if (chansess->cmd == NULL) {
@@ -616,6 +636,9 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
} }
} }
} }
/* take public key option 'command' into account */
svr_pubkey_set_forced_command(chansess);
#ifdef LOG_COMMANDS #ifdef LOG_COMMANDS
if (chansess->cmd) { if (chansess->cmd) {
@@ -627,6 +650,12 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
} }
#endif #endif
/* uClinux will vfork(), so there'll be a race as
connection_string is freed below. */
#ifndef __uClinux__
chansess->connection_string = make_connection_string();
#endif
if (chansess->term == NULL) { if (chansess->term == NULL) {
/* no pty */ /* no pty */
ret = noptycommand(channel, chansess); ret = noptycommand(channel, chansess);
@@ -635,6 +664,10 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
ret = ptycommand(channel, chansess); ret = ptycommand(channel, chansess);
} }
#ifndef __uClinux__
m_free(chansess->connection_string);
#endif
if (ret == DROPBEAR_FAILURE) { if (ret == DROPBEAR_FAILURE) {
m_free(chansess->cmd); m_free(chansess->cmd);
} }
@@ -736,13 +769,10 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
/* write the utmp/wtmp login record - must be after changing the /* write the utmp/wtmp login record - must be after changing the
* terminal used for stdout with the dup2 above */ * terminal used for stdout with the dup2 above */
li= login_alloc_entry(getpid(), ses.authstate.username, li = chansess_login_alloc(chansess);
ses.remotehost, chansess->tty);
login_login(li); login_login(li);
login_free_entry(li); login_free_entry(li);
m_free(chansess->tty);
#ifdef DO_MOTD #ifdef DO_MOTD
if (svr_opts.domotd) { if (svr_opts.domotd) {
/* don't show the motd if ~/.hushlogin exists */ /* don't show the motd if ~/.hushlogin exists */
@@ -883,6 +913,22 @@ static void execchild(void *user_data) {
addnewvar("TERM", chansess->term); addnewvar("TERM", chansess->term);
} }
if (chansess->tty) {
addnewvar("SSH_TTY", chansess->tty);
}
if (chansess->connection_string) {
addnewvar("SSH_CONNECTION", chansess->connection_string);
}
#ifdef ENABLE_SVR_PUBKEY_OPTIONS
if (ses.authstate.pubkey_options &&
ses.authstate.pubkey_options->original_command) {
addnewvar("SSH_ORIGINAL_COMMAND",
ses.authstate.pubkey_options->original_command);
}
#endif
/* change directory */ /* change directory */
if (chdir(ses.authstate.pw_dir) < 0) { if (chdir(ses.authstate.pw_dir) < 0) {
dropbear_exit("error changing directory"); dropbear_exit("error changing directory");
@@ -894,7 +940,7 @@ static void execchild(void *user_data) {
#endif #endif
#ifndef DISABLE_AGENTFWD #ifndef DISABLE_AGENTFWD
/* set up agent env variable */ /* set up agent env variable */
agentset(chansess); svr_agentset(chansess);
#endif #endif
usershell = m_strdup(get_user_shell()); usershell = m_strdup(get_user_shell());

View File

@@ -77,22 +77,16 @@ int main(int argc, char ** argv)
#ifdef INETD_MODE #ifdef INETD_MODE
static void main_inetd() { static void main_inetd() {
char *host, *port = NULL;
struct sockaddr_storage remoteaddr;
socklen_t remoteaddrlen;
char * addrstring = NULL;
/* Set up handlers, syslog, seed random */ /* Set up handlers, syslog, seed random */
commonsetup(); commonsetup();
remoteaddrlen = sizeof(remoteaddr);
if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) {
dropbear_exit("Unable to getpeername: %s", strerror(errno));
}
/* In case our inetd was lax in logging source addresses */ /* In case our inetd was lax in logging source addresses */
addrstring = getaddrstring(&remoteaddr, 1); get_socket_address(0, NULL, NULL, &host, &port, 0);
dropbear_log(LOG_INFO, "Child connection from %s", addrstring); dropbear_log(LOG_INFO, "Child connection from %s:%s", host, port);
m_free(host);
m_free(port);
/* Don't check the return value - it may just fail since inetd has /* Don't check the return value - it may just fail since inetd has
* already done setsid() after forking (xinetd on Darwin appears to do * already done setsid() after forking (xinetd on Darwin appears to do
@@ -102,7 +96,7 @@ static void main_inetd() {
/* Start service program /* Start service program
* -1 is a dummy childpipe, just something we can close() without * -1 is a dummy childpipe, just something we can close() without
* mattering. */ * mattering. */
svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring); svr_session(0, -1);
/* notreached */ /* notreached */
} }
@@ -133,7 +127,7 @@ void main_noinetd() {
for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
childpipes[i] = -1; childpipes[i] = -1;
} }
bzero(preauth_addrs, sizeof(preauth_addrs)); memset(preauth_addrs, 0x0, sizeof(preauth_addrs));
/* Set up the listening sockets */ /* Set up the listening sockets */
listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
@@ -218,14 +212,13 @@ void main_noinetd() {
/* handle each socket which has something to say */ /* handle each socket which has something to say */
for (i = 0; i < listensockcount; i++) { for (i = 0; i < listensockcount; i++) {
struct sockaddr_storage remoteaddr;
socklen_t remoteaddrlen = 0;
size_t num_unauthed_for_addr = 0; size_t num_unauthed_for_addr = 0;
size_t num_unauthed_total = 0; size_t num_unauthed_total = 0;
char * remote_addr_str = NULL; char *remote_host = NULL, *remote_port = NULL;
pid_t fork_ret = 0; pid_t fork_ret = 0;
size_t conn_idx = 0; size_t conn_idx = 0;
struct sockaddr_storage remoteaddr;
socklen_t remoteaddrlen;
if (!FD_ISSET(listensocks[i], &fds)) if (!FD_ISSET(listensocks[i], &fds))
continue; continue;
@@ -240,14 +233,14 @@ void main_noinetd() {
} }
/* Limit the number of unauthenticated connections per IP */ /* Limit the number of unauthenticated connections per IP */
remote_addr_str = getaddrstring(&remoteaddr, 0); getaddrstring(&remoteaddr, &remote_host, NULL, 0);
num_unauthed_for_addr = 0; num_unauthed_for_addr = 0;
num_unauthed_total = 0; num_unauthed_total = 0;
for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) { for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
if (childpipes[j] >= 0) { if (childpipes[j] >= 0) {
num_unauthed_total++; num_unauthed_total++;
if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) { if (strcmp(remote_host, preauth_addrs[j]) == 0) {
num_unauthed_for_addr++; num_unauthed_for_addr++;
} }
} else { } else {
@@ -280,21 +273,21 @@ void main_noinetd() {
/* parent */ /* parent */
childpipes[conn_idx] = childpipe[0]; childpipes[conn_idx] = childpipe[0];
m_close(childpipe[1]); m_close(childpipe[1]);
preauth_addrs[conn_idx] = remote_addr_str; preauth_addrs[conn_idx] = remote_host;
remote_addr_str = NULL; remote_host = NULL;
} else { } else {
/* child */ /* child */
char * addrstring = NULL;
#ifdef DEBUG_FORKGPROF #ifdef DEBUG_FORKGPROF
extern void _start(void), etext(void); extern void _start(void), etext(void);
monstartup((u_long)&_start, (u_long)&etext); monstartup((u_long)&_start, (u_long)&etext);
#endif /* DEBUG_FORKGPROF */ #endif /* DEBUG_FORKGPROF */
m_free(remote_addr_str); getaddrstring(&remoteaddr, NULL, &remote_port, 0);
addrstring = getaddrstring(&remoteaddr, 1); dropbear_log(LOG_INFO, "Child connection from %s:%s", remote_host, remote_port);
dropbear_log(LOG_INFO, "Child connection from %s", addrstring); m_free(remote_host);
m_free(remote_port);
#ifndef DEBUG_NOFORK #ifndef DEBUG_NOFORK
if (setsid() < 0) { if (setsid() < 0) {
@@ -310,9 +303,7 @@ void main_noinetd() {
m_close(childpipe[0]); m_close(childpipe[0]);
/* start the session */ /* start the session */
svr_session(childsock, childpipe[1], svr_session(childsock, childpipe[1]);
getaddrhostname(&remoteaddr),
addrstring);
/* don't return */ /* don't return */
dropbear_assert(0); dropbear_assert(0);
} }
@@ -320,8 +311,8 @@ void main_noinetd() {
out: out:
/* This section is important for the parent too */ /* This section is important for the parent too */
m_close(childsock); m_close(childsock);
if (remote_addr_str) { if (remote_host) {
m_free(remote_addr_str); m_free(remote_host);
} }
} }
} /* for(;;) loop */ } /* for(;;) loop */

View File

@@ -124,6 +124,9 @@ void svr_getopts(int argc, char ** argv) {
#endif #endif
#ifdef ENABLE_SVR_REMOTETCPFWD #ifdef ENABLE_SVR_REMOTETCPFWD
svr_opts.noremotetcp = 0; svr_opts.noremotetcp = 0;
#endif
#ifndef DISABLE_ZLIB
opts.enable_compress = 1;
#endif #endif
/* not yet /* not yet
opts.ipv4 = 1; opts.ipv4 = 1;
@@ -296,15 +299,19 @@ void svr_getopts(int argc, char ** argv) {
} }
if (keepalive_arg) { if (keepalive_arg) {
if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) { unsigned int val;
if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
dropbear_exit("Bad keepalive '%s'", keepalive_arg); dropbear_exit("Bad keepalive '%s'", keepalive_arg);
} }
opts.keepalive_secs = val;
} }
if (idle_timeout_arg) { if (idle_timeout_arg) {
if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) { unsigned int val;
if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg); dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
} }
opts.idle_timeout_secs = val;
} }
} }

View File

@@ -74,23 +74,36 @@ static const struct ChanType *svr_chantypes[] = {
NULL /* Null termination is mandatory. */ NULL /* Null termination is mandatory. */
}; };
void svr_session(int sock, int childpipe, void svr_session(int sock, int childpipe) {
char* remotehost, char *addrstring) { char *host, *port;
size_t len;
reseedrandom(); reseedrandom();
crypto_init(); crypto_init();
common_session_init(sock, sock, remotehost); common_session_init(sock, sock);
/* Initialise server specific parts of the session */ /* Initialise server specific parts of the session */
svr_ses.childpipe = childpipe; svr_ses.childpipe = childpipe;
svr_ses.addrstring = addrstring; #ifdef __uClinux__
svr_ses.server_pid = getpid();
#endif
svr_authinitialise(); svr_authinitialise();
chaninitialise(svr_chantypes); chaninitialise(svr_chantypes);
svr_chansessinitialise(); svr_chansessinitialise();
ses.connect_time = time(NULL); ses.connect_time = time(NULL);
/* for logging the remote address */
get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0);
len = strlen(host) + strlen(port) + 2;
svr_ses.addrstring = m_malloc(len);
snprintf(svr_ses.addrstring, len, "%s:%s", host, port);
m_free(host);
m_free(port);
get_socket_address(ses.sock_in, NULL, NULL,
&svr_ses.remotehost, NULL, 1);
/* set up messages etc */ /* set up messages etc */
ses.remoteclosed = svr_remoteclosed; ses.remoteclosed = svr_remoteclosed;
@@ -144,11 +157,20 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
_dropbear_log(LOG_INFO, fmtbuf, param); _dropbear_log(LOG_INFO, fmtbuf, param);
/* free potential public key options */ #ifdef __uClinux__
svr_pubkey_options_cleanup(); /* only the main server process should cleanup - we don't want
* forked children doing that */
if (svr_ses.server_pid == getpid())
#else
if (1)
#endif
{
/* free potential public key options */
svr_pubkey_options_cleanup();
/* must be after we've done with username etc */ /* must be after we've done with username etc */
common_session_cleanup(); common_session_cleanup();
}
exit(exitcode); exit(exitcode);

View File

@@ -146,10 +146,6 @@
#define DISABLE_X11FWD #define DISABLE_X11FWD
#endif #endif
#ifndef ENABLE_AGENTFWD
#define DISABLE_AGENTFWD
#endif
#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
#define ENABLE_CLI_ANYTCPFWD #define ENABLE_CLI_ANYTCPFWD
#endif #endif
@@ -160,7 +156,7 @@
#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \ #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \ defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD) defined(ENABLE_SVR_AGENTFWD) || defined(ENABLE_X11FWD)
#define USING_LISTENERS #define USING_LISTENERS
#endif #endif
@@ -168,6 +164,10 @@
#define ENABLE_CLI_MULTIHOP #define ENABLE_CLI_MULTIHOP
#endif #endif
#if defined(ENABLE_CLI_AGENTFWD) || defined(DROPBEAR_PRNGD_SOCKET)
#define ENABLE_CONNECT_UNIX
#endif
#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH) #if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */ #define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
#endif #endif
@@ -202,5 +202,8 @@
#define IS_DROPBEAR_CLIENT 1 #define IS_DROPBEAR_CLIENT 1
#else #else
#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected /* Just building key utils? */
#define IS_DROPBEAR_SERVER 0
#define IS_DROPBEAR_CLIENT 0
#endif #endif

View File

@@ -25,6 +25,7 @@
#define _TCPFWD_H #define _TCPFWD_H
#include "channel.h" #include "channel.h"
#include "list.h"
struct TCPListener { struct TCPListener {
@@ -43,17 +44,14 @@ struct TCPListener {
enum {direct, forwarded} tcp_type; enum {direct, forwarded} tcp_type;
}; };
/* A link in a list of forwards */ /* A forwarding entry */
struct TCPFwdList { struct TCPFwdEntry {
const unsigned char* connectaddr; const unsigned char* connectaddr;
unsigned int connectport; unsigned int connectport;
const unsigned char* listenaddr; const unsigned char* listenaddr;
unsigned int listenport; unsigned int listenport;
unsigned int have_reply; /* is set to 1 after a reply has been received unsigned int have_reply; /* is set to 1 after a reply has been received
when setting up the forwarding */ when setting up the forwarding */
struct TCPFwdList * next;
}; };
/* Server */ /* Server */
@@ -71,5 +69,4 @@ void cli_recv_msg_request_failure();
/* Common */ /* Common */
int listen_tcpfwd(struct TCPListener* tcpinfo); int listen_tcpfwd(struct TCPListener* tcpinfo);
#endif #endif