1 From 305ffb5ef0ba5ab1df32ef80f266a4c9e395ca13 Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Sat, 16 Mar 2019 18:17:17 +0000
4 Subject: [PATCH 45/57] Improve kernel-capability manipulation code under
7 Dnsmasq now fails early if a required capability is not available,
8 and tries not to request capabilities not required by its
11 Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
14 src/dnsmasq.c | 119 +++++++++++++++++++++++++++++++++-----------------
15 2 files changed, 84 insertions(+), 42 deletions(-)
19 @@ -28,7 +28,12 @@ version 2.81
21 Support TCP-fastopen (RFC-7413) on both incoming and
22 outgoing TCP connections, if supported and enabled in the OS.
25 + Improve kernel-capability manipulation code under Linux. Dnsmasq
26 + now fails early if a required capability is not available, and
27 + tries not to request capabilities not required by its
32 Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
35 @@ -52,6 +52,9 @@ int main (int argc, char **argv)
36 #if defined(HAVE_LINUX_NETWORK)
37 cap_user_header_t hdr = NULL;
38 cap_user_data_t data = NULL;
39 + int need_cap_net_admin = 0;
40 + int need_cap_net_raw = 0;
41 + int need_cap_net_bind_service = 0;
42 char *bound_device = NULL;
45 @@ -285,11 +288,24 @@ int main (int argc, char **argv)
48 if (daemon->dhcp || daemon->relay4)
52 +# ifdef HAVE_LINUX_NETWORK
53 + if (!option_bool(OPT_NO_PING))
54 + need_cap_net_raw = 1;
55 + need_cap_net_admin = 1;
60 if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
64 +# ifdef HAVE_LINUX_NETWORK
65 + need_cap_net_raw = 1;
66 + need_cap_net_admin = 1;
70 if (daemon->doing_dhcp6 || daemon->relay6)
72 @@ -299,7 +315,12 @@ int main (int argc, char **argv)
79 +# ifdef HAVE_LINUX_NETWORK
80 + need_cap_net_admin = 1;
85 #if defined(HAVE_LINUX_NETWORK)
86 @@ -440,28 +461,58 @@ int main (int argc, char **argv)
89 #if defined(HAVE_LINUX_NETWORK)
90 + /* We keep CAP_NETADMIN (for ARP-injection) and
91 + CAP_NET_RAW (for icmp) if we're doing dhcp,
92 + if we have yet to bind ports because of DAD,
93 + or we're doing it dynamically,
94 + we need CAP_NET_BIND_SERVICE. */
95 + if ((is_dad_listeners() || option_bool(OPT_CLEVERBIND)) &&
96 + (option_bool(OPT_TFTP) || (daemon->port != 0 && daemon->port <= 1024)))
97 + need_cap_net_bind_service = 1;
99 /* determine capability API version here, while we can still
101 - if (ent_pw && ent_pw->pw_uid != 0)
102 + int capsize = 1; /* for header version 1 */
105 + hdr = safe_malloc(sizeof(*hdr));
107 + /* find version supported by kernel */
108 + memset(hdr, 0, sizeof(*hdr));
111 + if (hdr->version != LINUX_CAPABILITY_VERSION_1)
113 - int capsize = 1; /* for header version 1 */
114 - hdr = safe_malloc(sizeof(*hdr));
116 - /* find version supported by kernel */
117 - memset(hdr, 0, sizeof(*hdr));
120 - if (hdr->version != LINUX_CAPABILITY_VERSION_1)
122 - /* if unknown version, use largest supported version (3) */
123 - if (hdr->version != LINUX_CAPABILITY_VERSION_2)
124 - hdr->version = LINUX_CAPABILITY_VERSION_3;
128 - data = safe_malloc(sizeof(*data) * capsize);
129 - memset(data, 0, sizeof(*data) * capsize);
130 + /* if unknown version, use largest supported version (3) */
131 + if (hdr->version != LINUX_CAPABILITY_VERSION_2)
132 + hdr->version = LINUX_CAPABILITY_VERSION_3;
136 + data = safe_malloc(sizeof(*data) * capsize);
137 + capget(hdr, data); /* Get current values, for verification */
139 + if (need_cap_net_admin && !(data->permitted & (1 << CAP_NET_ADMIN)))
140 + fail = "NET_ADMIN";
141 + else if (need_cap_net_raw && !(data->permitted & (1 << CAP_NET_RAW)))
143 + else if (need_cap_net_bind_service && !(data->permitted & (1 << CAP_NET_BIND_SERVICE)))
144 + fail = "NET_BIND_SERVICE";
147 + die(_("process is missing required capability %s"), fail, EC_MISC);
149 + /* Now set bitmaps to set caps after daemonising */
150 + memset(data, 0, sizeof(*data) * capsize);
152 + if (need_cap_net_admin)
153 + data->effective |= (1 << CAP_NET_ADMIN);
154 + if (need_cap_net_raw)
155 + data->effective |= (1 << CAP_NET_RAW);
156 + if (need_cap_net_bind_service)
157 + data->effective |= (1 << CAP_NET_BIND_SERVICE);
159 + data->permitted = data->effective;
162 /* Use a pipe to carry signals and other events back to the event loop
163 @@ -626,18 +677,9 @@ int main (int argc, char **argv)
164 if (ent_pw && ent_pw->pw_uid != 0)
166 #if defined(HAVE_LINUX_NETWORK)
167 - /* On linux, we keep CAP_NETADMIN (for ARP-injection) and
168 - CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind
169 - ports because of DAD, or we're doing it dynamically,
170 - we need CAP_NET_BIND_SERVICE too. */
171 - if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
172 - data->effective = data->permitted = data->inheritable =
173 - (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
174 - (1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
176 - data->effective = data->permitted = data->inheritable =
177 - (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
179 + /* Need to be able to drop root. */
180 + data->effective |= (1 << CAP_SETUID);
181 + data->permitted |= (1 << CAP_SETUID);
182 /* Tell kernel to not clear capabilities when dropping root */
183 if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
184 bad_capabilities = errno;
185 @@ -678,15 +720,10 @@ int main (int argc, char **argv)
188 #ifdef HAVE_LINUX_NETWORK
189 - if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
190 - data->effective = data->permitted =
191 - (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
193 - data->effective = data->permitted =
194 - (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
195 - data->inheritable = 0;
196 + data->effective &= ~(1 << CAP_SETUID);
197 + data->permitted &= ~(1 << CAP_SETUID);
199 - /* lose the setuid and setgid capabilities */
200 + /* lose the setuid capability */
201 if (capset(hdr, data) == -1)
203 send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);