./0000755000175000017500000000000014003465661007022 5ustar fsfs./src/0000755000175000017500000000000014003517324007603 5ustar fsfs./src/tunip.h0000644000175000017500000000647514003465661011135 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 */ uint32_t seq_backlog; 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; uint8_t current_udp_tos; } ipsec; }; extern int volatile do_kill; extern void vpnc_doit(struct sa_block *s); #endif ./src/pcf2vpnc0000755000175000017500000000740514003465661011266 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"; } ./src/isakmp-pkt.h0000644000175000017500000000737314003465661012054 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 ./src/test-crypto.c0000644000175000017500000000760214003465661012257 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; } ./src/isakmp-pkt.c0000644000175000017500000006636514003465661012055 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); } ./src/config.c0000644000175000017500000006500414003465661011227 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 #include #include "sysdep.h" #include "config.h" #include "vpnc.h" #include "supp.h" #include "decrypt-utils.h" const char *config[LAST_CONFIG]; const char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:?()/%@!$"; 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 unsigned long get_microseconds() { struct timeval tv; gettimeofday(&tv, NULL); return (unsigned long) 1000000 * tv.tv_sec + tv.tv_usec; } static void rand_str(char *dest, size_t length) { srand(get_microseconds()); while (length-- > 0) { size_t index = (double) rand() / RAND_MAX * (sizeof charset - 1); *dest++ = charset[index]; } *dest = '\0'; } extern void logmsg(int priority, const char *format, ...) { if (opt_debug == 0 && priority == LOG_DEBUG) { return; } va_list ap; va_start(ap, format); if (!opt_nd) { vsyslog(priority, format, ap); } else { fprintf(stderr, "vpnc: "); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); } va_end(ap); } 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 = NULL; 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 SCRIPT_PATH; } static const char *config_def_pid_file(void) { return "/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 needsEncryption; 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, 0, "--gateway", "IPSec gateway", "", "IP/name of your IPSec gateway", NULL }, { CONFIG_IPSEC_ID, 1, 0, 0, "--id", "IPSec ID", "", "your group name", NULL }, { CONFIG_IPSEC_SECRET, 1, 1, 0, "--secret", "IPSec secret", "", "your group password (cleartext)", NULL }, { CONFIG_IPSEC_SECRET_OBF, 1, 0, 1, NULL, "IPSec obfuscated secret", "", "your group password (obfuscated)", NULL }, { CONFIG_XAUTH_USERNAME, 1, 0, 0, "--username", "Xauth username", "", "your username", NULL }, { CONFIG_XAUTH_PASSWORD, 1, 1, 0, "--password", "Xauth password", "", "your password (cleartext)", NULL }, { CONFIG_XAUTH_PASSWORD_OBF, 1, 0, 1, NULL, "Xauth obfuscated password", "", "your password (obfuscated)", NULL }, { CONFIG_DOMAIN, 1, 0, 1, "--domain", "Domain", "", "(NT-) Domain name for authentication", NULL }, { CONFIG_XAUTH_INTERACTIVE, 0, 0, 1, "--xauth-inter", "Xauth interactive", NULL, "enable interactive extended authentication (for challenge response auth)", NULL }, { CONFIG_VENDOR, 1, 0, 1, "--vendor", "Vendor", "", "vendor of your IPSec gateway", config_def_vendor }, { CONFIG_NATT_MODE, 1, 0, 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, 0, 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, 0, 1, "--dh", "IKE DH Group", "", "name of the IKE DH Group", config_def_ike_dh }, { CONFIG_IPSEC_PFS, 1, 0, 1, "--pfs", "Perfect Forward Secrecy", "", "Diffie-Hellman group to use for PFS", config_def_pfs }, { CONFIG_ENABLE_1DES, 0, 0, 1, "--enable-1des", "Enable Single DES", NULL, "enables weak single DES encryption", NULL }, { CONFIG_ENABLE_NO_ENCRYPTION, 0, 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, 0, 1, "--application-version", "Application version", "", "Application Version to report. Note: Default string is generated at runtime.", config_def_app_version }, { CONFIG_IF_NAME, 1, 0, 1, "--ifname", "Interface name", "", "visible name of the TUN/TAP interface", NULL }, { CONFIG_IF_MODE, 1, 0, 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, 0, 1, "--ifmtu", "Interface MTU", "<0-65535>", "Set MTU for TUN/TAP interface (default 0 == automatic detect)", NULL }, { CONFIG_DEBUG, 1, 0, 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, 0, 1, "--no-detach", "No Detach", NULL, "Don't detach from the console after login", NULL }, { CONFIG_PID_FILE, 1, 0, 1, "--pid-file", "Pidfile", "", "store the pid of background process in ", config_def_pid_file }, { CONFIG_LOCAL_ADDR, 1, 0, 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, 0, 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, 0, 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, 0, 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, 0, 1, "--non-inter", "Noninteractive", NULL, "Don't ask anything, exit on missing options", NULL }, { CONFIG_AUTH_MODE, 1, 0, 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, 0, 1, "--ca-file", "CA-File", "", "filename and path to the CA-PEM-File", NULL }, { CONFIG_CA_DIR, 1, 0, 1, "--ca-dir", "CA-Dir", "", "path of the trusted CA-Directory", config_ca_dir }, { CONFIG_IPSEC_TARGET_NETWORK, 1, 0, 1, "--target-network", "IPSEC target network", "", "Target network in dotted decimal or CIDR notation\n", config_def_target_network }, { CONFIG_PASSWORD_HELPER, 1, 0, 1, "--password-helper", "Password helper", "", "path to password program or helper name\n", NULL }, { 0, 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) { if (config_names[c].needsEncryption) { int field_len = strlen(argv[i]); char *field = malloc(field_len * sizeof(char)); strcpy(field, argv[i]); config[config_names[c].nm] = field; rand_str(argv[i], field_len); } else { 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 if (!strcmp(config[CONFIG_VENDOR], "fortigate")) { opt_vendor = VENDOR_FORTIGATE; } else { printf("%s: unknown vendor %s\nknown vendors: cisco netscreen fortigate\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 gateway 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; } ./src/cisco-decrypt.c0000644000175000017500000000322414003465661012526 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); } ./src/crypto.c0000644000175000017500000000623314003465661011301 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; } ./src/cisco-decrypt.10000644000175000017500000000173714003465661012453 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) ./src/dh.c0000644000175000017500000000534214003465661010354 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; } ./src/crypto.h0000644000175000017500000001014614003465661011304 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__ */ ./src/math_group.h0000644000175000017500000000541514003465661012134 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_ */ ./src/enum2debug.pl0000755000175000017500000000240514003465661012207 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__ ./src/vpnc.h0000644000175000017500000000221514003465661010730 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); void rekey_phase1(struct sa_block *s); #endif ./src/math_group.c0000644000175000017500000002044414003465661012126 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; l = grp->getlen(grp); gcry_mpi_aprint(GCRYMPI_FMT_STD, &tmp, &l2, v); memcpy(d, tmp + (l2 - l), l); gcry_free(tmp); } static int modp_setraw(struct group *grp __attribute__((unused)), 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]); } 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; } ./src/sysdep.c0000644000175000017500000004117614003465661011275 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__) # if !defined(__GLIBC__) && !defined(__UCLIBC__) # define _LINUX_IF_ETHER_H # include # endif #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) { unsigned int memlen = strnlen(dev, IFNAMSIZ - 1); memcpy(ifr.ifr_name, dev, memlen + 1); } 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 __attribute__((unused))) { dev = NULL; /* unused */ return CloseHandle((HANDLE) get_osfhandle(fd)); } #else int tun_close(int fd, char *dev __attribute__((unused))) { 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; } unsigned int memlen = strnlen(dev, IFNAMSIZ - 1); memset(&ifr, 0, sizeof(struct ifreq)); memcpy(ifr.ifr_name, dev, memlen + 1); 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 ./src/tunip.c0000644000175000017500000010373614003465661011126 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. * */ /* get_current_dir_name() is a GNU extension */ #define _GNU_SOURCE #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, uint32_t seq_id); }; /* 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; struct ip *tip; uint8_t tos; buf += MAX_HEADER; /* get the TOS value of the original frame */ tip = (struct ip *)buf; tos = (bufsize < sizeof(struct ip)) ? 0 : tip->ip_tos; 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); } /* set outer TOS header to the one of the original frame */ /* but only if it differs from the already set TOS */ if (tos != s->ipsec.current_udp_tos) { if (setsockopt(s->esp_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) { logmsg(LOG_ERR, "udp setsockopt: %m"); } s->ipsec.current_udp_tos = tos; } 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_validate_seqid(struct sa_block *s, uint32_t seq_id) { /* * For incoming, s->ipsec.rx.seq_id is the next *expected* packet, * being the sequence number *after* the latest we have received. * * Since it must always be true that packet s->ipsec.rx.seq_id-1 * has been received, there's no need to explicitly record that. * * So the backlog bitmap covers the 32 packets prior to that, with * the LSB representing packet (s->ipsec.rx.seq_id - 2), and the * MSB representing (s->ipsec.rx.seq_id - 33). A received packet * is represented by a zero bit, and a missing packet is * represented by a one. * * Thus we can allow out-of-order reception of packets that are * within a reasonable interval of the latest packet received. */ if (seq_id == s->ipsec.rx.seq_id) { /* The common case. This is the packet we expected next. */ s->ipsec.rx.seq_backlog <<= 1; s->ipsec.rx.seq_id++; logmsg(LOG_DEBUG, "Accepting expected ESP packet with seq %u\n", seq_id); return 0; } else if (seq_id + 33 < s->ipsec.rx.seq_id) { /* Too old. We can't know if it's a replay. */ logmsg(LOG_NOTICE, "Discarding ancient ESP packet with seq %u (expected %u)\n", seq_id, s->ipsec.rx.seq_id); return -EINVAL; } else if (seq_id == s->ipsec.rx.seq_id - 1) { /* This is a repeat of the latest packet we already received. */ replayed: logmsg(LOG_NOTICE, "Discarding replayed ESP packet with seq %u\n", seq_id); return -EINVAL; } else if (seq_id < s->ipsec.rx.seq_id) { /* Within the backlog window, so we remember whether we've seen it or not. */ uint32_t mask = 1 << (s->ipsec.rx.seq_id - seq_id - 2); if (!(s->ipsec.rx.seq_backlog & mask)) { goto replayed; } logmsg(LOG_DEBUG, "Accepting out-of-order ESP packet with seq %u (expected %u)\n", seq_id, s->ipsec.rx.seq_id); s->ipsec.rx.seq_backlog &= ~mask; return 0; } else { /* The packet we were expecting has gone missing; this one is newer. */ int delta = seq_id - s->ipsec.rx.seq_id; if (delta >= 32) { /* We jumped a long way into the future. We have not seen * any of the previous 32 packets so set the backlog bitmap * to all ones. */ s->ipsec.rx.seq_backlog = 0xffffffff; } else if (delta == 31) { /* Avoid undefined behaviour that shifting by 32 would incur. * The (clear) top bit represents the packet which is currently * s->ipsec.rx.seq_id - 1, which we know was already received. */ s->ipsec.rx.seq_backlog = 0x7fffffff; } else { /* We have missed (delta) packets. Shift the backlog by that * amount *plus* the one we would have shifted it anyway if * we'd received the packet we were expecting. The zero bit * representing the packet which is currently s->ipsec.rx.seq_id - 1, * which we know has been received, ends up at bit position * (1<ipsec.rx.seq_backlog <<= delta + 1; s->ipsec.rx.seq_backlog |= (1<ipsec.rx.seq_id); s->ipsec.rx.seq_id = seq_id + 1; return 0; } } static int encap_esp_recv_peer(struct sa_block *s, uint32_t seq_id) { 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; } } if (encap_esp_validate_seqid(s, seq_id)) 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, ntohl(eh->seq_id)) != 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; /* the below code had keepalive_v2 for NATT_ACTIVE_RFC [FIXME] * however, ASA 9.4(1) produced * [IKEv1]: IKE Receiver: Runt ISAKMP packet discarded on Port 4500 from ip:port * according to RFC 3948, the keepalive should be 0xFF * * 2.3. NAT-Keepalive Packet Format * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Source Port | Destination Port | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Length | Checksum | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0xFF | * +-+-+-+-+-+-+-+-+ * */ if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) { keepalive = keepalive_v1; keepalive_size = sizeof(keepalive_v1); } else if (s->ipsec.natt_active_mode == NATT_ACTIVE_RFC) { keepalive = keepalive_v1; keepalive_size = sizeof(keepalive_v1); } else { /* active_mode is 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 */ DEBUG(3,printf("keepalive %d\n", (int)keepalive_size)); 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, ike: %ld of %u seconds 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, time(NULL) - s->ike.life.start, s->ike.life.seconds)); if (timed_mode) { time_t now = time(NULL); /* start rekey at 80% of lifetime */ if ((now - s->ike.life.start) + ((s->ike.life.seconds*20)/100) > s->ike.life.seconds) { DEBUG(3,printf("starting phase1 rekey at %d s\n", s->ike.life.seconds)); rekey_phase1(s); } } } 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]; char *cwd; 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); /* save cwd */ cwd = get_current_dir_name(); 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); } else { printf("VPNC started in foreground...\n"); } write_pidfile(pidfile); vpnc_main_loop(s); /* restore cwd */ chdir(cwd); free(cwd); if (pidfile) unlink(pidfile); /* ignore errors */ } ./src/pcf2vpnc.10000644000175000017500000000217114003465661011415 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) ./src/doc/0000755000175000017500000000000014003522042010341 5ustar fsfs./src/doc/Home.md0000644000175000017500000000007714003521726011567 0ustar fsfs1. [About](About) 2. [Installation](Installation) 3. [FAQ](FAQ)./src/doc/FAQ.md0000644000175000017500000000667614003522042011311 0ustar fsfs# FAQ ## Use the client with Nortel Contivity Matt Chapman (`matthewc@cse.unsw.edu.au`) got vpnc working with a Nortel Contivity VPN concentrator. According to him, 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`); - 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. According to Zingo Andersen, `NORTELVPN_XAUTHTYPE_AS_REQUEST` has to be set and this patch applied: ``` #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 ``` ## Setting up VPNC on Windows Vista (64 bit) 1. Install `cygwin`: follow steps at [cygwin.com](http://www.cygwin.com/) 2. Make sure you install the development options for `cygwin` to give you access to `make`, `gcc`, and all the other develpment libraries 3. Make sure you install `libgcrypt` for `cygwin` as it is needed in the `make` procedure 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` 5. Download the latest VPNC tarball 6. Unzip and explode the tarball 7. Modify `tap-win32.h` to change `#define TAP_COMPONENT_ID "tap0801"` to `#define TAP_COMPONENT_ID "tap0901"` (not sure if this is always necessary, but at least once it has been needed) 8. `make` 9. Download [OpenVPN](http://openvpn.net/download.html). It has been tested with success on version `2.1_rc4` 10. Just install `TAP-Win32 Adapter V9` 11. Go to _Control Panel_, and then _Network Connections_ and rename the TAP devic to `my-tap` 12. Use a `default.conf` built like this: ```text 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 ``` ## Suddenly client stops without any specific 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 To solve the issue, fix your `dhcpclient`: on _Debian_ it can be done by installing and using `resolvconf` to modify that file instead of modifying it directly. ./src/doc/Installation.md0000644000175000017500000001261614003521726013342 0ustar fsfs# Installation ```bash git clone https://github.com/streambinder/vpnc.git cd vpnc make sudo make install ``` ## General configuration Few libraries are required to let VPNC work properly: - `libgcrypt` (version: `1.1.90+`) - `libopenssl` (optional, needed to provide hybrid support) Configuration data gets read from: - command-line options - config file(s) specified on the command line - `/etc/vpnc/default.conf`, if no config file was given on the command line - `/etc/vpnc.conf`, if no config file was given on the command line - output prompt, if a settings can't get loaded from any of those places above Essential configuration informations (both with key name for specifying option via command line or config file) it currently needs are: Input option | File option ------------ | ---------------- `--gateway` | `IPSec gateway` `--id` | `IPSec ID` `--secret` | `IPSec secret` `--username` | `Xauth username` `--password` | `Xauth password` A sample configuration file is: ```text IPSec gateway 127.0.0.1 IPSec ID sample-vpn IPSec secret s4mpl3 Xauth username johndoe ``` 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 are (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.sh` / `Script script.sh` (the first one to pass it as input parameters, the other one as config file value). 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 tree times: 1. `$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. 2. `$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. 3. `$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 mask length (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 config files. However, a workaround is to use a `wrapper-script` like this, to disable `/etc/resolv.conf` rewriting and setup a custom split-routing: ```bash #!/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 ``` 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 Input option | File option -------------------- | ------------------ `--hybrid` | `Use Hybrid Auth` `--ca-file ` | `CA-File ` `--ca-dir ` | `CA-Dir ` Default `CA-Dir` is `/etc/ssl`. A link can also be used like in `/etc/ssl/certs/`. As the trusted certificate is referenced by the hash of the subject name, the directory has to contain the certificate named like that hash value. As an example, the hash value can be calculated using the following command: `openssl x509 -in -noout -hash` ./src/doc/About.md0000644000175000017500000000221514003522042011735 0ustar fsfs# About VPNC is a VPN client compatible with Cisco's EasyVPN equipment. It 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. ## Development status This repository has been forked to follow the work started originally by Maurice Massar. For more information about that, please, point to [VPNC original web page](http://www.unix-ag.uni-kl.de/~massar/vpnc/). As stated in `vpnc-devel` mailing-list ([vpnc-devel@2017-November](http://lists.unix-ag.uni-kl.de/pipermail/vpnc-devel/2017-November/004233.html)), this repository hasn't been started to start working actively on this project, but to passively merge security patches, fixes and features additions explicitly requested by the community. **This means I won't even consider issues such as _"Please, implement this"_, or _"Look at that, maybe you can find ideas and fixes"_, but I will if requested via explicit PRs and/or issues pointing to a (or many) specific patch**. ./src/crypto-openssl.h0000644000175000017500000000210114003465661012755 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__ */ ./src/crypto-openssl.c0000644000175000017500000001737214003465661012770 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; } ./src/decrypt-utils.c0000644000175000017500000000523014003465661012565 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; } ./src/test/0000755000175000017500000000000014003465661010570 5ustar fsfs./src/test/Makefile0000644000175000017500000000437514003465661012241 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 ./src/test/ca_list.pem0000644000175000017500000003576514003465661012731 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----- ./src/test/dec_data.bin0000644000175000017500000000040014003465661013000 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þÞejxGc./src/test/sig_data.bin0000644000175000017500000000040014003465661013027 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. ./src/test/cert1.pem0000644000175000017500000001212414003465661012311 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----- ./src/test/cert2.pem0000644000175000017500000001155514003465661012321 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----- ./src/vpnc-disconnect0000755000175000017500000000051514003465661012635 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 ./src/supp.c0000644000175000017500000000737214003465661010755 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); } ./src/vpnc.c0000644000175000017500000033416414003512353010725 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); DEBUG(3, printf("sent IKE keepalive\n")); } 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); } DEBUG(3, printf("sent DPD packet\n")); } 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_cookie(struct sa_block *s, uint8_t *i_cookie __attribute__((unused)), uint8_t *r_cookie __attribute__((unused))) { 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 send_delete_isakmp(struct sa_block *s) { DEBUGTOP(2, printf("S7.11 send isakmp termination message\n")); send_delete_isakmp_cookie(s, s->ike.i_cookie, s->ike.r_cookie); } 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); /* * Workaround for broken RESPONDER_LIFETIME payload from Fortigate * firewall. If the next attribute is not the LIFE_DURATION, then * just ignore this payload and move on. */ if (a->next->type != IKE_ATTRIB_LIFE_DURATION) { DEBUG(2, printf("got bogus type %d instead of IKE_ATTRIB_LIFE_DURATION. Ignoring this payload.\n", a->next->type)); return; } if (a->next->af == isakmp_attr_16) value = a->next->u.attr_16; else if (a->next->af == isakmp_attr_lots && a->next->u.lots.length == 4) value = ntohl(*((uint32_t *) a->next->u.lots.data)); else { DEBUG(2, printf("got unknown ike lifetime attributes af %d len %d\n", a->next->af, a->next->u.lots.length)); return; } DEBUG(2, printf("got ike lifetime attributes: %d %s\n", value, (a->u.attr_16 == IKE_LIFE_TYPE_SECONDS) ? "seconds" : "kilobyte")); if (a->u.attr_16 == IKE_LIFE_TYPE_SECONDS) s->ike.life.seconds = value; else s->ike.life.kbytes = value; } static void lifetime_ipsec_process(struct sa_block *s, struct isakmp_attribute *a) { uint32_t value; assert(a != NULL); assert(a->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE); assert(a->af == isakmp_attr_16); assert(a->u.attr_16 == IPSEC_LIFE_SECONDS || a->u.attr_16 == IPSEC_LIFE_K); assert(a->next != NULL); assert(a->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_DURATION); if (a->next->af == isakmp_attr_16) value = a->next->u.attr_16; else if (a->next->af == isakmp_attr_lots && a->next->u.lots.length == 4) value = ntohl(*((uint32_t *) a->next->u.lots.data)); else assert(0); DEBUG(2, printf("got ipsec lifetime attributes: %d %s\n", value, (a->u.attr_16 == IPSEC_LIFE_SECONDS) ? "seconds" : "kilobyte")); if (a->u.attr_16 == IPSEC_LIFE_SECONDS) s->ipsec.life.seconds = value; else s->ipsec.life.kbytes = value; /* FIXME: for notice-payloads: write a seperate function to handle them */ /* bug: this may process lifetime-attributes of SAs twice but to no consequence */ if (a->next->next != NULL && a->next->next->type == ISAKMP_IPSEC_ATTRIB_SA_LIFE_TYPE) lifetime_ipsec_process(s, a->next->next); } static void do_phase1_am_init(struct sa_block *s) { s->ike.natd_type = 0; s->ike.natd_us = s->ike.natd_them = NULL; s->ike.sa_f = s->ike.idi_f = NULL; DEBUGTOP(2, printf("S4.1 create_nonce\n")); gcry_create_nonce(s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); s->ike.life.start = time(NULL); s->ipsec.do_pfs = -1; if (s->ike.i_cookie[0] == 0) s->ike.i_cookie[0] = 1; hex_dump("i_cookie", s->ike.i_cookie, ISAKMP_COOKIE_LENGTH, NULL); gcry_create_nonce(s->ike.i_nonce, sizeof(s->ike.i_nonce)); hex_dump("i_nonce", s->ike.i_nonce, sizeof(s->ike.i_nonce), NULL); DEBUGTOP(2, printf("S4.2 dh setup\n")); /* Set up the Diffie-Hellman stuff. */ { s->ike.dh_grp = group_get(get_dh_group_ike()->my_id); s->ike.dh_public = xallocc(dh_getlen(s->ike.dh_grp)); dh_create_exchange(s->ike.dh_grp, s->ike.dh_public); hex_dump("dh_public", s->ike.dh_public, dh_getlen(s->ike.dh_grp), NULL); } } static void do_phase1_am_packet1(struct sa_block *s, const char *key_id) { DEBUGTOP(2, printf("S4.3 AM packet_1\n")); /* Create the first packet. */ { struct isakmp_packet *p1; struct isakmp_payload *l; uint8_t *pkt; size_t pkt_len; p1 = new_isakmp_packet(); memcpy(p1->i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); p1->isakmp_version = ISAKMP_VERSION; p1->exchange_type = ISAKMP_EXCHANGE_AGGRESSIVE; p1->payload = l = make_our_sa_ike(); flatten_isakmp_payload(l, &s->ike.sa_f, &s->ike.sa_size); l->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_KE, s->ike.dh_public, dh_getlen(s->ike.dh_grp)); l->next->next = new_isakmp_data_payload(ISAKMP_PAYLOAD_NONCE, s->ike.i_nonce, sizeof(s->ike.i_nonce)); l = l->next->next; l->next = new_isakmp_payload(ISAKMP_PAYLOAD_ID); l = l->next; if (opt_vendor == VENDOR_CISCO || opt_vendor == VENDOR_FORTIGATE) 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; unsigned int dh_len; /* Determine the shared secret. */ dh_len = dh_getlen(s->ike.dh_grp); dh_shared_secret = xallocc(dh_len); 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, dh_len); 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(); } DEBUG(1, printf("NAT-T mode: %d\n", natt_draft)); 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, int re_key) { 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 = pl = new_isakmp_data_payload(ISAKMP_PAYLOAD_HASH, s->ike.returned_hash, s->ike.md_len); if (!re_key) { 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, int re_key) { do_phase1_am_init(s); do_phase1_am_packet1(s, key_id); do_phase1_am_packet2(s, shared_key); do_phase1_am_packet3(s, re_key); 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 life_only) { 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")); if (life_only) return 0; 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, 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, 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, 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), 0); /* 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 = 0; struct isakmp_packet *r; struct isakmp_payload *rp; DEBUG(2,printf("got late ike packet: %zd bytes\n", r_length)); if (r_length > ISAKMP_PAYLOAD_O && r_packet[ISAKMP_EXCHANGE_TYPE_O] == ISAKMP_EXCHANGE_AGGRESSIVE) { DEBUG(2,printf("can't respond to phase1 aggressive... terminating\n")); do_kill = -1; return; } else { /* we should ignore resent pakets here. * unpack_verify_phase2 will fail to decode them probably */ DEBUG(2,printf("processing as phase2\n")); 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) { DEBUG(3, printf("got ISAKMP_EXCHANGE_INFORMATIONAL\n")); /* 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")); } else if (rp->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")); } } } /* 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 */ /* initialize last set TOS value in case UDP encap used */ s->ipsec.current_udp_tos = 0; 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, 0); 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 || opt_vendor == VENDOR_FORTIGATE) && (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; } void rekey_phase1(struct sa_block *s) { struct isakmp_packet *r; uint8_t i_cookie[ISAKMP_COOKIE_LENGTH], r_cookie[ISAKMP_COOKIE_LENGTH]; memcpy(i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH); memcpy(r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH); do_phase1_am(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s, 1); (void)do_phase2_xauth(s); DEBUG(3, printf("notice_checks\n")); r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0); do_phase2_notice_check(s, &r, NULL, 0, 1); if (r) free_isakmp_packet(r); DEBUG(3, printf("notice_checks done\n")); /* delete the old SA and read the response */ DEBUG(3, printf("delete old cookie\n")); send_delete_isakmp_cookie(s, i_cookie, r_cookie); } ./src/supp.h0000644000175000017500000000312514003465661010752 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 ./src/makeman.pl0000755000175000017500000000724214003465661011567 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); use File::Basename; chdir(dirname(__FILE__)) or die "$!"; my $vpnc = $ARGV[0]; -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) ); my $vpnc_version = $ENV{'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; ./src/vpnc.8.template0000644000175000017500000001342314003465661012465 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/ ./src/crypto-gnutls.c0000644000175000017500000003152514003465661012615 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_t 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_t 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_t 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_t 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; } ./src/isakmp.h0000644000175000017500000003407414003465661011256 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 */ /* https://www.cisco.com/c/en/us/td/docs/security/asa/asa-command-reference/A-H/cmdref1/c6.html */ enum dwr_ike_delete { IKE_DELETE_RESERVED = 0, /* An invalid code. Do not send. */ IKE_DELETE_BY_ERROR, /* A transmission error for a timeout or failure when expecting a response to a keepalive or any other IKE packet ACK. The default text is "Connectivity to client lost". */ IKE_DELETE_BY_USER_COMMAND, /* The SA was actively deleted with manual intervention by the user or administrator. The default text is "Manually Disconnected by Administrator". */ IKE_DELETE_BY_EXPIRED_LIFETIME, /* The SA has expired. The default text is "Maximum Configured Lifetime Exceeded". */ IKE_DELETE_NO_ERROR, /* An unknown error caused the delete. */ IKE_DELETE_SERVER_SHUTDOWN, /* The server is being shut down. */ IKE_DELETE_SERVER_IN_FLAMES, /* The server has some severe problems. The default text is "Peer is having heat problems". */ IKE_DELETE_MAX_CONNECT_TIME, /* The maximum allowed time of an active tunnel has expired. Unlike EXPIRED_LIFETIME, this reason indicates that the entire IKE-negotiated/controlled tunnel is being disconnected, not just this one SA. The default text is "Maximum Configured Connection Time Exceeded". */ IKE_DELETE_IDLE_TIMEOUT, /* The tunnel has been idle for the maximum allowed time; therefore, the entire IKE-negotiated tunnel has been disconnected, not just this one SA. The default text is "Maximum Idle Time for Session Exceeded". */ IKE_DELETE_SERVER_REBOOT, /* The server is rebooting. */ IKE_DELETE_P2_PROPOSAL_MISMATCH, /* Phase2 proposal mismatch. */ IKE_DELETE_FIREWALL_MISMATCH, /* Firewall parameter mismatch. */ IKE_DELETE_CERT_EXPIRED, /* User certification required. The default message is "User or Root Certificate has Expired". */ IKE_DELETE_CLIENT_NOT_ALLOWED, /* Client type or version not allowed. */ IKE_DELETE_FW_SERVER_FAIL, /* Failed to contact Zone Integrity Server. */ IKE_DELETE_ACL_ERROR /* ACL downloaded from AAA cannot be inserted. The default message is "ACL parsing error". */ }; /* 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 ./src/dh.h0000644000175000017500000000337314003465661010363 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 ./src/tap-win32.h0000644000175000017500000000671314003465661011515 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 }; ./src/sysdep.h0000644000175000017500000001331714003465661011276 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(__GLIBC__) || defined(__UCLIBC__) #include #define HAVE_ERROR 1 #endif #if defined(__linux__) || defined(__GLIBC__) #define HAVE_VASPRINTF 1 #define HAVE_ASPRINTF 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 ./src/decrypt-utils.h0000644000175000017500000000205114003465661012570 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 ./src/vpnc@.service0000644000175000017500000000130514003517212012226 0ustar fsfs[Unit] Description=VPNC connection to %i Documentation=man:vpnc(8) https://github.com/streambinder/vpnc ConditionPathExists=/dev/net/tun Requires=network.target After=network.target [Service] Type=forking ExecStart=/usr/sbin/vpnc --pid-file=/run/vpnc@%i.pid /etc/vpnc/%i.conf ExecStartPre=-/etc/vpnc/scripts.d/%i-preup.sh ExecStartPost=-/etc/vpnc/scripts.d/%i-postup.sh ExecStopPost=-/etc/vpnc/scripts.d/%i-postdown.sh PIDFile=/run/vpnc@%i.pid Restart=always NoNewPrivileges=yes RestrictRealtime=yes ProtectHome=yes InaccessiblePaths=/home PrivateTmp=yes MemoryDenyWriteExecute=yes RestrictNamespaces=yes RemoveIPC=yes ProtectControlGroups=yes ProtectKernelModules=yes [Install] WantedBy=multi-user.target ./src/config.h0000644000175000017500000000633214003465661011233 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, VENDOR_FORTIGATE }; 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 ./src/crypto-gnutls.h0000644000175000017500000000172614003465661012622 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__ */ ./src/vpnc.conf0000644000175000017500000000065214003465661011431 0ustar fsfs# example vpnc configuration file # see vpnc --long-help for details #Interface name tun0 #IKE DH Group dh2 #Perfect Forward Secrecy nopfs # You may replace this script with something better #Script /etc/vpnc/vpnc-script # Enable this option for NAT traversal #UDP Encapsulate #IPSec gateway #IPSec ID #IPSec secret #IKE Authmode hybrid #Xauth username #Xauth password ./Makefile0000644000175000017500000001345214003465661010467 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 # Copyright (C) 2017-2020 Davide Pucci # 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 LICENSEDIR=$(PREFIX)/share/licenses/vpnc SYSTEMDDIR=$(PREFIX)/lib/systemd/system # 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 = src/crypto-gnutls.c ifeq ($(OPENSSL_GPL_VIOLATION), yes) CRYPTO_LDADD = -lcrypto CRYPTO_CFLAGS = -DOPENSSL_GPL_VIOLATION -DCRYPTO_OPENSSL CRYPTO_SRCS = src/crypto-openssl.c endif SRCS = src/sysdep.c src/vpnc-debug.c src/isakmp-pkt.c src/tunip.c src/config.c src/dh.c src/math_group.c src/supp.c src/decrypt-utils.c src/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)) SCRIPT_PATH := /etc/vpnc/vpnc-script ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) BUILDDIR := $(ROOT_DIR)/bin export BUILDDIR VERSION := 0.5.3 export VERSION CC ?= gcc CFLAGS ?= -O3 -g CFLAGS += -W -Wall -Wmissing-declarations -Wwrite-strings CFLAGS += $(shell libgcrypt-config --cflags) $(CRYPTO_CFLAGS) CPPFLAGS += -DVERSION=\"$(VERSION)\" -DSCRIPT_PATH=\"$(SCRIPT_PATH)\" 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: $(BUILDDIR) $(BINS) vpnc.8 $(BUILDDIR): @mkdir $@ vpnc: $(OBJS) src/vpnc.o $(CC) $(LDFLAGS) -o $(BUILDDIR)/$@ $^ $(LIBS) vpnc.8: src/vpnc.8.template src/makeman.pl vpnc ./src/makeman.pl $(BUILDDIR)/vpnc cisco-decrypt: src/cisco-decrypt.o src/decrypt-utils.o $(CC) $(LDFLAGS) -o $(BUILDDIR)/$@ $^ $(LIBS) test-crypto: src/sysdep.o src/test-crypto.o src/crypto.o $(CRYPTO_OBJS) $(CC) $(LDFLAGS) -o $(BUILDDIR)/$@ $^ $(LIBS) .depend: $(SRCS) $(BINSRCS) $(CC) -MM $(SRCS) $(BINSRCS) $(CFLAGS) $(CPPFLAGS) > $@ src/vpnc-debug.c src/vpnc-debug.h: src/isakmp.h src/enum2debug.pl cd src && LC_ALL=C perl -w ./enum2debug.pl isakmp.h >vpnc-debug.c 2>vpnc-debug.h vpnc.ps: src/vpnc.c enscript -E -G -T 4 --word-wrap -o- $^ | psnup -2 /dev/stdin $@ etags: etags *.[ch] ctags: ctags *.[ch] test: all $(BUILDDIR)/test-crypto src/test/sig_data.bin src/test/dec_data.bin src/test/ca_list.pem \ src/test/cert3.pem src/test/cert2.pem src/test/cert1.pem src/test/cert0.pem dist: vpnc.8 clean: -rm -rf $(OBJS) $(BINOBJS) $(BUILDDIR) tags src/cisco-decrypt.o src/test-crypto.o src/vpnc.o distclean: clean -rm -f src/vpnc-debug.c src/vpnc-debug.h src/vpnc.ps src/vpnc.8 src/.depend install-common: all install -d $(DESTDIR)$(ETCDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(DOCDIR) $(DESTDIR)$(LICENSEDIR) install -m600 src/vpnc.conf $(DESTDIR)$(ETCDIR)/default.conf install -m755 src/vpnc-disconnect $(DESTDIR)$(SBINDIR) install -m755 src/pcf2vpnc $(DESTDIR)$(BINDIR) install -m644 src/vpnc.8 $(DESTDIR)$(MANDIR)/man8 install -m644 src/pcf2vpnc.1 $(DESTDIR)$(MANDIR)/man1 install -m644 src/cisco-decrypt.1 $(DESTDIR)$(MANDIR)/man1 install -m644 src/vpnc@.service -t $(DESTDIR)$(SYSTEMDDIR) install -m644 LICENSE $(DESTDIR)$(LICENSEDIR) install-doc: git submodule update --init src/doc install -m644 src/doc/*.md $(DESTDIR)$(DOCDIR) rm -f $(DESTDIR)$(DOCDIR)/Home.md install: install-common install-doc install -m755 $(BUILDDIR)/vpnc $(DESTDIR)$(SBINDIR) install -m755 $(BUILDDIR)/cisco-decrypt $(DESTDIR)$(BINDIR) install-strip: install-common install -s -m755 $(BUILDDIR)/vpnc $(DESTDIR)$(SBINDIR) install -s -m755 $(BUILDDIR)/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 ./LICENSE0000644000175000017500000010450514003465661010034 0ustar fsfs GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: {project} Copyright (C) {year} {fullname} This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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 Lesser General Public License instead of this License. But first, please read .