]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Add `openconnect_get_auth_expiration` function to library and JNI
authorDaniel Lenski <dlenski@gmail.com>
Thu, 24 Sep 2020 23:54:57 +0000 (16:54 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Mon, 14 Dec 2020 22:44:03 +0000 (14:44 -0800)
This allows protocols to save the moment when a session's authentication
(`vpninfo->cookie`) is expected to expire and no longer be useful for
reconnection.

The motivation is to eventually allow front-ends to know whether
reauthentication is needed, or whether they should try using a cached
cookie.

Current state:

- AnyConnect protocol: expiration is determined from the CONNECT
  response header `X-CSTP-Session-Timeout-Remaining` (with
  `X-CSTP-Session-Timeout` or `X-CSTP-Lease-Duration` as upper bounds in its
  absence)
- GlobalProtect protocol: expiration is determined from the `<lifetime>` tag of
  the XML config.
- Juniper Network Connect protocol: no currently known way to determine
  expiration. The `DSID` cookie is a standard HTTP cookie, so perhaps its
  expiration timestamp is intended for this purpose; however, I can find
  no real-world case where it has an expiration timestamp set.
- None of the currently-supported protocols provide the expiration
  timestamp until the connection phase, so it can't be obtained for
  export by the `--authenticate` option.

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
cstp.c
gpst.c
java/src/com/example/LibTest.java
java/src/org/infradead/libopenconnect/LibOpenConnect.java
jni.c
libopenconnect.map.in
library.c
main.c
openconnect-internal.h
openconnect.h

diff --git a/cstp.c b/cstp.c
index 4225760b1561eeb5b37ab3a3122f1f0d887f6bd1..5f806cf7517e57db2cb8558298f1528ef02fa87d 100644 (file)
--- a/cstp.c
+++ b/cstp.c
@@ -529,6 +529,18 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
 
                if (!strcmp(buf + 7, "Keepalive")) {
                        vpninfo->ssl_times.keepalive = atol(colon);
+               } else if (!strcmp(buf + 7, "Lease-Duration") ||
+                          !strcmp(buf + 7, "Session-Timeout") ||
+                          !strcmp(buf + 7, "Session-Timeout-Remaining")) {
+
+                       /* XX: Distinction between Lease-Duration and Session-Timeout is rather unclear. Cisco doc:
+                        * https://www.cisco.com/assets/sol/sb/RV345P_Emulators/RV345P_Emulator_v1-0-01-17/help/help/t_SSL_VPN.html
+                        * Empirically, it appears that the best behavior is to accept whichever of these headers has the
+                        * lowest non-zero value.
+                        */
+                       long j = atol(colon);
+                       if (j && (!vpninfo->auth_expiration || j < vpninfo->auth_expiration))
+                               vpninfo->auth_expiration = time(NULL) + j;
                } else if (!strcmp(buf + 7, "Idle-Timeout")) {
                        vpninfo->idle_timeout = atol(colon);
                } else if (!strcmp(buf + 7, "DPD")) {
diff --git a/gpst.c b/gpst.c
index 761a91423f049d7e6908c0365c2abefb54ef72e3..f8143a68dfaba1e38c358623f9cf39478caa70b9 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -491,7 +491,7 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_
                } else if (!xmlnode_get_val(xml_node, "mtu", &s))
                        vpninfo->ip_info.mtu = atoi(s);
                else if (!xmlnode_get_val(xml_node, "lifetime", &s))
-                       vpn_progress(vpninfo, PRG_INFO, _("Session will expire after %d minutes.\n"), atoi(s)/60);
+                       vpninfo->auth_expiration = time(NULL) + atol(s);
                else if (!xmlnode_get_val(xml_node, "disconnect-on-idle", &s)) {
                        int sec = atoi(s);
                        vpn_progress(vpninfo, PRG_INFO, _("Idle timeout is %d minutes.\n"), sec/60);
index 280ea1d2803f3f252b0a5a89c375ecc8fe024561..d2097ca2adb0dfb5576cb6905817589fa65f5828 100644 (file)
@@ -17,6 +17,7 @@ package com.example;
 
 import java.io.*;
 import java.util.*;
+import java.time.Instant;
 import org.infradead.libopenconnect.LibOpenConnect;
 
 public final class LibTest {
@@ -279,6 +280,8 @@ public final class LibTest {
 
                int idleTimeout = lib.getIdleTimeout();
                System.out.println("Idle Timeout: " + idleTimeout + " seconds");
+               Instant authExpiration = lib.getAuthExpiration();
+               System.out.println("Auth Expiration: " + authExpiration.toString());
                printIPInfo(lib.getIPInfo());
 
                if (lib.setupDTLS(60) != 0)
index 2f60e1078174787833aee6bce4a3a362be8bd914..e9606ba0054270bc7b24d739a7222b28fd52f4aa 100644 (file)
@@ -17,6 +17,7 @@ package org.infradead.libopenconnect;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.time.Instant;
 
 public abstract class LibOpenConnect {
 
@@ -166,6 +167,7 @@ public abstract class LibOpenConnect {
        public synchronized native String getDTLSCompression();
        public synchronized native String getProtocol();
        public synchronized native int getIdleTimeout();
+       public synchronized native Instant getAuthExpiration();
 
        /* certificate info */
 
diff --git a/jni.c b/jni.c
index 8f5b982a03a070376b6bd9d11a91222bb959446a..9ef7959ec80945736f8305e32993d4d98c183f8f 100644 (file)
--- a/jni.c
+++ b/jni.c
@@ -1164,6 +1164,36 @@ JNIEXPORT jint JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getIdleT
        return openconnect_get_idle_timeout(ctx->vpninfo);
 }
 
+JNIEXPORT jobject JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_getAuthExpiration(
+       JNIEnv *jenv, jobject jobj)
+{
+       struct libctx *ctx = getctx(jenv, jobj);
+       jmethodID mid;
+       jobject result;
+       jclass jcls;
+       time_t auth_expiration;
+
+       if (!ctx)
+               return NULL;
+
+       auth_expiration = openconnect_get_auth_expiration(ctx->vpninfo);
+       jcls = (*ctx->jenv)->FindClass(ctx->jenv, "java/time/Instant");
+       if (jcls == NULL)
+               goto err;
+       mid = (*jenv)->GetStaticMethodID(jenv, jcls, "ofEpochSecond", "(J)Ljava/time/Instant;");
+       if (!mid)
+               goto err;
+       result = (*jenv)->CallStaticObjectMethod(jenv, jcls, mid, auth_expiration);
+       if (result == NULL)
+               goto err;
+
+       return result;
+
+err:
+       return NULL;
+}
+
+
 /* simple cases: return a const string (no need to free it) */
 
 #define RETURN_STRING_START \
index a45b25fd7c33ca7f47a68c514e0344c0c63d08da..9c5171fb667347879c7727da66aa816137af93b2 100644 (file)
@@ -112,6 +112,7 @@ OPENCONNECT_5_7 {
  global:
        openconnect_set_cookie;
        openconnect_set_allow_insecure_crypto;
+       openconnect_get_auth_expiration;
 } OPENCONNECT_5_6;
 
 OPENCONNECT_PRIVATE {
index 4146e8f1828f8a18a97546a1b5bd96fa1de4afad..3a8d6cd664c01fc796e58edeffb97649b23b4c80 100644 (file)
--- a/library.c
+++ b/library.c
@@ -611,6 +611,11 @@ int openconnect_get_idle_timeout(struct openconnect_info *vpninfo)
        return vpninfo->idle_timeout;
 }
 
+time_t openconnect_get_auth_expiration(struct openconnect_info *vpninfo)
+{
+       return vpninfo->auth_expiration;
+}
+
 int openconnect_get_ip_info(struct openconnect_info *vpninfo,
                            const struct oc_ip_info **info,
                            const struct oc_vpn_option **cstp_options,
diff --git a/main.c b/main.c
index 7978af221d42b902fbb6b5a7a7a7db1356d7e10b..58a7fc0f9806d5d4bf6b769a97be5458a59c7d3e 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1420,6 +1420,9 @@ static void print_connection_info(struct openconnect_info *vpninfo)
                     ssl_compr ? " + " : "", ssl_compr ? : "",
                     vpninfo->proto->udp_protocol ? : "UDP", udp_compr ? " + " : "", udp_compr ? : "",
                     dtls_state);
+       if (vpninfo->auth_expiration != 0)
+               vpn_progress(vpninfo, PRG_INFO, _("Session authentication will expire at %s"),
+                            ctime(&vpninfo->auth_expiration));
 }
 
 #ifndef _WIN32
index 0db75b272b82cef70604acaf840a59a6c913a9ac..af11c36641418dc6dc51e141e86df64ddbf62620 100644 (file)
@@ -567,6 +567,7 @@ struct openconnect_info {
        int reconnect_timeout;
        int reconnect_interval;
        int dtls_attempt_period;
+       time_t auth_expiration;
        time_t new_dtls_started;
 #if defined(OPENCONNECT_OPENSSL)
        SSL_CTX *dtls_ctx;
index b89f2a6949d25ef78b68f01b3c5f0f16eb667fdb..8fba0ddfd00c7c5ac06b3b3ab48e34972bca9809 100644 (file)
@@ -39,6 +39,7 @@ extern "C" {
  * API version 5.7:
  *  - Add openconnect_set_cookie()
  *  - Add openconnect_set_allow_insecure_crypto()
+ *  - Add openconnect_get_auth_expiration()
  *
  * API version 5.6 (v8.06; 2020-03-31):
  *  - Add openconnect_set_trojan_interval()
@@ -532,6 +533,7 @@ void openconnect_set_reqmtu(struct openconnect_info *, int reqmtu);
 void openconnect_set_dpd(struct openconnect_info *, int min_seconds);
 void openconnect_set_trojan_interval(struct openconnect_info *, int seconds);
 int openconnect_get_idle_timeout(struct openconnect_info *);
+time_t openconnect_get_auth_expiration(struct openconnect_info *);
 
 /* The returned structures are owned by the library and may be freed/replaced
    due to rekey or reconnect. Assume that once the mainloop starts, the