From f2e4a5f9b0919dc16ea83a8826e8d52b02178b38 Mon Sep 17 00:00:00 2001 From: Jakub Sławiński Date: Sun, 3 Jul 2005 23:15:19 +0200 Subject: v0.7.1 - Added: afserver certificate storing and checking - Modified: generating keys and certificate - Fixed: creating apf directory - Fixed: some bugs in proxy tunnel initialization --- ChangeLog | 6 + NEWS | 4 + README | 5 +- configure.ac | 18 ++- doc/afclient.1 | 8 +- doc/afserver.1 | 2 +- doc/afserver.conf.5 | 2 +- doc/en/README | 5 +- src/Makefile.am | 7 +- src/activefor.h | 2 +- src/afclient.c | 37 +++++- src/afclient.h | 1 + src/afserver.c | 37 ++++-- src/afserver.h | 1 + src/base64.c | 250 ++++++++++++++++++++++++++++++++++++++ src/base64.h | 41 +++++++ src/client_initialization.c | 86 ++++++++++++- src/client_initialization.h | 2 +- src/daemon.c | 61 ++++++++++ src/daemon.h | 39 ++++++ src/first_run.c | 286 ++++++++++++++++++++++++++++++++++++-------- src/first_run.h | 6 +- src/http_proxy_server.c | 1 + src/inet_ntop.c | 4 + src/make_ssl_handshake.c | 2 +- src/server_get.c | 67 +++++++++-- src/ssl_routines.c | 81 +++++++++++++ src/ssl_routines.h | 34 ++++++ src/usage.c | 5 +- 29 files changed, 1002 insertions(+), 98 deletions(-) create mode 100644 src/base64.c create mode 100644 src/base64.h create mode 100644 src/daemon.c create mode 100644 src/daemon.h create mode 100644 src/ssl_routines.c create mode 100644 src/ssl_routines.h diff --git a/ChangeLog b/ChangeLog index 4d27954..730ae5b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +03.07.2005 (v0.7.1): + - Added: afserver certificate storing and checking + - Modified: generating keys and certificate + - Fixed: creating apf directory + - Fixed: some bugs in proxy tunnel initialization + 07.06.2005 (v0.7): - Added: http proxy tunnels between afserver and afclient - Fixed: sigint interception with threads enabled (in http proxy mode) diff --git a/NEWS b/NEWS index 45ebebe..21ad085 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +03.07.2005: + * a lot of code cleanups and modifications have been made in order + to make sources more portable. APF compile under Solaris 10. + 06.06.2005: * apf 0.7 is ready for public release diff --git a/README b/README index d676098..5dca91c 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -AF - Active Port Forwarder 0.7 - README +AF - Active Port Forwarder 0.7.1 - README Copyright (C) 2003,2004,2005 jeremian - ================================================================= @@ -192,10 +192,13 @@ Multiple clients allow to create more sophisticated tunneling scheme. -i, --id - sends the id string to afserver --pass - set the password used for client identification (default: no password) + --ignorepkeys - ignore invalid server's public keys Configuration: -k, --keyfile - the name of the file with RSA key (default: client.rsa) + -s, --storefile - the name of the file with stored public keys + (default: known_hosts) -D, --dateformat - format of the date printed in logs (see 'man strftime' for details) (default: %d.%m.%Y %H:%M:%S) -K, --keep-alive N - send keepalive packets every N seconds diff --git a/configure.ac b/configure.ac index 6f56eff..e135f66 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT([Active port forwarder], [0.7], [jeremian@poczta.fm], [apf]) -AM_INIT_AUTOMAKE([apf], [0.7]) +AC_INIT([Active port forwarder], [0.7.1], [jeremian@poczta.fm], [apf]) +AM_INIT_AUTOMAKE([apf], [0.7.1]) AC_COPYRIGHT([ Copyright (C) 2003,2004,2005 jeremian - =================== @@ -56,15 +56,10 @@ AC_CHECK_LIB([dl], [dlopen], ]) AC_SUBST(LINKED_LDLIB) AC_SUBST(USE_RDYNAMIC) -AC_CHECK_LIB([pthread], [pthread_create], - [ - LINKED_PTHREADLIB="-lpthread" - AC_DEFINE(HAVE_LIBPTHREAD, 1, [Define to 1 if you have the `pthread' library (-lpthread).]) - ], - [ - LINKED_PTHREADLIB="" - ]) -AC_SUBST(LINKED_PTHREADLIB) +AC_CHECK_LIB([pthread], [pthread_create], [], []) +AC_CHECK_LIB([socket], [socket], [], []) +AC_CHECK_LIB([nsl], [gethostbyaddr], [], []) +AC_CHECK_LIB([crypto], [X509_sign], [], []) # Checks for header files. AC_HEADER_STDC @@ -86,6 +81,7 @@ AC_CHECK_FUNCS([SSL_library_init SSL_get_cipher_name SSL_get_cipher_version ERR_ AC_CHECK_FUNCS([compress uncompress]) AC_CHECK_FUNCS([dlopen dlsym dlclose]) AC_CHECK_FUNCS([getpwnam]) +AC_CHECK_FUNCS([daemon]) AC_CHECK_FUNCS([inet_ntop getnameinfo]) AC_OUTPUT diff --git a/doc/afclient.1 b/doc/afclient.1 index 7fd5a1c..c2d1bc3 100644 --- a/doc/afclient.1 +++ b/doc/afclient.1 @@ -1,4 +1,4 @@ -.TH afclient 1 "apf 0.7" Jeremian +.TH afclient 1 "apf 0.7.1" Jeremian .SH NAME afclient \- active port forwarder client .SH SYNOPSIS @@ -55,10 +55,16 @@ is running (required) .B --pass PASSWORD set the password used for client identification (default: no password) +.B --ignorepkeys + ignore invalid server's public keys + .I Configuration .B -k, --keyfile FILE the name of the file with RSA key (default: client.rsa) + +.B -s, --storefile + the name of the file with stored public keys (default: known_hosts) .B -D, --dateformat FORMAT format of the date printed in logs (see 'man strftime' for details) (default: %d.%m.%Y %H:%M:%S) diff --git a/doc/afserver.1 b/doc/afserver.1 index cf17b49..042789e 100644 --- a/doc/afserver.1 +++ b/doc/afserver.1 @@ -1,4 +1,4 @@ -.TH afserver 1 "apf 0.7" Jeremian +.TH afserver 1 "apf 0.7.1" Jeremian .SH NAME afserver \- active port forwarder server .SH SYNOPSIS diff --git a/doc/afserver.conf.5 b/doc/afserver.conf.5 index 18d1b2a..0337469 100644 --- a/doc/afserver.conf.5 +++ b/doc/afserver.conf.5 @@ -1,4 +1,4 @@ -.TH afserver.conf 5 "apf 0.7" Jeremian +.TH afserver.conf 5 "apf 0.7.1" Jeremian .SH NAME afserver.conf \- Configuration File for afserver .SH INTRODUCTION diff --git a/doc/en/README b/doc/en/README index d676098..5dca91c 100644 --- a/doc/en/README +++ b/doc/en/README @@ -1,4 +1,4 @@ -AF - Active Port Forwarder 0.7 - README +AF - Active Port Forwarder 0.7.1 - README Copyright (C) 2003,2004,2005 jeremian - ================================================================= @@ -192,10 +192,13 @@ Multiple clients allow to create more sophisticated tunneling scheme. -i, --id - sends the id string to afserver --pass - set the password used for client identification (default: no password) + --ignorepkeys - ignore invalid server's public keys Configuration: -k, --keyfile - the name of the file with RSA key (default: client.rsa) + -s, --storefile - the name of the file with stored public keys + (default: known_hosts) -D, --dateformat - format of the date printed in logs (see 'man strftime' for details) (default: %d.%m.%Y %H:%M:%S) -K, --keep-alive N - send keepalive packets every N seconds diff --git a/src/Makefile.am b/src/Makefile.am index 282b514..9910314 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,13 +1,12 @@ bin_PROGRAMS = afserver afclient -afserver_LDFLAGS = ${LINKED_PTHREADLIB} afserver_SOURCES = afserver.c network.c file.c stats.c buflist.c remoteadmin.c \ server_check.c server_set.c server_eval.c server_find.c server_remove.c \ make_ssl_handshake.c first_run.c inet_ntop.c realmnames.c clientnames.c usernames.c \ http_proxy_functions.c http_proxy_server.c server_get.c thread_management.c \ - server_signals.c usage.c logging.c audit.c -afclient_LDFLAGS = ${USE_RDYNAMIC} ${LINKED_LDLIB} ${LINKED_PTHREADLIB} + server_signals.c usage.c logging.c audit.c daemon.c +afclient_LDFLAGS = ${USE_RDYNAMIC} ${LINKED_LDLIB} afclient_SOURCES = afclient.c network.c stats.c buflist.c modules.c remoteadmin.c \ make_ssl_handshake.c first_run.c inet_ntop.c realmnames.c clientnames.c usernames.c \ http_proxy_functions.c http_proxy_client.c thread_management.c \ client_reverse_udp.c server_check.c client_initialization.c client_shutdown.c \ - client_signals.c usage.c logging.c audit.c + client_signals.c usage.c logging.c audit.c daemon.c base64.c ssl_routines.c diff --git a/src/activefor.h b/src/activefor.h index 76564cb..d058a9c 100644 --- a/src/activefor.h +++ b/src/activefor.h @@ -46,7 +46,7 @@ #define S_STATE_OPEN 7 #define S_STATE_STOPPED 11 -#define AF_VER(info) info" v0.7" +#define AF_VER(info) info" v0.7.1" #define TYPE_TCP 1 #define TYPE_UDP 3 diff --git a/src/afclient.c b/src/afclient.c index 72f1f9c..798ec55 100644 --- a/src/afclient.c +++ b/src/afclient.c @@ -32,8 +32,10 @@ static struct option long_options[] = { {"portnum", 1, 0, 'p'}, {"verbose", 0, 0, 'v'}, {"keyfile", 1, 0, 'k'}, + {"storefile", 1, 0, 's'}, {"log", 1, 0, 'o'}, {"pass", 1, 0, 301}, + {"ignorepkeys", 0, 0, 302}, #ifdef AF_INET6 {"ipv4", 0, 0, '4'}, {"ipv6", 0, 0, '6'}, @@ -85,6 +87,7 @@ main(int argc, char **argv) char* desnam = NULL; char* despor = NULL; char* keys = NULL; + char* store = NULL; char* dateformat = NULL; char* katimeout = NULL; char* artries = NULL; @@ -95,6 +98,7 @@ main(int argc, char **argv) char reverse = 0; char tunneltype = 0; char type = 0; + char ignorepkeys = 0; struct sigaction act; #ifdef HAVE_LIBDL moduleT module = {0, NULL, NULL, NULL, NULL}, secmodule = {0, NULL, NULL, NULL, NULL}; @@ -132,7 +136,7 @@ main(int argc, char **argv) #endif while ((n = getopt_long(argc, argv, - GETOPT_LONG_LIBDL(GETOPT_LONG_LIBPTHREAD(GETOPT_LONG_AF_INET6("huUn:m:d:p:vk:o:i:D:rP:X:VK:A:T:"))) + GETOPT_LONG_LIBDL(GETOPT_LONG_LIBPTHREAD(GETOPT_LONG_AF_INET6("huUn:m:d:p:vk:s:o:i:D:rP:X:VK:A:T:"))) , long_options, 0)) != -1) { switch (n) { case 'h': { @@ -185,6 +189,10 @@ main(int argc, char **argv) keys = optarg; break; } + case 's': { + store = optarg; + break; + } case 'o': { addlogtarget(optarg); break; @@ -197,6 +205,10 @@ main(int argc, char **argv) } break; } + case 302: { + ignorepkeys = 1; + break; + } #ifdef AF_INET6 case '4': { if (ipfam != 0) { @@ -290,6 +302,9 @@ main(int argc, char **argv) if (keys == NULL) { keys = "client.rsa"; } + if (store == NULL) { + store = "known_hosts"; + } if ((reverse == 0) && (remote == 0) && (desnam == NULL)) { gethostname(hostname, 100); desnam = hostname; @@ -360,9 +375,17 @@ main(int argc, char **argv) "Setting cipher list failed... exiting"); exit(1); } - if ((temp2 = create_apf_dir())) { + if ((temp2 = create_apf_dir(0))) { aflog(LOG_T_INIT, LOG_I_WARNING, "Warning: Creating ~/.apf directory failed (%d)", temp2); + if ((temp2 = create_apf_dir(1))) { + aflog(LOG_T_INIT, LOG_I_WARNING, + "Warning: Creating ./apf directory failed (%d)", temp2); + } + } + if ((temp2 = create_publickey_store(&store))) { + aflog(LOG_T_INIT, LOG_I_WARNING, + "Warning: Something bad happened when creating public key store... (%d)", temp2); } if ((temp2 = generate_rsa_key(&keys))) { aflog(LOG_T_INIT, LOG_I_WARNING, @@ -397,9 +420,11 @@ main(int argc, char **argv) } #ifdef HAVE_LIBPTHREAD - initialize_client_stage1(tunneltype, &master, name, manage, proxyname, proxyport, ipfam, ctx, buff, pass, 1); + initialize_client_stage1(tunneltype, &master, name, manage, proxyname, proxyport, + ipfam, ctx, buff, pass, 1, ignorepkeys); #else - initialize_client_stage1(tunneltype, &master, name, manage, NULL, NULL, ipfam, ctx, buff, pass, 1); + initialize_client_stage1(tunneltype, &master, name, manage, NULL, NULL, + ipfam, ctx, buff, pass, 1, ignorepkeys); #endif if (remote) { @@ -678,10 +703,10 @@ main(int argc, char **argv) if (temp2 == 0) { #ifdef HAVE_LIBPTHREAD if (initialize_client_stage1(tunneltype, &master, name, manage, proxyname, proxyport, - ipfam, ctx, buff, pass, 0)) { + ipfam, ctx, buff, pass, 0, ignorepkeys)) { #else if (initialize_client_stage1(tunneltype, &master, name, manage, NULL, NULL, - ipfam, ctx, buff, pass, 0)) { + ipfam, ctx, buff, pass, 0, ignorepkeys)) { #endif temp2 = 1; } diff --git a/src/afclient.h b/src/afclient.h index c2b52bb..156b408 100644 --- a/src/afclient.h +++ b/src/afclient.h @@ -36,6 +36,7 @@ #include "usage.h" #include "logging.h" #include "audit.h" +#include "daemon.h" #include #include diff --git a/src/afserver.c b/src/afserver.c index 76f8ffc..2361ebe 100644 --- a/src/afserver.c +++ b/src/afserver.c @@ -428,9 +428,13 @@ main(int argc, char **argv) "Setting ciphers list failed... exiting"); exit(1); } - if ((flags = create_apf_dir())) { + if ((flags = create_apf_dir(0))) { aflog(LOG_T_INIT, LOG_I_WARNING, "Warning: Creating ~/.apf directory failed (%d)", flags); + if ((flags = create_apf_dir(1))) { + aflog(LOG_T_INIT, LOG_I_WARNING, + "Warning: Creating ./apf directory failed (%d)", flags); + } } if ((flags = generate_rsa_key(&config.keys))) { aflog(LOG_T_INIT, LOG_I_WARNING, @@ -450,7 +454,6 @@ main(int argc, char **argv) "Setting certificate failed (%s)... exiting", config.certif); exit(1); } - if (config.size == 0) { aflog(LOG_T_INIT, LOG_I_CRIT, "Working without sense is really without sense..."); @@ -964,8 +967,14 @@ main(int argc, char **argv) len = pointer->addrlen; sent = accept(pointer->usrclitable[l].listenfd, pointer->cliaddr, &len); if (sent == -1) { - aflog(LOG_T_USER, LOG_I_DDEBUG, - "realm[%s]: listenfd: FD_ISSET --> EAGAIN", get_realmname(&config, j)); + if (errno == EAGAIN) { + aflog(LOG_T_USER, LOG_I_DDEBUG, + "realm[%s]: listenfd: FD_ISSET --> EAGAIN", get_realmname(&config, j)); + } + else { + aflog(LOG_T_USER, LOG_I_DDEBUG, + "realm[%s]: listenfd: FD_ISSET --> errno=%d", get_realmname(&config, j), errno); + } continue; } flags = fcntl(sent, F_GETFL, 0); @@ -1033,8 +1042,14 @@ main(int argc, char **argv) len = pointer->addrlen; sent = accept(pointer->clitable[k].listenfd, pointer->cliaddr, &len); if (sent == -1) { - aflog(LOG_T_USER, LOG_I_DDEBUG, - "realm[%s]: listenfd: FD_ISSET --> EAGAIN", get_realmname(&config, j)); + if (errno == EAGAIN) { + aflog(LOG_T_USER, LOG_I_DDEBUG, + "realm[%s]: listenfd: FD_ISSET --> EAGAIN", get_realmname(&config, j)); + } + else { + aflog(LOG_T_USER, LOG_I_DDEBUG, + "realm[%s]: listenfd: FD_ISSET --> errno=%d", get_realmname(&config, j), errno); + } continue; } flags = fcntl(sent, F_GETFL, 0); @@ -1854,8 +1869,14 @@ main(int argc, char **argv) sent = accept(pointer->usrclitable[l].managefd, pointer->cliaddr, &len); #endif if (sent == -1) { - aflog(LOG_T_USER, LOG_I_DDEBUG, - "realm[%s]: listenfd: FD_ISSET --> EAGAIN", get_realmname(&config, j)); + if (errno == EAGAIN) { + aflog(LOG_T_USER, LOG_I_DDEBUG, + "realm[%s]: managefd: FD_ISSET --> EAGAIN", get_realmname(&config, j)); + } + else { + aflog(LOG_T_USER, LOG_I_DDEBUG, + "realm[%s]: managefd: FD_ISSET --> errno=%d", get_realmname(&config, j), errno); + } continue; } flags = fcntl(sent, F_GETFL, 0); diff --git a/src/afserver.h b/src/afserver.h index 816185d..1c9dab5 100644 --- a/src/afserver.h +++ b/src/afserver.h @@ -39,6 +39,7 @@ #include "server_signals.h" #include "usage.h" #include "logging.h" +#include "daemon.h" #include #include diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..a3732ca --- /dev/null +++ b/src/base64.c @@ -0,0 +1,250 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003,2004,2005 jeremian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "base64.h" + +/* + * Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +int +b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) +{ + size_t datalength = 0; + uint8_t input[3]; + uint8_t output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + if (output[0] >= 64) return -1; + if (output[1] >= 64) return -1; + if (output[2] >= 64) return -1; + if (output[3] >= 64) return -1; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = (uint8_t) '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + if (output[0] >= 64) return -1; + if (output[1] >= 64) return -1; + if (output[2] >= 64) return -1; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (int) (datalength); +} + +int +b64_pton(char const *src, uint8_t *target, size_t targsize) +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace((unsigned char)ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) { + /* A non-base64 character. */ + return (-1); + } + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace((unsigned char)ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace((unsigned char)ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..0e88f78 --- /dev/null +++ b/src/base64.h @@ -0,0 +1,41 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003,2004,2005 jeremian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#ifndef _JS_BASE64_H +#define _JS_BASE64_H + +/* routine to encode src with base64 algorithm */ +int b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize); +/* routine to decode src with base64 algorithm */ +int b64_pton(char const *src, uint8_t *target, size_t targsize); + +#endif diff --git a/src/client_initialization.c b/src/client_initialization.c index a5ab560..c6022ae 100644 --- a/src/client_initialization.c +++ b/src/client_initialization.c @@ -21,14 +21,25 @@ #include #include "client_initialization.h" +#include "first_run.h" #include "network.h" +#include "base64.h" +#include "ssl_routines.h" int initialize_client_stage1(char tunneltype, clifd* master, char* name, char* manage, char* proxyname, char* proxyport, char ipfam, SSL_CTX* ctx, unsigned char* buff, unsigned char* pass, - char wanttoexit) + char wanttoexit, char ignorepkeys) { - int n; + int n, nlen, elen, len; + unsigned int olen; + X509* server_cert; + const EVP_MD *md; + EVP_PKEY* pkey; + EVP_MD_CTX md_ctx; + unsigned char *encoded = NULL; + char b64_encoded[100]; + unsigned char *key_buf = NULL; switch (tunneltype) { case 0: { if (ip_connect(&(master->commfd), name, manage, ipfam)) { @@ -82,6 +93,7 @@ initialize_client_stage1(char tunneltype, clifd* master, char* name, char* manag break; } } + master->ssl = SSL_new(ctx); if (SSL_set_fd(master->ssl, master->commfd) != 1) { aflog(LOG_T_INIT, LOG_I_CRIT, @@ -97,12 +109,80 @@ initialize_client_stage1(char tunneltype, clifd* master, char* name, char* manag aflog(LOG_T_INIT, LOG_I_INFO, "Trying SSL_connect"); if ((n = SSL_connect(master->ssl)) == 1) { + if ((server_cert = SSL_get_peer_certificate(master->ssl)) == NULL) { + aflog(LOG_T_MAIN, LOG_I_CRIT, + "Server did not present a certificate... exiting"); + exit(1); + } + /* FIXME: change almost everything here */ + pkey = X509_get_pubkey(server_cert); + if (pkey == NULL) { + aflog(LOG_T_MAIN, LOG_I_CRIT, + "Server's public key is invalid... exiting"); + exit(1); + } + nlen = BN_num_bytes(pkey->pkey.rsa->n); + elen = BN_num_bytes(pkey->pkey.rsa->e); + len = nlen + elen; + key_buf = malloc(len); + if (key_buf == NULL) { + aflog(LOG_T_MAIN, LOG_I_CRIT, + "Cannot allocate memory for server's public key checking... exiting"); + exit(1); + } + BN_bn2bin(pkey->pkey.rsa->n, key_buf); + BN_bn2bin(pkey->pkey.rsa->e, key_buf + nlen); + md = EVP_md5(); + EVP_DigestInit(&md_ctx, md); + EVP_DigestUpdate(&md_ctx, key_buf, len); + encoded = calloc(1, EVP_MAX_MD_SIZE+1); + if (encoded == NULL) { + aflog(LOG_T_MAIN, LOG_I_CRIT, + "Cannot allocate memory for server's public key checking... exiting"); + exit(1); + } + EVP_DigestFinal(&md_ctx, encoded, &olen); + + if (b64_ntop(encoded, olen, b64_encoded, 100) == -1) { + aflog(LOG_T_MAIN, LOG_I_CRIT, + "Problem with base64 encoding... exiting"); + exit(1); + } + + switch (check_public_key(get_store_filename(), name, b64_encoded)) { + case SSL_PUBLIC_KEY_VALID: + /* public key is ok - do nothing */ + break; + case SSL_PUBLIC_KEY_NOT_KNOWN: + aflog(LOG_T_MAIN, LOG_I_WARNING, + "WARNING: implicitly added new server's public key to the list of known hosts"); + add_public_key(get_store_filename(), name, b64_encoded); + break; + default: + if (ignorepkeys) { + aflog(LOG_T_MAIN, LOG_I_WARNING, + "WARNING: Invalid server's public key... ignoring"); + } + else { + aflog(LOG_T_MAIN, LOG_I_CRIT, + "Invalid server's public key... exiting"); + aflog(LOG_T_MAIN, LOG_I_CRIT, + "Please delete conflicting entry in %s or use '--ignorepkeys' option", + get_store_filename()); + exit(1); + } + } + + memset(key_buf, 0, len); + free(key_buf); + free(encoded); + aflog(LOG_T_INIT, LOG_I_INFO, "SSL_connect successful"); } else { aflog(LOG_T_INIT, LOG_I_CRIT, - "SSL_connect has failed (%d)... exiting", n); + "SSL_connect has failed (%d | %d)... exiting", n, SSL_get_error(master->ssl, n)); if (wanttoexit) { exit(1); } diff --git a/src/client_initialization.h b/src/client_initialization.h index b8c534a..89235c7 100644 --- a/src/client_initialization.h +++ b/src/client_initialization.h @@ -32,7 +32,7 @@ int initialize_client_stage1(char tunneltype, clifd* master, char* name, char* manage, char* proxyname, char* proxyport, char ipfam, SSL_CTX* ctx, unsigned char* buff, unsigned char* pass, - char wanttoexit); + char wanttoexit, char ignorepkeys); int initialize_client_stage2(char *type, clifd* master, int* usernum, unsigned char* buff, char wanttoexit); int initialize_client_stage3(ConnectuserT** contable, clifd* master, int usernum, int* buflength, socklen_t* len, fd_set* allset, fd_set* wset, int* maxfdp1, char wanttoexit); diff --git a/src/daemon.c b/src/daemon.c new file mode 100644 index 0000000..8472d3a --- /dev/null +++ b/src/daemon.c @@ -0,0 +1,61 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003,2004,2005 jeremian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "daemon.h" + +#ifndef HAVE_DAEMON + +#ifndef HAVE_THIS_DAEMON +#define HAVE_THIS_DAEMON + +int +daemon(int nochdir, int noclose) +{ + int retval; + if ((retval = fork()) == 0) { + /* child process */ + setsid(); + if (nochdir == 0) { + chdir("/"); + } + if (noclose == 0) { + retval = open("/dev/null", O_RDWR); + if (retval == -1) { + return retval; + } + dup2(retval, STDIN_FILENO); + dup2(retval, STDOUT_FILENO); + dup2(retval, STDERR_FILENO); + close(retval); + } + } + else { + /* parent process */ + if (retval == -1) { + return retval; + } + _exit(0); + } + return 0; +} + +#endif + +#endif diff --git a/src/daemon.h b/src/daemon.h new file mode 100644 index 0000000..2994845 --- /dev/null +++ b/src/daemon.h @@ -0,0 +1,39 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003,2004,2005 jeremian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef HAVE_DAEMON + +#include +#include +#include +#include + +# ifndef _JS_DAEMON_H +# define _JS_DAEMON_H + +int daemon(int nochdir, int noclose); + +# endif + +#endif diff --git a/src/first_run.c b/src/first_run.c index 842a3e0..4c866c0 100644 --- a/src/first_run.c +++ b/src/first_run.c @@ -28,52 +28,148 @@ #include #include #include +#include +#include static char* home_dir = NULL; +static char* home_dir_store = NULL; static char* home_dir_key = NULL; static char* home_dir_cer = NULL; +typedef struct entry +{ + char *key; + unsigned char *value; +} entryT; + +static +entryT entries[6] = { + {"countryName", (unsigned char*) "PL"}, + {"stateOrProvinceName", (unsigned char*) "War-Maz"}, + {"localityName", (unsigned char*) "Olsztyn"}, + {"organizationName", (unsigned char*) "gray-world.net"}, + {"organizationalUnitName", (unsigned char*) "APF team"}, + {"commonName", (unsigned char*) "Jeremian "}, +}; + +static void +callback(int i, int j, void* k) +{ + if (k == NULL) { + printf("%d", i); + fflush(stdout); + } +} + +/* + * Name: create_apf_dir + * Description: creates .apf directory in ~/ or apf directory locally + * Arguments: type - type of the directory to create: + * 0 - .apf in ~/ + * 1 - apf in current dir + * Returns: 0 - success + * 1 - problems with fetching user info + * 2 - home directory is not set + * 3 - calloc failure + * 4 - directory creation failure + */ + int -create_apf_dir() +create_apf_dir(char type) { int length; struct stat buf; struct passwd *user = getpwuid(getuid()); - if (user == NULL) { - return 1; /* some problems witch fetching user info*/ + if (type == 0) { + if (user == NULL) { + return 1; /* some problems with fetching user info*/ + } + if (user->pw_dir == NULL) { + return 2; /* home directory is not set? */ + } + if (home_dir) { + free(home_dir); + home_dir = NULL; + } + length = strlen(user->pw_dir); + home_dir = calloc(1, length + 6); + if (home_dir == NULL) { + return 3; /* calloc failed */ + } + strcpy(home_dir, user->pw_dir); + if (home_dir[length] == '/') { + strcpy(&home_dir[length], ".apf"); + } + else { + strcpy(&home_dir[length], "/.apf"); + } + if (stat(home_dir, &buf)) { + if (mkdir(home_dir, 0700)) { + return 4; /* creating directory failed */ + } + } } - if (user->pw_dir == NULL) { - return 2; /* home directory is not set? */ + else { + if (home_dir) { + free(home_dir); + home_dir = NULL; + } + home_dir = calloc(1, 4); + if (home_dir == NULL) { + return 3; /* calloc failed */ + } + strcpy(home_dir, "apf"); + if (stat(home_dir, &buf)) { + if (mkdir(home_dir, 0700)) { + return 4; /* creating directory failed */ + } + } } - if (home_dir) { - free(home_dir); - home_dir = NULL; + return 0; +} + +int +create_publickey_store(char** storefile) +{ + int store_length, home_length; + struct stat buf; + FILE* store_file; + /* check in local directory first */ + if (stat(*storefile, &buf) == 0) { + return 0; } - length = strlen(user->pw_dir); - home_dir = calloc(1, length + 6); - if (home_dir == NULL) { - return 3; /* calloc failed */ + /* check in home_dir */ + store_length = strlen(*storefile); + home_length = strlen(home_dir); + if (home_dir_store) { + free(home_dir_store); + home_dir_store = NULL; } - strcpy(home_dir, user->pw_dir); - if (home_dir[length] == '/') { - strcpy(&home_dir[length], ".apf"); + home_dir_store = calloc(1, home_length + store_length + 2); + if (home_dir_store == NULL) { + return 1; /* calloc failed */ } - else { - strcpy(&home_dir[length], "/.apf"); + strcpy(home_dir_store, home_dir); + home_dir_store[home_length] = '/'; + strcpy(&home_dir_store[home_length+1], *storefile); + *storefile = home_dir_store; + store_file = fopen(home_dir_store, "a"); + if (store_file == NULL) { + return 1; } - if (stat(home_dir, &buf)) { - if (mkdir(home_dir, 0700)) { - return 4; /* creating directory failed */ - } + fclose(store_file); + if (stat(home_dir_store, &buf) == 0) { + return 0; } - return 0; + return 2; } int generate_rsa_key(char** keyfile) { - int key_length, home_length, status; - char openssl_cmd[101]; + int key_length, home_length; + RSA* rsa_key; + FILE* rsa_file; struct stat buf; /* check in local directory first */ if (stat(*keyfile, &buf) == 0) { @@ -98,22 +194,34 @@ generate_rsa_key(char** keyfile) return 0; } /* have to generate the key */ - if (snprintf(openssl_cmd, 101, "openssl genrsa -out %s 2048", home_dir_key) > 100) { - return 2; /* string is too long */ + printf("generating rsa key: 2048 bits\n"); + rsa_key = RSA_generate_key(2048, 65537, callback, NULL); + if (RSA_check_key(rsa_key)==1) { + printf(" OK!\n"); } - status = system(openssl_cmd); - if (status == -1) { - return -1; + else { + printf(" FAILED!\n"); + return 1; } - return WEXITSTATUS(status); + + rsa_file = fopen(home_dir_key, "a"); + PEM_write_RSAPrivateKey(rsa_file, rsa_key, NULL, NULL, 0, NULL, NULL); + fclose(rsa_file); + return 0; } int generate_certificate(char** cerfile, char* keyfile) { - int cer_length, home_length, status, tmp_fd1, tmp_fd2; - char openssl_cmd[301]; + int cer_length, home_length, i; struct stat buf; + X509* cert; + X509_REQ* req; + X509_NAME* subj; + RSA* rsa_key; + EVP_PKEY* pkey; + const EVP_MD *digest; + FILE* fp; /* check in local directory first */ if (stat(*cerfile, &buf) == 0) { return 0; @@ -137,20 +245,102 @@ generate_certificate(char** cerfile, char* keyfile) return 0; } /* have to generate the certificate */ - if (snprintf(openssl_cmd, 201, "echo -e \"pl\nWar-Maz\nOlsztyn\nSHEG\nUtils productions\njeremian\njeremian@poczta.fm\" | openssl req -new -x509 -key %s -out %s -days 1095", keyfile, home_dir_cer) > 300) { - return 2; /* string is too long */ - } - tmp_fd1 = dup(STDOUT_FILENO); - tmp_fd2 = dup(STDERR_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - status = system(openssl_cmd); - dup2(tmp_fd1, STDOUT_FILENO); - dup2(tmp_fd2, STDERR_FILENO); - close(tmp_fd1); - close(tmp_fd2); - if (status == -1) { - return -1; - } - return WEXITSTATUS(status); + printf("generating self signed certificate\n"); + fp = fopen(keyfile, "r"); + if (fp == NULL) { + return 2; /* can't open keyfile */ + } + rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); + fclose(fp); + if (rsa_key == NULL) { + return 3; /* can't read RSAPrivateKey */ + } + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + return 4; /* creating new pkey failed */ + } + if (EVP_PKEY_set1_RSA(pkey, rsa_key) == 0) { + return 5; /* setting rsa key failed */ + } + req = X509_REQ_new(); + if (req == NULL) { + return 6; /* creating new request failed */ + } + X509_REQ_set_pubkey(req, pkey); + subj = X509_NAME_new(); + if (subj == NULL) { + return 7; /* creating new subject name failed */ + } + + for (i = 0; i < 6; i++) + { + int nid; + X509_NAME_ENTRY *ent; + + if ((nid = OBJ_txt2nid(entries[i].key)) == NID_undef) + { + return 8; /* finding NID for a key failed */ + } + ent = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC,entries[i].value, -1); + if (ent == NULL) { + return 9; /* creating name entry from NID failed */ + } + if (X509_NAME_add_entry(subj, ent, -1, 0) == 0) { + return 10; /* adding entry to name failed */ + } + } + if (X509_REQ_set_subject_name(req, subj) == 0) { + return 11; /* adding subject to request failed */ + } + + digest = EVP_sha1(); + + if (X509_REQ_sign(req, pkey, digest) == 0) { + return 12; /* signing request failed */ + } + + cert = X509_REQ_to_X509(req, 1000, pkey); + + if (X509_set_version(cert, 2L) == 0) { + return 13; /* setting certificate version failed */ + } + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + + if (cert == NULL) { + return 14; /* creating certificate failed */ + } + + if (X509_sign(cert, pkey, digest) == 0) { + return 15; /* signing failed */ + } + + fp = fopen(home_dir_cer, "w"); + if (fp == NULL) { + return 16; /* writing certificate failed */ + } + PEM_write_X509(fp, cert); + fclose(fp); + + EVP_PKEY_free(pkey); + X509_REQ_free(req); + X509_free(cert); + return 0; +} + +char* +get_store_filename() +{ + return home_dir_store; +} + +char* +get_key_filename() +{ + return home_dir_key; +} + +char* +get_cer_filename() +{ + return home_dir_cer; } diff --git a/src/first_run.h b/src/first_run.h index 5a36ff6..ee9d6d5 100644 --- a/src/first_run.h +++ b/src/first_run.h @@ -21,9 +21,13 @@ #ifndef _JS_FIRST_RUN_H #define _JS_FIRST_RUN_H -int create_apf_dir(); +int create_apf_dir(char type); +int create_publickey_store(char**); int generate_rsa_key(char**); int generate_certificate(char**, char*); +char* get_store_filename(); +char* get_key_filename(); +char* get_cer_filename(); #endif diff --git a/src/http_proxy_server.c b/src/http_proxy_server.c index a07d6ce..f98026c 100644 --- a/src/http_proxy_server.c +++ b/src/http_proxy_server.c @@ -258,6 +258,7 @@ http_proxy_server(void *vptr) else { aflog(LOG_T_MAIN, LOG_I_DEBUG, "http proxy: New connection --> EAGAIN"); + continue; } memset(tab, 0, 9000); nothttp = 0; diff --git a/src/inet_ntop.c b/src/inet_ntop.c index 6d37620..cb52713 100644 --- a/src/inet_ntop.c +++ b/src/inet_ntop.c @@ -38,6 +38,8 @@ #include "inet_ntop.h" +#ifndef HAVE_INET_NTOP + #ifndef HAVE_THIS_INET_NTOP #define HAVE_THIS_INET_NTOP @@ -225,3 +227,5 @@ inet_ntop6(src, dst, size) #endif #endif + +#endif diff --git a/src/make_ssl_handshake.c b/src/make_ssl_handshake.c index d4cdd55..ffb4ebd 100644 --- a/src/make_ssl_handshake.c +++ b/src/make_ssl_handshake.c @@ -108,7 +108,7 @@ get_ssl_error(clifd *cliconn, char* info, int result) "%s(%d): unrecognized error (%d)", info, result, errno); } } - if (merror == SSL_ERROR_WANT_READ) { + if ((merror == SSL_ERROR_WANT_READ) || (merror == SSL_ERROR_WANT_WRITE)) { return 1; } return 2; diff --git a/src/server_get.c b/src/server_get.c index 7ada39c..5066f54 100644 --- a/src/server_get.c +++ b/src/server_get.c @@ -22,28 +22,79 @@ #include "server_get.h" #include +#include int get_new_socket(int sockfd, char type, struct sockaddr *addr, socklen_t *addrlen, char* tunneltype) { int tmp; + int n, i; switch (type) { case 0: { return accept(sockfd, addr, addrlen); break; } case 1: { - if (read(sockfd, &tmp, 4) != 4) { - return -1; + i = 0; + while (i < 4) { + if ((n = read(sockfd, &tmp+i, 4-i)) != (4-i)) { + sleep(2); + if ((n > 0) && (n < 4)) { + i += n; + continue; + } + if ((n == -1) && (errno == EAGAIN)) { + continue; + } + return -1; + } + else { + break; + } } - if (read(sockfd, tunneltype, 1) != 1) { - return -1; + i = 0; + while (i < 1) { + if ((n = read(sockfd, tunneltype+i, 1-i)) != (1-i)) { + if ((n == -1) && (errno == EAGAIN)) { + continue; + } + return -1; + } + else { + break; + } } - if (read(sockfd, addrlen, 4) != 4) { - return -1; + i = 0; + while (i < 4) { + if ((n = read(sockfd, addrlen+i, 4-i)) != (4-i)) { + if ((n > 0) && (n < 4)) { + i += n; + continue; + } + if ((n == -1) && (errno == EAGAIN)) { + continue; + } + return -1; + } + else { + break; + } } - if (read(sockfd, addr, *addrlen) != *addrlen) { - return -1; + i = 0; + while (i < *addrlen) { + if ((n = read(sockfd, addr+i, (*addrlen)-i)) != ((*addrlen)-i)) { + if ((n > 0) && (n < *addrlen)) { + i += n; + continue; + } + if ((n == -1) && (errno == EAGAIN)) { + continue; + } + return -1; + } + else { + break; + } } return tmp; break; diff --git a/src/ssl_routines.c b/src/ssl_routines.c new file mode 100644 index 0000000..29f0968 --- /dev/null +++ b/src/ssl_routines.c @@ -0,0 +1,81 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003,2004,2005 jeremian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "ssl_routines.h" +#include +#include +#include + +int +check_public_key(char* filename, char* hostname, char* keyhash) +{ + FILE* storefile; + char buff[256]; + int lspaceind, i; + + memset(buff, 0, 256); + + storefile = fopen(filename, "r"); + if (storefile == NULL) { + return SSL_PUBLIC_KEY_NOT_KNOWN; + } + + while (fgets(buff, 256, storefile) != NULL) { + lspaceind = -1; + for (i = 0; i < 256; ++i) { + if (buff[i] == 0) { + break; + } + if (buff[i] == ' ') { + lspaceind = i; + } + } + if (lspaceind == -1) { + continue; + } + if (buff[strlen(buff)-1] == '\n') { + buff[strlen(buff)-1] = 0; + } + buff[lspaceind] = 0; + if (strcmp(buff, hostname) == 0) { + if (strcmp(&buff[lspaceind+1], keyhash) == 0) { + return SSL_PUBLIC_KEY_VALID; + } + else { + return SSL_PUBLIC_KEY_INVALID; + } + } + } + return SSL_PUBLIC_KEY_NOT_KNOWN; +} + +void +add_public_key(char* filename, char* hostname, char* keyhash) +{ + FILE* storefile; + storefile = fopen(filename, "a"); + if (storefile == NULL) { + return; + } + fprintf(storefile, "%s %s\n", hostname, keyhash); + fclose(storefile); +} diff --git a/src/ssl_routines.h b/src/ssl_routines.h new file mode 100644 index 0000000..d35a344 --- /dev/null +++ b/src/ssl_routines.h @@ -0,0 +1,34 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003,2004,2005 jeremian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#define SSL_PUBLIC_KEY_INVALID 0 +#define SSL_PUBLIC_KEY_VALID 1 +#define SSL_PUBLIC_KEY_NOT_KNOWN 2 + +#ifndef _JS_SSL_ROUTINES_H +#define _JS_SSL_ROUTINES_H + +/* check if hostname and keyhash is known */ +int check_public_key(char* filename, char* hostname, char* keyhash); +/* add hostname and keyhash to known_hosts file */ +void add_public_key(char* filename, char* hostname, char* keyhash); + +#endif + diff --git a/src/usage.c b/src/usage.c index 4510781..7d73f61 100644 --- a/src/usage.c +++ b/src/usage.c @@ -126,9 +126,12 @@ client_long_usage(char* info) printf(" Authorization:\n\n"); printf(" -i, --id - sends the id string to afserver\n"); printf(" --pass - set the password used for client identification\n"); - printf(" (default: no password)\n\n"); + printf(" (default: no password)\n"); + printf(" --ignorepkeys - ignore invalid server's public keys\n\n"); printf(" Configuration:\n\n"); printf(" -k, --keyfile - the name of the file with RSA key (default: client.rsa)\n"); + printf(" -s, --storefile - the name of the file with stored public keys\n"); + printf(" (default: known_hosts)\n"); printf(" -D, --dateformat - format of the date printed in logs (see 'man strftime'\n"); printf(" for details) (default: %%d.%%m.%%Y %%H:%%M:%%S)\n"); printf(" -K, --keep-alive N - send keepalive packets every N seconds\n"); -- cgit v1.1