]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Add openconnect_set_external_browser_callback() and defaults
authorDavid Woodhouse <dwmw2@infradead.org>
Sun, 10 Apr 2022 21:11:39 +0000 (22:11 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Mon, 11 Apr 2022 13:50:18 +0000 (14:50 +0100)
There is a little bit of duplication here since the *library* will spawn
the default (xdg-open) and the application (main.c) has basically the same
code, but that's because we don't want to *require* that the app register
the callback. With suitable default behaviour from the library, it will
work even with existing versions of the NM auth-dialog or other GUI tools.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
configure.ac
hpke.c
libopenconnect.map.in
library.c
main.c
openconnect-internal.h
openconnect.h

index 33b3e12fefdc8281dcea8ab370f4099fb3262d8b..ba5ef6064d7f5a964c65478d7c302cc4b44d0f3c 100644 (file)
@@ -129,6 +129,22 @@ fi
 AM_CONDITIONAL(BUILD_NSIS, [ test "$build_nsis" = "yes" ])
 AM_CONDITIONAL(OPENCONNECT_WINTUN, [ test "${wintun_arch}" != "" ])
 
+AC_ARG_WITH([external-browser],
+       [AS_HELP_STRING([--with-external-browser],
+         [command to use for spawning external web browser])])
+
+if test "$with_external_browser" = "yes" || test "$with_external_browser" = ""; then
+   AC_MSG_CHECKING([for xdg-open])
+   AC_PATH_PROG(with_external_browser, xdg-open, no)
+fi
+if test "$with_external_browser" != "no"; then
+   if test -x "${with_external_browser}"; then
+      AC_DEFINE_UNQUOTED(DEFAULT_EXTERNAL_BROWSER, "${with_external_browser}", [External browser executable])
+   else
+      AC_MSG_ERROR([${with_external_browser} does not seem to be executable.])
+   fi
+fi
+
 AC_ARG_WITH([vpnc-script],
        [AS_HELP_STRING([--with-vpnc-script],
          [default location of vpnc-script helper])])
@@ -308,6 +324,8 @@ AC_DISABLE_STATIC
 
 AC_CHECK_FUNC(nl_langinfo, [AC_DEFINE(HAVE_NL_LANGINFO, 1, [Have nl_langinfo() function])], [])
 
+AC_CHECK_FUNC(posix_spawn, [AC_DEFINE(HAVE_POSIX_SPAWN, 1, [Have posix_spawn() function])], [])
+
 if test "$ac_cv_func_nl_langinfo" = "yes"; then
     AM_ICONV
     if test "$am_cv_func_iconv" = "yes"; then
diff --git a/hpke.c b/hpke.c
index aa40057f09bd1d5e0bd1e33c5a4805155277afc1..5f952959c7cdb03578f9e8b48653e9232b44cb7f 100644 (file)
--- a/hpke.c
+++ b/hpke.c
@@ -24,6 +24,10 @@ int handle_external_browser(struct openconnect_info *vpninfo)
 }
 #else
 
+#ifdef HAVE_POSIX_SPAWN
+#include <spawn.h>
+#endif
+
 #include <ctype.h>
 
 #define HPKE_TAG_PUBKEY                1
@@ -102,12 +106,30 @@ int handle_external_browser(struct openconnect_info *vpninfo)
        if (set_sock_nonblock(listen_fd))
                goto sockerr;
 
+       /* Now that we are listening on the socket, we can spawn the browser */
+       if (vpninfo->open_ext_browser) {
+               ret = vpninfo->open_ext_browser(vpninfo, vpninfo->sso_login, vpninfo->cbdata);
+#if defined(HAVE_POSIX_SPAWN) && defined(DEFAULT_EXTERNAL_BROWSER)
+       } else {
+               vpn_progress(vpninfo, PRG_TRACE, _("Spawning external browser '%s'\n"),
+                            DEFAULT_EXTERNAL_BROWSER);
+
+               pid_t pid = 0;
+               char * browser_argv[3] = { (char *)DEFAULT_EXTERNAL_BROWSER, vpninfo->sso_login, NULL };
 
-       /* Now that we are listening on the socket, we can spawn the browser...
-          or just tell the user to, for now */
-       vpn_progress(vpninfo, PRG_ERR,
-                    _("Point browser to this URL:\n%s\n"),
-                    vpninfo->sso_login);
+               if (posix_spawn(&pid, DEFAULT_EXTERNAL_BROWSER, NULL, NULL, browser_argv, environ)) {
+                       ret = -errno;
+                       vpn_perror(vpninfo, _("Spawn browser"));
+               }
+#else
+       } else {
+               ret = -EINVAL;
+#endif
+       }
+       if (ret)
+               vpn_progress(vpninfo, PRG_ERR,
+                            _("Failed to spawn external browser for %s\n"),
+                            vpninfo->sso_login);
 
        char *returl = NULL;
        struct oc_text_buf *b64_buf = NULL;
index c8bd45c2ec706c478272fce7474ec7921062f340..757341beb12f71ecd25813f42547a085269218c8 100644 (file)
@@ -119,6 +119,11 @@ OPENCONNECT_5_7 {
        openconnect_webview_load_changed;
 } OPENCONNECT_5_6;
 
+OPENCONNECT_5_8 {
+ global:
+       openconnect_set_external_browser_callback;
+} OPENCONNECT_5_7;
+
 OPENCONNECT_PRIVATE {
  global: @SYMVER_TIME@ @SYMVER_GETLINE@ @SYMVER_JAVA@ @SYMVER_ASPRINTF@ @SYMVER_VASPRINTF@ @SYMVER_WIN32_STRERROR@ @SYMVER_WIN32_SETENV@
        openconnect_get_tls_library_version;
index 969c8b8eab23b75dde82437de48260dc1762e43a..a492ee1c53d825da14902f47f0036e1bba38600a 100644 (file)
--- a/library.c
+++ b/library.c
@@ -1668,6 +1668,13 @@ void openconnect_set_webview_callback(struct openconnect_info *vpninfo,
        vpninfo->try_http_auth = 0;
 }
 
+void openconnect_set_external_browser_callback(struct openconnect_info *vpninfo,
+                                              openconnect_open_webview_vfn browser_fn)
+{
+       vpninfo->open_ext_browser = browser_fn;
+       vpninfo->try_http_auth = 0;
+}
+
 int openconnect_webview_load_changed(struct openconnect_info *vpninfo,
                                      const struct oc_webview_result *result)
 {
diff --git a/main.c b/main.c
index e6d409a30c851f2112bf8d79c1c03e40bb891dc8..f3b5810436b7361021793a588b8933e648bf4a35 100644 (file)
--- a/main.c
+++ b/main.c
@@ -96,6 +96,8 @@ static int allow_stdin_read;
 static char *token_filename;
 static int allowed_fingerprints;
 
+static char *ext_browser;
+
 struct accepted_cert {
        struct accepted_cert *next;
        char *fingerprint;
@@ -178,6 +180,7 @@ enum {
        OPT_DTLS_CIPHERS,
        OPT_DTLS12_CIPHERS,
        OPT_DUMP_HTTP,
+       OPT_EXT_BROWSER,
        OPT_FORCE_DPD,
        OPT_FORCE_TROJAN,
        OPT_GNUTLS_DEBUG,
@@ -234,6 +237,9 @@ static const struct option long_options[] = {
        OPTION("syslog", 0, 'l'),
        OPTION("csd-user", 1, OPT_CSD_USER),
        OPTION("csd-wrapper", 1, OPT_CSD_WRAPPER),
+#endif
+#ifdef HAVE_POSIX_SPAWN
+       OPTION("external-browser", 1, OPT_EXT_BROWSER),
 #endif
        OPTION("pfs", 0, OPT_PFS),
        OPTION("allow-insecure-crypto", 0, OPT_ALLOW_INSECURE_CRYPTO),
@@ -885,7 +891,24 @@ static BOOL WINAPI console_ctrl_handler(DWORD dwCtrlType)
        return TRUE;
 }
 #endif
+#ifdef HAVE_POSIX_SPAWN
+#include <spawn.h>
+static int spawn_browser(struct openconnect_info *vpninfo, const char *url, void *cbdata)
+{
+       vpn_progress(vpninfo, PRG_TRACE,
+                    _("Main Spawning external browser '%s'\n"),
+                    ext_browser);
+       pid_t pid = 0;
+       char *browser_argv[3] = { ext_browser, (char *)url, NULL };
 
+       if (posix_spawn(&pid, ext_browser, NULL, NULL, browser_argv, environ)) {
+               vpn_perror(vpninfo, _("Spawn browser"));
+               return -errno;
+       }
+
+       return 0;
+}
+#endif
 static void print_default_vpncscript(void)
 {
        printf("%s %s\n", _("Default vpnc-script (override with --script):"),
@@ -937,6 +960,7 @@ static void usage(void)
        printf("  -e, --cert-expire-warning=DAYS  %s\n", _("Warn when certificate lifetime < DAYS"));
        printf("  -g, --usergroup=GROUP           %s\n", _("Set login usergroup"));
        printf("  -p, --key-password=PASS         %s\n", _("Set key passphrase or TPM SRK PIN"));
+       printf("      --external-browser=BROWSER  %s\n", _("Set external browser executable"));
        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, hotp or oidc"));
        printf("      --token-secret=STRING       %s\n", _("Software token secret or oidc token"));
@@ -1386,6 +1410,7 @@ static int autocomplete(int argc, char **argv)
 
                        case 's': /* --script */
                        case OPT_CSD_WRAPPER: /* --csd-wrapper */
+                       case OPT_EXT_BROWSER: /* --external-browser */
                                autocomplete_special("EXECUTABLE", comp_opt, prefixlen, NULL);
                                break;
 
@@ -2013,6 +2038,9 @@ int main(int argc, char **argv)
                case 's':
                        vpnc_script = dup_config_arg();
                        break;
+               case OPT_EXT_BROWSER:
+                       ext_browser = dup_config_arg();
+                       break;
                case 'u':
                        free(username);
                        username = dup_config_arg();
@@ -2165,7 +2193,6 @@ int main(int argc, char **argv)
                verbose = PRG_DEBUG;
 
        openconnect_set_loglevel(vpninfo, verbose);
-
        if (autoproxy) {
 #ifdef LIBPROXY_HDR
                vpninfo->proxy_factory = px_proxy_factory_new();
@@ -2181,6 +2208,11 @@ int main(int argc, char **argv)
        if (proxy && openconnect_set_http_proxy(vpninfo, strdup(proxy)))
                exit(1);
 
+#ifdef HAVE_POSIX_SPAWN
+       if (ext_browser)
+               openconnect_set_external_browser_callback(vpninfo, spawn_browser);
+#endif
+
 #ifndef _WIN32
        memset(&sa, 0, sizeof(sa));
 
index 629136cc59806f288fb5f2ea67d44f9e80e949df..ad77ec5c7cb308311fdf89fd66d538b22542b759 100644 (file)
@@ -780,6 +780,7 @@ struct openconnect_info {
        openconnect_validate_peer_cert_vfn validate_peer_cert;
        openconnect_write_new_config_vfn write_new_config;
        openconnect_open_webview_vfn open_webview;
+       openconnect_open_webview_vfn open_ext_browser;
        openconnect_process_auth_form_vfn process_auth_form;
        openconnect_progress_vfn progress;
        openconnect_protect_socket_vfn protect_socket;
index 9c3e92d46ae8fcaf45242e36eb57c3df8ebb2356..ae11b6f4ba7825ce3c0c8c1ae5ce111041e41f28 100644 (file)
@@ -36,6 +36,9 @@ extern "C" {
 #define OPENCONNECT_API_VERSION_MINOR 7
 
 /*
+ * API version 5.8:
+ *  - Add openconnect_set_external_browser_callback()
+ *
  * API version 5.7 (v8.20; 2022-02-20):
  *  - Add openconnect_get_connect_url()
  *  - Add openconnect_set_cookie()
@@ -757,6 +760,9 @@ void openconnect_set_webview_callback(struct openconnect_info *vpninfo,
 int openconnect_webview_load_changed(struct openconnect_info *vpninfo,
                                      const struct oc_webview_result *result);
 
+void openconnect_set_external_browser_callback(struct openconnect_info *vpninfo,
+                                              openconnect_open_webview_vfn);
+
 /* Callback to allow binding a newly created socket's file descriptor to
    a specific interface, e.g. with SO_BINDTODEVICE. This tells the kernel
    not to route the traffic in question over the VPN tunnel. */