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:
Simon Kelley
2019-03-16 18:17:17 +00:00
parent 608aa9fcfc
commit 305ffb5ef0
2 changed files with 84 additions and 42 deletions

View File

@@ -29,6 +29,11 @@ version 2.81
Support TCP-fastopen (RFC-7413) on both incoming and
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
Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method

View File

@@ -52,6 +52,9 @@ int main (int argc, char **argv)
#if defined(HAVE_LINUX_NETWORK)
cap_user_header_t hdr = 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;
int did_bind = 0;
#endif
@@ -285,11 +288,24 @@ int main (int argc, char **argv)
}
if (daemon->dhcp || daemon->relay4)
{
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
if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
{
ra_init(now);
# ifdef HAVE_LINUX_NETWORK
need_cap_net_raw = 1;
need_cap_net_admin = 1;
# endif
}
if (daemon->doing_dhcp6 || daemon->relay6)
dhcp6_init();
@@ -299,7 +315,12 @@ int main (int argc, char **argv)
#ifdef HAVE_IPSET
if (daemon->ipsets)
{
ipset_init();
# ifdef HAVE_LINUX_NETWORK
need_cap_net_admin = 1;
# endif
}
#endif
#if defined(HAVE_LINUX_NETWORK)
@@ -440,11 +461,20 @@ int main (int argc, char **argv)
}
#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
call safe_malloc */
if (ent_pw && ent_pw->pw_uid != 0)
{
int capsize = 1; /* for header version 1 */
char *fail = NULL;
hdr = safe_malloc(sizeof(*hdr));
/* find version supported by kernel */
@@ -460,8 +490,29 @@ int main (int argc, char **argv)
}
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);
}
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
/* 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 defined(HAVE_LINUX_NETWORK)
/* On linux, 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 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);
/* Need to be able to drop root. */
data->effective |= (1 << CAP_SETUID);
data->permitted |= (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
bad_capabilities = errno;
@@ -678,15 +720,10 @@ int main (int argc, char **argv)
}
#ifdef HAVE_LINUX_NETWORK
if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
data->effective = data->permitted =
(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;
data->effective &= ~(1 << CAP_SETUID);
data->permitted &= ~(1 << CAP_SETUID);
/* lose the setuid and setgid capabilities */
/* lose the setuid capability */
if (capset(hdr, data) == -1)
{
send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);