1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "sandboxed_api/sandbox2/network_proxy/filtering.h"
16
17 #include <arpa/inet.h>
18 #include <netinet/in.h>
19 #include <sys/socket.h>
20
21 #include <algorithm>
22 #include <cerrno>
23 #include <cstdint>
24 #include <cstring>
25 #include <string>
26 #include <vector>
27
28 #include "absl/log/log.h"
29 #include "absl/status/status.h"
30 #include "absl/status/statusor.h"
31 #include "absl/strings/match.h"
32 #include "absl/strings/numbers.h"
33 #include "absl/strings/str_cat.h"
34 #include "absl/strings/str_split.h"
35 #include "sandboxed_api/util/status_macros.h"
36
37 namespace sandbox2 {
38
Addr6ToString(const struct sockaddr_in6 * saddr)39 static absl::StatusOr<std::string> Addr6ToString(
40 const struct sockaddr_in6* saddr) {
41 char addr[INET6_ADDRSTRLEN];
42 int port = htons(saddr->sin6_port);
43 if (!inet_ntop(AF_INET6, &saddr->sin6_addr, addr, sizeof addr)) {
44 return absl::InternalError(
45 "Error in converting sockaddr_in6 addres to string");
46 }
47 return absl::StrCat("IP: ", addr, ", port: ", port);
48 }
49
50 // Converts sockaddr_in structure into a string IPv4 representation.
Addr4ToString(const struct sockaddr_in * saddr)51 static absl::StatusOr<std::string> Addr4ToString(
52 const struct sockaddr_in* saddr) {
53 char addr[INET_ADDRSTRLEN];
54 int port = htons(saddr->sin_port);
55 if (!inet_ntop(AF_INET, &saddr->sin_addr, addr, sizeof addr)) {
56 return absl::InternalError(
57 "Error in converting sockaddr_in addres to string");
58 }
59 return absl::StrCat("IP: ", addr, ", port: ", port);
60 }
61
62 // Converts sockaddr_in6 structure into a string IPv6 representation.
AddrToString(const struct sockaddr * saddr)63 absl::StatusOr<std::string> AddrToString(const struct sockaddr* saddr) {
64 switch (saddr->sa_family) {
65 case AF_INET:
66 return Addr4ToString(reinterpret_cast<const struct sockaddr_in*>(saddr));
67 case AF_INET6:
68 return Addr6ToString(reinterpret_cast<const struct sockaddr_in6*>(saddr));
69 default:
70 return absl::InternalError(
71 absl::StrCat("Unexpected sa_family value: ", saddr->sa_family));
72 }
73 }
74
IPStringToAddr(const std::string & ip,int address_family,void * addr)75 static absl::Status IPStringToAddr(const std::string& ip, int address_family,
76 void* addr) {
77 if (int err = inet_pton(address_family, ip.c_str(), addr); err == 0) {
78 return absl::InvalidArgumentError(absl::StrCat("Invalid address: ", ip));
79 } else if (err == -1) {
80 return absl::ErrnoToStatus(errno,
81 absl::StrCat("inet_pton() failed for ", ip));
82 }
83 return absl::OkStatus();
84 }
85
86 // Parses a string of type IP or IP/mask or IP/cidr and saves appropriate
87 // values in output arguments.
ParseIpAndMask(const std::string & ip_and_mask,std::string * ip,std::string * mask,uint32_t * cidr)88 static absl::Status ParseIpAndMask(const std::string& ip_and_mask,
89 std::string* ip, std::string* mask,
90 uint32_t* cidr) {
91 // mask is checked later because only IPv4 format supports mask
92 if (ip == nullptr || cidr == nullptr) {
93 return absl::InvalidArgumentError(
94 "ip and cidr arguments of ParseIpAndMask cannot be nullptr");
95 }
96 *cidr = 0;
97
98 std::vector<std::string> ip_and_mask_split =
99 absl::StrSplit(ip_and_mask, absl::MaxSplits('/', 1));
100
101 *ip = ip_and_mask_split[0];
102 if (ip_and_mask_split.size() == 1) {
103 return absl::OkStatus();
104 }
105 std::string mask_or_cidr = ip_and_mask_split[1];
106
107 const bool has_dot = absl::StrContains(mask_or_cidr, '.');
108 if (has_dot) {
109 if (mask == nullptr) {
110 return absl::InvalidArgumentError(
111 "mask argument of ParseIpAndMask cannot be NULL in this case");
112 }
113 *mask = std::string(mask_or_cidr);
114 } else { // mask_or_cidr is cidr
115 bool res = absl::SimpleAtoi<uint32_t>(mask_or_cidr, cidr);
116 if (!res || !*cidr) {
117 return absl::InvalidArgumentError(
118 absl::StrCat(mask_or_cidr, " is not a correct cidr"));
119 }
120 }
121 return absl::OkStatus();
122 }
123
CidrToIn6Addr(uint32_t cidr,in6_addr * addr)124 static absl::Status CidrToIn6Addr(uint32_t cidr, in6_addr* addr) {
125 if (cidr > 128) {
126 return absl::InvalidArgumentError(
127 absl::StrCat(cidr, " is not a correct cidr"));
128 }
129
130 memset(addr, 0, sizeof(*addr));
131
132 int i = 0;
133 while (cidr >= 8) {
134 addr->s6_addr[i++] = 0xff;
135 cidr -= 8;
136 }
137 if (cidr) {
138 uint8_t tmp = 0x0;
139 while (cidr--) {
140 tmp >>= 1;
141 tmp |= 0x80;
142 }
143 addr->s6_addr[i] = tmp;
144 }
145 return absl::OkStatus();
146 }
147
CidrToInAddr(uint32_t cidr,in_addr * addr)148 static absl::Status CidrToInAddr(uint32_t cidr, in_addr* addr) {
149 if (cidr > 32) {
150 return absl::InvalidArgumentError(
151 absl::StrCat(cidr, " is not a correct cidr"));
152 }
153
154 memset(addr, 0, sizeof(*addr));
155
156 uint32_t tmp = 0x0;
157 while (cidr--) {
158 tmp >>= 1;
159 tmp |= 0x80000000;
160 }
161 addr->s_addr = htonl(tmp);
162 return absl::OkStatus();
163 }
164
IsIPv4MaskCorrect(in_addr_t m)165 static bool IsIPv4MaskCorrect(in_addr_t m) {
166 m = ntohl(m);
167 if (m == 0) {
168 return false;
169 }
170 m = ~m + 1;
171 return !(m & (m - 1));
172 }
173
AllowIPv4(const std::string & ip_and_mask,uint32_t port)174 absl::Status AllowedHosts::AllowIPv4(const std::string& ip_and_mask,
175 uint32_t port) {
176 std::string ip, mask;
177 uint32_t cidr;
178 SAPI_RETURN_IF_ERROR(ParseIpAndMask(ip_and_mask, &ip, &mask, &cidr));
179 SAPI_RETURN_IF_ERROR(AllowIPv4(ip, mask, cidr, port));
180
181 return absl::OkStatus();
182 }
183
AllowIPv6(const std::string & ip_and_mask,uint32_t port)184 absl::Status AllowedHosts::AllowIPv6(const std::string& ip_and_mask,
185 uint32_t port) {
186 std::string ip;
187 uint32_t cidr;
188 SAPI_RETURN_IF_ERROR(ParseIpAndMask(ip_and_mask, &ip, NULL, &cidr));
189 SAPI_RETURN_IF_ERROR(AllowIPv6(ip, cidr, port));
190 return absl::OkStatus();
191 }
192
AllowIPv4(const std::string & ip,const std::string & mask,uint32_t cidr,uint32_t port)193 absl::Status AllowedHosts::AllowIPv4(const std::string& ip,
194 const std::string& mask, uint32_t cidr,
195 uint32_t port) {
196 in_addr addr{};
197 in_addr m{};
198
199 if (!mask.empty()) {
200 SAPI_RETURN_IF_ERROR(IPStringToAddr(mask, AF_INET, &m));
201
202 if (!IsIPv4MaskCorrect(m.s_addr)) {
203 return absl::InvalidArgumentError(
204 absl::StrCat(mask, " is not a correct mask"));
205 }
206
207 } else {
208 if (cidr > 32) {
209 return absl::InvalidArgumentError(
210 absl::StrCat(cidr, " is not a correct cidr"));
211 }
212 if (!cidr) {
213 cidr = 32;
214 }
215
216 SAPI_RETURN_IF_ERROR(CidrToInAddr(cidr, &m));
217 }
218
219 SAPI_RETURN_IF_ERROR(IPStringToAddr(ip, AF_INET, &addr));
220 allowed_IPv4_.emplace_back(addr.s_addr, m.s_addr, htons(port));
221
222 return absl::OkStatus();
223 }
224
AllowIPv6(const std::string & ip,uint32_t cidr,uint32_t port)225 absl::Status AllowedHosts::AllowIPv6(const std::string& ip, uint32_t cidr,
226 uint32_t port) {
227 if (cidr == 0) {
228 cidr = 128;
229 }
230
231 in6_addr addr{};
232 SAPI_RETURN_IF_ERROR(IPStringToAddr(ip, AF_INET6, &addr));
233
234 in6_addr m;
235 SAPI_RETURN_IF_ERROR(CidrToIn6Addr(cidr, &m));
236
237 allowed_IPv6_.emplace_back(addr, m, htons(port));
238 return absl::OkStatus();
239 }
240
IsHostAllowed(const struct sockaddr * saddr) const241 bool AllowedHosts::IsHostAllowed(const struct sockaddr* saddr) const {
242 switch (saddr->sa_family) {
243 case AF_INET:
244 return IsIPv4Allowed(reinterpret_cast<const struct sockaddr_in*>(saddr));
245 case AF_INET6:
246 return IsIPv6Allowed(reinterpret_cast<const struct sockaddr_in6*>(saddr));
247 default:
248 LOG(FATAL) << absl::StrCat("Unexpected sa_family value: ",
249 saddr->sa_family);
250 return false;
251 }
252 }
253
IsIPv6Allowed(const struct sockaddr_in6 * saddr) const254 bool AllowedHosts::IsIPv6Allowed(const struct sockaddr_in6* saddr) const {
255 auto result = std::find_if(
256 allowed_IPv6_.begin(), allowed_IPv6_.end(), [saddr](const IPv6& entry) {
257 for (int i = 0; i < 16; i++) {
258 if ((entry.ip.s6_addr[i] & entry.mask.s6_addr[i]) !=
259 (saddr->sin6_addr.s6_addr[i] & entry.mask.s6_addr[i])) {
260 return false;
261 }
262 }
263 if (!entry.port || (entry.port == saddr->sin6_port)) {
264 return true;
265 }
266 return false;
267 });
268
269 return result != allowed_IPv6_.end();
270 }
271
IsIPv4Allowed(const struct sockaddr_in * saddr) const272 bool AllowedHosts::IsIPv4Allowed(const struct sockaddr_in* saddr) const {
273 auto result = std::find_if(
274 allowed_IPv4_.begin(), allowed_IPv4_.end(), [saddr](const IPv4& entry) {
275 return ((entry.ip & entry.mask) ==
276 (saddr->sin_addr.s_addr & entry.mask)) &&
277 (!entry.port || (entry.port == saddr->sin_port));
278 });
279
280 return result != allowed_IPv4_.end();
281 }
282
283 } // namespace sandbox2
284