This issue is caused by dnsmasq incorrectly handling the client's tag 
information. It is not related to adding the opts item.
This is a necessary issue, and the steps to reproduce it are as follows:

1.       Run dnsmasq with --dhcp-hostsfile
# dnsmasq --dhcp-hostsfile=/var/run/test_dhcp/host 
--dhcp-range=192.168.0.0,192.168.0.240,infinite
# cat /var/run/test_dhcp/host

d0:c0:c4:66:25:81,host-192-168-0-159.dhcp-test, 
192.168.0.159,set:port-cfc5b9f7-2760-42d6-899b-6817fe405721


2.       Request a dynamic address for the client that comply with the host rule
# dhclient -cf /etc/dhcp/dhclient.conf -pf /tmp/eth1.pid -lf /tmp/eth1.lease 
eth1
# cat /etc/dhcp/dhclient.conf
send dhcp-client-identifier " host-192-168-0-159.dhcp-test";
# ip link show eth1
2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode 
DEFAULT group default qlen 1000
link/ether d0:c0:c4:66:25:81 brd ff:ff:ff:ff:ff:ff


3.       Send SIGHUP to dnsmasq
# kill -1 446255

In addition to the above issue, dnsmasq may also be related to a UAF problem. 
Here is the patch I provided for fixing these issues.

>From 31b883b2bcaf70b74549128ded853f4942e5f6ae Mon Sep 17 00:00:00 2001
From: renmingshuai <renmingsh...@huawei.com>
Date: Thu, 1 Aug 2024 19:56:47 +0800
Subject: [PATCH] Fix a coredump and UAF caused by trying to free non-heap
objects.

There is an issue with the process of adding a netid in dhcp_deply for dnsmasq, 
which will trigger coredump and UAF.
When processing DHCP requests, dnsmasq uses local variables such as known_id, 
iface_id and cpewan_id, to store the client's tag information and add them to 
the netid list.
Afterwards, dnsmasq will call find_config() to find a dhcp config and add the 
netid list to the global daemon->dhcp_conf->netid list.
In addition, dnsmasq will also add the netid from other global variables to the 
config->netid list, such as daemon->dhcp_match->netid

When dnsmasq receives the hup signal, it will call clear_dynamic_conf() to free 
the netid in the config's netid list, which will trigger at least the following 
three issues:
1. config->netid->list may point to a stack address (such as the address of the 
local variable "known_id" mentioned above), and the content of this address may 
be assigned other values.
Attempting to free this illegal address value will trigger coredump.
2. config->netid->list->net may point to a string constant (such as 
known_id.net="knwon"), and attempting to free this address will also trigger 
coredump.
3. The address pointed to by config->netid->list may be also added to other 
global lists, such as daemon->dhcp_match->netid.
After the address is freed, it still exists in the daemon->dhcp_match list, 
which may trigger UAF issues.

Calling dhcp_netid_creat() to allocate new memory for the client's tag 
information can solve the above issues. These memories will be fully freed in 
clear_rynamic_comf().

Signed-off-by: renmingshuai <renmingsh...@huawei.com>
---
src/dnsmasq.h |  1 +
src/option.c  |  2 +-
src/rfc2131.c | 48 ++++++++++++++----------------------------------
3 files changed, 16 insertions(+), 35 deletions(-)

diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index e455c3f..3376655 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1519,6 +1519,7 @@ char *parse_server(char *arg, struct server_details 
*sdetails);
char *parse_server_addr(struct server_details *sdetails);
int parse_server_next(struct server_details *sdetails);
int option_read_dynfile(char *file, int flags);
+struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next);

/* forward.c */
void reply_query(int fd, time_t now);
diff --git a/src/option.c b/src/option.c
index f4ff7c0..480ef5f 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1314,7 +1314,7 @@ static char *set_prefix(char *arg)
    return arg;
}

-static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid 
*next)
+struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
{
   struct dhcp_netid *tt;
   tt = opt_malloc(sizeof (struct dhcp_netid));
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 68834ea..050d610 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -96,7 +96,6 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
   unsigned char *agent_id = NULL, *uuid = NULL;
   unsigned char *emac = NULL;
   int vendor_class_len = 0, emac_len = 0;
-  struct dhcp_netid known_id, iface_id, cpewan_id;
   struct dhcp_opt *o;
   unsigned char pxe_uuid[17];
   unsigned char *oui = NULL, *serial = NULL;
@@ -107,9 +106,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
   subnet_addr.s_addr = override.s_addr = 0;

   /* set tag with name == interface */
-  iface_id.net = iface_name;
-  iface_id.next = NULL;
-  netid = &iface_id;
+  netid = dhcp_netid_create(iface_name, NULL);

   if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
     return 0;
@@ -173,9 +170,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
                     the gateway id back. Note that the device class is 
optional */
                  if (oui && serial)
                    {
-                     cpewan_id.net = "cpewan-id";
-                     cpewan_id.next = netid;
-                     netid = &cpewan_id;
+                     netid = dhcp_netid_create("cpewan-id", netid);
                    }
                  break;
               }
@@ -226,8 +221,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
                  vendor->len == option_len(sopt) &&
                  memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
                {
-                 vendor->netid.next = netid;
-                 netid = &vendor->netid;
+                 netid = dhcp_netid_create(vendor->netid.net, netid);
                }
            }
        }
@@ -263,8 +257,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
        (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
        memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
       {
-       mac->netid.next = netid;
-       netid = &mac->netid;
+       netid = dhcp_netid_create(mac->netid.net, netid);
       }

   /* Determine network for this packet. Our caller will have already linked 
all the
@@ -453,8 +446,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,

       if (match)
        {
-         o->netid->next = netid;
-         netid = o->netid;
+         netid = dhcp_netid_create(o->netid->net, netid);
        }
     }

@@ -499,8 +491,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
          for (i = 0; i <= (option_len(opt) - vendor->len); i++)
            if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
             {
-               vendor->netid.next = netid;
-               netid = &vendor->netid;
+               netid = dhcp_netid_create(vendor->netid.net, netid);
                break;
              }
        }
@@ -531,16 +522,12 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
   /* set "known" tag for known hosts */
   if (config)
     {
-      known_id.net = "known";
-      known_id.next = netid;
-      netid = &known_id;
+      netid = dhcp_netid_create("known", netid);
     }
   else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len,
                       mess->chaddr, mess->hlen, mess->htype, NULL, 
run_tag_if(netid)))
     {
-      known_id.net = "known-othernet";
-      known_id.next = netid;
-      netid = &known_id;
+      netid = dhcp_netid_create("known-othernet", netid);
     }

   if (mess_type == 0 && !pxe)
@@ -580,16 +567,12 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
        {
          memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
          daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. 
*/
-         id.net = (char *)daemon->dhcp_buff2;
-         id.next = netid;
-         netid = &id;
+         netid = dhcp_netid_create((char *)daemon->dhcp_buff2, netid);
        }

       /* Add "bootp" as a tag to allow different options, address ranges etc
         for BOOTP clients */
-      bootp_id.net = "bootp";
-      bootp_id.next = netid;
-      netid = &bootp_id;
+      netid = dhcp_netid_create("bootp", netid);

       tagif_netid = run_tag_if(netid);

@@ -634,8 +617,8 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
            message = _("wrong network");
          else if (context->netid.net)
            {
-             context->netid.next = netid;
-             tagif_netid = run_tag_if(&context->netid);
+             netid = dhcp_netid_create(context->netid.net, netid);
+             tagif_netid = run_tag_if(netid);
            }

          log_tags(tagif_netid, ntohl(mess->xid));
@@ -770,8 +753,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
          if (hostname_isequal(client_hostname, m->name) &&
              (save == 0 || m->wildcard))
            {
-             m->netid->next = netid;
-             netid = m->netid;
+             netid = dhcp_netid_create(m->netid->net, netid);
            }

          if (save != 0)
@@ -808,9 +790,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
*iface_name, int int_index,
                {
                  config = new;
                  /* set "known" tag for known hosts */
-                 known_id.net = "known";
-                 known_id.next = netid;
-                 netid = &known_id;
+                 netid = dhcp_netid_create("known", netid);
                }
            }
        }
--
2.33.0
_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss

Reply via email to