]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Add a fake IPSEC/ESP configuration to fake-gp-server.py
authorDaniel Lenski <dlenski@gmail.com>
Sat, 27 Jul 2024 22:04:58 +0000 (15:04 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Sun, 28 Jul 2024 23:02:03 +0000 (16:02 -0700)
This allows testing for correct interpretation of the ESP configuration, as in
https://lists.infradead.org/pipermail/openconnect-devel/2024-July/005447.html

Also needed to fix a mistake in the logout handler of fake-gp-server.py
("POST not GET"), and an oversight in how GP propagated errors when falling
back to TLS tunnel from ESP.

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
gpst.c
tests/fake-gp-server.py

diff --git a/gpst.c b/gpst.c
index 0450107ad4ecaf8f07ef51527c839c9cf2b02660..cb3ff89e58b903a3dd9b3e3d14045b67ccd09717 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -769,6 +769,7 @@ static int gpst_connect(struct openconnect_info *vpninfo)
                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);
+                       dump_buf(vpninfo, '<', buf);
                }
                int status = check_http_status(buf, ret);
                /* XX: GP servers return 502 when they don't like the cookie */
@@ -1152,9 +1153,9 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
                                     _("Failed to connect ESP tunnel; using HTTPS instead.\n"));
                /* XX: gpst_connect does nothing if ESP is enabled and has secrets */
                vpninfo->dtls_state = DTLS_NOSECRET;
-               if (gpst_connect(vpninfo)) {
+               if (ret = gpst_connect(vpninfo)) {
                        vpninfo->quit_reason = "GPST connect failed";
-                       return 1;
+                       return ret;
                }
                break;
        case DTLS_NOSECRET:
@@ -1310,9 +1311,9 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
                        return ret;
                }
                /* XX: no need to do_reconnect, since ESP doesn't need reconnection */
-               if (gpst_connect(vpninfo))
+               if (ret = gpst_connect(vpninfo))
                        vpninfo->quit_reason = "GPST connect failed";
-               return 1;
+               return ret;
        }
 
        switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
index 98792e20117b31bbd1fa6b326aa48776b9344710..908f91c736a18d23d8a277e89653b084a82e921a 100755 (executable)
@@ -95,6 +95,7 @@ class TestConfiguration:
     gateway_saml: str = None
     saml_comments_only: int = None
     saml_needs_js: int = None
+    esp: bool = True
 C = TestConfiguration()
 OUTSTANDING_SAML_TOKENS = set()
 
@@ -103,7 +104,7 @@ OUTSTANDING_SAML_TOKENS = set()
 def configure():
     global C
     if request.method == 'POST':
-        gateways, portal_2fa, gw_2fa, portal_cookie, portal_saml, gateway_saml, saml_comments_only, saml_needs_js = request.form.get('gateways'), request.form.get('portal_2fa'), request.form.get('gw_2fa'), request.form.get('portal_cookie'), request.form.get('portal_saml'), request.form.get('gateway_saml'), request.form.get('saml_comments_only'), request.form.get('saml_needs_js')
+        gateways, portal_2fa, gw_2fa, portal_cookie, portal_saml, gateway_saml, saml_comments_only, saml_needs_js, esp = request.form.get('gateways'), request.form.get('portal_2fa'), request.form.get('gw_2fa'), request.form.get('portal_cookie'), request.form.get('portal_saml'), request.form.get('gateway_saml'), request.form.get('saml_comments_only'), request.form.get('saml_needs_js'), request.form.get('esp')
         C.gateways = gateways.split(',') if gateways else ('Default gateway',)
         C.portal_cookie = portal_cookie
         C.portal_2fa = portal_2fa and portal_2fa.strip().lower()
@@ -112,6 +113,7 @@ def configure():
         C.gateway_saml = gateway_saml
         C.saml_comments_only = int(saml_comments_only) if saml_comments_only else None
         C.saml_needs_js = int(saml_needs_js) if saml_needs_js else None
+        C.esp = int(esp) if esp else None
         return '', 201
     else:
         return 'Current configuration of fake GP server configuration:\n{}\n'.format(C)
@@ -340,9 +342,25 @@ def gateway_login():
 def getconfig():
     session.update(step='gateway-config')
     addrs = '<ip-address>{}</ip-address>'.format(session['preferred_ip'])
+    addrs += '<gw-address>127.0.0.1</gw-address>'
+    addrs += '<gw-address-v6>::1</gw-address-v6>'  # some(?) servers send the IPv6 address even if not otherwise configured for IPv6
     if session['ipv6_support'] == 'yes':
         addrs += '<ip-address-v6>{}</ip-address-v6>'.format(session['preferred_ipv6'])
-    return '''<response>{}<ssl-tunnel-url>/ssl-tunnel-connect.sslvpn</ssl-tunnel-url></response>'''.format(addrs)
+    ipsec = ''
+    if C.esp:
+        ipsec = '''<ipsec>
+            <udp-port>4501</udp-port>
+            <ipsec-mode>esp-tunnel</ipsec-mode>
+            <enc-algo>aes-128-cbc</enc-algo>
+            <hmac-algo>sha1</hmac-alog>
+            <c2s-spi>0xa5a5a5a5</c2s-spi>
+            <s2c-spi>0x5a5a5a5a</s2c-spi>
+            <akey-s2c><bits>160</bits><val>deadbeefdeadbeefdeadbeefdeadbeefdeadbeef</val></akey-s2c>
+            <akey-c2s><bits>160</bits><val>deadbeefdeadbeefdeadbeefdeadbeefdeadbee1</val></akey-c2s>
+            <ekey-s2c><bits>128</bits><val>deadbeefdeadbeefdeadbeefdeadbeef</val></ekey-s2c>
+            <ekey-c2s><bits>128</bits><val>deadbeefdeadbeefdeadbeefdeadbee1</val></ekey-c2s>
+        </ipsec>'''
+    return '''<response>{}{}<ssl-tunnel-url>/ssl-tunnel-connect.sslvpn</ssl-tunnel-url></response>'''.format(addrs, ipsec)
 
 
 # Respond to gateway hipreportcheck request
@@ -366,7 +384,7 @@ def tunnel():
 
 
 # Respond to 'GET /ssl-vpn/logout.esp' by clearing session and MRHSession
-@app.route('/ssl-vpn/logout.esp')
+@app.route('/ssl-vpn/logout.esp', methods=('POST',))
 # XX: real server really requires all these fields; see auth-globalprotect.c
 @check_form_against_session('authcookie', 'portal', 'user', 'computer')
 def logout():