diff --git a/.github/issue_template.md b/.github/issue_template.md index 3c844b45..e156436e 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,3 +1,38 @@ + + # Problem description 1. I ran openconnect-gp as follows: `openconnect --protocol=gp ` diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3218c8b1..db2d4dbd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,9 +10,9 @@ CentOS7/GnuTLS: 'pkgconfig(libproxy-1.0)' 'pkgconfig(liboath)' 'pkgconfig(stoken)' ocserv softhsm 'pkgconfig(uid_wrapper)' 'pkgconfig(socket_wrapper)' vpnc-script 'pkgconfig(libpskc)' 'pkgconfig(libpcsclite)' - java-devel-openjdk + java-devel-openjdk glibc-langpack-cs - ./autogen.sh - - ./configure --with-java + - ./configure --with-java CFLAGS=-g - make -j4 # XFAIL the auth-pkcs11 test because GnuTLS 3.3.8 doesn't support pin-value - make VERBOSE=1 -j4 check @@ -36,9 +36,9 @@ CentOS7/OpenSSL: 'pkgconfig(libproxy-1.0)' 'pkgconfig(liboath)' 'pkgconfig(stoken)' ocserv softhsm 'pkgconfig(uid_wrapper)' 'pkgconfig(socket_wrapper)' vpnc-script 'pkgconfig(libpskc)' 'pkgconfig(libpcsclite)' - java-devel-openjdk 'pkgconfig(libp11)' + java-devel-openjdk 'pkgconfig(libp11)' glibc-langpack-cs - ./autogen.sh - - ./configure --without-gnutls --with-openssl --with-java --without-openssl-version-check --enable-dtls-xfail --disable-dsa-tests + - ./configure --without-gnutls --with-openssl --with-java --without-openssl-version-check --enable-dtls-xfail --disable-dsa-tests CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -62,8 +62,9 @@ CentOS6/OpenSSL: ocserv softhsm 'pkgconfig(uid_wrapper)' 'pkgconfig(socket_wrapper)' vpnc-script 'pkgconfig(libpskc)' 'pkgconfig(libpcsclite)' java-devel-openjdk vpnc 'pkgconfig(libp11)' 'pkgconfig(p11-kit-1)' + glibc-langpack-cs - ./autogen.sh - - ./configure --with-java --without-openssl-version-check --enable-dtls-xfail + - ./configure --with-java --without-openssl-version-check --enable-dtls-xfail CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -84,9 +85,9 @@ Fedora/GnuTLS: 'pkgconfig(libproxy-1.0)' 'pkgconfig(liboath)' 'pkgconfig(stoken)' ocserv softhsm 'pkgconfig(uid_wrapper)' 'pkgconfig(socket_wrapper)' vpnc-script 'pkgconfig(libpskc)' 'pkgconfig(libpcsclite)' - java-devel-openjdk + java-devel-openjdk glibc-langpack-cs - ./autogen.sh - - ./configure --with-java + - ./configure --with-java CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -107,9 +108,9 @@ Fedora/GnuTLS/clang: 'pkgconfig(libproxy-1.0)' 'pkgconfig(liboath)' 'pkgconfig(stoken)' ocserv softhsm 'pkgconfig(uid_wrapper)' 'pkgconfig(socket_wrapper)' vpnc-script 'pkgconfig(libpskc)' 'pkgconfig(libpcsclite)' - java-devel-openjdk clang + java-devel-openjdk clang glibc-langpack-cs - ./autogen.sh - - ./configure --with-java CC=clang + - ./configure --with-java CC=clang CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -130,10 +131,10 @@ Fedora/OpenSSL: 'pkgconfig(libproxy-1.0)' 'pkgconfig(liboath)' 'pkgconfig(stoken)' ocserv softhsm 'pkgconfig(uid_wrapper)' 'pkgconfig(socket_wrapper)' vpnc-script 'pkgconfig(libpskc)' 'pkgconfig(libpcsclite)' - java-devel-openjdk 'pkgconfig(libp11)' + java-devel-openjdk 'pkgconfig(libp11)' glibc-langpack-cs - dnf --enablerepo=updates-testing update -y libp11\* gnutls - ./autogen.sh - - ./configure --without-gnutls --with-openssl --without-openssl-version-check --disable-dsa-tests + - ./configure --without-gnutls --with-openssl --without-openssl-version-check --disable-dsa-tests CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -154,10 +155,10 @@ Fedora/OpenSSL/clang: 'pkgconfig(libproxy-1.0)' 'pkgconfig(liboath)' 'pkgconfig(stoken)' ocserv softhsm 'pkgconfig(uid_wrapper)' 'pkgconfig(socket_wrapper)' vpnc-script 'pkgconfig(libpskc)' 'pkgconfig(libpcsclite)' - java-devel-openjdk 'pkgconfig(libp11)' clang + java-devel-openjdk 'pkgconfig(libp11)' clang glibc-langpack-cs - dnf --enablerepo=updates-testing update -y libp11\* gnutls - ./autogen.sh - - ./configure CC=clang --without-gnutls --with-openssl --without-openssl-version-check --disable-dsa-tests + - ./configure CC=clang --without-gnutls --with-openssl --without-openssl-version-check --disable-dsa-tests CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -178,7 +179,7 @@ MinGW32/GnuTLS: - mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc - echo ':DOSWin:M::MZ::/usr/bin/wine:' > /proc/sys/fs/binfmt_misc/register - ./autogen.sh - - mingw32-configure + - mingw32-configure CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -199,7 +200,7 @@ MinGW32/OpenSSL: - mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc - echo ':DOSWin:M::MZ::/usr/bin/wine:' > /proc/sys/fs/binfmt_misc/register - ./autogen.sh - - mingw32-configure --without-gnutls --with-openssl --without-openssl-version-check + - mingw32-configure --without-gnutls --with-openssl --without-openssl-version-check CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -220,7 +221,7 @@ MinGW64/GnuTLS: - mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc - echo ':DOSWin:M::MZ::/usr/bin/wine:' > /proc/sys/fs/binfmt_misc/register - ./autogen.sh - - mingw64-configure + - mingw64-configure CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: @@ -241,7 +242,7 @@ MinGW64/OpenSSL: - mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc - echo ':DOSWin:M::MZ::/usr/bin/wine:' > /proc/sys/fs/binfmt_misc/register - ./autogen.sh - - mingw64-configure --without-gnutls --with-openssl --without-openssl-version-check + - mingw64-configure --without-gnutls --with-openssl --without-openssl-version-check CFLAGS=-g - make -j4 - make VERBOSE=1 -j4 check tags: diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..937aad59 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,39 @@ +notifications: + email: + false + +dist: trusty +sudo: required + +language: c +compiler: + - gcc + +services: + - docker + +jobs: + include: + - stage: app build + env: MAKEFLAGS="-j 2" + before_script: + - sudo apt-get update -qq + - sudo apt-get install -qq build-essential autoconf automake libtool pkg-config + vpnc-scripts + gettext libproxy-dev libxml2-dev liblz4-1 liblz4-dev libstoken-dev liboath-dev + libgnutls28-dev # actually GnuTLS 3.2.11 ¯\_(ツ)_/¯ + script: + - ./autogen.sh + - ./configure + - make VERBOSE=1 version.c + - make + - make VERBOSE=1 -j4 check + - stage: docker build + services: + - docker + before_script: + - docker build -t openconnect . + script: + - docker run openconnect "/openconnect/openconnect" "-V"| grep gp + # This last grep should be changed if this goes upstream, as it + # only checks for GlobalProtect support availability. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..cbebbe90 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM debian:9-slim as builder +WORKDIR /openconnect +RUN apt update \ + && apt install -y \ + build-essential \ + gettext \ + autoconf \ + automake \ + libproxy-dev \ + libxml2-dev \ + libtool \ + vpnc-scripts \ + pkg-config \ + libgnutls28-dev \ + git \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +ADD . . +RUN ./autogen.sh +RUN ./configure +RUN make + +#FROM debian:9-slim +#WORKDIR /openconnect +#COPY --from=builder /openconnect . +#RUN make install +#RUN ldconfig diff --git a/Makefile.am b/Makefile.am index 95fa2f4c..bcd8f5b3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,7 @@ library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c m lib_srcs_cisco = auth.c cstp.c lib_srcs_juniper = oncp.c lzo.c auth-juniper.c lib_srcs_globalprotect = gpst.c auth-globalprotect.c -lib_srcs_gnutls = gnutls.c gnutls_pkcs12.c gnutls_tpm.c +lib_srcs_gnutls = gnutls.c gnutls_tpm.c lib_srcs_openssl = openssl.c openssl-pkcs11.c lib_srcs_win32 = tun-win32.c sspi.c lib_srcs_posix = tun.c diff --git a/PAN_GlobalProtect_protocol_doc.md b/PAN_GlobalProtect_protocol_doc.md index d1a19c52..3f88ecf9 100644 --- a/PAN_GlobalProtect_protocol_doc.md +++ b/PAN_GlobalProtect_protocol_doc.md @@ -1,4 +1,8 @@ -This is an anonymized log of the authentication, configuration, tunnel data transfer, and logout interactions between a [PAN](http://www.paloaltonetworks.com) GlobalProtect VPN server and client (Windows client, v3.0.1-10). +This is an anonymized log of the authentication, configuration, tunnel data transfer, and logout interactions between a +[PAN](http://www.paloaltonetworks.com) GlobalProtect VPN server and client. The logs below are based on the official Windows +client, v3.0.1-10, with some updates from v4.0.5-8. + +Client version 4.0 [adds IPv6 support](https://live.paloaltonetworks.com/t5/Colossal-Event-Blog/New-GlobalProtect-4-0-announced-with-IPv6-support/ba-p/141593) and SAML authentication support. The correct user-agent (`User-Agent: PAN Globalprotect`) is **required** for all HTTP interactions with the GlobalProtect VPN. It treats any other user-agent as a web browser, not a VPN client. @@ -38,6 +42,20 @@ portal-prelogonuserauthcookie: empty host-id: deadbeef-dead-beef-dead-beefdeadbeef ``` +New parameters sent by Windows client v4.0.5-8: + +``` +clientgpversion: 4.0.5-8 +prelogin-cookie: +ipv6-support: yes +client-ip: 34.56.78.90 +client-ipv6: . +preferred-ipv6: +``` + +The `client-ip{,v6}` parameters refer to the client's _external_ internet-facing IP address, while `preferred-ip{,v6}` parameters +refer to the expected/desired addresses within the VPN. + Successful login response ========================= @@ -70,11 +88,20 @@ In order to handle the getconfig, tunnel-connect, and logon requests properly (d tunnel -1 4100 - preferred ip address as provided above + preferred IP address as sent in request ``` +Windows client v4.0.5-8 receives additional input-parroting arguments at the end: + +```xml + portal-userauthcookie as sent in request + prelogon-userauthcookie as sent in request + preferred IPv6 address as sent in request +``` + + getconfig request ================= @@ -108,6 +135,17 @@ enc-algo: aes-256-gcm,aes-128-gcm,aes-128-cbc, hmac-algo: sha1, ``` +Windows client v4.0.5-8 adds additional parameters at the end: + +```xml +app-version: 4.0.5-8 +addr1-v6-1: f00f::/16 +addr1-v6-2: f00f:dead:beef::dead:beef/128 +preferred-ipv6: +hmac-algo: sha1,. +``` + + Response #2 (getconfig) ======================= @@ -190,14 +228,22 @@ In the back-and-forth flows shown below, `<` means sent by the gateway, `>` mean ### ESP-over-UDP -Uses the keying information obtained in response to the `getconfig` request. In order to initiate the connection, the client sends 3 ESP-encapsulated ICMP request ("ping") packets to the gateway. They are sent _from_ the client's in-VPN IP address _to_ the IP address specified by the `` from the `getconfig` response (this is normally the same as the gateway's **public** IP address, but is sometimes a VPN-internal address ¯\\\_(ツ)\_/¯). These ICMP request packets include the following magic payload: +Uses the keying information obtained in response to the `getconfig` request. In order to initiate the connection, the client sends 3 ICMP request ("ping") packets to the gateway. - "monitor\x00\x00pan ha 0123456789:;<=>? !\"#$%&\'()*+,-./\x10\x11\x12\x13\x14\x15\x16\x18" - "monitor\x00\x00pan ha " (first 16 bytes) +* These packets are ESP-encapsulated +* These packets are sent _from_ **the client's in-VPN IP address** _to_ **the IP address specified by the `` from + the `getconfig` response**. + * The destination address is usually the same as the gateway's **public** internet-facing IP address, but sometimes it is a + VPN-internal address ¯\\\_(ツ)\_/¯ +* These ICMP request packets include the following magic payload — though only the first 16 bytes of the payload appear + to be necessary to elicit a response from the gateway. -Only the first 16 bytes of the payload appear to be necessary to elicit a response from the gateway. Once the gateway has responded with a corresponding ICMP reply, the client and server send and receive arbitrary ESP-encapsulated traffic. + "monitor\x00\x00pan ha 0123456789:;<=>? !\"#$%&\'()*+,-./\x10\x11\x12\x13\x14\x15\x16\x18" + "monitor\x00\x00pan ha " (first 16 bytes) -The client continues to periodically send the "magic ping" packets as a keepalive. +* Once the gateway has responded with a corresponding ICMP reply, the client and server send and receive arbitrary + ESP-encapsulated traffic. +* The client continues to periodically send the same "magic ping" packets as a keepalive. ### SSL vpn tunnel @@ -264,5 +310,9 @@ Successful logout response company domain name Myusername DEADBEEF01 + + + + -``` \ No newline at end of file +``` diff --git a/README.md b/README.md index 24b50a17..77a90744 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,39 @@ +# OpenConnect with PAN GlobalProtect support + +[![Build Status](https://api.travis-ci.org/dlenski/openconnect.png)](https://travis-ci.org/dlenski/openconnect) + +# Table of Contents + + * [What is this?](#what-is-this) + * [Feedback and troubleshooting](#feedback-and-troubleshooting) + * [Installation](#installation) + * [Building from source on Linux](#building-from-source-on-linux) + * [Building on the Mac](#building-on-the-mac) + * [Connecting](#connecting) + * [Docker](#docker) + * [Portal vs. gateway servers](#portal-vs-gateway-servers) + * [HIP report submission](#hip-report-submission) + * [TODO](#todo) + # What is this? This is a modified version of the fantastic open-source VPN client -[OpenConnect](https://infradead.org/openconnect) which supports the +[OpenConnect](https://www.infradead.org/openconnect) which supports the PAN GlobalProtect VPN in its native modes (SSL and [ESP](http://wikipedia.org/wiki/Encapsulating_Security_Payload))—with no assistance or cooperation needed from your VPN administrators. -## Feedback and troubleshooting - -This is a [work in progress](http://lists.infradead.org/pipermail/openconnect-devel/2016-October/004035.html). +I began developing it [in October 2016](http://lists.infradead.org/pipermail/openconnect-devel/2016-October/004035.html), +and started using it for "real work" almost immediately. It has become +increasingly polished since then. -That said, I've been using it for real work for many weeks, and it works very well for me. +## Feedback and troubleshooting -Having other people test it would be awesome and I welcome your -feedback! Please report any problems here on Github rather than -bothering the OpenConnect mailing list, since this is *not part of any -official OpenConnect release*. +GlobalProtect support is *not yet part of any official OpenConnect release* +(but see discussions on [official mailing list](https://lists.infradead.org/mailman/listinfo/openconnect-devel)). +Keep this in mind when discussing GlobalProtect issues on the mailing list. -If you are having trouble +Please report any problems as Github issues. If you are having trouble authenticating to your GlobalProtect server, please run OpenConnect with the `--dump -vvv` flags to dump the authentication flow; please compare the back-and-forth configuration requests to [this anonymized @@ -25,20 +41,25 @@ transcript](PAN_GlobalProtect_protocol_doc.md) and include information about relevant differences in your issue report. -## Installation +**Bonus points:** If your VPN uses a weird authentication flow, please +check out the Gist where I wrote a [quick-and-dirty "GlobalProtect server +simulator"](https://gist.github.com/dlenski/08359391270337f7894b1e2a97d0d9a9) in +Python. It's fairly straightforward to understand, and if you can +modify it to reproduce the authentication flow used by your VPN, it'll +make it a whole lot easier to add support. -### Ubuntu +## Installation -Ubuntu 16.04 users can install from [this PPA](https://launchpad.net/~lenski/+archive/ubuntu/openconnect-gp). +Please refer to the [build requirements for the official releases of OpenConnect](https://www.infradead.org/openconnect/building.html). **This version has the exact same build dependencies as OpenConnect v7.06**; modern versions of `autoconf`, `automake`, `gcc`, `libxml`, etc. ### Building from source on Linux -Please refer to the [build requirements for the official releases of OpenConnect](http://www.infradead.org/openconnect/building.html). **This version has the exact same build dependencies as OpenConnect v7.06**; modern versions of `autoconf`, `automake`, `gcc`, `libxml`, etc. - Under Debian-based or Ubuntu-based distributions, this should install the requirements: ```sh -$ sudo apt-get install build-essential autoconf automake libgnutls-dev libproxy-dev libxml2-dev libtool vpnc-scripts +$ sudo apt-get install build-essential gettext autoconf automake libproxy-dev libxml2-dev libtool vpnc-scripts pkg-config \ + libgnutls-dev # may be named libgnutls28-dev on some recent Debian/Ubuntu-based distros + ``` Once you have all the build dependencies installed, checkout and build the `globalprotect` branch from this repository. @@ -50,6 +71,7 @@ $ git checkout globalprotect $ ./autogen.sh $ ./configure $ make +$ sudo make install && sudo ldconfig ``` ### Building on the Mac @@ -57,7 +79,7 @@ $ make [Homebrew](https://brew.sh) is required. To build and install into `/usr/local`: ```sh -$ brew install pkg-config gettext gnutls lz4 +$ brew install pkg-config gettext gnutls lz4 automake $ export LIBTOOLIZE=glibtoolize $ ./autogen.sh $ ./configure --prefix=/usr/local --with-vpnc-script=/usr/local/etc/vpnc-script --disable-nls @@ -70,8 +92,7 @@ Please see [this Gist](https://gist.github.com/moklett/3170636) on how to set up ## Connecting Run openconnect like this to test it with your GlobalProtect VPN -provider. (Include `--certificate cert_with_privkey.pem` if your VPN -requires a client certificate and/or private key.) +provider. ```sh $ ./openconnect --protocol=gp server.company.com --dump -vvv @@ -80,36 +101,118 @@ Username: Password: ``` -Currently it only supports username, password, and optionally client -certificate authentication… since that's the only example I have. But -I'd welcome feedback if there are other authentication methods in use -out there. +It currently supports the following authentication mechanisms: + +* username and password +* "challenge"-based multi-factor authentication, wherein the server requests a secondary username and password after the first one +* TLS/SSL client certificate (include `--certificate cert_with_privkey.pem` if your VPN requires a _client_ certificate and private key) + +I'd welcome feedback on how to support other authentication methods in use with GlobalProtect. + +### Docker + +Building an openconnect Docker image is as easy as: + +```sh +$ docker build -t openconnect . +``` + +Then, you can run that docker image as a container: + +```sh +$ docker run -ti openconnect +/openconnect# ./openconnect --protocol=gp server.company.com +``` + +But that'll restrict the use of the tunnel to *inside* the container, +and maybe you want to use it system-wide. For that, you'll need a +privileged container making use of the host (you computer) network: + +```sh +$ docker run -ti --rm --privileged --net=host openconnect +/openconnect# ./openconnect --protocol=gp server.company.com +``` +Leave that container running, open another terminal, and you'll see a +newly created tun connection for your whole system to use. + +### HIP report submission + +The HIP ("Host Integrity Protection") mechanism is a security scanner +for PAN GlobalProtect VPNs, in the same vein as Cisco's CSD and +Juniper's Host Checker. + +The server requests a "HIP report" upon client connection, then the +client generates a "HIP report" XML file, and then the client uploads +it to the server. + +If all goes well, the client should have the expected level of access +to resources on the network after these steps are complete. At least +two things can go wrong: + +* Many GlobalProtect servers report that they require HIP reports, but + don't actually enforce this requirement. (For this reason, + OpenConnect _does not currently fail_ if a HIP report is required + but no HIP report script is provided.) +* Many GlobalProtect servers will claim that the HIP report was + accepted successfully but silently fail to enable the expected + network access, presumably because some aspect of the HIP report + contents were not approved. + +OpenConnect supports HIP report generation and submission by passing +the `--csd-wrapper=SCRIPT` argument with a shell script to generate a +HIP report in the format expected by the server. This shell script +must output the HIP report to standard output and exit successfully +(status code 0). -## Portal vs. gateway servers +An example [`hipreport.sh`](hipreport.sh) script is included in the +repository. Depending on how picky your GlobalProtect VPN is, it may +be necessary to spoof or alter some of the parameters of the HIP +report to match your GlobalProtect VPN's expectations as to its +contents. -For my VPN, the VPN tunnel server is the *same* as the VPN "portal" -server, but your VPN may differ. Try using both the "Portal address" -and the "GlobalProtect Gateway IP" shown in the Windows client with -OpenConnect: +### Portal vs. gateway servers + +For some GlobalProtect VPNs, there is a distinction between "portal" +and "gateway" servers, although in many GlobalProtect VPNs they run on +the _same_ server. "Portal" application URLs are found under `/global-protect`, +while "gateway" application URLs are under `/ssl-vpn`. + +Try using both the "Portal address" and the "GlobalProtect Gateway IP" shown in the Windows client with OpenConnect: [![GlobalProtect Windows client](https://i.stack.imgur.com/2JC9T.png)] -You can also use [`get-globalprotect-config.py`](get-globalprotect-config.py) to list the available gateway servers: +The official GlobalProtect VPN clients _always_ connect first via the +portal. The portal then sends a choice of one or more +gateways. However, this behavior is unnecessary, and adds an +additional delay in establishing a connection. + +Recent versions of `openconnect` can connect via _either_ the portal +endpoint _or_ the gateway endpoint: + +* If unspecified, the gateway endpoint is tried first, then the portal endpoint. +* For the gateway, include a URL-path starting with `/ssl-vpn` or specify `--usergroup=gateway` +* For the portal, include a URL-path starting with `/global-protect` or specify `--usergroup=portal` + * To choose a specific gateway from the portal without further prompting, add `--authgroup $GATEWAYNAME` + +Example of connecting via the portal interface and getting a choice of gateway servers: ```sh -$ ./get-globalprotect-config.py [--cert client_cert_with_privkey.pem] portal.company.com - ... - - 5 - - - - 1 - yes - WowSuchGateway - - - - - ... +$ openconnect --protocol=gp --usergroup=portal server.company.com +Please enter your username and password. +Username: +Password: +.. +Connected to HTTPS on server.company.com +3 gateway servers available: + NorthAmerica (vpn-na.company.com) + Europe (vpn-eu.company.com) + Asia (vpn-asia.company.com) +Please select GlobalProtect gateway. +GATEWAY: [NorthAmerica|Europe|Asia]: +... ``` + +# TODO + +* Support web-based/SAML-based authentication flows (see [pull #98](//github.com/dlenski/openconnect/issues/98) for preliminary work) +* Configure multi-stage build into the Dockerfile, to get a smaller Docker image diff --git a/auth-globalprotect.c b/auth-globalprotect.c index 392fd8b7..5cf64877 100644 --- a/auth-globalprotect.c +++ b/auth-globalprotect.c @@ -27,8 +27,17 @@ void gpst_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *b http_common_headers(vpninfo, buf); } -/* our "auth form" always has a username and password or challenge */ -static struct oc_auth_form *auth_form(struct openconnect_info *vpninfo, char *prompt, char *auth_id) +/* The GlobalProtect auth form always has two visible fields: + * 1) username + * 2) one secret value: + * - normal account password + * - "challenge" (2FA) password, along with form name in auth_id + * - cookie from external authentication flow (INSTEAD OF password) + * + * This function steals the value of auth_id and prompt and username for + * use in the auth form; pw_or_cookie_field is NOT stolen. + */ +static struct oc_auth_form *auth_form(struct openconnect_info *vpninfo, char *prompt, char *auth_id, char *username, char *pw_or_cookie_field) { static struct oc_auth_form *form; static struct oc_form_opt *opt, *opt2; @@ -37,22 +46,27 @@ static struct oc_auth_form *auth_form(struct openconnect_info *vpninfo, char *pr if (!form) return NULL; - if (prompt) form->message = strdup(prompt); - form->auth_id = strdup(auth_id ? : "_gateway"); + form->message = prompt ? : strdup(_("Please enter your username and password")); + form->auth_id = auth_id ? : strdup("_default"); opt = form->opts = calloc(1, sizeof(*opt)); if (!opt) return NULL; opt->name=strdup("user"); opt->label=strdup(_("Username: ")); - opt->type = OC_FORM_OPT_TEXT; - opt->flags = OC_FORM_OPT_FILL_USERNAME; + if (username) { + opt->type = OC_FORM_OPT_HIDDEN; + opt->_value = username; + } else { + opt->type = OC_FORM_OPT_TEXT; + opt->flags = OC_FORM_OPT_FILL_USERNAME; + } opt2 = opt->next = calloc(1, sizeof(*opt)); if (!opt2) return NULL; - opt2->name = strdup("passwd"); - opt2->label = auth_id ? strdup(_("Challenge: ")) : strdup(_("Password: ")); + opt2->name = strdup(pw_or_cookie_field ? : "passwd"); + asprintf(&opt2->label, "%s: ", auth_id ? _("Challenge") : (pw_or_cookie_field ? : _("Password"))); opt2->type = vpninfo->token_mode!=OC_TOKEN_MODE_NONE ? OC_FORM_OPT_TOKEN : OC_FORM_OPT_PASSWORD; opt2->flags = OC_FORM_OPT_FILL_PASSWORD; @@ -79,7 +93,7 @@ static const struct gp_login_arg gp_login_args[] = { [10] = { .opt="unknown-arg10", .show=1 }, [11] = { .opt="unknown-arg11", .show=1 }, [12] = { .opt="connection-type", .err_missing=1, .check="tunnel" }, - [13] = { .opt="minus1", .err_missing=1, .check="-1" }, + [13] = { .opt="password-expiration-days", .show=1 }, /* days until password expires, if not -1 */ [14] = { .opt="clientVer", .err_missing=1, .check="4100" }, [15] = { .opt="preferred-ip", .save=1 }, }; @@ -95,6 +109,9 @@ static int parse_login_xml(struct openconnect_info *vpninfo, xmlNode *xml_node) goto err_out; xml_node = xml_node->children; + while (xml_node && xml_node->type != XML_ELEMENT_NODE) + xml_node = xml_node->next; + if (!xmlnode_is_named(xml_node, "application-desc")) goto err_out; @@ -103,13 +120,16 @@ static int parse_login_xml(struct openconnect_info *vpninfo, xmlNode *xml_node) if (!arg->opt) continue; + while (xml_node && xml_node->type != XML_ELEMENT_NODE) + xml_node = xml_node->next; + if (!xml_node) value = NULL; else if (!xmlnode_is_named(xml_node, "argument")) goto err_out; else { value = (const char *)xmlNodeGetContent(xml_node); - if (value && (!strlen(value) || !strcmp(value, "(null)"))) { + if (value && (!strlen(value) || !strcmp(value, "(null)") || !strcmp(value, "-1"))) { free((void *)value); value = NULL; } @@ -133,10 +153,10 @@ static int parse_login_xml(struct openconnect_info *vpninfo, xmlNode *xml_node) append_opt(cookie, arg->opt, value); free((void *)value); } + append_opt(cookie, "computer", vpninfo->localname); vpninfo->cookie = strdup(cookie->data); - buf_free(cookie); - return 0; + return buf_free(cookie); err_out: free((void *)value); @@ -146,109 +166,135 @@ static int parse_login_xml(struct openconnect_info *vpninfo, xmlNode *xml_node) static int parse_portal_xml(struct openconnect_info *vpninfo, xmlNode *xml_node) { - static struct oc_auth_form form = {.message=(char *)"Please select GlobalProtect gateway.", .auth_id=(char *)"_portal"}; - - xmlNode *x; + struct oc_auth_form *form; + xmlNode *x = NULL; struct oc_form_opt_select *opt; - struct oc_text_buf *buf; + struct oc_text_buf *buf = NULL; int max_choices = 0, result; char *portal = NULL; - opt = calloc(1, sizeof(*opt)); - if (!opt) + form = calloc(1, sizeof(*form)); + if (!form) return -ENOMEM; + + form->message = strdup(_("Please select GlobalProtect gateway.")); + form->auth_id = strdup("_portal"); + + opt = form->authgroup_opt = calloc(1, sizeof(*opt)); + if (!opt) { + result = -ENOMEM; + goto out; + } opt->form.type = OC_FORM_OPT_SELECT; opt->form.name = strdup("gateway"); opt->form.label = strdup(_("GATEWAY:")); + form->opts = (void *)opt; /* The portal contains a ton of stuff, but basically none of it is useful to a VPN client * that wishes to give control to the client user, as opposed to the VPN administrator. * The exception is the list of gateways in policy/gateways/external/list */ - if (xmlnode_is_named(xml_node, "policy")) - for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) - if (xmlnode_is_named(xml_node, "portal-name")) - portal = (char *)xmlNodeGetContent(xml_node); - else if (xmlnode_is_named(xml_node, "gateways")) - for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) - if (xmlnode_is_named(xml_node, "external")) - for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) - if (xmlnode_is_named(xml_node, "list")) - goto gateways; + if (xmlnode_is_named(xml_node, "policy")) { + for (x = xml_node->children, xml_node = NULL; x; x = x->next) { + if (xmlnode_is_named(x, "portal-name")) + portal = (char *)xmlNodeGetContent(x); + else if (xmlnode_is_named(x, "gateways")) + xml_node = x; + } + } + + if (xml_node) { + for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) + if (xmlnode_is_named(xml_node, "external")) + for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) + if (xmlnode_is_named(xml_node, "list")) + goto gateways; + } result = -EINVAL; - free(portal); goto out; gateways: - buf = buf_alloc(); - buf_append(buf, "\n \n"); - if (portal) { - buf_append(buf, " %s%s", portal, vpninfo->hostname); - if (vpninfo->port!=443) - buf_append(buf, ":%d", vpninfo->port); - buf_append(buf, "/global-protect\n"); + if (vpninfo->write_new_config) { + buf = buf_alloc(); + buf_append(buf, "\n \n"); + if (portal) { + buf_append(buf, " "); + buf_append_xmlescaped(buf, portal); + buf_append(buf, "%s", vpninfo->hostname); + if (vpninfo->port!=443) + buf_append(buf, ":%d", vpninfo->port); + buf_append(buf, "/global-protect\n"); + } } - free(portal); /* first, count the number of gateways */ - for (x = xml_node->children; x; x=x->next) + for (x = xml_node->children; x; x = x->next) if (xmlnode_is_named(x, "entry")) max_choices++; - opt->choices = calloc(1, max_choices * sizeof(struct oc_choice *)); + opt->choices = calloc(max_choices, sizeof(opt->choices[0])); if (!opt->choices) { - free_opt((struct oc_form_opt *)opt); - return -ENOMEM; + result = -ENOMEM; + goto out; } /* each entry looks like Label */ vpn_progress(vpninfo, PRG_INFO, _("%d gateway servers available:\n"), max_choices); - for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) { + for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) { if (xmlnode_is_named(xml_node, "entry")) { struct oc_choice *choice = calloc(1, sizeof(*choice)); if (!choice) { - free_opt((struct oc_form_opt *)opt); - return -ENOMEM; + result = -ENOMEM; + goto out; } xmlnode_get_prop(xml_node, "name", &choice->name); for (x = xml_node->children; x; x=x->next) - if (xmlnode_is_named(x, "description")) - buf_append(buf, " %s%s/ssl-vpn\n", - choice->label = (char *)xmlNodeGetContent(x), - choice->name); + if (xmlnode_is_named(x, "description")) { + choice->label = (char *)xmlNodeGetContent(x); + if (vpninfo->write_new_config) { + buf_append(buf, " "); + buf_append_xmlescaped(buf, choice->label); + buf_append(buf, "%s/ssl-vpn\n", + choice->name); + } + } opt->choices[opt->nr_choices++] = choice; vpn_progress(vpninfo, PRG_INFO, _(" %s (%s)\n"), - choice->label, choice->name); + choice->label, choice->name); } } - buf_append(buf, " \n\n"); - if (vpninfo->write_new_config) - result = vpninfo->write_new_config(vpninfo->cbdata, buf->data, buf->pos); - buf_free(buf); + if (vpninfo->write_new_config) { + buf_append(buf, " \n\n"); + if ((result = buf_error(buf))) + goto out; + if ((result = vpninfo->write_new_config(vpninfo, buf->data, buf->pos))) + goto out; + } - /* process static auth form to select gateway */ - form.opts = (struct oc_form_opt *)(form.authgroup_opt = opt); - result = process_auth_form(vpninfo, &form); + /* process auth form to select gateway */ + result = process_auth_form(vpninfo, form); if (result != OC_FORM_RESULT_NEWGROUP) goto out; /* redirect to the gateway (no-op if it's the same host) */ - if ((vpninfo->redirect_url = malloc(strlen(vpninfo->authgroup) + 9)) == NULL) { + free(vpninfo->redirect_url); + if (asprintf(&vpninfo->redirect_url, "https://%s", vpninfo->authgroup) == 0) { result = -ENOMEM; goto out; } - sprintf(vpninfo->redirect_url, "https://%s", vpninfo->authgroup); result = handle_redirect(vpninfo); out: - free_opt((struct oc_form_opt *)opt); + buf_free(buf); + free(portal); + free_auth_form(form); return result; } -static int gpst_login(struct openconnect_info *vpninfo, int portal) +static int gpst_login(struct openconnect_info *vpninfo, int portal, const char *pw_or_cookie_field) { int result; @@ -256,8 +302,8 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal) struct oc_text_buf *request_body = buf_alloc(); const char *request_body_type = "application/x-www-form-urlencoded"; const char *method = "POST"; - char *xml_buf=NULL, *orig_path, *orig_ua; - char *prompt=_("Please enter your username and password"), *auth_id=NULL; + char *xml_buf=NULL, *orig_path; + char *prompt=NULL, *auth_id=NULL, *username=NULL; #ifdef HAVE_LIBSTOKEN /* Step 1: Unlock software token (if applicable) */ @@ -268,7 +314,7 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal) } #endif - form = auth_form(vpninfo, prompt, auth_id); + form = auth_form(vpninfo, prompt, auth_id, username, pw_or_cookie_field); if (!form) return -ENOMEM; @@ -279,7 +325,7 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal) if (result) goto out; - redo_gateway: + reuse_whole_form: buf_truncate(request_body); /* generate token code if specified */ @@ -293,37 +339,54 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal) /* submit gateway login (ssl-vpn/login.esp) or portal config (global-protect/getconfig.esp) request */ buf_truncate(request_body); buf_append(request_body, "jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:"); + if (!strcmp(vpninfo->platname, "win")) + append_opt(request_body, "clientos", "Windows"); + else + append_opt(request_body, "clientos", vpninfo->platname); append_opt(request_body, "server", vpninfo->hostname); append_opt(request_body, "computer", vpninfo->localname); + if (vpninfo->ip_info.addr) + append_opt(request_body, "preferred-ip", vpninfo->ip_info.addr); if (form->auth_id && form->auth_id[0]!='_') append_opt(request_body, "inputStr", form->auth_id); append_form_opts(vpninfo, form, request_body); + if ((result = buf_error(request_body))) + goto out; orig_path = vpninfo->urlpath; - orig_ua = vpninfo->useragent; - vpninfo->useragent = (char *)"PAN GlobalProtect"; vpninfo->urlpath = strdup(portal ? "global-protect/getconfig.esp" : "ssl-vpn/login.esp"); result = do_https_request(vpninfo, method, request_body_type, request_body, &xml_buf, 0); free(vpninfo->urlpath); vpninfo->urlpath = orig_path; - vpninfo->useragent = orig_ua; /* Result could be either a JavaScript challenge or XML */ result = gpst_xml_or_error(vpninfo, result, xml_buf, portal ? parse_portal_xml : parse_login_xml, &prompt, &auth_id); if (result == -EAGAIN) { + reuse_username: + /* Steal and reuse username from first form */ + username = form->opts ? form->opts->_value : NULL; + form->opts->_value = NULL; free_auth_form(form); - form = auth_form(vpninfo, prompt, auth_id); + form = auth_form(vpninfo, prompt, auth_id, username, pw_or_cookie_field); + prompt = auth_id = username = NULL; if (!form) return -ENOMEM; - continue; } else if (portal && result == 0) { + /* Portal login succeeded; reuse same credentials to login to gateway, + * unless it was a challenge auth form, in which case we only + * reuse the username. + */ portal = 0; - goto redo_gateway; - } else if (result == -EACCES) /* Invalid username/password */ - continue; - else + if (form->auth_id && form->auth_id[0] != '_') + goto reuse_username; + else + goto reuse_whole_form; + } else if (result == -EACCES) { + /* Invalid username/password; reuse same form, but blank */ + nuke_opt_values(form->opts); + } else break; } @@ -336,19 +399,30 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal) int gpst_obtain_cookie(struct openconnect_info *vpninfo) { + char *pw_or_cookie_field = NULL; int result; + /* An alternate password/secret field may be specified in the "URL path". Known possibilities: + * /portal:portal-userauthcookie + * /gateway:prelogin-cookie + */ + if (vpninfo->urlpath + && (pw_or_cookie_field = strrchr(vpninfo->urlpath, ':'))!=NULL) { + *pw_or_cookie_field = '\0'; + pw_or_cookie_field++; + } + if (vpninfo->urlpath && (!strcmp(vpninfo->urlpath, "portal") || !strncmp(vpninfo->urlpath, "global-protect", 14))) { /* assume the server is a portal */ - return gpst_login(vpninfo, 1); + return gpst_login(vpninfo, 1, pw_or_cookie_field); } else if (vpninfo->urlpath && (!strcmp(vpninfo->urlpath, "gateway") || !strncmp(vpninfo->urlpath, "ssl-vpn", 7))) { /* assume the server is a gateway */ - return gpst_login(vpninfo, 0); + return gpst_login(vpninfo, 0, pw_or_cookie_field); } else { /* first try handling it as a gateway, then a portal */ - result = gpst_login(vpninfo, 0); + result = gpst_login(vpninfo, 0, pw_or_cookie_field); if (result == -EEXIST) { - result = gpst_login(vpninfo, 1); + result = gpst_login(vpninfo, 1, pw_or_cookie_field); if (result == -EEXIST) vpn_progress(vpninfo, PRG_ERR, _("Server is neither a GlobalProtect portal nor a gateway.\n")); } @@ -358,7 +432,7 @@ int gpst_obtain_cookie(struct openconnect_info *vpninfo) int gpst_bye(struct openconnect_info *vpninfo, const char *reason) { - char *orig_path, *orig_ua; + char *orig_path; int result; struct oc_text_buf *request_body = buf_alloc(); const char *request_body_type = "application/x-www-form-urlencoded"; @@ -376,23 +450,21 @@ int gpst_bye(struct openconnect_info *vpninfo, const char *reason) * * Don't blame me. I didn't design this. */ - append_opt(request_body, "computer", vpninfo->localname); - buf_append(request_body, "&%s", vpninfo->cookie); + buf_append(request_body, "%s", vpninfo->cookie); + if ((result = buf_error(request_body))) + goto out; /* We need to close and reopen the HTTPS connection (to kill * the tunnel session) and submit a new HTTPS request to * logout. */ orig_path = vpninfo->urlpath; - orig_ua = vpninfo->useragent; - vpninfo->useragent = (char *)"PAN GlobalProtect"; vpninfo->urlpath = strdup("ssl-vpn/logout.esp"); openconnect_close_https(vpninfo, 0); result = do_https_request(vpninfo, method, request_body_type, request_body, &xml_buf, 0); free(vpninfo->urlpath); vpninfo->urlpath = orig_path; - vpninfo->useragent = orig_ua; /* logout.esp returns HTTP status 200 and when * successful, and all manner of malformed junk when unsuccessful. @@ -403,6 +475,7 @@ int gpst_bye(struct openconnect_info *vpninfo, const char *reason) else vpn_progress(vpninfo, PRG_INFO, _("Logout successful\n")); +out: buf_free(request_body); free(xml_buf); return result; diff --git a/auth-juniper.c b/auth-juniper.c index 4b889d6b..30ceb3ae 100644 --- a/auth-juniper.c +++ b/auth-juniper.c @@ -47,7 +47,6 @@ void oncp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *b { http_common_headers(vpninfo, buf); - buf_append(buf, "Connection: close\r\n"); // buf_append(buf, "Content-Length: 256\r\n"); buf_append(buf, "NCP-Version: 3\r\n"); // buf_append(buf, "Accept-Encoding: gzip\r\n"); @@ -77,7 +76,7 @@ static int oncp_can_gen_tokencode(struct openconnect_info *vpninfo, if (strcmp(form->auth_id, "frmDefender") && strcmp(form->auth_id, "frmNextToken") && - strcmp(form->auth_id, "ftmTotpToken")) + strcmp(form->auth_id, "frmTotpToken")) return -EINVAL; return can_gen_tokencode(vpninfo, form, opt); @@ -122,6 +121,13 @@ static int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_for ret = -ENOMEM; goto out; } + } else if (!strcasecmp(type, "username")) { + opt->type = OC_FORM_OPT_TEXT; + xmlnode_get_prop(node, "name", &opt->name); + if (asprintf(&opt->label, "%s:", opt->name) == -1) { + ret = -ENOMEM; + goto out; + } } else if (!strcasecmp(type, "submit")) { xmlnode_get_prop(node, "name", &opt->name); if (opt->name && (!strcmp(opt->name, submit_button) || diff --git a/configure.ac b/configure.ac index 2643c631..0762b06c 100644 --- a/configure.ac +++ b/configure.ac @@ -286,18 +286,9 @@ fi # First, check if GnuTLS exists and is usable if test "$with_gnutls" = "yes" || test "$with_gnutls" = ""; then - PKG_CHECK_MODULES(GNUTLS, gnutls >= 2.12.16, - [if ! $PKG_CONFIG --atleast-version=2.12.16 gnutls; then - AC_MSG_WARN([Your GnuTLS is too old. At least v2.12.16 is required]) - elif test "$have_win" = "yes"; then - AC_MSG_CHECKING([for broken GnuTLS Windows versions]) - if $PKG_CONFIG --atleast-version=3.2.0 gnutls && - ! $PKG_CONFIG --atleast-version=3.2.10 gnutls; then - AC_MSG_RESULT([broken]) - else - AC_MSG_RESULT([OK]) - ssl_library=GnuTLS - fi + PKG_CHECK_MODULES(GNUTLS, gnutls, + [if ! $PKG_CONFIG --atleast-version=3.2.10 gnutls; then + AC_MSG_WARN([Your GnuTLS is too old. At least v3.2.10 is required]) else ssl_library=GnuTLS fi], [:]) @@ -466,64 +457,10 @@ case "$ssl_library" in oldcflags="$CFLAGS" LIBS="$LIBS $GNUTLS_LIBS" CFLAGS="$CFLAGS $GNUTLS_CFLAGS" - AC_CHECK_FUNC(gnutls_dtls_set_data_mtu, - [AC_DEFINE(HAVE_GNUTLS_DTLS_SET_DATA_MTU, 1, [From GnuTLS 3.0.20])], []) - AC_CHECK_FUNC(gnutls_pkcs11_get_raw_issuer, - [AC_DEFINE(HAVE_GNUTLS_PKCS11_GET_RAW_ISSUER, 1, [From GnuTLS 3.2.7])], []) - AC_CHECK_FUNC(gnutls_certificate_set_x509_system_trust, - [AC_DEFINE(HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST, 1, [From GnuTLS 3.0.20])], []) - if test "$ac_cv_func_gnutls_certificate_set_x509_system_trust" != "yes"; then - # We will need to tell GnuTLS the path to the system CA file. - if test "$with_system_cafile" = "yes" || test "$with_system_cafile" = ""; then - unset with_system_cafile - AC_MSG_CHECKING([For location of system CA trust file]) - for file in /etc/ssl/certs/ca-certificates.crt \ - /etc/pki/tls/cert.pem \ - /usr/local/share/certs/ca-root-nss.crt \ - /etc/ssl/cert.pem \ - /etc/ssl/ca-bundle.pem \ - ; do - if grep 'BEGIN CERTIFICATE-----' $file >/dev/null 2>&1; then - with_system_cafile=${file} - break - fi - done - AC_MSG_RESULT([${with_system_cafile-NOT FOUND}]) - elif test "$with_system_cafile" = "no"; then - AC_MSG_ERROR([You cannot disable the system CA certificate file.]) - fi - if test "$with_system_cafile" = ""; then - AC_MSG_ERROR([Unable to find a standard system CA certificate file.] - [Your GnuTLS requires a path to a CA certificate store. This is a file] - [which contains a list of the Certificate Authorities which are trusted.] - [Most distributions ship with this file in a standard location, but none] - [the known standard locations exist on your system. You should provide a] - [--with-system-cafile= argument to this configure script, giving the full] - [path to a default CA certificate file for GnuTLS to use. Also, please] - [send full details of your system, including 'uname -a' output and the] - [location of the system CA certificate store on your system, to the] - [openconnect-devel@lists.infradead.org mailing list.]) - fi - AC_DEFINE_UNQUOTED([DEFAULT_SYSTEM_CAFILE], ["$with_system_cafile"], [Location of System CA trust file]) - fi - AC_CHECK_FUNC(gnutls_cipher_set_iv, - [esp=yes], []) - AC_CHECK_FUNC(gnutls_pkcs12_simple_parse, - [AC_DEFINE(HAVE_GNUTLS_PKCS12_SIMPLE_PARSE, 1, [From GnuTLS 3.1.0])], []) - AC_CHECK_FUNC(gnutls_certificate_set_key, - [AC_DEFINE(HAVE_GNUTLS_CERTIFICATE_SET_KEY, 1, [From GnuTLS 3.0.4])], []) - AC_CHECK_FUNC(gnutls_pk_to_sign, - [AC_DEFINE(HAVE_GNUTLS_PK_TO_SIGN, 1, [From GnuTLS 3.1.0])], []) - AC_CHECK_FUNC(gnutls_pubkey_export2, - [AC_DEFINE(HAVE_GNUTLS_PUBKEY_EXPORT2, 1, [From GnuTLS 3.1.3])], []) - AC_CHECK_FUNC(gnutls_x509_crt_set_pin_function, - [AC_DEFINE(HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION, 1, [From GnuTLS 3.1.0])], []) - AC_CHECK_FUNC(gnutls_url_is_supported, - [AC_DEFINE(HAVE_GNUTLS_URL_IS_SUPPORTED, 1, [From GnuTLS 3.1.0])], []) + esp=yes + dtls=yes AC_CHECK_FUNC(gnutls_system_key_add_x509, [AC_DEFINE(HAVE_GNUTLS_SYSTEM_KEYS, 1, [From GnuTLS 3.4.0])], []) - AC_CHECK_FUNC(gnutls_session_set_premaster, - [dtls=yes], []) AC_CHECK_FUNC(gnutls_pkcs11_add_provider, [PKG_CHECK_MODULES(P11KIT, p11-kit-1, [AC_DEFINE(HAVE_P11KIT, 1, [Have. P11. Kit.]) diff --git a/cstp.c b/cstp.c index 5477c5c8..47e8e961 100644 --- a/cstp.c +++ b/cstp.c @@ -262,6 +262,11 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) buf_append(reqbuf, "X-CSTP-MTU: %d\r\n", mtu); buf_append(reqbuf, "X-CSTP-Address-Type: %s\r\n", vpninfo->disable_ipv6 ? "IPv4" : "IPv6,IPv4"); + /* Explicitly request the same IPv4 address on reconnect (or on + * initial connection if specified with the --request-ip option) + */ + if (old_addr) + buf_append(reqbuf, "X-CSTP-Address: %s\r\n", old_addr); if (!vpninfo->disable_ipv6) buf_append(reqbuf, "X-CSTP-Full-IPv6-Capability: true\r\n"); #ifdef HAVE_DTLS @@ -577,11 +582,20 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) mtu); } if (old_addr) { - if (strcmp(old_addr, vpninfo->ip_info.addr)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different Legacy IP address (%s != %s)\n"), - vpninfo->ip_info.addr, old_addr); - return -EINVAL; + /* XXX: if --request-ip option is used, we'll have old_addr!=NULL even on the + first connection attempt, but if old_netmask is also non-NULL then we know + it's a reconnect. */ + if (vpninfo->ip_info.addr==NULL || strcmp(old_addr, vpninfo->ip_info.addr)) { + if (!old_netmask) + vpn_progress(vpninfo, PRG_ERR, + _("Legacy IP address %s was requested, but server provided %s\n"), + old_addr, vpninfo->ip_info.addr); + else { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different Legacy IP address (%s != %s)\n"), + vpninfo->ip_info.addr, old_addr); + return -EINVAL; + } } } if (old_netmask) { @@ -729,7 +743,11 @@ static int cstp_reconnect(struct openconnect_info *vpninfo) int decompress_and_queue_packet(struct openconnect_info *vpninfo, int compr_type, unsigned char *buf, int len) { - struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->ip_info.mtu); + /* Some servers send us packets that are larger than + negotiated MTU after decompression. We reserve some extra + space to handle that */ + int receive_mtu = MAX(16384, vpninfo->ip_info.mtu); + struct pkt *new = malloc(sizeof(struct pkt) + receive_mtu); const char *comprname = ""; if (!new) @@ -746,7 +764,7 @@ int decompress_and_queue_packet(struct openconnect_info *vpninfo, int compr_type vpninfo->inflate_strm.avail_in = len - 4; vpninfo->inflate_strm.next_out = new->data; - vpninfo->inflate_strm.avail_out = vpninfo->ip_info.mtu; + vpninfo->inflate_strm.avail_out = receive_mtu; vpninfo->inflate_strm.total_out = 0; if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) { @@ -768,7 +786,7 @@ int decompress_and_queue_packet(struct openconnect_info *vpninfo, int compr_type } else if (compr_type == COMPR_LZS) { comprname = "LZS"; - new->len = lzs_decompress(new->data, vpninfo->ip_info.mtu, buf, len); + new->len = lzs_decompress(new->data, receive_mtu, buf, len); if (new->len < 0) { len = new->len; if (len == 0) @@ -781,7 +799,7 @@ int decompress_and_queue_packet(struct openconnect_info *vpninfo, int compr_type #ifdef HAVE_LZ4 } else if (compr_type == COMPR_LZ4) { comprname = "LZ4"; - new->len = LZ4_decompress_safe((void *)buf, (void *)new->data, len, vpninfo->ip_info.mtu); + new->len = LZ4_decompress_safe((void *)buf, (void *)new->data, len, receive_mtu); if (new->len <= 0) { len = new->len; if (len == 0) @@ -882,18 +900,21 @@ int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout) and add POLLOUT. As it is, though, it'll just chew CPU time in that fairly unlikely situation, until the write backlog clears. */ while (1) { - int len = MAX(16384, vpninfo->deflate_pkt_size ? : vpninfo->ip_info.mtu); - int payload_len; + /* Some servers send us packets that are larger than + negotiated MTU. We reserve some extra space to + handle that */ + int receive_mtu = MAX(16384, vpninfo->deflate_pkt_size ? : vpninfo->ip_info.mtu); + int len, payload_len; if (!vpninfo->cstp_pkt) { - vpninfo->cstp_pkt = malloc(sizeof(struct pkt) + len); + vpninfo->cstp_pkt = malloc(sizeof(struct pkt) + receive_mtu); if (!vpninfo->cstp_pkt) { vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n")); break; } } - len = ssl_nonblock_read(vpninfo, vpninfo->cstp_pkt->cstp.hdr, len + 8); + len = ssl_nonblock_read(vpninfo, vpninfo->cstp_pkt->cstp.hdr, receive_mtu + 8); if (!len) break; if (len < 0) diff --git a/esp.c b/esp.c index ae60269e..d51e44aa 100644 --- a/esp.c +++ b/esp.c @@ -23,8 +23,19 @@ #include #include #include +#ifdef __FreeBSD__ +#include +#include +#endif + +#ifdef _WIN32 +#include +#include +#include "win32-ipicmp.h" +#else #include #include +#endif #include "openconnect-internal.h" #include "lzo.h" @@ -248,22 +259,24 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout) int work_done = 0; int ret; + /* Some servers send us packets that are larger than negotiated + MTU, or lack the ability to negotiate MTU (see gpst.c). We + reserve some extra space to handle that */ + int receive_mtu = MAX(2048, vpninfo->ip_info.mtu + 256); + if (vpninfo->dtls_state == DTLS_SLEEPING) { - int when = vpninfo->new_dtls_started + vpninfo->dtls_attempt_period - time(NULL); - if (when <= 0 || vpninfo->dtls_need_reconnect) { + if (ka_check_deadline(timeout, time(NULL), vpninfo->new_dtls_started + vpninfo->dtls_attempt_period) + || vpninfo->dtls_need_reconnect) { vpn_progress(vpninfo, PRG_DEBUG, _("Send ESP probes\n")); if (vpninfo->proto->udp_send_probes) vpninfo->proto->udp_send_probes(vpninfo); - when = vpninfo->dtls_attempt_period; } - if (*timeout > when * 1000) - *timeout = when * 1000; } if (vpninfo->dtls_fd == -1) return 0; while (1) { - int len = vpninfo->ip_info.mtu + vpninfo->pkt_trailer; + int len = receive_mtu + vpninfo->pkt_trailer; int i; struct pkt *pkt; @@ -283,6 +296,7 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout) len); work_done = 1; + /* both supported algos (SHA1 and MD5) have 12-byte MAC lengths (RFC2403 and RFC2404) */ if (len <= sizeof(pkt->esp) + 12) continue; @@ -295,7 +309,7 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout) } else if (pkt->esp.spi == old_esp->spi && ntohl(pkt->esp.seq) + esp->seq < vpninfo->old_esp_maxseq) { vpn_progress(vpninfo, PRG_TRACE, - _("Consider SPI 0x%x, seq %u against outgoing ESP setup\n"), + _("Received ESP packet from old SPI 0x%x, seq %u\n"), (unsigned)ntohl(old_esp->spi), (unsigned)ntohl(pkt->esp.seq)); if (decrypt_esp_packet(vpninfo, old_esp, pkt)) continue; @@ -306,6 +320,11 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout) continue; } + /* Possible values of the Next Header field are: + 0x04: IP[v4]-in-IP + 0x05: supposed to mean Internet Stream Protocol + (XXX: but used for LZO compressed packets by Juniper) + 0x29: IPv6 encapsulation */ if (pkt->data[len - 1] != 0x04 && pkt->data[len - 1] != 0x29 && pkt->data[len - 1] != 0x05) { vpn_progress(vpninfo, PRG_ERR, @@ -345,8 +364,8 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout) } } if (pkt->data[len - 1] == 0x05) { - struct pkt *newpkt = malloc(sizeof(*pkt) + vpninfo->ip_info.mtu + vpninfo->pkt_trailer); - int newlen = vpninfo->ip_info.mtu; + struct pkt *newpkt = malloc(sizeof(*pkt) + receive_mtu + vpninfo->pkt_trailer); + int newlen = receive_mtu; if (!newpkt) { vpn_progress(vpninfo, PRG_ERR, _("Failed to allocate memory to decrypt ESP packet\n")); @@ -359,7 +378,7 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout) free(newpkt); continue; } - newpkt->len = vpninfo->ip_info.mtu - newlen; + newpkt->len = receive_mtu - newlen; vpn_progress(vpninfo, PRG_TRACE, _("LZO decompressed %d bytes into %d\n"), len - 2 - pkt->data[len-2], newpkt->len); @@ -454,7 +473,8 @@ void esp_close(struct openconnect_info *vpninfo) void esp_close_secret(struct openconnect_info *vpninfo) { esp_close(vpninfo); - vpninfo->dtls_state = DTLS_NOSECRET; + if (vpninfo->dtls_state > DTLS_DISABLED) + vpninfo->dtls_state = DTLS_NOSECRET; } void esp_shutdown(struct openconnect_info *vpninfo) diff --git a/globalprotect-challenge-login.py b/globalprotect-challenge-login.py index e592a2a2..0fb3d7ed 100755 --- a/globalprotect-challenge-login.py +++ b/globalprotect-challenge-login.py @@ -35,6 +35,10 @@ else: cert = None +if not args.verify: + from requests.packages.urllib3.exceptions import InsecureRequestWarning + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + s = requests.Session() s.headers['User-Agent'] = 'PAN GlobalProtect' s.cert = cert diff --git a/gnutls-dtls.c b/gnutls-dtls.c index 4b515a7b..7d15be6c 100644 --- a/gnutls-dtls.c +++ b/gnutls-dtls.c @@ -354,7 +354,6 @@ int dtls_try_handshake(struct openconnect_info *vpninfo) return -EIO; } -#ifdef HAVE_GNUTLS_DTLS_SET_DATA_MTU /* Make sure GnuTLS's idea of the MTU is sufficient to take a full VPN MTU (with 1-byte header) in a data record. */ err = gnutls_dtls_set_data_mtu(vpninfo->dtls_ssl, vpninfo->ip_info.mtu + 1); @@ -364,14 +363,6 @@ int dtls_try_handshake(struct openconnect_info *vpninfo) gnutls_strerror(err)); goto error; } -#else - /* If we don't have gnutls_dtls_set_data_mtu() then make sure - we leave enough headroom by adding the worst-case overhead. - We only support AES128-CBC and DES-CBC3-SHA anyway, so - working out the worst case isn't hard. */ - gnutls_dtls_set_mtu(vpninfo->dtls_ssl, - vpninfo->ip_info.mtu + DTLS_OVERHEAD); -#endif } vpninfo->dtls_state = DTLS_CONNECTED; diff --git a/gnutls-esp.c b/gnutls-esp.c index 916cbc7c..24d66201 100644 --- a/gnutls-esp.c +++ b/gnutls-esp.c @@ -166,6 +166,8 @@ int decrypt_esp_packet(struct openconnect_info *vpninfo, struct esp *esp, struct if (vpninfo->esp_replay_protect && verify_packet_seqno(vpninfo, esp, ntohl(pkt->esp.seq))) return -EINVAL; + else + esp->seq = ntohl(pkt->esp.seq) + 1; gnutls_cipher_set_iv(esp->cipher, pkt->esp.iv, sizeof(pkt->esp.iv)); diff --git a/gnutls.c b/gnutls.c index ea96cef5..baf1cd14 100644 --- a/gnutls.c +++ b/gnutls.c @@ -27,12 +27,6 @@ #include #include -#ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY -/* Shut up about gnutls_sign_callback_set() being deprecated. We only use it - in the GnuTLS 2.12 case, and there just isn't another way of doing it. */ -#define GNUTLS_INTERNAL_BUILD 1 -#endif - #include #include #include @@ -54,26 +48,6 @@ static int gnutls_pin_callback(void *priv, int attempt, const char *uri, const char *token_label, unsigned int flags, char *pin, size_t pin_max); - -#ifndef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION -/* If we don't have this (3.1.0+) then we'll use p11-kit callbacks instead - * because the old GnuTLS callback was global rather than context-specific, - * which makes it basically unusable from libopenconnect. The p11-kit - * callback function is a simple wrapper around the GnuTLS native version. */ -typedef enum { - GNUTLS_PIN_USER = (1 << 0), - GNUTLS_PIN_SO = (1 << 1), - GNUTLS_PIN_FINAL_TRY = (1 << 2), - GNUTLS_PIN_COUNT_LOW = (1 << 3), - GNUTLS_PIN_CONTEXT_SPECIFIC = (1 << 4), - GNUTLS_PIN_WRONG = (1 << 5) -} gnutls_pin_flag_t; - -static P11KitPin *p11kit_pin_callback(const char *pin_source, P11KitUri *pin_uri, - const char *pin_description, - P11KitPinFlags flags, - void *_vpninfo); -#endif /* !HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION */ #endif /* HAVE_P11KIT || HAVE_GNUTLS_SYSTEM_KEYS */ #include "gnutls.h" @@ -593,86 +567,9 @@ static int get_cert_name(gnutls_x509_crt_t cert, char *name, size_t namelen) } #if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined (HAVE_GNUTLS_SYSTEM_KEYS) -#ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY -/* For GnuTLS 2.12 even if we *have* a privkey (as we do for PKCS#11), we - can't register it. So we have to use the cert_callback function. This - just hands out the certificate chain we prepared in load_certificate(). - If we have a pkey then return that too; otherwise leave the key NULL — - we'll also have registered a sign_callback for the session, which will - handle that. */ -static int gtls_cert_cb(gnutls_session_t sess, const gnutls_datum_t *req_ca_dn, - int nreqs, const gnutls_pk_algorithm_t *pk_algos, - int pk_algos_length, gnutls_retr2_st *st) { - - struct openconnect_info *vpninfo = gnutls_session_get_ptr(sess); - int algo = GNUTLS_PK_RSA; /* TPM */ - int i; - -#ifdef HAVE_P11KIT - if (vpninfo->my_p11key) { - st->key_type = GNUTLS_PRIVKEY_PKCS11; - st->key.pkcs11 = vpninfo->my_p11key; - algo = gnutls_pkcs11_privkey_get_pk_algorithm(vpninfo->my_p11key, NULL); - }; -#endif - for (i = 0; i < pk_algos_length; i++) { - if (algo == pk_algos[i]) - break; - } - if (i == pk_algos_length) - return GNUTLS_E_UNKNOWN_PK_ALGORITHM; - - st->cert_type = GNUTLS_CRT_X509; - st->cert.x509 = vpninfo->my_certs; - st->ncerts = vpninfo->nr_my_certs; - st->deinit_all = 0; - - return 0; -} - -/* For GnuTLS 2.12, this has to set the cert_callback to the function - above, which will return the pkey and certs on demand. Or in the - case of TPM we can't make a suitable pkey, so we have to set a - sign_callback too (which is done in openconnect_open_https() since - it has to be done on the *session*). */ -static int assign_privkey(struct openconnect_info *vpninfo, - gnutls_privkey_t pkey, - gnutls_x509_crt_t *certs, - unsigned int nr_certs, - uint8_t *free_certs) -{ - vpninfo->my_certs = gnutls_calloc(nr_certs, sizeof(*certs)); - if (!vpninfo->my_certs) - return GNUTLS_E_MEMORY_ERROR; - - vpninfo->free_my_certs = gnutls_malloc(nr_certs); - if (!vpninfo->free_my_certs) { - gnutls_free(vpninfo->my_certs); - vpninfo->my_certs = NULL; - return GNUTLS_E_MEMORY_ERROR; - } - - memcpy(vpninfo->free_my_certs, free_certs, nr_certs); - memcpy(vpninfo->my_certs, certs, nr_certs * sizeof(*certs)); - vpninfo->nr_my_certs = nr_certs; - - /* We are *keeping* the certs, unlike in GnuTLS 3 where our caller - can free them after gnutls_certificate_set_key() has been called. - So wipe the 'free_certs' array. */ - memset(free_certs, 0, nr_certs); - - gnutls_certificate_set_retrieve_function(vpninfo->https_cred, - gtls_cert_cb); - vpninfo->my_pkey = pkey; - - return 0; -} -#else /* !SET_KEY */ - -/* For GnuTLS 3+ this is saner than the GnuTLS 2.12 version. But still we - have to convert the array of X509 certificates to gnutls_pcert_st for - ourselves. There's no function that takes a gnutls_privkey_t as the key - and gnutls_x509_crt_t certificates. */ +/* We have to convert the array of X509 certificates to gnutls_pcert_st + for ourselves. There's no function that takes a gnutls_privkey_t as + the key and gnutls_x509_crt_t certificates. */ static int assign_privkey(struct openconnect_info *vpninfo, gnutls_privkey_t pkey, gnutls_x509_crt_t *certs, @@ -708,22 +605,16 @@ static int assign_privkey(struct openconnect_info *vpninfo, } return err; } -#endif /* !SET_KEY */ static int verify_signed_data(gnutls_pubkey_t pubkey, gnutls_privkey_t privkey, const gnutls_datum_t *data, const gnutls_datum_t *sig) { -#ifdef HAVE_GNUTLS_PK_TO_SIGN - gnutls_sign_algorithm_t algo = GNUTLS_SIGN_RSA_SHA1; /* TPM keys */ + gnutls_sign_algorithm_t algo; - if (privkey != OPENCONNECT_TPM_PKEY) - algo = gnutls_pk_to_sign(gnutls_privkey_get_pk_algorithm(privkey, NULL), - GNUTLS_DIG_SHA1); + algo = gnutls_pk_to_sign(gnutls_privkey_get_pk_algorithm(privkey, NULL), + GNUTLS_DIG_SHA1); return gnutls_pubkey_verify_data2(pubkey, algo, 0, data, sig); -#else - return gnutls_pubkey_verify_data(pubkey, 0, data, sig); -#endif } #endif /* (P11KIT || TROUSERS || SYSTEM_KEYS) */ @@ -1047,15 +938,9 @@ static int load_certificate(struct openconnect_info *vpninfo) key_is_p11 = !strncmp(vpninfo->sslkey, "pkcs11:", 7); cert_is_p11 = !strncmp(vpninfo->cert, "pkcs11:", 7); -#ifdef HAVE_GNUTLS_URL_IS_SUPPORTED /* GnuTLS returns true for pkcs11:, tpmkey:, system:, and custom URLs. */ key_is_sys = !key_is_p11 && gnutls_url_is_supported(vpninfo->sslkey); cert_is_sys = !cert_is_p11 && gnutls_url_is_supported(vpninfo->cert); -#else - /* Fallback for GnuTLS < 3.1.0. */ - key_is_sys = !strncmp(vpninfo->sslkey, "system:", 7); - cert_is_sys = !strncmp(vpninfo->cert, "system:", 7); -#endif #ifndef HAVE_GNUTLS_SYSTEM_KEYS if (key_is_sys || cert_is_sys) { @@ -1076,12 +961,6 @@ static int load_certificate(struct openconnect_info *vpninfo) CK_OBJECT_CLASS class; CK_ATTRIBUTE attr; P11KitUri *uri; -#ifndef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION - char pin_source[40]; - - sprintf(pin_source, "openconnect:%p", vpninfo); - p11_kit_pin_register_callback(pin_source, p11kit_pin_callback, vpninfo, NULL); -#endif uri = p11_kit_uri_new(); attr.type = CKA_CLASS; @@ -1092,10 +971,6 @@ static int load_certificate(struct openconnect_info *vpninfo) both certificate and key URLs, unless they already exist. */ if (cert_is_p11 && !p11_kit_uri_parse(cert_url, P11_KIT_URI_FOR_ANY, uri)) { -#ifndef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION - if (!p11_kit_uri_get_pin_source(uri)) - p11_kit_uri_set_pin_source(uri, pin_source); -#endif if (!p11_kit_uri_get_attribute(uri, CKA_CLASS)) { class = CKO_CERTIFICATE; p11_kit_uri_set_attribute(uri, &attr); @@ -1105,10 +980,6 @@ static int load_certificate(struct openconnect_info *vpninfo) if (key_is_p11 && !p11_kit_uri_parse(key_url, P11_KIT_URI_FOR_ANY, uri)) { -#ifndef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION - if (!p11_kit_uri_get_pin_source(uri)) - p11_kit_uri_set_pin_source(uri, pin_source); -#endif if (vpninfo->sslkey == vpninfo->cert || !p11_kit_uri_get_attribute(uri, CKA_CLASS)) { class = CKO_PRIVATE_KEY; @@ -1133,9 +1004,9 @@ static int load_certificate(struct openconnect_info *vpninfo) ret = -ENOMEM; goto out; } -#ifdef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION + gnutls_x509_crt_set_pin_function(cert, gnutls_pin_callback, vpninfo); -#endif + /* Yes, even for *system* URLs the only API GnuTLS offers us is ...import_pkcs11_url(). */ err = gnutls_x509_crt_import_pkcs11_url(cert, cert_url, 0); @@ -1240,9 +1111,9 @@ static int load_certificate(struct openconnect_info *vpninfo) ret = -EIO; goto out; } -#ifdef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION + gnutls_privkey_set_pin_function(pkey, gnutls_pin_callback, vpninfo); -#endif + err = gnutls_privkey_import_url(pkey, vpninfo->sslkey, 0); if (err) { vpn_progress(vpninfo, PRG_ERR, @@ -1267,9 +1138,9 @@ static int load_certificate(struct openconnect_info *vpninfo) ret = -EIO; goto out; } -#ifdef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION + gnutls_pkcs11_privkey_set_pin_function(p11key, gnutls_pin_callback, vpninfo); -#endif + err = gnutls_pkcs11_privkey_import_url(p11key, key_url, 0); /* Annoyingly, some tokens don't even admit the *existence* of @@ -1415,16 +1286,6 @@ static int load_certificate(struct openconnect_info *vpninfo) ret = -EIO; goto out; } -#ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY - /* This can be set now and doesn't need to be separately freed. - It goes with the pkey. This is a PITA; it would be better - if there was a way to get the p11key *back* from a privkey - that we *know* is based on one. In fact, since this is only - for GnuTLS 2.12 and we *know* the gnutls_privkey_st won't - ever change there, so we *could* do something evil... but - we won't :) */ - vpninfo->my_p11key = p11key; -#endif /* !SET_KEY */ goto match_cert; } #endif /* HAVE_P11KIT */ @@ -1618,7 +1479,7 @@ static int load_certificate(struct openconnect_info *vpninfo) fdata.size = 20; } - err = sign_dummy_data(vpninfo, pkey, &fdata, &pkey_sig); + err = gnutls_privkey_sign_data(pkey, GNUTLS_DIG_SHA1, 0, &fdata, &pkey_sig); if (err) { vpn_progress(vpninfo, PRG_ERR, _("Error signing test data with private key: %s\n"), @@ -1751,7 +1612,7 @@ static int load_certificate(struct openconnect_info *vpninfo) } free_issuer = 0; -#if defined(HAVE_P11KIT) && defined(HAVE_GNUTLS_PKCS11_GET_RAW_ISSUER) +#ifdef HAVE_P11KIT if (err && cert_is_p11) { gnutls_datum_t t; @@ -1882,7 +1743,7 @@ static int load_certificate(struct openconnect_info *vpninfo) gnutls_free(extra_certs); #if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_GNUTLS_SYSTEM_KEYS) - if (pkey && pkey != OPENCONNECT_TPM_PKEY) + if (pkey) gnutls_privkey_deinit(pkey); /* If we support arbitrary privkeys, we might have abused fdata.data just to point to something to hash. Don't free it in that case! */ @@ -1937,37 +1798,11 @@ static int set_peer_cert_hash(struct openconnect_info *vpninfo) return err; err = gnutls_pubkey_import_x509(pkey, vpninfo->peer_cert, 0); - if (err) { - gnutls_pubkey_deinit(pkey); - return err; - } -#ifdef HAVE_GNUTLS_PUBKEY_EXPORT2 - err = gnutls_pubkey_export2(pkey, GNUTLS_X509_FMT_DER, &d); - if (err) { - gnutls_pubkey_deinit(pkey); - return err; - } -#else - shalen = 0; - err = gnutls_pubkey_export(pkey, GNUTLS_X509_FMT_DER, NULL, &shalen); - if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) { - gnutls_pubkey_deinit(pkey); - return err; - } - d.size = shalen; - d.data = gnutls_malloc(d.size); - if (!d.data) { - gnutls_pubkey_deinit(pkey); - return -ENOMEM; - } - err = gnutls_pubkey_export(pkey, GNUTLS_X509_FMT_DER, d.data, &shalen); - if (err) { - gnutls_free(d.data); - gnutls_pubkey_deinit(pkey); - return err; - } -#endif + if (!err) + err = gnutls_pubkey_export2(pkey, GNUTLS_X509_FMT_DER, &d); gnutls_pubkey_deinit(pkey); + if (err) + return err; shalen = sizeof(vpninfo->peer_cert_sha256_raw); err = gnutls_fingerprint(GNUTLS_DIG_SHA256, &d, vpninfo->peer_cert_sha256_raw, &shalen); @@ -2228,15 +2063,9 @@ int openconnect_open_https(struct openconnect_info *vpninfo) if (!vpninfo->https_cred) { gnutls_certificate_allocate_credentials(&vpninfo->https_cred); - if (!vpninfo->no_system_trust) { -#ifdef HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST + if (!vpninfo->no_system_trust) gnutls_certificate_set_x509_system_trust(vpninfo->https_cred); -#else - gnutls_certificate_set_x509_trust_file(vpninfo->https_cred, - DEFAULT_SYSTEM_CAFILE, - GNUTLS_X509_FMT_PEM); -#endif - } + gnutls_certificate_set_verify_function(vpninfo->https_cred, verify_peer); @@ -2321,10 +2150,6 @@ int openconnect_open_https(struct openconnect_info *vpninfo) } gnutls_init(&vpninfo->https_sess, GNUTLS_CLIENT); gnutls_session_set_ptr(vpninfo->https_sess, (void *) vpninfo); -#if defined(HAVE_TROUSERS) && !defined(HAVE_GNUTLS_CERTIFICATE_SET_KEY) - if (vpninfo->my_pkey == OPENCONNECT_TPM_PKEY) - gnutls_sign_callback_set(vpninfo->https_sess, gtls2_tpm_sign_cb, vpninfo); -#endif /* * For versions of GnuTLS older than 3.2.9, we try to avoid long * packets by silently disabling extensions such as SNI. @@ -2489,14 +2314,6 @@ void openconnect_close_https(struct openconnect_info *vpninfo, int final) if (final && vpninfo->https_cred) { gnutls_certificate_free_credentials(vpninfo->https_cred); vpninfo->https_cred = NULL; -#if defined(HAVE_P11KIT) && !defined(HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION) - if ((vpninfo->cert && !strncmp(vpninfo->cert, "pkcs11:", 7)) || - (vpninfo->sslkey && !strncmp(vpninfo->sslkey, "pkcs11:", 7))) { - char pin_source[40]; - sprintf(pin_source, "openconnect:%p", vpninfo); - p11_kit_pin_unregister_callback(pin_source, p11kit_pin_callback, vpninfo); - } -#endif #ifdef HAVE_TROUSERS if (vpninfo->tpm_key_policy) { Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->tpm_key_policy); @@ -2518,23 +2335,6 @@ void openconnect_close_https(struct openconnect_info *vpninfo, int final) Tspi_Context_Close(vpninfo->tpm_context); vpninfo->tpm_context = 0; } -#endif -#ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY - if (vpninfo->my_pkey && vpninfo->my_pkey != OPENCONNECT_TPM_PKEY) { - gnutls_privkey_deinit(vpninfo->my_pkey); - vpninfo->my_pkey = NULL; - /* my_p11key went with it */ - } - if (vpninfo->my_certs) { - int i; - for (i = 0; i < vpninfo->nr_my_certs; i++) - if (vpninfo->free_my_certs[i]) - gnutls_x509_crt_deinit(vpninfo->my_certs[i]); - gnutls_free(vpninfo->my_certs); - gnutls_free(vpninfo->free_my_certs); - vpninfo->my_certs = NULL; - vpninfo->free_my_certs = NULL; - } #endif } } @@ -2812,51 +2612,6 @@ static int gnutls_pin_callback(void *priv, int attempt, const char *uri, return 0; } - -#ifndef HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION -static P11KitPin *p11kit_pin_callback(const char *pin_source, P11KitUri *pin_uri, - const char *pin_description, - P11KitPinFlags flags, - void *_vpninfo) -{ - struct openconnect_info *vpninfo = _vpninfo; - char *uri; - P11KitPin *pin = NULL; - char pin_str[1024]; - unsigned gnutls_flags = 0; - int attempt = 0; - - if (!vpninfo || !vpninfo->process_auth_form) - return NULL; - - if (p11_kit_uri_format(pin_uri, P11_KIT_URI_FOR_TOKEN, &uri)) - return NULL; - - /* - * In p11-kit <= 0.12, these flags are *odd*. - * RETRY is 0xa, FINAL_TRY is 0x14 and MANY_TRIES is 0x28. - * So don't treat it like a sane bitmask. Fixed in - * http://cgit.freedesktop.org/p11-glue/p11-kit/commit/?id=59774b11 - */ - if ((flags & P11_KIT_PIN_FLAGS_RETRY) == P11_KIT_PIN_FLAGS_RETRY) { - attempt = 1; - gnutls_flags |= GNUTLS_PIN_WRONG; - } - if ((flags & P11_KIT_PIN_FLAGS_FINAL_TRY) == P11_KIT_PIN_FLAGS_FINAL_TRY) - gnutls_flags |= GNUTLS_PIN_FINAL_TRY; - if ((flags & P11_KIT_PIN_FLAGS_MANY_TRIES) == P11_KIT_PIN_FLAGS_MANY_TRIES) - gnutls_flags |= GNUTLS_PIN_COUNT_LOW; - - if (!gnutls_pin_callback(vpninfo, attempt, uri, pin_description, - gnutls_flags, pin_str, sizeof(pin_str))) - pin = p11_kit_pin_new_for_string(pin_str); - - memset(pin_str, 0x5a, sizeof(pin_str)); - free(uri); - - return pin; -} -#endif /* !HAVE_GNUTLS_X509_CRT_SET_PIN_FUNCTION */ #endif /* HAVE_P11KIT || HAVE_GNUTLS_SYSTEM_KEYS */ #ifdef HAVE_LIBPCSCLITE diff --git a/gnutls.h b/gnutls.h index e61443b0..5eeacb0f 100644 --- a/gnutls.h +++ b/gnutls.h @@ -24,48 +24,6 @@ #include "openconnect-internal.h" -#ifndef HAVE_GNUTLS_PKCS12_SIMPLE_PARSE -/* If we're using a version of GnuTLS from before this was - exported, pull in our local copy. */ -int gnutls_pkcs12_simple_parse(gnutls_pkcs12_t p12, const char *password, - gnutls_x509_privkey_t *key, - gnutls_x509_crt_t **chain, - unsigned int *chain_len, - gnutls_x509_crt_t **extra_certs, - unsigned int *extra_certs_len, - gnutls_x509_crl_t *crl, - unsigned int flags); - -#endif /* !HAVE_GNUTLS_PKCS12_SIMPLE_PARSE */ - - -#ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY -int gtls2_tpm_sign_cb(gnutls_session_t sess, void *_vpninfo, - gnutls_certificate_type_t cert_type, - const gnutls_datum_t *cert, const gnutls_datum_t *data, - gnutls_datum_t *sig); -int gtls2_tpm_sign_dummy_data(struct openconnect_info *vpninfo, - const gnutls_datum_t *data, - gnutls_datum_t *sig); -#endif /* !HAVE_GNUTLS_CERTIFICATE_SET_KEY */ - -/* In GnuTLS 2.12 this can't be a real private key; we have to use the sign_callback - instead. But we want to set the 'pkey' variable to *something* non-NULL in order - to indicate that we aren't just using an x509 key. */ -#define OPENCONNECT_TPM_PKEY ((void *)1UL) - -static inline int sign_dummy_data(struct openconnect_info *vpninfo, - gnutls_privkey_t pkey, - const gnutls_datum_t *data, - gnutls_datum_t *sig) -{ -#if defined(HAVE_TROUSERS) && !defined(HAVE_GNUTLS_CERTIFICATE_SET_KEY) - if (pkey == OPENCONNECT_TPM_PKEY) - return gtls2_tpm_sign_dummy_data(vpninfo, data, sig); -#endif - return gnutls_privkey_sign_data(pkey, GNUTLS_DIG_SHA1, 0, data, sig); -} - int load_tpm_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig); diff --git a/gnutls_pkcs12.c b/gnutls_pkcs12.c deleted file mode 100644 index a90f8a4b..00000000 --- a/gnutls_pkcs12.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * This is (now) gnutls_pkcs12_simple_parse() from GnuTLS 3.1, although - * it was actually taken from parse_pkcs12() in GnuTLS 2.12.x (where it - * was under LGPLv2.1) and modified locally. The modifications were - * accepted back into GnuTLS in commit 9a43e8fa. Further modifications - * by Nikos Mavrogiannopoulos are included here under LGPLv2.1 with his - * explicit permission. - */ - -#include - -#ifndef HAVE_GNUTLS_PKCS12_SIMPLE_PARSE - -#include -#include "gnutls.h" - -#define opaque unsigned char -#define gnutls_assert() do {} while(0) -#define gnutls_assert_val(x) (x) - -/* - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - * Free Software Foundation, Inc. - * - * Author: Nikos Mavrogiannopoulos - * - * This file WAS part of GnuTLS. - * - * The GnuTLS is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - */ - - -/* Checks if the extra_certs contain certificates that may form a chain - * with the first certificate in chain (it is expected that chain_len==1) - * and appends those in the chain. - */ -static int make_chain(gnutls_x509_crt_t **chain, unsigned int *chain_len, - gnutls_x509_crt_t **extra_certs, unsigned int *extra_certs_len) -{ -unsigned int i; - - if (*chain_len != 1) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); - - i = 0; - while(i<*extra_certs_len) - { - /* if it is an issuer but not a self-signed one */ - if (gnutls_x509_crt_check_issuer((*chain)[*chain_len - 1], (*extra_certs)[i]) != 0 && - gnutls_x509_crt_check_issuer((*extra_certs)[i], (*extra_certs)[i]) == 0) - { - void *tmp = *chain; - *chain = gnutls_realloc (*chain, sizeof((*chain)[0]) * - ++(*chain_len)); - if (*chain == NULL) - { - gnutls_assert(); - gnutls_free(tmp); - return GNUTLS_E_MEMORY_ERROR; - } - (*chain)[*chain_len - 1] = (*extra_certs)[i]; - - (*extra_certs)[i] = (*extra_certs)[*extra_certs_len-1]; - (*extra_certs_len)--; - - i=0; - continue; - } - i++; - } - return 0; -} - -/** - * gnutls_pkcs12_simple_parse: - * @p12: the PKCS#12 blob. - * @password: optional password used to decrypt PKCS#12 blob, bags and keys. - * @key: a structure to store the parsed private key. - * @chain: the corresponding to key certificate chain - * @chain_len: will be updated with the number of additional - * @extra_certs: optional pointer to receive an array of additional - * certificates found in the PKCS#12 blob. - * @extra_certs_len: will be updated with the number of additional - * certs. - * @crl: an optional structure to store the parsed CRL. - * @flags: should be zero - * - * This function parses a PKCS#12 blob in @p12blob and extracts the - * private key, the corresponding certificate chain, and any additional - * certificates and a CRL. - * - * The @extra_certs_ret and @extra_certs_ret_len parameters are optional - * and both may be set to %NULL. If either is non-%NULL, then both must - * be. - * - * MAC:ed PKCS#12 files are supported. Encrypted PKCS#12 bags are - * supported. Encrypted PKCS#8 private keys are supported. However, - * only password based security, and the same password for all - * operations, are supported. - * - * The private keys may be RSA PKCS#1 or DSA private keys encoded in - * the OpenSSL way. - * - * PKCS#12 file may contain many keys and/or certificates, and there - * is no way to identify which key/certificate pair you want. You - * should make sure the PKCS#12 file only contain one key/certificate - * pair and/or one CRL. - * - * It is believed that the limitations of this function is acceptable - * for most usage, and that any more flexibility would introduce - * complexity that would make it harder to use this functionality at - * all. - * - * If the provided structure has encrypted fields but no password - * is provided then this function returns %GNUTLS_E_DECRYPTION_FAILED. - * - * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a - * negative error value. - * - * Since: 3.1 - **/ -int -gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12, - const char *password, - gnutls_x509_privkey_t * key, - gnutls_x509_crt_t ** chain, - unsigned int * chain_len, - gnutls_x509_crt_t ** extra_certs, - unsigned int * extra_certs_len, - gnutls_x509_crl_t * crl, - unsigned int flags) -{ - gnutls_pkcs12_bag_t bag = NULL; - gnutls_x509_crt_t *_extra_certs = NULL; - unsigned int _extra_certs_len = 0; - gnutls_x509_crt_t *_chain = NULL; - unsigned int _chain_len = 0; - int idx = 0; - int ret; - size_t cert_id_size = 0; - size_t key_id_size = 0; - opaque cert_id[20]; - opaque key_id[20]; - int privkey_ok = 0; - - *key = NULL; - - if (crl) - *crl = NULL; - - /* find the first private key */ - for (;;) - { - int elements_in_bag; - int i; - - ret = gnutls_pkcs12_bag_init (&bag); - if (ret < 0) - { - bag = NULL; - gnutls_assert (); - goto done; - } - - ret = gnutls_pkcs12_get_bag (p12, idx, bag); - if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) - break; - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - ret = gnutls_pkcs12_bag_get_type (bag, 0); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - if (ret == GNUTLS_BAG_ENCRYPTED) - { - if (password == NULL) - { - ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); - goto done; - } - - ret = gnutls_pkcs12_bag_decrypt (bag, password); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - } - - elements_in_bag = gnutls_pkcs12_bag_get_count (bag); - if (elements_in_bag < 0) - { - gnutls_assert (); - goto done; - } - - for (i = 0; i < elements_in_bag; i++) - { - int type; - gnutls_datum_t data; - - type = gnutls_pkcs12_bag_get_type (bag, i); - if (type < 0) - { - gnutls_assert (); - goto done; - } - - ret = gnutls_pkcs12_bag_get_data (bag, i, &data); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - switch (type) - { - case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: - if (password == NULL) - { - ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); - goto done; - } - - case GNUTLS_BAG_PKCS8_KEY: - if (*key != NULL) /* too simple to continue */ - { - gnutls_assert (); - break; - } - - ret = gnutls_x509_privkey_init (key); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - ret = gnutls_x509_privkey_import_pkcs8 - (*key, &data, GNUTLS_X509_FMT_DER, password, - type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - key_id_size = sizeof (key_id); - ret = - gnutls_x509_privkey_get_key_id (*key, 0, key_id, - &key_id_size); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - privkey_ok = 1; /* break */ - break; - default: - break; - } - } - - idx++; - gnutls_pkcs12_bag_deinit (bag); - - if (privkey_ok != 0) /* private key was found */ - break; - } - - if (privkey_ok == 0) /* no private key */ - { - gnutls_assert (); - return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; - } - - /* now find the corresponding certificate - */ - idx = 0; - bag = NULL; - for (;;) - { - int elements_in_bag; - int i; - - ret = gnutls_pkcs12_bag_init (&bag); - if (ret < 0) - { - bag = NULL; - gnutls_assert (); - goto done; - } - - ret = gnutls_pkcs12_get_bag (p12, idx, bag); - if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) - break; - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - ret = gnutls_pkcs12_bag_get_type (bag, 0); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - if (ret == GNUTLS_BAG_ENCRYPTED) - { - ret = gnutls_pkcs12_bag_decrypt (bag, password); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - } - - elements_in_bag = gnutls_pkcs12_bag_get_count (bag); - if (elements_in_bag < 0) - { - gnutls_assert (); - goto done; - } - - for (i = 0; i < elements_in_bag; i++) - { - int type; - gnutls_datum_t data; - gnutls_x509_crt_t this_cert; - - type = gnutls_pkcs12_bag_get_type (bag, i); - if (type < 0) - { - gnutls_assert (); - goto done; - } - - ret = gnutls_pkcs12_bag_get_data (bag, i, &data); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - switch (type) - { - case GNUTLS_BAG_CERTIFICATE: - ret = gnutls_x509_crt_init (&this_cert); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - ret = - gnutls_x509_crt_import (this_cert, &data, GNUTLS_X509_FMT_DER); - if (ret < 0) - { - gnutls_assert (); - gnutls_x509_crt_deinit (this_cert); - goto done; - } - - /* check if the key id match */ - cert_id_size = sizeof (cert_id); - ret = - gnutls_x509_crt_get_key_id (this_cert, 0, cert_id, &cert_id_size); - if (ret < 0) - { - gnutls_assert (); - gnutls_x509_crt_deinit (this_cert); - goto done; - } - - if (memcmp (cert_id, key_id, cert_id_size) != 0) - { /* they don't match - skip the certificate */ - if (extra_certs) - { - void *tmp = _extra_certs; - _extra_certs = gnutls_realloc (_extra_certs, - sizeof(_extra_certs[0]) * - ++_extra_certs_len); - if (!_extra_certs) - { - gnutls_assert (); - gnutls_free(tmp); - ret = GNUTLS_E_MEMORY_ERROR; - goto done; - } - _extra_certs[_extra_certs_len - 1] = this_cert; - this_cert = NULL; - } - else - { - gnutls_x509_crt_deinit (this_cert); - } - } - else - { - if (_chain_len == 0) - { - _chain = gnutls_malloc (sizeof(_chain[0]) * (++_chain_len)); - if (!_chain) - { - gnutls_assert (); - ret = GNUTLS_E_MEMORY_ERROR; - goto done; - } - _chain[_chain_len - 1] = this_cert; - this_cert = NULL; - } - else - { - gnutls_x509_crt_deinit (this_cert); - } - } - break; - - case GNUTLS_BAG_CRL: - if (crl == NULL || *crl != NULL) - { - gnutls_assert (); - break; - } - - ret = gnutls_x509_crl_init (crl); - if (ret < 0) - { - gnutls_assert (); - goto done; - } - - ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER); - if (ret < 0) - { - gnutls_assert (); - gnutls_x509_crl_deinit (*crl); - goto done; - } - break; - - case GNUTLS_BAG_ENCRYPTED: - /* XXX Bother to recurse one level down? Unlikely to - use the same password anyway. */ - case GNUTLS_BAG_EMPTY: - default: - break; - } - } - - idx++; - gnutls_pkcs12_bag_deinit (bag); - } - - if (_chain_len != 1) - { - ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; - goto done; - } - - ret = make_chain(&_chain, &_chain_len, &_extra_certs, &_extra_certs_len); - if (ret < 0) - { - gnutls_assert(); - goto done; - } - - ret = 0; - -done: - if (bag) - gnutls_pkcs12_bag_deinit (bag); - - if (ret < 0) - { - if (*key) - gnutls_x509_privkey_deinit(*key); - if (_extra_certs_len && _extra_certs != NULL) - { - unsigned int i; - for (i = 0; i < _extra_certs_len; i++) - gnutls_x509_crt_deinit(_extra_certs[i]); - gnutls_free(_extra_certs); - } - if (_chain_len && _chain != NULL) - { - unsigned int i; - for (i = 0; i < _chain_len; i++) - gnutls_x509_crt_deinit(_chain[i]); - gnutls_free(_chain); - } - } - else - { - if (extra_certs) - { - *extra_certs = _extra_certs; - *extra_certs_len = _extra_certs_len; - } - - *chain = _chain; - *chain_len = _chain_len; - } - - return ret; -} - -#endif /* HAVE_GNUTLS_PKCS12_SIMPLE_PARSE */ diff --git a/gnutls_tpm.c b/gnutls_tpm.c index 070796f5..ed417d19 100644 --- a/gnutls_tpm.c +++ b/gnutls_tpm.c @@ -33,68 +33,6 @@ #ifdef HAVE_TROUSERS /* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */ -static int tpm_sign_fn(gnutls_privkey_t key, void *_vpninfo, - const gnutls_datum_t *data, gnutls_datum_t *sig); - - -#ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY -/* We *want* to use gnutls_privkey_import_ext() to create a privkey with our - own signing function tpm_sign_fn(). But GnuTLS 2.12 doesn't support that, - so instead we have to register this sign_callback function with the - *session* */ -int gtls2_tpm_sign_cb(gnutls_session_t sess, void *_vpninfo, - gnutls_certificate_type_t cert_type, - const gnutls_datum_t *cert, const gnutls_datum_t *data, - gnutls_datum_t *sig) -{ - struct openconnect_info *vpninfo = _vpninfo; - - if (cert_type != GNUTLS_CRT_X509) - return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; - - return tpm_sign_fn(NULL, vpninfo, data, sig); -} - -/* In GnuTLS 2.12 since we don't have a normal privkey and hence can't just - use gnutls_privkey_sign_data() with it, we have to jump through hoops to - prepare the hash in exactly the right way and call our internal TPM - signing function. */ -int gtls2_tpm_sign_dummy_data(struct openconnect_info *vpninfo, - const gnutls_datum_t *data, - gnutls_datum_t *sig) -{ - static const unsigned char ber_encode[15] = { - 0x30, 0x21, /* SEQUENCE, length 31 */ - 0x30, 0x09, /* SEQUENCE, length 9 */ - 0x06, 0x05, /* OBJECT_ID, length 5 */ - 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* SHA1 OID: 1.3.14.3.2.26 */ - 0x05, 0x00, /* NULL (parameters) */ - 0x04, 0x14, /* OCTET_STRING, length 20 */ - /* followed by the 20-byte sha1 */ - }; - gnutls_datum_t hash; - unsigned char digest[sizeof(ber_encode) + SHA1_SIZE]; - size_t shalen = SHA1_SIZE; - int err; - - err = gnutls_fingerprint(GNUTLS_DIG_SHA1, data, - &digest[sizeof(ber_encode)], &shalen); - if (err) { - vpn_progress(vpninfo, PRG_ERR, - _("Failed to SHA1 input data for signing: %s\n"), - gnutls_strerror(err)); - return err; - } - - memcpy(digest, ber_encode, sizeof(ber_encode)); - - hash.data = digest; - hash.size = sizeof(digest); - - return tpm_sign_fn(NULL, vpninfo, &hash, sig); -} -#endif /* !HAVE_GNUTLS_CERTIFICATE_SET_KEY */ - static int tpm_sign_fn(gnutls_privkey_t key, void *_vpninfo, const gnutls_datum_t *data, gnutls_datum_t *sig) { @@ -258,18 +196,14 @@ int load_tpm_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, goto out_srkpol; } -#ifdef HAVE_GNUTLS_CERTIFICATE_SET_KEY gnutls_privkey_init(pkey); /* This would be nicer if there was a destructor callback. I could allocate a data structure with the TPM handles and the vpninfo pointer, and destroy that properly when the key is destroyed. */ gnutls_privkey_import_ext(*pkey, GNUTLS_PK_RSA, vpninfo, tpm_sign_fn, NULL, 0); -#else - *pkey = OPENCONNECT_TPM_PKEY; -#endif retry_sign: - err = sign_dummy_data(vpninfo, *pkey, fdata, pkey_sig); + err = gnutls_privkey_sign_data(*pkey, GNUTLS_DIG_SHA1, 0, fdata, pkey_sig); if (err == GNUTLS_E_INSUFFICIENT_CREDENTIALS) { if (!vpninfo->tpm_key_policy) { err = Tspi_Context_CreateObject(vpninfo->tpm_context, diff --git a/gpst.c b/gpst.c index a1088b2e..de7b9793 100644 --- a/gpst.c +++ b/gpst.c @@ -25,6 +25,9 @@ #include #include #include +#ifndef _WIN32 +#include +#endif #ifdef HAVE_LZ4 #include #endif @@ -89,13 +92,40 @@ static const char *add_option(struct openconnect_info *vpninfo, const char *opt, free(new); return NULL; } - new->value = strdup(val); + new->value = val; new->next = vpninfo->cstp_options; vpninfo->cstp_options = new; return new->value; } + +static int filter_opts(struct oc_text_buf *buf, const char *query, const char *incexc, int include) +{ + const char *f, *endf, *eq; + const char *found, *comma; + + for (f = query; *f; f=(*endf) ? endf+1 : endf) { + endf = strchr(f, '&') ? : f+strlen(f); + eq = strchr(f, '='); + if (!eq || eq > endf) + eq = endf; + + for (found = incexc; *found; found=(*comma) ? comma+1 : comma) { + comma = strchr(found, ',') ? : found+strlen(found); + if (!strncmp(found, f, MAX(comma-found, eq-f))) + break; + } + + if ((include && *found) || (!include && !*found)) { + if (buf->pos && buf->data[buf->pos-1] != '?' && buf->data[buf->pos-1] != '&') + buf_append(buf, "&"); + buf_append_bytes(buf, f, (int)(endf-f)); + } + } + return buf_error(buf); +} + /* Parse this JavaScript-y mess: "var respStatus = \"Challenge|Error\";\n" @@ -231,6 +261,20 @@ int gpst_xml_or_error(struct openconnect_info *vpninfo, int result, char *respon goto bad_xml; } + /* is it user.name...... */ + if (xmlnode_is_named(xml_node, "challenge")) { + for (xml_node=xml_node->children; xml_node; xml_node=xml_node->next) { + if (inputStr && xmlnode_is_named(xml_node, "inputstr")) + xmlnode_get_text(xml_node, "inputstr", (const char **)inputStr); + else if (prompt && xmlnode_is_named(xml_node, "respmsg")) + xmlnode_get_text(xml_node, "respmsg", (const char **)prompt); + else if (xmlnode_is_named(xml_node, "user")) + ; /* XXX: override the username passed to the next form? */ + } + result = -EAGAIN; + goto out; + } + if (xml_cb) result = xml_cb(vpninfo, xml_node); @@ -262,20 +306,26 @@ int gpst_xml_or_error(struct openconnect_info *vpninfo, int result, char *respon return result; } -#define ESP_OVERHEAD (4 /* SPI */ + 4 /* sequence number */ + \ - 20 /* biggest supported MAC (SHA1) */ + 16 /* biggest supported IV (AES-128) */ + \ - 1 /* pad length */ + 1 /* next header */ + \ - 16 /* max padding */ ) + +#define ESP_HEADER_SIZE (4 /* SPI */ + 4 /* sequence number */) +#define ESP_FOOTER_SIZE (1 /* pad length */ + 1 /* next header */) #define UDP_HEADER_SIZE 8 +#define TCP_HEADER_SIZE 20 /* with no options */ #define IPV4_HEADER_SIZE 20 #define IPV6_HEADER_SIZE 40 -static int calculate_mtu(struct openconnect_info *vpninfo) +/* Based on cstp.c's calculate_mtu(). + * + * With HTTPS tunnel, there are 21 bytes of overhead beyond the + * TCP MSS: 5 bytes for TLS and 16 for GPST. + */ +static int calculate_mtu(struct openconnect_info *vpninfo, int can_use_esp) { int mtu = vpninfo->reqmtu, base_mtu = vpninfo->basemtu; + int mss = 0; #if defined(__linux__) && defined(TCP_INFO) - if (!mtu || !base_mtu) { + if (!mtu) { struct tcp_info ti; socklen_t ti_size = sizeof(ti); @@ -289,23 +339,19 @@ static int calculate_mtu(struct openconnect_info *vpninfo) base_mtu = ti.tcpi_pmtu; } - if (!base_mtu) { - if (ti.tcpi_rcv_mss < ti.tcpi_snd_mss) - base_mtu = ti.tcpi_rcv_mss - 13; - else - base_mtu = ti.tcpi_snd_mss - 13; - } + /* XXX: GlobalProtect has no mechanism to inform the server about the + * desired MTU, so could just ignore the "incoming" MSS (tcpi_rcv_mss). + */ + mss = MIN(ti.tcpi_rcv_mss, ti.tcpi_snd_mss); } } #endif #ifdef TCP_MAXSEG - if (!base_mtu) { - int mss; + if (!mtu && !mss) { socklen_t mss_size = sizeof(mss); if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_MAXSEG, &mss, &mss_size)) { vpn_progress(vpninfo, PRG_DEBUG, _("TCP_MAXSEG %d\n"), mss); - base_mtu = mss - 13; } } #endif @@ -317,17 +363,41 @@ static int calculate_mtu(struct openconnect_info *vpninfo) if (base_mtu < 1280) base_mtu = 1280; - if (!mtu) { - /* remove IP/UDP and ESP overhead from base MTU to calculate tunnel MTU */ - mtu = base_mtu - ESP_OVERHEAD - UDP_HEADER_SIZE; +#ifdef HAVE_ESP + /* If we can use the ESP tunnel then we should pick the optimal MTU for ESP. */ + if (!mtu && can_use_esp) { + /* remove ESP, UDP, IP headers from base (wire) MTU */ + mtu = ( base_mtu - UDP_HEADER_SIZE - ESP_HEADER_SIZE + - 12 /* both supported algos (SHA1 and MD5) have 96-bit MAC lengths (RFC2403 and RFC2404) */ + - (vpninfo->enc_key_len ? : 32) /* biggest supported IV (AES-256) */ ); if (vpninfo->peer_addr->sa_family == AF_INET6) mtu -= IPV6_HEADER_SIZE; else mtu -= IPV4_HEADER_SIZE; + /* round down to a multiple of blocksize */ + mtu -= mtu % (vpninfo->enc_key_len ? : 32); + /* subtract ESP footer, which is included in the payload before padding to the blocksize */ + mtu -= ESP_FOOTER_SIZE; + + } else +#endif + + /* We are definitely using the TLS tunnel, so we should base our MTU on the TCP MSS. */ + if (!mtu) { + if (mss) + mtu = mss - 21; + else { + mtu = base_mtu - TCP_HEADER_SIZE - 21; + if (vpninfo->peer_addr->sa_family == AF_INET6) + mtu -= IPV6_HEADER_SIZE; + else + mtu -= IPV4_HEADER_SIZE; + } } return mtu; } +#ifdef HAVE_ESP static int set_esp_algo(struct openconnect_info *vpninfo, const char *s, int hmac) { if (hmac) { @@ -360,6 +430,7 @@ static int get_key_bits(xmlNode *xml_node, unsigned char *dest) } return (bits == 0) ? 0 : -EINVAL; } +#endif /* Return value: * < 0, on error @@ -380,6 +451,8 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ vpninfo->ip_info.domain = NULL; vpninfo->ip_info.mtu = 0; vpninfo->esp_magic = inet_addr(vpninfo->ip_info.gateway_addr); + vpninfo->esp_replay_protect = 1; + vpninfo->ssl_times.rekey_method = REKEY_NONE; vpninfo->cstp_options = NULL; for (ii = 0; ii < 3; ii++) @@ -395,6 +468,18 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ else if (!xmlnode_get_text(xml_node, "mtu", &s)) { vpninfo->ip_info.mtu = atoi(s); free((void *)s); + } else if (!xmlnode_get_text(xml_node, "ssl-tunnel-url", &s)) { + free(vpninfo->urlpath); + vpninfo->urlpath = (char *)s; + if (strcmp(s, "/ssl-tunnel-connect.sslvpn")) + vpn_progress(vpninfo, PRG_INFO, _("Non-standard SSL tunnel path: %s\n"), s); + } else if (!xmlnode_get_text(xml_node, "timeout", &s)) { + int sec = atoi(s); + vpn_progress(vpninfo, PRG_INFO, _("Tunnel timeout (rekey interval) is %d minutes.\n"), sec/60); + vpninfo->ssl_times.last_rekey = time(NULL); + vpninfo->ssl_times.rekey = sec - 60; + vpninfo->ssl_times.rekey_method = REKEY_TUNNEL; + free((void *)s); } else if (!xmlnode_get_text(xml_node, "gw-address", &s)) { /* As remarked in oncp.c, "this is a tunnel; having a * gateway is meaningless." See esp_send_probes_gp for the @@ -433,7 +518,7 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ struct oc_split_include *inc = malloc(sizeof(*inc)); if (!inc) continue; - inc->route = s; + inc->route = add_option(vpninfo, "split-include", s); inc->next = vpninfo->ip_info.split_includes; vpninfo->ip_info.split_includes = inc; } @@ -442,6 +527,7 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ #ifdef HAVE_ESP if (vpninfo->dtls_state != DTLS_DISABLED) { int c = (vpninfo->current_esp_in ^= 1); + vpninfo->old_esp_maxseq = vpninfo->esp_in[c^1].seq + 32; for (member = xml_node->children; member; member=member->next) { s = NULL; if (!xmlnode_get_text(member, "udp-port", &s)) udp_sockaddr(vpninfo, atoi(s)); @@ -460,7 +546,8 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ if (setup_esp_keys(vpninfo, 0)) vpn_progress(vpninfo, PRG_ERR, "Failed to setup ESP keys.\n"); else - vpninfo->dtls_times.last_rekey = time(NULL); + /* prevent race condition between esp_mainloop() and gpst_mainloop() timers */ + vpninfo->dtls_times.last_rekey = time(&vpninfo->new_dtls_started); } #else vpn_progress(vpninfo, PRG_DEBUG, _("Ignoring ESP keys since ESP support not available in this build\n")); @@ -483,7 +570,7 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ static int gpst_get_config(struct openconnect_info *vpninfo) { - char *orig_path, *orig_ua; + char *orig_path; int result; struct oc_text_buf *request_body = buf_alloc(); struct oc_vpn_option *old_cstp_opts = vpninfo->cstp_options; @@ -495,25 +582,29 @@ static int gpst_get_config(struct openconnect_info *vpninfo) /* submit getconfig request */ buf_append(request_body, "client-type=1&protocol-version=p1&app-version=3.0.1-10"); append_opt(request_body, "os-version", vpninfo->platname); - append_opt(request_body, "clientos", vpninfo->platname); + if (!strcmp(vpninfo->platname, "win")) + append_opt(request_body, "clientos", "Windows"); + else + append_opt(request_body, "clientos", vpninfo->platname); append_opt(request_body, "hmac-algo", "sha1,md5"); append_opt(request_body, "enc-algo", "aes-128-cbc,aes-256-cbc"); - if (old_addr) + if (old_addr) { append_opt(request_body, "preferred-ip", old_addr); - buf_append(request_body, "&%s", vpninfo->cookie); + filter_opts(request_body, vpninfo->cookie, "preferred-ip", 0); + } else + buf_append(request_body, "&%s", vpninfo->cookie); + if ((result = buf_error(request_body))) + goto out; orig_path = vpninfo->urlpath; - orig_ua = vpninfo->useragent; - vpninfo->useragent = (char *)"PAN GlobalProtect"; vpninfo->urlpath = strdup("ssl-vpn/getconfig.esp"); result = do_https_request(vpninfo, method, request_body_type, request_body, &xml_buf, 0); free(vpninfo->urlpath); vpninfo->urlpath = orig_path; - vpninfo->useragent = orig_ua; if (result < 0) - goto out; + goto pre_opt_out; /* parse getconfig result */ result = gpst_xml_or_error(vpninfo, result, xml_buf, gpst_parse_config_xml, NULL, NULL); @@ -522,9 +613,19 @@ static int gpst_get_config(struct openconnect_info *vpninfo) if (!vpninfo->ip_info.mtu) { /* FIXME: GP gateway config always seems to be 0 */ - vpninfo->ip_info.mtu = calculate_mtu(vpninfo); + char *no_esp_reason = NULL; +#ifdef HAVE_ESP + if (vpninfo->dtls_state == DTLS_DISABLED) + no_esp_reason = _("ESP disabled"); + else if (vpninfo->dtls_state == DTLS_NOSECRET) + no_esp_reason = _("No ESP keys received"); +#else + no_esp_reason = _("ESP support not available in this build"); +#endif + vpninfo->ip_info.mtu = calculate_mtu(vpninfo, !no_esp_reason); vpn_progress(vpninfo, PRG_ERR, - _("No MTU received. Calculated %d\n"), vpninfo->ip_info.mtu); + _("No MTU received. Calculated %d for %s%s\n"), vpninfo->ip_info.mtu, + no_esp_reason ? "SSL tunnel. " : "ESP tunnel", no_esp_reason ? : ""); /* return -EINVAL; */ } if (!vpninfo->ip_info.addr) { @@ -534,12 +635,21 @@ static int gpst_get_config(struct openconnect_info *vpninfo) goto out; } if (old_addr) { + /* XXX: if --request-ip option is used, we'll have old_addr!=NULL even on the + first connection attempt, but if old_netmask is also non-NULL then we know + it's a reconnect. */ if (strcmp(old_addr, vpninfo->ip_info.addr)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different Legacy IP address (%s != %s)\n"), - vpninfo->ip_info.addr, old_addr); - result = -EINVAL; - goto out; + if (!old_netmask) + vpn_progress(vpninfo, PRG_ERR, + _("Legacy IP address %s was requested, but server provided %s\n"), + old_addr, vpninfo->ip_info.addr); + else { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different Legacy IP address (%s != %s)\n"), + vpninfo->ip_info.addr, old_addr); + result = -EINVAL; + goto out; + } } } if (old_netmask) { @@ -553,8 +663,9 @@ static int gpst_get_config(struct openconnect_info *vpninfo) } out: - buf_free(request_body); free_optlist(old_cstp_opts); +pre_opt_out: + buf_free(request_body); free(xml_buf); return result; } @@ -563,6 +674,7 @@ static int gpst_connect(struct openconnect_info *vpninfo) { int ret; struct oc_text_buf *reqbuf; + const char start_tunnel[12] = "START_TUNNEL"; /* NOT zero-terminated */ char buf[256]; /* Connect to SSL VPN tunnel */ @@ -574,32 +686,36 @@ static int gpst_connect(struct openconnect_info *vpninfo) return ret; reqbuf = buf_alloc(); - buf_append(reqbuf, "GET /ssl-tunnel-connect.sslvpn?%s HTTP/1.1\r\n\r\n", vpninfo->cookie); + buf_append(reqbuf, "GET %s?", vpninfo->urlpath); + filter_opts(reqbuf, vpninfo->cookie, "user,authcookie", 1); + buf_append(reqbuf, " HTTP/1.1\r\n\r\n"); + if ((ret = buf_error(reqbuf))) + goto out; if (vpninfo->dump_http_traffic) dump_buf(vpninfo, '>', reqbuf->data); vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos); - buf_free(reqbuf); if ((ret = vpninfo->ssl_read(vpninfo, buf, 12)) < 0) { if (ret == -EINTR) - return ret; + goto out; vpn_progress(vpninfo, PRG_ERR, _("Error fetching GET-tunnel HTTPS response.\n")); - return -EINVAL; + ret = -EINVAL; + goto out; } - if (!strncmp(buf, "START_TUNNEL", 12)) { + if (!strncmp(buf, start_tunnel, sizeof(start_tunnel))) { ret = 0; } else if (ret==0) { vpn_progress(vpninfo, PRG_ERR, _("Gateway disconnected immediately after GET-tunnel request.\n")); ret = -EPIPE; } else { - if (ret==12) { - ret = vpninfo->ssl_gets(vpninfo, buf+12, 244); - ret = (ret>0 ? ret : 0) + 12; + if (ret==sizeof(start_tunnel)) { + ret = vpninfo->ssl_gets(vpninfo, buf+sizeof(start_tunnel), sizeof(buf)-sizeof(start_tunnel)); + ret = (ret>0 ? ret : 0) + sizeof(start_tunnel); } vpn_progress(vpninfo, PRG_ERR, _("Got inappropriate HTTP GET-tunnel response: %.*s\n"), ret, buf); @@ -612,22 +728,230 @@ static int gpst_connect(struct openconnect_info *vpninfo) monitor_fd_new(vpninfo, ssl); monitor_read_fd(vpninfo, ssl); monitor_except_fd(vpninfo, ssl); - vpninfo->ssl_times.last_rekey = vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL); - if (vpninfo->dtls_state != DTLS_DISABLED) - vpninfo->dtls_state = DTLS_NOSECRET; + vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL); + if (vpninfo->proto->udp_close) + vpninfo->proto->udp_close(vpninfo); } +out: + buf_free(reqbuf); return ret; } +static int parse_hip_report_check(struct openconnect_info *vpninfo, xmlNode *xml_node) +{ + const char *s; + int result = -EINVAL; + + if (!xml_node || !xmlnode_is_named(xml_node, "response")) + goto out; + + for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) { + if (!xmlnode_get_text(xml_node, "hip-report-needed", &s)) { + if (!strcmp(s, "no")) + result = 0; + else if (!strcmp(s, "yes")) + result = -EAGAIN; + else + result = -EINVAL; + free((void *)s); + goto out; + } + } + +out: + return result; +} + +/* Unlike CSD, the HIP security checker runs during the connection + * phase, not during the authentication phase. + * + * The HIP security checker will (probably) ask us to resubmit the + * HIP report if either of the following changes: + * - Client IP address + * - Client HIP report md5sum + * + * I'm not sure what the md5sum is computed over in the official + * client, but it doesn't really matter. + * + * We just need an identifier for the combination of the local host + * and the VPN gateway which won't change when our IP address + * or authcookie are changed. + */ +static int build_csd_token(struct openconnect_info *vpninfo) +{ + struct oc_text_buf *buf; + unsigned char md5[16]; + int i; + + if (vpninfo->csd_token) + return 0; + + vpninfo->csd_token = malloc(MD5_SIZE * 2 + 1); + if (!vpninfo->csd_token) + return -ENOMEM; + + /* use cookie (excluding volatile authcookie and preferred-ip) to build md5sum */ + buf = buf_alloc(); + filter_opts(buf, vpninfo->cookie, "authcookie,preferred-ip", 0); + if (buf_error(buf)) + goto out; + + /* save as csd_token */ + openconnect_md5(md5, buf->data, buf->pos); + for (i=0; i < MD5_SIZE; i++) + sprintf(&vpninfo->csd_token[i*2], "%02x", md5[i]); + +out: + return buf_free(buf); +} + +/* check if HIP report is needed (to ssl-vpn/hipreportcheck.esp) or submit HIP report contents (to ssl-vpn/hipreport.esp) */ +static int check_or_submit_hip_report(struct openconnect_info *vpninfo, const char *report) +{ + int result; + + struct oc_text_buf *request_body = buf_alloc(); + const char *request_body_type = "application/x-www-form-urlencoded"; + const char *method = "POST"; + char *xml_buf=NULL, *orig_path; + + /* cookie gives us these fields: authcookie, portal, user, domain, computer, and (maybe the unnecessary) preferred-ip */ + buf_append(request_body, "client-role=global-protect-full&%s", vpninfo->cookie); + append_opt(request_body, "client-ip", vpninfo->ip_info.addr); + if (report) { + /* XML report contains many characters requiring URL-encoding (%xx) */ + buf_ensure_space(request_body, strlen(report)*3); + append_opt(request_body, "report", report); + } else { + result = build_csd_token(vpninfo); + if (result) + goto out; + append_opt(request_body, "md5", vpninfo->csd_token); + } + if ((result = buf_error(request_body))) + goto out; + + orig_path = vpninfo->urlpath; + vpninfo->urlpath = strdup(report ? "ssl-vpn/hipreport.esp" : "ssl-vpn/hipreportcheck.esp"); + result = do_https_request(vpninfo, method, request_body_type, request_body, + &xml_buf, 0); + free(vpninfo->urlpath); + vpninfo->urlpath = orig_path; + + result = gpst_xml_or_error(vpninfo, result, xml_buf, report ? NULL : parse_hip_report_check, NULL, NULL); + +out: + buf_free(request_body); + free(xml_buf); + return result; +} + +static int run_hip_script(struct openconnect_info *vpninfo) +{ +#if !defined(_WIN32) && !defined(__native_client__) + int pipefd[2]; + int ret; + pid_t child; +#endif + + if (!vpninfo->csd_wrapper) { + vpn_progress(vpninfo, PRG_ERR, + _("WARNING: Server asked us to submit HIP report with md5sum %s.\n" + "VPN connectivity may be disabled or limited without HIP report submission.\n" + "You need to provide a --csd-wrapper argument with the HIP report submission script.\n"), + vpninfo->csd_token); + /* XXX: Many GlobalProtect VPNs work fine despite allegedly requiring HIP report submission */ + return 0; + } + +#if defined(_WIN32) || defined(__native_client__) + vpn_progress(vpninfo, PRG_ERR, + _("Error: Running the 'HIP Report' script on this platform is not yet implemented.\n")); + return -EPERM; +#else + if (pipe(pipefd) == -1) + goto out; + child = fork(); + if (child == -1) { + goto out; + } else if (child > 0) { + /* in parent: read report from child */ + struct oc_text_buf *report_buf = buf_alloc(); + char b[256]; + int i, status; + close(pipefd[1]); + + buf_truncate(report_buf); + while ((i = read(pipefd[0], b, sizeof(b))) > 0) + buf_append_bytes(report_buf, b, i); + + waitpid(child, &status, 0); + if (status != 0) { + vpn_progress(vpninfo, PRG_ERR, + _("HIP script returned non-zero status: %d\n"), status); + ret = -EINVAL; + } else { + ret = check_or_submit_hip_report(vpninfo, report_buf->data); + if (ret < 0) + vpn_progress(vpninfo, PRG_ERR, _("HIP report submission failed.\n")); + else { + vpn_progress(vpninfo, PRG_INFO, _("HIP report submitted successfully.\n")); + ret = 0; + } + } + buf_free(report_buf); + return ret; + } else { + /* in child: run HIP script */ + char *hip_argv[32]; + int i = 0; + close(pipefd[0]); + dup2(pipefd[1], 1); + + hip_argv[i++] = openconnect_utf8_to_legacy(vpninfo, vpninfo->csd_wrapper); + hip_argv[i++] = (char *)"--cookie"; + hip_argv[i++] = vpninfo->cookie; + hip_argv[i++] = (char *)"--client-ip"; + hip_argv[i++] = (char *)vpninfo->ip_info.addr; + hip_argv[i++] = (char *)"--md5"; + hip_argv[i++] = vpninfo->csd_token; + hip_argv[i++] = NULL; + execv(hip_argv[0], hip_argv); + + out: + vpn_progress(vpninfo, PRG_ERR, + _("Failed to exec HIP script %s\n"), hip_argv[0]); + exit(1); + } + +#endif /* !_WIN32 && !__native_client__ */ +} + int gpst_setup(struct openconnect_info *vpninfo) { int ret; + /* ESP tunnel is unusable as soon as we (re-)fetch the configuration */ + if (vpninfo->proto->udp_close) + vpninfo->proto->udp_close(vpninfo); + /* Get configuration */ ret = gpst_get_config(vpninfo); if (ret) - return ret; + goto out; + + /* Check HIP */ + ret = check_or_submit_hip_report(vpninfo, NULL); + if (ret == -EAGAIN) { + vpn_progress(vpninfo, PRG_DEBUG, + _("Gateway says HIP report submission is needed.\n")); + ret = run_hip_script(vpninfo); + if (ret != 0) + goto out; + } else if (ret == 0) + vpn_progress(vpninfo, PRG_DEBUG, + _("Gateway says no HIP report submission is needed.\n")); /* We do NOT actually start the HTTPS tunnel yet if we want to * use ESP, because the ESP tunnel won't work if the HTTPS tunnel @@ -635,18 +959,8 @@ int gpst_setup(struct openconnect_info *vpninfo) */ if (vpninfo->dtls_state == DTLS_DISABLED || vpninfo->dtls_state == DTLS_NOSECRET) ret = gpst_connect(vpninfo); - else { - /* We want to prevent the mainloop timers from frantically - * calling the GPST mainloop. - */ - vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL); - - /* Using (abusing?) last_rekey as the time when the SSL tunnel - * was brought up. - */ - vpninfo->ssl_times.last_rekey = 0; - } +out: return ret; } @@ -667,15 +981,16 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout) _("ESP tunnel connected; exiting HTTPS mainloop.\n")); vpninfo->dtls_state = DTLS_CONNECTED; case DTLS_CONNECTED: + /* Rekey if needed */ + if (keepalive_action(&vpninfo->ssl_times, timeout) == KA_REKEY) + goto do_rekey; return 0; case DTLS_SECRET: case DTLS_SLEEPING: - if (time(NULL) < vpninfo->dtls_times.last_rekey + 5) { + if (!ka_check_deadline(timeout, time(NULL), vpninfo->new_dtls_started + 5)) { /* Allow 5 seconds after configuration for ESP to start */ - if (*timeout > 5000) - *timeout = 5000; return 0; - } else if (!vpninfo->ssl_times.last_rekey) { + } else { /* ... before we switch to HTTPS instead */ vpn_progress(vpninfo, PRG_ERR, _("Failed to connect ESP tunnel; using HTTPS instead.\n")); @@ -696,7 +1011,10 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout) goto do_reconnect; while (1) { - int receive_mtu = MAX(2048, vpninfo->ip_info.mtu + 256); + /* Some servers send us packets that are larger than + negotiated MTU. We reserve some extra space to + handle that */ + int receive_mtu = MAX(16384, vpninfo->ip_info.mtu); int len, payload_len; if (!vpninfo->cstp_pkt) { @@ -791,6 +1109,8 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout) goto do_reconnect; else if (!ret) { switch (ka_stalled_action(&vpninfo->ssl_times, timeout)) { + case KA_REKEY: + goto do_rekey; case KA_DPD_DEAD: goto peer_dead; case KA_NONE: @@ -813,6 +1133,11 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout) } switch (keepalive_action(&vpninfo->ssl_times, timeout)) { + case KA_REKEY: + do_rekey: + vpn_progress(vpninfo, PRG_INFO, _("GlobalProtect rekey due\n")); + goto do_reconnect; + case KA_DPD_DEAD: peer_dead: vpn_progress(vpninfo, PRG_ERR, @@ -824,7 +1149,8 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout) vpninfo->quit_reason = "GPST reconnect failed"; return ret; } - esp_setup(vpninfo, vpninfo->dtls_attempt_period); + if (vpninfo->proto->udp_setup) + vpninfo->proto->udp_setup(vpninfo, vpninfo->dtls_attempt_period); return 1; case KA_KEEPALIVE: diff --git a/hipreport.sh b/hipreport.sh new file mode 100755 index 00000000..832aabc8 --- /dev/null +++ b/hipreport.sh @@ -0,0 +1,181 @@ +#!/bin/sh + +# openconnect will call this script with the follow command-line +# arguments, which are needed to populate the contents of the +# HIP report: +# +# --cookie: a URL-encoded string, as output by openconnect +# --authenticate --protocol=gp, which includes parameters +# from the /ssl-vpn/login.esp response +# +# --client-ip: IPv4 address allocated by the GlobalProtect VPN for +# this client (included in /ssl-vpn/getconfig.esp +# response) +# +# --md5: The md5 digest to encode into this HIP report. I'm not sure +# exactly what this is the md5 digest *of*, but all that +# really matters is that the value in the HIP report +# submission should match the value in the HIP report check. + +# Read command line arguments into variables +COOKIE= +IP= +MD5= + +while [ "$1" ]; do + if [ "$1" = "--cookie" ]; then shift; COOKIE="$1"; fi + if [ "$1" = "--client-ip" ]; then shift; IP="$1"; fi + if [ "$1" = "--md5" ]; then shift; MD5="$1"; fi + shift +done + +if [ -z "$COOKIE" -o -z "$IP" -o -z "$MD5" ]; then + echo "Parameters --cookie, --computer, --client-ip, and --md5 are required" >&2 + exit 1; +fi + +# Extract username and domain and computer from cookie +USER=$(echo "$COOKIE" | sed -rn 's/(.+&|^)user=([^&]+)(&.+|$)/\2/p') +DOMAIN=$(echo "$COOKIE" | sed -rn 's/(.+&|^)domain=([^&]+)(&.+|$)/\2/p') +COMPUTER=$(echo "$COOKIE" | sed -rn 's/(.+&|^)computer=([^&]+)(&.+|$)/\2/p') + +# Timestamp in the format expected by GlobalProtect server +NOW=$(date +'%m/%d/%Y %H:%M:%S') + +# This value may need to be extracted from the official HIP report, if a made-up value is not accepted. +HOSTID="deadbeef-dead-beef-dead-beefdeadbeef" + +cat < + $MD5 + $USER + $DOMAIN + $COMPUTER + $HOSTID + $IP + + $NOW + + + 4.0.2-19 + Microsoft Windows 10 Pro , 64-bit + Microsoft + $DOMAIN.internal + $COMPUTER + $HOSTID + + + PANGP Virtual Ethernet Adapter #2 + 01-02-03-00-00-01 + + + + + + + + + + + + + + + + yes + 10/11/2017 15:23:41 + + + + + + + no + n/a + + + + + + + + + + + yes + 10/11/2017 15:23:41 + + + + + + + no + n/a + + + + + + + + + + + n/a + + + + + + + + + + + + + C: + full + + + + + + + + + + + + + yes + + + + + + + + + + + yes + + + + + + + yes + + + + + + + + + + +EOF diff --git a/http.c b/http.c index 812e0023..35f5ae39 100644 --- a/http.c +++ b/http.c @@ -54,6 +54,19 @@ void buf_append_urlencoded(struct oc_text_buf *buf, const char *str) } } +void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str) +{ + while (str && *str) { + unsigned char c = *str; + if (c=='<' || c=='>' || c=='&' || c=='"' || c=='\'') + buf_append(buf, "&#x%02x;", c); + else + buf_append_bytes(buf, str, 1); + + str++; + } +} + void buf_append_hex(struct oc_text_buf *buf, const void *str, unsigned len) { const unsigned char *data = str; @@ -818,7 +831,7 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, int result; int rq_retry; int rlen, pad; - int auth = 0; + int i, auth = 0; int max_redirects = 10; if (request_body_type && buf_error(request_body)) @@ -913,17 +926,22 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, if (vpninfo->dump_http_traffic) dump_buf(vpninfo, '>', buf->data); - result = vpninfo->ssl_write(vpninfo, buf->data, buf->pos); - if (rq_retry && result < 0) { - openconnect_close_https(vpninfo, 0); - goto retry; + for (i = 0; i < buf->pos; i += 16384) { + result = vpninfo->ssl_write(vpninfo, buf->data + i, MIN(buf->pos - i, 16384) ); + if (result < 0) { + if (rq_retry) { + /* Retry if we failed to send the request on + an already-open connection */ + openconnect_close_https(vpninfo, 0); + goto retry; + } + /* We'll already have complained about whatever offended us */ + goto out; + } } - if (result < 0) - goto out; result = process_http_response(vpninfo, 0, http_auth_hdrs, buf); if (result < 0) { - /* We'll already have complained about whatever offended us */ goto out; } if (vpninfo->dump_http_traffic && buf->pos) @@ -1467,7 +1485,7 @@ void http_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *b buf_append(buf, "Host: %s\r\n", vpninfo->hostname); else buf_append(buf, "Host: %s:%d\r\n", vpninfo->hostname, vpninfo->port); - buf_append(buf, "User-Agent: %s\r\n", vpninfo->useragent); + buf_append(buf, "User-Agent: %s\r\n", vpninfo->proto->override_useragent ? : vpninfo->useragent); if (vpninfo->cookies) { buf_append(buf, "Cookie: "); diff --git a/library.c b/library.c index 46ab659f..b05d197a 100644 --- a/library.c +++ b/library.c @@ -117,6 +117,7 @@ const struct vpn_proto openconnect_protos[] = { .tcp_mainloop = cstp_mainloop, .add_http_headers = cstp_common_headers, .obtain_cookie = cstp_obtain_cookie, + .udp_protocol = "DTLS", #ifdef HAVE_DTLS .udp_setup = dtls_setup, .udp_mainloop = dtls_mainloop, @@ -133,6 +134,7 @@ const struct vpn_proto openconnect_protos[] = { .tcp_mainloop = oncp_mainloop, .add_http_headers = oncp_common_headers, .obtain_cookie = oncp_obtain_cookie, + .udp_protocol = "ESP", #ifdef HAVE_ESP .udp_setup = esp_setup, .udp_mainloop = esp_mainloop, @@ -145,12 +147,14 @@ const struct vpn_proto openconnect_protos[] = { .name = "gp", .pretty_name = N_("Palo Alto Networks GlobalProtect"), .description = N_("Compatible with Palo Alto Networks (PAN) GlobalProtect SSL VPN"), + .override_useragent = "PAN GlobalProtect", .flags = OC_PROTO_PROXY | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_AUTH_STOKEN, .vpn_close_session = gpst_bye, .tcp_connect = gpst_setup, .tcp_mainloop = gpst_mainloop, .add_http_headers = gpst_common_headers, .obtain_cookie = gpst_obtain_cookie, + .udp_protocol = "ESP", #ifdef HAVE_ESP .udp_setup = esp_setup, .udp_mainloop = esp_mainloop, @@ -904,7 +908,8 @@ int openconnect_setup_tun_device(struct openconnect_info *vpninfo, static const char *compr_name_map[] = { [COMPR_DEFLATE] = "Deflate", [COMPR_LZS] = "LZS", - [COMPR_LZ4] = "LZ4" + [COMPR_LZ4] = "LZ4", + [COMPR_LZO] = "LZO", }; const char *openconnect_get_cstp_compression(struct openconnect_info * vpninfo) diff --git a/main.c b/main.c index 6deab488..1ce4d080 100644 --- a/main.c +++ b/main.c @@ -187,7 +187,9 @@ enum { OPT_HTTP_AUTH, OPT_LOCAL_HOSTNAME, OPT_PROTOCOL, + OPT_SERVER, OPT_PASSTOS, + OPT_REQUEST_IP, }; #ifdef __sun__ @@ -269,6 +271,8 @@ static const struct option long_options[] = { OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP), OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST), OPTION("protocol", 1, OPT_PROTOCOL), + OPTION("request-ip", 1, OPT_REQUEST_IP), + OPTION("server", 1, OPT_SERVER), #ifdef OPENCONNECT_GNUTLS OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG), #endif @@ -367,36 +371,37 @@ static char *convert_arg_to_utf8(char **argv, char *arg) static void read_stdin(char **string, int hidden, int allow_fail) { CONSOLE_READCONSOLE_CONTROL rcc = { sizeof(rcc), 0, 13, 0 }; - HANDLE conh, stdinh = GetStdHandle(STD_INPUT_HANDLE); + HANDLE stdinh = GetStdHandle(STD_INPUT_HANDLE); DWORD cmode, nr_read; wchar_t wbuf[1024]; char *buf; - conh = stdinh; - if (!GetConsoleMode(conh, &cmode)) { - /* STDIN is not a console? Try opening it explicitly */ - conh = CreateFile("CONIN$", GENERIC_READ, FILE_SHARE_READ, - 0,OPEN_EXISTING, 0, 0); - if (conh == INVALID_HANDLE_VALUE || !GetConsoleMode(conh, &cmode)) { + if (GetConsoleMode(stdinh, &cmode)) { + if (hidden) + SetConsoleMode(stdinh, cmode & (~ENABLE_ECHO_INPUT)); + + if (!ReadConsoleW(stdinh, wbuf, sizeof(wbuf)/2, &nr_read, &rcc)) { char *errstr = openconnect__win32_strerror(GetLastError()); - fprintf(stderr, _("Failed to open CONIN$: %s\n"), errstr); + fprintf(stderr, _("ReadConsole() failed: %s\n"), errstr); free(errstr); *string = NULL; - goto out; + if (hidden) + SetConsoleMode(stdinh, cmode); + return; } + if (hidden) + SetConsoleMode(stdinh, cmode); + } else { + /* Not a console; maybe reading from a piped stdin? */ + if (!fgetws(wbuf, sizeof(wbuf)/2, stdin)) { + char *errstr = openconnect__win32_strerror(GetLastError()); + fprintf(stderr, _("fgetws() failed: %s\n"), errstr); + free(errstr); + *string = NULL; + return; + } + nr_read = wcslen(wbuf); } - if (hidden) { - SetConsoleMode(conh, cmode & (~ENABLE_ECHO_INPUT)); - } - - if (!ReadConsoleW(conh, wbuf, sizeof(wbuf)/2, &nr_read, &rcc)) { - char *errstr = openconnect__win32_strerror(GetLastError()); - fprintf(stderr, _("ReadConsole() failed: %s\n"), errstr); - free(errstr); - *string = NULL; - goto out; - } - if (nr_read >= 2 && wbuf[nr_read - 1] == 10 && wbuf[nr_read - 2] == 13) { wbuf[nr_read - 2] = 0; nr_read -= 2; @@ -408,7 +413,7 @@ static void read_stdin(char **string, int hidden, int allow_fail) fprintf(stderr, _("Error converting console input: %s\n"), errstr); free(errstr); - goto out; + return; } buf = malloc(nr_read); if (!buf) { @@ -422,18 +427,10 @@ static void read_stdin(char **string, int hidden, int allow_fail) errstr); free(errstr); free(buf); - goto out; + return; } *string = buf; - -out: - if (hidden) { - SetConsoleMode(conh, cmode); - fprintf(stderr, "\n"); - } - if (conh != stdinh && conh != INVALID_HANDLE_VALUE) - CloseHandle(conh); } #elif defined(HAVE_ICONV) @@ -664,7 +661,7 @@ static void print_supported_protocols_usage(void) struct oc_vpn_proto *protos, *p; if (openconnect_get_supported_protocols(&protos)>=0) { - printf(_("\n Set VPN protocol:\n")); + printf("\n%s:\n", _("Set VPN protocol")); for (p=protos; p->name; p++) printf(" --protocol=%-16s %s%s\n", p->name, p->description, p==protos ? _(" (default)") : ""); @@ -784,39 +781,43 @@ static int gai_override_cb(void *cbdata, const char *node, static void usage(void) { - printf(_("Usage: openconnect [options] \n")); + printf(_("Usage: openconnect [options] [--server=]\n")); printf(_("Open client for multiple VPN protocols, version %s\n\n"), openconnect_version_str); print_build_opts(); printf(" --config=CONFIGFILE %s\n", _("Read options from config file")); -#ifndef _WIN32 - printf(" -b, --background %s\n", _("Continue in background after startup")); - printf(" --pid-file=PIDFILE %s\n", _("Write the daemon's PID to this file")); -#endif + printf(" -V, --version %s\n", _("Report version number")); + printf(" -h, --help %s\n", _("Display help text")); + + print_supported_protocols_usage(); + + printf("\n%s:\n", _("Authentication")); + printf(" -u, --user=NAME %s\n", _("Set login username")); + printf(" --no-passwd %s\n", _("Disable password/SecurID authentication")); + printf(" --non-inter %s\n", _("Do not expect user input; exit if it is required")); + printf(" --passwd-on-stdin %s\n", _("Read password from standard input")); + printf(" --authgroup=GROUP %s\n", _("Choose authentication login selection")); printf(" -c, --certificate=CERT %s\n", _("Use SSL client certificate CERT")); - printf(" -e, --cert-expire-warning=DAYS %s\n", _("Warn when certificate lifetime < DAYS")); printf(" -k, --sslkey=KEY %s\n", _("Use SSL private key file KEY")); - printf(" -C, --cookie=COOKIE %s\n", _("Use WebVPN cookie COOKIE")); - printf(" --cookie-on-stdin %s\n", _("Read cookie from standard input")); - printf(" -d, --deflate %s\n", _("Enable compression (default)")); - printf(" -D, --no-deflate %s\n", _("Disable compression")); - printf(" --force-dpd=INTERVAL %s\n", _("Set minimum Dead Peer Detection interval")); + printf(" -e, --cert-expire-warning=DAYS %s\n", _("Warn when certificate lifetime < DAYS")); printf(" -g, --usergroup=GROUP %s\n", _("Set login usergroup")); - printf(" -h, --help %s\n", _("Display help text")); - printf(" -i, --interface=IFNAME %s\n", _("Use IFNAME for tunnel interface")); -#ifndef _WIN32 - printf(" -l, --syslog %s\n", _("Use syslog for progress messages")); -#endif - printf(" --timestamp %s\n", _("Prepend timestamp to progress messages")); - printf(" --passtos %s\n", _("copy TOS / TCLASS when using DTLS")); -#ifndef _WIN32 - printf(" -U, --setuid=USER %s\n", _("Drop privileges after connecting")); - printf(" --csd-user=USER %s\n", _("Drop privileges during CSD execution")); - printf(" --csd-wrapper=SCRIPT %s\n", _("Run SCRIPT instead of CSD binary")); -#endif - printf(" -m, --mtu=MTU %s\n", _("Request MTU from server (legacy servers only)")); - printf(" --base-mtu=MTU %s\n", _("Indicate path MTU to/from server")); printf(" -p, --key-password=PASS %s\n", _("Set key passphrase or TPM SRK PIN")); printf(" --key-password-from-fsid %s\n", _("Key passphrase is fsid of file system")); + printf(" --token-mode=MODE %s\n", _("Software token type: rsa, totp or hotp")); + printf(" --token-secret=STRING %s\n", _("Software token secret")); +#ifndef HAVE_LIBSTOKEN + printf(" %s\n", _("(NOTE: libstoken (RSA SecurID) disabled in this build)")); +#endif +#ifndef HAVE_LIBPCSCLITE + printf(" %s\n", _("(NOTE: Yubikey OATH disabled in this build)")); +#endif + + printf("\n%s:\n", _("Server validation")); + printf(" --servercert=FINGERPRINT %s\n", _("Server's certificate SHA1 fingerprint")); + printf(" --no-cert-check %s\n", _("Do not require server SSL cert to be valid")); + printf(" --no-system-trust %s\n", _("Disable default system certificate authorities")); + printf(" --cafile=FILE %s\n", _("Cert file for server verification")); + + printf("\n%s:\n", _("Internet connectivity")); printf(" -P, --proxy=URL %s\n", _("Set proxy server")); printf(" --proxy-auth=METHODS %s\n", _("Set proxy authentication methods")); printf(" --no-proxy %s\n", _("Disable proxy")); @@ -824,50 +825,70 @@ static void usage(void) #ifndef LIBPROXY_HDR printf(" %s\n", _("(NOTE: libproxy disabled in this build)")); #endif - printf(" --pfs %s\n", _("Require perfect forward secrecy")); + printf(" --reconnect-timeout %s\n", _("Connection retry timeout in seconds")); + printf(" --resolve=HOST:IP %s\n", _("Use IP when connecting to HOST")); + printf(" --passtos %s\n", _("copy TOS / TCLASS when using DTLS")); + printf(" --dtls-local-port=PORT %s\n", _("Set local port for DTLS datagrams")); + + printf("\n%s:\n", _("Authentication (two-phase)")); + printf(" -C, --cookie=COOKIE %s\n", _("Use WebVPN cookie COOKIE")); + printf(" --cookie-on-stdin %s\n", _("Read cookie from standard input")); + printf(" --authenticate %s\n", _("Authenticate only and print login info")); + printf(" --cookieonly %s\n", _("Fetch webvpn cookie only; don't connect")); + printf(" --printcookie %s\n", _("Print webvpn cookie before connecting")); + +#ifndef _WIN32 + printf("\n%s:\n", _("Process control")); + printf(" -b, --background %s\n", _("Continue in background after startup")); + printf(" --pid-file=PIDFILE %s\n", _("Write the daemon's PID to this file")); + printf(" -U, --setuid=USER %s\n", _("Drop privileges after connecting")); +#endif + + printf("\n%s:\n", _("Logging (two-phase)")); +#ifndef _WIN32 + printf(" -l, --syslog %s\n", _("Use syslog for progress messages")); +#endif + printf(" -v, --verbose %s\n", _("More output")); printf(" -q, --quiet %s\n", _("Less output")); - printf(" -Q, --queue-len=LEN %s\n", _("Set packet queue limit to LEN pkts")); + printf(" --dump-http-traffic %s\n", _("Dump HTTP authentication traffic (implies --verbose")); + printf(" --timestamp %s\n", _("Prepend timestamp to progress messages")); + + printf("\n%s:\n", _("VPN configuration script")); + printf(" -i, --interface=IFNAME %s\n", _("Use IFNAME for tunnel interface")); printf(" -s, --script=SCRIPT %s\n", _("Shell command line for using a vpnc-compatible config script")); printf(" %s: \"%s\"\n", _("default"), default_vpncscript); #ifndef _WIN32 printf(" -S, --script-tun %s\n", _("Pass traffic to 'script' program, not tun")); #endif - printf(" -u, --user=NAME %s\n", _("Set login username")); - printf(" -V, --version %s\n", _("Report version number")); - printf(" -v, --verbose %s\n", _("More output")); - printf(" --dump-http-traffic %s\n", _("Dump HTTP authentication traffic (implies --verbose")); - printf(" -x, --xmlconfig=CONFIG %s\n", _("XML config file")); - printf(" --authgroup=GROUP %s\n", _("Choose authentication login selection")); - printf(" --authenticate %s\n", _("Authenticate only and print login info")); - printf(" --cookieonly %s\n", _("Fetch webvpn cookie only; don't connect")); - printf(" --printcookie %s\n", _("Print webvpn cookie before connecting")); - printf(" --cafile=FILE %s\n", _("Cert file for server verification")); + + printf("\n%s:\n", _("Tunnel control")); printf(" --disable-ipv6 %s\n", _("Do not ask for IPv6 connectivity")); - printf(" --dtls-ciphers=LIST %s\n", _("OpenSSL ciphers to support for DTLS")); + printf(" -x, --xmlconfig=CONFIG %s\n", _("XML config file")); + printf(" -m, --mtu=MTU %s\n", _("Request MTU from server (legacy servers only)")); + printf(" --base-mtu=MTU %s\n", _("Indicate path MTU to/from server")); + printf(" -d, --deflate %s\n", _("Enable stateful compression (default is stateless only)")); + printf(" -D, --no-deflate %s\n", _("Disable all compression")); + printf(" --force-dpd=INTERVAL %s\n", _("Set minimum Dead Peer Detection interval")); + printf(" --pfs %s\n", _("Require perfect forward secrecy")); printf(" --no-dtls %s\n", _("Disable DTLS")); - printf(" --no-http-keepalive %s\n", _("Disable HTTP connection re-use")); - printf(" --no-passwd %s\n", _("Disable password/SecurID authentication")); - printf(" --no-cert-check %s\n", _("Do not require server SSL cert to be valid")); - printf(" --no-system-trust %s\n", _("Disable default system certificate authorities")); - printf(" --no-xmlpost %s\n", _("Do not attempt XML POST authentication")); - printf(" --non-inter %s\n", _("Do not expect user input; exit if it is required")); - printf(" --passwd-on-stdin %s\n", _("Read password from standard input")); - printf(" --token-mode=MODE %s\n", _("Software token type: rsa, totp or hotp")); - printf(" --token-secret=STRING %s\n", _("Software token secret")); -#ifndef HAVE_LIBSTOKEN - printf(" %s\n", _("(NOTE: libstoken (RSA SecurID) disabled in this build)")); -#endif -#ifndef HAVE_LIBPCSCLITE - printf(" %s\n", _("(NOTE: Yubikey OATH disabled in this build)")); -#endif - printf(" --reconnect-timeout %s\n", _("Connection retry timeout in seconds")); - printf(" --servercert=FINGERPRINT %s\n", _("Server's certificate SHA1 fingerprint")); + printf(" --dtls-ciphers=LIST %s\n", _("OpenSSL ciphers to support for DTLS")); + printf(" -Q, --queue-len=LEN %s\n", _("Set packet queue limit to LEN pkts")); + printf(" --request-ip=IP %s\n", _("Request a specific IPv4 address")); + + printf("\n%s:\n", _("Local system information")); printf(" --useragent=STRING %s\n", _("HTTP header User-Agent: field")); printf(" --local-hostname=STRING %s\n", _("Local hostname to advertise to server")); - printf(" --resolve=HOST:IP %s\n", _("Use IP when connecting to HOST")); printf(" --os=STRING %s\n", _("OS type (linux,linux-64,win,...) to report")); - printf(" --dtls-local-port=PORT %s\n", _("Set local port for DTLS datagrams")); - print_supported_protocols_usage(); + +#ifndef _WIN32 + printf("\n%s:\n", _("CSD execution")); + printf(" --csd-user=USER %s\n", _("Drop privileges during CSD execution")); + printf(" --csd-wrapper=SCRIPT %s\n", _("Run SCRIPT instead of CSD binary")); +#endif + + printf("\n%s:\n", _("Server bugs")); + printf(" --no-http-keepalive %s\n", _("Disable HTTP connection re-use")); + printf(" --no-xmlpost %s\n", _("Do not attempt XML POST authentication")); printf("\n"); @@ -913,9 +934,21 @@ static char *xstrdup(const char *arg) #define keep_config_arg() \ (config_file ? xstrdup(config_arg) : convert_arg_to_utf8(argv, config_arg)) -#define dup_config_arg() \ - ((config_file || is_arg_utf8(config_arg)) ? xstrdup(config_arg) : \ - convert_arg_to_utf8(argv, config_arg)) +#define dup_config_arg() __dup_config_arg(argv, config_arg) + +static inline char *__dup_config_arg(char **argv, char *config_arg) +{ + char *res; + + if (config_file || is_arg_utf8(config_arg)) + return xstrdup(config_arg); + + res = convert_arg_to_utf8(argv, config_arg); + /* Force a copy, even if conversion failed */ + if (res == config_arg) + res = xstrdup(res); + return res; +} static int next_option(int argc, char **argv, char **config_arg) { @@ -1054,7 +1087,7 @@ int main(int argc, char **argv) char *urlpath = NULL; struct oc_vpn_option *gai; char *ip; - const char *compr = ""; + const char *ssl_compr, *udp_compr; char *proxy = getenv("https_proxy"); char *vpnc_script = NULL; const struct oc_ip_info *ip_info; @@ -1081,7 +1114,9 @@ int main(int argc, char **argv) bindtextdomain("openconnect", LOCALEDIR); #endif - setlocale(LC_ALL, ""); + if (!setlocale(LC_ALL, "")) + fprintf(stderr, + _("WARNING: Cannot set locale: %s\n"), strerror(errno)); #ifdef HAVE_NL_LANGINFO charset = nl_langinfo(CODESET); @@ -1156,6 +1191,10 @@ int main(int argc, char **argv) if (openconnect_set_protocol(vpninfo, config_arg)) exit(1); break; + case OPT_SERVER: + if (openconnect_parse_url(vpninfo, config_arg)) + exit(1); + break; case OPT_JUNIPER: fprintf(stderr, "WARNING: Juniper Network Connect support is experimental.\n"); fprintf(stderr, "It will probably be superseded by Junos Pulse support.\n"); @@ -1263,6 +1302,9 @@ int main(int argc, char **argv) case OPT_AUTHGROUP: authgroup = keep_config_arg(); break; + case OPT_REQUEST_IP: + vpninfo->ip_info.addr = keep_config_arg(); + break; case 'C': vpninfo->cookie = dup_config_arg(); break; @@ -1448,7 +1490,7 @@ int main(int argc, char **argv) if (optind < argc - 1) { fprintf(stderr, _("Too many arguments on command line\n")); usage(); - } else if (optind > argc - 1) { + } else if (optind > argc - 1 && !vpninfo->hostname) { fprintf(stderr, _("No server specified\n")); usage(); } @@ -1504,7 +1546,10 @@ int main(int argc, char **argv) if (config_lookup_host(vpninfo, argv[optind])) exit(1); - if (!vpninfo->hostname) { + /* The last argument without a corresponding --option is taken + * to be the server URL and overrides any --server option on the + * command line or from a --config */ + if (!vpninfo->hostname || optind < argc) { char *url = strdup(argv[optind]); if (openconnect_parse_url(vpninfo, url)) @@ -1564,33 +1609,21 @@ int main(int argc, char **argv) * reconnects end up in infinite loop trying to connect * to non existing DTLS */ vpninfo->dtls_state = DTLS_DISABLED; - fprintf(stderr, _("Set up DTLS failed; using SSL instead\n")); + fprintf(stderr, _("Set up UDP failed; using SSL instead\n")); } openconnect_get_ip_info(vpninfo, &ip_info, NULL, NULL); - if (vpninfo->dtls_state != DTLS_CONNECTED) { - if (vpninfo->cstp_compr == COMPR_DEFLATE) - compr = " + deflate"; - else if (vpninfo->cstp_compr == COMPR_LZS) - compr = " + lzs"; - else if (vpninfo->cstp_compr == COMPR_LZ4) - compr = " + lz4"; - } else { - if (vpninfo->dtls_compr == COMPR_DEFLATE) - compr = " + deflate"; - else if (vpninfo->dtls_compr == COMPR_LZS) - compr = " + lzs"; - else if (vpninfo->dtls_compr == COMPR_LZ4) - compr = " + lz4"; - } + ssl_compr = openconnect_get_cstp_compression(vpninfo); + udp_compr = openconnect_get_dtls_compression(vpninfo); vpn_progress(vpninfo, PRG_INFO, - _("Connected as %s%s%s, using %s%s\n"), + _("Connected as %s%s%s, using SSL%s%s, with %s%s%s %s\n"), ip_info->addr?:"", (ip_info->netmask6 && ip_info->addr) ? " + " : "", ip_info->netmask6 ? : "", - (vpninfo->dtls_state != DTLS_CONNECTED) ? "SSL" - : "DTLS", compr); + ssl_compr ? " + " : "", ssl_compr ? : "", + vpninfo->proto->udp_protocol ? : "UDP", udp_compr ? " + " : "", udp_compr ? : "", + (vpninfo->dtls_state == DTLS_DISABLED || vpninfo->dtls_state == DTLS_NOSECRET ? _("disabled") : _("in progress"))); if (!vpninfo->vpnc_script) { vpn_progress(vpninfo, PRG_INFO, diff --git a/mainloop.c b/mainloop.c index 754d7d4f..6c125c31 100644 --- a/mainloop.c +++ b/mainloop.c @@ -315,7 +315,7 @@ int openconnect_mainloop(struct openconnect_info *vpninfo, return ret < 0 ? ret : -EIO; } -static int ka_check_deadline(int *timeout, time_t now, time_t due) +int ka_check_deadline(int *timeout, time_t now, time_t due) { if (now >= due) return 1; diff --git a/oncp.c b/oncp.c index 17853af9..675b9ae1 100644 --- a/oncp.c +++ b/oncp.c @@ -323,6 +323,7 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, if (attrlen != 1) goto badlen; vpninfo->esp_compr = data[0]; + vpninfo->dtls_compr = data[0] ? COMPR_LZO : 0; vpn_progress(vpninfo, PRG_DEBUG, _("ESP compression: %d\n"), data[0]); break; @@ -559,52 +560,14 @@ int oncp_connect(struct openconnect_info *vpninfo) reqbuf = buf_alloc(); - buf_append(reqbuf, "POST /dana/js?prot=1&svc=1 HTTP/1.1\r\n"); - oncp_common_headers(vpninfo, reqbuf); - buf_append(reqbuf, "Content-Length: 256\r\n"); - buf_append(reqbuf, "\r\n"); - - if (buf_error(reqbuf)) { - vpn_progress(vpninfo, PRG_ERR, - _("Error creating oNCP negotiation request\n")); - ret = buf_error(reqbuf); - goto out; - } - - ret = vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos); - if (ret < 0) - goto out; - - /* The server is fairly weird. It sends Connection: close which would - * indicate an HTTP 1.0-style body, but doesn't seem to actually close - * the connection. So tell process_http_response() it was a CONNECT - * request, since we don't care about the body anyway, and then close - * the connection for ourselves. */ - ret = process_http_response(vpninfo, 1, NULL, reqbuf); - openconnect_close_https(vpninfo, 0); - if (ret < 0) { - /* We'll already have complained about whatever offended us */ - goto out; - } - if (ret != 200) { - vpn_progress(vpninfo, PRG_ERR, - _("Unexpected %d result from server\n"), - ret); - ret = -EINVAL; - goto out; - } - - /* Now the second request. We should reduce the duplication - here but let's not overthink it for now; we should see what - the authentication requests are going to look like, and make - do_https_request() or a new helper function work for those - too. */ - ret = openconnect_open_https(vpninfo); - if (ret) - goto out; - - buf_truncate(reqbuf); buf_append(reqbuf, "POST /dana/js?prot=1&svc=4 HTTP/1.1\r\n"); + /* The TLS socket actually remains open for use by the oNCP + tunnel, but the "Connection: close" header is nevertheless + required here. It appears to signal to the server to stop + treating this as an HTTP connection and to start treating + it as an oNCP connection. + */ + buf_append(reqbuf, "Connection: close\r\n"); oncp_common_headers(vpninfo, reqbuf); buf_append(reqbuf, "Content-Length: 256\r\n"); buf_append(reqbuf, "\r\n"); diff --git a/openconnect-internal.h b/openconnect-internal.h index 7b3284df..63c2f348 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -122,6 +122,9 @@ #ifndef MAX #define MAX(x,y) ((x)>(y))?(x):(y) #endif +#ifndef MIN +#define MIN(x,y) ((x)<(y))?(x):(y) +#endif /****************************************************************************/ struct pkt { @@ -171,10 +174,11 @@ struct pkt { #define COMPR_DEFLATE (1<<0) #define COMPR_LZS (1<<1) #define COMPR_LZ4 (1<<2) -#define COMPR_MAX COMPR_LZ4 +#define COMPR_LZO (1<<3) +#define COMPR_MAX COMPR_LZO #ifdef HAVE_LZ4 -#define COMPR_STATELESS (COMPR_LZS | COMPR_LZ4) +#define COMPR_STATELESS (COMPR_LZS | COMPR_LZ4 | COMPR_LZO) #else #define COMPR_STATELESS (COMPR_LZS) #endif @@ -260,6 +264,8 @@ struct vpn_proto { const char *name; const char *pretty_name; const char *description; + const char *udp_protocol; + const char *override_useragent; unsigned int flags; int (*vpn_close_session)(struct openconnect_info *vpninfo, const char *reason); @@ -375,7 +381,7 @@ struct openconnect_info { struct esp esp_out; int enc_key_len; int hmac_key_len; - in_addr_t esp_magic; /* GlobalProtect magic ping address (network-endian) */ + uint32_t esp_magic; /* GlobalProtect magic ping address (network-endian) */ int tncc_fd; /* For Juniper TNCC */ const char *csd_xmltag; @@ -514,15 +520,6 @@ struct openconnect_info { TSS_HKEY tpm_key; TSS_HPOLICY tpm_key_policy; #endif -#ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY -#ifdef HAVE_P11KIT - gnutls_pkcs11_privkey_t my_p11key; -#endif - gnutls_privkey_t my_pkey; - gnutls_x509_crt_t *my_certs; - uint8_t *free_my_certs; - unsigned int nr_my_certs; -#endif #endif /* OPENCONNECT_GNUTLS */ struct pin_cache *pin_cache; struct keepalive_info ssl_times; @@ -970,6 +967,7 @@ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout); int queue_new_packet(struct pkt_q *q, void *buf, int len); int keepalive_action(struct keepalive_info *ka, int *timeout); int ka_stalled_action(struct keepalive_info *ka, int *timeout); +int ka_check_deadline(int *timeout, time_t now, time_t due); /* xml.c */ ssize_t read_file_into_string(struct openconnect_info *vpninfo, const char *fname, @@ -1043,6 +1041,7 @@ int get_utf8char(const char **utf8); void buf_append_from_utf16le(struct oc_text_buf *buf, const void *utf16); void buf_truncate(struct oc_text_buf *buf); void buf_append_urlencoded(struct oc_text_buf *buf, const char *str); +void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str); int buf_error(struct oc_text_buf *buf); int buf_free(struct oc_text_buf *buf); char *openconnect_create_useragent(const char *base); diff --git a/openconnect.8.in b/openconnect.8.in index 9f46b305..65b26f3c 100644 --- a/openconnect.8.in +++ b/openconnect.8.in @@ -66,6 +66,7 @@ openconnect \- Multi-protocol VPN client, for Cisco AnyConnect VPNs and others .OP \-\-useragent string .OP \-\-local-hostname string .OP \-\-os string +.OP \-\-request-ip ip .B [https://]\fIserver\fB[:\fIport\fB][/\fIgroup\fB] .YS @@ -523,6 +524,11 @@ applied to the VPN session. If the gateway requires CSD, it will also cause the corresponding CSD trojan binary to be downloaded, so you may need to use .B \-\-csd\-wrapper if this code is not executable on the local machine. +.TP +.B \-\-request-ip=IP +Request a specific IPv4 address from the gateway. Currently, OpenConnect +will print a warning but will not abort if the gateway provides a different +IPv4 address. .SH SIGNALS In the data phase of the connection, the following signals are handled: .TP diff --git a/openssl-esp.c b/openssl-esp.c index 92662ddf..8af838b9 100644 --- a/openssl-esp.c +++ b/openssl-esp.c @@ -155,8 +155,8 @@ int setup_esp_keys(struct openconnect_info *vpninfo, int new_keys) if (new_keys) { if (!RAND_bytes((void *)&esp_in->spi, sizeof(esp_in->spi)) || - !RAND_bytes((void *)&esp_in->enc_key, vpninfo->enc_key_len) || - !RAND_bytes((void *)&esp_in->hmac_key, vpninfo->hmac_key_len) ) { + !RAND_bytes((void *)&esp_in->enc_key, vpninfo->enc_key_len) || + !RAND_bytes((void *)&esp_in->hmac_key, vpninfo->hmac_key_len) ) { vpn_progress(vpninfo, PRG_ERR, _("Failed to generate random keys for ESP:\n")); openconnect_report_ssl_errors(vpninfo); @@ -204,7 +204,8 @@ int decrypt_esp_packet(struct openconnect_info *vpninfo, struct esp *esp, struct if (vpninfo->esp_replay_protect && verify_packet_seqno(vpninfo, esp, ntohl(pkt->esp.seq))) return -EINVAL; - + else + esp->seq = ntohl(pkt->esp.seq) + 1; if (!EVP_DecryptInit_ex(esp->cipher, NULL, NULL, NULL, pkt->esp.iv)) { diff --git a/po/pl.po b/po/pl.po index bed87c55..05aa7d66 100644 --- a/po/pl.po +++ b/po/pl.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: openconnect\n" "Report-Msgid-Bugs-To: openconnect-devel@lists.infradead.org\n" -"POT-Creation-Date: 2017-05-15 00:47+0100\n" +"POT-Creation-Date: 2017-05-31 11:37+0100\n" "PO-Revision-Date: 2011-09-22 22:31+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: Polish (http://www.transifex.net/projects/p/meego/team/pl/)\n" @@ -464,7 +464,7 @@ msgstr "" msgid "deflate failed %d\n" msgstr "%d „deflate” się nie powiodło\n" -#: cstp.c:903 dtls.c:261 dtls.c:720 esp.c:171 mainloop.c:69 oncp.c:937 +#: cstp.c:903 dtls.c:281 dtls.c:740 esp.c:166 mainloop.c:69 oncp.c:954 msgid "Allocation failed\n" msgstr "Przydzielenie się nie powiodło\n" @@ -491,7 +491,7 @@ msgstr "Otrzymano odpowiedź „DPD” CSTP\n" msgid "Got CSTP Keepalive\n" msgstr "Otrzymano „Keepalive” CSTP\n" -#: cstp.c:957 oncp.c:1026 +#: cstp.c:957 oncp.c:1043 #, c-format msgid "Received uncompressed data packet of %d bytes\n" msgstr "Otrzymano nieskompresowany pakiet danych o rozmiarze %d bajtów\n" @@ -518,35 +518,35 @@ msgstr "otrzymano pakiet wymuszenia zakończenia serwera\n" msgid "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n" msgstr "Nieznany pakiet %02x %02x %02x %02x %02x %02x %02x %02x\n" -#: cstp.c:1044 oncp.c:1143 +#: cstp.c:1044 oncp.c:1160 #, c-format msgid "SSL wrote too few bytes! Asked for %d, sent %d\n" msgstr "SSL zapisało za mało bajtów. Poproszono o %d, wysłano %d\n" #. Not that this will ever happen; we don't even process #. the setting when we're asked for it. -#: cstp.c:1072 oncp.c:1181 +#: cstp.c:1072 oncp.c:1198 msgid "CSTP rekey due\n" msgstr "„rekey” CSTP do\n" #. if we failed rehandshake try establishing a new-tunnel instead of failing -#: cstp.c:1079 oncp.c:1188 +#: cstp.c:1079 oncp.c:1205 msgid "Rehandshake failed; attempting new-tunnel\n" msgstr "Ponowne powitanie się nie powiodło. Próbowanie „new-tunnel”\n" -#: cstp.c:1090 oncp.c:1199 +#: cstp.c:1090 oncp.c:1216 msgid "CSTP Dead Peer Detection detected dead peer!\n" msgstr "Wykrywanie martwych partnerów CSTP wykryło martwego partnera.\n" -#: cstp.c:1094 oncp.c:1113 oncp.c:1203 +#: cstp.c:1094 oncp.c:1130 oncp.c:1220 msgid "Reconnect failed\n" msgstr "Ponowne połączenie się nie powiodło\n" -#: cstp.c:1110 oncp.c:1219 +#: cstp.c:1110 oncp.c:1236 msgid "Send CSTP DPD\n" msgstr "Wysłanie „DPD” CSTP\n" -#: cstp.c:1122 oncp.c:1230 +#: cstp.c:1122 oncp.c:1247 msgid "Send CSTP Keepalive\n" msgstr "Wysłanie „Keepalive” CSTP\n" @@ -557,7 +557,7 @@ msgstr "" "Wysyłanie nieskompresowanego pakietu danych o rozmiarze %d bajtów (wynosił " "%d)\n" -#: cstp.c:1158 oncp.c:1255 +#: cstp.c:1158 oncp.c:1272 #, c-format msgid "Sending uncompressed data packet of %d bytes\n" msgstr "Wysyłanie nieskompresowanego pakietu danych o rozmiarze %d bajtów\n" @@ -576,206 +576,206 @@ msgstr "Próbowanie uwierzytelnienia „Digest” do pośrednika\n" msgid "Attempting Digest authentication to server '%s'\n" msgstr "Próbowanie uwierzytelnienia „Digest” do serwera „%s”\n" -#: dtls.c:93 +#: dtls.c:113 msgid "DTLS connection attempted with an existing fd\n" msgstr "Próbowano połączenia DTLS za pomocą istniejącego deskryptora pliku\n" -#: dtls.c:99 +#: dtls.c:119 msgid "No DTLS address\n" msgstr "Brak adresu DTLS\n" #. We probably didn't offer it any ciphers it liked -#: dtls.c:106 +#: dtls.c:126 msgid "Server offered no DTLS cipher option\n" msgstr "Serwer nie zaproponował żadnej opcji szyfrowania DTLS\n" #. XXX: Theoretically, SOCKS5 proxies can do UDP too -#: dtls.c:113 +#: dtls.c:133 msgid "No DTLS when connected via proxy\n" msgstr "Brak DTLS podczas łączenia przez pośrednika\n" -#: dtls.c:179 +#: dtls.c:199 #, c-format msgid "DTLS option %s : %s\n" msgstr "Opcja DTLS %s: %s\n" -#: dtls.c:220 +#: dtls.c:240 #, c-format msgid "DTLS initialised. DPD %d, Keepalive %d\n" msgstr "Zainicjowano DTLS. DPD %d, Keepalive %d\n" -#: dtls.c:246 +#: dtls.c:266 msgid "Attempt new DTLS connection\n" msgstr "Próba nowego połączenia DTLS\n" -#: dtls.c:272 +#: dtls.c:292 #, c-format msgid "Received DTLS packet 0x%02x of %d bytes\n" msgstr "Otrzymano pakiet DTLS 0x%02x z %d bajtów\n" -#: dtls.c:286 +#: dtls.c:306 msgid "Got DTLS DPD request\n" msgstr "Otrzymano żądanie „DPD” DTLS\n" -#: dtls.c:292 +#: dtls.c:312 msgid "Failed to send DPD response. Expect disconnect\n" msgstr "" "Wysłanie odpowiedzi DPD się nie powiodło. Należy oczekiwać rozłączenia\n" -#: dtls.c:296 +#: dtls.c:316 msgid "Got DTLS DPD response\n" msgstr "Otrzymano odpowiedź „DPD” DTLS\n" -#: dtls.c:300 +#: dtls.c:320 msgid "Got DTLS Keepalive\n" msgstr "Otrzymano „Keepalive” DTLS\n" -#: dtls.c:306 +#: dtls.c:326 msgid "Compressed DTLS packet received when compression not enabled\n" msgstr "Otrzymano skompresowany pakiet DTLS, kiedy kompresja jest wyłączona\n" -#: dtls.c:314 +#: dtls.c:334 #, c-format msgid "Unknown DTLS packet type %02x, len %d\n" msgstr "Nieznany typ pakietu DTLS %02x, len %d\n" -#: dtls.c:336 +#: dtls.c:356 msgid "DTLS rekey due\n" msgstr "„rekey” DTLS do\n" -#: dtls.c:343 +#: dtls.c:363 msgid "DTLS Rehandshake failed; reconnecting.\n" msgstr "Ponowne powitanie DTLS się nie powiodło. Łączenie ponownie.\n" -#: dtls.c:352 +#: dtls.c:372 msgid "DTLS Dead Peer Detection detected dead peer!\n" msgstr "Wykrywanie martwych partnerów DTLS wykryło martwego partnera.\n" -#: dtls.c:358 +#: dtls.c:378 msgid "Send DTLS DPD\n" msgstr "Wysłanie „DPD” DTLS\n" -#: dtls.c:363 +#: dtls.c:383 msgid "Failed to send DPD request. Expect disconnect\n" msgstr "Wysłanie żądania DPD się nie powiodło. Należy oczekiwać rozłączenia\n" -#: dtls.c:376 +#: dtls.c:396 msgid "Send DTLS Keepalive\n" msgstr "Wysłanie „Keepalive” DTLS\n" -#: dtls.c:381 +#: dtls.c:401 msgid "Failed to send keepalive request. Expect disconnect\n" msgstr "" "Wysłanie żądania Keepalive się nie powiodło. Należy oczekiwać rozłączenia\n" -#: dtls.c:412 tun.c:541 +#: dtls.c:432 tun.c:541 #, c-format msgid "Unknown packet (len %d) received: %02x %02x %02x %02x...\n" msgstr "Otrzymano nieznany pakiet (len %d): %02x %02x %02x %02x...\n" -#: dtls.c:419 +#: dtls.c:439 #, c-format msgid "TOS this: %d, TOS last: %d\n" msgstr "Ten TOS: %d, ostatni TOS: %d\n" -#: dtls.c:423 +#: dtls.c:443 msgid "UDP setsockopt" msgstr "setsockopt UDP" -#: dtls.c:454 +#: dtls.c:474 #, c-format msgid "DTLS got write error %d. Falling back to SSL\n" msgstr "DTLS otrzymało błąd zapisu %d. Używanie SSL\n" -#: dtls.c:468 +#: dtls.c:488 #, c-format msgid "DTLS got write error: %s. Falling back to SSL\n" msgstr "DTLS otrzymało błąd zapisu: %s. Używanie SSL\n" -#: dtls.c:483 +#: dtls.c:503 #, c-format msgid "Sent DTLS packet of %d bytes; DTLS send returned %d\n" msgstr "Wysłano pakiet DTLS o rozmiarze %d bajtów. Wysłanie DTLS zwróciło %d\n" -#: dtls.c:511 +#: dtls.c:531 #, c-format msgid "Initiating IPv4 MTU detection (min=%d, max=%d)\n" msgstr "Inicjowanie wykrywania MTU IPv4 (min=%d, max=%d)\n" -#: dtls.c:531 +#: dtls.c:551 msgid "Too long time in MTU detect loop; assuming negotiated MTU.\n" msgstr "" "Za dużo czasu w pętli wykrywania MTU. Przyjmowanie wynegocjowanego MTU.\n" -#: dtls.c:535 +#: dtls.c:555 #, c-format msgid "Too long time in MTU detect loop; MTU set to %d.\n" msgstr "Za dużo czasu w pętli wykrywania MTU. Ustawiono MTU na %d.\n" -#: dtls.c:544 +#: dtls.c:564 #, c-format msgid "Sending MTU DPD probe (%u bytes, min=%u, max=%u)\n" msgstr "Wysyłanie sondy „DPD” MTU (%u bajtów, min=%u, max=%u)\n" -#: dtls.c:548 +#: dtls.c:568 #, c-format msgid "Failed to send DPD request (%d %d)\n" msgstr "Wysłanie żądania DPD się nie powiodło (%d %d)\n" -#: dtls.c:562 dtls.c:670 +#: dtls.c:582 dtls.c:690 #, c-format msgid "Received unexpected packet (%.2x) in MTU detection; skipping.\n" msgstr "" "Odebrano nieoczekiwany pakiet (%.2x) podczas wykrywania MTU. Pomijanie.\n" -#: dtls.c:574 +#: dtls.c:594 #, c-format msgid "Timeout while waiting for DPD response; trying %d\n" msgstr "" "Przekroczono czas oczekiwania podczas oczekiwania na odpowiedź DPD. " "Próbowanie %d\n" -#: dtls.c:581 dtls.c:662 +#: dtls.c:601 dtls.c:682 msgid "Timeout while waiting for DPD response; resending probe.\n" msgstr "" "Przekroczono czas oczekiwania podczas czekania na odpowiedź DPD. Ponowne " "wysyłanie sondy.\n" -#: dtls.c:588 dtls.c:689 +#: dtls.c:608 dtls.c:709 #, c-format msgid "Failed to recv DPD request (%d)\n" msgstr "Odebranie żądania DPD się nie powiodło (%d)\n" -#: dtls.c:593 +#: dtls.c:613 #, c-format msgid "Received MTU DPD probe (%u bytes of %u)\n" msgstr "Otrzymano sondę „DPD” MTU (%u bajtów z %u)\n" -#: dtls.c:631 +#: dtls.c:651 msgid "Initiating IPv6 MTU detection\n" msgstr "Inicjowanie wykrywania MTU IPv6\n" -#: dtls.c:646 +#: dtls.c:666 #, c-format msgid "Sending MTU DPD probe (%u bytes)\n" msgstr "Wysyłanie sondy „DPD” MTU (%u bajtów)\n" -#: dtls.c:650 +#: dtls.c:670 #, c-format msgid "Failed to send DPD request (%d)\n" msgstr "Wysłanie żądania DPD się nie powiodło (%d)\n" -#: dtls.c:675 +#: dtls.c:695 #, c-format msgid "Received MTU DPD probe (%u bytes)\n" msgstr "Otrzymano sondę „DPD” MTU (%u bajtów)\n" -#: dtls.c:739 +#: dtls.c:759 #, c-format msgid "Detected MTU of %d bytes (was %d)\n" msgstr "Wykryto MTU o rozmiarze %d bajtów (wynosił %d)\n" -#: dtls.c:742 +#: dtls.c:762 #, c-format msgid "No change in MTU after detection (was %d)\n" msgstr "Brak zmian w MTU po wykrywaniu (wynosił %d)\n" @@ -810,101 +810,101 @@ msgstr "" "Przyjmowanie pakietu ESP poza kolejnością z sekwencją %u (oczekiwano " "%)\n" -#: esp.c:68 +#: esp.c:63 #, c-format msgid "Parameters for %s ESP: SPI 0x%08x\n" msgstr "Parametry dla ESP %s: SPI 0x%08x\n" -#: esp.c:71 +#: esp.c:66 #, c-format msgid "ESP encryption type %s key 0x%s\n" msgstr "Szyfrowanie ESP typu %s klucz 0x%s\n" -#: esp.c:74 +#: esp.c:69 #, c-format msgid "ESP authentication type %s key 0x%s\n" msgstr "Uwierzytelnienie ESP typu %s klucz 0x%s\n" -#: esp.c:133 +#: esp.c:128 msgid "incoming" msgstr "przychodzące" -#: esp.c:134 +#: esp.c:129 msgid "outgoing" msgstr "wychodzące" -#: esp.c:136 esp.c:153 +#: esp.c:131 esp.c:148 msgid "Send ESP probes\n" msgstr "Wysłanie próbek ESP\n" -#: esp.c:180 +#: esp.c:175 #, c-format msgid "Received ESP packet of %d bytes\n" msgstr "Otrzymano pakiet ESP o rozmiarze %d bajtów\n" -#: esp.c:196 +#: esp.c:191 #, c-format msgid "Consider SPI 0x%x, seq %u against outgoing ESP setup\n" msgstr "Rozważenie SPI 0x%x, sekwencja %u z wychodzącymi ustawieniami ESP\n" -#: esp.c:202 +#: esp.c:197 #, c-format msgid "Received ESP packet with invalid SPI 0x%08x\n" msgstr "Otrzymano pakiet ESP z nieprawidłowym SPI 0x%08x\n" -#: esp.c:210 +#: esp.c:205 #, c-format msgid "Received ESP packet with unrecognised payload type %02x\n" msgstr "Otrzymano pakiet ESP z nierozpoznanym typem ładunku %02x\n" -#: esp.c:217 +#: esp.c:212 #, c-format msgid "Invalid padding length %02x in ESP\n" msgstr "Nieprawidłowa długość wypełnienia %02x w ESP\n" -#: esp.c:229 +#: esp.c:224 msgid "Invalid padding bytes in ESP\n" msgstr "Nieprawidłowe bajty wypełnienia w ESP\n" -#: esp.c:237 +#: esp.c:232 msgid "ESP session established with server\n" msgstr "Nawiązano sesję ESP z serwerem\n" -#: esp.c:248 +#: esp.c:243 msgid "Failed to allocate memory to decrypt ESP packet\n" msgstr "Przydzielenie pamięci do odszyfrowania pakietu ESP się nie powiodło\n" -#: esp.c:254 +#: esp.c:249 msgid "LZO decompression of ESP packet failed\n" msgstr "Dekompresja LZO pakietu ESP się nie powiodła\n" -#: esp.c:260 +#: esp.c:255 #, c-format msgid "LZO decompressed %d bytes into %d\n" msgstr "LZO zdekompresowało %d bajtów do %d\n" -#: esp.c:274 +#: esp.c:269 msgid "Rekey not implemented for ESP\n" msgstr "„rekey” nie jest zaimplementowane dla ESP\n" -#: esp.c:278 +#: esp.c:273 msgid "ESP detected dead peer\n" msgstr "ESP wykryło martwego partnera\n" -#: esp.c:285 +#: esp.c:280 msgid "Send ESP probes for DPD\n" msgstr "Wysłanie próbek ESP dla DPD\n" -#: esp.c:291 +#: esp.c:286 msgid "Keepalive not implemented for ESP\n" msgstr "„Keepalive” nie jest zaimplementowane dla ESP\n" -#: esp.c:314 +#: esp.c:309 #, c-format msgid "Failed to send ESP packet: %s\n" msgstr "Wysłanie pakietu ESP się nie powiodło: %s\n" -#: esp.c:320 +#: esp.c:315 #, c-format msgid "Sent ESP packet of %d bytes\n" msgstr "Wysłano pakiet ESP o rozmiarze %d bajtów\n" @@ -1014,31 +1014,31 @@ msgstr "Zainicjowanie szyfru ESP się nie powiodło: %s\n" msgid "Failed to initialize ESP HMAC: %s\n" msgstr "Zainicjowanie „HMAC” ESP się nie powiodło: %s\n" -#: gnutls-esp.c:116 +#: gnutls-esp.c:117 #, c-format msgid "Failed to generate random keys for ESP: %s\n" msgstr "Utworzenie losowych kluczy dla ESP się nie powiodło: %s\n" -#: gnutls-esp.c:146 gnutls-esp.c:212 +#: gnutls-esp.c:147 gnutls-esp.c:213 #, c-format msgid "Failed to calculate HMAC for ESP packet: %s\n" msgstr "Obliczenie HMAC dla pakietu ESP się nie powiodło: %s\n" -#: gnutls-esp.c:153 openssl-esp.c:191 +#: gnutls-esp.c:154 openssl-esp.c:192 msgid "Received ESP packet with invalid HMAC\n" msgstr "Otrzymano pakiet ESP z nieprawidłowym HMAC\n" -#: gnutls-esp.c:169 +#: gnutls-esp.c:170 #, c-format msgid "Decrypting ESP packet failed: %s\n" msgstr "Odszyfrowanie pakietu ESP się nie powiodło: %s\n" -#: gnutls-esp.c:189 +#: gnutls-esp.c:190 #, c-format msgid "Failed to generate ESP packet IV: %s\n" msgstr "Utworzenie pakietu „IV” ESP się nie powiodło: %s\n" -#: gnutls-esp.c:204 +#: gnutls-esp.c:205 #, c-format msgid "Failed to encrypt ESP packet: %s\n" msgstr "Zaszyfrowanie pakietu ESP się nie powiodło: %s\n" @@ -1082,11 +1082,11 @@ msgstr "Wysłanie SSL się nie powiodło: %s\n" msgid "Could not extract expiration time of certificate\n" msgstr "Nie można wydobyć czasu wygaśnięcia certyfikatu\n" -#: gnutls.c:351 openssl.c:1621 +#: gnutls.c:351 openssl.c:1616 msgid "Client certificate has expired at" msgstr "Certyfikat klienta wygasł w" -#: gnutls.c:353 openssl.c:1626 +#: gnutls.c:353 openssl.c:1621 msgid "Client certificate expires soon at" msgstr "Certyfikat klienta niedługo wygaśnie w" @@ -1390,158 +1390,158 @@ msgstr "Dodawanie wspierającego CA „%s”\n" msgid "Setting certificate failed: %s\n" msgstr "Ustawienie certyfikatu się nie powiodło: %s\n" -#: gnutls.c:2078 +#: gnutls.c:2073 msgid "Server presented no certificate\n" msgstr "Serwer nie przedstawił żadnego certyfikatu\n" -#: gnutls.c:2086 +#: gnutls.c:2081 #, c-format msgid "Error comparing server's cert on rehandshake: %s\n" msgstr "" "Błąd podczas porównywania certyfikatu serwera przy ponownym powitaniu: %s\n" -#: gnutls.c:2091 openssl.c:1544 +#: gnutls.c:2086 openssl.c:1539 msgid "Server presented different cert on rehandshake\n" msgstr "Serwer przedstawił inny certyfikat podczas ponownego powitania\n" -#: gnutls.c:2096 openssl.c:1547 +#: gnutls.c:2091 openssl.c:1542 msgid "Server presented identical cert on rehandshake\n" msgstr "Serwer przedstawił identyczny certyfikat podczas ponownego powitania\n" -#: gnutls.c:2102 +#: gnutls.c:2097 msgid "Error initialising X509 cert structure\n" msgstr "Błąd podczas inicjowania struktury certyfikatów X.509\n" -#: gnutls.c:2108 +#: gnutls.c:2103 msgid "Error importing server's cert\n" msgstr "Błąd podczas importowania certyfikatu serwera\n" -#: gnutls.c:2117 main.c:1748 +#: gnutls.c:2112 main.c:1760 msgid "Could not calculate hash of server's certificate\n" msgstr "Nie można obliczyć sumy kontrolnej certyfikatu serwera\n" -#: gnutls.c:2122 +#: gnutls.c:2117 msgid "Error checking server cert status\n" msgstr "Błąd podczas sprawdzania stanu certyfikatu serwera\n" -#: gnutls.c:2127 +#: gnutls.c:2122 msgid "certificate revoked" msgstr "unieważniony certyfikat" -#: gnutls.c:2129 +#: gnutls.c:2124 msgid "signer not found" msgstr "nie odnaleziono podpisującego" -#: gnutls.c:2131 +#: gnutls.c:2126 msgid "signer not a CA certificate" msgstr "podpisujący nie jest certyfikatem CA" -#: gnutls.c:2133 +#: gnutls.c:2128 msgid "insecure algorithm" msgstr "niebezpieczny algorytm" -#: gnutls.c:2135 +#: gnutls.c:2130 msgid "certificate not yet activated" msgstr "certyfikat nie został jeszcze aktywowany" -#: gnutls.c:2137 +#: gnutls.c:2132 msgid "certificate expired" msgstr "certyfikat wygasł" #. If this is set and no other reason, it apparently means #. that signature verification failed. Not entirely sure #. why we don't just set a bit for that too. -#: gnutls.c:2142 +#: gnutls.c:2137 msgid "signature verification failed" msgstr "sprawdzenie poprawności podpisu się nie powiodło" -#: gnutls.c:2191 openssl.c:1428 openssl.c:1580 +#: gnutls.c:2186 openssl.c:1423 openssl.c:1575 msgid "certificate does not match hostname" msgstr "certyfikat nie pasuje do nazwy komputera" -#: gnutls.c:2196 openssl.c:1427 openssl.c:1586 +#: gnutls.c:2191 openssl.c:1422 openssl.c:1581 #, c-format msgid "Server certificate verify failed: %s\n" msgstr "Sprawdzenie poprawności certyfikatu klienta się nie powiodło: %s\n" -#: gnutls.c:2270 +#: gnutls.c:2264 msgid "Failed to allocate memory for cafile certs\n" msgstr "Przydzielenie pamięci dla certyfikatów pliku CA się nie powiodło\n" -#: gnutls.c:2291 +#: gnutls.c:2285 #, c-format msgid "Failed to read certs from cafile: %s\n" msgstr "Odczytanie certyfikatów z pliku CA się nie powiodło: %s\n" -#: gnutls.c:2307 +#: gnutls.c:2301 #, c-format msgid "Failed to open CA file '%s': %s\n" msgstr "Otwarcie pliku CA „%s” się nie powiodło: %s\n" -#: gnutls.c:2320 openssl.c:1710 +#: gnutls.c:2314 openssl.c:1703 msgid "Loading certificate failed. Aborting.\n" msgstr "Wczytanie certyfikatu się nie powiodło. Przerywanie.\n" -#: gnutls.c:2391 +#: gnutls.c:2385 #, c-format msgid "Failed to set TLS priority string (\"%s\"): %s\n" msgstr "Ustawienie ciągu priorytetu TLS się nie powiodło („%s”): %s\n" -#: gnutls.c:2403 openssl.c:1827 +#: gnutls.c:2397 openssl.c:1820 #, c-format msgid "SSL negotiation with %s\n" msgstr "Negocjacja SSL z %s\n" -#: gnutls.c:2450 openssl.c:1853 +#: gnutls.c:2444 openssl.c:1846 msgid "SSL connection cancelled\n" msgstr "Anulowano połączenie SSL\n" -#: gnutls.c:2457 +#: gnutls.c:2451 #, c-format msgid "SSL connection failure: %s\n" msgstr "Niepowodzenie połączenia SSL: %s\n" -#: gnutls.c:2466 +#: gnutls.c:2460 #, c-format msgid "GnuTLS non-fatal return during handshake: %s\n" msgstr "Niekrytyczny zwrot biblioteki GnuTLS podczas powitania: %s\n" -#: gnutls.c:2472 openssl.c:1870 +#: gnutls.c:2466 openssl.c:1863 #, c-format msgid "Connected to HTTPS on %s\n" msgstr "Połączono z HTTPS na %s\n" -#: gnutls.c:2475 +#: gnutls.c:2469 #, c-format msgid "Renegotiated SSL on %s\n" msgstr "Ponownie negocjowano SSL na %s\n" -#: gnutls.c:2795 openssl-pkcs11.c:199 +#: gnutls.c:2789 openssl-pkcs11.c:199 #, c-format msgid "PIN required for %s" msgstr "Wymagany jest kod PIN dla %s" -#: gnutls.c:2799 openssl-pkcs11.c:202 +#: gnutls.c:2793 openssl-pkcs11.c:202 msgid "Wrong PIN" msgstr "Błędny kod PIN" -#: gnutls.c:2802 +#: gnutls.c:2796 msgid "This is the final try before locking!" msgstr "To ostatnia próba przed zablokowaniem." -#: gnutls.c:2804 +#: gnutls.c:2798 msgid "Only a few tries left before locking!" msgstr "Pozostało tylko kilka prób przed zablokowaniem." -#: gnutls.c:2809 openssl-pkcs11.c:206 +#: gnutls.c:2803 openssl-pkcs11.c:206 msgid "Enter PIN:" msgstr "Proszę wprowadzić kod PIN:" -#: gnutls.c:2940 openssl.c:1998 +#: gnutls.c:2934 openssl.c:1991 msgid "Unsupported OATH HMAC algorithm\n" msgstr "Nieobsługiwany algorytm HMAC „OATH”\n" -#: gnutls.c:2949 +#: gnutls.c:2943 #, c-format msgid "Failed to calculate OATH HMAC: %s\n" msgstr "Obliczenie HMAC „OATH” się nie powiodło: %s\n" @@ -1853,7 +1853,7 @@ msgstr "" "Przydzielenie nowej ścieżki dla względnego przekierowania się nie powiodło: " "%s\n" -#: http.c:938 oncp.c:592 oncp.c:629 +#: http.c:938 oncp.c:607 oncp.c:644 #, c-format msgid "Unexpected %d result from server\n" msgstr "Oczekiwano %d wyników z serwera\n" @@ -2007,15 +2007,15 @@ msgstr "Obsługiwane są tylko pośredniki HTTP i SOCKS(5)\n" #: library.c:112 msgid "Cisco AnyConnect or openconnect" -msgstr "" +msgstr "Cisco AnyConnect lub openconnect" #: library.c:113 msgid "Compatible with Cisco AnyConnect SSL VPN, as well as ocserv" -msgstr "" +msgstr "Zgodny z VPN Cisco AnyConnect SSL, a także z ocserv" #: library.c:128 msgid "Juniper Network Connect" -msgstr "" +msgstr "Juniper Network Connect" #: library.c:129 msgid "Compatible with Juniper Network Connect / Pulse Secure SSL VPN" @@ -2030,12 +2030,12 @@ msgstr "Nieznany protokół VPN „%s”\n" msgid "Built against SSL library with no Cisco DTLS support\n" msgstr "Zbudowano z biblioteką SSL bez obsługi DTLS firmy Cisco\n" -#: library.c:613 +#: library.c:611 #, c-format msgid "Failed to parse server URL '%s'\n" msgstr "Przetworzenie adresu URL serwera „%s” się nie powiodło\n" -#: library.c:619 +#: library.c:617 msgid "Only https:// permitted for server URL\n" msgstr "W adresach URL serwera dozwolone jest tylko „https://”\n" @@ -2044,14 +2044,14 @@ msgstr "W adresach URL serwera dozwolone jest tylko „https://”\n" msgid "Unknown certificate hash: %s.\n" msgstr "Nieznana suma kontrolna certyfikatu: %s.\n" -#: library.c:1017 +#: library.c:1014 #, c-format msgid "" "The size of the provided fingerprint is less than the minimum required " "(%u).\n" msgstr "Rozmiar podanego odcisku jest mniejszy niż minimalnie wymagany (%u).\n" -#: library.c:1076 +#: library.c:1075 msgid "No form handler; cannot authenticate.\n" msgstr "Brak programu obsługującego formularze. Nie można uwierzytelnić.\n" @@ -2114,15 +2114,17 @@ msgid "" "WARNING: This binary lacks DTLS and/or ESP support. Performance will be " "impaired.\n" msgstr "" +"OSTRZEŻENIE: ten plik binarny nie obsługuje DTLS lub ESP. Wydajność będzie " +"zmniejszona.\n" #: main.c:652 #, c-format msgid "Supported protocols:" -msgstr "" +msgstr "Obsługiwane protokoły:" #: main.c:654 main.c:670 msgid " (default)" -msgstr "" +msgstr " (domyślny)" #: main.c:667 #, c-format @@ -2130,6 +2132,8 @@ msgid "" "\n" " Set VPN protocol:\n" msgstr "" +"\n" +" Ustawienie protokołu VPN:\n" #: main.c:708 msgid "fgets (stdin)" @@ -2161,6 +2165,8 @@ msgid "" "Open client for multiple VPN protocols, version %s\n" "\n" msgstr "" +"Otwarty klient dla wielu protokołów VPN, wersja %s\n" +"\n" #: main.c:790 msgid "Read options from config file" @@ -2436,37 +2442,42 @@ msgstr "Ustawia lokalny port dla datagramów DTLS" msgid "Failed to allocate string\n" msgstr "Przydzielenie ciągu się nie powiodło\n" -#: main.c:953 +#: main.c:963 #, c-format msgid "Failed to get line from config file: %s\n" msgstr "Uzyskanie wiersza z pliku konfiguracji się nie powiodło: %s\n" -#: main.c:993 +#: main.c:1003 #, c-format msgid "Unrecognised option at line %d: '%s'\n" msgstr "Nierozpoznana opcja w wierszu %d: „%s”\n" -#: main.c:1003 +#: main.c:1013 #, c-format msgid "Option '%s' does not take an argument at line %d\n" msgstr "Opcja „%s” nie przyjmuje parametru w wierszu %d\n" -#: main.c:1007 +#: main.c:1017 #, c-format msgid "Option '%s' requires an argument at line %d\n" msgstr "Opcja „%s” wymaga parametru w wierszu %d\n" -#: main.c:1032 +#: main.c:1042 #, c-format msgid "Invalid user \"%s\": %s\n" msgstr "Nieprawidłowy użytkownik „%s”: %s\n" -#: main.c:1042 +#: main.c:1052 #, c-format msgid "Invalid user ID \"%d\": %s\n" msgstr "Nieprawidłowy identyfikator użytkownika „%d”: %s\n" -#: main.c:1094 +#: main.c:1096 +#, c-format +msgid "WARNING: Cannot set locale: %s\n" +msgstr "" + +#: main.c:1106 #, c-format msgid "" "WARNING: This version of openconnect was built without iconv\n" @@ -2477,7 +2488,7 @@ msgstr "" " a używany jest przestarzały zestaw znaków „%s”.\n" " Program może się dziwnie zachowywać.\n" -#: main.c:1101 +#: main.c:1113 #, c-format msgid "" "WARNING: This version of openconnect is %s but\n" @@ -2486,42 +2497,42 @@ msgstr "" "OSTRZEŻENIE: ta wersja OpenConnect to %s, ale\n" " biblioteki libopenconnect to %s\n" -#: main.c:1111 +#: main.c:1123 #, c-format msgid "Failed to allocate vpninfo structure\n" msgstr "Przydzielenie struktury vpninfo się nie powiodło\n" -#: main.c:1166 +#: main.c:1178 #, c-format msgid "Cannot use 'config' option inside config file\n" msgstr "Nie można użyć opcji „config” wewnątrz pliku konfiguracji\n" -#: main.c:1174 +#: main.c:1186 #, c-format msgid "Cannot open config file '%s': %s\n" msgstr "Nie można otworzyć pliku konfiguracji „%s”: %s\n" -#: main.c:1190 +#: main.c:1202 #, c-format msgid "Invalid compression mode '%s'\n" msgstr "Nieprawidłowy tryb kompresji \"%s\"\n" -#: main.c:1211 +#: main.c:1223 #, c-format msgid "Missing colon in resolve option\n" msgstr "Brak dwukropka w opcji „resolve”\n" -#: main.c:1216 +#: main.c:1228 #, c-format msgid "Failed to allocate memory\n" msgstr "Przydzielenie pamięci się nie powiodło\n" -#: main.c:1296 main.c:1305 +#: main.c:1308 main.c:1317 #, c-format msgid "MTU %d too small\n" msgstr "MTU %d jest za małe\n" -#: main.c:1335 +#: main.c:1347 #, c-format msgid "" "Disabling all HTTP connection re-use due to --no-http-keepalive option.\n" @@ -2532,7 +2543,7 @@ msgstr "" "Jeśli to pomoże, to prosimy to zgłosić na adres (w języku angielskim).\n" -#: main.c:1341 +#: main.c:1353 #, c-format msgid "" "The --no-cert-check option was insecure and has been removed.\n" @@ -2542,129 +2553,129 @@ msgstr "" "Należy naprawić certyfikat serwera lub użyć opcji --servercert, aby mu " "zaufać.\n" -#: main.c:1358 +#: main.c:1370 #, c-format msgid "Queue length zero not permitted; using 1\n" msgstr "Zerowa długość kolejki jest niedozwolona. Używanie 1\n" -#: main.c:1372 +#: main.c:1384 #, c-format msgid "OpenConnect version %s\n" msgstr "OpenConnect wersja %s\n" -#: main.c:1406 +#: main.c:1418 #, c-format msgid "Invalid software token mode \"%s\"\n" msgstr "Nieprawidłowy tryb tokena programowego „%s”\n" -#: main.c:1416 +#: main.c:1428 #, c-format msgid "Invalid OS identity \"%s\"\n" msgstr "Nieprawidłowa tożsamość systemu operacyjnego „%s”\n" -#: main.c:1449 +#: main.c:1461 #, c-format msgid "Too many arguments on command line\n" msgstr "Za dużo parametrów w wierszu poleceń\n" -#: main.c:1452 +#: main.c:1464 #, c-format msgid "No server specified\n" msgstr "Nie podano serwera\n" -#: main.c:1468 +#: main.c:1480 #, c-format msgid "This version of openconnect was built without libproxy support\n" msgstr "" "Ta wersja OpenConnect została zbudowana bez obsługi biblioteki libproxy\n" -#: main.c:1497 +#: main.c:1509 #, c-format msgid "Error opening cmd pipe\n" msgstr "Błąd podczas otwierania potoku cmd\n" -#: main.c:1530 +#: main.c:1542 #, c-format msgid "Failed to obtain WebVPN cookie\n" msgstr "Uzyskanie ciasteczka WebVPN się nie powiodło\n" -#: main.c:1551 +#: main.c:1563 #, c-format msgid "Creating SSL connection failed\n" msgstr "Utworzenie połączenia SSL się nie powiodło\n" -#: main.c:1567 +#: main.c:1579 #, c-format msgid "Set up DTLS failed; using SSL instead\n" msgstr "Ustawienie DTLS się nie powiodło. Używanie SSL zamiast tego\n" -#: main.c:1588 +#: main.c:1600 #, c-format msgid "Connected as %s%s%s, using %s%s\n" msgstr "Połączono jako %s%s%s, używając %s%s\n" -#: main.c:1597 +#: main.c:1609 msgid "No --script argument provided; DNS and routing are not configured\n" msgstr "" "Nie podano parametru --script. DNS i trasowanie nie są skonfigurowane\n" -#: main.c:1599 +#: main.c:1611 msgid "See http://www.infradead.org/openconnect/vpnc-script.html\n" msgstr "" "Proszę zobaczyć http://www.infradead.org/openconnect/vpnc-script.html\n" -#: main.c:1612 +#: main.c:1624 #, c-format msgid "Failed to open '%s' for write: %s\n" msgstr "Otwarcie „%s” do zapisu się nie powiodło: %s\n" -#: main.c:1624 +#: main.c:1636 #, c-format msgid "Continuing in background; pid %d\n" msgstr "Kontynuowanie w tle. PID %d\n" -#: main.c:1641 +#: main.c:1653 msgid "User requested reconnect\n" msgstr "Użytkownik zażądał ponownego połączenia\n" -#: main.c:1649 +#: main.c:1661 msgid "Cookie was rejected on reconnection; exiting.\n" msgstr "" "Ciasteczko zostało odrzucone podczas łączenia ponownie. Kończenie " "działania.\n" -#: main.c:1653 +#: main.c:1665 msgid "Session terminated by server; exiting.\n" msgstr "Sesja została zakończona przez serwer. Kończenie działania.\n" -#: main.c:1657 +#: main.c:1669 msgid "User cancelled (SIGINT); exiting.\n" msgstr "Anulowane przez użytkownika (SIGINT). Kończenie działania.\n" -#: main.c:1661 +#: main.c:1673 msgid "User detached from session (SIGHUP); exiting.\n" msgstr "Użytkownik odłączył od sesji (SIGHUP). Kończenie działania.\n" -#: main.c:1665 +#: main.c:1677 msgid "Unknown error; exiting.\n" msgstr "Nieznany błąd; kończenie działania.\n" -#: main.c:1684 +#: main.c:1696 #, c-format msgid "Failed to open %s for write: %s\n" msgstr "Otwarcie %s do zapisu się nie powiodło: %s\n" -#: main.c:1692 +#: main.c:1704 #, c-format msgid "Failed to write config to %s: %s\n" msgstr "Zapisanie konfiguracji do %s się nie powiodło: %s\n" -#: main.c:1751 +#: main.c:1763 #, c-format msgid "Server SSL certificate didn't match: %s\n" msgstr "Certyfikat SSL serwera się nie zgadza: %s\n" -#: main.c:1770 +#: main.c:1782 #, c-format msgid "" "\n" @@ -2675,103 +2686,103 @@ msgstr "" "Sprawdzenie poprawność certyfikatu z serwera VPN „%s” się nie powiodło.\n" "Przyczyna: %s\n" -#: main.c:1773 +#: main.c:1785 #, c-format msgid "" "To trust this server in future, perhaps add this to your command line:\n" msgstr "" "Aby zaufać temu serwerowi w przyszłości, można dodać to do wiersza poleceń:\n" -#: main.c:1774 +#: main.c:1786 #, c-format msgid " --servercert %s\n" msgstr " --servercert %s\n" -#: main.c:1779 +#: main.c:1791 #, c-format msgid "Enter '%s' to accept, '%s' to abort; anything else to view: " msgstr "" "Wpisanie \"%s\" zaakceptuje, \"%s\" przerwie, inne wartości spowodują " "wyświetlenie: " -#: main.c:1780 main.c:1798 +#: main.c:1792 main.c:1810 msgid "no" msgstr "nie" -#: main.c:1780 main.c:1786 +#: main.c:1792 main.c:1798 msgid "yes" msgstr "tak" -#: main.c:1807 +#: main.c:1819 #, c-format msgid "Server key hash: %s\n" msgstr "Suma kontrolna klucza serwera: %s\n" -#: main.c:1841 +#: main.c:1853 #, c-format msgid "Auth choice \"%s\" matches multiple options\n" msgstr "Wybór uwierzytelnienia „%s” pasuje do wielu opcji\n" -#: main.c:1844 +#: main.c:1856 #, c-format msgid "Auth choice \"%s\" not available\n" msgstr "Wybór uwierzytelnienia „%s” jest niedostępny\n" -#: main.c:1865 +#: main.c:1877 msgid "User input required in non-interactive mode\n" msgstr "Wymagane jest działanie użytkownika w trybie nieinteraktywnym\n" -#: main.c:2042 +#: main.c:2054 #, c-format msgid "Failed to open token file for write: %s\n" msgstr "Otwarcie pliku token do zapisu się nie powiodło: %s\n" -#: main.c:2050 +#: main.c:2062 #, c-format msgid "Failed to write token: %s\n" msgstr "Zapisanie tokenu się nie powiodło: %s\n" -#: main.c:2096 main.c:2117 +#: main.c:2108 main.c:2129 #, c-format msgid "Soft token string is invalid\n" msgstr "Ciąg tokena programowego jest nieprawidłowy\n" -#: main.c:2099 +#: main.c:2111 #, c-format msgid "Can't open ~/.stokenrc file\n" msgstr "Nie można otworzyć pliku ~/.stokenrc\n" -#: main.c:2102 +#: main.c:2114 #, c-format msgid "OpenConnect was not built with libstoken support\n" msgstr "OpenConnect zostało zbudowane bez obsługi biblioteki libstoken\n" -#: main.c:2105 +#: main.c:2117 #, c-format msgid "General failure in libstoken\n" msgstr "Ogólne niepowodzenie w bibliotece libstoken\n" -#: main.c:2120 +#: main.c:2132 #, c-format msgid "OpenConnect was not built with liboath support\n" msgstr "OpenConnect zostało zbudowane bez obsługi biblioteki liboath\n" -#: main.c:2123 +#: main.c:2135 #, c-format msgid "General failure in liboath\n" msgstr "Ogólne niepowodzenie w bibliotece liboath\n" -#: main.c:2134 +#: main.c:2146 #, c-format msgid "Yubikey token not found\n" msgstr "Nie odnaleziono tokena Yubikey\n" -#: main.c:2137 +#: main.c:2149 #, c-format msgid "OpenConnect was not built with Yubikey support\n" msgstr "OpenConnect zostało zbudowane bez obsługi Yubikey\n" -#: main.c:2140 +#: main.c:2152 #, c-format msgid "General Yubikey failure: %s\n" msgstr "Ogólne niepowodzenie Yubikey: %s\n" @@ -2922,216 +2933,216 @@ msgstr "Otrzymano trasę wykluczania „split” %s\n" msgid "Received WINS server %s\n" msgstr "Otrzymano serwer WINS %s\n" -#: oncp.c:311 +#: oncp.c:313 #, c-format msgid "ESP encryption: 0x%02x (%s)\n" msgstr "Szyfrowanie ESP: 0x%02x (%s)\n" -#: oncp.c:328 +#: oncp.c:332 #, c-format msgid "ESP HMAC: 0x%02x (%s)\n" msgstr "HMAC ESP: 0x%02x (%s)\n" -#: oncp.c:338 +#: oncp.c:342 #, c-format msgid "ESP compression: %d\n" msgstr "Kompresja ESP: %d\n" -#: oncp.c:346 +#: oncp.c:350 #, c-format msgid "ESP port: %d\n" msgstr "Port ESP: %d\n" -#: oncp.c:353 +#: oncp.c:357 #, c-format msgid "ESP key lifetime: %u bytes\n" msgstr "Czas życia klucza ESP: %u bajtów\n" -#: oncp.c:361 +#: oncp.c:365 #, c-format msgid "ESP key lifetime: %u seconds\n" msgstr "Czas życia klucza ESP: %u sekund\n" -#: oncp.c:369 +#: oncp.c:373 #, c-format msgid "ESP to SSL fallback: %u seconds\n" msgstr "Przechodzenie z ESP do SSL: %u sekund\n" -#: oncp.c:377 +#: oncp.c:381 #, c-format msgid "ESP replay protection: %d\n" msgstr "Ochrona powtarzania ESP: %d\n" -#: oncp.c:385 +#: oncp.c:389 #, c-format msgid "ESP SPI (outbound): %x\n" msgstr "„SPI” ESP (wychodzące): %x\n" -#: oncp.c:393 +#: oncp.c:398 #, c-format msgid "%d bytes of ESP secrets\n" msgstr "%d bajtów haseł ESP\n" -#: oncp.c:405 +#: oncp.c:410 #, c-format msgid "Unknown TLV group %d attr %d len %d:%s\n" msgstr "Nieznana grupa TLV %d attr %d len %d:%s\n" -#: oncp.c:483 +#: oncp.c:488 msgid "Failed to parse KMP header\n" msgstr "Przetworzenie nagłówka KMP się nie powiodło\n" -#: oncp.c:499 +#: oncp.c:505 msgid "Failed to parse KMP message\n" msgstr "Przetworzenie komunikatu KMP się nie powiodło\n" -#: oncp.c:505 +#: oncp.c:511 #, c-format msgid "Got KMP message %d of size %d\n" msgstr "Otrzymano komunikat KMP %d o rozmiarze %d\n" -#: oncp.c:521 +#: oncp.c:527 #, c-format msgid "Received non-ESP TLVs (group %d) in ESP negotiation KMP\n" msgstr "Otrzymano TLV niebędące ESP (grupa %d) w KMP negocjacji ESP\n" -#: oncp.c:570 oncp.c:615 oncp.c:647 oncp.c:773 +#: oncp.c:585 oncp.c:630 oncp.c:662 oncp.c:788 msgid "Error creating oNCP negotiation request\n" msgstr "Błąd podczas tworzenia żądania negocjacji oNCP\n" -#: oncp.c:656 oncp.c:807 +#: oncp.c:671 oncp.c:823 msgid "Short write in oNCP negotiation\n" msgstr "Krótki zapis w negocjacji oNCP\n" -#: oncp.c:668 oncp.c:692 +#: oncp.c:683 oncp.c:707 #, c-format msgid "Read %d bytes of SSL record\n" msgstr "Odczyt %d bajtów wpisu SSL\n" -#: oncp.c:672 +#: oncp.c:687 #, c-format msgid "Unexpected response of size %d after hostname packet\n" msgstr "Oczekiwano odpowiedzi o rozmiarze %d po pakiecie nazwy komputera\n" -#: oncp.c:679 +#: oncp.c:694 #, c-format msgid "Server response to hostname packet is error 0x%02x\n" msgstr "Odpowiedź serwera na pakiet nazwy komputera to błąd 0x%02x\n" -#: oncp.c:696 +#: oncp.c:711 msgid "Invalid packet waiting for KMP 301\n" msgstr "Nieprawidłowy pakiet oczekujący na KMP 301\n" -#: oncp.c:709 +#: oncp.c:724 #, c-format msgid "Expected KMP message 301 from server but got %d\n" msgstr "Oczekiwano komunikatu „301” KMP z serwera, ale otrzymano %d\n" -#: oncp.c:718 +#: oncp.c:733 #, c-format msgid "KMP message 301 from server too large (%d bytes)\n" msgstr "Komunikat „301” KMP z serwera jest za duży (%d bajtów)\n" -#: oncp.c:724 +#: oncp.c:739 #, c-format msgid "Got KMP message 301 of length %d\n" msgstr "Otrzymano komunikat „301” KMP o długości %d\n" -#: oncp.c:731 +#: oncp.c:746 msgid "Failed to read continuation record length\n" msgstr "Odczytanie długości wpisu kontynuacji się nie powiodło\n" -#: oncp.c:737 +#: oncp.c:752 #, c-format msgid "Record of additional %d bytes too large; would make %d\n" msgstr "Wpis jest za dużo o dodatkowe %d bajtów. Byłby %d\n" -#: oncp.c:746 +#: oncp.c:761 #, c-format msgid "Failed to read continuation record of length %d\n" msgstr "Odczytanie wpisu kontynuacji o długości %d się nie powiodło\n" -#: oncp.c:752 +#: oncp.c:767 #, c-format msgid "Read additional %d bytes of KMP 301 message\n" msgstr "Odczyt dodatkowych %d bajtów komunikatu „301” KMP\n" -#: oncp.c:792 +#: oncp.c:808 msgid "Error negotiating ESP keys\n" msgstr "Błąd podczas negocjowania kluczy ESP\n" -#: oncp.c:852 +#: oncp.c:869 msgid "new incoming" msgstr "nowe przychodzące" -#: oncp.c:853 +#: oncp.c:870 msgid "new outgoing" msgstr "nowe wychodzące" -#: oncp.c:858 +#: oncp.c:875 msgid "Ignoring ESP keys since ESP support not available in this build\n" msgstr "Ignorowanie kluczy ESP, ponieważ obsługa ESP jest niedostępna\n" -#: oncp.c:878 +#: oncp.c:895 msgid "Read only 1 byte of oNCP length field\n" msgstr "Odczyt tylko 1 bajtu pola długości oNCP\n" -#: oncp.c:887 +#: oncp.c:904 msgid "Server terminated connection (session expired)\n" msgstr "Serwer zakończył połączenie (sesja wygasła)\n" -#: oncp.c:891 +#: oncp.c:908 #, c-format msgid "Server terminated connection (reason: %d)\n" msgstr "Serwer zakończył połączenie (przyczyna: %d)\n" -#: oncp.c:897 +#: oncp.c:914 msgid "Server sent zero-length oNCP record\n" msgstr "Serwer wysłał wpis oNCP o zerowej długości\n" -#: oncp.c:992 +#: oncp.c:1009 #, c-format msgid "Incoming KMP message %d of size %d (got %d)\n" msgstr "Przychodzący komunikat KMP %d o rozmiarze %d (otrzymano %d)\n" -#: oncp.c:995 +#: oncp.c:1012 #, c-format msgid "Continuing to process KMP message %d now size %d (got %d)\n" msgstr "" "Kontynuowanie przetwarzania komunikatu KMP %d o obecnym rozmiarze %d " "(otrzymano %d)\n" -#: oncp.c:1014 +#: oncp.c:1031 msgid "Unrecognised data packet\n" msgstr "Nierozpoznany pakiet danych\n" -#: oncp.c:1076 +#: oncp.c:1093 #, c-format msgid "Unknown KMP message %d of size %d:\n" msgstr "Nieznany komunikat KMP %d o rozmiarze %d:\n" -#: oncp.c:1081 +#: oncp.c:1098 #, c-format msgid ".... + %d more bytes unreceived\n" msgstr "… + %d więcej bajtów nie zostało otrzymanych\n" -#: oncp.c:1096 +#: oncp.c:1113 msgid "Packet outgoing:\n" msgstr "Wychodzący pakiet:\n" -#: oncp.c:1160 +#: oncp.c:1177 msgid "Sent ESP enable control packet\n" msgstr "Wysłano pakiet kontroli włączenia ESP\n" -#: oncp.c:1282 +#: oncp.c:1299 msgid "Logout failed.\n" -msgstr "" +msgstr "Wylogowanie się nie powiodło.\n" -#: oncp.c:1284 +#: oncp.c:1301 msgid "Logout successful.\n" -msgstr "" +msgstr "Pomyślnie wylogowano.\n" -#: openconnect-internal.h:1071 openconnect-internal.h:1079 +#: openconnect-internal.h:1084 openconnect-internal.h:1092 #, c-format msgid "ERROR: %s() called with invalid UTF-8 for '%s' argument\n" msgstr "" @@ -3234,28 +3245,28 @@ msgstr "Zainicjowanie szyfru ESP się nie powiodło:\n" msgid "Failed to initialize ESP HMAC\n" msgstr "Zainicjowanie „HMAC” ESP się nie powiodło\n" -#: openssl-esp.c:156 +#: openssl-esp.c:157 msgid "Failed to generate random keys for ESP:\n" msgstr "Utworzenie losowych kluczy dla ESP się nie powiodło:\n" -#: openssl-esp.c:206 +#: openssl-esp.c:207 msgid "Failed to set up decryption context for ESP packet:\n" msgstr "" "Ustawienie kontekstu odszyfrowywania dla pakietu ESP się nie powiodło:\n" -#: openssl-esp.c:214 +#: openssl-esp.c:215 msgid "Failed to decrypt ESP packet:\n" msgstr "Odszyfrowanie pakietu ESP się nie powiodło:\n" -#: openssl-esp.c:234 +#: openssl-esp.c:235 msgid "Failed to generate random IV for ESP packet:\n" msgstr "Utworzenie losowego „IV” dla pakietu ESP się nie powiodło:\n" -#: openssl-esp.c:248 +#: openssl-esp.c:249 msgid "Failed to set up encryption context for ESP packet:\n" msgstr "Ustawienie kontekstu szyfrowania dla pakietu ESP się nie powiodło:\n" -#: openssl-esp.c:257 +#: openssl-esp.c:258 msgid "Failed to encrypt ESP packet:\n" msgstr "Zaszyfrowanie pakietu ESP się nie powiodło:\n" @@ -3509,105 +3520,105 @@ msgstr "Konwertowanie PKCS#8 na EVP_PKEY biblioteki OpenSSL się nie powiodło\n msgid "Failed to identify private key type in '%s'\n" msgstr "Zidentyfikowanie typu klucza prywatnego w „%s” się nie powiodło\n" -#: openssl.c:1232 +#: openssl.c:1227 #, c-format msgid "Matched DNS altname '%s'\n" msgstr "Pasujące „altname” DNS „%s”\n" -#: openssl.c:1239 +#: openssl.c:1234 #, c-format msgid "No match for altname '%s'\n" msgstr "Nic nie pasuje do „altname” „%s”\n" -#: openssl.c:1253 +#: openssl.c:1248 #, c-format msgid "Certificate has GEN_IPADD altname with bogus length %d\n" msgstr "Certyfikat posiada „altname” GEN_IPADD z fałszywą długością %d\n" -#: openssl.c:1264 openssl.c:1410 +#: openssl.c:1259 openssl.c:1405 #, c-format msgid "Matched %s address '%s'\n" msgstr "Pasujący adres %s „%s”\n" -#: openssl.c:1271 +#: openssl.c:1266 #, c-format msgid "No match for %s address '%s'\n" msgstr "Nic nie pasuje do adresu %s „%s”\n" -#: openssl.c:1313 +#: openssl.c:1308 #, c-format msgid "URI '%s' has non-empty path; ignoring\n" msgstr "Adres URI „%s” posiada niepustą ścieżkę. Ignorowanie\n" -#: openssl.c:1318 +#: openssl.c:1313 #, c-format msgid "Matched URI '%s'\n" msgstr "Pasujący adres URI „%s”\n" -#: openssl.c:1329 +#: openssl.c:1324 #, c-format msgid "No match for URI '%s'\n" msgstr "Nic nie pasuje do adresu URI „%s”\n" -#: openssl.c:1344 +#: openssl.c:1339 #, c-format msgid "No altname in peer cert matched '%s'\n" msgstr "Żadne „altname” w certyfikacie partnera nie pasuje do „%s”\n" -#: openssl.c:1352 +#: openssl.c:1347 msgid "No subject name in peer cert!\n" msgstr "Brak nazwy tematu w certyfikacie partnera\n" -#: openssl.c:1372 +#: openssl.c:1367 msgid "Failed to parse subject name in peer cert\n" msgstr "Przetworzenie nazwy tematu w certyfikacie partnera się nie powiodło\n" -#: openssl.c:1379 +#: openssl.c:1374 #, c-format msgid "Peer cert subject mismatch ('%s' != '%s')\n" msgstr "Niedopasowanie tematu certyfikatu partnera („%s” != „%s”)\n" -#: openssl.c:1384 openssl.c:1418 +#: openssl.c:1379 openssl.c:1413 #, c-format msgid "Matched peer certificate subject name '%s'\n" msgstr "Pasująca nazwa tematu certyfikatu partnera „%s”\n" -#: openssl.c:1480 +#: openssl.c:1475 #, c-format msgid "Extra cert from cafile: '%s'\n" msgstr "Dodatkowy certyfikat z pliku CA: „%s”\n" -#: openssl.c:1618 +#: openssl.c:1613 msgid "Error in client cert notAfter field\n" msgstr "Błąd w polu „notAfter” certyfikatu klienta\n" -#: openssl.c:1631 +#: openssl.c:1626 msgid "" msgstr "" -#: openssl.c:1686 +#: openssl.c:1679 msgid "Create TLSv1 CTX failed\n" msgstr "Utworzenie „CTX” TLSv1 się nie powiodło\n" -#: openssl.c:1705 +#: openssl.c:1698 msgid "SSL certificate and key do not match\n" msgstr "Certyfikat SSL i klucz nie pasują\n" -#: openssl.c:1750 +#: openssl.c:1743 #, c-format msgid "Failed to read certs from CA file '%s'\n" msgstr "Odczytanie certyfikatów z pliku CA „%s” się nie powiodło\n" -#: openssl.c:1783 +#: openssl.c:1776 #, c-format msgid "Failed to open CA file '%s'\n" msgstr "Otwarcie pliku CA „%s” się nie powiodło\n" -#: openssl.c:1843 +#: openssl.c:1836 msgid "SSL connection failure\n" msgstr "Niepowodzenie połączenia SSL\n" -#: openssl.c:2004 +#: openssl.c:1997 msgid "Failed to calculate OATH HMAC\n" msgstr "Obliczenie „HMAC” OATH się nie powiodło\n" @@ -4345,16 +4356,16 @@ msgid "Unrecognised response from Yubikey when generating tokencode\n" msgstr "Nierozpoznana odpowiedź z Yubikey podczas tworzenia kodu tokena\n" #~ msgid "" -#~ "Open client for Cisco AnyConnect VPN, version %s\n" #~ "\n" +#~ "WARNING: No DTLS support in this binary. Performance will be impaired.\n" #~ msgstr "" -#~ "Otwarty klient dla Cisco AnyConnect VPN, wersja %s\n" #~ "\n" +#~ "OSTRZEŻENIE: ten plik binarny nie obsługuje DTLS. Wydajność będzie " +#~ "zmniejszona.\n" #~ msgid "" +#~ "Open client for Cisco AnyConnect VPN, version %s\n" #~ "\n" -#~ "WARNING: No DTLS support in this binary. Performance will be impaired.\n" #~ msgstr "" +#~ "Otwarty klient dla Cisco AnyConnect VPN, wersja %s\n" #~ "\n" -#~ "OSTRZEŻENIE: ten plik binarny nie obsługuje DTLS. Wydajność będzie " -#~ "zmniejszona.\n" diff --git a/script.c b/script.c index 5f140296..43fd7f32 100644 --- a/script.c +++ b/script.c @@ -81,7 +81,7 @@ static int netmasklen(struct in_addr addr) static uint32_t netmaskbits(int masklen) { - return htonl((0xffffffff << (32-masklen))); + return htonl(masklen>0 ? (0xffffffff << (32-masklen)) : 0); } static int process_split_xxclude(struct openconnect_info *vpninfo, diff --git a/get-globalprotect-config.py b/test-globalprotect-login.py similarity index 55% rename from get-globalprotect-config.py rename to test-globalprotect-login.py index 6eed9fae..ee00a62d 100755 --- a/get-globalprotect-config.py +++ b/test-globalprotect-login.py @@ -1,6 +1,7 @@ #!/usr/bin/python from __future__ import print_function +from urlparse import urlparse import requests import argparse import getpass @@ -9,15 +10,20 @@ p = argparse.ArgumentParser() p.add_argument('-v','--verbose', default=0, action='count') -p.add_argument('portal', help='Hostname of GlobalProtect portal') +p.add_argument('endpoint', help='GlobalProtect server; can append /ssl-vpn/login.esp (default) or /global-protect/getconfig.esp') g = p.add_argument_group('Login credentials') -g.add_argument('--user', help='Username (will prompt if unspecified)') -g.add_argument('--password', help='Password (will prompt if unspecified)') -g.add_argument('--cert', help='PEM file containing client certificate (and optionally private key)') +g.add_argument('-u','--user', help='Username (will prompt if unspecified)') +g.add_argument('-p','--password', help='Password (will prompt if unspecified)') +g.add_argument('-c','--cert', help='PEM file containing client certificate (and optionally private key)') g.add_argument('--key', help='PEM file containing client private key (if not included in same file as certificate)') g.add_argument('--no-verify', dest='verify', action='store_false', default=True, help='Ignore invalid server certificate') args = p.parse_args() +endpoint = urlparse(('https://' if '//' not in args.endpoint else '') + args.endpoint, 'https:') +if not endpoint.path: + print("Endpoint path unspecified: defaulting to /ssl-vpn/login.esp", file=stderr) + endpoint = endpoint._replace(path = '/ssl-vpn/login.esp') + if args.cert and args.key: cert = (args.cert, args.key) elif args.cert: @@ -37,13 +43,15 @@ s.cert = cert # same request params work for /global-protect/getconfig.esp as for /ssl-vpn/login.esp -login = 'https://{}/global-protect/getconfig.esp'.format(args.portal) -res = s.post(login, verify=args.verify, data=dict(user=args.user, passwd=args.password, +res = s.post(endpoint.geturl(), verify=args.verify, data=dict(user=args.user, passwd=args.password, # required jnlpReady='jnlpReady', ok='Login', direct='yes', # optional but might affect behavior - clientVer=4100, server=args.portal, prot='https:', - computer='localhost')) + clientVer=4100, server=endpoint.netloc, prot='https:', + computer=os.uname()[1])) + +if args.verbose: + print("Request body:\n", res.request.content, file=stderr) res.raise_for_status() print(res.text) diff --git a/tests/Makefile.am b/tests/Makefile.am index 88261546..213834fb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -37,7 +37,7 @@ USER_CERTS = $(certsdir)/user-cert.pem $(certsdir)/dsa-cert.pem $(certsdir)/ec-c EXTRA_DIST = certs/ca.pem certs/ca-key.pem certs/user-cert.pem $(USER_KEYS) $(USER_CERTS) \ certs/server-cert.pem certs/server-key.pem configs/test1.passwd \ common.sh configs/test-user-cert.config configs/test-user-pass.config \ - configs/user-cert.prm softhsm2.conf.in softhsm .config/pkcs11/modules/softhsm2.module + configs/user-cert.prm softhsm2.conf.in softhsm dist_check_SCRIPTS = diff --git a/tests/auth-nonascii b/tests/auth-nonascii index d930b117..1da8de25 100755 --- a/tests/auth-nonascii +++ b/tests/auth-nonascii @@ -34,7 +34,7 @@ set -x for CHARSET in UTF-8 ISO8859-2; do echo -n "Connecting to obtain cookie (with password charset ${CHARSET})... " CERTARGS="-c ${KEY} --key-password $(cat ${srcdir}/pass-${CHARSET})" - ( echo "test" | LC_CTYPE=cs_CZ.${CHARSET} LD_PRELOAD=libsocket_wrapper.so $OPENCONNECT -q $ADDRESS:443 -u test $CERTARGS --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --cookieonly --passwd-on-stdin ) || + ( echo "test" | LC_ALL=cs_CZ.${CHARSET} LD_PRELOAD=libsocket_wrapper.so $OPENCONNECT -q $ADDRESS:443 -u test $CERTARGS --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --cookieonly --passwd-on-stdin ) || fail $PID "Could not connect with charset ${CHARSET}!" done diff --git a/win32-ipicmp.h b/win32-ipicmp.h new file mode 100644 index 00000000..ac7942c4 --- /dev/null +++ b/win32-ipicmp.h @@ -0,0 +1,117 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Author: Daniel Lenski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1, 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 + * Lesser General Public License for more details. + */ + +#ifndef __OPENCONNECT_WIN32_IPICMP_H__ +#define __OPENCONNECT_WIN32_IPICMP_H__ + +/* IPv4 header and flags used in esp.c */ + +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ + +struct ip { + u_char ip_hl:4; /* header length */ + u_char ip_v:4; /* version */ + u_char ip_tos; /* type of service */ + short ip_len; /* total length */ + u_short ip_id; /* identification */ + short ip_off; /* fragment offset field */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +}; + +/* ICMP header and flags used in esp.c */ + +#define ICMP_MINLEN 8 /* abs minimum */ +#define ICMP_ECHO 8 /* Echo Request */ + +#define icmp_pptr icmp_hun.ih_pptr +#define icmp_gwaddr icmp_hun.ih_gwaddr +#define icmp_id icmp_hun.ih_idseq.icd_id +#define icmp_seq icmp_hun.ih_idseq.icd_seq +#define icmp_void icmp_hun.ih_void +#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void +#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu +#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs +#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa +#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime + +#define icmp_otime icmp_dun.id_ts.its_otime +#define icmp_rtime icmp_dun.id_ts.its_rtime +#define icmp_ttime icmp_dun.id_ts.its_ttime +#define icmp_ip icmp_dun.id_ip.idi_ip +#define icmp_radv icmp_dun.id_radv +#define icmp_mask icmp_dun.id_mask +#define icmp_data icmp_dun.id_data + +struct icmp_ra_addr +{ + uint32_t ira_addr; + uint32_t ira_preference; +}; + +struct icmp +{ + uint8_t icmp_type; /* type of message, see below */ + uint8_t icmp_code; /* type sub code */ + uint16_t icmp_cksum; /* ones complement checksum of struct */ + union + { + u_char ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* gateway address */ + struct ih_idseq /* echo datagram */ + { + uint16_t icd_id; + uint16_t icd_seq; + } ih_idseq; + uint32_t ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu + { + uint16_t ipm_void; + uint16_t ipm_nextmtu; + } ih_pmtu; + + struct ih_rtradv + { + uint8_t irt_num_addrs; + uint8_t irt_wpa; + uint16_t irt_lifetime; + } ih_rtradv; + } icmp_hun; + + union + { + struct + { + uint32_t its_otime; + uint32_t its_rtime; + uint32_t its_ttime; + } id_ts; + struct + { + struct ip idi_ip; + /* options and then 64 bits of data */ + } id_ip; + struct icmp_ra_addr id_radv; + uint32_t id_mask; + uint8_t id_data[1]; + } icmp_dun; +}; + +#endif /* __OPENCONNECT_WIN32_IPICMP_H__ */ diff --git a/www/Makefile.am b/www/Makefile.am index f791a004..225a20ac 100644 --- a/www/Makefile.am +++ b/www/Makefile.am @@ -3,8 +3,8 @@ SUBDIRS = styles inc images CONV = "$(srcdir)/html.py" -FTR_PAGES = csd.html charset.html token.html pkcs11.html tpm.html features.html gui.html nonroot.html -START_PAGES = building.html connecting.html manual.html vpnc-script.html +FTR_PAGES = csd.html charset.html token.html pkcs11.html tpm.html features.html gui.html nonroot.html hip.html +START_PAGES = building.html connecting.html manual.html vpnc-script.html INDEX_PAGES = changelog.html download.html index.html packages.html platforms.html PROTO_PAGES = anyconnect.html juniper.html globalprotect.html TOPLEVEL_PAGES = contribute.html mail.html diff --git a/www/anyconnect.xml b/www/anyconnect.xml index 5ee1ce17..fd7e90ac 100644 --- a/www/anyconnect.xml +++ b/www/anyconnect.xml @@ -59,7 +59,7 @@ The username/password for OpenSSL RT is 'guest/guest'

GnuTLS

-

Support for Cisco's version of DTLS was included in GnuTLS from 3.0.21 onwards.

+

Support for Cisco's version of DTLS was included in GnuTLS from 3.0.21 onwards (commited in fd5ca1af).

diff --git a/www/building.xml b/www/building.xml index bcee63b9..d15f48bc 100644 --- a/www/building.xml +++ b/www/building.xml @@ -23,7 +23,7 @@ libraries and tools installed:

  • libxml2
  • zlib
  • -
  • Either OpenSSL or GnuTLS
  • +
  • Either OpenSSL or GnuTLS (v3.2.10+)
  • pkg-config
And optionally also: diff --git a/www/changelog.xml b/www/changelog.xml index 6016256a..300ea2c8 100644 --- a/www/changelog.xml +++ b/www/changelog.xml @@ -15,6 +15,8 @@
  • OpenConnect HEAD
      +
    • Drop support for GnuTLS older than 3.2.10.
    • +
    • Fix --passwd-on-stdin for Windows to not forcibly open console.
    • Fix portability of shell scripts in test suite.
    • Add Google Authenticator TOTP support for Juniper.
    • Add RFC7469 key PIN support for cert hashes.
    • diff --git a/www/features.xml b/www/features.xml index 92457dc4..f878e96d 100644 --- a/www/features.xml +++ b/www/features.xml @@ -24,7 +24,7 @@
    • Automatic update of VPN server list / configuration.
    • Roaming support, allowing reconnection when the local IP address changes.
    • Run without root privileges (see here).
    • -
    • "Cisco Secure Desktop" support (see here).
    • +
    • Support for "Cisco Secure Desktop" (see here), Juniper TNCC (see here), and "GlobalProtect HIP report" (see here).
    • Graphical connection tools for various environments (see here).
    diff --git a/www/globalprotect.xml b/www/globalprotect.xml index ee458199..a9de423a 100644 --- a/www/globalprotect.xml +++ b/www/globalprotect.xml @@ -16,15 +16,28 @@ href="https://tools.ietf.org/html/rfc3948">ESP, with routing and configuration information distributed in XML format.

    +

    GlobalProtect mode is requested by adding --protocol=gp +to the command line: +

    +  openconnect --protocol=gp vpn.example.com
    +

    + +

    Authentication

    +

    To authenticate, you connect to the secure web server (POST /ssl-vpn/login.esp), provide a username, password, and (optionally) a certificate, and receive an authcookie. The username, authcookie, and a couple other bits of information obtained at login are combined into the OpenConnect cookie.

    +

    Tunnel configuration

    +

    To connect to the secure tunnel, the cookie is used to read routing and tunnel configuration information (POST /ssl-vpn/getconfig.esp).

    +

    Next, a HIP report (security scanner report) is +generated by the client and submitted to the server, if required.

    +

    Finally, either an HTTPS-based or ESP-based tunnel is setup:

      diff --git a/www/hip.xml b/www/hip.xml new file mode 100644 index 00000000..0009c320 --- /dev/null +++ b/www/hip.xml @@ -0,0 +1,89 @@ + + + + + + + + + + +

      PAN GlobalProtect HIP

      + +

      The HIP ('Host Integrity Protection') mechanism is a security +scanner for the PAN GlobalProtect +VPNs, in the same vein as Cisco's CSD and Juniper's Host Checker (tncc.jar).

      + +

      How it works

      + +

      It is somewhat less intrusive than CSD or TNCC, because it +does not appear to work by downloading a trojan binary from the VPN +server. Instead, it runs a HIP report generator (built-in as part of +the official GlobalProtect VPN client software), which generates an +"HIP report" XML file.

      + +

      HIP flow used in the official clients:

      +
        +
      1. Client authenticates and fetches the tunnel configuration from the GlobalProtect gateway.
      2. +
      3. Client runs HIP report generator and computes MD5 digest of report.
      4. +
      5. Client checks whether a HIP report is required (/ssl-vpn/hipreportcheck.esp), including its MD5 digest and gateway-assigned IP address in the report.
      6. +
      7. Gateway responds whether or not a HIP report is required (normally, it doesn't require a new one if a report with the same MD5 digest and same IP address have been submitted recently).
      8. +
      9. Client uploads the complete HIP report to (/ssl-vpn/hipreport.esp).
      10. +
      11. Server confirms acceptance of HIP report with a success message.
      12. +
      + +

      If all goes well, the client should have the expected level of +access to resources on the network after these steps are +complete. However, two things can go wrong:

      + +
        +
      • Many GlobalProtect servers report that they require HIP reports + (#3 above), but don't actually enforce this requirement. (For this + reason, OpenConnect does not currently fail if a HIP report is + required but no HIP report script is provided.)
      • +
      • Many GlobalProtect servers will claim that the HIP report was + accepted successfully (#6 above) but silently fail to enable the + expected network access, presumably because some aspect of the + HIP report contents were not approved.
      • +
      + +

      HIP support in openconnect

      + +

      OpenConnect supports HIP report generation and submission by passing the --csd-wrapper=SCRIPT argument with a shell script to generate a HIP report in the format expected by the +server. This shell script must output the HIP report to standard output and exit successfully (status code 0). The HIP script is called with the following command-line arguments:

      + +
      +   --cookie: a URL-encoded string, as output by openconnect
      +             --authenticate --protocol=gp, which includes parameters
      +             --from the /ssl-vpn/login.esp response
      +
      +   --computer: local hostname, which can be overriden with
      +               --openconnect local-hostname=HOSTNAME
      +
      +   --client-ip: IPv4 address allocated by the GlobalProtect VPN for
      +                this client (included in /ssl-vpn/getconfig.esp
      +                response)
      +
      +   --md5: The md5 digest to encode into this HIP report. All that
      +          really matters is that the value in the HIP report
      +          submission should match the value in the HIP report check.
      +
      + +

      Generating/spoofing a HIP report

      + +

      An example hipreport.sh script is included in the +openconnect distribution.

      + +

      Depending on how picky your GlobalProtect +VPN is, it may be necessary to spoof or alter some of the parameters +of the HIP report to match the output of one of the official +clients. In order to capture the contents of the official Windows +client's HIP reports, enable the highest logging level for the "PanGPS +Service", and then sift through the giant PanGPS.log file +(which should be in the same directory as the executables, normally +c:\Program Files\PaloAlto Networks\GlobalProtect) to find +the HIP report submission.

      + + +
      diff --git a/www/index.xml b/www/index.xml index 28d0b95a..ec2147e9 100644 --- a/www/index.xml +++ b/www/index.xml @@ -9,15 +9,18 @@

      OpenConnect

      -

      OpenConnect is an SSL VPN client initially created to support Cisco's AnyConnect SSL VPN. It has since been ported to support the Juniper SSL VPN which is now known as Pulse Connect Secure.

      +

      OpenConnect is an SSL VPN client initially created to support Cisco's AnyConnect SSL VPN. +It has since been ported to support the Juniper SSL VPN (which is now known as Pulse Connect Secure), +and to the Palo Alto Networks GlobalProtect SSL VPN.

      OpenConnect is released under the GNU Lesser Public License, version 2.1.

      Like vpnc, OpenConnect is not officially supported by, or associated in any way -with, Cisco Systems, Juniper Networks or Pulse Secure. It just happens to interoperate with their equipment. +with, Cisco Systems, Juniper Networks, Pulse Secure, or Palo Alto Networks. +It just happens to interoperate with their equipment.

      -

      Development of OpenConnect was started after a trial of the Cisco +

      Development of OpenConnect was started after a trial of the Cisco client under Linux found it to have many deficiencies:

      • Inability to use SSL certificates from a TPM or diff --git a/www/juniper.xml b/www/juniper.xml index d4f3fbfe..82f31061 100644 --- a/www/juniper.xml +++ b/www/juniper.xml @@ -16,10 +16,10 @@ experimental, and is quite likely to be deprecated in favour of the newer Junos Pulse protocol.

        -

        For the time being, Juniper mode is requested by adding --juniper +

        Juniper mode is requested by adding --protocol=nc to the command line:

        -  openconnect --juniper vpn.example.com
        +  openconnect --protocol=nc vpn.example.com
         

        Network Connect works very similarly to @@ -65,7 +65,7 @@ pass the cookie to OpenConnect with its -C option, for example:

        -

        Host Checker (tncc.jar)

        +

        Host Checker (tncc.jar)

        Many sites require a Java applet to run certain tests as a precondition of authentication. This works by sending a DSPREAUTH cookie @@ -80,7 +80,7 @@ along with the tncc-preload.so from this repository. It may also be necessary to pass a Mozilla-compatible user agent string:

        -  ./openconnect --juniper --useragent  'Mozilla/5.0 (Linux) Firefox' --csd-wrapper=./tncc-wrapper.py vpn.example.com
        +  ./openconnect --protocol=nc --useragent 'Mozilla/5.0 (Linux) Firefox' --csd-wrapper=./tncc-wrapper.py vpn.example.com