Imported Upstream version 0.5.3r517 upstream upstream/0.5.3r517
authorAnas Nashif <anas.nashif@intel.com>
Sun, 10 Mar 2013 23:49:02 +0000 (16:49 -0700)
committerAnas Nashif <anas.nashif@intel.com>
Sun, 10 Mar 2013 23:49:02 +0000 (16:49 -0700)
53 files changed:
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION [new file with mode: 0644]
cisco-decrypt.1 [new file with mode: 0644]
cisco-decrypt.c [new file with mode: 0644]
config.c [new file with mode: 0644]
config.h [new file with mode: 0644]
crypto-gnutls.c [new file with mode: 0644]
crypto-gnutls.h [new file with mode: 0644]
crypto-openssl.c [new file with mode: 0644]
crypto-openssl.h [new file with mode: 0644]
crypto.c [new file with mode: 0644]
crypto.h [new file with mode: 0644]
decrypt-utils.c [new file with mode: 0644]
decrypt-utils.h [new file with mode: 0644]
dh.c [new file with mode: 0644]
dh.h [new file with mode: 0644]
enum2debug.pl [new file with mode: 0755]
isakmp-pkt.c [new file with mode: 0644]
isakmp-pkt.h [new file with mode: 0644]
isakmp.h [new file with mode: 0644]
makeman.pl [new file with mode: 0755]
math_group.c [new file with mode: 0644]
math_group.h [new file with mode: 0644]
mk-version [new file with mode: 0755]
nortel.txt [new file with mode: 0644]
pcf2vpnc [new file with mode: 0755]
pcf2vpnc.1 [new file with mode: 0644]
split_tunnel.txt [new file with mode: 0644]
supp.c [new file with mode: 0644]
supp.h [new file with mode: 0644]
sysdep.c [new file with mode: 0644]
sysdep.h [new file with mode: 0644]
tap-win32.h [new file with mode: 0644]
test-crypto.c [new file with mode: 0644]
test/cert.pem [new file with mode: 0644]
test/cert0.pem [new file with mode: 0644]
test/cert1.pem [new file with mode: 0644]
test/cert2.pem [new file with mode: 0644]
test/root.pem [new file with mode: 0644]
tunip.c [new file with mode: 0644]
tunip.h [new file with mode: 0644]
vpnc-disconnect [new file with mode: 0755]
vpnc-script [new file with mode: 0755]
vpnc-script-win [new file with mode: 0755]
vpnc-script-win.js [new file with mode: 0755]
vpnc.8.template [new file with mode: 0644]
vpnc.c [new file with mode: 0644]
vpnc.conf [new file with mode: 0644]
vpnc.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..6d01db6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,374 @@
+This file contains two licenses:
+
+1) The GNU GENERAL PUBLIC LICENSE Version 2
+2) dh.[hc] and math_group.[hc] license
+
+-------------------------------------------------------------
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+-------------------------------------------------------------
+
+Copyright (c) 1998 Niels Provos.  All rights reserved.
+Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..59ee346
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,207 @@
+* vpnc-0.5.3.tar.gz Wed Nov 19 21:29:22 CET 2008
+
+  User visible changes:
+
+     * Don't crash while rekeying, by Maurice Massar
+     * Fix lifetime handling if both options are present, by Maurice Massar
+     * Support providing the destination network's netmask
+     * Working with concentrators that require a firewall capable
+       client might work, by Nicholas Reilly
+     * Fix a case where pcf2vpnc would create an incorrect config line,
+       by Wolfram Sang
+     * Make vpnc work with newer development versions of openvpn on
+       Windows, by Paolo Zarpellon
+     * print logmessages while opening tun to syslog as well as stderr
+
+* vpnc-0.5.2.tar.gz Wed Nov 19 17:49:46 CET 2008
+
+  User visible changes:
+
+     * Install the license file with the binary
+     * Fix routing issues in vpcn-script-win.js, by Paolo Zarpellon
+     * Fix Phase 2 rekeying, by various authors
+     * Improvements to debug messages, by various authors
+     * Support for the NEXT_PIN for SecureID, by Phil Dibowitz and Rob West
+     * Print hints on how to fix some error conditions
+     * Add --target-network option, by Stelian Pop and Tom Schneider
+     * Try to work around the "payload too short" message instead
+       of aborting, by John Williams
+     * Fix some problems with keepalives during xauth on SonicWall and
+       ScreenOS >= 6, by Johan Fischer
+     * Improvements to syslog messages, by various authors
+     * On Linux calculate the MTU size instead of hardcoding it, by
+       Tomas Mraz
+     * Remove pid file also when not running daemonized, by Martin von Gagern
+     * Always send FW_TYPE xauth attribute, by Johan Dahlin
+     * Fix default route while setting DNS on Darwin, by Felix Buenemann
+
+  Under the hood:
+     * Move decryption code into its own files, by Wolfram Sang
+     * Use ony awk instead of awk+sed in vpnc-script, by Jukka Salmi
+     * Fix some alignment errors on ARM, by Karzist
+     * Memory handling fixes, by various authors
+
+* vpnc-0.5.1.tar.gz Mon Sep 10 23:16:41 CEST 2007
+
+     * link against -lcrypto instead of -lssl, fix from: Christophe Thil
+     * fixed crashes on 64bit platforms by Tomas Mraz, report by Brian Downing
+     * fixes to keepalive code from Brian Downing
+     * generate options part of the manpage automatically, by Wolfram Sang
+     * fix dead peer detection problems with Sonicwall, by Gerald Hanusch
+       and Wolfgang Astleitner
+     * fix disconnect problems with Sonicwall (please test if it fixes the known
+       problems with Cisco), by Gerald Hanusch and Wolfgang Astleitner
+     * again special thanks Joerg Mayer for handling all patches since the
+       last release (-:
+     * various other fixes contributed by Scott Rankin, Markus Meschederu
+
+* vpnc-0.5.0.tar.gz Thu Aug 30 19:17:10 CEST 2007
+
+     * Dead-Peer-Detection support by Kyle McKay
+     * Hybrid-Auth support by Andreas Hoffmann, merged by Chris Walter
+       (depends on OpenSSL, deactivatable at compile-time)
+     * granted Joerg Mayer svn commit privileges, special thanks to him
+       for doing so much work on vpnc during the last month (-:
+     * various other fixes contributed by Petr Salinger,
+       Christian Faulhammer, Kyle McKay, Paolo Zarpellon, Joerg Mayer,
+       Marcus Obst, Mika Liljeberg, Eduard Bloch, Wolfram Sang, Jukka
+       Salmi, Gustavo Sverzut Barbieri, Soren Hansen, Mike Javorski.
+     * first round of a general code cleanup (far less global variables
+       / etc)
+
+* vpnc-0.4.0.tar.gz Mon Feb 19 22:22:22 CET 2007
+
+     * DragonFly BSD support by Hans-Werner Hilse
+     * Solaris 10 fixes by Sunil
+     * support to read obfuscated passwords from .pcf files, based on
+       work from "HAL-9000@evilscientists.de"
+     * granted Dan Villiom Podlaski Christiansen svn commit privileges
+     * Darwin support by Dan Villiom Podlaski Christiansen
+     * UDP IP keepalive support from FreeBSD port
+     * Juniper/ScreenOS support from Marc Huber
+     * replace "--disable-natt --force-natt --udp" with "--natt-mode"
+     * null cipher support from Simon Lipp
+     * Windows/Cygwin and tap support from Paolo Zarpellon
+     * rekeying support
+     * various other fixes contributed by Joerg Mayer, Heiko Stamer,
+       Plamen Todorov, Asgeir, Jukka Salmi, Wolfram Sang, Laurence
+       MOINDROT, Chris Osicki, Anton Altaparmakov, Adam Simpkins, Ken
+       Bell, Hanno Boeck, Kyle McKay, Dennis Schneider
+
+* vpnc-0.3.3.tar.gz Sat May 14 12:23:27 CEST 2005
+
+     * ignore \r in config files
+     * (hopefuly) fixed 64bit bugs (Nicolas Boichat and Zach Brown)
+     * added support for "Split-Net" Routing
+     * introduced vpnc-script and removed vpnc-connect
+     * always search for configfiles in /etc/vpnc/ expect if the
+       filename contains at least one "/"
+     * only read /etc/vpnc/default.conf and /etc/vpnc.conf if no other
+       configfiles are provided
+     * various other fixes contributed by Anton Altaparmakov, Randy
+       Chou, "krabat", Andre Vanha and Nikolay Sturm
+
+* vpnc-0.3.2.tar.gz Mon Nov 22 01:14:29 CET 2004
+
+     * added support for preshared without xauth
+     * fixed NAT-T support with IOS and PIX
+     * fixed IP-Len header (Christian Lackas)
+     * fixed reconnection problems with IOS and PIX
+
+* vpnc-0.3.1.tar.gz Sat Nov 13 01:46:42 CET 2004
+
+     * fixed segfault in --print-config
+
+* vpnc-0.3.tar.gz Sat Nov 13 01:16:37 CET 2004
+
+     * included IPSec over UDP and NAT-T support, thanks to Tomas Mraz
+       and Martin von Gagern
+     * added support for interactive authentication (security tokens for
+       example)
+     * fixed IOS support
+     * updated man-page
+     * updated TODO list
+     * fixed byte-order in debug ouput
+
+* vpnc-0.2-rm+zomb.1.tar.gz Thu May 13 23:34:09 CEST 2004
+
+     * Fixed an off-by-two bug, thanks to Christian Lackas for this
+     * Fixed Solaris7 supported (Solaris9 does not work probably because
+       of built-in IPsec support)
+     * added support for (NT-) Domain xauth attribute
+     * cleaned up and reformatted --help output
+     * Fixed Application Version vpnc sends, fixes problems with some
+       vpn-concentrator default config where vpnc is incorrectly
+       detected as hardware client..
+
+* vpnc-0.2-rm+zomb-pre9.tar.gz Sun May 2 05:32:00 CEST 2004
+
+     * Fixed PIX supported (and PIXs are broken (-;)
+     * send and ignore lifetime update in isakmp-sa/ipsec-sa
+     * Fixed vpnc-connect to supporte load-balancing, see below
+     * added --script which gets all modecfg infos like dns-server. see
+       README
+     * automatically get pfs setting from server. --pfs should not be
+       needed anymore (broken PIXs excluded)
+     * single DES support can be enabled with --enable-1des
+
+* vpnc-0.2-rm+zomb-pre8.tar.gz Sun Apr 25 02:13:30 CEST 2004
+
+     * Fixed OpenBSD supported
+     * added support for "Cisco extension: Load Balancing"
+     * ignore lifetime update in phase1
+
+* vpnc-0.2-rm+zomb-pre7.tar.gz Wed Dec 17 20:58:51 CET 2003
+
+     * Fixed FreeBSD supported
+     * ignore "Cisco extension: XAUTH Vendor" XAuth-Attribute
+     * treat passcode as password
+     * filter "metric10 64" and the like from ip route get output
+     * updated to libgcrypt-1.1.90
+     * create /var/run/vpnc/ as necessary
+
+* vpnc-0.2-rm+zomb-pre6.tar.gz Sun Nov 2 02:15:56 CET 2003
+
+     * Fixed NetBSD supported (add routes like this: route add -host
+       131.246.89.7 -ifp tun0 131.246.89.7)
+     * cosmetic fixes
+
+* vpnc-0.2-rm+zomb-pre5.tar.gz Thu Oct 30 00:53:02 CET 2003
+
+     * created debug levels: 0 default/nothing, 1 basic, 2 control flow,
+       3 packet dump, 99 including username/password (hex encoded)
+     * small fixes to connect/disconnect scripts
+     * added --local-port to allow multiple instances of vpnc running
+       (use 0 to get a "random" port)
+
+* vpnc-0.2-rm+zomb-pre4.tar.gz Tue Oct 28 02:34:42 CET 2003
+
+     * fixed handling of errors at ipsec-sa-negotiation stage
+     * cleaned up option handling, help, version
+     * small fixes to connect/disconnect scripts
+
+* vpnc-0.2-rm+zomb-pre3.tar.gz Sun Oct 26 06:04:09 CET 2003
+
+     * added support for dh1 dh2 dh5 (pfs or ike-sa), sha1, aes128
+       aes192 aes256
+     * automatic negotiation of encryption/hash method (Note: dh-group /
+       pfs is not negotiable)
+     * cleaned up option handling
+     * small fixes to connect/disconnect scripts
+
+* vpnc-0.2-rm+zomb-pre2.tar.gz Fri Oct 24 20:27:56 CEST 2003
+
+     * debugging and detach configurable
+     * akward hanlding of options which don't require an argument
+
+* vpnc-0.2-rm+zomb-pre1.tar.gz Thu Oct 23 06:13:02 CEST 2003
+
+     * first version with libgcrypt instead of openssl (GPL compatible).
+     * far to much debugging enabled (-;
+     * works with a Version 4 VPN Concentrator.
+     * supports only 3des-md5-dh2 and no-pfs.
+
+* vpnc-0.1.tar.gz
+
+     * original version from Geoffrey Keating.
+     * doesn't work with a Version 4 VPN Concentrator.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..270d7e1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,165 @@
+# Makefile for an IPSec VPN client compatible with Cisco equipment.
+# Copyright (C) 2002  Geoffrey Keating
+# Copyright (C) 2003-2004  Maurice Massar
+# Copyright (C) 2006-2007 Dan Villiom Podlaski Christiansen
+
+# 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: Makefile 483 2012-01-14 07:54:24Z Antonio Borneo $
+
+DESTDIR=
+PREFIX=/usr/local
+ETCDIR=/etc/vpnc
+BINDIR=$(PREFIX)/bin
+SBINDIR=$(PREFIX)/sbin
+MANDIR=$(PREFIX)/share/man
+DOCDIR=$(PREFIX)/share/doc/vpnc
+
+# The license of vpnc (Gpl >= 2) is quite likely incompatible with the
+# openssl license. Openssl is one possible library used to provide certificate
+# support for vpnc (hybrid only).
+# While it is OK for users to build their own binaries linking in openssl
+# with vpnc and even providing dynamically linked binaries it is probably
+# not OK to provide the binaries inside a distribution.
+# See http://www.gnome.org/~markmc/openssl-and-the-gpl.html for further
+# details.
+# Some distributions like Suse and Fedora seem to think otherwise.
+
+# Comment this in to obtain a binary with certificate support which is
+# GPL incompliant though.
+#OPENSSL_GPL_VIOLATION=yes
+
+CRYPTO_LDADD = $(shell pkg-config --libs gnutls)
+CRYPTO_CFLAGS = $(shell pkg-config --cflags gnutls) -DCRYPTO_GNUTLS
+CRYPTO_SRCS = crypto-gnutls.c
+
+ifeq ($(OPENSSL_GPL_VIOLATION), yes)
+CRYPTO_LDADD = -lcrypto
+CRYPTO_CFLAGS = -DOPENSSL_GPL_VIOLATION -DCRYPTO_OPENSSL
+CRYPTO_SRCS = crypto-openssl.c
+endif
+
+SRCS = sysdep.c vpnc-debug.c isakmp-pkt.c tunip.c config.c dh.c math_group.c supp.c decrypt-utils.c crypto.c $(CRYPTO_SRCS)
+BINS = vpnc cisco-decrypt test-crypto
+OBJS = $(addsuffix .o,$(basename $(SRCS)))
+CRYPTO_OBJS = $(addsuffix .o,$(basename $(CRYPTO_SRCS)))
+BINOBJS = $(addsuffix .o,$(BINS))
+BINSRCS = $(addsuffix .c,$(BINS))
+VERSION := $(shell sh mk-version)
+RELEASE_VERSION := $(shell cat VERSION)
+
+CC=gcc
+CFLAGS ?= -O3 -g
+CFLAGS += -W -Wall -Wmissing-declarations -Wwrite-strings
+CFLAGS +=  $(shell libgcrypt-config --cflags) $(CRYPTO_CFLAGS)
+CPPFLAGS += -DVERSION=\"$(VERSION)\"
+LDFLAGS ?= -g
+LDFLAGS += $(shell libgcrypt-config --libs) $(CRYPTO_LDADD)
+
+ifeq ($(shell uname -s), SunOS)
+LDFLAGS += -lnsl -lresolv -lsocket
+endif
+ifneq (,$(findstring Apple,$(shell $(CC) --version)))
+# enabled in FSF GCC, disabled by default in Apple GCC
+CFLAGS += -fstrict-aliasing -freorder-blocks -fsched-interblock
+endif
+
+all : $(BINS) vpnc.8
+
+vpnc : $(OBJS) vpnc.o
+       $(CC) -o $@ $^ $(LDFLAGS)
+
+vpnc.8 : vpnc.8.template makeman.pl vpnc
+       ./makeman.pl
+
+cisco-decrypt : cisco-decrypt.o decrypt-utils.o
+       $(CC) -o $@ $^ $(LDFLAGS)
+
+test-crypto : sysdep.o test-crypto.o crypto.o $(CRYPTO_OBJS)
+       $(CC) -o $@ $^ $(LDFLAGS)
+
+.depend: $(SRCS) $(BINSRCS)
+       $(CC) -MM $(SRCS) $(BINSRCS) $(CFLAGS) $(CPPFLAGS) > $@
+
+vpnc-debug.c vpnc-debug.h : isakmp.h enum2debug.pl
+       LC_ALL=C perl -w ./enum2debug.pl isakmp.h >vpnc-debug.c 2>vpnc-debug.h
+
+vpnc.ps : vpnc.c
+       enscript -E -G -T 4 --word-wrap -o- $^ | psnup -2 /dev/stdin $@
+
+../vpnc-%.tar.gz : vpnc-$*.tar.gz
+
+etags :
+       etags *.[ch]
+ctags :
+       ctags *.[ch]
+
+vpnc-%.tar.gz :
+       mkdir vpnc-$*
+       LC_ALL=C svn info -R | awk -v RS='' -v FS='\n' '/Node Kind: file/ {print substr($$1,7)}' | \
+               tar -cf - -T - | tar -xf - -C vpnc-$*/
+       tar -czf ../$@ vpnc-$*
+       rm -rf vpnc-$*
+
+test : all
+       ./test-crypto test/cert.pem test/cert0.pem test/cert1.pem test/cert2.pem test/root.pem
+       #./test-crypto test/cert.pem test/cert0.crt test/cert1.crt test/cert2.crt test/root.crt
+
+dist : VERSION vpnc.8 vpnc-$(RELEASE_VERSION).tar.gz
+
+clean :
+       -rm -f $(OBJS) $(BINOBJS) $(BINS) tags
+
+distclean : clean
+       -rm -f vpnc-debug.c vpnc-debug.h vpnc.ps vpnc.8 .depend
+
+install-common: all
+       install -d $(DESTDIR)$(ETCDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(DOCDIR)
+       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 -m600 vpnc.conf $(DESTDIR)$(ETCDIR)/default.conf
+       install -m755 vpnc-disconnect $(DESTDIR)$(SBINDIR)
+       install -m755 pcf2vpnc $(DESTDIR)$(BINDIR)
+       install -m644 vpnc.8 $(DESTDIR)$(MANDIR)/man8
+       install -m644 pcf2vpnc.1 $(DESTDIR)$(MANDIR)/man1
+       install -m644 cisco-decrypt.1 $(DESTDIR)$(MANDIR)/man1
+       install -m644 COPYING $(DESTDIR)$(DOCDIR)
+
+install : install-common
+       install -m755 vpnc $(DESTDIR)$(SBINDIR)
+       install -m755 cisco-decrypt $(DESTDIR)$(BINDIR)
+
+install-strip : install-common
+       install -s -m755 vpnc $(DESTDIR)$(SBINDIR)
+       install -s -m755 cisco-decrypt $(DESTDIR)$(BINDIR)
+
+uninstall :
+       rm -f $(DESTDIR)$(SBINDIR)/vpnc \
+               $(DESTDIR)$(SBINDIR)/vpnc-disconnect \
+               $(DESTDIR)$(BINDIR)/pcf2vpnc \
+               $(DESTDIR)$(BINDIR)/cisco-decrypt \
+               $(DESTDIR)$(MANDIR)/man1/cisco-decrypt.1 \
+               $(DESTDIR)$(MANDIR)/man1/pcf2vpnc \
+               $(DESTDIR)$(MANDIR)/man8/vpnc.8
+       @echo NOTE: remove $(DESTDIR)$(ETCDIR) manually
+
+.PHONY : clean distclean dist all install install-strip uninstall
+
+#
+-include .depend
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..ac3fd28
--- /dev/null
+++ b/README
@@ -0,0 +1,233 @@
+A VPN client compatible with Cisco's EasyVPN equipment.
+
+Supports IPSec (ESP) with Mode Configuration and Xauth.  Supports only
+shared-secret IPSec authentication with Xauth,
+AES (256, 192, 128), 3DES, 1DES, MD5, SHA1,
+DH1/2/5 and IP tunneling.
+
+It runs entirely in userspace. Only "Universal TUN/TAP device
+driver support" is needed in kernel.
+
+Project home page: http://www.unix-ag.uni-kl.de/~massar/vpnc/
+
+
+========= Contents of this file ============================================
+
+
+- General configuration of vpnc
+- Using a modified script
+- Additional steps to configure hybrid authentication
+- Setting up vpnc on Vista 64bit
+- Known problems
+
+
+========= General configuration of vpnc ====================================
+
+
+Required Libraries: libgcrypt (version 1.1.90 for 0.2-rm+zomb-pre7 or later)
+                    libopenssl (optional, to provide hybrid support)
+
+It reads configuration data from the following places:
+
+- From command-line options
+- From config file(s) specified on the command line
+- From /etc/vpnc/default.conf  only if no configfile was given on the command line
+- From /etc/vpnc.conf          same as default.conf, ie: both are used, or none
+- If a setting is not given in any of those places, it prompts the user.
+
+The configuration information it currently needs is:
+
+          Option Config file item
+       --gateway IPSec gateway
+            --id IPSec ID
+     (no option) IPSec secret
+      --username Xauth username
+     (no option) Xauth password
+
+A sample configuration file is:
+
+# This is a sample configuration file.
+IPSec gateway 127.0.0.1
+IPSec ID laughing-vpn
+IPSec secret hahaha
+Xauth username geoffk
+
+Note that all strings start exactly one space after the keyword
+string, and run to the end of the line.  This lets you put any kind of
+weird character (except CR, LF and NUL) in your strings, but it does mean
+you can't add comments after a string, or spaces before them.
+
+It may be easier to use the --print-config option to generate the
+config file, and then delete any lines (like a password) that you want
+to be prompted for.
+
+If you don't know the Group ID and Secret string, ask your
+administrator. If (s)he declines and refers to the
+configuration files provided for the vpnclient program, tell
+him/her that the contents of that files is (though scrambled)
+not really protected. If you have a working configuration file
+(.pcf file) for the Cisco client then you can use the pcf2vpnc
+utility instead, which will extract most/all of the required
+information and convert it into a vpnc configuration file.
+
+
+========= Using a modified script ==========================================
+
+
+Please note that vpnc itself does NOT setup routing. You need to do this
+yourself, or use --script "Script" in the config file.
+The default script is /etc/vpnc/vpnc-script which sets a default route
+to the remote network, or if the Concentrator provided split-network
+settings, these are used to setup routes.
+
+This option is passed to system(), so you can use any shell-specials you
+like. This script gets called three times:
+$reason == pre-init: this is before vpnc opens the tun device
+   so you can do what is necessary to ensure that it is available.
+   Note that none of the variables mentioned below is available
+$reason == connect: this is what used to be "Config Script".
+   The connection is established, but vpnc will not begin forwarding
+   packets until the script finishes.
+$reason == disconnect: This is called just after vpnc received a signal.
+   Note that vpnc will not forward packets anymore while the script is
+   running or thereafter.
+
+Information is passed from vpnc via environment variables:
+
+#* 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 servers
+#* 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)
+
+Currently vpnc-script is not directly configurable from configfiles.
+However, a workaround is to use a "wrapper-script" like this, to
+disable /etc/resolv.conf rewriting and setup a custom split-routing:
+
+------------------------------
+#!/bin/sh
+
+# this effectively disables changes to /etc/resolv.conf
+INTERNAL_IP4_DNS=
+
+# This sets up split networking regardless
+# of the concentrators specifications.
+# You can add as many routes as you want,
+# but you must set the counter $CISCO_SPLIT_INC
+# accordingly
+CISCO_SPLIT_INC=1
+CISCO_SPLIT_INC_0_ADDR=131.246.89.7
+CISCO_SPLIT_INC_0_MASK=255.255.255.255
+CISCO_SPLIT_INC_0_MASKLEN=32
+CISCO_SPLIT_INC_0_PROTOCOL=0
+CISCO_SPLIT_INC_0_SPORT=0
+CISCO_SPLIT_INC_0_DPORT=0
+
+. /etc/vpnc/vpnc-script
+------------------------------
+
+Store this example script, for example in /etc/vpnc/custom-script,
+do a "chmod +x /etc/vpnc/custom-script" and add
+"Script /etc/vpnc/custom-script" to your configuration.
+
+
+========= Additional steps to configure hybrid authentication ==============
+
+
+To use the hybrid extension add
+       Use Hybrid Auth
+to your .conf file or add
+       --hybrid
+when starting vpnc.
+
+The trusted root certificate may be passed by adding
+       CA-File <root_certificate.pem>
+to your .conf file or adding
+       --ca-file <root_certificate.pem>
+when starting vpnc.
+
+The trusted root certificate may be contained in a directory by adding
+       CA-Dir <trusted_certificate_directory>
+to your .conf file or adding
+       --ca-dir <trusted_certificate_directory>
+when starting vpnc.
+The default is
+       /etc/ssl
+
+As the trusted certificate is referenced by the hash of the subject name,
+the directory has to contain the certificate named like this hash_value.
+A link can also be used like in /etc/ssl/certs/.
+The hash value can be calculated by e.g.
+       openssl x509 -in <ca_certfile.pem> -noout -hash
+
+
+========= Setting up vpnc on Vista 64bit ===================================
+
+
+1. Install cygwin onto vista.  Details here: http://www.cygwin.com/
+2. Make sure you install the development options for cygwin to give you
+   access to make and gcc etc
+3. Make sure you install libgcrypt for cygwin as it is needed in the make
+4. Modify the bash.exe to run as administrator or you will have
+   privilege issues later, this is done on the properties tab of the
+   executable in c:/cygwin/bin
+4. Download the latest vpnc tarball from here
+   http://www.unix-ag.uni-kl.de/~massar/vpnc/
+5. Unzip and explode the tarball
+6. modify tap-win32.h to change #define TAP_COMPONENT_ID "tap0801" to
+   "tap0901" (No sure if this is necessary but I did it and it is working
+   for me)
+7. make
+8. You should have a shinny new vpnc.exe
+9. Download openvpn from http://openvpn.net/download.html.  I used
+   openvpn-2.1_rc4-install.exe as all other version I tried had errors
+   during install
+10. Run the exe but only install the TAP-Win32 Adapter V9
+11. Go to control Panel | Network Connections and rename the TAP device
+    to my-tap
+12. create a /etc/vpnc/default.conf file something like this
+------------- begin -------------
+IPSec gateway YOURGATEWAY
+IPSec ID YOURID
+IPSec obfuscated secret YOURREALYLONGHEXVALUE (you can use your clear
+text password here if you remove obfuscated)
+Xauth username YOURUSERNAME
+Xauth password YOURPASSWORD
+Interface name my-tap
+Interface mode tap
+Local Port 0
+------------- end ---------------
+    See the general config section above and the manpage for details.
+
+
+========= Known problems ===================================================
+
+
+Known problems:
+
+Problem:
+In some environments it may happen that stuff works for a while and then
+stops working.
+
+Reason:
+The dhcp leases are very short intervals and on each renew the dhcp
+client overwrites things like /etc/resolv.conf and maybe the default route.
+
+Solution:
+Fix your dhcpclient. On Debian that problem can be fixed by installing
+and using resolvconf to modify that file instead of modifying it directly.
+
+
+============================================================================
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..3634312
--- /dev/null
+++ b/TODO
@@ -0,0 +1,131 @@
+TODO list
+
+* On opensolaris we need to add -interface in case the route points
+  to an interface instead of a next hop, see
+  http://www.cwinters.com/blog/2008/02/02/getting_vpnc_to_work_on_opensolaris.html
+
+* Add native ESP support
+
+* Allow PSK without xauth.
+
+* further research into the "packet too short" messages.
+  - see http://lists.unix-ag.uni-kl.de/pipermail/vpnc-devel/2005-February/000553.html
+    for more information
+
+* pass IPSEC target network to script
+  - use it to initialize the tunnel interface and routes
+
+* clean up scripts
+  - config-support for vpnc-script
+  - customizable handling of routing
+  - switch to disable resolv.conf rewriting
+  - do $something with split_dns
+
+* beautify packet dump output
+
+* large code cleanup
+  - at least one function per packet (instead of one function per phase)
+  - factor out a central select-loop, send / receive code, nat-t handling
+  - maybe even add some sort of state machine
+  - get a rid of remaining (non-const) global variables
+
+* implement phase1 rekeying (with or without xauth-reauthentication)
+* implement compression
+* try a list of gateways (backup server)
+* Generate the manpage command line part directly from vpnc
+
+* optionally use in-kernel-ipsec with pf-key
+  - merge patch
+
+* add support for pcap and dump decrypted traffic
+
+* research/bugs:
+  - usernames containing "@" unable to login
+  - ipsec over tcp
+  - nortel support?
+  - segfault if > 100 routes/acls (to large packet? read size?)
+    (probably "fixed" by increasing the size in r_packet in vpnc.c,
+    but why did it crash?)
+  - amd64 somehow broken? maybe gcc bugs??
+  - some debug prints get the endianess wrong
+  - In case the psk in hybrid isn't correct, the server sends annother AM_2
+    packet - to port 500 of course, even if we are using nat-t and talked on
+    4500 already. We currently don't handle that.
+
+* optional drop root (rekey? reconnect? vpnc-script calls?)
+  - Don't drop privileges, ever, but allow to be run suid.
+  - If euid != ruid, clear out env on program start.
+  - Sanitize variables for vpnc-script (snarf code from
+    callscript.c from dhcpclient).
+  - If euid != ruid, disable command line options (but not the profile
+    parameter).
+  - If euid != ruid, treat profiles as filenames only. They must not
+    be paths, i.e. contain PATHSEP. Read them relative to /etc/vpnc.
+  - Make sure vpnc-disconnect only kills processes owned by same user.
+
+* implement certificate support
+* implement dsa certificates in hybrid mode
+* Adapt lifetime (when given as time) to certificate lifetime etc
+  (rfc2401, 4.4.3)
+* implement main mode for phase 1 (needed to *use* certificates in
+  many cases)
+
+* factor out crypto stuff (cipher, hmac, dh)
+  - http://libtomcrypt.org/features.html
+  - http://www.foldr.org/~michaelw/ patch fertig
+  - libgcrypt (old too?)
+  - autodetect?
+  - openssl??
+  - relicense to gpl+ssl?
+
+* links to packages, howtos, etc.
+  - kvpnc http://home.gna.org/kvpnc/
+  - vpnc+Zaurus http://users.ox.ac.uk/~oliver/vpnc.html
+  - linux-mipsel (WRT54G) http://openwrt.alphacore.net/vpnc_0.3.2_mipsel.ipk
+  - howto-de http://localhost.ruhr.de/~stefan/uni-duisburg.ai/vpnc.shtml
+
+----
+
+* DONE implement hybrid-auth
+* DONE implement DPD, RFC 3706 Dead Peer Detection
+* DONE --local-address
+* DONE implement phase2 rekeying
+* DONE support rsa-SecurID token which sometimes needs 2 IDs
+* DONE add macosx support
+* DONE update "check pfs setting" error message
+* DONE make doing xauth optional
+* DONE implement udp transport NAT-T
+* DONE fix Makefile (install, DESTDIR, CFLAGS, ...)
+* DONE implement udp encap via port 10.000
+* DONE svn-Repository
+* DONE XAUTH Domain: (empty)
+* DONE check /dev/net/tun, reject /dev/tun* on linux
+* DONE spawn post-connect script
+* DONE ask for dns/wins servers, default domain, pfs setting, netmask
+* DONE automatic handling of pfs
+* DONE send version string
+* DONE send lifetime in phase1 and phase2
+* DONE accept (== ignore) lifetime update in phase1
+* DONE load balancing support (fixes INVALID_EXCHANGE_TYPE in S4.5)
+* DONE include OpenBSD support from Nikolay Sturm
+* DONE memleak fix from Sebastian Biallas
+* DONE fix link at alioth
+* DONE include man-page
+* DONE post rfcs and drafts
+* DONE post link to http://www.liebchen-online.de/vpn-zaurus.html
+* DONE passcode == password
+* DONE support for new libgcrypt versions
+* DONE make /var/run/vpnc as needed
+* DONE ignore "metric10 xx"
+* DONE ignore attr 32136! (Cisco extension: XAUTH Vendor)
+* DONE FreeBSD supported
+* DONE NetBSD supported
+* DONE fix vpnc-disconnect
+* DONE --verbose
+* DONE hide user/pass from --debug output
+* DONE don't ignore all notifies at ipsec-sa-negotation
+* DONE VERSION
+* DONE --pid-file
+* DONE --non-interactive
+* DONE fix delete message
+* DONE implement ISAKMP and IPSEC SA negotiate support
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..be14282
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.5.3
diff --git a/cisco-decrypt.1 b/cisco-decrypt.1
new file mode 100644 (file)
index 0000000..3345fd0
--- /dev/null
@@ -0,0 +1,29 @@
+.TH "CISCO-DECRYPT" "1" "August 2007" "cisco-decrypt" "vpnc"
+.SH "NAME"
+cisco-decrypt \- decrypts an obfuscated Cisco vpn client pre-shared key
+.\"
+.\" $Id: cisco-decrypt.1 377 2008-11-26 08:03:43Z Joerg Mayer $
+.\"
+.SH "SYNOPSIS"
+.B cisco-decrypt
+\fI<obfuscated pre-shared key>
+.SH "DESCRIPTION"
+This command accompanies \fBvpnc\fR. It decrypts the obfuscated
+pre-shared key from *.pcf\-configuration files, which must be
+specified on the command line.
+
+The result will be printed to STDOUT.
+.SH "AUTHOR"
+cisco-decrypt was originally written by Maurice Massar. This man\-page was
+written by Jörg Mayer, based on the pcf2vpnc manpage written by Wolfram Sang
+(ninja(at)the\-dreams.de).
+
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+.PP
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common\-licenses/GPL.
+.SH "SEE ALSO"
+.BR vpnc(8)
+.BR pcf2vpnc(1)
diff --git a/cisco-decrypt.c b/cisco-decrypt.c
new file mode 100644 (file)
index 0000000..f66388a
--- /dev/null
@@ -0,0 +1,64 @@
+/* Decoder for password encoding of Cisco VPN client.
+   Copyright (C) 2005 Maurice Massar
+
+   Thanks to HAL-9000@evilscientists.de for decoding and posting the algorithm!
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "decrypt-utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <gcrypt.h>
+#include <errno.h>
+
+int main(int argc, char *argv[])
+{
+       int i, len, ret = 0;
+       char *bin, *pw = NULL;
+
+       gcry_check_version(NULL);
+
+       if (argc == 1 || *argv[1] == '-') {
+               fprintf(stderr,
+                       "\nUsage: %s DEADBEEF...012345678 424242...7261\n"
+                       "    Print decoded result to stdout\n\n",
+                       argv[0]);
+               exit(1);
+       }
+       /* Hack for use in pcf2vpnc */
+       if (*argv[1] == 'q') {
+               exit(1);
+       }
+
+       for (i = 1; i < argc; i++) {
+               ret = hex2bin(argv[i], &bin, &len);
+               if (ret != 0) {
+                       perror("decoding input");
+                       continue;
+               }
+               ret = deobfuscate(bin, len, (const char **)&pw, NULL);
+               free(bin);
+               if (ret != 0) {
+                       perror("decrypting input");
+                       continue;
+               }
+               printf("%s\n", pw);
+               free(pw);
+       }
+
+       exit(ret != 0);
+}
diff --git a/config.c b/config.c
new file mode 100644 (file)
index 0000000..875b5ca
--- /dev/null
+++ b/config.c
@@ -0,0 +1,998 @@
+/* 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 514 2012-01-29 11:23:05Z Antonio Borneo $
+*/
+
+#define _GNU_SOURCE
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/utsname.h>
+
+#include <gcrypt.h>
+
+#include "sysdep.h"
+#include "config.h"
+#include "vpnc.h"
+#include "supp.h"
+#include "decrypt-utils.h"
+
+const char *config[LAST_CONFIG];
+
+int opt_debug = 0;
+int opt_nd;
+int opt_1des, opt_no_encryption, opt_auth_mode;
+enum natt_mode_enum opt_natt_mode;
+enum vendor_enum opt_vendor;
+enum if_mode_enum opt_if_mode;
+uint16_t opt_udpencapport;
+uint16_t opt_nortel_client_id;
+
+static void log_to_stderr(int priority __attribute__((unused)), const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       vfprintf(stderr, format, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+}
+
+void (*logmsg)(int priority, const char *format, ...) = log_to_stderr;
+
+
+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;
+
+       printf("   ");
+       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:%s", str, (len <= 16) ? " " : "\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 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_ike_dh(void)
+{
+       return "dh2";
+}
+
+static const char *config_def_pfs(void)
+{
+       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";
+}
+
+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_dpd_idle(void)
+{
+       return "300";
+}
+
+static const char *config_ca_dir(void)
+{
+       return "/etc/ssl/certs";
+}
+
+static const char *config_def_auth_mode(void)
+{
+       return "default";
+}
+
+static const char *config_def_nortel_client_id(void)
+{
+       return "V04_15";
+}
+
+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_target_network(void)
+{
+       return "0.0.0.0/0.0.0.0";
+}
+
+static const struct config_names_s {
+       enum config_enum nm;
+       const int needsArgument;
+       const int long_only;
+       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_IPSEC_GATEWAY, 1, 0,
+               "--gateway",
+               "IPSec gateway ",
+               "<ip/hostname>",
+               "IP/name of your IPSec gateway",
+               NULL
+       }, {
+               CONFIG_IPSEC_ID, 1, 0,
+               "--id",
+               "IPSec ID ",
+               "<ASCII string>",
+               "your group name",
+               NULL
+       }, {
+               CONFIG_IPSEC_SECRET, 1, 0,
+               NULL,
+               "IPSec secret ",
+               "<ASCII string>",
+               "your group password (cleartext)",
+               NULL
+       }, {
+               CONFIG_IPSEC_SECRET_OBF, 1, 1,
+               NULL,
+               "IPSec obfuscated secret ",
+               "<hex string>",
+               "your group password (obfuscated)",
+               NULL
+       }, {
+               CONFIG_XAUTH_USERNAME, 1, 0,
+               "--username",
+               "Xauth username ",
+               "<ASCII string>",
+               "your username",
+               NULL
+       }, {
+               CONFIG_XAUTH_PIN, 1, 0,
+               NULL,
+               "Xauth PIN ",
+               "<ASCII string>",
+               "PIN for Nortel Two-Factor Authentication",
+               NULL
+       }, {
+               CONFIG_XAUTH_PASSWORD, 1, 0,
+               NULL,
+               "Xauth password ",
+               "<ASCII string>",
+               "your password (cleartext)",
+               NULL
+       }, {
+               CONFIG_XAUTH_PASSWORD_OBF, 1, 1,
+               NULL,
+               "Xauth obfuscated password ",
+               "<hex string>",
+               "your password (obfuscated)",
+               NULL
+       }, {
+               CONFIG_DOMAIN, 1, 1,
+               "--domain",
+               "Domain ",
+               "<ASCII string>",
+               "(NT-) Domain name for authentication",
+               NULL
+       }, {
+               CONFIG_XAUTH_INTERACTIVE, 0, 1,
+               "--xauth-inter",
+               "Xauth interactive",
+               NULL,
+               "enable interactive extended authentication (for challenge response auth)",
+               NULL
+       }, {
+               CONFIG_VENDOR, 1, 1,
+               "--vendor",
+               "Vendor ",
+               "<cisco/netscreen/nortel>",
+               "vendor of your IPSec gateway",
+               config_def_vendor
+       }, {
+               CONFIG_NATT_MODE, 1, 1,
+               "--natt-mode",
+               "NAT Traversal Mode ",
+               "<natt/none/force-natt/cisco-udp/nortel-udp>",
+               "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"
+               " * nortel-udp -- Nortel proprietary UDP encapsulation\n"
+               "Note: cisco-tcp encapsulation is not yet supported\n",
+               config_def_natt_mode
+       }, {
+               CONFIG_SCRIPT, 1, 1,
+               "--script",
+               "Script ",
+               "<command>",
+               "command is executed using system() to configure the interface,\n"
+               "routing and so on. Device name, IP, etc. are passed using environment\n"
+               "variables, see README. This script is executed right after ISAKMP is\n"
+               "done, but before tunneling is enabled. It is called when vpnc\n"
+               "terminates, too\n",
+               config_def_script
+       }, {
+               CONFIG_IKE_DH, 1, 1,
+               "--dh",
+               "IKE DH Group ",
+               "<dh1/dh2/dh5>",
+               "name of the IKE DH Group",
+               config_def_ike_dh
+       }, {
+               CONFIG_IPSEC_PFS, 1, 1,
+               "--pfs",
+               "Perfect Forward Secrecy ",
+               "<nopfs/dh1/dh2/dh5/server>",
+               "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_NORTEL_CLIENT_ID, 1, 1,
+               "--nortel-client-id",
+               "Nortel Client ID ",
+               "<list/0-65535/ASCII string>",
+               "Nortel Client version ID sent during connection.\n"
+               "Use \"list\" to print allowed values.",
+               config_def_nortel_client_id
+       }, {
+               CONFIG_VERSION, 1, 1,
+               "--application-version",
+               "Application version ",
+               "<ASCII string>",
+               "Application Version to report. Note: Default string is generated at runtime.",
+               config_def_app_version
+       }, {
+               CONFIG_IF_NAME, 1, 1,
+               "--ifname",
+               "Interface name ",
+               "<ASCII string>",
+               "visible name of the TUN/TAP interface",
+               NULL
+       }, {
+               CONFIG_IF_MODE, 1, 1,
+               "--ifmode",
+               "Interface mode ",
+               "<tun/tap>",
+               "mode of TUN/TAP interface:\n"
+               " * tun: virtual point to point interface (default)\n"
+               " * tap: virtual ethernet interface\n",
+               config_def_if_mode
+       }, {
+               CONFIG_IF_MTU, 1, 1,
+               "--ifmtu",
+               "Interface MTU ",
+               "<0-65535>",
+               "Set MTU for TUN/TAP interface (default 0 == automatic detect)",
+               NULL
+       }, {
+               CONFIG_DEBUG, 1, 1,
+               "--debug",
+               "Debug ",
+               "<0/1/2/3/99>",
+               "Show verbose debug messages\n"
+               " *  0: Do not print debug information.\n"
+               " *  1: Print minimal debug information.\n"
+               " *  2: Show statemachine and packet/payload type information.\n"
+               " *  3: Dump everything exluding authentication data.\n"
+               " * 99: Dump everything INCLUDING AUTHENTICATION data (e.g. PASSWORDS).\n",
+               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 ",
+               "<filename>",
+               "store the pid of background process in <filename>",
+               config_def_pid_file
+       }, {
+               CONFIG_LOCAL_ADDR, 1, 1,
+               "--local-addr",
+               "Local Addr ",
+               "<ip/hostname>",
+               "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 ",
+               "<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_DPD_IDLE, 1, 1,
+               "--dpd-idle",
+               "DPD idle timeout (our side) ",
+               "<0,10-86400>",
+               "Send DPD packet after not receiving anything for <idle> seconds.\n"
+               "Use 0 to disable DPD completely (both ways).\n",
+               config_def_dpd_idle
+       }, {
+               CONFIG_NON_INTERACTIVE, 0, 1,
+               "--non-inter",
+               "Noninteractive",
+               NULL,
+               "Don't ask anything, exit on missing options",
+               NULL
+       }, {
+               CONFIG_AUTH_MODE, 1, 1,
+               "--auth-mode",
+               "IKE Authmode ",
+               "<default/cert/psk/hybrid/username/token/PIN-token/token-SW/gpassword>",
+               "Authentication mode:\n"
+               " * default:   maps to vendor specific default mode\n"
+               " * cert:      server + client certificate (not implemented yet)\n"
+               " * psk:       Cisco pre-shared key (default for Cisco)\n"
+               " * hybrid:    Cisco server certificate + xauth (if built with openssl support)\n"
+               " * username:  Nortel User Name and Password Authentication\n"
+               " * token:     Nortel Group Security - Response Only Token - Use Passcode (default for Nortel)\n"
+               " * PIN-token: Nortel Group Security - Response Only Token - Use Two-Factor Card\n"
+               " * token-SW:  Nortel Group Security - Response Only Token - Use SoftID Software (not implemented yet)\n"
+               " * gpassword: Nortel Group Security - Group Password Authentication",
+               config_def_auth_mode
+       }, {
+               CONFIG_CA_FILE, 1, 1,
+               "--ca-file",
+               "CA-File ",
+               "<filename>",
+               "filename and path to the CA-PEM-File",
+               NULL
+       }, {
+               CONFIG_CA_DIR, 1, 1,
+               "--ca-dir",
+               "CA-Dir ",
+               "<directory>",
+               "path of the trusted CA-Directory",
+               config_ca_dir
+       }, {
+               CONFIG_IPSEC_TARGET_NETWORK, 1, 1,
+               "--target-network",
+               "IPSEC target network ",
+               "<target network/netmask>",
+               "Target network in dotted decimal or CIDR notation\n",
+               config_def_target_network
+       }, {
+               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 (llen > 0 && line[llen - 1] == '\n')
+                       line[--llen] = 0;
+               if (llen > 0 && line[llen - 1] == '\r')
+                       line[--llen] = 0;
+               linenum++;
+               for (i = 0; config_names[i].name != NULL; i++) {
+                       if (strncasecmp(config_names[i].name, line,
+                                       strlen(config_names[i].name)) == 0) {
+                               /* boolean implementation, using harmless 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 print_level)
+{
+       int c;
+
+       printf("Usage: %s [--version] [--print-config] [--help] [--long-help] [options] [config files]\n\n",
+               argv0);
+       printf("Options:\n");
+       for (c = 0; config_names[c].name != NULL; c++) {
+               if (config_names[c].long_only > print_level)
+                       continue;
+
+               printf("  %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));
+
+               print_desc("      ", config_names[c].desc);
+
+               if (config_names[c].get_def != NULL)
+                       printf("    Default: %s\n", config_names[c].get_def());
+
+               printf("  conf-variable: %s%s\n", config_names[c].name,
+                       (config_names[c].type == NULL ? "" : config_names[c].type));
+
+               printf("\n");
+       }
+
+       if (!print_level)
+               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");
+#ifdef OPENSSL_GPL_VIOLATION
+       printf("Built with openssl certificate support. Be aware of the\n"
+               "license implications.\n");
+#else /* OPENSSL_GPL_VIOLATION */
+       printf("Built with certificate support.\n");
+#endif /* OPENSSL_GPL_VIOLATION */
+       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
+                               || 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]
+                               && 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;
+
+               if (!strcmp(config[CONFIG_VENDOR], "cisco")) {
+                       opt_vendor = VENDOR_CISCO;
+               } else if (!strcmp(config[CONFIG_VENDOR], "netscreen")) {
+                       opt_vendor = VENDOR_NETSCREEN;
+               } else if (!strcmp(config[CONFIG_VENDOR], "nortel")) {
+                       opt_vendor = VENDOR_NORTEL;
+               } else {
+                       printf("%s: unknown vendor %s\nknown vendors: cisco netscreen nortel\n",
+                               argv[0], config[CONFIG_VENDOR]);
+                       exit(1);
+               }
+
+               if (!strcmp(config[CONFIG_AUTH_MODE], "psk")) {
+                       opt_auth_mode = AUTH_MODE_PSK;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "cert")) {
+                       opt_auth_mode = AUTH_MODE_CERT;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "hybrid")) {
+                       opt_auth_mode = AUTH_MODE_HYBRID;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "username")) {
+                       opt_auth_mode = AUTH_MODE_NORTEL_USERNAME;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "token")) {
+                       opt_auth_mode = AUTH_MODE_NORTEL_TOKEN;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "PIN-token")) {
+                       opt_auth_mode = AUTH_MODE_NORTEL_PINTOKEN;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "token-SW")) {
+                       opt_auth_mode = AUTH_MODE_NORTEL_TOKENSW;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "gpassword")) {
+                       opt_auth_mode = AUTH_MODE_NORTEL_GPASSWORD;
+               } else if (!strcmp(config[CONFIG_AUTH_MODE], "default")) {
+                       switch (opt_vendor) {
+                               case VENDOR_NORTEL:
+                                       opt_auth_mode = AUTH_MODE_NORTEL_TOKEN;
+                                       break;
+                               case VENDOR_NETSCREEN:
+                               case VENDOR_CISCO:
+                               default:
+                                       opt_auth_mode = AUTH_MODE_PSK;
+                                       break;
+                       }
+               } else {
+                       printf("%s: unknown authentication mode \"%s\"\nknown modes: "
+                               "default/cert/psk/hybrid/username/token/PIN-token/token-SW/gpassword\n",
+                               argv[0], config[CONFIG_AUTH_MODE]);
+                       exit(1);
+               }
+
+               if (((opt_vendor == VENDOR_NORTEL) &&
+                       ((opt_auth_mode != AUTH_MODE_CERT) &&
+                        (opt_auth_mode != AUTH_MODE_NORTEL_USERNAME) &&
+                        (opt_auth_mode != AUTH_MODE_NORTEL_TOKEN) &&
+                        (opt_auth_mode != AUTH_MODE_NORTEL_PINTOKEN) &&
+                        (opt_auth_mode != AUTH_MODE_NORTEL_TOKENSW) &&
+                        (opt_auth_mode != AUTH_MODE_NORTEL_GPASSWORD))) ||
+                   ((opt_vendor == VENDOR_CISCO) &&
+                       ((opt_auth_mode != AUTH_MODE_CERT) &&
+                        (opt_auth_mode != AUTH_MODE_PSK) &&
+                        (opt_auth_mode != AUTH_MODE_HYBRID))) ||
+                   ((opt_vendor == VENDOR_NETSCREEN) &&
+                       ((opt_auth_mode != AUTH_MODE_CERT) &&
+                        (opt_auth_mode != AUTH_MODE_PSK) &&
+                        (opt_auth_mode != AUTH_MODE_HYBRID)))) {
+                       printf("%s: Auth Mode \"%s\" not valid for Vendor \"%s\"\n",
+                               argv[0], config[CONFIG_AUTH_MODE], config[CONFIG_VENDOR]);
+                       exit(1);
+               }
+
+               if (opt_auth_mode == AUTH_MODE_CERT ||
+                   opt_auth_mode == AUTH_MODE_NORTEL_TOKENSW) {
+                       printf("%s: unimplemented Auth Mode \"%s\"\n",
+                               argv[0], config[CONFIG_AUTH_MODE]);
+                       exit(1);
+               }
+
+               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 if (!strcmp(config[CONFIG_NATT_MODE], "nortel-udp")) {
+                       opt_natt_mode = NATT_NORTEL_UDP;
+               } else {
+                       printf("%s: unknown nat traversal mode %s\nknown modes: natt none force-natt cisco-udp nortel-udp\n", argv[0], config[CONFIG_NATT_MODE]);
+                       exit(1);
+               }
+               if ((opt_vendor == VENDOR_NORTEL) &&
+                   (opt_natt_mode != NATT_NONE) &&
+                   (opt_natt_mode != NATT_NORTEL_UDP)) {
+                       printf("%s: Vendor nortel only accepts nat traversal modes: none nortel-udp\n"
+                               "\tdefaults to \"none\"\n", argv[0]);
+                       opt_natt_mode = NATT_NONE;
+               }
+               if ((opt_vendor != VENDOR_NORTEL) &&
+                   (opt_natt_mode == NATT_NORTEL_UDP)) {
+                       printf("%s: nat traversal mode nortel-udp allowed for Vendor nortel only\n"
+                               "\tdefaults to \"none\"\n", argv[0]);
+                       opt_natt_mode = NATT_NONE;
+               }
+
+               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_NORTEL_CLIENT_ID], "list")) {
+                       const struct debug_strings *p;
+                       printf("vpnc: Valid values for Nortel Client version ID:\n"
+                               "  Integers in the range:\n  - <0-65535>\n"
+                               "  and following strings, equivalent to value in brackets:\n");
+                       for (p = ike_nortel_client_id_enum_array ; p->string ; p++)
+                               printf("  - \"%.*s\"\t(%d)\n", strlen(p->string) - 17, p->string + 16, p->id);
+                       exit(0);
+               }
+
+               if (config[CONFIG_NORTEL_CLIENT_ID][0] == 'V') {
+                       char *s;
+                       const struct debug_strings *p;
+                       asprintf(&s, " (NORTEL_CLIENT_%s)", config[CONFIG_NORTEL_CLIENT_ID]);
+                       for (p = ike_nortel_client_id_enum_array ; p->string ; p++)
+                               if (!strcmp(s, p->string)) {
+                                       opt_nortel_client_id = p->id;
+                                       break;
+                               }
+                       free(s);
+                       if (!p->string) {
+                               printf("%s: invalid [1] Nortel Client version ID %s\n"
+                                       " To list valid values, run \"%s --nortel-client id list\"\n",
+                                       argv[0], config[CONFIG_NORTEL_CLIENT_ID], argv[0]);
+                               exit(1);
+                       }
+               } else {
+                       long int tmp;
+                       char *endptr;
+                       tmp = strtol(config[CONFIG_NORTEL_CLIENT_ID], &endptr, 0);
+                       if (endptr == config[CONFIG_NORTEL_CLIENT_ID] ||
+                               tmp < 0 || tmp > 65535) {
+                               printf("%s: invalid [2] Nortel Client version ID %s\n"
+                                       " To list valid values, run \"%s --nortel-client id list\"\n",
+                                       argv[0], config[CONFIG_NORTEL_CLIENT_ID], argv[0]);
+                               exit(1);
+                       }
+                       opt_nortel_client_id = tmp;
+               }
+       }
+
+       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;
+               if (opt_auth_mode == AUTH_MODE_NORTEL_USERNAME
+                   && (i == CONFIG_XAUTH_USERNAME || i == CONFIG_XAUTH_PASSWORD))
+                       continue;
+               if (opt_auth_mode != AUTH_MODE_NORTEL_PINTOKEN
+                   && i == CONFIG_XAUTH_PIN)
+                       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_PIN:
+                       printf("Enter PIN for %s@%s: ",
+                               config[CONFIG_XAUTH_USERNAME],
+                               config[CONFIG_IPSEC_GATEWAY]);
+                       break;
+               case CONFIG_XAUTH_PASSWORD:
+                       printf("Enter password for %s@%s: ",
+                               config[CONFIG_XAUTH_USERNAME],
+                               config[CONFIG_IPSEC_GATEWAY]);
+                       break;
+               default:
+                       continue;
+               }
+               fflush(stdout);
+               switch (i) {
+               case CONFIG_IPSEC_SECRET:
+               case CONFIG_XAUTH_PIN:
+               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 (opt_auth_mode != AUTH_MODE_NORTEL_USERNAME) {
+               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 (opt_auth_mode == AUTH_MODE_NORTEL_PINTOKEN && !config[CONFIG_XAUTH_PIN])
+               error(1, 0, "missing Xauth PIN");
+       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 --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..521bc99
--- /dev/null
+++ b/config.h
@@ -0,0 +1,148 @@
+/* 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 510 2012-01-14 07:57:39Z Antonio Borneo $
+*/
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "vpnc-debug.h"
+
+enum config_enum {
+       CONFIG_SCRIPT,
+       CONFIG_DEBUG,
+       CONFIG_DOMAIN,
+       CONFIG_ENABLE_1DES,
+       CONFIG_ENABLE_NO_ENCRYPTION,
+       CONFIG_ND,
+       CONFIG_NON_INTERACTIVE,
+       CONFIG_PID_FILE,
+       CONFIG_LOCAL_ADDR,
+       CONFIG_LOCAL_PORT,
+       CONFIG_NORTEL_CLIENT_ID,
+       CONFIG_VERSION,
+       CONFIG_IF_NAME,
+       CONFIG_IF_MODE,
+       CONFIG_IF_MTU,
+       CONFIG_IKE_DH,
+       CONFIG_IPSEC_PFS,
+       CONFIG_IPSEC_GATEWAY,
+       CONFIG_IPSEC_TARGET_NETWORK,
+       CONFIG_IPSEC_ID,
+       CONFIG_IPSEC_SECRET,
+       CONFIG_IPSEC_SECRET_OBF,
+       CONFIG_XAUTH_USERNAME,
+       CONFIG_XAUTH_PIN,
+       CONFIG_XAUTH_PASSWORD,
+       CONFIG_XAUTH_PASSWORD_OBF,
+       CONFIG_XAUTH_INTERACTIVE,
+       CONFIG_VENDOR,
+       CONFIG_NATT_MODE,
+       CONFIG_UDP_ENCAP_PORT,
+       CONFIG_DPD_IDLE,
+       CONFIG_AUTH_MODE,
+       CONFIG_CA_FILE,
+       CONFIG_CA_DIR,
+       LAST_CONFIG
+};
+
+enum hex_dump_enum {
+       DUMP_UINT8 = -1,
+       DUMP_UINT16 = -2,
+       DUMP_UINT32 = -4
+};
+
+enum vendor_enum {
+       VENDOR_CISCO,
+       VENDOR_NETSCREEN,
+       VENDOR_NORTEL
+};
+
+enum natt_mode_enum {
+       NATT_NONE,
+       NATT_NORMAL,
+       NATT_FORCE,
+       NATT_CISCO_UDP,
+       NATT_NORTEL_UDP
+};
+
+enum if_mode_enum {
+       IF_MODE_TUN,
+       IF_MODE_TAP
+};
+
+enum auth_mode_enum {
+       AUTH_MODE_PSK,                  /* pre-shared key */
+       AUTH_MODE_RSA1,
+       AUTH_MODE_RSA2,
+       AUTH_MODE_CERT,                 /* Digital Certificate Authentication */
+       AUTH_MODE_HYBRID,               /* server certificate + xauth */
+       AUTH_MODE_NORTEL_USERNAME,      /* User Name and Password Authentication */
+       AUTH_MODE_NORTEL_TOKEN,         /* Group Security - Response Only Token - Use Passcode */
+       AUTH_MODE_NORTEL_PINTOKEN,      /* Group Security - Response Only Token - Use Two-Factor Card */
+       AUTH_MODE_NORTEL_TOKENSW,       /* Group Security - Response Only Token - Use SoftID Software */
+       AUTH_MODE_NORTEL_GPASSWORD      /* Group Security - Group Password Authentication */
+};
+
+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, opt_auth_mode;
+extern enum natt_mode_enum opt_natt_mode;
+extern enum if_mode_enum opt_if_mode;
+extern uint16_t opt_udpencapport;
+extern uint16_t opt_nortel_client_id;
+
+#define TIMESTAMP() ({                         \
+       char st[20];                            \
+       time_t t;                               \
+       struct tm *tm;                          \
+       t = time(NULL);                         \
+       tm = localtime(&t);                     \
+       strftime(st, sizeof(st), "%F %T", tm);  \
+       st;                                     \
+       })
+
+#define DEBUGTOP(LVL, COMMAND) do {                    \
+               if (opt_debug >= (LVL)) {               \
+                       printf("\n");                   \
+                       COMMAND;                        \
+                       printf(" [%s]\n", TIMESTAMP()); \
+               }                                       \
+       } while (0)
+
+#define DEBUG(LVL, COMMAND) do {               \
+               if (opt_debug >= (LVL)) {       \
+                       if (opt_debug > 1)      \
+                               printf("   ");  \
+                       COMMAND;                \
+               }                               \
+       } 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 void (*logmsg)(int priority, const char *format, ...)
+       __attribute__ ((__format__ (__printf__, 2, 3)));
+
+#endif
diff --git a/crypto-gnutls.c b/crypto-gnutls.c
new file mode 100644 (file)
index 0000000..d150749
--- /dev/null
@@ -0,0 +1,540 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gcrypt.h>
+
+#include "config.h"
+#include "sysdep.h"
+#include "crypto.h"
+
+static int gnutls_initialized = 0;
+
+#define CERT_STACK_DEPTH 20
+
+crypto_ctx *crypto_ctx_new(crypto_error **error)
+{
+       crypto_ctx *ctx;
+
+       if (!gnutls_initialized) {
+               if (gnutls_global_init() != 0) {
+                       crypto_error_set(error, 1, 0, "error initializing gnutls globals");
+                       return NULL;
+               }
+               gnutls_initialized = 1;
+       }
+
+       ctx = gnutls_calloc(1, sizeof(crypto_ctx));
+       if (!ctx) {
+               crypto_error_set(error, 1, ENOMEM, "not enough memory for crypto context");
+               return NULL;
+       }
+
+       ctx->stack = gnutls_calloc(CERT_STACK_DEPTH, sizeof(gnutls_x509_crt_t));
+       if (!ctx->stack) {
+               crypto_ctx_free(ctx);
+               crypto_error_set(error, 1, ENOMEM,
+                                "not enough memory for crypto certificate stack");
+               ctx = NULL;
+       }
+
+       return ctx;
+}
+
+void crypto_ctx_free(crypto_ctx *ctx)
+{
+       if (ctx) {
+               int i;
+
+               for (i = 0; i < ctx->num; i++)
+                       gnutls_x509_crt_deinit(ctx->stack[i]);
+               gnutls_free(ctx->stack);
+               memset(ctx, 0, sizeof(crypto_ctx));
+               gnutls_free(ctx);
+       }
+}
+
+unsigned char *crypto_read_cert(const char *path,
+                                size_t *out_len,
+                                crypto_error **error)
+{
+       gnutls_x509_crt_t cert;
+       unsigned char *data = NULL;
+       gnutls_datum dt;
+       size_t fsize = 0;
+       int err;
+
+       dt.data = crypto_read_file(path, &fsize, error);
+       if (!dt.data)
+               return NULL;
+
+       dt.size = (unsigned int) fsize;
+       if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS) {
+               crypto_error_set(error, 1, ENOMEM, "not enough memory for certificate");
+               goto out;
+       }
+
+       err = gnutls_x509_crt_import(cert, &dt, GNUTLS_X509_FMT_PEM);
+       if (err != GNUTLS_E_SUCCESS)
+               err = gnutls_x509_crt_import(cert, &dt, GNUTLS_X509_FMT_DER);
+       if (err != GNUTLS_E_SUCCESS) {
+               crypto_error_set(error, 1, 0, "certificate (%s) format unknown", path);
+               goto out;
+       }
+
+       *out_len = 10000;
+       data = malloc(*out_len);
+       err = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, data, out_len);
+       if (err != GNUTLS_E_SUCCESS) {
+               free(data);
+               *out_len = 0;
+               crypto_error_set(error, 1, 0, "certificate could not be exported");
+       }
+
+out:
+       if (dt.data)
+               gnutls_free(dt.data);
+       gnutls_x509_crt_deinit(cert);
+       return data;
+}
+
+int crypto_push_cert(crypto_ctx *ctx,
+                     const unsigned char *data,
+                     size_t len,
+                     crypto_error **error)
+{
+       gnutls_x509_crt_t cert;
+       gnutls_datum dt;
+       int err;
+
+       if (!ctx || !data || (len <= 0)) {
+               crypto_error_set(error, 1, 0, "invalid crypto context or data");
+               return 1;
+       }
+
+       if (ctx->num >= CERT_STACK_DEPTH) {
+               crypto_error_set(error, 1, 0, "too many certificates in the chain.");
+               return 1;
+       }
+
+       gnutls_x509_crt_init (&cert);
+
+       dt.data = (unsigned char *) data;
+       dt.size = len;
+       err = gnutls_x509_crt_import (cert, &dt, GNUTLS_X509_FMT_DER);
+       if (err != GNUTLS_E_SUCCESS) {
+               gnutls_x509_crt_deinit (cert);
+               crypto_error_set(error, 1, 0, "failed to decode certificate");
+               return 1;
+       }
+
+       ctx->stack[ctx->num] = cert;
+       ctx->num++;
+       return 0;
+}
+
+static int verify_issuer(gnutls_x509_crt_t crt,
+                         gnutls_x509_crt_t issuer,
+                         crypto_error **error)
+{
+       unsigned int output;
+       time_t now = time (0);
+
+       if (gnutls_x509_crt_verify(crt, &issuer, 1, 0, &output) < 0) {
+               crypto_error_set(error, 1, 0, "failed to verify against issuer");
+               return 1;
+       }
+
+       if (output & GNUTLS_CERT_INVALID) {
+               if (output & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+                       crypto_error_set(error, 1, 0, "certificate signer not found");
+                       return 1;
+               }
+               if (output & GNUTLS_CERT_SIGNER_NOT_CA) {
+                       crypto_error_set(error, 1, 0, "certificate signer not a CA");
+                       return 1;
+               }
+       }
+
+       if (gnutls_x509_crt_get_activation_time(crt) > now) {
+               crypto_error_set(error, 1, 0, "certificate activation in the future");
+               return 1;
+       }
+
+       if (gnutls_x509_crt_get_expiration_time(crt) < now) {
+               crypto_error_set(error, 1, 0, "certificate expired");
+               return 1;
+       }
+
+       return 0;
+}
+
+static int verify_last(gnutls_x509_crt_t crt,
+                       gnutls_x509_crt_t *ca_list,
+                       size_t ca_list_size,
+                       crypto_error **error)
+{
+       unsigned int output;
+       time_t now = time (0);
+
+       if (gnutls_x509_crt_verify (crt, ca_list, ca_list_size,
+                                   GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+                                   &output) < 0) {
+               crypto_error_set(error, 1, 0, "failed to verify against CA list");
+               return 1;
+       }
+
+       if (output & GNUTLS_CERT_INVALID) {
+               if (output & GNUTLS_CERT_SIGNER_NOT_CA) {
+                       crypto_error_set(error, 1, 0, "certificate signer not a CA");
+                       return 1;
+               }
+       }
+
+       if (gnutls_x509_crt_get_activation_time(crt) > now) {
+               crypto_error_set(error, 1, 0, "certificate activation in the future");
+               return 1;
+       }
+
+       if (gnutls_x509_crt_get_expiration_time(crt) < now) {
+               crypto_error_set(error, 1, 0, "certificate expired");
+               return 1;
+       }
+
+       return 0;
+}
+
+static gnutls_x509_crt_t *load_one_ca_file(const char *path, crypto_error **error)
+{
+       gnutls_x509_crt_t *list = NULL;
+       gnutls_x509_crt_t cert;
+       gnutls_datum dt;
+       size_t fsize = 0;
+       int err;
+
+       dt.data = crypto_read_file(path, &fsize, error);
+       if (!dt.data)
+               return NULL;
+
+       dt.size = (unsigned int) fsize;
+       if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
+               gnutls_free(dt.data);
+               crypto_error_set(error, 1, ENOMEM, "not enough memory for certificate");
+               goto out;
+       }
+
+       err = gnutls_x509_crt_import (cert, &dt, GNUTLS_X509_FMT_PEM);
+       if (err != GNUTLS_E_SUCCESS)
+               err = gnutls_x509_crt_import (cert, &dt, GNUTLS_X509_FMT_DER);
+       gnutls_free(dt.data);
+       if (err != GNUTLS_E_SUCCESS) {
+               crypto_error_set(error, 1, 0, "certificate (%s) format unknown", path);
+               goto out;
+       }
+
+       list = gnutls_malloc(sizeof(gnutls_x509_crt_t));
+       if (!list) {
+               crypto_error_set(error, 1, ENOMEM, "not enough memory for certificate list");
+               goto out;
+       } else
+               list[0] = cert;
+
+out:
+       gnutls_x509_crt_deinit (cert);
+       return list;
+}
+
+static gnutls_x509_crt_t *load_ca_list_file(const char *path,
+                                            size_t *out_list_size,
+                                            crypto_error **error)
+{
+       gnutls_x509_crt_t *list;
+       gnutls_datum dt = { NULL, 0 };
+       size_t fsize = 0;
+       int err;
+       unsigned int num = 200;
+
+       dt.data = crypto_read_file(path, &fsize, error);
+       if (!dt.data)
+               return NULL;
+
+       dt.size = (unsigned int) fsize;
+       list = gnutls_malloc(sizeof(gnutls_x509_crt_t) * num);
+       if (!list) {
+               crypto_error_set(error, 1, ENOMEM, "not enough memory for CA list");
+               goto out;
+       }
+
+       err = gnutls_x509_crt_list_import(list, &num, &dt, GNUTLS_X509_FMT_PEM, 0);
+       if (err <= 0) {
+               /* DER then maybe */
+               gnutls_free(list);
+               list = load_one_ca_file(path, error);
+               if (!list)
+                       goto out;
+               num = 1;
+       } else
+               num = err;  /* gnutls_x509_crt_list_import() returns # read */
+
+       if (err < 0) {
+               crypto_error_set(error, 1, 0, "importing CA list (%d)", err);
+               gnutls_free(list);
+               list = NULL;
+       } else
+               *out_list_size = num;
+
+out:
+       gnutls_free(dt.data);
+       return list;
+}
+
+int crypto_verify_chain(crypto_ctx *ctx,
+                        const char *ca_file,
+                        const char *ca_dir,
+                        crypto_error **error)
+{
+       int err, i, ret = 1, start = 0;
+       gnutls_x509_crt_t *ca_list = NULL;
+       size_t ca_list_size = 0;
+
+       if (!ctx)
+               return 1;
+
+       if (ctx->num == 0)
+               return 0;
+
+       if (ca_file) {
+               ca_list = load_ca_list_file(ca_file, &ca_list_size, error);
+               if (!ca_list)
+                       return 1;
+       } else if (ca_dir) {
+               /* FIXME: Try to load all files in the directory I guess... */
+               crypto_error_set(error, 1, 0, "ca_dir not yet supported");
+               return 1;
+       }
+
+       /* If the server cert is self-signed, ignore it in the issuers check */
+       err = gnutls_x509_crt_check_issuer(ctx->stack[0], ctx->stack[0]);
+       if (err > 0)
+               start++;
+
+       /* Check each certificate against its issuer */
+       for (i = start; i < ctx->num - 1; i++) {
+               if (verify_issuer(ctx->stack[i], ctx->stack[i + 1], error))
+                       goto out;
+       }
+
+       /* Verify the last certificate */
+       if (verify_last(ctx->stack[ctx->num - 1], ca_list, ca_list_size, error))
+               goto out;
+
+       ret = 0;
+
+out:
+       if (ca_list) {
+               for (i = 0; i < (int) ca_list_size; i++)
+                       gnutls_x509_crt_deinit(ca_list[i]);
+               gnutls_free(ca_list);
+       }
+       return ret;
+}
+
+static unsigned char *check_pkcs1_padding(unsigned char* from,
+                                          size_t from_len,
+                                          size_t *out_len,
+                                          crypto_error **error)
+{
+       int i = 0;
+       unsigned char *rec_hash = NULL;
+       size_t hash_len = 0;
+
+       /* No function provided to check that hash conforms to
+        * PKCS#1 1.5 padding scheme. Moreover gcrypt trim first
+        * 0 bytes */
+       if (from[i++] != 0x01) {
+               crypto_error_set(error, 1, 0, "hash doesn't conform to PKCS#1 padding");
+               goto out;
+       }
+
+       while (from[i] != 0x00) {
+               if (from[i++] != 0xFF) {
+                       crypto_error_set(error, 1, 0, "hash doesn't conform to PKCS#1 padding");
+                       goto out;
+               }
+       }
+
+       i++; /* Skips 00 byte */
+
+       if (i < 10) {
+               crypto_error_set(error, 1, 0, "PKCS#1 padding too short");
+               goto out;
+       }
+
+       hash_len = from_len - i;
+       rec_hash = calloc(1, hash_len);
+       if (!rec_hash)
+               goto out;
+
+       memcpy(rec_hash, from + i, hash_len);
+       *out_len = hash_len;
+
+out:
+       return rec_hash;
+}
+
+
+unsigned char *crypto_decrypt_signature(crypto_ctx *ctx,
+                                        const unsigned char *sig_data,
+                                        size_t sig_len,
+                                        size_t *out_len,
+                                        unsigned int padding,
+                                        crypto_error **error)
+{
+       unsigned char *buf = NULL, *rec_hash = NULL;
+       gnutls_datum_t n = { NULL, 0 }, e = { NULL, 0 };
+       int err, algo;
+       gcry_sexp_t key = NULL, sig = NULL, decrypted = NULL, child = NULL;
+       gcry_mpi_t n_mpi = NULL, e_mpi = NULL, sig_mpi = NULL, dec_mpi = NULL;
+       size_t buf_len = 0, hash_len = 0;
+
+       if (!ctx) {
+               crypto_error_set(error, 1, 0, "invalid crypto context");
+               return NULL;
+       }
+
+       if (!ctx->num) {
+               crypto_error_set(error, 1, 0, "no certificates in the stack");
+               return NULL;
+       }
+
+       algo = gnutls_x509_crt_get_pk_algorithm(ctx->stack[ctx->num - 1], NULL);
+       if (algo != GNUTLS_PK_RSA) {
+               crypto_error_set(error, 1, 0, "certificate public key algorithm not RSA");
+               return NULL;
+       }
+
+       err = gnutls_x509_crt_get_pk_rsa_raw(ctx->stack[ctx->num - 1], &n, &e);
+       if (err != GNUTLS_E_SUCCESS) {
+               crypto_error_set(error, 1, 0, "error getting certificate public key");
+               return NULL;
+       }
+
+       err = gcry_mpi_scan(&n_mpi, GCRYMPI_FMT_USG, n.data, n.size, NULL);
+       if (err) {
+               crypto_error_set(error, 1, 0, "invalid RSA key 'n' format");
+               goto out;
+       }
+
+       err = gcry_mpi_scan(&e_mpi, GCRYMPI_FMT_USG, e.data, e.size, NULL);
+       if (err) {
+               crypto_error_set(error, 1, 0, "invalid RSA key 'e' format");
+               goto out;
+       }
+
+       err = gcry_sexp_build(&key, NULL, "(public-key (rsa (n %m) (e %m)))", n_mpi, e_mpi);
+       if (err) {
+               crypto_error_set(error, 1, 0, "could not create public-key expression");
+               goto out;
+       }
+
+       err = gcry_mpi_scan(&sig_mpi, GCRYMPI_FMT_USG, sig_data, sig_len, NULL);
+       if (err) {
+               crypto_error_set(error, 1, 0, "invalid signature format");
+               goto out;
+       }
+
+       err = gcry_sexp_build(&sig, NULL, "(data (flags raw) (value %m))", sig_mpi);
+       if (err) {
+               crypto_error_set(error, 1, 0, "could not create signature expression");
+               goto out;
+       }
+
+       /* encrypt is equivalent to public key decryption for RSA keys */
+       err = gcry_pk_encrypt(&decrypted, sig, key);
+       if (err) {
+               crypto_error_set(error, 1, 0, "could not decrypt signature");
+               goto out;
+       }
+
+       child = gcry_sexp_find_token(decrypted, "a", 1);
+       if (!child) {
+               crypto_error_set(error, 1, 0, "could not get decrypted signature result");
+               goto out;
+       }
+
+       dec_mpi = gcry_sexp_nth_mpi(child, 1, GCRYMPI_FMT_USG);
+       gcry_sexp_release(child);
+
+       if (!dec_mpi) {
+               crypto_error_set(error, 1, 0, "could not get decrypted signature result");
+               goto out;
+       }
+
+       gcry_mpi_aprint(GCRYMPI_FMT_USG, &buf, &buf_len, dec_mpi);
+       if (!buf) {
+               crypto_error_set(error, 1, 0, "could not get extract decrypted signature");
+               goto out;
+       }
+
+       switch (padding) {
+       case CRYPTO_PAD_NONE:
+               rec_hash = buf;
+               hash_len = buf_len;
+               buf = NULL;
+               *out_len = (int) hash_len;
+               break;
+       case CRYPTO_PAD_PKCS1:
+               rec_hash = check_pkcs1_padding(buf, buf_len, &hash_len, error);
+               if (!rec_hash) {
+                       crypto_error_set(error, 1, 0, "could not get extract decrypted padded signature");
+                       goto out;
+               }
+               *out_len = (int) hash_len;
+               break;
+       default:
+               crypto_error_set(error, 1, 0, "unknown padding mechanism %d", padding);
+               break;
+       }
+
+out:
+       if (buf)
+               free(buf);
+       if (dec_mpi)
+               gcry_mpi_release(dec_mpi);
+       if (decrypted)
+               gcry_sexp_release(decrypted);
+       if (key)
+               gcry_sexp_release(key);
+       if (sig)
+               gcry_sexp_release(sig);
+       if (sig_mpi)
+               gcry_mpi_release(sig_mpi);
+       if (n_mpi)
+               gcry_mpi_release(n_mpi);
+       if (e_mpi)
+               gcry_mpi_release(e_mpi);
+       if (n.data)
+               gcry_free(n.data);
+       if (e.data)
+               gcry_free(e.data);
+
+       return rec_hash;
+}
+
diff --git a/crypto-gnutls.h b/crypto-gnutls.h
new file mode 100644 (file)
index 0000000..5931f06
--- /dev/null
@@ -0,0 +1,30 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+   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
+*/
+
+#ifndef __CRYPTO_GNUTLS_H__
+#define __CRTPTO_GNUTLS_H__
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+typedef struct {
+       int num;
+       gnutls_x509_crt_t *stack;
+} crypto_ctx;
+
+#endif  /* __CRYPTO_GNUTLS_H__ */
+
diff --git a/crypto-openssl.c b/crypto-openssl.c
new file mode 100644 (file)
index 0000000..9d98f8c
--- /dev/null
@@ -0,0 +1,323 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <openssl/pem.h>
+#include "config.h"
+#include "sysdep.h"
+#include "crypto.h"
+
+crypto_ctx *crypto_ctx_new(crypto_error **error)
+{
+       crypto_ctx *ctx;
+
+       ctx = malloc(sizeof(crypto_ctx));
+       if (!ctx) {
+               crypto_error_set(error, 1, ENOMEM,
+                                "not enough memory for crypto context");
+               return NULL;
+       }
+
+       OpenSSL_add_all_ciphers();
+       OpenSSL_add_all_digests();
+       OpenSSL_add_all_algorithms();
+       ERR_load_crypto_strings();
+
+       memset(ctx, 0, sizeof(crypto_ctx));
+       ctx->stack = sk_X509_new_null();
+       if (!ctx->stack) {
+               crypto_ctx_free(ctx);
+               crypto_error_set(error, 1, ENOMEM,
+                                "not enough memory for crypto certificate stack");
+               ctx = NULL;
+       }
+
+       return ctx;
+}
+
+void crypto_ctx_free(crypto_ctx *ctx)
+{
+       if (ctx) {
+               if (ctx->stack)
+                       sk_X509_free(ctx->stack);
+
+               memset(ctx, 0, sizeof(crypto_ctx));
+               free(ctx);
+       }
+}
+
+static int password_cb(char *buf, int size, int rwflag, void *userdata)
+{
+       /* Dummy callback to ensure openssl doesn't prompt for a password */
+       return 0;
+}
+
+unsigned char *crypto_read_cert(const char *path,
+                                size_t *out_len,
+                                crypto_error **error)
+{
+       FILE *fp;
+       X509 *cert = NULL;
+       unsigned char *data = NULL, *p;
+
+       fp = fopen(path, "r");
+       if (!fp) {
+               crypto_error_set(error, 1, 0, "certificate (%s) could not be opened", path);
+               return NULL;
+       }
+
+       cert = PEM_read_X509(fp, NULL, password_cb, NULL);
+       fclose (fp);
+
+       if (!cert) {
+               /* Try DER then */
+               p = data = crypto_read_file(path, out_len, error);
+               if (!data || !*out_len) {
+                       crypto_error_set(error, 1, 0, "could not read certificate %s", path);
+                       return NULL;
+               }
+
+               cert = d2i_X509(NULL, (const unsigned char **) &p, (int) (*out_len));
+               if (!cert) {
+                       free(data);
+                       crypto_error_set(error, 1, 0, "could not allocate memory for certificate");
+                       return NULL;
+               }
+
+               return data;
+       }
+
+       /* Get length of DER data */
+       *out_len = i2d_X509(cert, NULL);
+       if (!*out_len) {
+               crypto_error_set(error, 1, 0, "invalid certificate length");
+               goto out;
+       }
+
+       p = data = malloc(*out_len);
+       if (!data) {
+               crypto_error_set(error, 1, 0, "could not allocate memory for certificate");
+               goto out;
+       }
+
+       /* Encode the certificate to DER */
+       *out_len = i2d_X509(cert, &p);
+       if (!*out_len) {
+               crypto_error_set(error, 1, 0, "could not export certificate data");
+               if (data) {
+                       free(data);
+                       data = NULL;
+               }
+               goto out;
+       }
+
+out:
+       if (cert)
+               X509_free(cert);
+       return data;
+}
+
+int crypto_push_cert(crypto_ctx *ctx,
+                     const unsigned char *data,
+                     size_t len,
+                     crypto_error **error)
+{
+       X509 *cert = NULL;
+
+       if (!ctx || !data || (len <= 0)) {
+               crypto_error_set(error, 1, 0, "invalid crypto context or data");
+               return 1;
+       }
+
+       /* convert the certificate to an openssl-X509 structure and push it onto the chain stack */
+       cert = d2i_X509(NULL, &data, (int) len);
+       if (!cert) {
+               ERR_print_errors_fp(stderr);
+               crypto_error_set(error, 1, 0, "failed to decode certificate");
+               return 1;
+       }
+       sk_X509_push(ctx->stack, cert);
+       return 0;
+}
+
+int crypto_verify_chain(crypto_ctx *ctx,
+                        const char *ca_file,
+                        const char *ca_dir,
+                        crypto_error **error)
+{
+       X509            *x509;
+       X509_STORE      *store = NULL;
+       X509_LOOKUP     *lookup = NULL;
+       X509_STORE_CTX  *verify_ctx = NULL;
+       int             ret = 1;
+
+       if (!ctx) {
+               crypto_error_set(error, 1, 0, "invalid crypto context");
+               return 1;
+       }
+
+       x509 = sk_X509_value(ctx->stack, sk_X509_num(ctx->stack) - 1);
+       if (x509 == NULL) {
+               ERR_print_errors_fp (stderr);
+               crypto_error_set(error, 1, 0, "no certificates in the stack");
+               return 1;
+       }
+
+       /* BEGIN - verify certificate chain */
+       /* create the cert store */
+       if (!(store = X509_STORE_new())) {
+               crypto_error_set(error, 1, 0, "error creating X509_STORE object");
+               return 1;
+       }
+       /* load the CA certificates */
+       if (X509_STORE_load_locations (store, ca_file, ca_dir) != 1) {
+               crypto_error_set(error, 1, 0, "error loading the CA file (%s) "
+                                "or directory (%s)", ca_file, ca_dir);
+               goto out;
+       }
+       if (X509_STORE_set_default_paths (store) != 1) {
+               crypto_error_set(error, 1, 0, "error loading the system-wide CA"
+                                " certificates");
+               goto out;
+       }
+
+#if 0
+       /* check CRLs */
+       /* add the corresponding CRL for each CA in the chain to the lookup */
+#define CRL_FILE "root-ca-crl.crl.pem"
+
+       if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) {
+               crypto_error_set(error, 1, 0, "error creating X509 lookup object.");
+               goto out;
+       }
+       if (X509_load_crl_file(lookup, CRL_FILE, X509_FILETYPE_PEM) != 1) {
+               ERR_print_errors_fp(stderr);
+               crypto_error_set(error, 1, 0, "error reading CRL file");
+               goto out;
+       }
+       X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#endif /* 0 */
+
+       /* create a verification context and initialize it */
+       if (!(verify_ctx = X509_STORE_CTX_new ())) {
+               crypto_error_set(error, 1, 0, "error creating X509_STORE_CTX object");
+               goto out;
+       }
+       /* X509_STORE_CTX_init did not return an error condition in prior versions */
+       if (X509_STORE_CTX_init (verify_ctx, store, x509, ctx->stack) != 1) {
+               crypto_error_set(error, 1, 0, "error intializing verification context");
+               goto out;
+       }
+
+       /* verify the certificate */
+       if (X509_verify_cert(verify_ctx) != 1) {
+               ERR_print_errors_fp(stderr);
+               crypto_error_set(error, 2, 0, "error verifying the certificate "
+                                "chain");
+               goto out;
+       }
+
+       ret = 0;
+
+out:
+       if (lookup)
+               X509_LOOKUP_free(lookup);
+       if (store)
+               X509_STORE_free(store);
+       if (verify_ctx)
+               X509_STORE_CTX_free(verify_ctx);
+       return ret;
+}
+
+unsigned char *crypto_decrypt_signature(crypto_ctx *ctx,
+                                        const unsigned char *sig_data,
+                                        size_t sig_len,
+                                        size_t *out_len,
+                                        unsigned int padding,
+                                        crypto_error **error)
+{
+       X509            *x509;
+       EVP_PKEY        *pkey = NULL;
+       RSA             *rsa;
+       unsigned char   *hash = NULL;
+       int             tmp_len = -1, ossl_pad;
+
+       *out_len = 0;
+
+       if (!ctx) {
+               crypto_error_set(error, 1, 0, "invalid crypto context");
+               return NULL;
+       }
+
+       x509 = sk_X509_value(ctx->stack, sk_X509_num(ctx->stack) - 1);
+       if (x509 == NULL) {
+               ERR_print_errors_fp (stderr);
+               crypto_error_set(error, 1, 0, "no certificates in the stack");
+               return NULL;
+       }
+
+       pkey = X509_get_pubkey(x509);
+       if (pkey == NULL) {
+               ERR_print_errors_fp (stderr);
+               crypto_error_set(error, 1, 0, "error getting certificate public key");
+               return NULL;
+       }
+
+       rsa = EVP_PKEY_get1_RSA(pkey);
+       if (rsa == NULL) {
+               ERR_print_errors_fp (stderr);
+               crypto_error_set(error, 1, 0, "error getting public key RSA");
+               goto out;
+       }
+
+       hash = calloc(1, RSA_size(rsa));
+       if (!hash) {
+               crypto_error_set(error, 1, 0, "not enough memory to decrypt signature");
+               goto out;
+       }
+
+       switch (padding) {
+       case CRYPTO_PAD_NONE:
+               ossl_pad = RSA_NO_PADDING;
+               break;
+       case CRYPTO_PAD_PKCS1:
+               ossl_pad = RSA_PKCS1_PADDING;
+               break;
+       default:
+               crypto_error_set(error, 1, 0, "unknown padding mechanism %d", padding);
+               goto out;
+       }
+
+       tmp_len = RSA_public_decrypt(sig_len, sig_data, hash, rsa, ossl_pad);
+       if (tmp_len > 0) {
+               *out_len = (size_t) tmp_len;
+       } else {
+               ERR_print_errors_fp (stderr);
+               crypto_error_set(error, 1, 0, "could not decrypt signature");
+               free(hash);
+               hash = NULL;
+       }
+
+out:
+       if (pkey)
+               EVP_PKEY_free(pkey);
+       return hash;
+}
+
diff --git a/crypto-openssl.h b/crypto-openssl.h
new file mode 100644 (file)
index 0000000..57202ff
--- /dev/null
@@ -0,0 +1,33 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+   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
+*/
+
+#ifndef OPENSSL_GPL_VIOLATION
+#error "openssl support cannot be built without defining OPENSSL_GPL_VIOLATION"
+#endif
+
+#ifndef __CRYPTO_OPENSSL_H__
+#define __CRYPTO_OPENSSL_H__
+
+#include <openssl/x509.h>
+#include <openssl/err.h>
+
+typedef struct {
+       STACK_OF(X509) *stack;
+} crypto_ctx;
+
+#endif  /* __CRYPTO_OPENSSL_H__ */
+
diff --git a/crypto.c b/crypto.c
new file mode 100644 (file)
index 0000000..f652af6
--- /dev/null
+++ b/crypto.c
@@ -0,0 +1,143 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "sysdep.h"
+#include "crypto.h"
+
+
+#define MSG_SIZE 200
+void crypto_error_set(crypto_error **error,
+                      int code,
+                      int in_errno,
+                      const char *fmt, ...)
+{
+       va_list args;
+
+       if (!error)
+               return;
+       if (*error) {
+               fprintf(stderr, "%s: called with non-NULL *error\n", __func__);
+               return;
+       }
+
+       *error = calloc(1, sizeof(crypto_error));
+       if (!*error)
+               return;
+
+       (*error)->code = code;
+       (*error)->err = in_errno;
+
+       (*error)->msg = malloc(MSG_SIZE);
+       if (!(*error)->msg) {
+               fprintf(stderr, "%s: not enough memory for error message\n", __func__);
+               crypto_error_clear(error);
+               return;
+       }
+
+       va_start(args, fmt);
+       if (vsnprintf((*error)->msg, MSG_SIZE, fmt, args) == -1)
+               (*error)->msg[0] = '\0';
+       va_end(args);
+}
+
+void crypto_error_free(crypto_error *error)
+{
+       if (error) {
+               if (error->msg)
+                       free(error->msg);
+               memset(error, 0, sizeof(crypto_error));
+               free(error);
+       }
+}
+
+void crypto_error_clear(crypto_error **error)
+{
+       if (error && *error) {
+               crypto_error_free(*error);
+               *error = NULL;
+       }
+}
+
+void crypto_call_error(crypto_error *err)
+{
+       if (err)
+               error(err->code, err->err, "%s\n", err->msg);
+       else
+               error(1, 0, "unknown error");
+}
+
+unsigned char *
+crypto_read_file(const char *path, size_t *out_len, crypto_error **error)
+{
+       struct stat st;
+       int fd;
+       ssize_t bytes_read;
+       size_t file_size;
+       unsigned char *data = NULL;
+
+       *out_len = 0;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               crypto_error_set(error, 1, errno, "failed to open file '%s'", path);
+               return NULL;
+       }
+
+       if (fstat(fd, &st) < 0) {
+               crypto_error_set(error, 1, errno, "failed to stat file '%s'", path);
+               goto out;
+       }
+
+       if (st.st_size <= 0 || st.st_size > INT_MAX) {
+               crypto_error_set(error, 1, errno, "invalid file '%s' length %ld", path, st.st_size);
+               goto out;
+       }
+
+       file_size = st.st_size;
+       data = malloc(file_size);
+       if (!data) {
+               crypto_error_set(error, 1, ENOMEM, "not enough memory to read file '%s'", path);
+               goto out;
+       }
+
+       do {
+               bytes_read = read(fd, &(data[*out_len]), (st.st_size - *out_len));
+               if (bytes_read < 0) {
+                       free(data);
+                       data = NULL;
+                       *out_len = 0;
+                       crypto_error_set(error, 1, errno, "failed to read file '%s'", path);
+                       goto out;
+               }
+               *out_len += bytes_read;
+       } while ((bytes_read > 0) && (*out_len <= file_size));
+
+out:
+       close(fd);
+       return data;
+}
+
diff --git a/crypto.h b/crypto.h
new file mode 100644 (file)
index 0000000..72307cf
--- /dev/null
+++ b/crypto.h
@@ -0,0 +1,139 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+   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
+*/
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include <stdarg.h>
+
+typedef struct {
+       int code;
+       int err;
+       char *msg;
+} crypto_error;
+
+void crypto_error_set(crypto_error **error, int code, int in_errno, const char *fmt, ...);
+
+void crypto_error_free(crypto_error *error);
+
+void crypto_error_clear(crypto_error **error);
+
+void crypto_call_error(crypto_error *err);
+
+unsigned char *crypto_read_file(const char *path, size_t *out_len, crypto_error **error);
+
+#if CRYPTO_GNUTLS
+#include "crypto-gnutls.h"
+#elif CRYPTO_OPENSSL
+#include "crypto-openssl.h"
+#else
+#error "no crypto library defined"
+#endif
+
+#define CRYPTO_PAD_NONE  0
+#define CRYPTO_PAD_PKCS1 1
+
+/**
+ * crypto_push_cert:
+ *
+ * Allocates a crypto context with the resources necessary for the specific
+ * crypto library being used.
+ *
+ * Returns: a valid crypto context, or #NULL on error
+ **/
+crypto_ctx *crypto_ctx_new(crypto_error **error);
+
+/**
+ * crypto_ctx_free:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ *
+ * Frees resources allocated by crypo_ctx_new().
+ **/
+void crypto_ctx_free(crypto_ctx *ctx);
+
+/**
+ * crypto_read_cert:
+ * @path: path to certificate file in either PEM or DER format
+ * @out_len: length of raw certificate data
+ * @error: return location for an error
+ *
+ * Loads a certificate and returns the binary ASN certificate data;
+ *
+ * Returns: certificate data on success, NULL on error
+ **/
+unsigned char *crypto_read_cert(const char *path,
+                                size_t *out_len,
+                                crypto_error **error);
+
+/**
+ * crypto_push_cert:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ * @data: buffer containing raw certificate data
+ * @len: length of raw certificate data
+ * @error: return location for an error
+ *
+ * Pushes the given certificate onto the context's certificate stack.
+ *
+ * Returns: 0 on success, 1 on error
+ **/
+int crypto_push_cert(crypto_ctx *ctx,
+                     const unsigned char *data,
+                     size_t len,
+                     crypto_error **error);
+
+/**
+ * crypto_verify_chain:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ * @ca_file: path of a CA certificate file to use for verification of the
+ *           certificate stack.  File may be a PEM-encoded file containing
+ *           multiple CA certificates.  @ca_file is preferred over @ca_dir
+ * @ca_dir: directory containing CA certificates to use for verification of the
+ *          certificate stack
+ * @error: return location for an error
+ *
+ * Verifies the certificate stack previously built with crypto_push_cert() using
+ * the supplied CA certificates or certificate locations.
+ *
+ * Returns: 0 on success, 1 on error
+ **/
+int crypto_verify_chain(crypto_ctx *ctx,
+                        const char *ca_file,
+                        const char *ca_dir,
+                        crypto_error **error);
+
+/**
+ * crypto_decrypt_signature:
+ * @ctx: a valid crypto context created with crypto_ctx_new()
+ * @sig_data: encrypted signature data
+ * @sig_len: length of encrypted signature data
+ * @out_len: size of decrypted signature data
+ * @error: return location for an error
+ *
+ * Recovers the message digest stored in @sig_data using the public key of the
+ * last certificate on the certificate stack
+ *
+ * Returns: decrypted message digest, or #NULL on error
+ **/
+unsigned char *crypto_decrypt_signature(crypto_ctx *ctx,
+                                        const unsigned char *sig_data,
+                                        size_t sig_len,
+                                        size_t *out_hash_len,
+                                        unsigned int padding,
+                                        crypto_error **error);
+
+#endif  /* __CRYPTO_H__ */
+
diff --git a/decrypt-utils.c b/decrypt-utils.c
new file mode 100644 (file)
index 0000000..caf71cf
--- /dev/null
@@ -0,0 +1,128 @@
+/* IPSec VPN client compatible with Cisco equipment.
+   Copyright (C) 2004-2007 Maurice Massar
+   A bit reorganized in 2007 by Wolfram Sang
+
+   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: decrypt-utils.c 377 2008-11-26 08:03:43Z Joerg Mayer $
+*/
+
+#define _GNU_SOURCE
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <gcrypt.h>
+
+#include "decrypt-utils.h"
+
+
+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;
+}
diff --git a/decrypt-utils.h b/decrypt-utils.h
new file mode 100644 (file)
index 0000000..826bc33
--- /dev/null
@@ -0,0 +1,28 @@
+/* IPSec VPN client compatible with Cisco equipment.
+   Copyright (C) 2004-2007 Maurice Massar
+   A bit reorganized in 2007 by Wolfram Sang
+
+   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: decrypt-utils.h 377 2008-11-26 08:03:43Z Joerg Mayer $
+*/
+
+#ifndef __DECRYPT_UTILS_H__
+#define __DECRYPT_UTILS_H__
+
+extern int hex2bin(const char *str, char **bin, int *len);
+extern int deobfuscate(char *ct, int len, const char **resp, char *reslenp);
+
+#endif
diff --git a/dh.c b/dh.c
new file mode 100644 (file)
index 0000000..4b69a84
--- /dev/null
+++ b/dh.c
@@ -0,0 +1,76 @@
+/* borrowed from isakmpd-20030718 (-; */
+
+/*     $OpenBSD: dh.c,v 1.8 2003/06/03 14:28:16 ho Exp $       */
+/*     $EOM: dh.c,v 1.5 1999/04/17 23:20:22 niklas Exp $       */
+
+/*
+ * Copyright (c) 1998 Niels Provos.  All rights reserved.
+ * Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This code was written under funding by Ericsson Radio Systems.
+ */
+
+#include "math_group.h"
+#include "dh.h"
+
+/*
+ * Returns the length of our exchange value.
+ */
+
+int dh_getlen(struct group *group)
+{
+       return group->getlen(group);
+}
+
+/*
+ * Creates the exchange value we are offering to the other party.
+ * Each time this function is called a new value is created, that
+ * means the application has to save the exchange value itself,
+ * dh_create_exchange should only be called once.
+ */
+int dh_create_exchange(struct group *group, unsigned char *buf)
+{
+       if (group->setrandom(group, group->c))
+               return -1;
+       if (group->operation(group, group->a, group->gen, group->c))
+               return -1;
+       group->getraw(group, group->a, buf);
+       return 0;
+}
+
+/*
+ * Creates the Diffie-Hellman shared secret in 'secret', where 'exchange'
+ * is the exchange value offered by the other party. No length verification
+ * is done for the value, the application has to do that.
+ */
+int dh_create_shared(struct group *group, unsigned char *secret, unsigned char *exchange)
+{
+       if (group->setraw(group, group->b, exchange, group->getlen(group)))
+               return -1;
+       if (group->operation(group, group->a, group->b, group->c))
+               return -1;
+       group->getraw(group, group->a, secret);
+       return 0;
+}
diff --git a/dh.h b/dh.h
new file mode 100644 (file)
index 0000000..941c34f
--- /dev/null
+++ b/dh.h
@@ -0,0 +1,45 @@
+/* borrowed from isakmpd-20030718 (-; */
+
+/*     $OpenBSD: dh.h,v 1.5 2003/06/03 14:28:16 ho Exp $       */
+/*     $EOM: dh.h,v 1.4 1999/04/17 23:20:24 niklas Exp $       */
+
+/*
+ * Copyright (c) 1998 Niels Provos.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This code was written under funding by Ericsson Radio Systems.
+ */
+
+#ifndef __DH_H__
+#define __DH_H__
+
+#include <sys/types.h>
+
+struct group;
+
+int dh_getlen(struct group *);
+int dh_create_exchange(struct group *, unsigned char *);
+int dh_create_shared(struct group *, unsigned char *, unsigned char *);
+
+#endif
diff --git a/enum2debug.pl b/enum2debug.pl
new file mode 100755 (executable)
index 0000000..318fce3
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/env perl
+
+# Usage: ./enum2debug.pl isakmp.h  >vpnc-debug.c 2>vpnc-debug.h
+
+use strict;
+use warnings;
+
+my $in_enum = 0;
+
+print STDERR << 'EOF';
+/* Automatically generated with enum2debug.pl: Don't edit! */
+
+struct debug_strings {
+       unsigned int id;
+       const char *string;
+};
+
+extern const char *val_to_string(unsigned int, const struct debug_strings *);
+
+EOF
+
+print << 'EOF';
+/* Automatically generated with enum2debug.pl: Don't edit! */
+
+#include <stdio.h>
+
+#include "vpnc-debug.h"
+#include "isakmp.h"
+
+const char *val_to_string(unsigned int val, const struct debug_strings *dstrings)
+{
+       static const char *unknown = " (unknown)";
+       static const char *na = "";
+       unsigned int i;
+
+       if (dstrings == NULL)
+               return na;
+
+       for (i = 0; dstrings[i].id != 0 || dstrings[i].string != NULL; i++)
+               if (dstrings[i].id == val)
+                       return dstrings[i].string;
+       return unknown;
+}
+
+EOF
+
+while (<>) {
+       if (/^enum\W+(\w+)\W*/) {
+               print STDERR "extern const struct debug_strings $1_array[];\n";
+               print "const struct debug_strings $1_array[] = {\n";
+               $in_enum = 1;
+       } elsif ($in_enum && /^}/) {
+               print "\t{ 0,\t(const char *) 0 }\n};\n\n";
+               $in_enum = 0;
+       } elsif (/^\s*\/\*.*\*\/\s*$/) {
+               next;
+       } elsif ($in_enum && /^\W*(\w+)\W*/) {
+               print "\t{ $1,\t\" ($1)\" },\n";
+       }
+}
+
+exit 0;
+
+__END__
diff --git a/isakmp-pkt.c b/isakmp-pkt.c
new file mode 100644 (file)
index 0000000..f5e4cd3
--- /dev/null
@@ -0,0 +1,1032 @@
+/* ISAKMP packing and unpacking routines.
+   Copyright (C) 2002  Geoffrey Keating
+   Copyright (C) 2003-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: isakmp-pkt.c 511 2012-01-14 07:57:51Z Antonio Borneo $
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "sysdep.h"
+#include "config.h"
+#include "isakmp-pkt.h"
+#include "math_group.h"
+#include "vpnc.h"
+
+void *xallocc(size_t x)
+{
+       void *result;
+       result = calloc(1, x);
+       if (result == NULL)
+               error(1, errno, "malloc of %lu bytes failed", (unsigned long)x);
+       return result;
+}
+
+void *dup_data(const void *data, size_t x)
+{
+       void *newdata = xallocc(x);
+       memcpy(newdata, data, x);
+       return newdata;
+}
+
+struct flow {
+       size_t len;
+       uint8_t *base;
+       uint8_t *end;
+};
+
+static uint8_t *flow_reserve_p(struct flow *f, size_t sz)
+{
+       size_t l = f->end - f->base;
+       if (l + sz > f->len) {
+               size_t new_len = f->len == 0 ? 128 : f->len;
+               while (l + sz >= new_len)
+                       new_len *= 2;
+
+               if (f->base == NULL)
+                       f->base = malloc(new_len);
+               else
+                       f->base = realloc(f->base, new_len);
+               if (f->base == NULL)
+                       error(1, errno, "alloc of %lud bytes failed", (unsigned long)new_len);
+               memset(f->base + f->len, 0, new_len - f->len);
+               f->end = f->base + l;
+               f->len = new_len;
+       }
+       f->end += sz;
+       return f->end - sz;
+}
+
+static size_t flow_reserve(struct flow *f, size_t sz)
+{
+       uint8_t *p = flow_reserve_p(f, sz);
+       return p - f->base;
+}
+
+static void flow_x(struct flow *f, uint8_t * data, size_t data_len)
+{
+       memcpy(flow_reserve_p(f, data_len), data, data_len);
+}
+
+static void flow_1(struct flow *f, uint8_t d)
+{
+       flow_reserve_p(f, 1)[0] = d;
+}
+
+static void flow_2(struct flow *f, uint16_t d)
+{
+       uint8_t dd[2];
+       dd[0] = d >> 8;
+       dd[1] = d;
+       flow_x(f, dd, sizeof(dd));
+}
+
+static void flow_4(struct flow *f, uint32_t d)
+{
+       uint8_t dd[4];
+       dd[0] = d >> 24;
+       dd[1] = d >> 16;
+       dd[2] = d >> 8;
+       dd[3] = d;
+       flow_x(f, dd, sizeof(dd));
+}
+
+static void init_flow(struct flow *f)
+{
+       memset(f, 0, sizeof(*f));
+}
+
+static void flow_attribute(struct flow *f, struct isakmp_attribute *p)
+{
+       for (; p; p = p->next)
+               switch (p->af) {
+               case isakmp_attr_lots:
+                       flow_2(f, p->type);
+                       flow_2(f, p->u.lots.length);
+                       flow_x(f, p->u.lots.data, p->u.lots.length);
+                       break;
+               case isakmp_attr_16:
+                       flow_2(f, p->type | 0x8000);
+                       flow_2(f, p->u.attr_16);
+                       break;
+               case isakmp_attr_2x8:
+                       flow_2(f, p->type | 0x8000);
+                       flow_x(f, p->u.attr_2x8, 2);
+                       break;
+               default:
+                       abort();
+               }
+}
+
+static void flow_payload(struct flow *f, struct isakmp_payload *p)
+{
+       size_t lpos;
+       size_t baselen;
+
+       if (p == NULL)
+               return;
+
+       baselen = f->end - f->base;
+       if (p->next == NULL)
+               flow_1(f, 0);
+       else
+               flow_1(f, p->next->type);
+       flow_1(f, 0);
+       lpos = flow_reserve(f, 2);
+       switch (p->type) {
+       case ISAKMP_PAYLOAD_SA:
+               flow_4(f, p->u.sa.doi);
+               flow_4(f, p->u.sa.situation);
+               flow_payload(f, p->u.sa.proposals);
+               break;
+       case ISAKMP_PAYLOAD_P:
+               flow_1(f, p->u.p.number);
+               flow_1(f, p->u.p.prot_id);
+               flow_1(f, p->u.p.spi_size);
+               {
+                       uint8_t num_xform = 0;
+                       struct isakmp_payload *xform;
+                       for (xform = p->u.p.transforms; xform; xform = xform->next)
+                               num_xform++;
+                       flow_1(f, num_xform);
+               }
+               flow_x(f, p->u.p.spi, p->u.p.spi_size);
+               flow_payload(f, p->u.p.transforms);
+               break;
+       case ISAKMP_PAYLOAD_T:
+               flow_1(f, p->u.t.number);
+               flow_1(f, p->u.t.id);
+               flow_2(f, 0);
+               flow_attribute(f, p->u.t.attributes);
+               break;
+       case ISAKMP_PAYLOAD_KE:
+       case ISAKMP_PAYLOAD_HASH:
+       case ISAKMP_PAYLOAD_SIG:
+       case ISAKMP_PAYLOAD_NONCE:
+       case ISAKMP_PAYLOAD_VID:
+       case ISAKMP_PAYLOAD_NAT_D:
+       case ISAKMP_PAYLOAD_NAT_D_OLD:
+               flow_x(f, p->u.ke.data, p->u.ke.length);
+               break;
+       case ISAKMP_PAYLOAD_ID:
+               flow_1(f, p->u.id.type);
+               flow_1(f, p->u.id.protocol);
+               flow_2(f, p->u.id.port);
+               flow_x(f, p->u.id.data, p->u.id.length);
+               break;
+       case ISAKMP_PAYLOAD_CERT:
+       case ISAKMP_PAYLOAD_CR:
+               flow_1(f, p->u.cert.encoding);
+               flow_x(f, p->u.cert.data, p->u.cert.length);
+               break;
+       case ISAKMP_PAYLOAD_N:
+               flow_4(f, p->u.n.doi);
+               flow_1(f, p->u.n.protocol);
+               flow_1(f, p->u.n.spi_length);
+               flow_2(f, p->u.n.type);
+               flow_x(f, p->u.n.spi, p->u.n.spi_length);
+               flow_x(f, p->u.n.data, p->u.n.data_length);
+               break;
+       case ISAKMP_PAYLOAD_D:
+               flow_4(f, p->u.d.doi);
+               flow_1(f, p->u.d.protocol);
+               flow_1(f, p->u.d.spi_length);
+               flow_2(f, p->u.d.num_spi);
+               if (p->u.d.spi_length > 0) {
+                       int i;
+                       for (i = 0; i < p->u.d.num_spi; i++)
+                               flow_x(f, p->u.d.spi[i], p->u.d.spi_length);
+               }
+               break;
+       case ISAKMP_PAYLOAD_MODECFG_ATTR:
+               flow_1(f, p->u.modecfg.type);
+               flow_1(f, 0);
+               flow_2(f, p->u.modecfg.id);
+               flow_attribute(f, p->u.modecfg.attributes);
+               break;
+       default:
+               abort();
+       }
+       f->base[lpos] = (f->end - f->base - baselen) >> 8;
+       f->base[lpos + 1] = (f->end - f->base - baselen);
+       flow_payload(f, p->next);
+}
+
+void flatten_isakmp_payloads(struct isakmp_payload *p, uint8_t ** result, size_t * size)
+{
+       struct flow f;
+       init_flow(&f);
+       flow_payload(&f, p);
+       *result = f.base;
+       *size = f.end - f.base;
+}
+
+void flatten_isakmp_payload(struct isakmp_payload *p, uint8_t ** result, size_t * size)
+{
+       struct isakmp_payload *next;
+       next = p->next;
+       p->next = NULL;
+       flatten_isakmp_payloads(p, result, size);
+       p->next = next;
+}
+
+void flatten_isakmp_packet(struct isakmp_packet *p, uint8_t ** result, size_t * size, size_t blksz)
+{
+       struct flow f;
+       size_t lpos, sz, padding;
+
+       init_flow(&f);
+       flow_x(&f, p->i_cookie, ISAKMP_COOKIE_LENGTH);
+       flow_x(&f, p->r_cookie, ISAKMP_COOKIE_LENGTH);
+       if (p->payload == NULL)
+               flow_1(&f, 0);
+       else
+               flow_1(&f, p->payload->type);
+       flow_1(&f, p->isakmp_version);
+       flow_1(&f, p->exchange_type);
+       flow_1(&f, p->flags);
+       flow_4(&f, p->message_id);
+       lpos = flow_reserve(&f, 4);
+       flow_payload(&f, p->payload);
+       if (p->extra_data_length && p->extra_data)
+               flow_x(&f, p->extra_data, p->extra_data_length);
+       if (p->flags & ISAKMP_FLAG_E) {
+               assert(blksz != 0);
+               sz = (f.end - f.base) - ISAKMP_PAYLOAD_O;
+               padding = blksz - (sz % blksz);
+               if (padding == blksz)
+                       padding = 0;
+               DEBUG(3, printf("size = %ld, blksz = %ld, padding = %ld\n",
+                               (long)sz, (long)blksz, (long)padding));
+               flow_reserve(&f, padding);
+       }
+       f.base[lpos] = (f.end - f.base) >> 24;
+       f.base[lpos + 1] = (f.end - f.base) >> 16;
+       f.base[lpos + 2] = (f.end - f.base) >> 8;
+       f.base[lpos + 3] = (f.end - f.base);
+       *result = f.base;
+       *size = f.end - f.base;
+        /*DUMP*/ if (opt_debug >= 3) {
+               printf("\n sending: ========================>\n");
+               free_isakmp_packet(parse_isakmp_packet(f.base, f.end - f.base, NULL));
+       }
+}
+
+struct isakmp_attribute *new_isakmp_attribute(uint16_t type, struct isakmp_attribute *next)
+{
+       struct isakmp_attribute *r = xallocc(sizeof(struct isakmp_attribute));
+       r->type = type;
+       r->next = next;
+       return r;
+}
+
+struct isakmp_attribute *new_isakmp_attribute_16(uint16_t type, uint16_t data,
+       struct isakmp_attribute *next)
+{
+       struct isakmp_attribute *r = xallocc(sizeof(struct isakmp_attribute));
+       r->next = next;
+       r->type = type;
+       r->af = isakmp_attr_16;
+       r->u.attr_16 = data;
+       return r;
+}
+
+
+struct isakmp_attribute *dup_isakmp_attributes(struct isakmp_attribute *attributes)
+{
+       struct isakmp_attribute *att, *newatt, *prevatt = NULL, *start = NULL;
+       for (att = attributes; att; att = att->next) {
+               newatt = xallocc(sizeof(struct isakmp_attribute));
+               *newatt = *att;
+               if (att->af == isakmp_attr_lots)
+                       att->u.lots.data = dup_data(att->u.lots.data, att->u.lots.length);
+               if (prevatt)
+                       prevatt->next = newatt;
+               else
+                       start = newatt;
+               newatt->next = NULL;
+               prevatt = newatt;
+       }
+
+       return start;
+}
+
+void free_isakmp_attributes(struct isakmp_attribute *attributes)
+{
+       struct isakmp_attribute *att, *nextatt;
+       for (att = attributes; att; att = nextatt) {
+               nextatt = att->next;
+               if (att->af == isakmp_attr_lots)
+                       free(att->u.lots.data);
+               if (att->af == isakmp_attr_acl)
+                       free(att->u.acl.acl_ent);
+               free(att);
+       }
+}
+
+struct isakmp_packet *new_isakmp_packet(void)
+{
+       return xallocc(sizeof(struct isakmp_packet));
+}
+
+struct isakmp_payload *new_isakmp_payload(uint8_t type)
+{
+       struct isakmp_payload *result = xallocc(sizeof(struct isakmp_payload));
+       result->type = type;
+       return result;
+}
+
+struct isakmp_payload *new_isakmp_data_payload(uint8_t type, const void *data, size_t data_length)
+{
+       struct isakmp_payload *result = xallocc(sizeof(struct isakmp_payload));
+
+       if (type != ISAKMP_PAYLOAD_KE && type != ISAKMP_PAYLOAD_HASH
+               && type != ISAKMP_PAYLOAD_SIG && type != ISAKMP_PAYLOAD_NONCE
+               && type != ISAKMP_PAYLOAD_VID && type != ISAKMP_PAYLOAD_NAT_D
+               && type != ISAKMP_PAYLOAD_NAT_D_OLD)
+               abort();
+       if (data_length >= 16384)
+               abort();
+
+       result->type = type;
+       result->u.ke.length = data_length;
+       result->u.ke.data = xallocc(data_length);
+       memcpy(result->u.ke.data, data, data_length);
+       return result;
+}
+
+struct isakmp_payload *dup_isakmp_payload(struct isakmp_payload *p)
+{
+       struct isakmp_payload *np = xallocc(sizeof(struct isakmp_payload));
+       *np = *p;
+
+       switch (np->type) {
+       case ISAKMP_PAYLOAD_SA:
+               np->u.sa.proposals = dup_isakmp_payload(np->u.sa.proposals);
+               break;
+       case ISAKMP_PAYLOAD_P:
+               np->u.p.spi = dup_data(np->u.p.spi, np->u.p.spi_size);
+               np->u.p.transforms = dup_isakmp_payload(np->u.p.transforms);
+               break;
+       case ISAKMP_PAYLOAD_T:
+               np->u.t.attributes = dup_isakmp_attributes(np->u.t.attributes);
+               break;
+       case ISAKMP_PAYLOAD_KE:
+       case ISAKMP_PAYLOAD_HASH:
+       case ISAKMP_PAYLOAD_SIG:
+       case ISAKMP_PAYLOAD_NONCE:
+       case ISAKMP_PAYLOAD_VID:
+       case ISAKMP_PAYLOAD_NAT_D:
+       case ISAKMP_PAYLOAD_NAT_D_OLD:
+               np->u.ke.data = dup_data(np->u.ke.data, np->u.ke.length);
+               break;
+       case ISAKMP_PAYLOAD_ID:
+               np->u.id.data = dup_data(np->u.id.data, np->u.id.length);
+               break;
+       case ISAKMP_PAYLOAD_CERT:
+       case ISAKMP_PAYLOAD_CR:
+               np->u.cert.data = dup_data(np->u.cert.data, np->u.cert.length);
+               break;
+       case ISAKMP_PAYLOAD_N:
+               np->u.n.spi = dup_data(np->u.n.spi, np->u.n.spi_length);
+               np->u.n.data = dup_data(np->u.n.data, np->u.n.data_length);
+               np->u.n.attributes = dup_isakmp_attributes(np->u.n.attributes);
+               break;
+       case ISAKMP_PAYLOAD_D:
+               if (np->u.d.spi) {
+                       int i;
+                       np->u.d.spi = xallocc(np->u.d.num_spi * sizeof(uint8_t *));
+                       for (i = 0; i < np->u.d.num_spi; i++)
+                               np->u.d.spi[i] = dup_data(np->u.d.spi[i], np->u.d.spi_length);
+               }
+               break;
+       case ISAKMP_PAYLOAD_MODECFG_ATTR:
+               np->u.modecfg.attributes = dup_isakmp_attributes(np->u.modecfg.attributes);
+               break;
+       default:
+               abort();
+
+       }
+
+       /* unlike free_isakmp_payload this doesn't recurse */
+       np->next = NULL;
+       return np;
+}
+
+static void free_isakmp_payload(struct isakmp_payload *p)
+{
+       struct isakmp_payload *nxt;
+
+       if (p == NULL)
+               return;
+
+       switch (p->type) {
+       case ISAKMP_PAYLOAD_SA:
+               free_isakmp_payload(p->u.sa.proposals);
+               break;
+       case ISAKMP_PAYLOAD_P:
+               free(p->u.p.spi);
+               free_isakmp_payload(p->u.p.transforms);
+               break;
+       case ISAKMP_PAYLOAD_T:
+               free_isakmp_attributes(p->u.t.attributes);
+               break;
+       case ISAKMP_PAYLOAD_KE:
+       case ISAKMP_PAYLOAD_HASH:
+       case ISAKMP_PAYLOAD_SIG:
+       case ISAKMP_PAYLOAD_NONCE:
+       case ISAKMP_PAYLOAD_VID:
+       case ISAKMP_PAYLOAD_NAT_D:
+       case ISAKMP_PAYLOAD_NAT_D_OLD:
+               free(p->u.ke.data);
+               break;
+       case ISAKMP_PAYLOAD_ID:
+               free(p->u.id.data);
+               break;
+       case ISAKMP_PAYLOAD_CERT:
+       case ISAKMP_PAYLOAD_CR:
+               free(p->u.cert.data);
+               break;
+       case ISAKMP_PAYLOAD_N:
+               free(p->u.n.spi);
+               free(p->u.n.data);
+               free_isakmp_attributes(p->u.n.attributes);
+               break;
+       case ISAKMP_PAYLOAD_D:
+               if (p->u.d.spi) {
+                       int i;
+                       for (i = 0; i < p->u.d.num_spi; i++)
+                               free(p->u.d.spi[i]);
+                       free(p->u.d.spi);
+               }
+               break;
+       case ISAKMP_PAYLOAD_MODECFG_ATTR:
+               free_isakmp_attributes(p->u.modecfg.attributes);
+               break;
+       default:
+               abort();
+
+       }
+       nxt = p->next;
+       free(p);
+       free_isakmp_payload(nxt);
+}
+
+void free_isakmp_packet(struct isakmp_packet *p)
+{
+       if (p == NULL)
+               return;
+       free(p->extra_data);
+       free_isakmp_payload(p->payload);
+       free(p);
+}
+
+static const struct debug_strings *transform_id_to_debug_strings(enum isakmp_ipsec_proto_enum decode_proto)
+{
+       switch (decode_proto) {
+       case ISAKMP_IPSEC_PROTO_ISAKMP:
+               return isakmp_ipsec_key_enum_array;
+       case ISAKMP_IPSEC_PROTO_IPSEC_AH:
+               return isakmp_ipsec_ah_enum_array;
+       case ISAKMP_IPSEC_PROTO_IPSEC_ESP:
+               return isakmp_ipsec_esp_enum_array;
+       case ISAKMP_IPSEC_PROTO_IPCOMP:
+               return isakmp_ipsec_ipcomp_enum_array;
+       default:
+               return NULL;
+       }
+}
+
+static const struct debug_strings *attr_type_to_debug_strings(enum isakmp_ipsec_proto_enum decode_proto)
+{
+       switch (decode_proto) {
+       case ISAKMP_IPSEC_PROTO_ISAKMP:
+               return ike_attr_enum_array;
+       case ISAKMP_IPSEC_PROTO_IPSEC_AH:
+       case ISAKMP_IPSEC_PROTO_IPSEC_ESP:
+               return isakmp_ipsec_attr_enum_array;
+       case ISAKMP_IPSEC_PROTO_MODECFG:
+               return isakmp_modecfg_attrib_enum_array;
+       default:
+               return NULL;
+       }
+}
+
+static const struct debug_strings *attr_val_to_debug_strings(enum isakmp_ipsec_proto_enum decode_proto, uint16_t type)
+{
+       switch (decode_proto) {
+       case ISAKMP_IPSEC_PROTO_ISAKMP:
+               switch (type) {
+               case IKE_ATTRIB_ENC:              return ike_enc_enum_array;
+               case IKE_ATTRIB_HASH:             return ike_hash_enum_array;
+               case IKE_ATTRIB_AUTH_METHOD:      return ike_auth_enum_array;
+               case IKE_ATTRIB_GROUP_DESC:       return ike_group_enum_array;
+               case IKE_ATTRIB_GROUP_TYPE:       return ike_group_type_enum_array;
+               case IKE_ATTRIB_LIFE_TYPE:        return ike_life_enum_array;
+               case IKE_ATTRIB_NORTEL_CLIENT_ID: return ike_nortel_client_id_enum_array;
+               default:  return NULL;
+               }
+       case ISAKMP_IPSEC_PROTO_IPSEC_AH:
+       case ISAKMP_IPSEC_PROTO_IPSEC_ESP:
+               switch (type) {
+               case ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE: return ipsec_life_enum_array;
+               case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE:   return ipsec_encap_enum_array;
+               case ISAKMP_IPSEC_ATTRIB_AUTH_ALG:     return ipsec_auth_enum_array;
+               default:  return NULL;
+               }
+       default:
+               return NULL;
+       }
+}
+
+#define fetch4()                                       \
+  (data += 4, data_len -= 4,                           \
+   (uint32_t)(data[-4]) << 24 | (uint32_t)(data[-3]) << 16     \
+   | (uint32_t)(data[-2]) << 8 | data[-1])
+#define fetch2()                               \
+  (data += 2, data_len -= 2,                   \
+   (uint16_t)(data[-2]) << 8 | data[-1])
+#define fetch1() (data_len--, *data++)
+#define fetchn(d,n)  \
+  (memcpy ((d), data, (n)), data += (n), data_len -= (n))
+
+static struct isakmp_attribute *parse_isakmp_attributes(const uint8_t ** data_p,
+       size_t data_len, int * reject, enum isakmp_ipsec_proto_enum decode_proto)
+{
+       const uint8_t *data = *data_p;
+       struct isakmp_attribute *r;
+       uint16_t type, length;
+       int i;
+
+       if (data_len < 4)
+               return NULL;
+
+       r = new_isakmp_attribute(0, NULL);
+       type = fetch2();
+       length = fetch2();
+       if (type & 0x8000) {
+               r->type = type & ~0x8000;
+               hex_dump("t.attributes.type", &r->type, DUMP_UINT16, attr_type_to_debug_strings(decode_proto));
+               r->af = isakmp_attr_16;
+               r->u.attr_16 = length;
+               if ((((ISAKMP_XAUTH_06_ATTRIB_TYPE < r->type)
+                       && (r->type <= ISAKMP_XAUTH_06_ATTRIB_ANSWER)
+                       && (r->type != ISAKMP_XAUTH_06_ATTRIB_STATUS))
+                       || ((ISAKMP_XAUTH_02_ATTRIB_TYPE < r->type)
+                       && (r->type <= ISAKMP_XAUTH_02_ATTRIB_ANSWER)
+                       && (r->type != ISAKMP_XAUTH_02_ATTRIB_STATUS)))
+                       && (length > 0)
+                       && (opt_debug < 99))
+                       DEBUG(3, printf("(not dumping xauth data)\n"));
+               else
+                       hex_dump("t.attributes.u.attr_16", &r->u.attr_16, DUMP_UINT16,
+                               attr_val_to_debug_strings(decode_proto, r->type));
+       } else {
+               r->type = type;
+               hex_dump("t.attributes.type", &r->type, DUMP_UINT16, attr_type_to_debug_strings(decode_proto));
+               r->af = isakmp_attr_lots;
+               r->u.lots.length = length;
+               if ((((ISAKMP_XAUTH_06_ATTRIB_TYPE < type)
+                       && (type <= ISAKMP_XAUTH_06_ATTRIB_ANSWER)
+                       && (type != ISAKMP_XAUTH_06_ATTRIB_STATUS))
+                       || ((ISAKMP_XAUTH_02_ATTRIB_TYPE < type)
+                       && (type <= ISAKMP_XAUTH_02_ATTRIB_ANSWER)
+                       && (type != ISAKMP_XAUTH_02_ATTRIB_STATUS)))
+                       && (length > 0)
+                       && (opt_debug < 99))
+                       DEBUG(3, printf("(not dumping xauth data length)\n"));
+               else
+                       hex_dump("t.attributes.u.lots.length", &r->u.lots.length, DUMP_UINT16, NULL);
+               if (data_len < length) {
+                       *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                       return r;
+               }
+               if (r->type == ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC) {
+                       r->af = isakmp_attr_acl;
+                       r->u.acl.count = length / (4+4+2+2+2);
+                       if (r->u.acl.count * (4+4+2+2+2) != length) {
+                               *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                               return r;
+                       }
+                       r->u.acl.acl_ent = xallocc(r->u.acl.count * sizeof(struct acl_ent_s));
+
+                       for (i = 0; i < r->u.acl.count; i++) {
+                               fetchn(&r->u.acl.acl_ent[i].addr.s_addr, 4);
+                               fetchn(&r->u.acl.acl_ent[i].mask.s_addr, 4);
+                               r->u.acl.acl_ent[i].protocol = fetch2();
+                               r->u.acl.acl_ent[i].sport = fetch2();
+                               r->u.acl.acl_ent[i].dport = fetch2();
+                               hex_dump("t.attributes.u.acl.addr", &r->u.acl.acl_ent[i].addr.s_addr, 4, NULL);
+                               hex_dump("t.attributes.u.acl.mask", &r->u.acl.acl_ent[i].mask.s_addr, 4, NULL);
+                               hex_dump("t.attributes.u.acl.protocol", &r->u.acl.acl_ent[i].protocol, DUMP_UINT16, NULL);
+                               hex_dump("t.attributes.u.acl.sport", &r->u.acl.acl_ent[i].sport, DUMP_UINT16, NULL);
+                               hex_dump("t.attributes.u.acl.dport", &r->u.acl.acl_ent[i].dport, DUMP_UINT16, NULL);
+                       }
+               } else if ((r->type == ISAKMP_MODECFG_ATTRIB_NORTEL_SPLIT_INC)
+                               || (r->type == ISAKMP_MODECFG_ATTRIB_NORTEL_SPLIT_INV)) {
+                       r->af = isakmp_attr_acl;
+                       r->u.acl.count = length / (4 + 4);
+                       if (r->u.acl.count * (4 + 4) != length) {
+                               *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                               return r;
+                       }
+                       r->u.acl.acl_ent = xallocc(r->u.acl.count * sizeof(struct acl_ent_s));
+                       for (i = 0; i < r->u.acl.count; i++) {
+                               fetchn(&r->u.acl.acl_ent[i].addr.s_addr, 4);
+                               fetchn(&r->u.acl.acl_ent[i].mask.s_addr, 4);
+                               hex_dump("t.attributes.u.acl.addr", &r->u.acl.acl_ent[i].addr.s_addr, 4, NULL);
+                               hex_dump("t.attributes.u.acl.mask", &r->u.acl.acl_ent[i].mask.s_addr, 4, NULL);
+                       }
+               } else {
+                       r->u.lots.data = xallocc(length);
+                       fetchn(r->u.lots.data, length);
+                       if ((((ISAKMP_XAUTH_06_ATTRIB_TYPE < type)
+                               && (type <= ISAKMP_XAUTH_06_ATTRIB_ANSWER)
+                               && (type != ISAKMP_XAUTH_06_ATTRIB_STATUS))
+                               || ((ISAKMP_XAUTH_02_ATTRIB_TYPE < type)
+                               && (type <= ISAKMP_XAUTH_02_ATTRIB_ANSWER)
+                               && (type != ISAKMP_XAUTH_02_ATTRIB_STATUS)))
+                               && (length > 0)
+                               && (opt_debug < 99))
+                               DEBUG(3, printf("(not dumping xauth data)\n"));
+                       else
+                               hex_dump("t.attributes.u.lots.data", r->u.lots.data, r->u.lots.length, NULL);
+               }
+       }
+       r->next = parse_isakmp_attributes(&data, data_len, reject, decode_proto);
+       *data_p = data;
+       return r;
+}
+
+static struct isakmp_payload *parse_isakmp_payload(uint8_t type,
+       const uint8_t ** data_p, size_t * data_len_p, int * reject, enum isakmp_ipsec_proto_enum decode_proto)
+{
+       const uint8_t *data = *data_p, *tmpdata;
+       size_t data_len = *data_len_p;
+       struct isakmp_payload *r;
+       uint8_t next_type;
+       size_t length, olength;
+
+       static const uint16_t min_payload_len[ISAKMP_PAYLOAD_MODECFG_ATTR + 1] = {
+               4, 12, 8, 8, 4, 8, 5, 5, 4, 4, 4, 12, 12, 4, 8
+       };
+
+       DEBUG(3, printf("\n"));
+       hex_dump("PARSING PAYLOAD type", &type, DUMP_UINT8, isakmp_payload_enum_array);
+       if (type == 0)
+               return NULL;
+       if (type <= ISAKMP_PAYLOAD_MODECFG_ATTR) {
+               if (data_len < min_payload_len[type]) {
+                       *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                       return NULL;
+               }
+       } else if (data_len < 4) {
+               *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+               return NULL;
+       }
+
+       r = new_isakmp_payload(type);
+       next_type = fetch1();
+       hex_dump("next_type", &next_type, DUMP_UINT8, isakmp_payload_enum_array);
+       if (fetch1() != 0) {
+               *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+               return r;
+       }
+       length = fetch2();
+       hex_dump("length", &length, DUMP_UINT16, NULL);
+       if (length > data_len + 4
+               || ((type <= ISAKMP_PAYLOAD_MODECFG_ATTR)&&(length < min_payload_len[type]))
+               || (length < 4)) {
+               *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+               return r;
+       }
+       olength = length;
+       switch (type) {
+       case ISAKMP_PAYLOAD_SA:
+               r->u.sa.doi = fetch4();
+               hex_dump("sa.doi", &r->u.sa.doi, DUMP_UINT32, isakmp_doi_enum_array);
+               if (r->u.sa.doi != ISAKMP_DOI_IPSEC) {
+                       *reject = ISAKMP_N_DOI_NOT_SUPPORTED;
+                       return r;
+               }
+               r->u.sa.situation = fetch4();
+               hex_dump("sa.situation", &r->u.sa.situation, DUMP_UINT32, isakmp_ipsec_sit_enum_array);
+               if (r->u.sa.situation != ISAKMP_IPSEC_SIT_IDENTITY_ONLY) {
+                       *reject = ISAKMP_N_SITUATION_NOT_SUPPORTED;
+                       return r;
+               }
+               *reject = 0;
+               length -= 12;
+               r->u.sa.proposals = parse_isakmp_payload(ISAKMP_PAYLOAD_P, &data, &length, reject, decode_proto);
+               if (*reject != 0)
+                       return r;
+               /* Allow trailing garbage at end of payload.  */
+               data_len -= olength - 12;
+               break;
+
+       case ISAKMP_PAYLOAD_P:
+               if (next_type != ISAKMP_PAYLOAD_P && next_type != 0) {
+                       *reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
+                       return r;
+               }
+               {
+                       uint8_t num_xform;
+                       struct isakmp_payload *xform;
+
+                       r->u.p.number = fetch1();
+                       hex_dump("p.number", &r->u.p.number, DUMP_UINT8, NULL);
+                       r->u.p.prot_id = fetch1();
+                       hex_dump("p.prot_id", &r->u.p.prot_id, DUMP_UINT8, isakmp_ipsec_proto_enum_array);
+                       r->u.p.spi_size = fetch1();
+                       hex_dump("p.spi_size", &r->u.p.spi_size, DUMP_UINT8, NULL);
+                       num_xform = fetch1();
+                       hex_dump("length", &num_xform, DUMP_UINT8, NULL);
+
+                       if (data_len < r->u.p.spi_size) {
+                               *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                               return r;
+                       }
+                       r->u.p.spi = xallocc(r->u.p.spi_size);
+                       fetchn(r->u.p.spi, r->u.p.spi_size);
+                       hex_dump("p.spi", r->u.p.spi, r->u.p.spi_size, NULL);
+                       length -= 8 + r->u.p.spi_size;
+                       r->u.p.transforms = parse_isakmp_payload(ISAKMP_PAYLOAD_T,
+                               &data, &length, reject, r->u.p.prot_id);
+                       for (xform = r->u.p.transforms; xform; xform = xform->next)
+                               if (num_xform-- == 0)
+                                       break;
+                       if (num_xform != 0) {
+                               *reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+                               return r;
+                       }
+
+                       /* Allow trailing garbage at end of payload.  */
+                       data_len -= olength - 8 - r->u.p.spi_size;
+               }
+               break;
+
+       case ISAKMP_PAYLOAD_T:
+               if (next_type != ISAKMP_PAYLOAD_T && next_type != 0) {
+                       *reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
+                       return r;
+               }
+               r->u.t.number = fetch1();
+               hex_dump("t.number", &r->u.t.number, DUMP_UINT8, NULL);
+               r->u.t.id = fetch1();
+               hex_dump("t.id", &r->u.t.id, DUMP_UINT8, transform_id_to_debug_strings(decode_proto));
+               if (fetch2() != 0) {
+                       *reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+                       return r;
+               }
+               length -= 8;
+               r->u.t.attributes = parse_isakmp_attributes(&data, length, reject, decode_proto);
+               data_len -= olength - 8;
+               break;
+
+       case ISAKMP_PAYLOAD_KE:
+       case ISAKMP_PAYLOAD_HASH:
+       case ISAKMP_PAYLOAD_SIG:
+       case ISAKMP_PAYLOAD_NONCE:
+       case ISAKMP_PAYLOAD_VID:
+       case ISAKMP_PAYLOAD_NAT_D:
+       case ISAKMP_PAYLOAD_NAT_D_OLD:
+               r->u.ke.length = length - 4;
+               r->u.ke.data = xallocc(r->u.ke.length);
+               fetchn(r->u.ke.data, r->u.ke.length);
+               hex_dump("ke.data", r->u.ke.data, r->u.ke.length, NULL);
+               if (type == ISAKMP_PAYLOAD_VID)
+                       print_vid(r->u.ke.data, r->u.ke.length);
+               break;
+       case ISAKMP_PAYLOAD_ID:
+               r->u.id.type = fetch1();
+               hex_dump("id.type", &r->u.id.type, DUMP_UINT8, isakmp_ipsec_id_enum_array);
+               r->u.id.protocol = fetch1();
+               hex_dump("id.protocol", &r->u.id.protocol, DUMP_UINT8, NULL); /* IP protocol nr */
+               r->u.id.port = fetch2();
+               hex_dump("id.port", &r->u.id.port, DUMP_UINT16, NULL);
+               r->u.id.length = length - 8;
+               r->u.id.data = xallocc(r->u.id.length);
+               fetchn(r->u.id.data, r->u.id.length);
+               hex_dump("id.data", r->u.id.data, r->u.id.length, NULL);
+               break;
+       case ISAKMP_PAYLOAD_CERT:
+       case ISAKMP_PAYLOAD_CR:
+               r->u.cert.encoding = fetch1();
+               hex_dump("cert.encoding", &r->u.cert.encoding, DUMP_UINT8, NULL);
+               r->u.cert.length = length - 5;
+               r->u.cert.data = xallocc(r->u.cert.length);
+               fetchn(r->u.cert.data, r->u.cert.length);
+               hex_dump("cert.data", r->u.cert.data, r->u.cert.length, NULL);
+               break;
+       case ISAKMP_PAYLOAD_N:
+               r->u.n.doi = fetch4();
+               hex_dump("n.doi", &r->u.n.doi, DUMP_UINT32, isakmp_doi_enum_array);
+               r->u.n.protocol = fetch1();
+               hex_dump("n.protocol", &r->u.n.protocol, DUMP_UINT8, isakmp_ipsec_proto_enum_array);
+               r->u.n.spi_length = fetch1();
+               hex_dump("n.spi_length", &r->u.n.spi_length, DUMP_UINT8, NULL);
+               r->u.n.type = fetch2();
+               hex_dump("n.type", &r->u.n.type, DUMP_UINT16, isakmp_notify_enum_array);
+               if (r->u.n.spi_length + 12u > length) {
+                       *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                       return r;
+               }
+               r->u.n.spi = xallocc(r->u.n.spi_length);
+               fetchn(r->u.n.spi, r->u.n.spi_length);
+               hex_dump("n.spi", r->u.n.spi, r->u.n.spi_length, NULL);
+               r->u.n.data_length = length - 12 - r->u.n.spi_length;
+               r->u.n.data = xallocc(r->u.n.data_length);
+               fetchn(r->u.n.data, r->u.n.data_length);
+               hex_dump("n.data", r->u.n.data, r->u.n.data_length, NULL);
+               if ((r->u.n.doi == ISAKMP_DOI_IPSEC)&&(r->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME)) {
+                       tmpdata = r->u.n.data;
+                       r->u.n.attributes = parse_isakmp_attributes(&tmpdata, r->u.n.data_length, reject,
+                               r->u.n.protocol);
+               }
+               break;
+       case ISAKMP_PAYLOAD_D:
+               r->u.d.doi = fetch4();
+               hex_dump("d.doi", &r->u.d.doi, DUMP_UINT32, isakmp_doi_enum_array);
+               r->u.d.protocol = fetch1();
+               hex_dump("d.protocol", &r->u.d.protocol, DUMP_UINT8, isakmp_ipsec_proto_enum_array);
+               r->u.d.spi_length = fetch1();
+               hex_dump("d.spi_length", &r->u.d.spi_length, DUMP_UINT8, NULL);
+               r->u.d.num_spi = fetch2();
+               hex_dump("d.num_spi", &r->u.d.num_spi, DUMP_UINT16, NULL);
+               if (r->u.d.num_spi * r->u.d.spi_length + 12u != length) {
+                       *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                       return r;
+               }
+               r->u.d.spi = xallocc(sizeof(uint8_t *) * r->u.d.num_spi);
+               {
+                       int i;
+                       for (i = 0; i < r->u.d.num_spi; i++) {
+                               r->u.d.spi[i] = xallocc(r->u.d.spi_length);
+                               fetchn(r->u.d.spi[i], r->u.d.spi_length);
+                               hex_dump("d.spi", r->u.d.spi[i], r->u.d.spi_length, NULL);
+                       }
+               }
+               break;
+       case ISAKMP_PAYLOAD_MODECFG_ATTR:
+               r->u.modecfg.type = fetch1();
+               hex_dump("modecfg.type", &r->u.modecfg.type, DUMP_UINT8, isakmp_modecfg_cfg_enum_array);
+               if (fetch1() != 0) {
+                       *reject = ISAKMP_N_PAYLOAD_MALFORMED;
+                       return r;
+               }
+               r->u.modecfg.id = fetch2();
+               hex_dump("modecfg.id", &r->u.modecfg.id, DUMP_UINT16, NULL);
+               length -= 8;
+               r->u.modecfg.attributes = parse_isakmp_attributes(&data, length, reject,
+                       ISAKMP_IPSEC_PROTO_MODECFG); /* this "proto" is a hack for simplicity */
+               data_len -= olength - 8;
+               break;
+
+       default:
+               r->u.ke.length = length - 4;
+               r->u.ke.data = xallocc(r->u.ke.length);
+               fetchn(r->u.ke.data, r->u.ke.length);
+               hex_dump("UNKNOWN.data", r->u.ke.data, r->u.ke.length, NULL);
+               break;
+       }
+       *data_p = data;
+       *data_len_p = data_len;
+       hex_dump("DONE PARSING PAYLOAD type", &type, DUMP_UINT8, isakmp_payload_enum_array);
+       r->next = parse_isakmp_payload(next_type, data_p, data_len_p, reject, decode_proto);
+       return r;
+}
+
+struct isakmp_packet *parse_isakmp_packet(const uint8_t * data, size_t data_len, int * reject)
+{
+       int reason = 0;
+       uint8_t payload;
+       struct isakmp_packet *r = new_isakmp_packet();
+       size_t o_data_len = data_len;
+       size_t isakmp_data_len;
+
+       if (data_len < ISAKMP_PAYLOAD_O) {
+               DEBUG(2, printf("packet to short: len = %lld < min = %lld\n", (long long) data_len, (long long)ISAKMP_PAYLOAD_O));
+               reason = ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS;
+               goto error;
+       }
+
+       DEBUG(3, printf("BEGIN_PARSE\n"));
+       DEBUG(3, printf("Received Packet Len: %zu\n", data_len));
+       fetchn(r->i_cookie, ISAKMP_COOKIE_LENGTH);
+       hex_dump("i_cookie", r->i_cookie, ISAKMP_COOKIE_LENGTH, NULL);
+       fetchn(r->r_cookie, ISAKMP_COOKIE_LENGTH);
+       hex_dump("r_cookie", r->r_cookie, ISAKMP_COOKIE_LENGTH, NULL);
+       payload = fetch1();
+       hex_dump("payload", &payload, DUMP_UINT8, isakmp_payload_enum_array);
+
+       r->isakmp_version = fetch1();
+       hex_dump("isakmp_version", &r->isakmp_version, DUMP_UINT8, NULL);
+       if (r->isakmp_version > ISAKMP_VERSION) {
+               if ((r->isakmp_version & 0xF0) >= (ISAKMP_VERSION & 0xF0))
+                       reason = ISAKMP_N_INVALID_MAJOR_VERSION;
+               else
+                       reason = ISAKMP_N_INVALID_MINOR_VERSION;
+               goto error;
+       }
+
+       r->exchange_type = fetch1();
+       hex_dump("exchange_type", &r->exchange_type, DUMP_UINT8, isakmp_exchange_enum_array);
+       r->flags = fetch1();
+       hex_dump("flags", &r->flags, DUMP_UINT8, NULL);
+       r->message_id = fetch4();
+       hex_dump("message_id", &r->message_id, sizeof(r->message_id), NULL);
+
+       isakmp_data_len = fetch4();
+       hex_dump("len", &isakmp_data_len, DUMP_UINT32, NULL);
+       if (o_data_len != isakmp_data_len) {
+               DEBUG(2, printf("isakmp length does not match packet length: isakmp = %lld != datalen = %lld\n",
+                       (long long)isakmp_data_len, (long long)o_data_len));
+               reason = ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS;
+               goto error;
+       }
+
+       r->payload = parse_isakmp_payload(payload, &data, &data_len, &reason, 0);
+       if (reason != 0)
+               goto error;
+
+       if (data_len) {
+               r->extra_data_length = data_len;
+               r->extra_data = dup_data(data, data_len);
+               data += data_len;
+               data_len = 0;
+               hex_dump("extra data", r->extra_data, r->extra_data_length, NULL);
+       }
+
+       DEBUG(3, printf("PARSE_OK\n"));
+       return r;
+
+      error:
+       free_isakmp_packet(r);
+       if (reject)
+               *reject = reason;
+       return NULL;
+}
+
+void test_pack_unpack(void)
+{
+       static const uint8_t pack[] = {
+               0x7f, 0xba, 0x51, 0x29, 0x11, 0x9e, 0x76, 0xf7, 0x9a, 0x71, 0xee, 0x70,
+               0xaa, 0x82, 0xb9, 0x7f, 0x01, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x01, 0x4c, 0x04, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01,
+               0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01, 0x00, 0x01,
+               0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x05,
+               0x80, 0x02, 0x00, 0x01, 0x80, 0x04, 0x00, 0x02, 0x80, 0x03, 0x00, 0x01,
+               0x0a, 0x00, 0x00, 0x84, 0x1b, 0x1d, 0x4b, 0x29, 0x0e, 0x29, 0xb9, 0x6f,
+               0x18, 0x34, 0xd1, 0x2d, 0xba, 0x92, 0x7c, 0x53, 0x35, 0x76, 0x0e, 0x3b,
+               0x25, 0x92, 0x4f, 0x7c, 0x1e, 0x31, 0x41, 0x8c, 0xb9, 0xe3, 0xda, 0xf7,
+               0x53, 0xd3, 0x22, 0x8e, 0xff, 0xeb, 0xed, 0x5b, 0x95, 0x56, 0x8d, 0xba,
+               0xa8, 0xe3, 0x2a, 0x9b, 0xb4, 0x04, 0x5c, 0x90, 0xf0, 0xfe, 0x92, 0xc8,
+               0x57, 0xa2, 0xc6, 0x0c, 0x85, 0xbb, 0x56, 0xe8, 0x1c, 0xa7, 0x2c, 0x57,
+               0x04, 0xb6, 0xe0, 0x43, 0x82, 0xe1, 0x9f, 0x0b, 0xa6, 0x8b, 0xce, 0x7f,
+               0x9b, 0x75, 0xbb, 0xd3, 0xff, 0x0e, 0x89, 0x19, 0xaf, 0xc6, 0x2e, 0xf6,
+               0x92, 0x06, 0x46, 0x4f, 0xc7, 0x97, 0x22, 0xf4, 0xa6, 0xf9, 0x26, 0x34,
+               0x04, 0x33, 0x89, 0x34, 0xa9, 0x2f, 0x81, 0x92, 0xd3, 0x21, 0x4f, 0x45,
+               0xbe, 0x38, 0x12, 0x26, 0xec, 0x87, 0x45, 0xdd, 0x10, 0x1c, 0xd6, 0x16,
+               0x05, 0x00, 0x00, 0x18, 0x77, 0xdf, 0x37, 0x3c, 0x03, 0x02, 0xe2, 0xc8,
+               0xe1, 0x2f, 0x92, 0xf0, 0x2e, 0xa2, 0xa6, 0x00, 0x17, 0x8f, 0xdf, 0xb4,
+               0x08, 0x00, 0x00, 0x0c, 0x01, 0x11, 0x01, 0xf4, 0xcd, 0xb4, 0x53, 0x6d,
+               0x0d, 0x00, 0x00, 0x14, 0x07, 0x47, 0x8d, 0xa7, 0x0b, 0xd6, 0xd1, 0x66,
+               0x7a, 0xaf, 0x2e, 0x61, 0x2a, 0x91, 0x80, 0x94, 0x0d, 0x00, 0x00, 0x14,
+               0x12, 0xf5, 0xf2, 0x8c, 0x45, 0x71, 0x68, 0xa9, 0x70, 0x2d, 0x9f, 0xe2,
+               0x74, 0xcc, 0x01, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x26, 0x89,
+               0xdf, 0xd6, 0xb7, 0x12, 0x0d, 0x00, 0x00, 0x14, 0xaf, 0xca, 0xd7, 0x13,
+               0x68, 0xa1, 0xf1, 0xc9, 0x6b, 0x86, 0x96, 0xfc, 0x77, 0x57, 0x01, 0x00,
+               0x00, 0x00, 0x00, 0x14, 0x1f, 0x07, 0xf7, 0x0e, 0xaa, 0x65, 0x14, 0xd3,
+               0xb0, 0xfa, 0x96, 0x54, 0x2a, 0x50, 0x03, 0x05
+       };
+       uint8_t *unpack;
+       size_t unpack_len;
+       struct isakmp_packet *p;
+       int reject;
+
+       p = parse_isakmp_packet(pack, sizeof(pack), &reject);
+       flatten_isakmp_packet(p, &unpack, &unpack_len, 8);
+       if (unpack_len != sizeof(pack)
+               || memcmp(unpack, pack, sizeof(pack)) != 0)
+               abort();
+       free(unpack);
+       free_isakmp_packet(p);
+}
diff --git a/isakmp-pkt.h b/isakmp-pkt.h
new file mode 100644 (file)
index 0000000..2616391
--- /dev/null
@@ -0,0 +1,153 @@
+/* ISAKMP packing and unpacking routines.
+   Copyright (C) 2002  Geoffrey Keating
+   Copyright (C) 2003-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: isakmp-pkt.h 377 2008-11-26 08:03:43Z Joerg Mayer $
+*/
+
+#ifndef __ISAKMP_PKT_H__
+#define __ISAKMP_PKT_H__
+#if defined(__linux__)
+#include <stdint.h>
+#endif
+#include <sys/types.h>
+
+#include "isakmp.h"
+
+struct isakmp_attribute {
+       struct isakmp_attribute *next;
+       uint16_t type;
+       enum {
+               isakmp_attr_lots,
+               isakmp_attr_16,
+               isakmp_attr_2x8,
+               isakmp_attr_acl
+       } af;
+       union {
+               uint16_t attr_16;
+               uint8_t attr_2x8[2];
+               struct {
+                       uint16_t length;
+                       uint8_t *data;
+               } lots;
+               struct {
+                       uint16_t count;
+                       struct acl_ent_s {
+                               struct in_addr addr, mask;
+                               uint16_t protocol, sport, dport;
+                       } *acl_ent;
+               } acl;
+       } u;
+};
+
+struct isakmp_payload {
+       struct isakmp_payload *next;
+       enum isakmp_payload_enum type;
+       union {
+               struct {
+                       uint32_t doi;
+                       uint32_t situation;
+                       struct isakmp_payload *proposals;
+               } sa;
+               struct {
+                       uint8_t number;
+                       uint8_t prot_id;
+                       uint8_t spi_size;
+                       uint8_t *spi;
+                       struct isakmp_payload *transforms;
+               } p;
+               struct {
+                       uint8_t number;
+                       uint8_t id;
+                       struct isakmp_attribute *attributes;
+               } t;
+               struct {
+                       uint16_t length;
+                       uint8_t *data;
+               } ke, hash, sig, nonce, vid, natd;
+               struct {
+                       uint8_t type;
+                       uint8_t protocol;
+                       uint16_t port;
+                       uint16_t length;
+                       uint8_t *data;
+               } id;
+               struct {
+                       uint8_t encoding;
+                       uint16_t length;
+                       uint8_t *data;
+               } cert, cr;
+               struct {
+                       uint32_t doi;
+                       uint8_t protocol;
+                       uint8_t spi_length;
+                       uint8_t *spi;
+                       uint16_t type;
+                       uint16_t data_length;
+                       uint8_t *data;
+                       struct isakmp_attribute *attributes; /* sometimes, data is an attributes array */
+               } n;
+               struct {
+                       uint32_t doi;
+                       uint8_t protocol;
+                       uint8_t spi_length;
+                       uint16_t num_spi;
+                       uint8_t **spi;
+               } d;
+               struct {
+                       uint8_t type;
+                       uint16_t id;
+                       struct isakmp_attribute *attributes;
+               } modecfg;
+       } u;
+};
+
+struct isakmp_packet {
+       uint8_t i_cookie[ISAKMP_COOKIE_LENGTH];
+       uint8_t r_cookie[ISAKMP_COOKIE_LENGTH];
+       uint8_t isakmp_version;
+       uint8_t exchange_type;
+       uint8_t flags;
+       uint32_t message_id;
+       struct isakmp_payload *payload;
+       uint16_t extra_data_length;
+       uint8_t *extra_data;
+};
+
+extern void *xallocc(size_t x);
+extern void *dup_data(const void *data, size_t x);
+extern struct isakmp_packet *new_isakmp_packet(void);
+extern struct isakmp_payload *new_isakmp_payload(uint8_t);
+extern struct isakmp_payload *new_isakmp_data_payload(uint8_t type, const void *data,
+       size_t data_length);
+extern struct isakmp_payload *dup_isakmp_payload(struct isakmp_payload *p);
+//extern void free_isakmp_payload(struct isakmp_payload *p);
+extern struct isakmp_attribute *new_isakmp_attribute(uint16_t, struct isakmp_attribute *);
+extern struct isakmp_attribute *new_isakmp_attribute_16(uint16_t type, uint16_t data,
+       struct isakmp_attribute *next);
+extern struct isakmp_attribute *dup_isakmp_attributes(struct isakmp_attribute *);
+extern void free_isakmp_attributes(struct isakmp_attribute *);
+extern void free_isakmp_packet(struct isakmp_packet *p);
+extern void flatten_isakmp_payloads(struct isakmp_payload *p, uint8_t ** result, size_t * size);
+extern void flatten_isakmp_payload(struct isakmp_payload *p, uint8_t ** result, size_t * size);
+extern void flatten_isakmp_packet(struct isakmp_packet *p,
+       uint8_t ** result, size_t * size, size_t blksz);
+extern struct isakmp_packet *parse_isakmp_packet(const uint8_t * data,
+       size_t data_len, int * reject);
+extern void test_pack_unpack(void);
+
+#endif
diff --git a/isakmp.h b/isakmp.h
new file mode 100644 (file)
index 0000000..ba24e6f
--- /dev/null
+++ b/isakmp.h
@@ -0,0 +1,518 @@
+/* ISAKMP constants.
+   Copyright (C) 2002  Geoffrey Keating
+
+   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: isakmp.h 460 2011-08-22 14:19:18Z Antonio Borneo $
+*/
+
+#ifndef __ISAKMP_H__
+#define __ISAKMP_H__
+
+/* Flag bits for header.  */
+#define ISAKMP_FLAG_E  0x1
+#define ISAKMP_FLAG_C  0x2
+#define ISAKMP_FLAG_A  0x4
+
+/* Payload types */
+enum isakmp_payload_enum {
+       ISAKMP_PAYLOAD_NONE = 0,        /* RFC 2408 */
+       ISAKMP_PAYLOAD_SA,              /* RFC 2408, Security Association       */
+       ISAKMP_PAYLOAD_P,               /* RFC 2408, Proposal                   */
+       ISAKMP_PAYLOAD_T,               /* RFC 2408, Transform                  */
+       ISAKMP_PAYLOAD_KE,              /* RFC 2408, Key Exchange               */
+       ISAKMP_PAYLOAD_ID,              /* RFC 2408, Identification             */
+       ISAKMP_PAYLOAD_CERT,            /* RFC 2408, Certificate                */
+       ISAKMP_PAYLOAD_CR,              /* RFC 2408, Certificate Request        */
+       ISAKMP_PAYLOAD_HASH,            /* RFC 2408, Hash                       */
+       ISAKMP_PAYLOAD_SIG,             /* RFC 2408, Signature                  */
+       ISAKMP_PAYLOAD_NONCE,           /* RFC 2408, Nonce                      */
+       ISAKMP_PAYLOAD_N,               /* RFC 2408, Notification               */
+       ISAKMP_PAYLOAD_D,               /* RFC 2408, Delete                     */
+       ISAKMP_PAYLOAD_VID,             /* RFC 2408, Vendor ID                  */
+       ISAKMP_PAYLOAD_MODECFG_ATTR,
+       ISAKMP_PAYLOAD_SAK,             /* RFC 3547, SA KEK                     */
+       ISAKMP_PAYLOAD_SAT,             /* RFC 3547, SA TEK                     */
+       ISAKMP_PAYLOAD_KD,              /* RFC 3547, Key Download               */
+       ISAKMP_PAYLOAD_SEQNO,           /* RFC 3547, Sequence number            */
+       ISAKMP_PAYLOAD_POP,             /* RFC 3547, Proof of Possession        */
+       ISAKMP_PAYLOAD_NAT_D,           /* RFC 3947, NAT Discovery              */
+       ISAKMP_PAYLOAD_NAT_OA,          /* RFC 3947, NAT Original Address       */
+       ISAKMP_PAYLOAD_NAT_D_OLD = 0x82,
+       ISAKMP_PAYLOAD_FRAG = 0x84
+};
+
+/* Exchange types.  */
+enum isakmp_exchange_enum {
+       ISAKMP_EXCHANGE_NONE = 0,
+       ISAKMP_EXCHANGE_BASE,
+       ISAKMP_EXCHANGE_IDENTITY,
+       ISAKMP_EXCHANGE_AUTH_ONLY,
+       ISAKMP_EXCHANGE_AGGRESSIVE,
+       ISAKMP_EXCHANGE_INFORMATIONAL,
+       ISAKMP_EXCHANGE_MODECFG_TRANSACTION,
+       ISAKMP_EXCHANGE_IKE_QUICK = 32,
+       ISAKMP_EXCHANGE_IKE_NEW_GROUP
+};
+
+/* DOI types.  */
+enum isakmp_doi_enum {
+       ISAKMP_DOI_GENERIC = 0,
+       ISAKMP_DOI_IPSEC
+};
+
+/* Notify message types (error: 1-16383; status: 16384-65535).  */
+enum isakmp_notify_enum {
+       ISAKMP_N_INVALID_PAYLOAD_TYPE = 1,
+       ISAKMP_N_DOI_NOT_SUPPORTED,
+       ISAKMP_N_SITUATION_NOT_SUPPORTED,
+       ISAKMP_N_INVALID_COOKIE,
+       ISAKMP_N_INVALID_MAJOR_VERSION,
+       ISAKMP_N_INVALID_MINOR_VERSION,
+       ISAKMP_N_INVALID_EXCHANGE_TYPE,
+       ISAKMP_N_INVALID_FLAGS,
+       ISAKMP_N_INVALID_MESSAGE_ID,
+       ISAKMP_N_INVALID_PROTOCOL_ID,
+       ISAKMP_N_INVALID_SPI,
+       ISAKMP_N_INVALID_TRANSFORM_ID,
+       ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED,
+       ISAKMP_N_NO_PROPOSAL_CHOSEN,
+       ISAKMP_N_BAD_PROPOSAL_SYNTAX,
+       ISAKMP_N_PAYLOAD_MALFORMED,
+       ISAKMP_N_INVALID_KEY_INFORMATION,
+       ISAKMP_N_INVALID_ID_INFORMATION,
+       ISAKMP_N_INVALID_CERT_ENCODING,
+       ISAKMP_N_INVALID_CERTIFICATE,
+       ISAKMP_N_CERT_TYPE_UNSUPPORTED,
+       ISAKMP_N_INVALID_CERT_AUTHORITY,
+       ISAKMP_N_INVALID_HASH_INFORMATION,
+       ISAKMP_N_AUTHENTICATION_FAILED,
+       ISAKMP_N_INVALID_SIGNATURE,
+       ISAKMP_N_ADDRESS_NOTIFICATION,
+       ISAKMP_N_NOTIFY_SA_LIFETIME,
+       ISAKMP_N_CERTIFICATE_UNAVAILABLE,
+       ISAKMP_N_UNSUPPORTED_EXCHANGE_TYPE,
+       ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS,
+       ISAKMP_N_CONNECTED = 16384,
+       ISAKMP_N_IPSEC_RESPONDER_LIFETIME = 24576,
+       ISAKMP_N_IPSEC_REPLAY_STATUS,
+       ISAKMP_N_IPSEC_INITIAL_CONTACT,
+       ISAKMP_N_CISCO_HELLO = 30000,
+       ISAKMP_N_CISCO_WWTEBR,
+       ISAKMP_N_CISCO_SHUT_UP,
+       ISAKMP_N_IOS_KEEP_ALIVE_REQ = 32768,
+       ISAKMP_N_IOS_KEEP_ALIVE_ACK,
+       ISAKMP_N_R_U_THERE = 36136,
+       ISAKMP_N_R_U_THERE_ACK,
+       ISAKMP_N_CISCO_LOAD_BALANCE = 40501,
+       ISAKMP_N_CISCO_PRESHARED_KEY_HASH = 40503
+};
+
+/* Delete with reason values */
+/* Note: The values are random, i.e. we don't know them yet */
+enum dwr_ike_delete {
+       IKE_DELETE_SERVER_SHUTDOWN = 0, /* Peer has been shut down */
+       IKE_DELETE_SERVER_REBOOT, /* Peer has been rebooted. */
+       IKE_DELETE_MAX_CONNECT_TIME, /* Maximum configured connection time exceeded. */
+       IKE_DELETE_BY_USER_COMMAND, /* Manually disconnected by administrator. */
+       IKE_DELETE_BY_ERROR, /* Connectivity to Client lost. */
+       IKE_DELETE_NO_ERROR, /* Unknown error. */
+       IKE_DELETE_IDLE_TIMEOUT, /* Maximum idle time for session exceeded. */
+       IKE_DELETE_P2_PROPOSAL_MISMATCH, /* Policy negotiation failed */
+       IKE_DELETE_FIREWALL_MISMATCH, /* Firewall policy mismatch. */
+       IKE_DELETE_CERT_EXPIRED, /* Certificates used with this connection entry have expired. */
+       IKE_DELETE_BY_EXPIRED_LIFETIME, /* Maximum configured lifetime exceeded. */
+       DEL_REASON_RESET_SADB /* (found in vpnclient log file) */
+};
+
+/* Certificate types.  */
+enum isakmp_certificate_enum {
+       ISAKMP_CERT_NONE = 0,
+       ISAKMP_CERT_PKCS7_X509,
+       ISAKMP_CERT_PGP,
+       ISAKMP_CERT_DNS_SIG_KEY,
+       ISAKMP_CERT_X509_SIG,
+       ISAKMP_CERT_X509_KEX_EXCHANGE,
+       ISAKMP_CERT_KERBEROS_TOKENS,
+       ISAKMP_CERT_CRL,
+       ISAKMP_CERT_ARL,
+       ISAKMP_CERT_SPKI,
+       ISAKMP_CERT_X509_ATTRIBUTE
+};
+
+/* IKE attribute types.  */
+enum ike_attr_enum {
+       IKE_ATTRIB_ENC = 1,
+       IKE_ATTRIB_HASH,
+       IKE_ATTRIB_AUTH_METHOD,
+       IKE_ATTRIB_GROUP_DESC,
+       IKE_ATTRIB_GROUP_TYPE,
+       IKE_ATTRIB_GROUP_PRIME,
+       IKE_ATTRIB_GROUP_GEN_1,
+       IKE_ATTRIB_GROUP_GEN_2,
+       IKE_ATTRIB_GROUP_CURVE_A,
+       IKE_ATTRIB_GROUP_CURVE_B,
+       IKE_ATTRIB_LIFE_TYPE,
+       IKE_ATTRIB_LIFE_DURATION,
+       IKE_ATTRIB_PRF,
+       IKE_ATTRIB_KEY_LENGTH,
+       IKE_ATTRIB_FIELD_SIZE,
+       IKE_ATTRIB_GROUP_ORDER,
+       IKE_ATTRIB_BLOCK_SIZE,
+       IKE_ATTRIB_NORTEL_CLIENT_ID = 32767
+};
+
+/* IKE encryption algorithm IDs.  */
+enum ike_enc_enum {
+       IKE_ENC_NO_CBC = 0,
+       IKE_ENC_DES_CBC,
+       IKE_ENC_IDEA_CBC,
+       IKE_ENC_BLOWFISH_CBC,
+       IKE_ENC_RC5_R16_B16_CBC,
+       IKE_ENC_3DES_CBC,
+       IKE_ENC_CAST_CBC,
+       IKE_ENC_AES_CBC
+};
+
+/* IKE hash algorithm IDs.  */
+enum ike_hash_enum {
+       IKE_HASH_MD5 = 1,
+       IKE_HASH_SHA,
+       IKE_HASH_TIGER,
+       IKE_HASH_SHA2_256,
+       IKE_HASH_SHA2_384,
+       IKE_HASH_SHA2_512
+};
+
+/* IKE authentication method IDs.  */
+enum ike_auth_enum {
+       IKE_AUTH_PRESHARED = 1,
+       IKE_AUTH_DSS,
+       IKE_AUTH_RSA_SIG,
+       IKE_AUTH_RSA_ENC,
+       IKE_AUTH_RSA_ENC_2,
+       IKE_AUTH_EL_GAMAL_ENC,
+       IKE_AUTH_EL_GAMAL_ENC_REV,
+       IKE_AUTH_ECDSA_SIG,
+       IKE_AUTH_HybridInitRSA = 64221,
+       IKE_AUTH_HybridRespRSA,
+       IKE_AUTH_HybridInitDSS,
+       IKE_AUTH_HybridRespDSS,
+       IKE_AUTH_XAUTHInitPreShared = 65001,
+       IKE_AUTH_XAUTHRespPreShared,
+       IKE_AUTH_XAUTHInitDSS,
+       IKE_AUTH_XAUTHRespDSS,
+       IKE_AUTH_XAUTHInitRSA,
+       IKE_AUTH_XAUTHRespRSA,
+       IKE_AUTH_XAUTHInitRSAEncryption,
+       IKE_AUTH_XAUTHRespRSAEncryption,
+       IKE_AUTH_XAUTHInitRSARevisedEncryption,
+       IKE_AUTH_XAUTHRespRSARevisedEncryption
+};
+
+/* IKE group IDs.  */
+enum ike_group_enum {
+       IKE_GROUP_MODP_768 = 1,
+       IKE_GROUP_MODP_1024,
+       IKE_GROUP_EC2N_155,
+       IKE_GROUP_EC2N_185,
+       IKE_GROUP_MODP_1536,
+       IKE_GROUP_EC2N_163sect,
+       IKE_GROUP_EC2N_163K,
+       IKE_GROUP_EC2N_283sect,
+       IKE_GROUP_EC2N_283K,
+       IKE_GROUP_EC2N_409sect,
+       IKE_GROUP_EC2N_409K,
+       IKE_GROUP_EC2N_571sect,
+       IKE_GROUP_EC2N_571K
+};
+
+/* IKE group type IDs.  */
+enum ike_group_type_enum {
+       IKE_GROUP_TYPE_MODP = 1,
+       IKE_GROUP_TYPE_ECP,
+       IKE_GROUP_TYPE_EC2N
+};
+
+/* IKE life type IDs.  */
+enum ike_life_enum {
+       IKE_LIFE_TYPE_SECONDS = 1,
+       IKE_LIFE_TYPE_K
+};
+
+/* IKE NORTEL client version IDs.  */
+enum ike_nortel_client_id_enum {
+       NORTEL_CLIENT_V00_00 = 0,
+       NORTEL_CLIENT_V01_00 = 1,
+       NORTEL_CLIENT_V01_05 = 2,
+       NORTEL_CLIENT_V02_00 = 3,
+       NORTEL_CLIENT_V02_50 = 4,
+       NORTEL_CLIENT_V02_60 = 5,
+       NORTEL_CLIENT_V02_62 = 6,
+       NORTEL_CLIENT_V02_62_47 = 7,
+       NORTEL_CLIENT_V03_70 = 8,
+       NORTEL_CLIENT_V04_10 = 9,
+       NORTEL_CLIENT_V04_15 = 10,
+       NORTEL_CLIENT_V04_60 = 11,
+       NORTEL_CLIENT_V04_65 = 12,
+       NORTEL_CLIENT_V04_65_330 = 13,
+       NORTEL_CLIENT_V04_86 = 15,
+       NORTEL_CLIENT_V04_91 = 16,
+       NORTEL_CLIENT_V05_01 = 20,
+       NORTEL_CLIENT_V05_11 = 23,
+       NORTEL_CLIENT_V06_01 = 26,
+       NORTEL_CLIENT_V06_07 = 30,
+       NORTEL_CLIENT_V07_01 = 33,
+       NORTEL_CLIENT_VEXTRA = 65535
+};
+
+/* IPSEC situation masks.  */
+enum isakmp_ipsec_sit_enum {
+       ISAKMP_IPSEC_SIT_IDENTITY_ONLY = 0x1,
+       ISAKMP_IPSEC_SIT_SECRECY       = 0x2,
+       ISAKMP_IPSEC_SIT_INTEGRITY     = 0x4
+};
+
+/* IPSEC Identification types.  */
+enum isakmp_ipsec_id_enum {
+       ISAKMP_IPSEC_ID_RESERVED = 0,
+       ISAKMP_IPSEC_ID_IPV4_ADDR,
+       ISAKMP_IPSEC_ID_FQDN,
+       ISAKMP_IPSEC_ID_USER_FQDN,
+       ISAKMP_IPSEC_ID_IPV4_ADDR_SUBNET,
+       ISAKMP_IPSEC_ID_IPV6_ADDR,
+       ISAKMP_IPSEC_ID_IPV6_ADDR_SUBNET,
+       ISAKMP_IPSEC_ID_IPV4_ADDR_RANGE,
+       ISAKMP_IPSEC_ID_IPV6_ADDR_RANGE,
+       ISAKMP_IPSEC_ID_DER_ASN1_DN,
+       ISAKMP_IPSEC_ID_DER_ASN1_GN,
+       ISAKMP_IPSEC_ID_KEY_ID
+};
+
+/* IPSEC protocol IDs.  */
+enum isakmp_ipsec_proto_enum {
+       ISAKMP_IPSEC_PROTO_RESERVED = 0,
+       ISAKMP_IPSEC_PROTO_ISAKMP,
+       ISAKMP_IPSEC_PROTO_IPSEC_AH,
+       ISAKMP_IPSEC_PROTO_IPSEC_ESP,
+       ISAKMP_IPSEC_PROTO_IPCOMP,
+       ISAKMP_IPSEC_PROTO_MODECFG = 512 /* hack for simplicity in debug code */
+};
+
+/* IPSEC transform IDs.  */
+enum isakmp_ipsec_key_enum {
+       ISAKMP_IPSEC_KEY_RESERVED = 0,
+       ISAKMP_IPSEC_KEY_IKE
+};
+
+/* IPSEC AH IDs.  */
+enum isakmp_ipsec_ah_enum {
+       ISAKMP_IPSEC_AH_RESERVED = 0,
+       ISAKMP_IPSEC_AH_MD5 = 2,
+       ISAKMP_IPSEC_AH_SHA,
+       ISAKMP_IPSEC_AH_DES,
+       ISAKMP_IPSEC_AH_SHA2_256,
+       ISAKMP_IPSEC_AH_SHA2_384,
+       ISAKMP_IPSEC_AH_SHA2_512,
+       ISAKMP_IPSEC_AH_RIPEMD
+};
+
+/* IPSEC ESP IDs.  */
+enum isakmp_ipsec_esp_enum {
+       ISAKMP_IPSEC_ESP_RESERVED = 0,
+       ISAKMP_IPSEC_ESP_DES_IV64,
+       ISAKMP_IPSEC_ESP_DES,
+       ISAKMP_IPSEC_ESP_3DES,
+       ISAKMP_IPSEC_ESP_RC5,
+       ISAKMP_IPSEC_ESP_IDEA,
+       ISAKMP_IPSEC_ESP_CAST,
+       ISAKMP_IPSEC_ESP_BLOWFISH,
+       ISAKMP_IPSEC_ESP_3IDEA,
+       ISAKMP_IPSEC_ESP_DES_IV32,
+       ISAKMP_IPSEC_ESP_RC4,
+       ISAKMP_IPSEC_ESP_NULL,
+       ISAKMP_IPSEC_ESP_AES,
+       ISAKMP_IPSEC_ESP_AES_128_CTR,
+       ISAKMP_IPSEC_ESP_AES_MARS = 249,
+       ISAKMP_IPSEC_ESP_AES_RC6,
+       ISAKMP_IPSEC_ESP_AES_RIJNDAEL,
+       ISAKMP_IPSEC_ESP_AES_SERPENT,
+       ISAKMP_IPSEC_ESP_AES_TWOFISH
+};
+
+/* IPSEC attribute types.  */
+enum isakmp_ipsec_attr_enum {
+       ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE = 1,
+       ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION,
+       ISAKMP_IPSEC_ATTRIB_GROUP_DESC,
+       ISAKMP_IPSEC_ATTRIB_ENCAP_MODE,
+       ISAKMP_IPSEC_ATTRIB_AUTH_ALG,
+       ISAKMP_IPSEC_ATTRIB_KEY_LENGTH,
+       ISAKMP_IPSEC_ATTRIB_KEY_ROUNDS,
+       ISAKMP_IPSEC_ATTRIB_COMP_DICT_SIZE,
+       ISAKMP_IPSEC_ATTRIB_COMP_PRIVATE_ALG,
+       ISAKMP_IPSEC_ATTRIB_ECN_TUNNEL,
+       ISAKMP_IPSEC_ATTRIB_NORTEL_NATT_UDP_PORT = 0x7ffe
+};
+
+/* IPSEC compression IDs.  */
+enum isakmp_ipsec_ipcomp_enum {
+       ISAKMP_IPSEC_IPCOMP_RESERVED = 0,
+       ISAKMP_IPSEC_IPCOMP_OUI,
+       ISAKMP_IPSEC_IPCOMP_DEFLATE,
+       ISAKMP_IPSEC_IPCOMP_LZS,
+       ISAKMP_IPSEC_IPCOMP_V42BIS
+};
+
+/* IPSEC lifetime attribute values.  */
+enum ipsec_life_enum {
+       IPSEC_LIFE_SECONDS = 1,
+       IPSEC_LIFE_K
+};
+
+/* IPSEC encapsulation attribute numbers.  */
+enum ipsec_encap_enum {
+       IPSEC_ENCAP_TUNNEL = 1,
+       IPSEC_ENCAP_TRANSPORT,
+       IPSEC_ENCAP_UDP_TUNNEL,
+       IPSEC_ENCAP_UDP_TRANSPORT,
+       IPSEC_ENCAP_UDP_TUNNEL_OLD = 61443,
+       IPSEC_ENCAP_UDP_TRANSPORT_OLD
+};
+
+/* IPSEC authentication attribute numbers.  */
+enum ipsec_auth_enum {
+       IPSEC_AUTH_HMAC_MD5 = 1,
+       IPSEC_AUTH_HMAC_SHA,
+       IPSEC_AUTH_DES_MAC,
+       IPSEC_AUTH_KPDK
+};
+
+/* Other numbers.  */
+#define ISAKMP_COOKIE_LENGTH           8
+#define ISAKMP_VERSION                 0x10
+/* offsets */
+#define ISAKMP_EXCHANGE_TYPE_O         18
+#define ISAKMP_I_COOKIE_O              0
+#define ISAKMP_R_COOKIE_O              8
+#define ISAKMP_MESSAGE_ID_O            20
+#define ISAKMP_PAYLOAD_O               28
+
+/* 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,
+       ISAKMP_MODECFG_CFG_SET,
+       ISAKMP_MODECFG_CFG_ACK,
+       ISAKMP_MODECFG_CFG_NORTEL_OK,
+       ISAKMP_MODECFG_CFG_NORTEL_ERR
+};
+
+enum isakmp_modecfg_attrib_enum {
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_ADDRESS = 1,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NETMASK,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DNS,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_NBNS,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_ADDRESS_EXPIRY,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_DHCP,
+       ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_ADDRESS,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_NETMASK,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_DNS,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_NBNS,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_DHCP,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP4_SUBNET,
+       ISAKMP_MODECFG_ATTRIB_SUPPORTED_ATTRIBUTES,
+       ISAKMP_MODECFG_ATTRIB_INTERNAL_IP6_SUBNET,
+
+       ISAKMP_XAUTH_02_ATTRIB_TYPE = 0x0d,
+       ISAKMP_XAUTH_02_ATTRIB_USER_NAME,
+       ISAKMP_XAUTH_02_ATTRIB_USER_PASSWORD,
+       ISAKMP_XAUTH_02_ATTRIB_PASSCODE,
+       ISAKMP_XAUTH_02_ATTRIB_MESSAGE,
+       ISAKMP_XAUTH_02_ATTRIB_CHALLENGE,
+       ISAKMP_XAUTH_02_ATTRIB_DOMAIN,
+       ISAKMP_XAUTH_02_ATTRIB_STATUS,
+       ISAKMP_XAUTH_02_ATTRIB_NEXT_PIN,
+       ISAKMP_XAUTH_02_ATTRIB_ANSWER, /* TYPE .. ANSWER is excluded from dump */
+
+       ISAKMP_MODECFG_ATTRIB_NORTEL_SPLIT_INC = 0x4000,
+       ISAKMP_MODECFG_ATTRIB_NORTEL_DEF_DOMAIN_A = 0x4005,
+       ISAKMP_MODECFG_ATTRIB_NORTEL_UNKNOWN_4011 = 0x4011,
+       ISAKMP_MODECFG_ATTRIB_NORTEL_CLIENT_ID = 0x4012,
+       ISAKMP_MODECFG_ATTRIB_NORTEL_SPLIT_INV = 0x4014,
+       ISAKMP_MODECFG_ATTRIB_NORTEL_SPLIT_INV_MODE = 0x4015,
+       ISAKMP_MODECFG_ATTRIB_NORTEL_DEF_DOMAIN_B = 0x4018,
+
+       ISAKMP_XAUTH_06_ATTRIB_TYPE = 0x4088,
+       ISAKMP_XAUTH_06_ATTRIB_USER_NAME,
+       ISAKMP_XAUTH_06_ATTRIB_USER_PASSWORD,
+       ISAKMP_XAUTH_06_ATTRIB_PASSCODE,
+       ISAKMP_XAUTH_06_ATTRIB_MESSAGE,
+       ISAKMP_XAUTH_06_ATTRIB_CHALLENGE,
+       ISAKMP_XAUTH_06_ATTRIB_DOMAIN,
+       ISAKMP_XAUTH_06_ATTRIB_STATUS,
+       ISAKMP_XAUTH_06_ATTRIB_NEXT_PIN,
+       ISAKMP_XAUTH_06_ATTRIB_ANSWER, /* TYPE .. ANSWER is excluded from dump */
+
+       ISAKMP_MODECFG_ATTRIB_CISCO_BANNER = 0x7000,
+       ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW,
+       ISAKMP_MODECFG_ATTRIB_CISCO_DEF_DOMAIN,
+       ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_DNS,
+       ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC,
+       ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT,
+       ISAKMP_MODECFG_ATTRIB_CISCO_UNKNOWN, /* whatever 0x7006 is... */
+       ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS,
+       /* Cisco Ext: Smartcard Disconnect */
+       /* Cisco Ext: IKE_CFG_FWTYPE_VENDOR */
+       /* Cisco Ext: IKE_CFG_FWTYPE_PRODUCT */
+       /* Cisco Ext: IKE_CFG_FWTYPE_CAPABILITIES??? */
+       ISAKMP_MODECFG_ATTRIB_CISCO_FW_TYPE,
+       ISAKMP_MODECFG_ATTRIB_CISCO_BACKUP_SERVER,
+       ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME,
+       ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR = 0x7d88 /* strange cisco things ... need docs! */
+};
+
+enum isakmp_modecfg_type_enum { /* draft-ietf-ipsec-isakmp-xauth-05.txt */
+       ISAKMP_MODECFG_TYPE_GENERIC,
+       ISAKMP_MODECFG_TYPE_RADIUS,
+       ISAKMP_MODECFG_TYPE_OTP,
+       ISAKMP_MODECFG_TYPE_NTDOMAIN,
+       ISAKMP_MODECFG_TYPE_UNIX,
+       ISAKMP_MODECFG_TYPE_SECURID,
+       ISAKMP_MODECFG_TYPE_AXENT,
+       ISAKMP_MODECFG_TYPE_LEEMAH,
+       ISAKMP_MODECFG_TYPE_ACTIVECARD,
+       ISAKMP_MODECFG_TYPE_DESGOLD,
+       ISAKMP_MODECFG_TYPE_TACACS,
+       ISAKMP_MODECFG_TYPE_TACACSPLUS,
+       ISAKMP_MODECFG_TYPE_SKEY,
+       ISAKMP_MODECFG_TYPE_NDS,
+       ISAKMP_MODECFG_TYPE_DIAMETER,
+       ISAKMP_MODECFG_TYPE_LDAP
+};
+
+#endif
diff --git a/makeman.pl b/makeman.pl
new file mode 100755 (executable)
index 0000000..e8806cb
--- /dev/null
@@ -0,0 +1,133 @@
+#! /usr/bin/env perl
+
+# $Id: makeman.pl 445 2009-09-12 17:17:49Z Antonio Borneo $
+
+# Written by Wolfram Sang (wolfram@the-dreams.de) in 2007,
+# some inspiration from help2man by Brendan O'Dea and from Perl::Critic
+
+# Generate the vpnc-manpage from a template and the --long-help-output.
+# Version 0.2
+
+# Command-line options: none
+# Files needed        : ./vpnc ./vpnc.8.template ./VERSION
+# Files created       : ./vpnc.8
+# Exit status         : errno-values or 255 (Magic string not found)
+
+# Distributed under the same licence as vpnc.
+
+use strict;
+use warnings;
+use Fatal    qw(open close);
+use filetest qw(access);       # to always get errno-values on filetests
+use POSIX    qw(strftime setlocale LC_ALL);
+
+my $vpnc = './vpnc';
+-e $vpnc or die "$0: Can't find $vpnc. Did you compile it?\n";
+-x $vpnc or die "$0: Can't execute $vpnc. Please check permissions.\n";
+
+# The code converting the help-output to manpage format is lots of
+# regex-fiddling, sorry. It got a bit more complicated by additionally
+# indenting lists (those originally starting with an asterisk). I hope
+# this pays off when converting the manpage to HTML or such.
+
+open my $LONGHELP, '-|', "$vpnc --long-help";
+my $vpnc_options    = '';
+my $relative_indent = 0;
+my $indent_needed   = 0;
+
+while (<$LONGHELP>) {
+    if (/^  /) {
+
+       # Check if additional indent needs to be finished by comparing the
+       # amount of spaces at the beginning. A bit ugly, but I don't see a
+       # better way to do it.
+       if ($relative_indent) {
+           /^( *)/;
+           if (length($1) < $relative_indent) {
+               $vpnc_options .= ".RE\n";
+               $relative_indent = 0;
+               $indent_needed = 1;
+           }
+       }
+
+       # Highlight the option and make an optional argument italic.
+       if (s/^ *(--[\w-]+)/\n.TP\n.BI "$1"/) {
+           s/(<.+>)/ " $1"/;
+       }
+
+       # Highlight conffile-only options.
+       s/^ *(\(configfile only option\))/\n.TP\n.B $1/;
+
+       # Position the Default-string
+       s/^ *(Default:)/.IP\n$1/;
+
+       # Highlight the conf-variable and make an optional argument italic.
+       if (s/^ *(conf-variable:) (.+?) ?([<\n])/.P\n$1\n.BI "$2"$3/) {
+           s/(<.+>)/ " $1"/;
+       }
+
+       # Replace asterisk with bulletin; indent if needed.
+       if (s/^( +)\* /.IP \\(bu\n/) {
+           if (not $relative_indent) {
+               $vpnc_options .= ".RS\n";
+               $relative_indent = length $1;
+           }
+       }
+
+       # Do we need to add an .IP-command after .RE or is there already one?
+       if ($indent_needed and not /^\n?\.[TI]?P/) {
+           $vpnc_options .= ".IP\n";
+           $indent_needed = 0;
+       }
+
+       # Finalize string and add it to buffer
+        s/^ *//;
+       s/ *$//;
+       s/-/\\-/g;
+        $vpnc_options .= $_;
+    }
+}
+close $LONGHELP;
+
+# Hopefully the code speaks for itself from now on...
+
+setlocale( LC_ALL, 'C' );
+my $date = strftime( '%B %Y', localtime );
+
+open my $VERSION, '<', './VERSION';
+my $vpnc_version = <$VERSION>;
+close $VERSION;
+chomp $vpnc_version;
+
+open my $TEMPLATE, '<', './vpnc.8.template';
+open my $MANPAGE , '>', './vpnc.8';
+my $magic_found;
+my $MAGIC_FOR_HEADER  = qq(.\\" ###makeman.pl: Replace header here!\n);
+my $MAGIC_FOR_OPTIONS = qq(.\\" ###makeman.pl: Insert options from help-output here!\n);
+
+# Skip the template-header
+while (<$TEMPLATE>) {
+    last if ($magic_found = ($_ eq $MAGIC_FOR_HEADER));
+}
+die "$0: Missing magic: $MAGIC_FOR_HEADER" if not $magic_found;
+
+print {$MANPAGE} <<"END_MANPAGE_HEADER";
+.\\" This manpage is generated!
+.\\" Please edit the template-file in the source-distribution only.
+.TH VPNC "8" "$date" "vpnc version $vpnc_version" "System Administration Utilities"
+END_MANPAGE_HEADER
+
+$magic_found = 0;
+
+while (<$TEMPLATE>) {
+    if ($_ ne $MAGIC_FOR_OPTIONS) {
+       print {$MANPAGE} $_;
+    } else {
+       print {$MANPAGE} $vpnc_options;
+       $magic_found = 1;
+    }
+}
+die "$0: Missing magic: $MAGIC_FOR_OPTIONS" if not $magic_found;
+
+close $TEMPLATE;
+close $MANPAGE;
diff --git a/math_group.c b/math_group.c
new file mode 100644 (file)
index 0000000..5590450
--- /dev/null
@@ -0,0 +1,305 @@
+/* borrowed from isakmpd-20030718 (-; */
+
+/*     $OpenBSD: math_group.c,v 1.18 2003/06/03 14:28:16 ho Exp $      */
+/*     $EOM: math_group.c,v 1.25 2000/04/07 19:53:26 niklas Exp $      */
+
+/*
+ * Copyright (c) 1998 Niels Provos.  All rights reserved.
+ * Copyright (c) 1999, 2000 Niklas Hallqvist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This code was written under funding by Ericsson Radio Systems.
+ */
+
+#include <assert.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <gcrypt.h>
+
+#include "math_group.h"
+
+/* We do not want to export these definitions.  */
+static void modp_free(struct group *);
+static struct group *modp_clone(struct group *, struct group *);
+static void modp_init(struct group *);
+
+static int modp_getlen(struct group *);
+static void modp_getraw(struct group *, gcry_mpi_t, unsigned char *);
+static int modp_setraw(struct group *, gcry_mpi_t, unsigned char *, int);
+static int modp_setrandom(struct group *, gcry_mpi_t);
+static int modp_operation(struct group *, gcry_mpi_t, gcry_mpi_t, gcry_mpi_t);
+
+/*
+ * This module provides access to the operations on the specified group
+ * and is absolutly free of any cryptographic devices. This is math :-).
+ */
+
+/* Describe preconfigured MODP groups */
+
+/*
+ * The Generalized Number Field Sieve has an asymptotic running time
+ * of: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))), where q is the
+ * group order, e.g. q = 2**768.
+ */
+
+static const struct modp_dscr oakley_modp[] = {
+       {
+               OAKLEY_GRP_1, 72, /* This group is insecure, only sufficient for DES */
+               "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+               "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+               "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+               "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF",
+               "2"
+       },
+       {
+               OAKLEY_GRP_2, 82, /* This group is a bit better */
+               "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+               "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+               "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+               "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+               "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+               "FFFFFFFFFFFFFFFF",
+               "2"
+       },
+       {
+               OAKLEY_GRP_5, 102, /* This group is yet a bit better, but non-standard */
+               "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+               "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+               "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+               "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+               "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+               "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+               "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+               "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",
+               "2"
+       },
+};
+
+/* XXX I want to get rid of the casting here.  */
+static struct group groups[] = {
+       {
+               MODP, OAKLEY_GRP_1, 0, NULL, &oakley_modp[0], NULL, NULL, NULL, NULL, NULL,
+               (int (*)(struct group *))modp_getlen,
+               (void (*)(struct group *, void *, unsigned char *))modp_getraw,
+               (int (*)(struct group *, void *, unsigned char *, int))modp_setraw,
+               (int (*)(struct group *, void *))modp_setrandom,
+               (int (*)(struct group *, void *, void *, void *))modp_operation
+       },
+       {
+               MODP, OAKLEY_GRP_2, 0, NULL, &oakley_modp[1], NULL, NULL, NULL, NULL, NULL,
+               (int (*)(struct group *))modp_getlen,
+               (void (*)(struct group *, void *, unsigned char *))modp_getraw,
+               (int (*)(struct group *, void *, unsigned char *, int))modp_setraw,
+               (int (*)(struct group *, void *))modp_setrandom,
+               (int (*)(struct group *, void *, void *, void *))modp_operation
+       },
+       {
+               MODP, OAKLEY_GRP_5, 0, NULL, &oakley_modp[2], NULL, NULL, NULL, NULL, NULL,
+               (int (*)(struct group *))modp_getlen,
+               (void (*)(struct group *, void *, unsigned char *))modp_getraw,
+               (int (*)(struct group *, void *, unsigned char *, int))modp_setraw,
+               (int (*)(struct group *, void *))modp_setrandom,
+               (int (*)(struct group *, void *, void *, void *))modp_operation
+       },
+};
+
+/*
+ * Initialize the group structure for later use,
+ * this is done by converting the values given in the describtion
+ * and converting them to their native representation.
+ */
+void group_init(void)
+{
+       int i;
+
+       for (i = sizeof(groups) / sizeof(groups[0]) - 1; i >= 0; i--) {
+               assert(groups[i].type == MODP);
+               modp_init(&groups[i]); /* Initialize an over GF(p) */
+       }
+}
+
+struct group *group_get(int id)
+{
+       struct group *new, *clone;
+
+       assert(id >= 1);
+       assert(id <= (int)(sizeof(groups) / sizeof(groups[0])));
+
+       clone = &groups[id - 1];
+
+       new = malloc(sizeof *new);
+       assert(new);
+
+       assert(clone->type == MODP);
+       new = modp_clone(new, clone);
+       return new;
+}
+
+void group_free(struct group *grp)
+{
+       assert(grp->type == MODP);
+       modp_free(grp);
+       free(grp);
+}
+
+static struct group *modp_clone(struct group *new, struct group *clone)
+{
+       struct modp_group *new_grp, *clone_grp = clone->group;
+
+       new_grp = malloc(sizeof *new_grp);
+       assert(new_grp);
+
+       memcpy(new, clone, sizeof(struct group));
+
+       new->group = new_grp;
+       new_grp->p = gcry_mpi_copy(clone_grp->p);
+       new_grp->gen = gcry_mpi_copy(clone_grp->gen);
+
+       new_grp->a = gcry_mpi_new(clone->bits);
+       new_grp->b = gcry_mpi_new(clone->bits);
+       new_grp->c = gcry_mpi_new(clone->bits);
+
+       new->gen = new_grp->gen;
+       new->a = new_grp->a;
+       new->b = new_grp->b;
+       new->c = new_grp->c;
+
+       return new;
+}
+
+static void modp_free(struct group *old)
+{
+       struct modp_group *grp = old->group;
+
+       gcry_mpi_release(grp->p);
+       gcry_mpi_release(grp->gen);
+       gcry_mpi_release(grp->a);
+       gcry_mpi_release(grp->b);
+       gcry_mpi_release(grp->c);
+
+       free(grp);
+}
+
+static void modp_init(struct group *group)
+{
+       const struct modp_dscr *dscr = group->group_dscr;
+       struct modp_group *grp;
+
+       grp = malloc(sizeof *grp);
+       assert(grp);
+
+       group->bits = dscr->bits;
+
+       gcry_mpi_scan(&grp->p, GCRYMPI_FMT_HEX, (const unsigned char*)dscr->prime, 0, NULL);
+       gcry_mpi_scan(&grp->gen, GCRYMPI_FMT_HEX, (const unsigned char *)dscr->gen, 0, NULL);
+
+       grp->a = gcry_mpi_new(group->bits);
+       grp->b = gcry_mpi_new(group->bits);
+       grp->c = gcry_mpi_new(group->bits);
+
+       group->gen = grp->gen;
+       group->a = grp->a;
+       group->b = grp->b;
+       group->c = grp->c;
+
+       group->group = grp;
+}
+
+static int modp_getlen(struct group *group)
+{
+       struct modp_group *grp = (struct modp_group *)group->group;
+
+       return (gcry_mpi_get_nbits(grp->p) + 7) / 8;
+}
+
+static void modp_getraw(struct group *grp, gcry_mpi_t v, unsigned char *d)
+{
+       size_t l, l2;
+       unsigned char *tmp;
+       int ret;
+
+       l = grp->getlen(grp);
+       ret = gcry_mpi_aprint(GCRYMPI_FMT_STD, &tmp, &l2, v);
+       memcpy(d, tmp + (l2 - l), l);
+       gcry_free(tmp);
+#if 0
+       {
+               char *p;
+               gcry_mpi_aprint(GCRYMPI_FMT_HEX, (void **)&p, NULL, v);
+               printf("export %d - %d(%d):\n%s\n", l, l2, ret, p);
+               gcry_free(p);
+       }
+#endif
+}
+
+static int modp_setraw(struct group *grp, gcry_mpi_t d, unsigned char *s, int l)
+{
+       int i;
+
+       grp = NULL; /* unused */
+
+       gcry_mpi_set_ui(d, 0);
+       for (i = 0; i < l; i++) {
+               gcry_mpi_mul_2exp(d, d, 8);
+               gcry_mpi_add_ui(d, d, s[i]);
+       }
+#if 0
+       {
+               char *p;
+               gcry_mpi_aprint(GCRYMPI_FMT_HEX, (void **)&p, NULL, d);
+               printf("import %d:\n%s\n", l, p);
+               gcry_free(p);
+       }
+#endif
+       return 0;
+}
+
+static int modp_setrandom(struct group *grp, gcry_mpi_t d)
+{
+       int i, l = grp->getlen(grp);
+       uint32_t tmp = 0;
+
+       gcry_mpi_set_ui(d, 0);
+
+       for (i = 0; i < l; i++) {
+               if (i % 4)
+                       gcry_randomize((unsigned char *)&tmp, sizeof(tmp), GCRY_STRONG_RANDOM);
+
+               gcry_mpi_mul_2exp(d, d, 8);
+               gcry_mpi_add_ui(d, d, tmp & 0xFF);
+               tmp >>= 8;
+       }
+       return 0;
+}
+
+static int modp_operation(struct group *group, gcry_mpi_t d, gcry_mpi_t a, gcry_mpi_t e)
+{
+       struct modp_group *grp = (struct modp_group *)group->group;
+
+       gcry_mpi_powm(d, a, e, grp->p);
+       return 0;
+}
diff --git a/math_group.h b/math_group.h
new file mode 100644 (file)
index 0000000..9f86189
--- /dev/null
@@ -0,0 +1,88 @@
+/* borrowed from isakmpd-20030718 (-; */
+
+/*     $OpenBSD: math_group.h,v 1.7 2003/06/03 14:28:16 ho Exp $       */
+/*     $EOM: math_group.h,v 1.7 1999/04/17 23:20:40 niklas Exp $       */
+
+/*
+ * Copyright (c) 1998 Niels Provos.  All rights reserved.
+ * Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This code was written under funding by Ericsson Radio Systems.
+ */
+
+#ifndef __MATH_GROUP_H__
+#define __MATH_GROUP_H__
+
+#include <gcrypt.h>
+
+enum groups {
+       MODP  /* F_p, Z modulo a prime */
+};
+
+#define OAKLEY_GRP_1   1
+#define OAKLEY_GRP_2   2
+#define OAKLEY_GRP_5   3
+
+/*
+ * The group on which diffie hellmann calculations are done.
+ */
+
+/* Description of F_p for Boot-Strapping */
+
+struct modp_dscr {
+       int id;
+       int bits; /* Key Bits provided by this group */
+       const char *prime; /* Prime */
+       const char *gen; /* Generator */
+};
+
+struct modp_group {
+       gcry_mpi_t gen; /* Generator */
+       gcry_mpi_t p; /* Prime */
+       gcry_mpi_t a, b, c, d;
+};
+
+struct group {
+       enum groups type;
+       int id; /* Group ID */
+       int bits; /* Number of key bits provided by this group */
+       struct modp_group *group;
+       const struct modp_dscr *group_dscr;
+       void *a, *b, *c, *d;
+       void *gen; /* Group Generator */
+       int (*getlen) (struct group *);
+       void (*getraw) (struct group *, void *, unsigned char *);
+       int (*setraw) (struct group *, void *, unsigned char *, int);
+       int (*setrandom) (struct group *, void *);
+       int (*operation) (struct group *, void *, void *, void *);
+};
+
+/* Prototypes */
+
+void group_init(void);
+void group_free(struct group *);
+struct group *group_get(int);
+
+#endif /* _MATH_GROUP_H_ */
diff --git a/mk-version b/mk-version
new file mode 100755 (executable)
index 0000000..a8ffb74
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# print vpnc version from file VERSION, appending the string printed
+# by svnversion(1) if appropriate
+
+in_git_repository ()
+{
+       git rev-parse --is-inside-work-tree > /dev/null 2>&1
+       return $?
+}
+
+git_svn_version ()
+{
+       # search for svn-remote defined in git configuration
+       svn_url_pattern=""
+       while read key value; do
+               svn_url_pattern="${svn_url_pattern}\\|${value}"
+       done << EOF
+       $(git config --get-regexp '^svn-remote\..*\.url$' 2>/dev/null)
+EOF
+
+       # exit if no svn-remote defined
+       if [ -z "${svn_url_pattern}" ]; then
+               return
+       fi
+
+       # scan git-log for latest commit from any svn-remote above
+       set -- $(git log --first-parent -1 \
+               --grep="^git-svn-id: \(${svn_url_pattern#??}\)@" 2>/dev/null)
+
+       # check commit hash is 40 hex digits
+       svn_commit=$2
+       if [ ${#svn_commit} -ne 40 -o -z "${svn_commit##*[!0-9a-f]*}" ]; then
+               return
+       fi
+
+       # check svn version is numeric
+       shift $(($# - 2))
+       svn_ver=${1#*@}
+       if [ -z "${svn_ver}" -o -z "${svn_ver##*[!0-9]*}" ]; then
+               return
+       fi
+
+       if [ $(git rev-list HEAD...${svn_commit} | wc -l) -eq 0 ]; then
+               if [ `git status -s | grep -vc '^??'` -eq 0 ]; then
+                       # no git commits and tree clean
+                       echo "${svn_ver}"
+                       return
+               fi
+       fi
+       # there are local git commits after latest svn commit or tree is dirty
+       echo "${svn_ver}M"
+}
+
+
+_version="`cat VERSION`"
+
+if [ -d .svn ]; then
+       if which svnversion > /dev/null; then
+               _version="$_version-`svnversion`"
+       else
+               _version="$_version-`sed -n '/^dir$/{n;p;q;}' .svn/entries`"
+       fi
+elif which git > /dev/null && in_git_repository; then
+       git_ext=$(git_svn_version)
+       if [ -n "${git_ext}" ]; then
+               _version="$_version-${git_ext}"
+       fi
+fi
+
+echo "$_version"
+
+exit 0
diff --git a/nortel.txt b/nortel.txt
new file mode 100644 (file)
index 0000000..c6d697f
--- /dev/null
@@ -0,0 +1,89 @@
+[vpnc-devel] vpnc with Nortel Contivity
+
+Matt Chapman <matthewc@cse.unsw.edu.au>
+Thu Jun 9 21:51:50 CEST 2005
+
+
+I've managed to get vpnc working with a Nortel Contivity VPN
+concentrator.
+
+Basically the differences are:
+
+* The group name and password are pre-transformed:
+  key_id = SHA1(group_name)
+  shared_key = HMAC_SHA1(group_name, SHA1(group_password))
+
+* The XAUTH implementation follows
+  draft-ietf-ipsec-isakmp-xauth-02.txt (whereas CISCO uses a
+  later version).  Specifically:
+  - the encoding of the proposal is not defined in that spec,
+    and Nortel does it differently
+  - the XAUTH attributes have different numerical values
+    (which overlap with Mode-Config, argh)
+  - success/failure are encoded as Mode-Config message types
+    5/6 (or sometimes as an ISAKMP notify?) rather than in
+    an attribute
+  - the concentrator always sends 0 in XAUTH_TYPE and the
+    client may have to return a different value (xauth-02 is
+    not clear on whether this is allowed, it is not
+    clarified until xauth-05).  In my case I'm using an
+    ActivCard token for which I have to specify 5 (SecurID).
+
+* Mode-Config is done as a push, i.e. the server sends SET,
+  instead of a pull.
+
+* The concentrator wants to be the initiator in phase 2
+  quick mode, so we have to support being a responder.
+
+Thus the changes are fairly intrusive - phase 1 is common
+but XAUTH/Mode-Config/phase 2 diverge.
+
+If people are interested, I can clean up what I've done
+and send patches.
+
+Matt
+----------------
+I've tried to connect to my corporate VPN, but the only message I got was:
+
+vpnc: xauth packet unsupported: ATTRIBUTES_NOT_SUPPORTED
+
+Problem was found in xauth_type. In my case request attribute "xauth_type" consisted 5 as value. There is quick
+fix for this issue below. But...
+
+In the chapter 6.2 of the draft-beaulieu-ike-xauth-02.txt we can read:
+
+"XAUTH-TYPE - The type of extended authentication requested whose
+   values are described in the next section.  This is an optional
+   attribute for the ISAKMP_CFG_REQUEST and ISAKMP_CFG_REPLY messages.
+   If the XAUTH-TYPE is not present, then it is assumed to be Generic.
+   The XAUTH-TYPE in a REPLY MUST be identical to the XAUTH-TYPE in the
+   REQUEST."
+
+So I think the better way is to send reply with the same xauth_type value as in request. But I can't test it
+because our server always use 5 and never 0.
+
+Thanks.
+----
+Volodymyr Buell
+
+------------ This ended up in the folowin patch
+        if (ap->af != isakmp_attr_16 || !(ap->u.attr_16 == 0 || ap->u.attr_16 == 5))
+          reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+                xauth_type_requested = ap->u.attr_16;
+----------------------
+
+This patch didn't work in my NortelVPN setup I mad a buildflag of until a proper fix can be made
+so please set NORTELVPN_XAUTHTYPE_AS_REQUEST flag to vpnc.c to get this
+
+#ifdef NORTELVPN_XAUTHTYPE_AS_REQUEST
+        if (ap->af != isakmp_attr_16 || !(ap->u.attr_16 == 0 || ap->u.attr_16 == 5))
+          reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+                xauth_type_requested = ap->u.attr_16;
+#else
+
+        if (ap->af != isakmp_attr_16 || ap->u.attr_16 != 0)
+          reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+#endif
+
+
+/Zingo Andersen
diff --git a/pcf2vpnc b/pcf2vpnc
new file mode 100755 (executable)
index 0000000..c93bb75
--- /dev/null
+++ b/pcf2vpnc
@@ -0,0 +1,117 @@
+#!/usr/bin/env perl
+# Stefan Tomanek <stefan@pico.ruhr.de>
+# updated by Wolfram Sang <ninja@the-dreams.de> on 21.10.06 and on 26.06.07
+##
+# pcf2vpnc <pcf file> [vpnc file]
+##
+# 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: pcf2vpnc 445 2009-09-12 17:17:49Z Antonio Borneo $
+
+use IO::File;
+use strict;
+use warnings;
+
+my %authmode = ( 1 => 'psk', 3 => 'cert', 5 => 'hybrid' );
+my $needs_cert = 0;
+my $no_decrypt = 0;
+if (system("cisco-decrypt", "q") == -1) {
+    $no_decrypt = 1;
+    print STDERR "cisco-decrypt not in search path,\n";
+    print STDERR "  adding passwords in obfuscated form\n";
+}
+
+sub readPCF($) {
+    my ($file) = @_;
+    my %config;
+    while (<$file>) {
+       # Filter unnecessary chars at beginning & end of line
+       s/^!*//; s/[\r ]*$//;
+       if (/^(.*?)=(.*?)$/) {
+           # We don't need emtpy config strings
+           next if ($2 eq "");
+           if ($1 eq "Host") {
+               $config{IPSec}{gateway} = $2;
+           } elsif ($1 eq "GroupName") {
+               $config{IPSec}{ID} = $2;
+           } elsif ($1 eq "GroupPwd") {
+               $config{IPSec}{secret} = $2;
+           } elsif ($1 eq "enc_GroupPwd") {
+               if ($no_decrypt) {
+                   $config{IPSec}{obfuscated} = "secret $2";
+               } else {
+                   $config{IPSec}{secret} = `cisco-decrypt $2`;
+               }
+           } elsif ($1 eq "AuthType") {
+               $config{IKE}{Authmode} = $authmode{$2};
+               if ($2 == 3 || $2 == 5) {
+                 $needs_cert = 1;
+               }
+           } elsif ($1 eq "DHGroup") {
+               $config{IKE}{DH} = "Group dh$2";
+           } elsif ($1 eq "Username") {
+               $config{Xauth}{username} = $2;
+           } elsif ($1 eq "UserPassword") {
+               $config{Xauth}{password} = $2;
+           } elsif ($1 eq "enc_UserPassword") {
+               if ($no_decrypt) {
+                   $config{Xauth}{obfuscated} = "password $2";
+               } else {
+                   $config{Xauth}{password} = `cisco-decrypt $2`;
+               }
+           } elsif ($1 eq "NTDomain") {
+               $config{Domain}{""} = $2;
+           }
+       }
+    }
+    return \%config;
+}
+
+sub writeVPNC($) {
+    my ($config) = @_;
+    my $text = "## generated by pcf2vpnc\n";
+    foreach my $section (keys %$config) {
+       foreach my $item (keys %{ $config->{$section} }) {
+           $text .= "$section ".($item ? "$item " : '').$config->{$section}{$item}."\n";
+       }
+    }
+    unless (defined $config->{Xauth}) {
+       $text .= "\n## To add your username and password,\n";
+       $text .= "## use the following lines:\n";
+       $text .= "# Xauth username <your username>\n";
+       $text .= "# Xauth password <your password>\n";
+    }
+    return $text;
+}
+
+if (defined $ARGV[0]) {
+    my $src = new IO::File($ARGV[0]) || die "Unable to open file ".$ARGV[0]."\n";
+    if (defined $ARGV[1]) {
+       my $dst = new IO::File($ARGV[1], "w") || die "Unable to open file ".$ARGV[1]."\n";
+       $dst->write( writeVPNC(readPCF($src)) ) || die "Unable to write to file ".$ARGV[1]."\n";
+       $dst->close() || die "Unable to close file ".$ARGV[1]."\n";
+       printf STDERR "vpnc config written to '%s' with permissions '%04o'.\n", $ARGV[1], (stat($ARGV[1]))[2];
+       print  STDERR "Please take care of permissions.\n";
+    } else {
+       print writeVPNC(readPCF($src));
+    }
+    $src->close() || die "Unable to close file ".$ARGV[0]."\n";
+    if ($needs_cert) {
+       print STDERR "\nDon't forget to copy the needed certificate(s).\n\n";
+    }
+} else {
+    print STDERR "$0 converts VPN-config files from pcf to vpnc-format.\n";
+    print STDERR "Usage: $0 <pcf file> [vpnc file]\n";
+}
diff --git a/pcf2vpnc.1 b/pcf2vpnc.1
new file mode 100644 (file)
index 0000000..1cc6136
--- /dev/null
@@ -0,0 +1,31 @@
+.TH "PCF2VPNC" "1" "June 2007" "pcf2vpnc " "vpnc"
+.SH "NAME"
+pcf2vpnc \- converts VPN\-config files from pcf to vpnc\-format
+.\"
+.\" $Id: pcf2vpnc.1 473 2011-11-13 00:48:06Z Antonio Borneo $
+.\"
+.SH "SYNOPSIS"
+.B pcf2vpnc
+\fI<pcf file> \fR[\fIvpnc file\fR]
+.SH "DESCRIPTION"
+This script accompanies \fBvpnc\fR. It attempts to
+convert *.pcf\-configuration files often spread with proprietary
+(read Cisco) VPN\-clients into vpnc\-configuration files,
+usually named \fB*.conf\fR.
+
+If [\fIvpnc file\fR] is not specified, the result will be
+printed to STDOUT. If specified, it will be written to that
+file. Please make sure that it has appropriate permissions as
+it may contain sensitive data!
+.SH "AUTHOR"
+pcf2vpnc was originally written by Stefan Tomanek. Updates and this man\-page were made by Wolfram Sang (ninja(at)the\-dreams.de).
+
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+.PP
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common\-licenses/GPL.
+.SH "SEE ALSO"
+.BR vpnc(8)
+.BR cisco-decrypt(8)
diff --git a/split_tunnel.txt b/split_tunnel.txt
new file mode 100644 (file)
index 0000000..58d9fb3
--- /dev/null
@@ -0,0 +1,270 @@
+$Id: split_tunnel.txt 398 2009-06-16 12:34:47Z Antonio Borneo $
+
+User defined "Split Tunnel" tutorial
+Antonio Borneo, borneo.antonio@gmail.com
+v0.1, February 28, 2009
+
+This tutorial targets creating your own split tunnel with vpnc. Split tunnel
+is configured on client side, independently form settings deployed by your VPN
+server.
+
+______________________________________________________________________________
+
+
+Table of Contents
+
+       1. DISCLAIMER
+       2. Status
+       3. Introduction
+       4. Security issues
+       5. DNS
+       6. Web Proxy
+       7. Extended connection script
+       8. Example
+       9. Changelog
+
+______________________________________________________________________________
+
+
+1. DISCLAIMER
+=============
+
+Misusing the information provided in this document, you can open a security
+hole in the network protected by VPN.
+Nor the authors of this document nor the developers of vpnc can be considered
+responsible for any damage or legal issue caused by using the information in
+this document.
+If you are not fully aware of all the possible consequences created by your
+actions, DON'T use any of the configuration below.
+
+
+2. Status
+=========
+
+Following information has been tested only with Linux host and Nortel server.
+Any support or suggestion to improve this tutorial and to include other hosts
+and servers is warmly welcome.
+
+
+3. Introduction
+===============
+
+A usual VPN configuration routes all the network connections through the VPN
+tunnel.
+Split tunnel is able to discriminate between IPs that have to be accessed
+through the VPN tunnel and IPs that have to be accessed directly.
+Practically, split tunnel lets your computer accessing the secure network
+through the VPN tunnel, while also accessing internet directly.
+
+Split tunnel is usually set and controlled by the VPN server configuration and
+deployed to VPN client.
+This tutorial explains how to use vpnc to set your own split tunnel on client
+side, bypassing server setting.
+Possible applications are:
+- fixing, on client side, a server misconfiguration;
+- enabling split tunnel when server doesn't offer it.
+
+Several users connect to VPN just to access their corporate internal mail
+server. When VPN is on, lack of split tunnel stops every connection not
+supported or allowed in the corporate network (e.g. video, voip, chat, ssh) or
+dramatically slows down connection to other internet resources.
+
+Thanks to split tunnel, you can still read mail form corporate server, while
+enjoying full internet experience.
+
+
+4. Security issues
+==================
+
+Split tunnel is usually not enabled in VPN servers, since it can open a
+security hole.
+In fact, in a split tunnel configuration, your computer can act as a bridge
+between internet and the protected network.
+Typical risky situations are:
+- your computer is not fully secured (e.g. infected by virus or, even worst,
+  part of a botnet);
+- your computer has active accounts that can be accessed remotely;
+- you use a pear-to-pear SW that builds a mesh network.
+For such reason, most VPN server administrator guide discourage split tunnel,
+and system administrators usually don't take risk, keeping it disabled.
+So, be aware that any split tunnel on client side is done "by you" "at your
+own risk!"
+My personal suggestion is to enable it ONLY in specific situation, and only
+for few dedicated servers in the protected network (e.g. mail server). List
+one by one each server you need to connect with, and never enable a whole
+subnet.
+
+
+5. DNS
+======
+
+Consider that your computer only manages "one" set of DNS servers.
+Usually corporate networks deploy their own DNS server, that only resolves
+corporate internal name space.
+In a split tunnel your computer cannot inquire either corporate and internet
+DNS, but only one of them.
+You have now to decide which DNS you have to use, while you can use the local
+static lookup table in /etc/hosts for the other case.
+
+Of course, you can setup a local DNS server, that inquires either corporate or
+internet DNS. Such arrangement is not covered by this tutorial.
+
+If you only want to use the corporate mail server (supposed having fix IP),
+you can resolve it with /etc/hosts, and use internet DNS for all other cases.
+
+
+6. Web Proxy
+============
+
+Some corporate network is organized with a proxy server to access external web
+sites that can also be used to access internal ones.
+In this case, you do not have to configure routes to every corporate internal
+website you want to access, but just route to the proxy server.
+You can then use the same proxy to access internet web sites, or you can use a
+browser proxy switch (e.g. FoxyProxy http://foxyproxy.mozdev.org/ for Firefox)
+to select the best routing.
+
+
+7. Extended connection script
+=============================
+
+Current vpnc just follows what VPN server configures.
+To setup your own split tunnel, there is no need to modify vpnc code, nor the
+connection script /etc/vpnc/vpnc-script.
+It is possible to create an extension of such script; practically a script
+that sets few variables and in turns calls /etc/vpnc/vpnc-script script.
+More details in example below.
+
+
+8. Example
+==========
+In the following example we will consider:
+- Linux host (maybe valid for other UNIXes);
+- Nortel server (maybe works as is also with Cisco);
+- split tunnel to access just few hosts inside the corporate network;
+- hosts in corporate network have fixed IP addresses;
+- DNS and all other routes to internet.
+
+
+8.1 Step 1
+----------
+
+List all the hosts you need to access in the corporate network.
+In the following example we will consider:
+- mail server, to read messages: pop3.mycom.com;
+- smtp server, to send messages out: smtp.mycom.com;
+- ldap server, to search mail accounts: ldap.mycom.com;
+- internet proxy, to access internal websites: proxy.mycom.com.
+Avoid a long list; keep security in mind and just map what you really need.
+
+
+8.2 Step 2
+----------
+
+Resolve IP address of all the names you listed in Step 1, and put them in your
+local file /etc/hosts. We suppose all of them are fixed IP.
+Sometimes two or more servers are mapped to the same IP. Practically it is the
+same server that implements multiple functions. In the example below, we
+suppose that pop3 and smtp services are on the same server.
+Example of /etc/hosts:
+       ______________________________________________________________________
+       127.0.0.1       localhost.localdomain localhost
+       ::1             localhost6.localdomain6 localhost6
+       10.0.0.130      pop3.mycom.com smtp.mycom.com
+       10.0.14.1       ldap.mycom.com
+       10.1.0.5        proxy.mycom.com
+       ______________________________________________________________________
+
+
+8.3 Step 3
+----------
+
+Create a copy of your working vpnc config file:
+#> cp /etc/vpnc/corp.conf /etc/vpnc/split.conf
+
+
+8.4 Step 4
+----------
+
+Edit the new file "split.conf" and add the following line:
+       Script /etc/vpnc/vpnc-script-corp-split
+It will force this new configuration to use a special script file.
+
+
+8.5 Step 5
+----------
+
+Create the file /etc/vpnc/vpnc-script-corp-split with following content
+       ______________________________________________________________________
+       #!/bin/sh
+
+       # Add one IP to the list of split tunnel
+       add_ip ()
+       {
+               export CISCO_SPLIT_INC_${CISCO_SPLIT_INC}_ADDR=$1
+               export CISCO_SPLIT_INC_${CISCO_SPLIT_INC}_MASK=255.255.255.255
+               export CISCO_SPLIT_INC_${CISCO_SPLIT_INC}_MASKLEN=32
+               export CISCO_SPLIT_INC=$(($CISCO_SPLIT_INC + 1))
+       }
+
+       # Initialize empty split tunnel list
+       export CISCO_SPLIT_INC=0
+
+       # Delete DNS info provided by VPN server to use internet DNS
+       # Comment following line to use DNS beyond VPN tunnel
+       unset INTERNAL_IP4_DNS
+
+       # List of IPs beyond VPN tunnel
+       add_ip 10.0.0.130       # pop3.mycom.com and smtp
+       add_ip 10.0.14.1        # ldap.mycom.com
+       add_ip 10.1.0.5         # proxy.mycom.com
+
+       # Execute default script
+       . /etc/vpnc/vpnc-script
+
+       # End of script
+       ______________________________________________________________________
+
+Parameter passed to "add_ip" is used, in /etc/vpnc/vpnc-script, to set routing
+table by running either "ip" or "route" command, depending on system
+configuration.
+While "route" accepts both host names and IP in the command line, "ip"
+strictly requires numeric IP.
+This is quite annoying, since would be easier using only host names in the
+script abobe, keeping numeric IP relations in /etc/hosts only.
+Eventually, could be possible improving the script above by resolving names
+before running /etc/vpnc/vpnc-script.
+The command "gethostip" could be used for name resolution. Does anybody knows
+if the command "gethostip" is present in every Linux distro?
+
+
+8.6 Step 6
+----------
+
+At last, provide the proper execution permission:
+#> chmod 755 /etc/vpnc/vpnc-script-corp-split
+
+That's all, folks!
+You can now run:
+#> vpnc split.conf
+
+Reading routing table, you can verify the split is active.
+#> route
+Kernel IP routing table
+Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
+proxy.mycom.com *               255.255.255.255 UH    0      0        0 tun0
+ldap.mycom.com  *               255.255.255.255 UH    0      0        0 tun0
+pop3.mycom.com  *               255.255.255.255 UH    0      0        0 tun0
+vpn.mycom.com   192.168.1.1     255.255.255.255 UGH   0      0        0 eth0
+192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
+10.2.0.0        *               255.255.255.0   U     0      0        0 tun0
+169.254.0.0     *               255.255.0.0     U     0      0        0 eth0
+default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
+
+
+9 Changelog
+===========
+
+2009-02-28     v0.1    Antonio Borneo <borneo.antonio@gmail.com>
+       * first version
+
diff --git a/supp.c b/supp.c
new file mode 100644 (file)
index 0000000..43cd9ea
--- /dev/null
+++ b/supp.c
@@ -0,0 +1,130 @@
+/* Algorithm support checks
+   Copyright (C) 2005 Maurice Massar
+   Reorganised 2006 by Dan Villiom Podlaski Christiansen
+
+   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: supp.c 444 2009-09-12 17:10:32Z Antonio Borneo $
+*/
+
+#include "supp.h"
+#include "math_group.h"
+#include "config.h"
+#include "isakmp.h"
+
+#include <gcrypt.h>
+#include <stdlib.h>
+
+const supported_algo_t supp_dh_group[] = {
+       {"nopfs", 0, 0, 0, 0},
+       {"dh1", OAKLEY_GRP_1, IKE_GROUP_MODP_768,  IKE_GROUP_MODP_768,  0},
+       {"dh2", OAKLEY_GRP_2, IKE_GROUP_MODP_1024, IKE_GROUP_MODP_1024, 0},
+       {"dh5", OAKLEY_GRP_5, IKE_GROUP_MODP_1536, IKE_GROUP_MODP_1536, 0},
+       /*{ "dh7", OAKLEY_GRP_7, IKE_GROUP_EC2N_163K, IKE_GROUP_EC2N_163K, 0 } note: code missing */
+       {NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t supp_hash[] = {
+       {"md5", GCRY_MD_MD5, IKE_HASH_MD5, IPSEC_AUTH_HMAC_MD5, 0},
+       {"sha1", GCRY_MD_SHA1, IKE_HASH_SHA, IPSEC_AUTH_HMAC_SHA, 0},
+       {NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t supp_crypt[] = {
+       {"null", GCRY_CIPHER_NONE, IKE_ENC_NO_CBC, ISAKMP_IPSEC_ESP_NULL, 0},
+       {"des", GCRY_CIPHER_DES, IKE_ENC_DES_CBC, ISAKMP_IPSEC_ESP_DES, 0},
+       {"3des", GCRY_CIPHER_3DES, IKE_ENC_3DES_CBC, ISAKMP_IPSEC_ESP_3DES, 0},
+       {"aes128", GCRY_CIPHER_AES128, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 128},
+       {"aes192", GCRY_CIPHER_AES192, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 192},
+       {"aes256", GCRY_CIPHER_AES256, IKE_ENC_AES_CBC, ISAKMP_IPSEC_ESP_AES, 256},
+       {NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t supp_auth[] = {
+       {"psk", 0, IKE_AUTH_PRESHARED, 0, 0},
+       {"psk+xauth", 0, IKE_AUTH_XAUTHInitPreShared, 0, 0},
+#if 0
+       {"cert(dsa)", 0, IKE_AUTH_RSA_SIG, 0, 0},
+       {"cert(rsasig)", 0, IKE_AUTH_DSS, 0, 0},
+       {"hybrid(dsa)", 0, IKE_AUTH_DSS, 0, 0},
+#endif /* 0 */
+       {"hybrid(rsa)", 0, IKE_AUTH_HybridInitRSA, 0, 0},
+       {NULL, 0, 0, 0, 0}
+};
+
+const supported_algo_t *get_algo(enum algo_group what, enum supp_algo_key key, int id,
+       const char *name, int keylen)
+{
+       const supported_algo_t *sa = NULL;
+       int i = 0, val = 0;
+       const char *valname = NULL;
+
+       switch (what) {
+       case SUPP_ALGO_DH_GROUP:
+               sa = supp_dh_group;
+               break;
+       case SUPP_ALGO_HASH:
+               sa = supp_hash;
+               break;
+       case SUPP_ALGO_CRYPT:
+               sa = supp_crypt;
+               break;
+       case SUPP_ALGO_AUTH:
+               sa = supp_auth;
+               break;
+       default:
+               abort();
+       }
+
+       for (i = 0; sa[i].name != NULL; i++) {
+               switch (key) {
+               case SUPP_ALGO_NAME:
+                       valname = sa[i].name;
+                       break;
+               case SUPP_ALGO_MY_ID:
+                       val = sa[i].my_id;
+                       break;
+               case SUPP_ALGO_IKE_SA:
+                       val = sa[i].ike_sa_id;
+                       break;
+               case SUPP_ALGO_IPSEC_SA:
+                       val = sa[i].ipsec_sa_id;
+                       break;
+               default:
+                       abort();
+               }
+               if ((key == SUPP_ALGO_NAME) ? !strcasecmp(name, valname) : (val == id))
+                       if (keylen == sa[i].keylen)
+                               return sa + i;
+       }
+
+       return NULL;
+}
+
+const supported_algo_t *get_dh_group_ike(void)
+{
+       return get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_NAME, 0, config[CONFIG_IKE_DH], 0);
+}
+const supported_algo_t *get_dh_group_ipsec(int server_setting)
+{
+       const char *pfs_setting = config[CONFIG_IPSEC_PFS];
+
+       if (!strcmp(config[CONFIG_IPSEC_PFS], "server")) {
+               /* treat server_setting == -1 (unknown) as 0 */
+               pfs_setting = (server_setting == 1) ? "dh2" : "nopfs";
+       }
+
+       return get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_NAME, 0, pfs_setting, 0);
+}
diff --git a/supp.h b/supp.h
new file mode 100644 (file)
index 0000000..639c7d4
--- /dev/null
+++ b/supp.h
@@ -0,0 +1,54 @@
+/* Algorithm support checks
+   Copyright (C) 2005 Maurice Massar
+   Reorganised 2006 by Dan Villiom Podlaski Christiansen
+
+   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: supp.h 377 2008-11-26 08:03:43Z Joerg Mayer $
+*/
+
+#ifndef __SUPP_H__
+#define __SUPP_H__
+
+enum supp_algo_key {
+       SUPP_ALGO_NAME,
+       SUPP_ALGO_MY_ID,
+       SUPP_ALGO_IKE_SA,
+       SUPP_ALGO_IPSEC_SA
+};
+
+enum algo_group {
+       SUPP_ALGO_DH_GROUP,
+       SUPP_ALGO_HASH,
+       SUPP_ALGO_CRYPT,
+       SUPP_ALGO_AUTH
+};
+
+typedef struct {
+       const char *name;
+       int my_id, ike_sa_id, ipsec_sa_id;
+       int keylen;
+} supported_algo_t;
+
+extern const supported_algo_t supp_dh_group[];
+extern const supported_algo_t supp_hash[];
+extern const supported_algo_t supp_crypt[];
+extern const supported_algo_t supp_auth[];
+
+extern const supported_algo_t *get_algo(enum algo_group what, enum supp_algo_key key, int id, const char *name, int keylen);
+extern const supported_algo_t *get_dh_group_ike(void);
+extern const supported_algo_t *get_dh_group_ipsec(int server_setting);
+
+#endif
diff --git a/sysdep.c b/sysdep.c
new file mode 100644 (file)
index 0000000..354308a
--- /dev/null
+++ b/sysdep.c
@@ -0,0 +1,828 @@
+/* IPSec VPN client compatible with Cisco equipment.
+    Copyright (C) 2007      Maurice Massar
+    Copyright (C) 2007      Paolo Zarpellon <paolo.zarpellon@gmail.com> (Cygwin support)
+
+    based on VTun - Virtual Tunnel over TCP/IP network.
+    Copyright (C) 1998-2000  Maxim Krasnyansky <max_mk@yahoo.com>
+    VTun has been derived from VPPP package by Maxim Krasnyansky.
+
+    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.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#ifdef __sun__
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <signal.h>
+#include <stropts.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#endif
+
+#if defined(__CYGWIN__)
+#include <io.h>
+#include <w32api/windef.h>
+#include <w32api/winbase.h>
+#include <w32api/winnt.h>
+#include <w32api/winioctl.h>
+#include <w32api/iphlpapi.h>
+#include <w32api/iptypes.h>
+#include <w32api/winreg.h>
+#include <sys/cygwin.h>
+#endif
+
+#if defined(__DragonFly__)
+#include <net/tun/if_tun.h>
+#elif defined(__linux__)
+#include <linux/if_tun.h>
+#elif defined(__APPLE__)
+/* no header for tun */
+#elif defined(__CYGWIN__)
+#include "tap-win32.h"
+#else
+#include <net/if_tun.h>
+#endif
+
+#include "sysdep.h"
+
+#if !defined(HAVE_VASPRINTF) || !defined(HAVE_ASPRINTF) || !defined(HAVE_ERROR)
+#include <stdarg.h>
+#endif
+
+#if defined(__sun__)
+extern char **environ;
+static int ip_fd = -1, muxid;
+#endif
+
+#if defined(__CYGWIN__)
+/*
+ * Overlapped structures for asynchronous read and write
+ */
+static OVERLAPPED overlap_read, overlap_write;
+
+typedef enum {
+       SEARCH_IF_GUID_FROM_NAME,
+       SEARCH_IF_NAME_FROM_GUID
+} search_if_en;
+#endif
+
+/*
+ * Allocate TUN/TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+#if defined(__sun__)
+int tun_open(char *dev, enum if_mode_enum mode)
+{
+       int tun_fd, if_fd, ppa = -1;
+       struct ifreq ifr;
+       char *ptr;
+
+       if (*dev) {
+               ptr = dev;
+               while (*ptr && !isdigit((int)*ptr))
+                       ptr++;
+               ppa = atoi(ptr);
+       }
+
+       if ((ip_fd = open("/dev/ip", O_RDWR, 0)) < 0) {
+               logmsg(LOG_ERR, "Can't open /dev/ip");
+               return -1;
+       }
+
+       if ((tun_fd = open(((mode == IF_MODE_TUN) ? "/dev/tun" : "/dev/tap"), O_RDWR, 0)) < 0) {
+               logmsg(LOG_ERR, "Can't open /dev/tun");
+               return -1;
+       }
+
+       /* Assign a new PPA and get its unit number. */
+       if ((ppa = ioctl(tun_fd, TUNNEWPPA, ppa)) < 0) {
+               logmsg(LOG_ERR, "Can't assign new interface");
+               return -1;
+       }
+
+       if ((if_fd = open(((mode == IF_MODE_TUN) ? "/dev/tun" : "/dev/tap"), O_RDWR, 0)) < 0) {
+               logmsg(LOG_ERR, "Can't open /dev/tun (2)");
+               return -1;
+       }
+       if (ioctl(if_fd, I_PUSH, "ip") < 0) {
+               logmsg(LOG_ERR, "Can't push IP module");
+               return -1;
+       }
+
+       /* Assign ppa according to the unit number returned by tun device */
+       if (ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0 && errno != EEXIST) {
+               logmsg(LOG_ERR, "Can't set PPA %d", ppa);
+               return -1;
+       }
+       if ((muxid = ioctl(ip_fd, I_PLINK, if_fd)) < 0) {
+               logmsg(LOG_ERR, "Can't link TUN device to IP");
+               return -1;
+       }
+       close(if_fd);
+
+       snprintf(dev, IFNAMSIZ, "%s%d", ((mode == IF_MODE_TUN) ? "tun" : "tap"), ppa);
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev);
+       ifr.ifr_ip_muxid = muxid;
+
+       if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
+               ioctl(ip_fd, I_PUNLINK, muxid);
+               logmsg(LOG_ERR, "Can't set multiplexor id");
+               return -1;
+       }
+
+       return tun_fd;
+}
+#elif defined(__CYGWIN__)
+/*
+ * Get interface guid/name from registry
+ */
+static char *search_if(char *value, char *key, search_if_en type)
+{
+       int i = 0;
+       LONG status;
+       DWORD len;
+       HKEY net_conn_key;
+       BOOL found = FALSE;
+       char guid[256];
+       char ifname[256];
+       char conn_string[512];
+       HKEY conn_key;
+       DWORD value_type;
+
+       if (!value || !key) {
+               return NULL;
+       }
+
+       status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+               NETWORK_CONNECTIONS_KEY,
+               0,
+               KEY_READ,
+               &net_conn_key);
+
+       if (status != ERROR_SUCCESS) {
+               printf("Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY);
+               return NULL;
+       }
+
+       while (!found) {
+               len = sizeof(guid);
+               status = RegEnumKeyEx(net_conn_key, i++, guid, &len,
+                       NULL, NULL, NULL, NULL);
+               if (status == ERROR_NO_MORE_ITEMS) {
+                       break;
+               } else if (status != ERROR_SUCCESS) {
+                       continue;
+               }
+               snprintf(conn_string, sizeof(conn_string),
+                       "%s\\%s\\Connection",
+                       NETWORK_CONNECTIONS_KEY, guid);
+               status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                       conn_string,
+                       0,
+                       KEY_READ,
+                       &conn_key);
+               if (status != ERROR_SUCCESS) {
+                       continue;
+               }
+               len = sizeof(ifname);
+               status = RegQueryValueEx(conn_key, "Name", NULL,
+                       &value_type, ifname, &len);
+               if (status != ERROR_SUCCESS || value_type != REG_SZ) {
+                       RegCloseKey(conn_key);
+                       continue;
+               }
+
+               switch (type) {
+               case SEARCH_IF_GUID_FROM_NAME:
+                       if (!strcmp(key, ifname)) {
+                               strcpy(value, guid);
+                               found = TRUE;
+                       }
+                       break;
+               case SEARCH_IF_NAME_FROM_GUID:
+                       if (!strcmp(key, guid)) {
+                               strcpy(value, ifname);
+                               found = TRUE;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               RegCloseKey(conn_key);
+       }
+       RegCloseKey(net_conn_key);
+
+       if (found) {
+               return value;
+       }
+
+       return NULL;
+}
+
+/*
+ * Open the TUN/TAP device with the provided guid
+ */
+static int open_tun_device (char *guid, char *dev, enum if_mode_enum mode)
+{
+       HANDLE handle;
+       ULONG len, status, info[3];
+       char device_path[512];
+
+       printf("Device: %s\n", dev);
+
+       if (mode == IF_MODE_TUN) {
+               printf("TUN mode is not supported\n");
+               return -1;
+       }
+
+       /*
+        * Let's try to open Windows TAP-Win32 adapter
+        */
+       snprintf(device_path, sizeof(device_path), "%s%s%s",
+               USERMODEDEVICEDIR, guid, TAPSUFFIX);
+
+       handle = CreateFile(device_path,
+               GENERIC_READ | GENERIC_WRITE,
+               0, /* Don't let other processes share or open
+               the resource until the handle's been closed */
+               0,
+               OPEN_EXISTING,
+               FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+               0);
+
+       if (handle == INVALID_HANDLE_VALUE) {
+               return -1;
+       }
+
+       /*
+        * get driver version info
+        */
+       memset(info, 0, sizeof(info));
+       if (DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
+               &info, sizeof(info),
+               &info, sizeof(info), &len, NULL)) {
+               printf("TAP-Win32 Driver Version %d.%d %s\n",
+               (int) info[0],
+               (int) info[1],
+               (info[2] ? "(DEBUG)" : ""));
+       }
+
+       /*
+        * Set driver media status to 'connected'
+        */
+       status = TRUE;
+       if (!DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
+               &status, sizeof(status),
+               &status, sizeof(status), &len, NULL)) {
+               printf("WARNING: The TAP-Win32 driver rejected a "
+               "TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.\n");
+       }
+
+       /*
+        * Initialize overlapped structures
+        */
+       overlap_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       overlap_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (!overlap_read.hEvent || !overlap_write.hEvent) {
+               return -1;
+       }
+
+       /*
+        * Return fd
+        */
+       return cygwin_attach_handle_to_fd(NULL, -1, handle, 1, GENERIC_READ | GENERIC_WRITE);
+}
+
+/*
+ * Allocate TUN device, returns opened fd.
+ * Stores dev name in the first arg (must be large enough).
+ */
+int tun_open (char *dev, enum if_mode_enum mode)
+{
+       int fd = -1;
+       HKEY unit_key;
+       char guid[256];
+       char comp_id[256];
+       char enum_name[256];
+       char unit_string[512];
+       BOOL found = FALSE;
+       HKEY adapter_key;
+       DWORD value_type;
+       LONG status;
+       DWORD len;
+
+       if (!dev) {
+               return -1;
+       }
+
+       /*
+        * Device name has been provided. Open such device.
+        */
+       if (*dev != '\0') {
+               if (!search_if(guid, dev, SEARCH_IF_GUID_FROM_NAME)) {
+                       return -1;
+               }
+               return open_tun_device(guid, dev, mode);
+       }
+
+       /*
+        * Device name has non been specified. Look for one available!
+        */
+       int i = 0;
+       status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+               ADAPTER_KEY,
+               0,
+               KEY_READ,
+               &adapter_key);
+       if (status != ERROR_SUCCESS) {
+               printf("Error opening registry key: %s", ADAPTER_KEY);
+               return -1;
+       }
+
+       while (!found) {
+               len = sizeof(enum_name);
+               status = RegEnumKeyEx(adapter_key, i++,
+                       enum_name, &len,
+                       NULL, NULL, NULL, NULL);
+               if (status == ERROR_NO_MORE_ITEMS) {
+                       break;
+               } else if (status != ERROR_SUCCESS) {
+                       continue;
+               }
+               snprintf(unit_string, sizeof(unit_string), "%s\\%s",
+                       ADAPTER_KEY, enum_name);
+               status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                       unit_string,
+                       0,
+                       KEY_READ,
+                       &unit_key);
+               if (status != ERROR_SUCCESS) {
+                       continue;
+               }
+               len = sizeof(comp_id);
+               status = RegQueryValueEx(unit_key,
+                       "ComponentId", NULL,
+                       &value_type, comp_id, &len);
+               if (status != ERROR_SUCCESS || value_type != REG_SZ) {
+                       RegCloseKey(unit_key);
+                       continue;
+               }
+               len = sizeof(guid);
+               status = RegQueryValueEx(unit_key,
+                       "NetCfgInstanceId", NULL,
+                       &value_type, guid, &len);
+               if (status != ERROR_SUCCESS || value_type != REG_SZ) {
+                       RegCloseKey(unit_key);
+                       continue;
+               }
+
+               int j = 0;
+               while (TAP_COMPONENT_ID[j]) {
+                       if (!strcmp(comp_id, TAP_COMPONENT_ID[j])) {
+                               break;
+                       }
+                       j++;
+               }
+               if (!TAP_COMPONENT_ID[j]) {
+                       RegCloseKey(unit_key);
+                       continue;
+               }
+
+               /*
+                * Let's try to open this device
+                */
+               search_if(dev, guid, SEARCH_IF_NAME_FROM_GUID);
+               fd = open_tun_device(guid, dev, mode);
+               if (fd != -1) {
+                       found = TRUE;
+               }
+
+               RegCloseKey(unit_key);
+       }
+       RegCloseKey(adapter_key);
+
+       return fd;
+}
+#elif defined(IFF_TUN)
+int tun_open(char *dev, enum if_mode_enum mode)
+{
+       struct ifreq ifr;
+       int fd, err;
+
+       if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
+               error(0, errno,
+                       "can't open /dev/net/tun, check that it is either device char 10 200 or (with DevFS) a symlink to ../misc/net/tun (not misc/net/tun)");
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = ((mode == IF_MODE_TUN) ? IFF_TUN : IFF_TAP) | IFF_NO_PI;
+       if (*dev)
+               strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+       if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
+               close(fd);
+               return err;
+       }
+       strcpy(dev, ifr.ifr_name);
+       return fd;
+}
+#else
+int tun_open(char *dev, enum if_mode_enum mode)
+{
+       char tunname[14];
+       int i, fd;
+
+       if (*dev) {
+               if (strncmp(dev, ((mode == IF_MODE_TUN) ? "tun" : "tap"), 3))
+                       error(1, 0,
+                               "error: arbitrary naming tunnel interface is not supported in this version\n");
+               snprintf(tunname, sizeof(tunname), "/dev/%s", dev);
+               return open(tunname, O_RDWR);
+       }
+
+       for (i = 0; i < 255; i++) {
+               snprintf(tunname, sizeof(tunname), "/dev/%s%d",
+                       ((mode == IF_MODE_TUN) ? "tun" : "tap"), i);
+               /* Open device */
+               if ((fd = open(tunname, O_RDWR)) > 0) {
+                       snprintf(dev, IFNAMSIZ, "%s%d",
+                               ((mode == IF_MODE_TUN) ? "tun" : "tap"), i);
+                       return fd;
+               }
+       }
+       return -1;
+}
+#endif /* New driver support */
+
+/*
+ * Close TUN device.
+ */
+#if defined(__sun__)
+int tun_close(int fd, char *dev)
+{
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev);
+       if (ioctl(ip_fd, SIOCGIFFLAGS, &ifr) < 0) {
+               logmsg(LOG_ERR, "Can't get iface flags");
+               return 0;
+       }
+
+       if (ioctl(ip_fd, I_PUNLINK, muxid) < 0) {
+               logmsg(LOG_ERR, "Can't unlink interface");
+               return 0;
+       }
+
+       close(ip_fd);
+       ip_fd = -1;
+       close(fd);
+       return 0;
+}
+#elif defined(__CYGWIN__)
+int tun_close(int fd, char *dev)
+{
+       dev = NULL; /* unused */
+       return CloseHandle((HANDLE) get_osfhandle(fd));
+}
+#else
+int tun_close(int fd, char *dev)
+{
+       dev = NULL; /*unused */
+       return close(fd);
+}
+#endif
+
+
+#if defined(__sun__)
+int tun_write(int fd, unsigned char *buf, int len)
+{
+       struct strbuf sbuf;
+       sbuf.len = len;
+       sbuf.buf = buf;
+       return putmsg(fd, NULL, &sbuf, 0) >= 0 ? sbuf.len : -1;
+}
+
+int tun_read(int fd, unsigned char *buf, int len)
+{
+       struct strbuf sbuf;
+       int f = 0;
+
+       sbuf.maxlen = len;
+       sbuf.buf = buf;
+       return getmsg(fd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1;
+}
+#elif defined(__CYGWIN__)
+int tun_read(int fd, unsigned char *buf, int len)
+{
+       DWORD read_size;
+
+       ResetEvent(overlap_read.hEvent);
+       if (ReadFile((HANDLE) get_osfhandle(fd), buf, len, &read_size, &overlap_read)) {
+               return read_size;
+       }
+       switch (GetLastError()) {
+       case ERROR_IO_PENDING:
+               WaitForSingleObject(overlap_read.hEvent, INFINITE);
+               GetOverlappedResult((HANDLE) get_osfhandle(fd), &overlap_read, &read_size, FALSE);
+               return read_size;
+               break;
+       default:
+               break;
+       }
+
+       return -1;
+}
+
+int tun_write(int fd, unsigned char *buf, int len)
+{
+       DWORD write_size;
+
+       ResetEvent(overlap_write.hEvent);
+       if (WriteFile((HANDLE) get_osfhandle(fd),
+               buf,
+               len,
+               &write_size,
+               &overlap_write)) {
+               return write_size;
+       }
+       switch (GetLastError()) {
+       case ERROR_IO_PENDING:
+               WaitForSingleObject(overlap_write.hEvent, INFINITE);
+               GetOverlappedResult((HANDLE) get_osfhandle(fd), &overlap_write,
+                       &write_size, FALSE);
+               return write_size;
+               break;
+       default:
+               break;
+       }
+
+       return -1;
+}
+#elif defined(NEW_TUN)
+#define MAX_MRU 2048
+struct tun_data {
+       union {
+               uint32_t family;
+               uint32_t timeout;
+       } header;
+       u_char data[MAX_MRU];
+};
+
+/* Read/write frames from TUN device */
+int tun_write(int fd, unsigned char *buf, int len)
+{
+       char *data;
+       struct tun_data tun;
+
+       if (len > (int)sizeof(tun.data))
+               return -1;
+
+       memcpy(tun.data, buf, len);
+       tun.header.family = htonl(AF_INET);
+       len += (sizeof(tun) - sizeof(tun.data));
+       data = (char *)&tun;
+
+       return write(fd, data, len) - (sizeof(tun) - sizeof(tun.data));
+}
+
+int tun_read(int fd, unsigned char *buf, int len)
+{
+       struct tun_data tun;
+       char *data;
+       size_t sz;
+       int pack;
+
+       data = (char *)&tun;
+       sz = sizeof(tun);
+       pack = read(fd, data, sz);
+       if (pack == -1)
+               return -1;
+
+       pack -= sz - sizeof(tun.data);
+       if (pack > len)
+               pack = len; /* truncate packet */
+
+       memcpy(buf, tun.data, pack);
+
+       return pack;
+}
+
+#else
+
+int tun_write(int fd, unsigned char *buf, int len)
+{
+       return write(fd, buf, len);
+}
+
+int tun_read(int fd, unsigned char *buf, int len)
+{
+       return read(fd, buf, len);
+}
+
+#endif
+
+/*
+ * Get HW addr
+ */
+int tun_get_hwaddr(int fd, char *dev, uint8_t *hwaddr)
+{
+#if defined(__CYGWIN__)
+       ULONG len;
+
+       dev = NULL; /* unused */
+       if (!DeviceIoControl((HANDLE) get_osfhandle(fd), TAP_IOCTL_GET_MAC,
+               hwaddr, ETH_ALEN, hwaddr, ETH_ALEN, &len, NULL)) {
+               printf("Cannot get HW address\n");
+               return -1;
+       }
+
+       return 0;
+#elif defined(SIOCGIFHWADDR)
+       struct ifreq ifr;
+
+       /* Use a new socket fd! */
+       if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(struct ifreq));
+       strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+       if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+               return -1;
+       }
+
+       memcpy(hwaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+       return 0;
+#else
+       /* todo: implement using SIOCGLIFADDR */
+       fd = 0;
+       dev = 0;
+       hwaddr = 0;
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+/***********************************************************************/
+/* other support functions */
+
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **strp, const char *fmt, va_list ap)
+{
+       int ret;
+       char *strbuf;
+
+       ret = vsnprintf(NULL, 0, fmt, ap);
+       strbuf = (char *)malloc(ret + 1);
+       if (strbuf == NULL) {
+               errno = ENOMEM;
+               ret = -1;
+       }
+       vsnprintf(strbuf, ret + 1, fmt, ap);
+       *strp = strbuf;
+       return ret;
+}
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **strp, const char *fmt, ...)
+{
+       int ret;
+       va_list ap;
+
+       va_start(ap, fmt);
+       ret = vasprintf(strp, fmt, ap);
+       va_end(ap);
+
+       return ret;
+}
+#endif
+
+#ifndef HAVE_ERROR
+void error(int status, int errornum, const char *fmt, ...)
+{
+       char *buf2;
+       va_list ap;
+
+       va_start(ap, fmt);
+       vasprintf(&buf2, fmt, ap);
+       va_end(ap);
+       fprintf(stderr, "%s", buf2);
+       if (errornum)
+               fprintf(stderr, ": %s\n", strerror(errornum));
+       else
+               fprintf(stderr, "\n");
+       free(buf2);
+
+       if (status)
+               exit(status);
+}
+#endif
+
+#ifndef HAVE_GETLINE
+int getline(char **line, size_t * length, FILE * stream)
+{
+       size_t len;
+#ifdef HAVE_FGETLN
+       char *tmpline;
+
+       tmpline = fgetln(stream, &len);
+#else
+       char tmpline[512];
+
+       fgets(tmpline, sizeof(tmpline), stream);
+       len = strlen(tmpline);
+#endif
+       if (feof(stream))
+               return -1;
+       if (*line == NULL) {
+               *line = malloc(len + 1);
+               *length = len + 1;
+       }
+       if (*length < len + 1) {
+               *line = realloc(*line, len + 1);
+               *length = len + 1;
+       }
+       if (*line == NULL)
+               return -1;
+       memcpy(*line, tmpline, len);
+       (*line)[len] = '\0';
+       return len;
+}
+#endif
+
+#ifndef HAVE_UNSETENV
+int unsetenv(const char *name)
+{
+       int i, len;
+
+       len = strlen(name);
+       for (i = 0; environ[i]; i++)
+               if (!strncmp(name, environ[i], len))
+                       if (environ[i][len] == '=')
+                               break;
+
+       for (; environ[i] && environ[i + 1]; i++)
+               environ[i] = environ[i + 1];
+
+       return 0;
+}
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite)
+{
+       int ret;
+       char *newenv;
+
+       if (overwrite == 0)
+               if (getenv(name) != NULL)
+                       return 0;
+
+       newenv = malloc(strlen(name) + 1 + strlen(value) + 1);
+       if (newenv == NULL)
+               return -1;
+
+       *newenv = '\0';
+       strcat(newenv, name);
+       strcat(newenv, "=");
+       strcat(newenv, value);
+
+       ret = putenv(newenv);
+       if (ret == -1)
+               free(newenv);
+
+       return ret;
+}
+#endif
diff --git a/sysdep.h b/sysdep.h
new file mode 100644 (file)
index 0000000..a5cb7e3
--- /dev/null
+++ b/sysdep.h
@@ -0,0 +1,253 @@
+#ifndef __SYSDEP_H__
+#define __SYSDEP_H__
+
+/*
+ * Different systems define different macros.
+ * For vpnc, this list should be used as
+ * reference:
+ *
+ * __linux__
+ * __NetBSD__
+ * __OpenBSD__
+ * __FreeBSD__
+ * __DragonFly__
+ * __APPLE__      Darwin / MacOS X
+ * __sun__        SunOS / Solaris
+ * __CYGWIN__
+ * __SKYOS__
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#if !defined(__CYGWIN__)
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include "config.h"
+
+int tun_open(char *dev, enum if_mode_enum mode);
+int tun_close(int fd, char *dev);
+int tun_write(int fd, unsigned char *buf, int len);
+int tun_read(int fd, unsigned char *buf, int len);
+int tun_get_hwaddr(int fd, char *dev, uint8_t *hwaddr);
+
+/***************************************************************************/
+#if defined(__linux__) || defined(__GLIBC__)
+#include <error.h>
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_ERROR     1
+#define HAVE_GETLINE   1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__NetBSD__)
+#define HAVE_SA_LEN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__OpenBSD__)
+#define HAVE_SA_LEN 1
+#define NEED_IPLEN_FIX 1
+#define NEW_TUN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__FreeBSD_kernel__)
+#define HAVE_SA_LEN 1
+#endif
+
+/***************************************************************************/
+#if defined(__FreeBSD__)
+#define HAVE_SA_LEN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__DragonFly__)
+#define HAVE_SA_LEN 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#endif
+
+/***************************************************************************/
+#if defined(__APPLE__)
+#define HAVE_SA_LEN 1
+#define NEED_IPLEN_FIX 1
+
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) >= 1070
+#define HAVE_GETLINE 1
+#endif
+#endif
+
+/***************************************************************************/
+#if defined(__sun__)
+#define NEED_IPLEN_FIX 1
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+
+#define getpass(prompt) getpassphrase(prompt)
+
+/* where is this defined? */
+#include <sys/socket.h>
+const char *inet_ntop(int af, const void *src, char *dst, size_t cnt);
+#endif
+/***************************************************************************/
+#if defined (__SKYOS__)
+#define HAVE_UNSETENV  1
+
+#ifndef IPPROTO_ENCAP
+#define IPPROTO_ENCAP 4
+#endif
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#endif
+/***************************************************************************/
+#if defined (__CYGWIN__)
+#define HAVE_VASPRINTF 1
+#define HAVE_ASPRINTF  1
+#define HAVE_GETLINE   1
+#define HAVE_FGETLN    1
+#define HAVE_UNSETENV  1
+#define HAVE_SETENV    1
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+
+#ifndef IPPROTO_ENCAP
+#define IPPROTO_ENCAP 4
+#endif
+
+#ifdef IFNAMSIZ
+#undef IFNAMSIZ
+#endif
+#define IFNAMSIZ 256
+
+/*
+ * At the moment the Cygwin environment does not have header files
+ * for raw ethernet access, hence we need to define here what
+ * is usually found in net/ethernet.h and netinet/if_ether.h
+ */
+
+#define ETH_ALEN 6
+
+/* Ethernet header */
+struct ether_header
+{
+       unsigned char ether_dhost[ETH_ALEN]; /* destination eth addr */
+       unsigned char ether_shost[ETH_ALEN]; /* source ether addr    */
+       unsigned short ether_type;           /* packet type ID field */
+} __attribute__ ((__packed__));
+
+#define ETHERTYPE_IP  0x0800 /* IP  */
+#define ETHERTYPE_ARP 0x0806 /* ARP */
+
+/* Common ARP header */
+struct arphdr {
+       unsigned short ar_hrd; /* format of hardware address   */
+       unsigned short ar_pro; /* format of protocol address   */
+       unsigned char  ar_hln; /* length of hardware address   */
+       unsigned char  ar_pln; /* length of protocol address   */
+       unsigned short ar_op;  /* ARP opcode (command)         */
+};
+
+/* Ethernet ARP header */
+struct ether_arp {
+       struct arphdr ea_hdr;            /* fixed-size header */
+       unsigned char arp_sha[ETH_ALEN]; /* sender hardware address */
+       unsigned char arp_spa[4];        /* sender protocol address */
+       unsigned char arp_tha[ETH_ALEN]; /* target hardware address */
+       unsigned char arp_tpa[4];        /* target protocol address */
+};
+#define arp_hrd ea_hdr.ar_hrd
+#define arp_pro ea_hdr.ar_pro
+#define arp_hln ea_hdr.ar_hln
+#define arp_pln ea_hdr.ar_pln
+#define arp_op  ea_hdr.ar_op
+
+#define ARPHRD_ETHER 1 /* Ethernet */
+
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY   2 /* ARP reply   */
+
+#endif
+/***************************************************************************/
+
+
+#ifndef IPDEFTTL
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#endif
+
+#ifndef IPPROTO_IPIP
+#define IPPROTO_IPIP IPPROTO_ENCAP
+#endif
+
+#ifndef ETH_HLEN
+#define ETH_HLEN (sizeof(struct ether_header))
+#endif
+
+#ifndef ETH_ALEN
+#define ETH_ALEN (sizeof(struct ether_addr))
+#endif
+
+#ifndef HAVE_ERROR
+extern void error(int fd, int errorno, const char *fmt, ...);
+#endif
+#ifndef HAVE_GETLINE
+extern int getline(char **line, size_t * length, FILE * stream);
+#endif
+#ifndef HAVE_VASPRINTF
+#include <stdarg.h>
+extern int vasprintf(char **strp, const char *fmt, va_list ap);
+#endif
+#ifndef HAVE_ASPRINTF
+extern int asprintf(char **strp, const char *fmt, ...);
+#endif
+#ifndef HAVE_SETENV
+extern int setenv(const char *name, const char *value, int overwrite);
+#endif
+#ifndef HAVE_UNSETENV
+extern int unsetenv(const char *name);
+#endif
+
+
+#endif
diff --git a/tap-win32.h b/tap-win32.h
new file mode 100644 (file)
index 0000000..7711762
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  This file has been borrowed from the Win32 OpenVPN Tap driver
+ *  (common.h is the original file name).
+ *
+ *  TAP-Win32 -- A kernel driver to provide virtual tap device functionality
+ *               on Windows.  Originally derived from the CIPE-Win32
+ *               project by Damion K. Wilson, with extensive modifications by
+ *               James Yonan.
+ *
+ *  All source code which derives from the CIPE-Win32 project is
+ *  Copyright (C) Damion K. Wilson, 2003, and is released under the
+ *  GPL version 2 (see below).
+ *
+ *  All other source code is Copyright (C) 2002-2005 OpenVPN Solutions LLC,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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 (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  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.
+   =============================================== */
+
+/* =============
+    TAP IOCTLs
+   ============= */
+
+#define TAP_CONTROL_CODE(request,method) \
+  CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+/* 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)
+#define TAP_IOCTL_GET_MTU               TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO              TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS      TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ      TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#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 */
+
+/* obsoletes TAP_IOCTL_CONFIG_POINT_TO_POINT */
+#define TAP_IOCTL_CONFIG_TUN            TAP_CONTROL_CODE (10, METHOD_BUFFERED)
+
+/* =================
+    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
+   ====================== */
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define SYSDEVICEDIR      "\\Device\\"
+#define USERDEVICEDIR     "\\DosDevices\\Global\\"
+#define TAPSUFFIX         ".tap"
+
+/* =========================================================
+    TAP_COMPONENT_ID -- These strings defines the TAP driver
+    types -- different component IDs can reside in the system
+    simultaneously.
+   ========================================================= */
+
+static const char* TAP_COMPONENT_ID[] = { "tap0901", "tap0801", NULL };
diff --git a/test-crypto.c b/test-crypto.c
new file mode 100644 (file)
index 0000000..bba0599
--- /dev/null
@@ -0,0 +1,133 @@
+/* IPSec VPN client compatible with Cisco equipment.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "crypto.h"
+
+int main(int argc, char *argv[])
+{
+       crypto_ctx *cctx;
+       crypto_error *error = NULL;
+       int i;
+       unsigned char *data;
+       size_t size = 0;
+       const unsigned char sig_data[] = {
+   0x30, 0x82, 0x04, 0xb5, 0x30, 0x82, 0x03, 0x9d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02,
+   0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30,
+   0x81, 0x9f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+   0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x09, 0x42, 0x65, 0x72, 0x6b, 0x73, 0x68,
+
+   0x69, 0x72, 0x65, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x4e, 0x65,
+   0x77, 0x62, 0x75, 0x72, 0x79, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+   0x4d, 0x79, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x11,
+   0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x08, 0x54, 0x68, 0x65, 0x20, 0x55, 0x6e, 0x69,
+
+   0x74, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x74, 0x65, 0x73, 0x74,
+   0x2e, 0x73, 0x6f, 0x6d, 0x65, 0x77, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x21,
+   0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x74,
+   0x65, 0x73, 0x74, 0x40, 0x73, 0x6f, 0x6d, 0x65, 0x77, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x6f, 0x72,
+
+   0x67, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x34, 0x32, 0x38, 0x30, 0x32, 0x35, 0x30, 0x35,
+   0x32, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x30, 0x34, 0x32, 0x36, 0x30, 0x32, 0x35, 0x30, 0x35, 0x32,
+   0x5a, 0x30, 0x81, 0x8f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+   0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x09, 0x42, 0x65, 0x72, 0x6b,
+               };
+       const unsigned char dec_data[] = {
+   0x7c, 0x2a, 0xe4, 0x60, 0x10, 0x9f, 0xab, 0xd6, 0x76, 0x7b, 0x9d, 0x16, 0xbb, 0xd3, 0x16, 0xa3,
+   0x61, 0x50, 0x56, 0x13, 0xe4, 0x61, 0x0e, 0x90, 0x71, 0x5c, 0x47, 0xae, 0x4a, 0xc2, 0x89, 0xf8,
+   0x47, 0x61, 0x4c, 0x3f, 0xd6, 0x11, 0x97, 0xb7, 0x0d, 0x84, 0x86, 0xdd, 0xe9, 0x6d, 0x3e, 0x89,
+   0xe0, 0x4f, 0x7a, 0x95, 0x3f, 0x6e, 0xe4, 0xcd, 0xb2, 0x80, 0x3e, 0x19, 0x3e, 0x97, 0x7c, 0xdf,
+   0xd5, 0xff, 0xcb, 0x90, 0xfb, 0x71, 0x9c, 0xef, 0xa1, 0xf6, 0x8c, 0x36, 0xb3, 0x1f, 0x63, 0x7f,
+   0x32, 0xf5, 0x00, 0x12, 0x5e, 0x13, 0x84, 0x88, 0xe3, 0x13, 0x1c, 0x11, 0x2d, 0x9a, 0xd7, 0xec,
+   0x51, 0x94, 0x20, 0x6e, 0x8f, 0x69, 0xdf, 0x07, 0xe9, 0x46, 0x3b, 0xd9, 0x1c, 0x0a, 0xc0, 0x60,
+   0x90, 0x3a, 0x9a, 0x18, 0xa8, 0x19, 0xc6, 0x78, 0xc9, 0xf3, 0x1a, 0xbb, 0xca, 0xa8, 0xb5, 0x05,
+   0x6b, 0xa8, 0xfb, 0xeb, 0xdd, 0x19, 0x56, 0xc4, 0xfe, 0x7c, 0x84, 0xb1, 0xfd, 0x92, 0xbd, 0xe2,
+   0xb2, 0x94, 0x57, 0x3d, 0x03, 0x0a, 0xf1, 0xee, 0xca, 0xec, 0x8a, 0x0f, 0xb6, 0x23, 0x0b, 0x44,
+   0x14, 0x0d, 0xe0, 0xb1, 0x68, 0x38, 0x56, 0x7c, 0x66, 0x60, 0x8f, 0x54, 0x8b, 0x5c, 0x80, 0x37,
+   0x94, 0x27, 0x89, 0x47, 0x2c, 0x24, 0x45, 0x6b, 0x76, 0xdd, 0xfb, 0xf1, 0x31, 0xef, 0x7f, 0xa4,
+   0xba, 0x95, 0x4b, 0x91, 0x9c, 0x86, 0xa6, 0x48, 0xa2, 0x5a, 0x41, 0x64, 0x31, 0x14, 0x80, 0x6b,
+   0xb3, 0x0d, 0x46, 0x14, 0xb2, 0x61, 0x49, 0x81, 0xf5, 0x14, 0x2e, 0x1c, 0x3b, 0x7b, 0xc2, 0x23,
+   0x9d, 0x31, 0x66, 0x49, 0x56, 0x50, 0x69, 0x69, 0x5a, 0x5c, 0x82, 0x68, 0x96, 0x04, 0xc1, 0x76,
+   0x18, 0x19, 0x13, 0x95, 0xad, 0xbd, 0x5f, 0x96, 0x6d, 0xfe, 0xde, 0x65, 0x6a, 0x78, 0x47, 0x63,
+               };
+
+       if (argc < 4) {
+               fprintf(stderr, "Need at least 3 arguments: <ca> <cert1> <server>\n");
+               return 1;
+       }
+
+       cctx = crypto_ctx_new(&error);
+       if (!cctx) {
+               fprintf(stderr, "Error initializing crypto: %s\n", error->msg);
+               return error->code;
+       }
+
+       /* Load certificates */
+       for (i = 2; i < argc; i++) {
+               data = crypto_read_cert(argv[i], &size, &error);
+               if (!data) {
+                       fprintf(stderr, "Error reading cert %d: %s\n", i + 1, error->msg);
+                       return error->code;
+               }
+               if (crypto_push_cert(cctx, data, size, &error)) {
+                       free(data);
+                       fprintf(stderr, "Error pushing cert %d: %s\n", i + 1, error->msg);
+                       return error->code;
+               }
+               free(data);
+       }
+
+       /* Verify the cert chain */
+       if (crypto_verify_chain(cctx, argv[1], NULL, &error) != 0) {
+               fprintf(stderr, "Error verifying chain: %s\n", error && error->msg ? error->msg : "(none)");
+               return error->code;
+       }
+
+       /* Decrypt something using the public key of the server certificate */
+       size = 0;
+       data = crypto_decrypt_signature(cctx, &sig_data[0], sizeof(sig_data), &size, CRYPTO_PAD_NONE, &error);
+       if (!data || !size) {
+               fprintf(stderr, "Error decrypting signature: %s\n", error && error->msg ? error->msg : "(none)");
+               return error->code;
+       }
+
+       if (size != sizeof(dec_data)) {
+               fprintf(stderr, "Error decrypting signature: unexpected "
+                       "decrypted size %zd (expected %zu)\n", size, sizeof(dec_data));
+               return 1;
+       }
+
+       if (memcmp(data, dec_data, sizeof(dec_data))) {
+               fprintf(stderr, "Error decrypting signature: decrypted data did"
+                       " not match expected decrypted data\n");
+               return 1;
+       }
+       free(data);
+
+       fprintf(stdout, "Success\n");
+
+       crypto_ctx_free(cctx);
+       return 0;
+}
+
diff --git a/test/cert.pem b/test/cert.pem
new file mode 100644 (file)
index 0000000..a491c35
--- /dev/null
@@ -0,0 +1,373 @@
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 985026699 (0x3ab6508b)
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=BM, O=QuoVadis Limited, OU=Root Certification Authority, CN=QuoVadis Root Certification Authority
+        Validity
+            Not Before: Mar 19 18:33:33 2001 GMT
+            Not After : Mar 17 18:33:33 2021 GMT
+        Subject: C=BM, O=QuoVadis Limited, OU=Root Certification Authority, CN=QuoVadis Root Certification Authority
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:bf:61:b5:95:53:ba:57:fc:fa:f2:67:0b:3a:1a:
+                    df:11:80:64:95:b4:d1:bc:cd:7a:cf:f6:29:96:2e:
+                    24:54:40:24:38:f7:1a:85:dc:58:4c:cb:a4:27:42:
+                    97:d0:9f:83:8a:c3:e4:06:03:5b:00:a5:51:1e:70:
+                    04:74:e2:c1:d4:3a:ab:d7:ad:3b:07:18:05:8e:fd:
+                    83:ac:ea:66:d9:18:1b:68:8a:f5:57:1a:98:ba:f5:
+                    ed:76:3d:7c:d9:de:94:6a:3b:4b:17:c1:d5:8f:bd:
+                    65:38:3a:95:d0:3d:55:36:4e:df:79:57:31:2a:1e:
+                    d8:59:65:49:58:20:98:7e:ab:5f:7e:9f:e9:d6:4d:
+                    ec:83:74:a9:c7:6c:d8:ee:29:4a:85:2a:06:14:f9:
+                    54:e6:d3:da:65:07:8b:63:37:12:d7:d0:ec:c3:7b:
+                    20:41:44:a3:ed:cb:a0:17:e1:71:65:ce:1d:66:31:
+                    f7:76:01:19:c8:7d:03:58:b6:95:49:1d:a6:12:26:
+                    e8:c6:0c:76:e0:e3:66:cb:ea:5d:a6:26:ee:e5:cc:
+                    5f:bd:67:a7:01:27:0e:a2:ca:54:c5:b1:7a:95:1d:
+                    71:1e:4a:29:8a:03:dc:6a:45:c1:a4:19:5e:6f:36:
+                    cd:c3:a2:b0:b7:fe:5c:38:e2:52:bc:f8:44:43:e6:
+                    90:bb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            Authority Information Access:
+                OCSP - URI:https://ocsp.quovadisoffshore.com
+
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Certificate Policies:
+                Policy: 1.3.6.1.4.1.8024.0.1
+                  User Notice:
+                    Explicit Text: Reliance on the QuoVadis Root Certificate by any party assumes acceptance of the then applicable standard terms and conditions of use, certification practices, and the QuoVadis Certificate Policy.
+                  CPS: http://www.quovadis.bm
+
+            X509v3 Subject Key Identifier:
+                8B:4B:6D:ED:D3:29:B9:06:19:EC:39:39:A9:F0:97:84:6A:CB:EF:DF
+            X509v3 Authority Key Identifier:
+                keyid:8B:4B:6D:ED:D3:29:B9:06:19:EC:39:39:A9:F0:97:84:6A:CB:EF:DF
+                DirName:/C=BM/O=QuoVadis Limited/OU=Root Certification Authority/CN=QuoVadis Root Certification Authority
+                serial:3A:B6:50:8B
+
+            X509v3 Key Usage: critical
+                Certificate Sign, CRL Sign
+    Signature Algorithm: sha1WithRSAEncryption
+        8a:d4:14:b5:fe:f4:9a:92:a7:19:d4:a4:7e:72:18:8f:d9:68:
+        7c:52:24:dd:67:6f:39:7a:c4:aa:5e:3d:e2:58:b0:4d:70:98:
+        84:61:e8:1b:e3:69:18:0e:ce:fb:47:50:a0:4e:ff:f0:24:1f:
+        bd:b2:ce:f5:27:fc:ec:2f:53:aa:73:7b:03:3d:74:6e:e6:16:
+        9e:eb:a5:2e:c4:bf:56:27:50:2b:62:ba:be:4b:1c:3c:55:5c:
+        41:1d:24:be:82:20:47:5d:d5:44:7e:7a:16:68:df:7d:4d:51:
+        70:78:57:1d:33:1e:fd:02:99:9c:0c:cd:0a:05:4f:c7:bb:8e:
+        a4:75:fa:4a:6d:b1:80:8e:09:56:b9:9c:1a:60:fe:5d:c1:d7:
+        7a:dc:11:78:d0:d6:5d:c1:b7:d5:ad:32:99:03:3a:8a:cc:54:
+        25:39:31:81:7b:13:22:51:ba:46:6c:a1:bb:9e:fa:04:6c:49:
+        26:74:8f:d2:73:eb:cc:30:a2:e6:ea:59:22:87:f8:97:f5:0e:
+        fd:ea:cc:92:a4:16:c4:52:18:ea:21:ce:b1:f1:e6:84:81:e5:
+        ba:a9:86:28:f2:43:5a:5d:12:9d:ac:1e:d9:a8:e5:0a:6a:a7:
+        7f:a0:87:29:cf:f2:89:4d:d4:ec:c5:e2:e6:7a:d0:36:23:8a:
+        4a:74:36:f9
+SHA1 Fingerprint=DE:3F:40:BD:50:93:D3:9B:6C:60:F6:DA:BC:07:62:01:00:89:76:C9
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1289 (0x509)
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=BM, O=QuoVadis Limited, CN=QuoVadis Root CA 2
+        Validity
+            Not Before: Nov 24 18:27:00 2006 GMT
+            Not After : Nov 24 18:23:33 2031 GMT
+        Subject: C=BM, O=QuoVadis Limited, CN=QuoVadis Root CA 2
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (4096 bit)
+                Modulus (4096 bit):
+                    00:9a:18:ca:4b:94:0d:00:2d:af:03:29:8a:f0:0f:
+                    81:c8:ae:4c:19:85:1d:08:9f:ab:29:44:85:f3:2f:
+                    81:ad:32:1e:90:46:bf:a3:86:26:1a:1e:fe:7e:1c:
+                    18:3a:5c:9c:60:17:2a:3a:74:83:33:30:7d:61:54:
+                    11:cb:ed:ab:e0:e6:d2:a2:7e:f5:6b:6f:18:b7:0a:
+                    0b:2d:fd:e9:3e:ef:0a:c6:b3:10:e9:dc:c2:46:17:
+                    f8:5d:fd:a4:da:ff:9e:49:5a:9c:e6:33:e6:24:96:
+                    f7:3f:ba:5b:2b:1c:7a:35:c2:d6:67:fe:ab:66:50:
+                    8b:6d:28:60:2b:ef:d7:60:c3:c7:93:bc:8d:36:91:
+                    f3:7f:f8:db:11:13:c4:9c:77:76:c1:ae:b7:02:6a:
+                    81:7a:a9:45:83:e2:05:e6:b9:56:c1:94:37:8f:48:
+                    71:63:22:ec:17:65:07:95:8a:4b:df:8f:c6:5a:0a:
+                    e5:b0:e3:5f:5e:6b:11:ab:0c:f9:85:eb:44:e9:f8:
+                    04:73:f2:e9:fe:5c:98:8c:f5:73:af:6b:b4:7e:cd:
+                    d4:5c:02:2b:4c:39:e1:b2:95:95:2d:42:87:d7:d5:
+                    b3:90:43:b7:6c:13:f1:de:dd:f6:c4:f8:89:3f:d1:
+                    75:f5:92:c3:91:d5:8a:88:d0:90:ec:dc:6d:de:89:
+                    c2:65:71:96:8b:0d:03:fd:9c:bf:5b:16:ac:92:db:
+                    ea:fe:79:7c:ad:eb:af:f7:16:cb:db:cd:25:2b:e5:
+                    1f:fb:9a:9f:e2:51:cc:3a:53:0c:48:e6:0e:bd:c9:
+                    b4:76:06:52:e6:11:13:85:72:63:03:04:e0:04:36:
+                    2b:20:19:02:e8:74:a7:1f:b6:c9:56:66:f0:75:25:
+                    dc:67:c1:0e:61:60:88:b3:3e:d1:a8:fc:a3:da:1d:
+                    b0:d1:b1:23:54:df:44:76:6d:ed:41:d8:c1:b2:22:
+                    b6:53:1c:df:35:1d:dc:a1:77:2a:31:e4:2d:f5:e5:
+                    e5:db:c8:e0:ff:e5:80:d7:0b:63:a0:ff:33:a1:0f:
+                    ba:2c:15:15:ea:97:b3:d2:a2:b5:be:f2:8c:96:1e:
+                    1a:8f:1d:6c:a4:61:37:b9:86:73:33:d7:97:96:9e:
+                    23:7d:82:a4:4c:81:e2:a1:d1:ba:67:5f:95:07:a3:
+                    27:11:ee:16:10:7b:bc:45:4a:4c:b2:04:d2:ab:ef:
+                    d5:fd:0c:51:ce:50:6a:08:31:f9:91:da:0c:8f:64:
+                    5c:03:c3:3a:8b:20:3f:6e:8d:67:3d:3a:d6:fe:7d:
+                    5b:88:c9:5e:fb:cc:61:dc:8b:33:77:d3:44:32:35:
+                    09:62:04:92:16:10:d8:9e:27:47:fb:3b:21:e3:f8:
+                    eb:1d:5b
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Key Usage:
+                Certificate Sign, CRL Sign
+            X509v3 Subject Key Identifier:
+                1A:84:62:BC:48:4C:33:25:04:D4:EE:D0:F6:03:C4:19:46:D1:94:6B
+            X509v3 Authority Key Identifier:
+                keyid:1A:84:62:BC:48:4C:33:25:04:D4:EE:D0:F6:03:C4:19:46:D1:94:6B
+                DirName:/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2
+                serial:05:09
+
+    Signature Algorithm: sha1WithRSAEncryption
+        3e:0a:16:4d:9f:06:5b:a8:ae:71:5d:2f:05:2f:67:e6:13:45:
+        83:c4:36:f6:f3:c0:26:0c:0d:b5:47:64:5d:f8:b4:72:c9:46:
+        a5:03:18:27:55:89:78:7d:76:ea:96:34:80:17:20:dc:e7:83:
+        f8:8d:fc:07:b8:da:5f:4d:2e:67:b2:84:fd:d9:44:fc:77:50:
+        81:e6:7c:b4:c9:0d:0b:72:53:f8:76:07:07:41:47:96:0c:fb:
+        e0:82:26:93:55:8c:fe:22:1f:60:65:7c:5f:e7:26:b3:f7:32:
+        90:98:50:d4:37:71:55:f6:92:21:78:f7:95:79:fa:f8:2d:26:
+        87:66:56:30:77:a6:37:78:33:52:10:58:ae:3f:61:8e:f2:6a:
+        b1:ef:18:7e:4a:59:63:ca:8d:a2:56:d5:a7:2f:bc:56:1f:cf:
+        39:c1:e2:fb:0a:a8:15:2c:7d:4d:7a:63:c6:6c:97:44:3c:d2:
+        6f:c3:4a:17:0a:f8:90:d2:57:a2:19:51:a5:2d:97:41:da:07:
+        4f:a9:50:da:90:8d:94:46:e1:3e:f0:94:fd:10:00:38:f5:3b:
+        e8:40:e1:b4:6e:56:1a:20:cc:6f:58:8d:ed:2e:45:8f:d6:e9:
+        93:3f:e7:b1:2c:df:3a:d6:22:8c:dc:84:bb:22:6f:d0:f8:e4:
+        c6:39:e9:04:88:3c:c3:ba:eb:55:7a:6d:80:99:24:f5:6c:01:
+        fb:f8:97:b0:94:5b:eb:fd:d2:6f:f1:77:68:0d:35:64:23:ac:
+        b8:55:a1:03:d1:4d:42:19:dc:f8:75:59:56:a3:f9:a8:49:79:
+        f8:af:0e:b9:11:a0:7c:b7:6a:ed:34:d0:b6:26:62:38:1a:87:
+        0c:f8:e8:fd:2e:d3:90:7f:07:91:2a:1d:d6:7e:5c:85:83:99:
+        b0:38:08:3f:e9:5e:f9:35:07:e4:c9:62:6e:57:7f:a7:50:95:
+        f7:ba:c8:9b:e6:8e:a2:01:c5:d6:66:bf:79:61:f3:3c:1c:e1:
+        b9:82:5c:5d:a0:c3:e9:d8:48:bd:19:a2:11:14:19:6e:b2:86:
+        1b:68:3e:48:37:1a:88:b7:5d:96:5e:9c:c7:ef:27:62:08:e2:
+        91:19:5c:d2:f1:21:dd:ba:17:42:82:97:71:81:53:31:a9:9f:
+        f6:7d:62:bf:72:e1:a3:93:1d:cc:8a:26:5a:09:38:d0:ce:d7:
+        0d:80:16:b4:78:a5:3a:87:4c:8d:8a:a5:d5:46:97:f2:2c:10:
+        b9:bc:54:22:c0:01:50:69:43:9e:f4:b2:ef:6d:f8:ec:da:f1:
+        e3:b1:ef:df:91:8f:54:2a:0b:25:c1:26:19:c4:52:10:05:65:
+        d5:82:10:ea:c2:31:cd:2e
+SHA1 Fingerprint=CA:3A:FB:CF:12:40:36:4B:44:B2:16:20:88:80:48:39:19:93:7C:F7
+-----BEGIN CERTIFICATE-----
+MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
+YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
+GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
+Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
+WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
+rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
+ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
+Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
+PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
+/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
+oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
+yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
+EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
+A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
+MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
+ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
+BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
+g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
+fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
+WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
+B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
+hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
+TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
+mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
+ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
+4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
+8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
+-----END CERTIFICATE-----
+
+
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1478 (0x5c6)
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=BM, O=QuoVadis Limited, CN=QuoVadis Root CA 3
+        Validity
+            Not Before: Nov 24 19:11:23 2006 GMT
+            Not After : Nov 24 19:06:44 2031 GMT
+        Subject: C=BM, O=QuoVadis Limited, CN=QuoVadis Root CA 3
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (4096 bit)
+                Modulus (4096 bit):
+                    00:cc:57:42:16:54:9c:e6:98:d3:d3:4d:ee:fe:ed:
+                    c7:9f:43:39:4a:65:b3:e8:16:88:34:db:0d:59:91:
+                    74:cf:92:b8:04:40:ad:02:4b:31:ab:bc:8d:91:68:
+                    d8:20:0e:1a:01:e2:1a:7b:4e:17:5d:e2:8a:b7:3f:
+                    99:1a:cd:eb:61:ab:c2:65:a6:1f:b7:b7:bd:b7:8f:
+                    fc:fd:70:8f:0b:a0:67:be:01:a2:59:cf:71:e6:0f:
+                    29:76:ff:b1:56:79:45:2b:1f:9e:7a:54:e8:a3:29:
+                    35:68:a4:01:4f:0f:a4:2e:37:ef:1b:bf:e3:8f:10:
+                    a8:72:ab:58:57:e7:54:86:c8:c9:f3:5b:da:2c:da:
+                    5d:8e:6e:3c:a3:3e:da:fb:82:e5:dd:f2:5c:b2:05:
+                    33:6f:8a:36:ce:d0:13:4e:ff:bf:4a:0c:34:4c:a6:
+                    c3:21:bd:50:04:55:eb:b1:bb:9d:fb:45:1e:64:15:
+                    de:55:01:8c:02:76:b5:cb:a1:3f:42:69:bc:2f:bd:
+                    68:43:16:56:89:2a:37:61:91:fd:a6:ae:4e:c0:cb:
+                    14:65:94:37:4b:92:06:ef:04:d0:c8:9c:88:db:0b:
+                    7b:81:af:b1:3d:2a:c4:65:3a:78:b6:ee:dc:80:b1:
+                    d2:d3:99:9c:3a:ee:6b:5a:6b:b3:8d:b7:d5:ce:9c:
+                    c2:be:a5:4b:2f:16:b1:9e:68:3b:06:6f:ae:7d:9f:
+                    f8:de:ec:cc:29:a7:98:a3:25:43:2f:ef:f1:5f:26:
+                    e1:88:4d:f8:5e:6e:d7:d9:14:6e:19:33:69:a7:3b:
+                    84:89:93:c4:53:55:13:a1:51:78:40:f8:b8:c9:a2:
+                    ee:7b:ba:52:42:83:9e:14:ed:05:52:5a:59:56:a7:
+                    97:fc:9d:3f:0a:29:d8:dc:4f:91:0e:13:bc:de:95:
+                    a4:df:8b:99:be:ac:9b:33:88:ef:b5:81:af:1b:c6:
+                    22:53:c8:f6:c7:ee:97:14:b0:c5:7c:78:52:c8:f0:
+                    ce:6e:77:60:84:a6:e9:2a:76:20:ed:58:01:17:30:
+                    93:e9:1a:8b:e0:73:63:d9:6a:92:94:49:4e:b4:ad:
+                    4a:85:c4:a3:22:30:fc:09:ed:68:22:73:a6:88:0c:
+                    55:21:58:c5:e1:3a:9f:2a:dd:ca:e1:90:e0:d9:73:
+                    ab:6c:80:b8:e8:0b:64:93:a0:9c:8c:19:ff:b3:d2:
+                    0c:ec:91:26:87:8a:b3:a2:e1:70:8f:2c:0a:e5:cd:
+                    6d:68:51:eb:da:3f:05:7f:8b:32:e6:13:5c:6b:fe:
+                    5f:40:e2:22:c8:b4:b4:64:4f:d6:ba:7d:48:3e:a8:
+                    69:0c:d7:bb:86:71:c9:73:b8:3f:3b:9d:25:4b:da:
+                    ff:40:eb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Certificate Policies:
+                Policy: 1.3.6.1.4.1.8024.0.3
+                  User Notice:
+                    Explicit Text: Any use of this Certificate constitutes acceptance of the QuoVadis Root CA 3 Certificate Policy / Certification Practice Statement.
+                  CPS: http://www.quovadisglobal.com/cps
+
+            X509v3 Key Usage:
+                Certificate Sign, CRL Sign
+            X509v3 Subject Key Identifier:
+                F2:C0:13:E0:82:43:3E:FB:EE:2F:67:32:96:35:5C:DB:B8:CB:02:D0
+            X509v3 Authority Key Identifier:
+                keyid:F2:C0:13:E0:82:43:3E:FB:EE:2F:67:32:96:35:5C:DB:B8:CB:02:D0
+                DirName:/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 3
+                serial:05:C6
+
+    Signature Algorithm: sha1WithRSAEncryption
+        4f:ad:a0:2c:4c:fa:c0:f2:6f:f7:66:55:ab:23:34:ee:e7:29:
+        da:c3:5b:b6:b0:83:d9:d0:d0:e2:21:fb:f3:60:a7:3b:5d:60:
+        53:27:a2:9b:f6:08:22:2a:e7:bf:a0:72:e5:9c:24:6a:31:b1:
+        90:7a:27:db:84:11:89:27:a6:77:5a:38:d7:bf:ac:86:fc:ee:
+        5d:83:bc:06:c6:d1:77:6b:0f:6d:24:2f:4b:7a:6c:a7:07:96:
+        ca:e3:84:9f:ad:88:8b:1d:ab:16:8d:5b:66:17:d9:16:f4:8b:
+        80:d2:dd:f8:b2:76:c3:fc:38:13:aa:0c:de:42:69:2b:6e:f3:
+        3c:eb:80:27:db:f5:a6:44:0d:9f:5a:55:59:0b:d5:0d:52:48:
+        c5:ae:9f:f2:2f:80:c5:ea:32:50:35:12:97:2e:c1:e1:ff:f1:
+        23:88:51:38:9f:f2:66:56:76:e7:0f:51:97:a5:52:0c:4d:49:
+        51:95:36:3d:bf:a2:4b:0c:10:1d:86:99:4c:aa:f3:72:11:93:
+        e4:ea:f6:9b:da:a8:5d:a7:4d:b7:9e:02:ae:73:00:c8:da:23:
+        03:e8:f9:ea:19:74:62:00:94:cb:22:20:be:94:a7:59:b5:82:
+        6a:be:99:79:7a:a9:f2:4a:24:52:f7:74:fd:ba:4e:e6:a8:1d:
+        02:6e:b1:0d:80:44:c1:ae:d3:23:37:5f:bb:85:7c:2b:92:2e:
+        e8:7e:a5:8b:dd:99:e1:bf:27:6f:2d:5d:aa:7b:87:fe:0a:dd:
+        4b:fc:8e:f5:26:e4:6e:70:42:6e:33:ec:31:9e:7b:93:c1:e4:
+        c9:69:1a:3d:c0:6b:4e:22:6d:ee:ab:58:4d:c6:d0:41:c1:2b:
+        ea:4f:12:87:5e:eb:45:d8:6c:f5:98:02:d3:a0:d8:55:8a:06:
+        99:19:a2:a0:77:d1:30:9e:ac:cc:75:ee:83:f5:b0:62:39:cf:
+        6c:57:e2:4c:d2:91:0b:0e:75:28:1b:9a:bf:fd:1a:43:f1:ca:
+        77:fb:3b:8f:61:b8:69:28:16:42:04:5e:70:2a:1c:21:d8:8f:
+        e1:bd:23:5b:2d:74:40:92:d9:63:19:0d:73:dd:69:bc:62:47:
+        bc:e0:74:2b:b2:eb:7d:be:41:1b:b5:c0:46:c5:a1:22:cb:5f:
+        4e:c1:28:92:de:18:ba:d5:2a:28:bb:11:8b:17:93:98:99:60:
+        94:5c:23:cf:5a:27:97:5e:0b:05:06:93:37:1e:3b:69:36:eb:
+        a9:9e:61:1d:8f:32:da:8e:0c:d6:74:3e:7b:09:24:da:01:77:
+        47:c4:3b:cd:34:8c:99:f5:ca:e1:25:61:33:b2:59:1b:e2:6e:
+        d7:37:57:b6:0d:a9:12:da
+SHA1 Fingerprint=1F:49:14:F7:D8:74:95:1D:DD:AE:02:C0:BE:FD:3A:2D:82:75:51:85
+-----BEGIN CERTIFICATE-----
+MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
+YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
+V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
+4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
+H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
+8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
+vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
+mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
+btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
+T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
+WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
+c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
+4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
+VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
+CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
+aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
+aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
+dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
+czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
+A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
+Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
+7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
+d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
+4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
+t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
+DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
+k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
+zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
+Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
+mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
+4SVhM7JZG+Ju1zdXtg2pEto=
+-----END CERTIFICATE-----
diff --git a/test/cert0.pem b/test/cert0.pem
new file mode 100644 (file)
index 0000000..51b0bf5
--- /dev/null
@@ -0,0 +1,36 @@
+ 0 s:/1.3.6.1.4.1.311.60.2.1.3=CH/1.3.6.1.4.1.311.60.2.1.2=Bern/2.5.4.15=V1.0, Clause 5(b)/serialNumber=CH-035.7.001.278-9/C=CH/ST=Zuerich/L=Zuerich/O=SWITCH/CN=www.switch.ch
+   i:/C=BM/O=QuoVadis Limited/OU=www.quovadisglobal.com/CN=QuoVadis Global SSL ICA
+-----BEGIN CERTIFICATE-----
+MIIFpjCCBI6gAwIBAgICD4YwDQYJKoZIhvcNAQEFBQAwazELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHzAdBgNVBAsTFnd3dy5xdW92YWRp
+c2dsb2JhbC5jb20xIDAeBgNVBAMTF1F1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBMB4X
+DTA5MDExNTA5MjEzM1oXDTExMDExNTA5MjEzM1owgb8xEzARBgsrBgEEAYI3PAIB
+AxMCQ0gxFTATBgsrBgEEAYI3PAIBAhMEQmVybjEaMBgGA1UEDxMRVjEuMCwgQ2xh
+dXNlIDUoYikxGzAZBgNVBAUTEkNILTAzNS43LjAwMS4yNzgtOTELMAkGA1UEBhMC
+Q0gxEDAOBgNVBAgTB1p1ZXJpY2gxEDAOBgNVBAcTB1p1ZXJpY2gxDzANBgNVBAoT
+BlNXSVRDSDEWMBQGA1UEAxMNd3d3LnN3aXRjaC5jaDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAKqwpnO5zcYxC829nQpHkFeZp9Hp4gzlyvHj0BHaLx9F
+pQxaFw7bsgbrMR+M+OjI+NXbWhPbc6ftY5VjqYwaVQAWmA3vvo5ELsy11lzyQusi
+ZT2wjx0Rx1SV7ocP20rDS0gkFqrej0ymdQKO/mcyht53a076goaUuacOElhNttlM
+baXiGwSMFURVUA/9dcOC8HhYPokzWnQD7BkFl3pg3BsmHz5mQ+rh79e+rKJylsXS
+qfSI1zD0QQTLd01JBzX4iOM37IlHBAJb/EWAuNJPjA9SHZlfILhphaAiEtKUlcyL
+4atAUUgbM2SI9yFfwALHliyBgoBcsZSd7ZlzhaFVA6UCAwEAAaOCAf0wggH5MHQG
+CCsGAQUFBwEBBGgwZjAqBggrBgEFBQcwAYYeaHR0cDovL29jc3AucXVvdmFkaXNn
+bG9iYWwuY29tMDgGCCsGAQUFBzAChixodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i
+YWwuY29tL3F2c3NsaWNhLmNydDBRBgNVHSAESjBIMEYGDCsGAQQBvlgAAmQBAjA2
+MDQGCCsGAQUFBwIBFihodHRwOi8vd3d3LnF1b3ZhZGlzZ2xvYmFsLmNvbS9yZXBv
+c2l0b3J5MIGEBgNVHREEfTB7gg13d3cuc3dpdGNoLmNogglzd2l0Y2guY2iCEXd3
+dy1kYXYuc3dpdGNoLmNoggxjbXMuc21zY2cuY2iCEWNtcy53d3cuc3dpdGNoLmNo
+ggllZHVodWIuY2iCDXd3dy5lZHVodWIuY2iCEWNtcy53d3cuZWR1aHViLmNoMAsG
+A1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0j
+BBgwFoAUMk2hT+rwrpm27psHLIQIEVCL4n4wOwYDVR0fBDQwMjAwoC6gLIYqaHR0
+cDovL2NybC5xdW92YWRpc2dsb2JhbC5jb20vcXZzc2xpY2EuY3JsMB0GA1UdDgQW
+BBRj4EuKFVw3hT+IAecPzu1V+KBfRDANBgkqhkiG9w0BAQUFAAOCAQEAOGUv6vmY
+Bz1d8aewypeEpfGG6HEM59xXEnawhywiT7642y0ZCrAIYQASpKhI4sLPKOJpmQRg
+IzApWKaYvLhUsqvnaEvGS+zj+WGvPps7Ky23mwNmLr4qlMdlW6HuXacZvePAUp9v
+qCzQzcxD2QRncZ1vmG1uz/2gR34b/pgb2HnUS4tT6HbUQxTbQAEEbRubTMjFAD5w
+MXIFvNdOl+fhsehC9xxRnXy0dprXE2Wtk29fqnnXmpTSaOOuzc5BhXamdjebCeY/
+ACI+6A2o7ZbwRLN/J/lnBItJuWam78u0ypLOpWpDImt7eWMP+3JjJcegxVwp80dU
+2TumER72gt2EOA==
+-----END CERTIFICATE-----
+
diff --git a/test/cert1.pem b/test/cert1.pem
new file mode 100644 (file)
index 0000000..d734bd7
--- /dev/null
@@ -0,0 +1,34 @@
+ 1 s:/C=BM/O=QuoVadis Limited/OU=www.quovadisglobal.com/CN=QuoVadis Global SSL ICA
+   i:/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2
+-----BEGIN CERTIFICATE-----
+MIIFTjCCAzagAwIBAgICBXowDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
+GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
+b3QgQ0EgMjAeFw0wNzAxMTIxNjEzMzNaFw0xNzAxMTIxNjEzMTFaMGsxCzAJBgNV
+BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR8wHQYDVQQLExZ3d3cu
+cXVvdmFkaXNnbG9iYWwuY29tMSAwHgYDVQQDExdRdW9WYWRpcyBHbG9iYWwgU1NM
+IElDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKk1mD/CiG1+aGcM
+xI7LJL0x4qQpmljkCt1BFL1oaoyuFW4l0GKVTNPFsJ6w4a7pLejG1uQJgeRmKy8n
+xm12NXgIshfqBvTqVFAcuGViwCreo5S+oZWlLxTIYRVJZB3OujED5IyXVibMLR7g
+xWwcXS2BCSNDUnCAN2x+sGHSR9o4sGTbiYFMZPWZfOc0rIbWtms/cUSVfqneyRGN
+WgoIvKPdT2vGvf70RpszxqjEEBLT2A1F2QwM/BxgxylzyelGCN6qVDJrE2rP1KRq
+AN+qiV7kK9MphZ9RYRkjtHE3qNkIxTi4KLy/FBWCy9abwK7t8+AGP6y+N8Oxf7Ed
+9AU37VcCAwEAAaOCASAwggEcMA8GA1UdEwEB/wQFMAMBAf8wOgYIKwYBBQUHAQEE
+LjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20w
+QgYDVR0gBDswOTA3BgRVHSAAMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVv
+dmFkaXNnbG9iYWwuY29tL2NwczAOBgNVHQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAU
+GoRivEhMMyUE1O7Q9gPEGUbRlGswOQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL2Ny
+bC5xdW92YWRpc2dsb2JhbC5jb20vcXZyY2EyLmNybDAdBgNVHQ4EFgQUMk2hT+rw
+rpm27psHLIQIEVCL4n4wDQYJKoZIhvcNAQEFBQADggIBAI5zWxH+LIAvrc/dYIWZ
+8zHozDuc1kbd7IaiSgjJCZwNo1vMSLbNfgPg7XIoTDJ903URzDUWh4l8/XncwRil
+rRafR23N/iFkM+NF+LoABd9qpF/oAmOGuJ6GwPUf/yhioc8nQ/WXuMVF4/OTdvGF
+0QRsk7rivttpGx2aQhGBwO39ft4cySvXToNsBjH4VWcduEooZDg6plIec8S2zrFA
+dXvxSgz/sV41QHwyUokTxEY1UoXF9aA5VeGLKIkC1NasTyy26bzuOYOKxgqRUXIu
+n6M+CdWiKKJWVi3rBpbnFQWSrsotp4jeQn9zBuovTR0OOijTBWHj9ThxrIG5pb4g
+Nmd03/NZDe5l3ja59+UtBUpfCbdqPCCZSUy7t6PLAoDo5JwQKCEOrmNpwD/207GP
+2WMo77wh5/mvJRJMFfEZ+CwQXk5LPXXU7EJr+7PYpJB67hryxts1I6FJI0AF3ET9
+3YZ4sgEK009h6bdeZbIOvcT4e0v33EAJggFtxU/5xRdtk/PmwxBjSxeg+jBK2xeH
+3TScxc6nNvtcw22Lds5GucMsoxmpblYV1adrowg3twQvSXQZ96jzyT3qfmk09M+e
+bBTqd3GFwZcJNaQigOw8EQHQtjJm9Zco7FtJ+SxEqcQYFJ+M7QZz+0wWCPwlflMo
+7aGlYILpWH4iR3ZhuH/3xMkx
+-----END CERTIFICATE-----
+
diff --git a/test/cert2.pem b/test/cert2.pem
new file mode 100644 (file)
index 0000000..c1f00ad
--- /dev/null
@@ -0,0 +1,34 @@
+ 2 s:/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2
+   i:/C=BM/O=QuoVadis Limited/OU=Root Certification Authority/CN=QuoVadis Root Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIFQjCCBCqgAwIBAgIEQh/RwTANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNzA3MTAxNDMyMjFaFw0xNzA3MTAxNDMx
+MDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRsw
+GQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+j
+hiYaHv5+HBg6XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp
+3MJGF/hd/aTa/55JWpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02
+kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw
+419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7ds
+E/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3
+FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslW
+ZvB1JdxnwQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C31
+5eXbyOD/5YDXC2Og/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9
+gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqL
+ID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQAB
+o4H/MIH8MA8GA1UdEwEB/wQFMAMBAf8wQgYDVR0gBDswOTA3BgRVHSAAMC8wLQYI
+KwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczA6Bggr
+BgEFBQcBAQQuMCwwKgYIKwYBBQUHMAGGHmh0dHA6Ly9vY3NwLnF1b3ZhZGlzZ2xv
+YmFsLmNvbTAOBgNVHQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAUi0tt7dMpuQYZ7Dk5
+qfCXhGrL798wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5xdW92YWRpc2ds
+b2JhbC5jb20vcXZyY2EuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQBMcgnQQhxa0o7B
+SMNVmyE8suH3nmg3+FC6sAWUsYEKZUZU+PdUrOYGTybAjdPSghSzyhWf/h+l1zwb
+/vxiaZET3ikOni1G1L9rmNPNd0o8Omr/sxTtNyCIEugoJtiBV324XD9wjYr4TjzH
+fn5pq33j+5iqCS0oaynouNevRB/Kcn36esUBg5eEL84cu7JxoOgyPxIccskf5Zp+
+4pqUlQod9cedCi2NaSJ6ZyNExTTtsWXRZM2DYfwMNilHBwPhgj472vQqxN3wb7f6
+ndMU7j2DXbO6G9V891AT1OM6J0JC1DYaA4bMr4m31lJs2sIn99IgrondrOsPSWuu
+TYzbyDZK
+-----END CERTIFICATE-----
+
diff --git a/test/root.pem b/test/root.pem
new file mode 100644 (file)
index 0000000..0050532
--- /dev/null
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+
diff --git a/tunip.c b/tunip.c
new file mode 100644 (file)
index 0000000..d0bc971
--- /dev/null
+++ b/tunip.c
@@ -0,0 +1,1065 @@
+/* IPSec ESP and AH support.
+   Copyright (c) 1999      Pierre Beyssac
+   Copyright (C) 2002      Geoffrey Keating
+   Copyright (C) 2003-2007 Maurice Massar
+   Copyright (C) 2004      Tomas Mraz
+   Copyright (C) 2005      Michael Tilstra
+   Copyright (C) 2006      Daniel Roethlisberger
+   Copyright (C) 2007      Paolo Zarpellon (tap+Cygwin support)
+
+   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: tunip.c 511 2012-01-14 07:57:51Z Antonio Borneo $
+*/
+
+/* borrowed from pipsecd (-; */
+
+/*-
+ * Copyright (c) 1999 Pierre Beyssac
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#ifndef __SKYOS__
+#include <netinet/ip_icmp.h>
+#endif
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/select.h>
+#include <signal.h>
+
+#ifdef __CYGWIN__
+#include <pthread.h>
+#endif
+
+#if !defined(__sun__) && !defined(__SKYOS__)
+#include <err.h>
+#endif
+
+#include <gcrypt.h>
+#include "sysdep.h"
+#include "config.h"
+#include "vpnc.h"
+
+#include "tunip.h"
+
+#ifndef MAX
+#define MAX(a,b)       ((a)>(b)?(a):(b))
+#endif
+
+#ifndef FD_COPY
+#define FD_COPY(f, t)  ((void)memcpy((t), (f), sizeof(*(f))))
+#endif
+
+/* A real ESP header (RFC 2406) */
+typedef struct esp_encap_header {
+       uint32_t spi; /* security parameters index */
+       uint32_t seq_id; /* sequence id (unimplemented) */
+       /* variable-length payload data + padding */
+       /* unsigned char next_header */
+       /* optional auth data */
+} __attribute__((packed)) esp_encap_header_t;
+
+struct encap_method {
+       int fixed_header_size;
+
+       int  (*recv)      (struct sa_block *s, unsigned char *buf, unsigned int bufsize);
+       void (*send_peer) (struct sa_block *s, unsigned char *buf, unsigned int bufsize);
+       int  (*recv_peer) (struct sa_block *s);
+};
+
+/* Yuck! Global variables... */
+
+#define MAX_HEADER 72
+#define MAX_PACKET 4096
+int volatile do_kill;
+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 --
+ *     Checksum routine for Internet Protocol family headers (C Version)
+ */
+static u_short in_cksum(u_short *addr, int len)
+{
+       register int nleft = len;
+       register u_short *w = addr;
+       register int sum = 0;
+       u_short answer = 0;
+
+       /*
+        * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+        * sequential 16 bit words to it, and at the end, fold back all the
+        * carry bits from the top 16 bits into the lower 16 bits.
+        */
+       while (nleft > 1) {
+               sum += *w++;
+               nleft -= 2;
+       }
+
+       /* mop up an odd byte, if necessary */
+       if (nleft == 1) {
+               *(u_char *) (&answer) = *(u_char *) w;
+               sum += answer;
+       }
+
+       /* add back carry outs from top 16 bits to low 16 bits */
+       sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+       sum += (sum >> 16); /* add carry */
+       answer = ~sum; /* truncate to 16 bits */
+       return (answer);
+}
+
+/*
+ * Decapsulate from a raw IP packet
+ */
+static int encap_rawip_recv(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
+{
+       ssize_t r;
+       struct ip *p = (struct ip *)buf;
+       struct sockaddr_in from;
+       socklen_t fromlen = sizeof(from);
+
+       r = recvfrom(s->esp_fd, buf, bufsize, 0, (struct sockaddr *)&from, &fromlen);
+       if (r == -1) {
+               logmsg(LOG_ERR, "recvfrom: %m");
+               return -1;
+       }
+       if (from.sin_addr.s_addr != s->dst.s_addr) {
+               logmsg(LOG_ALERT, "packet from unknown host %s", inet_ntoa(from.sin_addr));
+               return -1;
+       }
+       if (r < (p->ip_hl << 2) + s->ipsec.em->fixed_header_size) {
+               logmsg(LOG_ALERT, "packet too short. got %zd, expected %d", r, (p->ip_hl << 2) + s->ipsec.em->fixed_header_size);
+               return -1;
+       }
+
+#ifdef NEED_IPLEN_FIX
+       p->ip_len = r;
+#else
+       p->ip_len = ntohs(r);
+#endif
+
+       s->ipsec.rx.buf = buf;
+       s->ipsec.rx.buflen = r;
+       s->ipsec.rx.bufpayload = (p->ip_hl << 2);
+       s->ipsec.rx.bufsize = bufsize;
+       return r;
+}
+
+/*
+ * Decapsulate from an UDP packet
+ */
+static int encap_udp_recv(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
+{
+       ssize_t r;
+
+       r = recv(s->esp_fd, buf, bufsize, 0);
+       if (r == -1) {
+               logmsg(LOG_ERR, "recvfrom: %m");
+               return -1;
+       }
+       if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD && r > 8) {
+               r -= 8;
+               memmove(buf, buf + 8, r);
+       }
+       if( r == 1 && *buf == 0xff )
+       {
+               DEBUGTOP(1, printf("UDP NAT keepalive packet received\n"));
+               return -1;
+       }
+       if (r < s->ipsec.em->fixed_header_size) {
+               logmsg(LOG_ALERT, "packet too short from %s. got %zd, expected %d",
+                       inet_ntoa(s->dst), r, s->ipsec.em->fixed_header_size);
+               return -1;
+       }
+
+       s->ipsec.rx.buf = buf;
+       s->ipsec.rx.buflen = r;
+       s->ipsec.rx.bufpayload = 0;
+       s->ipsec.rx.bufsize = bufsize;
+       return r;
+}
+
+/*
+ * Decapsulate packet
+ */
+static int encap_any_decap(struct sa_block *s)
+{
+       s->ipsec.rx.buflen -= s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size;
+       s->ipsec.rx.buf    += s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size;
+       if (s->ipsec.rx.buflen == 0)
+               return 0;
+       return 1;
+}
+
+/*
+ * Send decapsulated packet to tunnel device
+ */
+static int tun_send_ip(struct sa_block *s)
+{
+       int sent, len;
+       uint8_t *start;
+
+       start = s->ipsec.rx.buf;
+       len   = s->ipsec.rx.buflen;
+
+       if (opt_if_mode == IF_MODE_TAP) {
+#ifndef __sun__
+               /*
+                * Add ethernet header before s->ipsec.rx.buf where
+                * at least ETH_HLEN bytes should be available.
+                */
+               struct ether_header *eth_hdr = (struct ether_header *) (s->ipsec.rx.buf - ETH_HLEN);
+
+               memcpy(eth_hdr->ether_dhost, s->tun_hwaddr, ETH_ALEN);
+               memcpy(eth_hdr->ether_shost, s->tun_hwaddr, ETH_ALEN);
+
+               /* Use a different MAC as source */
+               eth_hdr->ether_shost[0] ^= 0x80; /* toggle some visible bit */
+               eth_hdr->ether_type = htons(ETHERTYPE_IP);
+
+               start = (uint8_t *) eth_hdr;
+               len += ETH_HLEN;
+#endif
+       }
+
+       sent = tun_write(s->tun_fd, start, len);
+       if (sent != len)
+               logmsg(LOG_ERR, "truncated in: %d -> %d\n", len, sent);
+       hex_dump("Tx pkt", start, len, NULL);
+       return 1;
+}
+
+/*
+ * Compute HMAC for an arbitrary stream of bytes
+ */
+static int hmac_compute(int md_algo,
+       const unsigned char *data, unsigned int data_size,
+       unsigned char *digest, unsigned char do_store,
+       const unsigned char *secret, unsigned short secret_size)
+{
+       gcry_md_hd_t md_ctx;
+       int ret;
+       unsigned char *hmac_digest;
+       unsigned int hmac_len;
+
+       /* See RFC 2104 */
+       gcry_md_open(&md_ctx, md_algo, GCRY_MD_FLAG_HMAC);
+       assert(md_ctx != NULL);
+       ret = gcry_md_setkey(md_ctx, secret, secret_size);
+       assert(ret == 0);
+       gcry_md_write(md_ctx, data, data_size);
+       gcry_md_final(md_ctx);
+       hmac_digest = gcry_md_read(md_ctx, 0);
+       hmac_len = 12; /*gcry_md_get_algo_dlen(md_algo); see RFC .. only use 96 bit */
+
+       if (do_store) {
+               memcpy(digest, hmac_digest, hmac_len);
+               ret = 0;
+       } else
+               ret = memcmp(digest, hmac_digest, hmac_len);
+
+       gcry_md_close(md_ctx);
+       return ret;
+}
+
+/*
+ * Encapsulate a packet in ESP
+ */
+static void encap_esp_encapsulate(struct sa_block *s)
+{
+       esp_encap_header_t *eh;
+       unsigned char *iv, *cleartext;
+       size_t i, padding, pad_blksz;
+       unsigned int cleartextlen;
+
+       /*
+        * Add padding as necessary
+        *
+        * done: this should be checked, RFC 2406 section 2.4 is quite
+        *      obscure on that point.
+        * seems fine
+        */
+       pad_blksz = s->ipsec.blk_len;
+       while (pad_blksz & 3) /* must be multiple of 4 */
+               pad_blksz <<= 1;
+       padding = pad_blksz - ((s->ipsec.tx.buflen + 2 - s->ipsec.tx.var_header_size - s->ipsec.tx.bufpayload) % pad_blksz);
+       DEBUG(3, printf("sending packet: len = %d, padding = %lu\n", s->ipsec.tx.buflen, (unsigned long)padding));
+       if (padding == pad_blksz)
+               padding = 0;
+
+       for (i = 1; i <= padding; i++) {
+               s->ipsec.tx.buf[s->ipsec.tx.buflen] = i;
+               s->ipsec.tx.buflen++;
+       }
+
+       /* Add trailing padlen and next_header */
+       s->ipsec.tx.buf[s->ipsec.tx.buflen++] = padding;
+       s->ipsec.tx.buf[s->ipsec.tx.buflen++] = IPPROTO_IPIP;
+
+       cleartext = s->ipsec.tx.buf + s->ipsec.tx.var_header_size + s->ipsec.tx.bufpayload;
+       cleartextlen = s->ipsec.tx.buflen - s->ipsec.tx.var_header_size - s->ipsec.tx.bufpayload;
+
+       eh = (esp_encap_header_t *) (s->ipsec.tx.buf + s->ipsec.tx.bufpayload);
+       eh->spi = s->ipsec.tx.spi;
+       eh->seq_id = htonl(s->ipsec.tx.seq_id++);
+
+       /* Copy initialization vector in packet */
+       iv = (unsigned char *)(eh + 1);
+       gcry_create_nonce(iv, s->ipsec.iv_len);
+       hex_dump("iv", iv, s->ipsec.iv_len, NULL);
+
+       hex_dump("sending ESP packet (before crypt)", s->ipsec.tx.buf, s->ipsec.tx.buflen, NULL);
+
+       if (s->ipsec.cry_algo) {
+               gcry_cipher_setiv(s->ipsec.tx.cry_ctx, iv, s->ipsec.iv_len);
+               gcry_cipher_encrypt(s->ipsec.tx.cry_ctx, cleartext, cleartextlen, NULL, 0);
+       }
+
+       hex_dump("sending ESP packet (after crypt)", s->ipsec.tx.buf, s->ipsec.tx.buflen, NULL);
+
+       /* Handle optional authentication field */
+       if (s->ipsec.md_algo) {
+               hmac_compute(s->ipsec.md_algo,
+                       s->ipsec.tx.buf + s->ipsec.tx.bufpayload,
+                       s->ipsec.tx.var_header_size + cleartextlen,
+                       s->ipsec.tx.buf + s->ipsec.tx.bufpayload
+                       + s->ipsec.tx.var_header_size + cleartextlen,
+                       1, s->ipsec.tx.key_md, s->ipsec.md_len);
+               s->ipsec.tx.buflen += 12; /*gcry_md_get_algo_dlen(md_algo); see RFC .. only use 96 bit */
+               hex_dump("sending ESP packet (after ah)", s->ipsec.tx.buf, s->ipsec.tx.buflen, NULL);
+       }
+}
+
+/*
+ * Encapsulate a packet in IP ESP and send to the peer.
+ * "buf" should have exactly MAX_HEADER free bytes at its beginning
+ * to account for encapsulation data (not counted in "size").
+ */
+static void encap_esp_send_peer(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
+{
+       ssize_t sent;
+       struct ip *tip, ip;
+       struct sockaddr_in dstaddr;
+
+       buf += MAX_HEADER;
+
+       /* Keep a pointer to the old IP header */
+       tip = (struct ip *)buf;
+
+       s->ipsec.tx.buf = buf;
+       s->ipsec.tx.buflen = bufsize;
+
+       /* Prepend our encapsulation header and new IP header */
+       s->ipsec.tx.var_header_size = (s->ipsec.em->fixed_header_size + s->ipsec.iv_len);
+
+       s->ipsec.tx.buf -= sizeof(struct ip) + s->ipsec.tx.var_header_size;
+       s->ipsec.tx.buflen += sizeof(struct ip) + s->ipsec.tx.var_header_size;
+
+       s->ipsec.tx.bufpayload = sizeof(struct ip);
+
+       /* Fill non-mutable fields */
+       ip.ip_v = IPVERSION;
+       ip.ip_hl = 5;
+       /*gcry_md_get_algo_dlen(md_algo); see RFC .. only use 96 bit */
+       ip.ip_id = htons(s->ipsec.ip_id++);
+       ip.ip_p = IPPROTO_ESP;
+       ip.ip_src = s->src;
+       ip.ip_dst = s->dst;
+
+       /* Fill mutable fields */
+       ip.ip_tos = (bufsize < sizeof(struct ip)) ? 0 : tip->ip_tos;
+       ip.ip_off = 0;
+       ip.ip_ttl = IPDEFTTL;
+       ip.ip_sum = 0;
+
+       encap_esp_encapsulate(s);
+
+       ip.ip_len = s->ipsec.tx.buflen;
+#ifdef NEED_IPLEN_FIX
+       ip.ip_len = htons(ip.ip_len);
+#endif
+       ip.ip_sum = in_cksum((u_short *) s->ipsec.tx.buf, sizeof(struct ip));
+
+       memcpy(s->ipsec.tx.buf, &ip, sizeof ip);
+
+       dstaddr.sin_family = AF_INET;
+       dstaddr.sin_addr = s->dst;
+       dstaddr.sin_port = 0;
+       sent = sendto(s->esp_fd, s->ipsec.tx.buf, s->ipsec.tx.buflen, 0, (struct sockaddr *)&dstaddr, sizeof(struct sockaddr_in));
+       if (sent == -1) {
+               logmsg(LOG_ERR, "esp sendto: %m");
+               return;
+       }
+       if (sent != s->ipsec.tx.buflen)
+               logmsg(LOG_ALERT, "esp truncated out (%lld out of %d)", (long long)sent, s->ipsec.tx.buflen);
+}
+
+/*
+ * Encapsulate a packet in UDP ESP and send to the peer.
+ * "buf" should have exactly MAX_HEADER free bytes at its beginning
+ * to account for encapsulation data (not counted in "size").
+ */
+static void encap_udp_send_peer(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
+{
+       ssize_t sent;
+
+       buf += MAX_HEADER;
+
+       s->ipsec.tx.buf = buf;
+       s->ipsec.tx.buflen = bufsize;
+
+       /* Prepend our encapsulation header and new IP header */
+       s->ipsec.tx.var_header_size = (s->ipsec.em->fixed_header_size + s->ipsec.iv_len);
+
+       s->ipsec.tx.buf -= s->ipsec.tx.var_header_size;
+       s->ipsec.tx.buflen += s->ipsec.tx.var_header_size;
+
+       s->ipsec.tx.bufpayload = 0;
+
+       encap_esp_encapsulate(s);
+
+       if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) {
+               s->ipsec.tx.buf -= 8;
+               s->ipsec.tx.buflen += 8;
+               memset(s->ipsec.tx.buf, 0, 8);
+       }
+
+       sent = send(s->esp_fd, s->ipsec.tx.buf, s->ipsec.tx.buflen, 0);
+       if (sent == -1) {
+               logmsg(LOG_ERR, "udp sendto: %m");
+               return;
+       }
+       if (sent != s->ipsec.tx.buflen)
+               logmsg(LOG_ALERT, "udp truncated out (%lld out of %d)",
+                       (long long)sent, s->ipsec.tx.buflen);
+}
+
+static int encap_esp_recv_peer(struct sa_block *s)
+{
+       int len, i;
+       size_t blksz;
+       unsigned char padlen, next_header;
+       unsigned char *pad;
+       unsigned char *iv;
+
+       s->ipsec.rx.var_header_size = s->ipsec.iv_len;
+       iv = s->ipsec.rx.buf + s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size;
+
+       len = s->ipsec.rx.buflen - s->ipsec.rx.bufpayload - s->ipsec.em->fixed_header_size - s->ipsec.rx.var_header_size;
+
+       if (len < 0) {
+               logmsg(LOG_ALERT, "Packet too short");
+               return -1;
+       }
+
+       /* Handle optional authentication field */
+       if (s->ipsec.md_algo) {
+               len -= 12; /*gcry_md_get_algo_dlen(peer->local_sa->md_algo); */
+               s->ipsec.rx.buflen -= 12;
+               if (hmac_compute(s->ipsec.md_algo,
+                               s->ipsec.rx.buf + s->ipsec.rx.bufpayload,
+                               s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len,
+                               s->ipsec.rx.buf + s->ipsec.rx.bufpayload
+                               + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len,
+                               0,
+                               s->ipsec.rx.key_md,
+                               s->ipsec.md_len) != 0) {
+                       logmsg(LOG_ALERT, "HMAC mismatch in ESP mode");
+                       return -1;
+               }
+       }
+
+       blksz = s->ipsec.blk_len;
+       if (s->ipsec.cry_algo && ((len % blksz) != 0)) {
+               logmsg(LOG_ALERT,
+                       "payload len %d not a multiple of algorithm block size %lu", len,
+                       (unsigned long)blksz);
+               return -1;
+       }
+
+       hex_dump("receiving ESP packet (before decrypt)",
+               &s->ipsec.rx.buf[s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size +
+                        s->ipsec.rx.var_header_size], len, NULL);
+
+       if (s->ipsec.cry_algo) {
+               unsigned char *data;
+
+               data = (s->ipsec.rx.buf + s->ipsec.rx.bufpayload
+                       + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size);
+               gcry_cipher_setiv(s->ipsec.rx.cry_ctx, iv, s->ipsec.iv_len);
+               gcry_cipher_decrypt(s->ipsec.rx.cry_ctx, data, len, NULL, 0);
+       }
+
+       hex_dump("receiving ESP packet (after decrypt)",
+               &s->ipsec.rx.buf[s->ipsec.rx.bufpayload + s->ipsec.em->fixed_header_size +
+                       s->ipsec.rx.var_header_size], len, NULL);
+
+       padlen = s->ipsec.rx.buf[s->ipsec.rx.bufpayload
+               + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len - 2];
+       next_header = s->ipsec.rx.buf[s->ipsec.rx.bufpayload
+               + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len - 1];
+
+       if (padlen + 2 > len) {
+               logmsg(LOG_ALERT, "Inconsistent padlen");
+               return -1;
+       }
+       if (next_header != IPPROTO_IPIP) {
+               logmsg(LOG_ALERT, "Inconsistent next_header %d", next_header);
+               return -1;
+       }
+       DEBUG(3, printf("pad len: %d, next_header: %d\n", padlen, next_header));
+
+       len -= padlen + 2;
+       s->ipsec.rx.buflen -= padlen + 2;
+
+       /* Check padding */
+       pad = s->ipsec.rx.buf + s->ipsec.rx.bufpayload
+               + s->ipsec.em->fixed_header_size + s->ipsec.rx.var_header_size + len;
+       for (i = 1; i <= padlen; i++) {
+               if (*pad != i) {
+                       logmsg(LOG_ALERT, "Bad padding");
+                       return -1;
+               }
+               pad++;
+       }
+
+       return 0;
+}
+
+static void encap_esp_new(struct encap_method *encap)
+{
+       encap->recv = encap_rawip_recv;
+       encap->send_peer = encap_esp_send_peer;
+       encap->recv_peer = encap_esp_recv_peer;
+       encap->fixed_header_size = sizeof(esp_encap_header_t);
+}
+
+static void encap_udp_new(struct encap_method *encap)
+{
+       encap->recv = encap_udp_recv;
+       encap->send_peer = encap_udp_send_peer;
+       encap->recv_peer = encap_esp_recv_peer;
+       encap->fixed_header_size = sizeof(esp_encap_header_t);
+}
+
+/*
+ * Process ARP
+ * Return 1 if packet has been processed, 0 otherwise
+ */
+static int process_arp(struct sa_block *s, uint8_t *frame)
+{
+#ifndef __sun__
+       int frame_size;
+       uint8_t tmp[4];
+       struct ether_header *eth = (struct ether_header *) frame;
+       struct ether_arp *arp = (struct ether_arp *) (frame + ETH_HLEN);
+
+       if (ntohs(eth->ether_type) != ETHERTYPE_ARP) {
+               return 0;
+       }
+
+       if (ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
+               ntohs(arp->arp_pro) != 0x800 ||
+               arp->arp_hln != ETH_ALEN ||
+               arp->arp_pln != 4 ||
+               ntohs(arp->arp_op) != ARPOP_REQUEST ||
+               !memcmp(arp->arp_spa, arp->arp_tpa, 4) ||
+               memcmp(eth->ether_shost, s->tun_hwaddr, ETH_ALEN) ||
+               !memcmp(arp->arp_tpa, &s->our_address, 4)) {
+               /* whatever .. just drop it */
+               return 1;
+       }
+
+       /* send arp reply */
+
+       memcpy(eth->ether_dhost, s->tun_hwaddr, ETH_ALEN);
+       eth->ether_shost[0] ^= 0x80; /* Use a different MAC as source */
+
+       memcpy(tmp, arp->arp_spa, 4);
+       memcpy(arp->arp_spa, arp->arp_tpa, 4);
+       memcpy(arp->arp_tpa, tmp, 4);
+
+       memcpy(arp->arp_tha, s->tun_hwaddr, ETH_ALEN);
+       arp->arp_sha[0] ^= 0x80; /* Use a different MAC as source */
+
+       arp->arp_op = htons(ARPOP_REPLY);
+
+       frame_size = ETH_HLEN + sizeof(struct ether_arp);
+       tun_write(s->tun_fd, frame, frame_size);
+       hex_dump("ARP reply", frame, frame_size, NULL);
+
+       return 1;
+#else
+       s = 0;
+       frame = 0;
+       return 0;
+#endif
+}
+
+/*
+ * Process non-IP packets
+ * Return 1 if packet has been processed, 0 otherwise
+ */
+static int process_non_ip(uint8_t *frame)
+{
+       struct ether_header *eth = (struct ether_header *) frame;
+
+       if (ntohs(eth->ether_type) != ETHERTYPE_IP) {
+               /* drop non-ip traffic */
+               return 1;
+       }
+
+       return 0;
+}
+
+static void process_tun(struct sa_block *s)
+{
+       int pack;
+       int size = MAX_PACKET;
+       uint8_t *start = global_buffer_rx + MAX_HEADER;
+
+       if (opt_if_mode == IF_MODE_TAP) {
+               /* Make sure IP packet starts at buf + MAX_HEADER */
+               start -= ETH_HLEN;
+               size += ETH_HLEN;
+       }
+
+       /* Receive a packet from the tunnel interface */
+       pack = tun_read(s->tun_fd, start, size);
+
+       hex_dump("Rx pkt", start, pack, NULL);
+
+       if (opt_if_mode == IF_MODE_TAP) {
+               if (process_arp(s, start)) {
+                       return;
+               }
+               if (process_non_ip(start)) {
+                       return;
+               }
+               pack -= ETH_HLEN;
+       }
+
+       if (pack == -1) {
+               logmsg(LOG_ERR, "read: %m");
+               return;
+       }
+
+       /* 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)) {
+               logmsg(LOG_ALERT, "routing loop to %s",
+                       inet_ntoa(s->dst));
+               return;
+       }
+
+       /* Encapsulate and send to the other end of the tunnel */
+       s->ipsec.life.tx += 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_tx;
+       esp_encap_header_t *eh;
+
+       if (opt_if_mode == IF_MODE_TAP) {
+               start += ETH_HLEN;
+       }
+
+       pack = s->ipsec.em->recv(s, start, MAX_HEADER + MAX_PACKET);
+       if (pack == -1)
+               return;
+
+       eh = (esp_encap_header_t *) (s->ipsec.rx.buf + s->ipsec.rx.bufpayload);
+       if (eh->spi == 0) {
+               process_late_ike(s, s->ipsec.rx.buf + s->ipsec.rx.bufpayload + 4 /* SPI-size */,
+                       s->ipsec.rx.buflen - s->ipsec.rx.bufpayload - 4);
+               return;
+       } else if (eh->spi != s->ipsec.rx.spi) {
+               logmsg(LOG_NOTICE, "unknown spi %#08x from peer", ntohl(eh->spi));
+               return;
+       }
+
+       /* Check auth digest and/or decrypt */
+       if (s->ipsec.em->recv_peer(s) != 0)
+               return;
+
+       if (encap_any_decap(s) == 0) {
+               logmsg(LOG_DEBUG, "received update probe from peer");
+       } else {
+               /* Send the decapsulated packet to the tunnel interface */
+               s->ipsec.life.rx += s->ipsec.rx.buflen;
+               tun_send_ip(s);
+       }
+}
+
+#if defined(__CYGWIN__)
+static void *tun_thread (void *arg)
+{
+       struct sa_block *s = (struct sa_block *) arg;
+
+       while (!do_kill) {
+               process_tun(s);
+       }
+       return NULL;
+}
+#endif
+
+static void vpnc_main_loop(struct sa_block *s)
+{
+       fd_set rfds, refds;
+       int nfds=0;
+       int enable_keepalives;
+       int timed_mode;
+       ssize_t len;
+       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) */
+       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) {
+               keepalive = keepalive_v1;
+               keepalive_size = sizeof(keepalive_v1);
+       } else { /* active_mode is either RFC or CISCO_UDP */
+               keepalive = keepalive_v2;
+               keepalive_size = sizeof(keepalive_v2);
+       }
+
+       /* 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__)
+       FD_SET(s->tun_fd, &rfds);
+       nfds = MAX(nfds, s->tun_fd +1);
+#endif
+
+       FD_SET(s->esp_fd, &rfds);
+       nfds = MAX(nfds, s->esp_fd +1);
+
+       if (s->ike_fd != s->esp_fd) {
+               FD_SET(s->ike_fd, &rfds);
+               nfds = MAX(nfds, s->ike_fd +1);
+       }
+
+#if defined(__CYGWIN__)
+       if (pthread_create(&tid, NULL, tun_thread, s)) {
+               logmsg(LOG_ERR, "Cannot create tun thread!\n");
+               return;
+       }
+#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) {
+               normal_timeout.tv_sec = 9;
+               normal_timeout.tv_usec = 500000;
+
+               if (s->ike_fd != s->esp_fd) {
+                       /* send initial nat ike keepalive packet */
+                       next_ike_keepalive = time(NULL) + 9;
+                       keepalive_ike(s);
+               }
+       }
+
+       select_timeout = normal_timeout;
+
+       while (!do_kill) {
+               int presult;
+
+               do {
+                       struct timeval *tvp = NULL;
+                       FD_COPY(&rfds, &refds);
+                       if (s->ike.do_dpd || enable_keepalives)
+                               tvp = &select_timeout;
+                       presult = select(nfds, &refds, NULL, NULL, tvp);
+                       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) {
+                                               logmsg(LOG_ERR, "keepalive 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;
+                                       }
+                               }
+                       }
+                       DEBUG(2,printf("lifetime status: %ld of %u seconds used, %u|%u of %u kbytes used\n",
+                               time(NULL) - s->ipsec.life.start,
+                               s->ipsec.life.seconds,
+                               s->ipsec.life.rx/1024,
+                               s->ipsec.life.tx/1024,
+                               s->ipsec.life.kbytes));
+               } while ((presult == 0 || (presult == -1 && errno == EINTR)) && !do_kill);
+               if (presult == -1) {
+                       logmsg(LOG_ERR, "select: %m");
+                       continue;
+               }
+
+#if !defined(__CYGWIN__)
+               if (FD_ISSET(s->tun_fd, &refds)) {
+                       process_tun(s);
+               }
+#endif
+
+               if (FD_ISSET(s->esp_fd, &refds) ) {
+                       process_socket(s);
+               }
+
+               if (s->ike_fd != s->esp_fd && FD_ISSET(s->ike_fd, &refds) ) {
+                       DEBUG(3,printf("received something on ike fd..\n"));
+                       len = recv(s->ike_fd, global_buffer_tx, MAX_HEADER + MAX_PACKET, 0);
+                       process_late_ike(s, global_buffer_tx, len);
+               }
+
+               if (timed_mode) {
+                       time_t now = time(NULL);
+                       time_t next_up = now + 86400;
+                       if (enable_keepalives) {
+                               /* never wait more than 9 seconds for a UDP keepalive */
+                               next_up = now + 9;
+                               if (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;
+                               }
+                       }
+                       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;
+               }
+
+       }
+
+       switch (do_kill) {
+               case -2:
+                       logmsg(LOG_NOTICE, "connection terminated by dead peer detection");
+                       break;
+               case -1:
+                       logmsg(LOG_NOTICE, "connection terminated by peer");
+                       break;
+               default:
+                       logmsg(LOG_NOTICE, "terminated by signal: %d", do_kill);
+                       break;
+       }
+}
+
+static void killit(int signum)
+{
+       do_kill = signum;
+}
+
+static void write_pidfile(const char *pidfile)
+{
+       FILE *pf;
+
+       if (pidfile == NULL || pidfile[0] == '\0')
+               return;
+
+       pf = fopen(pidfile, "w");
+       if (pf == NULL) {
+               logmsg(LOG_WARNING, "can't open pidfile %s for writing", pidfile);
+               return;
+       }
+
+       fprintf(pf, "%d\n", (int)getpid());
+       fclose(pf);
+}
+
+void vpnc_doit(struct sa_block *s)
+{
+       struct sigaction act;
+       struct encap_method meth;
+
+       const char *pidfile = config[CONFIG_PID_FILE];
+
+       switch (s->ipsec.encap_mode) {
+               case IPSEC_ENCAP_TUNNEL:
+                       encap_esp_new(&meth);
+                       gcry_create_nonce(&s->ipsec.ip_id, sizeof(uint16_t));
+                       break;
+               case IPSEC_ENCAP_UDP_TUNNEL:
+               case IPSEC_ENCAP_UDP_TUNNEL_OLD:
+                       encap_udp_new(&meth);
+                       break;
+               default:
+                       abort();
+       }
+       s->ipsec.em = &meth;
+
+       s->ipsec.rx.key_cry = s->ipsec.rx.key;
+       hex_dump("rx.key_cry", s->ipsec.rx.key_cry, s->ipsec.key_len, NULL);
+
+       s->ipsec.rx.key_md = s->ipsec.rx.key + s->ipsec.key_len;
+       hex_dump("rx.key_md", s->ipsec.rx.key_md, s->ipsec.md_len, NULL);
+
+       if (s->ipsec.cry_algo) {
+               gcry_cipher_open(&s->ipsec.rx.cry_ctx, s->ipsec.cry_algo, GCRY_CIPHER_MODE_CBC, 0);
+               gcry_cipher_setkey(s->ipsec.rx.cry_ctx, s->ipsec.rx.key_cry, s->ipsec.key_len);
+       } else {
+               s->ipsec.rx.cry_ctx = NULL;
+       }
+
+       s->ipsec.tx.key_cry = s->ipsec.tx.key;
+       hex_dump("tx.key_cry", s->ipsec.tx.key_cry, s->ipsec.key_len, NULL);
+
+       s->ipsec.tx.key_md = s->ipsec.tx.key + s->ipsec.key_len;
+       hex_dump("tx.key_md", s->ipsec.tx.key_md, s->ipsec.md_len, NULL);
+
+       if (s->ipsec.cry_algo) {
+               gcry_cipher_open(&s->ipsec.tx.cry_ctx, s->ipsec.cry_algo, GCRY_CIPHER_MODE_CBC, 0);
+               gcry_cipher_setkey(s->ipsec.tx.cry_ctx, s->ipsec.tx.key_cry, s->ipsec.key_len);
+       } else {
+               s->ipsec.tx.cry_ctx = NULL;
+       }
+
+       DEBUG(2, printf("remote -> local spi: %#08x\n", ntohl(s->ipsec.rx.spi)));
+       DEBUG(2, printf("local -> remote spi: %#08x\n", ntohl(s->ipsec.tx.spi)));
+
+       do_kill = 0;
+
+       sigaction(SIGHUP, NULL, &act);
+       if (act.sa_handler == SIG_DFL)
+               signal(SIGHUP, killit);
+
+       signal(SIGINT, killit);
+       signal(SIGTERM, killit);
+
+       chdir("/");
+
+       if (!opt_nd) {
+               pid_t pid;
+               if ((pid = fork()) < 0) {
+                       fprintf(stderr, "Warning, could not fork the child process!\n");
+               } else if (pid == 0) {
+                       close(0); open("/dev/null", O_RDONLY, 0666);
+                       close(1); open("/dev/null", O_WRONLY, 0666);
+                       close(2); open("/dev/null", O_WRONLY, 0666);
+                       setsid();
+               } else {
+                       printf("VPNC started in background (pid: %d)...\n", (int)pid);
+                       exit(0);
+               }
+               openlog("vpnc", LOG_PID | LOG_PERROR, LOG_DAEMON);
+               logmsg = syslog;
+       } else {
+               printf("VPNC started in foreground...\n");
+       }
+       write_pidfile(pidfile);
+
+       vpnc_main_loop(s);
+
+       if (pidfile)
+               unlink(pidfile); /* ignore errors */
+}
diff --git a/tunip.h b/tunip.h
new file mode 100644 (file)
index 0000000..170642c
--- /dev/null
+++ b/tunip.h
@@ -0,0 +1,129 @@
+/* IPSec ESP and AH support.
+   Copyright (C) 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: tunip.h 464 2011-08-22 14:19:38Z Antonio Borneo $
+*/
+
+#ifndef __TUNIP_H__
+#define __TUNIP_H__
+
+#include "isakmp.h"
+
+#include <time.h>
+#include <net/if.h>
+
+struct lifetime {
+       time_t   start;
+       uint32_t seconds;
+       uint32_t kbytes;
+       uint32_t rx;
+       uint32_t tx;
+};
+
+struct ike_sa {
+       uint32_t spi;
+       uint32_t seq_id; /* for replay protection (not implemented) */
+
+       uint8_t *key;
+       uint8_t *key_cry;
+       gcry_cipher_hd_t cry_ctx;
+       uint8_t *key_md;
+
+       /* Description of the packet being processed */
+       unsigned char *buf;
+       unsigned int bufsize, bufpayload, var_header_size;
+       int buflen;
+};
+
+struct encap_method; /* private to tunip.c */
+
+enum natt_active_mode_enum{
+       NATT_ACTIVE_NONE,
+       NATT_ACTIVE_CISCO_UDP, /* isakmp and esp on different ports => never encap */
+       NATT_ACTIVE_DRAFT_OLD, /* as in natt-draft 0 and 1 */
+       NATT_ACTIVE_RFC        /* draft 2 and RFC3947 / RFC3948 */
+};
+
+struct sa_block {
+       const char *pidfile;
+
+       int tun_fd; /* fd to host via tun/tap */
+       char tun_name[IFNAMSIZ];
+       uint8_t tun_hwaddr[ETH_ALEN];
+
+       struct in_addr dst; /* ip of concentrator, must be set */
+       struct in_addr src; /* local ip, from getsockname() */
+
+       struct in_addr opt_src_ip; /* configured local ip, can be 0.0.0.0 */
+
+       /* these sockets are connect()ed */
+       int ike_fd; /* fd over isakmp traffic, and in case of NAT-T esp too */
+       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];
+               uint8_t *key; /* ike encryption key */
+               size_t keylen;
+               uint8_t *initial_iv;
+               uint8_t *skeyid_a;
+               uint8_t *skeyid_d;
+               int auth_algo; /* PSK, PSK+Xauth, Hybrid ToDo: Cert/... */
+               int cry_algo, md_algo;
+               size_t ivlen, md_len;
+               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;
+               uint8_t *psk_hash;
+               uint8_t *sa_f, *idi_f;
+               size_t sa_size, idi_size;
+               uint8_t *dh_public;
+               struct group *dh_grp;
+               uint8_t i_nonce[20];
+               uint8_t *returned_hash;
+               int natd_type;
+               uint8_t *natd_us, *natd_them;
+       } ike;
+       struct in_addr our_address;
+       struct {
+               int do_pfs;
+               int cry_algo, md_algo;
+               size_t key_len, md_len;
+               size_t blk_len, iv_len;
+               uint16_t encap_mode;
+               uint16_t peer_udpencap_port;
+               enum natt_active_mode_enum natt_active_mode;
+               struct lifetime life;
+               struct ike_sa rx, tx;
+               struct encap_method *em;
+               uint16_t ip_id;
+       } ipsec;
+};
+
+extern int volatile do_kill;
+extern void vpnc_doit(struct sa_block *s);
+
+#endif
diff --git a/vpnc-disconnect b/vpnc-disconnect
new file mode 100755 (executable)
index 0000000..6806b93
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+pid=/var/run/vpnc/pid
+
+if [ $# -ne 0 ]; then
+       echo "Usage: $0" 1>&2
+       exit 1
+fi
+
+PID="$(cat "$pid" 2> /dev/null)"
+
+if [ -z "$PID" ]; then
+       echo "no vpnc found running"
+       exit 1
+fi
+
+if ! kill -0 "$PID" > /dev/null 2>&1; then
+       echo "no vpnc found running"
+       exit 1
+fi
+
+echo "Terminating vpnc daemon (pid: $PID)"
+exec kill $PID
diff --git a/vpnc-script b/vpnc-script
new file mode 100755 (executable)
index 0000000..91586f6
--- /dev/null
@@ -0,0 +1,686 @@
+#!/bin/sh
+# List of parameters passed through environment
+#* 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_MTU             -- mtu (often unset)
+#* INTERNAL_IP4_NETMASK         -- netmask (often unset)
+#* INTERNAL_IP4_NETMASKLEN      -- netmask length (often unset)
+#* INTERNAL_IP4_NETADDR         -- address of network (only present if netmask is set)
+#* INTERNAL_IP4_DNS             -- list of dns servers
+#* INTERNAL_IP4_NBNS            -- list of wins servers
+#* INTERNAL_IP6_ADDRESS         -- IPv6 address
+#* INTERNAL_IP6_NETMASK         -- IPv6 netmask
+#* INTERNAL_IP6_DNS             -- IPv6 list of dns 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)
+#* CISCO_IPV6_SPLIT_INC         -- number of networks in IPv6 split-network-list
+#* CISCO_IPV6_SPLIT_INC_%d_ADDR -- IPv6 network address
+#* CISCO_IPV6_SPLIT_INC_$%d_MASKLEN -- IPv6 subnet masklen
+
+# FIXMEs:
+
+# Section A: route handling
+
+# 1) The 3 values CISCO_SPLIT_INC_%d_PROTOCOL/SPORT/DPORT are currently being ignored
+#   In order to use them, we'll probably need os specific solutions
+#   * Linux: iptables -t mangle -I PREROUTING <conditions> -j ROUTE --oif $TUNDEV
+#       This would be an *alternative* to changing the routes (and thus 2) and 3)
+#       shouldn't be relevant at all)
+# 2) There are two different functions to set routes: generic routes and the
+#   default route. Why isn't the defaultroute handled via the generic route case?
+# 3) In the split tunnel case, all routes but the default route might get replaced
+#   without getting restored later. We should explicitely check and save them just
+#   like the defaultroute
+# 4) Replies to a dhcp-server should never be sent into the tunnel
+
+# Section B: Split DNS handling
+
+# 1) Maybe dnsmasq can do something like that
+# 2) Parse dns packets going out via tunnel and redirect them to original dns-server
+
+#env | sort
+#set -x
+
+# =========== script (variable) setup ====================================
+
+PATH=/sbin:/usr/sbin:$PATH
+
+OS="`uname -s`"
+
+DEFAULT_ROUTE_FILE=/var/run/vpnc/defaultroute
+RESOLV_CONF_BACKUP=/var/run/vpnc/resolv.conf-backup
+SCRIPTNAME=`basename $0`
+
+# some systems, eg. Darwin & FreeBSD, prune /var/run on boot
+if [ ! -d "/var/run/vpnc" ]; then
+       mkdir -p /var/run/vpnc
+       [ -x /sbin/restorecon ] && /sbin/restorecon /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
+if [ "$OS" = "SunOS" ]; then
+       route_syntax_interface="-interface"
+       ifconfig_syntax_ptpv6="$INTERNAL_IP6_ADDRESS"
+else
+       route_syntax_interface=""
+       ifconfig_syntax_ptpv6=""
+fi
+
+if [ -x /sbin/resolvconf ]; then # Optional tool on Debian, Ubuntu, Gentoo
+       MODIFYRESOLVCONF=modify_resolvconf_manager
+       RESTORERESOLVCONF=restore_resolvconf_manager
+elif [ -x /sbin/netconfig ]; then # tool on Suse after 11.1
+       MODIFYRESOLVCONF=modify_resolvconf_suse_netconfig
+       RESTORERESOLVCONF=restore_resolvconf_suse_netconfig
+elif [ -x /sbin/modify_resolvconf ]; then # Mandatory tool on Suse earlier than 11.1
+       MODIFYRESOLVCONF=modify_resolvconf_suse
+       RESTORERESOLVCONF=restore_resolvconf_suse
+else # Generic for any OS
+       MODIFYRESOLVCONF=modify_resolvconf_generic
+       RESTORERESOLVCONF=restore_resolvconf_generic
+fi
+
+# =========== tunnel interface handling ====================================
+
+do_ifconfig() {
+       if [ -n "$INTERNAL_IP4_MTU" ]; then
+               MTU=$INTERNAL_IP4_MTU
+       elif [ -n "$IPROUTE" ]; then
+               MTUDEV=`$IPROUTE route get "$VPNGATEWAY" | sed -ne 's/^.*dev \([a-z0-9]*\).*$/\1/p'`
+               MTU=`$IPROUTE link show "$MTUDEV" | sed -ne 's/^.*mtu \([[:digit:]]\+\).*$/\1/p'`
+               if [ -n "$MTU" ]; then
+                       MTU=`expr $MTU - 88`
+               fi
+       fi
+
+       if [ -z "$MTU" ]; then
+               MTU=1412
+       fi
+
+       # Point to point interface require a netmask of 255.255.255.255 on some systems
+       if [ -n "$IPROUTE" ]; then
+               $IPROUTE link set dev "$TUNDEV" up mtu "$MTU"
+               $IPROUTE addr add "$INTERNAL_IP4_ADDRESS/255.255.255.255" peer "$INTERNAL_IP4_ADDRESS" dev "$TUNDEV"
+       else
+               ifconfig "$TUNDEV" inet "$INTERNAL_IP4_ADDRESS" $ifconfig_syntax_ptp "$INTERNAL_IP4_ADDRESS" netmask 255.255.255.255 mtu ${MTU} up
+       fi
+
+       if [ -n "$INTERNAL_IP4_NETMASK" ]; then
+               set_network_route $INTERNAL_IP4_NETADDR $INTERNAL_IP4_NETMASK $INTERNAL_IP4_NETMASKLEN
+       fi
+
+       # If the netmask is provided, it contains the address _and_ netmask
+       if [ -n "$INTERNAL_IP6_ADDRESS" ] && [ -z "$INTERNAL_IP6_NETMASK" ]; then
+           INTERNAL_IP6_NETMASK="$INTERNAL_IP6_ADDRESS/128"
+       fi
+       if [ -n "$INTERNAL_IP6_NETMASK" ]; then
+           if [ -n "$IPROUTE" ]; then
+               $IPROUTE -6 addr add $INTERNAL_IP6_NETMASK dev $TUNDEV
+           else
+               # Unlike for Legacy IP, we don't specify the dest_address
+               # here on *BSD. OpenBSD for one will refuse to accept
+               # incoming packets to that address if we do.
+               # OpenVPN does the same (gives dest_address for Legacy IP
+               # but not for IPv6).
+               # Only Solaris needs it; hence $ifconfig_syntax_ptpv6
+               ifconfig "$TUNDEV" inet6 $INTERNAL_IP6_NETMASK $ifconfig_syntax_ptpv6 mtu $MTU up
+           fi
+       fi
+}
+
+destroy_tun_device() {
+       case "$OS" in
+       NetBSD|FreeBSD) # and probably others...
+               ifconfig "$TUNDEV" destroy
+               ;;
+       esac
+}
+
+# =========== route handling ====================================
+
+if [ -n "$IPROUTE" ]; then
+       fix_ip_get_output () {
+               sed -e 's/ /\n/g' | \
+                   sed -ne '1p;/via/{N;p};/dev/{N;p};/src/{N;p};/mtu/{N;p}'
+       }
+
+       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 replace default dev "$TUNDEV"
+               $IPROUTE route flush cache
+       }
+
+       set_network_route() {
+               NETWORK="$1"
+               NETMASK="$2"
+               NETMASKLEN="$3"
+               $IPROUTE route replace "$NETWORK/$NETMASKLEN" dev "$TUNDEV"
+               $IPROUTE route flush cache
+       }
+
+       reset_default_route() {
+               if [ -s "$DEFAULT_ROUTE_FILE" ]; then
+                       $IPROUTE route replace `cat "$DEFAULT_ROUTE_FILE"`
+                       $IPROUTE route flush cache
+                       rm -f -- "$DEFAULT_ROUTE_FILE"
+               fi
+       }
+
+       del_network_route() {
+               NETWORK="$1"
+               NETMASK="$2"
+               NETMASKLEN="$3"
+               $IPROUTE route $route_syntax_del "$NETWORK/$NETMASKLEN" dev "$TUNDEV"
+               $IPROUTE route flush cache
+       }
+
+       set_ipv6_default_route() {
+               # We don't save/restore IPv6 default route; just add a higher-priority one.
+               $IPROUTE -6 route add default dev "$TUNDEV" metric 1
+               $IPROUTE -6 route flush cache
+       }
+
+       set_ipv6_network_route() {
+               NETWORK="$1"
+               NETMASKLEN="$2"
+               $IPROUTE -6 route replace "$NETWORK/$NETMASKLEN" dev "$TUNDEV"
+               $IPROUTE route flush cache
+       }
+
+       reset_ipv6_default_route() {
+               $IPROUTE -6 route del default dev "$TUNDEV"
+               $IPROUTE route flush cache
+       }
+
+       del_ipv6_network_route() {
+               NETWORK="$1"
+               NETMASKLEN="$2"
+               $IPROUTE -6 route del "$NETWORK/$NETMASKLEN" dev "$TUNDEV"
+               $IPROUTE -6 route flush cache
+       }
+else # use route command
+       get_default_gw() {
+               # isn't -n supposed to give --numeric output?
+               # apperently not...
+               # Get rid of lines containing IPv6 addresses (':')
+               netstat -r -n | awk '/:/ { next; } /^(default|0\.0\.0\.0)/ { print $2; }'
+       }
+
+       set_vpngateway_route() {
+               route add -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`"
+       }
+
+       del_vpngateway_route() {
+               route $route_syntax_del -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`"
+       }
+
+       set_default_route() {
+               DEFAULTGW="`get_default_gw`"
+               echo "$DEFAULTGW" > "$DEFAULT_ROUTE_FILE"
+               route $route_syntax_del default $route_syntax_gw "$DEFAULTGW"
+               route add default $route_syntax_gw "$INTERNAL_IP4_ADDRESS" $route_syntax_interface
+       }
+
+       set_network_route() {
+               NETWORK="$1"
+               NETMASK="$2"
+               NETMASKLEN="$3"
+               del_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
+               route add -net "$NETWORK" $route_syntax_netmask "$NETMASK" $route_syntax_gw "$INTERNAL_IP4_ADDRESS" $route_syntax_interface
+       }
+
+       reset_default_route() {
+               if [ -s "$DEFAULT_ROUTE_FILE" ]; then
+                       route $route_syntax_del default $route_syntax_gw "`get_default_gw`" $route_syntax_interface
+                       route add default $route_syntax_gw `cat "$DEFAULT_ROUTE_FILE"`
+                       rm -f -- "$DEFAULT_ROUTE_FILE"
+               fi
+       }
+
+       del_network_route() {
+               case "$OS" in
+               Linux|NetBSD|Darwin|SunOS) # 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"
+       }
+
+       set_ipv6_default_route() {
+               route add -inet6 default "$INTERNAL_IP6_ADDRESS" $route_syntax_interface
+       }
+
+       set_ipv6_network_route() {
+               NETWORK="$1"
+               NETMASK="$2"
+               route add -inet6 -net "$NETWORK/$NETMASK" "$INTERNAL_IP6_ADDRESS" $route_syntax_interface
+               :
+       }
+
+       reset_ipv6_default_route() {
+               route $route_syntax_del -inet6 default "$INTERNAL_IP6_ADDRESS"
+               :
+       }
+
+       del_ipv6_network_route() {
+               NETWORK="$1"
+               NETMASK="$2"
+               route $route_syntax_del -inet6 "$NETWORK/$NETMASK" "$INTERNAL_IP6_ADDRESS"
+               :
+       }
+
+fi
+
+# =========== resolv.conf handling ====================================
+
+# =========== resolv.conf handling for any OS =========================
+
+modify_resolvconf_generic() {
+       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"
+
+       # 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< "$RESOLV_CONF_BACKUP"
+       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
+                               ;;
+                       search*)
+                               if [ -n "$CISCO_DEF_DOMAIN" ]; then
+                                       LINE="$LINE $CISCO_DEF_DOMAIN"
+                                       CISCO_DEF_DOMAIN=""
+                               fi
+                               ;;
+                       domain*)
+                               if [ -n "$CISCO_DEF_DOMAIN" ]; then
+                                       LINE="domain $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
+                       *)
+                               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
+                                       d.init
+                                       # next line overrides the default gateway and breaks split routing
+                                       # 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_ORIG" ]; then
+                                       scutil >/dev/null 2>&1 <<-EOF
+                                               open
+                                               get State:/Network/Service/$TUNDEV/DNS
+                                               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
+                               ;;
+               esac
+       fi
+}
+
+restore_resolvconf_generic() {
+       if [ ! -f "$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
+}
+# === resolv.conf handling via /sbin/netconfig (Suse 11.1) =====================
+
+# Suse provides a script that modifies resolv.conf. Use it because it will
+# restart/reload all other services that care about it (e.g. lwresd).  [unclear if this is still true, but probably --mlk]
+
+modify_resolvconf_suse_netconfig()
+{
+       /sbin/netconfig modify -s vpnc -i "$TUNDEV" <<-EOF
+               INTERFACE='$TUNDEV'
+               DNSSERVERS='$INTERNAL_IP4_DNS'
+               DNSDOMAIN='$CISCO_DEF_DOMAIN'
+               EOF
+}
+# Restore resolv.conf to old contents on Suse
+restore_resolvconf_suse_netconfig()
+{
+       /sbin/netconfig remove -s vpnc -i "$TUNDEV"
+}
+
+# === resolv.conf handling via /sbin/modify_resolvconf (Suse) =====================
+
+# Suse provides a script that modifies resolv.conf. Use it because it will
+# restart/reload all other services that care about it (e.g. lwresd).
+
+modify_resolvconf_suse()
+{
+       FULL_SCRIPTNAME=`readlink -f $0`
+       RESOLV_OPTS=''
+       test -n "$INTERNAL_IP4_DNS" && RESOLV_OPTS="-n \"$INTERNAL_IP4_DNS\""
+       test -n "$CISCO_DEF_DOMAIN" && RESOLV_OPTS="$RESOLV_OPTS -d $CISCO_DEF_DOMAIN"
+       test -n "$RESOLV_OPTS" && eval /sbin/modify_resolvconf modify -s vpnc -p $SCRIPTNAME -f $FULL_SCRIPTNAME -e $TUNDEV $RESOLV_OPTS -t \"This file was created by $SCRIPTNAME\"
+}
+
+# Restore resolv.conf to old contents on Suse
+restore_resolvconf_suse()
+{
+       FULL_SCRIPTNAME=`readlink -f $0`
+       /sbin/modify_resolvconf restore -s vpnc -p $SCRIPTNAME -f $FULL_SCRIPTNAME -e $TUNDEV
+}
+
+# === resolv.conf handling via /sbin/resolvconf (Debian, Ubuntu, Gentoo)) =========
+
+modify_resolvconf_manager() {
+       NEW_RESOLVCONF=""
+       for i in $INTERNAL_IP4_DNS; do
+               NEW_RESOLVCONF="$NEW_RESOLVCONF
+nameserver $i"
+       done
+       if [ -n "$CISCO_DEF_DOMAIN" ]; then
+               NEW_RESOLVCONF="$NEW_RESOLVCONF
+domain $CISCO_DEF_DOMAIN"
+       fi
+       echo "$NEW_RESOLVCONF" | /sbin/resolvconf -a $TUNDEV
+}
+
+restore_resolvconf_manager() {
+       /sbin/resolvconf -d $TUNDEV
+}
+
+# ========= Toplevel state handling  =======================================
+
+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 \
+                               -a ! -e /dev/net/misc/net/tun -a -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
+                               [ -x /sbin/restorecon ] && /sbin/restorecon /dev/net/tun
+                       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" = "GNU/kFreeBSD" ]; 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_connect() {
+       if [ -n "$CISCO_BANNER" ]; then
+               echo "Connect Banner:"
+               echo "$CISCO_BANNER" | while read LINE ; do echo "|" "$LINE" ; done
+               echo
+       fi
+
+       set_vpngateway_route
+       do_ifconfig
+       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}"
+                       if [ $NETWORK != "0.0.0.0" ]; then
+                               set_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
+                       else
+                               set_default_route
+                       fi
+                       i=`expr $i + 1`
+               done
+               for i in $INTERNAL_IP4_DNS ; do
+                       echo "$i" | grep : >/dev/null || \
+                               set_network_route "$i" "255.255.255.255" "32"
+               done
+       elif [ -n "$INTERNAL_IP4_ADDRESS" ]; then
+               set_default_route
+       fi
+       if [ -n "$CISCO_IPV6_SPLIT_INC" ]; then
+               i=0
+               while [ $i -lt $CISCO_IPV6_SPLIT_INC ] ; do
+                       eval NETWORK="\${CISCO_IPV6_SPLIT_INC_${i}_ADDR}"
+                       eval NETMASKLEN="\${CISCO_IPV6_SPLIT_INC_${i}_MASKLEN}"
+                       if [ $NETMASKLEN -lt 128 ]; then
+                               set_ipv6_network_route "$NETWORK" "$NETMASKLEN"
+                       else
+                               set_ipv6_default_route
+                       fi
+                       i=`expr $i + 1`
+               done
+               for i in $INTERNAL_IP4_DNS ; do
+                       if echo "$i" | grep : >/dev/null; then
+                               set_ipv6_network_route "$i" "128"
+                       fi
+               done
+       elif [ -n "$INTERNAL_IP6_NETMASK" -o -n "$INTERNAL_IP6_ADDRESS" ]; then
+               set_ipv6_default_route
+       fi
+
+       if [ -n "$INTERNAL_IP4_DNS" ]; then
+               $MODIFYRESOLVCONF
+       fi
+}
+
+do_disconnect() {
+       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}"
+                       if [ $NETWORK != "0.0.0.0" ]; then
+                               # FIXME: This doesn't restore previously overwritten
+                               #        routes.
+                               del_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
+                       else
+                               reset_default_route
+                       fi
+                       i=`expr $i + 1`
+               done
+               for i in $INTERNAL_IP4_DNS ; do
+                       del_network_route "$i" "255.255.255.255" "32"
+               done
+       else
+               reset_default_route
+       fi
+       if [ -n "$CISCO_IPV6_SPLIT_INC" ]; then
+               i=0
+               while [ $i -lt $CISCO_IPV6_SPLIT_INC ] ; do
+                       eval NETWORK="\${CISCO_IPV6_SPLIT_INC_${i}_ADDR}"
+                       eval NETMASKLEN="\${CISCO_IPV6_SPLIT_INC_${i}_MASKLEN}"
+                       if [ $NETMASKLEN -eq 0 ]; then
+                               reset_ipv6_default_route
+                       else
+                               del_ipv6_network_route "$NETWORK" "$NETMASKLEN"
+                       fi
+                       i=`expr $i + 1`
+               done
+               for i in $INTERNAL_IP6_DNS ; do
+                       del_ipv6_network_route "$i" "128"
+               done
+       elif [ -n "$INTERNAL_IP6_NETMASK" -o -n "$INTERNAL_IP6_ADDRESS" ]; then
+               reset_ipv6_default_route
+       fi
+
+       del_vpngateway_route
+
+       if [ -n "$INTERNAL_IP4_DNS" ]; then
+               $RESTORERESOLVCONF
+       fi
+       destroy_tun_device
+}
+
+#### Main
+
+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 --git a/vpnc-script-win b/vpnc-script-win
new file mode 100755 (executable)
index 0000000..2c39997
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+cscript `cygpath -w /etc/vpnc/vpnc-script-win.js`
diff --git a/vpnc-script-win.js b/vpnc-script-win.js
new file mode 100755 (executable)
index 0000000..1664d9b
--- /dev/null
@@ -0,0 +1,104 @@
+// 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 internal_ip4_netmask = "255.255.255.0"
+
+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") + "\"");
+
+       if (env("INTERNAL_IP4_NETMASK")) {
+           internal_ip4_netmask = env("INTERNAL_IP4_NETMASK");
+       }
+
+       echo("Configuring \"" + env("TUNDEV") + "\" interface...");
+       run("netsh interface ip set address \"" + env("TUNDEV") + "\" static " +
+           env("INTERNAL_IP4_ADDRESS") + " " + internal_ip4_netmask);
+
+       // 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 --git a/vpnc.8.template b/vpnc.8.template
new file mode 100644 (file)
index 0000000..b4cbfaf
--- /dev/null
@@ -0,0 +1,214 @@
+.\" Template to generate the vpnc-manpage
+.\" $Id: vpnc.8.template 511 2012-01-14 07:57:51Z Antonio Borneo $
+.\"
+.TH VPNC "8" "Warning: Just a template!" "vpnc man-template" "Warning: Just a template!"
+.\" Fake header just to make this file viewable with man.
+.\" ###makeman.pl: Replace header here!
+.SH NAME
+vpnc \- client for Cisco VPN3000 Concentrator, IOS and PIX
+.SH SYNOPSIS
+.B vpnc
+[\fI--version\fR] [\fI--print-config\fR] [\fI--help\fR] [\fI--long-help\fR] [\fIoptions\fR] [\fIconfig files\fR]
+.SH "DESCRIPTION"
+.PP
+This manual page documents briefly the
+\fBvpnc\fR and
+\fBvpnc\-disconnect\fR commands.
+.PP
+\fBvpnc\fR is a
+VPN client for the Cisco 3000 VPN  Concentrator,  creating  a IPSec-like
+connection as a tunneling network device for the local system. It uses
+the TUN/TAP driver in  Linux  kernel  2.4  and  above  and device tun(4)
+on BSD. The created connection is presented as a tunneling network
+device to the local system.
+.PP
+OBLIGATORY WARNING: the most used configuration (XAUTH authentication
+with pre-shared keys and password 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
+for split-tunneling these networks are added to the routing table.
+Otherwise the default-route will be modified to point to the tunnel.
+Further a host route to the concentrator is added in the later case.
+If the client host needs DHCP, care must be taken to add another
+host route to the DHCP-Server around the tunnel.
+.PP
+The \fBvpnc\-disconnect\fR command is used to terminate
+the connection previously created by \fBvpnc\fR
+and restore the previous routing configuration.
+
+.SH CONFIGURATION
+The daemon reads configuration data from the following places:
+.PD 0
+.IP \(bu
+command line options
+.IP \(bu
+config file(s) specified on the command line
+.IP \(bu
+/etc/vpnc/default.conf
+.IP \(bu
+/etc/vpnc.conf
+.IP \(bu
+prompting the user if not found above
+
+.PP
+
+vpnc can parse options and
+.B configuration files
+in any order. However the first
+place to set an option wins.
+configuration filenames
+which do not contain a /
+will be searched at
+.B /etc/vpnc/<filename>
+and
+.B /etc/vpnc/<filename>.conf.
+Otherwise
+.B <filename>
+and
+.B <filename>.conf
+will be used.
+If no configuration file
+is specified on the command-line
+at all, both
+.B /etc/vpnc/default.conf
+and
+.B /etc/vpnc.conf
+will be loaded.
+
+.SH OPTIONS
+The program options can be either given as arguments (but not all of them
+for security reasons) or be stored in a configuration file.
+.PD 0
+.\" ###makeman.pl: Insert options from help-output here!
+
+.HP
+\fB\-\-print\-config\fR
+.IP
+Prints your configuration; output can be used as vpnc.conf
+
+.SH FILES
+.I /etc/vpnc.conf
+.I /etc/vpnc/default.conf
+.RS
+The default configuration file. You can specify the same config
+directives as with command line options and additionally
+.B IPSec secret
+and
+.B Xauth password
+both supplying a cleartext password. Scrambled passwords from the Cisco
+configuration profiles can be used with
+.B IPSec obfuscated secret
+and
+.B Xauth obfuscated password.
+
+See
+.BR EXAMPLES
+for further details.
+.RE
+
+.I /etc/vpnc/*.conf
+.RS
+vpnc will read configuration files in this directory when
+the config filename (with or without .conf) is specified on the command line.
+.RE
+
+
+.SH EXAMPLES
+This is an example vpnc.conf with pre-shared keys:
+
+.RS
+.PD 0
+IPSec gateway vpn.example.com
+.P
+IPSec ID ExampleVpnPSK
+.P
+IKE Authmode psk
+.P
+IPSec secret PskS3cret!
+.P
+Xauth username user@example.com
+.P
+Xauth password USecr3t
+.PD
+.RE
+
+And another one with hybrid authentication (requires that vpnc was
+built with openssl support):
+
+.RS
+.PD 0
+IPSec gateway vpn.example.com
+.P
+IPSec ID ExampleVpnHybrid
+.P
+IKE Authmode hybrid
+.P
+
+.P
+CA-Dir /etc/vpnc
+.P
+\fBor\fR
+.P
+CA-File /etc/vpnc/vpn-example-com.pem
+.P
+
+.P
+IPSec secret HybS3cret?
+.P
+Xauth username user@example.com
+.P
+Xauth password 123456
+.PD
+.RE
+
+The lines begin with a keyword (no leading spaces!).
+The values start exactly one space after the keywords, and run to the end of
+line. This lets you put any kind of weird character (except CR, LF and NUL) in
+your strings, but it does mean you can't add comments after a string, or spaces
+before them.
+
+In case the the \fBCA-Dir\fR option is used, your certificate needs to be
+named something like 722d15bd.X, where X is a manually assigned number to
+make sure that files with colliding hashes have different names. The number
+can be derived from the certificate file itself:
+.P
+openssl x509 \-subject_hash \-noout \-in /etc/vpnc/vpn\-example\-com.pem
+
+See also the
+.B \-\-print\-config
+option to generate a config file, and the example file in the package
+documentation directory where more advanced usage is demonstrated.
+
+Advanced features like manual setting of multiple target routes and
+disabling /etc/resolv.conf rewriting is documented in the README of the
+vpnc package.
+
+.SH TODO
+.PD 0
+Certificate support (Pre-Shared-Key + XAUTH is known to be insecure).
+.P
+Further points can be found in the TODO file.
+.PD
+
+.SH AUTHOR
+This man-page has been written by Eduard Bloch <blade(at)debian.org> and
+Christian Lackas <delta(at)lackas.net>, based on vpnc README by
+Maurice Massar <vpnc(at)unix\-ag.uni\-kl.de>.
+Permission is
+granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+.PP
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common\-licenses/GPL.
+.SH "SEE ALSO"
+.BR pcf2vpnc (1),
+.BR cisco\-decrypt (1),
+.BR ip (8),
+.BR ifconfig (8),
+.BR route (1),
+.BR http://www.unix\-ag.uni\-kl.de/~massar/vpnc/
diff --git a/vpnc.c b/vpnc.c
new file mode 100644 (file)
index 0000000..fc78c23
--- /dev/null
+++ b/vpnc.c
@@ -0,0 +1,3875 @@
+/* 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 516 2012-03-24 15:07:08Z Antonio Borneo $
+*/
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <ctype.h>
+
+#include <gcrypt.h>
+
+#include "crypto.h"
+#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"
+
+#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-ipsra-isakmp-xauth-06.txt"/8 */
+       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[] = { /* "CISCO-UNITY"/14 + major + minor */
+       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_03[] = { /* "draft-ietf-ipsec-nat-t-ike-03" */
+       0x7d, 0x94, 0x19, 0xa6, 0x53, 0x10, 0xca, 0x6f,
+       0x2c, 0x17, 0x9d, 0x92, 0x15, 0x52, 0x9d, 0x56
+};
+const unsigned char VID_NATT_RFC[] = { /* "RFC 3947" */
+       0x4A, 0x13, 0x1C, 0x81, 0x07, 0x03, 0x58, 0x45,
+       0x5C, 0x57, 0x28, 0xF2, 0x0E, 0x95, 0x45, 0x2F
+};
+const unsigned char VID_DWR[] = { /* DWR: Delete with reason */
+       0x2D, 0x79, 0x22, 0xC6, 0xB3, 0x01, 0xD9, 0xB0,
+       0xE1, 0x34, 0x27, 0x39, 0xE9, 0xCF, 0xBB, 0xD5
+};
+/* Cisco Unknown1:
+ *const unsigned char VID_CISCO_UNKNOWN_1[] = {
+ *     1f07f70eaa6514d3b0fa96542a500407
+ *};
+ */
+
+const unsigned char VID_NATSI[] = { /* NaT-SI */
+       0x4e, 0x61, 0x54, 0x2d, 0x53, 0x49
+};
+const unsigned char VID_NATSI_LONG[] = { /* NaT-SI sent by client */
+       0x4e, 0x61, 0x54, 0x2d, 0x53, 0x49,
+       0x00, 0x00, 0x00, 0x00, /* 20 bytes almost random (MD5? of what?) */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+const unsigned char VID_CISCO_FRAG[] = { /* "FRAGMENTATION" */
+       0x40, 0x48, 0xB7, 0xD5, 0x6E, 0xBC, 0xE8, 0x85,
+       0x25, 0xE7, 0xDE, 0x7F, 0x00, 0xD6, 0xC2, 0xD3,
+       0x80, 0x00, 0x00, 0x00
+};
+const unsigned char VID_NETSCREEN_15[] = { /* netscreen 15 */
+       0x16, 0x6f, 0x93, 0x2d, 0x55, 0xeb, 0x64, 0xd8,
+       0xe4, 0xdf, 0x4f, 0xd3, 0x7e, 0x23, 0x13, 0xf0,
+       0xd0, 0xfd, 0x84, 0x51, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00
+};
+const unsigned char VID_HEARTBEAT_NOTIFY[] = { /* Heartbeat Notify */
+       0x48, 0x65, 0x61, 0x72, 0x74, 0x42, 0x65, 0x61,
+       0x74, 0x5f, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79,
+       0x38, 0x6b, 0x01, 0x00
+};
+const unsigned char VID_NORTEL_CONT_09[] = { /* BNES: Bay Networks Enterprise Switch + version/id of some kind */
+       0x42, 0x4e, 0x45, 0x53, 0x00, 0x00, 0x00, 0x09
+};
+const unsigned char VID_NORTEL_CONT_0A[] = {
+       0x42, 0x4e, 0x45, 0x53, 0x00, 0x00, 0x00, 0x0a
+};
+
+const unsigned char FW_UNKNOWN_TYPEINFO[] = {
+       0x80, 0x01, 0x00, 0x01, 0x80, 0x02, 0x00, 0x01,
+       0x80, 0x03, 0x00, 0x02
+};
+
+struct vid_element {
+       const unsigned char* valueptr;
+       const uint16_t length;
+       const char* descr;
+};
+
+const struct vid_element vid_list[] = {
+       { VID_XAUTH,            sizeof(VID_XAUTH),      "Xauth" },
+       { VID_DPD,              sizeof(VID_DPD),        "DPD" },
+       { VID_UNITY,            sizeof(VID_UNITY),      "Cisco Unity" },
+       { VID_NATT_00,          sizeof(VID_NATT_00),    "Nat-T 00" },
+       { VID_NATT_01,          sizeof(VID_NATT_01),    "Nat-T 01" },
+       { VID_NATT_02,          sizeof(VID_NATT_02),    "Nat-T 02" },
+       { VID_NATT_02N,         sizeof(VID_NATT_02N),   "Nat-T 02N" },
+       { VID_NATT_03,          sizeof(VID_NATT_03),    "Nat-T 03" },
+       { VID_NATT_RFC,         sizeof(VID_NATT_RFC),   "Nat-T RFC" },
+       { VID_DWR,              sizeof(VID_DWR),        "Delete With Reason" },
+       { VID_CISCO_FRAG,       sizeof(VID_CISCO_FRAG), "Cisco Fragmentation" },
+       { VID_NETSCREEN_15,     sizeof(VID_NETSCREEN_15),       "Netscreen 15" },
+       { VID_NORTEL_CONT_09,   sizeof(VID_NORTEL_CONT_09),     "Nortel Contivity v9" },
+       { VID_NORTEL_CONT_0A,   sizeof(VID_NORTEL_CONT_0A),     "Nortel Contivity v10" },
+       { VID_HEARTBEAT_NOTIFY, sizeof(VID_HEARTBEAT_NOTIFY),   "Heartbeat Notify" },
+       { VID_NATSI_LONG,       sizeof(VID_NATSI_LONG),         "Netlock NaT-SI" },
+       { VID_NATSI,            sizeof(VID_NATSI),              "Netlock NaT-SI" },
+
+       { NULL, 0, NULL }
+};
+
+// These vars are in use by NORTELVPN
+static int ipsec_cry_algo, ipsec_hash_algo, dh_group;
+static uint16_t encap_mode = IPSEC_ENCAP_TUNNEL;
+static void phase2_fatal(struct sa_block *s, const char *msg, int id);
+
+
+/* What are DWR-Code and DWR-Text ? */
+
+static uint8_t r_packet[8192];
+static ssize_t r_length;
+
+void print_vid(const unsigned char *vid, uint16_t len) {
+
+       int vid_index = 0;
+
+       if (opt_debug < 2)
+               return;
+
+       while (vid_list[vid_index].length) {
+               if (len == vid_list[vid_index].length &&
+                       memcmp(vid_list[vid_index].valueptr, vid, len) == 0) {
+                       printf("   (%s)\n", vid_list[vid_index].descr);
+                       return;
+               }
+               vid_index++;
+       }
+       printf("   (unknown)\n");
+}
+
+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);
+
+       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;
+       socklen_t len = sizeof(name);
+
+       /* create the socket */
+       sock = socket(PF_INET, SOCK_DGRAM, 0);
+       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, "Error binding to source port. Try '--local-port 0'\nFailed to bind to %s:%d", inet_ntoa(s->opt_src_ip), 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 cleanup(struct sa_block *s) {
+       if (s->ike_fd != 0) {
+               close(s->ike_fd);
+               s->ike_fd = 0;
+       }
+       if (s->esp_fd != 0) {
+               close(s->esp_fd);
+               s->esp_fd = 0;
+       }
+       if (s->ike.resend_hash) {
+               free(s->ike.resend_hash);
+               s->ike.resend_hash = NULL;
+       }
+       if (s->ike.skeyid_d) {
+               free(s->ike.skeyid_d);
+               s->ike.skeyid_d = NULL;
+       }
+       if (s->ike.skeyid_a) {
+               free(s->ike.skeyid_a);
+               s->ike.skeyid_a = NULL;
+       }
+       if (s->ike.initial_iv) {
+               free(s->ike.initial_iv);
+               s->ike.initial_iv = NULL;
+       }
+       if (s->ike.current_iv) {
+               free(s->ike.current_iv);
+               s->ike.current_iv = NULL;
+       }
+       if (s->ike.key) {
+               free(s->ike.key);
+               s->ike.key = NULL;
+       }
+       if (s->ipsec.rx.key) {
+               free(s->ipsec.rx.key);
+               s->ipsec.rx.key = NULL;
+       }
+       if (s->ipsec.tx.key) {
+               free(s->ipsec.tx.key);
+               s->ipsec.tx.key = NULL;
+       }
+       if (s->ipsec.rx.cry_ctx) {
+               gcry_cipher_close(s->ipsec.rx.cry_ctx);
+               s->ipsec.rx.cry_ctx = NULL;
+       }
+       if (s->ipsec.tx.cry_ctx) {
+               gcry_cipher_close(s->ipsec.tx.cry_ctx);
+               s->ipsec.tx.cry_ctx = NULL;
+       }
+}
+
+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 init_netaddr(struct in_addr *net, const char *string)
+{
+       char *p;
+
+       if ((p = strchr(string, '/')) != NULL) {
+               char *host = xallocc(p - string + 1);
+               memcpy(host, string, p - string + 1);
+               host[p - string] = '\0';
+               init_sockaddr((struct in_addr *)net, host);
+               free(host);
+               if (strchr(p + 1, '.') != NULL)
+                       init_sockaddr(net + 1, p + 1);
+               else {
+                       int bits = atoi(p + 1);
+                       unsigned long mask = (1 << bits) - 1;
+                       memcpy((char *)(net + 1), (char *)&mask, 4);
+               }
+       } else {
+               memset((char *)net, 0, 8);
+       }
+}
+
+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");
+#ifdef FD_CLOEXEC
+       /* do not pass socket to vpnc-script, etc. */
+       fcntl(s->tun_fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+       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);
+       }
+
+       unsetenv("INTERNAL_IP4_MTU");
+       if (config[CONFIG_IF_MTU]) {
+               int mtu;
+
+               mtu = atoi(config[CONFIG_IF_MTU]);
+               if (mtu < 0 || mtu > 65535) {
+                       DEBUG(1, printf("ignore MTU option. Use automatic detection\n"));
+                       mtu = 0;
+               }
+               if (mtu > 0) {
+                       char *strbuf;
+                       asprintf(&strbuf, "%d", mtu);
+                       setenv("INTERNAL_IP4_MTU", strbuf, 1);
+                       free(strbuf);
+               }
+       }
+}
+
+static void config_tunnel(struct sa_block *s)
+{
+       setenv("VPNGATEWAY", inet_ntoa(s->dst), 1);
+       setenv("reason", "connect", 1);
+       system(config[CONFIG_SCRIPT]);
+}
+
+static void close_tunnel(struct sa_block *s)
+{
+       setenv("reason", "disconnect", 1);
+       system(config[CONFIG_SCRIPT]);
+       tun_close(s->tun_fd, s->tun_name);
+}
+
+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 < 0)
+               error(1, errno, "receiving packet");
+       if ((unsigned int)recvsize > recvbufsize)
+               error(1, errno, "received packet too large for buffer");
+
+       /* skip (not only) NAT-T draft-0 keepalives */
+       if ( /* (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) && */
+           (recvsize == 1) && (*((u_char *)(recvbuf)) == 0xff))
+       {
+               if ((s->ipsec.natt_active_mode != NATT_ACTIVE_DRAFT_OLD))
+               {
+                       DEBUG(2, printf("Received UDP NAT-Keepalive bug nat active mode incorrect: %d\n", s->ipsec.natt_active_mode));
+               }
+               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 (s->ike.resend_hash && memcmp(s->ike.resend_hash, resend_check_hash, hash_len) == 0) {
+               free(resend_check_hash);
+               /* FIXME: if we get a retransmission, we probably should do a retransmission too */
+               DEBUG(2, printf("Received duplicated packet, dropping it!\n"));
+               return -1;
+       }
+       if (!s->ike.resend_hash) {
+               s->ike.resend_hash = resend_check_hash;
+       } else {
+               memcpy(s->ike.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);
+               memmove((char*)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, s->ike.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, (char *)recvbuf+4, recvsize);
+       }
+
+       DEBUGTOP(3, printf("\n receiving: <========================\n"));
+
+       /* Wait at least 2s for a response or 4 times the time it took
+        * last time.  */
+       if (start >= end)
+               s->ike.timeout = 2000;
+       else
+               s->ike.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 packet 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;
+
+       /* Some users report "payload ... not padded..." errors. It seems that they
+        * are harmless, so ignore and fix the symptom
+        */
+       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));
+               hex_dump("Payload", r_packet, r_length, NULL);
+               if (r_length < ISAKMP_PAYLOAD_O ) {
+                       return ISAKMP_N_UNEQUAL_PAYLOAD_LENGTHS;
+               }
+               r_length -= (r_length - ISAKMP_PAYLOAD_O) % s->ike.ivlen;
+       }
+
+       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) {
+                       if (r) free_isakmp_packet(r);
+                       return reject;
+               }
+       }
+
+       /* Verify the basic stuff.  */
+       if (r->flags != ISAKMP_FLAG_E) {
+               free_isakmp_packet(r);
+               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) {
+                       free_isakmp_packet(r);
+                       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);
+
+               DEBUG(3,printf("hashlen: %lu\n", (unsigned long)s->ike.md_len));
+               DEBUG(3,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) {
+                       free_isakmp_packet(r);
+                       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_payloads(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 * nonce_i, int ni_len, uint8_t * nonce_r, int nr_len)
+{
+       uint8_t *p_flat;
+       size_t p_size;
+       ssize_t recvlen;
+
+       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);
+       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;
+
+       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);
+}
+
+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);
+       *((uint32_t *) pl->u.n.data) = htonl(seqno);
+       gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
+       /* 2007-09-06 JKU/ZID: Sonicwall drops non hashed r_u_there-requests */
+       sendrecv_phase2(s, pl, ISAKMP_EXCHANGE_INFORMATIONAL, msgid,
+               1 , NULL, 0, NULL, 0);
+}
+
+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 send_delete_ipsec(struct sa_block *s)
+{
+       /* 2007-08-31 JKU/ZID: Sonicwall doesn't like the chained
+        * request but wants them split. Cisco does fine with it. */
+       DEBUGTOP(2, printf("S7.10 send ipsec termination message\n"));
+       {
+               struct isakmp_payload *d_ipsec;
+               uint8_t del_msgid;
+
+               gcry_create_nonce((uint8_t *) & del_msgid, sizeof(del_msgid));
+               d_ipsec = new_isakmp_payload(ISAKMP_PAYLOAD_D);
+               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, 0, NULL, 0);
+       }
+}
+
+static void send_delete_isakmp(struct sa_block *s)
+{
+       DEBUGTOP(2, printf("S7.11 send isakmp termination message\n"));
+       {
+               struct isakmp_payload *d_isakmp;
+               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);
+               sendrecv_phase2(s, d_isakmp, ISAKMP_EXCHANGE_INFORMATIONAL,
+                       del_msgid, 1, NULL, 0, NULL, 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);
+
+       send_delete_isakmp(s);
+
+       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 mask_to_masklen(struct in_addr mask)
+{
+       int len;
+       uint32_t addr;
+
+       addr = ntohl(mask.s_addr);
+       for (len = 0; addr; addr <<= 1, len++)
+               ;
+       return len;
+}
+
+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("CISCO_IPV6_SPLIT_INC");
+       unsetenv("INTERNAL_IP4_NBNS");
+       unsetenv("INTERNAL_IP4_DNS");
+       unsetenv("INTERNAL_IP4_NETMASK");
+       unsetenv("INTERNAL_IP4_ADDRESS");
+       unsetenv("INTERNAL_IP6_DNS");
+       unsetenv("INTERNAL_IP6_NETMASK");
+       unsetenv("INTERNAL_IP6_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 {
+                               uint32_t netaddr = s->our_address.s_addr & ((struct in_addr *)(a->u.lots.data))->s_addr;
+                               addenv_ipv4("INTERNAL_IP4_NETMASK", a->u.lots.data);
+                               asprintf(&strbuf, "%d", mask_to_masklen(*((struct in_addr *)a->u.lots.data)));
+                               setenv("INTERNAL_IP4_NETMASKLEN", strbuf, 1);
+                               free(strbuf);
+                               addenv_ipv4("INTERNAL_IP4_NETADDR",  (uint8_t *)&netaddr);
+                       }
+                       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:
+               case ISAKMP_MODECFG_ATTRIB_NORTEL_DEF_DOMAIN_A:
+               case ISAKMP_MODECFG_ATTRIB_NORTEL_DEF_DOMAIN_B:
+                       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_NORTEL_SPLIT_INV:
+                       DEBUG(0, printf("Warning: Unsupported option \"Inverse Split Tunnel\"\n"));
+                       break;
+
+               case ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC:
+               case ISAKMP_MODECFG_ATTRIB_NORTEL_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 */
+                               asprintf(&strbuf, "CISCO_SPLIT_INC_%d_MASKLEN", i);
+                               asprintf(&strbuf2, "%d", mask_to_masklen(a->u.acl.acl_ent[i].mask));
+                               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;
+       if (opt_vendor == VENDOR_NORTEL) {
+               a = new_isakmp_attribute_16(IKE_ATTRIB_NORTEL_CLIENT_ID,
+                       opt_nortel_client_id, a);
+       } else {
+               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_AUTH_METHOD, auth, a);
+       a = new_isakmp_attribute_16(IKE_ATTRIB_GROUP_DESC, dh_group, 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_group_ike = 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;
+
+       if (opt_vendor == VENDOR_NORTEL) {
+               auth = 0;
+               if ((opt_auth_mode == AUTH_MODE_CERT) &&
+               (supp_auth[auth].ike_sa_id != IKE_AUTH_RSA_SIG) &&
+                       (supp_auth[auth].ike_sa_id != IKE_AUTH_DSS)) {
+               } else {
+                       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_group_ike, 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;
+                               }
+                       }
+               }
+       } else {
+               for (auth = 0; supp_auth[auth].name != NULL; auth++) {
+                       if (opt_auth_mode == AUTH_MODE_CERT) {
+                               if ((supp_auth[auth].ike_sa_id != IKE_AUTH_RSA_SIG) &&
+                                       (supp_auth[auth].ike_sa_id != IKE_AUTH_DSS))
+                                       continue;
+                       } else if (opt_auth_mode == AUTH_MODE_HYBRID) {
+                               if ((supp_auth[auth].ike_sa_id != IKE_AUTH_HybridInitRSA) &&
+                                       (supp_auth[auth].ike_sa_id != IKE_AUTH_HybridInitDSS))
+                                       continue;
+                       } else {
+                               if (supp_auth[auth].ike_sa_id == IKE_AUTH_HybridInitRSA ||
+                                       supp_auth[auth].ike_sa_id == IKE_AUTH_HybridInitDSS ||
+                                       supp_auth[auth].ike_sa_id == IKE_AUTH_RSA_SIG ||
+                                       supp_auth[auth].ike_sa_id == IKE_AUTH_DSS)
+                                       continue;
+                       }
+                       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_group_ike, 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 {
+               DEBUG(2, printf("got unknown ike lifetime attributes af %d len %d\n",
+                                       a->next->af, a->next->u.lots.length));
+               return;
+       }
+
+       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;
+
+       /* FIXME: for notice-payloads: write a seperate function to handle them */
+       /* bug: this may process lifetime-attributes of SAs twice but to no consequence */
+       if (a->next->next != NULL && a->next->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE)
+               lifetime_ipsec_process(s, a->next->next);
+}
+
+static void do_phase1_am_init(struct sa_block *s)
+{
+
+       s->ike.natd_type = 0;
+       s->ike.natd_us = s->ike.natd_them = NULL;
+       s->ike.sa_f = s->ike.idi_f = NULL;
+
+       DEBUGTOP(2, printf("S4.1 create_nonce\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(s->ike.i_nonce, sizeof(s->ike.i_nonce));
+       hex_dump("i_nonce", s->ike.i_nonce, sizeof(s->ike.i_nonce), NULL);
+       DEBUGTOP(2, printf("S4.2 dh setup\n"));
+       /* Set up the Diffie-Hellman stuff.  */
+       {
+               s->ike.dh_grp = group_get(get_dh_group_ike()->my_id);
+               s->ike.dh_public = xallocc(dh_getlen(s->ike.dh_grp));
+               dh_create_exchange(s->ike.dh_grp, s->ike.dh_public);
+               hex_dump("dh_public", s->ike.dh_public, dh_getlen(s->ike.dh_grp), NULL);
+       }
+
+}
+
+static void do_phase1_am_packet1(struct sa_block *s, const char *key_id)
+{
+       DEBUGTOP(2, printf("S4.3 AM packet_1\n"));
+       /* Create the first packet.  */
+       {
+               struct isakmp_packet *p1;
+               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();
+               flatten_isakmp_payload(l, &s->ike.sa_f, &s->ike.sa_size);
+               l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_KE, s->ike.dh_public, dh_getlen(s->ike.dh_grp));
+               l->next->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE,
+                       s->ike.i_nonce, sizeof(s->ike.i_nonce));
+               l = l->next->next;
+               l->next = new_isakmp_payload(ISAKMP_PAYLOAD_ID);
+               l = l->next;
+               if (opt_vendor == VENDOR_CISCO || opt_vendor == VENDOR_NORTEL)
+                       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 = ISAKMP_PORT; /* this must be 500, see rfc2407, 4.6.2 */
+               if (opt_vendor == VENDOR_NORTEL) {
+                       if (opt_auth_mode == AUTH_MODE_NORTEL_USERNAME)
+                               l->u.id.length = 20;
+                       else
+                               l->u.id.length = 24;
+                       l->u.id.data = xallocc(l->u.id.length);
+                       gcry_md_hash_buffer(GCRY_MD_SHA1, l->u.id.data, key_id, strlen(key_id));
+                       /* memcpy(l->u.id.data, key_id, strlen(key_id)); */
+               } else {
+                       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));
+               }
+               flatten_isakmp_payload(l, &s->ike.idi_f, &s->ike.idi_size);
+
+               if (opt_vendor == VENDOR_NORTEL) {
+                       if (opt_natt_mode == NATT_NORTEL_UDP)
+                               l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                                       VID_NATSI_LONG, sizeof(VID_NATSI_LONG));
+               } else { /* (opt_vendor != VENDOR_NORTEL) */
+                       l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                               VID_XAUTH, sizeof(VID_XAUTH));
+                       l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_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,
+                                       VID_NATT_RFC, sizeof(VID_NATT_RFC));
+                               l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                                       VID_NATT_03, sizeof(VID_NATT_03));
+                               l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                                       VID_NATT_02N, sizeof(VID_NATT_02N));
+                               l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                                       VID_NATT_02, sizeof(VID_NATT_02));
+                               l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                                       VID_NATT_01, sizeof(VID_NATT_01));
+                               l = l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                                       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->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                                       VID_DPD, sizeof(VID_DPD));
+                       }
+               }
+               flatten_isakmp_packet(p1, &pkt, &pkt_len, 0);
+               free_isakmp_packet(p1);
+
+               /* Now, send that packet and receive a new one.  */
+               r_length = sendrecv(s, r_packet, sizeof(r_packet), pkt, pkt_len, 0);
+               free(pkt);
+       }
+}
+
+static void do_phase1_am_packet2(struct sa_block *s, const char *key_id, const char *shared_key)
+{
+       DEBUGTOP(2, printf("S4.4 AM_packet2\n"));
+       /* Decode the recieved packet.  */
+       {
+               int reject, ret;
+               struct isakmp_packet *r;
+               struct isakmp_payload *rp;
+               struct isakmp_payload *nonce = NULL;
+               struct isakmp_payload *ke = NULL;
+               struct isakmp_payload *hash = NULL;
+               struct isakmp_payload *sig = NULL;
+               struct isakmp_payload *idp = NULL;
+               int seen_sa = 0;
+               uint8_t *psk_skeyid;
+               uint8_t *skeyid;
+               gcry_md_hd_t skeyid_ctx;
+               uint8_t *dh_shared_secret;
+               int seen_natd = 0, seen_natd_them = 0, seen_natd_us = 0;
+               int seen_natsi = 0;
+               int natt_draft = -1;
+               crypto_ctx *cctx;
+               crypto_error *crerr = NULL;
+
+               cctx = crypto_ctx_new (&crerr);
+               if (crerr)
+                       crypto_call_error(crerr);
+
+               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: */
+                                               case IKE_ATTRIB_NORTEL_CLIENT_ID:
+                                                       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 \"encryption\" 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_CERT:
+                               if (rp->u.cert.encoding == ISAKMP_CERT_X509_SIG) {
+                                       hex_dump("cert", rp->u.cert.data, rp->u.cert.length, NULL);
+
+                                       ret = crypto_push_cert(cctx,
+                                                              (const unsigned char *) rp->u.cert.data,
+                                                              rp->u.cert.length,
+                                                              &crerr);
+                                       if (ret)
+                                               crypto_call_error(crerr);
+                               }
+                               break;
+                       case ISAKMP_PAYLOAD_SIG:
+                               sig = rp;
+                               break;
+                       case ISAKMP_PAYLOAD_VID:
+                               if (rp->u.vid.length == sizeof(VID_XAUTH)
+                                       && memcmp(rp->u.vid.data, VID_XAUTH,
+                                               sizeof(VID_XAUTH)) == 0) {
+                                       DEBUG(2, printf("peer is XAUTH capable (draft-ietf-ipsec-isakmp-xauth-06)\n"));
+                               } else if (rp->u.vid.length == sizeof(VID_NATT_RFC)
+                                       && memcmp(rp->u.vid.data, VID_NATT_RFC,
+                                               sizeof(VID_NATT_RFC)) == 0) {
+                                       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(VID_NATT_03)
+                                       && memcmp(rp->u.vid.data, VID_NATT_03,
+                                               sizeof(VID_NATT_03)) == 0) {
+                                       if (natt_draft < 1) natt_draft = 2;
+                                       DEBUG(2, printf("peer is NAT-T capable (draft-03)\n"));
+                               } else if (rp->u.vid.length == sizeof(VID_NATT_02N)
+                                       && memcmp(rp->u.vid.data, VID_NATT_02N,
+                                               sizeof(VID_NATT_02N)) == 0) {
+                                       if (natt_draft < 1) natt_draft = 2;
+                                       DEBUG(2, printf("peer is NAT-T capable (draft-02)\\n\n")); /* sic! */
+                               } else if (rp->u.vid.length == sizeof(VID_NATT_02)
+                                       && memcmp(rp->u.vid.data, VID_NATT_02,
+                                               sizeof(VID_NATT_02)) == 0) {
+                                       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(VID_NATT_01)
+                                       && memcmp(rp->u.vid.data, VID_NATT_01,
+                                               sizeof(VID_NATT_01)) == 0) {
+                                       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(VID_NATT_00)
+                                       && memcmp(rp->u.vid.data, VID_NATT_00,
+                                               sizeof(VID_NATT_00)) == 0) {
+                                       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 if (rp->u.vid.length == sizeof(VID_NETSCREEN_15)
+                                       && memcmp(rp->u.vid.data, VID_NETSCREEN_15,
+                                               sizeof(VID_NETSCREEN_15)) == 0) {
+                                       DEBUG(2, printf("peer is using ScreenOS 5.3, 5.4 or 6.0\n"));
+                               } else if (rp->u.vid.length == sizeof(VID_HEARTBEAT_NOTIFY)
+                                       && memcmp(rp->u.vid.data, VID_HEARTBEAT_NOTIFY,
+                                               sizeof(VID_HEARTBEAT_NOTIFY)) == 0) {
+                                       DEBUG(2, printf("peer sent Heartbeat Notify payload\n"));
+                               } else if (rp->u.vid.length == sizeof(VID_NATSI)
+                                       && memcmp(rp->u.vid.data, VID_NATSI,
+                                               sizeof(VID_NATSI)) == 0) {
+                                       seen_natsi = 1;
+                                       DEBUG(2, printf("peer is Netlock NaT-SI\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:
+                               s->ike.natd_type = rp->type;
+                               DEBUG(2, printf("peer is using type %d%s for NAT-Discovery payloads\n",
+                                       s->ike.natd_type, val_to_string(s->ike.natd_type, isakmp_payload_enum_array)));
+                               if (!seen_sa) {
+                                       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);
+
+                                       s->ike.natd_us = xallocc(s->ike.md_len);
+                                       s->ike.natd_them = xallocc(s->ike.md_len);
+                                       memcpy(s->ike.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(s->ike.natd_them, gcry_md_read(hm, 0), s->ike.md_len);
+                                       gcry_md_close(hm);
+                                       seen_natd = 1;
+                               } else {
+                                       if (memcmp(s->ike.natd_them, rp->u.natd.data, s->ike.md_len) == 0)
+                                               seen_natd_them = 1;
+                               }
+                               break;
+                       case ISAKMP_PAYLOAD_N:
+                               if (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"));
+                               } else {
+                                       DEBUG(1, printf("rejecting ISAKMP_PAYLOAD_N, type is not lifetime\n"));
+                                       reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
+                               }
+                               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(s->ike.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;
+
+               /* Decide if signature or hash is expected (sig only if vpnc is initiator of hybrid-auth */
+               if (reject == 0 &&
+                       ((opt_auth_mode == AUTH_MODE_PSK) ||
+                        (opt_vendor == VENDOR_NORTEL && opt_auth_mode != AUTH_MODE_CERT)) &&
+                       (hash == NULL || hash->u.hash.length != s->ike.md_len))
+                       reject = ISAKMP_N_INVALID_HASH_INFORMATION;
+               if (reject == 0 && sig == NULL &&
+                       (opt_auth_mode == AUTH_MODE_CERT ||
+                        opt_auth_mode == AUTH_MODE_HYBRID))
+                       reject = ISAKMP_N_INVALID_SIGNATURE;
+               if (reject != 0)
+                       error(1, 0, "response was invalid [3]: %s(%d)", val_to_string(reject, isakmp_notify_enum_array), reject);
+
+               /* Determine the shared secret.  */
+               dh_shared_secret = xallocc(dh_getlen(s->ike.dh_grp));
+               dh_create_shared(s->ike.dh_grp, dh_shared_secret, ke->u.ke.data);
+               hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(s->ike.dh_grp), NULL);
+               /* Generate SKEYID.  */
+               if (opt_vendor == VENDOR_NORTEL) {
+                       gcry_md_hd_t hm;
+                       char shared_key_sha1[20];
+                       uint8_t *hmac;
+
+                       gcry_md_hash_buffer(GCRY_MD_SHA1, shared_key_sha1, shared_key, strlen(shared_key));
+
+                       gcry_md_open(&hm, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
+                       gcry_md_setkey(hm, shared_key_sha1, sizeof(shared_key_sha1));
+                       gcry_md_write(hm, key_id, strlen(key_id));
+                       gcry_md_final(hm);
+                       hmac = gcry_md_read(hm, 0);
+
+                       gcry_md_open(&skeyid_ctx, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+                       gcry_md_setkey(skeyid_ctx, hmac, 20);
+                       gcry_md_write(skeyid_ctx, s->ike.i_nonce, sizeof(s->ike.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);
+                       gcry_md_close(hm);
+               } else { /* opt_vendor != VENDOR_NORTEL */
+                       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, s->ike.i_nonce, sizeof(s->ike.i_nonce));
+                       gcry_md_write(skeyid_ctx, nonce->u.nonce.data, nonce->u.nonce.length);
+                       gcry_md_final(skeyid_ctx);
+                       psk_skeyid = xallocc(s->ike.md_len);
+                       memcpy(psk_skeyid, gcry_md_read(skeyid_ctx, 0), s->ike.md_len);
+                       if (opt_debug < 99)
+                               DEBUG(3, printf("(not dumping psk hash)\n"));
+                       else
+                               hex_dump("psk_skeyid", psk_skeyid, s->ike.md_len, NULL);
+                       free(psk_skeyid);
+                       gcry_md_close(skeyid_ctx);
+                       DEBUG(99, printf("shared-key: %s\n",shared_key));
+
+                       /* SKEYID - psk only */
+                       if (s->ike.auth_algo == IKE_AUTH_PRESHARED ||
+                               s->ike.auth_algo == IKE_AUTH_XAUTHInitPreShared ||
+                               s->ike.auth_algo == IKE_AUTH_XAUTHRespPreShared) {
+                               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, s->ike.i_nonce, sizeof(s->ike.i_nonce));
+                               gcry_md_write(skeyid_ctx, nonce->u.nonce.data, nonce->u.nonce.length);
+                               gcry_md_final(skeyid_ctx);
+                       } else if (s->ike.auth_algo == IKE_AUTH_DSS ||
+                               s->ike.auth_algo == IKE_AUTH_RSA_SIG ||
+                               s->ike.auth_algo == IKE_AUTH_ECDSA_SIG ||
+                               s->ike.auth_algo == IKE_AUTH_HybridInitRSA ||
+                               s->ike.auth_algo == IKE_AUTH_HybridRespRSA ||
+                               s->ike.auth_algo == IKE_AUTH_HybridInitDSS ||
+                               s->ike.auth_algo == IKE_AUTH_HybridRespDSS ||
+                               s->ike.auth_algo == IKE_AUTH_XAUTHInitDSS ||
+                               s->ike.auth_algo == IKE_AUTH_XAUTHRespDSS ||
+                               s->ike.auth_algo == IKE_AUTH_XAUTHInitRSA ||
+                               s->ike.auth_algo == IKE_AUTH_XAUTHRespRSA) {
+                               unsigned char *key;
+                               int key_len;
+                               key_len = sizeof(s->ike.i_nonce) + nonce->u.nonce.length;
+                               key = xallocc(key_len);
+                               memcpy(key, s->ike.i_nonce, sizeof(s->ike.i_nonce));
+                               memcpy(key + sizeof(s->ike.i_nonce), nonce->u.nonce.data, nonce->u.nonce.length);
+                               gcry_md_open(&skeyid_ctx, s->ike.md_algo, GCRY_MD_FLAG_HMAC);
+                               gcry_md_setkey(skeyid_ctx, key, key_len);
+                               gcry_md_write(skeyid_ctx, dh_shared_secret, dh_getlen(s->ike.dh_grp));
+                               gcry_md_final(skeyid_ctx);
+                       } else
+                               error(1, 0, "SKEYID could not be computed: %s", "the selected authentication method is not supported");
+                       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, *rec_hash;
+                       uint8_t *idp_f;
+                       size_t idp_size;
+                       size_t decr_size = 0;
+
+                       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, s->ike.dh_public, dh_getlen(s->ike.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, s->ike.sa_f + 4, s->ike.sa_size - 4);
+                       gcry_md_write(hm, idp_f + 4, idp_size - 4);
+                       gcry_md_final(hm);
+                       expected_hash = gcry_md_read(hm, 0);
+                       hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);
+
+                       if ((opt_auth_mode == AUTH_MODE_PSK) ||
+                               (opt_vendor == VENDOR_NORTEL && opt_auth_mode != AUTH_MODE_CERT)) {
+                               if (memcmp(expected_hash, hash->u.hash.data, s->ike.md_len) != 0)
+                                       error(2, 0, "hash comparison failed: %s(%d)\ncheck group password!",
+                                               val_to_string(ISAKMP_N_AUTHENTICATION_FAILED, isakmp_notify_enum_array),
+                                               ISAKMP_N_AUTHENTICATION_FAILED);
+                               hex_dump("received hash", hash->u.hash.data, hash->u.hash.length, NULL);
+                       } else if (opt_auth_mode == AUTH_MODE_CERT ||
+                               opt_auth_mode == AUTH_MODE_HYBRID) {
+                               hex_dump("received signature", sig->u.sig.data, sig->u.sig.length, NULL);
+
+                               ret = crypto_verify_chain(cctx,
+                                                         config[CONFIG_CA_FILE],
+                                                         config[CONFIG_CA_DIR],
+                                                         &crerr);
+                               if (ret)
+                                       crypto_call_error(crerr);
+
+                               /* Verify signature */
+                               rec_hash = crypto_decrypt_signature (cctx,
+                                                                    sig->u.sig.data,
+                                                                    sig->u.sig.length,
+                                                                    &decr_size,
+                                                                    CRYPTO_PAD_PKCS1,
+                                                                    &crerr);
+                               if (!rec_hash)
+                                       crypto_call_error(crerr);
+
+                               if (decr_size != s->ike.md_len) {
+                                       printf("Decrypted-Size: %zd\n",decr_size);
+                                       hex_dump("    decr_hash", rec_hash, decr_size, NULL);
+                                       hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);
+
+                                       error(2, 0, "The hash-value, which was decrypted from the received signature, and the expected hash-value differ in size.\n");
+                               } else {
+                                       if (memcmp(rec_hash, expected_hash, decr_size) != 0) {
+                                               printf("Decrypted-Size: %zd\n",decr_size);
+                                               hex_dump("    decr_hash", rec_hash, decr_size, NULL);
+                                               hex_dump("expected hash", expected_hash, s->ike.md_len, NULL);
+
+                                               error(2, 0, "The hash-value, which was decrypted from the received signature, and the expected hash-value differ.\n");
+                                       } else {
+                                               DEBUG(3, printf("Signature MATCH!!\n"));
+                                       }
+                               }
+                               /* END - Signature Verification */
+
+                               free(rec_hash);
+                       }
+
+                       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, s->ike.dh_public, dh_getlen(s->ike.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, s->ike.sa_f + 4, s->ike.sa_size - 4);
+                       gcry_md_write(hm, s->ike.idi_f + 4, s->ike.idi_size - 4);
+                       gcry_md_final(hm);
+                       s->ike.returned_hash = xallocc(s->ike.md_len);
+                       memcpy(s->ike.returned_hash, gcry_md_read(hm, 0), s->ike.md_len);
+                       gcry_md_close(hm);
+                       hex_dump("returned_hash", s->ike.returned_hash, s->ike.md_len, NULL);
+
+                       /* PRESHARED_KEY_HASH */
+                       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, shared_key, strlen(shared_key));
+                       gcry_md_final(hm);
+                       s->ike.psk_hash = xallocc(s->ike.md_len);
+                       memcpy(s->ike.psk_hash, gcry_md_read(hm, 0), s->ike.md_len);
+                       gcry_md_close(hm);
+                       hex_dump("psk_hash", s->ike.psk_hash, s->ike.md_len, NULL);
+                       /* End PRESHARED_KEY_HASH */
+
+                       free(s->ike.sa_f);
+                       free(s->ike.idi_f);
+                       free(idp_f);
+                       s->ike.sa_f = NULL;
+                       s->ike.idi_f = NULL;
+               }
+
+               /* 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(s->ike.dh_grp));
+                       dh_create_shared(s->ike.dh_grp, dh_shared_secret, ke->u.ke.data);
+                       hex_dump("dh_shared_secret", dh_shared_secret, dh_getlen(s->ike.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(s->ike.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);
+                       if (s->ike.skeyid_d) free(s->ike.skeyid_d);
+                       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(s->ike.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);
+                       if (s->ike.skeyid_a) free(s->ike.skeyid_a);
+                       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(s->ike.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));
+                       free(dh_shared_secret);
+
+                       /* Determine the IKE encryption key.  */
+                       if (s->ike.key) free(s->ike.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);
+                       free(skeyid_e);
+               }
+
+               /* 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, s->ike.dh_public, dh_getlen(s->ike.dh_grp));
+                       gcry_md_write(hm, ke->u.ke.data, ke->u.ke.length);
+                       gcry_md_final(hm);
+                       if (s->ike.current_iv) free(s->ike.current_iv);
+                       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);
+               crypto_ctx_free(cctx);
+               free(dh_shared_secret);
+
+               /* Determine presence of NAT */
+               if (opt_vendor != VENDOR_NORTEL && s->ike.natd_type != 0) {
+                       seen_natd_us = 0;
+                       /* this could be repeated for 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);
+                               if (memcmp(s->ike.natd_us, gcry_md_read(hm, 0), s->ike.md_len) == 0)
+                                       seen_natd_us = 1;
+                               memcpy(s->ike.natd_us, gcry_md_read(hm, 0), s->ike.md_len);
+                               if (opt_natt_mode == NATT_FORCE) {
+                                       /* force detection of "this end behind NAT" */
+                                       /* by flipping a bit in the nat-detection-hash */
+                                       s->ike.natd_us[0] ^= 1;
+                                       seen_natd_us = 0;
+                               }
+                               gcry_md_close(hm);
+                       }
+                       /* 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 (s->ike.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 == 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;
+                               }
+                       } else {
+                               DEBUG(1, printf("NAT status: NAT-T VID seen, no NAT device detected\n"));
+                       }
+
+               } else if (opt_vendor == VENDOR_NORTEL && seen_natsi) {
+                       DEBUG(1, printf("NAT status: NaT-SI\n"));
+#if 1 /* AB: it's not mandatory to re-open socket */
+                       /* close and re-create the socket on random port*/
+                       close(s->ike_fd);
+                       s->ike.src_port = 0;
+                       s->ike_fd = make_socket(s, s->ike.src_port, s->ike.dst_port);
+                       if (s->ike_fd < 0)
+                               error(1, errno, "re-opening socket");
+#endif
+               } else {
+                       DEBUG(1, printf("NAT status: no NAT-T VID seen\n"));
+               }
+
+               /* This seems to cause a duplicate free of some data when rekeying:
+                * *** glibc detected *** vpnc-connect: free(): invalid pointer: 0x09d63ba5
+                * See also: http://bugs.gentoo.org/show_bug.cgi?id=229003
+                */
+#if 1
+               free_isakmp_packet(r);
+#endif
+
+       }
+}
+
+static void do_phase1_am_packet3(struct sa_block *s)
+{
+       DEBUGTOP(2, printf("S4.5 AM_packet3\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;
+       /* XXX CERT Add id(?), cert and sig here in case of cert auth */
+               p2->payload = new_isakmp_data_payload(ISAKMP_PAYLOAD_HASH,
+                       s->ike.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;
+
+               if (opt_vendor != VENDOR_NORTEL) {
+                       /* AB: not sent by netlock */
+                       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);
+
+                       /* send PSK-hash if hybrid authentication is negotiated */
+                       if (s->ike.auth_algo == IKE_AUTH_HybridInitRSA ||
+                               s->ike.auth_algo == IKE_AUTH_HybridInitDSS) {
+                               /* Notify - PRESHARED_KEY_HASH */
+                               pl = pl->next = new_isakmp_payload(ISAKMP_PAYLOAD_N);
+                               pl->u.n.doi = ISAKMP_DOI_IPSEC;
+                               pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
+                               /* Notify Message - Type: PRESHARED_KEY_HASH */
+                               pl->u.n.type =  ISAKMP_N_CISCO_PRESHARED_KEY_HASH;
+                               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 = s->ike.md_len;
+                               pl->u.n.data = xallocc(pl->u.n.data_length);
+                               memcpy(pl->u.n.data, s->ike.psk_hash, pl->u.n.data_length);
+                               /* End Notify - PRESHARED_KEY_HASH */
+                       }
+                       pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                               VID_UNKNOWN, sizeof(VID_UNKNOWN));
+                       pl = pl->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_VID,
+                               VID_UNITY, sizeof(VID_UNITY));
+
+                       /* include NAT traversal discovery payloads */
+                       if (s->ike.natd_type != 0) {
+                               pl = pl->next = new_isakmp_data_payload(s->ike.natd_type,
+                                       s->ike.natd_them, s->ike.md_len);
+                               pl->next = new_isakmp_data_payload(s->ike.natd_type,
+                                       s->ike.natd_us, s->ike.md_len);
+                               free(s->ike.natd_us);
+                               free(s->ike.natd_them);
+                               s->ike.natd_us = NULL;
+                               s->ike.natd_them = NULL;
+                       }
+               }
+
+               flatten_isakmp_packet(p2, &p2kt, &p2kt_len, s->ike.ivlen);
+               free_isakmp_packet(p2);
+               isakmp_crypt(s, p2kt, p2kt_len, 1);
+
+               if (s->ike.initial_iv) free(s->ike.initial_iv);
+               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);
+       }
+}
+
+static void do_phase1_am_cleanup(struct sa_block *s)
+{
+       DEBUGTOP(2, printf("S4.6 cleanup\n"));
+
+       free(s->ike.psk_hash);
+       s->ike.psk_hash = NULL;
+       free(s->ike.dh_public);
+       group_free(s->ike.dh_grp);
+       free(s->ike.returned_hash);
+       s->ike.returned_hash = NULL;
+}
+
+static void do_phase1_am(const char *key_id, const char *shared_key, struct sa_block *s)
+{
+       do_phase1_am_init(s);
+       do_phase1_am_packet1(s, key_id);
+       do_phase1_am_packet2(s, key_id, shared_key);
+       do_phase1_am_packet3(s);
+       do_phase1_am_cleanup(s);
+}
+
+static int do_phase2_notice_check(struct sa_block *s, struct isakmp_packet **r_p,
+       const uint8_t * nonce, size_t nonce_size)
+{
+       int reject = 0;
+       struct isakmp_packet *r;
+
+       while (1) {
+               reject = unpack_verify_phase2(s, r_packet, r_length, r_p, nonce, nonce_size);
+               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 == 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",
+                                                       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_phase2_xauth(struct sa_block *s)
+{
+       struct isakmp_packet *r = NULL;
+       int loopcount;
+       int reject;
+       int passwd_used = 0;
+
+       DEBUGTOP(2, printf("S5.1 xauth_request\n"));
+       /* This can go around for a while.  */
+       for (loopcount = 0;; loopcount++) {
+               struct isakmp_payload *rp;
+               struct isakmp_attribute *a, *ap, *reply_attr, *last_reply_attr;
+               char ntop_buf[32];
+               int seen_answer = 0;
+
+               DEBUGTOP(2, printf("S5.2 notice_check\n"));
+
+               /* recv and check for notices */
+               if (r) free_isakmp_packet(r);
+               r = NULL;
+               reject = do_phase2_notice_check(s, &r, NULL, 0);
+               if (reject == -1) {
+                       if (r) free_isakmp_packet(r);
+                       return 1;
+               }
+
+               DEBUGTOP(2, printf("S5.3 type-is-xauth check\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) {
+                       /* OK, the server has finished requesting information, go for the final set/ack */
+                       break;
+               }
+
+               if (opt_vendor == VENDOR_NORTEL) {
+                       if (reject == 0 && r->payload->next->u.modecfg.type == ISAKMP_MODECFG_CFG_NORTEL_OK)
+                               break;
+                       if (reject == 0 && r->payload->next->u.modecfg.type == ISAKMP_MODECFG_CFG_NORTEL_ERR)
+                       {
+                               printf("authentication failed\n");
+                               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);
+
+               DEBUGTOP(2, printf("S5.4 xauth type check\n"));
+               a = r->payload->next->u.modecfg.attributes;
+               /* First, print any messages, and verify that we understand the
+                * conversation. This looks for any place were input is
+                * required - in these cases, we need to print the prompt
+                * regardless of whether the user requested interactive mode
+                * or not. */
+               for (ap = a; ap && seen_answer == 0; ap = ap->next)
+                       if (ap->type == ISAKMP_XAUTH_06_ATTRIB_ANSWER
+                           || ap->type == ISAKMP_XAUTH_06_ATTRIB_NEXT_PIN
+                           /* || ap->type == ISAKMP_XAUTH_06_ATTRIB_PASSCODE */
+                           || ap->type == ISAKMP_XAUTH_02_ATTRIB_ANSWER
+                           || ap->type == ISAKMP_XAUTH_02_ATTRIB_NEXT_PIN
+                           /* || ap->type == ISAKMP_XAUTH_02_ATTRIB_PASSCODE */)
+                               seen_answer = 1;
+
+               for (ap = a; ap && reject == 0; ap = ap->next)
+                       switch (ap->type) {
+                       case ISAKMP_XAUTH_06_ATTRIB_TYPE:
+                       case ISAKMP_XAUTH_02_ATTRIB_TYPE:
+
+                               if (ap->af != isakmp_attr_16 || !(ap->u.attr_16 == 0 || ap->u.attr_16 == 5))
+                                       reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+                               break;
+                       case ISAKMP_XAUTH_06_ATTRIB_USER_NAME:
+                       case ISAKMP_XAUTH_02_ATTRIB_USER_NAME:
+                       case ISAKMP_XAUTH_06_ATTRIB_USER_PASSWORD:
+                       case ISAKMP_XAUTH_02_ATTRIB_USER_PASSWORD:
+                       case ISAKMP_XAUTH_06_ATTRIB_PASSCODE:
+                       case ISAKMP_XAUTH_02_ATTRIB_PASSCODE:
+                       case ISAKMP_XAUTH_06_ATTRIB_DOMAIN:
+                       case ISAKMP_XAUTH_02_ATTRIB_DOMAIN:
+                       case ISAKMP_XAUTH_06_ATTRIB_ANSWER:
+                       case ISAKMP_XAUTH_02_ATTRIB_ANSWER:
+                       case ISAKMP_XAUTH_06_ATTRIB_NEXT_PIN:
+                       case ISAKMP_XAUTH_02_ATTRIB_NEXT_PIN:
+                       case ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR:
+                       case ISAKMP_MODECFG_ATTRIB_NORTEL_UNKNOWN_4011:
+                       case ISAKMP_MODECFG_ATTRIB_NORTEL_CLIENT_ID:
+                               break;
+                       case ISAKMP_XAUTH_06_ATTRIB_MESSAGE:
+                       case ISAKMP_XAUTH_02_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;
+                       }
+               if (reject != 0)
+                       phase2_fatal(s, "xauth packet unsupported: %s(%d)", reject);
+
+               DEBUGTOP(2, printf("S5.5 do xauth reply\n"));
+               inet_ntop(AF_INET, &s->dst, ntop_buf, sizeof(ntop_buf));
+
+               /* Collect data from the user.  */
+               reply_attr = last_reply_attr = NULL;
+               for (ap = a; ap && reject == 0; ap = ap->next) {
+                       struct isakmp_attribute *na = NULL;
+
+                       switch (ap->type) {
+                       case ISAKMP_XAUTH_06_ATTRIB_TYPE:
+                       {
+                               na = new_isakmp_attribute_16(ap->type, ap->u.attr_16, NULL);
+                               break;
+                       }
+                       case ISAKMP_XAUTH_02_ATTRIB_TYPE:
+                               if (opt_auth_mode == AUTH_MODE_NORTEL_GPASSWORD)
+                                       na = new_isakmp_attribute_16(ISAKMP_XAUTH_02_ATTRIB_TYPE, ISAKMP_MODECFG_TYPE_RADIUS, NULL);
+                               else
+                                       na = new_isakmp_attribute_16(ISAKMP_XAUTH_02_ATTRIB_TYPE, ISAKMP_MODECFG_TYPE_SECURID, NULL);
+                               break;
+                       case ISAKMP_XAUTH_06_ATTRIB_DOMAIN:
+                       case ISAKMP_XAUTH_02_ATTRIB_DOMAIN:
+                                       na = new_isakmp_attribute(ap->type, NULL);
+                                       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]);
+                                       na->u.lots.data = xallocc(na->u.lots.length);
+                                       memcpy(na->u.lots.data, config[CONFIG_DOMAIN],
+                                               na->u.lots.length);
+                                       break;
+                       case ISAKMP_MODECFG_ATTRIB_NORTEL_UNKNOWN_4011:
+                               na = new_isakmp_attribute_16(ISAKMP_MODECFG_ATTRIB_NORTEL_UNKNOWN_4011, 0, NULL);
+                               break;
+                       case ISAKMP_MODECFG_ATTRIB_NORTEL_CLIENT_ID:
+                               /* e.g. "Netlock Contivity Client  3.3     Linux           " */
+                               na = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_NORTEL_CLIENT_ID, NULL);
+                               na->af = isakmp_attr_lots;
+                               na->u.lots.length = strlen(config[CONFIG_VERSION]);
+                               na->u.lots.data = xallocc(na->u.lots.length);
+                               memcpy(na->u.lots.data, config[CONFIG_VERSION], na->u.lots.length);
+                               break;
+                       case ISAKMP_XAUTH_06_ATTRIB_USER_NAME:
+                       case ISAKMP_XAUTH_02_ATTRIB_USER_NAME:
+                               {
+                                       na = new_isakmp_attribute(ap->type, NULL);
+                                       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_06_ATTRIB_ANSWER:
+                       case ISAKMP_XAUTH_02_ATTRIB_ANSWER:
+                       case ISAKMP_XAUTH_06_ATTRIB_USER_PASSWORD:
+                       case ISAKMP_XAUTH_02_ATTRIB_USER_PASSWORD:
+                       case ISAKMP_XAUTH_06_ATTRIB_PASSCODE:
+                       case ISAKMP_XAUTH_02_ATTRIB_PASSCODE:
+                       case ISAKMP_XAUTH_06_ATTRIB_NEXT_PIN:
+                       case ISAKMP_XAUTH_02_ATTRIB_NEXT_PIN:
+                               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 failed (requires interactive mode)");
+                               } else if (seen_answer || passwd_used || config[CONFIG_XAUTH_INTERACTIVE]) {
+                                       char *pass, *prompt = NULL;
+
+                                       asprintf(&prompt, "%s for VPN %s@%s: ",
+                                               (ap->type == ISAKMP_XAUTH_06_ATTRIB_ANSWER
+                                               || ap->type == ISAKMP_XAUTH_02_ATTRIB_ANSWER) ?
+                                               "Answer" :
+                                               (ap->type == ISAKMP_XAUTH_06_ATTRIB_USER_PASSWORD
+                                               || ap->type == ISAKMP_XAUTH_02_ATTRIB_USER_PASSWORD) ?
+                                               "Password" : "Passcode",
+                                               config[CONFIG_XAUTH_USERNAME], ntop_buf);
+                                       pass = getpass(prompt);
+                                       free(prompt);
+
+                                       na = new_isakmp_attribute(ap->type, NULL);
+                                       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 {
+                                       if (opt_vendor == VENDOR_NORTEL
+                                           && opt_auth_mode != AUTH_MODE_NORTEL_GPASSWORD)
+                                               na = new_isakmp_attribute(ISAKMP_XAUTH_02_ATTRIB_PASSCODE, NULL);
+                                       else
+                                               na = new_isakmp_attribute(ap->type, NULL);
+                                       if (opt_vendor == VENDOR_NORTEL
+                                           && opt_auth_mode == AUTH_MODE_NORTEL_PINTOKEN) {
+                                               int l_pin, l_pas;
+                                               l_pin = strlen(config[CONFIG_XAUTH_PIN]);
+                                               l_pas = strlen(config[CONFIG_XAUTH_PASSWORD]);
+                                               na->u.lots.length = l_pin + l_pas;
+                                               na->u.lots.data = xallocc(na->u.lots.length);
+                                               memcpy(na->u.lots.data, config[CONFIG_XAUTH_PIN], l_pin);
+                                               memcpy(na->u.lots.data + l_pin, config[CONFIG_XAUTH_PASSWORD], l_pas);
+                                       } else {
+                                               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:
+                               ;
+                       }
+                       if (na == NULL)
+                               continue;
+                       if (last_reply_attr != NULL) {
+                               last_reply_attr->next = na;
+                               last_reply_attr = na;
+                       } else {
+                               last_reply_attr = reply_attr = na;
+                       }
+               }
+
+               /* 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);
+
+       }
+
+       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;
+
+               DEBUGTOP(2, printf("S5.5.1 do netscreen modecfg extra\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);
+
+               reject = do_phase2_notice_check(s, &r, NULL, 0);
+               if (reject == -1) {
+                       free_isakmp_packet(r);
+                       return 1;
+               }
+       }
+
+       DEBUGTOP(2, printf("S5.6 process xauth set\n"));
+
+       if (opt_vendor != VENDOR_NORTEL) {
+               /* 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_06_ATTRIB_STATUS
+                       || a->af != isakmp_attr_16 || a->next != NULL) {
+                       reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
+                       phase2_fatal(s, "xauth SET message rejected: %s(%d)", reject);
+               } else {
+                       set_result = a->u.attr_16;
+               }
+
+               /* ACK the SET.  */
+               DEBUGTOP(2, printf("S5.7 send xauth ack\n"));
+               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);
+               r->payload->next = NULL; /* this part is already free()d by sendrecv_phase2 */
+               free_isakmp_packet(r); /* this frees the received set packet (header+hash) */
+
+               if (set_result == 0)
+                       error(2, 0, "authentication unsuccessful");
+       }
+       DEBUGTOP(2, printf("S5.8 xauth done\n"));
+       return 0;
+}
+
+static int do_phase2_config(struct sa_block *s)
+{
+       struct isakmp_packet *r;
+       struct isakmp_payload *rp;
+       struct isakmp_attribute *a, *reply_attr = NULL;
+       int reject;
+       uint32_t msgid;
+
+       if (opt_vendor != VENDOR_NORTEL) {
+               struct utsname uts;
+
+               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);
+               a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_FW_TYPE, a);
+               a->u.lots.length = sizeof(FW_UNKNOWN_TYPEINFO);
+               a->u.lots.data = xallocc(a->u.lots.length);
+               memcpy(a->u.lots.data, FW_UNKNOWN_TYPEINFO, a->u.lots.length);
+               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;
+               DEBUGTOP(2, printf("S6.1 phase2_config send modecfg\n"));
+               sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_MODECFG_TRANSACTION, msgid, 0, 0, 0, 0, 0);
+       } else {
+               if (opt_auth_mode != AUTH_MODE_NORTEL_USERNAME)
+                       r_length = sendrecv(s,r_packet, sizeof(r_packet), NULL, 0, 0);
+       }
+
+       DEBUGTOP(2, printf("S6.2 phase2_config receive modecfg\n"));
+       /* recv and check for notices */
+       reject = do_phase2_notice_check(s, &r, NULL, 0);
+       if (reject == -1) {
+               if (r) free_isakmp_packet(r);
+               return 1;
+       }
+
+       /* Check the transaction type & message ID are OK.  */
+       if (opt_vendor != VENDOR_NORTEL)
+               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 (opt_vendor != VENDOR_NORTEL) {
+               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;
+       } else {
+               /* 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_PAYLOAD_MALFORMED;
+
+               if (reject == 0
+                       && (r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_REPLY)
+                       && (r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_SET))
+                       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")));
+
+       if (opt_vendor == VENDOR_NORTEL) {
+               for (a = r->payload->next->u.modecfg.attributes; a; a = a->next)
+               {
+                       if (a->af == isakmp_attr_16)
+                               reply_attr = new_isakmp_attribute_16(a->type, 0, reply_attr);
+                       else
+                               reply_attr = new_isakmp_attribute(a->type, reply_attr);
+               }
+
+               rp = new_isakmp_payload(ISAKMP_PAYLOAD_MODECFG_ATTR);
+               rp->u.modecfg.type = ISAKMP_MODECFG_CFG_ACK;
+               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, 1, 0, 0, 0, 0);
+       }
+
+       free_isakmp_packet(r);
+       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;
+}
+
+// Nortel specific version
+static struct isakmp_payload *make_our_sa_ipsec_nortel(struct sa_block *s, struct isakmp_payload *transform, int proposal_number)
+{
+       struct isakmp_payload *r;
+       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;
+
+       if (transform) {
+               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 = dup_isakmp_payload(transform);
+               p->u.p.number = proposal_number;
+       } else {
+               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 struct isakmp_payload *make_our_sa_ipsec(struct sa_block *s)
+{
+       struct isakmp_payload *r;
+       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;
+       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;
+}
+
+
+// Nortel specific
+static int check_transform(struct sa_block *s,struct isakmp_payload *transform)
+{
+       int seen_enc, seen_auth = 0, seen_encap = 0, seen_group = 0, seen_keylen = 0;
+       struct isakmp_attribute *a;
+       const supported_algo_t *cry_algo, *hash_algo;
+       int reject = 0;
+
+       /* moved to do_phase2 for NortelVPN
+     * memcpy(&s->ipsec.tx.spi, rp->u.sa.proposals->u.p.spi, 4);
+     */
+
+       seen_enc = transform->u.t.id;
+       for (a = transform->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
+                               reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+                       break;
+               case ISAKMP_IPSEC_ATTRIB_ENCAP_MODE:
+                       if (a->af == isakmp_attr_16 )
+                               seen_encap = a->u.attr_16;
+                       else
+                               reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+                       break;
+               case ISAKMP_IPSEC_ATTRIB_GROUP_DESC:
+                       if (a->af == isakmp_attr_16 )
+                               seen_group = a->u.attr_16;
+                       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;
+               case ISAKMP_IPSEC_ATTRIB_NORTEL_NATT_UDP_PORT:
+                       /* Contivity puts here server's UDP port, but claims
+                          IPSEC_ENCAP_TUNNEL instead of IPSEC_ENCAP_UDP_TUNNEL */
+                       if (a->af == isakmp_attr_16) {
+                               s->ipsec.peer_udpencap_port = ntohs(a->u.attr_16);
+                               DEBUG(2, printf("got peer udp encapsulation port: %hu\n", s->ipsec.peer_udpencap_port));
+                       } else
+                               reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+                       break;
+               default:
+                       reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED;
+                       break;
+               }
+
+/* removed for NortelVPN
+
+       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 (!seen_auth
+           || (seen_encap && (seen_encap != encap_mode))
+           || (dh_group && (seen_group != dh_group)))
+               return 0;
+
+       cry_algo = get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc, NULL, seen_keylen);
+       hash_algo = get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0);
+       if ((cry_algo == NULL) || (hash_algo == NULL))
+               return 0;
+
+       dh_group = seen_group;
+
+       s->ipsec.cry_algo = ipsec_cry_algo  = cry_algo->my_id;
+       s->ipsec.md_algo  = ipsec_hash_algo = hash_algo->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 too 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);
+       }
+
+       return 1;
+}
+
+// Nortel specific
+static void do_phase2(struct sa_block *s)
+{
+
+       struct isakmp_packet *r;
+       struct isakmp_payload *rp, *proposal = NULL, *transform = NULL, *ke = NULL, *nonce_r = NULL, *idci = NULL, *idcr = NULL;
+       uint8_t nonce[20], *dh_public, *dh_shared_secret = NULL;
+
+       static struct group *dh_grp;
+       int reject;
+
+       r_length = sendrecv(s,r_packet, sizeof(r_packet), NULL, 0, 0);
+       reject = do_phase2_notice_check(s, &r, NULL, 0);
+
+       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;
+
+       DEBUGTOP(2, printf("do_phase2: S7.5 QM_packet2 check reject offer\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);
+
+       DEBUGTOP(2, printf("do_phase2: S7.6 QM_packet2 check and process proposal\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;
+
+/* Removed for NORTELVPN
+                       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) {
+                               for (proposal = rp->u.sa.proposals; proposal; proposal = proposal->next)
+                               {
+                                       if (proposal->u.p.prot_id != ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+                                               continue;
+                                       if (proposal->u.p.spi_size != 4)
+                                               continue;
+                                       for (transform = proposal->u.p.transforms; transform; transform = transform->next)
+                                       {
+                                               if (check_transform(s,transform))
+                                               {
+                                                       break;
+                                               }
+                                       }
+                                       if (!transform)
+                                               continue;
+                                       break;
+                               }
+
+                       }
+                       if (!proposal)
+                       {
+                               reject = ISAKMP_N_BAD_PROPOSAL_SYNTAX;
+                       }
+                       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:
+                       /* FIXME: Parse payload ID and add route-env in case of ipv4_prefix */
+                       if (idci == NULL) idci = rp;
+                       else if (idcr == NULL) idcr = rp;
+                       else abort();
+                       break;
+               case ISAKMP_PAYLOAD_KE:
+                       ke = rp;
+                       break;
+               case ISAKMP_PAYLOAD_NONCE:
+                       nonce_r = rp;
+                       break;
+
+               default:
+                       reject = ISAKMP_N_INVALID_PAYLOAD_TYPE;
+                       break;
+               }
+/* Removed for NORTELVPN
+
+       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);
+
+/*
+ * Moved from check_transform()
+ */
+       memcpy(&s->ipsec.tx.spi, proposal->u.p.spi, proposal->u.p.spi_size);
+
+       DEBUGTOP(2, printf("do_phase2: S7.1 QM_packet1\n"));
+
+       gcry_create_nonce((uint8_t *) & s->ipsec.rx.spi, sizeof(s->ipsec.rx.spi));
+       rp = make_our_sa_ipsec_nortel(s, transform, proposal->u.p.number); /* FIXME: LEAK: allocated memory never freed */
+       gcry_create_nonce((uint8_t *) nonce, sizeof(nonce));
+       rp->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE, nonce, sizeof(nonce));
+
+       /* copy IDci and IDcr from initiator ISAKMP_PAYLOAD_ID */
+       if (idci)
+               idci = dup_isakmp_payload(idci);
+       if (idcr) {
+               idcr = dup_isakmp_payload(idcr);
+               idci->next = idcr;
+       }
+
+       if (!dh_group) {
+               rp->next->next = idci;
+               dh_grp = NULL;
+       } else {
+               const supported_algo_t *algo;
+
+               /*NORTELVPN from S7.1 QM_packet1 */
+               /* Set up the Diffie-Hellman stuff.  ??*/
+               algo = get_algo(SUPP_ALGO_DH_GROUP, SUPP_ALGO_IPSEC_SA, dh_group, NULL, 0);
+               if (algo == NULL)
+                       abort();
+               dh_grp = group_get(algo->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);
+
+               /* Determine the shared secret.  from S7.8 setup ipsec tunnel*/
+               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);
+
+               /*NORTELVPN from S7.1 QM_packet1 */
+               rp->next->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_KE,
+                       dh_public, dh_getlen(dh_grp));
+               rp->next->next->next = idci;
+       }
+
+       s->ipsec.life.start = time(NULL);
+
+       /* send final packet S7.7 QM_packet3 sent - run script */
+       sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_IKE_QUICK,
+               r->message_id, 0, nonce_r->u.nonce.data, nonce_r->u.nonce.length,0,0);
+
+       DEBUGTOP(2, printf("do_phase2: S7.7 QM_packet3 sent - run script\n"));
+
+       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_r->u.nonce.data, nonce_r->u.nonce.length, nonce, sizeof(nonce));
+
+       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_r->u.nonce.data, nonce_r->u.nonce.length, nonce, sizeof(nonce));
+
+}
+
+
+
+static void do_phase2_qm(struct sa_block *s)
+{
+       struct group *dh_grp = NULL;
+       uint8_t nonce_i[20], *dh_public = NULL;
+       struct isakmp_payload *rp, *us, *ke = NULL, *them, *nonce_r = NULL;
+       struct isakmp_packet *r;
+
+       if (opt_vendor != VENDOR_NORTEL) {
+               uint32_t msgid;
+               int reject;
+
+               DEBUGTOP(2, printf("S7.1 QM_packet1\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); /* FIXME: LEAK: allocated memory never freed */
+               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);
+               init_netaddr((struct in_addr *)them->u.id.data,
+                    config[CONFIG_IPSEC_TARGET_NETWORK]);
+               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;
+
+               DEBUGTOP(2, printf("S7.2 QM_packet2 send_receive\n"));
+               sendrecv_phase2(s, rp, ISAKMP_EXCHANGE_IKE_QUICK,
+                       msgid, 0, 0, 0, 0, 0);
+
+               DEBUGTOP(2, printf("S7.3 QM_packet2 validate type\n"));
+               reject = do_phase2_notice_check(s, &r, nonce_i, sizeof(nonce_i)); /* FIXME: LEAK */
+
+               /* 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;
+
+               DEBUGTOP(2, printf("S7.5 QM_packet2 check reject offer\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 except windows\n"
+                       "     which is an obvious security improvement. 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, except on low-bandwith (read: ISDN) links, because it\n"
+                       "     uses much CPU-resources on the concentrator\n",
+                       reject);
+
+               DEBUGTOP(2, printf("S7.6 QM_packet2 check and process proposal\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 too 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:
+                               /* FIXME: Parse payload ID and add route-env in case of ipv4_prefix */
+                               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, nonce_i, sizeof(nonce_i),
+                       nonce_r->u.nonce.data, nonce_r->u.nonce.length);
+
+               DEBUGTOP(2, printf("S7.7 QM_packet3 sent\n"));
+       }
+
+       DEBUGTOP(2, printf("S7.8 setup ipsec tunnel\n"));
+       {
+               if (opt_vendor != VENDOR_NORTEL) {
+                       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);
+                       free(dh_shared_secret);
+                       free_isakmp_packet(r);
+               }
+
+               if (s->esp_fd == 0) {
+                       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 ((opt_natt_mode == NATT_NORTEL_UDP) && s->ipsec.peer_udpencap_port) {
+                               s->esp_fd = make_socket(s, 0, s->ipsec.peer_udpencap_port);
+                               s->ipsec.encap_mode = IPSEC_ENCAP_UDP_TUNNEL;
+                               s->ipsec.natt_active_mode = NATT_ACTIVE_CISCO_UDP; /* AB: change it */
+                       } 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) {
+                                       close_tunnel(s);
+                                       error(1, errno, "Couldn't open socket of ESP. Maybe something registered ESP already.\nPlease try '--natt-mode force-natt' or disable whatever is using ESP.\nsocket(PF_INET, SOCK_RAW, IPPROTO_ESP)");
+                               }
+#ifdef FD_CLOEXEC
+                               /* do not pass socket to vpnc-script, etc. */
+                               fcntl(s->esp_fd, F_SETFD, FD_CLOEXEC);
+#endif
+#ifdef IP_HDRINCL
+                               if (setsockopt(s->esp_fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) == -1) {
+                                       close_tunnel(s);
+                                       error(1, errno, "setsockopt(esp_fd, IPPROTO_IP, IP_HDRINCL, 1)");
+                               }
+#endif
+                       }
+               }
+
+               s->ipsec.rx.seq_id = s->ipsec.tx.seq_id = 1;
+       }
+       free(dh_public);
+}
+
+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)
+               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;
+
+       /* FIXME: Current code has a limitation that will cause problems if
+        * different algorithms are negotiated during re-keying
+        */
+       if ((get_algo(SUPP_ALGO_HASH, SUPP_ALGO_IPSEC_SA, seen_auth, NULL, 0) == NULL) ||
+           (get_algo(SUPP_ALGO_CRYPT, SUPP_ALGO_IPSEC_SA, seen_enc, NULL, seen_keylen) == NULL)) {
+               printf("\nFIXME: vpnc doesn't support change of algorightms during rekeying\n");
+               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:
+                       /* FIXME: Parse payload ID and add route-env in case of ipv4_prefix */
+                       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, 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 packet: %zd bytes\n", r_length));
+       /* we should ignore resent packets 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) {
+               if (r) free_isakmp_packet(r);
+               return;
+       }
+
+       /* everything must be encrypted by now */
+       if (r->payload == NULL || r->payload->type != ISAKMP_PAYLOAD_HASH) {
+               free_isakmp_packet(r);
+               return;
+       }
+
+       /* empty packet? well, nothing to see here */
+       if (r->payload->next == NULL) {
+               free_isakmp_packet(r);
+               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));
+               /* FIXME: LEAK but will create segfault for double free */
+               /* free_isakmp_packet(r); */
+               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;
+                               }
+                               seq = ntohl(*((uint32_t *) rp->u.n.data));
+                               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;
+                               }
+                               seqack = ntohl(*((uint32_t *) rp->u.n.data));
+                               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) {
+               /* search for delete payloads */
+               if (rp->type != ISAKMP_PAYLOAD_D)
+                       continue;
+               if (rp->u.d.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP) {
+                       /* RFC2408, 5.15:
+                        * Process the Delete payload and take appropriate action, according
+                        * to local security policy.  As described above, one appropriate
+                        * action SHOULD include cleaning up the local SA database.
+                        */
+                       /* FIXME: any cleanup needed??? */
+
+                       if (rp->u.d.num_spi >= 1 && memcmp(rp->u.d.spi[0], &s->ipsec.tx.spi, 4) == 0) {
+                               free_isakmp_packet(r);
+                               do_phase2_qm(s);
+                               return;
+                       } else {
+                               DEBUG(2, printf("got isakmp delete with bogus spi (expected %d, received %d), ignoring...\n", s->ipsec.tx.spi, *(rp->u.d.spi[0]) ));
+                               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"));
+               free_isakmp_packet(r);
+               return;
+       }
+
+       free_isakmp_packet(r);
+       return;
+}
+
+static char * tolowercase(const char *s)
+{
+       int i, l;
+       char *r;
+
+       l = strlen(s);
+       r = xallocc(l + 1);
+       for (i = 0; i < l; i++)
+               r[i] = tolower(s[i]);
+       r[l] = 0;
+
+       return r;
+}
+
+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;
+       const char *group_id;
+
+       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);
+
+       if (opt_vendor == VENDOR_NORTEL)
+               group_id = tolowercase(config[CONFIG_IPSEC_ID]);
+       else
+               group_id = config[CONFIG_IPSEC_ID];
+
+       DEBUG(1, printf("\nvpnc version " VERSION "\n"));
+       hex_dump("hex_test", hex_test, sizeof(hex_test), NULL);
+
+       DEBUGTOP(2, printf("S1 init_sockaddr\n"));
+       init_sockaddr(&s->dst, config[CONFIG_IPSEC_GATEWAY]);
+       init_sockaddr(&s->opt_src_ip, config[CONFIG_LOCAL_ADDR]);
+       DEBUGTOP(2, printf("S2 make_socket\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);
+       DEBUGTOP(2, printf("S3 setup_tunnel\n"));
+       setup_tunnel(s);
+
+       do_load_balance = 0;
+       do {
+               DEBUGTOP(2, printf("S4 do_phase1_am\n"));
+               do_phase1_am(group_id, config[CONFIG_IPSEC_SECRET], s);
+
+               if (opt_vendor == VENDOR_NORTEL) {
+                       if (opt_auth_mode != AUTH_MODE_NORTEL_USERNAME) {
+                               DEBUGTOP(2, printf("S5 do_phase2_xauth [1]\n"));
+                               do_load_balance = do_phase2_xauth(s);
+                       }
+                       DEBUGTOP(2, printf("S6 do_phase2_config [1]\n"));
+                       do_load_balance = do_phase2_config(s);
+                       DEBUGTOP(2, printf("S6 do_phase2\n"));
+                       do_phase2(s);
+               } else {
+                       /* FIXME: Create and use a generic function in supp.[hc] */
+                       if (s->ike.auth_algo >= IKE_AUTH_HybridInitRSA) {
+                               DEBUGTOP(2, printf("S5 do_phase2_xauth [2]\n"));
+                               do_load_balance = do_phase2_xauth(s);
+                       }
+                       if ((opt_vendor == VENDOR_CISCO) && (do_load_balance == 0)) {
+                               DEBUGTOP(2, printf("S6 do_phase2_config [2]\n"));
+                               do_load_balance = do_phase2_config(s);
+                       }
+               }
+       } while (do_load_balance);
+       DEBUGTOP(2, printf("S7 setup_link (phase 2 + main_loop)\n"));
+       DEBUGTOP(2, printf("S7.0 run interface setup script\n"));
+       config_tunnel(s);
+       do_phase2_qm(s);
+       DEBUGTOP(2, printf("S7.9 main loop (receive and transmit ipsec packets)\n"));
+       vpnc_doit(s);
+
+       /* Tear down phase 2 and 1 tunnels */
+       send_delete_ipsec(s);
+       send_delete_isakmp(s);
+
+       /* Cleanup routing */
+       DEBUGTOP(2, printf("S8 close_tunnel\n"));
+       close_tunnel(s);
+
+       /* Free resources */
+       DEBUGTOP(2, printf("S9 cleanup\n"));
+       cleanup(s);
+       if (opt_vendor == VENDOR_NORTEL)
+               free((void *)group_id);
+
+       return 0;
+}
diff --git a/vpnc.conf b/vpnc.conf
new file mode 100644 (file)
index 0000000..fa6742d
--- /dev/null
+++ b/vpnc.conf
@@ -0,0 +1,6 @@
+#IPSec gateway <gateway>
+#IPSec ID <group-id>
+#IPSec secret <group-psk>
+#IKE Authmode hybrid
+#Xauth username <username>
+#Xauth password <password>
diff --git a/vpnc.h b/vpnc.h
new file mode 100644 (file)
index 0000000..3f2f2ce
--- /dev/null
+++ b/vpnc.h
@@ -0,0 +1,31 @@
+/* IPSec VPN client compatible with Cisco equipment.
+   Copyright (C) 2002, 2003, 2004  Geoffrey Keating and 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: vpnc.h 377 2008-11-26 08:03:43Z Joerg Mayer $
+*/
+
+#ifndef __VPNC_H__
+#define __VPNC_H__
+
+#include "tunip.h"
+
+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);
+void print_vid(const unsigned char *vid, uint16_t len);
+
+#endif