]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
ppp: Clean up negotiated IP/DNS option handling
authorDavid Woodhouse <dwmw2@infradead.org>
Sun, 11 Apr 2021 16:46:09 +0000 (17:46 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Wed, 21 Apr 2021 12:12:31 +0000 (13:12 +0100)
We were leaking these strings. Fix it up to properly attempt negotiation
and set options in the option list with the results.

Also make it abort IPCP if the server doesn't accept a Legacy IP address
which had already been configured. We can't do the same for IPv6 as PPP
only actually configures the link-local address.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
library.c
openconnect-internal.h
ppp.c
ppp.h

index c3cdec52c29188e321640a65945b4d4ff57dd2c6..7b84b5c65483f677bfa2511347da20d61cdec9fc 100644 (file)
--- a/library.c
+++ b/library.c
@@ -424,6 +424,17 @@ const char *add_option_steal(struct openconnect_info *vpninfo, const char *opt,
        return new->value;
 }
 
+const char *add_option_ipaddr(struct openconnect_info *vpninfo, 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);
+}
+
 
 void free_optlist(struct oc_vpn_option *opt)
 {
index ec396d7ca5dc6dde24cda312d61559f1566eedcc..57342608a9488f8b3886d34c45d58f36e743a517 100644 (file)
@@ -1297,6 +1297,7 @@ int digest_authorization(struct openconnect_info *vpninfo, int proxy, struct htt
 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);
 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/ppp.c b/ppp.c
index 65dc5ea0f8f644e194cf6d39ee5e8138946cb42a..8c759a2ca8f793d404da827e3f630a4090d1ba70 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -222,9 +222,8 @@ int openconnect_ppp_new(struct openconnect_info *vpninfo,
 
        /* Nameservers to request from peer
         * (see https://tools.ietf.org/html/rfc1877#section-1) */
-       ppp->solicit_peerns = 0;
        if (!vpninfo->ip_info.dns[0] && !vpninfo->ip_info.nbns[0])
-               ppp->solicit_peerns |= IPCP_DNS0|IPCP_DNS1|IPCP_NBNS0|IPCP_NBNS1;
+               ppp->solicit_peerns = IPCP_DNS0|IPCP_DNS1|IPCP_NBNS0|IPCP_NBNS1;
 
        /* Outgoing IPv4 address and IPv6 interface identifier bits,
         * if already configured via another mechanism */
@@ -279,7 +278,7 @@ int openconnect_ppp_new(struct openconnect_info *vpninfo,
 
        if (ppp->hdlc) ppp->out_lcp_opts |= BIT_ASYNCMAP;
        ppp->want_ipv4 = want_ipv4;
-       ppp->want_ipv6 = want_ipv6;
+       ppp->want_ipv6 = want_ipv6 && !vpninfo->disable_ipv6;
        ppp->exp_ppp_hdr_size = 4; /* Address(1), Control(1), Proto(2) */
 
        return 0;
@@ -288,16 +287,32 @@ int openconnect_ppp_new(struct openconnect_info *vpninfo,
 static void print_ppp_state(struct openconnect_info *vpninfo, int level)
 {
        struct oc_ppp *ppp = vpninfo->ppp;
-       char buf[40] = {0};
+       char buf4[20];
+       char buf6[40];
 
-       vpn_progress(vpninfo, level, _("Current PPP state: %s (encap %s):\n"), ppps_names[ppp->ppp_state], encap_names[ppp->encap]);
+       if (ppp->want_ipv4)
+               inet_ntop(AF_INET, &ppp->in_ipv4_addr, buf4, sizeof(buf4));
+       else
+               snprintf(buf4, sizeof(buf4), "none");
+
+       if (ppp->want_ipv6)
+               inet_ntop(AF_INET6, &ppp->in_ipv6_addr, buf6, sizeof(buf6));
+       else
+               snprintf(buf6, sizeof(buf6), "none");
+
+       vpn_progress(vpninfo, level, _("Current PPP state: %s (encap %s):\n"),
+                    ppps_names[ppp->ppp_state], encap_names[ppp->encap]);
        vpn_progress(vpninfo, level, _("    in: asyncmap=0x%08x, lcp_opts=%d, lcp_magic=0x%08x, ipv4=%s, ipv6=%s\n"),
-                    ppp->in_asyncmap, ppp->in_lcp_opts, (unsigned)ntohl(ppp->in_lcp_magic), inet_ntoa(ppp->in_ipv4_addr),
-                    inet_ntop(AF_INET6, &ppp->in_ipv6_addr, buf, sizeof(buf)));
-       inet_ntop(AF_INET6, &ppp->out_ipv6_addr, buf, sizeof(buf));
-       vpn_progress(vpninfo, level, _("   out: asyncmap=0x%08x, lcp_opts=%d, lcp_magic=0x%08x, ipv4=%s, ipv6=%s, solicit_peerns=%d\n"),
-                    ppp->out_asyncmap, ppp->out_lcp_opts, (unsigned)ntohl(ppp->out_lcp_magic), inet_ntoa(ppp->out_ipv4_addr),
-                    inet_ntop(AF_INET6, &ppp->out_ipv6_addr, buf, sizeof(buf)), ppp->solicit_peerns);
+                    ppp->in_asyncmap, ppp->in_lcp_opts, (unsigned)ntohl(ppp->in_lcp_magic),
+                    buf4, buf6);
+
+       if (ppp->want_ipv4)
+               inet_ntop(AF_INET, &ppp->out_ipv4_addr, buf4, sizeof(buf4));
+       if (ppp->want_ipv6)
+               inet_ntop(AF_INET6, &ppp->out_ipv6_addr, buf6, sizeof(buf6));
+       vpn_progress(vpninfo, level, _("   out: asyncmap=0x%08x, lcp_opts=%d, lcp_magic=0x%08x, ipv4=%s, ipv6=%s, solicit_peerns=%d, got_peerns=%d\n"),
+                    ppp->out_asyncmap, ppp->out_lcp_opts, (unsigned)ntohl(ppp->out_lcp_magic),
+                    buf4, buf6, ppp->solicit_peerns, ppp->got_peerns);
 }
 
 static int buf_append_ppp_tlv(struct oc_text_buf *buf, int tag, int len, const void *data)
@@ -665,7 +680,12 @@ static int handle_config_rejnak(struct openconnect_info *vpninfo,
                                vpn_progress(vpninfo, PRG_DEBUG,
                                             _("Server nak-offered IPv4 address: %s\n"), s);
                                ppp->out_ipv4_addr = *a;
-                               vpninfo->ip_info.addr = strdup(s); /* XX: need free and add_option() */
+                               if (vpninfo->ip_info.addr) {
+                                       vpn_progress(vpninfo, PRG_ERR,
+                                                    _("Server rejected Legacy IP address %s\n"),
+                                                      vpninfo->ip_info.addr);
+                                       ppp->want_ipv4 = 0;
+                               }
                        } else {
                                vpn_progress(vpninfo, PRG_DEBUG,
                                             _("Server rejected/nak'ed our IPv4 address or request: %s\n"), s);
@@ -686,11 +706,8 @@ static int handle_config_rejnak(struct openconnect_info *vpninfo,
                                vpn_progress(vpninfo, PRG_DEBUG,
                                             _("Server nak-offered IPCP request for %s[%d] server: %s\n"),
                                             is_dns ? "DNS" : "NBNS", entry, s);
-                               /* XX: need free and add_option() */
-                               if (is_dns)
-                                       vpninfo->ip_info.dns[entry] = strdup(s);
-                               else
-                                       vpninfo->ip_info.nbns[entry] = strdup(s);
+                               ppp->nameservers[t & 3] = *a;
+                               ppp->got_peerns |= (1<<(t-IPCP_xNS_BASE));
                        } else {
                                vpn_progress(vpninfo, PRG_DEBUG,
                                             _("Server rejected/nak'ed IPCP request for %s[%d] server\n"),
@@ -708,26 +725,19 @@ static int handle_config_rejnak(struct openconnect_info *vpninfo,
                                memcpy(ipv6_ll + 8, val, 8);
                                if (!inet_ntop(AF_INET6, ipv6_ll, buf, sizeof(buf)))
                                        return -EINVAL;
+
                                vpn_progress(vpninfo, PRG_DEBUG,
                                             _("Server nak-offered IPv6 link-local address %s\n"), buf);
-
                                /* If we don't already have a valid global IPv6 address, then we are
                                 * supposed to use this one to create a valid link-local IPv6
                                 * address to allow autoconfiguration (https://tools.ietf.org/html/rfc5072)
                                 */
-                               if (!vpninfo->ip_info.addr6 && !vpninfo->ip_info.netmask6) {
-                                       memcpy(&ppp->out_ipv6_addr, ipv6_ll, 16);
-                                       /* XX: need add-option */
-                                       if ((asprintf((char **)&vpninfo->ip_info.netmask6, "%s/64", buf)) <= 0)
-                                               return -ENOMEM;
-                                       vpn_progress(vpninfo, PRG_INFO,
-                                                    _("Configured IPv6 link-local address %s.\n"),
-                                                    vpninfo->ip_info.netmask6);
-                               }
+                               memcpy(&ppp->out_ipv6_addr, ipv6_ll, 16);
                        } else {
-                               vpn_progress(vpninfo, PRG_DEBUG,
+                               vpn_progress(vpninfo, PRG_INFO,
                                             _("Server rejected/nak'ed our IPv6 interface identifier\n"));
-                               return -EINVAL;
+                               ppp->want_ipv6 = 0;
+                               return 0;
                        }
                        break;
                }
@@ -908,6 +918,36 @@ static int handle_state_transition(struct openconnect_info *vpninfo, int *timeou
                        break;
 
                ppp->ppp_state = PPPS_NETWORK;
+
+               /* 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);
+               } 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);
+               } 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]);
+               if (ppp->got_peerns & IPCP_DNS0)
+                       vpninfo->ip_info.dns[0] = add_option_ipaddr(vpninfo, "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]);
+               if (ppp->got_peerns & IPCP_DNS1)
+                       vpninfo->ip_info.dns[1] = add_option_ipaddr(vpninfo, "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;
                break;
diff --git a/ppp.h b/ppp.h
index fe00f0db9ff9bb0dabecd36165b0fe311f49734c..c425677820b7fc8d7a200bbea24fd7f551029330 100644 (file)
--- a/ppp.h
+++ b/ppp.h
@@ -120,6 +120,7 @@ struct oc_ppp {
        struct in6_addr out_ipv6_addr;
 
        int solicit_peerns;     /* bitfield of DNS/NBNS to request */
+       int got_peerns;
 
        /* Incoming options */
        uint32_t in_asyncmap;
@@ -128,6 +129,8 @@ struct oc_ppp {
        struct in_addr in_ipv4_addr;
        struct in6_addr in_ipv6_addr;
 
+       struct in_addr nameservers[4];
+
        int exp_ppp_hdr_size;   /* predicted size of next PPP header */
 };