From: David Woodhouse Date: Sun, 10 Apr 2022 21:11:39 +0000 (+0100) Subject: Add openconnect_set_external_browser_callback() and defaults X-Git-Tag: v9.00~54 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=b474b2539aa2abfc4a32de9af1f812d87e1c9a66;p=users%2Fdwmw2%2Fopenconnect.git Add openconnect_set_external_browser_callback() and defaults 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 --- diff --git a/configure.ac b/configure.ac index 33b3e12f..ba5ef606 100644 --- a/configure.ac +++ b/configure.ac @@ -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 aa40057f..5f952959 100644 --- a/hpke.c +++ b/hpke.c @@ -24,6 +24,10 @@ int handle_external_browser(struct openconnect_info *vpninfo) } #else +#ifdef HAVE_POSIX_SPAWN +#include +#endif + #include #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; diff --git a/libopenconnect.map.in b/libopenconnect.map.in index c8bd45c2..757341be 100644 --- a/libopenconnect.map.in +++ b/libopenconnect.map.in @@ -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; diff --git a/library.c b/library.c index 969c8b8e..a492ee1c 100644 --- 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 e6d409a3..f3b58104 100644 --- 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 +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)); diff --git a/openconnect-internal.h b/openconnect-internal.h index 629136cc..ad77ec5c 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -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; diff --git a/openconnect.h b/openconnect.h index 9c3e92d4..ae11b6f4 100644 --- a/openconnect.h +++ b/openconnect.h @@ -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. */