--- vpnc-0.5.1r275.orig/debian/vpnc.examples +++ vpnc-0.5.1r275/debian/vpnc.examples @@ -0,0 +1 @@ +vpnc.conf --- vpnc-0.5.1r275.orig/debian/README.Debian +++ vpnc-0.5.1r275/debian/README.Debian @@ -0,0 +1,66 @@ +vpnc for Debian +=============== + +The Debian version of vpnc has few improvements compared to the +upstream version, mainly in the daemon invocation script. The +vpnc-connect script works with multiple "profile" files, so you can +manage multiple configurations easily. See vpnc-connect manpage for +details. + +UPGRADE NOTES +============= + +If you have been using Debian-specific extensions with "Target networks" and +"DNSupdate" directives, consider changing your praxis to use the official +upstream configuration way now. See /usr/share/doc/vpnc/README.gz file for +details about replacing network routes with custom ones using the variables of +vpnc-script. You can either wrap /etc/vpnc/vpnc-script into a custom script +which presets those variables (like documented in the example in README.gz) or +use vpnc-script-connect-action and vpnc-script-disconnect-action scripts to set +them separately (see below), which may or may not be wanted depending on your +setup. Or you can symlink them to have the same config in the both phases. + +The same applies to the disabling of DNS data update. The old methods are +preserved with compatibility wrappers for the near future but should be avoided +with new installations. + + +CONFIGURATION +============= + +To generate a sample config file for vpnc/vpnc-connect, copy +/etc/vpnc/example.conf to a config file with the appropriate name. Or use the +pcf2vpnc script to convert existing pcf files (Cisco client configuration), +running the following command (replace myvpn with your VPN name): + +/usr/share/vpnc/pcf2vpnc myvpn.pcf > /etc/vpnc/myvpn.conf + +You may edit the resulting file to add the remote username. + +In order to use the DNS server reported by the VPN server, please install the +resolvconf package. + +You may place commands to be run on connect to or disconnect from the +VPN server in /etc/vpnc/vpnc-script-connect-action and +/etc/vpnc/vpnc-script-disconnect-action respectively. These scripts +are sourced by /etc/vpnc/vpnc-script, see the comments at the top of +that file for the environment variables you have access to. There are also +possible /etc/vpnc/vpnc-script-post-connect-action and +/etc/vpnc/vpnc-script-post-disconnect-action executed after the link +configuration or shutdown. + +(IN)SECURITY WARNING +==================== + +Following this URL: + + + +and making simple considerations about the key exchange protocol shows +that the methods are inherently insecure. The scrambled key does not +really protect privacy, and there is nothing to prevent possible +man-in-the-middle-attacks. Though IPsec is used for data transfer, +simple XAUTH without certificates is not a secure way to pass the +login data! Be aware of this fact when you enter a password! + + -- Eduard Bloch -- Fri, 14 Apr 2006 23:08:54 +0200 --- vpnc-0.5.1r275.orig/debian/rules +++ vpnc-0.5.1r275/debian/rules @@ -0,0 +1,104 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +include /usr/share/dpatch/dpatch.make + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp patch + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + #/usr/bin/docbook-to-man debian/vpnc.sgml > vpnc.1 + + touch build-stamp + +clean: unpatch + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/vpnc. + cp vpnc vpnc-disconnect debian/vpnc/usr/sbin/ + install -m755 pcf2vpnc debian/vpnc/usr/share/vpnc/ + install -m755 cisco-decrypt debian/vpnc/usr/lib/vpnc/ + install -D -m600 vpnc.conf debian/vpnc/etc/vpnc/example.conf + install -D -m755 vpnc-script debian/vpnc/etc/vpnc/vpnc-script + chmod 700 debian/vpnc/etc/vpnc + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples + dh_link -pvpnc /usr/sbin/vpnc /usr/sbin/vpnc-connect +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman vpnc.8 + ln -sf vpnc.8.gz debian/vpnc/usr/share/man/man8/vpnc-connect.8.gz + ln -sf vpnc.8.gz debian/vpnc/usr/share/man/man8/vpnc-disconnect.8.gz + dh_link + dh_strip + dh_compress + dh_fixperms -X/etc/vpnc +# dh_perl +# dh_python +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure --- vpnc-0.5.1r275.orig/debian/docs +++ vpnc-0.5.1r275/debian/docs @@ -0,0 +1,2 @@ +README +TODO --- vpnc-0.5.1r275.orig/debian/copyright +++ vpnc-0.5.1r275/debian/copyright @@ -0,0 +1,24 @@ +This package was debianized by Eduard Bloch on +Mon, 27 Oct 2003 20:05:37 +0100. + +It was downloaded from http://www.unix-ag.uni-kl.de/~massar/vpnc/ and +will become an official repository on alioth.debian.org soon. + +Upstream Authors: + +Geoffrey Keating , 2002 +Maurice Massar , 2003 + +Copyright: + +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. + +You can find the complete text of the GPLv2 in the file +/usr/share/common-licenses/GPL-2 on Debian systems. + +The source code contains parts of the isakmpd package which are licensed +under the BSD license, refer to /usr/share/common-license/BSD for +details. --- vpnc-0.5.1r275.orig/debian/compat +++ vpnc-0.5.1r275/debian/compat @@ -0,0 +1 @@ +4 --- vpnc-0.5.1r275.orig/debian/vpnc.dirs +++ vpnc-0.5.1r275/debian/vpnc.dirs @@ -0,0 +1,4 @@ +usr/sbin +var/run/vpnc +usr/share/vpnc +usr/lib/vpnc --- vpnc-0.5.1r275.orig/debian/control +++ vpnc-0.5.1r275/debian/control @@ -0,0 +1,21 @@ +Source: vpnc +Section: net +Priority: extra +Maintainer: Ubuntu MOTU Developers +XSBC-Original-Maintainer: Eduard Bloch +Build-Depends: debhelper (>= 4.0.0), libgcrypt11-dev, dpatch +Standards-Version: 3.7.2 + +Package: vpnc +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Recommends: resolvconf, iproute +Description: Cisco-compatible VPN client + vpnc is a VPN client compatible with cisco3000 VPN Concentrator (also + known as Cisco's EasyVPN equipment). vpnc runs entirely in userspace + and does not require kernel modules except of the tun driver to + communicate with the network layer. + . + It supports most of the features needed to establish connection to the + VPN concentrator: MD5 and SHA1 hashes, 3DES and AES ciphers, PFS and + various IKE DH group settings. --- vpnc-0.5.1r275.orig/debian/changelog +++ vpnc-0.5.1r275/debian/changelog @@ -0,0 +1,310 @@ +vpnc (0.5.1r275-1ubuntu1) intrepid; urgency=low + + * Cherry-pick patch from upstream so that vpnc doesn't ignore the password + in the configuration file. (LP: #214399) + - This is debian/patches/07_fix_double_password_prompt.dpatch + - http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=492981 + + -- James Westby Wed, 30 Jul 2008 13:26:10 +0100 + +vpnc (0.5.1r275-1) unstable; urgency=low + + * New upstream SVN snapshot with various bugfixes + * A very very very very urgent "fix" from Nicolas Duboc to put cisco-decrypt + into /usr/lib/vpnc (closes: #454236) + + -- Eduard Bloch Sun, 23 Dec 2007 15:42:52 +0100 + +vpnc (0.5.1r254-1) unstable; urgency=low + + * New upstream release (with updates from trunk) + + removes bash specific function keyword (closes: #441045, #444859) + * adding cisco-decrypt to vpnc tool (closes: #444631) + + -- Eduard Bloch Thu, 18 Oct 2007 09:42:08 +0200 + +vpnc (0.5.0-1) unstable; urgency=low + + * New upstream release + + lots of add-ons and fixes moved into official source + + Dead Peer Detection improvements (closes: #416180) + + Fixes in net-tools usage (closes: #430799) + + unsuccessfull error code on authentication failure (closes: #414437) + * put resolvconf and iproute into Recommends + + -- Eduard Bloch Thu, 30 Aug 2007 20:51:37 +0200 + +vpnc (0.4.0-3) unstable; urgency=low + + * 06_stolen_from_head.dpatch: sync with SVN revision 174, including fixes + for DPD (closes: #416180) and also most likely solves the keepalive + problems (closes: #418906, reopen if not) + * 04_debianitis.dpatch: ifconfig call with full path (closes: 423146) + + -- Eduard Bloch Wed, 23 May 2007 22:45:46 +0200 + +vpnc (0.4.0-2) unstable; urgency=medium + + * proper increment variable picking when emulating Target networks + directive (closes: #412784) + * small bug in resolv.conf update forbidding directive handling fixed, + it was causing unuseable resolv.conf in certain cases + * 06_stolen_from_head.dpatch: upstream fixes in Revision 159 + (closes: #411668) + + -- Eduard Bloch Sun, 11 Mar 2007 18:48:27 +0100 + +vpnc (0.4.0-1) unstable; urgency=low + + * New upstream release + + GNU/kFreeBSD related fixes (closes: #400740) + + Supports phase2 rekeying (closes: #411108) + + auto-creating /var/run/vpnc (closes: #403783) + * Old config handling extensions replaced with wrappers to upstream + vpnc-script function variables which are declared official now + (closes: #399131) + * more connect/shutdown hooks (closes: #366257) + * not depending on iproute, though old extensions may not work without it + but users are warned in that case (closes: #393848) + + -- Eduard Bloch Mon, 19 Feb 2007 22:33:12 +0100 + +vpnc (0.3.3+SVN20051028-3) unstable; urgency=low + + * 08_keepalive_and_rekeying.dpatch: patch for basic rekeying and keepalive + support from Tomas Mraz + * stronger permissions of /etc/vpnc/ and /etc/vpnc/example.conf to protect + careless users from making their login data world-readable + (closes: #340105) + * documented connect/disconnect hooks in README.Debian, thanks to Elmar + Hoffmann (closes: #360704) + + -- Eduard Bloch Fri, 14 Apr 2006 23:30:36 +0200 + +vpnc (0.3.3+SVN20051028-2) unstable; urgency=low + + * TARGET_NETWORKS code was accidentaly removed in 04_debianitis.dpatch, now + restored (closes: #336532) + + -- Eduard Bloch Wed, 02 Nov 2005 09:07:12 +0100 + +vpnc (0.3.3+SVN20051028-1) unstable; urgency=low + + * new upstream snapshot + + includes a password string deobfuscater + + a bash specific loop construct has been rewritten (closes: #335989) + * inserts another default default value into setup variables (now + really closes: #334203, #335518) + * fallback to $configname.conf file scheme (closes: #335383) + * vpnc-script tries to open the device for 10 seconds after the module has + been loaded, to work around udev's timing problems (closes: #281663) + + -- Eduard Bloch Fri, 28 Oct 2005 16:08:00 +0200 + +vpnc (0.3.3+SVN20050909-5) unstable; urgency=low + + * set the default string "No" for DNS_UPDATE (closes: #334699) + * do not see any additional routes sent by the server when the + TARGET_NETWORKS is set (closes: #334203) + + -- Eduard Bloch Thu, 13 Oct 2005 22:14:39 +0200 + +vpnc (0.3.3+SVN20050909-4) unstable; urgency=low + + * added the magic [ words to the test commands in the new hooks + (closes: #333813) + + -- Eduard Bloch Thu, 13 Oct 2005 12:47:15 +0200 + +vpnc (0.3.3+SVN20050909-3) unstable; urgency=low + + * this should be the "good third revision" of 0.3.3 (closes: #314941) + * correct fixes for the DNSUpdate option check (for every corner case) and + patch merge errors in the TARGET_NETWORKS execution (closes: #333312) + * added a hook to vpnc-script to execute + /etc/vpnc/vpnc-script-disconnect-action on disconnect, if found. Create it + or even modify vpnc-script as needed. (closes: #254032) + * also added a hook for /etc/vpnc/vpnc-script-disconnect-action to add + custom stuff there (closes: #299472) + + -- Eduard Bloch Thu, 13 Oct 2005 12:43:23 +0200 + +vpnc (0.3.3+SVN20050909-2) unstable; urgency=low + + * fixed typo in vpnc-script patch which was causing the resolv.conf update + though disabled in the config + + -- Eduard Bloch Wed, 12 Oct 2005 01:11:44 +0200 + +vpnc (0.3.3+SVN20050909-1) unstable; urgency=low + + * New upstream release + + all functionality of vpnc-connect moved to vpnc and vpnc-script + * migrated additional vpnc-connect functionality (resolvconf, Target + Networks, DNSUpdate options) to vpnc-script and vpnc source + * set transitional symlink vpnc-connect (-> vpnc) + * reduced default MTU to 1390 to work around problems seen while testing + + -- Eduard Bloch Mon, 10 Oct 2005 12:22:42 +0200 + +vpnc (0.3.2+SVN20050326-2) unstable; urgency=high + + * added a check for having a slash in the config file specification (now it + really accepts absolute paths only and not some random, or even malicious, + script from the current directory). Before, it was like having "." on the + first place in root's $PATH. + * also reverted the vpnc binary lookup order to limit possible effects of + a similar problem + + -- Eduard Bloch Thu, 05 May 2005 19:39:05 +0200 + +vpnc (0.3.2+SVN20050326-1) unstable; urgency=low + + * New upstream SVN snapshot + + reported to solve 64bit problems (closes: #282732) + + -- Eduard Bloch Sat, 26 Mar 2005 10:58:35 +0100 + +vpnc (0.3.2+SVN20041123-1) unstable; urgency=low + + * New upstream release and update + * Changed the example gateway IP to one from the official example net + * do not try to run modprobe if there is no module support (closes: #281606) + + -- Eduard Bloch Tue, 23 Nov 2004 18:43:43 +0100 + +vpnc (0.3.1-1) unstable; urgency=low + + * New upstream release + * removed Interface name from the example config file - upstream request, + too many users tried to use this on kernel 2.2 :( + * added $@ to the vpnc call in vpnc-connect (closes: #274202) + * added /sbin to the PATH to reach ifconfig (closes: #278049) + + -- Eduard Bloch Sat, 13 Nov 2004 15:43:46 +0100 + +vpnc (0.2-rm+zomb.1-8) unstable; urgency=low + + * Rebuilt for libgcrypt11 + + -- Eduard Bloch Thu, 17 Jun 2004 16:37:41 +0200 + +vpnc (0.2-rm+zomb.1-7) unstable; urgency=low + + * pre-release upstream update + + keeping the same syslog facility after fork (closes: #251228) + + insecurity warnings in README.Debian and vpnc.8 (closes: #251935) + + general PIX support was added in the previous release (closes: #220233) + * changes to use dpatch + * upstream TODO file re-added (closes: #254034) + * patch from Wolfgang Ratzka to add direct gateway route even if Target + networks is set (closes: #253051) + + -- Eduard Bloch Wed, 26 May 2004 16:57:52 +0200 + +vpnc (0.2-rm+zomb.1-6) unstable; urgency=low + + * Made checks for tun_init be less precise to match on kernel 2.4 + + -- Eduard Bloch Wed, 26 May 2004 16:16:16 +0200 + +vpnc (0.2-rm+zomb.1-5) unstable; urgency=low + + * Fix of the fix of the last tree RC bugs, also use the right command [tm] + to display the help text, thanks to Michael Farmbauer (closes: #250839) + * More alternative checks for the tun driver presence + + -- Eduard Bloch Mon, 24 May 2004 18:12:05 +0200 + +vpnc (0.2-rm+zomb.1-4) unstable; urgency=low + + * Moved the config file argument into the quotes when specifying the + configuration script argument (closes: #250695, #250673, #240766) + * installing example config file into /etc/vpnc/ (closes: #246714) + * checking $1 before shift to not confuse dash + * made vpnc-connect be quiet if CONFIG_TUN=y was found in the guessed kernel + config file (closes: #250237) + + -- Eduard Bloch Mon, 17 May 2004 17:55:22 +0200 + +vpnc (0.2-rm+zomb.1-3) unstable; urgency=low + + * the third-time-lucky revision + * removed surrounding quotes in the DNS server list + + -- Eduard Bloch Sat, 15 May 2004 21:31:15 +0200 + +vpnc (0.2-rm+zomb.1-2) unstable; urgency=low + + * vpnc.c: Only warn about additional ("unknown") config directives in + debug mode (as it was done in -pre8 before) + * vpnc-connect: adding explicite routes to VPNed DNS servers if needed + * avoiding multi-line if-statements, report to break with some shell + + -- Eduard Bloch Fri, 14 May 2004 15:18:52 +0200 + +vpnc (0.2-rm+zomb.1-1) unstable; urgency=low + + * New upstream release + * resolvconf integration to implement DNS data update mechanism + + -- Eduard Bloch Fri, 14 May 2004 09:04:26 +0200 + +vpnc (0.2-rm+zomb-pre8-1) unstable; urgency=low + + * New upstream release + * added Interface name to the manpage example and a default value to the + vpnc-connect script + + -- Eduard Bloch Sun, 25 Apr 2004 10:56:01 +0200 + +vpnc (0.2-rm+zomb-pre7-4) unstable; urgency=low + + * Multiple config management patch by Tobias Oetiker + * Manpage updates based on the patch above + * Fixed the route check on the defaultroute restoring (thanks to Thomas + Deselaers, closes: #230806) + + -- Eduard Bloch Thu, 29 Jan 2004 11:06:00 +0100 + +vpnc (0.2-rm+zomb-pre7-3) unstable; urgency=low + + * Does not clobber the default route if custom routes have been defined + (successor of the #225776 fix), thanks to Steven Ihde (closes: #230201) + + -- Eduard Bloch Wed, 28 Jan 2004 20:23:10 +0100 + +vpnc (0.2-rm+zomb-pre7-2) unstable; urgency=low + + * vpnc-connect: allow customizable target routes in vpnc.conf + (closes: #225776) + * vpnc.c: don't bother about unknown options unless --debug is used + * merged relevant parts of the FreeBSD manpage Debian into out Linux + version, replaced hyphens with minus signs (\-) and stopped using the SGML + template. It just sucked. + + -- Eduard Bloch Thu, 18 Dec 2003 21:14:11 +0100 + +vpnc (0.2-rm+zomb-pre7-1) unstable; urgency=low + + * New upstream release + + vpnc-connect filters weird ip output (closes: #220495) + * Builds with the new libgcrypt generation, Build-Deps adjusted + * Typo in description fixed (closes: #220172) + + -- Eduard Bloch Thu, 18 Dec 2003 20:28:02 +0100 + +vpnc (0.2-rm+zomb-pre5-2) unstable; urgency=low + + * Fixed the test condition when looking for the tun device node + * vpnc.c: fixed --local-port description + * provisoric manpage written + + -- Eduard Bloch Mon, 3 Nov 2003 22:41:04 +0100 + +vpnc (0.2-rm+zomb-pre5-1) unstable; urgency=low + + * Initial Release (closes: #217838) + + -- Eduard Bloch Thu, 30 Oct 2003 07:08:26 +0100 + --- vpnc-0.5.1r275.orig/debian/patches/06_stolen_from_head.dpatch +++ vpnc-0.5.1r275/debian/patches/06_stolen_from_head.dpatch @@ -0,0 +1,5618 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 06_stolen_from_head.dpatch by Eduard Bloch +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +@DPATCH@ +diff -urNad vpnc~/Makefile vpnc/Makefile +--- vpnc~/Makefile 2007-02-19 21:51:12.000000000 +0100 ++++ vpnc/Makefile 2007-05-23 22:48:16.000000000 +0200 +@@ -35,9 +35,9 @@ + RELEASE_VERSION := $(shell cat VERSION) + + CC=gcc +-CFLAGS += -W -Wall -O3 -Wmissing-declarations -Wwrite-strings -g +-CPPFLAGS = -DVERSION=\"$(VERSION)\" +-LDFLAGS = -g $(shell libgcrypt-config --libs) ++CFLAGS := -W -Wall -O3 -Wmissing-declarations -Wwrite-strings -g $(CFLAGS) ++CPPFLAGS += -DVERSION=\"$(VERSION)\" ++LDFLAGS := -g $(shell libgcrypt-config --libs) $(LDFLAGS) + CFLAGS += $(shell libgcrypt-config --cflags) + + ifeq ($(shell uname -s), SunOS) +@@ -83,22 +83,24 @@ + distclean : clean + -rm -f vpnc-debug.c vpnc-debug.h vpnc.ps .depend + +-install : all ++install-common: all + install -d $(DESTDIR)$(ETCDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man8 +- install vpnc-script $(DESTDIR)$(ETCDIR) ++ if [ "`uname -s | cut -c-6`" = "CYGWIN" ]; then \ ++ install vpnc-script-win $(DESTDIR)$(ETCDIR)/vpnc-script; \ ++ install vpnc-script-win.js $(DESTDIR)$(ETCDIR); \ ++ else \ ++ install vpnc-script $(DESTDIR)$(ETCDIR); \ ++ fi + install -m 600 vpnc.conf $(DESTDIR)$(ETCDIR)/default.conf +- install vpnc vpnc-disconnect $(DESTDIR)$(SBINDIR) ++ install vpnc-disconnect $(DESTDIR)$(SBINDIR) + install pcf2vpnc $(DESTDIR)$(BINDIR) + install vpnc.8 $(DESTDIR)$(MANDIR)/man8 + +-install-strip : all +- install -d $(DESTDIR)$(ETCDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man8 +- install vpnc-script $(DESTDIR)$(ETCDIR) +- install -m 600 vpnc.conf $(DESTDIR)$(ETCDIR)/default.conf +- install pcf2vpnc $(DESTDIR)$(BINDIR) ++install : install-common ++ install vpnc $(DESTDIR)$(SBINDIR) ++ ++install-strip : install-common + install -s vpnc $(DESTDIR)$(SBINDIR) +- install vpnc-disconnect $(DESTDIR)$(SBINDIR) +- install vpnc.8 $(DESTDIR)$(MANDIR)/man8 + + uninstall : + rm -f $(DESTDIR)$(SBINDIR)/vpnc \ +diff -urNad vpnc~/TODO vpnc/TODO +--- vpnc~/TODO 2007-02-18 16:25:50.000000000 +0100 ++++ vpnc/TODO 2007-05-23 22:48:16.000000000 +0200 +@@ -1,7 +1,5 @@ + TODO list + +-* --local-address +- + * clean up scripts + - config-support for vpnc-script + - customizable handling of routing +@@ -17,7 +15,7 @@ + - get a rid of remaining (non-const) global variables + + * implement phase1 rekeying (with or without xauth-reauthentication) +-* implement DPD, RFC3 706 Dead Peer Detection ++* implement DPD, RFC 3706 Dead Peer Detection + * implement compression + * try a list of gateways (backup server) + +@@ -30,6 +28,8 @@ + - usernames containing "@" unable to login + - ipsec over tcp + - nortel support? ++ - segfault if > 100 routes/acls (to large paket? read size?) ++ - amd64 somehow broken? maybe gcc bugs?? + + * optional drop root (rekey? reconnect? vpnc-script calls?) + * implement hybrid-auth +@@ -51,6 +51,7 @@ + + ---- + ++* DONE --local-address + * DONE implement phase2 rekeying + * DONE support rsa-SecurID token which sometimes needs 2 IDs + * DONE add macosx support +diff -urNad vpnc~/config.c vpnc/config.c +--- vpnc~/config.c 2007-05-23 22:47:53.000000000 +0200 ++++ vpnc/config.c 2007-05-23 22:48:16.000000000 +0200 +@@ -219,6 +219,11 @@ + return "server"; + } + ++static const char *config_def_local_addr(void) ++{ ++ return "0.0.0.0"; ++} ++ + static const char *config_def_local_port(void) + { + return "500"; +@@ -239,6 +244,11 @@ + return "10000"; + } + ++static const char *config_def_dpd_idle(void) ++{ ++ return "300"; ++} ++ + static const char *config_def_app_version(void) + { + struct utsname uts; +@@ -277,7 +287,7 @@ + static const struct config_names_s { + enum config_enum nm; + const int needsArgument; +- const int lvl; ++ const int long_only; + const char *option; + const char *name; + const char *type; +@@ -463,6 +473,13 @@ + "store the pid of background process in ", + config_def_pid_file + }, { ++ CONFIG_LOCAL_ADDR, 1, 1, ++ "--local-addr", ++ "Local Addr ", ++ "", ++ "local IP to use for ISAKMP / ESP / ... (0.0.0.0 == automatically assign)", ++ config_def_local_addr ++ }, { + CONFIG_LOCAL_PORT, 1, 1, + "--local-port", + "Local Port ", +@@ -474,12 +491,20 @@ + "--udp-port", + "Cisco UDP Encapsulation Port ", + "<0-65535>", +- "local UDP port number to use (0 == use random port)\n" ++ "Local UDP port number to use (0 == use random port)\n" + "This is only relevant if cisco-udp nat-traversal is used.\n" + "This is the _local_ port, the remote udp port is discovered automatically.\n" + "It is especially not the cisco-tcp port\n", + config_def_udp_port + }, { ++ CONFIG_DPD_IDLE, 1, 1, ++ "--dpd-idle", ++ "DPD idle timeout (our side) ", ++ "<0,10-86400>", ++ "Send DPD packet after not receiving anything for seconds.\n" ++ "Use 0 to disable DPD completely (both ways).\n", ++ config_def_dpd_idle ++ }, { + CONFIG_NON_INTERACTIVE, 0, 1, + "--non-inter", + "Noninteractive", +@@ -600,7 +625,7 @@ + printf("%s%s\n", pre, p); + } + +-static void print_usage(char *argv0, int long_help) ++static void print_usage(char *argv0, int print_level) + { + int c; + +@@ -608,7 +633,7 @@ + argv0); + printf("Legend:\n"); + for (c = 0; config_names[c].name != NULL; c++) { +- if (config_names[c].lvl > long_help) ++ if (config_names[c].long_only > print_level) + continue; + + printf(" %s %s\n" +@@ -627,7 +652,7 @@ + printf("\n"); + } + +- if (!long_help) ++ if (!print_level) + printf("Use --long-help to see all options\n\n"); + + printf("Report bugs to vpnc@unix-ag.uni-kl.de\n"); +diff -urNad vpnc~/config.c.orig vpnc/config.c.orig +--- vpnc~/config.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ vpnc/config.c.orig 2007-05-23 22:47:53.000000000 +0200 +@@ -0,0 +1,869 @@ ++/* IPSec VPN client compatible with Cisco equipment. ++ Copyright (C) 2004-2005 Maurice Massar ++ ++ 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 ++ ++ $Id: config.c 133 2007-02-16 17:22:06Z Maurice Massar $ ++*/ ++ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "sysdep.h" ++#include "config.h" ++#include "vpnc.h" ++#include "supp.h" ++ ++const char *config[LAST_CONFIG]; ++ ++int opt_debug = 0; ++int opt_nd; ++int opt_1des, opt_no_encryption; ++enum natt_mode_enum opt_natt_mode; ++enum vendor_enum opt_vendor; ++enum if_mode_enum opt_if_mode; ++uint16_t opt_udpencapport; ++ ++void hex_dump(const char *str, const void *data, ssize_t len, const struct debug_strings *decode) ++{ ++ size_t i; ++ const uint8_t *p = data; ++ const char *decodedval; ++ ++ if (opt_debug < 3) ++ return; ++ ++ switch (len) { ++ case DUMP_UINT8: ++ decodedval = val_to_string(*(uint8_t *)p, decode); ++ printf("%s: %02x%s\n", str, *(uint8_t *)p, decodedval); ++ return; ++ case DUMP_UINT16: ++ decodedval = val_to_string(*(uint16_t *)p, decode); ++ printf("%s: %04x%s\n", str, *(uint16_t *)p, decodedval); ++ return; ++ case DUMP_UINT32: ++ decodedval = val_to_string(*(uint32_t *)p, decode); ++ printf("%s: %08x%s\n", str, *(uint32_t *)p, decodedval); ++ return; ++ } ++ ++ printf("%s:%c", str, (len <= 32) ? ' ' : '\n'); ++ for (i = 0; i < (size_t)len; i++) { ++ if (i && !(i % 32)) ++ printf("\n"); ++ else if (i && !(i % 4)) ++ printf(" "); ++ printf("%02x", p[i]); ++ } ++ printf("\n"); ++} ++ ++static int hex2bin_c(unsigned int c) ++{ ++ if ((c >= '0')&&(c <= '9')) ++ return c - '0'; ++ if ((c >= 'A')&&(c <= 'F')) ++ return c - 'A' + 10; ++ if ((c >= 'a')&&(c <= 'f')) ++ return c - 'a' + 10; ++ return -1; ++} ++ ++int hex2bin(const char *str, char **bin, int *len) ++{ ++ char *p; ++ int i, l; ++ ++ if (!bin) ++ return EINVAL; ++ ++ for (i = 0; str[i] != '\0'; i++) ++ if (hex2bin_c(str[i]) == -1) ++ return EINVAL; ++ ++ l = i; ++ if ((l & 1) != 0) ++ return EINVAL; ++ l /= 2; ++ ++ p = malloc(l); ++ if (p == NULL) ++ return ENOMEM; ++ ++ for (i = 0; i < l; i++) ++ p[i] = hex2bin_c(str[i*2]) << 4 | hex2bin_c(str[i*2+1]); ++ ++ *bin = p; ++ if (len) ++ *len = l; ++ ++ return 0; ++} ++ ++int deobfuscate(char *ct, int len, const char **resp, char *reslenp) ++{ ++ const char *h1 = ct; ++ const char *h4 = ct + 20; ++ const char *enc = ct + 40; ++ ++ char ht[20], h2[20], h3[20], key[24]; ++ const char *iv = h1; ++ char *res; ++ gcry_cipher_hd_t ctx; ++ int reslen; ++ ++ if (len < 48) ++ return -1; ++ len -= 40; ++ ++ memcpy(ht, h1, 20); ++ ++ ht[19]++; ++ gcry_md_hash_buffer(GCRY_MD_SHA1, h2, ht, 20); ++ ++ ht[19] += 2; ++ gcry_md_hash_buffer(GCRY_MD_SHA1, h3, ht, 20); ++ ++ memcpy(key, h2, 20); ++ memcpy(key+20, h3, 4); ++ /* who cares about parity anyway? */ ++ ++ gcry_md_hash_buffer(GCRY_MD_SHA1, ht, enc, len); ++ ++ if (memcmp(h4, ht, 20) != 0) ++ return -1; ++ ++ res = malloc(len); ++ if (res == NULL) ++ return -1; ++ ++ gcry_cipher_open(&ctx, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0); ++ gcry_cipher_setkey(ctx, key, 24); ++ gcry_cipher_setiv(ctx, iv, 8); ++ gcry_cipher_decrypt(ctx, (unsigned char *)res, len, (unsigned char *)enc, len); ++ gcry_cipher_close(ctx); ++ ++ reslen = len - res[len-1]; ++ res[reslen] = '\0'; ++ ++ if (resp) ++ *resp = res; ++ if (reslenp) ++ *reslenp = reslen; ++ return 0; ++} ++ ++static void config_deobfuscate(int obfuscated, int clear) ++{ ++ int ret, len = 0; ++ char *bin = NULL; ++ ++ if (config[obfuscated] == NULL) ++ return; ++ ++ if (config[clear] != NULL) { ++ config[obfuscated] = NULL; ++ error(0, 0, "warning: ignoring obfuscated password because cleartext password set"); ++ return; ++ } ++ ++ ret = hex2bin(config[obfuscated], &bin, &len); ++ if (ret != 0) { ++ error(1, 0, "error: deobfuscating of password failed (input not a hex string)"); ++ } ++ ++ ret = deobfuscate(bin, len, config+clear, NULL); ++ free(bin); ++ if (ret != 0) { ++ error(1, 0, "error: deobfuscating of password failed"); ++ } ++ ++ config[obfuscated] = NULL; ++ return; ++} ++ ++static const char *config_def_description(void) ++{ ++ return "default value for this option"; ++} ++ ++static const char *config_def_ike_dh(void) ++{ ++ return "dh2"; ++} ++ ++static const char *config_def_pfs(void) ++{ ++ return "server"; ++} ++ ++static const char *config_def_local_port(void) ++{ ++ return "500"; ++} ++ ++static const char *config_def_if_mode(void) ++{ ++ return "tun"; ++} ++ ++static const char *config_def_natt_mode(void) ++{ ++ return "natt"; ++} ++ ++static const char *config_def_udp_port(void) ++{ ++ return "10000"; ++} ++ ++static const char *config_def_app_version(void) ++{ ++ struct utsname uts; ++ char *version; ++ ++ uname(&uts); ++ asprintf(&version, "Cisco Systems VPN Client %s:%s", VERSION, uts.sysname); ++ return version; ++} ++ ++static const char *config_def_script(void) ++{ ++ return "/etc/vpnc/vpnc-script"; ++} ++ ++static const char *config_def_pid_file(void) ++{ ++ return "/var/run/vpnc/pid"; ++} ++ ++static const char *config_def_vendor(void) ++{ ++ return "cisco"; ++} ++ ++static const char *config_def_networks_list(void) ++{ ++ return ""; ++} ++ ++static const char *config_def_dns_update(void) ++{ ++ return "Yes"; ++} ++ ++static const struct config_names_s { ++ enum config_enum nm; ++ const int needsArgument; ++ const int lvl; ++ const char *option; ++ const char *name; ++ const char *type; ++ const char *desc; ++ const char *(*get_def) (void); ++} config_names[] = { ++ /* Note: broken config file parser does NOT support option ++ * names where one is a prefix of another option. Needs just a bit work to ++ * fix the parser to care about ' ' or '\t' after the wanted ++ * option... */ ++ { ++ CONFIG_NONE, 0, 0, ++ "commandline option,", ++ "configfile variable, ", ++ "argument type", ++ "description", ++ config_def_description ++ }, { ++ CONFIG_IPSEC_GATEWAY, 1, 0, ++ "--gateway", ++ "IPSec gateway ", ++ "", ++ "IP/name of your IPSec gateway", ++ NULL ++ }, { ++ CONFIG_IPSEC_ID, 1, 0, ++ "--id", ++ "IPSec ID ", ++ "", ++ "your group name", ++ NULL ++ }, { ++ CONFIG_IPSEC_SECRET, 1, 0, ++ NULL, ++ "IPSec secret ", ++ "", ++ "your group password (cleartext)", ++ NULL ++ }, { ++ CONFIG_IPSEC_SECRET_OBF, 1, 1, ++ NULL, ++ "IPSec obfuscated secret ", ++ "", ++ "your group password (obfuscated)", ++ NULL ++ }, { ++ CONFIG_XAUTH_USERNAME, 1, 0, ++ "--username", ++ "Xauth username ", ++ "", ++ "your username", ++ NULL ++ }, { ++ CONFIG_XAUTH_PASSWORD, 1, 0, ++ NULL, ++ "Xauth password ", ++ "", ++ "your password (cleartext)", ++ NULL ++ }, { ++ CONFIG_XAUTH_PASSWORD_OBF, 1, 1, ++ NULL, ++ "Xauth obfuscated password ", ++ "", ++ "your password (obfuscated)", ++ NULL ++ }, { ++ CONFIG_DOMAIN, 1, 1, ++ "--domain", ++ "Domain ", ++ "", ++ "(NT-) Domain name for authentication", ++ NULL ++ }, { ++ CONFIG_XAUTH_INTERACTIVE, 0, 1, ++ "--xauth-inter", ++ "Xauth interactive", ++ NULL, ++ "enable interactive extended authentication (for challange response auth)", ++ NULL ++ }, { ++ CONFIG_VENDOR, 1, 1, ++ "--vendor", ++ "Vendor ", ++ "", ++ "vendor of your IPSec gateway", ++ config_def_vendor ++ }, { ++ CONFIG_NATT_MODE, 1, 1, ++ "--natt-mode", ++ "NAT Traversal Mode ", ++ "", ++ "Which NAT-Traversal Method to use:\n" ++ " * natt -- NAT-T as defined in RFC3947\n" ++ " * none -- disable use of any NAT-T method\n" ++ " * force-natt -- always use NAT-T encapsulation even\n" ++ " without presence of a NAT device\n" ++ " (useful if the OS captures all ESP traffic)\n" ++ " * cisco-udp -- Cisco proprietary UDP encapsulation, commonly over Port 10000\n" ++ "Note: cisco-tcp encapsulation is not yet supported\n", ++ config_def_natt_mode ++ }, { ++ CONFIG_SCRIPT, 1, 1, ++ "--script", ++ "Script ", ++ "", ++ "command is executed using system() to configure the interface,\n" ++ "routing and so on. Device name, IP, etc. are passed using enviroment\n" ++ "variables, see README. This script is executed right after ISAKMP is\n" ++ "done, but befor tunneling is enabled. It is called when vpnc\n" ++ "terminates too\n", ++ config_def_script ++ }, { ++ CONFIG_IKE_DH, 1, 1, ++ "--dh", ++ "IKE DH Group ", ++ "", ++ "name of the IKE DH Group", ++ config_def_ike_dh ++ }, { ++ CONFIG_IPSEC_PFS, 1, 1, ++ "--pfs", ++ "Perfect Forward Secrecy ", ++ "", ++ "Diffie-Hellman group to use for PFS", ++ config_def_pfs ++ }, { ++ CONFIG_ENABLE_1DES, 0, 1, ++ "--enable-1des", ++ "Enable Single DES", ++ NULL, ++ "enables weak single DES encryption", ++ NULL ++ }, { ++ CONFIG_ENABLE_NO_ENCRYPTION, 0, 1, ++ "--enable-no-encryption", ++ "Enable no encryption", ++ NULL, ++ "enables using no encryption for data traffic (key exchanged must be encrypted)", ++ NULL ++ }, { ++ CONFIG_VERSION, 1, 1, ++ "--application-version", ++ "Application version ", ++ "", ++ "Application Version to report", ++ config_def_app_version ++ }, { ++ CONFIG_IF_NAME, 1, 1, ++ "--ifname", ++ "Interface name ", ++ "", ++ "visible name of the TUN/TAP interface", ++ NULL ++ }, { ++ CONFIG_IF_MODE, 1, 1, ++ "--ifmode", ++ "Interface mode ", ++ "", ++ "mode of TUN/TAP interface:\n" ++ " * tun: virtual point to point interface (default)\n" ++ " * tap: virtual ethernet interface\n", ++ config_def_if_mode ++ }, { ++ CONFIG_DEBUG, 1, 1, ++ "--debug", ++ "Debug ", ++ "<0/1/2/3/99>", ++ "Show verbose debug messages", ++ NULL ++ }, { ++ CONFIG_ND, 0, 1, ++ "--no-detach", ++ "No Detach", ++ NULL, ++ "Don't detach from the console after login", ++ NULL ++ }, { ++ CONFIG_PID_FILE, 1, 1, ++ "--pid-file", ++ "Pidfile ", ++ "", ++ "store the pid of background process in ", ++ config_def_pid_file ++ }, { ++ CONFIG_LOCAL_PORT, 1, 1, ++ "--local-port", ++ "Local Port ", ++ "<0-65535>", ++ "local ISAKMP port number to use (0 == use random port)", ++ config_def_local_port ++ }, { ++ CONFIG_UDP_ENCAP_PORT, 1, 1, ++ "--udp-port", ++ "Cisco UDP Encapsulation Port ", ++ "<0-65535>", ++ "local UDP port number to use (0 == use random port)\n" ++ "This is only relevant if cisco-udp nat-traversal is used.\n" ++ "This is the _local_ port, the remote udp port is discovered automatically.\n" ++ "It is especially not the cisco-tcp port\n", ++ config_def_udp_port ++ }, { ++ CONFIG_NON_INTERACTIVE, 0, 1, ++ "--non-inter", ++ "Noninteractive", ++ NULL, ++ "Don't ask anything, exit on missing options", ++ NULL ++ }, ++ ++ ++ { ++ CONFIG_DNS_UPDATE, 1, 1, ++ "--dns-update", ++ "DNSUpdate", ++ "", ++ "DEPRECATED extension, see README.Debian for details", ++ config_def_dns_update ++ }, ++ ++ { ++ CONFIG_TARGET_NETWORKS, 1, 1, ++ "--target-networks", ++ "Target Networks", ++ NULL, ++ "DEPRECATED extension, see README.Debian for details", ++ config_def_networks_list ++ }, ++ ++ ++ { ++ 0, 0, 0, NULL, NULL, NULL, NULL, NULL ++ } ++}; ++ ++static char *get_config_filename(const char *name, int add_dot_conf) ++{ ++ char *realname; ++ ++ asprintf(&realname, "%s%s%s", index(name, '/') ? "" : "/etc/vpnc/", name, add_dot_conf ? ".conf" : ""); ++ return realname; ++} ++ ++static void read_config_file(const char *name, const char **configs, int missingok) ++{ ++ FILE *f; ++ char *line = NULL; ++ size_t line_length = 0; ++ int linenum = 0; ++ char *realname; ++ ++ if (!strcmp(name, "-")) { ++ f = stdin; ++ realname = strdup("stdin"); ++ } else { ++ realname = get_config_filename(name, 0); ++ f = fopen(realname, "r"); ++ if (f == NULL && errno == ENOENT) { ++ free(realname); ++ realname = get_config_filename(name, 1); ++ f = fopen(realname, "r"); ++ } ++ if (missingok && f == NULL && errno == ENOENT) { ++ free(realname); ++ return; ++ } ++ if (f == NULL) ++ error(1, errno, "couldn't open `%s'", realname); ++ } ++ for (;;) { ++ ssize_t llen; ++ int i; ++ ++ llen = getline(&line, &line_length, f); ++ if (llen == -1 && feof(f)) ++ break; ++ if (llen == -1) ++ error(1, errno, "reading `%s'", realname); ++ if (line[llen - 1] == '\n') ++ line[--llen] = 0; ++ if (line[llen - 1] == '\r') ++ line[--llen] = 0; ++ linenum++; ++ for (i = 0; config_names[i].name != NULL; i++) { ++ if (config_names[i].nm == CONFIG_NONE) ++ continue; ++ if (strncasecmp(config_names[i].name, line, ++ strlen(config_names[i].name)) == 0) { ++ /* boolean implementation, using harmles pointer targets as true */ ++ if (!config_names[i].needsArgument) { ++ configs[config_names[i].nm] = config_names[i].name; ++ break; ++ } ++ if (configs[config_names[i].nm] == NULL) ++ configs[config_names[i].nm] = ++ strdup(line + strlen(config_names[i].name)); ++ if (configs[config_names[i].nm] == NULL) ++ error(1, errno, "can't allocate memory"); ++ break; ++ } ++ } ++ if (config_names[i].name == NULL && line[0] != '#' && line[0] != 0) ++ error(0, 0, "warning: unknown configuration directive in %s at line %d", ++ realname, linenum); ++ } ++ free(line); ++ free(realname); ++ if (strcmp(name, "-")) ++ fclose(f); ++} ++ ++static void print_desc(const char *pre, const char *text) ++{ ++ const char *p, *q; ++ ++ for (p = text, q = strchr(p, '\n'); q; p = q+1, q = strchr(p, '\n')) ++ printf("%s%.*s\n", pre, (int)(q-p), p); ++ ++ if (*p != '\0') ++ printf("%s%s\n", pre, p); ++} ++ ++static void print_usage(char *argv0, int long_help) ++{ ++ int c; ++ ++ printf("Usage: %s [--version] [--print-config] [--help] [--long-help] [options] [config files]\n\n", ++ argv0); ++ printf("Legend:\n"); ++ for (c = 0; config_names[c].name != NULL; c++) { ++ if (config_names[c].lvl > long_help) ++ continue; ++ ++ printf(" %s %s\n" ++ " %s%s\n", ++ (config_names[c].option == NULL ? "(configfile only option)" : ++ config_names[c].option), ++ ((config_names[c].type == NULL || config_names[c].option == NULL) ? ++ "" : config_names[c].type), ++ config_names[c].name, ++ (config_names[c].type == NULL ? "" : config_names[c].type)); ++ print_desc(" ", config_names[c].desc); ++ ++ if (config_names[c].get_def != NULL) ++ printf(" Default: %s\n", config_names[c].get_def()); ++ ++ printf("\n"); ++ } ++ ++ if (!long_help) ++ printf("Use --long-help to see all options\n\n"); ++ ++ printf("Report bugs to vpnc@unix-ag.uni-kl.de\n"); ++} ++ ++static void print_version(void) ++{ ++ unsigned int i; ++ ++ printf("vpnc version " VERSION "\n"); ++ printf("Copyright (C) 2002-2006 Geoffrey Keating, Maurice Massar, others\n"); ++ printf("vpnc comes with NO WARRANTY, to the extent permitted by law.\n" ++ "You may redistribute copies of vpnc under the terms of the GNU General\n" ++ "Public License. For more information about these matters, see the files\n" ++ "named COPYING.\n"); ++ printf("\n"); ++ ++ printf("Supported DH-Groups:"); ++ for (i = 0; supp_dh_group[i].name != NULL; i++) ++ printf(" %s", supp_dh_group[i].name); ++ printf("\n"); ++ ++ printf("Supported Hash-Methods:"); ++ for (i = 0; supp_hash[i].name != NULL; i++) ++ printf(" %s", supp_hash[i].name); ++ printf("\n"); ++ ++ printf("Supported Encryptions:"); ++ for (i = 0; supp_crypt[i].name != NULL; i++) ++ printf(" %s", supp_crypt[i].name); ++ printf("\n"); ++ ++ printf("Supported Auth-Methods:"); ++ for (i = 0; supp_auth[i].name != NULL; i++) ++ printf(" %s", supp_auth[i].name); ++ printf("\n"); ++} ++ ++void do_config(int argc, char **argv) ++{ ++ char *s; ++ int i, c, known; ++ int got_conffile = 0, print_config = 0; ++ size_t s_len; ++ ++ for (i = 1; i < argc; i++) { ++ if (argv[i][0] && (argv[i][0] != '-' || argv[i][1] == '\0')) { ++ read_config_file(argv[i], config, 0); ++ got_conffile = 1; ++ continue; ++ } ++ ++ known = 0; ++ ++ for (c = 0; config_names[c].name != NULL && !known; c++) { ++ if (config_names[c].option == NULL ++ || config_names[c].nm == CONFIG_NONE ++ || strncmp(argv[i], config_names[c].option, ++ strlen(config_names[c].option)) != 0) ++ continue; ++ ++ s = NULL; ++ ++ known = 1; ++ if (argv[i][strlen(config_names[c].option)] == '=') ++ s = argv[i] + strlen(config_names[c].option) + 1; ++ else if (argv[i][strlen(config_names[c].option)] == 0) { ++ if (config_names[c].needsArgument) { ++ if (i + 1 < argc) ++ s = argv[++i]; ++ else ++ known = 0; ++ } else ++ s = argv[i]; /* no arg, fill in something */ ++ } else ++ known = 0; ++ if (known) ++ config[config_names[c].nm] = s; ++ } ++ ++ if (!known && strcmp(argv[i], "--version") == 0) { ++ print_version(); ++ exit(0); ++ } ++ if (!known && strcmp(argv[i], "--print-config") == 0) { ++ print_config = 1; ++ known = 1; ++ } ++ if (!known && strcmp(argv[i], "--help") == 0) { ++ print_usage(argv[0], 0); ++ exit(0); ++ } ++ if (!known && strcmp(argv[i], "--long-help") == 0) { ++ print_usage(argv[0], 1); ++ exit(0); ++ } ++ if (!known) { ++ printf("%s: unknown option %s\n\n", argv[0], argv[i]); ++ ++ print_usage(argv[0], 1); ++ exit(1); ++ } ++ } ++ ++ if (!got_conffile) { ++ read_config_file("/etc/vpnc/default.conf", config, 1); ++ read_config_file("/etc/vpnc.conf", config, 1); ++ } ++ ++ if (!print_config) { ++ for (i = 0; config_names[i].name != NULL; i++) ++ if (!config[config_names[i].nm] && i != CONFIG_NONE ++ && config_names[i].get_def != NULL) ++ config[config_names[i].nm] = config_names[i].get_def(); ++ ++ opt_debug = (config[CONFIG_DEBUG]) ? atoi(config[CONFIG_DEBUG]) : 0; ++ opt_nd = (config[CONFIG_ND]) ? 1 : 0; ++ opt_1des = (config[CONFIG_ENABLE_1DES]) ? 1 : 0; ++ opt_no_encryption = (config[CONFIG_ENABLE_NO_ENCRYPTION]) ? 1 : 0; ++ opt_udpencapport=atoi(config[CONFIG_UDP_ENCAP_PORT]); ++ ++ if (!strcmp(config[CONFIG_NATT_MODE], "natt")) { ++ opt_natt_mode = NATT_NORMAL; ++ } else if (!strcmp(config[CONFIG_NATT_MODE], "none")) { ++ opt_natt_mode = NATT_NONE; ++ } else if (!strcmp(config[CONFIG_NATT_MODE], "force-natt")) { ++ opt_natt_mode = NATT_FORCE; ++ } else if (!strcmp(config[CONFIG_NATT_MODE], "cisco-udp")) { ++ opt_natt_mode = NATT_CISCO_UDP; ++ } else { ++ printf("%s: unknown nat traversal mode %s\nknown modes: natt none force-natt cisco-udp\n", argv[0], config[CONFIG_NATT_MODE]); ++ exit(1); ++ } ++ ++ if (!strcmp(config[CONFIG_IF_MODE], "tun")) { ++ opt_if_mode = IF_MODE_TUN; ++ } else if (!strcmp(config[CONFIG_IF_MODE], "tap")) { ++ opt_if_mode = IF_MODE_TAP; ++ } else { ++ printf("%s: unknown interface mode %s\nknown modes: tun tap\n", argv[0], config[CONFIG_IF_MODE]); ++ exit(1); ++ } ++ ++ if (!strcmp(config[CONFIG_VENDOR], "cisco")) { ++ opt_vendor = VENDOR_CISCO; ++ } else if (!strcmp(config[CONFIG_VENDOR], "netscreen")) { ++ opt_vendor = VENDOR_NETSCREEN; ++ } else { ++ printf("%s: unknown vendor %s\nknown vendors: cisco netscreen\n", argv[0], config[CONFIG_VENDOR]); ++ exit(1); ++ } ++ } ++ ++ if (opt_debug >= 99) { ++ printf("WARNING! active debug level is >= 99, output includes username and password (hex encoded)\n"); ++ fprintf(stderr, ++ "WARNING! active debug level is >= 99, output includes username and password (hex encoded)\n"); ++ } ++ ++ config_deobfuscate(CONFIG_IPSEC_SECRET_OBF, CONFIG_IPSEC_SECRET); ++ config_deobfuscate(CONFIG_XAUTH_PASSWORD_OBF, CONFIG_XAUTH_PASSWORD); ++ ++ for (i = 0; i < LAST_CONFIG; i++) { ++ if (config[i] != NULL || config[CONFIG_NON_INTERACTIVE] != NULL) ++ continue; ++ if (config[CONFIG_XAUTH_INTERACTIVE] && i == CONFIG_XAUTH_PASSWORD) ++ continue; ++ ++ s = NULL; ++ s_len = 0; ++ ++ switch (i) { ++ case CONFIG_IPSEC_GATEWAY: ++ printf("Enter IPSec gateway address: "); ++ break; ++ case CONFIG_IPSEC_ID: ++ printf("Enter IPSec ID for %s: ", config[CONFIG_IPSEC_GATEWAY]); ++ break; ++ case CONFIG_IPSEC_SECRET: ++ printf("Enter IPSec secret for %s@%s: ", ++ config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_GATEWAY]); ++ break; ++ case CONFIG_XAUTH_USERNAME: ++ printf("Enter username for %s: ", config[CONFIG_IPSEC_GATEWAY]); ++ break; ++ case CONFIG_XAUTH_PASSWORD: ++ printf("Enter password for %s@%s: ", ++ config[CONFIG_XAUTH_USERNAME], ++ config[CONFIG_IPSEC_GATEWAY]); ++ break; ++ } ++ fflush(stdout); ++ switch (i) { ++ case CONFIG_IPSEC_SECRET: ++ case CONFIG_XAUTH_PASSWORD: ++ s = strdup(getpass("")); ++ break; ++ case CONFIG_IPSEC_GATEWAY: ++ case CONFIG_IPSEC_ID: ++ case CONFIG_XAUTH_USERNAME: ++ getline(&s, &s_len, stdin); ++ } ++ if (s != NULL && strlen(s) > 0 && s[strlen(s) - 1] == '\n') ++ s[strlen(s) - 1] = 0; ++ config[i] = s; ++ } ++ ++ if (print_config) { ++ fprintf(stderr, "vpnc.conf:\n\n"); ++ for (i = 0; config_names[i].name != NULL; i++) { ++ if (config[config_names[i].nm] == NULL) ++ continue; ++ printf("%s%s\n", config_names[i].name, ++ config_names[i].needsArgument ? ++ config[config_names[i].nm] : ""); ++ } ++ exit(0); ++ } ++ ++ if (!config[CONFIG_IPSEC_GATEWAY]) ++ error(1, 0, "missing IPSec gatway address"); ++ if (!config[CONFIG_IPSEC_ID]) ++ error(1, 0, "missing IPSec ID"); ++ if (!config[CONFIG_IPSEC_SECRET]) ++ error(1, 0, "missing IPSec secret"); ++ if (!config[CONFIG_XAUTH_USERNAME]) ++ error(1, 0, "missing Xauth username"); ++ if (!config[CONFIG_XAUTH_PASSWORD] && !config[CONFIG_XAUTH_INTERACTIVE]) ++ error(1, 0, "missing Xauth password"); ++ if (get_dh_group_ike() == NULL) ++ error(1, 0, "IKE DH Group \"%s\" unsupported\n", config[CONFIG_IKE_DH]); ++ if (get_dh_group_ipsec(-1) == NULL) ++ error(1, 0, "Perfect Forward Secrecy \"%s\" unsupported\n", ++ config[CONFIG_IPSEC_PFS]); ++ if (get_dh_group_ike()->ike_sa_id == 0) ++ error(1, 0, "IKE DH Group must not be nopfs\n"); ++ ++ return; ++} +diff -urNad vpnc~/config.h vpnc/config.h +--- vpnc~/config.h 2007-05-23 22:47:53.000000000 +0200 ++++ vpnc/config.h 2007-05-23 22:49:05.000000000 +0200 +@@ -36,6 +36,7 @@ + CONFIG_ND, + CONFIG_NON_INTERACTIVE, + CONFIG_PID_FILE, ++ CONFIG_LOCAL_ADDR, + CONFIG_LOCAL_PORT, + CONFIG_VERSION, + CONFIG_IF_NAME, +@@ -53,6 +54,7 @@ + CONFIG_VENDOR, + CONFIG_NATT_MODE, + CONFIG_UDP_ENCAP_PORT, ++ CONFIG_DPD_IDLE, + + + +diff -urNad vpnc~/config.h.orig vpnc/config.h.orig +--- vpnc~/config.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ vpnc/config.h.orig 2007-05-23 22:47:53.000000000 +0200 +@@ -0,0 +1,106 @@ ++/* IPSec VPN client compatible with Cisco equipment. ++ Copyright (C) 2004-2005 Maurice Massar ++ ++ 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 ++ ++ $Id: config.h 117 2007-02-08 02:55:59Z Maurice Massar $ ++*/ ++ ++#ifndef __CONFIG_H__ ++#define __CONFIG_H__ ++ ++#include ++#include ++ ++#include "vpnc-debug.h" ++ ++enum config_enum { ++ CONFIG_NONE, ++ CONFIG_SCRIPT, ++ CONFIG_DEBUG, ++ CONFIG_DOMAIN, ++ CONFIG_ENABLE_1DES, ++ CONFIG_ENABLE_NO_ENCRYPTION, ++ CONFIG_ND, ++ CONFIG_NON_INTERACTIVE, ++ CONFIG_PID_FILE, ++ CONFIG_LOCAL_PORT, ++ CONFIG_VERSION, ++ CONFIG_IF_NAME, ++ CONFIG_IF_MODE, ++ CONFIG_IKE_DH, ++ CONFIG_IPSEC_PFS, ++ CONFIG_IPSEC_GATEWAY, ++ CONFIG_IPSEC_ID, ++ CONFIG_IPSEC_SECRET, ++ CONFIG_IPSEC_SECRET_OBF, ++ CONFIG_XAUTH_USERNAME, ++ CONFIG_XAUTH_PASSWORD, ++ CONFIG_XAUTH_PASSWORD_OBF, ++ CONFIG_XAUTH_INTERACTIVE, ++ CONFIG_VENDOR, ++ CONFIG_NATT_MODE, ++ CONFIG_UDP_ENCAP_PORT, ++ ++ ++ ++ CONFIG_DNS_UPDATE, ++ CONFIG_TARGET_NETWORKS, ++ ++ ++ LAST_CONFIG ++}; ++ ++enum hex_dump_enum { ++ DUMP_UINT8 = -1, ++ DUMP_UINT16 = -2, ++ DUMP_UINT32 = -4 ++}; ++ ++enum vendor_enum { ++ VENDOR_CISCO, ++ VENDOR_NETSCREEN ++}; ++ ++enum natt_mode_enum { ++ NATT_NONE, ++ NATT_NORMAL, ++ NATT_FORCE, ++ NATT_CISCO_UDP ++}; ++ ++enum if_mode_enum { ++ IF_MODE_TUN, ++ IF_MODE_TAP ++}; ++ ++extern const char *config[LAST_CONFIG]; ++ ++extern enum vendor_enum opt_vendor; ++extern int opt_debug; ++extern int opt_nd; ++extern int opt_1des, opt_no_encryption; ++extern enum natt_mode_enum opt_natt_mode; ++extern enum if_mode_enum opt_if_mode; ++extern uint16_t opt_udpencapport; ++ ++#define DEBUG(lvl, a) do {if (opt_debug >= (lvl)) {a;}} while (0) ++ ++extern void hex_dump(const char *str, const void *data, ssize_t len, const struct debug_strings *decode); ++extern void do_config(int argc, char **argv); ++extern int hex2bin(const char *str, char **bin, int *len); ++extern int deobfuscate(char *ct, int len, const char **resp, char *reslenp); ++ ++#endif +diff -urNad vpnc~/config.h.rej vpnc/config.h.rej +--- vpnc~/config.h.rej 1970-01-01 01:00:00.000000000 +0100 ++++ vpnc/config.h.rej 2007-05-23 22:48:16.000000000 +0200 +@@ -0,0 +1,16 @@ ++*************** ++*** 53,58 **** ++ CONFIG_VENDOR, ++ CONFIG_NATT_MODE, ++ CONFIG_UDP_ENCAP_PORT, ++ LAST_CONFIG ++ }; ++ ++--- 54,60 ---- ++ CONFIG_VENDOR, ++ CONFIG_NATT_MODE, ++ CONFIG_UDP_ENCAP_PORT, +++ CONFIG_DPD_IDLE, ++ LAST_CONFIG ++ }; ++ +diff -urNad vpnc~/isakmp.h vpnc/isakmp.h +--- vpnc~/isakmp.h 2007-02-18 04:12:42.000000000 +0100 ++++ vpnc/isakmp.h 2007-05-23 22:48:16.000000000 +0200 +@@ -103,6 +103,8 @@ + ISAKMP_N_IPSEC_RESPONDER_LIFETIME = 24576, + ISAKMP_N_IPSEC_REPLAY_STATUS, + ISAKMP_N_IPSEC_INITIAL_CONTACT, ++ ISAKMP_N_R_U_THERE = 36136, ++ ISAKMP_N_R_U_THERE_ACK, + ISAKMP_N_CISCO_LOAD_BALANCE = 40501 + }; + +@@ -332,34 +334,18 @@ + #define ISAKMP_MESSAGE_ID_O 20 + #define ISAKMP_PAYLOAD_O 28 + +-/* Support for draft-ietf-ipsec-isakmp-xauth-06.txt (yuk). */ +-#define XAUTH_VENDOR_ID { 0x09, 0x00, 0x26, 0x89, 0xDF, 0xD6, 0xB7, 0x12 } +-/* From dead-peer-detection RFC 3706 */ +-#define DPD_VENDOR_ID { 0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9, \ +- 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 0x01, 0x00} +-#define UNITY_VENDOR_ID { 0x12, 0xF5, 0xF2, 0x8C, 0x45, 0x71, 0x68, 0xA9, \ +- 0x70, 0x2D, 0x9F, 0xE2, 0x74, 0xCC, 0x01, 0x00 } +-#define UNKNOWN_VENDOR_ID { 0x12, 0x6E, 0x1F, 0x57, 0x72, 0x91, 0x15, 0x3B, \ +- 0x20, 0x48, 0x5F, 0x7F, 0x15, 0x5B, 0x4B, 0xC8 } +- +-/* draft-ietf-ipsec-nat-t-ike-00 */ +-#define NATT_VENDOR_ID_00 { 0x44, 0x85, 0x15, 0x2d, 0x18, 0xb6, 0xbb, 0xcd, \ +- 0x0b, 0xe8, 0xa8, 0x46, 0x95, 0x79, 0xdd, 0xcc } +-/* draft-ietf-ipsec-nat-t-ike-01 */ +-#define NATT_VENDOR_ID_01 { 0x16, 0xf6, 0xca, 0x16, 0xe4, 0xa4, 0x06, 0x6d, \ +- 0x83, 0x82, 0x1a, 0x0f, 0x0a, 0xea, 0xa8, 0x62 } +-/* draft-ietf-ipsec-nat-t-ike-02 */ +-#define NATT_VENDOR_ID_02 { 0xcd, 0x60, 0x46, 0x43, 0x35, 0xdf, 0x21, 0xf8, \ +- 0x7c, 0xfd, 0xb2, 0xfc, 0x68, 0xb6, 0xa4, 0x48 } +-/* draft-ietf-ipsec-nat-t-ike-02\n */ +-#define NATT_VENDOR_ID_02n { 0x90, 0xCB, 0x80, 0x91, 0x3E, 0xBB, 0x69, 0x6E, \ +- 0x08, 0x63, 0x81, 0xB5, 0xEC, 0x42, 0x7B, 0x1F } +-/* RFC 3947 */ +-#define NATT_VENDOR_ID_RFC { 0x4A, 0x13, 0x1C, 0x81, 0x07, 0x03, 0x58, 0x45, \ +- 0x5C, 0x57, 0x28, 0xF2, 0x0E, 0x95, 0x45, 0x2F } ++/* defined in vpnc.c */ ++extern const unsigned char VID_XAUTH[]; ++extern const unsigned char VID_DPD[]; ++extern const unsigned char VID_UNITY[]; ++extern const unsigned char VID_UNKNOWN[]; ++extern const unsigned char VID_NATT_00[]; ++extern const unsigned char VID_NATT_01[]; ++extern const unsigned char VID_NATT_02[]; ++extern const unsigned char VID_NATT_02N[]; ++extern const unsigned char VID_NATT_RFC[]; + + /* Support for draft-ietf-ipsec-isakmp-mode-cfg-05.txt (yuk). */ +- + enum isakmp_modecfg_cfg_enum { + ISAKMP_MODECFG_CFG_REQUEST = 1, + ISAKMP_MODECFG_CFG_REPLY, +diff -urNad vpnc~/sysdep.c vpnc/sysdep.c +--- vpnc~/sysdep.c 2007-02-19 20:08:36.000000000 +0100 ++++ vpnc/sysdep.c 2007-05-23 22:48:16.000000000 +0200 +@@ -314,9 +314,9 @@ + return -1; + } + +- // +- // Return fd +- // ++ /* ++ * Return fd ++ */ + return cygwin_attach_handle_to_fd(NULL, -1, handle, 1, GENERIC_READ | GENERIC_WRITE); + } + +diff -urNad vpnc~/sysdep.h vpnc/sysdep.h +--- vpnc~/sysdep.h 2007-02-19 20:07:27.000000000 +0100 ++++ vpnc/sysdep.h 2007-05-23 22:48:16.000000000 +0200 +@@ -36,7 +36,7 @@ + int tun_get_hwaddr(int fd, char *dev, uint8_t *hwaddr); + + /***************************************************************************/ +-#if defined(__linux__) ++#if defined(__linux__) || defined(__GLIBC__) + #include + + #define HAVE_VASPRINTF 1 +@@ -72,6 +72,11 @@ + #endif + + /***************************************************************************/ ++#if defined(__FreeBSD_kernel__) ++#define HAVE_SA_LEN 1 ++#endif ++ ++/***************************************************************************/ + #if defined(__FreeBSD__) + #define HAVE_SA_LEN 1 + +diff -urNad vpnc~/tap-win32.h vpnc/tap-win32.h +--- vpnc~/tap-win32.h 2007-02-07 01:32:04.000000000 +0100 ++++ vpnc/tap-win32.h 2007-05-23 22:48:16.000000000 +0200 +@@ -29,20 +29,20 @@ + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +-//=============================================== +-// This file is included both by OpenVPN and +-// the TAP-Win32 driver and contains definitions +-// common to both. +-//=============================================== ++/* =============================================== ++ This file is included both by OpenVPN and ++ the TAP-Win32 driver and contains definitions ++ common to both. ++ =============================================== */ + +-//============= +-// TAP IOCTLs +-//============= ++/* ============= ++ TAP IOCTLs ++ ============= */ + + #define TAP_CONTROL_CODE(request,method) \ + CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) + +-// Present in 8.1 ++/* Present in 8.1 */ + + #define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) + #define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) +@@ -54,32 +54,32 @@ + #define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) + #define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) + +-// Added in 8.2 ++/* Added in 8.2 */ + + /* obsoletes TAP_IOCTL_CONFIG_POINT_TO_POINT */ + #define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE (10, METHOD_BUFFERED) + +-//================= +-// Registry keys +-//================= ++/* ================= ++ Registry keys ++ ================= */ + + #define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + + #define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +-//====================== +-// Filesystem prefixes +-//====================== ++/* ====================== ++ Filesystem prefixes ++ ====================== */ + + #define USERMODEDEVICEDIR "\\\\.\\Global\\" + #define SYSDEVICEDIR "\\Device\\" + #define USERDEVICEDIR "\\DosDevices\\Global\\" + #define TAPSUFFIX ".tap" + +-//========================================================= +-// TAP_COMPONENT_ID -- This string defines the TAP driver +-// type -- different component IDs can reside in the system +-// simultaneously. +-//========================================================= ++/* ========================================================= ++ TAP_COMPONENT_ID -- This string defines the TAP driver ++ type -- different component IDs can reside in the system ++ simultaneously. ++ ========================================================= */ + + #define TAP_COMPONENT_ID "tap0801" +diff -urNad vpnc~/tunip.c vpnc/tunip.c +--- vpnc~/tunip.c 2007-02-19 21:49:51.000000000 +0100 ++++ vpnc/tunip.c 2007-05-23 22:48:16.000000000 +0200 +@@ -118,7 +118,8 @@ + #define MAX_HEADER 72 + #define MAX_PACKET 4096 + int volatile do_kill; +-static uint8_t global_buffer[MAX_HEADER + MAX_PACKET + ETH_HLEN]; ++static uint8_t global_buffer_rx[MAX_HEADER + MAX_PACKET + ETH_HLEN]; ++static uint8_t global_buffer_tx[MAX_HEADER + MAX_PACKET + ETH_HLEN]; + + /* + * in_cksum -- +@@ -593,14 +594,6 @@ + encap->fixed_header_size = sizeof(esp_encap_header_t); + } + +-#ifdef __CYGWIN__ +-/* +- * TODO: use libgcrypt init to make it thread-safe +- * instead of this mutex +- */ +-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +-#endif +- + /* + * Process ARP + * Return 1 if packet has been processed, 0 otherwise +@@ -677,7 +670,7 @@ + { + int pack; + int size = MAX_PACKET; +- uint8_t *start = global_buffer + MAX_HEADER; ++ uint8_t *start = global_buffer_rx + MAX_HEADER; + + if (opt_if_mode == IF_MODE_TAP) { + /* Make sure IP packet starts at buf + MAX_HEADER */ +@@ -688,10 +681,6 @@ + /* Receive a packet from the tunnel interface */ + pack = tun_read(s->tun_fd, start, size); + +-#if defined(__CYGWIN__) +- pthread_mutex_lock(&mutex); +-#endif +- + hex_dump("Rx pkt", start, pack, NULL); + + if (opt_if_mode == IF_MODE_TAP) { +@@ -709,7 +698,10 @@ + return; + } + +- if (((struct ip *)(global_buffer + MAX_HEADER))->ip_dst.s_addr == s->dst.s_addr) { ++ /* Don't access the contents of the buffer other than byte aligned. ++ * 12: Offset of ip source address in ip header, ++ * 4: Length of IP address */ ++ if (!memcmp(global_buffer_rx + MAX_HEADER + 12, &s->dst.s_addr, 4)) { + syslog(LOG_ALERT, "routing loop to %s", + inet_ntoa(s->dst)); + return; +@@ -717,21 +709,16 @@ + + /* Encapsulate and send to the other end of the tunnel */ + s->ipsec.life.tx += pack; +- s->ipsec.em->send_peer(s, global_buffer, pack); ++ s->ipsec.em->send_peer(s, global_buffer_rx, pack); + } + + static void process_socket(struct sa_block *s) + { + /* Receive a packet from a socket */ + int pack; +- uint8_t *start = global_buffer; ++ uint8_t *start = global_buffer_tx; + esp_encap_header_t *eh; + +- +-#if defined(__CYGWIN__) +- pthread_mutex_lock(&mutex); +-#endif +- + if (opt_if_mode == IF_MODE_TAP) { + start += ETH_HLEN; + } +@@ -770,7 +757,6 @@ + + while (!do_kill) { + process_tun(s); +- pthread_mutex_unlock(&mutex); + } + return NULL; + } +@@ -781,17 +767,20 @@ + fd_set rfds, refds; + int nfds=0; + int enable_keepalives; ++ int timed_mode; + ssize_t len; +- struct timeval select_timeout = { .tv_sec = 9, .tv_usec = 500000 }; ++ struct timeval select_timeout; ++ struct timeval normal_timeout; + time_t next_ike_keepalive=0; ++ time_t next_ike_dpd=0; + #if defined(__CYGWIN__) + pthread_t tid; + #endif + + /* non-esp marker, nat keepalive payload (0xFF) */ +- char keepalive_v2[5] = { 0x00, 0x00, 0x00, 0x00, 0xFF }; +- char keepalive_v1[1] = { 0xFF }; +- char *keepalive; ++ uint8_t keepalive_v2[5] = { 0x00, 0x00, 0x00, 0x00, 0xFF }; ++ uint8_t keepalive_v1[1] = { 0xFF }; ++ uint8_t *keepalive; + size_t keepalive_size; + + if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) { +@@ -805,6 +794,9 @@ + /* send keepalives if UDP encapsulation is enabled */ + enable_keepalives = (s->ipsec.encap_mode != IPSEC_ENCAP_TUNNEL); + ++ /* regular wakeups if keepalives on ike or dpd active */ ++ timed_mode = ((enable_keepalives && s->ike_fd != s->esp_fd) || s->ike.do_dpd); ++ + FD_ZERO(&rfds); + + #if !defined(__CYGWIN__) +@@ -827,34 +819,64 @@ + } + #endif + ++ normal_timeout.tv_sec = 86400; ++ normal_timeout.tv_usec = 0; ++ ++ if (s->ike.do_dpd) { ++ /* send initial dpd request */ ++ next_ike_dpd = time(NULL) + s->ike.dpd_idle; ++ dpd_ike(s); ++ normal_timeout.tv_sec = s->ike.dpd_idle; ++ normal_timeout.tv_usec = 0; ++ } ++ + if (enable_keepalives && s->ike_fd != s->esp_fd) { + /* send initial nat ike keepalive packet */ + next_ike_keepalive = time(NULL) + 9; + keepalive_ike(s); ++ normal_timeout.tv_sec = 9; ++ normal_timeout.tv_usec = 500000; + } +- ++ ++ select_timeout = normal_timeout; ++ + while (!do_kill) { + int presult; + + do { + struct timeval *tvp = NULL; + FD_COPY(&rfds, &refds); +- if (enable_keepalives) ++ if (s->ike.do_dpd || enable_keepalives) + tvp = &select_timeout; + presult = select(nfds, &refds, NULL, NULL, tvp); +- if (presult == 0 && enable_keepalives) { +- if (s->ike_fd != s->esp_fd) { +- /* send nat ike keepalive packet */ +- next_ike_keepalive = time(NULL) + 9; +- keepalive_ike(s); ++ if (presult == 0 && (s->ike.do_dpd || enable_keepalives)) { ++ /* reset to max timeout */ ++ select_timeout = normal_timeout; ++ if (enable_keepalives) { ++ if (s->ike_fd != s->esp_fd) { ++ /* send nat ike keepalive packet */ ++ next_ike_keepalive = time(NULL) + 9; ++ keepalive_ike(s); ++ } ++ /* send nat keepalive packet */ ++ if (send(s->esp_fd, keepalive, keepalive_size, 0) == -1) { ++ syslog(LOG_ERR, "sendto: %m"); ++ } + } +- /* send nat keepalive packet */ +- if (send(s->esp_fd, keepalive, keepalive_size, 0) == -1) { +- syslog(LOG_ERR, "sendto: %m"); ++ if (s->ike.do_dpd) { ++ time_t now = time(NULL); ++ if (s->ike.dpd_seqno != s->ike.dpd_seqno_ack) { ++ /* Wake up more often for dpd attempts */ ++ select_timeout.tv_sec = 5; ++ select_timeout.tv_usec = 0; ++ dpd_ike(s); ++ next_ike_dpd = now + s->ike.dpd_idle; ++ } ++ else if (now >= next_ike_dpd) { ++ dpd_ike(s); ++ next_ike_dpd = now + s->ike.dpd_idle; ++ } + } +- /* reset to max timeout */ +- select_timeout.tv_sec = 9; +- select_timeout.tv_usec = 500000; + } + DEBUG(2,printf("lifetime status: %ld of %u seconds used, %u|%u of %u kbytes used\n", + time(NULL) - s->ipsec.life.start, +@@ -876,47 +898,60 @@ + + if (FD_ISSET(s->esp_fd, &refds) ) { + process_socket(s); +-#if defined(__CYGWIN__) +- pthread_mutex_unlock(&mutex); +-#endif + } + + if (s->ike_fd != s->esp_fd && FD_ISSET(s->ike_fd, &refds) ) { + DEBUG(3,printf("received something on ike fd..\n")); +-#if defined(__CYGWIN__) +- pthread_mutex_lock(&mutex); +-#endif +- len = recv(s->ike_fd, global_buffer, MAX_HEADER + MAX_PACKET, 0); +- process_late_ike(s, global_buffer, len); +-#if defined(__CYGWIN__) +- pthread_mutex_unlock(&mutex); +-#endif ++ len = recv(s->ike_fd, global_buffer_tx, MAX_HEADER + MAX_PACKET, 0); ++ process_late_ike(s, global_buffer_tx, len); + } + +- if (enable_keepalives && s->ike_fd != s->esp_fd) { +- time_t cur_time = time(NULL); +- if (cur_time >= next_ike_keepalive) { +- /* send nat ike keepalive packet now */ +- next_ike_keepalive = cur_time + 9; +- keepalive_ike(s); +- /* reset to max timeout */ +- select_timeout.tv_sec = 9; +- select_timeout.tv_usec = 500000; ++ if (timed_mode) { ++ time_t now = time(NULL); ++ time_t next_up = now + 86400; ++ if (enable_keepalives && s->ike_fd != s->esp_fd) { ++ if (now >= next_ike_keepalive) { ++ /* send nat ike keepalive packet now */ ++ next_ike_keepalive = now + 9; ++ keepalive_ike(s); ++ select_timeout = normal_timeout; ++ } ++ if (next_ike_keepalive < next_up) ++ next_up = next_ike_keepalive; + } +- else { +- /* Reduce timeout so next ike keepalive goes on schedule */ +- select_timeout.tv_sec = next_ike_keepalive - cur_time; +- select_timeout.tv_usec = 0; ++ if (s->ike.do_dpd) { ++ if (s->ike.dpd_seqno != s->ike.dpd_seqno_ack) { ++ dpd_ike(s); ++ next_ike_dpd = now + s->ike.dpd_idle; ++ if (now + 5 < next_up) ++ next_up = now + 5; ++ } ++ else if (now >= next_ike_dpd) { ++ dpd_ike(s); ++ next_ike_dpd = now + s->ike.dpd_idle; ++ } ++ if (next_ike_dpd < next_up) ++ next_up = next_ike_dpd; + } ++ /* Reduce timeout so next activity happens on schedule */ ++ select_timeout.tv_sec = next_up - now; ++ select_timeout.tv_usec = 0; + } + + } + + tun_close(s->tun_fd, s->tun_name); +- if (do_kill == -1) +- syslog(LOG_NOTICE, "connection terminated by peer"); +- else +- syslog(LOG_NOTICE, "terminated by signal: %d", do_kill); ++ switch (do_kill) { ++ case -2: ++ syslog(LOG_NOTICE, "connection terminated by dead peer detection"); ++ break; ++ case -1: ++ syslog(LOG_NOTICE, "connection terminated by peer"); ++ break; ++ default: ++ syslog(LOG_NOTICE, "terminated by signal: %d", do_kill); ++ break; ++ } + } + + static void killit(int signum) +diff -urNad vpnc~/tunip.h vpnc/tunip.h +--- vpnc~/tunip.h 2007-02-16 10:49:31.000000000 +0100 ++++ vpnc/tunip.h 2007-05-23 22:48:16.000000000 +0200 +@@ -23,6 +23,7 @@ + + #include "isakmp.h" + ++#include + #include + + struct lifetime { +@@ -74,6 +75,8 @@ + int esp_fd; /* raw socket for ip-esp or Cisco-UDP or ike_fd (NAT-T) */ + + struct { ++ int timeout; ++ uint8_t *resend_hash; + uint16_t src_port, dst_port; + uint8_t i_cookie[ISAKMP_COOKIE_LENGTH]; + uint8_t r_cookie[ISAKMP_COOKIE_LENGTH]; +@@ -88,6 +91,12 @@ + uint8_t current_iv_msgid[4]; + uint8_t *current_iv; + struct lifetime life; ++ int do_dpd; ++ int dpd_idle; ++ uint32_t dpd_seqno; ++ uint32_t dpd_seqno_ack; ++ time_t dpd_sent; ++ unsigned int dpd_attempts; + } ike; + uint8_t our_address[4], our_netmask[4]; + struct { +diff -urNad vpnc~/vpnc-script vpnc/vpnc-script +--- vpnc~/vpnc-script 2007-05-23 22:47:53.000000000 +0200 ++++ vpnc/vpnc-script 2007-05-23 22:48:16.000000000 +0200 +@@ -96,6 +96,10 @@ + if [ ! -e /dev/tun ]; then + kldload if_tun + fi ++ elif [ "$OS" = "GNU/kFreeBSD" ]; then ++ if [ ! -e /dev/tun ]; then ++ kldload if_tun ++ fi + elif [ "$OS" = "NetBSD" ]; then + : + elif [ "$OS" = "OpenBSD" ]; then +@@ -238,9 +242,11 @@ + # and will be overwritten by vpnc + # as long as the above mark is intact" + ++ # Remember the original value of CISCO_DEF_DOMAIN we need it later ++ CISCO_DEF_DOMAIN_ORIG="$CISCO_DEF_DOMAIN" + # Don't step on INTERNAL_IP4_DNS value, use a temporary variable + INTERNAL_IP4_DNS_TEMP="$INTERNAL_IP4_DNS" +- exec 6< /etc/resolv.conf ++ exec 6< "$RESOLV_CONF_BACKUP" + while read LINE <&6 ; do + case "$LINE" in + nameserver*) +@@ -282,45 +288,47 @@ + ;; + # 10.4 and later require use of scutil for DNS to work properly + *) ++ OVERRIDE_PRIMARY="" ++ if [ -n "$CISCO_SPLIT_INC" ]; then ++ if [ $CISCO_SPLIT_INC -lt 1 ]; then ++ # Must override for correct default route ++ # Cannot use multiple DNS matching in this case ++ OVERRIDE_PRIMARY='d.add OverridePrimary # 1' ++ fi ++ fi ++ # Uncomment the following if/fi pair to use multiple ++ # DNS matching when available. When multiple DNS matching ++ # is present, anything reading the /etc/resolv.conf file ++ # directly will probably not work as intended. ++ #if [ -z "$CISCO_DEF_DOMAIN_ORIG" ]; then ++ # Cannot use multiple DNS matching without a domain ++ OVERRIDE_PRIMARY='d.add OverridePrimary # 1' ++ #fi + scutil >/dev/null 2>&1 <<-EOF + open + d.init + d.add ServerAddresses * $INTERNAL_IP4_DNS + set State:/Network/Service/$TUNDEV/DNS +- get State:/Network/Global/IPv4 +- d.remove PrimaryService +- d.remove PrimaryInterface ++ d.init ++ d.add Router $INTERNAL_IP4_ADDRESS + d.add Addresses * $INTERNAL_IP4_ADDRESS + d.add SubnetMasks * 255.255.255.255 + d.add InterfaceName $TUNDEV ++ $OVERRIDE_PRIMARY + set State:/Network/Service/$TUNDEV/IPv4 + close + EOF +- if [ -n "$CISCO_DEF_DOMAIN" ]; then ++ if [ -n "$CISCO_DEF_DOMAIN_ORIG" ]; then + scutil >/dev/null 2>&1 <<-EOF + open + get State:/Network/Service/$TUNDEV/DNS +- d.add DomainName $CISCO_DEF_DOMAIN +- d.add SearchDomains * $CISCO_DEF_DOMAIN +- d.add SupplementalMatchDomains * $CISCO_DEF_DOMAIN ++ d.add DomainName $CISCO_DEF_DOMAIN_ORIG ++ d.add SearchDomains * $CISCO_DEF_DOMAIN_ORIG ++ d.add SupplementalMatchDomains * $CISCO_DEF_DOMAIN_ORIG + set State:/Network/Service/$TUNDEV/DNS + close + EOF + fi +- # Uncomment the following if/fi pair to use multiple +- # DNS matching when available. When multiple DNS matching +- # is present, anything reading the /etc/resolv.conf file +- # directly will probably not work as intended. +- #if [ -z "$CISCO_DEF_DOMAIN" ]; then +- # Cannot use multiple DNS matching without a domain +- scutil >/dev/null 2>&1 <<-EOF +- open +- get State:/Network/Service/$TUNDEV/IPv4 +- d.add OverridePrimary # 1 +- set State:/Network/Service/$TUNDEV/IPv4 +- close +- EOF +- #fi + ;; + esac + fi +diff -urNad vpnc~/vpnc-script-win vpnc/vpnc-script-win +--- vpnc~/vpnc-script-win 2007-02-07 01:32:04.000000000 +0100 ++++ vpnc/vpnc-script-win 2007-05-23 22:48:16.000000000 +0200 +@@ -1,91 +1,2 @@ +-#!/bin/sh +-#* reason -- why this script was called, one of: pre-init connect disconnect +-#* VPNGATEWAY -- vpn gateway address (always present) +-#* TUNDEV -- tunnel device (always present) +-#* INTERNAL_IP4_ADDRESS -- address (always present) +-#* INTERNAL_IP4_NETMASK -- netmask (often unset) +-#* INTERNAL_IP4_DNS -- list of dns serverss +-#* INTERNAL_IP4_NBNS -- list of wins servers +-#* CISCO_DEF_DOMAIN -- default domain name +-#* CISCO_BANNER -- banner from server +-#* CISCO_SPLIT_INC -- number of networks in split-network-list +-#* CISCO_SPLIT_INC_%d_ADDR -- network address +-#* CISCO_SPLIT_INC_%d_MASK -- subnet mask (for example: 255.255.255.0) +-#* CISCO_SPLIT_INC_%d_MASKLEN -- subnet masklen (for example: 24) +-#* CISCO_SPLIT_INC_%d_PROTOCOL -- protocol (often just 0) +-#* CISCO_SPLIT_INC_%d_SPORT -- source port (often just 0) +-#* CISCO_SPLIT_INC_%d_DPORT -- destination port (often just 0) +- +-if [ ! -n "$INTERNAL_IP4_NETMASK" ]; then +- INTERNAL_IP4_NETMASK="255.255.255.0" +-fi +- +-DEFAULTROUTE=`route print 0.0.0.0 mask 0.0.0.0 | grep 0\.0\.0\.0 | tr -s ' ' | cut -f 5 -d ' '` +- +-case "$reason" in +- pre-init) +- ;; +- connect) +- echo VPN Gateway: $VPNGATEWAY +- echo Internal Address: $INTERNAL_IP4_ADDRESS +- echo Internal Netmask: $INTERNAL_IP4_NETMASK +- echo Interface: \"$TUNDEV\" +- +- # Interface IP configuration +- echo -n Configuring "$TUNDEV" interface... +- netsh interface ip set address "$TUNDEV" static \ +- $INTERNAL_IP4_ADDRESS 255.255.255.0 >/dev/null +- +- # Interface WINS configuration +- if [ -n "$INTERNAL_IP4_NBNS" ]; then +- WINS1=`echo $INTERNAL_IP4_NBNS | cut -f 1 -d ' '` +- WINS2=`echo $INTERNAL_IP4_NBNS | cut -f 2 -d ' '` +- netsh interface ip add wins "$TUNDEV" $WINS1 >/dev/null +- netsh interface ip add wins "$TUNDEV" $WINS2 index=2 >/dev/null +- fi +- +- # Interface DNS configuration +- if [ -n "$INTERNAL_IP4_DNS" ]; then +- DNS1=`echo $INTERNAL_IP4_DNS | cut -f 1 -d ' '` +- DNS2=`echo $INTERNAL_IP4_DNS | cut -f 2 -d ' '` +- netsh interface ip add dns "$TUNDEV" $DNS1 >/dev/null +- netsh interface ip add dns "$TUNDEV" $DNS2 index=2 >/dev/null +- fi +- +- echo done. +- +- # Add direct route for the VPN gateway to avoid routing loops +- route add $VPNGATEWAY mask 255.255.255.255 $DEFAULTROUTE +- +- # Add internal network routes +- echo "Configuring networks:" +- if [ -n "$CISCO_SPLIT_INC" ]; then +- for ((i = 0 ; i < CISCO_SPLIT_INC ; i++ )) ; do +- eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}" +- eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}" +- eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}" +- route add $NETWORK mask $NETMASK $INTERNAL_IP4_ADDRESS +- done +- else +- echo Gateway did not provide network configuration. +- fi +- echo Route configuration done. +- +- if [ -n "$CISCO_BANNER" ]; then +- echo -------------------------------------------------- +- echo "$CISCO_BANNER" +- echo -------------------------------------------------- +- fi +- ;; +- disconnect) +- # Delete direct route for the VPN gateway to avoid +- route delete $VPNGATEWAY mask 255.255.255.255 +- ;; +- *) +- echo "unknown reason '$reason'. +- echo Maybe vpnc-script-win is out of date" 1>&2 +- exit 1 +- ;; +-esac +- +-exit 0 ++#! /bin/sh ++cscript `cygpath -w /etc/vpnc/vpnc-script-win.js` +diff -urNad vpnc~/vpnc-script-win.js vpnc/vpnc-script-win.js +--- vpnc~/vpnc-script-win.js 2007-05-23 22:37:16.000000000 +0200 ++++ vpnc/vpnc-script-win.js 2007-05-23 22:48:16.000000000 +0200 +@@ -96,3 +96,101 @@ + run("route delete " + env("VPNGATEWAY") + " mask 255.255.255.255"); + } + ++// vpnc-script-win.js ++// ++// Sets up the Network interface and the routes ++// needed by vpnc. ++ ++// -------------------------------------------------------------- ++// Utilities ++// -------------------------------------------------------------- ++ ++function echo(msg) ++{ ++ WScript.echo(msg); ++} ++ ++function run(cmd) ++{ ++ return (ws.Exec(cmd).StdOut.ReadAll()); ++} ++ ++function getDefaultGateway() ++{ ++ if (run("route print").match(/Default Gateway: *(.*)/)) { ++ return (RegExp.$1); ++ } ++ return (""); ++} ++ ++// -------------------------------------------------------------- ++// Script starts here ++// -------------------------------------------------------------- ++ ++var ws = WScript.CreateObject("WScript.Shell"); ++var env = ws.Environment("Process"); ++ ++switch (env("reason")) { ++case "pre-init": ++ break; ++case "connect": ++ var gw = getDefaultGateway(); ++ echo("VPN Gateway: " + env("VPNGATEWAY")); ++ echo("Internal Address: " + env("INTERNAL_IP4_ADDRESS")); ++ echo("Internal Netmask: " + env("INTERNAL_IP4_NETMASK")); ++ echo("Interface: \"" + env("TUNDEV") + "\""); ++ ++ echo("Configuring \"" + env("TUNDEV") + "\" interface..."); ++ run("netsh interface ip set address " + env("TUNDEV") + " static " + ++ env("INTERNAL_IP4_ADDRESS") + " 255.255.255.0"); ++ ++ // Add direct route for the VPN gateway to avoid routing loops ++ run("route add " + env("VPNGATEWAY") + ++ " mask 255.255.255.255 " + gw); ++ ++ if (env("INTERNAL_IP4_NBNS")) { ++ var wins = env("INTERNAL_IP4_NBNS").split(/ /); ++ for (var i = 0; i < wins.length; i++) { ++ run("netsh interface ip add wins \"" + ++ env("TUNDEV") + "\" " + wins[i] ++ + " index=" + (i+1)); ++ } ++ } ++ ++ if (env("INTERNAL_IP4_DNS")) { ++ var dns = env("INTERNAL_IP4_DNS").split(/ /); ++ for (var i = 0; i < dns.length; i++) { ++ run("netsh interface ip add dns \"" + ++ env("TUNDEV") + "\" " + dns[i] ++ + " index=" + (i+1)); ++ } ++ } ++ echo("done."); ++ ++ // Add internal network routes ++ echo("Configuring networks:"); ++ if (env("CISCO_SPLIT_INC")) { ++ for (var i = 0 ; i < parseInt(env("CISCO_SPLIT_INC")); i++) { ++ var network = env("CISCO_SPLIT_INC_" + i + "_ADDR"); ++ var netmask = env("CISCO_SPLIT_INC_" + i + "_MASK"); ++ var netmasklen = env("CISCO_SPLIT_INC_" + i + ++ "_MASKLEN"); ++ run("route add " + network + " mask " + netmask + ++ + " " + env("INTERNAL_IP4_ADDRESS")); ++ } ++ } else { ++ echo("Gateway did not provide network configuration."); ++ } ++ echo("Route configuration done."); ++ ++ if (env("CISCO_BANNER")) { ++ echo("--------------------------------------------------"); ++ echo(env("CISCO_BANNER")); ++ echo("--------------------------------------------------"); ++ } ++ break; ++case "disconnect": ++ // Delete direct route for the VPN gateway to avoid ++ run("route delete " + env("VPNGATEWAY") + " mask 255.255.255.255"); ++} ++ +diff -urNad vpnc~/vpnc-script.orig vpnc/vpnc-script.orig +--- vpnc~/vpnc-script.orig 1970-01-01 01:00:00.000000000 +0100 ++++ vpnc/vpnc-script.orig 2007-05-23 22:47:53.000000000 +0200 +@@ -0,0 +1,483 @@ ++#!/bin/sh ++#* reason -- why this script was called, one of: pre-init connect disconnect ++#* VPNGATEWAY -- vpn gateway address (always present) ++#* TUNDEV -- tunnel device (always present) ++#* INTERNAL_IP4_ADDRESS -- address (always present) ++#* INTERNAL_IP4_NETMASK -- netmask (often unset) ++#* INTERNAL_IP4_DNS -- list of dns serverss ++#* INTERNAL_IP4_NBNS -- list of wins servers ++#* CISCO_DEF_DOMAIN -- default domain name ++#* CISCO_BANNER -- banner from server ++#* CISCO_SPLIT_INC -- number of networks in split-network-list ++#* CISCO_SPLIT_INC_%d_ADDR -- network address ++#* CISCO_SPLIT_INC_%d_MASK -- subnet mask (for example: 255.255.255.0) ++#* CISCO_SPLIT_INC_%d_MASKLEN -- subnet masklen (for example: 24) ++#* CISCO_SPLIT_INC_%d_PROTOCOL -- protocol (often just 0) ++#* CISCO_SPLIT_INC_%d_SPORT -- source port (often just 0) ++#* CISCO_SPLIT_INC_%d_DPORT -- destination port (often just 0) ++ ++#set -x ++ ++OS="`uname -s`" ++ ++VPNC_WIN_SCRIPT=/etc/vpnc/vpnc-script-win ++ ++case "$OS" in ++ CYGWIN_NT*) ++ # why does "--kernel-name" include "--kernel-release" on cygwin?! ++ exec $VPNC_WIN_SCRIPT ++ ;; ++ *) ++ ;; ++esac ++ ++DEFAULT_ROUTE_FILE=/var/run/vpnc/defaultroute ++RESOLV_CONF_BACKUP=/var/run/vpnc/resolv.conf-backup ++ ++# some systems, eg. Darwin & FreeBSD, prune /var/run on boot ++if ! [ -d "/var/run/vpnc" ]; then ++ mkdir -p /var/run/vpnc ++fi ++ ++# stupid SunOS: no blubber in /usr/local/bin ... (on stdout) ++IPROUTE="`which ip | grep '^/' 2> /dev/null`" ++ ++if [ "$OS" = "Linux" ]; then ++ ifconfig_syntax_ptp="pointopoint" ++ route_syntax_gw="gw" ++ route_syntax_del="del" ++ route_syntax_netmask="netmask" ++else ++ ifconfig_syntax_ptp="" ++ route_syntax_gw="" ++ route_syntax_del="delete" ++ route_syntax_netmask="-netmask" ++fi ++ ++#### ++ ++kernel_is_2_6_or_above() { ++ case `uname -r` in ++ 1.*|2.[012345]*) ++ return 1 ++ ;; ++ *) ++ return 0 ++ ;; ++ esac ++} ++ ++do_pre_init() { ++ if [ "$OS" = "Linux" ]; then ++ if (exec 6<> /dev/net/tun) > /dev/null 2>&1 ; then ++ : ++ else # can't open /dev/net/tun ++ test -e /proc/sys/kernel/modprobe && `cat /proc/sys/kernel/modprobe` tun 2>/dev/null ++ # fix for broken devfs in kernel 2.6.x ++ if [ "`readlink /dev/net/tun`" = misc/net/tun ] && \ ++ [ ! -e /dev/net/misc/net/tun ] && \ ++ [ -e /dev/misc/net/tun ] ; then ++ ln -sf /dev/misc/net/tun /dev/net/tun ++ fi ++ # make sure tun device exists ++ if [ ! -e /dev/net/tun ]; then ++ mkdir -p /dev/net ++ mknod -m 0640 /dev/net/tun c 10 200 ++ fi ++ # workaround for a possible latency caused by udev, sleep max. 10s ++ if kernel_is_2_6_or_above ; then ++ for x in `seq 100` ; do ++ (exec 6<> /dev/net/tun) > /dev/null 2>&1 && break; ++ sleep 0.1 ++ done ++ fi ++ fi ++ elif [ "$OS" = "FreeBSD" ]; then ++ if [ ! -e /dev/tun ]; then ++ kldload if_tun ++ fi ++ elif [ "$OS" = "NetBSD" ]; then ++ : ++ elif [ "$OS" = "OpenBSD" ]; then ++ : ++ elif [ "$OS" = "SunOS" ]; then ++ : ++ elif [ "$OS" = "Darwin" ]; then ++ : ++ fi ++} ++ ++do_ifconfig() { ++ ifconfig "$TUNDEV" inet "$INTERNAL_IP4_ADDRESS" $ifconfig_syntax_ptp "$INTERNAL_IP4_ADDRESS" netmask 255.255.255.255 mtu 1390 up ++} ++ ++destroy_tun_device() { ++ case "$OS" in ++ NetBSD) # and probably others... ++ ifconfig "$TUNDEV" destroy ++ ;; ++ esac ++} ++ ++if [ -n "$IPROUTE" ]; then ++ fix_ip_get_output () { ++ sed 's/cache//;s/metric \?[0-9]\+ [0-9]\+//g;s/hoplimit [0-9]\+//g' ++ } ++ ++ set_vpngateway_route() { ++ $IPROUTE route add `$IPROUTE route get "$VPNGATEWAY" | fix_ip_get_output` ++ $IPROUTE route flush cache ++ } ++ ++ del_vpngateway_route() { ++ $IPROUTE route $route_syntax_del "$VPNGATEWAY" ++ $IPROUTE route flush cache ++ } ++ ++ set_default_route() { ++ $IPROUTE route | grep '^default' | fix_ip_get_output > "$DEFAULT_ROUTE_FILE" ++ $IPROUTE route $route_syntax_del default ++ $IPROUTE route add default dev "$TUNDEV" ++ $IPROUTE route flush cache ++ } ++ ++ set_network_route() { ++ NETWORK="$1" ++ NETMASK="$2" ++ NETMASKLEN="$3" ++ $IPROUTE route add "$NETWORK/$NETMASKLEN" dev "$TUNDEV" ++ $IPROUTE route flush cache ++ } ++ ++ reset_default_route() { ++ $IPROUTE route $route_syntax_del default > /dev/null 2>&1 ++ $IPROUTE route add `cat "$DEFAULT_ROUTE_FILE"` ++ $IPROUTE route flush cache ++ } ++ ++ del_network_route() { ++ # linux deletes routes automatically if the device is shut down ++ return ++ #NETWORK="$1" ++ #NETMASK="$2" ++ #NETMASKLEN="$3" ++ #$IPROUTE route $route_syntax_del "$NETWORK/$NETMASKLEN" dev "$TUNDEV" ++ #$IPROUTE route flush cache ++ } ++else ++ get_default_gw() { ++ # isn't -n supposed to give --numeric output? ++ # apperently not... ++ netstat -r -n | sed 's/default/0.0.0.0/' | grep '^0.0.0.0' | awk '{print $2}' ++ } ++ ++ set_vpngateway_route() { ++ route add -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`" ++ } ++ ++ del_vpngateway_route() { ++ route $route_syntax_del -host "$VPNGATEWAY" ++ } ++ ++ set_default_route() { ++ DEFAULTGW="`get_default_gw`" ++ echo "$DEFAULTGW" > "$DEFAULT_ROUTE_FILE" ++ route $route_syntax_del default "$DEFAULTGW" ++ route add default $route_syntax_gw "$INTERNAL_IP4_ADDRESS" ++ } ++ ++ set_network_route() { ++ NETWORK="$1" ++ NETMASK="$2" ++ NETMASKLEN="$3" ++ route add -net "$NETWORK" $route_syntax_netmask "$NETMASK" $route_syntax_gw "$INTERNAL_IP4_ADDRESS" ++ } ++ ++ reset_default_route() { ++ route $route_syntax_del default ++ route add default $route_syntax_gw `cat "$DEFAULT_ROUTE_FILE"` ++ } ++ ++ del_network_route() { ++ case "$OS" in ++ Linux|NetBSD) # and probably others... ++ # routes are deleted automatically on device shutdown ++ return ++ ;; ++ esac ++ NETWORK="$1" ++ NETMASK="$2" ++ NETMASKLEN="$3" ++ route $route_syntax_del -net "$NETWORK" $route_syntax_netmask "$NETMASK" $route_syntax_gw "$INTERNAL_IP4_ADDRESS" ++ } ++fi ++ ++write_resolvconf() { ++ ++ case "$DNS_UPDATE" in ++ *no|*NO|*No|*nO) ++ return; ++ ;; ++ esac ++ ++ if [ -x /sbin/resolvconf ] ; then ++ ( ++ if [ "$CISCO_DEF_DOMAIN" ] ; then ++ echo domain "$CISCO_DEF_DOMAIN" ++ echo search "$CISCO_DEF_DOMAIN" ++ fi ++ for ip in "$INTERNAL_IP4_DNS" ; do ++ echo nameserver $ip ++ done ++ ) | /sbin/resolvconf -a $TUNDEV ++ return ++ fi ++ ++ grep '^#@VPNC_GENERATED@' /etc/resolv.conf > /dev/null 2>&1 || cp -- /etc/resolv.conf "$RESOLV_CONF_BACKUP" ++ NEW_RESOLVCONF="#@VPNC_GENERATED@ -- this file is generated by vpnc ++# and will be overwritten by vpnc ++# as long as the above mark is intact" ++ ++ # Don't step on INTERNAL_IP4_DNS value, use a temporary variable ++ INTERNAL_IP4_DNS_TEMP="$INTERNAL_IP4_DNS" ++ exec 6< /etc/resolv.conf ++ while read LINE <&6 ; do ++ case "$LINE" in ++ nameserver*) ++ if [ -n "$INTERNAL_IP4_DNS_TEMP" ]; then ++ read ONE_NAMESERVER INTERNAL_IP4_DNS_TEMP <<-EOF ++ $INTERNAL_IP4_DNS_TEMP ++EOF ++ LINE="nameserver $ONE_NAMESERVER" ++ else ++ LINE="" ++ fi ++ ;; ++ domain*|search*) ++ if [ -n "$CISCO_DEF_DOMAIN" ]; then ++ LINE="$LINE $CISCO_DEF_DOMAIN" ++ CISCO_DEF_DOMAIN="" ++ fi ++ ;; ++ esac ++ NEW_RESOLVCONF="$NEW_RESOLVCONF ++$LINE" ++ done ++ exec 6<&- ++ ++ for i in $INTERNAL_IP4_DNS_TEMP ; do ++ NEW_RESOLVCONF="$NEW_RESOLVCONF ++nameserver $i" ++ done ++ if [ -n "$CISCO_DEF_DOMAIN" ]; then ++ NEW_RESOLVCONF="$NEW_RESOLVCONF ++search $CISCO_DEF_DOMAIN" ++ fi ++ echo "$NEW_RESOLVCONF" > /etc/resolv.conf ++ ++ if [ "$OS" = "Darwin" ]; then ++ case "`uname -r`" in ++ # Skip for pre-10.4 systems ++ 4.*|5.*|6.*|7.*) ++ ;; ++ # 10.4 and later require use of scutil for DNS to work properly ++ *) ++ scutil >/dev/null 2>&1 <<-EOF ++ open ++ d.init ++ d.add ServerAddresses * $INTERNAL_IP4_DNS ++ set State:/Network/Service/$TUNDEV/DNS ++ get State:/Network/Global/IPv4 ++ d.remove PrimaryService ++ d.remove PrimaryInterface ++ d.add Addresses * $INTERNAL_IP4_ADDRESS ++ d.add SubnetMasks * 255.255.255.255 ++ d.add InterfaceName $TUNDEV ++ set State:/Network/Service/$TUNDEV/IPv4 ++ close ++ EOF ++ if [ -n "$CISCO_DEF_DOMAIN" ]; then ++ scutil >/dev/null 2>&1 <<-EOF ++ open ++ get State:/Network/Service/$TUNDEV/DNS ++ d.add DomainName $CISCO_DEF_DOMAIN ++ d.add SearchDomains * $CISCO_DEF_DOMAIN ++ d.add SupplementalMatchDomains * $CISCO_DEF_DOMAIN ++ set State:/Network/Service/$TUNDEV/DNS ++ close ++ EOF ++ fi ++ # Uncomment the following if/fi pair to use multiple ++ # DNS matching when available. When multiple DNS matching ++ # is present, anything reading the /etc/resolv.conf file ++ # directly will probably not work as intended. ++ #if [ -z "$CISCO_DEF_DOMAIN" ]; then ++ # Cannot use multiple DNS matching without a domain ++ scutil >/dev/null 2>&1 <<-EOF ++ open ++ get State:/Network/Service/$TUNDEV/IPv4 ++ d.add OverridePrimary # 1 ++ set State:/Network/Service/$TUNDEV/IPv4 ++ close ++ EOF ++ #fi ++ ;; ++ esac ++ fi ++} ++ ++reset_resolvconf() { ++ ++ if [ -x /sbin/resolvconf ] ; then ++ /sbin/resolvconf -d "$TUNDEV" ++ return ++ fi ++ ++ case "$DNS_UPDATE" in ++ *no|*NO|*No|*nO) ++ return ++ ;; ++ esac ++ ++ ++ if [ ! -e "$RESOLV_CONF_BACKUP" ]; then ++ return ++ fi ++ grep '^#@VPNC_GENERATED@' /etc/resolv.conf > /dev/null 2>&1 && cat "$RESOLV_CONF_BACKUP" > /etc/resolv.conf ++ rm -f -- "$RESOLV_CONF_BACKUP" ++ ++ if [ "$OS" = "Darwin" ]; then ++ case "`uname -r`" in ++ # Skip for pre-10.4 systems ++ 4.*|5.*|6.*|7.*) ++ ;; ++ # 10.4 and later require use of scutil for DNS to work properly ++ *) ++ scutil >/dev/null 2>&1 <<-EOF ++ open ++ remove State:/Network/Service/$TUNDEV/IPv4 ++ remove State:/Network/Service/$TUNDEV/DNS ++ close ++ EOF ++ ;; ++ esac ++ fi ++} ++ ++do_connect() { ++ # Debian specific, insert your code there to avoid modification of ++ # conffiles like this script ++ if [ -r /etc/vpnc/vpnc-script-connect-action ] ; then ++ . /etc/vpnc/vpnc-script-connect-action ++ fi ++ # backwards compatibility mapping for old extensions ++ if test "$TARGET_NETWORKS" ; then ++ i=0 ++ for network in $TARGET_NETWORKS ; do ++ eval CISCO_SPLIT_INC_${i}_ADDR=`echo $network | cut -f1 -d/` ++ eval CISCO_SPLIT_INC_${i}_MASKLEN=`echo $network | cut -f2 -d/` ++ eval CISCO_SPLIT_INC_${i}_MASK=$( perl -e '$ARGV[0]=~s,.*/,,;$m=(2**$ARGV[0]-1)<<(32-$ARGV[0]);printf "%d.%d.%d.%d\n", $m>>24 & 0xff, $m>>16 & 0xff, $m>>8 & 0xff, $m & 0xff;' $network ) ++ eval CISCO_SPLIT_INC_${i}_PROTOCOL=0 ++ eval CISCO_SPLIT_INC_${i}_SPORT=0 ++ eval CISCO_SPLIT_INC_${i}_DPORT=0 ++ i=`expr $i + 1` ++ done ++ CISCO_SPLIT_INC=$i ++ fi ++ ++ if [ -n "$CISCO_BANNER" ]; then ++ echo "Connect Banner:" ++ echo "$CISCO_BANNER" | while read LINE ; do echo "|" "$LINE" ; done ++ echo ++ fi ++ ++ do_ifconfig ++ set_vpngateway_route ++ if [ -n "$CISCO_SPLIT_INC" ]; then ++ i=0 ++ while [ $i -lt $CISCO_SPLIT_INC ] ; do ++ eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}" ++ eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}" ++ eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}" ++ set_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN" ++ i=`expr $i + 1` ++ done ++ for i in $INTERNAL_IP4_DNS ; do ++ set_network_route "$i" "255.255.255.255" "32" ++ done ++ else ++ set_default_route ++ fi ++ ++ if [ -n "$INTERNAL_IP4_DNS" ]; then ++ write_resolvconf ++ fi ++ ++ if [ -r /etc/vpnc/vpnc-script-post-connect-action ] ; then ++ . /etc/vpnc/vpnc-script-post-connect-action ++ fi ++ ++} ++ ++do_disconnect() { ++ ++ # Debian specific, insert your code there to avoid modification of ++ # conffiles like this script ++ if [ -r /etc/vpnc/vpnc-script-disconnect-action ] ; then ++ . /etc/vpnc/vpnc-script-disconnect-action ++ fi ++ ++ destroy_tun_device ++ if [ -n "$CISCO_SPLIT_INC" ]; then ++ i=0 ++ while [ $i -lt $CISCO_SPLIT_INC ] ; do ++ eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}" ++ eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}" ++ eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}" ++ del_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN" ++ i=`expr $i + 1` ++ done ++ for i in $INTERNAL_IP4_DNS ; do ++ del_network_route "$i" "255.255.255.255" "32" ++ done ++ else ++ if [ -s "$DEFAULT_ROUTE_FILE" ]; then ++ reset_default_route ++ rm -f -- "$DEFAULT_ROUTE_FILE" ++ fi ++ fi ++ ++ del_vpngateway_route ++ ++ if [ -n "$INTERNAL_IP4_DNS" ]; then ++ reset_resolvconf ++ fi ++ if [ -r /etc/vpnc/vpnc-script-post-disconnect-action ] ; then ++ . /etc/vpnc/vpnc-script-post-disconnect-action ++ fi ++} ++ ++#### ++ ++if [ -z "$reason" ]; then ++ echo "this script must be called from vpnc" 1>&2 ++ exit 1 ++fi ++ ++case "$reason" in ++ pre-init) ++ do_pre_init ++ ;; ++ connect) ++ do_connect ++ ;; ++ disconnect) ++ do_disconnect ++ ;; ++ *) ++ echo "unknown reason '$reason'. Maybe vpnc-script is out of date" 1>&2 ++ exit 1 ++ ;; ++esac ++ ++exit 0 +diff -urNad vpnc~/vpnc.c vpnc/vpnc.c +--- vpnc~/vpnc.c 2007-05-23 22:47:53.000000000 +0200 ++++ vpnc/vpnc.c 2007-05-23 22:48:16.000000000 +0200 +@@ -49,10 +49,49 @@ + #include "tunip.h" + #include "supp.h" + ++#if defined(__CYGWIN__) ++ GCRY_THREAD_OPTION_PTHREAD_IMPL; ++#endif ++ + #define ISAKMP_PORT (500) ++#define ISAKMP_PORT_NATT (4500) ++ ++const unsigned char VID_XAUTH[] = { /* draft-ietf-ipsec-isakmp-xauth-06.txt */ ++ 0x09, 0x00, 0x26, 0x89, 0xDF, 0xD6, 0xB7, 0x12 ++}; ++const unsigned char VID_DPD[] = { /* Dead Peer Detection, RFC 3706 */ ++ 0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9, ++ 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 0x01, 0x00 ++}; ++const unsigned char VID_UNITY[] = { ++ 0x12, 0xF5, 0xF2, 0x8C, 0x45, 0x71, 0x68, 0xA9, ++ 0x70, 0x2D, 0x9F, 0xE2, 0x74, 0xCC, 0x01, 0x00 ++}; ++const unsigned char VID_UNKNOWN[] = { ++ 0x12, 0x6E, 0x1F, 0x57, 0x72, 0x91, 0x15, 0x3B, ++ 0x20, 0x48, 0x5F, 0x7F, 0x15, 0x5B, 0x4B, 0xC8 ++}; ++const unsigned char VID_NATT_00[] = { /* draft-ietf-ipsec-nat-t-ike-00 */ ++ 0x44, 0x85, 0x15, 0x2d, 0x18, 0xb6, 0xbb, 0xcd, ++ 0x0b, 0xe8, 0xa8, 0x46, 0x95, 0x79, 0xdd, 0xcc ++}; ++const unsigned char VID_NATT_01[] = { /* draft-ietf-ipsec-nat-t-ike-01 */ ++ 0x16, 0xf6, 0xca, 0x16, 0xe4, 0xa4, 0x06, 0x6d, ++ 0x83, 0x82, 0x1a, 0x0f, 0x0a, 0xea, 0xa8, 0x62 ++}; ++const unsigned char VID_NATT_02[] = { /* draft-ietf-ipsec-nat-t-ike-02 */ ++ 0xcd, 0x60, 0x46, 0x43, 0x35, 0xdf, 0x21, 0xf8, ++ 0x7c, 0xfd, 0xb2, 0xfc, 0x68, 0xb6, 0xa4, 0x48 ++}; ++const unsigned char VID_NATT_02N[] = { /* draft-ietf-ipsec-nat-t-ike-02\n */ ++ 0x90, 0xCB, 0x80, 0x91, 0x3E, 0xBB, 0x69, 0x6E, ++ 0x08, 0x63, 0x81, 0xB5, 0xEC, 0x42, 0x7B, 0x1F ++}; ++const unsigned char VID_NATT_RFC[] = { /* RFC 3947 */ ++ 0x4A, 0x13, 0x1C, 0x81, 0x07, 0x03, 0x58, 0x45, ++ 0x5C, 0x57, 0x28, 0xF2, 0x0E, 0x95, 0x45, 0x2F ++}; + +-static int timeout = 1000; /* 1 second */ +-static uint8_t *resend_hash = NULL; + + static uint8_t r_packet[2048]; + static ssize_t r_length; +@@ -96,12 +135,17 @@ + if (sock < 0) + error(1, errno, "making socket"); + ++#ifdef FD_CLOEXEC ++ /* do not pass socket to vpnc-script, etc. */ ++ fcntl(sock, F_SETFD, FD_CLOEXEC); ++#endif ++ + /* give the socket a name */ + name.sin_family = AF_INET; + name.sin_addr = s->opt_src_ip; + name.sin_port = htons(src_port); + if (bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0) +- error(1, errno, "binding to %s:%d", inet_ntoa(s->opt_src_ip), ntohs(src_port)); ++ error(1, errno, "binding to %s:%d", inet_ntoa(s->opt_src_ip), src_port); + + /* connect the socket */ + name.sin_family = AF_INET; +@@ -180,14 +224,14 @@ + hash_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1); + resend_check_hash = malloc(hash_len); + gcry_md_hash_buffer(GCRY_MD_SHA1, resend_check_hash, recvbuf, recvsize); +- if (resend_hash && memcmp(resend_hash, resend_check_hash, hash_len) == 0) { ++ if (s->ike.resend_hash && memcmp(s->ike.resend_hash, resend_check_hash, hash_len) == 0) { + free(resend_check_hash); + return -1; + } +- if (!resend_hash) { +- resend_hash = resend_check_hash; ++ if (!s->ike.resend_hash) { ++ s->ike.resend_hash = resend_check_hash; + } else { +- memcpy(resend_hash, resend_check_hash, hash_len); ++ memcpy(s->ike.resend_hash, resend_check_hash, hash_len); + free(resend_check_hash); + } + +@@ -231,7 +275,7 @@ + break; + + do { +- pollresult = poll(&pfd, 1, timeout << tries); ++ pollresult = poll(&pfd, 1, s->ike.timeout << tries); + } while (pollresult == -1 && errno == EINTR); + + if (pollresult == -1) +@@ -262,10 +306,10 @@ + + /* Wait at least 2s for a response or 4 times the time it took + * last time. */ +- if (start == end) +- timeout = 2000; ++ if (start >= end) ++ s->ike.timeout = 2000; + else +- timeout = 4000 * (end - start); ++ s->ike.timeout = 4000 * (end - start); + + return recvsize; + } +@@ -503,12 +547,93 @@ + } + } + ++static void send_phase2_late(struct sa_block *s, struct isakmp_payload *pl, ++ uint8_t exchange_type, uint32_t msgid) ++{ ++ struct isakmp_packet *p; ++ uint8_t *p_flat; ++ size_t p_size; ++ ssize_t recvlen; ++ ++ /* Build up the packet. */ ++ p = new_isakmp_packet(); ++ memcpy(p->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ memcpy(p->r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ p->flags = ISAKMP_FLAG_E; ++ p->isakmp_version = ISAKMP_VERSION; ++ p->exchange_type = exchange_type; ++ p->message_id = msgid; ++ p->payload = pl; ++ ++ flatten_isakmp_packet(p, &p_flat, &p_size, s->ike.ivlen); ++ free_isakmp_packet(p); ++ isakmp_crypt(s, p_flat, p_size, 1); ++ ++ s->ike.life.tx += p_size; ++ ++ recvlen = sendrecv(s, NULL, 0, p_flat, p_size, 1); ++ free(p_flat); ++} ++ + void keepalive_ike(struct sa_block *s) + { + uint32_t msgid; + + gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid)); +- sendrecv_phase2(s, NULL, ISAKMP_EXCHANGE_INFORMATIONAL, msgid, 1, 0, 0, 0, 0, 0, 0); ++ send_phase2_late(s, NULL, ISAKMP_EXCHANGE_INFORMATIONAL, msgid); ++} ++ ++static void send_dpd(struct sa_block *s, int isack, uint32_t seqno) ++{ ++ struct isakmp_payload *pl; ++ uint32_t msgid; ++ ++ pl = new_isakmp_payload(ISAKMP_PAYLOAD_N); ++ pl->u.n.doi = ISAKMP_DOI_IPSEC; ++ pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP; ++ pl->u.n.type = isack ? ISAKMP_N_R_U_THERE_ACK : ISAKMP_N_R_U_THERE; ++ pl->u.n.spi_length = 2 * ISAKMP_COOKIE_LENGTH; ++ pl->u.n.spi = xallocc(2 * ISAKMP_COOKIE_LENGTH); ++ memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ pl->u.n.data_length = 4; ++ pl->u.n.data = xallocc(4); ++ memcpy(pl->u.n.data, &seqno, 4); ++ gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid)); ++ send_phase2_late(s, pl, ISAKMP_EXCHANGE_INFORMATIONAL, msgid); ++} ++ ++void dpd_ike(struct sa_block *s) ++{ ++ if (!s->ike.do_dpd) ++ return; ++ ++ if (s->ike.dpd_seqno == s->ike.dpd_seqno_ack) { ++ /* Increase the sequence number, reset the attempts to 6, record ++ ** the current time and send a dpd request ++ */ ++ s->ike.dpd_attempts = 6; ++ s->ike.dpd_sent = time(NULL); ++ ++s->ike.dpd_seqno; ++ send_dpd(s, 0, s->ike.dpd_seqno); ++ } else { ++ /* Our last dpd request has not yet been acked. If it's been ++ ** less than 5 seconds since we sent it do nothing. Otherwise ++ ** decrement dpd_attempts. If dpd_attempts is 0 dpd fails and we ++ ** terminate otherwise we send it again with the same sequence ++ ** number and record current time. ++ */ ++ time_t now = time(NULL); ++ if (now < s->ike.dpd_sent + 5) ++ return; ++ if (--s->ike.dpd_attempts == 0) { ++ DEBUG(2, printf("dead peer detected, terminating\n")); ++ do_kill = -2; ++ return; ++ } ++ s->ike.dpd_sent = now; ++ send_dpd(s, 0, s->ike.dpd_seqno); ++ } + } + + static void phase2_fatal(struct sa_block *s, const char *msg, int id) +@@ -861,28 +986,12 @@ + s->ipsec.life.kbytes = value; + } + +-static void do_phase_1(const char *key_id, const char *shared_key, struct sa_block *s) ++static void do_phase1(const char *key_id, const char *shared_key, struct sa_block *s) + { + unsigned char i_nonce[20]; + struct group *dh_grp; + unsigned char *dh_public; + unsigned char *returned_hash; +- static const uint8_t xauth_vid[] = XAUTH_VENDOR_ID; +- static const uint8_t unity_vid[] = UNITY_VENDOR_ID; +- static const uint8_t unknown_vid[] = UNKNOWN_VENDOR_ID; +- /* NAT traversal */ +- static const uint8_t natt_vid_00[] = NATT_VENDOR_ID_00; +- static const uint8_t natt_vid_01[] = NATT_VENDOR_ID_01; +- static const uint8_t natt_vid_02[] = NATT_VENDOR_ID_02; +- static const uint8_t natt_vid_02n[] = NATT_VENDOR_ID_02n; +- static const uint8_t natt_vid_rfc[] = NATT_VENDOR_ID_RFC; +-#if 0 +- static const uint8_t dpd_vid[] = DPD_VENDOR_ID; /* dead peer detection */ +- static const uint8_t my_vid[] = { +- 0x35, 0x53, 0x07, 0x6c, 0x4f, 0x65, 0x12, 0x68, 0x02, 0x82, 0xf2, 0x15, +- 0x8a, 0xa8, 0xa0, 0x9e +- }; +-#endif + + struct isakmp_packet *p1; + int seen_natt_vid = 0, seen_natd = 0, seen_natd_them = 0, seen_natd_us = 0, natd_type = 0; +@@ -930,30 +1039,35 @@ + else + l->u.id.type = ISAKMP_IPSEC_ID_USER_FQDN; + l->u.id.protocol = IPPROTO_UDP; +- l->u.id.port = 500; /* this must be 500, not local_port */ ++ l->u.id.port = ISAKMP_PORT; /* this must be 500, not local_port */ + l->u.id.length = strlen(key_id); + l->u.id.data = xallocc(l->u.id.length); + memcpy(l->u.id.data, key_id, strlen(key_id)); + l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- xauth_vid, sizeof(xauth_vid)); ++ VID_XAUTH, sizeof(VID_XAUTH)); + l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- unity_vid, sizeof(unity_vid)); ++ VID_UNITY, sizeof(VID_UNITY)); + if ((opt_natt_mode == NATT_NORMAL) || (opt_natt_mode == NATT_FORCE)) { + l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- natt_vid_rfc, sizeof(natt_vid_rfc)); ++ VID_NATT_RFC, sizeof(VID_NATT_RFC)); + l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- natt_vid_02n, sizeof(natt_vid_02n)); ++ VID_NATT_02N, sizeof(VID_NATT_02N)); + l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- natt_vid_02, sizeof(natt_vid_02)); ++ VID_NATT_02, sizeof(VID_NATT_02)); + l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- natt_vid_01, sizeof(natt_vid_01)); ++ VID_NATT_01, sizeof(VID_NATT_01)); + l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- natt_vid_00, sizeof(natt_vid_00)); ++ VID_NATT_00, sizeof(VID_NATT_00)); ++ } ++ s->ike.dpd_idle = atoi(config[CONFIG_DPD_IDLE]); ++ if (s->ike.dpd_idle != 0) { ++ if (s->ike.dpd_idle < 10) ++ s->ike.dpd_idle = 10; ++ if (s->ike.dpd_idle > 86400) ++ s->ike.dpd_idle = 86400; ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ VID_DPD, sizeof(VID_DPD)); + } +-#if 0 +- l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- dpd_vid, sizeof(dpd_vid)); +-#endif + flatten_isakmp_packet(p1, &pkt, &pkt_len, 0); + + /* Now, send that packet and receive a new one. */ +@@ -1130,40 +1244,52 @@ + hash = rp; + break; + case ISAKMP_PAYLOAD_VID: +- if (rp->u.vid.length == sizeof(xauth_vid) +- && memcmp(rp->u.vid.data, xauth_vid, +- sizeof(xauth_vid)) == 0) { ++ if (rp->u.vid.length == sizeof(VID_XAUTH) ++ && memcmp(rp->u.vid.data, VID_XAUTH, ++ sizeof(VID_XAUTH)) == 0) { + seen_xauth_vid = 1; +- } else if (rp->u.vid.length == sizeof(natt_vid_rfc) +- && memcmp(rp->u.vid.data, natt_vid_rfc, +- sizeof(natt_vid_rfc)) == 0) { ++ } else if (rp->u.vid.length == sizeof(VID_NATT_RFC) ++ && memcmp(rp->u.vid.data, VID_NATT_RFC, ++ sizeof(VID_NATT_RFC)) == 0) { + seen_natt_vid = 1; + if (natt_draft < 1) natt_draft = 2; + DEBUG(2, printf("peer is NAT-T capable (RFC 3947)\n")); +- } else if (rp->u.vid.length == sizeof(natt_vid_02n) +- && memcmp(rp->u.vid.data, natt_vid_02n, +- sizeof(natt_vid_02n)) == 0) { ++ } else if (rp->u.vid.length == sizeof(VID_NATT_02N) ++ && memcmp(rp->u.vid.data, VID_NATT_02N, ++ sizeof(VID_NATT_02N)) == 0) { + seen_natt_vid = 1; + if (natt_draft < 1) natt_draft = 2; + DEBUG(2, printf("peer is NAT-T capable (draft-02)\n\n")); +- } else if (rp->u.vid.length == sizeof(natt_vid_02) +- && memcmp(rp->u.vid.data, natt_vid_02, +- sizeof(natt_vid_02)) == 0) { ++ } else if (rp->u.vid.length == sizeof(VID_NATT_02) ++ && memcmp(rp->u.vid.data, VID_NATT_02, ++ sizeof(VID_NATT_02)) == 0) { + seen_natt_vid = 1; + if (natt_draft < 1) natt_draft = 2; + DEBUG(2, printf("peer is NAT-T capable (draft-02)\n")); +- } else if (rp->u.vid.length == sizeof(natt_vid_01) +- && memcmp(rp->u.vid.data, natt_vid_01, +- sizeof(natt_vid_01)) == 0) { ++ } else if (rp->u.vid.length == sizeof(VID_NATT_01) ++ && memcmp(rp->u.vid.data, VID_NATT_01, ++ sizeof(VID_NATT_01)) == 0) { + seen_natt_vid = 1; + if (natt_draft < 1) natt_draft = 1; + DEBUG(2, printf("peer is NAT-T capable (draft-01)\n")); +- } else if (rp->u.vid.length == sizeof(natt_vid_00) +- && memcmp(rp->u.vid.data, natt_vid_00, +- sizeof(natt_vid_00)) == 0) { ++ } else if (rp->u.vid.length == sizeof(VID_NATT_00) ++ && memcmp(rp->u.vid.data, VID_NATT_00, ++ sizeof(VID_NATT_00)) == 0) { + seen_natt_vid = 1; + if (natt_draft < 0) natt_draft = 0; + DEBUG(2, printf("peer is NAT-T capable (draft-00)\n")); ++ } else if (rp->u.vid.length == sizeof(VID_DPD) ++ && memcmp(rp->u.vid.data, VID_DPD, ++ sizeof(VID_DPD)) == 0) { ++ if (s->ike.dpd_idle != 0) { ++ gcry_create_nonce(&s->ike.dpd_seqno, sizeof(s->ike.dpd_seqno)); ++ s->ike.dpd_seqno &= 0x7FFFFFFF; ++ s->ike.dpd_seqno_ack = s->ike.dpd_seqno; ++ s->ike.do_dpd = 1; ++ DEBUG(2, printf("peer is DPD capable (RFC3706)\n")); ++ } else { ++ DEBUG(2, printf("ignoring that peer is DPD capable (RFC3706)\n")); ++ } + } else { + hex_dump("unknown ISAKMP_PAYLOAD_VID: ", + rp->u.vid.data, rp->u.vid.length, NULL); +@@ -1411,9 +1537,9 @@ + memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); + memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); + pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- unknown_vid, sizeof(unknown_vid)); ++ VID_UNKNOWN, sizeof(VID_UNKNOWN)); + pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, +- unity_vid, sizeof(unity_vid)); ++ VID_UNITY, sizeof(VID_UNITY)); + + /* include NAT traversal discovery payloads */ + if (seen_natt_vid) { +@@ -1460,9 +1586,9 @@ + if (natt_draft >= 2) { + s->ipsec.natt_active_mode = NATT_ACTIVE_RFC; + close(s->ike_fd); +- if (s->ike.src_port == 500) +- s->ike.src_port = 4500; +- s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port = 4500); ++ if (s->ike.src_port == ISAKMP_PORT) ++ s->ike.src_port = ISAKMP_PORT_NATT; ++ s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port = ISAKMP_PORT_NATT); + } else { + s->ipsec.natt_active_mode = NATT_ACTIVE_DRAFT_OLD; + } +@@ -1523,8 +1649,8 @@ + s->ike.dst_port = ISAKMP_PORT; + s->ipsec.encap_mode = IPSEC_ENCAP_TUNNEL; + s->ipsec.natt_active_mode = NATT_ACTIVE_NONE; +- if (s->ike.src_port == 4500) +- s->ike.src_port = 500; ++ if (s->ike.src_port == ISAKMP_PORT_NATT) ++ s->ike.src_port = ISAKMP_PORT; + close(s->ike_fd); + s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port); + DEBUG(2, printf("got cisco loadbalancing notice, diverting to %s\n", +@@ -1565,7 +1691,7 @@ + return reject; + } + +-static int do_phase_2_xauth(struct sa_block *s) ++static int do_phase2_xauth(struct sa_block *s) + { + struct isakmp_packet *r; + int loopcount; +@@ -1659,7 +1785,7 @@ + struct isakmp_attribute *na; + na = new_isakmp_attribute(ap->type, reply_attr); + reply_attr = na; +- if (!config[CONFIG_DOMAIN] || strlen(config[CONFIG_DOMAIN]) == 0) ++ if (!config[CONFIG_DOMAIN]) + error(1, 0, + "server requested domain, but none set (use \"Domain ...\" in config or --domain"); + na->u.lots.length = strlen(config[CONFIG_DOMAIN]); +@@ -1781,7 +1907,7 @@ + return 0; + } + +-static int do_phase_2_config(struct sa_block *s) ++static int do_phase2_config(struct sa_block *s) + { + struct isakmp_payload *rp; + struct isakmp_attribute *a; +@@ -2514,6 +2640,44 @@ + DEBUG(3, printf("do_rekey returned: %d\n", reject)); + return; + } ++ ++ if (r->exchange_type == ISAKMP_EXCHANGE_INFORMATIONAL) { ++ /* Search for notify payloads */ ++ for (rp = r->payload->next; rp; rp = rp->next) { ++ if (rp->type != ISAKMP_PAYLOAD_N) ++ continue; ++ /* did we get a DPD request or ACK? */ ++ if (rp->u.n.protocol != ISAKMP_IPSEC_PROTO_ISAKMP) { ++ DEBUG(2, printf("got non isakmp-notify, ignoring...\n")); ++ continue; ++ } ++ if (rp->u.n.type == ISAKMP_N_R_U_THERE) { ++ uint32_t seq; ++ if (rp->u.n.data_length != 4) { ++ DEBUG(2, printf("ignoring bad data length R-U-THERE request\n")); ++ continue; ++ } ++ memcpy(&seq, rp->u.n.data, 4); ++ send_dpd(s, 1, seq); ++ DEBUG(2, printf("got r-u-there request sent ack\n")); ++ continue; ++ } else if (rp->u.n.type == ISAKMP_N_R_U_THERE_ACK) { ++ uint32_t seqack; ++ if (rp->u.n.data_length != 4) { ++ DEBUG(2, printf("ignoring bad data length R-U-THERE-ACK\n")); ++ continue; ++ } ++ memcpy(&seqack, rp->u.n.data, 4); ++ if (seqack == s->ike.dpd_seqno) { ++ s->ike.dpd_seqno_ack = seqack; ++ } else { ++ DEBUG(2, printf("ignoring r-u-there ack %u (expecting %u)\n", seqack, s->ike.dpd_seqno)); ++ continue; ++ } ++ DEBUG(2, printf("got r-u-there ack\n")); ++ } ++ } ++ } + + /* check if our isakmp sa gets deleted */ + for (rp = r->payload->next; rp; rp = rp->next) { +@@ -2550,11 +2714,16 @@ + struct sa_block *s = oursa; + + test_pack_unpack(); ++#if defined(__CYGWIN__) ++ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); ++#endif + gcry_check_version("1.1.90"); + gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0); + group_init(); ++ + memset(s, 0, sizeof(*s)); + s->ipsec.encap_mode = IPSEC_ENCAP_TUNNEL; ++ s->ike.timeout = 1000; /* 1 second */ + + do_config(argc, argv); + +@@ -2563,6 +2732,7 @@ + DEBUG(1, printf("vpnc version " VERSION "\n")); + DEBUG(2, printf("S1\n")); + init_sockaddr(&s->dst, config[CONFIG_IPSEC_GATEWAY]); ++ init_sockaddr(&s->opt_src_ip, config[CONFIG_LOCAL_ADDR]); + DEBUG(2, printf("S2\n")); + s->ike.src_port = atoi(config[CONFIG_LOCAL_PORT]); + s->ike.dst_port = ISAKMP_PORT; +@@ -2573,13 +2743,13 @@ + do_load_balance = 0; + do { + DEBUG(2, printf("S4\n")); +- do_phase_1(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s); ++ do_phase1(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s); + DEBUG(2, printf("S5\n")); + if (s->ike.auth_algo == IKE_AUTH_XAUTHInitPreShared) +- do_load_balance = do_phase_2_xauth(s); ++ do_load_balance = do_phase2_xauth(s); + DEBUG(2, printf("S6\n")); + if ((opt_vendor != VENDOR_NETSCREEN) && (do_load_balance == 0)) +- do_load_balance = do_phase_2_config(s); ++ do_load_balance = do_phase2_config(s); + } while (do_load_balance); + DEBUG(2, printf("S7\n")); + setup_link(s); +diff -urNad vpnc~/vpnc.c.orig vpnc/vpnc.c.orig +--- vpnc~/vpnc.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ vpnc/vpnc.c.orig 2007-05-23 22:47:53.000000000 +0200 +@@ -0,0 +1,2591 @@ ++/* IPSec VPN client compatible with Cisco equipment. ++ Copyright (C) 2002 Geoffrey Keating ++ Copyright (C) 2003-2005 Maurice Massar ++ Copyright (C) 2004 Tomas Mraz ++ Copyright (C) 2004 Martin von Gagern ++ ++ 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 ++ ++ $Id: vpnc.c 147 2007-02-19 20:49:52Z Maurice Massar $ ++*/ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "sysdep.h" ++#include "config.h" ++#include "isakmp-pkt.h" ++#include "math_group.h" ++#include "dh.h" ++#include "vpnc.h" ++#include "tunip.h" ++#include "supp.h" ++ ++#define ISAKMP_PORT (500) ++ ++static int timeout = 1000; /* 1 second */ ++static uint8_t *resend_hash = NULL; ++ ++static uint8_t r_packet[2048]; ++static ssize_t r_length; ++ ++static __inline__ int min(int a, int b) ++{ ++ return (a < b) ? a : b; ++} ++ ++static void addenv(const void *name, const char *value) ++{ ++ char *strbuf = NULL, *oldval; ++ ++ oldval = getenv(name); ++ if (oldval != NULL) { ++ strbuf = xallocc(strlen(oldval) + 1 + strlen(value) + 1); ++ strcat(strbuf, oldval); ++ strcat(strbuf, " "); ++ strcat(strbuf, value); ++ } ++ ++ setenv(name, strbuf ? strbuf : value, 1); ++ ++ if (strbuf) ++ free(strbuf); ++} ++ ++static void addenv_ipv4(const void *name, uint8_t * data) ++{ ++ addenv(name, inet_ntoa(*((struct in_addr *)data))); ++} ++ ++static int make_socket(struct sa_block *s, uint16_t src_port, uint16_t dst_port) ++{ ++ int sock; ++ struct sockaddr_in name; ++ size_t len = sizeof(name); ++ ++ /* create the socket */ ++ sock = socket(PF_INET, SOCK_DGRAM, 0); ++ if (sock < 0) ++ error(1, errno, "making socket"); ++ ++ /* give the socket a name */ ++ name.sin_family = AF_INET; ++ name.sin_addr = s->opt_src_ip; ++ name.sin_port = htons(src_port); ++ if (bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0) ++ error(1, errno, "binding to %s:%d", inet_ntoa(s->opt_src_ip), ntohs(src_port)); ++ ++ /* connect the socket */ ++ name.sin_family = AF_INET; ++ name.sin_addr = s->dst; ++ name.sin_port = htons(dst_port); ++ if (connect(sock, (struct sockaddr *)&name, sizeof(name)) < 0) ++ error(1, errno, "connecting to port %d", ntohs(dst_port)); ++ ++ /* who am I */ ++ if (getsockname(sock, (struct sockaddr *)&name, &len) < 0) ++ error(1, errno, "reading local address from socket %d", sock); ++ s->src = name.sin_addr; ++ ++ return sock; ++} ++ ++static void init_sockaddr(struct in_addr *dst, const char *hostname) ++{ ++ struct hostent *hostinfo; ++ ++ if (inet_aton(hostname, dst) == 0) { ++ hostinfo = gethostbyname(hostname); ++ if (hostinfo == NULL) ++ error(1, 0, "unknown host `%s'\n", hostname); ++ *dst = *(struct in_addr *)hostinfo->h_addr; ++ } ++} ++ ++static void setup_tunnel(struct sa_block *s) ++{ ++ setenv("reason", "pre-init", 1); ++ system(config[CONFIG_SCRIPT]); ++ ++ if (config[CONFIG_IF_NAME]) ++ memcpy(s->tun_name, config[CONFIG_IF_NAME], strlen(config[CONFIG_IF_NAME])); ++ ++ s->tun_fd = tun_open(s->tun_name, opt_if_mode); ++ DEBUG(2, printf("using interface %s\n", s->tun_name)); ++ setenv("TUNDEV", s->tun_name, 1); ++ ++ if (s->tun_fd == -1) ++ error(1, errno, "can't initialise tunnel interface"); ++ ++ if (opt_if_mode == IF_MODE_TAP) { ++ if (tun_get_hwaddr(s->tun_fd, s->tun_name, s->tun_hwaddr) < 0) { ++ error(1, errno, "can't get tunnel HW address"); ++ } ++ hex_dump("interface HW addr", s->tun_hwaddr, ETH_ALEN, NULL); ++ } ++} ++ ++static void config_tunnel(struct sa_block *s) ++{ ++ setenv("VPNGATEWAY", inet_ntoa(s->dst), 1); ++ setenv("reason", "connect", 1); ++ // DEPRECATED, Debian specific ++ setenv("DNS_UPDATE", config[CONFIG_DNS_UPDATE], 1); ++ setenv("TARGET_NETWORKS", config[CONFIG_TARGET_NETWORKS], 1); ++ system(config[CONFIG_SCRIPT]); ++} ++ ++static int recv_ignore_dup(struct sa_block *s, void *recvbuf, size_t recvbufsize) ++{ ++ uint8_t *resend_check_hash; ++ int recvsize, hash_len; ++ ++ recvsize = recv(s->ike_fd, recvbuf, recvbufsize, 0); ++ if (recvsize == -1) ++ error(1, errno, "receiving packet"); ++ ++ /* skip NAT-T draft-0 keepalives */ ++ if ((s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) && ++ (recvsize == 1) && (*((u_char *)(recvbuf)) == 0xff)) ++ return -1; ++ ++ hash_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1); ++ resend_check_hash = malloc(hash_len); ++ gcry_md_hash_buffer(GCRY_MD_SHA1, resend_check_hash, recvbuf, recvsize); ++ if (resend_hash && memcmp(resend_hash, resend_check_hash, hash_len) == 0) { ++ free(resend_check_hash); ++ return -1; ++ } ++ if (!resend_hash) { ++ resend_hash = resend_check_hash; ++ } else { ++ memcpy(resend_hash, resend_check_hash, hash_len); ++ free(resend_check_hash); ++ } ++ ++ return recvsize; ++} ++ ++/* Send TOSEND of size SENDSIZE to the socket. Then wait for a new packet, ++ resending TOSEND on timeout, and ignoring duplicate packets; the ++ new packet is put in RECVBUF of size RECVBUFSIZE and the actual size ++ of the new packet is returned. */ ++ ++static ssize_t sendrecv(struct sa_block *s, void *recvbuf, size_t recvbufsize, void *tosend, size_t sendsize, int sendonly) ++{ ++ struct pollfd pfd; ++ int tries = 0; ++ int recvsize = -1; ++ time_t start = time(NULL); ++ time_t end = 0; ++ void *realtosend; ++ ++ pfd.fd = s->ike_fd; ++ pfd.events = POLLIN; ++ tries = 0; ++ ++ if ((s->ipsec.natt_active_mode == NATT_ACTIVE_RFC) && (tosend != NULL)) { ++ DEBUG(2, printf("NAT-T mode, adding non-esp marker\n")); ++ realtosend = xallocc(sendsize+4); ++ memcpy(realtosend+4, tosend, sendsize); ++ sendsize += 4; ++ } else { ++ realtosend = tosend; ++ } ++ ++ for (;;) { ++ int pollresult; ++ ++ if (realtosend != NULL) ++ if (write(s->ike_fd, realtosend, sendsize) != (int)sendsize) ++ error(1, errno, "can't send packet"); ++ if (sendonly) ++ break; ++ ++ do { ++ pollresult = poll(&pfd, 1, timeout << tries); ++ } while (pollresult == -1 && errno == EINTR); ++ ++ if (pollresult == -1) ++ error(1, errno, "can't poll socket"); ++ if (pollresult != 0) { ++ recvsize = recv_ignore_dup(s, recvbuf, recvbufsize); ++ end = time(NULL); ++ if (recvsize != -1) ++ break; ++ continue; ++ } ++ ++ if (tries > 2) ++ error(1, 0, "no response from target"); ++ tries++; ++ } ++ ++ if (realtosend != tosend) ++ free(realtosend); ++ ++ if (sendonly) ++ return 0; ++ ++ if ((s->ipsec.natt_active_mode == NATT_ACTIVE_RFC)&&(recvsize > 4)) { ++ recvsize -= 4; /* 4 bytes non-esp marker */ ++ memmove(recvbuf, recvbuf+4, recvsize); ++ } ++ ++ /* Wait at least 2s for a response or 4 times the time it took ++ * last time. */ ++ if (start == end) ++ timeout = 2000; ++ else ++ timeout = 4000 * (end - start); ++ ++ return recvsize; ++} ++ ++static int isakmp_crypt(struct sa_block *s, uint8_t * block, size_t blocklen, int enc) ++{ ++ unsigned char *new_iv, *iv = NULL; ++ int info_ex; ++ gcry_cipher_hd_t cry_ctx; ++ ++ if (blocklen < ISAKMP_PAYLOAD_O || ((blocklen - ISAKMP_PAYLOAD_O) % s->ike.ivlen != 0)) ++ abort(); ++ ++ if (!enc && (memcmp(block + ISAKMP_I_COOKIE_O, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH) != 0 ++ || memcmp(block + ISAKMP_R_COOKIE_O, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH) != 0)) { ++ DEBUG(2, printf("got paket with wrong cookies\n")); ++ return ISAKMP_N_INVALID_COOKIE; ++ } ++ ++ info_ex = block[ISAKMP_EXCHANGE_TYPE_O] == ISAKMP_EXCHANGE_INFORMATIONAL; ++ ++ if (memcmp(block + ISAKMP_MESSAGE_ID_O, s->ike.current_iv_msgid, 4) != 0) { ++ gcry_md_hd_t md_ctx; ++ ++ gcry_md_open(&md_ctx, s->ike.md_algo, 0); ++ gcry_md_write(md_ctx, s->ike.initial_iv, s->ike.ivlen); ++ gcry_md_write(md_ctx, block + ISAKMP_MESSAGE_ID_O, 4); ++ gcry_md_final(md_ctx); ++ if (info_ex) { ++ iv = xallocc(s->ike.ivlen); ++ memcpy(iv, gcry_md_read(md_ctx, 0), s->ike.ivlen); ++ } else { ++ memcpy(s->ike.current_iv, gcry_md_read(md_ctx, 0), s->ike.ivlen); ++ memcpy(s->ike.current_iv_msgid, block + ISAKMP_MESSAGE_ID_O, 4); ++ } ++ gcry_md_close(md_ctx); ++ } else if (info_ex) { ++ abort(); ++ } ++ ++ if (!info_ex) { ++ iv = s->ike.current_iv; ++ } ++ ++ new_iv = xallocc(s->ike.ivlen); ++ gcry_cipher_open(&cry_ctx, s->ike.cry_algo, GCRY_CIPHER_MODE_CBC, 0); ++ gcry_cipher_setkey(cry_ctx, s->ike.key, s->ike.keylen); ++ gcry_cipher_setiv(cry_ctx, iv, s->ike.ivlen); ++ if (!enc) { ++ memcpy(new_iv, block + blocklen - s->ike.ivlen, s->ike.ivlen); ++ gcry_cipher_decrypt(cry_ctx, block + ISAKMP_PAYLOAD_O, blocklen - ISAKMP_PAYLOAD_O, ++ NULL, 0); ++ if (!info_ex) ++ memcpy(s->ike.current_iv, new_iv, s->ike.ivlen); ++ } else { ++ gcry_cipher_encrypt(cry_ctx, block + ISAKMP_PAYLOAD_O, blocklen - ISAKMP_PAYLOAD_O, ++ NULL, 0); ++ if (!info_ex) ++ memcpy(s->ike.current_iv, block + blocklen - s->ike.ivlen, s->ike.ivlen); ++ } ++ gcry_cipher_close(cry_ctx); ++ ++ free(new_iv); ++ if (info_ex) ++ free(iv); ++ ++ return 0; ++} ++ ++static uint16_t unpack_verify_phase2(struct sa_block *s, uint8_t * r_packet, ++ size_t r_length, struct isakmp_packet **r_p, const uint8_t * nonce, size_t nonce_size) ++{ ++ struct isakmp_packet *r; ++ int reject = 0; ++ ++ *r_p = NULL; ++ ++ if (r_length < ISAKMP_PAYLOAD_O || ((r_length - ISAKMP_PAYLOAD_O) % s->ike.ivlen != 0)) { ++ DEBUG(2, printf("payload too short or not padded: len=%lld, min=%d (ivlen=%lld)\n", ++ (long long)r_length, ISAKMP_PAYLOAD_O, (long long)s->ike.ivlen)); ++ return ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS; ++ } ++ ++ reject = isakmp_crypt(s, r_packet, r_length, 0); ++ if (reject != 0) ++ return reject; ++ ++ s->ike.life.rx += r_length; ++ ++ { ++ r = parse_isakmp_packet(r_packet, r_length, &reject); ++ if (reject != 0) ++ return reject; ++ } ++ ++ /* Verify the basic stuff. */ ++ if (r->flags != ISAKMP_FLAG_E) ++ return ISAKMP_N_INVALID_FLAGS; ++ ++ { ++ size_t sz, spos; ++ gcry_md_hd_t hm; ++ unsigned char *expected_hash; ++ struct isakmp_payload *h = r->payload; ++ ++ if (h == NULL || h->type != ISAKMP_PAYLOAD_HASH || h->u.hash.length != s->ike.md_len) ++ return ISAKMP_N_INVALID_HASH_INFORMATION; ++ ++ spos = (ISAKMP_PAYLOAD_O + (r_packet[ISAKMP_PAYLOAD_O + 2] << 8) ++ + r_packet[ISAKMP_PAYLOAD_O + 3]); ++ ++ /* Compute the real length based on the payload lengths. */ ++ for (sz = spos; r_packet[sz] != 0; sz += r_packet[sz + 2] << 8 | r_packet[sz + 3]) ; ++ sz += r_packet[sz + 2] << 8 | r_packet[sz + 3]; ++ ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, s->ike.skeyid_a, s->ike.md_len); ++ gcry_md_write(hm, r_packet + ISAKMP_MESSAGE_ID_O, 4); ++ if (nonce) ++ gcry_md_write(hm, nonce, nonce_size); ++ gcry_md_write(hm, r_packet + spos, sz - spos); ++ gcry_md_final(hm); ++ expected_hash = gcry_md_read(hm, 0); ++ ++ if (opt_debug >= 3) { ++ printf("hashlen: %lu\n", (unsigned long)s->ike.md_len); ++ printf("u.hash.length: %d\n", h->u.hash.length); ++ hex_dump("expected_hash", expected_hash, s->ike.md_len, NULL); ++ hex_dump("h->u.hash.data", h->u.hash.data, s->ike.md_len, NULL); ++ } ++ ++ reject = 0; ++ if (memcmp(h->u.hash.data, expected_hash, s->ike.md_len) != 0) ++ reject = ISAKMP_N_AUTHENTICATION_FAILED; ++ gcry_md_close(hm); ++#if 0 ++ if (reject != 0) ++ return reject; ++#endif ++ } ++ *r_p = r; ++ return 0; ++} ++ ++static void ++phase2_authpacket(struct sa_block *s, struct isakmp_payload *pl, ++ uint8_t exchange_type, uint32_t msgid, ++ uint8_t ** p_flat, size_t * p_size, ++ uint8_t * nonce_i, int ni_len, uint8_t * nonce_r, int nr_len) ++{ ++ struct isakmp_packet *p; ++ uint8_t *pl_flat; ++ size_t pl_size; ++ gcry_md_hd_t hm; ++ uint8_t msgid_sent[4]; ++ ++ /* Build up the packet. */ ++ p = new_isakmp_packet(); ++ memcpy(p->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ memcpy(p->r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ p->flags = ISAKMP_FLAG_E; ++ p->isakmp_version = ISAKMP_VERSION; ++ p->exchange_type = exchange_type; ++ p->message_id = msgid; ++ p->payload = new_isakmp_payload(ISAKMP_PAYLOAD_HASH); ++ p->payload->next = pl; ++ p->payload->u.hash.length = s->ike.md_len; ++ p->payload->u.hash.data = xallocc(s->ike.md_len); ++ ++ /* Set the MAC. */ ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, s->ike.skeyid_a, s->ike.md_len); ++ ++ if (pl == NULL) { ++ DEBUG(3, printf("authing NULL package!\n")); ++ gcry_md_write(hm, "" /* \0 */ , 1); ++ } ++ ++ msgid_sent[0] = msgid >> 24; ++ msgid_sent[1] = msgid >> 16; ++ msgid_sent[2] = msgid >> 8; ++ msgid_sent[3] = msgid; ++ gcry_md_write(hm, msgid_sent, sizeof(msgid_sent)); ++ ++ if (nonce_i != NULL) ++ gcry_md_write(hm, nonce_i, ni_len); ++ ++ if (nonce_r != NULL) ++ gcry_md_write(hm, nonce_r, nr_len); ++ ++ if (pl != NULL) { ++ flatten_isakmp_payload(pl, &pl_flat, &pl_size); ++ gcry_md_write(hm, pl_flat, pl_size); ++ memset(pl_flat, 0, pl_size); ++ free(pl_flat); ++ } ++ ++ gcry_md_final(hm); ++ memcpy(p->payload->u.hash.data, gcry_md_read(hm, 0), s->ike.md_len); ++ gcry_md_close(hm); ++ ++ flatten_isakmp_packet(p, p_flat, p_size, s->ike.ivlen); ++ free_isakmp_packet(p); ++} ++ ++static void sendrecv_phase2(struct sa_block *s, struct isakmp_payload *pl, ++ uint8_t exchange_type, uint32_t msgid, int sendonly, ++ uint8_t ** save_p_flat, size_t * save_p_size, ++ uint8_t * nonce_i, int ni_len, uint8_t * nonce_r, int nr_len) ++{ ++ uint8_t *p_flat; ++ size_t p_size; ++ ssize_t recvlen; ++ ++ if ((save_p_flat == NULL) || (*save_p_flat == NULL)) { ++ phase2_authpacket(s, pl, exchange_type, msgid, &p_flat, &p_size, ++ nonce_i, ni_len, nonce_r, nr_len); ++ isakmp_crypt(s, p_flat, p_size, 1); ++ } else { ++ p_flat = *save_p_flat; ++ p_size = *save_p_size; ++ } ++ ++ s->ike.life.tx += p_size; ++ ++ recvlen = sendrecv(s, r_packet, sizeof(r_packet), p_flat, p_size, sendonly); ++ if (sendonly == 0) ++ r_length = recvlen; ++ ++ if (save_p_flat == NULL) { ++ free(p_flat); ++ } else { ++ *save_p_flat = p_flat; ++ *save_p_size = p_size; ++ } ++} ++ ++void keepalive_ike(struct sa_block *s) ++{ ++ uint32_t msgid; ++ ++ gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid)); ++ sendrecv_phase2(s, NULL, ISAKMP_EXCHANGE_INFORMATIONAL, msgid, 1, 0, 0, 0, 0, 0, 0); ++} ++ ++static void phase2_fatal(struct sa_block *s, const char *msg, int id) ++{ ++ struct isakmp_payload *pl; ++ uint32_t msgid; ++ ++ DEBUG(1, printf("\n\n---!!!!!!!!! entering phase2_fatal !!!!!!!!!---\n\n\n")); ++ gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid)); ++ pl = new_isakmp_payload(ISAKMP_PAYLOAD_N); ++ pl->u.n.doi = ISAKMP_DOI_IPSEC; ++ pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP; ++ pl->u.n.type = id; ++ sendrecv_phase2(s, pl, ISAKMP_EXCHANGE_INFORMATIONAL, msgid, 1, 0, 0, 0, 0, 0, 0); ++ ++ gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid)); ++ pl = new_isakmp_payload(ISAKMP_PAYLOAD_D); ++ pl->u.d.doi = ISAKMP_DOI_IPSEC; ++ pl->u.d.protocol = ISAKMP_IPSEC_PROTO_ISAKMP; ++ pl->u.d.spi_length = 2 * ISAKMP_COOKIE_LENGTH; ++ pl->u.d.num_spi = 1; ++ pl->u.d.spi = xallocc(1 * sizeof(uint8_t *)); ++ pl->u.d.spi[0] = xallocc(2 * ISAKMP_COOKIE_LENGTH); ++ memcpy(pl->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ memcpy(pl->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ sendrecv_phase2(s, pl, ISAKMP_EXCHANGE_INFORMATIONAL, msgid, 1, 0, 0, 0, 0, 0, 0); ++ ++ error(1, 0, msg, val_to_string(id, isakmp_notify_enum_array), id); ++} ++ ++static uint8_t *gen_keymat(struct sa_block *s, ++ uint8_t protocol, uint32_t spi, ++ const uint8_t * dh_shared, size_t dh_size, ++ const uint8_t * ni_data, size_t ni_size, const uint8_t * nr_data, size_t nr_size) ++{ ++ gcry_md_hd_t hm; ++ uint8_t *block; ++ int i; ++ int blksz; ++ int cnt; ++ ++ blksz = s->ipsec.md_len + s->ipsec.key_len; ++ cnt = (blksz + s->ike.md_len - 1) / s->ike.md_len; ++ block = xallocc(cnt * s->ike.md_len); ++ DEBUG(3, printf("generating %d bytes keymat (cnt=%d)\n", blksz, cnt)); ++ if (cnt < 1) ++ abort(); ++ ++ for (i = 0; i < cnt; i++) { ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, s->ike.skeyid_d, s->ike.md_len); ++ if (i != 0) ++ gcry_md_write(hm, block + (i - 1) * s->ike.md_len, s->ike.md_len); ++ if (dh_shared != NULL) ++ gcry_md_write(hm, dh_shared, dh_size); ++ gcry_md_write(hm, &protocol, 1); ++ gcry_md_write(hm, (uint8_t *) & spi, sizeof(spi)); ++ gcry_md_write(hm, ni_data, ni_size); ++ gcry_md_write(hm, nr_data, nr_size); ++ gcry_md_final(hm); ++ memcpy(block + i * s->ike.md_len, gcry_md_read(hm, 0), s->ike.md_len); ++ gcry_md_close(hm); ++ } ++ return block; ++} ++ ++static int do_config_to_env(struct sa_block *s, struct isakmp_attribute *a) ++{ ++ int i; ++ int reject = 0; ++ int seen_address = 0; ++ char *strbuf, *strbuf2; ++ ++ unsetenv("CISCO_BANNER"); ++ unsetenv("CISCO_DEF_DOMAIN"); ++ unsetenv("CISCO_SPLIT_INC"); ++ unsetenv("INTERNAL_IP4_NBNS"); ++ unsetenv("INTERNAL_IP4_DNS"); ++ unsetenv("INTERNAL_IP4_NETMASK"); ++ unsetenv("INTERNAL_IP4_ADDRESS"); ++ ++ for (; a && reject == 0; a = a->next) ++ switch (a->type) { ++ case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS: ++ if (a->af != isakmp_attr_lots || a->u.lots.length != 4) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ else { ++ addenv_ipv4("INTERNAL_IP4_ADDRESS", a->u.lots.data); ++ memcpy(s->our_address, a->u.lots.data, 4); ++ } ++ seen_address = 1; ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK: ++ if (a->af == isakmp_attr_lots && a->u.lots.length == 0) { ++ DEBUG(2, printf("ignoring zero length netmask\n")); ++ continue; ++ } ++ if (a->af != isakmp_attr_lots || a->u.lots.length != 4) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ else ++ addenv_ipv4("INTERNAL_IP4_NETMASK", a->u.lots.data); ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS: ++ if (a->af != isakmp_attr_lots || a->u.lots.length != 4) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ else ++ addenv_ipv4("INTERNAL_IP4_DNS", a->u.lots.data); ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS: ++ if (a->af != isakmp_attr_lots || a->u.lots.length != 4) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ else ++ addenv_ipv4("INTERNAL_IP4_NBNS", a->u.lots.data); ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN: ++ if (a->af != isakmp_attr_lots) { ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ break; ++ } ++ strbuf = xallocc(a->u.lots.length + 1); ++ memcpy(strbuf, a->u.lots.data, a->u.lots.length); ++ addenv("CISCO_DEF_DOMAIN", strbuf); ++ free(strbuf); ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_CISCO_BANNER: ++ if (a->af != isakmp_attr_lots) { ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ break; ++ } ++ strbuf = xallocc(a->u.lots.length + 1); ++ memcpy(strbuf, a->u.lots.data, a->u.lots.length); ++ addenv("CISCO_BANNER", strbuf); ++ free(strbuf); ++ DEBUG(1, printf("Banner: ")); ++ DEBUG(1, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout)); ++ DEBUG(1, printf("\n")); ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION: ++ DEBUG(2, printf("Remote Application Version: ")); ++ DEBUG(2, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout)); ++ DEBUG(2, printf("\n")); ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS: ++ if (a->af != isakmp_attr_16) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ else { ++ s->ipsec.do_pfs = a->u.attr_16; ++ DEBUG(2, printf("got pfs setting: %d\n", s->ipsec.do_pfs)); ++ } ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT: ++ if (a->af != isakmp_attr_16) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ else { ++ s->ipsec.peer_udpencap_port = a->u.attr_16; ++ DEBUG(2, printf("got peer udp encapsulation port: %hu\n", s->ipsec.peer_udpencap_port)); ++ } ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC: ++ if (a->af != isakmp_attr_acl) { ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ break; ++ } ++ ++ DEBUG(2, printf("got %d acls for split include\n", a->u.acl.count)); ++ asprintf(&strbuf, "%d", a->u.acl.count); ++ setenv("CISCO_SPLIT_INC", strbuf, 1); ++ free(strbuf); ++ ++ for (i = 0; i < a->u.acl.count; i++) { ++ DEBUG(2, printf("acl %d: ", i)); ++ /* NOTE: inet_ntoa returns one static buffer */ ++ ++ asprintf(&strbuf, "CISCO_SPLIT_INC_%d_ADDR", i); ++ asprintf(&strbuf2, "%s", inet_ntoa(a->u.acl.acl_ent[i].addr)); ++ DEBUG(2, printf("addr: %s/", strbuf2)); ++ setenv(strbuf, strbuf2, 1); ++ free(strbuf); free(strbuf2); ++ ++ asprintf(&strbuf, "CISCO_SPLIT_INC_%d_MASK", i); ++ asprintf(&strbuf2, "%s", inet_ntoa(a->u.acl.acl_ent[i].mask)); ++ DEBUG(2, printf("%s ", strbuf2)); ++ setenv(strbuf, strbuf2, 1); ++ free(strbuf); free(strbuf2); ++ ++ { /* this is just here because ip route does not accept netmasks */ ++ int len; ++ uint32_t addr; ++ ++ for (len = 0, addr = ntohl(a->u.acl.acl_ent[i].mask.s_addr); ++ addr; addr <<= 1, len++) ++ ; /* do nothing */ ++ ++ asprintf(&strbuf, "CISCO_SPLIT_INC_%d_MASKLEN", i); ++ asprintf(&strbuf2, "%d", len); ++ DEBUG(2, printf("(%s), ", strbuf2)); ++ setenv(strbuf, strbuf2, 1); ++ free(strbuf); free(strbuf2); ++ } ++ ++ asprintf(&strbuf, "CISCO_SPLIT_INC_%d_PROTOCOL", i); ++ asprintf(&strbuf2, "%hu", a->u.acl.acl_ent[i].protocol); ++ DEBUG(2, printf("protocol: %s, ", strbuf2)); ++ setenv(strbuf, strbuf2, 1); ++ free(strbuf); free(strbuf2); ++ ++ asprintf(&strbuf, "CISCO_SPLIT_INC_%d_SPORT", i); ++ asprintf(&strbuf2, "%hu", a->u.acl.acl_ent[i].sport); ++ DEBUG(2, printf("sport: %s, ", strbuf2)); ++ setenv(strbuf, strbuf2, 1); ++ free(strbuf); free(strbuf2); ++ ++ asprintf(&strbuf, "CISCO_SPLIT_INC_%d_DPORT", i); ++ asprintf(&strbuf2, "%hu", a->u.acl.acl_ent[i].dport); ++ DEBUG(2, printf("dport: %s\n", strbuf2)); ++ setenv(strbuf, strbuf2, 1); ++ free(strbuf); free(strbuf2); ++ } ++ break; ++ ++ case ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW: ++ DEBUG(2, printf("got save password setting: %d\n", a->u.attr_16)); ++ break; ++ ++ default: ++ DEBUG(2, printf("unknown attribute %d / 0x%X\n", a->type, a->type)); ++ break; ++ } ++ ++ if (reject == 0 && !seen_address) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ ++ return reject; ++} ++ ++/* * */ ++ ++static struct isakmp_attribute *make_transform_ike(int dh_group, int crypt, int hash, int keylen, int auth) ++{ ++ struct isakmp_attribute *a = NULL; ++ ++ a = new_isakmp_attribute(IKE_ATTRIB_LIFE_DURATION, a); ++ a->af = isakmp_attr_lots; ++ a->u.lots.length = 4; ++ a->u.lots.data = xallocc(a->u.lots.length); ++ *((uint32_t *) a->u.lots.data) = htonl(2147483); ++ a = new_isakmp_attribute_16(IKE_ATTRIB_LIFE_TYPE, IKE_LIFE_TYPE_SECONDS, a); ++ a = new_isakmp_attribute_16(IKE_ATTRIB_GROUP_DESC, dh_group, a); ++ a = new_isakmp_attribute_16(IKE_ATTRIB_AUTH_METHOD, auth, a); ++ a = new_isakmp_attribute_16(IKE_ATTRIB_HASH, hash, a); ++ a = new_isakmp_attribute_16(IKE_ATTRIB_ENC, crypt, a); ++ if (keylen != 0) ++ a = new_isakmp_attribute_16(IKE_ATTRIB_KEY_LENGTH, keylen, a); ++ return a; ++} ++ ++static struct isakmp_payload *make_our_sa_ike(void) ++{ ++ struct isakmp_payload *r = new_isakmp_payload(ISAKMP_PAYLOAD_SA); ++ struct isakmp_payload *t = NULL, *tn; ++ struct isakmp_attribute *a; ++ int dh_grp = get_dh_group_ike()->ike_sa_id; ++ unsigned int auth, crypt, hash, keylen; ++ int i; ++ ++ r->u.sa.doi = ISAKMP_DOI_IPSEC; ++ r->u.sa.situation = ISAKMP_IPSEC_SIT_IDENTITY_ONLY; ++ r->u.sa.proposals = new_isakmp_payload(ISAKMP_PAYLOAD_P); ++ r->u.sa.proposals->u.p.prot_id = ISAKMP_IPSEC_PROTO_ISAKMP; ++ for (auth = 0; supp_auth[auth].name != NULL; auth++) { ++ for (crypt = 0; supp_crypt[crypt].name != NULL; crypt++) { ++ keylen = supp_crypt[crypt].keylen; ++ for (hash = 0; supp_hash[hash].name != NULL; hash++) { ++ tn = t; ++ t = new_isakmp_payload(ISAKMP_PAYLOAD_T); ++ t->u.t.id = ISAKMP_IPSEC_KEY_IKE; ++ a = make_transform_ike(dh_grp, supp_crypt[crypt].ike_sa_id, ++ supp_hash[hash].ike_sa_id, keylen, supp_auth[auth].ike_sa_id); ++ t->u.t.attributes = a; ++ t->next = tn; ++ } ++ } ++ } ++ for (i = 0, tn = t; tn; tn = tn->next) ++ tn->u.t.number = i++; ++ r->u.sa.proposals->u.p.transforms = t; ++ return r; ++} ++ ++static void lifetime_ike_process(struct sa_block *s, struct isakmp_attribute *a) ++{ ++ uint32_t value; ++ ++ assert(a != NULL); ++ assert(a->type == IKE_ATTRIB_LIFE_TYPE); ++ assert(a->af == isakmp_attr_16); ++ assert(a->u.attr_16 == IKE_LIFE_TYPE_SECONDS || a->u.attr_16 == IKE_LIFE_TYPE_K); ++ assert(a->next != NULL); ++ assert(a->next->type == IKE_ATTRIB_LIFE_DURATION); ++ ++ if (a->next->af == isakmp_attr_16) ++ value = a->next->u.attr_16; ++ else if (a->next->af == isakmp_attr_lots && a->next->u.lots.length == 4) ++ value = ntohl(*((uint32_t *) a->next->u.lots.data)); ++ else ++ assert(0); ++ ++ DEBUG(2, printf("got ike lifetime attributes: %d %s\n", value, ++ (a->u.attr_16 == IKE_LIFE_TYPE_SECONDS) ? "seconds" : "kilobyte")); ++ ++ if (a->u.attr_16 == IKE_LIFE_TYPE_SECONDS) ++ s->ike.life.seconds = value; ++ else ++ s->ike.life.kbytes = value; ++} ++ ++static void lifetime_ipsec_process(struct sa_block *s, struct isakmp_attribute *a) ++{ ++ uint32_t value; ++ ++ assert(a != NULL); ++ assert(a->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE); ++ assert(a->af == isakmp_attr_16); ++ assert(a->u.attr_16 == IPSEC_LIFE_SECONDS || a->u.attr_16 == IPSEC_LIFE_K); ++ assert(a->next != NULL); ++ assert(a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION); ++ ++ if (a->next->af == isakmp_attr_16) ++ value = a->next->u.attr_16; ++ else if (a->next->af == isakmp_attr_lots && a->next->u.lots.length == 4) ++ value = ntohl(*((uint32_t *) a->next->u.lots.data)); ++ else ++ assert(0); ++ ++ DEBUG(2, printf("got ipsec lifetime attributes: %d %s\n", value, ++ (a->u.attr_16 == IPSEC_LIFE_SECONDS) ? "seconds" : "kilobyte")); ++ ++ if (a->u.attr_16 == IPSEC_LIFE_SECONDS) ++ s->ipsec.life.seconds = value; ++ else ++ s->ipsec.life.kbytes = value; ++} ++ ++static void do_phase_1(const char *key_id, const char *shared_key, struct sa_block *s) ++{ ++ unsigned char i_nonce[20]; ++ struct group *dh_grp; ++ unsigned char *dh_public; ++ unsigned char *returned_hash; ++ static const uint8_t xauth_vid[] = XAUTH_VENDOR_ID; ++ static const uint8_t unity_vid[] = UNITY_VENDOR_ID; ++ static const uint8_t unknown_vid[] = UNKNOWN_VENDOR_ID; ++ /* NAT traversal */ ++ static const uint8_t natt_vid_00[] = NATT_VENDOR_ID_00; ++ static const uint8_t natt_vid_01[] = NATT_VENDOR_ID_01; ++ static const uint8_t natt_vid_02[] = NATT_VENDOR_ID_02; ++ static const uint8_t natt_vid_02n[] = NATT_VENDOR_ID_02n; ++ static const uint8_t natt_vid_rfc[] = NATT_VENDOR_ID_RFC; ++#if 0 ++ static const uint8_t dpd_vid[] = DPD_VENDOR_ID; /* dead peer detection */ ++ static const uint8_t my_vid[] = { ++ 0x35, 0x53, 0x07, 0x6c, 0x4f, 0x65, 0x12, 0x68, 0x02, 0x82, 0xf2, 0x15, ++ 0x8a, 0xa8, 0xa0, 0x9e ++ }; ++#endif ++ ++ struct isakmp_packet *p1; ++ int seen_natt_vid = 0, seen_natd = 0, seen_natd_them = 0, seen_natd_us = 0, natd_type = 0; ++ unsigned char *natd_us = NULL, *natd_them = NULL; ++ int natt_draft = -1; ++ ++ DEBUG(2, printf("S4.1\n")); ++ gcry_create_nonce(s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ s->ike.life.start = time(NULL); ++ s->ipsec.do_pfs = -1; ++ if (s->ike.i_cookie[0] == 0) ++ s->ike.i_cookie[0] = 1; ++ hex_dump("i_cookie", s->ike.i_cookie, ISAKMP_COOKIE_LENGTH, NULL); ++ gcry_create_nonce(i_nonce, sizeof(i_nonce)); ++ hex_dump("i_nonce", i_nonce, sizeof(i_nonce), NULL); ++ DEBUG(2, printf("S4.2\n")); ++ /* Set up the Diffie-Hellman stuff. */ ++ { ++ dh_grp = group_get(get_dh_group_ike()->my_id); ++ dh_public = xallocc(dh_getlen(dh_grp)); ++ dh_create_exchange(dh_grp, dh_public); ++ hex_dump("dh_public", dh_public, dh_getlen(dh_grp), NULL); ++ } ++ ++ DEBUG(2, printf("S4.3\n")); ++ /* Create the first packet. */ ++ { ++ struct isakmp_payload *l; ++ uint8_t *pkt; ++ size_t pkt_len; ++ ++ p1 = new_isakmp_packet(); ++ memcpy(p1->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ p1->isakmp_version = ISAKMP_VERSION; ++ p1->exchange_type = ISAKMP_EXCHANGE_AGGRESSIVE; ++ p1->payload = l = make_our_sa_ike(); ++ l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_KE, dh_public, dh_getlen(dh_grp)); ++ l->next->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE, ++ i_nonce, sizeof(i_nonce)); ++ l = l->next->next; ++ l->next = new_isakmp_payload(ISAKMP_PAYLOAD_ID); ++ l = l->next; ++ if (opt_vendor == VENDOR_CISCO) ++ l->u.id.type = ISAKMP_IPSEC_ID_KEY_ID; ++ else ++ l->u.id.type = ISAKMP_IPSEC_ID_USER_FQDN; ++ l->u.id.protocol = IPPROTO_UDP; ++ l->u.id.port = 500; /* this must be 500, not local_port */ ++ l->u.id.length = strlen(key_id); ++ l->u.id.data = xallocc(l->u.id.length); ++ memcpy(l->u.id.data, key_id, strlen(key_id)); ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ xauth_vid, sizeof(xauth_vid)); ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ unity_vid, sizeof(unity_vid)); ++ if ((opt_natt_mode == NATT_NORMAL) || (opt_natt_mode == NATT_FORCE)) { ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ natt_vid_rfc, sizeof(natt_vid_rfc)); ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ natt_vid_02n, sizeof(natt_vid_02n)); ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ natt_vid_02, sizeof(natt_vid_02)); ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ natt_vid_01, sizeof(natt_vid_01)); ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ natt_vid_00, sizeof(natt_vid_00)); ++ } ++#if 0 ++ l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ dpd_vid, sizeof(dpd_vid)); ++#endif ++ flatten_isakmp_packet(p1, &pkt, &pkt_len, 0); ++ ++ /* Now, send that packet and receive a new one. */ ++ r_length = sendrecv(s, r_packet, sizeof(r_packet), pkt, pkt_len, 0); ++ free(pkt); ++ } ++ DEBUG(2, printf("S4.4\n")); ++ /* Decode the recieved packet. */ ++ { ++ struct isakmp_packet *r; ++ int reject; ++ struct isakmp_payload *rp; ++ struct isakmp_payload *nonce = NULL; ++ struct isakmp_payload *ke = NULL; ++ struct isakmp_payload *hash = NULL; ++ struct isakmp_payload *idp = NULL; ++ int seen_sa = 0, seen_xauth_vid = 0; ++ unsigned char *skeyid; ++ gcry_md_hd_t skeyid_ctx; ++ ++ reject = 0; ++ r = parse_isakmp_packet(r_packet, r_length, &reject); ++ ++ /* Verify the correctness of the recieved packet. */ ++ if (reject == 0 && memcmp(r->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH) != 0) ++ reject = ISAKMP_N_INVALID_COOKIE; ++ if (reject == 0) ++ memcpy(s->ike.r_cookie, r->r_cookie, ISAKMP_COOKIE_LENGTH); ++ if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_AGGRESSIVE) ++ reject = ISAKMP_N_INVALID_EXCHANGE_TYPE; ++ if (reject == 0 && r->flags != 0) ++ reject = ISAKMP_N_INVALID_FLAGS; ++ if (reject == 0 && r->message_id != 0) ++ reject = ISAKMP_N_INVALID_MESSAGE_ID; ++ if (reject != 0) ++ error(1, 0, "response was invalid [1]: %s(%d)", val_to_string(reject, isakmp_notify_enum_array), reject); ++ for (rp = r->payload; rp && reject == 0; rp = rp->next) ++ switch (rp->type) { ++ case ISAKMP_PAYLOAD_SA: ++ if (reject == 0 && rp->u.sa.doi != ISAKMP_DOI_IPSEC) ++ reject = ISAKMP_N_DOI_NOT_SUPPORTED; ++ if (reject == 0 && ++ rp->u.sa.situation != ISAKMP_IPSEC_SIT_IDENTITY_ONLY) ++ reject = ISAKMP_N_SITUATION_NOT_SUPPORTED; ++ if (reject == 0 && ++ (rp->u.sa.proposals == NULL ++ || rp->u.sa.proposals->next != NULL)) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (reject == 0 && ++ rp->u.sa.proposals->u.p.prot_id != ++ ISAKMP_IPSEC_PROTO_ISAKMP) ++ reject = ISAKMP_N_INVALID_PROTOCOL_ID; ++ if (reject == 0 && rp->u.sa.proposals->u.p.spi_size != 0) ++ reject = ISAKMP_N_INVALID_SPI; ++ if (reject == 0 && ++ (rp->u.sa.proposals->u.p.transforms == NULL ++ || rp->u.sa.proposals->u.p.transforms->next != ++ NULL)) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (reject == 0 && ++ (rp->u.sa.proposals->u.p.transforms->u.t.id ++ != ISAKMP_IPSEC_KEY_IKE)) ++ reject = ISAKMP_N_INVALID_TRANSFORM_ID; ++ if (reject == 0) { ++ struct isakmp_attribute *a ++ = ++ rp->u.sa.proposals->u.p.transforms->u.t.attributes; ++ int seen_enc = 0, seen_hash = 0, seen_auth = 0; ++ int seen_group = 0, seen_keylen = 0; ++ for (; a && reject == 0; a = a->next) ++ switch (a->type) { ++ case IKE_ATTRIB_GROUP_DESC: ++ if (a->af == isakmp_attr_16 && ++ a->u.attr_16 == ++ get_dh_group_ike()->ike_sa_id) ++ seen_group = 1; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case IKE_ATTRIB_AUTH_METHOD: ++ if (a->af == isakmp_attr_16) ++ seen_auth = a->u.attr_16; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case IKE_ATTRIB_HASH: ++ if (a->af == isakmp_attr_16) ++ seen_hash = a->u.attr_16; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case IKE_ATTRIB_ENC: ++ if (a->af == isakmp_attr_16) ++ seen_enc = a->u.attr_16; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case IKE_ATTRIB_KEY_LENGTH: ++ if (a->af == isakmp_attr_16) ++ seen_keylen = a->u.attr_16; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case IKE_ATTRIB_LIFE_TYPE: ++ /* lifetime duration MUST follow lifetype attribute */ ++ if (a->next->type == IKE_ATTRIB_LIFE_DURATION) { ++ lifetime_ike_process(s, a); ++ } else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case IKE_ATTRIB_LIFE_DURATION: ++ /* already processed above in IKE_ATTRIB_LIFE_TYPE: */ ++ break; ++ default: ++ DEBUG(1, printf ++ ("unknown attribute %d, arborting..\n", ++ a->type)); ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ break; ++ } ++ if (!seen_group || !seen_auth || !seen_hash || !seen_enc) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ if (get_algo(SUPP_ALGO_AUTH, SUPP_ALGO_IKE_SA, seen_auth, ++ NULL, 0) == NULL) ++ reject = ISAKMP_N_NO_PROPOSAL_CHOSEN; ++ if (get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IKE_SA, seen_hash, ++ NULL, 0) == NULL) ++ reject = ISAKMP_N_NO_PROPOSAL_CHOSEN; ++ if (get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IKE_SA, seen_enc, ++ NULL, seen_keylen) == NULL) ++ reject = ISAKMP_N_NO_PROPOSAL_CHOSEN; ++ ++ if (reject == 0) { ++ seen_sa = 1; ++ s->ike.auth_algo = seen_auth; ++ s->ike.cry_algo = ++ get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IKE_SA, ++ seen_enc, NULL, seen_keylen)->my_id; ++ s->ike.md_algo = ++ get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IKE_SA, ++ seen_hash, NULL, 0)->my_id; ++ s->ike.md_len = gcry_md_get_algo_dlen(s->ike.md_algo); ++ DEBUG(1, printf("IKE SA selected %s-%s-%s\n", ++ get_algo(SUPP_ALGO_AUTH, ++ SUPP_ALGO_IKE_SA, seen_auth, ++ NULL, 0)->name, ++ get_algo(SUPP_ALGO_CRYPT, ++ SUPP_ALGO_IKE_SA, seen_enc, ++ NULL, seen_keylen)->name, ++ get_algo(SUPP_ALGO_HASH, ++ SUPP_ALGO_IKE_SA, seen_hash, ++ NULL, 0)->name)); ++ if (s->ike.cry_algo == GCRY_CIPHER_DES && !opt_1des) { ++ error(1, 0, "peer selected (single) DES as \"encrytion\" method.\n" ++ "This algorithm is considered too weak today\n" ++ "If your vpn concentrator admin still insists on using DES\n" ++ "use the \"--enable-1des\" option.\n"); ++ } ++ } ++ } ++ break; ++ ++ case ISAKMP_PAYLOAD_ID: ++ idp = rp; ++ break; ++ case ISAKMP_PAYLOAD_KE: ++ ke = rp; ++ break; ++ case ISAKMP_PAYLOAD_NONCE: ++ nonce = rp; ++ break; ++ case ISAKMP_PAYLOAD_HASH: ++ hash = rp; ++ break; ++ case ISAKMP_PAYLOAD_VID: ++ if (rp->u.vid.length == sizeof(xauth_vid) ++ && memcmp(rp->u.vid.data, xauth_vid, ++ sizeof(xauth_vid)) == 0) { ++ seen_xauth_vid = 1; ++ } else if (rp->u.vid.length == sizeof(natt_vid_rfc) ++ && memcmp(rp->u.vid.data, natt_vid_rfc, ++ sizeof(natt_vid_rfc)) == 0) { ++ seen_natt_vid = 1; ++ if (natt_draft < 1) natt_draft = 2; ++ DEBUG(2, printf("peer is NAT-T capable (RFC 3947)\n")); ++ } else if (rp->u.vid.length == sizeof(natt_vid_02n) ++ && memcmp(rp->u.vid.data, natt_vid_02n, ++ sizeof(natt_vid_02n)) == 0) { ++ seen_natt_vid = 1; ++ if (natt_draft < 1) natt_draft = 2; ++ DEBUG(2, printf("peer is NAT-T capable (draft-02)\n\n")); ++ } else if (rp->u.vid.length == sizeof(natt_vid_02) ++ && memcmp(rp->u.vid.data, natt_vid_02, ++ sizeof(natt_vid_02)) == 0) { ++ seen_natt_vid = 1; ++ if (natt_draft < 1) natt_draft = 2; ++ DEBUG(2, printf("peer is NAT-T capable (draft-02)\n")); ++ } else if (rp->u.vid.length == sizeof(natt_vid_01) ++ && memcmp(rp->u.vid.data, natt_vid_01, ++ sizeof(natt_vid_01)) == 0) { ++ seen_natt_vid = 1; ++ if (natt_draft < 1) natt_draft = 1; ++ DEBUG(2, printf("peer is NAT-T capable (draft-01)\n")); ++ } else if (rp->u.vid.length == sizeof(natt_vid_00) ++ && memcmp(rp->u.vid.data, natt_vid_00, ++ sizeof(natt_vid_00)) == 0) { ++ seen_natt_vid = 1; ++ if (natt_draft < 0) natt_draft = 0; ++ DEBUG(2, printf("peer is NAT-T capable (draft-00)\n")); ++ } else { ++ hex_dump("unknown ISAKMP_PAYLOAD_VID: ", ++ rp->u.vid.data, rp->u.vid.length, NULL); ++ } ++ break; ++ case ISAKMP_PAYLOAD_NAT_D_OLD: ++ case ISAKMP_PAYLOAD_NAT_D: ++ natd_type = rp->type; ++ DEBUG(2, printf("peer is using type %d for NAT-Discovery payloads\n", natd_type)); ++ if (!seen_sa /*|| !seen_natt_vid*/) { ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ } else if (opt_natt_mode == NATT_NONE) { ++ ; ++ } else if (rp->u.natd.length != s->ike.md_len) { ++ reject = ISAKMP_N_PAYLOAD_MALFORMED; ++ } else if (seen_natd == 0) { ++ gcry_md_hd_t hm; ++ uint16_t n_dst_port = htons(s->ike.dst_port); ++ ++ natd_us = xallocc(s->ike.md_len); ++ natd_them = xallocc(s->ike.md_len); ++ memcpy(natd_us, rp->u.natd.data, s->ike.md_len); ++ gcry_md_open(&hm, s->ike.md_algo, 0); ++ gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, &s->dst, sizeof(struct in_addr)); ++ gcry_md_write(hm, &n_dst_port, sizeof(uint16_t)); ++ gcry_md_final(hm); ++ memcpy(natd_them, gcry_md_read(hm, 0), s->ike.md_len); ++ gcry_md_close(hm); ++ seen_natd = 1; ++ } else { ++ if (memcmp(natd_them, rp->u.natd.data, s->ike.md_len) == 0) ++ seen_natd_them = 1; ++ } ++ break; ++ default: ++ DEBUG(1, printf("rejecting invalid payload type %d\n", rp->type)); ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ break; ++ } ++ ++ if (reject == 0) { ++ gcry_cipher_algo_info(s->ike.cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(s->ike.ivlen)); ++ gcry_cipher_algo_info(s->ike.cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(s->ike.keylen)); ++ } ++ ++ if (reject == 0 && (ke == NULL || ke->u.ke.length != dh_getlen(dh_grp))) ++ reject = ISAKMP_N_INVALID_KEY_INFORMATION; ++ if (reject == 0 && nonce == NULL) ++ reject = ISAKMP_N_INVALID_HASH_INFORMATION; ++ if (reject != 0) ++ error(1, 0, "response was invalid [2]: %s(%d)", val_to_string(reject, isakmp_notify_enum_array), reject); ++ if (reject == 0 && idp == NULL) ++ reject = ISAKMP_N_INVALID_ID_INFORMATION; ++ if (reject == 0 && (hash == NULL || hash->u.hash.length != s->ike.md_len)) ++ reject = ISAKMP_N_INVALID_HASH_INFORMATION; ++ if (reject != 0) ++ error(1, 0, "response was invalid [3]: %s(%d)", val_to_string(reject, isakmp_notify_enum_array), reject); ++ ++ /* Generate SKEYID. */ ++ { ++ gcry_md_open(&skeyid_ctx, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(skeyid_ctx, shared_key, strlen(shared_key)); ++ gcry_md_write(skeyid_ctx, i_nonce, sizeof(i_nonce)); ++ gcry_md_write(skeyid_ctx, nonce->u.nonce.data, nonce->u.nonce.length); ++ gcry_md_final(skeyid_ctx); ++ skeyid = gcry_md_read(skeyid_ctx, 0); ++ hex_dump("skeyid", skeyid, s->ike.md_len, NULL); ++ } ++ ++ /* Verify the hash. */ ++ { ++ gcry_md_hd_t hm; ++ unsigned char *expected_hash; ++ uint8_t *sa_f, *idi_f, *idp_f; ++ size_t sa_size, idi_size, idp_size; ++ struct isakmp_payload *sa, *idi; ++ ++ sa = p1->payload; ++ for (idi = sa; idi->type != ISAKMP_PAYLOAD_ID; idi = idi->next) ; ++ sa->next = NULL; ++ idi->next = NULL; ++ idp->next = NULL; ++ flatten_isakmp_payload(sa, &sa_f, &sa_size); ++ flatten_isakmp_payload(idi, &idi_f, &idi_size); ++ flatten_isakmp_payload(idp, &idp_f, &idp_size); ++ ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, skeyid, s->ike.md_len); ++ gcry_md_write(hm, ke->u.ke.data, ke->u.ke.length); ++ gcry_md_write(hm, dh_public, dh_getlen(dh_grp)); ++ gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, sa_f + 4, sa_size - 4); ++ gcry_md_write(hm, idp_f + 4, idp_size - 4); ++ gcry_md_final(hm); ++ expected_hash = gcry_md_read(hm, 0); ++ ++ if (memcmp(expected_hash, hash->u.hash.data, s->ike.md_len) != 0) { ++ error(1, 0, "hash comparison failed: %s(%d)\ncheck group password!", ++ val_to_string(ISAKMP_N_AUTHENTICATION_FAILED, isakmp_notify_enum_array), ++ ISAKMP_N_AUTHENTICATION_FAILED); ++ } ++ gcry_md_close(hm); ++ ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, skeyid, s->ike.md_len); ++ gcry_md_write(hm, dh_public, dh_getlen(dh_grp)); ++ gcry_md_write(hm, ke->u.ke.data, ke->u.ke.length); ++ gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, sa_f + 4, sa_size - 4); ++ gcry_md_write(hm, idi_f + 4, idi_size - 4); ++ gcry_md_final(hm); ++ returned_hash = xallocc(s->ike.md_len); ++ memcpy(returned_hash, gcry_md_read(hm, 0), s->ike.md_len); ++ gcry_md_close(hm); ++ hex_dump("returned_hash", returned_hash, s->ike.md_len, NULL); ++ ++ free(sa_f); ++ free(idi); ++ free(idp); ++ } ++ ++ /* Determine all the SKEYID_x keys. */ ++ { ++ gcry_md_hd_t hm; ++ int i; ++ static const unsigned char c012[3] = { 0, 1, 2 }; ++ unsigned char *skeyid_e; ++ unsigned char *dh_shared_secret; ++ ++ /* Determine the shared secret. */ ++ dh_shared_secret = xallocc(dh_getlen(dh_grp)); ++ dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data); ++ hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL); ++ ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, skeyid, s->ike.md_len); ++ gcry_md_write(hm, dh_shared_secret, dh_getlen(dh_grp)); ++ gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, c012 + 0, 1); ++ gcry_md_final(hm); ++ s->ike.skeyid_d = xallocc(s->ike.md_len); ++ memcpy(s->ike.skeyid_d, gcry_md_read(hm, 0), s->ike.md_len); ++ gcry_md_close(hm); ++ hex_dump("skeyid_d", s->ike.skeyid_d, s->ike.md_len, NULL); ++ ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, skeyid, s->ike.md_len); ++ gcry_md_write(hm, s->ike.skeyid_d, s->ike.md_len); ++ gcry_md_write(hm, dh_shared_secret, dh_getlen(dh_grp)); ++ gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, c012 + 1, 1); ++ gcry_md_final(hm); ++ s->ike.skeyid_a = xallocc(s->ike.md_len); ++ memcpy(s->ike.skeyid_a, gcry_md_read(hm, 0), s->ike.md_len); ++ gcry_md_close(hm); ++ hex_dump("skeyid_a", s->ike.skeyid_a, s->ike.md_len, NULL); ++ ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, skeyid, s->ike.md_len); ++ gcry_md_write(hm, s->ike.skeyid_a, s->ike.md_len); ++ gcry_md_write(hm, dh_shared_secret, dh_getlen(dh_grp)); ++ gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, c012 + 2, 1); ++ gcry_md_final(hm); ++ skeyid_e = xallocc(s->ike.md_len); ++ memcpy(skeyid_e, gcry_md_read(hm, 0), s->ike.md_len); ++ gcry_md_close(hm); ++ hex_dump("skeyid_e", skeyid_e, s->ike.md_len, NULL); ++ ++ memset(dh_shared_secret, 0, sizeof(dh_shared_secret)); ++ ++ /* Determine the IKE encryption key. */ ++ s->ike.key = xallocc(s->ike.keylen); ++ ++ if (s->ike.keylen > s->ike.md_len) { ++ for (i = 0; i * s->ike.md_len < s->ike.keylen; i++) { ++ gcry_md_open(&hm, s->ike.md_algo, GCRY_MD_FLAG_HMAC); ++ gcry_md_setkey(hm, skeyid_e, s->ike.md_len); ++ if (i == 0) ++ gcry_md_write(hm, "" /* &'\0' */ , 1); ++ else ++ gcry_md_write(hm, s->ike.key + (i - 1) * s->ike.md_len, ++ s->ike.md_len); ++ gcry_md_final(hm); ++ memcpy(s->ike.key + i * s->ike.md_len, gcry_md_read(hm, 0), ++ min(s->ike.md_len, s->ike.keylen - i * s->ike.md_len)); ++ gcry_md_close(hm); ++ } ++ } else { /* keylen <= md_len */ ++ memcpy(s->ike.key, skeyid_e, s->ike.keylen); ++ } ++ hex_dump("enc-key", s->ike.key, s->ike.keylen, NULL); ++ ++ memset(skeyid_e, 0, s->ike.md_len); ++ } ++ ++ /* Determine the initial IV. */ ++ { ++ gcry_md_hd_t hm; ++ ++ assert(s->ike.ivlen <= s->ike.md_len); ++ gcry_md_open(&hm, s->ike.md_algo, 0); ++ gcry_md_write(hm, dh_public, dh_getlen(dh_grp)); ++ gcry_md_write(hm, ke->u.ke.data, ke->u.ke.length); ++ gcry_md_final(hm); ++ s->ike.current_iv = xallocc(s->ike.ivlen); ++ memcpy(s->ike.current_iv, gcry_md_read(hm, 0), s->ike.ivlen); ++ gcry_md_close(hm); ++ hex_dump("current_iv", s->ike.current_iv, s->ike.ivlen, NULL); ++ memset(s->ike.current_iv_msgid, 0, 4); ++ } ++ ++ gcry_md_close(skeyid_ctx); ++ } ++ ++ DEBUG(2, printf("S4.5\n")); ++ /* Send final phase 1 packet. */ ++ { ++ struct isakmp_packet *p2; ++ uint8_t *p2kt; ++ size_t p2kt_len; ++ struct isakmp_payload *pl; ++ ++ p2 = new_isakmp_packet(); ++ memcpy(p2->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ memcpy(p2->r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ p2->flags = ISAKMP_FLAG_E; ++ p2->isakmp_version = ISAKMP_VERSION; ++ p2->exchange_type = ISAKMP_EXCHANGE_AGGRESSIVE; ++ p2->payload = new_isakmp_data_payload(ISAKMP_PAYLOAD_HASH, ++ returned_hash, s->ike.md_len); ++ p2->payload->next = pl = new_isakmp_payload(ISAKMP_PAYLOAD_N); ++ pl->u.n.doi = ISAKMP_DOI_IPSEC; ++ pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP; ++ pl->u.n.type = ISAKMP_N_IPSEC_INITIAL_CONTACT; ++ pl->u.n.spi_length = 2 * ISAKMP_COOKIE_LENGTH; ++ pl->u.n.spi = xallocc(2 * ISAKMP_COOKIE_LENGTH); ++ memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ unknown_vid, sizeof(unknown_vid)); ++ pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID, ++ unity_vid, sizeof(unity_vid)); ++ ++ /* include NAT traversal discovery payloads */ ++ if (seen_natt_vid) { ++ assert(natd_type != 0); ++ pl = pl->next = new_isakmp_data_payload(natd_type, ++ natd_them, s->ike.md_len); ++ /* this could be repeated fo any known outbound interfaces */ ++ { ++ gcry_md_hd_t hm; ++ uint16_t n_src_port = htons(s->ike.src_port); ++ ++ gcry_md_open(&hm, s->ike.md_algo, 0); ++ gcry_md_write(hm, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); ++ gcry_md_write(hm, &s->src, sizeof(struct in_addr)); ++ gcry_md_write(hm, &n_src_port, sizeof(uint16_t)); ++ gcry_md_final(hm); ++ pl = pl->next = new_isakmp_data_payload(natd_type, ++ gcry_md_read(hm, 0), s->ike.md_len); ++ if (opt_natt_mode == NATT_FORCE) /* force detection of "this end behind NAT" */ ++ pl->u.ke.data[0] ^= 1; /* by flipping a bit in the nat-detection-hash */ ++ if (seen_natd && memcmp(natd_us, pl->u.ke.data, s->ike.md_len) == 0) ++ seen_natd_us = 1; ++ gcry_md_close(hm); ++ } ++ if (seen_natd) { ++ free(natd_us); ++ free(natd_them); ++ } ++ /* if there is a NAT, change to port 4500 and select UDP encap */ ++ if (!seen_natd_us || !seen_natd_them) { ++ DEBUG(1, printf("NAT status: this end behind NAT? %s -- remote end behind NAT? %s\n", ++ seen_natd_us ? "no" : "YES", seen_natd_them ? "no" : "YES")); ++ switch (natd_type) { ++ case ISAKMP_PAYLOAD_NAT_D: ++ s->ipsec.encap_mode = IPSEC_ENCAP_UDP_TUNNEL; ++ break; ++ case ISAKMP_PAYLOAD_NAT_D_OLD: ++ s->ipsec.encap_mode = IPSEC_ENCAP_UDP_TUNNEL_OLD; ++ break; ++ default: ++ abort(); ++ } ++ if (natt_draft >= 2) { ++ s->ipsec.natt_active_mode = NATT_ACTIVE_RFC; ++ close(s->ike_fd); ++ if (s->ike.src_port == 500) ++ s->ike.src_port = 4500; ++ s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port = 4500); ++ } else { ++ s->ipsec.natt_active_mode = NATT_ACTIVE_DRAFT_OLD; ++ } ++ } else { ++ DEBUG(1, printf("NAT status: NAT-T VID seen, no NAT device detected\n")); ++ } ++ } else { ++ DEBUG(1, printf("NAT status: no NAT-T VID seen\n")); ++ } ++ ++ ++ flatten_isakmp_packet(p2, &p2kt, &p2kt_len, s->ike.ivlen); ++ free_isakmp_packet(p2); ++ isakmp_crypt(s, p2kt, p2kt_len, 1); ++ ++ s->ike.initial_iv = xallocc(s->ike.ivlen); ++ memcpy(s->ike.initial_iv, s->ike.current_iv, s->ike.ivlen); ++ hex_dump("initial_iv", s->ike.initial_iv, s->ike.ivlen, NULL); ++ ++ /* Now, send that packet and receive a new one. */ ++ r_length = sendrecv(s, r_packet, sizeof(r_packet), p2kt, p2kt_len, 0); ++ free(p2kt); ++ } ++ DEBUG(2, printf("S4.6\n")); ++ ++ free_isakmp_packet(p1); ++ free(returned_hash); ++ free(dh_public); ++ group_free(dh_grp); ++} ++ ++static int do_phase2_notice_check(struct sa_block *s, struct isakmp_packet **r_p) ++{ ++ int reject = 0; ++ struct isakmp_packet *r; ++ ++ while (1) { ++ reject = unpack_verify_phase2(s, r_packet, r_length, r_p, NULL, 0); ++ if (reject == ISAKMP_N_INVALID_COOKIE) { ++ r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0); ++ continue; ++ } ++ if (*r_p == NULL) { ++ assert(reject != 0); ++ return reject; ++ } ++ r = *r_p; ++ ++ /* check for notices */ ++ if (r->exchange_type == ISAKMP_EXCHANGE_INFORMATIONAL && ++ r->payload->next != NULL) { ++ if (r->payload->next->type == ISAKMP_PAYLOAD_N) { ++ if (r->payload->next->u.n.type == ISAKMP_N_CISCO_LOAD_BALANCE) { ++ /* load balancing notice ==> restart with new gw */ ++ if (r->payload->next->u.n.data_length != 4) ++ error(1, 0, "malformed loadbalance target"); ++ s->dst = *(struct in_addr *)r->payload->next->u.n.data; ++ s->ike.dst_port = ISAKMP_PORT; ++ s->ipsec.encap_mode = IPSEC_ENCAP_TUNNEL; ++ s->ipsec.natt_active_mode = NATT_ACTIVE_NONE; ++ if (s->ike.src_port == 4500) ++ s->ike.src_port = 500; ++ close(s->ike_fd); ++ s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port); ++ DEBUG(2, printf("got cisco loadbalancing notice, diverting to %s\n", ++ inet_ntoa(s->dst))); ++ return -1; ++ } else if (r->payload->next->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) { ++ if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP) ++ lifetime_ike_process(s, r->payload->next->u.n.attributes); ++ else if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP) ++ lifetime_ipsec_process(s, r->payload->next->u.n.attributes); ++ else ++ DEBUG(2, printf("got unknown lifetime notice, ignoring..\n")); ++ r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0); ++ continue; ++ } else if (r->payload->next->u.n.type == ISAKMP_N_IPSEC_INITIAL_CONTACT) { ++ /* why in hell do we get this?? */ ++ DEBUG(2, printf("got initial contact notice, ignoring..\n")); ++ r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0); ++ continue; ++ } else { ++ /* whatever */ ++ printf("received notice of type %s(%d), giving up\n", ++ val_to_string(r->payload->next->u.n.type, isakmp_notify_enum_array), ++ r->payload->next->u.n.type); ++ return reject; ++ } ++ } ++ if (r->payload->next->type == ISAKMP_PAYLOAD_D) { ++ /* delete notice ==> ignore */ ++ DEBUG(2, printf("got delete for old connection, ignoring..\n")); ++ r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0); ++ continue; ++ } ++ } ++ ++ break; ++ } ++ return reject; ++} ++ ++static int do_phase_2_xauth(struct sa_block *s) ++{ ++ struct isakmp_packet *r; ++ int loopcount; ++ int reject; ++ int passwd_used = 0; ++ ++ DEBUG(2, printf("S5.1\n")); ++ /* This can go around for a while. */ ++ for (loopcount = 0;; loopcount++) { ++ struct isakmp_payload *rp; ++ struct isakmp_attribute *a, *ap, *reply_attr; ++ char ntop_buf[32]; ++ int seen_answer = 0; ++ ++ DEBUG(2, printf("S5.2\n")); ++ ++ /* recv and check for notices */ ++ reject = do_phase2_notice_check(s, &r); ++ if (reject == -1) ++ return 1; ++ ++ DEBUG(2, printf("S5.3\n")); ++ /* Check the transaction type is OK. */ ++ if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_MODECFG_TRANSACTION) ++ reject = ISAKMP_N_INVALID_EXCHANGE_TYPE; ++ ++ /* After the hash, expect an attribute block. */ ++ if (reject == 0 ++ && (r->payload->next == NULL ++ || r->payload->next->next != NULL ++ || r->payload->next->type != ISAKMP_PAYLOAD_MODECFG_ATTR)) ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ ++ if (reject == 0 && r->payload->next->u.modecfg.type == ISAKMP_MODECFG_CFG_SET) ++ break; ++ if (reject == 0 && r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_REQUEST) ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ ++ if (reject != 0) ++ phase2_fatal(s, "expected xauth packet; rejected: %s(%d)", reject); ++ ++ DEBUG(2, printf("S5.4\n")); ++ a = r->payload->next->u.modecfg.attributes; ++ /* First, print any messages, and verify that we understand the ++ * conversation. */ ++ for (ap = a; ap && seen_answer == 0; ap = ap->next) ++ if (ap->type == ISAKMP_XAUTH_ATTRIB_ANSWER) ++ seen_answer = 1; ++ ++ for (ap = a; ap && reject == 0; ap = ap->next) ++ switch (ap->type) { ++ case ISAKMP_XAUTH_ATTRIB_TYPE: ++ if (ap->af != isakmp_attr_16 || ap->u.attr_16 != 0) ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ break; ++ case ISAKMP_XAUTH_ATTRIB_USER_NAME: ++ case ISAKMP_XAUTH_ATTRIB_USER_PASSWORD: ++ case ISAKMP_XAUTH_ATTRIB_PASSCODE: ++ case ISAKMP_XAUTH_ATTRIB_DOMAIN: ++ case ISAKMP_XAUTH_ATTRIB_ANSWER: ++ case ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR: ++ break; ++ case ISAKMP_XAUTH_ATTRIB_MESSAGE: ++ if (opt_debug || seen_answer || config[CONFIG_XAUTH_INTERACTIVE]) { ++ if (ap->af == isakmp_attr_16) ++ printf("%c%c\n", ap->u.attr_16 >> 8, ap->u.attr_16); ++ else ++ printf("%.*s%s", ap->u.lots.length, ap->u.lots.data, ++ ((ap->u.lots.data ++ && ap->u.lots.data[ap->u. ++ lots.length - 1] != ++ '\n') ++ ? "\n" : "")); ++ } ++ break; ++ default: ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ } ++ DEBUG(2, printf("S5.5\n")); ++ if (reject != 0) ++ phase2_fatal(s, "xauth packet unsupported: %s(%d)", reject); ++ ++ inet_ntop(AF_INET, &s->dst, ntop_buf, sizeof(ntop_buf)); ++ ++ /* Collect data from the user. */ ++ reply_attr = NULL; ++ for (ap = a; ap && reject == 0; ap = ap->next) ++ switch (ap->type) { ++ case ISAKMP_XAUTH_ATTRIB_DOMAIN: ++ { ++ struct isakmp_attribute *na; ++ na = new_isakmp_attribute(ap->type, reply_attr); ++ reply_attr = na; ++ if (!config[CONFIG_DOMAIN] || strlen(config[CONFIG_DOMAIN]) == 0) ++ error(1, 0, ++ "server requested domain, but none set (use \"Domain ...\" in config or --domain"); ++ na->u.lots.length = strlen(config[CONFIG_DOMAIN]); ++ na->u.lots.data = xallocc(na->u.lots.length); ++ memcpy(na->u.lots.data, config[CONFIG_DOMAIN], ++ na->u.lots.length); ++ break; ++ } ++ case ISAKMP_XAUTH_ATTRIB_USER_NAME: ++ { ++ struct isakmp_attribute *na; ++ na = new_isakmp_attribute(ap->type, reply_attr); ++ reply_attr = na; ++ na->u.lots.length = strlen(config[CONFIG_XAUTH_USERNAME]); ++ na->u.lots.data = xallocc(na->u.lots.length); ++ memcpy(na->u.lots.data, config[CONFIG_XAUTH_USERNAME], ++ na->u.lots.length); ++ break; ++ } ++ case ISAKMP_XAUTH_ATTRIB_ANSWER: ++ case ISAKMP_XAUTH_ATTRIB_USER_PASSWORD: ++ case ISAKMP_XAUTH_ATTRIB_PASSCODE: ++ if (passwd_used && config[CONFIG_NON_INTERACTIVE]) { ++ reject = ISAKMP_N_AUTHENTICATION_FAILED; ++ phase2_fatal(s, "noninteractive can't reuse password", reject); ++ error(2, 0, "authentication unsuccessful"); ++ } else if (seen_answer || passwd_used || config[CONFIG_XAUTH_INTERACTIVE]) { ++ char *pass, *prompt = NULL; ++ struct isakmp_attribute *na; ++ ++ asprintf(&prompt, "%s for VPN %s@%s: ", ++ (ap->type == ISAKMP_XAUTH_ATTRIB_ANSWER) ? ++ "Answer" : ++ (ap->type == ISAKMP_XAUTH_ATTRIB_USER_PASSWORD) ? ++ "Password" : "Passcode", ++ config[CONFIG_XAUTH_USERNAME], ntop_buf); ++ pass = getpass(prompt); ++ free(prompt); ++ ++ na = new_isakmp_attribute(ap->type, reply_attr); ++ reply_attr = na; ++ na->u.lots.length = strlen(pass); ++ na->u.lots.data = xallocc(na->u.lots.length); ++ memcpy(na->u.lots.data, pass, na->u.lots.length); ++ memset(pass, 0, na->u.lots.length); ++ } else { ++ struct isakmp_attribute *na; ++ na = new_isakmp_attribute(ap->type, reply_attr); ++ reply_attr = na; ++ na->u.lots.length = strlen(config[CONFIG_XAUTH_PASSWORD]); ++ na->u.lots.data = xallocc(na->u.lots.length); ++ memcpy(na->u.lots.data, config[CONFIG_XAUTH_PASSWORD], ++ na->u.lots.length); ++ passwd_used = 1; /* Provide canned password at most once */ ++ } ++ break; ++ default: ++ ; ++ } ++ ++ /* Send the response. */ ++ rp = new_isakmp_payload(ISAKMP_PAYLOAD_MODECFG_ATTR); ++ rp->u.modecfg.type = ISAKMP_MODECFG_CFG_REPLY; ++ rp->u.modecfg.id = r->payload->next->u.modecfg.id; ++ rp->u.modecfg.attributes = reply_attr; ++ sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_MODECFG_TRANSACTION, ++ r->message_id, 0, 0, 0, 0, 0, 0, 0); ++ ++ } ++ ++ if ((opt_vendor == VENDOR_NETSCREEN) && ++ (r->payload->next->u.modecfg.type == ISAKMP_MODECFG_CFG_SET)) { ++ struct isakmp_attribute *a = r->payload->next->u.modecfg.attributes; ++ ++ DEBUG(2, printf("S5.5.1\n")); ++ ++ do_config_to_env(s, a); ++ ++ for (; a; a = a->next) ++ if(a->af == isakmp_attr_lots) ++ a->u.lots.length = 0; ++ ++ r->payload->next->u.modecfg.type = ISAKMP_MODECFG_CFG_ACK; ++ sendrecv_phase2(s, r->payload->next, ++ ISAKMP_EXCHANGE_MODECFG_TRANSACTION, ++ r->message_id, 0, 0, 0, 0, 0, 0, 0); ++ ++ reject = do_phase2_notice_check(s, &r); ++ if (reject == -1) ++ return 1; ++ } ++ ++ DEBUG(2, printf("S5.6\n")); ++ { ++ /* The final SET should have just one attribute. */ ++ struct isakmp_attribute *a = r->payload->next->u.modecfg.attributes; ++ uint16_t set_result = 1; ++ ++ if (a == NULL ++ || a->type != ISAKMP_XAUTH_ATTRIB_STATUS ++ || a->af != isakmp_attr_16 || a->next != NULL) { ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ phase2_fatal(s, "xauth SET response rejected: %s(%d)", reject); ++ } else { ++ set_result = a->u.attr_16; ++ } ++ ++ /* ACK the SET. */ ++ r->payload->next->u.modecfg.type = ISAKMP_MODECFG_CFG_ACK; ++ sendrecv_phase2(s, r->payload->next, ISAKMP_EXCHANGE_MODECFG_TRANSACTION, ++ r->message_id, 1, 0, 0, 0, 0, 0, 0); ++ r->payload->next = NULL; ++ free_isakmp_packet(r); ++ ++ if (set_result == 0) ++ error(2, 0, "authentication unsuccessful"); ++ } ++ DEBUG(2, printf("S5.7\n")); ++ return 0; ++} ++ ++static int do_phase_2_config(struct sa_block *s) ++{ ++ struct isakmp_payload *rp; ++ struct isakmp_attribute *a; ++ struct isakmp_packet *r; ++ struct utsname uts; ++ uint32_t msgid; ++ int reject; ++ ++ uname(&uts); ++ ++ gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid)); ++ if (msgid == 0) ++ msgid = 1; ++ ++ rp = new_isakmp_payload(ISAKMP_PAYLOAD_MODECFG_ATTR); ++ rp->u.modecfg.type = ISAKMP_MODECFG_CFG_REQUEST; ++ rp->u.modecfg.id = 20; ++ a = NULL; ++ ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION, a); ++ a->u.lots.length = strlen(config[CONFIG_VERSION]); ++ a->u.lots.data = xallocc(a->u.lots.length); ++ memcpy(a->u.lots.data, config[CONFIG_VERSION], a->u.lots.length); ++ ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME, a); ++ a->u.lots.length = strlen(uts.nodename); ++ a->u.lots.data = xallocc(a->u.lots.length); ++ memcpy(a->u.lots.data, uts.nodename, a->u.lots.length); ++ ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC, a); ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW, a); ++ ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_BANNER, a); ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS, a); ++ if (opt_natt_mode == NATT_CISCO_UDP) ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT, a); ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN, a); ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS, a); ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS, a); ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK, a); ++ a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS, a); ++ ++ rp->u.modecfg.attributes = a; ++ sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_MODECFG_TRANSACTION, msgid, 0, 0, 0, 0, 0, 0, 0); ++ ++ /* recv and check for notices */ ++ reject = do_phase2_notice_check(s, &r); ++ if (reject == -1) ++ return 1; ++ ++ /* Check the transaction type & message ID are OK. */ ++ if (reject == 0 && r->message_id != msgid) ++ reject = ISAKMP_N_INVALID_MESSAGE_ID; ++ if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_MODECFG_TRANSACTION) ++ reject = ISAKMP_N_INVALID_EXCHANGE_TYPE; ++ ++ /* After the hash, expect an attribute block. */ ++ if (reject == 0 ++ && (r->payload->next == NULL ++ || r->payload->next->next != NULL ++ || r->payload->next->type != ISAKMP_PAYLOAD_MODECFG_ATTR ++#if 0 ++ || r->payload->next->u.modecfg.id != 20 ++#endif ++ || r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_REPLY)) ++ reject = ISAKMP_N_PAYLOAD_MALFORMED; ++ ++ if (reject != 0) ++ phase2_fatal(s, "configuration response rejected: %s(%d)", reject); ++ ++ if (reject == 0) ++ reject = do_config_to_env(s, r->payload->next->u.modecfg.attributes); ++ ++ if (reject != 0) ++ phase2_fatal(s, "configuration response rejected: %s(%d)", reject); ++ ++ DEBUG(1, printf("got address %s\n", getenv("INTERNAL_IP4_ADDRESS"))); ++ return 0; ++} ++ ++static struct isakmp_attribute *make_transform_ipsec(struct sa_block *s, int dh_group, int hash, int keylen) ++{ ++ struct isakmp_attribute *a = NULL; ++ ++ a = new_isakmp_attribute(ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION, a); ++ a->af = isakmp_attr_lots; ++ a->u.lots.length = 4; ++ a->u.lots.data = xallocc(a->u.lots.length); ++ *((uint32_t *) a->u.lots.data) = htonl(2147483); ++ a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE, IPSEC_LIFE_SECONDS, a); ++ ++ if (dh_group) ++ a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_GROUP_DESC, dh_group, a); ++ a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_AUTH_ALG, hash, a); ++ a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_ENCAP_MODE, s->ipsec.encap_mode, a); ++ if (keylen != 0) ++ a = new_isakmp_attribute_16(ISAKMP_IPSEC_ATTRIB_KEY_LENGTH, keylen, a); ++ ++ return a; ++} ++ ++static struct isakmp_payload *make_our_sa_ipsec(struct sa_block *s) ++{ ++ struct isakmp_payload *r = new_isakmp_payload(ISAKMP_PAYLOAD_SA); ++ struct isakmp_payload *p = NULL, *pn; ++ struct isakmp_attribute *a; ++ int dh_grp = get_dh_group_ipsec(s->ipsec.do_pfs)->ipsec_sa_id; ++ unsigned int crypt, hash, keylen; ++ int i; ++ ++ r = new_isakmp_payload(ISAKMP_PAYLOAD_SA); ++ r->u.sa.doi = ISAKMP_DOI_IPSEC; ++ r->u.sa.situation = ISAKMP_IPSEC_SIT_IDENTITY_ONLY; ++ r->u.sa.proposals = new_isakmp_payload(ISAKMP_PAYLOAD_P); ++ r->u.sa.proposals->u.p.spi_size = 4; ++ r->u.sa.proposals->u.p.spi = xallocc(4); ++ /* The sadb_sa_spi field is already in network order. */ ++ memcpy(r->u.sa.proposals->u.p.spi, &s->ipsec.rx.spi, 4); ++ r->u.sa.proposals->u.p.prot_id = ISAKMP_IPSEC_PROTO_IPSEC_ESP; ++ for (crypt = 0; supp_crypt[crypt].name != NULL; crypt++) { ++ keylen = supp_crypt[crypt].keylen; ++ for (hash = 0; supp_hash[hash].name != NULL; hash++) { ++ pn = p; ++ p = new_isakmp_payload(ISAKMP_PAYLOAD_P); ++ p->u.p.spi_size = 4; ++ p->u.p.spi = xallocc(4); ++ /* The sadb_sa_spi field is already in network order. */ ++ memcpy(p->u.p.spi, &s->ipsec.rx.spi, 4); ++ p->u.p.prot_id = ISAKMP_IPSEC_PROTO_IPSEC_ESP; ++ p->u.p.transforms = new_isakmp_payload(ISAKMP_PAYLOAD_T); ++ p->u.p.transforms->u.t.id = supp_crypt[crypt].ipsec_sa_id; ++ a = make_transform_ipsec(s, dh_grp, supp_hash[hash].ipsec_sa_id, keylen); ++ p->u.p.transforms->u.t.attributes = a; ++ p->next = pn; ++ } ++ } ++ for (i = 0, pn = p; pn; pn = pn->next) ++ pn->u.p.number = i++; ++ r->u.sa.proposals = p; ++ return r; ++} ++ ++static void setup_link(struct sa_block *s) ++{ ++ struct isakmp_payload *rp, *us, *ke = NULL, *them, *nonce_r = NULL; ++ struct isakmp_packet *r; ++ struct group *dh_grp = NULL; ++ uint32_t msgid; ++ int reject; ++ uint8_t *p_flat = NULL, *realiv = NULL, realiv_msgid[4]; ++ size_t p_size = 0; ++ uint8_t nonce_i[20], *dh_public = NULL; ++ int i; ++ ++ DEBUG(2, printf("S7.1\n")); ++ /* Set up the Diffie-Hellman stuff. */ ++ if (get_dh_group_ipsec(s->ipsec.do_pfs)->my_id) { ++ dh_grp = group_get(get_dh_group_ipsec(s->ipsec.do_pfs)->my_id); ++ DEBUG(3, printf("len = %d\n", dh_getlen(dh_grp))); ++ dh_public = xallocc(dh_getlen(dh_grp)); ++ dh_create_exchange(dh_grp, dh_public); ++ hex_dump("dh_public", dh_public, dh_getlen(dh_grp), NULL); ++ } ++ ++ gcry_create_nonce((uint8_t *) & s->ipsec.rx.spi, sizeof(s->ipsec.rx.spi)); ++ rp = make_our_sa_ipsec(s); ++ gcry_create_nonce((uint8_t *) nonce_i, sizeof(nonce_i)); ++ rp->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE, nonce_i, sizeof(nonce_i)); ++ ++ us = new_isakmp_payload(ISAKMP_PAYLOAD_ID); ++ us->u.id.type = ISAKMP_IPSEC_ID_IPV4_ADDR; ++ us->u.id.length = 4; ++ us->u.id.data = xallocc(4); ++ memcpy(us->u.id.data, s->our_address, sizeof(struct in_addr)); ++ them = new_isakmp_payload(ISAKMP_PAYLOAD_ID); ++ them->u.id.type = ISAKMP_IPSEC_ID_IPV4_ADDR_SUBNET; ++ them->u.id.length = 8; ++ them->u.id.data = xallocc(8); ++ memset(them->u.id.data, 0, 8); ++ us->next = them; ++ s->ipsec.life.start = time(NULL); ++ ++ if (!dh_grp) { ++ rp->next->next = us; ++ } else { ++ rp->next->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_KE, ++ dh_public, dh_getlen(dh_grp)); ++ rp->next->next->next = us; ++ } ++ ++ gcry_create_nonce((uint8_t *) & msgid, sizeof(&msgid)); ++ if (msgid == 0) ++ msgid = 1; ++ ++ DEBUG(2, printf("S7.2\n")); ++ for (i = 0; i < 4; i++) { ++ sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_IKE_QUICK, ++ msgid, 0, &p_flat, &p_size, 0, 0, 0, 0); ++ ++ if (realiv == NULL) { ++ realiv = xallocc(s->ike.ivlen); ++ memcpy(realiv, s->ike.current_iv, s->ike.ivlen); ++ memcpy(realiv_msgid, s->ike.current_iv_msgid, 4); ++ } ++ ++ DEBUG(2, printf("S7.3\n")); ++ reject = unpack_verify_phase2(s, r_packet, r_length, &r, nonce_i, sizeof(nonce_i)); ++ ++ DEBUG(2, printf("S7.4\n")); ++ if (((reject == 0) || (reject == ISAKMP_N_AUTHENTICATION_FAILED)) ++ && r->exchange_type == ISAKMP_EXCHANGE_INFORMATIONAL) { ++ /* handle notifie responder-lifetime */ ++ /* (broken hash => ignore AUTHENTICATION_FAILED) */ ++ if (reject == 0 && r->payload->next->type != ISAKMP_PAYLOAD_N) ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ ++ if (reject == 0 ++ && r->payload->next->u.n.type == ++ ISAKMP_N_IPSEC_RESPONDER_LIFETIME) { ++ if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP) ++ lifetime_ike_process(s, r->payload->next->u.n.attributes); ++ else if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP) ++ lifetime_ipsec_process(s, r->payload->next->u.n.attributes); ++ else ++ DEBUG(2, printf("got unknown lifetime notice, ignoring..\n")); ++ memcpy(s->ike.current_iv, realiv, s->ike.ivlen); ++ memcpy(s->ike.current_iv_msgid, realiv_msgid, 4); ++ continue; ++ } ++ } ++ ++ /* Check the transaction type & message ID are OK. */ ++ if (reject == 0 && r->message_id != msgid) ++ reject = ISAKMP_N_INVALID_MESSAGE_ID; ++ ++ if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_IKE_QUICK) ++ reject = ISAKMP_N_INVALID_EXCHANGE_TYPE; ++ ++ /* The SA payload must be second. */ ++ if (reject == 0 && r->payload->next->type != ISAKMP_PAYLOAD_SA) ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ ++ if (p_flat) ++ free(p_flat); ++ if (realiv) ++ free(realiv); ++ break; ++ } ++ ++ DEBUG(2, printf("S7.5\n")); ++ if (reject != 0) ++ phase2_fatal(s, "quick mode response rejected: %s(%d)\n" ++ "this means the concentrator did not like what we had to offer.\n" ++ "Possible reasons are:\n" ++ " * concentrator configured to require a firewall\n" ++ " this locks out even Cisco clients on any platform expect windows\n" ++ " which is an obvious security improvment. There is no workaround (yet).\n" ++ " * concentrator configured to require IP compression\n" ++ " this is not yet supported by vpnc.\n" ++ " Note: the Cisco Concentrator Documentation recommends against using\n" ++ " compression, expect on low-bandwith (read: ISDN) links, because it\n" ++ " uses much CPU-resources on the concentrator\n", ++ reject); ++ ++ DEBUG(2, printf("S7.6\n")); ++ for (rp = r->payload->next; rp && reject == 0; rp = rp->next) ++ switch (rp->type) { ++ case ISAKMP_PAYLOAD_SA: ++ if (reject == 0 && rp->u.sa.doi != ISAKMP_DOI_IPSEC) ++ reject = ISAKMP_N_DOI_NOT_SUPPORTED; ++ if (reject == 0 && rp->u.sa.situation != ISAKMP_IPSEC_SIT_IDENTITY_ONLY) ++ reject = ISAKMP_N_SITUATION_NOT_SUPPORTED; ++ if (reject == 0 && ++ (rp->u.sa.proposals == NULL || rp->u.sa.proposals->next != NULL)) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (reject == 0 && ++ rp->u.sa.proposals->u.p.prot_id != ISAKMP_IPSEC_PROTO_IPSEC_ESP) ++ reject = ISAKMP_N_INVALID_PROTOCOL_ID; ++ if (reject == 0 && rp->u.sa.proposals->u.p.spi_size != 4) ++ reject = ISAKMP_N_INVALID_SPI; ++ if (reject == 0 && ++ (rp->u.sa.proposals->u.p.transforms == NULL ++ || rp->u.sa.proposals->u.p.transforms->next != NULL)) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (reject == 0) { ++ struct isakmp_attribute *a ++ = rp->u.sa.proposals->u.p.transforms->u.t.attributes; ++ int seen_enc = rp->u.sa.proposals->u.p.transforms->u.t.id; ++ int seen_auth = 0, seen_encap = 0, seen_group = 0, seen_keylen = 0; ++ ++ memcpy(&s->ipsec.tx.spi, rp->u.sa.proposals->u.p.spi, 4); ++ ++ for (; a && reject == 0; a = a->next) ++ switch (a->type) { ++ case ISAKMP_IPSEC_ATTRIB_AUTH_ALG: ++ if (a->af == isakmp_attr_16) ++ seen_auth = a->u.attr_16; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE: ++ if (a->af == isakmp_attr_16 && ++ a->u.attr_16 == s->ipsec.encap_mode) ++ seen_encap = 1; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_GROUP_DESC: ++ if (dh_grp && ++ a->af == isakmp_attr_16 && ++ a->u.attr_16 == ++ get_dh_group_ipsec(s->ipsec.do_pfs)->ipsec_sa_id) ++ seen_group = 1; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_KEY_LENGTH: ++ if (a->af == isakmp_attr_16) ++ seen_keylen = a->u.attr_16; ++ else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: ++ /* lifetime duration MUST follow lifetype attribute */ ++ if (a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION) { ++ lifetime_ipsec_process(s, a); ++ } else ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION: ++ /* already processed above in ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: */ ++ break; ++ default: ++ reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ break; ++ } ++ if (reject == 0 && (!seen_auth || !seen_encap || ++ (dh_grp && !seen_group))) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ if (reject == 0 ++ && get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, ++ NULL, 0) == NULL) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (reject == 0 ++ && get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc, ++ NULL, seen_keylen) == NULL) ++ reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ if (reject == 0) { ++ s->ipsec.cry_algo = ++ get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, ++ seen_enc, NULL, seen_keylen)->my_id; ++ s->ipsec.md_algo = ++ get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, ++ seen_auth, NULL, 0)->my_id; ++ if (s->ipsec.cry_algo) { ++ gcry_cipher_algo_info(s->ipsec.cry_algo, GCRYCTL_GET_KEYLEN, NULL, &(s->ipsec.key_len)); ++ gcry_cipher_algo_info(s->ipsec.cry_algo, GCRYCTL_GET_BLKLEN, NULL, &(s->ipsec.blk_len)); ++ s->ipsec.iv_len = s->ipsec.blk_len; ++ } else { ++ s->ipsec.key_len = 0; ++ s->ipsec.iv_len = 0; ++ s->ipsec.blk_len = 8; /* seems to be this without encryption... */ ++ } ++ s->ipsec.md_len = gcry_md_get_algo_dlen(s->ipsec.md_algo); ++ DEBUG(1, printf("IPSEC SA selected %s-%s\n", ++ get_algo(SUPP_ALGO_CRYPT, ++ SUPP_ALGO_IPSEC_SA, seen_enc, NULL, ++ seen_keylen)->name, ++ get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, ++ seen_auth, NULL, 0)->name)); ++ if (s->ipsec.cry_algo == GCRY_CIPHER_DES && !opt_1des) { ++ error(1, 0, "peer selected (single) DES as \"encrytion\" method.\n" ++ "This algorithm is considered to weak today\n" ++ "If your vpn concentrator admin still insists on using DES\n" ++ "use the \"--enable-1des\" option.\n"); ++ } else if (s->ipsec.cry_algo == GCRY_CIPHER_NONE && !opt_no_encryption) { ++ error(1, 0, "peer selected NULL as \"encrytion\" method.\n" ++ "This is _no_ encryption at all.\n" ++ "Your traffic is still protected against modification with %s\n" ++ "If your vpn concentrator admin still insists on not using encryption\n" ++ "use the \"--enable-no-encryption\" option.\n", ++ get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0)->name); ++ } ++ } ++ } ++ break; ++ ++ case ISAKMP_PAYLOAD_N: ++ if (reject == 0 && rp->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) { ++ if (rp->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP) ++ lifetime_ike_process(s, rp->u.n.attributes); ++ else if (rp->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP) ++ lifetime_ipsec_process(s, rp->u.n.attributes); ++ else ++ DEBUG(2, printf("got unknown lifetime notice, ignoring..\n")); ++ } ++ break; ++ case ISAKMP_PAYLOAD_ID: ++ break; ++ case ISAKMP_PAYLOAD_KE: ++ ke = rp; ++ break; ++ case ISAKMP_PAYLOAD_NONCE: ++ nonce_r = rp; ++ break; ++ ++ default: ++ reject = ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ break; ++ } ++ ++ if (reject == 0 && nonce_r == NULL) ++ reject = ISAKMP_N_INVALID_HASH_INFORMATION; ++ if (reject == 0 && dh_grp && (ke == NULL || ke->u.ke.length != dh_getlen(dh_grp))) ++ reject = ISAKMP_N_INVALID_KEY_INFORMATION; ++ if (reject != 0) ++ phase2_fatal(s, "quick mode response rejected [2]: %s(%d)", reject); ++ ++ /* send final packet */ ++ sendrecv_phase2(s, NULL, ISAKMP_EXCHANGE_IKE_QUICK, ++ msgid, 1, 0, 0, nonce_i, sizeof(nonce_i), ++ nonce_r->u.nonce.data, nonce_r->u.nonce.length); ++ ++ DEBUG(2, printf("S7.7\n")); ++ ++ /* Set up the interface here so it's ready when our acknowledgement ++ * arrives. */ ++ config_tunnel(s); ++ DEBUG(2, printf("S7.8\n")); ++ { ++ unsigned char *dh_shared_secret = NULL; ++ ++ if (dh_grp) { ++ /* Determine the shared secret. */ ++ dh_shared_secret = xallocc(dh_getlen(dh_grp)); ++ dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data); ++ hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL); ++ } ++ ++ s->ipsec.rx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.rx.spi, ++ dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0, ++ nonce_i, sizeof(nonce_i), nonce_r->u.nonce.data, nonce_r->u.nonce.length); ++ ++ s->ipsec.tx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.tx.spi, ++ dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0, ++ nonce_i, sizeof(nonce_i), nonce_r->u.nonce.data, nonce_r->u.nonce.length); ++ ++ if (dh_grp) ++ group_free(dh_grp); ++ ++ if ((opt_natt_mode == NATT_CISCO_UDP) && s->ipsec.peer_udpencap_port) { ++ s->esp_fd = make_socket(s, opt_udpencapport, s->ipsec.peer_udpencap_port); ++ s->ipsec.encap_mode = IPSEC_ENCAP_UDP_TUNNEL; ++ s->ipsec.natt_active_mode = NATT_ACTIVE_CISCO_UDP; ++ } else if (s->ipsec.encap_mode != IPSEC_ENCAP_TUNNEL) { ++ s->esp_fd = s->ike_fd; ++ } else { ++#ifdef IP_HDRINCL ++ int hincl = 1; ++#endif ++ ++ s->esp_fd = socket(PF_INET, SOCK_RAW, IPPROTO_ESP); ++ if (s->esp_fd == -1) { ++ error(1, errno, "socket(PF_INET, SOCK_RAW, IPPROTO_ESP)"); ++ } ++#ifdef IP_HDRINCL ++ if (setsockopt(s->esp_fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) == -1) { ++ error(1, errno, "setsockopt(esp_fd, IPPROTO_IP, IP_HDRINCL, 1)"); ++ } ++#endif ++ } ++ ++ s->ipsec.rx.seq_id = s->ipsec.tx.seq_id = 1; ++ DEBUG(2, printf("S7.9\n")); ++ vpnc_doit(s); ++ } ++ ++ DEBUG(2, printf("S7.10\n")); ++ /* finished, send the delete message */ ++ { ++ struct isakmp_payload *d_isakmp, *d_ipsec; ++ uint8_t del_msgid; ++ ++ gcry_create_nonce((uint8_t *) & del_msgid, sizeof(del_msgid)); ++ d_isakmp = new_isakmp_payload(ISAKMP_PAYLOAD_D); ++ d_isakmp->u.d.doi = ISAKMP_DOI_IPSEC; ++ d_isakmp->u.d.protocol = ISAKMP_IPSEC_PROTO_ISAKMP; ++ d_isakmp->u.d.spi_length = 2 * ISAKMP_COOKIE_LENGTH; ++ d_isakmp->u.d.num_spi = 1; ++ d_isakmp->u.d.spi = xallocc(1 * sizeof(uint8_t *)); ++ d_isakmp->u.d.spi[0] = xallocc(2 * ISAKMP_COOKIE_LENGTH); ++ memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ++ ISAKMP_COOKIE_LENGTH); ++ memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ++ ISAKMP_COOKIE_LENGTH); ++ d_ipsec = new_isakmp_payload(ISAKMP_PAYLOAD_D); ++ d_ipsec->next = d_isakmp; ++ d_ipsec->u.d.doi = ISAKMP_DOI_IPSEC; ++ d_ipsec->u.d.protocol = ISAKMP_IPSEC_PROTO_IPSEC_ESP; ++ d_ipsec->u.d.spi_length = 4; ++ d_ipsec->u.d.num_spi = 2; ++ d_ipsec->u.d.spi = xallocc(2 * sizeof(uint8_t *)); ++ d_ipsec->u.d.spi[0] = xallocc(d_ipsec->u.d.spi_length); ++ memcpy(d_ipsec->u.d.spi[0], &s->ipsec.rx.spi, 4); ++ d_ipsec->u.d.spi[1] = xallocc(d_ipsec->u.d.spi_length); ++ memcpy(d_ipsec->u.d.spi[1], &s->ipsec.tx.spi, 4); ++ sendrecv_phase2(s, d_ipsec, ISAKMP_EXCHANGE_INFORMATIONAL, ++ del_msgid, 1, NULL, NULL, ++ NULL, 0, NULL, 0); ++ } ++} ++ ++static int do_rekey(struct sa_block *s, struct isakmp_packet *r) ++{ ++ struct isakmp_payload *rp, *ke = NULL, *nonce_i = NULL; ++ struct isakmp_attribute *a; ++ int seen_enc; ++ int seen_auth = 0, seen_encap = 0, seen_group = 0, seen_keylen = 0; ++ int nonce_i_copy_len; ++ struct group *dh_grp = NULL; ++ uint8_t nonce_r[20], *dh_public = NULL, *nonce_i_copy = NULL; ++ unsigned char *dh_shared_secret = NULL; ++ ++ if (get_dh_group_ipsec(s->ipsec.do_pfs)->my_id) { ++ dh_grp = group_get(get_dh_group_ipsec(s->ipsec.do_pfs)->my_id); ++ DEBUG(3, printf("len = %d\n", dh_getlen(dh_grp))); ++ dh_public = xallocc(dh_getlen(dh_grp)); ++ dh_create_exchange(dh_grp, dh_public); ++ hex_dump("dh_public", dh_public, dh_getlen(dh_grp), NULL); ++ } ++ ++ rp = r->payload->next; ++ /* rp->type == ISAKMP_PAYLOAD_SA, verified by caller */ ++ ++ if (rp->u.sa.doi != ISAKMP_DOI_IPSEC) ++ return ISAKMP_N_DOI_NOT_SUPPORTED; ++ if (rp->u.sa.situation != ISAKMP_IPSEC_SIT_IDENTITY_ONLY) ++ return ISAKMP_N_SITUATION_NOT_SUPPORTED; ++ if (rp->u.sa.proposals == NULL || rp->u.sa.proposals->next != NULL) /* rekeying should only have one proposal */ ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (rp->u.sa.proposals->u.p.prot_id != ISAKMP_IPSEC_PROTO_IPSEC_ESP) ++ return ISAKMP_N_INVALID_PROTOCOL_ID; ++ if (rp->u.sa.proposals->u.p.spi_size != 4) ++ return ISAKMP_N_INVALID_SPI; ++ if (rp->u.sa.proposals->u.p.transforms == NULL || rp->u.sa.proposals->u.p.transforms->next != NULL) ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ seen_enc = rp->u.sa.proposals->u.p.transforms->u.t.id; ++ ++ memcpy(&s->ipsec.tx.spi, rp->u.sa.proposals->u.p.spi, 4); ++ ++ for (a = rp->u.sa.proposals->u.p.transforms->u.t.attributes; a; a = a->next) ++ switch (a->type) { ++ case ISAKMP_IPSEC_ATTRIB_AUTH_ALG: ++ if (a->af == isakmp_attr_16) ++ seen_auth = a->u.attr_16; ++ else ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE: ++ if (a->af == isakmp_attr_16 && ++ a->u.attr_16 == ( ++ (s->ipsec.natt_active_mode != NATT_ACTIVE_CISCO_UDP) ? ++ s->ipsec.encap_mode : ++ IPSEC_ENCAP_TUNNEL /* cisco-udp claims to use encap tunnel... */ ++ )) ++ seen_encap = 1; ++ else ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_GROUP_DESC: ++ if (dh_grp && a->af == isakmp_attr_16 && ++ a->u.attr_16 == get_dh_group_ipsec(s->ipsec.do_pfs)->ipsec_sa_id) ++ seen_group = 1; ++ else ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_KEY_LENGTH: ++ if (a->af == isakmp_attr_16) ++ seen_keylen = a->u.attr_16; ++ else ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: ++ /* lifetime duration MUST follow lifetype attribute */ ++ if (a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION) { ++ lifetime_ipsec_process(s, a); ++ } else ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ break; ++ case ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION: ++ /* already processed above in ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: */ ++ break; ++ default: ++ return ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; ++ break; ++ } ++ if (!seen_auth || !seen_encap || (dh_grp && !seen_group)) ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ if (get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0) == NULL) ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc, NULL, seen_keylen) == NULL) ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ /* we don't want to change ciphers during rekeying */ ++ if (s->ipsec.cry_algo != get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc, NULL, seen_keylen)->my_id) ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ if (s->ipsec.md_algo != get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0)->my_id) ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ for (rp = rp->next; rp; rp = rp->next) ++ switch (rp->type) { ++ case ISAKMP_PAYLOAD_ID: ++ break; ++ case ISAKMP_PAYLOAD_KE: ++ ke = rp; ++ break; ++ case ISAKMP_PAYLOAD_NONCE: ++ nonce_i = rp; ++ break; ++ default: ++ return ISAKMP_N_INVALID_PAYLOAD_TYPE; ++ break; ++ } ++ ++ if ((dh_grp && ke == NULL) || nonce_i == NULL) ++ return ISAKMP_N_BAD_PROPOSAL_SYNTAX; ++ ++ DEBUG(3, printf("everything fine so far...\n")); ++ gcry_create_nonce((uint8_t *) nonce_r, sizeof(nonce_r)); ++ gcry_create_nonce((uint8_t *) & s->ipsec.rx.spi, sizeof(s->ipsec.rx.spi)); ++ ++ if (dh_grp) { ++ /* Determine the shared secret. */ ++ dh_shared_secret = xallocc(dh_getlen(dh_grp)); ++ dh_create_shared(dh_grp, dh_shared_secret, ke->u.ke.data); ++ hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(dh_grp), NULL); ++ } ++ ++ free(s->ipsec.rx.key); ++ free(s->ipsec.tx.key); ++ ++ s->ipsec.rx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.rx.spi, ++ dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0, ++ nonce_i->u.nonce.data, nonce_i->u.nonce.length, nonce_r, sizeof(nonce_r)); ++ ++ s->ipsec.tx.key = gen_keymat(s, ISAKMP_IPSEC_PROTO_IPSEC_ESP, s->ipsec.tx.spi, ++ dh_shared_secret, dh_grp ? dh_getlen(dh_grp) : 0, ++ nonce_i->u.nonce.data, nonce_i->u.nonce.length, nonce_r, sizeof(nonce_r)); ++ ++ s->ipsec.rx.key_cry = s->ipsec.rx.key; ++ s->ipsec.rx.key_md = s->ipsec.rx.key + s->ipsec.key_len; ++ s->ipsec.tx.key_cry = s->ipsec.tx.key; ++ s->ipsec.tx.key_md = s->ipsec.tx.key + s->ipsec.key_len; ++ ++ nonce_i_copy_len = nonce_i->u.nonce.length; ++ nonce_i_copy = xallocc(nonce_i_copy_len); ++ memcpy(nonce_i_copy, nonce_i->u.nonce.data, nonce_i_copy_len); ++ ++ s->ipsec.rx.seq_id = s->ipsec.tx.seq_id = 1; ++ s->ipsec.life.start = time(NULL); ++ s->ipsec.life.tx = 0; ++ s->ipsec.life.rx = 0; ++ ++ if (s->ipsec.cry_algo) { ++ gcry_cipher_setkey(s->ipsec.rx.cry_ctx, s->ipsec.rx.key_cry, s->ipsec.key_len); ++ gcry_cipher_setkey(s->ipsec.tx.cry_ctx, s->ipsec.tx.key_cry, s->ipsec.key_len); ++ } ++ ++ /* use request as template and just exchange some values */ ++ /* this overwrites data in nonce_i, ke! */ ++ rp = r->payload->next; ++ /* SA, change the SPI */ ++ memcpy(rp->u.sa.proposals->u.p.spi, &s->ipsec.rx.spi, 4); ++ ++ for (rp = rp->next; rp; rp = rp->next) ++ switch (rp->type) { ++ case ISAKMP_PAYLOAD_ID: ++ break; ++ case ISAKMP_PAYLOAD_KE: ++ memcpy(rp->u.ke.data, dh_public, dh_getlen(dh_grp)); ++ break; ++ case ISAKMP_PAYLOAD_NONCE: ++ memcpy(rp->u.nonce.data, nonce_r, sizeof(nonce_r)); ++ break; ++ default: ++ assert(0); ++ break; ++ } ++ ++ sendrecv_phase2(s, r->payload->next, ISAKMP_EXCHANGE_IKE_QUICK, ++ r->message_id, 0, 0, 0, nonce_i_copy, nonce_i_copy_len, 0,0); ++ unpack_verify_phase2(s, r_packet, r_length, &r, NULL, 0); ++ free(nonce_i_copy); ++ /* don't care about answer ... */ ++ ++ return 0; ++} ++ ++void process_late_ike(struct sa_block *s, uint8_t *r_packet, ssize_t r_length) ++{ ++ int reject; ++ struct isakmp_packet *r; ++ struct isakmp_payload *rp; ++ ++ DEBUG(2,printf("got late ike paket: %d bytes\n", r_length)); ++ /* we should ignore resend pakets here. ++ * unpack_verify_phase2 will fail to decode them probably */ ++ reject = unpack_verify_phase2(s, r_packet, r_length, &r, NULL, 0); ++ ++ /* just ignore broken stuff for now */ ++ if (reject != 0) ++ return; ++ ++ /* everything must be encrypted by now */ ++ if (r->payload == NULL || r->payload->type != ISAKMP_PAYLOAD_HASH) ++ return; ++ ++ /* empty packet? well, nothing to see here */ ++ if (r->payload->next == NULL) ++ return; ++ ++ /* do we get an SA proposal for rekeying? */ ++ if (r->exchange_type == ISAKMP_EXCHANGE_IKE_QUICK && ++ r->payload->next->type == ISAKMP_PAYLOAD_SA) { ++ reject = do_rekey(s, r); ++ DEBUG(3, printf("do_rekey returned: %d\n", reject)); ++ return; ++ } ++ ++ /* check if our isakmp sa gets deleted */ ++ for (rp = r->payload->next; rp; rp = rp->next) { ++ /* search for delete payloads */ ++ if (rp->type != ISAKMP_PAYLOAD_D) ++ continue; ++ ++ /* skip ipsec-esp delete */ ++ if (rp->u.d.protocol != ISAKMP_IPSEC_PROTO_ISAKMP) { ++ DEBUG(2, printf("got non isakmp-delete, ignoring...\n")); ++ continue; ++ }; ++ ++ /* ++ * RFC 2408, 3.15 Delete Payload ++ * it is not stated that the SPI field of a delete ++ * payload can be ignored, because it is given in ++ * the headers, but I assume so. In other cases ++ * RFC 2408 (notifications) states this. ++ */ ++ do_kill = -1; ++ DEBUG(2, printf("got isakmp-delete, terminating...\n")); ++ return; ++ } ++ ++ return; ++} ++ ++int main(int argc, char **argv) ++{ ++ int do_load_balance; ++ const uint8_t hex_test[] = { 0, 1, 2, 3 }; ++ struct sa_block oursa[1]; ++ struct sa_block *s = oursa; ++ ++ test_pack_unpack(); ++ gcry_check_version("1.1.90"); ++ gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0); ++ group_init(); ++ memset(s, 0, sizeof(*s)); ++ s->ipsec.encap_mode = IPSEC_ENCAP_TUNNEL; ++ ++ do_config(argc, argv); ++ ++ hex_dump("hex_test", hex_test, sizeof(hex_test), NULL); ++ ++ DEBUG(1, printf("vpnc version " VERSION "\n")); ++ DEBUG(2, printf("S1\n")); ++ init_sockaddr(&s->dst, config[CONFIG_IPSEC_GATEWAY]); ++ DEBUG(2, printf("S2\n")); ++ s->ike.src_port = atoi(config[CONFIG_LOCAL_PORT]); ++ s->ike.dst_port = ISAKMP_PORT; ++ s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port); ++ DEBUG(2, printf("S3\n")); ++ setup_tunnel(s); ++ ++ do_load_balance = 0; ++ do { ++ DEBUG(2, printf("S4\n")); ++ do_phase_1(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s); ++ DEBUG(2, printf("S5\n")); ++ if (s->ike.auth_algo == IKE_AUTH_XAUTHInitPreShared) ++ do_load_balance = do_phase_2_xauth(s); ++ DEBUG(2, printf("S6\n")); ++ if ((opt_vendor != VENDOR_NETSCREEN) && (do_load_balance == 0)) ++ do_load_balance = do_phase_2_config(s); ++ } while (do_load_balance); ++ DEBUG(2, printf("S7\n")); ++ setup_link(s); ++ DEBUG(2, printf("S8\n")); ++ setenv("reason", "disconnect", 1); ++ system(config[CONFIG_SCRIPT]); ++ ++ return 0; ++} +diff -urNad vpnc~/vpnc.h vpnc/vpnc.h +--- vpnc~/vpnc.h 2007-02-18 02:59:37.000000000 +0100 ++++ vpnc/vpnc.h 2007-05-23 22:48:16.000000000 +0200 +@@ -25,5 +25,6 @@ + + void process_late_ike(struct sa_block *s, uint8_t *r_packet, ssize_t r_length); + void keepalive_ike(struct sa_block *s); ++void dpd_ike(struct sa_block *s); + + #endif --- vpnc-0.5.1r275.orig/debian/patches/05_vpnc.conf.dpatch +++ vpnc-0.5.1r275/debian/patches/05_vpnc.conf.dpatch @@ -0,0 +1,50 @@ +#! /bin/sh -e +## 05_vpnc.conf.dpatch by Eduard Bloch +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +if [ $# -lt 1 ]; then + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1 +fi + +[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts +patch_opts="${patch_opts:--f --no-backup-if-mismatch} ${2:+-d $2}" + +case "$1" in + -patch) patch -p1 ${patch_opts} < $0;; + -unpatch) patch -R -p1 ${patch_opts} < $0;; + *) + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1;; +esac + +exit 0 + +@DPATCH@ +diff -urNad vpnc/vpnc.conf /tmp/dpep.fwDvsW/vpnc/vpnc.conf +--- vpnc/vpnc.conf 2004-11-13 16:00:17.000000000 +0100 ++++ /tmp/dpep.fwDvsW/vpnc/vpnc.conf 2004-11-23 19:00:49.000000000 +0100 +@@ -1,4 +1,17 @@ +-IPSec gateway 131.246.118.240 +-IPSec ID unikl +-IPSec secret unikl +-Xauth username abcdef ++IPSec gateway 192.0.2.32 ++IPSec ID myGroup ++IPSec secret myGroupPWD ++Xauth username myUserName ++ ++# OPTIONAL ++# ======== ++ ++# ++# ++# Varios options not undestood by vpnc itself but by some other scripts ++# ++# Target networks 123.234.210.0/24 10.1.0.0/16 ++# If Target networks is defined here, the default route is not replaced! ++ ++# Don't update resolv.conf though resolvconf is installed ++# DNSUpdate no --- vpnc-0.5.1r275.orig/debian/patches/03_vpnc.8.dpatch +++ vpnc-0.5.1r275/debian/patches/03_vpnc.8.dpatch @@ -0,0 +1,80 @@ +#! /bin/sh -e +## 03_vpnc.8.dpatch by Eduard Bloch +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +if [ $# -lt 1 ]; then + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1 +fi + +[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts +patch_opts="${patch_opts:--f --no-backup-if-mismatch} ${2:+-d $2}" + +case "$1" in + -patch) patch -p1 ${patch_opts} < $0;; + -unpatch) patch -R -p1 ${patch_opts} < $0;; + *) + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1;; +esac + +exit 0 + +@DPATCH@ +diff -urNad vpnc-0.5.1r254~/vpnc.8.template vpnc-0.5.1r254/vpnc.8.template +--- vpnc-0.5.1r254~/vpnc.8.template 2007-10-18 09:42:04.000000000 +0200 ++++ vpnc-0.5.1r254/vpnc.8.template 2007-10-18 09:44:36.000000000 +0200 +@@ -27,6 +27,10 @@ + be aware of this fact when you use vpnc to exchange sensitive data like + passwords! + .PP ++OBLICATORY WARNING: the most used configuration (XAUTH authentication) ++is insecure by design, be aware of this fact when you use vpnc to ++exchange sensitive data like passwords! ++.PP + The vpnc daemon by itself does not set any routes, but it calls + \fBvpnc\-script\fR to do this job. \fBvpnc\-script\fR displays + a connect banner. If the concentrator supplies a network list +@@ -187,6 +191,40 @@ + disabling /etc/resolv.conf rewriting is documented in the README of the + vpnc package. + ++.SH ADVANCED USAGE ++The vpnc-connect stript shipped with Debian has some additional ++features: ++.IP "Custom route setting" ++By default, the default route is deleted after connection and replaced ++with the new one (going trough the VPN tunnel device). However, some ++people wish to limit the target address range to few IP ranges. ++This can be done using the config directive ++.B Target networks ++in the config file. For example: ++.RS ++.PD 0 ++Target networks 123.234.210.0/24 10.1.0.0/16 ++.PD ++.RE ++.IP "Multiple config profiles management" ++You can have multiple config files and select one on connection by ++specifying a short profile name instead of a config file path. In this ++case, the file ++.I /etc/vpnc/PROFILE.conf ++is used as config file (where PROFILE is the short profile name). ++.IP "/etc/resolv.conf update" ++If the package ++.B resolvconf ++is installed and the VPN gateway sends some DNS server data, the ++script will use resolution to integrate the received data into ++.I /etc/resolv.conf. ++To disable this behaviour, set the config directive ++.I DNSUpdate ++to the ++.I "no" ++value. ++ ++ + .SH TODO + .PD 0 + Certificate support (Pre-Shared-Key + XAUTH is known to be insecure). --- vpnc-0.5.1r275.orig/debian/patches/04_debianitis.dpatch +++ vpnc-0.5.1r275/debian/patches/04_debianitis.dpatch @@ -0,0 +1,201 @@ +#!/bin/sh /usr/share/dpatch/dpatch-run +## 04_debianitis.dpatch by Eduard Bloch +## + +@DPATCH@ +diff -urNad vpnc-0.5.1r275~/config.c vpnc-0.5.1r275/config.c +--- vpnc-0.5.1r275~/config.c 2007-12-23 15:32:14.000000000 +0100 ++++ vpnc-0.5.1r275/config.c 2007-12-23 16:28:24.000000000 +0100 +@@ -186,6 +186,16 @@ + return "cisco"; + } + ++static const char *config_def_networks_list(void) ++{ ++ return ""; ++} ++ ++static const char *config_def_dns_update(void) ++{ ++ return "Yes"; ++} ++ + static const struct config_names_s { + enum config_enum nm; + const int needsArgument; +@@ -435,7 +445,27 @@ + "", + "path of the trusted CA-Directory", + config_ca_dir +- }, { ++ }, ++ { ++ CONFIG_DNS_UPDATE, 1, 1, ++ "--dns-update", ++ "DNSUpdate", ++ "", ++ "DEPRECATED extension, see README.Debian for details", ++ config_def_dns_update ++ }, ++ ++ { ++ CONFIG_TARGET_NETWORKS, 1, 1, ++ "--target-networks", ++ "Target Networks", ++ NULL, ++ "DEPRECATED extension, see README.Debian for details", ++ config_def_networks_list ++ }, ++ ++ ++ { + 0, 0, 0, NULL, NULL, NULL, NULL, NULL + } + }; +diff -urNad vpnc-0.5.1r275~/config.h vpnc-0.5.1r275/config.h +--- vpnc-0.5.1r275~/config.h 2007-12-23 15:32:14.000000000 +0100 ++++ vpnc-0.5.1r275/config.h 2007-12-23 16:28:24.000000000 +0100 +@@ -57,6 +57,11 @@ + CONFIG_AUTH_MODE, + CONFIG_CA_FILE, + CONFIG_CA_DIR, ++ ++ ++ CONFIG_DNS_UPDATE, ++ CONFIG_TARGET_NETWORKS, ++ + LAST_CONFIG + }; + +diff -urNad vpnc-0.5.1r275~/pcf2vpnc vpnc-0.5.1r275/pcf2vpnc +--- vpnc-0.5.1r275~/pcf2vpnc 2007-12-23 15:32:14.000000000 +0100 ++++ vpnc-0.5.1r275/pcf2vpnc 2007-12-23 16:28:24.000000000 +0100 +@@ -24,6 +24,8 @@ + use strict; + use warnings; + ++$ENV{"PATH"}.=":/usr/lib/vpnc"; ++ + my %authmode = ( 1 => 'psk', 3 => 'cert', 5 => 'hybrid' ); + my $needs_cert = 0; + my $no_decrypt = 0; +diff -urNad vpnc-0.5.1r275~/vpnc-script.in vpnc-0.5.1r275/vpnc-script.in +--- vpnc-0.5.1r275~/vpnc-script.in 2007-12-23 15:32:14.000000000 +0100 ++++ vpnc-0.5.1r275/vpnc-script.in 2007-12-23 16:29:13.000000000 +0100 +@@ -84,7 +84,7 @@ + # =========== tunnel interface handling ==================================== + + do_ifconfig() { +- ifconfig "$TUNDEV" inet "$INTERNAL_IP4_ADDRESS" $ifconfig_syntax_ptp "$INTERNAL_IP4_ADDRESS" netmask ${INTERNAL_IP4_NETMASK:-255.255.255.255} mtu 1412 up ++ /sbin/ifconfig "$TUNDEV" inet "$INTERNAL_IP4_ADDRESS" $ifconfig_syntax_ptp "$INTERNAL_IP4_ADDRESS" netmask ${INTERNAL_IP4_NETMASK:-255.255.255.255} mtu 1390 up + } + + destroy_tun_device() { +@@ -384,8 +384,9 @@ + else # can't open /dev/net/tun + test -e /proc/sys/kernel/modprobe && `cat /proc/sys/kernel/modprobe` tun 2>/dev/null + # fix for broken devfs in kernel 2.6.x +- if [ "`readlink /dev/net/tun`" = misc/net/tun \ +- -a ! -e /dev/net/misc/net/tun -a -e /dev/misc/net/tun ] ; then ++ if [ "`readlink /dev/net/tun`" = misc/net/tun ] && \ ++ [ ! -e /dev/net/misc/net/tun ] && \ ++ [ -e /dev/misc/net/tun ] ; then + ln -sf /dev/misc/net/tun /dev/net/tun + fi + # make sure tun device exists +@@ -421,6 +422,27 @@ + } + + do_connect() { ++ # Debian specific, insert your code there to avoid modification of ++ # conffiles like this script ++ if [ -r /etc/vpnc/vpnc-script-connect-action ] ; then ++ . /etc/vpnc/vpnc-script-connect-action ++ fi ++ # backwards compatibility mapping for old extensions ++ if test "$TARGET_NETWORKS" ; then ++ i=0 ++ for network in $TARGET_NETWORKS ; do ++ eval CISCO_SPLIT_INC_${i}_ADDR=`echo $network | cut -f1 -d/` ++ eval CISCO_SPLIT_INC_${i}_MASKLEN=`echo $network | cut -f2 -d/` ++ eval CISCO_SPLIT_INC_${i}_MASK=$( perl -e '$ARGV[0]=~s,.*/,,;$m=(2**$ARGV[0]-1)<<(32-$ARGV[0]);printf "%d.%d.%d.%d\n", $m>>24 & 0xff, $m>>16 & 0xff, $m>>8 & 0xff, $m & 0xff;' $network ) ++ eval CISCO_SPLIT_INC_${i}_PROTOCOL=0 ++ eval CISCO_SPLIT_INC_${i}_SPORT=0 ++ eval CISCO_SPLIT_INC_${i}_DPORT=0 ++ i=`expr $i + 1` ++ done ++ CISCO_SPLIT_INC=$i ++ fi ++ ## end Debian specific ++ + if [ -n "$CISCO_BANNER" ]; then + echo "Connect Banner:" + echo "$CISCO_BANNER" | while read LINE ; do echo "|" "$LINE" ; done +@@ -448,13 +470,31 @@ + else + set_default_route + fi +- +- if [ -n "$INTERNAL_IP4_DNS" ]; then +- $MODIFYRESOLVCONF ++ ++ case "$DNS_UPDATE" in ++ *no|*NO|*No|*nO) ++ ;; ++ *) ++ if [ -n "$INTERNAL_IP4_DNS" ]; then ++ $MODIFYRESOLVCONF ++ fi ++ ;; ++ esac ++ ++ if [ -r /etc/vpnc/vpnc-script-post-connect-action ] ; then ++ . /etc/vpnc/vpnc-script-post-connect-action + fi ++ + } + + do_disconnect() { ++ ++ # Debian specific, insert your code there to avoid modification of ++ # conffiles like this script ++ if [ -r /etc/vpnc/vpnc-script-disconnect-action ] ; then ++ . /etc/vpnc/vpnc-script-disconnect-action ++ fi ++ + destroy_tun_device + if [ -n "$CISCO_SPLIT_INC" ]; then + i=0 +@@ -480,8 +520,17 @@ + + del_vpngateway_route + +- if [ -n "$INTERNAL_IP4_DNS" ]; then +- $RESTORERESOLVCONF ++ case "$DNS_UPDATE" in ++ *no|*NO|*No|*nO) ++ ;; ++ *) ++ if [ -n "$INTERNAL_IP4_DNS" ]; then ++ $RESTORERESOLVCONF ++ fi ++ ;; ++ esac ++ if [ -r /etc/vpnc/vpnc-script-post-disconnect-action ] ; then ++ . /etc/vpnc/vpnc-script-post-disconnect-action + fi + } + +diff -urNad vpnc-0.5.1r275~/vpnc.c vpnc-0.5.1r275/vpnc.c +--- vpnc-0.5.1r275~/vpnc.c 2007-12-23 15:32:14.000000000 +0100 ++++ vpnc-0.5.1r275/vpnc.c 2007-12-23 16:28:24.000000000 +0100 +@@ -264,6 +264,9 @@ + { + setenv("VPNGATEWAY", inet_ntoa(s->dst), 1); + setenv("reason", "connect", 1); ++ // DEPRECATED, Debian specific ++ setenv("DNS_UPDATE", config[CONFIG_DNS_UPDATE], 1); ++ setenv("TARGET_NETWORKS", config[CONFIG_TARGET_NETWORKS], 1); + system(config[CONFIG_SCRIPT]); + } + --- vpnc-0.5.1r275.orig/debian/patches/00list +++ vpnc-0.5.1r275/debian/patches/00list @@ -0,0 +1,5 @@ +03_vpnc.8 +04_debianitis +#05_vpnc.conf +#06_stolen_from_head +07_fix_double_password_prompt --- vpnc-0.5.1r275.orig/debian/patches/07_fix_double_password_prompt.dpatch +++ vpnc-0.5.1r275/debian/patches/07_fix_double_password_prompt.dpatch @@ -0,0 +1,19 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 07_fix_double_password_prompt.dpatch by +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Fix taken from upstream for LP: #214399 + +@DPATCH@ +diff -urNad vpnc-0.5.1r275~/vpnc.c vpnc-0.5.1r275/vpnc.c +--- vpnc-0.5.1r275~/vpnc.c 2007-12-09 19:13:48.000000000 +0000 ++++ vpnc-0.5.1r275/vpnc.c 2008-07-30 13:24:31.000000000 +0100 +@@ -2069,7 +2069,7 @@ + for (ap = a; ap && seen_answer == 0; ap = ap->next) + if (ap->type == ISAKMP_XAUTH_ATTRIB_ANSWER + || ap->type == ISAKMP_XAUTH_ATTRIB_NEXT_PIN +- || ap->type == ISAKMP_XAUTH_ATTRIB_PASSCODE) ++ /* || ap->type == ISAKMP_XAUTH_ATTRIB_PASSCODE */) + seen_answer = 1; + + for (ap = a; ap && reject == 0; ap = ap->next)