vpnc-0.5.3r550/0000755000175000017500000000000012402421271010676 5ustar fsfsvpnc-0.5.3r550/tunip.h0000644000175000017500000000643412402421271012215 0ustar fsfs/* 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$ */ #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 vpnc-0.5.3r550/pcf2vpnc0000755000175000017500000000740512402421271012353 0ustar fsfs#!/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$ 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"; } vpnc-0.5.3r550/isakmp-pkt.h0000644000175000017500000000731212402421271013132 0ustar fsfs/* 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$ */ #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; }; extern void *xallocc(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_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 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 vpnc-0.5.3r550/test-crypto.c0000644000175000017500000000761512402421271013350 0ustar fsfs/* 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 "crypto.h" static unsigned char *read_binfile(const char *filename, size_t *len) { int fd, ret; struct stat s; unsigned char *b; if (filename == NULL || len ==NULL) return NULL; fd = open(filename, O_RDONLY); if (fd < 0) { fprintf(stderr, "Error opening file %s\n", filename); return NULL; } ret = fstat(fd, &s); if (ret < 0) { fprintf(stderr, "Error while stat() file %s\n", filename); close(fd); return NULL; } if (s.st_size == 0) { fprintf(stderr, "Empty file %s\n", filename); close(fd); return NULL; } b = malloc(s.st_size); if (b == NULL) { fprintf(stderr, "Error allocating memory\n"); close(fd); return NULL; } ret = read(fd, b, s.st_size); if (ret != s.st_size) { fprintf(stderr, "Error reading file %s\n", filename); free(b); close(fd); return NULL; } close(fd); *len = s.st_size; return b; } int main(int argc, char *argv[]) { crypto_ctx *cctx; crypto_error *error = NULL; int i; unsigned char *data; size_t size = 0, sig_len, dec_len; unsigned char *sig_data, *dec_data; if (argc < 6) { fprintf(stderr, "Need at least 5 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 = 4; 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[3], 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 */ sig_data = read_binfile(argv[1], &sig_len); if (sig_data == NULL) return 1; dec_data = read_binfile(argv[2], &dec_len); if (dec_data == NULL) { free(sig_data); return 1; } size = 0; data = crypto_decrypt_signature(cctx, &sig_data[0], sig_len, &size, CRYPTO_PAD_NONE, &error); if (!data || !size) { fprintf(stderr, "Error decrypting signature: %s\n", error && error->msg ? error->msg : "(none)"); free(dec_data); free(sig_data); return error->code; } if (size != dec_len) { fprintf(stderr, "Error decrypting signature: unexpected " "decrypted size %zd (expected %zu)\n", size, dec_len); free(dec_data); free(sig_data); free(data); return 1; } if (memcmp(data, dec_data, dec_len)) { fprintf(stderr, "Error decrypting signature: decrypted data did" " not match expected decrypted data\n"); free(dec_data); free(sig_data); free(data); return 1; } free(dec_data); free(sig_data); free(data); fprintf(stdout, "Success\n"); crypto_ctx_free(cctx); return 0; } vpnc-0.5.3r550/README0000644000175000017500000002074112402421271011562 0ustar fsfsA 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. ============================================================================ vpnc-0.5.3r550/isakmp-pkt.c0000644000175000017500000006620112402421271013127 0ustar fsfs/* 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$ */ #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; } 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->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; } static 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; } 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_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; 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) && (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 <= r->type) && (r->type <= ISAKMP_XAUTH_06_ATTRIB_ANSWER) && (r->type != ISAKMP_XAUTH_06_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 { 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) && (r->type != ISAKMP_XAUTH_06_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; 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); } vpnc-0.5.3r550/config.c0000644000175000017500000006272312402421271012321 0ustar fsfs/* 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$ */ #define _GNU_SOURCE #include #include #include #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; static void log_to_stderr(int priority __attribute__((unused)), const char *format, ...) { va_list ap; fprintf(stderr, "vpnc: "); 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"); } #define GETLINE_MAX_BUFLEN 200 /* * mostly match getline() semantics but: * 1) accept CEOT (Ctrl-D, 0x04) at begining of line as an input terminator * 2) allocate the buffer at max line size of GETLINE_MAX_BUFLEN bytes * 3) remove trailing newline * * Returns: * -1 for errors or no line (EOF or CEOT) * n the characters in line, excluding (removed) newline and training '\0' */ static ssize_t vpnc_getline(char **lineptr, size_t *n, FILE *stream) { char *buf; size_t buflen, llen = 0; int c, buf_allocated = 0; if (lineptr == NULL || n == NULL) { errno = EINVAL; return -1; } buf = *lineptr; buflen = *n; if (buf == NULL || buflen == 0) { buflen = GETLINE_MAX_BUFLEN; buf = (char *)malloc(buflen); if (buf == NULL) return -1; buf_allocated = 1; } /* Read a line from the input */ while (llen < buflen - 1) { c = fgetc(stream); if (c == EOF || feof(stream)) { if (llen == 0) goto eof_or_ceot; else break; } if (llen == 0 && c == CEOT) goto eof_or_ceot; if (c == '\n' || c == '\r') break; buf[llen++] = (char) c; } buf[llen] = 0; if (buf_allocated) { *lineptr = buf; *n = buflen; } return llen; eof_or_ceot: if (buf_allocated) free(buf); return -1; } static char *vpnc_getpass_program(const char *prompt) { int status, r, i; pid_t pid; int fds[2] = {-1, -1}; char *pass; ssize_t bytes; if (pipe(fds) == -1) goto out; pid = fork(); if (pid == -1) goto out; if (pid == 0) { const char *program = config[CONFIG_PASSWORD_HELPER]; close(fds[0]); fds[0] = -1; if (dup2(fds[1], 1) == -1) _exit(1); close(fds[1]); fds[1] = -1; execl(program, program, prompt, NULL); _exit(1); } close(fds[1]); fds[1] = -1; while ((r = waitpid(pid, &status, 0)) == 0 || (r == -1 && errno == EINTR)) ; if (r == -1) goto out; if (!WIFEXITED(status)) { errno = EFAULT; goto out; } if (WEXITSTATUS(status) != 0) { errno = EIO; goto out; } pass = (char *)malloc(GETLINE_MAX_BUFLEN); if (pass == NULL) goto out; bytes = read(fds[0], pass, GETLINE_MAX_BUFLEN - 1); if (bytes == -1) { free(pass); pass = NULL; goto out; } pass[bytes] = '\0'; for (i = 0 ; i < bytes ; i++) if (pass[i] == '\n' || pass[i] == '\r') { pass[i] = 0; break; } out: if (fds[0] != -1) close(fds[0]); if (fds[1] != -1) close(fds[1]); return pass; } char *vpnc_getpass(const char *prompt) { struct termios t; char *buf = NULL; size_t len = 0; if (config[CONFIG_PASSWORD_HELPER]) { buf = vpnc_getpass_program(prompt); if (buf == NULL) error(1, errno, "can't run password helper program"); return buf; } printf("%s", prompt); fflush(stdout); tcgetattr(STDIN_FILENO, &t); t.c_lflag &= ~ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &t); vpnc_getline(&buf, &len, stdin); t.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &t); printf("\n"); return buf; } 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 "psk"; } 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 only support option * names where one is a prefix of another option IF the longer * option name comes first in this list. */ { CONFIG_IPSEC_GATEWAY, 1, 0, "--gateway", "IPSec gateway", "", "IP/name of your IPSec gateway", NULL }, { CONFIG_IPSEC_ID, 1, 0, "--id", "IPSec ID", "", "your group name", NULL }, { CONFIG_IPSEC_SECRET, 1, 0, NULL, "IPSec secret", "", "your group password (cleartext)", NULL }, { CONFIG_IPSEC_SECRET_OBF, 1, 1, NULL, "IPSec obfuscated secret", "", "your group password (obfuscated)", NULL }, { CONFIG_XAUTH_USERNAME, 1, 0, "--username", "Xauth username", "", "your username", NULL }, { CONFIG_XAUTH_PASSWORD, 1, 0, NULL, "Xauth password", "", "your password (cleartext)", NULL }, { CONFIG_XAUTH_PASSWORD_OBF, 1, 1, NULL, "Xauth obfuscated password", "", "your password (obfuscated)", NULL }, { CONFIG_DOMAIN, 1, 1, "--domain", "Domain", "", "(NT-) Domain name for authentication", NULL }, { CONFIG_XAUTH_INTERACTIVE, 0, 1, "--xauth-inter", "Xauth interactive", NULL, "enable interactive extended authentication (for 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" "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_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" " * psk: pre-shared key (default)\n" " * cert: server + client certificate (not implemented yet)\n" " * hybrid: server certificate + xauth (if built with openssl support)\n", 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 }, { CONFIG_PASSWORD_HELPER, 1, 1, "--password-helper", "Password helper", "", "path to password program or helper name\n", NULL }, { 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; errno = 0; llen = vpnc_getline(&line, &line_length, f); if (llen == -1 && errno) error(1, errno, "reading `%s'", realname); if (llen == -1) break; 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; } /* get option value*/ if (configs[config_names[i].nm] == NULL) { ssize_t start; start = strlen(config_names[i].name); /* ensure whitespace after option name */ if (line[start] == 0) error(0, 0, "option '%s' requires a value!", config_names[i].name); if (!(line[start] == ' ' || line[start] == '\t')) continue; /* fallthrough: "unknown configuration directive" */ /* skip further trailing and leading whitespace */ for (llen--; line[llen] == ' ' || line[llen] == '\t' ; llen--) line[llen] = 0; for (start++; line[start] == ' ' || line[start] == '\t'; start++) ; /* remove optional quotes */ if (start != llen && line[start] == '"' && line[llen] == '"') { start++; line[llen--] = 0; } if (start > llen) error(0, 0, "option '%s' requires a value!", config_names[i].name); configs[config_names[i].nm] = strdup(line + start); } 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, *prompt; 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_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 { printf("%s: unknown authentication mode %s\nknown modes: psk cert hybrid\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 { printf("%s: unknown nat traversal mode %s\nknown modes: natt none force-natt cisco-udp\n", argv[0], config[CONFIG_NATT_MODE]); exit(1); } if (!strcmp(config[CONFIG_IF_MODE], "tun")) { opt_if_mode = IF_MODE_TUN; } else if (!strcmp(config[CONFIG_IF_MODE], "tap")) { opt_if_mode = IF_MODE_TAP; } else { printf("%s: unknown interface mode %s\nknown modes: tun tap\n", argv[0], config[CONFIG_IF_MODE]); exit(1); } if (!strcmp(config[CONFIG_VENDOR], "cisco")) { opt_vendor = VENDOR_CISCO; } else if (!strcmp(config[CONFIG_VENDOR], "netscreen")) { opt_vendor = VENDOR_NETSCREEN; } else { printf("%s: unknown vendor %s\nknown vendors: cisco netscreen\n", argv[0], config[CONFIG_VENDOR]); exit(1); } } if (opt_debug >= 99) { printf("WARNING! active debug level is >= 99, output includes username and password (hex encoded)\n"); fprintf(stderr, "WARNING! active debug level is >= 99, output includes username and password (hex encoded)\n"); } config_deobfuscate(CONFIG_IPSEC_SECRET_OBF, CONFIG_IPSEC_SECRET); config_deobfuscate(CONFIG_XAUTH_PASSWORD_OBF, CONFIG_XAUTH_PASSWORD); for (i = 0; i < LAST_CONFIG; i++) { if (config[i] != NULL || config[CONFIG_NON_INTERACTIVE] != NULL) continue; if (config[CONFIG_XAUTH_INTERACTIVE] && i == CONFIG_XAUTH_PASSWORD) continue; s = NULL; s_len = 0; switch (i) { case CONFIG_IPSEC_GATEWAY: printf("Enter IPSec gateway address: "); break; case CONFIG_IPSEC_ID: printf("Enter IPSec ID for %s: ", config[CONFIG_IPSEC_GATEWAY]); break; case CONFIG_IPSEC_SECRET: asprintf(&prompt, "Enter IPSec secret for %s@%s: ", config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_GATEWAY]); break; case CONFIG_XAUTH_USERNAME: printf("Enter username for %s: ", config[CONFIG_IPSEC_GATEWAY]); break; case CONFIG_XAUTH_PASSWORD: asprintf(&prompt, "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_PASSWORD: s = vpnc_getpass(prompt); free(prompt); if (s == NULL) error(1, 0, "unable to get password"); break; case CONFIG_IPSEC_GATEWAY: case CONFIG_IPSEC_ID: case CONFIG_XAUTH_USERNAME: vpnc_getline(&s, &s_len, stdin); } 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 || config[config_names[i].nm][0] == 0) continue; printf("%s", config_names[i].name); if (config_names[i].needsArgument) { ssize_t last; last = strlen(config[config_names[i].nm]) - 1; if ( config[config_names[i].nm][0] == ' ' || config[config_names[i].nm][last] == ' ' || config[config_names[i].nm][0] == '\t' || config[config_names[i].nm][last] == '\t' || ( config[config_names[i].nm][0] == '"' && config[config_names[i].nm][last] == '"' ) ) { printf(" %s%s%s", "\"", config[config_names[i].nm], "\""); } else { printf(" %s", config[config_names[i].nm]); } } printf("\n"); } exit(0); } if (!config[CONFIG_IPSEC_GATEWAY]) error(1, 0, "missing IPSec gatway address"); if (!config[CONFIG_IPSEC_ID]) error(1, 0, "missing IPSec ID"); if (!config[CONFIG_IPSEC_SECRET]) error(1, 0, "missing IPSec secret"); if (!config[CONFIG_XAUTH_USERNAME]) error(1, 0, "missing Xauth username"); if (!config[CONFIG_XAUTH_PASSWORD] && !config[CONFIG_XAUTH_INTERACTIVE]) error(1, 0, "missing Xauth password"); if (get_dh_group_ike() == NULL) error(1, 0, "IKE DH Group \"%s\" unsupported\n", config[CONFIG_IKE_DH]); if (get_dh_group_ipsec(-1) == NULL) error(1, 0, "Perfect Forward Secrecy \"%s\" unsupported\n", config[CONFIG_IPSEC_PFS]); if (get_dh_group_ike()->ike_sa_id == 0) error(1, 0, "IKE DH Group must not be nopfs\n"); return; } vpnc-0.5.3r550/cisco-decrypt.c0000644000175000017500000000322012402421271013607 0ustar fsfs/* 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); } vpnc-0.5.3r550/crypto.c0000644000175000017500000000630712402421271012370 0ustar fsfs/* 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; } vpnc-0.5.3r550/cisco-decrypt.10000644000175000017500000000173712402421271013540 0ustar fsfs.TH "CISCO-DECRYPT" "1" "August 2007" "cisco-decrypt" "vpnc" .SH "NAME" cisco-decrypt \- decrypts an obfuscated Cisco vpn client pre-shared key .\" .\" $Id$ .\" .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) vpnc-0.5.3r550/Makefile0000644000175000017500000001267012402421271012344 0ustar fsfs# 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$ 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 LIBS += $(shell libgcrypt-config --libs) $(CRYPTO_LDADD) ifeq ($(shell uname -s), SunOS) LIBS += -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) $(LDFLAGS) -o $@ $^ $(LIBS) vpnc.8 : vpnc.8.template makeman.pl vpnc ./makeman.pl cisco-decrypt : cisco-decrypt.o decrypt-utils.o $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) test-crypto : sysdep.o test-crypto.o crypto.o $(CRYPTO_OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) .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/sig_data.bin test/dec_data.bin test/ca_list.pem \ test/cert3.pem test/cert2.pem test/cert1.pem test/cert0.pem 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 vpnc-0.5.3r550/dh.c0000644000175000017500000000534212402421271011441 0ustar fsfs/* 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; } vpnc-0.5.3r550/crypto.h0000644000175000017500000001061612402421271012373 0ustar fsfs/* 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__ */ vpnc-0.5.3r550/vpnc-script-win.js0000644000175000017500000000561112402421271014302 0ustar fsfs// 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"); } vpnc-0.5.3r550/vpnc-script0000755000175000017500000005474412402421271013112 0ustar fsfs#!/bin/sh # # Originally part of vpnc source code: # © 2005-2012 Maurice Massar, Jörg Mayer, Antonio Borneo et al. # © 2009-2012 David Woodhouse # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ################ # # List of parameters passed through environment #* reason -- why this script was called, one of: pre-init connect disconnect reconnect #* 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`" HOOKS_DIR=/etc/vpnc 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 2> /dev/null | grep '^/'`" if ifconfig --help 2>&1 | grep BusyBox > /dev/null; then ifconfig_syntax_inet="" else ifconfig_syntax_inet="inet" fi 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 [ -r /etc/openwrt_release ] && [ -n "$OPENWRT_INTERFACE" ]; then . /etc/functions.sh include /lib/network MODIFYRESOLVCONF=modify_resolvconf_openwrt RESTORERESOLVCONF=restore_resolvconf_openwrt elif [ -x /sbin/resolvconf ] && [ "$OS" != "FreeBSD" ]; then # Optional tool on Debian, Ubuntu, Gentoo - but not FreeBSD, it seems to work different 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 # =========== script hooks ================================================= run_hooks() { HOOK="$1" if [ -d ${HOOKS_DIR}/${HOOK}.d ]; then for script in ${HOOKS_DIR}/${HOOK}.d/* ; do [ -f $script ] && . $script done 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/32" peer "$INTERNAL_IP4_ADDRESS" dev "$TUNDEV" else ifconfig "$TUNDEV" ${ifconfig_syntax_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|OpenBSD) # and probably others... ifconfig "$TUNDEV" destroy ;; FreeBSD) ifconfig "$TUNDEV" destroy > /dev/null 2>&1 & ;; 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|OpenBSD|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 UCI (OpenWRT) ========= modify_resolvconf_openwrt() { add_dns $OPENWRT_INTERFACE $INTERNAL_IP4_DNS } restore_resolvconf_openwrt() { remove_dns $OPENWRT_INTERFACE } # === 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 ! kldstat -q -m if_tun > /dev/null; then kldload if_tun fi if ! ifconfig $TUNDEV > /dev/null; then ifconfig $TUNDEV create fi elif [ "$OS" = "GNU/kFreeBSD" ]; then if [ ! -e /dev/tun ]; then kldload if_tun fi elif [ "$OS" = "NetBSD" ]; then : elif [ "$OS" = "OpenBSD" ]; then if ! ifconfig $TUNDEV > /dev/null; then ifconfig $TUNDEV create fi : 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 if [ -n "$IPROUTE" ]; then if [ -n "$INTERNAL_IP4_ADDRESS" ]; then $IPROUTE addr del "$INTERNAL_IP4_ADDRESS/255.255.255.255" peer "$INTERNAL_IP4_ADDRESS" dev "$TUNDEV" 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 $IPROUTE -6 addr del $INTERNAL_IP6_NETMASK dev $TUNDEV fi else if [ -n "$INTERNAL_IP4_ADDRESS" ]; then ifconfig "$TUNDEV" 0.0.0.0 fi if [ -n "$INTERNAL_IP6_ADDRESS" ] && [ -z "$INTERNAL_IP6_NETMASK" ]; then INTERNAL_IP6_NETMASK="$INTERNAL_IP6_ADDRESS/128" fi if [ -n "$INTERNAL_IP6_NETMASK" ]; then ifconfig "$TUNDEV" inet6 del $INTERNAL_IP6_NETMASK fi 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) run_hooks pre-init do_pre_init ;; connect) run_hooks connect do_connect run_hooks post-connect ;; disconnect) run_hooks disconnect do_disconnect run_hooks post-disconnect ;; reconnect) run_hooks reconnect ;; *) echo "unknown reason '$reason'. Maybe vpnc-script is out of date" 1>&2 exit 1 ;; esac exit 0 vpnc-0.5.3r550/math_group.h0000644000175000017500000000540412402421271013217 0ustar fsfs/* 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_ */ vpnc-0.5.3r550/enum2debug.pl0000755000175000017500000000240512402421271013274 0ustar fsfs#!/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__ vpnc-0.5.3r550/vpnc.h0000644000175000017500000000214412402421271012016 0ustar fsfs/* 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$ */ #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 vpnc-0.5.3r550/math_group.c0000644000175000017500000002110412402421271013205 0ustar fsfs/* 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; } vpnc-0.5.3r550/sysdep.c0000644000175000017500000004014212402421271012352 0ustar fsfs/* 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_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 vpnc-0.5.3r550/tunip.c0000644000175000017500000007115412402421271012211 0ustar fsfs/* 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$ */ /* 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; } else if (ntohl(eh->spi) < 256) { syslog(LOG_NOTICE, "illegal spi %d from peer - continuing", ntohl(eh->spi)); } /* 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); /* * Use _exit(), since exit() will call the handler * registered with atexit() that will remove the * route path to concentrator. */ _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 */ } vpnc-0.5.3r550/pcf2vpnc.10000644000175000017500000000217112402421271012502 0ustar fsfs.TH "PCF2VPNC" "1" "June 2007" "pcf2vpnc " "vpnc" .SH "NAME" pcf2vpnc \- converts VPN\-config files from pcf to vpnc\-format .\" .\" $Id$ .\" .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) vpnc-0.5.3r550/TODO0000644000175000017500000001126212402421271011370 0ustar fsfsTODO 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 vpnc-0.5.3r550/crypto-openssl.h0000644000175000017500000000210012402421271014041 0ustar fsfs/* 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__ */ vpnc-0.5.3r550/crypto-openssl.c0000644000175000017500000002014012402421271014040 0ustar fsfs/* 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; } vpnc-0.5.3r550/decrypt-utils.c0000644000175000017500000000522712402421271013660 0ustar fsfs/* 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$ */ #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; } vpnc-0.5.3r550/test/0000755000175000017500000000000012402421271011655 5ustar fsfsvpnc-0.5.3r550/test/Makefile0000644000175000017500000000437512402421271013326 0ustar fsfs# Makefile to rebuild certificate chain for VPNC test. # Copyright (C) 2013 Antonio Borneo # 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 OPENSSL = openssl CFG = openssl.cnf TIME = -days 7120 # default targets empty. all default clean: # target to rebuild everything rebuild: ca_list.pem cert3.pem sig_data.bin ca1.key cert1.key: $(OPENSSL) genrsa -out $@ 4096 ca2.key ca3.key cert0.key cert2.key cert3.key: $(OPENSSL) genrsa -out $@ 2048 ca1.pem: ca1.key ca2.pem: ca2.key ca3.pem: ca3.key ca%.pem: ca%.key $(OPENSSL) req -new -x509 -key $< -out $@ $(TIME) -batch -text \ -subj "/OU=Root Certification Authority/CN="$@ ca_list.pem: ca1.pem ca2.pem ca3.pem cat $^ > $@ CHAIN_SIGN = $(OPENSSL) req -new -key $(2) -batch -subj "/OU=Cert/CN="$(1) \ | $(OPENSSL) x509 -req $(TIME) -CA $(3) -CAkey $(4) -set_serial 01 \ -out $(1) -text -extfile $(CFG) -extensions usr cert0.pem: cert0.key ca3.pem ca3.key $(CFG) $(call CHAIN_SIGN,cert0.pem,cert0.key,ca3.pem,ca3.key) cert1.pem: cert1.key cert0.pem cert0.key $(CFG) $(call CHAIN_SIGN,cert1.pem,cert1.key,cert0.pem,cert0.key) cert2.pem: cert2.key cert1.pem cert1.key $(CFG) $(call CHAIN_SIGN,cert2.pem,cert2.key,cert1.pem,cert1.key) cert3.pem: cert3.key cert2.pem cert2.key $(CFG) $(call CHAIN_SIGN,cert3.pem,cert3.key,cert2.pem,cert2.key) $(CFG): echo -e '[ usr ]\nbasicConstraints=CA:TRUE' > $(CFG) dec_data.bin: dd if=/dev/urandom of=$@ bs=256 count=1 sig_data.bin: dec_data.bin cert0.key $(OPENSSL) rsautl -decrypt -in $< -out $@ -inkey cert0.key -raw clean_build: rm -f *.pem $(CFG) sig_data.bin clean_key: rm -f *.key dec_data.bin clean_all: clean_build clean_key vpnc-0.5.3r550/test/ca_list.pem0000644000175000017500000003576512402421271014016 0ustar fsfsCertificate: Data: Version: 3 (0x2) Serial Number: 8f:b0:fb:e5:0b:46:cc:4f Signature Algorithm: sha1WithRSAEncryption Issuer: OU=Root Certification Authority, CN=ca1.pem Validity Not Before: Dec 4 13:26:12 2013 GMT Not After : Jun 2 13:26:12 2033 GMT Subject: OU=Root Certification Authority, CN=ca1.pem Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (4096 bit) Modulus: 00:d0:24:08:5e:8c:24:9f:df:fd:8f:f9:84:c0:d9: 88:4c:94:6f:e0:78:64:6d:b8:c6:4a:71:15:1d:ad: 8a:34:1d:64:03:f3:8f:cb:fb:2b:b9:bf:15:3a:18: 32:71:0b:89:29:3a:6a:65:29:43:d2:b7:ed:0b:02: 6f:7e:94:10:67:3e:38:f3:2f:d6:ad:94:e1:04:45: 80:1a:89:3d:20:3b:45:b7:ee:f5:b5:39:f3:1d:3a: 72:e0:ab:86:99:46:0f:0f:50:90:e2:28:b8:9d:1a: 61:84:ed:ca:fb:9f:15:e5:a1:2a:2f:01:de:c6:a6: 4a:4e:3a:5a:b8:a4:cf:8f:90:60:b7:b1:7d:ea:01: ad:d9:0d:01:64:92:a1:3f:b0:24:eb:ac:6e:ff:fb: 2c:65:fd:dd:bc:4a:95:ba:37:a0:ed:ed:13:e6:49: a8:4a:4e:6c:a0:92:40:d8:74:5f:3f:32:79:27:9e: 28:a0:0c:53:53:23:18:db:82:16:b8:72:89:8a:08: 2c:e0:67:71:d4:5d:32:bc:97:89:c5:14:55:2d:b5: ce:e4:28:94:21:38:f9:42:0e:e7:bc:45:8d:43:54: 28:93:75:de:2d:de:83:e2:f5:8a:f1:f7:80:e6:ad: 16:84:fd:2e:59:47:96:71:e9:49:08:72:77:d7:32: 08:2b:a9:a0:7e:bc:5d:a6:b6:2e:44:5e:9d:67:cd: c1:ec:0c:44:ec:47:2d:c3:f4:d2:8e:08:05:03:77: 72:0b:a2:b6:f4:da:32:f3:84:54:ad:46:85:82:9a: 08:79:4e:97:35:2d:14:35:22:35:51:6e:c8:73:8c: 9a:25:90:ef:c1:cf:8b:40:a6:5e:a4:23:d0:28:19: 4e:56:b4:8b:39:74:e8:5a:57:ca:28:0a:bb:aa:e3: 54:3a:18:2f:31:b9:41:53:3e:bf:a4:f0:8b:3b:85: bb:c0:7d:fd:5a:08:b9:d8:84:51:c7:23:27:44:82: c1:af:e4:f2:db:99:46:a1:7f:35:03:f4:1a:a1:3a: b9:55:a0:bf:6b:c9:7f:9f:66:ac:10:c4:99:12:8f: 72:b8:3d:39:85:bf:9f:e1:ea:e2:42:c6:e1:e7:58: 07:1e:0c:c7:43:1a:47:54:a6:77:59:59:20:15:98: e7:30:4e:94:23:ef:c2:96:bd:ca:ab:ea:03:b1:cf: 76:46:1f:d1:45:85:94:a0:f2:74:d4:50:8e:23:24: ca:79:fc:8c:a8:36:61:1c:40:67:fa:b2:f5:59:e9: fa:f2:73:11:52:0e:3c:db:42:21:76:9a:48:24:2e: f2:64:84:52:c2:57:6b:4d:2a:1c:15:8c:00:cf:94: 9e:b0:c3 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 99:77:DD:22:D3:28:B4:E6:7D:40:1D:ED:DB:7F:BD:3C:00:D3:88:28 X509v3 Authority Key Identifier: keyid:99:77:DD:22:D3:28:B4:E6:7D:40:1D:ED:DB:7F:BD:3C:00:D3:88:28 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption 7b:32:1c:c2:21:13:f6:c2:0c:30:e4:59:66:28:2f:69:de:50: c2:e2:c9:f3:b6:36:2f:a4:a9:34:1a:9f:e4:4c:e5:a1:f0:be: a7:e0:a4:f7:e2:a2:36:01:9a:68:4b:1f:a7:90:48:a9:c4:35: d6:47:86:d3:f0:f4:2c:55:f2:2b:39:61:ef:82:32:ea:aa:82: ec:20:7d:37:2f:ba:b4:a7:a6:86:92:72:dc:bc:27:73:d4:c3: f0:bc:2f:16:0d:69:63:5e:f8:78:5c:b7:98:bb:b4:4d:cc:47: f8:36:57:86:5c:bb:55:1b:7b:cd:32:8f:e7:bc:e0:fc:32:5a: ed:5c:c5:5b:1f:c0:c3:9a:54:8a:92:eb:97:c0:9f:24:f2:7f: df:e5:a5:f5:50:98:6c:65:31:40:df:4f:f1:47:f8:86:6f:4c: a1:6a:ef:ab:2a:1f:f1:79:04:fd:2d:80:ca:b3:f3:98:f2:7d: 2b:1a:43:3c:ad:30:59:bb:ea:34:5e:29:e3:76:4d:35:ff:0c: c1:73:5c:cb:9f:48:72:87:f0:d6:96:c1:6c:a1:d7:9a:92:b4: 46:2f:f0:ef:61:8b:02:93:ef:40:40:33:29:8c:c7:20:77:ca: 7f:8a:25:3e:10:0a:e0:23:c9:b5:6d:6b:15:89:56:2a:f2:d2: 75:52:15:80:4c:04:35:42:ac:bc:a0:1f:48:e4:cc:d8:62:88: 5e:5c:a2:aa:9e:d4:63:77:46:d5:51:5d:00:2c:fc:99:e2:c3: 31:a6:19:22:db:66:8c:37:35:c4:7d:5f:fc:ee:0f:a2:d3:cd: 2d:90:49:af:35:4a:47:96:6b:3e:91:6c:56:4b:29:39:01:f2: 17:1d:30:bb:74:0b:9d:8d:54:c3:37:1e:32:cb:b8:99:85:88: 00:be:e2:21:85:fc:42:d2:ab:bf:34:99:96:46:7f:28:6e:4c: c9:dc:f3:9d:37:9c:f6:1e:7e:bb:9e:9a:66:02:df:3f:68:1f: 5b:a3:38:dd:fe:c6:93:7b:1f:98:76:54:79:c1:8a:0b:ea:91: 00:e5:7c:9d:94:93:f1:e4:44:70:3f:81:8f:12:32:13:10:2a: 5f:81:b4:e7:40:ea:16:e9:23:f8:b7:f4:c9:70:24:08:4a:45: 50:36:48:e1:1a:25:fa:af:f3:17:26:34:43:22:aa:7c:86:45: f8:b7:36:5a:44:16:51:98:84:df:e2:50:33:b5:ff:42:61:8d: e5:ca:44:b0:06:12:ad:01:90:c6:fe:90:25:db:ab:42:b8:2a: d1:c8:f2:5f:ad:a0:21:df:a3:99:96:d4:1d:87:1e:45:42:a1: 85:5f:81:19:a5:db:5c:3e -----BEGIN CERTIFICATE----- MIIFRTCCAy2gAwIBAgIJAI+w++ULRsxPMA0GCSqGSIb3DQEBBQUAMDkxJTAjBgNV BAsMHFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEDAOBgNVBAMMB2NhMS5w ZW0wHhcNMTMxMjA0MTMyNjEyWhcNMzMwNjAyMTMyNjEyWjA5MSUwIwYDVQQLDBxS b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRAwDgYDVQQDDAdjYTEucGVtMIIC IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0CQIXowkn9/9j/mEwNmITJRv 4HhkbbjGSnEVHa2KNB1kA/OPy/srub8VOhgycQuJKTpqZSlD0rftCwJvfpQQZz44 8y/WrZThBEWAGok9IDtFt+71tTnzHTpy4KuGmUYPD1CQ4ii4nRphhO3K+58V5aEq LwHexqZKTjpauKTPj5Bgt7F96gGt2Q0BZJKhP7Ak66xu//ssZf3dvEqVujeg7e0T 5kmoSk5soJJA2HRfPzJ5J54ooAxTUyMY24IWuHKJiggs4Gdx1F0yvJeJxRRVLbXO 5CiUITj5Qg7nvEWNQ1Qok3XeLd6D4vWK8feA5q0WhP0uWUeWcelJCHJ31zIIK6mg frxdprYuRF6dZ83B7AxE7Ectw/TSjggFA3dyC6K29Noy84RUrUaFgpoIeU6XNS0U NSI1UW7Ic4yaJZDvwc+LQKZepCPQKBlOVrSLOXToWlfKKAq7quNUOhgvMblBUz6/ pPCLO4W7wH39Wgi52IRRxyMnRILBr+Ty25lGoX81A/QaoTq5VaC/a8l/n2asEMSZ Eo9yuD05hb+f4eriQsbh51gHHgzHQxpHVKZ3WVkgFZjnME6UI+/Clr3Kq+oDsc92 Rh/RRYWUoPJ01FCOIyTKefyMqDZhHEBn+rL1Wen68nMRUg4820IhdppIJC7yZIRS wldrTSocFYwAz5SesMMCAwEAAaNQME4wHQYDVR0OBBYEFJl33SLTKLTmfUAd7dt/ vTwA04goMB8GA1UdIwQYMBaAFJl33SLTKLTmfUAd7dt/vTwA04goMAwGA1UdEwQF MAMBAf8wDQYJKoZIhvcNAQEFBQADggIBAHsyHMIhE/bCDDDkWWYoL2neUMLiyfO2 Ni+kqTQan+RM5aHwvqfgpPfiojYBmmhLH6eQSKnENdZHhtPw9CxV8is5Ye+CMuqq guwgfTcvurSnpoaScty8J3PUw/C8LxYNaWNe+Hhct5i7tE3MR/g2V4Zcu1Ube80y j+e84PwyWu1cxVsfwMOaVIqS65fAnyTyf9/lpfVQmGxlMUDfT/FH+IZvTKFq76sq H/F5BP0tgMqz85jyfSsaQzytMFm76jReKeN2TTX/DMFzXMufSHKH8NaWwWyh15qS tEYv8O9hiwKT70BAMymMxyB3yn+KJT4QCuAjybVtaxWJViry0nVSFYBMBDVCrLyg H0jkzNhiiF5coqqe1GN3RtVRXQAs/JniwzGmGSLbZow3NcR9X/zuD6LTzS2QSa81 SkeWaz6RbFZLKTkB8hcdMLt0C52NVMM3HjLLuJmFiAC+4iGF/ELSq780mZZGfyhu TMnc8503nPYefruemmYC3z9oH1ujON3+xpN7H5h2VHnBigvqkQDlfJ2Uk/HkRHA/ gY8SMhMQKl+BtOdA6hbpI/i39MlwJAhKRVA2SOEaJfqv8xcmNEMiqnyGRfi3NlpE FlGYhN/iUDO1/0JhjeXKRLAGEq0BkMb+kCXbq0K4KtHI8l+toCHfo5mW1B2HHkVC oYVfgRml21w+ -----END CERTIFICATE----- Certificate: Data: Version: 3 (0x2) Serial Number: b6:36:ad:93:f7:5f:26:91 Signature Algorithm: sha1WithRSAEncryption Issuer: OU=Root Certification Authority, CN=ca2.pem Validity Not Before: Dec 4 13:26:13 2013 GMT Not After : Jun 2 13:26:13 2033 GMT Subject: OU=Root Certification Authority, CN=ca2.pem Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:ea:19:0b:81:0f:b8:f1:1b:8c:a3:d6:ca:43:06: 2d:b5:d3:45:95:cd:cb:cc:da:f4:4b:d0:da:77:2a: 60:53:47:61:aa:79:99:f8:24:5c:5f:e6:57:72:c8: 22:6d:3c:6f:a8:45:a3:5f:f9:c3:0d:76:bf:6c:a7: f9:25:be:d2:a2:08:cb:8b:4e:00:41:e8:40:88:86: a2:63:a3:c1:35:f8:82:ea:7e:53:8c:c9:3f:95:33: d6:24:51:22:e8:b9:b1:1b:43:67:49:aa:57:4a:d5: ad:0c:11:bf:c9:58:d7:24:97:51:34:9a:30:9a:d1: f0:ec:2d:7b:1c:ef:fd:af:05:e4:69:09:81:86:8d: 8e:dc:33:8d:1f:4d:20:de:d1:8d:5e:d7:de:fc:e3: 7e:b2:0a:0b:31:23:ff:de:ff:61:44:3f:72:ec:48: ca:01:94:2e:8e:3f:cf:fe:af:b3:19:da:e8:15:39: 66:15:db:a4:5a:c0:38:8e:2d:94:31:96:a7:08:fc: aa:03:2e:e2:ab:f9:53:fc:8a:42:ef:2c:d9:1d:cd: 81:b9:9a:fc:3e:08:c3:63:64:57:dd:18:9e:61:52: ab:43:fc:af:7a:3d:8d:99:52:73:0d:86:a7:d7:01: 34:2d:cd:e6:c4:cc:99:01:dd:c7:cf:b1:64:8f:2c: d5:73 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 68:C1:E8:F8:2B:27:CC:15:73:17:5E:E9:96:58:B0:1E:D8:9C:8D:9E X509v3 Authority Key Identifier: keyid:68:C1:E8:F8:2B:27:CC:15:73:17:5E:E9:96:58:B0:1E:D8:9C:8D:9E X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption 41:39:9e:d2:fb:82:4f:bc:92:56:d7:de:43:e4:94:72:60:a2: 0e:0c:50:9a:8a:fe:f2:74:63:36:4c:a8:1c:49:bc:aa:82:ae: 39:ff:69:67:46:04:f9:17:b2:da:b1:cd:14:f6:7e:0a:e7:87: 2d:87:98:59:81:91:93:e6:61:f4:56:6e:7d:22:79:3d:a4:73: d5:00:a2:1a:8f:5c:cb:e6:53:04:55:a3:cc:5b:de:da:8e:63: 49:72:d8:10:fd:be:dc:e3:50:83:06:4c:da:96:d3:37:dd:3e: f5:41:08:0b:63:3d:47:08:c1:0b:be:4c:87:28:44:9f:72:fd: 2a:aa:44:2b:cd:a5:a3:11:1e:01:e0:f5:c8:df:88:ed:8c:07: fa:99:dd:dd:2a:67:80:70:81:d3:1d:13:40:de:a1:25:e1:f3: 05:7d:97:b1:c4:d6:17:01:1c:57:a9:70:4c:22:31:45:6a:9e: 4c:d4:14:41:8a:22:d6:a5:49:6b:4b:8a:4d:80:80:ab:1d:b4: 8f:71:6f:78:c8:a2:52:cf:36:7c:f3:0f:f7:7d:19:22:31:ec: 88:f2:16:61:ff:a0:6b:4c:39:57:1d:a2:ce:5a:9e:dd:a7:4b: 31:52:80:9b:ca:fa:83:43:92:91:03:2a:d1:74:b7:b6:08:9b: fa:88:ef:e1 -----BEGIN CERTIFICATE----- MIIDRTCCAi2gAwIBAgIJALY2rZP3XyaRMA0GCSqGSIb3DQEBBQUAMDkxJTAjBgNV BAsMHFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEDAOBgNVBAMMB2NhMi5w ZW0wHhcNMTMxMjA0MTMyNjEzWhcNMzMwNjAyMTMyNjEzWjA5MSUwIwYDVQQLDBxS b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRAwDgYDVQQDDAdjYTIucGVtMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6hkLgQ+48RuMo9bKQwYttdNF lc3LzNr0S9DadypgU0dhqnmZ+CRcX+ZXcsgibTxvqEWjX/nDDXa/bKf5Jb7SogjL i04AQehAiIaiY6PBNfiC6n5TjMk/lTPWJFEi6LmxG0NnSapXStWtDBG/yVjXJJdR NJowmtHw7C17HO/9rwXkaQmBho2O3DONH00g3tGNXtfe/ON+sgoLMSP/3v9hRD9y 7EjKAZQujj/P/q+zGdroFTlmFdukWsA4ji2UMZanCPyqAy7iq/lT/IpC7yzZHc2B uZr8PgjDY2RX3RieYVKrQ/yvej2NmVJzDYan1wE0Lc3mxMyZAd3Hz7FkjyzVcwID AQABo1AwTjAdBgNVHQ4EFgQUaMHo+CsnzBVzF17plliwHticjZ4wHwYDVR0jBBgw FoAUaMHo+CsnzBVzF17plliwHticjZ4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B AQUFAAOCAQEAQTme0vuCT7ySVtfeQ+SUcmCiDgxQmor+8nRjNkyoHEm8qoKuOf9p Z0YE+Rey2rHNFPZ+CueHLYeYWYGRk+Zh9FZufSJ5PaRz1QCiGo9cy+ZTBFWjzFve 2o5jSXLYEP2+3ONQgwZM2pbTN90+9UEIC2M9RwjBC75MhyhEn3L9KqpEK82loxEe AeD1yN+I7YwH+pnd3SpngHCB0x0TQN6hJeHzBX2XscTWFwEcV6lwTCIxRWqeTNQU QYoi1qVJa0uKTYCAqx20j3FveMiiUs82fPMP930ZIjHsiPIWYf+ga0w5Vx2izlqe 3adLMVKAm8r6g0OSkQMq0XS3tgib+ojv4Q== -----END CERTIFICATE----- Certificate: Data: Version: 3 (0x2) Serial Number: 9f:ab:17:25:a8:44:2d:cd Signature Algorithm: sha1WithRSAEncryption Issuer: OU=Root Certification Authority, CN=ca3.pem Validity Not Before: Dec 4 13:26:13 2013 GMT Not After : Jun 2 13:26:13 2033 GMT Subject: OU=Root Certification Authority, CN=ca3.pem Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:bd:8b:3c:b6:0a:8e:17:f0:49:0e:36:c7:41:09: 9a:ab:b4:47:f5:66:38:ae:92:45:b9:2a:53:1e:a6: ea:01:db:2b:be:fc:cf:1b:82:a6:a0:df:ef:96:4b: f4:44:3a:97:37:fd:0f:93:22:ee:94:97:d1:17:50: 09:07:07:2a:6e:2d:ea:fb:21:cf:24:85:db:c1:93: 95:fc:95:3d:13:4e:42:6e:56:20:57:2c:a2:e6:b1: e4:41:eb:0c:14:b1:39:d5:8a:69:a0:df:26:af:15: cf:13:3d:81:18:38:32:9a:40:ad:9d:82:6f:43:38: 35:5b:44:55:fc:20:bd:30:3b:65:bb:eb:1c:52:6f: 1b:a9:04:19:15:47:f3:03:9d:b6:f6:a6:f9:da:0c: 5f:41:36:e6:47:f7:d2:15:25:3c:07:fc:7e:88:08: f3:b8:17:e8:f2:7b:8e:e5:ba:27:d0:43:9a:a5:01: 13:3b:bf:37:44:d6:65:ce:81:fb:a6:35:b4:d7:4d: 6d:31:11:de:20:0b:2d:49:fc:60:9b:37:bf:03:5b: c2:46:00:63:5d:64:80:48:b4:f5:49:dc:97:a9:7e: 6e:c7:33:74:71:1e:8a:7d:d4:d5:e0:d2:a5:9c:f0: 30:0a:1a:63:59:d3:f5:ce:93:e2:60:86:38:94:13: 35:05 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: E5:0F:44:4E:46:BE:03:BE:BF:7E:3F:83:26:17:94:39:9A:38:34:55 X509v3 Authority Key Identifier: keyid:E5:0F:44:4E:46:BE:03:BE:BF:7E:3F:83:26:17:94:39:9A:38:34:55 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption 82:3a:9b:f8:01:23:35:18:9e:ee:f0:0e:45:f9:45:de:cc:42: 69:f6:9d:9f:c6:6f:e3:78:21:94:1c:aa:23:9d:3c:11:fd:83: 92:2b:4d:c6:0b:3f:e5:f1:da:2e:ab:a4:30:56:ae:5d:90:62: ee:5e:ee:c3:27:2b:3e:9e:4f:57:69:65:50:52:60:41:07:8f: b2:15:ec:27:14:58:c6:9f:4f:f1:b2:86:09:15:f3:b6:53:36: 34:4c:c2:c5:50:b3:57:25:d0:44:0d:d6:2f:42:cc:54:b6:c8: e7:53:24:b7:b9:d4:63:ba:0a:a3:db:1e:16:40:4c:bb:1d:c4: 06:01:8d:b1:9a:7b:21:df:6d:c4:f3:e3:12:30:56:d9:43:3b: 43:1a:da:8e:8c:56:38:92:e9:d5:9d:3c:51:58:ed:e0:2b:f2: 29:7f:1c:0c:f0:df:a5:da:14:70:3b:85:a5:39:14:bf:a2:13: 05:25:95:0d:8d:3b:28:e9:5c:26:5c:14:e8:56:da:c4:a7:f9: 93:3a:c1:60:41:2b:bf:81:2a:fe:e2:75:ec:cf:8d:77:1b:6e: b8:b2:50:84:d1:ce:67:86:7f:06:6e:5d:7e:29:92:a0:d8:c4: 6c:bb:79:3d:8f:59:dd:d4:02:05:8a:93:ef:5c:7b:8f:38:7b: 0e:b9:3a:f3 -----BEGIN CERTIFICATE----- MIIDRTCCAi2gAwIBAgIJAJ+rFyWoRC3NMA0GCSqGSIb3DQEBBQUAMDkxJTAjBgNV BAsMHFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEDAOBgNVBAMMB2NhMy5w ZW0wHhcNMTMxMjA0MTMyNjEzWhcNMzMwNjAyMTMyNjEzWjA5MSUwIwYDVQQLDBxS b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRAwDgYDVQQDDAdjYTMucGVtMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYs8tgqOF/BJDjbHQQmaq7RH 9WY4rpJFuSpTHqbqAdsrvvzPG4KmoN/vlkv0RDqXN/0PkyLulJfRF1AJBwcqbi3q +yHPJIXbwZOV/JU9E05CblYgVyyi5rHkQesMFLE51YppoN8mrxXPEz2BGDgymkCt nYJvQzg1W0RV/CC9MDtlu+scUm8bqQQZFUfzA5229qb52gxfQTbmR/fSFSU8B/x+ iAjzuBfo8nuO5bon0EOapQETO783RNZlzoH7pjW0101tMRHeIAstSfxgmze/A1vC RgBjXWSASLT1SdyXqX5uxzN0cR6KfdTV4NKlnPAwChpjWdP1zpPiYIY4lBM1BQID AQABo1AwTjAdBgNVHQ4EFgQU5Q9ETka+A76/fj+DJheUOZo4NFUwHwYDVR0jBBgw FoAU5Q9ETka+A76/fj+DJheUOZo4NFUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B AQUFAAOCAQEAgjqb+AEjNRie7vAORflF3sxCafadn8Zv43ghlByqI508Ef2DkitN xgs/5fHaLqukMFauXZBi7l7uwycrPp5PV2llUFJgQQePshXsJxRYxp9P8bKGCRXz tlM2NEzCxVCzVyXQRA3WL0LMVLbI51Mkt7nUY7oKo9seFkBMux3EBgGNsZp7Id9t xPPjEjBW2UM7QxrajoxWOJLp1Z08UVjt4CvyKX8cDPDfpdoUcDuFpTkUv6ITBSWV DY07KOlcJlwU6FbaxKf5kzrBYEErv4Eq/uJ17M+NdxtuuLJQhNHOZ4Z/Bm5dfimS oNjEbLt5PY9Z3dQCBYqT71x7jzh7Drk68w== -----END CERTIFICATE----- vpnc-0.5.3r550/test/dec_data.bin0000644000175000017500000000040012402421271014065 0ustar fsfs|*ä`Ÿ«Öv{»Ó£aPVäaq\G®J‰øGaL?Ö—· „†Ýém>‰àOz•?näͲ€>>—|ßÕÿËûqœï¡öŒ6³c2õ^„ˆã-š×ìQ” nißéF;Ù À`:š¨ÆxÉó»Ê¨µk¨ûëÝVÄþ|„±ý’½â²”W= ñîÊ슶# D à±h8V|f`T‹\€7”'‰G,$EkvÝûñ1鷺•K‘œ†¦H¢ZAd1€k³ F²aIõ.;{Â#1fIVPiiZ\‚h–Áv•­½_–mþÞejxGcvpnc-0.5.3r550/test/sig_data.bin0000644000175000017500000000040012402421271014114 0ustar fsfs1O ágQ,ö±K "¤âeD£ ×=bquQ±ã”I0«x{fsÃê™O»2G wp~1û]Î…9»oÓòœUÍ0<‚U0æ%£ Õ¯n‚A®•[‘¹9„´’g“\mÿ³w«¹L*À÷:;*4ÅŒ¼Ž¨¸<¥¬\f,Ü%ÿ„sJƒ; `±A¥;BIÈç ¶söÌW$SÆœt öz®˜4TÇ ¬£ð\ç=©Tw½K·sÀ¼µé†ƒÜ!„wb©Nl×—2õµçDlê<†¤+ÑÏóCVXÂ’¨ZÅi£±'/¾ VPNC includes a wrapper around openssl and gnutls to offer single set of crypto-API. The program test-crypto.c is used to verify the API. This folder "test" provides a chain of certificates and an encrypted binary. test-crypto.c verifies the certificate chain, decrypts the binary and compare it against expected result. See below for more details on how to use test-crypto. openSSL is required to rebuild the test files. To avoid the dependence from openSSL during SW compile, all required files are distribuited together with the VPNC source code. The Makefile in this folder is able to rebuild all the certificates and the binary. make clean_all to cleanup the folder and make rebuild to re-build everything from scratch. Since both cryptographic keys and binary are generated through random functions, results are not replicable across executions. Use make clean_build if you want to cleanup the folder but keep either keys and binary file. Files in the folder: - readme.txt: This file. - Makefile: To rebuild all following file. - ca1.key ca2.key ca3.key: Pairs of private and public keys, used for certificate authorities. - ca1.pem ca2.pem ca3.pem: Self signed certificate of the certificate authorities. - ca_list.pem: Single file containing all the certificates of the three CA above. - cert0.key cert1.key cert2.key cert3.key: Pairs of private and public keys, used for certificates. - cert0.pem cert1.pem cert2.pem cert3.pem: Certificates derived from ".key" files above. Certificates are signed in chain: ca3.pem -> cert0.pem -> cert1.pem -> -> cert2.pem -> cert3.pem Self signed certificate "ca3.pem" signs the certificate "cert0.pem", that in turn signs "cert1.pem", and so on. - dec_data.bin: Binary random data. File size equal to private key size "cert0.key" (256 byte = 2048 bit). - sig_data.bin: Data from "dec_data.bin" RSA encrypted through private key in "cert0.pem". - openssl.cnf: Temporarily config file for openSSL flags that cannot be passed through command line. The program test-crypto.c requires at least 5 arguments: test-crypto - is the encrypted binary; - is the reference binary before encryption; - is a list of CA certificates, one of them signs ; - ... is the chain of certificates. vpnc-0.5.3r550/test/cert1.pem0000644000175000017500000001212412402421271013376 0ustar fsfsCertificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: OU=Cert, CN=cert0.pem Validity Not Before: Dec 4 13:26:13 2013 GMT Not After : Jun 2 13:26:13 2033 GMT Subject: OU=Cert, CN=cert1.pem Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (4096 bit) Modulus: 00:b7:8a:db:2e:f3:c7:6c:2c:78:7e:29:af:66:50: d4:ff:f9:c1:29:2b:96:ad:b9:5f:c0:7b:02:39:09: cc:46:5e:24:9e:e4:57:8b:43:d6:28:ae:91:2b:38: dd:c6:e1:08:cc:22:7e:dd:87:79:06:28:98:81:b0: 35:5e:75:ca:77:f6:15:34:9f:30:f9:cb:cc:fd:ae: c6:91:1c:eb:45:fa:4b:92:fc:d6:27:ad:07:ac:20: d2:6f:19:e4:c8:6b:3f:c0:17:20:c2:56:2a:6e:46: 0d:c1:a1:39:f2:9c:65:57:8d:4b:b8:a4:60:36:13: cf:68:3a:4c:cd:35:b0:77:3a:ec:e7:18:2b:da:b2: b5:95:97:ae:22:ae:23:44:99:62:10:4d:fa:1f:62: 93:93:35:7b:19:dc:51:3e:44:63:f8:95:c1:6a:62: cf:d4:d3:67:9b:82:74:f9:d8:ac:06:0e:f6:5e:3a: 76:8f:92:12:fe:ff:9d:11:8b:21:47:d6:b1:e8:53: c4:a5:12:7d:d7:21:06:96:93:34:f0:13:57:12:3b: 3c:4f:9b:7d:c0:a6:d0:cc:d2:c3:07:b9:e8:46:62: d0:8e:49:14:1d:ae:69:34:a5:21:58:da:95:d6:af: 84:5e:de:5f:e3:c3:b6:5d:0c:fd:33:f5:fe:c1:df: 69:f7:11:0d:88:63:24:ff:1a:79:cd:76:81:2a:59: f7:32:27:6f:b0:12:1b:0c:a8:ac:b8:c3:85:f6:63: 7e:bd:bd:97:86:09:b6:1b:51:54:2e:03:02:9e:ae: 44:07:2b:48:7e:34:76:fe:f8:6e:28:81:14:8b:ef: 24:d0:eb:c3:f2:1f:4c:93:24:51:cd:5f:06:af:26: 8e:08:da:aa:8b:8a:06:f5:ed:64:c2:4f:9b:f7:05: ea:be:ab:24:1b:64:f0:01:99:40:8c:11:dd:9c:28: 5d:6e:ac:b4:c0:f2:06:e9:14:ca:e0:b4:47:af:2d: 51:4e:ee:a7:26:38:ba:97:91:8d:fe:00:19:0c:ca: ac:2b:d1:57:ca:34:f4:1c:14:21:01:25:ed:9e:4c: cd:47:f8:7f:9a:88:37:50:0f:28:71:2d:e5:23:5a: 7f:08:1c:9e:05:ab:50:f6:a0:c4:63:74:d1:88:27: 8c:c5:16:5a:f5:f0:79:77:c3:69:6d:88:17:8f:79: 24:49:d3:69:79:59:c0:63:dc:a9:db:53:ea:dd:78: 8c:7a:83:31:b4:1c:c2:8c:9e:14:85:95:9f:3c:21: c2:f6:50:53:68:d9:c2:45:cf:94:91:87:94:62:3d: b1:97:ac:96:2d:f0:c1:7c:15:62:00:91:26:58:b9: 61:4e:ff Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption 9f:4e:99:95:c8:87:7f:87:56:4c:88:6b:9f:9d:ca:f7:2f:07: 88:5d:0e:14:a5:1a:6c:a0:4f:36:e1:76:a7:14:8c:44:51:1d: 61:35:aa:75:16:4a:94:a8:b2:05:0e:df:21:ad:53:2e:85:ca: dc:6a:8e:cf:78:77:01:e1:d5:e6:96:e0:3d:da:29:1c:3e:82: f8:9d:c1:ad:1c:dc:88:dd:b5:cf:27:db:74:3b:7b:33:04:44: b8:ae:e8:42:ae:16:67:a3:73:13:07:85:f7:0f:cf:54:a2:91: 8f:b6:51:3c:9a:42:c4:23:47:5f:de:69:93:4b:aa:80:b4:1c: 38:67:98:ab:ae:06:16:cf:55:b3:a2:4c:29:36:60:85:05:a1: 9f:e9:a5:85:6d:95:55:6b:ea:bb:bf:eb:a9:77:a6:50:5a:95: b2:7b:f1:3d:3e:a2:fe:c9:6d:f2:b7:a2:f2:cd:a2:20:92:cc: 16:fc:2e:62:e2:a2:5d:be:59:d2:cc:13:36:ca:58:4a:5a:de: e6:89:de:e8:f9:5e:1a:ca:05:c1:dd:46:4a:e8:3d:89:a4:78: 07:65:fc:ea:55:aa:b9:3b:c9:d7:a6:e0:2d:5d:0c:b1:9a:b4: 6f:95:1b:40:ae:17:f6:c6:2c:19:51:19:a7:48:68:0d:6f:e5: 5d:e9:33:24 -----BEGIN CERTIFICATE----- MIID0TCCArmgAwIBAgIBATANBgkqhkiG9w0BAQUFADAjMQ0wCwYDVQQLDARDZXJ0 MRIwEAYDVQQDDAljZXJ0MC5wZW0wHhcNMTMxMjA0MTMyNjEzWhcNMzMwNjAyMTMy NjEzWjAjMQ0wCwYDVQQLDARDZXJ0MRIwEAYDVQQDDAljZXJ0MS5wZW0wggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC3itsu88dsLHh+Ka9mUNT/+cEpK5at uV/AewI5CcxGXiSe5FeLQ9YorpErON3G4QjMIn7dh3kGKJiBsDVedcp39hU0nzD5 y8z9rsaRHOtF+kuS/NYnrQesINJvGeTIaz/AFyDCVipuRg3BoTnynGVXjUu4pGA2 E89oOkzNNbB3OuznGCvasrWVl64iriNEmWIQTfofYpOTNXsZ3FE+RGP4lcFqYs/U 02ebgnT52KwGDvZeOnaPkhL+/50RiyFH1rHoU8SlEn3XIQaWkzTwE1cSOzxPm33A ptDM0sMHuehGYtCOSRQdrmk0pSFY2pXWr4Re3l/jw7ZdDP0z9f7B32n3EQ2IYyT/ GnnNdoEqWfcyJ2+wEhsMqKy4w4X2Y369vZeGCbYbUVQuAwKerkQHK0h+NHb++G4o gRSL7yTQ68PyH0yTJFHNXwavJo4I2qqLigb17WTCT5v3Beq+qyQbZPABmUCMEd2c KF1urLTA8gbpFMrgtEevLVFO7qcmOLqXkY3+ABkMyqwr0VfKNPQcFCEBJe2eTM1H +H+aiDdQDyhxLeUjWn8IHJ4Fq1D2oMRjdNGIJ4zFFlr18Hl3w2ltiBePeSRJ02l5 WcBj3KnbU+rdeIx6gzG0HMKMnhSFlZ88IcL2UFNo2cJFz5SRh5RiPbGXrJYt8MF8 FWIAkSZYuWFO/wIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA A4IBAQCfTpmVyId/h1ZMiGufncr3LweIXQ4UpRpsoE824XanFIxEUR1hNap1FkqU qLIFDt8hrVMuhcrcao7PeHcB4dXmluA92ikcPoL4ncGtHNyI3bXPJ9t0O3szBES4 ruhCrhZno3MTB4X3D89UopGPtlE8mkLEI0df3mmTS6qAtBw4Z5irrgYWz1Wzokwp NmCFBaGf6aWFbZVVa+q7v+upd6ZQWpWye/E9PqL+yW3yt6LyzaIgkswW/C5i4qJd vlnSzBM2ylhKWt7mid7o+V4aygXB3UZK6D2JpHgHZfzqVaq5O8nXpuAtXQyxmrRv lRtArhf2xiwZURmnSGgNb+Vd6TMk -----END CERTIFICATE----- vpnc-0.5.3r550/test/cert2.pem0000644000175000017500000001155512402421271013406 0ustar fsfsCertificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: OU=Cert, CN=cert1.pem Validity Not Before: Dec 4 13:26:13 2013 GMT Not After : Jun 2 13:26:13 2033 GMT Subject: OU=Cert, CN=cert2.pem Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:bd:bc:80:34:96:c5:ea:6f:81:aa:91:be:f5:32: d9:8a:9f:02:67:b0:45:1c:3d:68:df:89:7d:af:be: fc:69:d4:1d:0f:72:ae:c8:4f:2c:f4:e2:04:b3:28: db:b9:05:ee:d7:87:c7:87:3f:76:b9:c7:8e:57:ec: 4a:c1:e3:8b:b4:14:d4:a3:a5:13:16:b7:18:3a:97: 5c:cd:c6:d6:aa:54:88:29:b1:75:d2:9d:2e:29:ef: e5:5c:50:46:02:13:b2:d7:1a:2e:38:50:cc:2c:fc: 62:fa:61:61:f7:86:18:a9:c9:b9:af:c0:0e:f9:d3: 88:1b:91:27:b0:e6:e6:16:98:fd:9b:f6:c4:e2:76: d2:63:da:77:21:b0:8d:a1:c8:d9:ce:84:3c:57:af: 99:19:7b:01:8c:f1:ae:e1:7c:ac:13:a6:03:a0:ab: a2:f6:ea:7d:de:b2:43:12:e5:23:ad:df:48:2e:bc: f2:76:96:2b:a0:1c:dc:60:84:d7:de:68:9e:2f:5c: f6:df:49:4e:05:8d:07:39:27:5e:49:45:88:86:33: 16:1a:5f:b1:a2:d1:78:ff:30:36:25:b8:05:c1:8a: dc:b4:6c:b6:3e:52:39:1e:61:dc:eb:bb:da:49:1d: d1:1a:06:76:22:ab:94:07:c7:0e:58:cc:e0:c6:ff: 8c:d3 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption 6f:02:24:5d:60:3b:76:e8:aa:a5:37:d7:75:18:72:fd:e0:9a: ce:aa:50:5e:e6:83:93:1b:2d:c4:47:5b:d6:d4:8d:d2:ba:6f: af:a7:a8:78:6a:06:78:7e:9d:83:29:7d:9b:8a:f7:8d:7b:76: d8:0d:0b:7e:b9:bd:15:e8:16:9a:c5:4b:48:c7:26:ba:37:fe: f3:8f:dd:05:13:38:31:79:1a:f4:24:49:03:6d:f8:53:d7:01: 44:79:67:ba:6a:d4:40:7d:56:4d:c4:a5:99:aa:a9:da:84:44: e8:29:ea:bd:5e:5a:7d:c0:7d:e0:7e:0c:12:85:65:ef:cd:f8: b6:56:9e:05:97:d4:48:d7:86:96:75:e6:cc:51:60:7f:eb:ed: a4:e0:9e:c6:70:d9:ce:17:8e:41:16:7b:06:3d:c7:33:d3:d9: 08:8d:17:4e:a5:13:6a:d7:e2:ce:cc:74:ce:14:76:0e:aa:1f: 8d:f5:c8:ef:a0:34:e4:ed:f8:25:b5:8d:d2:3f:65:c4:75:97: 6a:ae:0f:02:5e:61:a1:0d:a1:7c:53:fd:10:75:4f:19:71:05: 6b:26:18:4c:95:85:7f:50:0f:a5:2d:0f:0a:07:a4:aa:ce:df: 3c:32:47:14:88:73:e1:6b:70:fb:53:23:06:bb:66:91:b8:2a: 23:9f:63:ab:40:a5:71:3d:c6:0a:d3:e5:a2:c5:c8:52:36:40: 47:3c:6b:16:0c:08:d6:77:91:c5:ed:18:87:50:8e:2f:b0:83: 31:34:12:57:41:56:e8:47:69:cb:37:ea:05:3c:29:a2:b5:a3: 9a:82:08:ef:fd:2d:86:52:7d:99:eb:23:d6:28:2c:7e:bb:0a: d0:c0:6e:73:89:09:2b:13:a5:c8:29:4c:e8:02:82:76:b6:d5: 61:07:b0:78:c4:57:44:a7:c1:80:4f:51:0c:46:1e:d3:1b:45: 35:1f:34:f3:e5:4f:88:2e:cd:ee:ac:98:70:35:62:4b:ca:b1: db:37:a6:bb:24:b6:2c:71:d1:29:06:8f:7b:4b:e6:bf:86:57: 23:1a:ce:9a:c5:25:b1:fe:fc:95:4f:5b:f0:9a:32:25:07:b3: 25:87:55:e9:ed:e4:d3:76:53:f3:73:62:c7:63:ad:58:c3:8f: ee:8e:5e:4f:4a:3f:d2:a9:aa:62:a7:37:01:a8:22:de:54:e9: 06:10:7a:65:a9:06:78:47:c0:52:b4:c5:a1:a1:c1:2f:0c:f9: 14:88:31:65:fc:9f:5e:b2:09:8a:35:db:a6:4d:7b:34:e2:46: 97:b3:93:11:d6:a3:53:49:50:b0:5e:2a:64:a7:18:a0:0f:b1: 14:78:dd:35:61:89:73:2d -----BEGIN CERTIFICATE----- MIID0TCCAbmgAwIBAgIBATANBgkqhkiG9w0BAQUFADAjMQ0wCwYDVQQLDARDZXJ0 MRIwEAYDVQQDDAljZXJ0MS5wZW0wHhcNMTMxMjA0MTMyNjEzWhcNMzMwNjAyMTMy NjEzWjAjMQ0wCwYDVQQLDARDZXJ0MRIwEAYDVQQDDAljZXJ0Mi5wZW0wggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9vIA0lsXqb4Gqkb71MtmKnwJnsEUc PWjfiX2vvvxp1B0Pcq7ITyz04gSzKNu5Be7Xh8eHP3a5x45X7ErB44u0FNSjpRMW txg6l1zNxtaqVIgpsXXSnS4p7+VcUEYCE7LXGi44UMws/GL6YWH3hhipybmvwA75 04gbkSew5uYWmP2b9sTidtJj2nchsI2hyNnOhDxXr5kZewGM8a7hfKwTpgOgq6L2 6n3eskMS5SOt30guvPJ2liugHNxghNfeaJ4vXPbfSU4FjQc5J15JRYiGMxYaX7Gi 0Xj/MDYluAXBity0bLY+UjkeYdzru9pJHdEaBnYiq5QHxw5YzODG/4zTAgMBAAGj EDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggIBAG8CJF1gO3boqqU3 13UYcv3gms6qUF7mg5MbLcRHW9bUjdK6b6+nqHhqBnh+nYMpfZuK9417dtgNC365 vRXoFprFS0jHJro3/vOP3QUTODF5GvQkSQNt+FPXAUR5Z7pq1EB9Vk3EpZmqqdqE ROgp6r1eWn3AfeB+DBKFZe/N+LZWngWX1EjXhpZ15sxRYH/r7aTgnsZw2c4XjkEW ewY9xzPT2QiNF06lE2rX4s7MdM4Udg6qH431yO+gNOTt+CW1jdI/ZcR1l2quDwJe YaENoXxT/RB1TxlxBWsmGEyVhX9QD6UtDwoHpKrO3zwyRxSIc+FrcPtTIwa7ZpG4 KiOfY6tApXE9xgrT5aLFyFI2QEc8axYMCNZ3kcXtGIdQji+wgzE0EldBVuhHacs3 6gU8KaK1o5qCCO/9LYZSfZnrI9YoLH67CtDAbnOJCSsTpcgpTOgCgna21WEHsHjE V0SnwYBPUQxGHtMbRTUfNPPlT4guze6smHA1YkvKsds3prsktixx0SkGj3tL5r+G VyMazprFJbH+/JVPW/CaMiUHsyWHVent5NN2U/NzYsdjrVjDj+6OXk9KP9KpqmKn NwGoIt5U6QYQemWpBnhHwFK0xaGhwS8M+RSIMWX8n16yCYo126ZNezTiRpezkxHW o1NJULBeKmSnGKAPsRR43TVhiXMt -----END CERTIFICATE----- vpnc-0.5.3r550/vpnc-disconnect0000755000175000017500000000051512402421271013722 0ustar fsfs#!/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 vpnc-0.5.3r550/supp.c0000644000175000017500000000736112402421271012040 0ustar fsfs/* 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$ */ #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); } vpnc-0.5.3r550/COPYING0000644000175000017500000004622612402421271011743 0ustar fsfsThis 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. ------------------------------------------------------------- vpnc-0.5.3r550/vpnc.c0000644000175000017500000032631212402421271012017 0ustar fsfs/* 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$ */ #define _GNU_SOURCE #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_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[] = { /* BNES: Bay Networks Enterprise Switch + version/id of some kind */ 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, sizeof(VID_NORTEL_CONT), "Nortel Contivity" }, { VID_HEARTBEAT_NOTIFY, sizeof(VID_HEARTBEAT_NOTIFY), "Heartbeat Notify" }, { NULL, 0, NULL } }; /* What are DWR-Code and DWR-Text ? */ static uint8_t r_packet[8192]; static ssize_t r_length; static struct sa_block *s_atexit_sa; static void close_tunnel(struct sa_block *s); 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 atexit_close(void) { if (s_atexit_sa != NULL) { close_tunnel(s_atexit_sa); s_atexit_sa = NULL; } } static void config_tunnel(struct sa_block *s) { setenv("VPNGATEWAY", inet_ntoa(s->dst), 1); setenv("reason", "connect", 1); system(config[CONFIG_SCRIPT]); s_atexit_sa = s; atexit(atexit_close); } 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_DNS"); 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: if (a->af != isakmp_attr_lots) { reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; break; } strbuf = xallocc(a->u.lots.length + 1); memcpy(strbuf, a->u.lots.data, a->u.lots.length); addenv("CISCO_DEF_DOMAIN", strbuf); free(strbuf); break; case ISAKMP_MODECFG_ATTRIB_CISCO_BANNER: if (a->af != isakmp_attr_lots) { reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; break; } strbuf = xallocc(a->u.lots.length + 1); memcpy(strbuf, a->u.lots.data, a->u.lots.length); addenv("CISCO_BANNER", strbuf); free(strbuf); DEBUG(1, printf("Banner: ")); DEBUG(1, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout)); DEBUG(1, printf("\n")); break; case ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION: DEBUG(2, printf("Remote Application Version: ")); DEBUG(2, fwrite(a->u.lots.data, a->u.lots.length, 1, stdout)); DEBUG(2, printf("\n")); break; case ISAKMP_MODECFG_ATTRIB_CISCO_DO_PFS: if (a->af != isakmp_attr_16) reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; else { s->ipsec.do_pfs = a->u.attr_16; DEBUG(2, printf("got pfs setting: %d\n", s->ipsec.do_pfs)); } break; case ISAKMP_MODECFG_ATTRIB_CISCO_UDP_ENCAP_PORT: if (a->af != isakmp_attr_16) reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; else { s->ipsec.peer_udpencap_port = a->u.attr_16; DEBUG(2, printf("got peer udp encapsulation port: %hu\n", s->ipsec.peer_udpencap_port)); } break; case ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_INC: if (a->af != isakmp_attr_acl) { reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; break; } DEBUG(2, printf("got %d acls for split include\n", a->u.acl.count)); asprintf(&strbuf, "%d", a->u.acl.count); setenv("CISCO_SPLIT_INC", strbuf, 1); free(strbuf); for (i = 0; i < a->u.acl.count; i++) { DEBUG(2, printf("acl %d: ", i)); /* NOTE: inet_ntoa returns one static buffer */ asprintf(&strbuf, "CISCO_SPLIT_INC_%d_ADDR", i); asprintf(&strbuf2, "%s", inet_ntoa(a->u.acl.acl_ent[i].addr)); DEBUG(2, printf("addr: %s/", strbuf2)); setenv(strbuf, strbuf2, 1); free(strbuf); free(strbuf2); asprintf(&strbuf, "CISCO_SPLIT_INC_%d_MASK", i); asprintf(&strbuf2, "%s", inet_ntoa(a->u.acl.acl_ent[i].mask)); DEBUG(2, printf("%s ", strbuf2)); setenv(strbuf, strbuf2, 1); free(strbuf); free(strbuf2); /* this is just here because ip route does not accept netmasks */ 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_SPLIT_DNS: 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_SPLIT_DNS", strbuf); free(strbuf); DEBUG(2, printf("Split DNS: %s\n", a->u.lots.data)); break; case ISAKMP_MODECFG_ATTRIB_CISCO_SAVE_PW: DEBUG(2, printf("got save password setting: %d\n", a->u.attr_16)); break; default: DEBUG(2, printf("unknown attribute %d / 0x%X\n", a->type, a->type)); break; } if (reject == 0 && !seen_address) reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; return reject; } /* * */ static struct isakmp_attribute *make_transform_ike(int dh_group, int crypt, int hash, int keylen, int auth) { struct isakmp_attribute *a = NULL; a = new_isakmp_attribute(IKE_ATTRIB_LIFE_DURATION, a); a->af = isakmp_attr_lots; a->u.lots.length = 4; a->u.lots.data = xallocc(a->u.lots.length); *((uint32_t *) a->u.lots.data) = htonl(2147483); a = new_isakmp_attribute_16(IKE_ATTRIB_LIFE_TYPE, IKE_LIFE_TYPE_SECONDS, a); a = new_isakmp_attribute_16(IKE_ATTRIB_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_grp = get_dh_group_ike()->ike_sa_id; unsigned int auth, crypt, hash, keylen; int i; r->u.sa.doi = ISAKMP_DOI_IPSEC; r->u.sa.situation = ISAKMP_IPSEC_SIT_IDENTITY_ONLY; r->u.sa.proposals = new_isakmp_payload(ISAKMP_PAYLOAD_P); r->u.sa.proposals->u.p.prot_id = ISAKMP_IPSEC_PROTO_ISAKMP; for (auth = 0; supp_auth[auth].name != NULL; auth++) { 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_grp, supp_crypt[crypt].ike_sa_id, supp_hash[hash].ike_sa_id, keylen, supp_auth[auth].ike_sa_id); t->u.t.attributes = a; t->next = tn; } } } for (i = 0, tn = t; tn; tn = tn->next) tn->u.t.number = i++; r->u.sa.proposals->u.p.transforms = t; return r; } static void lifetime_ike_process(struct sa_block *s, struct isakmp_attribute *a) { uint32_t value; assert(a != NULL); assert(a->type == IKE_ATTRIB_LIFE_TYPE); assert(a->af == isakmp_attr_16); assert(a->u.attr_16 == IKE_LIFE_TYPE_SECONDS || a->u.attr_16 == IKE_LIFE_TYPE_K); assert(a->next != NULL); assert(a->next->type == IKE_ATTRIB_LIFE_DURATION); if (a->next->af == isakmp_attr_16) value = a->next->u.attr_16; else if (a->next->af == isakmp_attr_lots && a->next->u.lots.length == 4) value = ntohl(*((uint32_t *) a->next->u.lots.data)); else { 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) 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 */ 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); 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 *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 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: */ 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 { 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 && (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. */ { 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) { 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 (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 { 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; 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, 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 (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 */) seen_answer = 1; for (ap = a; ap && reject == 0; ap = ap->next) switch (ap->type) { case ISAKMP_XAUTH_06_ATTRIB_TYPE: if (ap->af != isakmp_attr_16 || ap->u.attr_16 != 0) reject = ISAKMP_N_ATTRIBUTES_NOT_SUPPORTED; break; case ISAKMP_XAUTH_06_ATTRIB_USER_NAME: case ISAKMP_XAUTH_06_ATTRIB_USER_PASSWORD: case ISAKMP_XAUTH_06_ATTRIB_PASSCODE: case ISAKMP_XAUTH_06_ATTRIB_DOMAIN: case ISAKMP_XAUTH_06_ATTRIB_ANSWER: case ISAKMP_XAUTH_06_ATTRIB_NEXT_PIN: case ISAKMP_XAUTH_ATTRIB_CISCOEXT_VENDOR: case ISAKMP_MODECFG_ATTRIB_CISCO_UNKNOWN_0X0015: break; case ISAKMP_XAUTH_06_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: case ISAKMP_MODECFG_ATTRIB_CISCO_UNKNOWN_0X0015: { na = new_isakmp_attribute_16(ap->type, ap->u.attr_16, NULL); break; } case ISAKMP_XAUTH_06_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_XAUTH_06_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_06_ATTRIB_USER_PASSWORD: case ISAKMP_XAUTH_06_ATTRIB_PASSCODE: case ISAKMP_XAUTH_06_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) ? "Answer" : (ap->type == ISAKMP_XAUTH_06_ATTRIB_USER_PASSWORD) ? "Password" : "Passcode", config[CONFIG_XAUTH_USERNAME], ntop_buf); pass = vpnc_getpass(prompt); free(prompt); if (pass == NULL) error(2, 0, "unable to get password"); 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); free(pass); } else { na = new_isakmp_attribute(ap->type, NULL); 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")); { /* 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_payload *rp; struct isakmp_attribute *a; struct isakmp_packet *r; struct utsname uts; uint32_t msgid; int reject; uname(&uts); gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid)); if (msgid == 0) msgid = 1; rp = new_isakmp_payload(ISAKMP_PAYLOAD_MODECFG_ATTR); rp->u.modecfg.type = ISAKMP_MODECFG_CFG_REQUEST; rp->u.modecfg.id = 20; a = NULL; a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_APPLICATION_VERSION, a); a->u.lots.length = strlen(config[CONFIG_VERSION]); a->u.lots.data = xallocc(a->u.lots.length); memcpy(a->u.lots.data, config[CONFIG_VERSION], a->u.lots.length); a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_DDNS_HOSTNAME, a); a->u.lots.length = strlen(uts.nodename); a->u.lots.data = xallocc(a->u.lots.length); memcpy(a->u.lots.data, uts.nodename, a->u.lots.length); a = new_isakmp_attribute(ISAKMP_MODECFG_ATTRIB_CISCO_SPLIT_DNS, a); 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); 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 (reject == 0 && r->message_id != msgid) reject = ISAKMP_N_INVALID_MESSAGE_ID; if (reject == 0 && r->exchange_type != ISAKMP_EXCHANGE_MODECFG_TRANSACTION) reject = ISAKMP_N_INVALID_EXCHANGE_TYPE; /* After the hash, expect an attribute block. */ if (reject == 0 && (r->payload->next == NULL || r->payload->next->next != NULL || r->payload->next->type != ISAKMP_PAYLOAD_MODECFG_ATTR #if 0 || r->payload->next->u.modecfg.id != 20 #endif || r->payload->next->u.modecfg.type != ISAKMP_MODECFG_CFG_REPLY)) reject = ISAKMP_N_PAYLOAD_MALFORMED; if (reject != 0) phase2_fatal(s, "configuration response rejected: %s(%d)", reject); if (reject == 0) reject = do_config_to_env(s, r->payload->next->u.modecfg.attributes); if (reject != 0) phase2_fatal(s, "configuration response rejected: %s(%d)", reject); DEBUG(1, printf("got address %s\n", getenv("INTERNAL_IP4_ADDRESS"))); 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; } 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; } static void do_phase2_qm(struct sa_block *s) { struct isakmp_payload *rp, *us, *ke = NULL, *them, *nonce_r = NULL; struct isakmp_packet *r; struct group *dh_grp = NULL; uint32_t msgid; int reject; uint8_t nonce_i[20], *dh_public = NULL; 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")); { 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 (s->ipsec.encap_mode != IPSEC_ENCAP_TUNNEL) { s->esp_fd = s->ike_fd; } else { #ifdef IP_HDRINCL int hincl = 1; #endif s->esp_fd = socket(PF_INET, SOCK_RAW, IPPROTO_ESP); if (s->esp_fd == -1) error(1, errno, "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) 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; } int main(int argc, char **argv) { int do_load_balance; const uint8_t hex_test[] = { 0, 1, 2, 3 }; struct sa_block oursa[1]; struct sa_block *s = oursa; test_pack_unpack(); #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); 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(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s); DEBUGTOP(2, printf("S5 do_phase2_xauth\n")); /* FIXME: Create and use a generic function in supp.[hc] */ if (s->ike.auth_algo >= IKE_AUTH_HybridInitRSA) do_load_balance = do_phase2_xauth(s); DEBUGTOP(2, printf("S6 do_phase2_config\n")); if ((opt_vendor == VENDOR_CISCO) && (do_load_balance == 0)) 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); s_atexit_sa = NULL; /* Free resources */ DEBUGTOP(2, printf("S9 cleanup\n")); cleanup(s); return 0; } vpnc-0.5.3r550/supp.h0000644000175000017500000000312412402421271012036 0ustar fsfs/* 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$ */ #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 vpnc-0.5.3r550/makeman.pl0000755000175000017500000000725312402421271012656 0ustar fsfs#! /usr/bin/env perl # $Id$ # 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 $write_secs = (stat("./vpnc.8.template"))[9]; my $date = strftime( '%B %Y', localtime($write_secs) ); 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; vpnc-0.5.3r550/vpnc.8.template0000644000175000017500000001342312402421271013552 0ustar fsfs.\" Template to generate the vpnc-manpage .\" $Id$ .\" .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. .PP Additionally, if the configuration file "-" is specified on the command-line vpnc will read configuration from stdin. The configuration is parsed and the connection proceeds when stdin is closed or the special character CEOT (CTRL-D) is read. .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 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/ vpnc-0.5.3r550/crypto-gnutls.c0000644000175000017500000003263012402421271013700 0ustar fsfs/* 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; } vpnc-0.5.3r550/isakmp.h0000644000175000017500000003145612402421271012344 0ustar fsfs/* 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$ */ #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_UNKNOWN = 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 }; /* 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 }; /* 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 }; 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_MODECFG_ATTRIB_CISCO_UNKNOWN_0X0015 = 0x0015, 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! */ }; #endif vpnc-0.5.3r550/vpnc-script-win0000644000175000017500000000007512402421271013666 0ustar fsfs#! /bin/sh cscript `cygpath -w /etc/vpnc/vpnc-script-win.js` vpnc-0.5.3r550/ChangeLog0000644000175000017500000002123012402421271012446 0ustar fsfs* 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. vpnc-0.5.3r550/dh.h0000644000175000017500000000337312402421271011450 0ustar fsfs/* 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 vpnc-0.5.3r550/mk-version0000755000175000017500000000316212402421271012720 0ustar fsfs#!/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 vpnc-0.5.3r550/tap-win32.h0000644000175000017500000000671412402421271012603 0ustar fsfs/* * 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 }; vpnc-0.5.3r550/sysdep.h0000644000175000017500000001323212402421271012357 0ustar fsfs#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_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_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_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_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_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_UNSETENV 1 #define HAVE_SETENV 1 #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) >= 1070 #endif #endif /***************************************************************************/ #if defined(__sun__) #define NEED_IPLEN_FIX 1 #ifndef IPPROTO_ESP #define IPPROTO_ESP 50 #endif /* 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_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_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 vpnc-0.5.3r550/VERSION0000644000175000017500000000000612402421271011742 0ustar fsfs0.5.3 vpnc-0.5.3r550/decrypt-utils.h0000644000175000017500000000205012402421271013654 0ustar fsfs/* 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$ */ #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 vpnc-0.5.3r550/config.h0000644000175000017500000000607112402421271012320 0ustar fsfs/* 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$ */ #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_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_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, CONFIG_PASSWORD_HELPER, LAST_CONFIG }; enum hex_dump_enum { DUMP_UINT8 = -1, DUMP_UINT16 = -2, DUMP_UINT32 = -4 }; enum vendor_enum { VENDOR_CISCO, VENDOR_NETSCREEN }; enum natt_mode_enum { NATT_NONE, NATT_NORMAL, NATT_FORCE, NATT_CISCO_UDP }; enum if_mode_enum { IF_MODE_TUN, IF_MODE_TAP }; enum auth_mode_enum { AUTH_MODE_PSK, AUTH_MODE_RSA1, AUTH_MODE_RSA2, AUTH_MODE_CERT, AUTH_MODE_HYBRID }; 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; #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 char *vpnc_getpass(const char *prompt); extern void (*logmsg)(int priority, const char *format, ...) __attribute__ ((__format__ (__printf__, 2, 3))); #endif vpnc-0.5.3r550/crypto-gnutls.h0000644000175000017500000000172512402421271013706 0ustar fsfs/* 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__ */ vpnc-0.5.3r550/vpnc.conf0000644000175000017500000000022312402421271012510 0ustar fsfs#IPSec gateway #IPSec ID #IPSec secret #IKE Authmode hybrid #Xauth username #Xauth password vpnc-0.5.3r550/nortel.txt0000644000175000017500000000621012402421271012741 0ustar fsfs[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