diff options
Diffstat (limited to '')
78 files changed, 3418 insertions, 668 deletions
@@ -3,6 +3,49 @@ Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net> $Id$ +2005.09.26 -- Version 2.1-beta1 + +* Merged with 2.0.3-rc1 +* easy-rsa/2.0 moved to easy-rsa +* old easy-rsa moved to easy-rsa/1.0 + +2005.09.23 -- Version 2.0.2-TO4 + +* Added feature to TAP-Win32 adapter to allow it to be + opened from non-administrator mode. This feature + is enabled by default, and can be enabled/disabled + in the adapter advanced properties dialog. +* Added --allow-nonadmin standalone option for Windows to + set TAP adapter to allow non-admin access. This + is a user-mode version of the code, and duplicates + the same feature as the above entry. +* Added fix that attempts to solve corner case of tunnel not + forwarding packets when system clock is reset to an earlier time. +* Added --redirect-gateway bypass-dns option. (Developers: + To add bypass-dhcp or bypass-dns support to other OSes, + add a get_bypass_addresses function to route.c for + your OS.) +* Added OPENVPN_PLUGIN_CLIENT_CONNECT_V2 plugin callback, which + allows a client-connect plugin to return configuration text + in memory, rather than via a file. +* Fixed a bug where --mode server --proto tcp-server --cipher none + operation could cause tunnel packet truncation. +* openvpn --version will show [LZO1] or [LZO2], depending on + version that was linked. + +2005.09.07 -- Version 2.0.2-TO1 + +* Added --topology directive. See man page. +* Added --redirect-gateway bypass-dhcp option to add a route + allowing DHCP packets to bypass the tunnel, when the + DHCP server is non-local. Currently only implemented + on Windows clients. +* Modified OpenVPN Service on Windows to declare the DHCP + client service as a dependency. +* Extended the plugin interface to allow plugins to declare + per-client constructor and destructor functions, to make + it simpler for plugins to maintain per-client state. + 2005.09.25 -- Version 2.0.3-rc1 * openvpn_plugin_abort_v1 function wasn't being properly diff --git a/config-win32.h.in b/config-win32.h.in index 1a3ce9f..e58029c 100644 --- a/config-win32.h.in +++ b/config-win32.h.in @@ -260,6 +260,9 @@ typedef unsigned long in_addr_t; /* Use LZO compression library */ #define USE_LZO 1 +/* LZO version number */ +#define LZO_VERSION_NUM "1" + /* Use OpenSSL SSL library */ #define USE_SSL 1 diff --git a/configure.ac b/configure.ac index 2b09280..8df15eb 100644 --- a/configure.ac +++ b/configure.ac @@ -25,7 +25,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT([OpenVPN], [2.0.3_rc1], [openvpn-users@lists.sourceforge.net], [openvpn]) +AC_INIT([OpenVPN], [2.1_beta1], [openvpn-users@lists.sourceforge.net], [openvpn]) AM_CONFIG_HEADER(config.h) AC_CONFIG_SRCDIR(syshead.h) @@ -489,6 +489,7 @@ if test "$LZO" = "yes"; then [ OPENVPN_ADD_LIBS(-l$i) AC_DEFINE(USE_LZO, 1, [Use LZO compression library]) + AC_DEFINE_UNQUOTED(LZO_VERSION_NUM, "$LZO_H", [LZO version number]) havelzolib=1 ] ) diff --git a/easy-rsa/1.0/README b/easy-rsa/1.0/README new file mode 100644 index 0000000..fd424ef --- /dev/null +++ b/easy-rsa/1.0/README @@ -0,0 +1,161 @@ +This is a small RSA key management package, +based on the openssl command line tool, that +can be found in the easy-rsa subdirectory +of the OpenVPN distribution. + +These are reference notes. For step +by step instructions, see the HOWTO: + +http://openvpn.net/howto.html + +INSTALL + +1. Edit vars. +2. Set KEY_CONFIG to point to the openssl.cnf file + included in this distribution. +3. Set KEY_DIR to point to a directory which will + contain all keys, certificates, etc. This + directory need not exist, and if it does, + it will be deleted with rm -rf, so BE + CAREFUL how you set KEY_DIR. +4. (Optional) Edit other fields in vars + per your site data. You may want to + increase KEY_SIZE to 2048 if you are + paranoid and don't mind slower key + processing, but certainly 1024 is + fine for testing purposes. KEY_SIZE + must be compatible across both peers + participating in a secure SSL/TLS + connection. +5 . vars +6. ./clean-all +7. As you create certificates, keys, and + certificate signing requests, understand that + only .key files should be kept confidential. + .crt and .csr files can be sent over insecure + channels such as plaintext email. +8. You should never need to copy a .key file + between computers. Normally each computer + will have its own certificate/key pair. + +BUILD YOUR OWN ROOT CERTIFICATE AUTHORITY (CA) CERTIFICATE/KEY + +1. ./build-ca +2. ca.crt and ca.key will be built in your KEY_DIR + directory + +BUILD AN INTERMEDIATE CERTIFICATE AUTHORITY CERTIFICATE/KEY (optional) + +1. ./build-inter inter +2. inter.crt and inter.key will be built in your KEY_DIR + directory and signed with your root certificate. + +BUILD DIFFIE-HELLMAN PARAMETERS (necessary for +the server end of a SSL/TLS connection). + +1. ./build-dh + +BUILD A CERTIFICATE SIGNING REQUEST (If +you want to sign your certificate with a root +certificate controlled by another individual +or organization, or residing on a different machine). + +1. Get ca.crt (the root certificate) from your + certificate authority. Though this + transfer can be over an insecure channel, to prevent + man-in-the-middle attacks you must confirm that + ca.crt was not tampered with. Large CAs solve this + problem by hardwiring their root certificates into + popular web browsers. A simple way to verify a root + CA is to call the issuer on the telephone and confirm + that the md5sum or sha1sum signatures on the ca.crt + files match (such as with the command: "md5sum ca.crt"). +2. Choose a name for your certificate such as your computer + name. In our example we will use "mycert". +3. ./build-req mycert +4. You can ignore most of the fields, but set + "Common Name" to something unique such as your + computer's host name. Leave all password + fields blank, unless you want your private key + to be protected by password. Using a password + is not required -- it will make your key more secure + but also more inconvenient to use, because you will + need to supply your password anytime the key is used. + NOTE: if you are using a password, use ./build-req-pass + instead of ./build-req +5. Your key will be written to $KEY_DIR/mycert.key +6. Your certificate signing request will be written to + to $KEY_DIR/mycert.csr +7. Email mycert.csr to the individual or organization + which controls the root certificate. This can be + done over an insecure channel. +8. After the .csr file is signed by the root certificate + authority, you will receive a file mycert.crt + (your certificate). Place mycert.crt in your + KEY_DIR directory. +9. The combined files of mycert.crt, mycert.key, + and ca.crt can now be used to secure one end of + an SSL/TLS connection. + +SIGN A CERTIFICATE SIGNING REQUEST + +1. ./sign-req mycert +2. mycert.crt will be built in your KEY_DIR + directory using mycert.csr and your root CA + file as input. + +BUILD AND SIGN A CERTIFICATE SIGNING REQUEST +USING A LOCALLY INSTALLED ROOT CERTIFICATE/KEY -- this +script generates and signs a certificate in one step, +but it requires that the generated certificate and private +key files be copied to the destination host over a +secure channel. + +1. ./build-key mycert (no password protection) +2. OR ./build-key-pass mycert (with password protection) +3. OR ./build-key-pkcs12 mycert (PKCS #12 format) +4. OR ./build-key-server mycert (with nsCertType=server) +5. mycert.crt and mycert.key will be built in your + KEY_DIR directory, and mycert.crt will be signed + by your root CA. If ./build-key-pkcs12 was used a + mycert.p12 file will also be created including the + private key, certificate and the ca certificate. + +IMPORTANT + +To avoid a possible Man-in-the-Middle attack where an authorized +client tries to connect to another client by impersonating the +server, make sure to enforce some kind of server certificate +verification by clients. There are currently four different ways +of accomplishing this, listed in the order of preference: + +(1) Build your server certificates with the build-key-server + script. This will designate the certificate as a + server-only certificate by setting nsCertType=server. + Now add the following line to your client configuration: + + ns-cert-type server + + This will block clients from connecting to any + server which lacks the nsCertType=server designation + in its certificate, even if the certificate has been + signed by the CA which is cited in the OpenVPN configuration + file (--ca directive). + +(2) Use the --tls-remote directive on the client to + accept/reject the server connection based on the common + name of the server certificate. + +(3) Use a --tls-verify script or plugin to accept/reject the + server connection based on a custom test of the server + certificate's embedded X509 subject details. + +(4) Sign server certificates with one CA and client certificates + with a different CA. The client config "ca" directive should + reference the server-signing CA while the server config "ca" + directive should reference the client-signing CA. + +NOTES + +Show certificate fields: + openssl x509 -in cert.crt -text diff --git a/easy-rsa/1.0/build-ca b/easy-rsa/1.0/build-ca new file mode 100755 index 0000000..5ad59cc --- /dev/null +++ b/easy-rsa/1.0/build-ca @@ -0,0 +1,13 @@ +#!/bin/sh + +# +# Build a root certificate +# + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -nodes -new -x509 -keyout ca.key -out ca.crt -config $KEY_CONFIG && \ + chmod 0600 ca.key +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-dh b/easy-rsa/1.0/build-dh new file mode 100755 index 0000000..6de4baf --- /dev/null +++ b/easy-rsa/1.0/build-dh @@ -0,0 +1,12 @@ +#!/bin/sh + +# +# Build Diffie-Hellman parameters for the server side +# of an SSL/TLS connection. +# + +if test $KEY_DIR; then + openssl dhparam -out ${KEY_DIR}/dh${KEY_SIZE}.pem ${KEY_SIZE} +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-inter b/easy-rsa/1.0/build-inter new file mode 100755 index 0000000..8b3a6b2 --- /dev/null +++ b/easy-rsa/1.0/build-inter @@ -0,0 +1,19 @@ +#!/bin/sh + +# +# Make an intermediate CA certificate/private key pair using a locally generated +# root certificate. +# + +if test $# -ne 1; then + echo "usage: build-inter <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ + openssl ca -extensions v3_ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-key b/easy-rsa/1.0/build-key new file mode 100755 index 0000000..3159d2b --- /dev/null +++ b/easy-rsa/1.0/build-key @@ -0,0 +1,20 @@ +#!/bin/sh + +# +# Make a certificate/private key pair using a locally generated +# root certificate. +# + +if test $# -ne 1; then + echo "usage: build-key <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ + openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \ + chmod 0600 $1.key +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-key-pass b/easy-rsa/1.0/build-key-pass new file mode 100755 index 0000000..03ab304 --- /dev/null +++ b/easy-rsa/1.0/build-key-pass @@ -0,0 +1,20 @@ +#!/bin/sh + +# +# Similar to build-key, but protect the private key +# with a password. +# + +if test $# -ne 1; then + echo "usage: build-key-pass <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ + openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \ + chmod 0600 $1.key +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-key-pkcs12 b/easy-rsa/1.0/build-key-pkcs12 new file mode 100755 index 0000000..f8a057b --- /dev/null +++ b/easy-rsa/1.0/build-key-pkcs12 @@ -0,0 +1,21 @@ +#!/bin/sh + +# +# Make a certificate/private key pair using a locally generated +# root certificate and convert it to a PKCS #12 file including the +# the CA certificate as well. + +if test $# -ne 1; then + echo "usage: build-key-pkcs12 <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ + openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \ + openssl pkcs12 -export -inkey $1.key -in $1.crt -certfile ca.crt -out $1.p12 && \ + chmod 0600 $1.key $1.p12 +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-key-server b/easy-rsa/1.0/build-key-server new file mode 100755 index 0000000..30dc41e --- /dev/null +++ b/easy-rsa/1.0/build-key-server @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Make a certificate/private key pair using a locally generated +# root certificate. +# +# Explicitly set nsCertType to server using the "server" +# extension in the openssl.cnf file. + +if test $# -ne 1; then + echo "usage: build-key-server <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -extensions server -config $KEY_CONFIG && \ + openssl ca -days 3650 -out $1.crt -in $1.csr -extensions server -config $KEY_CONFIG && \ + chmod 0600 $1.key +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-req b/easy-rsa/1.0/build-req new file mode 100755 index 0000000..30f62f5 --- /dev/null +++ b/easy-rsa/1.0/build-req @@ -0,0 +1,18 @@ +#!/bin/sh + +# +# Build a certificate signing request and private key. Use this +# when your root certificate and key is not available locally. +# + +if test $# -ne 1; then + echo "usage: build-req <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/build-req-pass b/easy-rsa/1.0/build-req-pass new file mode 100755 index 0000000..829b286 --- /dev/null +++ b/easy-rsa/1.0/build-req-pass @@ -0,0 +1,18 @@ +#!/bin/sh + +# +# Like build-req, but protect your private key +# with a password. +# + +if test $# -ne 1; then + echo "usage: build-req-pass <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl req -days 3650 -new -keyout $1.key -out $1.csr -config $KEY_CONFIG +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/clean-all b/easy-rsa/1.0/clean-all new file mode 100755 index 0000000..d10aef5 --- /dev/null +++ b/easy-rsa/1.0/clean-all @@ -0,0 +1,19 @@ +#!/bin/sh + +# +# Initialize the $KEY_DIR directory. +# Note that this script does a +# rm -rf on $KEY_DIR so be careful! +# + +d=$KEY_DIR + +if test $d; then + rm -rf $d + mkdir $d && \ + chmod go-rwx $d && \ + touch $d/index.txt && \ + echo 01 >$d/serial +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/list-crl b/easy-rsa/1.0/list-crl new file mode 100644 index 0000000..b214dbd --- /dev/null +++ b/easy-rsa/1.0/list-crl @@ -0,0 +1,18 @@ +#!/bin/sh + +# +# list revoked certificates +# +# + +if test $# -ne 1; then + echo "usage: list-crl <crlfile.pem>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl crl -text -noout -in $1 +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/make-crl b/easy-rsa/1.0/make-crl index 62fe6c1..62fe6c1 100644 --- a/easy-rsa/make-crl +++ b/easy-rsa/1.0/make-crl diff --git a/easy-rsa/1.0/openssl.cnf b/easy-rsa/1.0/openssl.cnf new file mode 100644 index 0000000..270b069 --- /dev/null +++ b/easy-rsa/1.0/openssl.cnf @@ -0,0 +1,255 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca' and 'req'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = $ENV::KEY_DIR # Where everything is kept +certs = $dir # Where the issued certs are kept +crl_dir = $dir # Where the issued crl are kept +database = $dir/index.txt # database index file. +new_certs_dir = $dir # default place for new certs. + +certificate = $dir/ca.crt # The CA certificate +serial = $dir/serial # The current serial number +crl = $dir/crl.pem # The current CRL +private_key = $dir/ca.key # The private key +RANDFILE = $dir/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 3650 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = md5 # which md to use. +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = $ENV::KEY_SIZE +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = $ENV::KEY_COUNTRY +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = $ENV::KEY_PROVINCE + +localityName = Locality Name (eg, city) +localityName_default = $ENV::KEY_CITY + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = $ENV::KEY_ORG + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (eg, your name or your server\'s hostname) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_default = $ENV::KEY_EMAIL +emailAddress_max = 40 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ server ] + +# JY ADDED -- Make a cert with nsCertType set to "server" +basicConstraints=CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer:always + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always diff --git a/easy-rsa/revoke-crt b/easy-rsa/1.0/revoke-crt index 35b071a..35b071a 100644 --- a/easy-rsa/revoke-crt +++ b/easy-rsa/1.0/revoke-crt diff --git a/easy-rsa/1.0/revoke-full b/easy-rsa/1.0/revoke-full new file mode 100755 index 0000000..66ea03f --- /dev/null +++ b/easy-rsa/1.0/revoke-full @@ -0,0 +1,29 @@ +#!/bin/sh + +# revoke a certificate, regenerate CRL, +# and verify revocation + +CRL=crl.pem +RT=revoke-test.pem + +if test $# -ne 1; then + echo "usage: revoke-full <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR + rm -f $RT + + # revoke key and generate a new CRL + openssl ca -revoke $1.crt -config $KEY_CONFIG + + # generate a new CRL + openssl ca -gencrl -out $CRL -config $KEY_CONFIG + cat ca.crt $CRL >$RT + + # verify the revocation + openssl verify -CAfile $RT -crl_check $1.crt +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/sign-req b/easy-rsa/1.0/sign-req new file mode 100755 index 0000000..59edc42 --- /dev/null +++ b/easy-rsa/1.0/sign-req @@ -0,0 +1,18 @@ +#!/bin/sh + +# +# Sign a certificate signing request (a .csr file) +# with a local root certificate and key. +# + +if test $# -ne 1; then + echo "usage: sign-req <name>"; + exit 1 +fi + +if test $KEY_DIR; then + cd $KEY_DIR && \ + openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG +else + echo you must define KEY_DIR +fi diff --git a/easy-rsa/1.0/vars b/easy-rsa/1.0/vars new file mode 100644 index 0000000..da89cd2 --- /dev/null +++ b/easy-rsa/1.0/vars @@ -0,0 +1,49 @@ +# easy-rsa parameter settings + +# NOTE: If you installed from an RPM, +# don't edit this file in place in +# /usr/share/openvpn/easy-rsa -- +# instead, you should copy the whole +# easy-rsa directory to another location +# (such as /etc/openvpn) so that your +# edits will not be wiped out by a future +# OpenVPN package upgrade. + +# This variable should point to +# the top level of the easy-rsa +# tree. +export D=`pwd` + +# This variable should point to +# the openssl.cnf file included +# with easy-rsa. +export KEY_CONFIG=$D/openssl.cnf + +# Edit this variable to point to +# your soon-to-be-created key +# directory. +# +# WARNING: clean-all will do +# a rm -rf on this directory +# so make sure you define +# it correctly! +export KEY_DIR=$D/keys + +# Issue rm -rf warning +echo NOTE: when you run ./clean-all, I will be doing a rm -rf on $KEY_DIR + +# Increase this to 2048 if you +# are paranoid. This will slow +# down TLS negotiation performance +# as well as the one-time DH parms +# generation process. +export KEY_SIZE=1024 + +# These are the default values for fields +# which will be placed in the certificate. +# Don't leave any of these fields blank. +export KEY_COUNTRY=KG +export KEY_PROVINCE=NA +export KEY_CITY=BISHKEK +export KEY_ORG="OpenVPN-TEST" +export KEY_EMAIL="me@myhost.mydomain" diff --git a/easy-rsa/README b/easy-rsa/README index fd424ef..02800c2 100644 --- a/easy-rsa/README +++ b/easy-rsa/README @@ -1,14 +1,53 @@ -This is a small RSA key management package, -based on the openssl command line tool, that -can be found in the easy-rsa subdirectory +EASY-RSA Version 2.0-rc1 + +This is a small RSA key management package, based on the openssl +command line tool, that can be found in the easy-rsa subdirectory of the OpenVPN distribution. -These are reference notes. For step -by step instructions, see the HOWTO: +These are reference notes. For step-by-step instructions, see the +HOWTO: http://openvpn.net/howto.html -INSTALL +This package is based on the ./pkitool script. Run ./pkitool +without arguments for a detailed help message (which is also pasted +below). + +Release Notes for easy-rsa-2.0 + +* Most functionality has been consolidated into the pkitool + script. For compatibility, all previous scripts from 1.0 such + as build-key and build-key-server are provided as stubs + which call pkitool to do the real work. + +* pkitool has a --batch flag (enabled by default) which generates + keys/certs without needing any interactive input. pkitool + can still generate certs/keys using interactive prompting by + using the --interact flag. + +* The inherit-inter script has been provided for creating + a new PKI rooted on an intermediate certificate built within a + higher-level PKI. See comments in the inherit-inter script + for more info. + +* The openssl.cnf file has been modified. pkitool will not + work with the openssl.cnf file included with previous + easy-rsa releases. + +* The vars file has been modified -- the following extra + variables have been added: EASY_RSA, CA_EXPIRE, + KEY_EXPIRE. + +* The make-crl and revoke-crt scripts have been removed and + are replaced by the revoke-full script. + +* The "Organizational Unit" X509 field can be set using + the KEY_OU environmental variable before calling pkitool. + +* This release only affects the Linux/Unix version of easy-rsa. + The Windows version (written to use the Windows shell) is unchanged. + +INSTALL easy-rsa 1. Edit vars. 2. Set KEY_CONFIG to point to the openssl.cnf file @@ -34,92 +73,6 @@ INSTALL only .key files should be kept confidential. .crt and .csr files can be sent over insecure channels such as plaintext email. -8. You should never need to copy a .key file - between computers. Normally each computer - will have its own certificate/key pair. - -BUILD YOUR OWN ROOT CERTIFICATE AUTHORITY (CA) CERTIFICATE/KEY - -1. ./build-ca -2. ca.crt and ca.key will be built in your KEY_DIR - directory - -BUILD AN INTERMEDIATE CERTIFICATE AUTHORITY CERTIFICATE/KEY (optional) - -1. ./build-inter inter -2. inter.crt and inter.key will be built in your KEY_DIR - directory and signed with your root certificate. - -BUILD DIFFIE-HELLMAN PARAMETERS (necessary for -the server end of a SSL/TLS connection). - -1. ./build-dh - -BUILD A CERTIFICATE SIGNING REQUEST (If -you want to sign your certificate with a root -certificate controlled by another individual -or organization, or residing on a different machine). - -1. Get ca.crt (the root certificate) from your - certificate authority. Though this - transfer can be over an insecure channel, to prevent - man-in-the-middle attacks you must confirm that - ca.crt was not tampered with. Large CAs solve this - problem by hardwiring their root certificates into - popular web browsers. A simple way to verify a root - CA is to call the issuer on the telephone and confirm - that the md5sum or sha1sum signatures on the ca.crt - files match (such as with the command: "md5sum ca.crt"). -2. Choose a name for your certificate such as your computer - name. In our example we will use "mycert". -3. ./build-req mycert -4. You can ignore most of the fields, but set - "Common Name" to something unique such as your - computer's host name. Leave all password - fields blank, unless you want your private key - to be protected by password. Using a password - is not required -- it will make your key more secure - but also more inconvenient to use, because you will - need to supply your password anytime the key is used. - NOTE: if you are using a password, use ./build-req-pass - instead of ./build-req -5. Your key will be written to $KEY_DIR/mycert.key -6. Your certificate signing request will be written to - to $KEY_DIR/mycert.csr -7. Email mycert.csr to the individual or organization - which controls the root certificate. This can be - done over an insecure channel. -8. After the .csr file is signed by the root certificate - authority, you will receive a file mycert.crt - (your certificate). Place mycert.crt in your - KEY_DIR directory. -9. The combined files of mycert.crt, mycert.key, - and ca.crt can now be used to secure one end of - an SSL/TLS connection. - -SIGN A CERTIFICATE SIGNING REQUEST - -1. ./sign-req mycert -2. mycert.crt will be built in your KEY_DIR - directory using mycert.csr and your root CA - file as input. - -BUILD AND SIGN A CERTIFICATE SIGNING REQUEST -USING A LOCALLY INSTALLED ROOT CERTIFICATE/KEY -- this -script generates and signs a certificate in one step, -but it requires that the generated certificate and private -key files be copied to the destination host over a -secure channel. - -1. ./build-key mycert (no password protection) -2. OR ./build-key-pass mycert (with password protection) -3. OR ./build-key-pkcs12 mycert (PKCS #12 format) -4. OR ./build-key-server mycert (with nsCertType=server) -5. mycert.crt and mycert.key will be built in your - KEY_DIR directory, and mycert.crt will be signed - by your root CA. If ./build-key-pkcs12 was used a - mycert.p12 file will also be created including the - private key, certificate and the ca certificate. IMPORTANT @@ -130,7 +83,8 @@ verification by clients. There are currently four different ways of accomplishing this, listed in the order of preference: (1) Build your server certificates with the build-key-server - script. This will designate the certificate as a + script, or using the --server option to pkitool. + This will designate the certificate as a server-only certificate by setting nsCertType=server. Now add the following line to your client configuration: @@ -159,3 +113,56 @@ NOTES Show certificate fields: openssl x509 -in cert.crt -text + +PKITOOL documentation + +pkitool 2.0 +Usage: pkitool [options...] [common-name] +Options: + --batch : batch mode (default) + --interact : interactive mode + --server : build server cert + --initca : build root CA + --inter : build intermediate CA + --pass : encrypt private key with password + --csr : only generate a CSR, do not sign + --sign : sign an existing CSR + --pkcs12 : generate a combined pkcs12 file +Notes: + Please edit the vars script to reflect your configuration, + then source it with "source ./vars". + Next, to start with a fresh PKI configuration and to delete any + previous certificates and keys, run "./clean-all". + Finally, you can run this tool (pkitool) to build certificates/keys. +Generated files and corresponding OpenVPN directives: +(Files will be placed in the $KEY_DIR directory, defined in ./vars) + ca.crt -> root certificate (--ca) + ca.key -> root key, keep secure (not directly used by OpenVPN) + .crt files -> client/server certificates (--cert) + .key files -> private keys, keep secure (--key) + .csr files -> certificate signing request (not directly used by OpenVPN) + dh1024.pem or dh2048.pem -> Diffie Hellman parameters (--dh) +Examples: + pkitool --initca -> Build root certificate + pkitool --initca --pass -> Build root certificate with password-protected key + pkitool --server server1 -> Build "server1" certificate/key + pkitool client1 -> Build "client1" certificate/key + pkitool --pass client2 -> Build password-protected "client2" certificate/key + pkitool --pkcs12 client3 -> Build "client3" certificate/key in PKCS #12 format + pkitool --csr client4 -> Build "client4" CSR to be signed by another CA + pkitool --sign client4 -> Sign "client4" CSR + pkitool --inter interca -> Build an intermediate key-signing certificate/key + Also see ./inherit-inter script. +Typical usage for initial PKI setup. Build myserver, client1, and client2 cert/keys. +Protect client2 key with a password. Build DH parms. Generated files in ./keys : + [edit vars with your site-specific info] + source ./vars + ./clean-all + ./build-dh -> takes a long time, consider backgrounding + ./pkitool --initca + ./pkitool --server myserver + ./pkitool client1 + ./pkitool --pass client2 +Typical usage for adding client cert to existing PKI: + source ./vars + ./pkitool client-new diff --git a/easy-rsa/build-ca b/easy-rsa/build-ca index 5ad59cc..fb1e2ca 100755 --- a/easy-rsa/build-ca +++ b/easy-rsa/build-ca @@ -1,13 +1,8 @@ -#!/bin/sh +#!/bin/bash # # Build a root certificate # -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -nodes -new -x509 -keyout ca.key -out ca.crt -config $KEY_CONFIG && \ - chmod 0600 ca.key -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --initca $* diff --git a/easy-rsa/build-dh b/easy-rsa/build-dh index 6de4baf..ec7a805 100755 --- a/easy-rsa/build-dh +++ b/easy-rsa/build-dh @@ -1,12 +1,11 @@ -#!/bin/sh +#!/bin/bash -# # Build Diffie-Hellman parameters for the server side # of an SSL/TLS connection. -# -if test $KEY_DIR; then +if [ -d $KEY_DIR ] && [ $KEY_SIZE ]; then openssl dhparam -out ${KEY_DIR}/dh${KEY_SIZE}.pem ${KEY_SIZE} else - echo you must define KEY_DIR + echo 'Please source the vars script first (i.e. "source ./vars")' + echo 'Make sure you have edited it to reflect your configuration.' fi diff --git a/easy-rsa/build-inter b/easy-rsa/build-inter index 8b3a6b2..f831d6f 100755 --- a/easy-rsa/build-inter +++ b/easy-rsa/build-inter @@ -1,19 +1,7 @@ -#!/bin/sh +#!/bin/bash -# # Make an intermediate CA certificate/private key pair using a locally generated # root certificate. -# -if test $# -ne 1; then - echo "usage: build-inter <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ - openssl ca -extensions v3_ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --inter $* diff --git a/easy-rsa/build-key b/easy-rsa/build-key index 3159d2b..6196308 100755 --- a/easy-rsa/build-key +++ b/easy-rsa/build-key @@ -1,20 +1,7 @@ -#!/bin/sh +#!/bin/bash -# # Make a certificate/private key pair using a locally generated # root certificate. -# -if test $# -ne 1; then - echo "usage: build-key <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ - openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \ - chmod 0600 $1.key -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact $* diff --git a/easy-rsa/build-key-pass b/easy-rsa/build-key-pass index 03ab304..35543e0 100755 --- a/easy-rsa/build-key-pass +++ b/easy-rsa/build-key-pass @@ -1,20 +1,7 @@ -#!/bin/sh +#!/bin/bash -# # Similar to build-key, but protect the private key # with a password. -# -if test $# -ne 1; then - echo "usage: build-key-pass <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ - openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \ - chmod 0600 $1.key -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --pass $* diff --git a/easy-rsa/build-key-pkcs12 b/easy-rsa/build-key-pkcs12 index f8a057b..5ef064f 100755 --- a/easy-rsa/build-key-pkcs12 +++ b/easy-rsa/build-key-pkcs12 @@ -1,21 +1,8 @@ -#!/bin/sh +#!/bin/bash -# # Make a certificate/private key pair using a locally generated # root certificate and convert it to a PKCS #12 file including the # the CA certificate as well. -if test $# -ne 1; then - echo "usage: build-key-pkcs12 <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \ - openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \ - openssl pkcs12 -export -inkey $1.key -in $1.crt -certfile ca.crt -out $1.p12 && \ - chmod 0600 $1.key $1.p12 -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --pkcs12 $* diff --git a/easy-rsa/build-key-server b/easy-rsa/build-key-server index 30dc41e..5502675 100755 --- a/easy-rsa/build-key-server +++ b/easy-rsa/build-key-server @@ -1,22 +1,10 @@ -#!/bin/sh +#!/bin/bash -# # Make a certificate/private key pair using a locally generated # root certificate. # # Explicitly set nsCertType to server using the "server" # extension in the openssl.cnf file. -if test $# -ne 1; then - echo "usage: build-key-server <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -extensions server -config $KEY_CONFIG && \ - openssl ca -days 3650 -out $1.crt -in $1.csr -extensions server -config $KEY_CONFIG && \ - chmod 0600 $1.key -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --server $* diff --git a/easy-rsa/build-req b/easy-rsa/build-req index 30f62f5..26587d1 100755 --- a/easy-rsa/build-req +++ b/easy-rsa/build-req @@ -1,18 +1,7 @@ -#!/bin/sh +#!/bin/bash -# # Build a certificate signing request and private key. Use this # when your root certificate and key is not available locally. -# -if test $# -ne 1; then - echo "usage: build-req <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --csr $* diff --git a/easy-rsa/build-req-pass b/easy-rsa/build-req-pass index 829b286..6e6c863 100755 --- a/easy-rsa/build-req-pass +++ b/easy-rsa/build-req-pass @@ -1,18 +1,7 @@ -#!/bin/sh +#!/bin/bash -# # Like build-req, but protect your private key # with a password. -# -if test $# -ne 1; then - echo "usage: build-req-pass <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl req -days 3650 -new -keyout $1.key -out $1.csr -config $KEY_CONFIG -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --csr --pass $* diff --git a/easy-rsa/clean-all b/easy-rsa/clean-all index d10aef5..0576db5 100755 --- a/easy-rsa/clean-all +++ b/easy-rsa/clean-all @@ -1,19 +1,16 @@ -#!/bin/sh +#!/bin/bash -# # Initialize the $KEY_DIR directory. # Note that this script does a # rm -rf on $KEY_DIR so be careful! -# -d=$KEY_DIR - -if test $d; then - rm -rf $d - mkdir $d && \ - chmod go-rwx $d && \ - touch $d/index.txt && \ - echo 01 >$d/serial +if [ "$KEY_DIR" ]; then + rm -rf "$KEY_DIR" + mkdir "$KEY_DIR" && \ + chmod go-rwx "$KEY_DIR" && \ + touch "$KEY_DIR/index.txt" && \ + echo 01 >"$KEY_DIR/serial" else - echo you must define KEY_DIR + echo 'Please source the vars script first (i.e. "source ./vars")' + echo 'Make sure you have edited it to reflect your configuration.' fi diff --git a/easy-rsa/inherit-inter b/easy-rsa/inherit-inter new file mode 100755 index 0000000..2101951 --- /dev/null +++ b/easy-rsa/inherit-inter @@ -0,0 +1,39 @@ +#!/bin/bash + +# Build a new PKI which is rooted on an intermediate certificate generated +# by ./build-inter or ./pkitool --inter from a parent PKI. The new PKI should +# have independent vars settings, and must use a different KEY_DIR directory +# from the parent. This tool can be used to generate arbitrary depth +# certificate chains. +# +# To build an intermediate CA, follow the same steps for a regular PKI but +# replace ./build-key or ./pkitool --initca with this script. + +# The EXPORT_CA file will contain the CA certificate chain and should be +# referenced by the OpenVPN "ca" directive in config files. The ca.crt file +# will only contain the local intermediate CA -- it's needed by the easy-rsa +# scripts but not by OpenVPN directly. +EXPORT_CA="export-ca.crt" + +if [ $# -ne 2 ]; then + echo "usage: $0 <parent-key-dir> <common-name>" + echo "parent-key-dir: the KEY_DIR directory of the parent PKI" + echo "common-name: the common name of the intermediate certificate in the parent PKI" + exit 1; +fi + +if [ "$KEY_DIR" ]; then + cp "$1/$2.crt" "$KEY_DIR/ca.crt" + cp "$1/$2.key" "$KEY_DIR/ca.key" + + if [ -e "$1/$EXPORT_CA" ]; then + PARENT_CA="$1/$EXPORT_CA" + else + PARENT_CA="$1/ca.crt" + fi + cp "$PARENT_CA" "$KEY_DIR/$EXPORT_CA" + cat "$KEY_DIR/ca.crt" >> "$KEY_DIR/$EXPORT_CA" +else + echo 'Please source the vars script first (i.e. "source ./vars")' + echo 'Make sure you have edited it to reflect your configuration.' +fi diff --git a/easy-rsa/list-crl b/easy-rsa/list-crl index b214dbd..7736fa8 100644..100755 --- a/easy-rsa/list-crl +++ b/easy-rsa/list-crl @@ -1,18 +1,13 @@ -#!/bin/sh +#!/bin/bash -# # list revoked certificates -# -# -if test $# -ne 1; then - echo "usage: list-crl <crlfile.pem>"; - exit 1 -fi +CRL="${1:-crl.pem}" -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl crl -text -noout -in $1 +if [ "$KEY_DIR" ]; then + cd "$KEY_DIR" && \ + openssl crl -text -noout -in "$CRL" else - echo you must define KEY_DIR + echo 'Please source the vars script first (i.e. "source ./vars")' + echo 'Make sure you have edited it to reflect your configuration.' fi diff --git a/easy-rsa/openssl.cnf b/easy-rsa/openssl.cnf index 270b069..7fedebe 100644..100755 --- a/easy-rsa/openssl.cnf +++ b/easy-rsa/openssl.cnf @@ -1,3 +1,5 @@ +# For use with easy-rsa version 2.0 + # # OpenSSL example configuration file. # This is mostly being used for generation of certificate requests. @@ -60,7 +62,7 @@ preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) -policy = policy_match +policy = policy_anything # For the CA policy [ policy_match ] @@ -136,6 +138,10 @@ emailAddress = Email Address emailAddress_default = $ENV::KEY_EMAIL emailAddress_max = 40 +# JY -- added for batch mode +organizationalUnitName_default = $ENV::KEY_OU +commonName_default = $ENV::KEY_CN + # SET-ex3 = SET extension number 3 [ req_attributes ] diff --git a/easy-rsa/pkitool b/easy-rsa/pkitool new file mode 100755 index 0000000..2d2d764 --- /dev/null +++ b/easy-rsa/pkitool @@ -0,0 +1,233 @@ +#!/bin/sh + +# OpenVPN -- An application to securely tunnel IP networks +# over a single TCP/UDP port, with support for SSL/TLS-based +# session authentication and key exchange, +# packet encryption, packet authentication, and +# packet compression. +# +# Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net> +# +# 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 + +# pkitool is a front-end for the openssl tool. + +# Calling scripts can set the certificate organizational +# unit with the KEY_OU environmental variable. + +PROGNAME=pkitool +VERSION=2.0 +DEBUG=0 + +GREP=grep +OPENSSL=openssl + +need_vars() +{ + echo ' Please edit the vars script to reflect your configuration,' + echo ' then source it with "source ./vars".' + echo ' Next, to start with a fresh PKI configuration and to delete any' + echo ' previous certificates and keys, run "./clean-all".' + echo " Finally, you can run this tool ($PROGNAME) to build certificates/keys." +} + +usage() +{ + echo "$PROGNAME $VERSION" + echo "Usage: $PROGNAME [options...] [common-name]" + echo "Options:" + echo " --batch : batch mode (default)" + echo " --interact : interactive mode" + echo " --server : build server cert" + echo " --initca : build root CA" + echo " --inter : build intermediate CA" + echo " --pass : encrypt private key with password" + echo " --csr : only generate a CSR, do not sign" + echo " --sign : sign an existing CSR" + echo " --pkcs12 : generate a combined pkcs12 file" + echo "Notes:" + need_vars + echo "Generated files and corresponding OpenVPN directives:" + echo '(Files will be placed in the $KEY_DIR directory, defined in ./vars)' + echo " ca.crt -> root certificate (--ca)" + echo " ca.key -> root key, keep secure (not directly used by OpenVPN)" + echo " .crt files -> client/server certificates (--cert)" + echo " .key files -> private keys, keep secure (--key)" + echo " .csr files -> certificate signing request (not directly used by OpenVPN)" + echo " dh1024.pem or dh2048.pem -> Diffie Hellman parameters (--dh)" + echo "Examples:" + echo " $PROGNAME --initca -> Build root certificate" + echo " $PROGNAME --initca --pass -> Build root certificate with password-protected key" + echo " $PROGNAME --server server1 -> Build \"server1\" certificate/key" + echo " $PROGNAME client1 -> Build \"client1\" certificate/key" + echo " $PROGNAME --pass client2 -> Build password-protected \"client2\" certificate/key" + echo " $PROGNAME --pkcs12 client3 -> Build \"client3\" certificate/key in PKCS #12 format" + echo " $PROGNAME --csr client4 -> Build \"client4\" CSR to be signed by another CA" + echo " $PROGNAME --sign client4 -> Sign \"client4\" CSR" + echo " $PROGNAME --inter interca -> Build an intermediate key-signing certificate/key" + echo " Also see ./inherit-inter script." + echo "Typical usage for initial PKI setup. Build myserver, client1, and client2 cert/keys." + echo "Protect client2 key with a password. Build DH parms. Generated files in ./keys :" + echo " [edit vars with your site-specific info]" + echo " source ./vars" + echo " ./clean-all" + echo " ./build-dh -> takes a long time, consider backgrounding" + echo " ./$PROGNAME --initca" + echo " ./$PROGNAME --server myserver" + echo " ./$PROGNAME client1" + echo " ./$PROGNAME --pass client2" + echo "Typical usage for adding client cert to existing PKI:" + echo " source ./vars" + echo " ./$PROGNAME client-new" +} + +# Set defaults +DO_REQ="1" +REQ_EXT="" +DO_CA="1" +CA_EXT="" +DO_P12="0" +DO_ROOT="0" +NODES_REQ="-nodes" +NODES_P12="" +BATCH="-batch" +CA="ca" + +# Process options +while [ $# -gt 0 ]; do + case "$1" in + --server ) REQ_EXT="$REQ_EXT -extensions server" + CA_EXT="$CA_EXT -extensions server" ;; + --batch ) BATCH="-batch" ;; + --interact ) BATCH="" ;; + --inter ) CA_EXT="$CA_EXT -extensions v3_ca" ;; + --initca ) DO_ROOT="1" ;; + --pass ) NODES_REQ="" ;; + --csr ) DO_CA="0" ;; + --sign ) DO_REQ="0" ;; + --pkcs12 ) DO_P12="1" ;; + --* ) echo "$PROGNAME: unknown option: $1" + exit 1 ;; + * ) break ;; + esac + shift +done + +# If we are generating pkcs12, only encrypt the final step +if [ $DO_P12 -eq 1 ]; then + NODES_P12="$NODES_REQ" + NODES_REQ="-nodes" +fi + +# If undefined, set default key expiration intervals +if [ -z "$KEY_EXPIRE" ]; then + KEY_EXPIRE=3650 +fi +if [ -z "$CA_EXPIRE" ]; then + CA_EXPIRE=3650 +fi + +# Set organizational unit to empty string if undefined +if [ -z "$KEY_OU" ]; then + KEY_OU="" +fi + +# Set KEY_CN +if [ $DO_ROOT -eq 1 ]; then + if [ -z "$KEY_CN" ]; then + if [ "$1" ]; then + KEY_CN="$1" + elif [ "$KEY_ORG" ]; then + KEY_CN="$KEY_ORG CA" + fi + fi + if [ $BATCH ] && [ "$KEY_CN" ]; then + echo "Using CA Common Name:" $KEY_CN + fi +elif [ $BATCH ] && [ "$KEY_CN" ] && [ $# -eq 0 ]; then + echo "Using Common Name:" $KEY_CN +else + if [ $# -ne 1 ]; then + usage + exit 1 + else + KEY_CN="$1" + fi +fi +export CA_EXPIRE KEY_EXPIRE KEY_OU KEY_CN + +# Show parameters (debugging) +if [ $DEBUG -eq 1 ]; then + echo DO_REQ $DO_REQ + echo REQ_EXT $REQ_EXT + echo DO_CA $DO_CA + echo CA_EXT $CA_EXT + echo NODES_REQ $NODES_REQ + echo NODES_P12 $NODES_P12 + echo DO_P12 $DO_P12 + echo KEY_CN $KEY_CN + echo BATCH $BATCH + echo DO_ROOT $DO_ROOT + echo KEY_EXPIRE $KEY_EXPIRE + echo CA_EXPIRE $CA_EXPIRE + echo KEY_OU $KEY_OU +fi + +# Make sure ./vars was sourced beforehand +if [ -d "$KEY_DIR" ] && [ "$KEY_CONFIG" ]; then + cd "$KEY_DIR" + + # Make sure $KEY_CONFIG points to the correct version + # of openssl.cnf + if $GREP -i 'easy-rsa version 2\.[0-9]' "$KEY_CONFIG" >/dev/null; then + : + else + echo "$PROGNAME: KEY_CONFIG (set by the ./vars script) is pointing to the wrong" + echo "version of openssl.cnf: $KEY_CONFIG" + echo "The correct version should have a comment that says: easy-rsa version 2.x"; + exit 1; + fi + + # Build root CA + if [ $DO_ROOT -eq 1 ]; then + $OPENSSL req $BATCH -days $CA_EXPIRE $NODES_REQ -new -x509 \ + -keyout "$CA.key" -out "$CA.crt" -config "$KEY_CONFIG" && \ + chmod 0600 "$CA.key" + else + # Make sure CA key/cert is available + if [ $DO_CA -eq 1 ] || [ $DO_P12 -eq 1 ]; then + if [ ! -r "$CA.crt" ] || [ ! -r "$CA.key" ]; then + echo "$PROGNAME: Need a readable $CA.crt and $CA.key in $KEY_DIR" + echo "Try $PROGNAME --initca to build a root certificate/key." + exit 1 + fi + fi + + # Build cert/key + ( [ $DO_REQ -eq 0 ] || $OPENSSL req $BATCH -days $KEY_EXPIRE $NODES_REQ -new \ + -keyout "$KEY_CN.key" -out "$KEY_CN.csr" $REQ_EXT -config "$KEY_CONFIG" ) && \ + ( [ $DO_CA -eq 0 ] || $OPENSSL ca $BATCH -days $KEY_EXPIRE -out "$KEY_CN.crt" \ + -in "$KEY_CN.csr" $CA_EXT -config "$KEY_CONFIG" ) && \ + ( [ $DO_P12 -eq 0 ] || $OPENSSL pkcs12 -export -inkey "$KEY_CN.key" \ + -in "$KEY_CN.crt" -certfile "$CA.crt" -out "$KEY_CN.p12" $NODES_P12 ) && \ + ( [ $DO_CA -eq 0 ] || chmod 0600 "$KEY_CN.key" ) && \ + ( [ $DO_P12 -eq 0 ] || chmod 0600 "$KEY_CN.p12" ) + + fi + +# Need definitions +else + need_vars +fi diff --git a/easy-rsa/revoke-full b/easy-rsa/revoke-full index 66ea03f..9dc9b1e 100755 --- a/easy-rsa/revoke-full +++ b/easy-rsa/revoke-full @@ -1,29 +1,39 @@ -#!/bin/sh +#!/bin/bash # revoke a certificate, regenerate CRL, # and verify revocation -CRL=crl.pem -RT=revoke-test.pem +CRL="crl.pem" +RT="revoke-test.pem" -if test $# -ne 1; then - echo "usage: revoke-full <name>"; - exit 1 +if [ $# -ne 1 ]; then + echo "usage: revoke-full <common-name>"; + exit 1 fi -if test $KEY_DIR; then - cd $KEY_DIR - rm -f $RT +if [ "$KEY_DIR" ]; then + cd "$KEY_DIR" + rm -f "$RT" - # revoke key and generate a new CRL - openssl ca -revoke $1.crt -config $KEY_CONFIG + # set defaults + export KEY_CN="" + export KEY_OU="" - # generate a new CRL - openssl ca -gencrl -out $CRL -config $KEY_CONFIG - cat ca.crt $CRL >$RT + # revoke key and generate a new CRL + openssl ca -revoke "$1.crt" -config "$KEY_CONFIG" + + # generate a new CRL -- try to be compatible with + # intermediate PKIs + openssl ca -gencrl -out "$CRL" -config "$KEY_CONFIG" + if [ -e export-ca.crt ]; then + cat export-ca.crt "$CRL" >"$RT" + else + cat ca.crt "$CRL" >"$RT" + fi - # verify the revocation - openssl verify -CAfile $RT -crl_check $1.crt + # verify the revocation + openssl verify -CAfile "$RT" -crl_check "$1.crt" else - echo you must define KEY_DIR + echo 'Please source the vars script first (i.e. "source ./vars")' + echo 'Make sure you have edited it to reflect your configuration.' fi diff --git a/easy-rsa/sign-req b/easy-rsa/sign-req index 59edc42..38655d3 100755 --- a/easy-rsa/sign-req +++ b/easy-rsa/sign-req @@ -1,18 +1,7 @@ -#!/bin/sh +#!/bin/bash -# # Sign a certificate signing request (a .csr file) # with a local root certificate and key. -# -if test $# -ne 1; then - echo "usage: sign-req <name>"; - exit 1 -fi - -if test $KEY_DIR; then - cd $KEY_DIR && \ - openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG -else - echo you must define KEY_DIR -fi +export EASY_RSA="${EASY_RSA:-.}" +"$EASY_RSA/pkitool" --interact --sign $* diff --git a/easy-rsa/vars b/easy-rsa/vars index da89cd2..a4bd149 100644..100755 --- a/easy-rsa/vars +++ b/easy-rsa/vars @@ -12,12 +12,12 @@ # This variable should point to # the top level of the easy-rsa # tree. -export D=`pwd` +export EASY_RSA="`pwd`" # This variable should point to # the openssl.cnf file included # with easy-rsa. -export KEY_CONFIG=$D/openssl.cnf +export KEY_CONFIG="$EASY_RSA/openssl.cnf" # Edit this variable to point to # your soon-to-be-created key @@ -27,10 +27,10 @@ export KEY_CONFIG=$D/openssl.cnf # a rm -rf on this directory # so make sure you define # it correctly! -export KEY_DIR=$D/keys +export KEY_DIR="$EASY_RSA/keys" # Issue rm -rf warning -echo NOTE: when you run ./clean-all, I will be doing a rm -rf on $KEY_DIR +echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR # Increase this to 2048 if you # are paranoid. This will slow @@ -39,11 +39,17 @@ echo NOTE: when you run ./clean-all, I will be doing a rm -rf on $KEY_DIR # generation process. export KEY_SIZE=1024 +# In how many days should the root CA key expire? +export CA_EXPIRE=3650 + +# In how many days should certificates expire? +export KEY_EXPIRE=3650 + # These are the default values for fields # which will be placed in the certificate. # Don't leave any of these fields blank. -export KEY_COUNTRY=KG -export KEY_PROVINCE=NA -export KEY_CITY=BISHKEK -export KEY_ORG="OpenVPN-TEST" +export KEY_COUNTRY="US" +export KEY_PROVINCE="CA" +export KEY_CITY="SanFrancisco" +export KEY_ORG="Fort-Funston" export KEY_EMAIL="me@myhost.mydomain" @@ -99,6 +99,7 @@ #define D_DHCP_OPT LOGLEV(4, 53, 0) /* show DHCP options binary string */ #define D_OSBUF LOGLEV(4, 54, 0) /* show socket/tun/tap buffer sizes */ #define D_MBUF LOGLEV(4, 55, 0) /* mbuf.[ch] routines */ +#define D_PACKET_TRUNC_ERR LOGLEV(4, 56, 0) /* PACKET_TRUNCATION_CHECK */ #define D_LOG_RW LOGLEV(5, 0, 0) /* Print 'R' or 'W' to stdout for read/write */ @@ -127,6 +128,7 @@ #define D_PLUGIN_DEBUG LOGLEV(7, 70, M_DEBUG) /* show verbose plugin calls */ #define D_SOCKET_DEBUG LOGLEV(7, 70, M_DEBUG) /* show socket.[ch] debugging info */ #define D_ALIGN_DEBUG LOGLEV(7, 70, M_DEBUG) /* show verbose struct alignment info */ +#define D_PACKET_TRUNC_DEBUG LOGLEV(7, 70, M_DEBUG) /* PACKET_TRUNCATION_CHECK verbose */ #define D_HANDSHAKE_VERBOSE LOGLEV(8, 70, M_DEBUG) /* show detailed description of each handshake */ #define D_TLS_DEBUG_MED LOGLEV(8, 70, M_DEBUG) /* limited info from tls_session routines */ @@ -252,7 +252,7 @@ send_control_channel_string (struct context *c, const char *str, int msglevel) static void check_add_routes_action (struct context *c, const bool errors) { - do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->c1.plugins, c->c2.es); + do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es); update_time (); event_timeout_clear (&c->c2.route_wakeup); event_timeout_clear (&c->c2.route_wakeup_expire); @@ -615,6 +615,7 @@ read_incoming_link (struct context *c) c->c2.buf = c->c2.buffers->read_link_buf; ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM_ADJ (&c->c2.frame, FRAME_HEADROOM_MARKER_READ_LINK))); + status = link_socket_read (c->c2.link_socket, &c->c2.buf, MAX_RW_SIZE_LINK (&c->c2.frame), &c->c2.from); if (socket_connection_reset (c->c2.link_socket, status)) @@ -766,6 +767,16 @@ process_incoming_link (struct context *c) if (c->options.comp_lzo) lzo_decompress (&c->c2.buf, c->c2.buffers->lzo_decompress_buf, &c->c2.lzo_compwork, &c->c2.frame); #endif + +#ifdef PACKET_TRUNCATION_CHECK + //if (c->c2.buf.len > 1) --c->c2.buf.len; // JYFIXME + ipv4_packet_size_verify (BPTR (&c->c2.buf), + BLEN (&c->c2.buf), + TUNNEL_TYPE (c->c1.tuntap), + "POST_DECRYPT", + &c->c2.n_trunc_post_decrypt); +#endif + /* * Set our "official" outgoing address, since * if buf.len is non-zero, we know the packet @@ -840,6 +851,14 @@ read_incoming_tun (struct context *c) c->c2.buf.len = read_tun (c->c1.tuntap, BPTR (&c->c2.buf), MAX_RW_SIZE_TUN (&c->c2.frame)); #endif +#ifdef PACKET_TRUNCATION_CHECK + ipv4_packet_size_verify (BPTR (&c->c2.buf), + BLEN (&c->c2.buf), + TUNNEL_TYPE (c->c1.tuntap), + "READ_TUN", + &c->c2.n_trunc_tun_read); +#endif + /* Was TUN/TAP interface stopped? */ if (tuntap_stop (c->c2.buf.len)) { @@ -889,6 +908,16 @@ process_incoming_tun (struct context *c) * us to examine the IPv4 header. */ process_ipv4_header (c, PIPV4_PASSTOS|PIPV4_MSSFIX, &c->c2.buf); + +#ifdef PACKET_TRUNCATION_CHECK + //if (c->c2.buf.len > 1) --c->c2.buf.len; // JYFIXME + ipv4_packet_size_verify (BPTR (&c->c2.buf), + BLEN (&c->c2.buf), + TUNNEL_TYPE (c->c1.tuntap), + "PRE_ENCRYPT", + &c->c2.n_trunc_pre_encrypt); +#endif + encrypt_sign (c, true); } else @@ -1071,7 +1100,7 @@ process_outgoing_tun (struct context *c) * The --mssfix option requires * us to examine the IPv4 header. */ - process_ipv4_header (c, PIPV4_MSSFIX, &c->c2.to_tun); + process_ipv4_header (c, PIPV4_MSSFIX|PIPV4_OUTGOING, &c->c2.to_tun); if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN (&c->c2.frame)) { @@ -1089,6 +1118,14 @@ process_outgoing_tun (struct context *c) format_hex (BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun), 80, &gc), MD5SUM (BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun), &gc)); +#ifdef PACKET_TRUNCATION_CHECK + ipv4_packet_size_verify (BPTR (&c->c2.to_tun), + BLEN (&c->c2.to_tun), + TUNNEL_TYPE (c->c1.tuntap), + "WRITE_TUN", + &c->c2.n_trunc_tun_write); +#endif + #ifdef TUN_PASS_BUFFER size = write_tun_buffered (c->c1.tuntap, &c->c2.to_tun); #else @@ -74,6 +74,7 @@ bool send_control_channel_string (struct context *c, const char *str, int msglev #define PIPV4_PASSTOS (1<<0) #define PIPV4_MSSFIX (1<<1) +#define PIPV4_OUTGOING (1<<2) void process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf); @@ -77,6 +77,16 @@ print_opt_route (const in_addr_t network, const in_addr_t netmask, struct gc_are } static const char * +print_opt_topology (const int topology, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc (128, gc); + + buf_printf (&out, "topology %s", print_topology (topology)); + + return BSTR (&out); +} + +static const char * print_str_int (const char *str, const int i, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (128, gc); @@ -132,19 +142,23 @@ helper_client_server (struct options *o) * * mode server * tls-server + * push "topology [topology]" * - * if tun: - * ifconfig 10.8.0.1 10.8.0.2 - * ifconfig-pool 10.8.0.4 10.8.0.251 + * if tun AND (topology == net30 OR topology == p2p): + * ifconfig 10.8.0.1 10.8.0.2 + * if !nopool: + * ifconfig-pool 10.8.0.4 10.8.0.251 * route 10.8.0.0 255.255.255.0 * if client-to-client: * push "route 10.8.0.0 255.255.255.0" - * else if !linear-addr: + * else if topology == net30: * push "route 10.8.0.1" * - * if tap: + * if tap OR (tun AND topology == subnet): * ifconfig 10.8.0.1 255.255.255.0 - * ifconfig-pool 10.8.0.2 10.8.0.254 255.255.255.0 + * ifconfig-pool-constraint 10.8.0.0 255.255.255.0 + * if !nopool: + * ifconfig-pool 10.8.0.2 10.8.0.254 255.255.255.0 * push "route-gateway 10.8.0.1" */ @@ -152,6 +166,7 @@ helper_client_server (struct options *o) * Get tun/tap/null device type */ const int dev = dev_type_enum (o->dev, o->dev_type); + const int topology = o->topology; if (o->server_defined) { @@ -197,16 +212,44 @@ helper_client_server (struct options *o) o->mode = MODE_SERVER; o->tls_server = true; - o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc); - o->ifconfig_remote_netmask = print_in_addr_t (o->server_network + 2, 0, &o->gc); - o->ifconfig_pool_defined = true; - o->ifconfig_pool_start = o->server_network + 4; - o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - pool_end_reserve; - helper_add_route (o->server_network, o->server_netmask, o); - if (o->enable_c2c) - push_option (o, print_opt_route (o->server_network, o->server_netmask, &o->gc), M_USAGE); - else if (!o->ifconfig_pool_linear) - push_option (o, print_opt_route (o->server_network + 1, 0, &o->gc), M_USAGE); + + if (topology == TOP_NET30 || topology == TOP_P2P) + { + o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc); + o->ifconfig_remote_netmask = print_in_addr_t (o->server_network + 2, 0, &o->gc); + + if (!(o->server_flags & SF_NOPOOL)) + { + o->ifconfig_pool_defined = true; + o->ifconfig_pool_start = o->server_network + 4; + o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - pool_end_reserve; + } + + helper_add_route (o->server_network, o->server_netmask, o); + if (o->enable_c2c) + push_option (o, print_opt_route (o->server_network, o->server_netmask, &o->gc), M_USAGE); + else if (topology == TOP_NET30) + push_option (o, print_opt_route (o->server_network + 1, 0, &o->gc), M_USAGE); + } + else if (topology == TOP_SUBNET) + { + o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc); + o->ifconfig_remote_netmask = print_in_addr_t (o->server_netmask, 0, &o->gc); + + if (!(o->server_flags & SF_NOPOOL)) + { + o->ifconfig_pool_defined = true; + o->ifconfig_pool_start = o->server_network + 2; + o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 2; + o->ifconfig_pool_netmask = o->server_netmask; + } + + push_option (o, print_opt_route_gateway (o->server_network + 1, &o->gc), M_USAGE); + } + else + ASSERT (0); + + push_option (o, print_opt_topology (topology, &o->gc), M_USAGE); } else if (dev == DEV_TYPE_TAP) { @@ -218,10 +261,15 @@ helper_client_server (struct options *o) o->tls_server = true; o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc); o->ifconfig_remote_netmask = print_in_addr_t (o->server_netmask, 0, &o->gc); - o->ifconfig_pool_defined = true; - o->ifconfig_pool_start = o->server_network + 2; - o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 1; - o->ifconfig_pool_netmask = o->server_netmask; + + if (!(o->server_flags & SF_NOPOOL)) + { + o->ifconfig_pool_defined = true; + o->ifconfig_pool_start = o->server_network + 2; + o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 1; + o->ifconfig_pool_netmask = o->server_netmask; + } + push_option (o, print_opt_route_gateway (o->server_network + 1, &o->gc), M_USAGE); } else @@ -229,6 +277,14 @@ helper_client_server (struct options *o) ASSERT (0); } + /* set push-ifconfig-constraint directive */ + if ((dev == DEV_TYPE_TAP || topology == TOP_SUBNET)) + { + o->push_ifconfig_constraint_defined = true; + o->push_ifconfig_constraint_network = o->server_network; + o->push_ifconfig_constraint_netmask = o->server_netmask; + } + if (o->proto == PROTO_TCPv4) o->proto = PROTO_TCPv4_SERVER; } @@ -543,7 +543,7 @@ do_init_route_list (const struct options *options, const char *gw = NULL; int dev = dev_type_enum (options->dev, options->dev_type); - if (dev == DEV_TYPE_TUN) + if (dev == DEV_TYPE_TUN && (options->topology == TOP_NET30 || options->topology == TOP_P2P)) gw = options->ifconfig_remote_netmask; if (options->route_default_gateway) gw = options->route_default_gateway; @@ -626,7 +626,7 @@ do_route (const struct options *options, if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP)) { - if (plugin_call (plugins, OPENVPN_PLUGIN_ROUTE_UP, NULL, es)) + if (plugin_call (plugins, OPENVPN_PLUGIN_ROUTE_UP, NULL, NULL, es)) msg (M_WARN, "WARNING: route-up plugin call failed"); } @@ -676,6 +676,7 @@ do_init_tun (struct context *c) { c->c1.tuntap = init_tun (c->options.dev, c->options.dev_type, + c->options.topology, c->options.ifconfig_local, c->options.ifconfig_remote_netmask, addr_host (&c->c1.link_socket_addr.local), @@ -712,7 +713,7 @@ do_open_tun (struct context *c) do_alloc_route_list (c); /* parse and resolve the route option list */ - if (c->c1.route_list && c->c2.link_socket) + if (c->options.routes && c->c1.route_list && c->c2.link_socket) do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es); /* do ifconfig */ @@ -741,7 +742,7 @@ do_open_tun (struct context *c) /* run the up script */ run_up_down (c->options.up_script, - c->c1.plugins, + c->plugins, OPENVPN_PLUGIN_UP, c->c1.tuntap->actual_name, TUN_MTU_SIZE (&c->c2.frame), @@ -755,7 +756,7 @@ do_open_tun (struct context *c) /* possibly add routes */ if (!c->options.route_delay_defined) - do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->c1.plugins, c->c2.es); + do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es); /* * Did tun/tap driver give us an MTU? @@ -775,7 +776,7 @@ do_open_tun (struct context *c) /* run the up script if user specified --up-restart */ if (c->options.up_restart) run_up_down (c->options.up_script, - c->c1.plugins, + c->plugins, OPENVPN_PLUGIN_UP, c->c1.tuntap->actual_name, TUN_MTU_SIZE (&c->c2.frame), @@ -836,7 +837,7 @@ do_close_tun (struct context *c, bool force) /* Run the down script -- note that it will run at reduced privilege if, for example, "--user nobody" was used. */ run_up_down (c->options.down_script, - c->c1.plugins, + c->plugins, OPENVPN_PLUGIN_DOWN, tuntap_actual, TUN_MTU_SIZE (&c->c2.frame), @@ -858,7 +859,7 @@ do_close_tun (struct context *c, bool force) /* run the down script on this restart if --up-restart was specified */ if (c->options.up_restart) run_up_down (c->options.down_script, - c->c1.plugins, + c->plugins, OPENVPN_PLUGIN_DOWN, tuntap_actual, TUN_MTU_SIZE (&c->c2.frame), @@ -946,18 +947,25 @@ do_up (struct context *c, bool pulled_options, unsigned int option_types_found) * These are the option categories which will be accepted by pull. */ unsigned int -pull_permission_mask (void) +pull_permission_mask (const struct context *c) { - return ( OPT_P_UP - | OPT_P_ROUTE - | OPT_P_IPWIN32 - | OPT_P_SETENV - | OPT_P_SHAPER - | OPT_P_TIMER - | OPT_P_PERSIST - | OPT_P_MESSAGES - | OPT_P_EXPLICIT_NOTIFY - | OPT_P_ECHO); + unsigned int flags = + OPT_P_UP + | OPT_P_ROUTE_EXTRAS + | OPT_P_IPWIN32 + | OPT_P_SETENV + | OPT_P_SHAPER + | OPT_P_TIMER + | OPT_P_PERSIST + | OPT_P_MESSAGES + | OPT_P_EXPLICIT_NOTIFY + | OPT_P_ECHO + | OPT_P_PULL_MODE; + + if (!c->options.route_nopull) + flags |= OPT_P_ROUTE; + + return flags; } /* @@ -1002,6 +1010,8 @@ do_deferred_options (struct context *c, const unsigned int found) msg (D_PUSH, "OPTIONS IMPORT: --ifconfig/up options modified"); if (found & OPT_P_ROUTE) msg (D_PUSH, "OPTIONS IMPORT: route options modified"); + if (found & OPT_P_ROUTE_EXTRAS) + msg (D_PUSH, "OPTIONS IMPORT: route-related options modified"); if (found & OPT_P_IPWIN32) msg (D_PUSH, "OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified"); if (found & OPT_P_SETENV) @@ -1367,7 +1377,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) to.gremlin = c->options.gremlin; #endif - to.plugins = c->c1.plugins; + to.plugins = c->plugins; #if P2MP_SERVER to.auth_user_pass_verify_script = options->auth_user_pass_verify_script; @@ -1724,7 +1734,7 @@ do_init_socket_1 (struct context *c, int mode) c->options.inetd, &c->c1.link_socket_addr, c->options.ipchange, - c->c1.plugins, + c->plugins, c->options.resolve_retry_seconds, c->options.connect_retry_seconds, c->options.mtu_discover_type, @@ -2094,32 +2104,68 @@ do_signal_on_tls_errors (struct context *c) #endif } +#ifdef ENABLE_PLUGIN -static void -do_open_plugins (struct context *c) +void +open_plugins (struct context *c, const bool import_options) { -#ifdef ENABLE_PLUGIN - if (c->options.plugin_list && !c->c1.plugins) + if (c->options.plugin_list && !c->plugins) { - c->c1.plugins = plugin_list_open (c->options.plugin_list, c->c2.es); - c->c1.plugins_owned = true; + if (import_options) + { + struct plugin_return pr, config; + plugin_return_init (&pr); + c->plugins = plugin_list_open (c->options.plugin_list, &pr, c->c2.es); + c->plugins_owned = true; + plugin_return_get_column (&pr, &config, "config"); + if (plugin_return_defined (&config)) + { + int i; + for (i = 0; i < config.n; ++i) + { + unsigned int option_types_found = 0; + if (config.list[i] && config.list[i]->value) + options_plugin_import (&c->options, + config.list[i]->value, + D_IMPORT_ERRORS|M_OPTERR, + OPT_P_DEFAULT & ~OPT_P_PLUGIN, + &option_types_found, + c->es); + } + } + plugin_return_free (&pr); + } + else + { + c->plugins = plugin_list_open (c->options.plugin_list, NULL, c->c2.es); + c->plugins_owned = true; + } } -#endif } static void do_close_plugins (struct context *c) { -#ifdef ENABLE_PLUGIN - if (c->c1.plugins && c->c1.plugins_owned && !(c->sig->signal_received == SIGUSR1)) + if (c->plugins && c->plugins_owned && !(c->sig->signal_received == SIGUSR1)) { - plugin_list_close (c->c1.plugins); - c->c1.plugins = NULL; - c->c1.plugins_owned = false; + plugin_list_close (c->plugins); + c->plugins = NULL; + c->plugins_owned = false; } -#endif } +static void +do_inherit_plugins (struct context *c, const struct context *src) +{ + if (!c->plugins && src->plugins) + { + c->plugins = plugin_list_inherit (src->plugins); + c->plugins_owned = true; + } +} + +#endif + #ifdef ENABLE_MANAGEMENT static void @@ -2299,9 +2345,11 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int if (env) do_inherit_env (c, env); +#ifdef ENABLE_PLUGIN /* initialize plugins */ if (c->mode == CM_P2P || c->mode == CM_TOP) - do_open_plugins (c); + open_plugins (c, false); +#endif /* should we enable fast I/O? */ if (c->mode == CM_P2P || c->mode == CM_TOP) @@ -2467,8 +2515,10 @@ close_instance (struct context *c) /* close TUN/TAP device */ do_close_tun (c, false); +#ifdef ENABLE_PLUGIN /* call plugin close functions and unload */ do_close_plugins (c); +#endif /* close packet-id persistance file */ do_close_packet_id (c); @@ -2538,8 +2588,10 @@ inherit_context_child (struct context *dest, dest->c2.accept_from = src->c2.link_socket; } +#ifdef ENABLE_PLUGIN /* inherit plugins */ - dest->c1.plugins = src->c1.plugins; + do_inherit_plugins (dest, src); +#endif /* context init */ init_instance (dest, src->c2.es, CC_USR1_TO_HUP | CC_GC_FREE); @@ -2589,15 +2641,21 @@ inherit_context_top (struct context *dest, gc_detach (&dest->gc); gc_detach (&dest->c2.gc); + /* detach plugins */ + dest->plugins_owned = false; + #if defined(USE_CRYPTO) && defined(USE_SSL) dest->c2.tls_multi = NULL; #endif + /* detach c1 ownership */ dest->c1.tuntap_owned = false; dest->c1.status_output_owned = false; #if P2MP_SERVER dest->c1.ifconfig_pool_persist_owned = false; #endif + + /* detach c2 ownership */ dest->c2.event_set_owned = false; dest->c2.link_socket_owned = false; dest->c2.buffers_owned = false; @@ -2715,7 +2773,7 @@ do_test_crypto (const struct options *o) /* print version number */ msg (M_INFO, "%s", title_string); - context_clear (&c); + context_clear (&c); c.options = *o; options_detach (&c.options); c.first_time = true; @@ -77,7 +77,7 @@ void do_up (struct context *c, bool pulled_options, unsigned int option_types_found); -unsigned int pull_permission_mask (void); +unsigned int pull_permission_mask (const struct context *c); const char *format_common_name (struct context *c, struct gc_arena *gc); @@ -117,4 +117,8 @@ void management_show_net_callback (void *arg, const int msglevel); void init_management_callback_p2p (struct context *c); void uninit_management_callback (void); +#ifdef ENABLE_PLUGIN +void open_plugins (struct context *c, const bool import_options); +#endif + #endif diff --git a/install-win32/openvpn.nsi.in b/install-win32/openvpn.nsi.in index 076479b..5813420 100755 --- a/install-win32/openvpn.nsi.in +++ b/install-win32/openvpn.nsi.in @@ -10,7 +10,7 @@ !include "MUI.nsh" !include "setpath.nsi" -!define HOME "c:\src\openvpn" +!define HOME ".." !define BIN "${HOME}\bin" !define PRODUCT_NAME "OpenVPN" @@ -43,6 +43,14 @@ #include "memdbg.h" +#define MANAGEMENT_ECHO_PULL_INFO 0 // JYFIXME + +#if MANAGEMENT_ECHO_PULL_INFO +#define MANAGEMENT_ECHO_FLAGS LOG_PRINT_INTVAL +#else +#define MANAGEMENT_ECHO_FLAGS 0 +#endif + struct management *management; /* GLOBAL */ /* static forward declarations */ @@ -429,7 +437,7 @@ man_echo (struct management *man, const char *parm) "echo", man->persist.echo, &man->connection.echo_realtime, - LOG_PRINT_INT_DATE); + LOG_PRINT_INT_DATE|MANAGEMENT_ECHO_FLAGS); } static void @@ -1361,7 +1369,7 @@ management_set_state (struct management *man, } void -management_echo (struct management *man, const char *string) +management_echo (struct management *man, const char *string, const bool pull) { if (man->persist.echo) { @@ -1372,13 +1380,13 @@ management_echo (struct management *man, const char *string) update_time (); CLEAR (e); e.timestamp = now; - e.u.msg_flags = 0; e.string = string; - + e.u.intval = BOOL_CAST (pull); + log_history_add (man->persist.echo, &e); if (man->connection.echo_realtime) - out = log_entry_print (&e, LOG_PRINT_INT_DATE|LOG_PRINT_ECHO_PREFIX|LOG_PRINT_CRLF, &gc); + out = log_entry_print (&e, LOG_PRINT_INT_DATE|LOG_PRINT_ECHO_PREFIX|LOG_PRINT_CRLF|MANAGEMENT_ECHO_FLAGS, &gc); if (out) man_output_list_push (man, out); @@ -2028,6 +2036,8 @@ log_entry_print (const struct log_entry *e, unsigned int flags, struct gc_arena buf_printf (&out, "%s,", msg_flags_string (e->u.msg_flags, gc)); if (flags & LOG_PRINT_STATE) buf_printf (&out, "%s,", man_state_name (e->u.state)); + if (flags & LOG_PRINT_INTVAL) + buf_printf (&out, "%d,", e->u.intval); if (e->string) buf_printf (&out, "%s", e->string); if (flags & LOG_PRINT_LOCAL_IP) @@ -89,6 +89,7 @@ void output_list_advance (struct output_list *ol, int n); union log_entry_union { unsigned int msg_flags; int state; + int intval; }; struct log_entry @@ -111,6 +112,8 @@ struct log_entry #define LOG_PRINT_CRLF (1<<7) #define LOG_FATAL_NOTIFY (1<<8) +#define LOG_PRINT_INTVAL (1<<9) + const char *log_entry_print (const struct log_entry *e, unsigned int flags, struct gc_arena *gc); struct log_history @@ -327,7 +330,7 @@ void management_set_state (struct management *man, * The management object keeps track of OpenVPN --echo * parameters. */ -void management_echo (struct management *man, const char *string); +void management_echo (struct management *man, const char *string, const bool pull); /* * OpenVPN calls here to indicate a password failure @@ -206,7 +206,7 @@ run_up_down (const char *command, ifconfig_local, ifconfig_remote, context); - if (plugin_call (plugins, plugin_type, BSTR (&cmd), es)) + if (plugin_call (plugins, plugin_type, BSTR (&cmd), NULL, es)) msg (M_FATAL, "ERROR: up/down plugin call failed"); } @@ -64,6 +64,7 @@ learn_address_script (const struct multi_context *m, struct gc_arena gc = gc_new (); struct env_set *es; bool ret = true; + struct plugin_list *plugins; /* get environmental variable source */ if (mi && mi->context.c2.es) @@ -71,7 +72,13 @@ learn_address_script (const struct multi_context *m, else es = env_set_create (&gc); - if (plugin_defined (m->top.c1.plugins, OPENVPN_PLUGIN_LEARN_ADDRESS)) + /* get plugin source */ + if (mi) + plugins = mi->context.plugins; + else + plugins = m->top.plugins; + + if (plugin_defined (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS)) { struct buffer cmd = alloc_buf_gc (256, &gc); @@ -81,7 +88,7 @@ learn_address_script (const struct multi_context *m, if (mi) buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false)); - if (plugin_call (m->top.c1.plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), es)) + if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), NULL, es)) { msg (M_WARN, "WARNING: learn-address plugin call failed"); ret = false; @@ -278,7 +285,7 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa */ if (t->options.ifconfig_pool_defined) { - if (dev == DEV_TYPE_TAP || t->options.ifconfig_pool_linear) + if (dev == DEV_TYPE_TAP) { m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV, t->options.ifconfig_pool_start, @@ -287,10 +294,11 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa } else if (dev == DEV_TYPE_TUN) { - m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_30NET, - t->options.ifconfig_pool_start, - t->options.ifconfig_pool_end, - t->options.duplicate_cn); + m->ifconfig_pool = ifconfig_pool_init ( + (t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV, + t->options.ifconfig_pool_start, + t->options.ifconfig_pool_end, + t->options.duplicate_cn); } else { @@ -409,9 +417,9 @@ multi_client_disconnect_script (struct multi_context *m, { multi_client_disconnect_setenv (m, mi); - if (plugin_defined (m->top.c1.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT)) + if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT)) { - if (plugin_call (m->top.c1.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, mi->context.c2.es)) + if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es)) msg (M_WARN, "WARNING: client-disconnect plugin call failed"); } @@ -759,6 +767,31 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int { status_printf (so, "ERROR: bad status format version number"); } + +#ifdef PACKET_TRUNCATION_CHECK + { + status_printf (so, "HEADER,ERRORS,Common Name,TUN Read Trunc,TUN Write Trunc,Pre-encrypt Trunc,Post-decrypt Trunc"); + hash_iterator_init (m->hash, &hi, true); + while ((he = hash_iterator_next (&hi))) + { + struct gc_arena gc = gc_new (); + const struct multi_instance *mi = (struct multi_instance *) he->value; + + if (!mi->halt) + { + status_printf (so, "ERRORS,%s," counter_format "," counter_format "," counter_format "," counter_format, + tls_common_name (mi->context.c2.tls_multi, false), + m->top.c2.n_trunc_tun_read, + mi->context.c2.n_trunc_tun_write, + mi->context.c2.n_trunc_pre_encrypt, + mi->context.c2.n_trunc_post_decrypt); + } + gc_free (&gc); + } + hash_iterator_free (&hi); + } +#endif + status_flush (so); gc_free (&gc_top); } @@ -1034,6 +1067,20 @@ multi_delete_dup (struct multi_context *m, struct multi_instance *new_mi) } /* + * Ensure that endpoint to be pushed to client + * complies with --ifconfig-push-constraint directive. + */ +static bool +ifconfig_push_constraint_satisfied (const struct context *c) +{ + const struct options *o = &c->options; + if (o->push_ifconfig_constraint_defined && c->c2.push_ifconfig_defined) + return (o->push_ifconfig_constraint_netmask & c->c2.push_ifconfig_local) == o->push_ifconfig_constraint_network; + else + return true; +} + +/* * Select a virtual address for a new client instance. * Use an --ifconfig-push directive, if given (static IP). * Otherwise use an --ifconfig-pool address (dynamic IP). @@ -1072,27 +1119,30 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn); if (mi->vaddr_handle >= 0) { - /* use pool ifconfig address(es) */ + const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap); + const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap); + + /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */ mi->context.c2.push_ifconfig_local = remote; - if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN) - { - if (mi->context.options.ifconfig_pool_linear) - mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->local; - else - mi->context.c2.push_ifconfig_remote_netmask = local; - mi->context.c2.push_ifconfig_defined = true; - } - else if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TAP) + if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET)) { mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.ifconfig_pool_netmask; if (!mi->context.c2.push_ifconfig_remote_netmask) mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->remote_netmask; - if (mi->context.c2.push_ifconfig_remote_netmask) - mi->context.c2.push_ifconfig_defined = true; - else - msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s", - multi_instance_string (mi, false, &gc)); } + else if (tunnel_type == DEV_TYPE_TUN) + { + if (tunnel_topology == TOP_P2P) + mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->local; + else if (tunnel_topology == TOP_NET30) + mi->context.c2.push_ifconfig_remote_netmask = local; + } + + if (mi->context.c2.push_ifconfig_remote_netmask) + mi->context.c2.push_ifconfig_defined = true; + else + msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s", + multi_instance_string (mi, false, &gc)); } else { @@ -1114,22 +1164,25 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi) if (mi->context.c2.push_ifconfig_defined) { + const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap); + const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap); + setenv_in_addr_t (mi->context.c2.es, "ifconfig_pool_remote_ip", mi->context.c2.push_ifconfig_local, SA_SET_IF_NONZERO); - if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN) + if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET)) { setenv_in_addr_t (mi->context.c2.es, - "ifconfig_pool_local_ip", + "ifconfig_pool_netmask", mi->context.c2.push_ifconfig_remote_netmask, SA_SET_IF_NONZERO); } - else if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TAP) + else if (tunnel_type == DEV_TYPE_TUN) { setenv_in_addr_t (mi->context.c2.es, - "ifconfig_pool_netmask", + "ifconfig_pool_local_ip", mi->context.c2.push_ifconfig_remote_netmask, SA_SET_IF_NONZERO); } @@ -1137,7 +1190,7 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi) } /* - * Called after client-connect script or plug-in is called + * Called after client-connect script is called */ static void multi_client_connect_post (struct multi_context *m, @@ -1171,6 +1224,50 @@ multi_client_connect_post (struct multi_context *m, } } +#ifdef ENABLE_PLUGIN + +/* + * Called after client-connect plug-in is called + */ +static void +multi_client_connect_post_plugin (struct multi_context *m, + struct multi_instance *mi, + const struct plugin_return *pr, + unsigned int option_permissions_mask, + unsigned int *option_types_found) +{ + struct plugin_return config; + + plugin_return_get_column (pr, &config, "config"); + + /* Did script generate a dynamic config file? */ + if (plugin_return_defined (&config)) + { + int i; + for (i = 0; i < config.n; ++i) + { + if (config.list[i] && config.list[i]->value) + options_plugin_import (&mi->context.options, + config.list[i]->value, + D_IMPORT_ERRORS|M_OPTERR, + option_permissions_mask, + option_types_found, + mi->context.c2.es); + } + + /* + * If the --client-connect script generates a config file + * with an --ifconfig-push directive, it will override any + * --ifconfig-push directive from the --client-config-dir + * directory or any --ifconfig-pool dynamic address. + */ + multi_select_virtual_addr (m, mi); + multi_set_virtual_addr_env (m, mi); + } +} + +#endif + /* * Called as soon as the SSL/TLS connection authenticates. * @@ -1261,16 +1358,19 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi /* setenv client virtual IP address */ multi_set_virtual_addr_env (m, mi); +#ifdef ENABLE_PLUGIN /* * Call client-connect plug-in. */ - if (plugin_defined (m->top.c1.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT)) + + /* deprecated callback, use a file for passing back return info */ + if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT)) { const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, &gc); delete_file (dc_file); - if (plugin_call (m->top.c1.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, mi->context.c2.es)) + if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, NULL, mi->context.c2.es)) { msg (M_WARN, "WARNING: client-connect plugin call failed"); cc_succeeded = false; @@ -1282,6 +1382,28 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi } } + /* V2 callback, use a plugin_return struct for passing back return info */ + if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2)) + { + struct plugin_return pr; + + plugin_return_init (&pr); + + if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es)) + { + msg (M_WARN, "WARNING: client-connect-v2 plugin call failed"); + cc_succeeded = false; + } + else + { + multi_client_connect_post_plugin (m, mi, &pr, option_permissions_mask, &option_types_found); + ++cc_succeeded_count; + } + + plugin_return_free (&pr); + } +#endif + /* * Run --client-connect script. */ @@ -1336,6 +1458,19 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi } /* + * make sure that ifconfig settings comply with constraints + */ + if (!ifconfig_push_constraint_satisfied (&mi->context)) + { + /* JYFIXME -- this should cause the connection to fail */ + msg (D_MULTI_ERRORS, "MULTI ERROR: primary virtual IP for %s (%s) violates tunnel network/netmask constraint (%s/%s)", + multi_instance_string (mi, false, &gc), + print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc), + print_in_addr_t (mi->context.options.push_ifconfig_constraint_network, 0, &gc), + print_in_addr_t (mi->context.options.push_ifconfig_constraint_netmask, 0, &gc)); + } + + /* * For routed tunnels, set up internal route to endpoint * plus add all iroute routes. */ diff --git a/openvpn-plugin.h b/openvpn-plugin.h index fcd823e..62124e8 100644 --- a/openvpn-plugin.h +++ b/openvpn-plugin.h @@ -22,6 +22,8 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define OPENVPN_PLUGIN_VERSION 2 + /* * Plug-in types. These types correspond to the set of script callbacks * supported by OpenVPN. @@ -35,7 +37,8 @@ #define OPENVPN_PLUGIN_CLIENT_CONNECT 6 #define OPENVPN_PLUGIN_CLIENT_DISCONNECT 7 #define OPENVPN_PLUGIN_LEARN_ADDRESS 8 -#define OPENVPN_PLUGIN_N 9 +#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9 +#define OPENVPN_PLUGIN_N 10 /* * Build a mask out of a set of plug-in types. @@ -86,6 +89,20 @@ typedef void *openvpn_plugin_handle_t; #endif /* + * Used by openvpn_plugin_func to return structured + * data. The plugin should allocate all structure + * instances, name strings, and value strings with + * malloc, since OpenVPN will assume that it + * can free the list by calling free() over the same. + */ +struct openvpn_plugin_string_list +{ + struct openvpn_plugin_string_list *next; + char *name; + char *value; +}; + +/* * Multiple plugin modules can be cascaded, and modules can be * used in tandem with scripts. The order of operation is that * the module func() functions are called in the order that @@ -113,7 +130,7 @@ typedef void *openvpn_plugin_handle_t; */ /* - * FUNCTION: openvpn_plugin_open_v1 + * FUNCTION: openvpn_plugin_open_v2 * * REQUIRED: YES * @@ -141,15 +158,20 @@ typedef void *openvpn_plugin_handle_t; * these variables are not actually written to the "official" * environmental variable store of the process. * + * return_list : used to return data back to OpenVPN. + * * RETURN VALUE * * An openvpn_plugin_handle_t value on success, NULL on failure */ -OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v1) - (unsigned int *type_mask, const char *argv[], const char *envp[]); +OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v2) + (unsigned int *type_mask, + const char *argv[], + const char *envp[], + struct openvpn_plugin_string_list **return_list); /* - * FUNCTION: openvpn_plugin_func_v1 + * FUNCTION: openvpn_plugin_func_v2 * * Called to perform the work of a given script type. * @@ -158,7 +180,7 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op * ARGUMENTS * * handle : the openvpn_plugin_handle_t value which was returned by - * openvpn_plugin_open_v1. + * openvpn_plugin_open. * * type : one of the PLUGIN_x types * @@ -171,12 +193,22 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op * these variables are not actually written to the "official" * environmental variable store of the process. * + * per_client_context : the per-client context pointer which was returned by + * openvpn_plugin_client_constructor_v1, if defined. + * + * return_list : used to return data back to OpenVPN. + * * RETURN VALUE * * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure */ -OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v1) - (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]); +OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2) + (openvpn_plugin_handle_t handle, + const int type, + const char *argv[], + const char *envp[], + void *per_client_context, + struct openvpn_plugin_string_list **return_list); /* * FUNCTION: openvpn_plugin_close_v1 @@ -186,7 +218,7 @@ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v1) * ARGUMENTS * * handle : the openvpn_plugin_handle_t value which was returned by - * openvpn_plugin_open_v1. + * openvpn_plugin_open. * * Called immediately prior to plug-in unload. */ @@ -201,11 +233,86 @@ OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_close_v1) * ARGUMENTS * * handle : the openvpn_plugin_handle_t value which was returned by - * openvpn_plugin_open_v1. + * openvpn_plugin_open. * * Called when OpenVPN is in the process of aborting due to a fatal error. * Will only be called on an open context returned by a prior successful - * openvpn_plugin_open_v1 callback. + * openvpn_plugin_open callback. */ OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_abort_v1) (openvpn_plugin_handle_t handle); + +/* + * FUNCTION: openvpn_plugin_client_constructor_v1 + * + * Called to allocate a per-client memory region, which + * is then passed to the openvpn_plugin_func_v2 function. + * This function is called every time the OpenVPN server + * constructs a client instance object, which normally + * occurs when a session-initiating packet is received + * by a new client, even before the client has authenticated. + * + * This function should allocate the private memory needed + * by the plugin to track individual OpenVPN clients, and + * return a void * to this memory region. + * + * REQUIRED: NO + * + * ARGUMENTS + * + * handle : the openvpn_plugin_handle_t value which was returned by + * openvpn_plugin_open. + * + * RETURN VALUE + * + * void * pointer to plugin's private per-client memory region, or NULL + * if no memory region is required. + */ +OPENVPN_PLUGIN_DEF void * OPENVPN_PLUGIN_FUNC(openvpn_plugin_client_constructor_v1) + (openvpn_plugin_handle_t handle); + +/* + * FUNCTION: openvpn_plugin_client_destructor_v1 + * + * This function is called on client instance object destruction. + * + * REQUIRED: NO + * + * ARGUMENTS + * + * handle : the openvpn_plugin_handle_t value which was returned by + * openvpn_plugin_open. + * + * per_client_context : the per-client context pointer which was returned by + * openvpn_plugin_client_constructor_v1, if defined. + */ +OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_client_destructor_v1) + (openvpn_plugin_handle_t handle, void *per_client_context); + +/* + * FUNCTION: openvpn_plugin_min_version_required_v1 + * + * This function is called by OpenVPN to query the minimum + plugin interface version number required by the plugin. + * + * REQUIRED: NO + * + * RETURN VALUE + * + * The minimum OpenVPN plugin interface version number necessary to support + * this plugin. + */ +OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_min_version_required_v1) + (void); + +/* + * Deprecated functions which are still supported for backward compatibility. + */ + +OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v1) + (unsigned int *type_mask, + const char *argv[], + const char *envp[]); + +OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v1) + (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]); @@ -94,6 +94,7 @@ openvpn \- secure IP tunnel daemon. .in +4 .ti -4 .B openvpn +[\ \fB\-\-allow\-nonadmin\fR\ \fI[TAP\-adapter]\fR\ ] [\ \fB\-\-askpass\fR\ \fI[file]\fR\ ] [\ \fB\-\-auth\-nocache\fR\ ] [\ \fB\-\-auth\-retry\fR\ \fItype\fR\ ] @@ -209,7 +210,7 @@ openvpn \- secure IP tunnel daemon. [\ \fB\-\-push\-reset\fR\ ] [\ \fB\-\-push\fR\ \fI"option"\fR\ ] [\ \fB\-\-rcvbuf\fR\ \fIsize\fR\ ] -[\ \fB\-\-redirect\-gateway\fR\ \fI["local"]\ ["def1"]\fR\ ] +[\ \fB\-\-redirect\-gateway\fR\ \fIflags...\fR\ ] [\ \fB\-\-remap\-usr1\fR\ \fIsignal\fR\ ] [\ \fB\-\-remote\-random\fR\ ] [\ \fB\-\-remote\fR\ \fIhost\ [port]\fR\ ] @@ -261,6 +262,7 @@ openvpn \- secure IP tunnel daemon. [\ \fB\-\-tls\-timeout\fR\ \fIn\fR\ ] [\ \fB\-\-tls\-verify\fR\ \fIcmd\fR\ ] [\ \fB\-\-tmp\-dir\fR\ \fIdir\fR\ ] +[\ \fB\-\-topology\fR\ \fImode\fR\ ] [\ \fB\-\-tran\-window\fR\ \fIn\fR\ ] [\ \fB\-\-tun\-ipv6\fR\ ] [\ \fB\-\-tun\-mtu\-extra\fR\ \fIn\fR\ ] @@ -732,9 +734,9 @@ or tap devices on both ends. You cannot mix them, as they represent different underlying protocols. .B tun -devices encapsulate IPv4 while +devices encapsulate IPv4 or IPv6 while .B tap -devices encapsulate ethernet 802.3. +devices encapsulate Ethernet 802.3. .\"********************************************************* .TP .B --dev-type device-type @@ -752,6 +754,60 @@ or .B tap. .\"********************************************************* .TP +.B --topology mode +Configure virtual addressing topology when running in +.B --dev tun +mode. This directive has no meaning in +.B --dev tap +mode, which always uses a +.B subnet +topology. + +If you set this directive on the server, the +.B --server +and +.B --server-bridge +directives will automatically push your chosen topology setting to clients +as well. This directive can also be manually pushed to clients. Like the +.B --dev +directive, this directive must always be compatible between client and server. + +.B mode +can be one of: + +.B net30 -- +Use a point-to-point topology, by allocating one /30 subnet per client. +This is designed to allow point-to-point semantics when some +or all of the connecting clients might be Windows systems. This is the +default on OpenVPN 2.0. + +.B p2p -- +Use a point-to-point topology where the remote endpoint of the client's +tun interface always points to the local endpoint of the server's tun interface. +This mode allocates a single IP address per connecting client. +Only use +when none of the connecting clients are Windows systems. This mode +is functionally equivalent to the +.B --ifconfig-pool-linear +directive which is available in OpenVPN 2.0 and is now deprecated. + +.B subnet -- +Use a subnet rather than a point-to-point topology by +configuring the tun interface with a local IP address and subnet mask, +similar to the topology used in +.B --dev tap +and ethernet bridging mode. +This mode allocates a single IP address per connecting client and works on +Windows as well. Only available when server and clients are OpenVPN 2.1 or +higher, or OpenVPN 2.0.x which has been manually patched with the +.B --topology +directive code. When used on Windows, requires version 8.2 or higher +of the TAP-Win32 driver. When used on *nix, requires that the tun +driver supports an +.BR ifconfig (8) +command which sets a subnet instead of a remote endpoint IP address. +.\"********************************************************* +.TP .B --tun-ipv6 Build a tun link capable of forwarding IPv6 traffic. Should be used in conjunction with @@ -996,7 +1052,7 @@ Don't add or remove routes automatically. Instead pass routes to script using environmental variables. .\"********************************************************* .TP -.B --redirect-gateway ["local"] ["def1"] +.B --redirect-gateway flags... (Experimental) Automatically execute routing commands to cause all outgoing IP traffic to be redirected over the VPN. @@ -1025,6 +1081,9 @@ is specified). When the tunnel is torn down, all of the above steps are reversed so that the original default route is restored. +Option flags: + +.B local -- Add the .B local flag if both OpenVPN servers are directly connected via a common subnet, @@ -1034,13 +1093,24 @@ flag will cause step .B 1 above to be omitted. -Add the -.B def1 -flag to override +.B def1 -- +Use this flag to override the default gateway by using 0.0.0.0/1 and 128.0.0.0/1 rather than 0.0.0.0/0. This has the benefit of overriding but not wiping out the original default gateway. +.B bypass-dhcp -- +Add a direct route to the DHCP server (if it is non-local) which +bypasses the tunnel +(Available on Windows clients, may not be available +on non-Windows clients). + +.B bypass-dns -- +Add a direct route to the DNS server(s) (if they are non-local) which +bypasses the tunnel +(Available on Windows clients, may not be available +on non-Windows clients). + Using the def1 flag is highly recommended, and is currently planned to become the default by OpenVPN 2.1. .\"********************************************************* @@ -2153,17 +2223,18 @@ expands as follows: .sp mode server tls-server + push "topology [topology]" - if dev tun: + if dev tun AND (topology == net30 OR topology == p2p): ifconfig 10.8.0.1 10.8.0.2 ifconfig-pool 10.8.0.4 10.8.0.251 route 10.8.0.0 255.255.255.0 if client-to-client: push "route 10.8.0.0 255.255.255.0" - else + else if topology == net30: push "route 10.8.0.1" - if dev tap: + if dev tap OR (dev tun AND topology == subnet): ifconfig 10.8.0.1 255.255.255.0 ifconfig-pool 10.8.0.2 10.8.0.254 255.255.255.0 push "route-gateway 10.8.0.1" @@ -2346,6 +2417,10 @@ directive to allocate individual TUN interface addresses for clients rather than /30 subnets. NOTE: This option is incompatible with Windows clients. + +This option is deprecated, and should be replaced with +.B --topology p2p +which is functionally equivalent. .\"********************************************************* .TP .B --ifconfig-push local remote-netmask @@ -4136,6 +4211,21 @@ option. On non-Windows systems, the command provides similar functionality. .\"********************************************************* .TP +.B --allow-nonadmin [TAP-adapter] +(Standalone) +Set +.B TAP-adapter +to allow access from non-administrative accounts. If +.B TAP-adapter +is omitted, all TAP adapters on the system will be configured to allow +non-admin access. +The non-admin access setting will only persist for the length of time that +the TAP-Win32 device object and driver remain loaded, and will need +to be re-enabled after a reboot, or if the driver is unloaded +and reloaded. +This directive can only be used by an administrator. +.\"********************************************************* +.TP .B --show-valid-subnets (Standalone) Show valid subnets for @@ -146,6 +146,12 @@ main (int argc, char *argv[]) /* parse command line options, and read configuration file */ parse_argv (&c.options, argc, argv, M_USAGE, OPT_P_DEFAULT, NULL, c.es); +#ifdef ENABLE_PLUGIN + /* plugins may contribute options configuration */ + init_verb_mute (&c, IVM_LEVEL_1); + open_plugins (&c, true); +#endif + /* init verbosity and mute levels */ init_verb_mute (&c, IVM_LEVEL_1); @@ -156,10 +156,6 @@ struct context_1 struct socks_proxy_info *socks_proxy; #endif - /* shared object plugins */ - struct plugin_list *plugins; - bool plugins_owned; - #if P2MP #if P2MP_SERVER @@ -237,6 +233,12 @@ struct context_2 counter_type link_read_bytes; counter_type link_read_bytes_auth; counter_type link_write_bytes; +#ifdef PACKET_TRUNCATION_CHECK + counter_type n_trunc_tun_read; + counter_type n_trunc_tun_write; + counter_type n_trunc_pre_encrypt; + counter_type n_trunc_post_decrypt; +#endif /* * Timer objects for ping and inactivity @@ -447,6 +449,10 @@ struct context /* signal info */ struct signal_info *sig; + /* shared object plugins */ + struct plugin_list *plugins; + bool plugins_owned; + /* level 1 context is preserved for SIGUSR1 restarts, but initialized for SIGHUP restarts */ @@ -64,7 +64,7 @@ const char title_string[] = #endif #endif #ifdef USE_LZO - " [LZO]" + " [LZO" LZO_VERSION_NUM "]" #endif #if EPOLL " [EPOLL]" @@ -129,6 +129,7 @@ static const char usage_message[] = " does not begin with \"tun\" or \"tap\".\n" "--dev-node node : Explicitly set the device node rather than using\n" " /dev/net/tun, /dev/tun, /dev/tap, etc.\n" + "--topology t : Set --dev tun topology: 'net30', 'p2p', or 'subnet'.\n" "--tun-ipv6 : Build tun link capable of forwarding IPv6 traffic.\n" "--ifconfig l rn : TUN: configure device to use IP address l as a local\n" " endpoint and rn as a remote endpoint. l & rn should be\n" @@ -159,7 +160,9 @@ static const char usage_message[] = " VPN. Add 'local' flag if both " PACKAGE_NAME " servers are directly\n" " connected via a common subnet, such as with WiFi.\n" " Add 'def1' flag to set default route using using 0.0.0.0/1\n" - " and 128.0.0.0/1 rather than 0.0.0.0/0.\n" + " and 128.0.0.0/1 rather than 0.0.0.0/0. Add 'bypass-dhcp'\n" + " flag to add a direct route to DHCP server, bypassing tunnel.\n" + " Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n" "--setenv name value : Set a custom environmental variable to pass to script.\n" "--shaper n : Restrict output to peer to n bytes per second.\n" "--keepalive n m : Helper option for setting timeouts in server mode. Send\n" @@ -496,6 +499,8 @@ static const char usage_message[] = "--show-adapters : Show all TAP-Win32 adapters.\n" "--show-net : Show " PACKAGE_NAME "'s view of routing table and net adapter list.\n" "--show-valid-subnets : Show valid subnets for --dev tun emulation.\n" + "--allow-nonadmin [TAP-adapter] : Allow " PACKAGE_NAME " running without admin privileges\n" + " to access TAP adapter.\n" #endif "\n" "Generate a random key (only for non-TLS static key encryption mode):\n" @@ -526,6 +531,7 @@ init_options (struct options *o) CLEAR (*o); gc_init (&o->gc); o->mode = MODE_POINT_TO_POINT; + o->topology = TOP_NET30; o->proto = PROTO_UDPv4; o->connect_retry_seconds = 5; o->local_port = o->remote_port = OPENVPN_PORT; @@ -793,7 +799,6 @@ show_p2mp_parms (const struct options *o) msg (D_SHOW_PARMS, " ifconfig_pool_netmask = %s", print_in_addr_t (o->ifconfig_pool_netmask, 0, &gc)); SHOW_STR (ifconfig_pool_persist_filename); SHOW_INT (ifconfig_pool_persist_refresh_freq); - SHOW_BOOL (ifconfig_pool_linear); SHOW_INT (n_bcast_buf); SHOW_INT (tcp_queue_limit); SHOW_INT (real_hash_size); @@ -959,6 +964,7 @@ show_settings (const struct options *o) SHOW_STR (dev); SHOW_STR (dev_type); SHOW_STR (dev_node); + SHOW_INT (topology); SHOW_BOOL (tun_ipv6); SHOW_STR (ifconfig_local); SHOW_STR (ifconfig_remote_netmask); @@ -1066,6 +1072,7 @@ show_settings (const struct options *o) SHOW_INT (route_delay); SHOW_INT (route_delay_window); SHOW_BOOL (route_delay_defined); + SHOW_BOOL (route_nopull); if (o->routes) print_route_options (o->routes, D_SHOW_PARMS); @@ -1463,13 +1470,13 @@ options_postprocess (struct options *options, bool first_time) msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"); if (options->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per)) msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead."); - if (dev != DEV_TYPE_TAP && options->ifconfig_pool_netmask) + if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask) msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode"); #ifdef ENABLE_OCC if (options->explicit_exit_notification) msg (M_USAGE, "--explicit-exit-notify cannot be used with --mode server"); #endif - if (options->routes && options->routes->redirect_default_gateway) + if (options->routes && (options->routes->flags & RG_ENABLE)) msg (M_USAGE, "--redirect-gateway cannot be used with --mode server (however --push \"redirect-gateway\" is fine)"); if (options->route_delay_defined) msg (M_USAGE, "--route-delay cannot be used with --mode server"); @@ -1525,8 +1532,6 @@ options_postprocess (struct options *options, bool first_time) msg (M_USAGE, "--username-as-common-name requires --mode server"); if (options->auth_user_pass_verify_script) msg (M_USAGE, "--auth-user-pass-verify requires --mode server"); - if (options->ifconfig_pool_linear) - msg (M_USAGE, "--ifconfig-pool-linear requires --mode server"); } #endif /* P2MP_SERVER */ @@ -1804,6 +1809,7 @@ options_string (const struct options *o, { tt = init_tun (o->dev, o->dev_type, + o->topology, o->ifconfig_local, o->ifconfig_remote_netmask, (in_addr_t)0, @@ -2117,6 +2123,44 @@ foreign_option (struct options *o, char *argv[], int len, struct env_set *es) } } +/* + * parse/print topology coding + */ + +int +parse_topology (const char *str, const int msglevel) +{ + if (streq (str, "net30")) + return TOP_NET30; + else if (streq (str, "p2p")) + return TOP_P2P; + else if (streq (str, "subnet")) + return TOP_SUBNET; + else + { + msg (msglevel, "--topology must be net30, p2p, or subnet"); + return TOP_UNDEF; + } +} + +const char * +print_topology (const int topology) +{ + switch (topology) + { + case TOP_UNDEF: + return "undef"; + case TOP_NET30: + return "net30"; + case TOP_P2P: + return "p2p"; + case TOP_SUBNET: + return "subnet"; + default: + return "unknown"; + } +} + #if P2MP /* @@ -2405,6 +2449,13 @@ parse_line (const char *line, return ret; } +static void +bypass_doubledash (char **p) +{ + if (strlen (*p) >= 3 && !strncmp (*p, "--", 2)) + *p += 2; +} + static int add_option (struct options *options, int i, @@ -2432,6 +2483,7 @@ read_config_file (struct options *options, FILE *fp; int line_num; char line[OPTION_LINE_SIZE]; + char *p[MAX_PARMS]; ++level; if (level <= max_recursive_levels) @@ -2442,13 +2494,11 @@ read_config_file (struct options *options, line_num = 0; while (fgets(line, sizeof (line), fp)) { - char *p[MAX_PARMS]; CLEAR (p); ++line_num; if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc)) { - if (strlen (p[0]) >= 3 && !strncmp (p[0], "--", 2)) - p[0] += 2; + bypass_doubledash (&p[0]); add_option (options, 0, p, file, line_num, level, msglevel, permission_mask, option_types_found, es); } } @@ -2465,6 +2515,34 @@ read_config_file (struct options *options, } } +static void +read_config_string (struct options *options, + const char *config, + const int msglevel, + const unsigned int permission_mask, + unsigned int *option_types_found, + struct env_set *es) +{ + const char *file = "[CONFIG-STRING]"; + char line[OPTION_LINE_SIZE]; + struct buffer multiline; + int line_num = 0; + + buf_set_read (&multiline, (uint8_t*)config, strlen (config)); + + while (buf_parse (&multiline, '\n', line, sizeof (line))) + { + char *p[MAX_PARMS]; + CLEAR (p); + ++line_num; + if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc)) + { + bypass_doubledash (&p[0]); + add_option (options, 0, p, NULL, line_num, 0, msglevel, permission_mask, option_types_found, es); + } + } +} + void parse_argv (struct options *options, const int argc, @@ -2565,6 +2643,20 @@ options_server_import (struct options *o, es); } +#ifdef ENABLE_PLUGIN + +void options_plugin_import (struct options *options, + const char *config, + const int msglevel, + const unsigned int permission_mask, + unsigned int *option_types_found, + struct env_set *es) +{ + read_config_string (options, config, msglevel, permission_mask, option_types_found, es); +} + +#endif + #if P2MP #define VERIFY_PERMISSION(mask) { if (!verify_permission(p[0], (mask), permission_mask, option_types_found, msglevel)) goto err; } @@ -2682,10 +2774,12 @@ add_option (struct options *options, read_config_file (options, p[1], level, file, line, msglevel, permission_mask, option_types_found, es); } - else if (streq (p[0], "echo")) + else if (streq (p[0], "echo") || streq (p[0], "parameter")) { struct buffer string = alloc_buf_gc (OPTION_PARM_SIZE, &gc); int j; + const bool pull_mode = BOOL_CAST (permission_mask & OPT_P_PULL_MODE); + VERIFY_PERMISSION (OPT_P_ECHO); for (j = 1; j < MAX_PARMS; ++j) @@ -2697,10 +2791,12 @@ add_option (struct options *options, buf_printf (&string, " "); buf_printf (&string, "%s", p[j]); } - msg (M_INFO, "ECHO:%s", BSTR (&string)); + msg (M_INFO, "%s:%s", + pull_mode ? "ECHO-PULL" : "ECHO", + BSTR (&string)); #ifdef ENABLE_MANAGEMENT if (management) - management_echo (management, BSTR (&string)); + management_echo (management, BSTR (&string), pull_mode); #endif } #ifdef ENABLE_MANAGEMENT @@ -2754,7 +2850,7 @@ add_option (struct options *options, else if (streq (p[0], "plugin") && p[1]) { ++i; - VERIFY_PERMISSION (OPT_P_SCRIPT); + VERIFY_PERMISSION (OPT_P_PLUGIN); if (p[2]) ++i; if (!no_more_than_n_args (msglevel, p, 3, NM_QUOTE_HINT)) @@ -2802,6 +2898,12 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_GENERAL); options->dev_node = p[1]; } + else if (streq (p[0], "topology") && p[1]) + { + ++i; + VERIFY_PERMISSION (OPT_P_UP); + options->topology = parse_topology (p[1], msglevel); + } else if (streq (p[0], "tun-ipv6")) { VERIFY_PERMISSION (OPT_P_UP); @@ -3502,12 +3604,12 @@ add_option (struct options *options, else if (streq (p[0], "route-gateway") && p[1]) { ++i; - VERIFY_PERMISSION (OPT_P_ROUTE); + VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); options->route_default_gateway = p[1]; } else if (streq (p[0], "route-delay")) { - VERIFY_PERMISSION (OPT_P_ROUTE); + VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); options->route_delay_defined = true; if (p[1]) { @@ -3537,6 +3639,11 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_SCRIPT); options->route_noexec = true; } + else if (streq (p[0], "route-nopull")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->route_nopull = true; + } else if (streq (p[0], "redirect-gateway")) { int j; @@ -3546,16 +3653,20 @@ add_option (struct options *options, { ++i; if (streq (p[j], "local")) - options->routes->redirect_local = true; + options->routes->flags |= RG_LOCAL; else if (streq (p[j], "def1")) - options->routes->redirect_def1 = true; + options->routes->flags |= RG_DEF1; + else if (streq (p[j], "bypass-dhcp")) + options->routes->flags |= RG_BYPASS_DHCP; + else if (streq (p[j], "bypass-dns")) + options->routes->flags |= RG_BYPASS_DNS; else { msg (msglevel, "unknown --redirect-gateway flag: %s", p[j]); goto err; } } - options->routes->redirect_default_gateway = true; + options->routes->flags |= RG_ENABLE; } else if (streq (p[0], "setenv") && p[1] && p[2]) { @@ -3602,6 +3713,18 @@ add_option (struct options *options, options->server_defined = true; options->server_network = network; options->server_netmask = netmask; + + if (p[3]) + { + ++i; + if (streq (p[3], "nopool")) + options->server_flags |= SF_NOPOOL; + else + { + msg (msglevel, "error parsing --server: %s is not a recognized flag", p[3]); + goto err; + } + } } else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4]) { @@ -3690,7 +3813,7 @@ add_option (struct options *options, else if (streq (p[0], "ifconfig-pool-linear")) { VERIFY_PERMISSION (OPT_P_GENERAL); - options->ifconfig_pool_linear = true; + options->topology = TOP_P2P; } else if (streq (p[0], "hash-size") && p[1] && p[2]) { @@ -3886,6 +4009,26 @@ add_option (struct options *options, goto err; } } + else if (streq (p[0], "ifconfig-push-constraint") && p[1] && p[2]) + { + in_addr_t network, netmask; + + i += 2; + VERIFY_PERMISSION (OPT_P_GENERAL); + network = getaddr (GETADDR_HOST_ORDER|GETADDR_RESOLVE, p[1], 0, NULL, NULL); + netmask = getaddr (GETADDR_HOST_ORDER, p[2], 0, NULL, NULL); + if (network && netmask) + { + options->push_ifconfig_constraint_defined = true; + options->push_ifconfig_constraint_network = network; + options->push_ifconfig_constraint_netmask = netmask; + } + else + { + msg (msglevel, "cannot parse --ifconfig-push-constraint addresses"); + goto err; + } + } else if (streq (p[0], "disable")) { VERIFY_PERMISSION (OPT_P_INSTANCE); @@ -3925,7 +4068,7 @@ add_option (struct options *options, else if (streq (p[0], "route-method") && p[1]) { ++i; - VERIFY_PERMISSION (OPT_P_ROUTE); + VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); if (streq (p[1], "ipapi")) options->route_method = ROUTE_METHOD_IPAPI; else if (streq (p[1], "exe")) @@ -4117,6 +4260,14 @@ add_option (struct options *options, options->exit_event_initial_state = (atoi(p[2]) != 0); } } + else if (streq (p[0], "allow-nonadmin")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + if (p[1]) + ++i; + tap_allow_nonadmin_access (p[1]); + openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ + } else if (streq (p[0], "user") && p[1]) { ++i; @@ -4153,7 +4304,7 @@ add_option (struct options *options, else if (streq (p[0], "route-method") && p[1]) /* ignore when pushed to non-Windows OS */ { ++i; - VERIFY_PERMISSION (OPT_P_ROUTE); + VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); } #endif #if PASSTOS_CAPABILITY @@ -123,6 +123,7 @@ struct options const char *dev; const char *dev_type; const char *dev_node; + int topology; /* one of the TOP_x values from proto.h */ const char *ifconfig_local; const char *ifconfig_remote_netmask; bool ifconfig_noexec; @@ -239,6 +240,7 @@ struct options int route_delay_window; bool route_delay_defined; struct route_option_list *routes; + bool route_nopull; #ifdef ENABLE_HTTP_PROXY struct http_proxy_options *http_proxy_options; @@ -283,6 +285,9 @@ struct options in_addr_t server_network; in_addr_t server_netmask; +# define SF_NOPOOL (1<<0) + unsigned int server_flags; + bool server_bridge_defined; in_addr_t server_bridge_ip; in_addr_t server_bridge_netmask; @@ -296,7 +301,6 @@ struct options in_addr_t ifconfig_pool_netmask; const char *ifconfig_pool_persist_filename; int ifconfig_pool_persist_refresh_freq; - bool ifconfig_pool_linear; int real_hash_size; int virtual_hash_size; const char *client_connect_script; @@ -312,6 +316,9 @@ struct options bool push_ifconfig_defined; in_addr_t push_ifconfig_local; in_addr_t push_ifconfig_remote_netmask; + bool push_ifconfig_constraint_defined; + in_addr_t push_ifconfig_constraint_network; + in_addr_t push_ifconfig_constraint_netmask; bool enable_c2c; bool duplicate_cn; int cf_max; @@ -437,8 +444,11 @@ struct options #define OPT_P_EXPLICIT_NOTIFY (1<<19) #define OPT_P_ECHO (1<<20) #define OPT_P_INHERIT (1<<21) +#define OPT_P_ROUTE_EXTRAS (1<<22) +#define OPT_P_PULL_MODE (1<<23) +#define OPT_P_PLUGIN (1<<24) -#define OPT_P_DEFAULT (~OPT_P_INSTANCE) +#define OPT_P_DEFAULT (~(OPT_P_INSTANCE|OPT_P_PULL_MODE)) #if P2MP #define PULL_DEFINED(opt) ((opt)->pull) @@ -546,6 +556,13 @@ int parse_line (const char *line, struct gc_arena *gc); /* + * parse/print topology coding + */ + +int parse_topology (const char *str, const int msglevel); +const char *print_topology (const int topology); + +/* * Manage auth-retry variable */ @@ -561,4 +578,15 @@ const char *auth_retry_print (void); #endif +#ifdef ENABLE_PLUGIN + +void options_plugin_import (struct options *options, + const char *config, + const int msglevel, + const unsigned int permission_mask, + unsigned int *option_types_found, + struct env_set *es); + +#endif + #endif @@ -60,7 +60,7 @@ static inline void update_time (void) { const time_t real_time = time (NULL); - if (real_time != now) + if (real_time > now) now = real_time; } @@ -42,7 +42,7 @@ #define PLUGIN_SYMBOL_REQUIRED (1<<0) /* used only for program aborts */ -static struct plugin_list *static_plugin_list = NULL; /* GLOBAL */ +static struct plugin_common *static_plugin_common = NULL; /* GLOBAL */ static void plugin_show_string_array (int msglevel, const char *name, const char *array[]) @@ -81,6 +81,8 @@ plugin_type_name (const int type) return "PLUGIN_AUTH_USER_PASS_VERIFY"; case OPENVPN_PLUGIN_CLIENT_CONNECT: return "PLUGIN_CLIENT_CONNECT"; + case OPENVPN_PLUGIN_CLIENT_CONNECT_V2: + return "PLUGIN_CLIENT_CONNECT"; case OPENVPN_PLUGIN_CLIENT_DISCONNECT: return "PLUGIN_CLIENT_DISCONNECT"; case OPENVPN_PLUGIN_LEARN_ADDRESS: @@ -174,7 +176,10 @@ dll_resolve_symbol (HMODULE module, void **dest, const char *symbol, const char #endif static void -plugin_init_item (struct plugin *p, const struct plugin_option *o, const char **envp) +plugin_init_item (struct plugin *p, + const struct plugin_option *o, + struct openvpn_plugin_string_list **retlist, + const char **envp) { struct gc_arena gc = gc_new (); const char **argv = make_arg_array (o->so_pathname, o->args, &gc); @@ -182,35 +187,73 @@ plugin_init_item (struct plugin *p, const struct plugin_option *o, const char ** p->plugin_type_mask = plugin_supported_types (); #if defined(USE_LIBDL) + p->handle = dlopen (p->so_pathname, RTLD_NOW); if (!p->handle) msg (M_ERR, "PLUGIN_INIT: could not load plugin shared object %s: %s", p->so_pathname, dlerror()); - libdl_resolve_symbol (p->handle, (void*)&p->open, "openvpn_plugin_open_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED); - libdl_resolve_symbol (p->handle, (void*)&p->func, "openvpn_plugin_func_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED); - libdl_resolve_symbol (p->handle, (void*)&p->close, "openvpn_plugin_close_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED); - libdl_resolve_symbol (p->handle, (void*)&p->abort, "openvpn_plugin_abort_v1", p->so_pathname, 0); + +# define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol (p->handle, (void*)&p->var, name, p->so_pathname, flags) + #elif defined(USE_LOAD_LIBRARY) + p->module = LoadLibrary (p->so_pathname); if (!p->module) msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname); - dll_resolve_symbol (p->module, (void*)&p->open, "openvpn_plugin_open_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED); - dll_resolve_symbol (p->module, (void*)&p->func, "openvpn_plugin_func_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED); - dll_resolve_symbol (p->module, (void*)&p->close, "openvpn_plugin_close_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED); - dll_resolve_symbol (p->module, (void*)&p->abort, "openvpn_plugin_abort_v1", p->so_pathname, 0); + +# define PLUGIN_SYM(var, name, flags) dll_resolve_symbol (p->module, (void*)&p->var, name, p->so_pathname, flags) + #endif + PLUGIN_SYM (open1, "openvpn_plugin_open_v1", 0); + PLUGIN_SYM (open2, "openvpn_plugin_open_v2", 0); + PLUGIN_SYM (func1, "openvpn_plugin_func_v1", 0); + PLUGIN_SYM (func2, "openvpn_plugin_func_v2", 0); + PLUGIN_SYM (close, "openvpn_plugin_close_v1", PLUGIN_SYMBOL_REQUIRED); + PLUGIN_SYM (abort, "openvpn_plugin_abort_v1", 0); + PLUGIN_SYM (min_version_required, "openvpn_plugin_min_version_required_v1", 0); + PLUGIN_SYM (client_constructor, "openvpn_plugin_client_constructor_v1", 0); + PLUGIN_SYM (client_destructor, "openvpn_plugin_client_destructor_v1", 0); + + if (!p->open1 && !p->open2) + msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_open_vX is undefined in plugin: %s", p->so_pathname); + + if (!p->func1 && !p->func2) + msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_func_vX is undefined in plugin: %s", p->so_pathname); + dmsg (D_PLUGIN_DEBUG, "PLUGIN_INIT: PRE"); plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp); /* + * Verify that we are sufficiently up-to-date to handle the plugin + */ + if (p->min_version_required) + { + const int plugin_needs_version = (*p->min_version_required)(); + if (plugin_needs_version > OPENVPN_PLUGIN_VERSION) + msg (M_FATAL, "PLUGIN_INIT: plugin needs interface version %d, but this version of OpenVPN only supports version %d: %s", + plugin_needs_version, + OPENVPN_PLUGIN_VERSION, + p->so_pathname); + } + + if (retlist) + *retlist = NULL; + + /* * Call the plugin initialization */ - p->plugin_handle = (*p->open)(&p->plugin_type_mask, argv, envp); + if (p->open2) + p->plugin_handle = (*p->open2)(&p->plugin_type_mask, argv, envp, retlist); + else if (p->open1) + p->plugin_handle = (*p->open1)(&p->plugin_type_mask, argv, envp); + else + ASSERT (0); - msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s", + msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s %s", p->so_pathname, o->args ? o->args : "[NULL]", - plugin_mask_string (p->plugin_type_mask, &gc)); + plugin_mask_string (p->plugin_type_mask, &gc), + (retlist && *retlist) ? "[RETLIST]" : ""); if ((p->plugin_type_mask | plugin_supported_types()) != plugin_supported_types()) msg (M_FATAL, "PLUGIN_INIT: plugin %s expressed interest in unsupported plugin types: [want=0x%08x, have=0x%08x]", @@ -226,7 +269,12 @@ plugin_init_item (struct plugin *p, const struct plugin_option *o, const char ** } static int -plugin_call_item (const struct plugin *p, const int type, const char *args, const char **envp) +plugin_call_item (const struct plugin *p, + void *per_client_context, + const int type, + const char *args, + struct openvpn_plugin_string_list **retlist, + const char **envp) { int status = OPENVPN_PLUGIN_FUNC_SUCCESS; @@ -238,10 +286,18 @@ plugin_call_item (const struct plugin *p, const int type, const char *args, cons dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type)); plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp); + if (retlist) + *retlist = NULL; + /* * Call the plugin work function */ - status = (*p->func)(p->plugin_handle, type, argv, envp); + if (p->func2) + status = (*p->func2)(p->plugin_handle, type, argv, envp, per_client_context, retlist); + else if (p->func1) + status = (*p->func1)(p->plugin_handle, type, argv, envp); + else + ASSERT (0); msg (D_PLUGIN, "PLUGIN_CALL: POST %s/%s status=%d", p->so_pathname, @@ -288,69 +344,179 @@ plugin_abort_item (const struct plugin *p) (*p->abort)(p->plugin_handle); } +static void +plugin_per_client_init (const struct plugin_common *pc, struct plugin_per_client *cli) +{ + const int n = pc->n; + int i; + + CLEAR (*cli); + for (i = 0; i < n; ++i) + { + const struct plugin *p = &pc->plugins[i]; + + if (p->client_constructor) + cli->per_client_context[i] = (*p->client_constructor)(p->plugin_handle); + } + cli->initialized = true; +} + +static void +plugin_per_client_destroy (const struct plugin_common *pc, struct plugin_per_client *cli) +{ + if (cli->initialized) + { + const int n = pc->n; + int i; + + for (i = 0; i < n; ++i) + { + const struct plugin *p = &pc->plugins[i]; + void *cc = cli->per_client_context[i]; + + if (p->client_destructor && cc) + (*p->client_destructor)(p->plugin_handle, cc); + } + CLEAR (*cli); + } +} + struct plugin_list * -plugin_list_open (const struct plugin_option_list *list, const struct env_set *es) +plugin_list_inherit (const struct plugin_list *src) +{ + struct plugin_list *pl; + ALLOC_OBJ_CLEAR (pl, struct plugin_list); + pl->common = src->common; + ASSERT (pl->common); + plugin_per_client_init (pl->common, &pl->per_client); + return pl; +} + +static struct plugin_common * +plugin_common_open (const struct plugin_option_list *list, + struct plugin_return *pr, + const struct env_set *es) { struct gc_arena gc = gc_new (); int i; - struct plugin_list *pl; + struct plugin_common *pc; const char **envp; - ALLOC_OBJ_CLEAR (pl, struct plugin_list); - static_plugin_list = pl; + ALLOC_OBJ_CLEAR (pc, struct plugin_common); envp = make_env_array (es, &gc); + if (pr) + plugin_return_init (pr); + for (i = 0; i < list->n; ++i) { - plugin_init_item (&pl->plugins[i], &list->plugins[i], envp); - pl->n = i + 1; + plugin_init_item (&pc->plugins[i], + &list->plugins[i], + pr ? &pr->list[i] : NULL, + envp); + pc->n = i + 1; } + if (pr) + pr->n = i; + gc_free (&gc); + static_plugin_common = pc; + return pc; +} + +static void +plugin_common_close (struct plugin_common *pc) +{ + static_plugin_common = NULL; + if (pc) + { + int i; + + for (i = 0; i < pc->n; ++i) + plugin_close_item (&pc->plugins[i]); + free (pc); + } +} + +struct plugin_list * +plugin_list_open (const struct plugin_option_list *list, + struct plugin_return *pr, + const struct env_set *es) +{ + struct plugin_list *pl; + ALLOC_OBJ_CLEAR (pl, struct plugin_list); + pl->common = plugin_common_open (list, pr, es); + pl->common_owned = true; + plugin_per_client_init (pl->common, &pl->per_client); return pl; } int -plugin_call (const struct plugin_list *pl, const int type, const char *args, struct env_set *es) +plugin_call (const struct plugin_list *pl, + const int type, + const char *args, + struct plugin_return *pr, + struct env_set *es) { - int count = 0; + if (pr) + plugin_return_init (pr); if (plugin_defined (pl, type)) { struct gc_arena gc = gc_new (); int i; const char **envp; + const int n = plugin_n (pl); + int count = 0; mutex_lock_static (L_PLUGIN); setenv_del (es, "script_type"); envp = make_env_array (es, &gc); - for (i = 0; i < pl->n; ++i) + for (i = 0; i < n; ++i) { - if (!plugin_call_item (&pl->plugins[i], type, args, envp)) + if (!plugin_call_item (&pl->common->plugins[i], + pl->per_client.initialized + ? pl->per_client.per_client_context[i] : NULL, + type, + args, + pr ? &pr->list[i] : NULL, + envp)) ++count; } + if (pr) + pr->n = i; + mutex_unlock_static (L_PLUGIN); gc_free (&gc); - } - return count == pl->n ? 0 : 1; /* if any one plugin in the chain failed, return failure (1) */ + return count == n ? 0 : 1; /* if any one plugin in the chain failed, return failure (1) */ + } + else + { + return 0; + } } void plugin_list_close (struct plugin_list *pl) { - static_plugin_list = NULL; if (pl) { - int i; - for (i = 0; i < pl->n; ++i) - plugin_close_item (&pl->plugins[i]); + if (pl->common) + { + plugin_per_client_destroy (pl->common, &pl->per_client); + + if (pl->common_owned) + plugin_common_close (pl->common); + } + free (pl); } } @@ -358,14 +524,14 @@ plugin_list_close (struct plugin_list *pl) void plugin_abort (void) { - struct plugin_list *pl = static_plugin_list; - static_plugin_list = NULL; - if (pl) + struct plugin_common *pc = static_plugin_common; + static_plugin_common = NULL; + if (pc) { int i; - for (i = 0; i < pl->n; ++i) - plugin_abort_item (&pl->plugins[i]); + for (i = 0; i < pc->n; ++i) + plugin_abort_item (&pc->plugins[i]); } } @@ -373,22 +539,113 @@ bool plugin_defined (const struct plugin_list *pl, const int type) { bool ret = false; + if (pl) { - int i; - const unsigned int mask = OPENVPN_PLUGIN_MASK (type); - for (i = 0; i < pl->n; ++i) + const struct plugin_common *pc = pl->common; + + if (pc) { - if (pl->plugins[i].plugin_type_mask & mask) + int i; + const unsigned int mask = OPENVPN_PLUGIN_MASK (type); + for (i = 0; i < pc->n; ++i) { - ret = true; - break; + if (pc->plugins[i].plugin_type_mask & mask) + { + ret = true; + break; + } } } } return ret; } +/* + * Plugin return functions + */ + +static void +openvpn_plugin_string_list_item_free (struct openvpn_plugin_string_list *l) +{ + if (l) + { + free (l->name); + free (l->value); + free (l); + } +} + +static void +openvpn_plugin_string_list_free (struct openvpn_plugin_string_list *l) +{ + struct openvpn_plugin_string_list *next; + while (l) + { + next = l->next; + openvpn_plugin_string_list_item_free (l); + l = next; + } +} + +static struct openvpn_plugin_string_list * +openvpn_plugin_string_list_find (struct openvpn_plugin_string_list *l, const char *name) +{ + while (l) + { + if (!strcmp (l->name, name)) + return l; + l = l->next; + } + return NULL; +} + +void +plugin_return_get_column (const struct plugin_return *src, + struct plugin_return *dest, + const char *colname) +{ + int i; + + dest->n = 0; + for (i = 0; i < src->n; ++i) + dest->list[i] = openvpn_plugin_string_list_find (src->list[i], colname); + dest->n = i; +} + +void +plugin_return_free (struct plugin_return *pr) +{ + int i; + for (i = 0; i < pr->n; ++i) + openvpn_plugin_string_list_free (pr->list[i]); + pr->n = 0; +} + +#ifdef ENABLE_DEBUG +void +plugin_return_print (const int msglevel, const char *prefix, const struct plugin_return *pr) +{ + int i; + msg (msglevel, "PLUGIN_RETURN_PRINT %s", prefix); + for (i = 0; i < pr->n; ++i) + { + struct openvpn_plugin_string_list *l = pr->list[i]; + int count = 0; + + msg (msglevel, "PLUGIN #%d (%s)", i, prefix); + while (l) + { + msg (msglevel, "[%d] '%s' -> '%s'\n", + ++count, + l->name, + l->value); + l = l->next; + } + } +} +#endif + #else static void dummy(void) {} #endif /* ENABLE_PLUGIN */ @@ -35,7 +35,7 @@ #include "misc.h" -#define MAX_PLUGINS 32 +#define MAX_PLUGINS 16 struct plugin_option { const char *so_pathname; @@ -50,24 +50,51 @@ struct plugin_option_list { struct plugin { const char *so_pathname; unsigned int plugin_type_mask; + #if defined(USE_LIBDL) void *handle; #elif defined(USE_LOAD_LIBRARY) HMODULE module; #endif - openvpn_plugin_open_v1 open; - openvpn_plugin_func_v1 func; + + openvpn_plugin_open_v1 open1; + openvpn_plugin_open_v2 open2; + openvpn_plugin_func_v1 func1; + openvpn_plugin_func_v2 func2; openvpn_plugin_close_v1 close; openvpn_plugin_abort_v1 abort; + openvpn_plugin_min_version_required_v1 min_version_required; + openvpn_plugin_client_constructor_v1 client_constructor; + openvpn_plugin_client_destructor_v1 client_destructor; openvpn_plugin_handle_t plugin_handle; }; -struct plugin_list { +struct plugin_per_client +{ + bool initialized; + void *per_client_context[MAX_PLUGINS]; +}; + +struct plugin_common +{ int n; struct plugin plugins[MAX_PLUGINS]; }; +struct plugin_list +{ + struct plugin_per_client per_client; + struct plugin_common *common; + bool common_owned; +}; + +struct plugin_return +{ + int n; + struct openvpn_plugin_string_list *list[MAX_PLUGINS]; +}; + struct plugin_option_list *plugin_option_list_new (struct gc_arena *gc); bool plugin_option_list_add (struct plugin_option_list *list, const char *so_pathname, const char *args); @@ -75,14 +102,56 @@ bool plugin_option_list_add (struct plugin_option_list *list, const char *so_pat void plugin_option_list_print (const struct plugin_option_list *list, int msglevel); #endif -struct plugin_list *plugin_list_open (const struct plugin_option_list *list, const struct env_set *es); -int plugin_call (const struct plugin_list *pl, const int type, const char *args, struct env_set *es); +struct plugin_list *plugin_list_open (const struct plugin_option_list *list, + struct plugin_return *pr, + const struct env_set *es); + +struct plugin_list *plugin_list_inherit (const struct plugin_list *src); + +int plugin_call (const struct plugin_list *pl, + const int type, + const char *args, + struct plugin_return *pr, + struct env_set *es); + void plugin_list_close (struct plugin_list *pl); bool plugin_defined (const struct plugin_list *pl, const int type); +void plugin_return_get_column (const struct plugin_return *src, + struct plugin_return *dest, + const char *colname); + +void plugin_return_free (struct plugin_return *pr); + +#ifdef ENABLE_DEBUG +void plugin_return_print (const int msglevel, const char *prefix, const struct plugin_return *pr); +#endif + +static inline int +plugin_n (const struct plugin_list *pl) +{ + if (pl && pl->common) + return pl->common->n; + else + return 0; +} + +static inline bool +plugin_return_defined (const struct plugin_return *pr) +{ + return pr->n >= 0; +} + +static inline void +plugin_return_init (struct plugin_return *pr) +{ + pr->n = 0; +} + #else struct plugin_list { int dummy; }; +struct plugin_return { int dummy; }; static inline bool plugin_defined (const struct plugin_list *pl, const int type) @@ -91,7 +160,11 @@ plugin_defined (const struct plugin_list *pl, const int type) } static inline int -plugin_call (const struct plugin_list *pl, const int type, const char *args, struct env_set *es) +plugin_call (const struct plugin_list *pl, + const int type, + const char *args, + struct plugin_return *pr, + struct env_set *es) { return 0; } @@ -31,6 +31,7 @@ #include "syshead.h" #include "proto.h" +#include "error.h" #include "memdbg.h" @@ -72,3 +73,56 @@ is_ipv4 (int tunnel_type, struct buffer *buf) else return false; } + +#ifdef PACKET_TRUNCATION_CHECK + +void +ipv4_packet_size_verify (const uint8_t *data, + const int size, + const int tunnel_type, + const char *prefix, + counter_type *errors) +{ + if (size > 0) + { + struct buffer buf; + + buf_set_read (&buf, data, size); + + if (is_ipv4 (tunnel_type, &buf)) + { + const struct openvpn_iphdr *pip; + int hlen; + int totlen; + const char *msgstr = "PACKET SIZE INFO"; + unsigned int msglevel = D_PACKET_TRUNC_DEBUG; + + if (BLEN (&buf) < (int) sizeof (struct openvpn_iphdr)) + return; + + verify_align_4 (&buf); + pip = (struct openvpn_iphdr *) BPTR (&buf); + + hlen = OPENVPN_IPH_GET_LEN (pip->version_len); + totlen = ntohs (pip->tot_len); + + if (BLEN (&buf) != totlen) + { + msgstr = "PACKET TRUNCATION ERROR"; + msglevel = D_PACKET_TRUNC_ERR; + if (errors) + ++(*errors); + } + + msg (msglevel, "%s %s: size=%d totlen=%d hlen=%d errcount=" counter_format, + msgstr, + prefix, + BLEN (&buf), + totlen, + hlen, + errors ? *errors : (counter_type)0); + } + } +} + +#endif @@ -25,6 +25,7 @@ #ifndef PROTO_H #define PROTO_H +#include "common.h" #include "buffer.h" /* @@ -35,6 +36,13 @@ #define DEV_TYPE_TUN 2 /* point-to-point IP tunnel */ #define DEV_TYPE_TAP 3 /* ethernet (802.3) tunnel */ +/* TUN topologies */ + +#define TOP_UNDEF 0 +#define TOP_NET30 1 +#define TOP_P2P 2 +#define TOP_SUBNET 3 + /* * IP and Ethernet protocol structs. For portability, * OpenVPN needs its own definitions of these structs, and @@ -160,4 +168,13 @@ struct openvpn_tcphdr { */ bool is_ipv4 (int tunnel_type, struct buffer *buf); +#ifdef PACKET_TRUNCATION_CHECK +void ipv4_packet_size_verify (const uint8_t *data, + const int size, + const int tunnel_type, + const char + *prefix, + counter_type *errors); +#endif + #endif @@ -99,7 +99,7 @@ incoming_push_message (struct context *c, const struct buffer *buffer) status = process_incoming_push_msg (c, buffer, c->options.pull, - pull_permission_mask (), + pull_permission_mask (c), &option_types_found); if (status == PUSH_MSG_ERROR) @@ -46,6 +46,45 @@ static void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); static bool get_default_gateway (in_addr_t *ret); +static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags); + +#ifdef ENABLE_DEBUG + +static void +print_bypass_addresses (const struct route_bypass *rb) +{ + struct gc_arena gc = gc_new (); + int i; + for (i = 0; i < rb->n_bypass; ++i) + { + msg (D_ROUTE_DEBUG, "ROUTE DEBUG: bypass_host_route[%d]=%s", + i, + print_in_addr_t (rb->bypass[i], 0, &gc)); + } + gc_free (&gc); +} + +#endif + +static bool +add_bypass_address (struct route_bypass *rb, const in_addr_t a) +{ + int i; + for (i = 0; i < rb->n_bypass; ++i) + { + if (a == rb->bypass[i]) /* avoid duplicates */ + return true; + } + if (rb->n_bypass < N_ROUTE_BYPASS) + { + rb->bypass[rb->n_bypass++] = a; + return true; + } + else + { + return false; + } +} struct route_option_list * new_route_option_list (struct gc_arena *a) @@ -287,10 +326,13 @@ init_route_list (struct route_list *rl, in_addr_t remote_host, struct env_set *es) { + struct gc_arena gc = gc_new (); bool ret = true; clear_route_list (rl); + rl->flags = opt->flags; + if (remote_host) { rl->spec.remote_host = remote_host; @@ -301,10 +343,16 @@ init_route_list (struct route_list *rl, if (rl->spec.net_gateway_defined) { setenv_route_addr (es, "net_gateway", rl->spec.net_gateway, -1); + dmsg (D_ROUTE_DEBUG, "ROUTE DEBUG: default_gateway=%s", print_in_addr_t (rl->spec.net_gateway, 0, &gc)); + } + + if (rl->flags & RG_ENABLE) + { + get_bypass_addresses (&rl->spec.bypass, rl->flags); +#ifdef ENABLE_DEBUG + print_bypass_addresses (&rl->spec.bypass); +#endif } - rl->redirect_default_gateway = opt->redirect_default_gateway; - rl->redirect_local = opt->redirect_local; - rl->redirect_def1 = opt->redirect_def1; if (is_route_parm_defined (remote_endpoint)) { @@ -348,6 +396,7 @@ init_route_list (struct route_list *rl, rl->n = j; } + gc_free (&gc); return ret; } @@ -386,11 +435,51 @@ del_route3 (in_addr_t network, } static void +add_bypass_routes (struct route_bypass *rb, + in_addr_t gateway, + const struct tuntap *tt, + unsigned int flags, + const struct env_set *es) +{ + int i; + for (i = 0; i < rb->n_bypass; ++i) + { + if (rb->bypass[i] != gateway) + add_route3 (rb->bypass[i], + ~0, + gateway, + tt, + flags, + es); + } +} + +static void +del_bypass_routes (struct route_bypass *rb, + in_addr_t gateway, + const struct tuntap *tt, + unsigned int flags, + const struct env_set *es) +{ + int i; + for (i = 0; i < rb->n_bypass; ++i) + { + if (rb->bypass[i] != gateway) + del_route3 (rb->bypass[i], + ~0, + gateway, + tt, + flags, + es); + } +} + +static void redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es) { const char err[] = "NOTE: unable to redirect default gateway --"; - if (rl->redirect_default_gateway) + if (rl->flags & RG_ENABLE) { if (!rl->spec.remote_endpoint_defined) { @@ -407,7 +496,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u else { /* route remote host to original default gateway */ - if (!rl->redirect_local) + if (!(rl->flags & RG_LOCAL)) add_route3 (rl->spec.remote_host, ~0, rl->spec.net_gateway, @@ -415,7 +504,10 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u flags, es); - if (rl->redirect_def1) + /* route DHCP/DNS server traffic through original default gateway */ + add_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es); + + if (rl->flags & RG_DEF1) { /* add new default route (1st component) */ add_route3 (0x00000000, @@ -464,7 +556,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap * if (rl->did_redirect_default_gateway) { /* delete remote host route */ - if (!rl->redirect_local) + if (!(rl->flags & RG_LOCAL)) del_route3 (rl->spec.remote_host, ~0, rl->spec.net_gateway, @@ -472,7 +564,10 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap * flags, es); - if (rl->redirect_def1) + /* delete special DHCP/DNS bypass route */ + del_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es); + + if (rl->flags & RG_DEF1) { /* delete default route (1st component) */ del_route3 (0x00000000, @@ -585,9 +680,9 @@ print_route_options (const struct route_option_list *rol, int level) { int i; - if (rol->redirect_default_gateway) + if (rol->flags & RG_ENABLE) msg (level, " [redirect_default_gateway local=%d]", - rol->redirect_local); + (rol->flags & RG_LOCAL) != 0); for (i = 0; i < rol->n; ++i) print_route_option (&rol->routes[i], level); } @@ -987,7 +1082,7 @@ test_routes (const struct route_list *rl, const struct tuntap *tt) for (i = 0; i < rl->n; ++i) test_route_helper (&ret, &count, &good, &ambig, adapters, rl->routes[i].gateway); - if (rl->redirect_default_gateway && rl->spec.remote_endpoint_defined) + if ((rl->flags & RG_ENABLE) && rl->spec.remote_endpoint_defined) test_route_helper (&ret, &count, &good, &ambig, adapters, rl->spec.remote_endpoint); } } @@ -1004,40 +1099,55 @@ test_routes (const struct route_list *rl, const struct tuntap *tt) return ret; } -static bool -get_default_gateway (in_addr_t *ret) +static const MIB_IPFORWARDROW * +get_default_gateway_row (const MIB_IPFORWARDTABLE *routes) { - struct gc_arena gc = gc_new (); - bool ret_bool = false; + DWORD lowest_index = ~0; + const MIB_IPFORWARDROW *ret = NULL; int i; - const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc); - if (!routes) - goto done; - - for (i = 0; i < routes->dwNumEntries; ++i) + if (routes) { - const MIB_IPFORWARDROW *row = &routes->table[i]; - const in_addr_t net = ntohl (row->dwForwardDest); - const in_addr_t mask = ntohl (row->dwForwardMask); - const in_addr_t gw = ntohl (row->dwForwardNextHop); + for (i = 0; i < routes->dwNumEntries; ++i) + { + const MIB_IPFORWARDROW *row = &routes->table[i]; + const in_addr_t net = ntohl (row->dwForwardDest); + const in_addr_t mask = ntohl (row->dwForwardMask); + const DWORD index = row->dwForwardIfIndex; #if 0 - msg (M_INFO, "route[%d] %s %s %s", - i, - print_in_addr_t ((in_addr_t) net, 0, &gc), - print_in_addr_t ((in_addr_t) mask, 0, &gc), - print_in_addr_t ((in_addr_t) gw, 0, &gc)); + msg (M_INFO, "route[%d] %s %s %s", + i, + print_in_addr_t ((in_addr_t) net, 0, &gc), + print_in_addr_t ((in_addr_t) mask, 0, &gc), + print_in_addr_t ((in_addr_t) gw, 0, &gc)); #endif - if (!net && !mask) - { - *ret = gw; - ret_bool = true; - break; + + if (!net && !mask && index < lowest_index) + { + ret = row; + lowest_index = index; + } } } - - done: + return ret; +} + +static bool +get_default_gateway (in_addr_t *ret) +{ + struct gc_arena gc = gc_new (); + bool ret_bool = false; + + const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc); + const MIB_IPFORWARDROW *row = get_default_gateway_row (routes); + + if (row) + { + *ret = ntohl (row->dwForwardNextHop); + ret_bool = true; + } + gc_free (&gc); return ret_bool; } @@ -1774,3 +1884,74 @@ netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbi } return false; } + +/* + * get_bypass_addresses() is used by the redirect-gateway bypass-x + * functions to build a route bypass to selected DHCP/DNS servers, + * so that outgoing packets to these servers don't end up in the tunnel. + */ + +#if defined(WIN32) + +static void +add_host_route_if_nonlocal (struct route_bypass *rb, const in_addr_t addr, const IP_ADAPTER_INFO *dgi) +{ + if (!is_ip_in_adapter_subnet (dgi, addr, NULL)) + add_bypass_address (rb, addr); +} + +static void +add_host_route_array (struct route_bypass *rb, const IP_ADAPTER_INFO *dgi, const IP_ADDR_STRING *iplist) +{ + while (iplist) + { + bool succeed = false; + const in_addr_t ip = getaddr (GETADDR_HOST_ORDER, iplist->IpAddress.String, 0, &succeed, NULL); + if (succeed) + { + add_host_route_if_nonlocal (rb, ip, dgi); + } + iplist = iplist->Next; + } +} + +static void +get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) +{ + struct gc_arena gc = gc_new (); + bool ret_bool = false; + + /* get full routing table */ + const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc); + + /* get the route which represents the default gateway */ + const MIB_IPFORWARDROW *row = get_default_gateway_row (routes); + + if (row) + { + /* get the adapter which the default gateway is associated with */ + const IP_ADAPTER_INFO *dgi = get_adapter_info (row->dwForwardIfIndex, &gc); + + /* get extra adapter info, such as DNS addresses */ + const IP_PER_ADAPTER_INFO *pai = get_per_adapter_info (row->dwForwardIfIndex, &gc); + + /* Bypass DHCP server address */ + if ((flags & RG_BYPASS_DHCP) && dgi && dgi->DhcpEnabled) + add_host_route_array (rb, dgi, &dgi->DhcpServer); + + /* Bypass DNS server addresses */ + if ((flags & RG_BYPASS_DNS) && pai) + add_host_route_array (rb, dgi, &pai->DnsServerList); + } + + gc_free (&gc); +} + +#else + +static void +get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) +{ +} + +#endif @@ -48,6 +48,13 @@ */ #define ROUTE_DELETE_FIRST 2 +struct route_bypass +{ +# define N_ROUTE_BYPASS 8 + int n_bypass; + in_addr_t bypass[N_ROUTE_BYPASS]; +}; + struct route_special_addr { in_addr_t remote_endpoint; @@ -56,6 +63,7 @@ struct route_special_addr bool net_gateway_defined; in_addr_t remote_host; bool remote_host_defined; + struct route_bypass bypass; }; struct route_option { @@ -65,11 +73,16 @@ struct route_option { const char *metric; }; +/* redirect-gateway flags */ +#define RG_ENABLE (1<<0) +#define RG_LOCAL (1<<1) +#define RG_DEF1 (1<<2) +#define RG_BYPASS_DHCP (1<<3) +#define RG_BYPASS_DNS (1<<4) + struct route_option_list { int n; - bool redirect_default_gateway; - bool redirect_local; - bool redirect_def1; + unsigned int flags; struct route_option routes[MAX_ROUTES]; }; @@ -86,11 +99,8 @@ struct route { struct route_list { bool routes_added; struct route_special_addr spec; - bool redirect_default_gateway; - bool redirect_local; - bool redirect_def1; + unsigned int flags; bool did_redirect_default_gateway; - int n; struct route routes[MAX_ROUTES]; }; diff --git a/sample-scripts/ucn.pl b/sample-scripts/ucn.pl new file mode 100755 index 0000000..6d708f8 --- /dev/null +++ b/sample-scripts/ucn.pl @@ -0,0 +1,11 @@ +#!/usr/bin/perl -t + +# OpenVPN --auth-user-pass-verify script. +# Only authenticate if username equals common_name. +# In OpenVPN config file: +# auth-user-pass-verify ./ucn.pl via-env + +$username = $ENV{'username'}; +$common_name = $ENV{'common_name'}; + +exit !(length($username) > 0 && length($common_name) > 0 && $username eq $common_name); diff --git a/service-win32/mkpatch b/service-win32/mkpatch index d87af52..5e65b94 100755 --- a/service-win32/mkpatch +++ b/service-win32/mkpatch @@ -1,4 +1,4 @@ # build service.[ch] patch against original # SDK sample -diff -u service.c.orig service.c >service.patch -diff -u service.h.orig service.h >>service.patch +diff -ub service.c.orig service.c >service.patch +diff -ub service.h.orig service.h >>service.patch diff --git a/service-win32/service.patch b/service-win32/service.patch index 0b60472..b4b2063 100755 --- a/service-win32/service.patch +++ b/service-win32/service.patch @@ -1,5 +1,5 @@ ---- service.c.orig Sat Jan 15 17:39:20 2005 -+++ service.c Sun Feb 20 11:28:30 2005 +--- service.c.orig Mon Sep 5 14:38:41 2005 ++++ service.c Tue Sep 6 13:58:52 2005 @@ -16,6 +16,7 @@ service_main(DWORD dwArgc, LPTSTR *lpszArgv); CmdInstallService(); @@ -29,7 +29,7 @@ { SERVICE_TABLE_ENTRY dispatchTable[] = { -@@ -77,12 +79,16 @@ +@@ -77,11 +79,15 @@ { if ( _stricmp( "install", argv[1]+1 ) == 0 ) { @@ -40,14 +40,13 @@ { - CmdRemoveService(); + return CmdRemoveService(); - } ++ } + else if ( _stricmp( "start", argv[1]+1 ) == 0) + { + return CmdStartService(); -+ } + } else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) { - bDebug = TRUE; @@ -92,7 +98,7 @@ { goto dispatch; @@ -98,9 +97,8 @@ if ( !bDebug ) { -- dwErr = GetLastError(); + if (flags & MSG_FLAGS_SYS_CODE) -+ dwErr = GetLastError(); + dwErr = GetLastError(); + else + dwErr = 0; @@ -163,40 +161,16 @@ } schSCManager = OpenSCManager( -@@ -366,19 +384,19 @@ - if ( schSCManager ) - { - schService = CreateService( -- schSCManager, // SCManager database -- TEXT(SZSERVICENAME), // name of service -- TEXT(SZSERVICEDISPLAYNAME), // name to display -- SERVICE_QUERY_STATUS, // desired access -- SERVICE_WIN32_OWN_PROCESS, // service type +@@ -371,7 +389,7 @@ + TEXT(SZSERVICEDISPLAYNAME), // name to display + SERVICE_QUERY_STATUS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type - SERVICE_DEMAND_START, // start type -- SERVICE_ERROR_NORMAL, // error control type -- szPath, // service's binary -- NULL, // no load ordering group -- NULL, // no tag identifier -- TEXT(SZDEPENDENCIES), // dependencies -- NULL, // LocalSystem account -- NULL); // no password -+ schSCManager, // SCManager database -+ TEXT(SZSERVICENAME), // name of service -+ TEXT(SZSERVICEDISPLAYNAME), // name to display -+ SERVICE_QUERY_STATUS, // desired access -+ SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type -- alternative: SERVICE_AUTO_START -+ SERVICE_ERROR_NORMAL, // error control type -+ szPath, // service's binary -+ NULL, // no load ordering group -+ NULL, // no tag identifier -+ TEXT(SZDEPENDENCIES), // dependencies -+ NULL, // LocalSystem account -+ NULL); // no password - - if ( schService ) - { -@@ -388,15 +406,78 @@ + SERVICE_ERROR_NORMAL, // error control type + szPath, // service's binary + NULL, // no load ordering group +@@ -388,16 +406,79 @@ else { _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256)); @@ -206,9 +180,8 @@ CloseServiceHandle(schSCManager); } else -- _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); + { -+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); + _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); + ret = 1; + } + return ret; @@ -233,7 +206,7 @@ + + SC_HANDLE schSCManager; + SC_HANDLE schService; -+ + + // Open a handle to the SC Manager database. + schSCManager = OpenSCManager( @@ -273,9 +246,10 @@ + CloseServiceHandle(schSCManager); + return ret; +} - ++ // // FUNCTION: CmdRemoveService() + // @@ -407,15 +488,17 @@ // none // @@ -300,9 +274,8 @@ if ( ssStatus.dwCurrentState == SERVICE_STOPPED ) _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) ); else -- _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) ); + { -+ _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) ); + _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) ); + ret = 1; + } @@ -312,9 +285,8 @@ if ( DeleteService(schService) ) _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); else -- _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256)); + { -+ _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256)); + _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256)); + ret = 1; + } @@ -322,18 +294,16 @@ CloseServiceHandle(schService); } else -- _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); + { -+ _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); + _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); + ret = 1; + } CloseServiceHandle(schSCManager); } else -- _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); + { -+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); + _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); + ret = 1; + } + return ret; @@ -349,8 +319,8 @@ } if ( lpszTemp ) ---- service.h.orig Sat Jan 15 17:39:20 2005 -+++ service.h Mon Feb 7 17:24:04 2005 +--- service.h.orig Mon Sep 5 14:38:41 2005 ++++ service.h Tue Sep 6 13:58:59 2005 @@ -62,13 +62,13 @@ //// todo: change to desired strings //// @@ -365,7 +335,7 @@ +#define SZSERVICEDISPLAYNAME "OpenVPN Service" // list of service dependencies - "dep1\0dep2\0\0" -#define SZDEPENDENCIES "" -+#define SZDEPENDENCIES "TAP0801\0\0" ++#define SZDEPENDENCIES "TAP0801\0Dhcp\0\0" ////////////////////////////////////////////////////////////////////////////// @@ -242,6 +242,12 @@ print_status (const struct context *c, struct status_output *so) if (c->options.comp_lzo) lzo_print_stats (&c->c2.lzo_compwork, so); #endif +#ifdef PACKET_TRUNCATION_CHECK + status_printf (so, "TUN read truncations," counter_format, c->c2.n_trunc_tun_read); + status_printf (so, "TUN write truncations," counter_format, c->c2.n_trunc_tun_write); + status_printf (so, "Pre-encrypt truncations," counter_format, c->c2.n_trunc_pre_encrypt); + status_printf (so, "Post-decrypt truncations," counter_format, c->c2.n_trunc_post_decrypt); +#endif #ifdef WIN32 if (tuntap_defined (c->c1.tuntap)) status_printf (so, "TAP-WIN32 driver status,\"%s\"", @@ -1317,7 +1317,7 @@ link_socket_connection_initiated (const struct buffer *buf, if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE)) { const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual, true, " ", &gc); - if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, es)) + if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, NULL, es)) msg (M_WARN, "WARNING: ipchange plugin call failed"); } @@ -533,7 +533,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) ctx->error_depth, subject); - ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, opt->es); + ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, NULL, opt->es); if (!ret) { @@ -2384,7 +2384,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up setenv_untrusted (session); /* call command */ - retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, session->opt->es); + retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es); if (!retval) ret = true; diff --git a/tap-win32/SOURCES b/tap-win32/SOURCES index 5320ae3..f7c8dd6 100755 --- a/tap-win32/SOURCES +++ b/tap-win32/SOURCES @@ -15,7 +15,7 @@ INCLUDES=$(DDK_INCLUDE_PATH) # config-win32.h C_DEFINES= C_DEFINES=$(C_DEFINES) -DTAP_DRIVER_MAJOR_VERSION=8 -C_DEFINES=$(C_DEFINES) -DTAP_DRIVER_MINOR_VERSION=1 +C_DEFINES=$(C_DEFINES) -DTAP_DRIVER_MINOR_VERSION=307 # Use 00:FF:XX:XX:XX:XX format MAC addresses where # the Xs are random (like Linux tap driver). diff --git a/tap-win32/common.h b/tap-win32/common.h index ef121b1..3c183c2 100755 --- a/tap-win32/common.h +++ b/tap-win32/common.h @@ -39,6 +39,8 @@ #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) @@ -49,6 +51,11 @@ #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 //================= diff --git a/tap-win32/i386/OemWin2k.inf b/tap-win32/i386/OemWin2k.inf index eb1e071..681c4b7 100755 --- a/tap-win32/i386/OemWin2k.inf +++ b/tap-win32/i386/OemWin2k.inf @@ -10,6 +10,11 @@ ; chkinf c:\src\openvpn\tap-win32\i386\oemwin2k.inf ; OUTPUT -> file:///c:/WINDDK/3790/tools/chkinf/htm/c%23+src+openvpn+tap-win32+i386+__OemWin2k.htm +; INSTALL/REMOVE DRIVER +; tapinstall install OemWin2k.inf TAP0801 +; tapinstall update OemWin2k.inf TAP0801 +; tapinstall remove TAP0801 + ;********************************************************* ; Note to Developers: ; @@ -91,22 +96,28 @@ HKR, , ProductName, 0, "%DeviceDescription%" [tap0801.params.reg] - HKR, Ndi\params\MTU, ParamDesc, 0, "MTU" - HKR, Ndi\params\MTU, Type, 0, "int" - HKR, Ndi\params\MTU, Default, 0, "1500" - HKR, Ndi\params\MTU, Optional, 0, "0" - HKR, Ndi\params\MTU, Min, 0, "100" - HKR, Ndi\params\MTU, Max, 0, "1500" - HKR, Ndi\params\MTU, Step, 0, "1" - HKR, Ndi\params\MediaStatus, ParamDesc, 0, "Media Status" - HKR, Ndi\params\MediaStatus, Type, 0, "enum" - HKR, Ndi\params\MediaStatus, Default, 0, "0" - HKR, Ndi\params\MediaStatus, Optional, 0, "0" - HKR, Ndi\params\MediaStatus\enum, "0", 0, "Application Controlled" - HKR, Ndi\params\MediaStatus\enum, "1", 0, "Always Connected" - HKR, Ndi\params\MAC, ParamDesc, 0, "MAC Address" - HKR, Ndi\params\MAC, Type, 0, "edit" - HKR, Ndi\params\MAC, Optional, 0, "1" + HKR, Ndi\params\MTU, ParamDesc, 0, "MTU" + HKR, Ndi\params\MTU, Type, 0, "int" + HKR, Ndi\params\MTU, Default, 0, "1500" + HKR, Ndi\params\MTU, Optional, 0, "0" + HKR, Ndi\params\MTU, Min, 0, "100" + HKR, Ndi\params\MTU, Max, 0, "1500" + HKR, Ndi\params\MTU, Step, 0, "1" + HKR, Ndi\params\MediaStatus, ParamDesc, 0, "Media Status" + HKR, Ndi\params\MediaStatus, Type, 0, "enum" + HKR, Ndi\params\MediaStatus, Default, 0, "0" + HKR, Ndi\params\MediaStatus, Optional, 0, "0" + HKR, Ndi\params\MediaStatus\enum, "0", 0, "Application Controlled" + HKR, Ndi\params\MediaStatus\enum, "1", 0, "Always Connected" + HKR, Ndi\params\MAC, ParamDesc, 0, "MAC Address" + HKR, Ndi\params\MAC, Type, 0, "edit" + HKR, Ndi\params\MAC, Optional, 0, "1" + HKR, Ndi\params\AllowNonAdmin, ParamDesc, 0, "Non-Admin Access" + HKR, Ndi\params\AllowNonAdmin, Type, 0, "enum" + HKR, Ndi\params\AllowNonAdmin, Default, 0, "1" + HKR, Ndi\params\AllowNonAdmin, Optional, 0, "0" + HKR, Ndi\params\AllowNonAdmin\enum, "0", 0, "Not Allowed" + HKR, Ndi\params\AllowNonAdmin\enum, "1", 0, "Allowed" ;---------------------------------------------------------------- ; Service Section diff --git a/tap-win32/prototypes.h b/tap-win32/prototypes.h index 81f58f4..2237ed0 100755 --- a/tap-win32/prototypes.h +++ b/tap-win32/prototypes.h @@ -160,7 +160,8 @@ BOOLEAN ProcessARP TapAdapterPointer p_Adapter, const PARP_PACKET src, const IPADDR adapter_ip, - const IPADDR ip, + const IPADDR ip_network, + const IPADDR ip_netmask, const MACADDR mac ); @@ -177,11 +178,42 @@ VOID InjectPacket const unsigned int len ); -VOID CheckIfDhcpAndPointToPointMode +VOID CheckIfDhcpAndTunMode ( TapAdapterPointer p_Adapter ); VOID HookDispatchFunctions(); +#if ENABLE_NONADMIN + +typedef struct _SECURITY_DESCRIPTOR { + unsigned char opaque[20]; +} SECURITY_DESCRIPTOR; + +NTSYSAPI +NTSTATUS +NTAPI +ZwSetSecurityObject ( + IN HANDLE Handle, + IN SECURITY_INFORMATION SecurityInformation, + IN PSECURITY_DESCRIPTOR SecurityDescriptor); + +VOID AllowNonAdmin (TapExtensionPointer p_Extension); + +#endif + +#if PACKET_TRUNCATION_CHECK + +VOID IPv4PacketSizeVerify + ( + const UCHAR *data, + ULONG length, + BOOLEAN tun, + const char *prefix, + LONG *counter + ); + +#endif + #endif diff --git a/tap-win32/tapdrvr.c b/tap-win32/tapdrvr.c index 7eecca7..0997bd5 100755 --- a/tap-win32/tapdrvr.c +++ b/tap-win32/tapdrvr.c @@ -35,7 +35,8 @@ // By default we operate as a "tap" virtual ethernet // 802.3 interface, but we can emulate a "tun" // interface (point-to-point IPv4) through the -// TAP_IOCTL_CONFIG_POINT_TO_POINT ioctl. +// TAP_IOCTL_CONFIG_POINT_TO_POINT or +// TAP_IOCTL_CONFIG_TUN ioctl. //====================================================== #define NDIS_MINIPORT_DRIVER @@ -46,11 +47,27 @@ #define NTSTRSAFE_LIB // Debug info output -#define ALSO_DBGPRINT 1 -#define DEBUGP_AT_DISPATCH 0 +#define ALSO_DBGPRINT 1 +#define DEBUGP_AT_DISPATCH 0 + +//======================================================== +// Check for truncated IPv4 packets, log errors if found. +//======================================================== +#define PACKET_TRUNCATION_CHECK 1 // JYFIXME + +//======================================================== +// EXPERIMENTAL -- Configure TAP device object to be +// accessible from non-administrative accounts, based +// on an advanced properties setting. +// +// Duplicates the functionality of OpenVPN's +// --allow-nonadmin directive. +//======================================================== +#define ENABLE_NONADMIN 1 // JYFIXME #include <ndis.h> #include <ntstrsafe.h> +#include <ntddk.h> #include "lock.h" #include "constants.h" @@ -274,6 +291,10 @@ NDIS_STATUS AdapterCreate UINT l_Index; NDIS_STATUS status; +#if ENABLE_NONADMIN + BOOLEAN enable_non_admin = FALSE; +#endif + //==================================== // Make sure adapter type is supported //==================================== @@ -410,6 +431,25 @@ NDIS_STATUS AdapterCreate } } +#if ENABLE_NONADMIN + /* Read AllowNonAdmin setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("AllowNonAdmin"); + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterInteger) + { + if (parm->ParameterData.IntegerData) + { + enable_non_admin = TRUE; + } + } + } + } +#endif + /* Read optional MAC setting from registry */ { NDIS_STRING key = NDIS_STRING_CONST("MAC"); @@ -479,6 +519,11 @@ NDIS_STATUS AdapterCreate l_Adapter->m_InterfaceIsRunning = TRUE; +#if ENABLE_NONADMIN + if (enable_non_admin) + AllowNonAdmin (&l_Adapter->m_Extension); +#endif + return NDIS_STATUS_SUCCESS; } @@ -1011,7 +1056,7 @@ NDIS_STATUS AdapterQuery break; case OID_GEN_LINK_SPEED: - l_Query.m_Long = 100000; + l_Query.m_Long = 100000; // rate / 100 bps break; case OID_802_3_PERMANENT_ADDRESS: @@ -1348,19 +1393,40 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, __try { - for (l_Index = 0; l_NDIS_Buffer && l_Index < l_PacketLength; - l_Index += l_BufferLength) + l_Index = 0; + while (l_NDIS_Buffer && l_Index < l_PacketLength) { + ULONG newlen; NdisQueryBuffer (l_NDIS_Buffer, (PVOID *) & l_Buffer, &l_BufferLength); + newlen = l_Index + l_BufferLength; + if (newlen > l_PacketLength) + { + NOTE_ERROR (); + goto no_queue; /* overflow */ + } NdisMoveMemory (l_PacketBuffer->m_Data + l_Index, l_Buffer, l_BufferLength); + l_Index = newlen; NdisGetNextBuffer (l_NDIS_Buffer, &l_NDIS_Buffer); } + if (l_Index != l_PacketLength) + { + NOTE_ERROR (); + goto no_queue; /* underflow */ + } DUMP_PACKET ("AdapterTransmit", l_PacketBuffer->m_Data, l_PacketLength); //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify (l_PacketBuffer->m_Data, l_PacketLength, FALSE, "TX", &l_Adapter->m_TxTrunc); +#endif + + //===================================================== // Are we running in DHCP server masquerade mode? // // If so, catch both DHCP requests and ARP queries @@ -1381,6 +1447,7 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, (PARP_PACKET) l_PacketBuffer->m_Data, l_Adapter->m_dhcp_addr, l_Adapter->m_dhcp_server_ip, + ~0, l_Adapter->m_dhcp_server_mac)) goto no_queue; } @@ -1417,7 +1484,7 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, // In Point-To-Point mode, check to see whether // packet is ARP or IPv4 (if neither, then drop). //=============================================== - if (l_Adapter->m_PointToPoint) + if (l_Adapter->m_tun) { ETH_HEADER *e; @@ -1438,7 +1505,8 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, ProcessARP (l_Adapter, (PARP_PACKET) l_PacketBuffer->m_Data, l_Adapter->m_localIP, - l_Adapter->m_remoteIP, + l_Adapter->m_remoteNetwork, + l_Adapter->m_remoteNetmask, l_Adapter->m_TapToUser.dest); default: @@ -1458,7 +1526,7 @@ AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, goto no_queue; // Packet looks like IPv4, queue it. - l_PacketBuffer->m_SizeFlags |= TP_POINT_TO_POINT; + l_PacketBuffer->m_SizeFlags |= TP_TUN; } } @@ -1669,15 +1737,25 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) NULL, NULL, STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS, +#if PACKET_TRUNCATION_CHECK + "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d,%d] Rx=[%d,%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d]", +#else "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d] Rx=[%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d]", +#endif state, g_LastErrorFilename, g_LastErrorLineNumber, (int)l_Adapter->m_Extension.m_NumTapOpens, (int)l_Adapter->m_Tx, (int)l_Adapter->m_TxErr, +#if PACKET_TRUNCATION_CHECK + (int)l_Adapter->m_TxTrunc, +#endif (int)l_Adapter->m_Rx, (int)l_Adapter->m_RxErr, +#if PACKET_TRUNCATION_CHECK + (int)l_Adapter->m_RxTrunc, +#endif (int)l_Adapter->m_Extension.m_IrpQueue->size, (int)l_Adapter->m_Extension.m_IrpQueue->max_size, (int)IRP_QUEUE_SIZE, @@ -1708,21 +1786,65 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) } #endif - case TAP_IOCTL_CONFIG_POINT_TO_POINT: + case TAP_IOCTL_CONFIG_TUN: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (IPADDR) * 3)) + { + MACADDR dest; + + l_Adapter->m_tun = FALSE; + + GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1); + + l_Adapter->m_localIP = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + l_Adapter->m_remoteNetmask = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2]; + + // sanity check on network/netmask + if ((l_Adapter->m_remoteNetwork & l_Adapter->m_remoteNetmask) != l_Adapter->m_remoteNetwork) + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + break; + } + + COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC); + COPY_MAC (l_Adapter->m_TapToUser.dest, dest); + COPY_MAC (l_Adapter->m_UserToTap.src, dest); + COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); + + l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); + + l_Adapter->m_tun = TRUE; + + CheckIfDhcpAndTunMode (l_Adapter); + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + case TAP_IOCTL_CONFIG_POINT_TO_POINT: // Obsoleted by TAP_IOCTL_CONFIG_TUN { if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= (sizeof (IPADDR) * 2)) { MACADDR dest; - l_Adapter->m_PointToPoint = FALSE; + l_Adapter->m_tun = FALSE; GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1); - l_Adapter->m_localIP = - ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; - l_Adapter->m_remoteIP = - ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + l_Adapter->m_localIP = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + l_Adapter->m_remoteNetmask = ~0; COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC); COPY_MAC (l_Adapter->m_TapToUser.dest, dest); @@ -1731,9 +1853,9 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); - l_Adapter->m_PointToPoint = TRUE; + l_Adapter->m_tun = TRUE; - CheckIfDhcpAndPointToPointMode (l_Adapter); + CheckIfDhcpAndTunMode (l_Adapter); p_IRP->IoStatus.Information = 1; // Simple boolean value } @@ -1791,7 +1913,7 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) l_Adapter->m_dhcp_enabled = TRUE; l_Adapter->m_dhcp_server_arp = TRUE; - CheckIfDhcpAndPointToPointMode (l_Adapter); + CheckIfDhcpAndTunMode (l_Adapter); p_IRP->IoStatus.Information = 1; // Simple boolean value } @@ -1979,7 +2101,7 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; p_IRP->IoStatus.Information = 0; } - else if (!l_Adapter->m_PointToPoint && ((l_IrpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE)) + else if (!l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE)) { __try { @@ -1989,6 +2111,18 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, l_IrpSp->Parameters.Write.Length); + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length, + FALSE, + "RX", + &l_Adapter->m_RxTrunc); +#endif + NdisMEthIndicateReceive (l_Adapter->m_MiniportAdapterHandle, (NDIS_HANDLE) l_Adapter, @@ -2011,7 +2145,7 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) p_IRP->IoStatus.Information = 0; } } - else if (l_Adapter->m_PointToPoint && ((l_IrpSp->Parameters.Write.Length) >= IP_HEADER_SIZE)) + else if (l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= IP_HEADER_SIZE)) { __try { @@ -2022,6 +2156,18 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, l_IrpSp->Parameters.Write.Length); + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length, + TRUE, + "RX", + &l_Adapter->m_RxTrunc); +#endif + NdisMEthIndicateReceive (l_Adapter->m_MiniportAdapterHandle, (NDIS_HANDLE) l_Adapter, @@ -2197,7 +2343,7 @@ CompleteIRP (IN PIRP p_IRP, // component. //------------------------------------------- - if (p_PacketBuffer->m_SizeFlags & TP_POINT_TO_POINT) + if (p_PacketBuffer->m_SizeFlags & TP_TUN) { offset = ETHERNET_HEADER_SIZE; len = (int) (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE; @@ -2379,16 +2525,16 @@ SetMediaStatus (TapAdapterPointer p_Adapter, BOOLEAN state) //====================================================== -// If DHCP mode is used together with Point-to-point -// mode, consider the fact that the P2P remote endpoint -// might be equal to the DHCP masq server address. +// If DHCP mode is used together with tun +// mode, consider the fact that the P2P remote subnet +// might enclose the DHCP masq server address. //====================================================== VOID -CheckIfDhcpAndPointToPointMode (TapAdapterPointer p_Adapter) +CheckIfDhcpAndTunMode (TapAdapterPointer p_Adapter) { - if (p_Adapter->m_PointToPoint && p_Adapter->m_dhcp_enabled) + if (p_Adapter->m_tun && p_Adapter->m_dhcp_enabled) { - if (p_Adapter->m_dhcp_server_ip == p_Adapter->m_remoteIP) + if ((p_Adapter->m_dhcp_server_ip & p_Adapter->m_remoteNetmask) == p_Adapter->m_remoteNetwork) { COPY_MAC (p_Adapter->m_dhcp_server_mac, p_Adapter->m_TapToUser.dest); p_Adapter->m_dhcp_server_arp = FALSE; @@ -2404,7 +2550,8 @@ BOOLEAN ProcessARP (TapAdapterPointer p_Adapter, const PARP_PACKET src, const IPADDR adapter_ip, - const IPADDR ip, + const IPADDR ip_network, + const IPADDR ip_netmask, const MACADDR mac) { //----------------------------------------------- @@ -2420,7 +2567,8 @@ ProcessARP (TapAdapterPointer p_Adapter, && src->m_PROTO_AddressType == htons (ETH_P_IP) && src->m_PROTO_AddressSize == sizeof (IPADDR) && src->m_ARP_IP_Source == adapter_ip - && src->m_ARP_IP_Destination == ip) + && (src->m_ARP_IP_Destination & ip_netmask) == ip_network + && src->m_ARP_IP_Destination != adapter_ip) { ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE); if (arp) @@ -2442,7 +2590,7 @@ ProcessARP (TapAdapterPointer p_Adapter, COPY_MAC (arp->m_MAC_Destination, p_Adapter->m_MAC); COPY_MAC (arp->m_ARP_MAC_Source, mac); COPY_MAC (arp->m_ARP_MAC_Destination, p_Adapter->m_MAC); - arp->m_ARP_IP_Source = ip; + arp->m_ARP_IP_Source = src->m_ARP_IP_Destination; arp->m_ARP_IP_Destination = adapter_ip; DUMP_PACKET ("ProcessARP", @@ -2508,9 +2656,10 @@ InjectPacket (TapAdapterPointer p_Adapter, VOID ResetTapAdapterState (TapAdapterPointer p_Adapter) { // Point-To-Point - p_Adapter->m_PointToPoint = FALSE; + p_Adapter->m_tun = FALSE; p_Adapter->m_localIP = 0; - p_Adapter->m_remoteIP = 0; + p_Adapter->m_remoteNetwork = 0; + p_Adapter->m_remoteNetmask = 0; NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser)); NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap)); @@ -2526,6 +2675,124 @@ VOID ResetTapAdapterState (TapAdapterPointer p_Adapter) p_Adapter->m_dhcp_bad_requests = 0; NdisZeroMemory (p_Adapter->m_dhcp_server_mac, sizeof (MACADDR)); } + +#if ENABLE_NONADMIN + +//=================================================================== +// Set TAP device handle to be accessible without admin privileges. +//=================================================================== +VOID AllowNonAdmin (TapExtensionPointer p_Extension) +{ + NTSTATUS stat; + SECURITY_DESCRIPTOR sd; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + HANDLE hand = NULL; + + NdisZeroMemory (&sd, sizeof (sd)); + NdisZeroMemory (&oa, sizeof (oa)); + NdisZeroMemory (&isb, sizeof (isb)); + + if (!p_Extension->m_CreatedUnicodeLinkName) + { + DEBUGP (("[TAP] AllowNonAdmin: UnicodeLinkName is uninitialized\n")); + NOTE_ERROR (); + return; + } + + stat = RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: RtlCreateSecurityDescriptor failed\n")); + NOTE_ERROR (); + return; + } + + InitializeObjectAttributes ( + &oa, + &p_Extension->m_UnicodeLinkName, + OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + stat = ZwOpenFile ( + &hand, + WRITE_DAC, + &oa, + &isb, + 0, + 0 + ); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwOpenFile failed, status=0x%08x\n", (unsigned int)stat)); + NOTE_ERROR (); + return; + } + + stat = ZwSetSecurityObject (hand, DACL_SECURITY_INFORMATION, &sd); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwSetSecurityObject failed\n")); + NOTE_ERROR (); + return; + } + + stat = ZwClose (hand); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwClose failed\n")); + NOTE_ERROR (); + return; + } + + DEBUGP (("[TAP] AllowNonAdmin: SUCCEEDED\n")); +} + +#endif + +#if PACKET_TRUNCATION_CHECK + +VOID +IPv4PacketSizeVerify (const UCHAR *data, ULONG length, BOOLEAN tun, const char *prefix, LONG *counter) +{ + const IPHDR *ip; + int len = length; + + if (tun) + { + ip = (IPHDR *) data; + } + else + { + if (length >= sizeof (ETH_HEADER)) + { + const ETH_HEADER *eth = (ETH_HEADER *) data; + + if (eth->proto != htons (ETH_P_IP)) + return; + + ip = (IPHDR *) (data + sizeof (ETH_HEADER)); + len -= sizeof (ETH_HEADER); + } + else + return; + } + + if (len >= sizeof (IPHDR)) + { + const int totlen = ntohs (ip->tot_len); + + DEBUGP (("[TAP] IPv4PacketSizeVerify %s len=%d totlen=%d\n", prefix, len, totlen)); + + if (len != totlen) + ++(*counter); + } +} + +#endif + //====================================================================== // End of Source //====================================================================== diff --git a/tap-win32/types.h b/tap-win32/types.h index f5a4291..45e0033 100755 --- a/tap-win32/types.h +++ b/tap-win32/types.h @@ -92,8 +92,8 @@ TapExtension, *TapExtensionPointer; typedef struct _TapPacket { # define TAP_PACKET_SIZE(data_size) (sizeof (TapPacket) + (data_size)) -# define TP_POINT_TO_POINT 0x80000000 -# define TP_SIZE_MASK (~TP_POINT_TO_POINT) +# define TP_TUN 0x80000000 +# define TP_SIZE_MASK (~TP_TUN) ULONG m_SizeFlags; UCHAR m_Data []; // m_Data must be the last struct member } @@ -107,6 +107,9 @@ typedef struct _TapAdapter BOOLEAN m_InterfaceIsRunning; NDIS_HANDLE m_MiniportAdapterHandle; LONG m_Rx, m_Tx, m_RxErr, m_TxErr; +#if PACKET_TRUNCATION_CHECK + LONG m_RxTrunc, m_TxTrunc; +#endif NDIS_MEDIUM m_Medium; ULONG m_Lookahead; ULONG m_MTU; @@ -123,9 +126,10 @@ typedef struct _TapAdapter char m_DeviceState; // Info for point-to-point mode - BOOLEAN m_PointToPoint; + BOOLEAN m_tun; IPADDR m_localIP; - IPADDR m_remoteIP; + IPADDR m_remoteNetwork; + IPADDR m_remoteNetmask; ETH_HEADER m_TapToUser; ETH_HEADER m_UserToTap; MACADDR m_MAC_Broadcast; @@ -169,14 +169,14 @@ static const char ifconfig_warn_how_to_silence[] = "(silence this warning with - * like an IPv4 address. */ static void -ifconfig_sanity_check (bool tun, in_addr_t addr) +ifconfig_sanity_check (bool tun, in_addr_t addr, int topology) { struct gc_arena gc = gc_new (); const bool looks_like_netmask = ((addr & 0xFF000000) == 0xFF000000); if (tun) { - if (looks_like_netmask) - msg (M_WARN, "WARNING: Since you are using --dev tun, the second argument to --ifconfig must be an IP address. You are using something (%s) that looks more like a netmask. %s", + if (looks_like_netmask && (topology == TOP_NET30 || topology == TOP_P2P)) + msg (M_WARN, "WARNING: Since you are using --dev tun with a point-to-point topology, the second argument to --ifconfig must be an IP address. You are using something (%s) that looks more like a netmask. %s", print_in_addr_t (addr, 0, &gc), ifconfig_warn_how_to_silence); } @@ -283,7 +283,13 @@ ifconfig_options_string (const struct tuntap* tt, bool remote, bool disable, str struct buffer out = alloc_buf_gc (256, gc); if (tt->did_ifconfig_setup && !disable) { - if (tt->type == DEV_TYPE_TUN) + if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET)) + { + buf_printf (&out, "%s %s", + print_in_addr_t (tt->local & tt->remote_netmask, 0, gc), + print_in_addr_t (tt->remote_netmask, 0, gc)); + } + else if (tt->type == DEV_TYPE_TUN) { const char *l, *r; if (remote) @@ -298,12 +304,6 @@ ifconfig_options_string (const struct tuntap* tt, bool remote, bool disable, str } buf_printf (&out, "%s %s", r, l); } - else if (tt->type == DEV_TYPE_TAP) - { - buf_printf (&out, "%s %s", - print_in_addr_t (tt->local & tt->remote_netmask, 0, gc), - print_in_addr_t (tt->remote_netmask, 0, gc)); - } else buf_printf (&out, "[undef]"); } @@ -346,6 +346,24 @@ tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc) } /* + * Return true for point-to-point topology, false for subnet topology + */ +bool +is_tun_p2p (const struct tuntap *tt) +{ + bool tun = false; + + if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET)) + tun = false; + else if (tt->type == DEV_TYPE_TUN) + tun = true; + else + ASSERT (0); /* should have been caught in init_tun */ + + return tun; +} + +/* * Init tun/tap object. * * Set up tuntap structure for ifconfig, @@ -354,6 +372,7 @@ tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc) struct tuntap * init_tun (const char *dev, /* --dev option */ const char *dev_type, /* --dev-type option */ + int topology, /* one of the TOP_x values */ const char *ifconfig_local_parm, /* --ifconfig parm 1 */ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */ in_addr_t local_public, @@ -368,6 +387,7 @@ init_tun (const char *dev, /* --dev option */ clear_tuntap (tt); tt->type = dev_type_enum (dev, dev_type); + tt->topology = topology; if (ifconfig_local_parm && ifconfig_remote_netmask_parm) { @@ -379,12 +399,7 @@ init_tun (const char *dev, /* --dev option */ /* * We only handle TUN/TAP devices here, not --dev null devices. */ - if (tt->type == DEV_TYPE_TUN) - tun = true; - else if (tt->type == DEV_TYPE_TAP) - tun = false; - else - msg (M_FATAL, "'%s' is not a TUN/TAP device. The --ifconfig option works only for TUN/TAP devices.", dev); + tun = is_tun_p2p (tt); /* * Convert arguments to binary IPv4 addresses. @@ -415,7 +430,7 @@ init_tun (const char *dev, /* --dev option */ */ if (strict_warn) { - ifconfig_sanity_check (tun, tt->remote_netmask); + ifconfig_sanity_check (tt->type == DEV_TYPE_TUN, tt->remote_netmask, tt->topology); /* * If local_public or remote_public addresses are defined, @@ -511,12 +526,7 @@ do_ifconfig (struct tuntap *tt, /* * We only handle TUN/TAP devices here, not --dev null devices. */ - if (tt->type == DEV_TYPE_TUN) - tun = true; - else if (tt->type == DEV_TYPE_TAP) - tun = false; - else - ASSERT (0); /* should have been caught in init_tun */ + tun = is_tun_p2p (tt); /* * Set ifconfig parameters @@ -2376,6 +2386,34 @@ get_adapter_info_list (struct gc_arena *gc) return pi; } +const IP_PER_ADAPTER_INFO * +get_per_adapter_info (const DWORD index, struct gc_arena *gc) +{ + ULONG size = 0; + IP_PER_ADAPTER_INFO *pi = NULL; + DWORD status; + + if ((status = GetPerAdapterInfo (index, NULL, &size)) != ERROR_BUFFER_OVERFLOW) + { + msg (M_INFO, "GetPerAdapterInfo #1 failed (status=%u) : %s", + (unsigned int)status, + strerror_win32 (status, gc)); + } + else + { + pi = (PIP_PER_ADAPTER_INFO) gc_malloc (size, false, gc); + if ((status = GetPerAdapterInfo ((ULONG)index, pi, &size)) == ERROR_SUCCESS) + return pi; + else + { + msg (M_INFO, "GetPerAdapterInfo #2 failed (status=%u) : %s", + (unsigned int)status, + strerror_win32 (status, gc)); + } + } + return pi; +} + static const IP_INTERFACE_INFO * get_interface_info_list (struct gc_arena *gc) { @@ -2443,7 +2481,7 @@ get_adapter (const IP_ADAPTER_INFO *ai, DWORD index) return NULL; } -static const IP_ADAPTER_INFO * +const IP_ADAPTER_INFO * get_adapter_info (DWORD index, struct gc_arena *gc) { return get_adapter (get_adapter_info_list (gc), index); @@ -2762,6 +2800,14 @@ show_adapter (int msglev, const IP_ADAPTER_INFO *a, struct gc_arena *gc) msg (msglev, " PRI WINS = %s", format_ip_addr_string (&a->PrimaryWinsServer, gc)); msg (msglev, " SEC WINS = %s", format_ip_addr_string (&a->SecondaryWinsServer, gc)); } + + { + const IP_PER_ADAPTER_INFO *pai = get_per_adapter_info (a->Index, gc); + if (pai) + { + msg (msglev, " DNS SERV = %s", format_ip_addr_string (&pai->DnsServerList, gc)); + } + } } /* @@ -2788,6 +2834,123 @@ show_adapters (int msglev) } /* + * Set a particular TAP-Win32 adapter (or all of them if + * adapter_name == NULL) to allow it to be opened from + * a non-admin account. This setting will only persist + * for the lifetime of the device object. + */ + +static void +tap_allow_nonadmin_access_handle (const char *device_path, HANDLE hand) +{ + struct security_attributes sa; + BOOL status; + + if (!init_security_attributes_allow_all (&sa)) + msg (M_ERR, "Error: init SA failed"); + + status = SetKernelObjectSecurity (hand, DACL_SECURITY_INFORMATION, &sa.sd); + if (!status) + { + msg (M_ERRNO, "Error: SetKernelObjectSecurity failed on %s", device_path); + } + else + { + msg (M_INFO|M_NOPREFIX, "TAP-Win32 device: %s [Non-admin access allowed]", device_path); + } +} + +void +tap_allow_nonadmin_access (const char *dev_node) +{ + struct gc_arena gc = gc_new (); + const struct tap_reg *tap_reg = get_tap_reg (&gc); + const struct panel_reg *panel_reg = get_panel_reg (&gc); + const char *device_guid = NULL; + HANDLE hand; + char guid_buffer[256]; + char device_path[256]; + + at_least_one_tap_win32 (tap_reg); + + if (dev_node) + { + /* Get the device GUID for the device specified with --dev-node. */ + device_guid = get_device_guid (dev_node, guid_buffer, sizeof (guid_buffer), tap_reg, panel_reg, &gc); + + if (!device_guid) + msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node); + + /* Open Windows TAP-Win32 adapter */ + openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAPSUFFIX); + + hand = CreateFile ( + device_path, + MAXIMUM_ALLOWED, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0 + ); + + if (hand == INVALID_HANDLE_VALUE) + msg (M_ERR, "CreateFile failed on TAP device: %s", device_path); + + tap_allow_nonadmin_access_handle (device_path, hand); + CloseHandle (hand); + } + else + { + int device_number = 0; + + /* Try opening all TAP devices */ + while (true) + { + device_guid = get_unspecified_device_guid (device_number, + guid_buffer, + sizeof (guid_buffer), + tap_reg, + panel_reg, + &gc); + + if (!device_guid) + break; + + /* Open Windows TAP-Win32 adapter */ + openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAPSUFFIX); + + hand = CreateFile ( + device_path, + MAXIMUM_ALLOWED, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0 + ); + + if (hand == INVALID_HANDLE_VALUE) + msg (M_WARN, "CreateFile failed on TAP device: %s", device_path); + else + { + tap_allow_nonadmin_access_handle (device_path, hand); + CloseHandle (hand); + } + + device_number++; + } + } + gc_free (&gc); +} + +/* * DHCP release/renewal */ @@ -2963,7 +3126,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 device_guid = get_device_guid (dev_node, guid_buffer, sizeof (guid_buffer), tap_reg, panel_reg, &gc); if (!device_guid) - msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node); + msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node); /* Open Windows TAP-Win32 adapter */ openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s", @@ -3070,17 +3233,41 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 if (tt->type == DEV_TYPE_TUN) { - in_addr_t ep[2]; - ep[0] = htonl (tt->local); - ep[1] = htonl (tt->remote_netmask); if (!tt->did_ifconfig_setup) { msg (M_FATAL, "ERROR: --dev tun also requires --ifconfig"); } - if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_POINT_TO_POINT, - ep, sizeof (ep), - ep, sizeof (ep), &len, NULL)) - msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun"); + + if (tt->topology == TOP_SUBNET) + { + in_addr_t ep[3]; + BOOL status; + + ep[0] = htonl (tt->local); + ep[1] = htonl (tt->local & tt->remote_netmask); + ep[2] = htonl (tt->remote_netmask); + + status = DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_TUN, + ep, sizeof (ep), + ep, sizeof (ep), &len, NULL); + + msg (status ? M_INFO : M_FATAL, "Set TAP-Win32 TUN subnet mode network/local/netmask = %s/%s/%s [%s]", + print_in_addr_t (ep[1], IA_NET_ORDER, &gc), + print_in_addr_t (ep[0], IA_NET_ORDER, &gc), + print_in_addr_t (ep[2], IA_NET_ORDER, &gc), + status ? "SUCCEEDED" : "FAILED"); + + } else { + + in_addr_t ep[2]; + ep[0] = htonl (tt->local); + ep[1] = htonl (tt->remote_netmask); + + if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_POINT_TO_POINT, + ep, sizeof (ep), + ep, sizeof (ep), &len, NULL)) + msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun"); + } } /* should we tell the TAP-Win32 driver to masquerade as a DHCP server as a means @@ -3096,7 +3283,14 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 /* At what IP address should the DHCP server masquerade at? */ if (tt->type == DEV_TYPE_TUN) { - ep[2] = htonl (tt->remote_netmask); + if (tt->topology == TOP_SUBNET) + { + const in_addr_t netmask_inv = ~tt->remote_netmask; + ep[2] = netmask_inv ? htonl ((tt->local | netmask_inv) - 1) : 0; + } + else + ep[2] = htonl (tt->remote_netmask); + if (tt->options.dhcp_masq_custom_offset) msg (M_WARN, "WARNING: because you are using '--dev tun' mode, the '--ip-win32 dynamic [offset]' option is ignoring the offset parameter"); } @@ -3186,7 +3380,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 (unsigned int)index, device_guid); else - msg (M_WARN, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s", + msg (D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s", (unsigned int)index, device_guid, (unsigned int)status, @@ -119,6 +119,9 @@ struct tuntap # define TUNNEL_TYPE(tt) ((tt) ? ((tt)->type) : DEV_TYPE_UNDEF) int type; /* DEV_TYPE_x as defined in proto.h */ +# define TUNNEL_TOPOLOGY(tt) ((tt) ? ((tt)->topology) : TOP_UNDEF) + int topology; /* one of the TOP_x values */ + bool did_ifconfig_setup; bool did_ifconfig; @@ -203,6 +206,7 @@ const char *guess_tuntap_dev (const char *dev, struct tuntap *init_tun (const char *dev, /* --dev option */ const char *dev_type, /* --dev-type option */ + int topology, /* one of the TOP_x values */ const char *ifconfig_local_parm, /* --ifconfig parm 1 */ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */ in_addr_t local_public, @@ -227,6 +231,8 @@ const char *dev_type_string (const char *dev, const char *dev_type); const char *ifconfig_options_string (const struct tuntap* tt, bool remote, bool disable, struct gc_arena *gc); +bool is_tun_p2p (const struct tuntap *tt); + /* * Inline functions */ @@ -292,6 +298,10 @@ void verify_255_255_255_252 (in_addr_t local, in_addr_t remote); const IP_ADAPTER_INFO *get_adapter_info_list (struct gc_arena *gc); const IP_ADAPTER_INFO *get_tun_adapter (const struct tuntap *tt, const IP_ADAPTER_INFO *list); + +const IP_ADAPTER_INFO *get_adapter_info (DWORD index, struct gc_arena *gc); +const IP_PER_ADAPTER_INFO *get_per_adapter_info (const DWORD index, struct gc_arena *gc); + bool is_adapter_up (const struct tuntap *tt, const IP_ADAPTER_INFO *list); bool is_ip_in_adapter_subnet (const IP_ADAPTER_INFO *ai, const in_addr_t ip, in_addr_t *highest_netmask); DWORD adapter_index_of_ip (const IP_ADAPTER_INFO *list, const in_addr_t ip, int *count); @@ -299,6 +309,8 @@ DWORD adapter_index_of_ip (const IP_ADAPTER_INFO *list, const in_addr_t ip, int void show_tap_win32_adapters (int msglev, int warnlev); void show_adapters (int msglev); +void tap_allow_nonadmin_access (const char *dev_node); + void show_valid_win32_tun_subnets (void); const char *tap_win32_getinfo (const struct tuntap *tt, struct gc_arena *gc); void tun_show_debug (struct tuntap *tt); |