From: Stephen Early Date: Fri, 19 Sep 2003 14:40:00 +0000 (+0100) Subject: Import release 0.1.16 X-Git-Tag: v0.1.16 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=commitdiff_plain;h=3b83c93292fbf6c4e859ce513bdf54ad90733f96 Import release 0.1.16 --- diff --git a/CREDITS b/CREDITS index 3b078bd..c934ab7 100644 --- a/CREDITS +++ b/CREDITS @@ -1,6 +1,7 @@ Stephen Early - original author Ross Anderson, Eli Biham, Lars Knudsen - serpent Colin Plumb, Ian Jackson - MD5 implementation +Ian Jackson - hacky parallelism, serpent keying and endianness bugfixes Steve Reid , James H. Brown - SHA1 implementation Cendio Systems AB - ipaddr.py @@ -8,4 +9,5 @@ Mark Martinec - portable snprintf Simon Tatham, Jonathan Amery, Ian Jackson - testing and debugging Simon Tatham - RSA signatures using Chinese Remainder Theorem -Richard Kettlewell - assorted bugfixes +Simon Tatham - endianness cleanups in transform.c +Richard Kettlewell, Peter Benie - assorted bugfixes diff --git a/INSTALL b/INSTALL index 49476d8..08eac9a 100644 --- a/INSTALL +++ b/INSTALL @@ -4,11 +4,6 @@ USE AT YOUR OWN RISK. THIS IS ALPHA TEST SOFTWARE. I DO NOT GUARANTEE THAT THERE WILL BE PROTOCOL COMPATIBILITY BETWEEN DIFFERENT VERSIONS. -PROTOCOL COMPATIBILITY WAS BROKEN BETWEEN secnet-0.06, secnet-0.07 AND -secnet-0.08 FOR ENDIANNESS FIXES. - -THERE WILL BE ANOTHER CHANGE IN PROTOCOL IN THE secnet-0.1.x SERIES - * Preparation ** System software support @@ -32,12 +27,6 @@ linux/Documentation/networking/tuntap.txt If you're using TUN/TAP on a platform other than Linux-2.4, see http://vtun.sourceforge.net/tun/ -Note than TUN comes in several flavours. Their names in the -configuration file are: - tun: Linux-2.4; only one device file (usually /dev/net/tun) - tun-old: Linux-2.2, BSD; device files /dev/tun* - tun-solaris: Solaris (not yet implemented) - ** System and network configuration If you intend to start secnet as root, I suggest you create a userid @@ -118,7 +107,7 @@ If installing for the first time, do # cp example.conf /etc/secnet/secnet.conf # cd /etc/secnet -# ssh-keygen -f key -N "" +# ssh-keygen -f key -t rsa1 -N "" [On BSD use $ LDFLAGS="-L/usr/local/lib" ./configure diff --git a/Makefile.in b/Makefile.in index af33bf5..60e63cd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -18,7 +18,7 @@ .PHONY: all clean realclean distclean dist install PACKAGE:=secnet -VERSION:=0.1.15 +VERSION:=0.1.16 @SET_MAKE@ @@ -31,6 +31,9 @@ CC:=@CC@ INSTALL:=@INSTALL@ INSTALL_PROGRAM:=@INSTALL_PROGRAM@ +# Use -DHACKY_PARALLEL if you are compiling secnet for an extremely +# slow machine +#CFLAGS:=-Wall @WRITESTRINGS@ @CFLAGS@ -DHACKY_PARALLEL CFLAGS:=-Wall @WRITESTRINGS@ @CFLAGS@ ALL_CFLAGS:=@DEFS@ -I$(srcdir) -I. $(CFLAGS) LDFLAGS:=@LDFLAGS@ @@ -47,15 +50,17 @@ TARGETS:=secnet OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \ resolver.o random.o udp.o site.o transform.o netlink.o rsa.o dh.o \ serpent.o md5.o version.o tun.o slip.o sha1.o ipaddr.o log.o \ - process.o @LIBOBJS@ + process.o @LIBOBJS@ \ + hackypar.o +DISTDIRS:=debian DISTFILES:=BUGS COPYING CREDITS INSTALL LICENSE.txt Makefile.in \ NEWS NOTES README TODO \ - alloca.c \ + ac_prog_cc_no_writeable_strings.m4 \ conffile.c conffile.fl conffile.h conffile.y \ conffile_internal.h config.h.bot \ config.h.in config.h.top configure \ - configure.in debian depend.sh dh.c \ + configure.in depend.sh dh.c \ example.conf \ getopt.c getopt1.c getopt.h \ install-sh ipaddr.c ipaddr.h ipaddr.py linux log.c \ @@ -66,7 +71,10 @@ DISTFILES:=BUGS COPYING CREDITS INSTALL LICENSE.txt Makefile.in \ secnet.c secnet.h serpent.c serpent.h serpentsboxes.h \ snprintf.c snprintf.h \ sha1.c site.c slip.c stamp-h.in transform.c tun.c udp.c \ - unaligned.h util.c util.h + unaligned.h util.c util.h \ + hackypar.c hackypar.h +DISTSUBDIRS:=debian/conffiles debian/copyright debian/changelog \ + debian/control debian/init debian/rules %.c: %.y @@ -144,7 +152,9 @@ pfname:=$(PACKAGE)-$(VERSION) dist: $(RM) -rf $(pfname) mkdir $(pfname) + for i in $(DISTDIRS) ; do mkdir $(pfname)/$$i ; done for i in $(DISTFILES) ; do ln -s ../$(srcdir)/$$i $(pfname)/ ; done + for i in $(DISTSUBDIRS) ; do ln -s ../../$(srcdir)/$$i $(pfname)/$$i ; done tar hcf ../$(pfname).tar --exclude=CVS --exclude=.cvsignore $(pfname) gzip -9f ../$(pfname).tar $(RM) -rf $(pfname) diff --git a/NEWS b/NEWS index 4af3024..4b50c59 100644 --- a/NEWS +++ b/NEWS @@ -1,22 +1,53 @@ * Planned for the future -Netlink device that implements an Ethernet bridge. - -Modular transform code: choice of block ciphers, modes, sequence -numbers / timestamps, etc. similar to IWJ's udptunnel - -Path-MTU discovery for each tunnel, and fragmentation/DF support in -netlink code. - -Separation of device drivers from IP router code - driver produces a -stream of packets (which has a tag indicating type and parameters). -Router module can be connected to stream to multiplex it between -different tunnels. - -Support for dynamic creation of streams/tunnels to cope with laptops, -etc. - -See also file "TODO". +Please note that the 0.1 series of secnet releases is now 'maintenance +only'; further development continues in secnet-0.2. + +* Planned for version 0.1.17 + +Debconf support - if you are using the Debian packaged version and +your secnet configuration is autogenerated using debconf then the +upgrade to version 0.2.0 should just involve installing the package; +an appropriate 0.2-style configuration file will be generated +automatically. + +* New in version 0.1.16 + +XXX XXX PROTOCOL COMPATIBILITY IS BROKEN BETWEEN VERSION 0.1.16 AND +XXX XXX ALL PREVIOUS VERSIONS. + +Bugfix: rsa.c private-key now works properly when you choose not to +verify it. + +Bugfix: serpent key setup was only using the first 8 bytes of the key +material. (Oops!) Ian Jackson contributed a fix so the full 32 bytes +are used, in big-endian mode. + +Debatable-bugfix: RSA operations now use PKCS1 v1.5-style padding + +"Hacky parallelism" contributed by Ian Jackson; this permits +public-key operations to be performed in a subprocess during key +exchange, to make secnet more usable on very slow machines. This is +not compiled in by default; if you find you need it (because key +exchanges are taking more than a second or two) then add +-DHACKY_PARALLEL to FLAGS in the Makefile.in and recompile. + +udp module updates from Peter Benie: + 1) Handle the case where authbind-helper terminates with a signal + 2) Cope with signals being delivered during waitpid + 3) Add 'address' (optional) to the udp settings. This is an IP address + that the socket will be bound to. + 4) Change the endianess of the arguments to authbind-helper. + sprintf("%04X") already translates from machine repesentation to most + significant octet first so htons reversed it again. + +All uses of alloca() expunged by Peter Benie. + +make-secnet-sites now supports configurations where each tunnel gets +its own interface on the host, and the IP router code in secnet is +disabled. make-secnet-sites has been rewritten for clarity. For +information on how to configure secnet for one-interface-per-tunnel, +see the example.conf file. * New in version 0.1.15 diff --git a/README b/README index 46df881..564b216 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ secnet - flexible VPN software * Copying -secnet is Copyright (C) 1995--2001 Stephen Early +secnet is Copyright (C) 1995--2003 Stephen Early It is distributed under the terms of the GNU General Public License, version 2 or later. See the file COPYING for more information. @@ -139,6 +139,9 @@ some keys in the root dictionary. The main ones are: makelist: turns a dictionary (arg1) into a list of definitions (ignoring the keys) readfile: reads a file (arg1) and returns it as a string + map: applies the closure specified as arg1 to each of the + remaining elements in the list in turn. Returns a list + made up of the outputs of the closure. Keys defined by modules are described below, in the module documentation. @@ -196,6 +199,7 @@ Defines: udp (closure => comm closure) udp: dict argument + address (string): IP address to listen and send on port (integer): UDP port to listen and send on buffer (buffer closure): buffer for incoming packets authbind (string): optional, path to authbind-helper program diff --git a/TODO b/TODO index 8e8800d..2d5e447 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,9 @@ dh.c: change format to binary from decimal string (without introducing endianness problems) -log.c: implement a file-descriptor-to-log module - netlink.c: test the 'allow_route' option properly. Add fragmentation code. Check that we comply with RFC1812. -process.c: capture output from children in sys_cmd() and log it - random.c: test properly resolver.c: ought to return a list of addresses for each address; the @@ -16,9 +12,6 @@ site code ought to remember them and try contacting them in turn. rsa.c: check padding type, change format to binary from decimal string (without introducing endianness problems) -secnet.c: optionally pipe stderr to a log when we become a daemon. -Don't just close it. - site.c: Abandon key exchanges when a bad packet is received. Modify protocol to include version fields, as described in the NOTES file. Implement keepalive mode. Make policy about when to initiate key diff --git a/ac_prog_cc_no_writeable_strings.m4 b/ac_prog_cc_no_writeable_strings.m4 new file mode 100644 index 0000000..273085c --- /dev/null +++ b/ac_prog_cc_no_writeable_strings.m4 @@ -0,0 +1,113 @@ +dnl @synopsis AC_PROG_CC_NO_WRITEABLE_STRINGS(substvar [,hard]) +dnl +dnl Try to find a compiler option that warns when a stringliteral is +dnl used in a place that could potentially modify the address. This +dnl should warn on giving an stringliteral to a function that asks of +dnl a non-const-modified char-pointer. +dnl +dnl The sanity check is done by looking at string.h which has a set +dnl of strcpy definitions that should be defined with const-modifiers +dnl to not emit a warning in all so many places. +dnl +dnl Currently this macro knows about GCC. +dnl hopefully will evolve to use: Solaris C compiler, +dnl Digital Unix C compiler, C for AIX Compiler, HP-UX C compiler, +dnl and IRIX C compiler. +dnl +dnl @version $Id: ac_prog_cc_no_writeable_strings.m4,v 1.1 2002/02/20 16:18:18 steve Exp $ +dnl @author Guido Draheim +dnl +AC_DEFUN([AC_PROG_CC_NO_WRITEABLE_STRINGS], [ + pushdef([CV],ac_cv_prog_cc_no_writeable_strings)dnl + hard=$2 + if test -z "$hard"; then + msg="C to warn about writing to stringliterals" + else + msg="C to prohibit any write to stringliterals" + fi + AC_CACHE_CHECK($msg, CV, [ + cat > conftest.c < +int main (void) +{ + char test[[16]]; + if (strcpy (test, "test")) return 0; + return 1; +} +EOF + dnl GCC + if test "$GCC" = "yes"; + then + if test -z "$hard"; then + CV="-Wwrite-strings" + else + CV="-fno-writable-strings -Wwrite-strings" + fi + + if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then + CV="suppressed: string.h" + fi + + dnl Solaris C compiler + elif $CC -flags 2>&1 | grep "Xc.*strict ANSI C" > /dev/null 2>&1 && + $CC -c -xstrconst conftest.c > /dev/null 2>&1 && + test -f conftest.o + then + # strings go into readonly segment + CV="-xstrconst" + + rm conftest.o + if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then + CV="suppressed: string.h" + fi + + dnl HP-UX C compiler + elif $CC > /dev/null 2>&1 && + $CC -c +ESlit conftest.c > /dev/null 2>&1 && + test -f conftest.o + then + # strings go into readonly segment + CV="+ESlit" + + rm conftest.o + if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then + CV="suppressed: string.h" + fi + + dnl Digital Unix C compiler + elif ! $CC > /dev/null 2>&1 && + $CC -c -readonly_strings conftest.c > /dev/null 2>&1 && + test -f conftest.o + then + # strings go into readonly segment + CV="-readonly_strings" + + rm conftest.o + if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then + CV="suppressed: string.h" + fi + + dnl C for AIX Compiler + + dnl IRIX C compiler + # -use_readonly_const is the default for IRIX C, + # puts them into .rodata, but they are copied later. + # need to be "-G0 -rdatashared" for strictmode but + # I am not sure what effect that has really. + + fi + rm -f conftest.* + ]) + if test -z "[$]$1" ; then + if test -n "$CV" ; then + case "$CV" in + suppressed*) $1="" ;; # known but suppressed + *) $1="$CV" ;; + esac + fi + fi + AC_SUBST($1) + popdef([CV])dnl +]) + + diff --git a/alloca.c b/alloca.c deleted file mode 100644 index bc34529..0000000 --- a/alloca.c +++ /dev/null @@ -1,460 +0,0 @@ -/* alloca.c -- allocate automatically reclaimed memory - (Mostly) portable public-domain implementation -- D A Gwyn - - This implementation of the PWB library alloca function, - which is used to allocate space off the run-time stack so - that it is automatically reclaimed upon procedure exit, - was inspired by discussions with J. Q. Johnson of Cornell. - J.Otto Tennant contributed the Cray support. - - There are some preprocessor constants that can - be defined when compiling for your specific system, for - improved efficiency; however, the defaults should be okay. - - The general concept of this implementation is to keep - track of all alloca-allocated blocks, and reclaim any - that are found to be deeper in the stack than the current - invocation. This heuristic does not reclaim storage as - soon as it becomes invalid, but it will do so eventually. - - As a special case, alloca(0) reclaims storage without - allocating any. It is a good idea to use alloca(0) in - your main control loop, etc. to force garbage collection. */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/* If compiling with GCC, this file's not needed. */ -#ifndef alloca - -#ifdef emacs -#ifdef static -/* actually, only want this if static is defined as "" - -- this is for usg, in which emacs must undefine static - in order to make unexec workable - */ -#ifndef STACK_DIRECTION -you -lose --- must know STACK_DIRECTION at compile-time -#endif /* STACK_DIRECTION undefined */ -#endif /* static */ -#endif /* emacs */ - -/* If your stack is a linked list of frames, you have to - provide an "address metric" ADDRESS_FUNCTION macro. */ - -#ifdef CRAY -long i00afunc (); -#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) -#else -#define ADDRESS_FUNCTION(arg) &(arg) -#endif - -#if __STDC__ -typedef void *pointer; -#else -typedef char *pointer; -#endif - -#define NULL 0 - -/* Define STACK_DIRECTION if you know the direction of stack - growth for your system; otherwise it will be automatically - deduced at run-time. - - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ - -#ifndef STACK_DIRECTION -#define STACK_DIRECTION 0 /* Direction unknown. */ -#endif - -#if STACK_DIRECTION != 0 - -#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ - -#else /* STACK_DIRECTION == 0; need run-time code. */ - -static int stack_dir; /* 1 or -1 once known. */ -#define STACK_DIR stack_dir - -static void -find_stack_direction () -{ - static char *addr = NULL; /* Address of first `dummy', once known. */ - auto char dummy; /* To get stack address. */ - - if (addr == NULL) - { /* Initial entry. */ - addr = ADDRESS_FUNCTION (dummy); - - find_stack_direction (); /* Recurse once. */ - } - else - { - /* Second entry. */ - if (ADDRESS_FUNCTION (dummy) > addr) - stack_dir = 1; /* Stack grew upward. */ - else - stack_dir = -1; /* Stack grew downward. */ - } -} - -#endif /* STACK_DIRECTION == 0 */ - -/* An "alloca header" is used to: - (a) chain together all alloca'ed blocks; - (b) keep track of stack depth. - - It is very important that sizeof(header) agree with malloc - alignment chunk size. The following default should work okay. */ - -#ifndef ALIGN_SIZE -#define ALIGN_SIZE sizeof(double) -#endif - -typedef union hdr -{ - char align[ALIGN_SIZE]; /* To force sizeof(header). */ - struct - { - union hdr *next; /* For chaining headers. */ - char *deep; /* For stack depth measure. */ - } h; -} header; - -static header *last_alloca_header = NULL; /* -> last alloca header. */ - -/* Return a pointer to at least SIZE bytes of storage, - which will be automatically reclaimed upon exit from - the procedure that called alloca. Originally, this space - was supposed to be taken from the current stack frame of the - caller, but that method cannot be made to work for some - implementations of C, for example under Gould's UTX/32. */ - -pointer -alloca (size) - unsigned size; -{ - auto char probe; /* Probes stack depth: */ - register char *depth = ADDRESS_FUNCTION (probe); - -#if STACK_DIRECTION == 0 - if (STACK_DIR == 0) /* Unknown growth direction. */ - find_stack_direction (); -#endif - - /* Reclaim garbage, defined as all alloca'd storage that - was allocated from deeper in the stack than currently. */ - - { - register header *hp; /* Traverses linked list. */ - - for (hp = last_alloca_header; hp != NULL;) - if ((STACK_DIR > 0 && hp->h.deep > depth) - || (STACK_DIR < 0 && hp->h.deep < depth)) - { - register header *np = hp->h.next; - - free ((pointer) hp); /* Collect garbage. */ - - hp = np; /* -> next header. */ - } - else - break; /* Rest are not deeper. */ - - last_alloca_header = hp; /* -> last valid storage. */ - } - - if (size == 0) - return NULL; /* No allocation required. */ - - /* Allocate combined header + user data storage. */ - - { - register pointer new = malloc (sizeof (header) + size); - /* Address of header. */ - - ((header *) new)->h.next = last_alloca_header; - ((header *) new)->h.deep = depth; - - last_alloca_header = (header *) new; - - /* User storage begins just after header. */ - - return (pointer) ((char *) new + sizeof (header)); - } -} - -#ifdef CRAY - -#ifdef DEBUG_I00AFUNC -#include -#endif - -#ifndef CRAY_STACK -#define CRAY_STACK -#ifndef CRAY2 -/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ -struct stack_control_header - { - long shgrow:32; /* Number of times stack has grown. */ - long shaseg:32; /* Size of increments to stack. */ - long shhwm:32; /* High water mark of stack. */ - long shsize:32; /* Current size of stack (all segments). */ - }; - -/* The stack segment linkage control information occurs at - the high-address end of a stack segment. (The stack - grows from low addresses to high addresses.) The initial - part of the stack segment linkage control information is - 0200 (octal) words. This provides for register storage - for the routine which overflows the stack. */ - -struct stack_segment_linkage - { - long ss[0200]; /* 0200 overflow words. */ - long sssize:32; /* Number of words in this segment. */ - long ssbase:32; /* Offset to stack base. */ - long:32; - long sspseg:32; /* Offset to linkage control of previous - segment of stack. */ - long:32; - long sstcpt:32; /* Pointer to task common address block. */ - long sscsnm; /* Private control structure number for - microtasking. */ - long ssusr1; /* Reserved for user. */ - long ssusr2; /* Reserved for user. */ - long sstpid; /* Process ID for pid based multi-tasking. */ - long ssgvup; /* Pointer to multitasking thread giveup. */ - long sscray[7]; /* Reserved for Cray Research. */ - long ssa0; - long ssa1; - long ssa2; - long ssa3; - long ssa4; - long ssa5; - long ssa6; - long ssa7; - long sss0; - long sss1; - long sss2; - long sss3; - long sss4; - long sss5; - long sss6; - long sss7; - }; - -#else /* CRAY2 */ -/* The following structure defines the vector of words - returned by the STKSTAT library routine. */ -struct stk_stat - { - long now; /* Current total stack size. */ - long maxc; /* Amount of contiguous space which would - be required to satisfy the maximum - stack demand to date. */ - long high_water; /* Stack high-water mark. */ - long overflows; /* Number of stack overflow ($STKOFEN) calls. */ - long hits; /* Number of internal buffer hits. */ - long extends; /* Number of block extensions. */ - long stko_mallocs; /* Block allocations by $STKOFEN. */ - long underflows; /* Number of stack underflow calls ($STKRETN). */ - long stko_free; /* Number of deallocations by $STKRETN. */ - long stkm_free; /* Number of deallocations by $STKMRET. */ - long segments; /* Current number of stack segments. */ - long maxs; /* Maximum number of stack segments so far. */ - long pad_size; /* Stack pad size. */ - long current_address; /* Current stack segment address. */ - long current_size; /* Current stack segment size. This - number is actually corrupted by STKSTAT to - include the fifteen word trailer area. */ - long initial_address; /* Address of initial segment. */ - long initial_size; /* Size of initial segment. */ - }; - -/* The following structure describes the data structure which trails - any stack segment. I think that the description in 'asdef' is - out of date. I only describe the parts that I am sure about. */ - -struct stk_trailer - { - long this_address; /* Address of this block. */ - long this_size; /* Size of this block (does not include - this trailer). */ - long unknown2; - long unknown3; - long link; /* Address of trailer block of previous - segment. */ - long unknown5; - long unknown6; - long unknown7; - long unknown8; - long unknown9; - long unknown10; - long unknown11; - long unknown12; - long unknown13; - long unknown14; - }; - -#endif /* CRAY2 */ -#endif /* not CRAY_STACK */ - -#ifdef CRAY2 -/* Determine a "stack measure" for an arbitrary ADDRESS. - I doubt that "lint" will like this much. */ - -static long -i00afunc (long *address) -{ - struct stk_stat status; - struct stk_trailer *trailer; - long *block, size; - long result = 0; - - /* We want to iterate through all of the segments. The first - step is to get the stack status structure. We could do this - more quickly and more directly, perhaps, by referencing the - $LM00 common block, but I know that this works. */ - - STKSTAT (&status); - - /* Set up the iteration. */ - - trailer = (struct stk_trailer *) (status.current_address - + status.current_size - - 15); - - /* There must be at least one stack segment. Therefore it is - a fatal error if "trailer" is null. */ - - if (trailer == 0) - abort (); - - /* Discard segments that do not contain our argument address. */ - - while (trailer != 0) - { - block = (long *) trailer->this_address; - size = trailer->this_size; - if (block == 0 || size == 0) - abort (); - trailer = (struct stk_trailer *) trailer->link; - if ((block <= address) && (address < (block + size))) - break; - } - - /* Set the result to the offset in this segment and add the sizes - of all predecessor segments. */ - - result = address - block; - - if (trailer == 0) - { - return result; - } - - do - { - if (trailer->this_size <= 0) - abort (); - result += trailer->this_size; - trailer = (struct stk_trailer *) trailer->link; - } - while (trailer != 0); - - /* We are done. Note that if you present a bogus address (one - not in any segment), you will get a different number back, formed - from subtracting the address of the first block. This is probably - not what you want. */ - - return (result); -} - -#else /* not CRAY2 */ -/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. - Determine the number of the cell within the stack, - given the address of the cell. The purpose of this - routine is to linearize, in some sense, stack addresses - for alloca. */ - -static long -i00afunc (long address) -{ - long stkl = 0; - - long size, pseg, this_segment, stack; - long result = 0; - - struct stack_segment_linkage *ssptr; - - /* Register B67 contains the address of the end of the - current stack segment. If you (as a subprogram) store - your registers on the stack and find that you are past - the contents of B67, you have overflowed the segment. - - B67 also points to the stack segment linkage control - area, which is what we are really interested in. */ - - stkl = CRAY_STACKSEG_END (); - ssptr = (struct stack_segment_linkage *) stkl; - - /* If one subtracts 'size' from the end of the segment, - one has the address of the first word of the segment. - - If this is not the first segment, 'pseg' will be - nonzero. */ - - pseg = ssptr->sspseg; - size = ssptr->sssize; - - this_segment = stkl - size; - - /* It is possible that calling this routine itself caused - a stack overflow. Discard stack segments which do not - contain the target address. */ - - while (!(this_segment <= address && address <= stkl)) - { -#ifdef DEBUG_I00AFUNC - fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); -#endif - if (pseg == 0) - break; - stkl = stkl - pseg; - ssptr = (struct stack_segment_linkage *) stkl; - size = ssptr->sssize; - pseg = ssptr->sspseg; - this_segment = stkl - size; - } - - result = address - this_segment; - - /* If you subtract pseg from the current end of the stack, - you get the address of the previous stack segment's end. - This seems a little convoluted to me, but I'll bet you save - a cycle somewhere. */ - - while (pseg != 0) - { -#ifdef DEBUG_I00AFUNC - fprintf (stderr, "%011o %011o\n", pseg, size); -#endif - stkl = stkl - pseg; - ssptr = (struct stack_segment_linkage *) stkl; - size = ssptr->sssize; - pseg = ssptr->sspseg; - result += size; - } - return (result); -} - -#endif /* not CRAY2 */ -#endif /* CRAY */ - -#endif /* no alloca */ diff --git a/conffile.c b/conffile.c index b17517b..660606e 100644 --- a/conffile.c +++ b/conffile.c @@ -466,6 +466,9 @@ static dict_t *process_config(struct p_node *c) context=root; /* Predefined keys for boolean values */ + /* "nowise" and "verily" have the advantage of being the same + length, so they line up nicely... thanks VKC and SGT (who also + point out that "mayhap" is a good "maybe" value as well) */ i=new_item(t_bool,no_loc); i->data.bool=False; false=list_append(NULL,i); @@ -478,12 +481,18 @@ static dict_t *process_config(struct p_node *c) dict_add(root,"no",false); dict_add(root,"No",false); dict_add(root,"NO",false); + dict_add(root,"nowise",false); + dict_add(root,"Nowise",false); + dict_add(root,"NOWISE",false); dict_add(root,"true",true); dict_add(root,"True",true); dict_add(root,"TRUE",true); dict_add(root,"yes",true); dict_add(root,"Yes",true); dict_add(root,"YES",true); + dict_add(root,"verily",true); + dict_add(root,"Verily",true); + dict_add(root,"VERILY",true); add_closure(root,"makelist",makelist); add_closure(root,"readfile",readfile); @@ -619,6 +628,7 @@ void add_closure(dict_t *dict, cstring_t name, apply_fn apply) closure_t *c; c=safe_malloc(sizeof(*c),"add_closure"); c->description=name; + c->type=CL_PURE; c->apply=apply; c->interface=NULL; diff --git a/config.h.in b/config.h.in index 8492df2..ec607ac 100644 --- a/config.h.in +++ b/config.h.in @@ -3,28 +3,6 @@ #define _CONFIG_H -/* Define if using alloca.c. */ -#undef C_ALLOCA - -/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. - This function is required for alloca.c support on those systems. */ -#undef CRAY_STACKSEG_END - -/* Define if you have alloca, as a function or macro. */ -#undef HAVE_ALLOCA - -/* Define if you have and it should be used (not on Ultrix). */ -#undef HAVE_ALLOCA_H - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at run-time. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown - */ -#undef STACK_DIRECTION - /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/configure b/configure index fecb0ca..c55421e 100755 --- a/configure +++ b/configure @@ -2211,240 +2211,6 @@ LIBOBJS="$LIBOBJS snprintf.o" fi done -# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works -# for constant arguments. Useless! -echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 -echo "configure:2218: checking for working alloca.h" >&5 -if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -int main() { -char *p = alloca(2 * sizeof(int)); -; return 0; } -EOF -if { (eval echo configure:2230: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - ac_cv_header_alloca_h=yes -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - ac_cv_header_alloca_h=no -fi -rm -f conftest* -fi - -echo "$ac_t""$ac_cv_header_alloca_h" 1>&6 -if test $ac_cv_header_alloca_h = yes; then - cat >> confdefs.h <<\EOF -#define HAVE_ALLOCA_H 1 -EOF - -fi - -echo $ac_n "checking for alloca""... $ac_c" 1>&6 -echo "configure:2251: checking for alloca" >&5 -if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -# define alloca _alloca -# else -# if HAVE_ALLOCA_H -# include -# else -# ifdef _AIX - #pragma alloca -# else -# ifndef alloca /* predefined by HP cc +Olibcalls */ -char *alloca (); -# endif -# endif -# endif -# endif -#endif - -int main() { -char *p = (char *) alloca(1); -; return 0; } -EOF -if { (eval echo configure:2284: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - ac_cv_func_alloca_works=yes -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - ac_cv_func_alloca_works=no -fi -rm -f conftest* -fi - -echo "$ac_t""$ac_cv_func_alloca_works" 1>&6 -if test $ac_cv_func_alloca_works = yes; then - cat >> confdefs.h <<\EOF -#define HAVE_ALLOCA 1 -EOF - -fi - -if test $ac_cv_func_alloca_works = no; then - # The SVR3 libPW and SVR4 libucb both contain incompatible functions - # that cause trouble. Some versions do not even contain alloca or - # contain a buggy version. If you still want to use their alloca, - # use ar to extract alloca.o from them instead of compiling alloca.c. - ALLOCA=alloca.${ac_objext} - cat >> confdefs.h <<\EOF -#define C_ALLOCA 1 -EOF - - -echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 -echo "configure:2316: checking whether alloca needs Cray hooks" >&5 -if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext <&5 | - egrep "webecray" >/dev/null 2>&1; then - rm -rf conftest* - ac_cv_os_cray=yes -else - rm -rf conftest* - ac_cv_os_cray=no -fi -rm -f conftest* - -fi - -echo "$ac_t""$ac_cv_os_cray" 1>&6 -if test $ac_cv_os_cray = yes; then -for ac_func in _getb67 GETB67 getb67; do - echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:2346: checking for $ac_func" >&5 -if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -/* Override any gcc2 internal prototype to avoid an error. */ -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func(); - -int main() { - -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -$ac_func(); -#endif - -; return 0; } -EOF -if { (eval echo configure:2374: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - eval "ac_cv_func_$ac_func=yes" -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_func_$ac_func=no" -fi -rm -f conftest* -fi - -if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then - echo "$ac_t""yes" 1>&6 - cat >> confdefs.h <&6 -fi - -done -fi - -echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 -echo "configure:2401: checking stack direction for C alloca" >&5 -if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test "$cross_compiling" = yes; then - ac_cv_c_stack_direction=0 -else - cat > conftest.$ac_ext < addr) ? 1 : -1; -} -main () -{ - exit (find_stack_direction() < 0); -} -EOF -if { (eval echo configure:2428: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null -then - ac_cv_c_stack_direction=1 -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -fr conftest* - ac_cv_c_stack_direction=-1 -fi -rm -fr conftest* -fi - -fi - -echo "$ac_t""$ac_cv_c_stack_direction" 1>&6 -cat >> confdefs.h < confcache <<\EOF @@ -2588,7 +2354,6 @@ s%@RM@%$RM%g s%@CPP@%$CPP%g s%@WRITESTRINGS@%$WRITESTRINGS%g s%@LIBOBJS@%$LIBOBJS%g -s%@ALLOCA@%$ALLOCA%g CEOF EOF diff --git a/configure.in b/configure.in index b57f9d4..cda2a2b 100644 --- a/configure.in +++ b/configure.in @@ -5,7 +5,7 @@ sinclude(ac_prog_cc_no_writeable_strings.m4) AC_INIT(secnet.c) AC_CONFIG_HEADER(config.h) -AC_REVISION($Id: configure.in,v 1.3 2002/02/20 16:18:18 steve Exp $) +AC_REVISION($Id: configure.in,v 1.4 2002/09/09 22:05:02 steve Exp $) AC_LANG_C @@ -42,8 +42,6 @@ AC_CHECK_LIB(adns,adns_init) dnl check for getopt in standard library AC_SUBST(LIBOBJS) AC_CHECK_FUNCS(getopt_long , , [LIBOBJS="$LIBOBJS getopt.o getopt1.o"] ) -dnl check for alloca AC_CHECK_FUNCS(snprintf , , [LIBOBJS="$LIBOBJS snprintf.o"] ) -AC_FUNC_ALLOCA() AC_OUTPUT(Makefile,echo timestamp >stamp-h) diff --git a/debian/changelog b/debian/changelog index 3da4b51..39e6dcd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -secnet (0.1.15-1) unstable; urgency=low +secnet (0.1.16-1) unstable; urgency=high * New upstream version. - -- Stephen Early Thu, 21 Feb 2002 13:40:00 +0000 + -- Stephen Early Sat, 19 Jul 2003 17:00:00 +0100 diff --git a/debian/dirs b/debian/dirs deleted file mode 100644 index 80a26ab..0000000 --- a/debian/dirs +++ /dev/null @@ -1,2 +0,0 @@ -etc/secnet -usr/sbin diff --git a/example.conf b/example.conf index 634467c..d6908ff 100644 --- a/example.conf +++ b/example.conf @@ -1,6 +1,9 @@ # secnet example configuration file # Log facility +# If you use this unaltered you should consider providing automatic log +# rotation for /var/log/secnet. secnet will close and re-open its logfiles +# when it receives SIGHUP. log logfile { filename "/var/log/secnet"; class "info","notice","warning","error","security","fatal"; @@ -11,7 +14,7 @@ log logfile { # 'quiet' -> fatal }; -# Alternatively you could log to syslog: +# Alternatively you could log through syslog: # log syslog { # ident "secnet"; # facility "local0"; @@ -23,6 +26,8 @@ log logfile { # userid who we try to run as after setup # pidfile system { + # Note that you should not specify 'userid' here unless secnet + # is being invoked as root. userid "secnet"; pidfile "/var/run/secnet.pid"; }; @@ -61,10 +66,19 @@ system { # wait-time wait between unsuccessful key setup attempts, in ms # renegotiate-time set up a new key if we see any traffic after this time +# Defaults that may be overridden on a per-site basis: setup-retries 10; setup-timeout 2000; -# Use the universal TUN/TAP driver to get packets to and from the kernel +# Use the universal TUN/TAP driver to get packets to and from the kernel, +# through a single interface. secnet will act as a router; it requires +# its own IP address which is specified below (you'll see it on traceroute, +# etc. for routes that go via tunnels). If you don't want secnet to act +# as a router, and instead want a separate kernel network interface per +# tunnel, then see the alternative configuration below + +# If you want to use userv-ipif to manage interfaces then replace the +# word "tun" with "userv-ipif". netlink tun { name "netlink-tun"; # Printed in log messages from this netlink # interface "tun0"; # You may set your own interface name if you wish; @@ -90,19 +104,17 @@ netlink tun { buffer sysbuffer(2048); }; -# Alternatively (or additionally, if you like) use userv-ipif to get -# packets to and from the kernel. -#netlink userv-ipif { -# name "netlink-userv-ipif"; -# # userv-path "/usr/bin/userv"; -# # service-user "root"; -# # service-name "ipif"; -# networks "whatever"; -# local-address "whatever"; -# secnet-address "whatever"; -# mtu 1400; -# buffer sysbuffer(2048); -#}; +# This alternative configuration allows you to create one kernel network +# interface per tunnel. IT WILL ONLY WORK WITH "tun" - IT WILL NOT +# WORK WITH "userv-ipif". This is because "tun" can share a single +# buffer between multiple network interfaces, but userv-ipif can't. +# To use userv-ipif in this style, process the sites.conf file so that +# each "netlink" section contains a "buffer sysbuffer(2048);" line. +#netlink tun; +#local-address "192.168.x.x"; # Address of local interfaces - all the same +#mtu 1400; +#buffer sysbuffer(2048); + # This defines the port that this instance of secnet will listen on, and # originate packets on. It does not _have_ to correspond to the advertised @@ -142,22 +154,25 @@ transform serpent256-cbc { include /etc/secnet/sites.conf -# Here you must list all the VPN sites that you wish to communicate with. # The /etc/secnet/sites file contains information on all reachable sites; # if the site you want to communicate with isn't listed, you should get # a newer version. MAKE SURE YOU GET AN AUTHENTIC COPY OF THE FILE - it # contains public keys for all sites. -sites map(site, - vpn-data/example/location1/site1, - vpn-data/example/location2/site1, - vpn-data/example/location2/site2); - # If you want to communicate with all the VPN sites, you can use something -# like the following instead: +# like the following: -# sites map(site,vpn/example/all-sites); +sites map(site,vpn/example/all-sites); + +# If you only want to communicate with a subset of the VPN sites, list +# them explicitly: + +# sites map(site, +# vpn-data/example/location1/site1, +# vpn-data/example/location2/site1, +# vpn-data/example/location2/site2); # If you want to communicate with a subset of locations, try the following: # sites map(site,vpn/example/location1,vpn/example/location2); + diff --git a/getopt.c b/getopt.c index a59a013..27128c5 100644 --- a/getopt.c +++ b/getopt.c @@ -20,28 +20,10 @@ along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* NOTE!!! AIX requires this to be the first thing in the file. - Do not put ANYTHING before it! */ -#if !defined (__GNUC__) && defined (_AIX) - #pragma alloca -#endif - #ifdef HAVE_CONFIG_H #include "config.h" #endif -#ifdef __GNUC__ -#define alloca __builtin_alloca -#else /* not __GNUC__ */ -#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) -#include -#else -#ifndef _AIX -char *alloca (); -#endif -#endif /* alloca.h */ -#endif /* not __GNUC__ */ - #if !__STDC__ && !defined(const) && IN_GCC #define const #endif @@ -67,12 +49,9 @@ char *alloca (); /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ -#undef alloca /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include -#else /* Not GNU C library. */ -#define __alloca alloca #endif /* GNU C library. */ /* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a @@ -237,7 +216,7 @@ exchange (argv) char **argv; { int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); - char **temp = (char **) __alloca (nonopts_size); + char **temp = (char **) safe_malloc (nonopts_size, "getopt"); /* Interchange the two blocks of data in ARGV. */ @@ -252,6 +231,7 @@ exchange (argv) first_nonopt += (optind - last_nonopt); last_nonopt = optind; + free(temp); } /* Scan elements of ARGV (whose length is ARGC) for option characters diff --git a/hackypar.c b/hackypar.c new file mode 100644 index 0000000..ebab051 --- /dev/null +++ b/hackypar.c @@ -0,0 +1,121 @@ +/* Hacky parallelism; Ian Jackson */ + +#define _GNU_SOURCE + +#include "secnet.h" +#include "util.h" +#include "hackypar.h" + +#ifdef HACKY_PARALLEL + +#include +#include +#include +#include +#include +#include +#include + +#define HASHSIZE 16 +#define CACHESIZE 16 + +typedef enum { hp_idle, hp_compute, hp_deferring, hp_fail } HPState; + +static HPState state; +static pid_t child; + +static void checkchild(void) { + int r, status; + + if (!child) return; + + r= waitpid(child,&status,WNOHANG); if (!r) return; + if (r==-1) { + Message(M_ERR,"hacky_par: waitpid: %s\n",strerror(errno)); + return; + } + child= 0; + + if (WIFSIGNALED(status)) { + Message(M_ERR,"hacky_par: signaled! %s\n",strsignal(WTERMSIG(status))); + } else if (!WIFEXITED(status)) { + Message(M_ERR,"hacky_par: unexpected status! %d\n", r); + } +} + +static HPState start(void) { + assert(!child); + + child= fork(); + if (child == -1) { + Message(M_ERR,"hacky_par: fork failed: %s\n",strerror(errno)); + return hp_fail; + } + + if (!child) { /* we are the child */ + return hp_compute; + } + + Message(M_INFO,"hacky_par: started, punting\n"); + return hp_deferring; +} + +int hacky_par_start_failnow(void) { + state= hp_idle; + checkchild(); + if (child) { + state= hp_deferring; + Message(M_INFO,"hacky_par: busy, punting\n"); + return 1; + } + return 0; +} + +int hacky_par_mid_failnow(void) { + state= start(); + return state != hp_compute; +} + +bool_t (*packy_par_gen)(struct site *st); + +void hacky_par_end(int *ok, + uint32_t retries, uint32_t timeout, + bool_t (*send_msg)(struct site *st), struct site *st) { + int i; + + switch (state) { + case hp_deferring: + assert(!*ok); + *ok= 1; + return; + case hp_fail: + assert(!*ok); + return; + case hp_idle: + return; + case hp_compute: + if (!ok) { + Message(M_ERR,"hacky_par: compute failed\n"); + _exit(2); + } + Message(M_INFO,"hacky_par: got result, sending\n"); + for (i=1; i +#include #include "ipaddr.h" #define DEFAULT_ALLOC 2 diff --git a/make-secnet-sites b/make-secnet-sites index 682840f..8024c16 100755 --- a/make-secnet-sites +++ b/make-secnet-sites @@ -1,5 +1,5 @@ #! /usr/bin/env python -# Copyright (C) 2001 Stephen Early +# Copyright (C) 2001-2002 Stephen Early # # 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 @@ -53,217 +53,319 @@ import string import time import sys import os +import getopt +# The ipaddr library is installed as part of secnet sys.path.append("/usr/local/share/secnet") sys.path.append("/usr/share/secnet") import ipaddr -VERSION="0.1.13" - -class vpn: - def __init__(self,name): - self.name=name - self.allow_defs=0 - self.locations={} - self.defs={} - -class location: - def __init__(self,name,vpn): - self.group=None - self.name=name - self.allow_defs=1 - self.vpn=vpn - self.sites={} - self.defs={} - -class site: - def __init__(self,name,location): - self.name=name - self.allow_defs=1 - self.location=location - self.defs={} - -class nets: +VERSION="0.1.16" + +# Classes describing possible datatypes in the configuration file + +class single_ipaddr: + "An IP address" + def __init__(self,w): + self.addr=ipaddr.ipaddr(w[1]) + def __str__(self): + return '"%s"'%self.addr.ip_str() + +class networks: + "A set of IP addresses specified as a list of networks" def __init__(self,w): - self.w=w self.set=ipaddr.ip_set() for i in w[1:]: x=string.split(i,"/") self.set.append(ipaddr.network(x[0],x[1], ipaddr.DEMAND_NETWORK)) - def subsetof(self,s): - # I'd like to do this: - # return self.set.is_subset(s) - # but there isn't an is_subset() method - # Instead we see if we intersect with the complement of s - sc=s.set.complement() - i=sc.intersection(self.set) - return i.is_empty() - def out(self): - if (self.w[0]=='restrict-nets'): pattern="# restrict-nets %s;" - else: - pattern="link netlink { routes %s; };" - return pattern%string.join(map(lambda x:'"%s/%s"'%(x.ip_str(), - x.mask.netmask_bits_str), - self.set.as_list_of_networks()),",") + def __str__(self): + return string.join(map(lambda x:'"%s/%s"'%(x.ip_str(), + x.mask.netmask_bits_str), + self.set.as_list_of_networks()),",") class dhgroup: + "A Diffie-Hellman group" def __init__(self,w): self.mod=w[1] self.gen=w[2] - def out(self): - return 'dh diffie-hellman("%s","%s");'%(self.mod,self.gen) + def __str__(self): + return 'diffie-hellman("%s","%s")'%(self.mod,self.gen) class hash: + "A choice of hash function" def __init__(self,w): self.ht=w[1] if (self.ht!='md5' and self.ht!='sha1'): complain("unknown hash type %s"%(self.ht)) - def out(self): - return 'hash %s;'%(self.ht) + def __str__(self): + return '%s'%(self.ht) class email: + "An email address" def __init__(self,w): self.addr=w[1] - def out(self): - return '# Contact email address: <%s>'%(self.addr) + def __str__(self): + return '<%s>'%(self.addr) class num: + "A decimal number" def __init__(self,w): - self.what=w[0] self.n=string.atol(w[1]) - def out(self): - return '%s %d;'%(self.what,self.n) + def __str__(self): + return '%d'%(self.n) class address: + "A DNS name and UDP port number" def __init__(self,w): - self.w=w self.adr=w[1] self.port=string.atoi(w[2]) if (self.port<1 or self.port>65535): complain("invalid port number") - def out(self): - return 'address "%s"; port %d;'%(self.adr,self.port) + def __str__(self): + return '"%s"; port %d'%(self.adr,self.port) class rsakey: + "An RSA public key" def __init__(self,w): self.l=string.atoi(w[1]) self.e=w[2] self.n=w[3] - def out(self): - return 'key rsa-public("%s","%s");'%(self.e,self.n) - -class mobileoption: + def __str__(self): + return 'rsa-public("%s","%s")'%(self.e,self.n) + +# Possible properties of configuration nodes +keywords={ + 'contact':(email,"Contact address"), + 'dh':(dhgroup,"Diffie-Hellman group"), + 'hash':(hash,"Hash function"), + 'key-lifetime':(num,"Maximum key lifetime (ms)"), + 'setup-timeout':(num,"Key setup timeout (ms)"), + 'setup-retries':(num,"Maximum key setup packet retries"), + 'wait-time':(num,"Time to wait after unsuccessful key setup (ms)"), + 'renegotiate-time':(num,"Time after key setup to begin renegotiation (ms)"), + 'restrict-nets':(networks,"Allowable networks"), + 'networks':(networks,"Claimed networks"), + 'pubkey':(rsakey,"RSA public site key"), + 'peer':(single_ipaddr,"Tunnel peer IP address"), + 'address':(address,"External contact address and port") +} + +def sp(name,value): + "Simply output a property - the default case" + return "%s %s;\n"%(name,value) + +# All levels support these properties +global_properties={ + 'contact':(lambda name,value:"# Contact email address: %s\n"%(value)), + 'dh':sp, + 'hash':sp, + 'key-lifetime':sp, + 'setup-timeout':sp, + 'setup-retries':sp, + 'wait-time':sp, + 'renegotiate-time':sp, + 'restrict-nets':(lambda name,value:"# restrict-nets %s\n"%value) +} + +class level: + "A level in the configuration hierarchy" + depth=0 + leaf=0 + allow_properties={} + require_properties={} + def __init__(self,w): + self.name=w[1] + self.properties={} + self.children={} + def indent(self,w,t): + w.write(" "[:t]) + def prop_out(self,n): + return self.allow_properties[n](n,str(self.properties[n])) + def output_props(self,w,ind): + for i in self.properties.keys(): + if self.allow_properties[i]: + self.indent(w,ind) + w.write("%s"%self.prop_out(i)) + def output_data(self,w,ind,np): + self.indent(w,ind) + w.write("%s {\n"%(self.name)) + self.output_props(w,ind+2) + if self.depth==1: w.write("\n"); + for c in self.children.values(): + c.output_data(w,ind+2,np+self.name+"/") + self.indent(w,ind) + w.write("};\n") + +class vpnlevel(level): + "VPN level in the configuration hierarchy" + depth=1 + leaf=0 + type="vpn" + allow_properties=global_properties.copy() + require_properties={ + 'contact':"VPN admin contact address" + } + def __init__(self,w): + level.__init__(self,w) + def output_vpnflat(self,w,ind,h): + "Output flattened list of site names for this VPN" + self.indent(w,ind) + w.write("%s {\n"%(self.name)) + for i in self.children.keys(): + self.children[i].output_vpnflat(w,ind+2, + h+"/"+self.name+"/"+i) + w.write("\n") + self.indent(w,ind+2) + w.write("all-sites %s;\n"% + string.join(self.children.keys(),',')) + self.indent(w,ind) + w.write("};\n") + +class locationlevel(level): + "Location level in the configuration hierarchy" + depth=2 + leaf=0 + type="location" + allow_properties=global_properties.copy() + require_properties={ + 'contact':"Location admin contact address", + } + def __init__(self,w): + level.__init__(self,w) + self.group=w[2] + def output_vpnflat(self,w,ind,h): + self.indent(w,ind) + # The "h=h,self=self" abomination below exists because + # Python didn't support nested_scopes until version 2.1 + w.write("%s %s;\n"%(self.name,string.join( + map(lambda x,h=h,self=self: + h+"/"+x,self.children.keys()),','))) + +class sitelevel(level): + "Site level (i.e. a leafnode) in the configuration hierarchy" + depth=3 + leaf=1 + type="site" + allow_properties=global_properties.copy() + allow_properties.update({ + 'address':sp, + 'networks':None, + 'peer':None, + 'pubkey':(lambda n,v:"key %s;\n"%v) + }) + require_properties={ + 'dh':"Diffie-Hellman group", + 'contact':"Site admin contact address", + 'address':"Site external access address", + 'networks':"Networks claimed by the site", + 'hash':"hash function", + 'peer':"Gateway address of the site", + 'pubkey':"RSA public key of the site" + } def __init__(self,w): - self.w=w - def out(self): - return '# netlink-options "soft";' + level.__init__(self,w) + def output_data(self,w,ind,np): + self.indent(w,ind) + w.write("%s {\n"%(self.name)) + self.indent(w,ind+2) + w.write("name \"%s\";\n"%(np+self.name)) + self.output_props(w,ind+2) + self.indent(w,ind+2) + w.write("link netlink {\n"); + self.indent(w,ind+4) + w.write("routes %s;\n"%str(self.properties["networks"])) + self.indent(w,ind+4) + w.write("ptp-address %s;\n"%str(self.properties["peer"])) + self.indent(w,ind+2) + w.write("};\n") + self.indent(w,ind) + w.write("};\n") + +# Levels in the configuration file +# (depth,properties) +levels={'vpn':vpnlevel, 'location':locationlevel, 'site':sitelevel} + +# Reserved vpn/location/site names +reserved={'all-sites':None} +reserved.update(keywords) +reserved.update(levels) def complain(msg): + "Complain about a particular input line" global complaints print ("%s line %d: "%(file,line))+msg complaints=complaints+1 def moan(msg): + "Complain about something in general" global complaints print msg; complaints=complaints+1 -# We don't allow redefinition of properties (because that would allow things -# like restrict-nets to be redefined, which would be bad) -def set(obj,defs,w): - if (obj.allow_defs | allow_defs): - if (obj.defs.has_key(w[0])): - complain("%s is already defined"%(w[0])) - else: - t=defs[w[0]] - obj.defs[w[0]]=t(w) +root=level(['root','root']) # All vpns are children of this node +obstack=[root] +allow_defs=0 # Level above which new definitions are permitted + +def set_property(obj,w): + "Set a property on a configuration node" + if obj.properties.has_key(w[0]): + complain("%s %s already has property %s defined"% + (obj.type,obj.name,w[0])) + else: + obj.properties[w[0]]=keywords[w[0]][0](w) -# Process a line of configuration file def pline(i): - global allow_defs, group, current_vpn, current_location, current_object + "Process a configuration file line" + global allow_defs, obstack, root w=string.split(i) if len(w)==0: return keyword=w[0] + current=obstack[len(obstack)-1] if keyword=='end-definitions': - allow_defs=0 - current_vpn=None - current_location=None - current_object=None + allow_defs=sitelevel.depth + obstack=[root] return - if keyword=='vpn': - if vpns.has_key(w[1]): - current_vpn=vpns[w[1]] - current_object=current_vpn + if levels.has_key(keyword): + # We may go up any number of levels, but only down by one + newdepth=levels[keyword].depth + currentdepth=len(obstack) # actually +1... + if newdepth<=currentdepth: + obstack=obstack[:newdepth] + if newdepth>currentdepth: + complain("May not go from level %d to level %d"% + (currentdepth-1,newdepth)) + # See if it's a new one (and whether that's permitted) + # or an existing one + current=obstack[len(obstack)-1] + if current.children.has_key(w[1]): + # Not new + current=current.children[w[1]] + if service and group and current.depth==2: + if group!=current.group: + complain("Incorrect group!") else: - if allow_defs: - current_vpn=vpn(w[1]) - vpns[w[1]]=current_vpn - current_object=current_vpn - else: - complain("no new VPN definitions allowed") + # New + # Ignore depth check for now + nl=levels[keyword](w) + if nl.depth0: if complaints==1: print "There was 1 problem." @@ -471,7 +517,7 @@ if service: f=open(groupfiledir+"/T"+group,'w') f.write("# Section submitted by user %s, %s\n"% (user,time.asctime(time.localtime(time.time())))) - f.write("# Checked by make-secnet-sites.py version %s\n\n"%VERSION) + f.write("# Checked by make-secnet-sites version %s\n\n"%VERSION) for i in userinput: f.write(i) f.write("\n") f.close() diff --git a/netlink.c b/netlink.c index 568cb2e..466e68f 100644 --- a/netlink.c +++ b/netlink.c @@ -97,6 +97,7 @@ their use. */ +#include #include "secnet.h" #include "util.h" #include "ipaddr.h" diff --git a/rsa.c b/rsa.c index 55bd24f..3c82a40 100644 --- a/rsa.c +++ b/rsa.c @@ -1,6 +1,13 @@ -/* CRT work by Simon Tatham */ +/* This file is part of secnet, and is distributed under the terms of + the GNU General Public License version 2 or later. + + Copyright (C) 1995-2002 Stephen Early + Copyright (C) 2001 Simon Tatham + Copyright (C) 2002 Ian Jackson + */ #include +#include #include #include "secnet.h" #include "util.h" @@ -40,22 +47,42 @@ static string_t rsa_sign(void *sst, uint8_t *data, uint32_t datalen) mpz_init(&a); mpz_init(&b); + /* RSA PKCS#1 v1.5 signature padding: + * + * <------------ msize hex digits ----------> + * + * 00 01 ff ff .... ff ff 00 vv vv vv .... vv + * + * <--- datalen --> + * bytes + * = datalen*2 hex digits + * + * NB that according to PKCS#1 v1.5 we're supposed to include a + * hash function OID in the data. We don't do that (because we + * don't have the hash function OID to hand here), thus violating + * the spec in a way that affects interop but not security. + * + * -iwj 17.9.2002 + */ + msize=mpz_sizeinbase(&st->n, 16); - if (datalen*2+4>=msize) { + if (datalen*2+6>=msize) { fatal("rsa_sign: message too big"); } strcpy(buff,"0001"); for (i=0; i>4]; - buff[5+i*2]=hexchars[data[i]&0xf]; + buff[msize+(-datalen+i)*2]=hexchars[(data[i]&0xf0)>>4]; + buff[msize+(-datalen+i)*2+1]=hexchars[data[i]&0xf]; } - buff[4+datalen*2]=0; - for (i=datalen*2+4; i>4]; - buff[5+i*2]=hexchars[data[i]&0xf]; + buff[msize+(-datalen+i)*2]=hexchars[(data[i]&0xf0)>>4]; + buff[msize+(-datalen+i)*2+1]=hexchars[data[i]&0xf]; } - buff[4+datalen*2]=0; - for (i=datalen*2+4; icl.description="rsapriv"; @@ -383,16 +413,15 @@ static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context, * Now verify the validity of the key, and set up the auxiliary * values for fast CRT signing. */ + valid=False; i=list_elem(args,1); + mpz_init(&tmp); + mpz_init(&tmp2); + mpz_init(&tmp3); if (i && i->type==t_bool && i->data.bool==False) { Message(M_INFO,"rsa-private (%s:%d): skipping RSA key validity " "check\n",loc.file,loc.line); } else { - int valid = 0; - mpz_init(&tmp); - mpz_init(&tmp2); - mpz_init(&tmp3); - /* Verify that p*q is equal to n. */ mpz_mul(&tmp, &st->p, &st->q); if (mpz_cmp(&tmp, &st->n) != 0) @@ -419,33 +448,35 @@ static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context, mpz_mod(&tmp2, &tmp, &st->p); if (mpz_cmp_si(&tmp2, 1) != 0) goto done_checks; - - /* Now we know the key is valid. */ - valid = 1; - - /* - * Now we compute auxiliary values dp, dq and w to allow us - * to use the CRT optimisation when signing. - * - * dp == d mod (p-1) so that a^dp == a^d mod p, for all a - * dq == d mod (q-1) similarly mod q - * w == iqmp * q so that w == 0 mod q, and w == 1 mod p - */ - mpz_sub_ui(&tmp, &st->p, 1); - mpz_mod(&st->dp, &d, &tmp); - mpz_sub_ui(&tmp, &st->q, 1); - mpz_mod(&st->dq, &d, &tmp); - mpz_mul(&st->w, &iqmp, &st->q); - - done_checks: - if (!valid) { - cfgfatal(loc,"rsa-private","file \"%s\" does not contain a " - "valid RSA key!\n",filename); - } - mpz_clear(&tmp); - mpz_clear(&tmp2); - mpz_clear(&tmp3); } + /* Now we know the key is valid (or we don't care). */ + valid = True; + + /* + * Now we compute auxiliary values dp, dq and w to allow us + * to use the CRT optimisation when signing. + * + * dp == d mod (p-1) so that a^dp == a^d mod p, for all a + * dq == d mod (q-1) similarly mod q + * w == iqmp * q so that w == 0 mod q, and w == 1 mod p + */ + mpz_init(&st->dp); + mpz_init(&st->dq); + mpz_init(&st->w); + mpz_sub_ui(&tmp, &st->p, 1); + mpz_mod(&st->dp, &d, &tmp); + mpz_sub_ui(&tmp, &st->q, 1); + mpz_mod(&st->dq, &d, &tmp); + mpz_mul(&st->w, &iqmp, &st->q); + +done_checks: + if (!valid) { + cfgfatal(loc,"rsa-private","file \"%s\" does not contain a " + "valid RSA key!\n",filename); + } + mpz_clear(&tmp); + mpz_clear(&tmp2); + mpz_clear(&tmp3); free(c); mpz_clear(&e); diff --git a/secnet.c b/secnet.c index bef236d..efe52b3 100644 --- a/secnet.c +++ b/secnet.c @@ -263,10 +263,7 @@ static void run(void) int timeout; struct pollfd *fds; - fds=alloca(sizeof(*fds)*total_nfds); - if (!fds) { - fatal("run: couldn't alloca"); - } + fds=safe_malloc(sizeof(*fds)*total_nfds, "run"); Message(M_NOTICE,"%s [%d]: starting\n",version,secnet_pid); @@ -309,6 +306,7 @@ static void run(void) } } while (rv<0); } while (!finished); + free(fds); } static void droppriv(void) diff --git a/secnet.h b/secnet.h index 4b211f5..7327350 100644 --- a/secnet.h +++ b/secnet.h @@ -12,6 +12,21 @@ #include #include +/* + * Macros added by SGT for endianness-independence + */ +#define GET_32BIT_MSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0] << 24) | \ + ((unsigned long)(unsigned char)(cp)[1] << 16) | \ + ((unsigned long)(unsigned char)(cp)[2] << 8) | \ + ((unsigned long)(unsigned char)(cp)[3])) + +#define PUT_32BIT_MSB_FIRST(cp, value) ( \ + (cp)[0] = (char)((value) >> 24), \ + (cp)[1] = (char)((value) >> 16), \ + (cp)[2] = (char)((value) >> 8), \ + (cp)[3] = (char)(value) ) + typedef char *string_t; typedef const char *cstring_t; typedef enum {False,True} bool_t; diff --git a/serpent.c b/serpent.c index 51b27ba..e41f3ce 100644 --- a/serpent.c +++ b/serpent.c @@ -32,9 +32,10 @@ void serpent_makekey(struct keyInstance *key, int keyLen, uint32_t w[132],k[132]; for(i=0; isubkeys[ 0]); @@ -195,23 +196,23 @@ void serpent_encrypt(struct keyInstance *key, keying(x0, x1, x2, x3, key->subkeys[32]); /* The ciphertext is now in x */ - ciphertext[0] = x0; - ciphertext[1] = x1; - ciphertext[2] = x2; - ciphertext[3] = x3; + PUT_32BIT_MSB_FIRST(ciphertext+12, x0); + PUT_32BIT_MSB_FIRST(ciphertext+8, x1); + PUT_32BIT_MSB_FIRST(ciphertext+4, x2); + PUT_32BIT_MSB_FIRST(ciphertext, x3); } void serpent_decrypt(struct keyInstance *key, - uint32_t ciphertext[4], - uint32_t plaintext[4]) + uint8_t ciphertext[16], + uint8_t plaintext[16]) { register uint32_t x0, x1, x2, x3; register uint32_t y0, y1, y2, y3; - x0=ciphertext[0]; - x1=ciphertext[1]; - x2=ciphertext[2]; - x3=ciphertext[3]; + x0=GET_32BIT_MSB_FIRST(ciphertext+12); + x1=GET_32BIT_MSB_FIRST(ciphertext+8); + x2=GET_32BIT_MSB_FIRST(ciphertext+4); + x3=GET_32BIT_MSB_FIRST(ciphertext); /* Start to decrypt the ciphertext x */ keying(x0, x1, x2, x3, key->subkeys[32]); @@ -313,8 +314,8 @@ void serpent_decrypt(struct keyInstance *key, keying(x0, x1, x2, x3, key->subkeys[ 0]); /* The plaintext is now in x */ - plaintext[0] = x0; - plaintext[1] = x1; - plaintext[2] = x2; - plaintext[3] = x3; + PUT_32BIT_MSB_FIRST(plaintext+12, x0); + PUT_32BIT_MSB_FIRST(plaintext+8, x1); + PUT_32BIT_MSB_FIRST(plaintext+4, x2); + PUT_32BIT_MSB_FIRST(plaintext, x3); } diff --git a/serpent.h b/serpent.h index dbbd36e..07176d7 100644 --- a/serpent.h +++ b/serpent.h @@ -10,10 +10,10 @@ struct keyInstance { void serpent_makekey(struct keyInstance *key, int keyLen, uint8_t *keyMaterial); -void serpent_encrypt(struct keyInstance *key, uint32_t plaintext[4], - uint32_t ciphertext[4]); +void serpent_encrypt(struct keyInstance *key, uint8_t plaintext[16], + uint8_t ciphertext[16]); -void serpent_decrypt(struct keyInstance *key, uint32_t ciphertext[4], - uint32_t plaintext[4]); +void serpent_decrypt(struct keyInstance *key, uint8_t ciphertext[16], + uint8_t plaintext[16]); #endif /* serpent_h */ diff --git a/site.c b/site.c index 9df0a2b..49e1b14 100644 --- a/site.c +++ b/site.c @@ -260,7 +260,7 @@ struct msg { static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what) { void *hst; - uint8_t *hash=alloca(st->hash->len); + uint8_t *hash; string_t dhpub, sig; st->retries=st->setup_retries; @@ -276,15 +276,20 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what) if (type==LABEL_MSG1) return True; memcpy(buf_append(&st->buffer,NONCELEN),st->remoteN,NONCELEN); if (type==LABEL_MSG2) return True; + + if (hacky_par_mid_failnow()) return False; + dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len); buf_append_string(&st->buffer,dhpub); free(dhpub); + hash=safe_malloc(st->hash->len, "generate_msg"); hst=st->hash->init(); st->hash->update(hst,st->buffer.start,st->buffer.size); st->hash->final(hst,hash); sig=st->privkey->sign(st->privkey->st,hash,st->hash->len); buf_append_string(&st->buffer,sig); free(sig); + free(hash); return True; } @@ -417,7 +422,7 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3, struct sockaddr_in *src) { struct msg m; - uint8_t *hash=alloca(st->hash->len); + uint8_t *hash; void *hst; cstring_t err; @@ -428,6 +433,7 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3, } /* Check signature and store g^x mod m */ + hash=safe_malloc(st->hash->len, "process_msg3"); hst=st->hash->init(); st->hash->update(hst,m.hashstart,m.hashlen); st->hash->final(hst,hash); @@ -435,8 +441,10 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3, m.sig[m.siglen]=0; if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) { slog(st,LOG_SEC,"msg3 signature failed check!"); + free(hash); return False; } + free(hash); /* Terminate their DH public key with a '0' */ m.pk[m.pklen]=0; @@ -465,7 +473,7 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4, struct sockaddr_in *src) { struct msg m; - uint8_t *hash=alloca(st->hash->len); + uint8_t *hash; void *hst; cstring_t err; @@ -476,6 +484,7 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4, } /* Check signature and store g^x mod m */ + hash=safe_malloc(st->hash->len, "process_msg4"); hst=st->hash->init(); st->hash->update(hst,m.hashstart,m.hashlen); st->hash->final(hst,hash); @@ -483,8 +492,10 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4, m.sig[m.siglen]=0; if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) { slog(st,LOG_SEC,"msg4 signature failed check!"); + free(hash); return False; } + free(hash); /* Terminate their DH public key with a '0' */ m.pk[m.pklen]=0; @@ -821,6 +832,8 @@ static bool_t enter_state_resolve(struct site *st) static bool_t enter_new_state(struct site *st, uint32_t next) { bool_t (*gen)(struct site *st); + int r; + slog(st,LOG_STATE,"entering state %s",state_name(next)); switch(next) { case SITE_SENTMSG1: @@ -858,7 +871,15 @@ static bool_t enter_new_state(struct site *st, uint32_t next) break; } - if (gen(st) && send_msg(st)) { + if (hacky_par_start_failnow()) return False; + + r= gen(st) && send_msg(st); + + hacky_par_end(&r, + st->setup_retries, st->setup_timeout, + send_msg, st); + + if (r) { st->state=next; if (next==SITE_RUN) { BUF_FREE(&st->buffer); /* Never reused */ @@ -949,9 +970,10 @@ static void site_afterpoll(void *sst, struct pollfd *fds, int nfds, st->now=*now; if (st->timeout && *now>st->timeout) { st->timeout=0; - if (st->state>=SITE_SENTMSG1 && st->state<=SITE_SENTMSG5) - send_msg(st); - else if (st->state==SITE_WAIT) { + if (st->state>=SITE_SENTMSG1 && st->state<=SITE_SENTMSG5) { + if (!hacky_par_start_failnow()) + send_msg(st); + } else if (st->state==SITE_WAIT) { enter_state_run(st); } else { slog(st,LOG_ERROR,"site_afterpoll: unexpected timeout, state=%d", diff --git a/transform.c b/transform.c index 53ff448..dc6ac3e 100644 --- a/transform.c +++ b/transform.c @@ -17,17 +17,6 @@ /* Required key length in bytes */ #define REQUIRED_KEYLEN ((512+64+32)/8) -#ifdef WORDS_BIGENDIAN -static inline uint32_t byteswap(uint32_t a) -{ - return - ((a&0x000000ff)<<24) | - ((a&0x0000ff00)<<8) | - ((a&0x00ff0000)>>8) | - ((a&0xff000000)>>24); -} -#endif - struct transform { closure_t cl; uint32_t line; @@ -71,9 +60,9 @@ static bool_t transform_setkey(void *sst, uint8_t *key, uint32_t keylen) serpent_makekey(&ti->cryptkey,256,key); serpent_makekey(&ti->mackey,256,key+32); - ti->cryptiv=ntohl(*(uint32_t *)(key+64)); - ti->maciv=ntohl(*(uint32_t *)(key+68)); - ti->sendseq=ntohl(*(uint32_t *)(key+72)); + ti->cryptiv=GET_32BIT_MSB_FIRST(key+64); + ti->maciv=GET_32BIT_MSB_FIRST(key+68); + ti->sendseq=GET_32BIT_MSB_FIRST(key+72); ti->lastrecvseq=ti->sendseq; ti->keyed=True; @@ -95,10 +84,11 @@ static uint32_t transform_forward(void *sst, struct buffer_if *buf, struct transform_inst *ti=sst; uint8_t *padp; int padlen; - uint32_t iv[4]; - uint32_t macplain[4]; - uint32_t macacc[4]; - uint32_t *n, *p; + uint8_t iv[16]; + uint8_t macplain[16]; + uint8_t macacc[16]; + uint8_t *p, *n; + int i; if (!ti->keyed) { *errmsg="transform unkeyed"; @@ -125,73 +115,35 @@ static uint32_t transform_forward(void *sst, struct buffer_if *buf, it we've have to add 16 bytes to each message, not 4, so that the message stays a multiple of 16 bytes long.) */ memset(iv,0,16); - iv[0]=ti->maciv; + PUT_32BIT_MSB_FIRST(iv, ti->maciv); serpent_encrypt(&ti->mackey,iv,macacc); /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted block encrypted once again. */ - for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + for (n=buf->start; nstart+buf->size; n+=16) { -#ifdef WORDS_BIGENDIAN - macplain[0]=macacc[0]^byteswap(n[0]); - macplain[1]=macacc[1]^byteswap(n[1]); - macplain[2]=macacc[2]^byteswap(n[2]); - macplain[3]=macacc[3]^byteswap(n[3]); -#else - macplain[0]=macacc[0]^n[0]; - macplain[1]=macacc[1]^n[1]; - macplain[2]=macacc[2]^n[2]; - macplain[3]=macacc[3]^n[3]; -#endif + for (i = 0; i < 16; i++) + macplain[i] = macacc[i] ^ n[i]; serpent_encrypt(&ti->mackey,macplain,macacc); } serpent_encrypt(&ti->mackey,macacc,macacc); -#ifdef WORDS_BIGENDIAN - macacc[0]=byteswap(macacc[0]); - macacc[1]=byteswap(macacc[1]); - macacc[2]=byteswap(macacc[2]); - macacc[3]=byteswap(macacc[3]); -#endif memcpy(buf_append(buf,16),macacc,16); /* Serpent-CBC. We expand the ID as for CBCMAC, do the encryption, and prepend the IV before increasing it. */ memset(iv,0,16); - iv[0]=ti->cryptiv; + PUT_32BIT_MSB_FIRST(iv, ti->cryptiv); serpent_encrypt(&ti->cryptkey,iv,iv); /* CBC: each block is XORed with the previous encrypted block (or the IV) before being encrypted. */ p=iv; -#ifdef WORDS_BIGENDIAN - /* This counters the byteswap() in the first half of the loop, which in - turn counters the byteswap() in the second half of the loop. Ick. */ - iv[0]=byteswap(iv[0]); - iv[1]=byteswap(iv[1]); - iv[2]=byteswap(iv[2]); - iv[3]=byteswap(iv[3]); -#endif - for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + + for (n=buf->start; nstart+buf->size; n+=16) { -#ifdef WORDS_BIGENDIAN - /* Think of this as byteswap(p[x])^byteswap(n[x]) */ - n[0]=byteswap(p[0]^n[0]); - n[1]=byteswap(p[1]^n[1]); - n[2]=byteswap(p[2]^n[2]); - n[3]=byteswap(p[3]^n[3]); -#else - n[0]=p[0]^n[0]; - n[1]=p[1]^n[1]; - n[2]=p[2]^n[2]; - n[3]=p[3]^n[3]; -#endif + for (i = 0; i < 16; i++) + n[i] ^= p[i]; serpent_encrypt(&ti->cryptkey,n,n); -#ifdef WORDS_BIGENDIAN - n[0]=byteswap(n[0]); - n[1]=byteswap(n[1]); - n[2]=byteswap(n[2]); - n[3]=byteswap(n[3]); -#endif p=n; } @@ -208,12 +160,12 @@ static uint32_t transform_reverse(void *sst, struct buffer_if *buf, unsigned padlen; int i; uint32_t seqnum, skew; - uint32_t iv[4]; - uint32_t pct[4]; - uint32_t macplain[4]; - uint32_t macacc[4]; - uint32_t *n; - uint32_t *macexpected; + uint8_t iv[16]; + uint8_t pct[16]; + uint8_t macplain[16]; + uint8_t macacc[16]; + uint8_t *n; + uint8_t *macexpected; if (!ti->keyed) { *errmsg="transform unkeyed"; @@ -223,66 +175,40 @@ static uint32_t transform_reverse(void *sst, struct buffer_if *buf, /* CBC */ memset(iv,0,16); - iv[0]=buf_unprepend_uint32(buf); + { + uint32_t ivword = buf_unprepend_uint32(buf); + PUT_32BIT_MSB_FIRST(iv, ivword); + } /* Assert bufsize is multiple of blocksize */ if (buf->size&0xf) { *errmsg="msg not multiple of cipher blocksize"; } serpent_encrypt(&ti->cryptkey,iv,iv); - for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + for (n=buf->start; nstart+buf->size; n+=16) { -#ifdef WORDS_BIGENDIAN - n[0]=byteswap(n[0]); - n[1]=byteswap(n[1]); - n[2]=byteswap(n[2]); - n[3]=byteswap(n[3]); -#endif - pct[0]=n[0]; pct[1]=n[1]; pct[2]=n[2]; pct[3]=n[3]; + for (i = 0; i < 16; i++) + pct[i] = n[i]; serpent_decrypt(&ti->cryptkey,n,n); -#ifdef WORDS_BIGENDIAN - n[0]=byteswap(iv[0]^n[0]); - n[1]=byteswap(iv[1]^n[1]); - n[2]=byteswap(iv[2]^n[2]); - n[3]=byteswap(iv[3]^n[3]); -#else - n[0]=iv[0]^n[0]; - n[1]=iv[1]^n[1]; - n[2]=iv[2]^n[2]; - n[3]=iv[3]^n[3]; -#endif - iv[0]=pct[0]; iv[1]=pct[1]; iv[2]=pct[2]; iv[3]=pct[3]; + for (i = 0; i < 16; i++) + n[i] ^= iv[i]; + memcpy(iv, pct, 16); } /* CBCMAC */ macexpected=buf_unappend(buf,16); memset(iv,0,16); - iv[0]=ti->maciv; + PUT_32BIT_MSB_FIRST(iv, ti->maciv); serpent_encrypt(&ti->mackey,iv,macacc); /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted block encrypted once again. */ - for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4) + for (n=buf->start; nstart+buf->size; n+=16) { -#ifdef WORDS_BIGENDIAN - macplain[0]=macacc[0]^byteswap(n[0]); - macplain[1]=macacc[1]^byteswap(n[1]); - macplain[2]=macacc[2]^byteswap(n[2]); - macplain[3]=macacc[3]^byteswap(n[3]); -#else - macplain[0]=macacc[0]^n[0]; - macplain[1]=macacc[1]^n[1]; - macplain[2]=macacc[2]^n[2]; - macplain[3]=macacc[3]^n[3]; -#endif + for (i = 0; i < 16; i++) + macplain[i] = macacc[i] ^ n[i]; serpent_encrypt(&ti->mackey,macplain,macacc); } serpent_encrypt(&ti->mackey,macacc,macacc); -#ifdef WORDS_BIGENDIAN - macacc[0]=byteswap(macacc[0]); - macacc[1]=byteswap(macacc[1]); - macacc[2]=byteswap(macacc[2]); - macacc[3]=byteswap(macacc[3]); -#endif if (memcmp(macexpected,macacc,16)!=0) { *errmsg="invalid MAC"; return 1; @@ -390,30 +316,74 @@ void transform_module(dict_t *dict) { struct keyInstance k; uint8_t data[32]; - uint32_t plaintext[4]; - uint32_t ciphertext[4]; + uint8_t plaintext[16]; + uint8_t ciphertext[16]; + + /* + * Serpent self-test. + * + * This test pattern is taken directly from the Serpent test + * vectors, to ensure we have all endianness issues correct. -sgt + */ /* Serpent self-test */ - memset(data,0,32); + memcpy(data, + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" + "\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00", + 32); serpent_makekey(&k,256,data); - plaintext[0]=0x00000000; - plaintext[1]=0x00000001; - plaintext[2]=0x00000002; - plaintext[3]=0x00000003; + + memcpy(plaintext, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10", + 16); serpent_encrypt(&k,plaintext,ciphertext); - if (ciphertext[3]!=0x7ca73bb0 || - ciphertext[2]!=0x83C31E69 || - ciphertext[1]!=0xec52bd82 || - ciphertext[0]!=0x27a46120) { + + if (memcmp(ciphertext, "\xca\x7f\xa1\x93\xe3\xeb\x9e\x99" + "\xbd\x87\xe3\xaf\x3c\x9a\xdf\x93", 16)) { fatal("transform_module: serpent failed self-test (encrypt)"); } serpent_decrypt(&k,ciphertext,plaintext); - if (plaintext[0]!=0 || - plaintext[1]!=1 || - plaintext[2]!=2 || - plaintext[3]!=3) { + if (memcmp(plaintext, "\x01\x23\x45\x67\x89\xab\xcd\xef" + "\xfe\xdc\xba\x98\x76\x54\x32\x10", 16)) { fatal("transform_module: serpent failed self-test (decrypt)"); } add_closure(dict,"serpent256-cbc",transform_apply); + +#ifdef TEST_WHOLE_TRANSFORM + { + struct transform *tr; + void *ti; + struct buffer_if buf; + const char text[] = "This is a piece of test text."; + char keymaterial[76] = + "Seventy-six bytes i" + "n four rows of 19; " + "this looks almost l" + "ike a poem but not."; + const char *errmsg; + int i; + + tr = malloc(sizeof(struct transform)); + tr->max_seq_skew = 20; + ti = transform_create(tr); + + transform_setkey(ti, keymaterial, 76); + + buf.base = malloc(4096); + buffer_init(&buf, 2048); + memcpy(buf_append(&buf, sizeof(text)), text, sizeof(text)); + if (transform_forward(ti, &buf, &errmsg)) { + fatal("transform_forward test: %s", errmsg); + } + printf("transformed text is:\n"); + for (i = 0; i < buf.size; i++) + printf("%02x%c", buf.start[i], + (i%16==15 || i==buf.size-1 ? '\n' : ' ')); + if (transform_reverse(ti, &buf, &errmsg)) { + fatal("transform_reverse test: %s", errmsg); + } + printf("transform reversal worked OK\n"); + } +#endif } diff --git a/tun.c b/tun.c index bf8cb1b..f7c0561 100644 --- a/tun.c +++ b/tun.c @@ -106,7 +106,7 @@ static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, } static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds, - const struct timeval *tv_now, uint64_t *now) + const struct timeval *tv_now, uint64_t *now) { struct tun *st=sst; int l; diff --git a/udp.c b/udp.c index add7d8d..9615a17 100644 --- a/udp.c +++ b/udp.c @@ -36,6 +36,7 @@ struct udp { closure_t cl; struct comm_if ops; struct cloc loc; + uint32_t addr; uint16_t port; int fd; string_t authbind; @@ -182,7 +183,7 @@ static void udp_phase_hook(void *sst, uint32_t new_phase) struct udp *st=sst; struct sockaddr_in addr; - st->fd=socket(AF_INET, SOCK_DGRAM, 0); + st->fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (st->fd<0) { fatal_perror("udp (%s:%d): socket",st->loc.file,st->loc.line); } @@ -197,6 +198,7 @@ static void udp_phase_hook(void *sst, uint32_t new_phase) memset(&addr, 0, sizeof(addr)); addr.sin_family=AF_INET; + addr.sin_addr.s_addr=htonl(st->addr); addr.sin_port=htons(st->port); if (st->authbind) { pid_t c; @@ -209,23 +211,29 @@ static void udp_phase_hook(void *sst, uint32_t new_phase) fatal_perror("udp_phase_hook: fork() for authbind"); } if (c==0) { - char *argv[4]; + char *argv[4], addrstr[9], portstr[5]; + sprintf(addrstr,"%08lX",(long)st->addr); + sprintf(portstr,"%04X",st->port); argv[0]=st->authbind; - argv[1]=strdup("00000000"); - if (!argv[1]) exit(ENOMEM); - argv[2]=alloca(8); - if (!argv[2]) exit(ENOMEM); - sprintf(argv[2],"%04X",htons(st->port)); + argv[1]=addrstr; + argv[2]=portstr; argv[3]=NULL; dup2(st->fd,0); execvp(st->authbind,argv); - exit(ENOEXEC); + _exit(255); } - waitpid(c,&status,0); - if (WEXITSTATUS(status)!=0) { - errno=WEXITSTATUS(status); + while (waitpid(c,&status,0)==-1) { + if (errno==EINTR) continue; fatal_perror("udp (%s:%d): authbind",st->loc.file,st->loc.line); } + if (WIFSIGNALED(status)) { + fatal("udp (%s:%d): authbind died on signal %d",st->loc.file, + st->loc.line, WTERMSIG(status)); + } + if (WIFEXITED(status) && WEXITSTATUS(status)!=0) { + fatal("udp (%s:%d): authbind died with status %d",st->loc.file, + st->loc.line, WEXITSTATUS(status)); + } } else { if (bind(st->fd, (struct sockaddr *)&addr, sizeof(addr))!=0) { fatal_perror("udp (%s:%d): bind",st->loc.file,st->loc.line); @@ -239,7 +247,7 @@ static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct udp *st; - item_t *i; + item_t *i,*j; dict_t *d; list_t *l; uint32_t a; @@ -265,6 +273,8 @@ static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, } d=i->data.dict; + j=dict_find_item(d,"address",False,"udp",st->loc); + st->addr=j?st->addr=string_item_to_ipaddr(j, "udp"):INADDR_ANY; st->port=dict_read_number(d,"port",True,"udp",st->loc,0); st->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,"udp",st->loc); st->authbind=dict_read_string(d,"authbind",False,"udp",st->loc); diff --git a/util.h b/util.h index 4563a24..f1be586 100644 --- a/util.h +++ b/util.h @@ -4,6 +4,8 @@ #include "secnet.h" #include +#include "hackypar.h" + #define BUF_ASSERT_FREE(buf) do { buffer_assert_free((buf), \ __FILE__,__LINE__); } \ while(0)