]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
parse real Fortinet config
authorDaniel Lenski <dlenski@gmail.com>
Thu, 14 May 2020 03:41:00 +0000 (20:41 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Mon, 29 Mar 2021 03:13:30 +0000 (20:13 -0700)
Based on these two real examples (https://forum.fortinet.com/tm.aspx?m=170415 and https://forum.fortinet.com/tm.aspx?m=105123).

Tested with sample XML in comments. Clean up a bunch of memory/state errors in PPP code (caught by static analyzer)

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
fortinet.c

index fd1bf03730ef9a6c6804053e2c79d55815abffcb..2e980535d7825e4f0c71a36cefecc35fe44b4955 100644 (file)
@@ -81,13 +81,34 @@ static const char *add_option(struct openconnect_info *vpninfo, const char *opt,
        return new->value;
 }
 
+/* Parse this:
+<?xml version="1.0" encoding="utf-8"?>
+<sslvpn-tunnel ver="2" dtls="1" patch="1">
+  <dtls-config heartbeat-interval="10" heartbeat-fail-count="10" heartbeat-idle-timeout="10" client-hello-timeout="10"/>
+  <tunnel-method value="ppp"/>
+  <tunnel-method value="tun"/>
+  <fos platform="FG100E" major="5" minor="06" patch="6" build="1630" branch="1630"/>
+  <client-config save-password="off" keep-alive="on" auto-connect="off"/>
+  <ipv4>
+    <dns ip="1.1.1.1"/>
+    <dns ip="8.8.8.8" domain="foo.com"/>
+    <assigned-addr ipv4="172.16.1.1"/>
+    <split-tunnel-info>
+      <addr ip="10.11.10.10" mask="255.255.255.255"/>
+      <addr ip="10.11.1.0" mask="255.255.255.0"/>
+    </split-tunnel-info>
+  </ipv4>
+  <idle-timeout val="3600"/>
+  <auth-timeout val="18000"/>
+</sslvpn-tunnel>
+*/
 static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf, int len,
                                     int *ipv4, int *ipv6)
 {
-       xmlNode *fav_node, *obj_node, *xml_node;
+       xmlNode *xml_node, *x, *x2;
        xmlDocPtr xml_doc;
-       int ret = 0, ii, n_dns = 0, n_nbns = 0, default_route = 0;
-       char *s = NULL;
+       int ret = 0, ii, n_dns = 0 /*, n_nbns = 0, default_route = 0 */;
+       char *s = NULL, *s2 = NULL;
        struct oc_text_buf *domains = NULL;
 
        if (!buf || !len)
@@ -102,13 +123,10 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf
                             _("Response was:%s\n"), buf);
                return -EINVAL;
        }
-       fav_node = xmlDocGetRootElement(xml_doc);
-       if (!xmlnode_is_named(fav_node, "favorite"))
-               goto err;
 
-       obj_node = xmlFirstElementChild(fav_node);
-       if (!xmlnode_is_named(obj_node, "object"))
-               goto err;
+       xml_node = xmlDocGetRootElement(xml_doc);
+       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;
@@ -121,79 +139,72 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf
 
        domains = buf_alloc();
 
-       for (xml_node = xmlFirstElementChild(obj_node);
-            xml_node;
-            xml_node = xmlNextElementSibling(xml_node)) {
-               if (xmlnode_is_named(xml_node, "IPV4_0"))
-                       *ipv4 = xmlnode_bool_or_int_value(vpninfo, xml_node);
-               else if (xmlnode_is_named(xml_node, "IPV6_0")) {
-                       if (!vpninfo->disable_ipv6)
-                               *ipv6 = xmlnode_bool_or_int_value(vpninfo, xml_node);
-               } else if (xmlnode_is_named(xml_node, "idle_session_timeout")) {
-                       int sec = vpninfo->idle_timeout = xmlnode_bool_or_int_value(vpninfo, xml_node);
+       for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) {
+               if (xmlnode_is_named(xml_node, "auth-timeout") && !xmlnode_get_prop(xml_node, "val", &s))
+                       vpn_progress(vpninfo, PRG_INFO, _("Session will expire after %d minutes.\n"), atoi(s)/60);
+               else if (xmlnode_is_named(xml_node, "idle-timeout") && !xmlnode_get_prop(xml_node, "val", &s)) {
+                       int sec = vpninfo->idle_timeout = atoi(s);
                        vpn_progress(vpninfo, PRG_INFO, _("Idle timeout is %d minutes.\n"), sec/60);
-               } else if (xmlnode_is_named(xml_node, "tunnel_port_dtls")) {
-                       int port = xmlnode_bool_or_int_value(vpninfo, xml_node);
-                       udp_sockaddr(vpninfo, port);
-                       vpn_progress(vpninfo, PRG_INFO, _("DTLS port is %d.\n"), port);
-               } else if (xmlnode_is_named(xml_node, "UseDefaultGateway0")) {
-                       default_route = xmlnode_bool_or_int_value(vpninfo, xml_node);
-                       vpn_progress(vpninfo, PRG_INFO, _("Got UseDefaultGateway0 value of %d.\n"), default_route);
-               } else if (xmlnode_is_named(xml_node, "SplitTunneling0")) {
-                       int st = xmlnode_bool_or_int_value(vpninfo, xml_node);
-                       vpn_progress(vpninfo, PRG_INFO, _("Got SplitTunneling0 value of %d.\n"), st);
-                }
-               /* XX: This is an objectively stupid way to use XML, a hierarchical data format. */
-               else if (   (!strncmp((char *)xml_node->name, "DNS", 3) && isdigit(xml_node->name[3]))
-                        || (!strncmp((char *)xml_node->name, "DNS6_", 5) && isdigit(xml_node->name[5])) ) {
-                       s = (char *)xmlNodeGetContent(xml_node);
-                       if (s && *s) {
-                               vpn_progress(vpninfo, PRG_INFO, _("Got IPv%d DNS server %s.\n"),
-                                            xml_node->name[4]=='_' ? 6 : 4, s);
-                               if (n_dns < 3) vpninfo->ip_info.dns[n_dns++] = add_option(vpninfo, "DNS", &s);
-                       }
-               } else if (!strncmp((char *)xml_node->name, "WINS", 4) && isdigit(xml_node->name[4])) {
-                       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(vpninfo, "WINS", &s);
+               } else if (xmlnode_is_named(xml_node, "fos")) {
+                       char platform[80], *p = platform, *e = platform + 80;
+                       if (!xmlnode_get_prop(xml_node, "platform", &s)) {
+                           p+=snprintf(p, e-p, "%s", s);
+                           if (!xmlnode_get_prop(xml_node, "major", &s))  p+=snprintf(p, e-p, " v%s", s);
+                           if (!xmlnode_get_prop(xml_node, "minor", &s))  p+=snprintf(p, e-p, ".%s", s);
+                           if (!xmlnode_get_prop(xml_node, "patch", &s))  p+=snprintf(p, e-p, ".%s", s);
+                           if (!xmlnode_get_prop(xml_node, "build", &s))  p+=snprintf(p, e-p, " build %s", s);
+                           if (!xmlnode_get_prop(xml_node, "branch", &s))    snprintf(p, e-p, " branch %s", s);
+                           vpn_progress(vpninfo, PRG_INFO,
+                                        _("Reported platform is %s\n"), platform);
                        }
-               } else if (!strncmp((char *)xml_node->name, "DNSSuffix", 9) && isdigit(xml_node->name[9])) {
-                       s = (char *)xmlNodeGetContent(xml_node);
-                       if (s && *s) {
-                               vpn_progress(vpninfo, PRG_INFO, _("Got search domain %s.\n"), s);
-                               buf_append(domains, "%s ", s);
-                       }
-               } else if (   (!strncmp((char *)xml_node->name, "LAN", 3) && isdigit((char)xml_node->name[3]))
-                          || (!strncmp((char *)xml_node->name, "LAN6_", 5) && isdigit((char)xml_node->name[5]))) {
-                       s = (char *)xmlNodeGetContent(xml_node);
-                       if (s && *s) {
-                               char *word, *next;
-                               struct oc_split_include *inc;
-
-                               for (word = (char *)add_option(vpninfo, "route-list", &s);
-                                    *word; word = next) {
-                                       for (next = word; *next && !isspace(*next); next++);
-                                       if (*next)
-                                               *next++ = 0;
-                                       if (next == word + 1)
-                                               continue;
-
-                                       inc = malloc(sizeof(*inc));
-                                       inc->route = word;
-                                       inc->next = vpninfo->ip_info.split_includes;
-                                       vpninfo->ip_info.split_includes = inc;
-                                       vpn_progress(vpninfo, PRG_INFO, _("Got IPv%d route %s.\n"),
-                                                    xml_node->name[4]=='_' ? 6 : 4, word);
+               } 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(vpninfo, "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);
+                                               buf_append(domains, "%s ", s);
+                                       }
+                                       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(vpninfo, "DNS", &s);
+                                       }
+                               } else if (xmlnode_is_named(x, "split-tunnel-info")) {
+                                       for (x2 = x->children; x2; x2=x2->next) {
+                                               if (xmlnode_is_named(x2, "addr")) {
+                                                       if (!xmlnode_get_prop(x2, "ip", &s) &&
+                                                           !xmlnode_get_prop(x2, "mask", &s2) &&
+                                                           s && s2 && *s && *s2) {
+                                                               struct oc_split_include *inc = malloc(sizeof(*inc));
+                                                               char *route = malloc(32);
+                                                               if (!route || !inc) {
+                                                                       free(route);
+                                                                       free(inc);
+                                                                       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(vpninfo, "split-include", &route);
+                                                               inc->next = vpninfo->ip_info.split_includes;
+                                                               vpninfo->ip_info.split_includes = inc;
+                                                               /* XX: static analyzer doesn't realize that add_option will steal route's reference, so... */
+                                                               free(route);
+                                                       }
+                                               }
+                                       }
                                }
                        }
                }
        }
 
-       if (default_route && ipv4)
-               vpninfo->ip_info.netmask = strdup("0.0.0.0");
-       if (default_route && ipv6)
-               vpninfo->ip_info.netmask6 = strdup("::");
+       /* if (default_route && *ipv4) */
+       /*      vpninfo->ip_info.netmask = strdup("0.0.0.0"); */
+       /* if (default_route && *ipv6) */
+       /*       vpninfo->ip_info.netmask6 = strdup("::/0"); */
        if (buf_error(domains) == 0 && domains->pos > 0) {
                domains->data[domains->pos-1] = '\0';
                vpninfo->ip_info.domain = add_option(vpninfo, "search", &domains->data);
@@ -201,15 +212,16 @@ static int parse_fortinet_xml_config(struct openconnect_info *vpninfo, char *buf
        buf_free(domains);
 
        if (*ipv4 < 1 && *ipv6 < 1) {
-       err:
                vpn_progress(vpninfo, PRG_ERR,
                             _("Failed to find VPN options\n"));
                vpn_progress(vpninfo, PRG_DEBUG,
                             _("Response was:%s\n"), buf);
                ret = -EINVAL;
        }
+ out:
        xmlFreeDoc(xml_doc);
        free(s);
+       free(s2);
        return ret;
 }