mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Improve kernel-capability manipulation code under Linux.
Dnsmasq now fails early if a required capability is not available, and tries not to request capabilities not required by its configuration.
This commit is contained in:
@@ -29,6 +29,11 @@ version 2.81
|
|||||||
Support TCP-fastopen (RFC-7413) on both incoming and
|
Support TCP-fastopen (RFC-7413) on both incoming and
|
||||||
outgoing TCP connections, if supported and enabled in the OS.
|
outgoing TCP connections, if supported and enabled in the OS.
|
||||||
|
|
||||||
|
Improve kernel-capability manipulation code under Linux. Dnsmasq
|
||||||
|
now fails early if a required capability is not available, and
|
||||||
|
tries not to request capabilities not required by its
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
|
||||||
version 2.80
|
version 2.80
|
||||||
Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
|
Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ int main (int argc, char **argv)
|
|||||||
#if defined(HAVE_LINUX_NETWORK)
|
#if defined(HAVE_LINUX_NETWORK)
|
||||||
cap_user_header_t hdr = NULL;
|
cap_user_header_t hdr = NULL;
|
||||||
cap_user_data_t data = NULL;
|
cap_user_data_t data = NULL;
|
||||||
|
int need_cap_net_admin = 0;
|
||||||
|
int need_cap_net_raw = 0;
|
||||||
|
int need_cap_net_bind_service = 0;
|
||||||
char *bound_device = NULL;
|
char *bound_device = NULL;
|
||||||
int did_bind = 0;
|
int did_bind = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -285,11 +288,24 @@ int main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (daemon->dhcp || daemon->relay4)
|
if (daemon->dhcp || daemon->relay4)
|
||||||
|
{
|
||||||
dhcp_init();
|
dhcp_init();
|
||||||
|
# ifdef HAVE_LINUX_NETWORK
|
||||||
|
if (!option_bool(OPT_NO_PING))
|
||||||
|
need_cap_net_raw = 1;
|
||||||
|
need_cap_net_admin = 1;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
# ifdef HAVE_DHCP6
|
# ifdef HAVE_DHCP6
|
||||||
if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
|
if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
|
||||||
|
{
|
||||||
ra_init(now);
|
ra_init(now);
|
||||||
|
# ifdef HAVE_LINUX_NETWORK
|
||||||
|
need_cap_net_raw = 1;
|
||||||
|
need_cap_net_admin = 1;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
if (daemon->doing_dhcp6 || daemon->relay6)
|
if (daemon->doing_dhcp6 || daemon->relay6)
|
||||||
dhcp6_init();
|
dhcp6_init();
|
||||||
@@ -299,7 +315,12 @@ int main (int argc, char **argv)
|
|||||||
|
|
||||||
#ifdef HAVE_IPSET
|
#ifdef HAVE_IPSET
|
||||||
if (daemon->ipsets)
|
if (daemon->ipsets)
|
||||||
|
{
|
||||||
ipset_init();
|
ipset_init();
|
||||||
|
# ifdef HAVE_LINUX_NETWORK
|
||||||
|
need_cap_net_admin = 1;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_LINUX_NETWORK)
|
#if defined(HAVE_LINUX_NETWORK)
|
||||||
@@ -440,11 +461,20 @@ int main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_LINUX_NETWORK)
|
#if defined(HAVE_LINUX_NETWORK)
|
||||||
|
/* We keep CAP_NETADMIN (for ARP-injection) and
|
||||||
|
CAP_NET_RAW (for icmp) if we're doing dhcp,
|
||||||
|
if we have yet to bind ports because of DAD,
|
||||||
|
or we're doing it dynamically,
|
||||||
|
we need CAP_NET_BIND_SERVICE. */
|
||||||
|
if ((is_dad_listeners() || option_bool(OPT_CLEVERBIND)) &&
|
||||||
|
(option_bool(OPT_TFTP) || (daemon->port != 0 && daemon->port <= 1024)))
|
||||||
|
need_cap_net_bind_service = 1;
|
||||||
|
|
||||||
/* determine capability API version here, while we can still
|
/* determine capability API version here, while we can still
|
||||||
call safe_malloc */
|
call safe_malloc */
|
||||||
if (ent_pw && ent_pw->pw_uid != 0)
|
|
||||||
{
|
|
||||||
int capsize = 1; /* for header version 1 */
|
int capsize = 1; /* for header version 1 */
|
||||||
|
char *fail = NULL;
|
||||||
|
|
||||||
hdr = safe_malloc(sizeof(*hdr));
|
hdr = safe_malloc(sizeof(*hdr));
|
||||||
|
|
||||||
/* find version supported by kernel */
|
/* find version supported by kernel */
|
||||||
@@ -460,8 +490,29 @@ int main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
data = safe_malloc(sizeof(*data) * capsize);
|
data = safe_malloc(sizeof(*data) * capsize);
|
||||||
|
capget(hdr, data); /* Get current values, for verification */
|
||||||
|
|
||||||
|
if (need_cap_net_admin && !(data->permitted & (1 << CAP_NET_ADMIN)))
|
||||||
|
fail = "NET_ADMIN";
|
||||||
|
else if (need_cap_net_raw && !(data->permitted & (1 << CAP_NET_RAW)))
|
||||||
|
fail = "NET_RAW";
|
||||||
|
else if (need_cap_net_bind_service && !(data->permitted & (1 << CAP_NET_BIND_SERVICE)))
|
||||||
|
fail = "NET_BIND_SERVICE";
|
||||||
|
|
||||||
|
if (fail)
|
||||||
|
die(_("process is missing required capability %s"), fail, EC_MISC);
|
||||||
|
|
||||||
|
/* Now set bitmaps to set caps after daemonising */
|
||||||
memset(data, 0, sizeof(*data) * capsize);
|
memset(data, 0, sizeof(*data) * capsize);
|
||||||
}
|
|
||||||
|
if (need_cap_net_admin)
|
||||||
|
data->effective |= (1 << CAP_NET_ADMIN);
|
||||||
|
if (need_cap_net_raw)
|
||||||
|
data->effective |= (1 << CAP_NET_RAW);
|
||||||
|
if (need_cap_net_bind_service)
|
||||||
|
data->effective |= (1 << CAP_NET_BIND_SERVICE);
|
||||||
|
|
||||||
|
data->permitted = data->effective;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Use a pipe to carry signals and other events back to the event loop
|
/* Use a pipe to carry signals and other events back to the event loop
|
||||||
@@ -626,18 +677,9 @@ int main (int argc, char **argv)
|
|||||||
if (ent_pw && ent_pw->pw_uid != 0)
|
if (ent_pw && ent_pw->pw_uid != 0)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_LINUX_NETWORK)
|
#if defined(HAVE_LINUX_NETWORK)
|
||||||
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
|
/* Need to be able to drop root. */
|
||||||
CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind
|
data->effective |= (1 << CAP_SETUID);
|
||||||
ports because of DAD, or we're doing it dynamically,
|
data->permitted |= (1 << CAP_SETUID);
|
||||||
we need CAP_NET_BIND_SERVICE too. */
|
|
||||||
if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
|
|
||||||
data->effective = data->permitted = data->inheritable =
|
|
||||||
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
|
|
||||||
(1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
|
|
||||||
else
|
|
||||||
data->effective = data->permitted = data->inheritable =
|
|
||||||
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
|
|
||||||
|
|
||||||
/* Tell kernel to not clear capabilities when dropping root */
|
/* Tell kernel to not clear capabilities when dropping root */
|
||||||
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
|
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
|
||||||
bad_capabilities = errno;
|
bad_capabilities = errno;
|
||||||
@@ -678,15 +720,10 @@ int main (int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_NETWORK
|
#ifdef HAVE_LINUX_NETWORK
|
||||||
if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
|
data->effective &= ~(1 << CAP_SETUID);
|
||||||
data->effective = data->permitted =
|
data->permitted &= ~(1 << CAP_SETUID);
|
||||||
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
|
|
||||||
else
|
|
||||||
data->effective = data->permitted =
|
|
||||||
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
|
|
||||||
data->inheritable = 0;
|
|
||||||
|
|
||||||
/* lose the setuid and setgid capabilities */
|
/* lose the setuid capability */
|
||||||
if (capset(hdr, data) == -1)
|
if (capset(hdr, data) == -1)
|
||||||
{
|
{
|
||||||
send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);
|
send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);
|
||||||
|
|||||||
Reference in New Issue
Block a user