From: David Woodhouse Date: Thu, 22 Apr 2021 15:46:17 +0000 (+0100) Subject: Rework cstp_options and ip_info handling X-Git-Tag: v8.20~259 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=3d845bc9bf62b3e816e0d2fd72970ef33964a191;p=users%2Fdwmw2%2Fopenconnect.git Rework cstp_options and ip_info handling Build up the new opt list and ip_info first, then have a function to "install" them into the vpninfo, doing the sanity check at that point. Should make the whole thing a little bit less vile. Signed-off-by: David Woodhouse --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f3f981dd..22d8ab24 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -507,8 +507,7 @@ static-analyzer/GnuTLS/Fedora: expire_in: 1 week when: always paths: - - tests/*.log - - openconnect-installer.exe + - scan-build-src/* static-analyzer/OpenSSL/Fedora: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:buildenv-fedora diff --git a/cstp.c b/cstp.c index 17d0f914..7e7916a6 100644 --- a/cstp.c +++ b/cstp.c @@ -208,44 +208,6 @@ static int parse_hex_val(const char *str, unsigned char *storage, unsigned int m return len/2; } -int check_address_sanity(struct openconnect_info *vpninfo, const char *old_addr, const char *old_netmask, const char *old_addr6, const char *old_netmask6) -{ - if (old_addr) { - if (!vpninfo->ip_info.addr || strcmp(old_addr, vpninfo->ip_info.addr)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different Legacy IP address (%s != %s)\n"), - vpninfo->ip_info.addr, old_addr); - /* EPERM means that the retry loop will abort and won't keep trying. */ - return -EPERM; - } - } - if (old_netmask) { - if (!vpninfo->ip_info.netmask || strcmp(old_netmask, vpninfo->ip_info.netmask)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different Legacy IP netmask (%s != %s)\n"), - vpninfo->ip_info.netmask, old_netmask); - return -EPERM; - } - } - if (old_addr6) { - if (!vpninfo->ip_info.addr6 || strcmp(old_addr6, vpninfo->ip_info.addr6)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different IPv6 address (%s != %s)\n"), - vpninfo->ip_info.addr6, old_addr6); - return -EPERM; - } - } - if (old_netmask6) { - if (!vpninfo->ip_info.netmask6 || strcmp(old_netmask6, vpninfo->ip_info.netmask6)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different IPv6 netmask (%s != %s)\n"), - vpninfo->ip_info.netmask6, old_netmask6); - return -EPERM; - } - } - return 0; -} - static int start_cstp_connection(struct openconnect_info *vpninfo) { struct oc_text_buf *reqbuf; @@ -253,27 +215,11 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) int i; int dtls_secret_set = 0; int retried = 0, sessid_found = 0; - struct oc_vpn_option **next_dtls_option = &vpninfo->dtls_options; - struct oc_vpn_option **next_cstp_option = &vpninfo->cstp_options; - struct oc_vpn_option *old_cstp_opts = vpninfo->cstp_options; - struct oc_vpn_option *old_dtls_opts = vpninfo->dtls_options; const char *old_addr = vpninfo->ip_info.addr; - const char *old_netmask = vpninfo->ip_info.netmask; const char *old_addr6 = vpninfo->ip_info.addr6; - const char *old_netmask6 = vpninfo->ip_info.netmask6; + const char *banner = NULL; int base_mtu = 0, mtu = 0; - /* Clear old options which will be overwritten */ - vpninfo->ip_info.addr = vpninfo->ip_info.netmask = NULL; - vpninfo->ip_info.addr6 = vpninfo->ip_info.netmask6 = NULL; - vpninfo->cstp_options = vpninfo->dtls_options = NULL; - vpninfo->ip_info.domain = vpninfo->ip_info.proxy_pac = NULL; - vpninfo->banner = NULL; - - for (i = 0; i < 3; i++) - vpninfo->ip_info.dns[i] = vpninfo->ip_info.nbns[i] = NULL; - free_split_routes(vpninfo); - retry: calculate_dtls_mtu(vpninfo, &base_mtu, &mtu); vpninfo->cstp_basemtu = base_mtu; @@ -417,12 +363,19 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) vpninfo->cstp_compr = vpninfo->dtls_compr = 0; mtu = 0; - while ((i = vpninfo->ssl_gets(vpninfo, buf, sizeof(buf)))) { + struct oc_vpn_option *new_cstp_opts = NULL; + struct oc_vpn_option *new_dtls_opts = NULL; + struct oc_vpn_option **next_dtls_option = &new_dtls_opts; + struct oc_vpn_option **next_cstp_option = &new_cstp_opts; + struct oc_ip_info new_ip_info = {}; + int ret = 0; + + while ((ret = vpninfo->ssl_gets(vpninfo, buf, sizeof(buf)))) { struct oc_vpn_option *new_option; char *colon; - if (i < 0) - return i; + if (ret < 0) + goto err; colon = strchr(buf, ':'); if (!colon) @@ -441,7 +394,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) new_option = malloc(sizeof(*new_option)); if (!new_option) { vpn_progress(vpninfo, PRG_ERR, _("No memory for options\n")); - return -ENOMEM; + ret = -ENOMEM; + goto err; } new_option->option = strdup(buf); new_option->value = strdup(colon); @@ -452,7 +406,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) free(new_option->option); free(new_option->value); free(new_option); - return -ENOMEM; + ret = -ENOMEM; + goto err; } /* This contains the whole document, including the webvpn cookie. */ @@ -479,7 +434,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) _("X-DTLS-Session-ID not 64 characters; is: \"%s\"\n"), colon); vpninfo->dtls_attempt_period = 0; - return -EINVAL; + ret = -EINVAL; + goto err; } sessid_found = 1; @@ -496,7 +452,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) _("X-DTLS-Session-ID is invalid; is: \"%s\"\n"), colon); vpninfo->dtls_attempt_period = 0; - return -EINVAL; + ret = -EINVAL; + goto err; } vpninfo->dtls_app_id_size = vsize; @@ -513,7 +470,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) vpn_progress(vpninfo, PRG_ERR, _("Unknown DTLS-Content-Encoding %s\n"), colon); - return -EINVAL; + ret = -EINVAL; + goto err; } } else if (!strcmp(buf + i, "CipherSuite")) { /* Remember if it came from a 'X-DTLS12-CipherSuite:' header */ @@ -587,7 +545,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) vpn_progress(vpninfo, PRG_ERR, _("Unknown CSTP-Content-Encoding %s\n"), colon); - return -EINVAL; + ret= -EINVAL; + goto err; } } else if (!strcmp(buf + 7, "Base-MTU")) { vpninfo->cstp_basemtu = atol(colon); @@ -599,92 +558,89 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) if (!strcmp(colon, "true")) vpninfo->is_dyndns = 1; } else if (!strcmp(buf + 7, "Address-IP6")) { - vpninfo->ip_info.netmask6 = new_option->value; + new_ip_info.netmask6 = new_option->value; } else if (!strcmp(buf + 7, "Address")) { if (strchr(new_option->value, ':')) { if (!vpninfo->disable_ipv6) - vpninfo->ip_info.addr6 = new_option->value; + new_ip_info.addr6 = new_option->value; } else - vpninfo->ip_info.addr = new_option->value; + new_ip_info.addr = new_option->value; } else if (!strcmp(buf + 7, "Netmask")) { if (strchr(new_option->value, ':')) { if (!vpninfo->disable_ipv6) - vpninfo->ip_info.netmask6 = new_option->value; + new_ip_info.netmask6 = new_option->value; } else - vpninfo->ip_info.netmask = new_option->value; + new_ip_info.netmask = new_option->value; } else if (!strcmp(buf + 7, "DNS") || !strcmp(buf + 7, "DNS-IP6")) { int j; for (j = 0; j < 3; j++) { - if (!vpninfo->ip_info.dns[j]) { - vpninfo->ip_info.dns[j] = new_option->value; + if (!new_ip_info.dns[j]) { + new_ip_info.dns[j] = new_option->value; break; } } } else if (!strcmp(buf + 7, "NBNS")) { int j; for (j = 0; j < 3; j++) { - if (!vpninfo->ip_info.nbns[j]) { - vpninfo->ip_info.nbns[j] = new_option->value; + if (!new_ip_info.nbns[j]) { + new_ip_info.nbns[j] = new_option->value; break; } } } else if (!strcmp(buf + 7, "Default-Domain")) { - vpninfo->ip_info.domain = new_option->value; + new_ip_info.domain = new_option->value; } else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) { - vpninfo->ip_info.proxy_pac = new_option->value; + new_ip_info.proxy_pac = new_option->value; } else if (!strcmp(buf + 7, "Banner")) { - vpninfo->banner = new_option->value; + banner = new_option->value; } else if (!strcmp(buf + 7, "Split-DNS")) { struct oc_split_include *dns = malloc(sizeof(*dns)); if (!dns) continue; dns->route = new_option->value; - dns->next = vpninfo->ip_info.split_dns; - vpninfo->ip_info.split_dns = dns; + dns->next = new_ip_info.split_dns; + new_ip_info.split_dns = dns; } else if (!strcmp(buf + 7, "Split-Include") || !strcmp(buf + 7, "Split-Include-IP6")) { struct oc_split_include *inc = malloc(sizeof(*inc)); if (!inc) continue; inc->route = new_option->value; - inc->next = vpninfo->ip_info.split_includes; - vpninfo->ip_info.split_includes = inc; + inc->next = new_ip_info.split_includes; + new_ip_info.split_includes = inc; } else if (!strcmp(buf + 7, "Split-Exclude") || !strcmp(buf + 7, "Split-Exclude-IP6")) { struct oc_split_include *exc = malloc(sizeof(*exc)); if (!exc) continue; exc->route = new_option->value; - exc->next = vpninfo->ip_info.split_excludes; - vpninfo->ip_info.split_excludes = exc; + exc->next = new_ip_info.split_excludes; + new_ip_info.split_excludes = exc; } } if (!mtu) { vpn_progress(vpninfo, PRG_ERR, _("No MTU received. Aborting\n")); - return -EINVAL; - } - vpninfo->ip_info.mtu = mtu; - - if (!vpninfo->ip_info.addr && !vpninfo->ip_info.addr6 && - !vpninfo->ip_info.netmask6) { - vpn_progress(vpninfo, PRG_ERR, - _("No IP address received. Aborting\n")); - return -EINVAL; + ret = -EINVAL; + goto err; } - if (mtu < 1280 && - (vpninfo->ip_info.addr6 || vpninfo->ip_info.netmask6)) { - vpn_progress(vpninfo, PRG_ERR, - _("IPv6 configuration received but MTU %d is too small.\n"), - mtu); + new_ip_info.mtu = mtu; + + ret = install_vpn_opts(vpninfo, new_cstp_opts, &new_ip_info); + if (ret) { + err: + free_optlist(new_cstp_opts); + free_optlist(new_dtls_opts); + free_split_routes(&new_ip_info); + return ret; } - i = check_address_sanity(vpninfo, old_addr, old_netmask, old_addr6, old_netmask6); - if (i) - return i; + /* DTLS opts are a special case. Perhaps should have been in the + * CSTP opts list anyway. This all very Cisco-specific. */ + free_optlist(vpninfo->dtls_options); + vpninfo->dtls_options = new_dtls_opts; + vpninfo->banner = banner; - free_optlist(old_dtls_opts); - free_optlist(old_cstp_opts); vpn_progress(vpninfo, PRG_INFO, _("CSTP connected. DPD %d, Keepalive %d\n"), vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive); diff --git a/f5.c b/f5.c index 4035f4ba..dda53d7c 100644 --- a/f5.c +++ b/f5.c @@ -308,7 +308,7 @@ static int parse_options(struct openconnect_info *vpninfo, char *buf, int len, { xmlNode *fav_node, *obj_node, *xml_node; xmlDocPtr xml_doc; - int ret = 0, ii, n_dns = 0, n_nbns = 0, default_route = 0, dtls = 0, dtls_port = 0; + int ret = 0, n_dns = 0, n_nbns = 0, default_route = 0, dtls = 0, dtls_port = 0; char *s = NULL; struct oc_text_buf *domains = NULL; @@ -332,14 +332,8 @@ static int parse_options(struct openconnect_info *vpninfo, char *buf, int len, if (!xmlnode_is_named(obj_node, "object")) goto err; - /* Clear old options which will be overwritten */ - vpninfo->ip_info.addr = vpninfo->ip_info.netmask = NULL; - vpninfo->ip_info.addr6 = vpninfo->ip_info.netmask6 = NULL; - vpninfo->ip_info.domain = NULL; - vpninfo->cstp_options = NULL; - for (ii = 0; ii < 3; ii++) - vpninfo->ip_info.dns[ii] = vpninfo->ip_info.nbns[ii] = NULL; - free_split_routes(vpninfo); + struct oc_vpn_option *new_opts = NULL; + struct oc_ip_info new_ip_info = {}; domains = buf_alloc(); @@ -382,14 +376,14 @@ static int parse_options(struct openconnect_info *vpninfo, char *buf, int len, s = (char *)xmlNodeGetContent(xml_node); if (s && *s) { vpn_progress(vpninfo, PRG_INFO, _("Got DNS server %s\n"), s); - if (n_dns < 3) vpninfo->ip_info.dns[n_dns++] = add_option_steal(vpninfo, "DNS", &s); + if (n_dns < 3) new_ip_info.dns[n_dns++] = add_option_steal(&new_opts, "DNS", &s); } } else if (!strncmp((char *)xml_node->name, "WINS", 4) && isdigit(xml_node->name[4])) { free(s); s = (char *)xmlNodeGetContent(xml_node); if (s && *s) { vpn_progress(vpninfo, PRG_INFO, _("Got WINS/NBNS server %s\n"), s); - if (n_nbns < 3) vpninfo->ip_info.dns[n_nbns++] = add_option_steal(vpninfo, "WINS", &s); + if (n_nbns < 3) new_ip_info.dns[n_nbns++] = add_option_steal(&new_opts, "WINS", &s); } } else if (!strncmp((char *)xml_node->name, "DNSSuffix", 9) && isdigit(xml_node->name[9])) { free(s); @@ -421,14 +415,14 @@ static int parse_options(struct openconnect_info *vpninfo, char *buf, int len, struct oc_split_include *inc = malloc(sizeof(*inc)); if (!inc) continue; - inc->route = add_option_dup(vpninfo, option, word, -1); + inc->route = add_option_dup(&new_opts, option, word, -1); if (is_exclude) { - inc->next = vpninfo->ip_info.split_excludes; - vpninfo->ip_info.split_excludes = inc; + inc->next = new_ip_info.split_excludes; + new_ip_info.split_excludes = inc; vpn_progress(vpninfo, PRG_INFO, _("Got split exclude route %s\n"), word); } else { - inc->next = vpninfo->ip_info.split_includes; - vpninfo->ip_info.split_includes = inc; + inc->next = new_ip_info.split_includes; + new_ip_info.split_includes = inc; vpn_progress(vpninfo, PRG_INFO, _("Got split include route %s\n"), word); } } @@ -454,16 +448,20 @@ static int parse_options(struct openconnect_info *vpninfo, char *buf, int len, } } if (default_route && *ipv4) - vpninfo->ip_info.netmask = add_option_dup(vpninfo, "netmask", "0.0.0.0", -1); + new_ip_info.netmask = add_option_dup(&new_opts, "netmask", "0.0.0.0", -1); if (default_route && *ipv6) - vpninfo->ip_info.netmask6 = add_option_dup(vpninfo, "netmask6", "::/0", -1); + new_ip_info.netmask6 = add_option_dup(&new_opts, "netmask6", "::/0", -1); if (buf_error(domains) == 0 && domains->pos > 0) { domains->data[domains->pos-1] = '\0'; - vpninfo->ip_info.domain = add_option_steal(vpninfo, "search", &domains->data); + new_ip_info.domain = add_option_steal(&new_opts, "search", &domains->data); } buf_free(domains); - if ( (*ipv4 < 1 && *ipv6 < 1) || !*ur_z || !*session_id) { + ret = install_vpn_opts(vpninfo, new_opts, &new_ip_info); + + if (ret || (*ipv4 < 1 && *ipv6 < 1) || !*ur_z || !*session_id) { + free_optlist(new_opts); + free_split_routes(&new_ip_info); err: vpn_progress(vpninfo, PRG_ERR, _("Failed to find VPN options\n")); @@ -513,11 +511,6 @@ static int f5_configure(struct openconnect_info *vpninfo) char *sid = NULL, *ur_z = NULL; int ipv4 = -1, ipv6 = -1, hdlc = 0; char *res_buf = NULL; - struct oc_vpn_option *old_cstp_opts = vpninfo->cstp_options; - const char *old_addr = vpninfo->ip_info.addr; - const char *old_netmask = vpninfo->ip_info.netmask; - const char *old_addr6 = vpninfo->ip_info.addr6; - const char *old_netmask6 = vpninfo->ip_info.netmask6; if (!vpninfo->cookies) { /* XX: This will happen if authentication was separate/external */ @@ -576,17 +569,6 @@ static int f5_configure(struct openconnect_info *vpninfo) if (ipv6 == -1) ipv6 = 0; - /* The addresses set in ip_info only after they're negotiated in PPP. - * If they were there before a reconnect, preserve them. */ - if (old_addr) - vpninfo->ip_info.addr = add_option_dup(vpninfo, "ppp_addr", old_addr, -1); - if (old_addr6) - vpninfo->ip_info.addr6 = add_option_dup(vpninfo, "ppp_addr6", old_addr6, -1); - - ret = check_address_sanity(vpninfo, old_addr, old_netmask, old_addr6, old_netmask6); - if (ret < 0) - goto out; - /* XX: This buffer is used to initiate the connection over either TLS or DTLS. * Cookies are not needed for it to succeed, and can potentially grow without bound, * which would make it too big to fit in a single DTLS packet (ick, HTTP over DTLS). @@ -619,8 +601,6 @@ static int f5_configure(struct openconnect_info *vpninfo) ret = openconnect_ppp_new(vpninfo, hdlc ? PPP_ENCAP_F5_HDLC : PPP_ENCAP_F5, ipv4, ipv6); out: - if (old_cstp_opts != vpninfo->cstp_options) - free_optlist(old_cstp_opts); free(res_buf); free(profile_params); free(sid); diff --git a/fortinet.c b/fortinet.c index e51994de..bc5ef8ab 100644 --- a/fortinet.c +++ b/fortinet.c @@ -278,12 +278,11 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo) */ -static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf, int len, - int *ipv4, int *ipv6) +static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf, int len) { xmlNode *xml_node, *x, *x2; xmlDocPtr xml_doc; - int ret = 0, ii, n_dns = 0, default_route = 1; + int ret = 0, n_dns = 0, default_route = 1; char *s = NULL, *s2 = NULL; struct oc_text_buf *domains = NULL; @@ -304,14 +303,8 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf if (!xml_node || !xmlnode_is_named(xml_node, "sslvpn-tunnel")) return -EINVAL; - /* Clear old options which will be overwritten */ - vpninfo->ip_info.addr = vpninfo->ip_info.netmask = NULL; - vpninfo->ip_info.addr6 = vpninfo->ip_info.netmask6 = NULL; - vpninfo->ip_info.domain = NULL; - vpninfo->cstp_options = NULL; - for (ii = 0; ii < 3; ii++) - vpninfo->ip_info.dns[ii] = vpninfo->ip_info.nbns[ii] = NULL; - free_split_routes(vpninfo); + struct oc_vpn_option *new_opts = NULL; + struct oc_ip_info new_ip_info = {}; domains = buf_alloc(); @@ -347,11 +340,10 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf _("Reported platform is %s\n"), platform); } } else if (xmlnode_is_named(xml_node, "ipv4")) { - *ipv4 = 1; for (x = xml_node->children; x; x=x->next) { if (xmlnode_is_named(x, "assigned-addr") && !xmlnode_get_prop(x, "ipv4", &s)) { vpn_progress(vpninfo, PRG_INFO, _("Got legacy IP address %s\n"), s); - vpninfo->ip_info.addr = add_option_steal(vpninfo, "ipaddr", &s); + new_ip_info.addr = add_option_steal(&new_opts, "ipaddr", &s); } else if (xmlnode_is_named(x, "dns")) { if (!xmlnode_get_prop(x, "domain", &s) && s && *s) { vpn_progress(vpninfo, PRG_INFO, _("Got search domain %s\n"), s); @@ -359,7 +351,7 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf } if (!xmlnode_get_prop(x, "ip", &s) && s && *s) { vpn_progress(vpninfo, PRG_INFO, _("Got IPv%d DNS server %s\n"), 4, s); - if (n_dns < 3) vpninfo->ip_info.dns[n_dns++] = add_option_steal(vpninfo, "DNS", &s); + if (n_dns < 3) new_ip_info.dns[n_dns++] = add_option_steal(&new_opts, "DNS", &s); } } else if (xmlnode_is_named(x, "split-dns")) { int ii; @@ -385,14 +377,16 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf if (!route || !inc) { free(route); free(inc); + free_optlist(new_opts); + free_split_routes(&new_ip_info); ret = -ENOMEM; goto out; } snprintf(route, 32, "%s/%s", s, s2); vpn_progress(vpninfo, PRG_INFO, _("Got IPv%d route %s\n"), 4, route); - inc->route = add_option_steal(vpninfo, "split-include", &route); - inc->next = vpninfo->ip_info.split_includes; - vpninfo->ip_info.split_includes = inc; + inc->route = add_option_steal(&new_opts, "split-include", &route); + inc->next = new_ip_info.split_includes; + new_ip_info.split_includes = inc; /* XX: static analyzer doesn't realize that add_option_steal will steal route's reference, so... */ free(route); } @@ -403,17 +397,19 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf } } - if (default_route && *ipv4) - vpninfo->ip_info.netmask = strdup("0.0.0.0"); - if (default_route && *ipv6) - vpninfo->ip_info.netmask6 = strdup("::/0"); + if (default_route && new_ip_info.addr) + new_ip_info.netmask = add_option_dup(&new_opts, "full-netmask", "0.0.0.0", -1); if (buf_error(domains) == 0 && domains->pos > 0) { domains->data[domains->pos-1] = '\0'; - vpninfo->ip_info.domain = add_option_steal(vpninfo, "search", &domains->data); + new_ip_info.domain = add_option_steal(&new_opts, "search", &domains->data); } buf_free(domains); - if (*ipv4 < 1 && *ipv6 < 1) { + ret = install_vpn_opts(vpninfo, new_opts, &new_ip_info); + if (ret) { + free_optlist(new_opts); + free_split_routes(&new_ip_info); + vpn_progress(vpninfo, PRG_ERR, _("Failed to find VPN options\n")); vpn_progress(vpninfo, PRG_DEBUG, @@ -432,7 +428,7 @@ static int fortinet_configure(struct openconnect_info *vpninfo) char *res_buf = NULL; struct oc_text_buf *reqbuf = NULL; struct oc_vpn_option *svpncookie = NULL; - int ret, ipv4 = -1, ipv6 = -1; + int ret; /* XXX: We should use check_address_sanity to verify that addresses haven't changed on a reconnect, except that: @@ -497,15 +493,10 @@ static int fortinet_configure(struct openconnect_info *vpninfo) } else if (ret == 0) goto invalid_cookie; - ret = parse_fortinet_xml_config(vpninfo, res_buf, ret, &ipv4, &ipv6); + ret = parse_fortinet_xml_config(vpninfo, res_buf, ret); if (ret) goto out; - if (ipv4 == -1) - ipv4 = 0; - if (ipv6 == -1) - ipv6 = 0; - reqbuf = vpninfo->ppp_tls_connect_req; if (!reqbuf) reqbuf = buf_alloc(); @@ -534,6 +525,8 @@ static int fortinet_configure(struct openconnect_info *vpninfo) vpninfo->ppp_dtls_connect_req = reqbuf; reqbuf = NULL; + int ipv4 = !!vpninfo->ip_info.addr; + int ipv6 = !!(vpninfo->ip_info.addr6 || vpninfo->ip_info.netmask6); ret = openconnect_ppp_new(vpninfo, PPP_ENCAP_FORTINET, ipv4, ipv6); out: diff --git a/gpst.c b/gpst.c index 39b6daa3..c3d0b5ed 100644 --- a/gpst.c +++ b/gpst.c @@ -348,39 +348,35 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ struct oc_split_include *inc; int split_route_is_default_route = 0; int n_dns = 0, got_esp = 0; + int ret = 0; int ii; if (!xml_node || !xmlnode_is_named(xml_node, "response")) return -EINVAL; - /* Clear old options which will be overwritten */ - vpninfo->ip_info.addr = vpninfo->ip_info.netmask = NULL; - vpninfo->ip_info.addr6 = vpninfo->ip_info.netmask6 = NULL; - vpninfo->ip_info.domain = NULL; - vpninfo->ip_info.mtu = 0; - vpninfo->esp_magic = inet_addr(vpninfo->ip_info.gateway_addr); + + struct oc_vpn_option *new_opts = NULL; + struct oc_ip_info new_ip_info = {}; + + if (vpninfo->ip_info.gateway_addr) + vpninfo->esp_magic = inet_addr(vpninfo->ip_info.gateway_addr); vpninfo->esp_replay_protect = 1; vpninfo->ssl_times.rekey_method = REKEY_NONE; - vpninfo->cstp_options = NULL; - - for (ii = 0; ii < 3; ii++) - vpninfo->ip_info.dns[ii] = vpninfo->ip_info.nbns[ii] = NULL; - free_split_routes(vpninfo); /* Parse config */ for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) { if (!xmlnode_get_val(xml_node, "ip-address", &s)) - vpninfo->ip_info.addr = add_option_steal(vpninfo, "ipaddr", &s); + new_ip_info.addr = add_option_steal(&new_opts, "ipaddr", &s); else if (!xmlnode_get_val(xml_node, "ip-address-v6", &s)) { if (!vpninfo->disable_ipv6) - vpninfo->ip_info.addr6 = add_option_steal(vpninfo, "ipaddr6", &s); + new_ip_info.addr6 = add_option_steal(&new_opts, "ipaddr6", &s); } else if (!xmlnode_get_val(xml_node, "netmask", &deferred_netmask)) { /* XX: GlobalProtect servers always (almost always?) send 255.255.255.255 as their netmask * (a /32 host route), and if they want to include an actual default route (0.0.0.0/0) * they instead put it under . We defer saving the netmask until later. */ } else if (!xmlnode_get_val(xml_node, "mtu", &s)) - vpninfo->ip_info.mtu = atoi(s); + new_ip_info.mtu = atoi(s); else if (!xmlnode_get_val(xml_node, "lifetime", &s)) vpninfo->auth_expiration = time(NULL) + atol(s); else if (!xmlnode_get_val(xml_node, "quarantine", &s) && strcmp(s, "no")) @@ -408,9 +404,9 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ * gateway is meaningless." See esp_send_probes_gp for the * gory details of what this field actually means. */ - if (strcmp(s, vpninfo->ip_info.gateway_addr)) + if (vpninfo->ip_info.gateway_addr && strcmp(s, vpninfo->ip_info.gateway_addr)) vpn_progress(vpninfo, PRG_DEBUG, - _("Gateway address in config XML (%s) differs from external gateway address (%s).\n"), s, vpninfo->ip_info.gateway_addr); + _("Gateway address in config XML (%s) differs from external gateway address (%s).\n"), s, new_ip_info.gateway_addr); vpninfo->esp_magic = inet_addr(s); } else if (!xmlnode_get_val(xml_node, "gw-address-v6", &s)) { /* This is probably used analogously to , but @@ -419,27 +415,28 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ vpn_progress(vpninfo, PRG_ERR, _("WARNING: IPv6 gateway address set in config XML (%s). IPv6 ESP may not yet be functional.\n"), s); } else if (!xmlnode_get_val(xml_node, "connected-gw-ip", &s)) { - if (strcmp(s, vpninfo->ip_info.gateway_addr)) + if (vpninfo->ip_info.gateway_addr && strcmp(s, vpninfo->ip_info.gateway_addr)) vpn_progress(vpninfo, PRG_DEBUG, _("Config XML address (%s) differs from external\n" "gateway address (%s). Please report any this to\n" ", including any problems\n" - "with ESP or other apparent loss of connectivity or performance.\n"), s, vpninfo->ip_info.gateway_addr); + "with ESP or other apparent loss of connectivity or performance.\n"), + s, vpninfo->ip_info.gateway_addr); } else if (xmlnode_is_named(xml_node, "dns-v6") || xmlnode_is_named(xml_node, "dns")) { for (member = xml_node->children; member && n_dns<3; member=member->next) { if (!xmlnode_get_val(member, "member", &s)) { for (ii=0; ii and */ - if (!strcmp(s, vpninfo->ip_info.dns[ii])) + if (!strcmp(s, new_ip_info.dns[ii])) break; if (ii==n_dns) - vpninfo->ip_info.dns[n_dns++] = add_option_steal(vpninfo, "DNS", &s); + new_ip_info.dns[n_dns++] = add_option_steal(&new_opts, "DNS", &s); } } } else if (xmlnode_is_named(xml_node, "wins")) { for (ii=0, member = xml_node->children; member && ii<3; member=member->next) if (!xmlnode_get_val(member, "member", &s)) - vpninfo->ip_info.nbns[ii++] = add_option_steal(vpninfo, "WINS", &s); + new_ip_info.nbns[ii++] = add_option_steal(&new_opts, "WINS", &s); } else if (xmlnode_is_named(xml_node, "dns-suffix")) { struct oc_text_buf *domains = buf_alloc(); for (member = xml_node->children; member; member=member->next) @@ -447,7 +444,7 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ buf_append(domains, "%s ", s); if (buf_error(domains) == 0 && domains->pos > 0) { domains->data[domains->pos-1] = '\0'; - vpninfo->ip_info.domain = add_option_steal(vpninfo, "search", &domains->data); + new_ip_info.domain = add_option_steal(&new_opts, "search", &domains->data); } buf_free(domains); } else if (xmlnode_is_named(xml_node, "access-routes-v6") || xmlnode_is_named(xml_node, "exclude-access-routes-v6") || @@ -464,16 +461,19 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ continue; } - if ((inc = malloc(sizeof(*inc))) == NULL) - return -ENOMEM; + inc = malloc(sizeof(*inc)); + if (!inc) { + ret = -ENOMEM; + goto err; + } if (is_inc) { - inc->route = add_option_steal(vpninfo, "split-include", &s); - inc->next = vpninfo->ip_info.split_includes; - vpninfo->ip_info.split_includes = inc; + inc->route = add_option_steal(&new_opts, "split-include", &s); + inc->next = new_ip_info.split_includes; + new_ip_info.split_includes = inc; } else { - inc->route = add_option_steal(vpninfo, "split-exclude", &s); - inc->next = vpninfo->ip_info.split_excludes; - vpninfo->ip_info.split_excludes = inc; + inc->route = add_option_steal(&new_opts, "split-exclude", &s); + inc->next = new_ip_info.split_excludes; + new_ip_info.split_excludes = inc; } } } @@ -540,25 +540,25 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ return -ENOMEM; /* If the original netmask wasn't /32, add it as a split route */ - if (vpninfo->ip_info.addr && original_netmask) { + if (new_ip_info.addr && original_netmask) { uint32_t nm_bits = inet_addr(original_netmask); if (nm_bits != 0xffffffff) { /* 255.255.255.255 */ struct in_addr net_addr; - inet_aton(vpninfo->ip_info.addr, &net_addr); + inet_aton(new_ip_info.addr, &net_addr); net_addr.s_addr &= nm_bits; /* clear host bits */ if ((inc = malloc(sizeof(*inc))) == NULL || asprintf(&s, "%s/%s", inet_ntoa(net_addr), original_netmask) <= 0) return -ENOMEM; - inc->route = add_option_steal(vpninfo, "split-include", &s); - inc->next = vpninfo->ip_info.split_includes; - vpninfo->ip_info.split_includes = inc; + inc->route = add_option_steal(&new_opts, "split-include", &s); + inc->next = new_ip_info.split_includes; + new_ip_info.split_includes = inc; } } free(original_netmask); } if (deferred_netmask) - vpninfo->ip_info.netmask = add_option_steal(vpninfo, "netmask", &deferred_netmask); + new_ip_info.netmask = add_option_steal(&new_opts, "netmask", &deferred_netmask); /* Set 10-second DPD/keepalive (same as Windows client) unless * overridden with --force-dpd */ @@ -567,7 +567,7 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ vpninfo->ssl_times.keepalive = vpninfo->esp_ssl_fallback = vpninfo->ssl_times.dpd; /* Warn about IPv6 config, if present, and ESP config, if absent */ - if (vpninfo->ip_info.addr6) + if (new_ip_info.addr6) vpn_progress(vpninfo, PRG_ERR, _("GlobalProtect IPv6 support is experimental. Please report results to .\n")); #ifdef HAVE_ESP @@ -577,7 +577,15 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_ #endif free(s); - return 0; + + ret = install_vpn_opts(vpninfo, new_opts, &new_ip_info); + if (ret) { + err: + free_optlist(new_opts); + free_split_routes(&new_ip_info); + } + + return ret; } static int gpst_get_config(struct openconnect_info *vpninfo) @@ -585,13 +593,12 @@ static int gpst_get_config(struct openconnect_info *vpninfo) char *orig_path; int result; struct oc_text_buf *request_body = buf_alloc(); - struct oc_vpn_option *old_cstp_opts = vpninfo->cstp_options; - const char *old_addr = vpninfo->ip_info.addr, *old_netmask = vpninfo->ip_info.netmask; - const char *old_addr6 = vpninfo->ip_info.addr6, *old_netmask6 = vpninfo->ip_info.netmask6; + const char *old_addr = vpninfo->ip_info.addr; + const char *old_addr6 = vpninfo->ip_info.addr6; const char *request_body_type = "application/x-www-form-urlencoded"; const char *method = "POST"; - char *xml_buf=NULL; - vpninfo->cstp_options = NULL; + char *xml_buf = NULL; + /* submit getconfig request */ buf_append(request_body, "client-type=1&protocol-version=p1&app-version=5.1.5-8"); @@ -653,18 +660,8 @@ static int gpst_get_config(struct openconnect_info *vpninfo) no_esp_reason ? "SSL tunnel. " : "ESP tunnel", no_esp_reason ? : ""); /* return -EINVAL; */ } - if (!vpninfo->ip_info.addr && !vpninfo->ip_info.addr6 && - !vpninfo->ip_info.netmask6) { - vpn_progress(vpninfo, PRG_ERR, - _("No IP address received. Aborting\n")); - result = -EINVAL; - goto out; - } - - result = check_address_sanity(vpninfo, old_addr, old_netmask, old_addr6, old_netmask6); out: - free_optlist(old_cstp_opts); buf_free(request_body); free(xml_buf); return result; diff --git a/library.c b/library.c index b0655ba7..0aa0bd4d 100644 --- a/library.c +++ b/library.c @@ -387,7 +387,8 @@ int openconnect_set_version_string(struct openconnect_info *vpninfo, return 0; } -const char *add_option_dup(struct openconnect_info *vpninfo, const char *opt, +const char *add_option_dup(struct oc_vpn_option **list, + const char *opt, const char *val, int val_len) { const char *ret; @@ -398,12 +399,13 @@ const char *add_option_dup(struct openconnect_info *vpninfo, const char *opt, else new_val = strdup(val); - ret = add_option_steal(vpninfo, opt, &new_val); + ret = add_option_steal(list, opt, &new_val); free(new_val); return ret; } -const char *add_option_steal(struct openconnect_info *vpninfo, const char *opt, char **val) +const char *add_option_steal(struct oc_vpn_option **list, + const char *opt, char **val) { struct oc_vpn_option *new = malloc(sizeof(*new)); if (!new) @@ -416,21 +418,21 @@ const char *add_option_steal(struct openconnect_info *vpninfo, const char *opt, } new->value = *val; *val = NULL; - new->next = vpninfo->cstp_options; - vpninfo->cstp_options = new; + new->next = *list; + *list = new; return new->value; } -const char *add_option_ipaddr(struct openconnect_info *vpninfo, const char *opt, - int af, void *addr) +const char *add_option_ipaddr(struct oc_vpn_option **list, + const char *opt, int af, void *addr) { char buf[40]; if (!inet_ntop(af, addr, buf, sizeof(buf))) return NULL; - return add_option_dup(vpninfo, opt, buf, -1); + return add_option_dup(list, opt, buf, -1); } @@ -446,6 +448,75 @@ void free_optlist(struct oc_vpn_option *opt) } } +int install_vpn_opts(struct openconnect_info *vpninfo, struct oc_vpn_option *opt, + struct oc_ip_info *ip_info) +{ + /* F5 doesn't get its IP address until it actually establishes the + * PPP connection. */ + if (vpninfo->proto->proto != PROTO_F5 && !ip_info->addr && + !ip_info->addr6 && !ip_info->netmask6) { + vpn_progress(vpninfo, PRG_ERR, + _("No IP address received. Aborting\n")); + return -EINVAL; + } + + if (vpninfo->ip_info.addr) { + if (!ip_info->addr || strcmp(ip_info->addr, vpninfo->ip_info.addr)) { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different Legacy IP address (%s != %s)\n"), + ip_info->addr, vpninfo->ip_info.addr); + /* EPERM means that the retry loop will abort and won't keep trying. */ + return -EPERM; + } + } + if (vpninfo->ip_info.netmask) { + if (!ip_info->netmask || strcmp(ip_info->netmask, vpninfo->ip_info.netmask)) { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different Legacy IP netmask (%s != %s)\n"), + ip_info->netmask, vpninfo->ip_info.netmask); + return -EPERM; + } + } + if (vpninfo->ip_info.addr6) { + if (!ip_info->addr6 || strcmp(ip_info->addr6, vpninfo->ip_info.addr6)) { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different IPv6 address (%s != %s)\n"), + ip_info->addr6, vpninfo->ip_info.addr6); + return -EPERM; + } + } + if (vpninfo->ip_info.netmask6) { + if (!ip_info->netmask6 || strcmp(ip_info->netmask6, vpninfo->ip_info.netmask6)) { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different IPv6 netmask (%s != %s)\n"), + ip_info->netmask6, vpninfo->ip_info.netmask6); + return -EPERM; + } + } + + /* Preserve gateway_addr and MTU if they were set */ + ip_info->gateway_addr = vpninfo->ip_info.gateway_addr; + if (!ip_info->mtu) + ip_info->mtu = vpninfo->ip_info.mtu; + + if (ip_info->mtu && ip_info->mtu < 1280 && + (ip_info->addr6 || ip_info->netmask6)) { + vpn_progress(vpninfo, PRG_ERR, + _("IPv6 configuration received but MTU %d is too small.\n"), + ip_info->mtu); + } + + /* Free the original options */ + free_split_routes(&vpninfo->ip_info); + free_optlist(vpninfo->cstp_options); + + /* Install the new options */ + vpninfo->cstp_options = opt; + vpninfo->ip_info = *ip_info; + + return 0; +} + void openconnect_vpninfo_free(struct openconnect_info *vpninfo) { openconnect_close_https(vpninfo, 1); @@ -485,7 +556,7 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo) free_optlist(vpninfo->cookies); free_optlist(vpninfo->cstp_options); free_optlist(vpninfo->dtls_options); - free_split_routes(vpninfo); + free_split_routes(&vpninfo->ip_info); free(vpninfo->hostname); free(vpninfo->unique_hostname); free(vpninfo->urlpath); diff --git a/oncp.c b/oncp.c index c297ba0c..f1fc0b5f 100644 --- a/oncp.c +++ b/oncp.c @@ -61,8 +61,9 @@ static const char authpkt_tail[] = { 0xbb, 0x01, 0x00, 0x00, 0x00, 0x00 }; #define GRP_ATTR(g, a) (((g) << 16) | (a)) -static int process_attr(struct openconnect_info *vpninfo, int group, int attr, - unsigned char *data, int attrlen) +static int process_attr(struct openconnect_info *vpninfo, + struct oc_vpn_option **new_opts, struct oc_ip_info *new_ip_info, + int group, int attr, unsigned char *data, int attrlen) { char buf[80]; int i; @@ -76,10 +77,10 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, attrlen, group, attr); return -EINVAL; } - vpninfo->ip_info.mtu = load_be32(data); + new_ip_info->mtu = load_be32(data); vpn_progress(vpninfo, PRG_DEBUG, _("Received MTU %d from server\n"), - vpninfo->ip_info.mtu); + new_ip_info->mtu); break; case GRP_ATTR(2, 1): @@ -90,8 +91,8 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, vpn_progress(vpninfo, PRG_DEBUG, _("Received DNS server %s\n"), buf); for (i = 0; i < 3; i++) { - if (!vpninfo->ip_info.dns[i]) { - vpninfo->ip_info.dns[i] = add_option_dup(vpninfo, "DNS", buf, -1); + if (!new_ip_info->dns[i]) { + new_ip_info->dns[i] = add_option_dup(new_opts, "DNS", buf, -1); break; } } @@ -100,9 +101,9 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, case GRP_ATTR(2, 2): vpn_progress(vpninfo, PRG_DEBUG, _("Received DNS search domain %.*s\n"), attrlen, (char *)data); - vpninfo->ip_info.domain = add_option_dup(vpninfo, "search", (char *)data, attrlen); - if (vpninfo->ip_info.domain) { - char *p = (char *)vpninfo->ip_info.domain; + new_ip_info->domain = add_option_dup(new_opts, "search", (char *)data, attrlen); + if (new_ip_info->domain) { + char *p = (char *)new_ip_info->domain; while ((p = strchr(p, ','))) *p = ' '; } @@ -114,7 +115,7 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]); vpn_progress(vpninfo, PRG_DEBUG, _("Received internal IP address %s\n"), buf); - vpninfo->ip_info.addr = add_option_dup(vpninfo, "ipaddr", buf, -1); + new_ip_info->addr = add_option_dup(new_opts, "ipaddr", buf, -1); break; case GRP_ATTR(1, 2): @@ -123,7 +124,7 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]); vpn_progress(vpninfo, PRG_DEBUG, _("Received netmask %s\n"), buf); - vpninfo->ip_info.netmask = add_option_dup(vpninfo, "netmask", buf, -1); + new_ip_info->netmask = add_option_dup(new_opts, "netmask", buf, -1); break; case GRP_ATTR(1, 3): @@ -134,7 +135,7 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, vpn_progress(vpninfo, PRG_DEBUG, _("Received internal gateway address %s\n"), buf); /* Hm, what are we supposed to do with this? It's a tunnel; having a gateway is meaningless. */ - add_option_dup(vpninfo, "ipaddr", buf, -1); + add_option_dup(new_opts, "ipaddr", buf, -1); break; case GRP_ATTR(3, 3): { @@ -149,10 +150,10 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, break; inc = malloc(sizeof(*inc)); if (inc) { - inc->route = add_option_dup(vpninfo, "split-include", buf, -1); + inc->route = add_option_dup(new_opts, "split-include", buf, -1); if (inc->route) { - inc->next = vpninfo->ip_info.split_includes; - vpninfo->ip_info.split_includes = inc; + inc->next = new_ip_info->split_includes; + new_ip_info->split_includes = inc; } else free(inc); } @@ -171,10 +172,10 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, break; exc = malloc(sizeof(*exc)); if (exc) { - exc->route = add_option_dup(vpninfo, "split-exclude", buf, -1); + exc->route = add_option_dup(new_opts, "split-exclude", buf, -1); if (exc->route) { - exc->next = vpninfo->ip_info.split_excludes; - vpninfo->ip_info.split_excludes = exc; + exc->next = new_ip_info->split_excludes; + new_ip_info->split_excludes = exc; } else free(exc); } @@ -189,8 +190,8 @@ static int process_attr(struct openconnect_info *vpninfo, int group, int attr, vpn_progress(vpninfo, PRG_DEBUG, _("Received WINS server %s\n"), buf); for (i = 0; i < 3; i++) { - if (!vpninfo->ip_info.nbns[i]) { - vpninfo->ip_info.nbns[i] = add_option_dup(vpninfo, "WINS", buf, -1); + if (!new_ip_info->nbns[i]) { + new_ip_info->nbns[i] = add_option_dup(new_opts, "WINS", buf, -1); break; } } @@ -396,6 +397,8 @@ static int parse_conf_pkt(struct openconnect_info *vpninfo, unsigned char *bytes int kmplen, kmpend, grouplen, groupend, group, attr, attrlen; int ofs = 0; int split_enc_hmac_keys = 0; + struct oc_vpn_option *new_opts = NULL; + struct oc_ip_info new_ip_info = {}; kmplen = load_be16(bytes + ofs + 18); kmpend = ofs + kmplen; @@ -404,6 +407,9 @@ static int parse_conf_pkt(struct openconnect_info *vpninfo, unsigned char *bytes vpn_progress(vpninfo, PRG_ERR, _("Failed to parse KMP message\n")); dump_buf_hex(vpninfo, PRG_ERR, '<', bytes, pktlen); + einval: + free_optlist(new_opts); + free_split_routes(&new_ip_info); return -EINVAL; } @@ -426,7 +432,7 @@ static int parse_conf_pkt(struct openconnect_info *vpninfo, unsigned char *bytes vpn_progress(vpninfo, PRG_ERR, _("Received non-ESP TLVs (group %d) in ESP negotiation KMP\n"), group); - return -EINVAL; + goto einval; } while (ofs < groupend) { @@ -437,7 +443,8 @@ static int parse_conf_pkt(struct openconnect_info *vpninfo, unsigned char *bytes ofs += 6; if (attrlen + ofs > groupend) goto eparse; - if (process_attr(vpninfo, group, attr, bytes + ofs, attrlen)) + if (process_attr(vpninfo, &new_opts, &new_ip_info, + group, attr, bytes + ofs, attrlen)) goto eparse; if (GRP_ATTR(group, attr)==GRP_ATTR(7, 2)) split_enc_hmac_keys = 1; @@ -450,6 +457,13 @@ static int parse_conf_pkt(struct openconnect_info *vpninfo, unsigned char *bytes if (split_enc_hmac_keys) memcpy(vpninfo->esp_out.hmac_key, vpninfo->esp_out.enc_key + vpninfo->enc_key_len, vpninfo->hmac_key_len); + int ret = install_vpn_opts(vpninfo, new_opts, &new_ip_info); + if (ret) { + free_optlist(new_opts); + free_split_routes(&new_ip_info); + return ret; + } + return 0; } @@ -459,7 +473,6 @@ int oncp_connect(struct openconnect_info *vpninfo) int ret, len, kmp, kmplen, group, check_len; struct oc_text_buf *reqbuf; unsigned char bytes[65536]; - const char *old_addr = vpninfo->ip_info.addr, *old_netmask = vpninfo->ip_info.netmask; if (!vpninfo->cookies) { /* XX: This will happen if authentication was separate/external */ @@ -704,8 +717,7 @@ int oncp_connect(struct openconnect_info *vpninfo) } goto out; } - - ret = check_address_sanity(vpninfo, old_addr, old_netmask, NULL, NULL); + ret = 0; out: if (ret) diff --git a/openconnect-internal.h b/openconnect-internal.h index 23383506..a6bffae8 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -936,7 +936,9 @@ int script_setenv_int(struct openconnect_info *vpninfo, const char *opt, int val void prepare_script_env(struct openconnect_info *vpninfo); int script_config_tun(struct openconnect_info *vpninfo, const char *reason); int apply_script_env(struct oc_vpn_option *envs); -void free_split_routes(struct openconnect_info *vpninfo); +void free_split_routes(struct oc_ip_info *ip_info); +int install_vpn_opts(struct openconnect_info *vpninfo, struct oc_vpn_option *opt, + struct oc_ip_info *ip_info); /* tun.c / tun-win32.c */ void os_shutdown_tun(struct openconnect_info *vpninfo); @@ -986,7 +988,6 @@ char *openconnect_bin2base64(const char *prefix, const uint8_t *data, unsigned l int calculate_mtu(struct openconnect_info *vpninfo, int is_udp, int unpadded_overhead, int padded_overhead, int block_size); /* cstp.c */ -int check_address_sanity(struct openconnect_info *vpninfo, const char *old_addr, const char *old_netmask, const char *old_addr6, const char *old_netmask6); void cstp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf); int cstp_connect(struct openconnect_info *vpninfo); int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable); @@ -1304,9 +1305,9 @@ int digest_authorization(struct openconnect_info *vpninfo, int proxy, struct htt /* library.c */ void nuke_opt_values(struct oc_form_opt *opt); -const char *add_option_dup(struct openconnect_info *vpninfo, const char *opt, const char *val, int val_len); -const char *add_option_steal(struct openconnect_info *vpninfo, const char *opt, char **val); -const char *add_option_ipaddr(struct openconnect_info *vpninfo, const char *opt, int af, void *addr); +const char *add_option_dup(struct oc_vpn_option **list, const char *opt, const char *val, int val_len); +const char *add_option_steal(struct oc_vpn_option **list, const char *opt, char **val); +const char *add_option_ipaddr(struct oc_vpn_option **list, const char *opt, int af, void *addr); void free_optlist(struct oc_vpn_option *opt); int process_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form); /* This is private for now since we haven't yet worked out what the API will be */ diff --git a/openconnect.h b/openconnect.h index 9cc142fa..389d5a87 100644 --- a/openconnect.h +++ b/openconnect.h @@ -284,9 +284,10 @@ struct oc_split_include { struct oc_ip_info { const char *addr; - const char *netmask; + const char *netmask; /* Just the netmask, in dotted-quad form. */ const char *addr6; - const char *netmask6; + const char *netmask6; /* This is the IPv6 address *and* netmask + * e.g. "2001::dead:beef/128". */ const char *dns[3]; const char *nbns[3]; const char *domain; diff --git a/ppp.c b/ppp.c index 6b6096c6..672ff177 100644 --- a/ppp.c +++ b/ppp.c @@ -937,32 +937,32 @@ static int handle_state_transition(struct openconnect_info *vpninfo, int dtls, /* Ensure that we use the addresses we configured on PPP */ if (ppp->want_ipv4) { - vpninfo->ip_info.addr = add_option_ipaddr(vpninfo, "ppp_ipv4", AF_INET, - &ppp->out_ipv4_addr); + vpninfo->ip_info.addr = add_option_ipaddr(&vpninfo->cstp_options, "ppp_ipv4", + AF_INET, &ppp->out_ipv4_addr); } else { vpninfo->ip_info.addr = NULL; } /* Ensure that we use the addresses we configured on PPP */ if (ppp->want_ipv6) { - vpninfo->ip_info.addr6 = add_option_ipaddr(vpninfo, "ppp_ipv6", AF_INET6, - &ppp->out_ipv6_addr); + vpninfo->ip_info.addr6 = add_option_ipaddr(&vpninfo->cstp_options, "ppp_ipv6", + AF_INET6, &ppp->out_ipv6_addr); } else { vpninfo->ip_info.addr6 = NULL; } if (ppp->got_peerns & IPCP_NBNS0) - vpninfo->ip_info.nbns[0] = add_option_ipaddr(vpninfo, "ppp_nbns0", AF_INET, - &ppp->nameservers[0]); + vpninfo->ip_info.nbns[0] = add_option_ipaddr(&vpninfo->cstp_options, "ppp_nbns0", + AF_INET, &ppp->nameservers[0]); if (ppp->got_peerns & IPCP_DNS0) - vpninfo->ip_info.dns[0] = add_option_ipaddr(vpninfo, "ppp_dns0", AF_INET, - &ppp->nameservers[1]); + vpninfo->ip_info.dns[0] = add_option_ipaddr(&vpninfo->cstp_options, "ppp_dns0", + AF_INET, &ppp->nameservers[1]); if (ppp->got_peerns & IPCP_NBNS1) - vpninfo->ip_info.nbns[1] = add_option_ipaddr(vpninfo, "ppp_nbns1", AF_INET, - &ppp->nameservers[2]); + vpninfo->ip_info.nbns[1] = add_option_ipaddr(&vpninfo->cstp_options, "ppp_nbns1", + AF_INET, &ppp->nameservers[2]); if (ppp->got_peerns & IPCP_DNS1) - vpninfo->ip_info.dns[1] = add_option_ipaddr(vpninfo, "ppp_dns1", AF_INET, - &ppp->nameservers[3]); + vpninfo->ip_info.dns[1] = add_option_ipaddr(&vpninfo->cstp_options, "ppp_dns1", + AF_INET, &ppp->nameservers[3]); /* on close, we will need to send TERMREQ, then receive TERMACK */ vpninfo->delay_close = DELAY_CLOSE_IMMEDIATE_CALLBACK; diff --git a/pulse.c b/pulse.c index a7ad0ad1..2e18c7d2 100644 --- a/pulse.c +++ b/pulse.c @@ -217,7 +217,8 @@ static int valid_ift_auth_eap_exj1(unsigned char *bytes, int len) return 1; } -static int process_attr(struct openconnect_info *vpninfo, uint16_t type, +static int process_attr(struct openconnect_info *vpninfo, struct oc_vpn_option **new_opts, + struct oc_ip_info *new_ip_info, uint16_t type, unsigned char *data, int attrlen) { struct oc_split_include *xc; @@ -232,7 +233,7 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]); vpn_progress(vpninfo, PRG_DEBUG, _("Received internal Legacy IP address %s\n"), buf); - vpninfo->ip_info.addr = add_option_dup(vpninfo, "ipaddr", buf, -1); + new_ip_info->addr = add_option_dup(new_opts, "ipaddr", buf, -1); break; case 0x0002: @@ -241,7 +242,7 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]); vpn_progress(vpninfo, PRG_DEBUG, _("Received netmask %s\n"), buf); - vpninfo->ip_info.netmask = add_option_dup(vpninfo, "netmask", buf, -1); + new_ip_info->netmask = add_option_dup(new_opts, "netmask", buf, -1); break; case 0x0003: @@ -252,8 +253,8 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, vpn_progress(vpninfo, PRG_DEBUG, _("Received DNS server %s\n"), buf); for (i = 0; i < 3; i++) { - if (!vpninfo->ip_info.dns[i]) { - vpninfo->ip_info.dns[i] = add_option_dup(vpninfo, "DNS", buf, -1); + if (!new_ip_info->dns[i]) { + new_ip_info->dns[i] = add_option_dup(new_opts, "DNS", buf, -1); break; } } @@ -267,8 +268,8 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, vpn_progress(vpninfo, PRG_DEBUG, _("Received WINS server %s\n"), buf); for (i = 0; i < 3; i++) { - if (!vpninfo->ip_info.nbns[i]) { - vpninfo->ip_info.nbns[i] = add_option_dup(vpninfo, "WINS", buf, -1); + if (!new_ip_info->nbns[i]) { + new_ip_info->nbns[i] = add_option_dup(new_opts, "WINS", buf, -1); break; } } @@ -282,11 +283,11 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, _("Failed to handle IPv6 address\n")); return -EINVAL; } - vpninfo->ip_info.addr6 = add_option_dup(vpninfo, "ip6addr", buf, -1); + new_ip_info->addr6 = add_option_dup(new_opts, "ip6addr", buf, -1); i = strlen(buf); snprintf(buf + i, sizeof(buf) - i, "/%d", data[16]); - vpninfo->ip_info.netmask6 = add_option_dup(vpninfo, "ip6netmask", buf, -1); + new_ip_info->netmask6 = add_option_dup(new_opts, "ip6netmask", buf, -1); vpn_progress(vpninfo, PRG_DEBUG, _("Received internal IPv6 address %s\n"), buf); break; @@ -301,8 +302,8 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, } for (i = 0; i < 3; i++) { - if (!vpninfo->ip_info.dns[i]) { - vpninfo->ip_info.dns[i] = add_option_dup(vpninfo, "DNS", buf, -1); + if (!new_ip_info->dns[i]) { + new_ip_info->dns[i] = add_option_dup(new_opts, "DNS", buf, -1); break; } } @@ -323,10 +324,10 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, xc = malloc(sizeof(*xc)); if (xc) { - xc->route = add_option_dup(vpninfo, "split-include6", buf, -1); + xc->route = add_option_dup(new_opts, "split-include6", buf, -1); if (xc->route) { - xc->next = vpninfo->ip_info.split_includes; - vpninfo->ip_info.split_includes = xc; + xc->next = new_ip_info->split_includes; + new_ip_info->split_includes = xc; } else free(xc); } @@ -346,10 +347,10 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, xc = malloc(sizeof(*xc)); if (xc) { - xc->route = add_option_dup(vpninfo, "split-exclude6", buf, -1); + xc->route = add_option_dup(new_opts, "split-exclude6", buf, -1); if (xc->route) { - xc->next = vpninfo->ip_info.split_excludes; - vpninfo->ip_info.split_excludes = xc; + xc->next = new_ip_info->split_excludes; + new_ip_info->split_excludes = xc; } else free(xc); } @@ -364,10 +365,10 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, attrlen, type); return -EINVAL; } - vpninfo->ip_info.mtu = load_be32(data); + new_ip_info->mtu = load_be32(data); vpn_progress(vpninfo, PRG_DEBUG, _("Received MTU %d from server\n"), - vpninfo->ip_info.mtu); + new_ip_info->mtu); break; case 0x4006: @@ -377,9 +378,9 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, attrlen--; vpn_progress(vpninfo, PRG_DEBUG, _("Received DNS search domain %.*s\n"), attrlen, (char *)data); - vpninfo->ip_info.domain = add_option_dup(vpninfo, "search", (char *)data, attrlen); - if (vpninfo->ip_info.domain) { - char *p = (char *)vpninfo->ip_info.domain; + new_ip_info->domain = add_option_dup(new_opts, "search", (char *)data, attrlen); + if (new_ip_info->domain) { + char *p = (char *)new_ip_info->domain; while ((p = strchr(p, ','))) *p = ' '; } @@ -393,7 +394,7 @@ static int process_attr(struct openconnect_info *vpninfo, uint16_t type, vpn_progress(vpninfo, PRG_DEBUG, _("Received internal gateway address %s\n"), buf); /* Hm, what are we supposed to do with this? It's a tunnel; having a gateway is meaningless. */ - add_option_dup(vpninfo, "ipaddr", buf, -1); + add_option_dup(new_opts, "ipaddr", buf, -1); break; case 0x4010: { @@ -2257,10 +2258,6 @@ static int handle_main_config_packet(struct openconnect_info *vpninfo, int routes_len = 0; int l; unsigned char *p; - const char *old_addr = vpninfo->ip_info.addr; - const char *old_netmask = vpninfo->ip_info.netmask; - const char *old_addr6 = vpninfo->ip_info.addr6; - const char *old_netmask6 = vpninfo->ip_info.netmask6; /* First part of header, similar to ESP, has already been checked */ if (len < 0x31 || @@ -2281,6 +2278,9 @@ static int handle_main_config_packet(struct openconnect_info *vpninfo, p = bytes + 0x34; routes_len -= 8; /* The header including length and number of routes */ + struct oc_vpn_option *new_opts = NULL; + struct oc_ip_info new_ip_info = {}; + /* We know it's a multiple of 0x10 now. We checked. */ while (routes_len) { char buf[80]; @@ -2310,7 +2310,7 @@ static int handle_main_config_packet(struct openconnect_info *vpninfo, vpn_progress(vpninfo, PRG_DEBUG, _("Received split include route %s\n"), buf); inc = malloc(sizeof(*inc)); if (inc) { - inc->route = add_option_dup(vpninfo, "split-include", buf, -1); + inc->route = add_option_dup(&new_opts, "split-include", buf, -1); if (inc->route) { inc->next = vpninfo->ip_info.split_includes; vpninfo->ip_info.split_includes = inc; @@ -2323,7 +2323,7 @@ static int handle_main_config_packet(struct openconnect_info *vpninfo, vpn_progress(vpninfo, PRG_DEBUG, _("Received split exclude route %s\n"), buf); exc = malloc(sizeof(*exc)); if (exc) { - exc->route = add_option_dup(vpninfo, "split-exclude", buf, -1); + exc->route = add_option_dup(&new_opts, "split-exclude", buf, -1); if (exc->route) { exc->next = vpninfo->ip_info.split_excludes; vpninfo->ip_info.split_excludes = exc; @@ -2358,14 +2358,19 @@ static int handle_main_config_packet(struct openconnect_info *vpninfo, p += 4; l -= 4; - process_attr(vpninfo, type, p, attrlen); + process_attr(vpninfo, &new_opts, &new_ip_info, type, p, attrlen); p += attrlen; l -= attrlen; if (l && l < 4) goto bad_config; } - return check_address_sanity(vpninfo, old_addr, old_netmask, old_addr6, old_netmask6); + int ret = install_vpn_opts(vpninfo, new_opts, &new_ip_info); + if (ret) { + free_optlist(new_opts); + free_split_routes(&new_ip_info); + } + return ret; } /* Example ESP config packet: diff --git a/script.c b/script.c index 17cda887..2ab66735 100644 --- a/script.c +++ b/script.c @@ -336,13 +336,16 @@ void prepare_script_env(struct openconnect_info *vpninfo) } } } - if (vpninfo->ip_info.addr6) { + + if (vpninfo->ip_info.addr6) script_setenv(vpninfo, "INTERNAL_IP6_ADDRESS", vpninfo->ip_info.addr6, 0, 0); + if (vpninfo->ip_info.netmask6) script_setenv(vpninfo, "INTERNAL_IP6_NETMASK", vpninfo->ip_info.netmask6, 0, 0); - } else if (vpninfo->ip_info.netmask6) { - char *slash = strchr(vpninfo->ip_info.netmask6, '/'); - script_setenv(vpninfo, "INTERNAL_IP6_NETMASK", vpninfo->ip_info.netmask6, 0, 0); - if (slash) + /* The 'netmask6' is actually the address *and* netmask. From which we + * obtain just the address on its own, if we don't have it separately */ + if (vpninfo->ip_info.netmask6 && !vpninfo->ip_info.addr6) { + char *slash = strchr(vpninfo->ip_info.netmask6, '/'); + if (slash) script_setenv(vpninfo, "INTERNAL_IP6_ADDRESS", vpninfo->ip_info.netmask6, slash - vpninfo->ip_info.netmask6, 0); } @@ -436,27 +439,27 @@ void prepare_script_env(struct openconnect_info *vpninfo) setenv_cstp_opts(vpninfo); } -void free_split_routes(struct openconnect_info *vpninfo) +void free_split_routes(struct oc_ip_info *ip_info) { struct oc_split_include *inc; - for (inc = vpninfo->ip_info.split_includes; inc; ) { + for (inc = ip_info->split_includes; inc; ) { struct oc_split_include *next = inc->next; free(inc); inc = next; } - for (inc = vpninfo->ip_info.split_excludes; inc; ) { + for (inc = ip_info->split_excludes; inc; ) { struct oc_split_include *next = inc->next; free(inc); inc = next; } - for (inc = vpninfo->ip_info.split_dns; inc; ) { + for (inc = ip_info->split_dns; inc; ) { struct oc_split_include *next = inc->next; free(inc); inc = next; } - vpninfo->ip_info.split_dns = vpninfo->ip_info.split_includes = - vpninfo->ip_info.split_excludes = NULL; + ip_info->split_dns = ip_info->split_includes = + ip_info->split_excludes = NULL; }