Daniel Lenski [Thu, 21 Dec 2017 06:56:58 +0000 (22:56 -0800)]
Protocols should try explicitly request the same IP addresses on reconnect, since they will abort if new addresses are sent by the server.
* GlobalProtect:
- Supported and used by official clients (POST /ssl-vpn/getconfig.esp with preferred-ip form field).
- GlobalProtect servers often give different IP addresses on reconnect if this mechanism is *not* used,
so this mechanism is necessary.
- Same mechanism appears to exist for IPv6 (preferred-ipv6) and was added to OpenConnect in d6db0ec03394234d41fbec7ffc794ceeb486a8f0, even though IPv6 support is not yet complete.
* AnyConnect:
- Not (yet) supported by ocserv
- It appears that *some* AnyConnect server will try to provide the IP address provided in the X-CSTP-Address
*request* header along with the CONNECT request, but other servers appear not to
- This patch reproduces the behavior of GPST: attempt to request same IPv4 and IPv6 addresses on reconnect,
via CONNECT headers.
* Juniper:
- There does not appear to be any way to provide this using the Juniper NC protocol.
- No known reports of Juniper servers giving out different IP address on reconnect.
Connect OpenConnect to the TLS socket, and watch it negotiate LCP/IPCP/IP6CP with its peer, and reject CCP:
# Add noipv4,noipv6 to cookie to try those
./openconnect --protocol=nullppp --cookie hdlc --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --dump localhost:5556
Daniel Lenski [Tue, 19 May 2020 17:54:22 +0000 (10:54 -0700)]
add standard-based PPP framing (RFC1661, RFC1662) as reference points
- PPP_ENCAP_RFC1661: Plain PPP. “Synchronous” in the ’90s-era language,
because the start and end of the frame are known from external means.
In modern instances, this is because they arrive in a complete packet with
a known length from its lower-level encapsulation. (TLS or DTLS records in
our expected use cases.)
This is what `pppd sync` does.
- PPP_ENCAP_RFC1662: “PPP in HDLC-like framing.”
This is what `pppd` without `sync` does.
At some point, we will be able to add automated testing of our PPP
implementation's ability to communicate with `pppd`.
There's no clear rationale for using with Pulse/oNCP ESP setup (yet):
- We don't do any MTU detection
- Unlike GPST, we can start sending and receiving packets via the TLS tunnel
immediately, while attempting to connect ESP as well.
Andreas Gnau [Sun, 17 May 2020 17:46:09 +0000 (19:46 +0200)]
ppp: Reject unknown protocols
NX won't send any IPCP requests until CCP negotiation is completed.
Since we don't want to implement compression for now (or ever), reject
unknown protocols with an LCP Protocol-Reject (cf. RFC 1661, ch. 5.7).
Daniel Lenski [Sun, 17 May 2020 19:46:43 +0000 (12:46 -0700)]
make delay_tunnel consistent with delay_close
1. Decrement counter on each mainloop iteration. Protocol needs to keep setting it to get more mainloop iterations before tunnel setup / close.
2. Value ≥2 causes us to set did_work=1, resulting in no delay before we call mainloop again. Protocol should only set this if it needs to SEND something in order to move things along.
3. Value =1 causes us to set did_work=0, resulting in a delay before we call mainloop again. Protocol should only set this if it needs to RECEIVE something in order to move things along.
Daniel Lenski [Sun, 17 May 2020 20:02:31 +0000 (13:02 -0700)]
attempt to coax server to accept a larger MRU by nak-offering our MTU if it's greater than the server's MRU
We only attempt this once before giving up.
Tested on F5, and this works: we nak-offer a larger MRU, and the server
responds with a new CONFACK requesting the same value.
The F5 server often offers erroneously low MRU values, too small for IPv6
(minimum MTU of 1280), and is consistently strict about not accepting
incoming IP packets which are even 1 byte larger than its MRU, so this is
useful. 🍺
Daniel Lenski [Sun, 17 May 2020 02:31:22 +0000 (19:31 -0700)]
add delay_close and use it for clean PPP termination on cancel, pause, or server-sent termination
Whatever delay_close is set to decrements on each mainloop iteration, and
if delay_close == 1, we don't set work_done.
This allows us to set delay_close = 2 for the case where we need to send a termination request
immediately, and then wait briefly for an acknowledgment.
Daniel Lenski [Sat, 16 May 2020 01:03:45 +0000 (18:03 -0700)]
use ACK/NAK request mechanism to request IPv4 address, (IPv4) DNS/NBNS addresses, and IPv6 interface identifiers
The idea is that if we CONFREQ an all-zero value for one of these.
PPP server/peer is supposed to:
- CONFNAK with a value filled in, which we then re-CONFREQ to confirm.
- CONFREJ if it can't supply a value (e.g. NBNS servers because it's
not 1994 and we're not using Windows for Workgroups 3.11).
Tested with F5, by overriding ppp->solicit_peerns=7, ppp->out_peer_addr.s_addr=0,
ppp->out_ipv6_int_ident=0 in openconnect_ppp_new (even though F5 in fact sends these
addresses in the XML config prior to PPP tunnel).
The CONFREQ/CONFNAK/re-CONFREQ exchange is inefficient, requiring three rounds trips
(request and reject, partial re-request and nak, confirming request and ack), but it
works:
Received proto 0x8021/id 1 Configure-Reject from server
Server rejected IPCP request for NBNS[1] server
Server rejected IPCP request for DNS[1] server
Server rejected IPCP request for NBNS[0] server
...
Sending our proto 0x8021/id 2 config request to server
< 0000: f5 00 00 10 80 57 02 01 00 0e 01 0a e0 a7 1c fb |.....W..........|
< 0010: 9e 55 00 00 |.U..|
Sending PPP IPCP Configure-Request packet (id 2, 22 bytes total)
> 0000: f5 00 00 12 80 21 01 02 00 10 03 06 00 00 00 00 |.....!..........|
> 0010: 81 06 00 00 00 00 |......|
No work to do; sleeping for 3000 ms...
< 0000: f5 00 00 12 80 21 03 02 00 10 03 06 0a 00 00 17 |.....!..........|
< 0010: 81 06 5a 9b 5c d1 |..Z.\.|
Received proto 0x8021/id 2 Configure-Nak from server
Server nak-offered IPv4 address: 10.0.0.23
Server nak-offered IPCP request for DNS[0] server: 90.155.92.209
...
Sending our proto 0x8021/id 3 config request to server
Sending PPP IPCP Configure-Request packet (id 3, 16 bytes total)
> 0000: f5 00 00 0c 80 21 01 03 00 0a 03 06 0a 00 00 17 |.....!..........|
No work to do; sleeping for 3000 ms...
< 0000: f5 00 00 0c 80 21 02 03 00 0a 03 06 0a 00 00 17 |.....!..........|
Received proto 0x8021/id 3 Configure-Ack from server
PPP state transition from OPENED to NETWORK
Current PPP state: NETWORK (encap F5):
in: asyncmap=0x00000000, lcp_opts=384, lcp_magic=0x04eb81f9, peer=1.1.1.1
out: asyncmap=0x00000000, lcp_opts=422, lcp_magic=0x70ac508f, peer=10.0.0.23, solicit_peerns=0
The purpose of the IPv6 interface identifier negotiation is unclear, but the F5
server does not accept a zero value, using CONFNAK to offer a new one.
Daniel Lenski [Fri, 15 May 2020 01:48:55 +0000 (18:48 -0700)]
attempted support for concatenated packets
Plus add comments about what each variable points to and when, during packet parsing.
Tested with F5 HDLC and non-HDLC. Doesn't complain about leftover bytes, or short/incomplete packets, but NOT YET
actually tested with concatenated packets.
Daniel Lenski [Wed, 13 May 2020 21:29:41 +0000 (14:29 -0700)]
add test-fortinet-login.py
Often easier to prototype HTTPS-based authentication flows in Python, since
they're so fiddly and arbitary. So I copied `test-f5-login.py` to
`test-fortinet-login.py`. Currently only handles basic
username-and-password auth, no 2FA:
positional arguments:
endpoint Fortinet server (or complete URL, e.g.
https://forti.vpn.com/remote/login)
extra Extra field to pass to include in the login query
string (e.g. "foo=bar")
optional arguments:
-h, --help show this help message and exit
-v, --verbose
--no-verify Ignore invalid server certificate
Login credentials:
-u USERNAME, --username USERNAME
Username (will prompt if unspecified)
-p PASSWORD, --password PASSWORD
Password (will prompt if unspecified)
-r REALM, --realm REALM
Realm (empty if unspecified)
-c CERT, --cert CERT PEM file containing client certificate (and optionally
private key)
--key KEY PEM file containing client private key (if not
included in same file as certificate)
```
David Woodhouse [Tue, 12 May 2020 21:27:24 +0000 (22:27 +0100)]
Handle ConfRej for anything that needs it.
If get a ConfReq with anything we don't want or understand — and that
includes bloody VJ header compression, since I'm not completely batshit
insane — send a ConfRej.
Do this by building up the options to be rejected in an oc_text_buf as
we go, then rejecting that set if it's non-empty once we get to the end.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Daniel Lenski [Tue, 12 May 2020 21:12:25 +0000 (14:12 -0700)]
Fix un-HDLC corner cases
1) The initial 0x7e is optional, the final 0x7e is not (was reversed).
2) Dangling escape can occur even when we haven't run out of buffer. 0x7d 0x7e is an invalid sequence.
While not breaking…
3) 0x7d can be the “target” of an escape (0x7d 0x7d → 0x5d)
4) 0x5d as the “target” of an escape (0x7d 0x5d → 0x7d) doesn't indicate a new escape