# HG changeset patch # User Matt Johnston # Date 1248836313 0 # Node ID 7f66b8e40f2d0a2930e32b32a9fe07a119a76588 # Parent de3653483ac0202c4e57728ecf4dfc893c4ac5e9# Parent bcc5b69d15a647daa9143286b52c30a6a3ac8d84 propagate from branch 'au.asn.ucc.matt.dropbear' (head bbe4e11695a7b22bd89a722600eb4a4020b6fdf3) to branch 'au.asn.ucc.matt.dropbear.cli-agent' (head 276cf5e82276b6c879d246ba64739ec6868f5150) diff -r bcc5b69d15a6 -r 7f66b8e40f2d Makefile.in --- a/Makefile.in Sun Jul 26 16:14:50 2009 +0000 +++ b/Makefile.in Wed Jul 29 02:58:33 2009 +0000 @@ -20,7 +20,7 @@ dss.o bignum.o \ signkey.o rsa.o random.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 \ svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ @@ -29,7 +29,8 @@ 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-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 \ common-channel.o common-chansession.o termcodes.o loginrec.o \ diff -r bcc5b69d15a6 -r 7f66b8e40f2d agentfwd.h --- a/agentfwd.h Sun Jul 26 16:14:50 2009 +0000 +++ b/agentfwd.h Wed Jul 29 02:58:33 2009 +0000 @@ -23,21 +23,32 @@ * SOFTWARE. */ #ifndef _AGENTFWD_H_ #define _AGENTFWD_H_ -#ifndef DISABLE_AGENTFWD #include "includes.h" #include "chansession.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 agentreq(struct ChanSess * chansess); -void agentsetauth(struct ChanSess *chansess); void agentcleanup(struct ChanSess * chansess); void agentset(struct ChanSess *chansess); +/* client functions */ +void load_agent_keys(m_list * ret_list); +void agent_buf_sign(buffer *sigblob, sign_key *key, + const unsigned char *data, unsigned int len); + #ifdef __hpux #define seteuid(a) setresuid(-1, (a), -1) #define setegid(a) setresgid(-1, (a), -1) #endif -#endif /* DROPBEAR_AGENTFWD */ +extern const struct ChanType cli_chan_agent; + #endif /* _AGENTFWD_H_ */ diff -r bcc5b69d15a6 -r 7f66b8e40f2d auth.h --- a/auth.h Sun Jul 26 16:14:50 2009 +0000 +++ b/auth.h Wed Jul 29 02:58:33 2009 +0000 @@ -26,6 +26,7 @@ #define _AUTH_H_ #include "includes.h" +#include "signkey.h" #include "chansession.h" void svr_authinitialise(); @@ -73,6 +74,7 @@ int cli_auth_pubkey(); void cli_auth_interactive(); char* getpass_or_cancel(char* prompt); +void cli_auth_pubkey_cleanup(); #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */ @@ -123,19 +125,6 @@ }; -struct SignKeyList; -/* A singly linked list of signing keys */ -struct SignKeyList { - - sign_key *key; - int type; /* The type of key */ - struct SignKeyList *next; - char *filename; - /* the buffer? for encrypted keys, so we can later get - * the private key portion */ - -}; - #ifdef ENABLE_SVR_PUBKEY_OPTIONS struct PubKeyOptions; struct PubKeyOptions { diff -r bcc5b69d15a6 -r 7f66b8e40f2d buffer.c --- a/buffer.c Sun Jul 26 16:14:50 2009 +0000 +++ b/buffer.c Wed Jul 29 02:58:33 2009 +0000 @@ -223,6 +223,20 @@ 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, * but don't bother copying/malloc()ing for it */ void buf_eatstring(buffer *buf) { diff -r bcc5b69d15a6 -r 7f66b8e40f2d buffer.h --- a/buffer.h Sun Jul 26 16:14:50 2009 +0000 +++ b/buffer.h Wed Jul 29 02:58:33 2009 +0000 @@ -55,6 +55,7 @@ unsigned char* buf_getptr(buffer* buf, unsigned int len); unsigned char* buf_getwriteptr(buffer* buf, unsigned int len); unsigned char* buf_getstring(buffer* buf, unsigned int *retlen); +buffer * buf_getstringbuf(buffer *buf); void buf_eatstring(buffer *buf); void buf_putint(buffer* buf, unsigned int val); void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len); diff -r bcc5b69d15a6 -r 7f66b8e40f2d channel.h --- a/channel.h Sun Jul 26 16:14:50 2009 +0000 +++ b/channel.h Wed Jul 29 02:58:33 2009 +0000 @@ -58,7 +58,7 @@ unsigned int recvmaxpacket, transmaxpacket; void* typedata; /* a pointer to type specific data */ 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. Doesn't exactly belong here, but is cleaner here */ circbuffer *writebuf; /* data from the wire, for local consumption */ diff -r bcc5b69d15a6 -r 7f66b8e40f2d chansession.h --- a/chansession.h Sun Jul 26 16:14:50 2009 +0000 +++ b/chansession.h Wed Jul 29 02:58:33 2009 +0000 @@ -60,7 +60,7 @@ unsigned char x11singleconn; #endif -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD struct Listener * agentlistener; char * agentfile; char * agentdir; diff -r bcc5b69d15a6 -r 7f66b8e40f2d cli-agentfwd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-agentfwd.c Wed Jul 29 02:58:33 2009 +0000 @@ -0,0 +1,295 @@ +/* + * 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); + + 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(); + + 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; + } +} + +/* Returned keys are prepended to ret_list, which will + be updated. */ +void 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) { + dropbear_log(LOG_INFO, "Failed to connect to agent"); + 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 diff -r bcc5b69d15a6 -r 7f66b8e40f2d cli-auth.c --- a/cli-auth.c Sun Jul 26 16:14:50 2009 +0000 +++ b/cli-auth.c Wed Jul 29 02:58:33 2009 +0000 @@ -234,6 +234,10 @@ ses.authstate.authdone = 1; cli_ses.state = USERAUTH_SUCCESS_RCVD; cli_ses.lastauthtype = AUTH_TYPE_NONE; + +#ifdef ENABLE_CLI_PUBKEY_AUTH + cli_auth_pubkey_cleanup(); +#endif } void cli_auth_try() { diff -r bcc5b69d15a6 -r 7f66b8e40f2d cli-authpubkey.c --- a/cli-authpubkey.c Sun Jul 26 16:14:50 2009 +0000 +++ b/cli-authpubkey.c Wed Jul 29 02:58:33 2009 +0000 @@ -30,6 +30,7 @@ #include "ssh.h" #include "runopts.h" #include "auth.h" +#include "agentfwd.h" #ifdef ENABLE_CLI_PUBKEY_AUTH static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); @@ -37,31 +38,23 @@ /* 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 */ void cli_pubkeyfail() { - - struct SignKeyList *keyitem; - struct SignKeyList **previtem; - - TRACE(("enter cli_pubkeyfail")) - previtem = &cli_opts.privkeys; - - /* Find the key we failed with, and remove it */ - for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) { - if (keyitem == cli_ses.lastprivkey) { - *previtem = keyitem->next; + m_list_elem *iter; + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { + sign_key *iter_key = (sign_key*)iter->item; + + if (iter_key == cli_ses.lastprivkey) + { + /* found the failing key */ + list_remove(iter); + sign_key_free(iter_key); + cli_ses.lastprivkey = NULL; + return; } - previtem = &keyitem; } - - sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */ - m_free(cli_ses.lastprivkey->filename); - m_free(cli_ses.lastprivkey); - - TRACE(("leave cli_pubkeyfail")) } void recv_msg_userauth_pk_ok() { - - struct SignKeyList *keyitem = NULL; + m_list_elem *iter; buffer* keybuf = NULL; char* algotype = NULL; unsigned int algolen; @@ -81,9 +74,9 @@ /* Iterate through our keys, find which one it was that matched, and * send a real request with that key */ - for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) { - - if (keyitem->type != keytype) { + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { + sign_key *key = (sign_key*)iter->item; + if (key->type != keytype) { /* Types differed */ TRACE(("types differed")) continue; @@ -91,7 +84,7 @@ /* Now we compare the contents of the key */ 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_incrpos(keybuf, 4); /* first int is the length of the remainder (ie remotelen) which has already been taken from @@ -115,11 +108,11 @@ } buf_free(keybuf); - if (keyitem != NULL) { + if (iter != NULL) { TRACE(("matching key")) /* XXX TODO: if it's an encrypted key, here we ask for their * password */ - send_msg_userauth_pubkey(keyitem->key, keytype, 1); + send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1); } 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")) } @@ -127,6 +120,25 @@ 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 */ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { @@ -162,7 +174,7 @@ sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len); buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE); 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 */ } @@ -170,20 +182,42 @@ TRACE(("leave send_msg_userauth_pubkey")) } +/* Returns 1 if a key was tried */ int cli_auth_pubkey() { TRACE(("enter cli_auth_pubkey")) - if (cli_opts.privkeys != NULL) { + if (cli_opts.agent_fwd && + !cli_opts.agent_keys_loaded) { + /* get the list of available keys from the agent */ + 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_msg_userauth_pubkey(cli_opts.privkeys->key, - cli_opts.privkeys->type, 0); - cli_ses.lastprivkey = cli_opts.privkeys; + send_msg_userauth_pubkey(key, key->type, 0); + cli_ses.lastprivkey = key; TRACE(("leave cli_auth_pubkey-success")) return 1; } else { + /* no more keys left */ TRACE(("leave cli_auth_pubkey-failure")) 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 */ diff -r bcc5b69d15a6 -r 7f66b8e40f2d cli-chansession.c --- a/cli-chansession.c Sun Jul 26 16:14:50 2009 +0000 +++ b/cli-chansession.c Wed Jul 29 02:58:33 2009 +0000 @@ -424,16 +424,3 @@ TRACE(("leave cli_send_chansess_request")) } - - -#if 0 - while (cli_opts.localfwds != NULL) { - ret = cli_localtcp(cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); - if (ret == DROPBEAR_FAILURE) { - dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d", - cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); -#endif diff -r bcc5b69d15a6 -r 7f66b8e40f2d cli-runopts.c --- a/cli-runopts.c Sun Jul 26 16:14:50 2009 +0000 +++ b/cli-runopts.c Wed Jul 29 02:58:33 2009 +0000 @@ -29,6 +29,7 @@ #include "dbutil.h" #include "algo.h" #include "tcpfwd.h" +#include "list.h" cli_runopts cli_opts; /* GLOBAL */ @@ -40,7 +41,7 @@ static void loadidentityfile(const char* filename); #endif #ifdef ENABLE_CLI_ANYTCPFWD -static void addforward(const char* str, struct TCPFwdList** fwdlist); +static void addforward(const char* str, m_list *fwdlist); #endif #ifdef ENABLE_CLI_NETCAT static void add_netcat(const char *str); @@ -66,6 +67,9 @@ #ifdef ENABLE_CLI_PUBKEY_AUTH "-i (multiple allowed)\n" #endif +#ifdef ENABLE_CLI_AGENTFWD + "-A Enable agent auth forwarding\n" +#endif #ifdef ENABLE_CLI_LOCALTCPFWD "-L Local port forwarding\n" "-g Allow remote hosts to connect to forwarded ports\n" @@ -125,14 +129,18 @@ cli_opts.always_accept_key = 0; cli_opts.is_subsystem = 0; #ifdef ENABLE_CLI_PUBKEY_AUTH - cli_opts.privkeys = NULL; + cli_opts.privkeys = list_new(); #endif #ifdef ENABLE_CLI_LOCALTCPFWD - cli_opts.localfwds = NULL; + cli_opts.localfwds = list_new(); opts.listen_fwd_all = 0; #endif #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 #ifdef ENABLE_CLI_PROXYCMD cli_opts.proxycmd = NULL; @@ -158,7 +166,7 @@ #ifdef ENABLE_CLI_REMOTETCPFWD if (nextisremote) { TRACE(("nextisremote true")) - addforward(argv[i], &cli_opts.remotefwds); + addforward(argv[i], cli_opts.remotefwds); nextisremote = 0; continue; } @@ -166,7 +174,7 @@ #ifdef ENABLE_CLI_LOCALTCPFWD if (nextislocal) { TRACE(("nextislocal true")) - addforward(argv[i], &cli_opts.localfwds); + addforward(argv[i], cli_opts.localfwds); nextislocal = 0; continue; } @@ -266,6 +274,11 @@ case 'I': next = &idle_timeout_arg; break; +#ifdef ENABLE_CLI_AGENTFWD + case 'A': + cli_opts.agent_fwd = 1; + break; +#endif #ifdef DEBUG_TRACE case 'v': debug_trace = 1; @@ -394,8 +407,6 @@ #ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename) { - - struct SignKeyList * nextkey; sign_key *key; int keytype; @@ -405,12 +416,10 @@ fprintf(stderr, "Failed loading keyfile '%s'\n", filename); sign_key_free(key); } else { - nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); - nextkey->key = key; - nextkey->filename = m_strdup(filename); - nextkey->next = cli_opts.privkeys; - nextkey->type = keytype; - cli_opts.privkeys = nextkey; + key->type = keytype; + key->source = SIGNKEY_SOURCE_RAW_FILE; + key->filename = m_strdup(filename); + list_append(cli_opts.privkeys, key); } } #endif @@ -422,12 +431,13 @@ char *ret; int total; unsigned int len = 0; - struct SignKeyList *nextkey; + m_list_elem *iter; /* Fill out -i and -W options that make sense for all * the intermediate processes */ - for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next) + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { - len += 3 + strlen(nextkey->filename); + sign_key * key = (sign_key*)iter->item; + len += 3 + strlen(key->filename); } len += 20; // space for -W , terminator. ret = m_malloc(len); @@ -439,10 +449,11 @@ total += written; } - for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next) + 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", nextkey->filename); + int written = snprintf(ret+total, size, "-i %s", key->filename); dropbear_assert(written < size); total += written; } @@ -608,12 +619,12 @@ #ifdef ENABLE_CLI_ANYTCPFWD /* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding * set, and add it to the forwarding list */ -static void addforward(const char* origstr, struct TCPFwdList** fwdlist) { +static void addforward(const char* origstr, m_list *fwdlist) { char * listenport = NULL; char * connectport = NULL; char * connectaddr = NULL; - struct TCPFwdList* newfwd = NULL; + struct TCPFwdEntry* newfwd = NULL; char * str = NULL; TRACE(("enter addforward")) @@ -640,7 +651,7 @@ *connectport = '\0'; connectport++; - 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, * the check later only checks for >= MAX_PORT */ @@ -667,8 +678,7 @@ } newfwd->have_reply = 0; - newfwd->next = *fwdlist; - *fwdlist = newfwd; + list_append(fwdlist, newfwd); TRACE(("leave addforward: done")) return; diff -r bcc5b69d15a6 -r 7f66b8e40f2d cli-session.c --- a/cli-session.c Sun Jul 26 16:14:50 2009 +0000 +++ b/cli-session.c Wed Jul 29 02:58:33 2009 +0000 @@ -35,6 +35,7 @@ #include "service.h" #include "runopts.h" #include "chansession.h" +#include "agentfwd.h" static void cli_remoteclosed(); static void cli_sessionloop(); @@ -75,6 +76,9 @@ #ifdef ENABLE_CLI_REMOTETCPFWD &cli_chan_tcpremote, #endif +#ifdef ENABLE_CLI_AGENTFWD + &cli_chan_agent, +#endif NULL /* Null termination */ }; diff -r bcc5b69d15a6 -r 7f66b8e40f2d cli-tcpfwd.c --- a/cli-tcpfwd.c Sun Jul 26 16:14:50 2009 +0000 +++ b/cli-tcpfwd.c Wed Jul 29 02:58:33 2009 +0000 @@ -59,27 +59,22 @@ #ifdef ENABLE_CLI_LOCALTCPFWD void setup_localtcp() { - + m_list_elem *iter; int ret; TRACE(("enter setup_localtcp")) - if (cli_opts.localfwds == NULL) { - TRACE(("cli_opts.localfwds == NULL")) - } - - while (cli_opts.localfwds != NULL) { - ret = cli_localtcp(cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); + for (iter = cli_opts.localfwds->first; iter; iter = iter->next) { + struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item; + ret = cli_localtcp(fwd->listenport, + fwd->connectaddr, + fwd->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); - } - - cli_opts.localfwds = cli_opts.localfwds->next; + fwd->listenport, + fwd->connectaddr, + fwd->connectport); + } } TRACE(("leave setup_localtcp")) @@ -148,60 +143,47 @@ * being in the same order as we sent the requests. This is the ordering * of the cli_opts.remotefwds list */ void cli_recv_msg_request_success() { - /* Nothing in the packet. We just mark off that we have received the reply, * so that we can report failure for later ones. */ - struct TCPFwdList * iter = NULL; - - iter = cli_opts.remotefwds; - while (iter != NULL) { - if (!iter->have_reply) - { - iter->have_reply = 1; + m_list_elem * iter = NULL; + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + if (!fwd->have_reply) { + fwd->have_reply = 1; return; } - iter = iter->next; } } void cli_recv_msg_request_failure() { - struct TCPFwdList * iter = NULL; - - iter = cli_opts.remotefwds; - while (iter != NULL) { - if (!iter->have_reply) - { - iter->have_reply = 1; - dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", iter->listenport, iter->connectaddr, iter->connectport); + m_list_elem *iter; + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + if (!fwd->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); return; } - iter = iter->next; } } void setup_remotetcp() { - - struct TCPFwdList * iter = NULL; - + m_list_elem *iter; TRACE(("enter setup_remotetcp")) - if (cli_opts.remotefwds == NULL) { - TRACE(("cli_opts.remotefwds == NULL")) + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + send_msg_global_request_remotetcp(fwd->listenport); } - iter = cli_opts.remotefwds; - - while (iter != NULL) { - send_msg_global_request_remotetcp(iter->listenport); - iter = iter->next; - } TRACE(("leave setup_remotetcp")) } static int newtcpforwarded(struct Channel * channel) { unsigned int origport; - struct TCPFwdList * iter = NULL; + m_list_elem * iter = NULL; + struct TCPFwdEntry *fwd; char portstring[NI_MAXSERV]; int sock; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; @@ -212,13 +194,11 @@ origport = buf_getint(ses.payload); /* Find which port corresponds */ - iter = cli_opts.remotefwds; - - while (iter != NULL) { - if (origport == iter->listenport) { + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + fwd = (struct TCPFwdEntry*)iter->item; + if (origport == fwd->listenport) { break; } - iter = iter->next; } if (iter == NULL) { @@ -228,8 +208,8 @@ goto out; } - snprintf(portstring, sizeof(portstring), "%d", iter->connectport); - sock = connect_remote(iter->connectaddr, portstring, 1, NULL); + snprintf(portstring, sizeof(portstring), "%d", fwd->connectport); + sock = connect_remote(fwd->connectaddr, portstring, 1, NULL); if (sock < 0) { TRACE(("leave newtcpdirect: sock failed")) err = SSH_OPEN_CONNECT_FAILED; diff -r bcc5b69d15a6 -r 7f66b8e40f2d dbutil.c --- a/dbutil.c Sun Jul 26 16:14:50 2009 +0000 +++ b/dbutil.c Wed Jul 29 02:58:33 2009 +0000 @@ -295,6 +295,28 @@ 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 * return immediately if nonblocking is set. On failure, if errstring * wasn't null, it will be a newly malloced error message */ @@ -341,15 +363,7 @@ } if (nonblocking) { - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - close(sock); - sock = -1; - if (errstring != NULL && *errstring == NULL) { - *errstring = m_strdup("Failed non-blocking"); - } - TRACE(("Failed non-blocking: %s", strerror(errno))) - continue; - } + setnonblocking(sock); } if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { @@ -568,7 +582,6 @@ } return retstring; - } /* Get the hostname corresponding to the address addr. On failure, the IP diff -r bcc5b69d15a6 -r 7f66b8e40f2d dbutil.h --- a/dbutil.h Sun Jul 26 16:14:50 2009 +0000 +++ b/dbutil.h Wed Jul 29 02:58:33 2009 +0000 @@ -52,6 +52,9 @@ int spawn_command(void(*exec_fn)(void *user_data), void *exec_data, int *writefd, int *readfd, int *errfd, pid_t *pid); void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell); +#ifdef ENABLE_CONNECT_UNIX +int connect_unix(const char* addr); +#endif int connect_remote(const char* remotehost, const char* remoteport, int nonblocking, char ** errstring); char* getaddrhostname(struct sockaddr_storage * addr); diff -r bcc5b69d15a6 -r 7f66b8e40f2d debug.h --- a/debug.h Sun Jul 26 16:14:50 2009 +0000 +++ b/debug.h Wed Jul 29 02:58:33 2009 +0000 @@ -39,7 +39,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -/*#define DEBUG_TRACE*/ +#define DEBUG_TRACE /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're diff -r bcc5b69d15a6 -r 7f66b8e40f2d list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/list.c Wed Jul 29 02:58:33 2009 +0000 @@ -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; +} \ No newline at end of file diff -r bcc5b69d15a6 -r 7f66b8e40f2d list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/list.h Wed Jul 29 02:58:33 2009 +0000 @@ -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 */ \ No newline at end of file diff -r bcc5b69d15a6 -r 7f66b8e40f2d options.h --- a/options.h Sun Jul 26 16:14:50 2009 +0000 +++ b/options.h Wed Jul 29 02:58:33 2009 +0000 @@ -65,7 +65,8 @@ #define ENABLE_SVR_REMOTETCPFWD /* 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 diff -r bcc5b69d15a6 -r 7f66b8e40f2d random.c --- a/random.c Sun Jul 26 16:14:50 2009 +0000 +++ b/random.c Wed Jul 29 02:58:33 2009 +0000 @@ -69,12 +69,8 @@ #endif #ifdef DROPBEAR_PRNGD_SOCKET - memset((void*)&egdsock, 0x0, sizeof(egdsock)); - egdsock.sun_family = AF_UNIX; - strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET, - sizeof(egdsock.sun_path)); + readfd = connect_unix(DROPBEAR_PRNGD_SOCKET); - readfd = socket(PF_UNIX, SOCK_STREAM, 0); if (readfd < 0) { dropbear_exit("couldn't open random device"); } diff -r bcc5b69d15a6 -r 7f66b8e40f2d runopts.h --- a/runopts.h Sun Jul 26 16:14:50 2009 +0000 +++ b/runopts.h Wed Jul 29 02:58:33 2009 +0000 @@ -112,13 +112,20 @@ int backgrounded; int is_subsystem; #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 #ifdef ENABLE_CLI_REMOTETCPFWD - struct TCPFwdList * remotefwds; + m_list * remotefwds; #endif #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 #ifdef ENABLE_CLI_NETCAT diff -r bcc5b69d15a6 -r 7f66b8e40f2d session.h --- a/session.h Sun Jul 26 16:14:50 2009 +0000 +++ b/session.h Wed Jul 29 02:58:33 2009 +0000 @@ -265,7 +265,7 @@ info request from the server for interactive auth.*/ #endif - struct SignKeyList *lastprivkey; + sign_key *lastprivkey; int retval; /* What the command exit status was - we emulate it */ #if 0 diff -r bcc5b69d15a6 -r 7f66b8e40f2d signkey.c --- a/signkey.c Sun Jul 26 16:14:50 2009 +0000 +++ b/signkey.c Wed Jul 29 02:58:33 2009 +0000 @@ -40,8 +40,10 @@ #ifdef DROPBEAR_RSA ret->rsakey = NULL; #endif + ret->filename = NULL; + ret->type = DROPBEAR_SIGNKEY_NONE; + ret->source = SIGNKEY_SOURCE_INVALID; return ret; - } /* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally @@ -81,6 +83,9 @@ } #endif + TRACE(("signkey_type_from_name unexpected key type.")) + printhex("Key type", name, namelen); + return DROPBEAR_SIGNKEY_NONE; } @@ -101,8 +106,11 @@ m_free(ident); if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) { + TRACE(("buf_get_pub_key bad type - got %d, expected %d", keytype, type)) return DROPBEAR_FAILURE; } + + TRACE(("buf_get_pub_key keytype is %d")) *type = keytype; @@ -255,6 +263,8 @@ key->rsakey = NULL; #endif + m_free(key->filename); + m_free(key); TRACE(("leave sign_key_free")) } @@ -358,7 +368,6 @@ const unsigned char *data, unsigned int len) { buffer *sigblob; - sigblob = buf_new(MAX_PUBKEY_SIZE); #ifdef DROPBEAR_DSS @@ -374,7 +383,6 @@ if (sigblob->len == 0) { dropbear_exit("non-matching signing type"); } - buf_setpos(sigblob, 0); buf_putstring(buf, buf_getptr(sigblob, sigblob->len), sigblob->len); diff -r bcc5b69d15a6 -r 7f66b8e40f2d signkey.h --- a/signkey.h Sun Jul 26 16:14:50 2009 +0000 +++ b/signkey.h Wed Jul 29 02:58:33 2009 +0000 @@ -29,8 +29,22 @@ #include "dss.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 { + 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 dss_key * dsskey; #endif diff -r bcc5b69d15a6 -r 7f66b8e40f2d ssh.h --- a/ssh.h Sun Jul 26 16:14:50 2009 +0000 +++ b/ssh.h Wed Jul 29 02:58:33 2009 +0000 @@ -105,3 +105,14 @@ #define SSH_SIGNKEY_DSS_LEN 7 #define SSH_SIGNKEY_RSA "ssh-rsa" #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 diff -r bcc5b69d15a6 -r 7f66b8e40f2d svr-agentfwd.c --- a/svr-agentfwd.c Sun Jul 26 16:14:50 2009 +0000 +++ b/svr-agentfwd.c Wed Jul 29 02:58:33 2009 +0000 @@ -181,7 +181,7 @@ } -static const struct ChanType chan_agent = { +static const struct ChanType chan_svr_agent = { 0, /* sepfds */ "auth-agent@openssh.com", NULL, @@ -194,7 +194,7 @@ /* helper for accepting an agent request */ 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(); return DROPBEAR_SUCCESS; } else { diff -r bcc5b69d15a6 -r 7f66b8e40f2d sysoptions.h --- a/sysoptions.h Sun Jul 26 16:14:50 2009 +0000 +++ b/sysoptions.h Wed Jul 29 02:58:33 2009 +0000 @@ -146,10 +146,6 @@ #define DISABLE_X11FWD #endif -#ifndef ENABLE_AGENTFWD -#define DISABLE_AGENTFWD -#endif - #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) #define ENABLE_CLI_ANYTCPFWD #endif @@ -160,7 +156,7 @@ #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_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 #endif @@ -168,6 +164,10 @@ #define ENABLE_CLI_MULTIHOP #endif +#if defined(ENABLE_CLI_AGENTFWD) || defined(DROPBEAR_PRNGD_SOCKET) +#define ENABLE_CONNECT_UNIX +#endif + #if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH) #define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */ #endif diff -r bcc5b69d15a6 -r 7f66b8e40f2d tcpfwd.h --- a/tcpfwd.h Sun Jul 26 16:14:50 2009 +0000 +++ b/tcpfwd.h Wed Jul 29 02:58:33 2009 +0000 @@ -25,6 +25,7 @@ #define _TCPFWD_H #include "channel.h" +#include "list.h" struct TCPListener { @@ -43,16 +44,13 @@ enum {direct, forwarded} tcp_type; }; -/* A link in a list of forwards */ -struct TCPFwdList { - +/* A forwarding entry */ +struct TCPFwdEntry { const unsigned char* connectaddr; unsigned int connectport; unsigned int listenport; unsigned int have_reply; /* is set to 1 after a reply has been received when setting up the forwarding */ - struct TCPFwdList * next; - }; /* Server */ @@ -70,5 +68,4 @@ /* Common */ int listen_tcpfwd(struct TCPListener* tcpinfo); - #endif