828f5adfa004f9ba5fcd0dad1affe953ca3c0e93
[oweals/openwrt.git] /
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
5  Linux.
6
7 Dnsmasq now fails early if a required capability is not available,
8 and tries not to request capabilities not required by its
9 configuration.
10
11 Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
12 ---
13  CHANGELOG     |   7 ++-
14  src/dnsmasq.c | 119 +++++++++++++++++++++++++++++++++-----------------
15  2 files changed, 84 insertions(+), 42 deletions(-)
16
17 --- a/CHANGELOG
18 +++ b/CHANGELOG
19 @@ -28,7 +28,12 @@ version 2.81
20  
21         Support TCP-fastopen (RFC-7413) on both incoming and
22         outgoing TCP connections, if supported and enabled in the OS.
23 -       
24 +
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
28 +       configuration.
29 +
30         
31  version 2.80
32         Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
33 --- a/src/dnsmasq.c
34 +++ b/src/dnsmasq.c
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;
43    int did_bind = 0;
44  #endif 
45 @@ -285,11 +288,24 @@ int main (int argc, char **argv)
46      }
47    
48    if (daemon->dhcp || daemon->relay4)
49 -    dhcp_init();
50 +    {
51 +      dhcp_init();
52 +#   ifdef HAVE_LINUX_NETWORK
53 +      if (!option_bool(OPT_NO_PING))
54 +       need_cap_net_raw = 1;
55 +      need_cap_net_admin = 1;
56 +#   endif
57 +    }
58    
59  #  ifdef HAVE_DHCP6
60    if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
61 -    ra_init(now);
62 +    {
63 +      ra_init(now);
64 +#   ifdef HAVE_LINUX_NETWORK
65 +      need_cap_net_raw = 1;
66 +      need_cap_net_admin = 1;
67 +#   endif
68 +    }
69    
70    if (daemon->doing_dhcp6 || daemon->relay6)
71      dhcp6_init();
72 @@ -299,7 +315,12 @@ int main (int argc, char **argv)
73  
74  #ifdef HAVE_IPSET
75    if (daemon->ipsets)
76 -    ipset_init();
77 +    {
78 +      ipset_init();
79 +#  ifdef HAVE_LINUX_NETWORK
80 +      need_cap_net_admin = 1;
81 +#  endif
82 +    }
83  #endif
84  
85  #if  defined(HAVE_LINUX_NETWORK)
86 @@ -440,28 +461,58 @@ int main (int argc, char **argv)
87      }
88  
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;
98 +
99    /* determine capability API version here, while we can still
100       call safe_malloc */
101 -  if (ent_pw && ent_pw->pw_uid != 0)
102 +  int capsize = 1; /* for header version 1 */
103 +  char *fail = NULL;
104 +  
105 +  hdr = safe_malloc(sizeof(*hdr));
106 +  
107 +  /* find version supported by kernel */
108 +  memset(hdr, 0, sizeof(*hdr));
109 +  capget(hdr, NULL);
110 +  
111 +  if (hdr->version != LINUX_CAPABILITY_VERSION_1)
112      {
113 -      int capsize = 1; /* for header version 1 */
114 -      hdr = safe_malloc(sizeof(*hdr));
115 -
116 -      /* find version supported by kernel */
117 -      memset(hdr, 0, sizeof(*hdr));
118 -      capget(hdr, NULL);
119 -      
120 -      if (hdr->version != LINUX_CAPABILITY_VERSION_1)
121 -       {
122 -         /* if unknown version, use largest supported version (3) */
123 -         if (hdr->version != LINUX_CAPABILITY_VERSION_2)
124 -           hdr->version = LINUX_CAPABILITY_VERSION_3;
125 -         capsize = 2;
126 -       }
127 -      
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;
133 +      capsize = 2;
134      }
135 +  
136 +  data = safe_malloc(sizeof(*data) * capsize);
137 +  capget(hdr, data); /* Get current values, for verification */
138 +
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)))
142 +    fail = "NET_RAW";
143 +  else if (need_cap_net_bind_service && !(data->permitted & (1 << CAP_NET_BIND_SERVICE)))
144 +    fail = "NET_BIND_SERVICE";
145 +  
146 +  if (fail)
147 +    die(_("process is missing required capability %s"), fail, EC_MISC);
148 +
149 +  /* Now set bitmaps to set caps after daemonising */
150 +  memset(data, 0, sizeof(*data) * capsize);
151 +  
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);
158 +  
159 +  data->permitted = data->effective;  
160  #endif
161  
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)
165         {     
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);
175 -         else
176 -           data->effective = data->permitted = data->inheritable =
177 -             (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
178 -         
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)
186             }     
187  
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);
192 -        else
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);
198           
199 -         /* lose the setuid and setgid capabilities */
200 +         /* lose the setuid capability */
201           if (capset(hdr, data) == -1)
202             {
203               send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);