From: David Woodhouse Date: Fri, 9 Apr 2021 12:09:11 +0000 (+0100) Subject: Add 'proto' integer value to struct vpn_proto X-Git-Tag: v8.20~302 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=7b6103ee36ee892254ed4dca03eeb80e14e28679;p=users%2Fdwmw2%2Fopenconnect.git Add 'proto' integer value to struct vpn_proto Despite making no mention of this in the commit message, commit 423449253 ("rename (resp_buf, form_buf) → (req_buf, resp_buf) in f5.c and fortinet.c") added a 'flavor' field to a bunch of functions in auth-html.c and passed it through from the protocol code to indicate some protocol-specific quirks for form parsing. Until then, we had mostly structured the code so that there weren't many cases where 'core' code needed to behave differently depending on the protocol. There was only one case, in fact, to handle the stupid Pulse ESP address family bug. That one *used* to check which protocol was in use by comparing proto->udp_send_probes with &oncp_esp_send_probes. So, add an integer 'proto' field to struct vpn_proto so it can be checked without a strcmp. Remove the now-redundant 'flavor' field from all those functions in auth-html.c since it's now in the vpninfo, and make the ESP code check that for NC|PULSE too instead of the trick of checking the proto->udp_send_probes function. Which was kind of icky. Signed-off-by: David Woodhouse --- diff --git a/auth-html.c b/auth-html.c index b5c150e5..9bf268e0 100644 --- a/auth-html.c +++ b/auth-html.c @@ -54,7 +54,7 @@ xmlNodePtr find_form_node(xmlDocPtr doc) } int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form, - xmlNodePtr node, const char *submit_button, int flavor, + xmlNodePtr node, const char *submit_button, int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt)) { char *type = (char *)xmlGetProp(node, (unsigned char *)"type"), *style = (char *)xmlGetProp(node, (unsigned char *)"style"); @@ -93,12 +93,12 @@ int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form ret = -ENOMEM; goto out; } - if (flavor == FORM_FLAVOR_JUNIPER && + if (vpninfo->proto->proto == PROTO_NC && !strcmp(form->auth_id, "loginForm") && !strcmp(opt->name, "VerificationCode") && can_gen_tokencode && !can_gen_tokencode(vpninfo, form, opt)) opt->type = OC_FORM_OPT_TOKEN; - } else if (flavor == FORM_FLAVOR_JUNIPER && !strcasecmp(type, "submit")) { + } else if (vpninfo->proto->proto == PROTO_NC && !strcasecmp(type, "submit")) { xmlnode_get_prop(node, "name", &opt->name); if (opt->name && submit_button && (!strcmp(opt->name, submit_button) || @@ -149,7 +149,7 @@ int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form } int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *form, - xmlNodePtr node, int flavor) + xmlNodePtr node) { xmlNodePtr child; struct oc_form_opt_select *opt; @@ -162,8 +162,8 @@ int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *for xmlnode_get_prop(node, "name", &opt->form.name); opt->form.label = strdup(opt->form.name); opt->form.type = OC_FORM_OPT_SELECT; - if ((flavor == FORM_FLAVOR_JUNIPER && !strcmp(opt->form.name, "realm")) || - (flavor == FORM_FLAVOR_F5 && !strcmp(opt->form.name, "domain"))) + if ((vpninfo->proto->proto == PROTO_NC && !strcmp(opt->form.name, "realm")) || + (vpninfo->proto->proto == PROTO_F5 && !strcmp(opt->form.name, "domain"))) form->authgroup_opt = opt; for (child = node->children; child; child = child->next) { @@ -196,7 +196,7 @@ int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *for } struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo, - xmlNodePtr node, const char *submit_button, int flavor, + xmlNodePtr node, const char *submit_button, int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt)) { struct oc_auth_form *form = calloc(1, sizeof(*form)); @@ -215,12 +215,12 @@ struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo, return NULL; } - if (flavor == FORM_FLAVOR_JUNIPER) { + if (vpninfo->proto->proto == PROTO_NC) { /* XX: some forms have 'id', but no 'name' */ if (!xmlnode_get_prop(node, "name", &form->auth_id) || !xmlnode_get_prop(node, "id", &form->auth_id)) form->banner = strdup(form->auth_id); - } else if (flavor == FORM_FLAVOR_F5) + } else if (vpninfo->proto->proto == PROTO_F5) xmlnode_get_prop(node, "id", &form->auth_id); /* XX: fallback auth_id (since other functions expect it to exist) */ @@ -232,13 +232,13 @@ struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo, continue; if (!strcasecmp((char *)child->name, "input")) - parse_input_node(vpninfo, form, child, submit_button, flavor, can_gen_tokencode); + parse_input_node(vpninfo, form, child, submit_button, can_gen_tokencode); else if (!strcasecmp((char *)child->name, "select")) { - parse_select_node(vpninfo, form, child, flavor); + parse_select_node(vpninfo, form, child); /* Skip its children */ while (child->children) child = child->last; - } else if (flavor == FORM_FLAVOR_F5 + } else if (vpninfo->proto->proto == PROTO_F5 && !strcasecmp((char *)child->name, "td")) { char *id = (char *)xmlGetProp(child, (unsigned char *)"id"); @@ -257,7 +257,7 @@ struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo, } free(id); - } else if (flavor == FORM_FLAVOR_JUNIPER && + } else if (vpninfo->proto->proto == PROTO_NC && !strcasecmp((char *)child->name, "textarea")) { /* display the post sign-in message, if any */ diff --git a/auth-juniper.c b/auth-juniper.c index 1dc6d205..8f30a3ba 100644 --- a/auth-juniper.c +++ b/auth-juniper.c @@ -519,14 +519,14 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo) _("Encountered form with no 'name' or 'id'\n")); goto dump_form; } else if (form_name && !strcmp(form_name, "frmLogin")) { - form = parse_form_node(vpninfo, node, "btnSubmit", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode); + form = parse_form_node(vpninfo, node, "btnSubmit", oncp_can_gen_tokencode); } else if (form_id && !strcmp(form_id, "loginForm")) { - form = parse_form_node(vpninfo, node, "submitButton", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode); + form = parse_form_node(vpninfo, node, "submitButton", oncp_can_gen_tokencode); } else if ((form_name && !strcmp(form_name, "frmDefender")) || (form_name && !strcmp(form_name, "frmNextToken"))) { - form = parse_form_node(vpninfo, node, "btnAction", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode); + form = parse_form_node(vpninfo, node, "btnAction", oncp_can_gen_tokencode); } else if (form_name && !strcmp(form_name, "frmConfirmation")) { - form = parse_form_node(vpninfo, node, "btnContinue", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode); + form = parse_form_node(vpninfo, node, "btnContinue", oncp_can_gen_tokencode); if (!form) { ret = -EINVAL; break; @@ -537,10 +537,10 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo) form = parse_roles_form_node(node); role_select = 1; } else if (form_name && !strcmp(form_name, "frmTotpToken")) { - form = parse_form_node(vpninfo, node, "totpactionEnter", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode); + form = parse_form_node(vpninfo, node, "totpactionEnter", oncp_can_gen_tokencode); } else if ((form_name && !strcmp(form_name, "hiddenform")) || (form_id && !strcmp(form_id, "formSAMLSSO"))) { - form = parse_form_node(vpninfo, node, "submit", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode); + form = parse_form_node(vpninfo, node, "submit", oncp_can_gen_tokencode); } else { char *form_action = (char *)xmlGetProp(node, (unsigned char *)"action"); if (form_action && strstr(form_action, "remediate.cgi")) { diff --git a/esp.c b/esp.c index 03f38343..385110c1 100644 --- a/esp.c +++ b/esp.c @@ -306,7 +306,8 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) if (!this) break; - if (vpninfo->proto->udp_send_probes == oncp_esp_send_probes) { + if (vpninfo->proto->proto == PROTO_NC || + vpninfo->proto->proto == PROTO_PULSE) { uint8_t dontsend; /* Pulse/NC can only accept ESP of the same protocol as the one diff --git a/f5.c b/f5.c index bbf8e48d..7023d99f 100644 --- a/f5.c +++ b/f5.c @@ -148,7 +148,7 @@ int f5_obtain_cookie(struct openconnect_info *vpninfo) if ((form = plain_auth_form()) == NULL) goto nomem; } else { - form = parse_form_node(vpninfo, node, NULL, FORM_FLAVOR_F5, NULL); + form = parse_form_node(vpninfo, node, NULL, NULL); if (form_order==1 && (xmlnode_get_prop(node, "id", &form_id) || strcmp(form_id, "auth_form"))) { vpn_progress(vpninfo, PRG_ERR, _("Unknown form ID '%s' (expected 'auth_form')\n"), form_id); diff --git a/library.c b/library.c index cd407d4b..4aca1d59 100644 --- a/library.c +++ b/library.c @@ -116,6 +116,7 @@ static const struct vpn_proto openconnect_protos[] = { .name = "anyconnect", .pretty_name = N_("Cisco AnyConnect or openconnect"), .description = N_("Compatible with Cisco AnyConnect SSL VPN, as well as ocserv"), + .proto = PROTO_ANYCONNECT, .flags = OC_PROTO_PROXY | OC_PROTO_CSD | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_AUTH_STOKEN, .vpn_close_session = cstp_bye, .tcp_connect = cstp_connect, @@ -134,6 +135,7 @@ static const struct vpn_proto openconnect_protos[] = { .name = "nc", .pretty_name = N_("Juniper Network Connect"), .description = N_("Compatible with Juniper Network Connect"), + .proto = PROTO_NC, .flags = OC_PROTO_PROXY | OC_PROTO_CSD | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_PERIODIC_TROJAN, .vpn_close_session = oncp_bye, .tcp_connect = oncp_connect, @@ -154,6 +156,7 @@ static const struct vpn_proto openconnect_protos[] = { .name = "gp", .pretty_name = N_("Palo Alto Networks GlobalProtect"), .description = N_("Compatible with Palo Alto Networks (PAN) GlobalProtect SSL VPN"), + .proto = PROTO_GPST, .flags = OC_PROTO_PROXY | OC_PROTO_CSD | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_AUTH_STOKEN | OC_PROTO_PERIODIC_TROJAN, .vpn_close_session = gpst_bye, .tcp_connect = gpst_setup, @@ -173,6 +176,7 @@ static const struct vpn_proto openconnect_protos[] = { .name = "pulse", .pretty_name = N_("Pulse Connect Secure"), .description = N_("Compatible with Pulse Connect Secure SSL VPN"), + .proto = PROTO_PULSE, .flags = OC_PROTO_PROXY | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_AUTH_STOKEN, .vpn_close_session = pulse_bye, .tcp_connect = pulse_connect, @@ -192,6 +196,7 @@ static const struct vpn_proto openconnect_protos[] = { .name = "f5", .pretty_name = N_("F5 BIG-IP SSL VPN"), .description = N_("Compatible with F5 BIG-IP SSL VPN"), + .proto = PROTO_F5, .flags = OC_PROTO_PROXY | OC_PROTO_AUTH_CERT, .vpn_close_session = f5_bye, .tcp_connect = f5_connect, @@ -212,6 +217,7 @@ static const struct vpn_proto openconnect_protos[] = { .name = "fortinet", .pretty_name = N_("Fortinet SSL VPN"), .description = N_("Compatible with FortiGate SSL VPN"), + .proto = PROTO_FORTINET, .flags = OC_PROTO_PROXY | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_AUTH_STOKEN, .vpn_close_session = fortinet_bye, .tcp_connect = fortinet_connect, @@ -232,6 +238,7 @@ static const struct vpn_proto openconnect_protos[] = { .name = "nullppp", .pretty_name = N_("PPP over TLS"), .description = N_("Unauthenticated RFC1661/RFC1662 PPP over TLS, for testing"), + .proto = PROTO_NULLPPP, .flags = OC_PROTO_PROXY | OC_PROTO_HIDDEN, .tcp_connect = nullppp_connect, .tcp_mainloop = nullppp_mainloop, diff --git a/openconnect-internal.h b/openconnect-internal.h index 93b30c64..ca77770a 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -193,10 +193,14 @@ struct pkt { #define DTLS_CONNECTING 4 /* ESP probe received; must tell server */ #define DTLS_CONNECTED 5 /* Server informed and should be sending ESP */ -/* Flavors of HTML forms to screen-scrape */ -#define FORM_FLAVOR_JUNIPER 1 -#define FORM_FLAVOR_F5 2 -#define FORM_FLAVOR_FORTINET 3 +/* Not to be confused with OC_PROTO_xxx flags which are library-visible */ +#define PROTO_ANYCONNECT 0 +#define PROTO_NC 1 +#define PROTO_GPST 2 +#define PROTO_PULSE 3 +#define PROTO_F5 4 +#define PROTO_FORTINET 5 +#define PROTO_NULLPPP 6 /* All supported PPP packet framings/encapsulations */ #define PPP_ENCAP_RFC1661 1 /* Plain/synchronous/pre-framed PPP (RFC1661) */ @@ -304,6 +308,7 @@ struct vpn_proto { const char *description; const char *secure_cookie; const char *udp_protocol; + int proto; unsigned int flags; int (*vpn_close_session)(struct openconnect_info *vpninfo, const char *reason); @@ -977,17 +982,17 @@ int decompress_and_queue_packet(struct openconnect_info *vpninfo, int compr_type unsigned char *buf, int len); int compress_packet(struct openconnect_info *vpninfo, int compr_type, struct pkt *this); -/* html-auth.c */ +/* auth-html.c */ xmlNodePtr htmlnode_next(xmlNodePtr top, xmlNodePtr node); xmlNodePtr htmlnode_dive(xmlNodePtr top, xmlNodePtr node); xmlNodePtr find_form_node(xmlDocPtr doc); int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form, - xmlNodePtr node, const char *submit_button, int flavor, + xmlNodePtr node, const char *submit_button, int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt)); int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *form, - xmlNodePtr node, int flavor); + xmlNodePtr node); struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo, - xmlNodePtr node, const char *submit_button, int flavor, + xmlNodePtr node, const char *submit_button, int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt)); /* auth-juniper.c */