From: Steven Walter Date: Wed, 18 Mar 2020 16:36:25 +0000 (-0400) Subject: Support AnyConnect single-sign-on-v2 X-Git-Tag: v9.00~93 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=024336a8ddeb1754ae5e8fb18770e90c206070b1;p=users%2Fdwmw2%2Fopenconnect.git Support AnyConnect single-sign-on-v2 Advertise support for auth-method single-sign-on-v2. This, combined with not sending X-AnyConnect-Platform and X-Support-HTTP-Auth, allows one to complete a web-based SSO authentication. Server replies with a response like: standard-group single-sign-on-v2 Anyconnect 1584128676139 Login Please complete the authentication process in the AnyConnect Login window. https://SERVER_NAME/+CSCOE+/saml/sp/login?tgname=standard-group&acsamlcap=v2 https://SERVER_NAME/+CSCOE+/saml_ac_login.html acSamlv2Token acSamlv2Error
If either X-AnyConnect-Platform or X-Support-HTTP-Auth is present, then this response is not received, and a 302 redirect to the "standard" auth flow is given. However, this auth flow does not work on my VPN server; presumably it is administratively disabled. Once you get the above response, you can open the URL from sso-v2-login in an HTML viewer and complete the steps. This requires an openconnect front-end that supports the open_webview callback. Eventually you end up at the URL from sso-v2-login-final, and then you can extract the cookie with the name in sso-v2-token-cookie-name (acSamlv2Token, in this case). Neither samlwebcookie nor openconnect-sso, mentioned in #84, worked for me. Signed-off-by: Steven Walter Co-authored-by: Luca Boccassi --- diff --git a/auth.c b/auth.c index f76b4b80..e5d975c5 100644 --- a/auth.c +++ b/auth.c @@ -227,6 +227,8 @@ static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *for opt->_value = (char *)xmlGetProp(xml_node, (unsigned char *)"value"); } else if (!strcmp(input_type, "text")) { opt->type = OC_FORM_OPT_TEXT; + } else if (!strcmp(input_type, "sso")) { + opt->type = OC_FORM_OPT_SSO; } else if (!strcmp(input_type, "password")) { if (!cstp_can_gen_tokencode(vpninfo, form, opt)) opt->type = OC_FORM_OPT_TOKEN; @@ -422,6 +424,10 @@ static int parse_auth_node(struct openconnect_info *vpninfo, xmlNode *xml_node, xmlnode_get_text(xml_node, "banner", &form->banner); xmlnode_get_text(xml_node, "message", &form->message); xmlnode_get_text(xml_node, "error", &form->error); + xmlnode_get_text(xml_node, "sso-v2-login", &vpninfo->sso_login); + xmlnode_get_text(xml_node, "sso-v2-login-final", &vpninfo->sso_login_final); + xmlnode_get_text(xml_node, "sso-v2-token-cookie-name", &vpninfo->sso_token_cookie); + xmlnode_get_text(xml_node, "sso-v2-error-cookie-name", &vpninfo->sso_error_cookie); if (xmlnode_is_named(xml_node, "form")) { @@ -753,7 +759,7 @@ static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char xmlNodePtr *rootp) { xmlDocPtr doc; - xmlNodePtr root, node; + xmlNodePtr root, node, capabilities; doc = xmlNewDoc(XCAST("1.0")); if (!doc) @@ -768,6 +774,8 @@ static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char goto bad; if (!xmlNewProp(root, XCAST("type"), XCAST(type))) goto bad; + if (!xmlNewProp(root, XCAST("aggregate-auth-version"), XCAST("2"))) + goto bad; node = xmlNewTextChild(root, NULL, XCAST("version"), XCAST(vpninfo->version_string ? : openconnect_version_str)); @@ -786,6 +794,21 @@ static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char goto bad; } + capabilities = xmlNewNode(NULL, XCAST("capabilities")); + if (!capabilities) + goto bad; + capabilities = xmlAddChild(root, capabilities); + if (!capabilities) + goto bad; + + node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("single-sign-on")); + if (!node) + goto bad; + + node = xmlNewTextChild(capabilities, NULL, XCAST("auth-method"), XCAST("single-sign-on-v2")); + if (!node) + goto bad; + *rootp = root; return doc; diff --git a/library.c b/library.c index 77e47126..9e21cab9 100644 --- a/library.c +++ b/library.c @@ -1572,6 +1572,13 @@ retry: int second_auth = opt->flags & OC_FORM_OPT_SECOND_AUTH; opt->flags &= ~OC_FORM_OPT_IGNORE; + if (opt->type == OC_FORM_OPT_SSO && vpninfo->open_webview) { + vpninfo->sso_cookie_value = NULL; + vpninfo->open_webview(vpninfo, vpninfo->sso_login, NULL); + opt->_value = vpninfo->sso_cookie_value; + vpninfo->sso_cookie_value = NULL; + } + if (!auth_choice || (opt->type != OC_FORM_OPT_TEXT && opt->type != OC_FORM_OPT_PASSWORD)) continue; @@ -1610,11 +1617,28 @@ retry: void openconnect_set_webview_callback(struct openconnect_info *vpninfo, openconnect_open_webview_vfn webview_fn) { - return; + vpninfo->open_webview = webview_fn; + vpninfo->try_http_auth = 0; } int openconnect_webview_load_changed(struct openconnect_info *vpninfo, const struct oc_webview_result *result) { - return -EOPNOTSUPP; + int i; + + // If we're not at the final URI, tell the webview to keep going + if (strcmp(result->uri, vpninfo->sso_login_final)) { + return 1; + } + + for (i=0; result->cookies[i] != NULL; i+=2) { + if (!strcmp(vpninfo->sso_token_cookie, result->cookies[i])) + { + vpninfo->sso_cookie_value = strdup(result->cookies[i+1]); + break; + } + } + + // Tell the webview to terminate + return 0; } diff --git a/openconnect-internal.h b/openconnect-internal.h index 5db87638..8b644cbe 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -762,10 +762,17 @@ struct openconnect_info { DELAY_CLOSE_IMMEDIATE_CALLBACK, } delay_close; /* Delay close of mainloop */ + char *sso_login; + char *sso_login_final; + char *sso_token_cookie; + char *sso_error_cookie; + char *sso_cookie_value; + int verbose; void *cbdata; openconnect_validate_peer_cert_vfn validate_peer_cert; openconnect_write_new_config_vfn write_new_config; + openconnect_open_webview_vfn open_webview; 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 5abb0512..168400dd 100644 --- a/openconnect.h +++ b/openconnect.h @@ -214,6 +214,7 @@ struct oc_vpn_proto { #define OC_FORM_OPT_SELECT 3 #define OC_FORM_OPT_HIDDEN 4 #define OC_FORM_OPT_TOKEN 5 +#define OC_FORM_OPT_SSO 6 #define OC_FORM_RESULT_ERR -1 #define OC_FORM_RESULT_OK 0