From: Anas Nashif Date: Sun, 10 Mar 2013 23:49:02 +0000 (-0700) Subject: Imported Upstream version 0.5.3r517 X-Git-Tag: upstream/0.5.3r517^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=934af1a635a6cbbf15bbe9d04a1d4783b393a5f3;p=platform%2Fupstream%2Fvpnc.git Imported Upstream version 0.5.3r517 --- 934af1a635a6cbbf15bbe9d04a1d4783b393a5f3 diff --git a/COPYING b/COPYING new file mode 100644 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 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 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 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 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 +to your .conf file or adding + --ca-file +when starting vpnc. + +The trusted root certificate may be contained in a directory by adding + CA-Dir +to your .conf file or adding + --ca-dir +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 -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 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 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 index 0000000..3345fd0 --- /dev/null +++ b/cisco-decrypt.1 @@ -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 +.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 index 0000000..f66388a --- /dev/null +++ b/cisco-decrypt.c @@ -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 +#include +#include +#include + +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 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#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/name of your IPSec gateway", + NULL + }, { + CONFIG_IPSEC_ID, 1, 0, + "--id", + "IPSec ID ", + "", + "your group name", + NULL + }, { + CONFIG_IPSEC_SECRET, 1, 0, + NULL, + "IPSec secret ", + "", + "your group password (cleartext)", + NULL + }, { + CONFIG_IPSEC_SECRET_OBF, 1, 1, + NULL, + "IPSec obfuscated secret ", + "", + "your group password (obfuscated)", + NULL + }, { + CONFIG_XAUTH_USERNAME, 1, 0, + "--username", + "Xauth username ", + "", + "your username", + NULL + }, { + CONFIG_XAUTH_PIN, 1, 0, + NULL, + "Xauth PIN ", + "", + "PIN for Nortel Two-Factor Authentication", + NULL + }, { + CONFIG_XAUTH_PASSWORD, 1, 0, + NULL, + "Xauth password ", + "", + "your password (cleartext)", + NULL + }, { + CONFIG_XAUTH_PASSWORD_OBF, 1, 1, + NULL, + "Xauth obfuscated password ", + "", + "your password (obfuscated)", + NULL + }, { + CONFIG_DOMAIN, 1, 1, + "--domain", + "Domain ", + "", + "(NT-) Domain name for authentication", + NULL + }, { + CONFIG_XAUTH_INTERACTIVE, 0, 1, + "--xauth-inter", + "Xauth interactive", + NULL, + "enable interactive extended authentication (for challenge response auth)", + NULL + }, { + CONFIG_VENDOR, 1, 1, + "--vendor", + "Vendor ", + "", + "vendor of your IPSec gateway", + config_def_vendor + }, { + CONFIG_NATT_MODE, 1, 1, + "--natt-mode", + "NAT Traversal Mode ", + "", + "Which NAT-Traversal Method to use:\n" + " * natt -- NAT-T as defined in RFC3947\n" + " * none -- disable use of any NAT-T method\n" + " * force-natt -- always use NAT-T encapsulation even\n" + " without presence of a NAT device\n" + " (useful if the OS captures all ESP traffic)\n" + " * cisco-udp -- Cisco proprietary UDP encapsulation, commonly over Port 10000\n" + " * 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 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 ", + "", + "name of the IKE DH Group", + config_def_ike_dh + }, { + CONFIG_IPSEC_PFS, 1, 1, + "--pfs", + "Perfect Forward Secrecy ", + "", + "Diffie-Hellman group to use for PFS", + config_def_pfs + }, { + CONFIG_ENABLE_1DES, 0, 1, + "--enable-1des", + "Enable Single DES", + NULL, + "enables weak single DES encryption", + NULL + }, { + CONFIG_ENABLE_NO_ENCRYPTION, 0, 1, + "--enable-no-encryption", + "Enable no encryption", + NULL, + "enables using no encryption for data traffic (key exchanged must be encrypted)", + NULL + }, { + CONFIG_NORTEL_CLIENT_ID, 1, 1, + "--nortel-client-id", + "Nortel Client ID ", + "", + "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 ", + "", + "Application Version to report. Note: Default string is generated at runtime.", + config_def_app_version + }, { + CONFIG_IF_NAME, 1, 1, + "--ifname", + "Interface name ", + "", + "visible name of the TUN/TAP interface", + NULL + }, { + CONFIG_IF_MODE, 1, 1, + "--ifmode", + "Interface mode ", + "", + "mode of TUN/TAP interface:\n" + " * tun: virtual point to point interface (default)\n" + " * tap: virtual ethernet interface\n", + config_def_if_mode + }, { + CONFIG_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 ", + "", + "store the pid of background process in ", + config_def_pid_file + }, { + CONFIG_LOCAL_ADDR, 1, 1, + "--local-addr", + "Local Addr ", + "", + "local IP to use for ISAKMP / ESP / ... (0.0.0.0 == automatically assign)", + config_def_local_addr + }, { + CONFIG_LOCAL_PORT, 1, 1, + "--local-port", + "Local Port ", + "<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 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 ", + "", + "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 and path to the CA-PEM-File", + NULL + }, { + CONFIG_CA_DIR, 1, 1, + "--ca-dir", + "CA-Dir ", + "", + "path of the trusted CA-Directory", + config_ca_dir + }, { + CONFIG_IPSEC_TARGET_NETWORK, 1, 1, + "--target-network", + "IPSEC target network ", + "", + "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 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 +#include + +#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 index 0000000..d150749 --- /dev/null +++ b/crypto-gnutls.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..5931f06 --- /dev/null +++ b/crypto-gnutls.h @@ -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 +#include + +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 index 0000000..9d98f8c --- /dev/null +++ b/crypto-openssl.c @@ -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 +#include +#include +#include +#include +#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 index 0000000..57202ff --- /dev/null +++ b/crypto-openssl.h @@ -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 +#include + +typedef struct { + STACK_OF(X509) *stack; +} crypto_ctx; + +#endif /* __CRYPTO_OPENSSL_H__ */ + diff --git a/crypto.c b/crypto.c new file mode 100644 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 + +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 index 0000000..caf71cf --- /dev/null +++ b/decrypt-utils.c @@ -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 +#include +#include +#include +#include + +#include + +#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 index 0000000..826bc33 --- /dev/null +++ b/decrypt-utils.h @@ -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 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 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 + +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 index 0000000..318fce3 --- /dev/null +++ b/enum2debug.pl @@ -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 + +#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 index 0000000..f5e4cd3 --- /dev/null +++ b/isakmp-pkt.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..2616391 --- /dev/null +++ b/isakmp-pkt.h @@ -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 +#endif +#include + +#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 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 index 0000000..e8806cb --- /dev/null +++ b/makeman.pl @@ -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 index 0000000..5590450 --- /dev/null +++ b/math_group.c @@ -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 +#include +#include +#include +#include + +#include + +#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 index 0000000..9f86189 --- /dev/null +++ b/math_group.h @@ -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 + +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 index 0000000..a8ffb74 --- /dev/null +++ b/mk-version @@ -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 index 0000000..c6d697f --- /dev/null +++ b/nortel.txt @@ -0,0 +1,89 @@ +[vpnc-devel] vpnc with Nortel Contivity + +Matt Chapman +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 index 0000000..c93bb75 --- /dev/null +++ b/pcf2vpnc @@ -0,0 +1,117 @@ +#!/usr/bin/env perl +# Stefan Tomanek +# updated by Wolfram Sang on 21.10.06 and on 26.06.07 +## +# pcf2vpnc [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 \n"; + $text .= "# Xauth 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 [vpnc file]\n"; +} diff --git a/pcf2vpnc.1 b/pcf2vpnc.1 new file mode 100644 index 0000000..1cc6136 --- /dev/null +++ b/pcf2vpnc.1 @@ -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 \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 index 0000000..58d9fb3 --- /dev/null +++ b/split_tunnel.txt @@ -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 + * first version + diff --git a/supp.c b/supp.c new file mode 100644 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 +#include + +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 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 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 (Cygwin support) + + based on VTun - Virtual Tunnel over TCP/IP network. + Copyright (C) 1998-2000 Maxim Krasnyansky + 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __sun__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(__CYGWIN__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(__DragonFly__) +#include +#elif defined(__linux__) +#include +#elif defined(__APPLE__) +/* no header for tun */ +#elif defined(__CYGWIN__) +#include "tap-win32.h" +#else +#include +#endif + +#include "sysdep.h" + +#if !defined(HAVE_VASPRINTF) || !defined(HAVE_ASPRINTF) || !defined(HAVE_ERROR) +#include +#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 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 +#include +#include + +#if !defined(__CYGWIN__) +#include +#include +#include +#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 + +#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 +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 +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 index 0000000..7711762 --- /dev/null +++ b/tap-win32.h @@ -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 index 0000000..bba0599 --- /dev/null +++ b/test-crypto.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#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: \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 index 0000000..a491c35 --- /dev/null +++ b/test/cert.pem @@ -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 index 0000000..51b0bf5 --- /dev/null +++ b/test/cert0.pem @@ -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 index 0000000..d734bd7 --- /dev/null +++ b/test/cert1.pem @@ -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 index 0000000..c1f00ad --- /dev/null +++ b/test/cert2.pem @@ -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 index 0000000..0050532 --- /dev/null +++ b/test/root.pem @@ -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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef __SKYOS__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CYGWIN__ +#include +#endif + +#if !defined(__sun__) && !defined(__SKYOS__) +#include +#endif + +#include +#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 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 +#include + +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 index 0000000..6806b93 --- /dev/null +++ b/vpnc-disconnect @@ -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 index 0000000..91586f6 --- /dev/null +++ b/vpnc-script @@ -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 -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 index 0000000..2c39997 --- /dev/null +++ b/vpnc-script-win @@ -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 index 0000000..1664d9b --- /dev/null +++ b/vpnc-script-win.js @@ -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 index 0000000..b4cbfaf --- /dev/null +++ b/vpnc.8.template @@ -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/ +and +.B /etc/vpnc/.conf. +Otherwise +.B +and +.B .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 and +Christian Lackas , based on vpnc README by +Maurice Massar . +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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 index 0000000..fa6742d --- /dev/null +++ b/vpnc.conf @@ -0,0 +1,6 @@ +#IPSec gateway +#IPSec ID +#IPSec secret +#IKE Authmode hybrid +#Xauth username +#Xauth password diff --git a/vpnc.h b/vpnc.h new file mode 100644 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