blob: b5c3e928746729fbdd38aa1e9bd0a4914b7e5b3a [file] [log] [blame]
ludi1a06aa72017-05-12 09:15:00 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.system.OsConstants.AF_INET;
20import static android.system.OsConstants.EADDRINUSE;
21import static android.system.OsConstants.IPPROTO_UDP;
22import static android.system.OsConstants.SOCK_DGRAM;
Brett Chabot1ae2aa62019-03-04 14:14:56 -080023
ludi1a06aa72017-05-12 09:15:00 -070024import static org.junit.Assert.assertEquals;
Benedict Wongf186d672017-10-10 20:44:28 -070025import static org.junit.Assert.assertNotEquals;
ludi1a06aa72017-05-12 09:15:00 -070026import static org.junit.Assert.assertNotNull;
ludiaa5c1dc2017-10-16 15:09:41 -070027import static org.junit.Assert.assertTrue;
ludi1a06aa72017-05-12 09:15:00 -070028import static org.junit.Assert.fail;
ludiaa5c1dc2017-10-16 15:09:41 -070029import static org.mockito.Matchers.anyInt;
30import static org.mockito.Matchers.anyString;
Benedict Wongbabe5d72017-12-03 19:42:36 -080031import static org.mockito.Matchers.argThat;
ludiaa5c1dc2017-10-16 15:09:41 -070032import static org.mockito.Matchers.eq;
ludi1a06aa72017-05-12 09:15:00 -070033import static org.mockito.Mockito.mock;
34import static org.mockito.Mockito.verify;
35import static org.mockito.Mockito.when;
36
37import android.content.Context;
38import android.net.INetd;
Benedict Wong4f255702017-11-06 20:49:10 -080039import android.net.IpSecAlgorithm;
40import android.net.IpSecConfig;
ludi1a06aa72017-05-12 09:15:00 -070041import android.net.IpSecManager;
42import android.net.IpSecSpiResponse;
ludi1a06aa72017-05-12 09:15:00 -070043import android.net.IpSecUdpEncapResponse;
44import android.os.Binder;
45import android.os.ParcelFileDescriptor;
Benedict Wongbabe5d72017-12-03 19:42:36 -080046import android.os.Process;
ludi1a06aa72017-05-12 09:15:00 -070047import android.system.ErrnoException;
48import android.system.Os;
Benedict Wongbabe5d72017-12-03 19:42:36 -080049import android.system.StructStat;
50
Brett Chabot1ae2aa62019-03-04 14:14:56 -080051import androidx.test.filters.SmallTest;
52import androidx.test.runner.AndroidJUnit4;
53
Benedict Wongbabe5d72017-12-03 19:42:36 -080054import dalvik.system.SocketTagger;
Nathan Harold2e9a5202017-09-26 11:44:23 -070055
Brett Chabot1ae2aa62019-03-04 14:14:56 -080056import org.junit.Before;
57import org.junit.Test;
58import org.junit.runner.RunWith;
59import org.mockito.ArgumentMatcher;
60
ludi1a06aa72017-05-12 09:15:00 -070061import java.io.FileDescriptor;
62import java.net.InetAddress;
63import java.net.ServerSocket;
64import java.net.Socket;
65import java.net.UnknownHostException;
ludiaa5c1dc2017-10-16 15:09:41 -070066import java.util.ArrayList;
67import java.util.List;
Nathan Harold2e9a5202017-09-26 11:44:23 -070068
ludi1a06aa72017-05-12 09:15:00 -070069/** Unit tests for {@link IpSecService}. */
70@SmallTest
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090071@RunWith(AndroidJUnit4.class)
ludi1a06aa72017-05-12 09:15:00 -070072public class IpSecServiceTest {
73
74 private static final int DROID_SPI = 0xD1201D;
ludiaa5c1dc2017-10-16 15:09:41 -070075 private static final int MAX_NUM_ENCAP_SOCKETS = 100;
76 private static final int MAX_NUM_SPIS = 100;
ludi1a06aa72017-05-12 09:15:00 -070077 private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
78 private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
ludi1a06aa72017-05-12 09:15:00 -070079
80 private static final InetAddress INADDR_ANY;
81
Benedict Wong4f255702017-11-06 20:49:10 -080082 private static final byte[] AEAD_KEY = {
83 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
84 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
85 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
86 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
87 0x73, 0x61, 0x6C, 0x74
88 };
89 private static final byte[] CRYPT_KEY = {
90 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
91 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
92 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
93 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
94 };
95 private static final byte[] AUTH_KEY = {
96 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
98 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
100 };
101
102 private static final IpSecAlgorithm AUTH_ALGO =
103 new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
104 private static final IpSecAlgorithm CRYPT_ALGO =
105 new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
106 private static final IpSecAlgorithm AEAD_ALGO =
107 new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
108
ludi1a06aa72017-05-12 09:15:00 -0700109 static {
110 try {
111 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
112 } catch (UnknownHostException e) {
113 throw new RuntimeException(e);
114 }
115 }
116
ludi1a06aa72017-05-12 09:15:00 -0700117 Context mMockContext;
118 INetd mMockNetd;
119 IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
120 IpSecService mIpSecService;
121
122 @Before
123 public void setUp() throws Exception {
124 mMockContext = mock(Context.class);
125 mMockNetd = mock(INetd.class);
126 mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
127 mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
128
129 // Injecting mock netd
130 when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
131 }
132
133 @Test
134 public void testIpSecServiceCreate() throws InterruptedException {
135 IpSecService ipSecSrv = IpSecService.create(mMockContext);
136 assertNotNull(ipSecSrv);
137 }
138
139 @Test
ludi1a06aa72017-05-12 09:15:00 -0700140 public void testReleaseInvalidSecurityParameterIndex() throws Exception {
141 try {
142 mIpSecService.releaseSecurityParameterIndex(1);
143 fail("IllegalArgumentException not thrown");
144 } catch (IllegalArgumentException e) {
145 }
146 }
147
148 /** This function finds an available port */
149 int findUnusedPort() throws Exception {
150 // Get an available port.
151 ServerSocket s = new ServerSocket(0);
152 int port = s.getLocalPort();
153 s.close();
154 return port;
155 }
156
157 @Test
158 public void testOpenAndCloseUdpEncapsulationSocket() throws Exception {
159 int localport = findUnusedPort();
160
161 IpSecUdpEncapResponse udpEncapResp =
162 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
163 assertNotNull(udpEncapResp);
164 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
165 assertEquals(localport, udpEncapResp.port);
166
167 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
168 udpEncapResp.fileDescriptor.close();
169
Benedict Wong4f9fb812017-12-13 17:16:53 -0800170 // Verify quota and RefcountedResource objects cleaned up
Benedict Wong344bd622017-11-16 15:27:22 -0800171 IpSecService.UserRecord userRecord =
172 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
173 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
174 try {
175 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
176 fail("Expected IllegalArgumentException on attempt to access deleted resource");
177 } catch (IllegalArgumentException expected) {
178
179 }
180 }
181
182 @Test
183 public void testUdpEncapsulationSocketBinderDeath() throws Exception {
Benedict Wong344bd622017-11-16 15:27:22 -0800184 IpSecUdpEncapResponse udpEncapResp =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800185 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
Benedict Wong344bd622017-11-16 15:27:22 -0800186
187 IpSecService.UserRecord userRecord =
188 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
189 IpSecService.RefcountedResource refcountedRecord =
190 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
191 udpEncapResp.resourceId);
192
193 refcountedRecord.binderDied();
194
Benedict Wong4f9fb812017-12-13 17:16:53 -0800195 // Verify quota and RefcountedResource objects cleaned up
Benedict Wong344bd622017-11-16 15:27:22 -0800196 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
197 try {
198 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
199 fail("Expected IllegalArgumentException on attempt to access deleted resource");
200 } catch (IllegalArgumentException expected) {
201
202 }
ludi1a06aa72017-05-12 09:15:00 -0700203 }
204
205 @Test
206 public void testOpenUdpEncapsulationSocketAfterClose() throws Exception {
207 int localport = findUnusedPort();
208 IpSecUdpEncapResponse udpEncapResp =
209 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
210 assertNotNull(udpEncapResp);
211 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
212 assertEquals(localport, udpEncapResp.port);
213
214 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
215 udpEncapResp.fileDescriptor.close();
216
217 /** Check if localport is available. */
218 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
219 Os.bind(newSocket, INADDR_ANY, localport);
220 Os.close(newSocket);
221 }
222
223 /**
224 * This function checks if the IpSecService holds the reserved port. If
225 * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete.
226 */
227 @Test
228 public void testUdpEncapPortNotReleased() throws Exception {
229 int localport = findUnusedPort();
230 IpSecUdpEncapResponse udpEncapResp =
231 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
232 assertNotNull(udpEncapResp);
233 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
234 assertEquals(localport, udpEncapResp.port);
235
236 udpEncapResp.fileDescriptor.close();
237
238 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
239 try {
240 Os.bind(newSocket, INADDR_ANY, localport);
241 fail("ErrnoException not thrown");
242 } catch (ErrnoException e) {
243 assertEquals(EADDRINUSE, e.errno);
244 }
245 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
246 }
247
248 @Test
249 public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception {
250 IpSecUdpEncapResponse udpEncapResp =
251 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
252 assertNotNull(udpEncapResp);
253 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
Benedict Wongf186d672017-10-10 20:44:28 -0700254 assertNotEquals(0, udpEncapResp.port);
ludi1a06aa72017-05-12 09:15:00 -0700255 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
256 udpEncapResp.fileDescriptor.close();
257 }
258
259 @Test
260 public void testOpenUdpEncapsulationSocketPortRange() throws Exception {
261 try {
262 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder());
263 fail("IllegalArgumentException not thrown");
264 } catch (IllegalArgumentException e) {
265 }
266
267 try {
268 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder());
269 fail("IllegalArgumentException not thrown");
270 } catch (IllegalArgumentException e) {
271 }
272 }
273
274 @Test
275 public void testOpenUdpEncapsulationSocketTwice() throws Exception {
276 int localport = findUnusedPort();
277
278 IpSecUdpEncapResponse udpEncapResp =
279 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
280 assertNotNull(udpEncapResp);
281 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
282 assertEquals(localport, udpEncapResp.port);
283 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
284
285 IpSecUdpEncapResponse testUdpEncapResp =
286 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
287 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status);
288
289 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
290 udpEncapResp.fileDescriptor.close();
291 }
292
293 @Test
294 public void testCloseInvalidUdpEncapsulationSocket() throws Exception {
295 try {
296 mIpSecService.closeUdpEncapsulationSocket(1);
297 fail("IllegalArgumentException not thrown");
298 } catch (IllegalArgumentException e) {
299 }
300 }
301
ludi1a06aa72017-05-12 09:15:00 -0700302 @Test
Benedict Wong4f255702017-11-06 20:49:10 -0800303 public void testValidateAlgorithmsAuth() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800304 // Validate that correct algorithm type succeeds
305 IpSecConfig config = new IpSecConfig();
306 config.setAuthentication(AUTH_ALGO);
307 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800308
Nathan Harold5676f5f2018-01-16 19:34:01 -0800309 // Validate that incorrect algorithm types fails
310 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
311 try {
312 config = new IpSecConfig();
313 config.setAuthentication(algo);
314 mIpSecService.validateAlgorithms(config);
315 fail("Did not throw exception on invalid algorithm type");
316 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800317 }
318 }
319 }
320
321 @Test
322 public void testValidateAlgorithmsCrypt() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800323 // Validate that correct algorithm type succeeds
324 IpSecConfig config = new IpSecConfig();
325 config.setEncryption(CRYPT_ALGO);
326 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800327
Nathan Harold5676f5f2018-01-16 19:34:01 -0800328 // Validate that incorrect algorithm types fails
329 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
330 try {
331 config = new IpSecConfig();
332 config.setEncryption(algo);
333 mIpSecService.validateAlgorithms(config);
334 fail("Did not throw exception on invalid algorithm type");
335 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800336 }
337 }
338 }
339
340 @Test
341 public void testValidateAlgorithmsAead() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800342 // Validate that correct algorithm type succeeds
343 IpSecConfig config = new IpSecConfig();
344 config.setAuthenticatedEncryption(AEAD_ALGO);
345 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800346
Nathan Harold5676f5f2018-01-16 19:34:01 -0800347 // Validate that incorrect algorithm types fails
348 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
349 try {
350 config = new IpSecConfig();
351 config.setAuthenticatedEncryption(algo);
352 mIpSecService.validateAlgorithms(config);
353 fail("Did not throw exception on invalid algorithm type");
354 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800355 }
356 }
357 }
358
359 @Test
360 public void testValidateAlgorithmsAuthCrypt() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800361 // Validate that correct algorithm type succeeds
362 IpSecConfig config = new IpSecConfig();
363 config.setAuthentication(AUTH_ALGO);
364 config.setEncryption(CRYPT_ALGO);
365 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800366 }
367
368 @Test
369 public void testValidateAlgorithmsNoAlgorithms() {
370 IpSecConfig config = new IpSecConfig();
371 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800372 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800373 fail("Expected exception; no algorithms specified");
374 } catch (IllegalArgumentException expected) {
375 }
376 }
377
378 @Test
379 public void testValidateAlgorithmsAeadWithAuth() {
380 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800381 config.setAuthenticatedEncryption(AEAD_ALGO);
382 config.setAuthentication(AUTH_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800383 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800384 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800385 fail("Expected exception; both AEAD and auth algorithm specified");
386 } catch (IllegalArgumentException expected) {
387 }
388 }
389
390 @Test
391 public void testValidateAlgorithmsAeadWithCrypt() {
392 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800393 config.setAuthenticatedEncryption(AEAD_ALGO);
394 config.setEncryption(CRYPT_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800395 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800396 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800397 fail("Expected exception; both AEAD and crypt algorithm specified");
398 } catch (IllegalArgumentException expected) {
399 }
400 }
401
402 @Test
403 public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
404 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800405 config.setAuthenticatedEncryption(AEAD_ALGO);
406 config.setAuthentication(AUTH_ALGO);
407 config.setEncryption(CRYPT_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800408 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800409 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800410 fail("Expected exception; AEAD, auth and crypt algorithm specified");
411 } catch (IllegalArgumentException expected) {
412 }
413 }
414
415 @Test
Benedict Wongf33f03132018-01-18 14:38:16 -0800416 public void testDeleteInvalidTransform() throws Exception {
ludi1a06aa72017-05-12 09:15:00 -0700417 try {
Benedict Wongf33f03132018-01-18 14:38:16 -0800418 mIpSecService.deleteTransform(1);
ludi1a06aa72017-05-12 09:15:00 -0700419 fail("IllegalArgumentException not thrown");
420 } catch (IllegalArgumentException e) {
421 }
422 }
423
424 @Test
ludi1a06aa72017-05-12 09:15:00 -0700425 public void testRemoveTransportModeTransform() throws Exception {
Josh Gao42bd8e12018-10-31 12:26:40 -0700426 Socket socket = new Socket();
427 socket.bind(null);
428 ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
Nathan Haroldf73d2522018-01-17 01:00:20 -0800429 mIpSecService.removeTransportModeTransforms(pfd);
ludi1a06aa72017-05-12 09:15:00 -0700430
Luke Huange8e522b2018-11-23 12:01:41 +0800431 verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
ludi1a06aa72017-05-12 09:15:00 -0700432 }
Nathan Harolda10003d2017-08-23 13:46:33 -0700433
434 @Test
435 public void testValidateIpAddresses() throws Exception {
436 String[] invalidAddresses =
437 new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
438 for (String address : invalidAddresses) {
439 try {
440 IpSecSpiResponse spiResp =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800441 mIpSecService.allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -0800442 address, DROID_SPI, new Binder());
Nathan Harolda10003d2017-08-23 13:46:33 -0700443 fail("Invalid address was passed through IpSecService validation: " + address);
444 } catch (IllegalArgumentException e) {
445 } catch (Exception e) {
446 fail(
447 "Invalid InetAddress was not caught in validation: "
448 + address
449 + ", Exception: "
450 + e);
451 }
452 }
453 }
ludiaa5c1dc2017-10-16 15:09:41 -0700454
455 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800456 * This function checks if the number of encap UDP socket that one UID can reserve has a
457 * reasonable limit.
ludiaa5c1dc2017-10-16 15:09:41 -0700458 */
459 @Test
460 public void testSocketResourceTrackerLimitation() throws Exception {
461 List<IpSecUdpEncapResponse> openUdpEncapSockets = new ArrayList<IpSecUdpEncapResponse>();
462 // Reserve sockets until it fails.
463 for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) {
464 IpSecUdpEncapResponse newUdpEncapSocket =
465 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
466 assertNotNull(newUdpEncapSocket);
467 if (IpSecManager.Status.OK != newUdpEncapSocket.status) {
468 break;
469 }
470 openUdpEncapSockets.add(newUdpEncapSocket);
471 }
472 // Assert that the total sockets quota has a reasonable limit.
ludi6b7fb6b2017-11-15 14:05:16 -0800473 assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty());
ludiaa5c1dc2017-10-16 15:09:41 -0700474 assertTrue(
ludi6b7fb6b2017-11-15 14:05:16 -0800475 "Number of open UDP encap sockets is out of bound",
476 openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
ludiaa5c1dc2017-10-16 15:09:41 -0700477
478 // Try to reserve one more UDP encapsulation socket, and should fail.
479 IpSecUdpEncapResponse extraUdpEncapSocket =
480 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
481 assertNotNull(extraUdpEncapSocket);
482 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
483
ludi6b7fb6b2017-11-15 14:05:16 -0800484 // Close one of the open UDP encapsulation sockets.
ludiaa5c1dc2017-10-16 15:09:41 -0700485 mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
486 openUdpEncapSockets.get(0).fileDescriptor.close();
487 openUdpEncapSockets.remove(0);
488
489 // Try to reserve one more UDP encapsulation socket, and should be successful.
490 extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
491 assertNotNull(extraUdpEncapSocket);
492 assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status);
493 openUdpEncapSockets.add(extraUdpEncapSocket);
494
495 // Close open UDP sockets.
496 for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) {
497 mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId);
498 openSocket.fileDescriptor.close();
499 }
500 }
501
502 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800503 * This function checks if the number of SPI that one UID can reserve has a reasonable limit.
504 * This test does not test for both address families or duplicate SPIs because resource tracking
505 * code does not depend on them.
ludiaa5c1dc2017-10-16 15:09:41 -0700506 */
507 @Test
508 public void testSpiResourceTrackerLimitation() throws Exception {
509 List<IpSecSpiResponse> reservedSpis = new ArrayList<IpSecSpiResponse>();
510 // Return the same SPI for all SPI allocation since IpSecService only
511 // tracks the resource ID.
512 when(mMockNetd.ipSecAllocateSpi(
513 anyInt(),
ludiaa5c1dc2017-10-16 15:09:41 -0700514 anyString(),
515 eq(InetAddress.getLoopbackAddress().getHostAddress()),
516 anyInt()))
517 .thenReturn(DROID_SPI);
518 // Reserve spis until it fails.
519 for (int i = 0; i < MAX_NUM_SPIS; i++) {
520 IpSecSpiResponse newSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800521 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700522 InetAddress.getLoopbackAddress().getHostAddress(),
523 DROID_SPI + i,
524 new Binder());
525 assertNotNull(newSpi);
526 if (IpSecManager.Status.OK != newSpi.status) {
527 break;
528 }
529 reservedSpis.add(newSpi);
530 }
531 // Assert that the SPI quota has a reasonable limit.
532 assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS);
533
534 // Try to reserve one more SPI, and should fail.
535 IpSecSpiResponse extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800536 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700537 InetAddress.getLoopbackAddress().getHostAddress(),
538 DROID_SPI + MAX_NUM_SPIS,
539 new Binder());
540 assertNotNull(extraSpi);
541 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status);
542
543 // Release one reserved spi.
544 mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId);
545 reservedSpis.remove(0);
546
547 // Should successfully reserve one more spi.
548 extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800549 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700550 InetAddress.getLoopbackAddress().getHostAddress(),
551 DROID_SPI + MAX_NUM_SPIS,
552 new Binder());
553 assertNotNull(extraSpi);
554 assertEquals(IpSecManager.Status.OK, extraSpi.status);
555
556 // Release reserved SPIs.
557 for (IpSecSpiResponse spiResp : reservedSpis) {
558 mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
559 }
560 }
Benedict Wongbabe5d72017-12-03 19:42:36 -0800561
562 @Test
563 public void testUidFdtagger() throws Exception {
564 SocketTagger actualSocketTagger = SocketTagger.get();
565
566 try {
567 FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
568
569 // Has to be done after socket creation because BlockGuardOS calls tag on new sockets
570 SocketTagger mockSocketTagger = mock(SocketTagger.class);
571 SocketTagger.set(mockSocketTagger);
572
573 mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID);
574 verify(mockSocketTagger).tag(eq(sockFd));
575 } finally {
576 SocketTagger.set(actualSocketTagger);
577 }
578 }
579
580 /**
581 * Checks if two file descriptors point to the same file.
582 *
583 * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated
584 * file descriptors is to check their inode and device. These two entries uniquely identify any
585 * file.
586 */
587 private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) {
588 try {
589 StructStat fd1Stat = Os.fstat(fd1);
590 StructStat fd2Stat = Os.fstat(fd2);
591
592 return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev;
593 } catch (ErrnoException e) {
594 return false;
595 }
596 }
597
598 @Test
599 public void testOpenUdpEncapSocketTagsSocket() throws Exception {
600 IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
601 IpSecService testIpSecService =
602 new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
603
604 IpSecUdpEncapResponse udpEncapResp =
605 testIpSecService.openUdpEncapsulationSocket(0, new Binder());
606 assertNotNull(udpEncapResp);
607 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
608
609 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
610 ArgumentMatcher<FileDescriptor> fdMatcher =
611 (argFd) -> {
612 return fileDescriptorsEqual(sockFd, argFd);
613 };
614 verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid()));
615
616 testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
617 udpEncapResp.fileDescriptor.close();
618 }
Benedict Wongba8d3132017-12-06 21:56:35 -0800619
620 @Test
621 public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception {
622 IpSecUdpEncapResponse udpEncapResp =
623 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
624
625 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
Luke Huange8e522b2018-11-23 12:01:41 +0800626 ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
Benedict Wongba8d3132017-12-06 21:56:35 -0800627 try {
628 StructStat sockStat = Os.fstat(sockFd);
Luke Huange8e522b2018-11-23 12:01:41 +0800629 StructStat argStat = Os.fstat(arg.getFileDescriptor());
Benedict Wongba8d3132017-12-06 21:56:35 -0800630
631 return sockStat.st_ino == argStat.st_ino
632 && sockStat.st_dev == argStat.st_dev;
633 } catch (ErrnoException e) {
634 return false;
635 }
636 };
637
638 verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
639 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
640 }
Benedict Wong8149f6e2018-01-18 18:31:45 -0800641
642 @Test
643 public void testReserveNetId() {
644 int start = mIpSecService.TUN_INTF_NETID_START;
645 for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
646 assertEquals(start + i, mIpSecService.reserveNetId());
647 }
648
649 // Check that resource exhaustion triggers an exception
650 try {
651 mIpSecService.reserveNetId();
652 fail("Did not throw error for all netIds reserved");
653 } catch (IllegalStateException expected) {
654 }
655
656 // Now release one and try again
657 int releasedNetId =
658 mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
659 mIpSecService.releaseNetId(releasedNetId);
660 assertEquals(releasedNetId, mIpSecService.reserveNetId());
661 }
ludi1a06aa72017-05-12 09:15:00 -0700662}