blob: 4a35015044ff2e446b66d996258cca5ccdc17dbb [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 {
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700159 int localport = -1;
160 IpSecUdpEncapResponse udpEncapResp = null;
ludi1a06aa72017-05-12 09:15:00 -0700161
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700162 for (int i = 0; i < IpSecService.MAX_PORT_BIND_ATTEMPTS; i++) {
163 localport = findUnusedPort();
164
165 udpEncapResp = mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
166 assertNotNull(udpEncapResp);
167 if (udpEncapResp.status == IpSecManager.Status.OK) {
168 break;
169 }
170
171 // Else retry to reduce possibility for port-bind failures.
172 }
173
ludi1a06aa72017-05-12 09:15:00 -0700174 assertNotNull(udpEncapResp);
175 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
176 assertEquals(localport, udpEncapResp.port);
177
178 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
179 udpEncapResp.fileDescriptor.close();
180
Benedict Wong4f9fb812017-12-13 17:16:53 -0800181 // Verify quota and RefcountedResource objects cleaned up
Benedict Wong344bd622017-11-16 15:27:22 -0800182 IpSecService.UserRecord userRecord =
183 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
184 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
185 try {
186 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
187 fail("Expected IllegalArgumentException on attempt to access deleted resource");
188 } catch (IllegalArgumentException expected) {
189
190 }
191 }
192
193 @Test
194 public void testUdpEncapsulationSocketBinderDeath() throws Exception {
Benedict Wong344bd622017-11-16 15:27:22 -0800195 IpSecUdpEncapResponse udpEncapResp =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800196 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
Benedict Wong344bd622017-11-16 15:27:22 -0800197
198 IpSecService.UserRecord userRecord =
199 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
200 IpSecService.RefcountedResource refcountedRecord =
201 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
202 udpEncapResp.resourceId);
203
204 refcountedRecord.binderDied();
205
Benedict Wong4f9fb812017-12-13 17:16:53 -0800206 // Verify quota and RefcountedResource objects cleaned up
Benedict Wong344bd622017-11-16 15:27:22 -0800207 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
208 try {
209 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
210 fail("Expected IllegalArgumentException on attempt to access deleted resource");
211 } catch (IllegalArgumentException expected) {
212
213 }
ludi1a06aa72017-05-12 09:15:00 -0700214 }
215
216 @Test
217 public void testOpenUdpEncapsulationSocketAfterClose() throws Exception {
ludi1a06aa72017-05-12 09:15:00 -0700218 IpSecUdpEncapResponse udpEncapResp =
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700219 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
ludi1a06aa72017-05-12 09:15:00 -0700220 assertNotNull(udpEncapResp);
221 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700222 int localport = udpEncapResp.port;
ludi1a06aa72017-05-12 09:15:00 -0700223
224 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
225 udpEncapResp.fileDescriptor.close();
226
227 /** Check if localport is available. */
228 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
229 Os.bind(newSocket, INADDR_ANY, localport);
230 Os.close(newSocket);
231 }
232
233 /**
234 * This function checks if the IpSecService holds the reserved port. If
235 * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete.
236 */
237 @Test
238 public void testUdpEncapPortNotReleased() throws Exception {
ludi1a06aa72017-05-12 09:15:00 -0700239 IpSecUdpEncapResponse udpEncapResp =
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700240 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
ludi1a06aa72017-05-12 09:15:00 -0700241 assertNotNull(udpEncapResp);
242 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700243 int localport = udpEncapResp.port;
ludi1a06aa72017-05-12 09:15:00 -0700244
245 udpEncapResp.fileDescriptor.close();
246
247 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
248 try {
249 Os.bind(newSocket, INADDR_ANY, localport);
250 fail("ErrnoException not thrown");
251 } catch (ErrnoException e) {
252 assertEquals(EADDRINUSE, e.errno);
253 }
254 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
255 }
256
257 @Test
258 public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception {
259 IpSecUdpEncapResponse udpEncapResp =
260 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
261 assertNotNull(udpEncapResp);
262 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
Benedict Wongf186d672017-10-10 20:44:28 -0700263 assertNotEquals(0, udpEncapResp.port);
ludi1a06aa72017-05-12 09:15:00 -0700264 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
265 udpEncapResp.fileDescriptor.close();
266 }
267
268 @Test
269 public void testOpenUdpEncapsulationSocketPortRange() throws Exception {
270 try {
271 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder());
272 fail("IllegalArgumentException not thrown");
273 } catch (IllegalArgumentException e) {
274 }
275
276 try {
277 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder());
278 fail("IllegalArgumentException not thrown");
279 } catch (IllegalArgumentException e) {
280 }
281 }
282
283 @Test
284 public void testOpenUdpEncapsulationSocketTwice() throws Exception {
ludi1a06aa72017-05-12 09:15:00 -0700285 IpSecUdpEncapResponse udpEncapResp =
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700286 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
ludi1a06aa72017-05-12 09:15:00 -0700287 assertNotNull(udpEncapResp);
288 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
Lorenzo Colittif0d45502019-04-04 09:18:29 -0700289 int localport = udpEncapResp.port;
ludi1a06aa72017-05-12 09:15:00 -0700290
291 IpSecUdpEncapResponse testUdpEncapResp =
292 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
293 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status);
294
295 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
296 udpEncapResp.fileDescriptor.close();
297 }
298
299 @Test
300 public void testCloseInvalidUdpEncapsulationSocket() throws Exception {
301 try {
302 mIpSecService.closeUdpEncapsulationSocket(1);
303 fail("IllegalArgumentException not thrown");
304 } catch (IllegalArgumentException e) {
305 }
306 }
307
ludi1a06aa72017-05-12 09:15:00 -0700308 @Test
Benedict Wong4f255702017-11-06 20:49:10 -0800309 public void testValidateAlgorithmsAuth() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800310 // Validate that correct algorithm type succeeds
311 IpSecConfig config = new IpSecConfig();
312 config.setAuthentication(AUTH_ALGO);
313 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800314
Nathan Harold5676f5f2018-01-16 19:34:01 -0800315 // Validate that incorrect algorithm types fails
316 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
317 try {
318 config = new IpSecConfig();
319 config.setAuthentication(algo);
320 mIpSecService.validateAlgorithms(config);
321 fail("Did not throw exception on invalid algorithm type");
322 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800323 }
324 }
325 }
326
327 @Test
328 public void testValidateAlgorithmsCrypt() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800329 // Validate that correct algorithm type succeeds
330 IpSecConfig config = new IpSecConfig();
331 config.setEncryption(CRYPT_ALGO);
332 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800333
Nathan Harold5676f5f2018-01-16 19:34:01 -0800334 // Validate that incorrect algorithm types fails
335 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
336 try {
337 config = new IpSecConfig();
338 config.setEncryption(algo);
339 mIpSecService.validateAlgorithms(config);
340 fail("Did not throw exception on invalid algorithm type");
341 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800342 }
343 }
344 }
345
346 @Test
347 public void testValidateAlgorithmsAead() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800348 // Validate that correct algorithm type succeeds
349 IpSecConfig config = new IpSecConfig();
350 config.setAuthenticatedEncryption(AEAD_ALGO);
351 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800352
Nathan Harold5676f5f2018-01-16 19:34:01 -0800353 // Validate that incorrect algorithm types fails
354 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
355 try {
356 config = new IpSecConfig();
357 config.setAuthenticatedEncryption(algo);
358 mIpSecService.validateAlgorithms(config);
359 fail("Did not throw exception on invalid algorithm type");
360 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800361 }
362 }
363 }
364
365 @Test
366 public void testValidateAlgorithmsAuthCrypt() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800367 // Validate that correct algorithm type succeeds
368 IpSecConfig config = new IpSecConfig();
369 config.setAuthentication(AUTH_ALGO);
370 config.setEncryption(CRYPT_ALGO);
371 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800372 }
373
374 @Test
375 public void testValidateAlgorithmsNoAlgorithms() {
376 IpSecConfig config = new IpSecConfig();
377 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800378 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800379 fail("Expected exception; no algorithms specified");
380 } catch (IllegalArgumentException expected) {
381 }
382 }
383
384 @Test
385 public void testValidateAlgorithmsAeadWithAuth() {
386 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800387 config.setAuthenticatedEncryption(AEAD_ALGO);
388 config.setAuthentication(AUTH_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800389 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800390 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800391 fail("Expected exception; both AEAD and auth algorithm specified");
392 } catch (IllegalArgumentException expected) {
393 }
394 }
395
396 @Test
397 public void testValidateAlgorithmsAeadWithCrypt() {
398 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800399 config.setAuthenticatedEncryption(AEAD_ALGO);
400 config.setEncryption(CRYPT_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800401 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800402 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800403 fail("Expected exception; both AEAD and crypt algorithm specified");
404 } catch (IllegalArgumentException expected) {
405 }
406 }
407
408 @Test
409 public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
410 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800411 config.setAuthenticatedEncryption(AEAD_ALGO);
412 config.setAuthentication(AUTH_ALGO);
413 config.setEncryption(CRYPT_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800414 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800415 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800416 fail("Expected exception; AEAD, auth and crypt algorithm specified");
417 } catch (IllegalArgumentException expected) {
418 }
419 }
420
421 @Test
Benedict Wongf33f03132018-01-18 14:38:16 -0800422 public void testDeleteInvalidTransform() throws Exception {
ludi1a06aa72017-05-12 09:15:00 -0700423 try {
Benedict Wongf33f03132018-01-18 14:38:16 -0800424 mIpSecService.deleteTransform(1);
ludi1a06aa72017-05-12 09:15:00 -0700425 fail("IllegalArgumentException not thrown");
426 } catch (IllegalArgumentException e) {
427 }
428 }
429
430 @Test
ludi1a06aa72017-05-12 09:15:00 -0700431 public void testRemoveTransportModeTransform() throws Exception {
Josh Gao42bd8e12018-10-31 12:26:40 -0700432 Socket socket = new Socket();
433 socket.bind(null);
434 ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
Nathan Haroldf73d2522018-01-17 01:00:20 -0800435 mIpSecService.removeTransportModeTransforms(pfd);
ludi1a06aa72017-05-12 09:15:00 -0700436
Luke Huange8e522b2018-11-23 12:01:41 +0800437 verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
ludi1a06aa72017-05-12 09:15:00 -0700438 }
Nathan Harolda10003d2017-08-23 13:46:33 -0700439
440 @Test
441 public void testValidateIpAddresses() throws Exception {
442 String[] invalidAddresses =
443 new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
444 for (String address : invalidAddresses) {
445 try {
446 IpSecSpiResponse spiResp =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800447 mIpSecService.allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -0800448 address, DROID_SPI, new Binder());
Nathan Harolda10003d2017-08-23 13:46:33 -0700449 fail("Invalid address was passed through IpSecService validation: " + address);
450 } catch (IllegalArgumentException e) {
451 } catch (Exception e) {
452 fail(
453 "Invalid InetAddress was not caught in validation: "
454 + address
455 + ", Exception: "
456 + e);
457 }
458 }
459 }
ludiaa5c1dc2017-10-16 15:09:41 -0700460
461 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800462 * This function checks if the number of encap UDP socket that one UID can reserve has a
463 * reasonable limit.
ludiaa5c1dc2017-10-16 15:09:41 -0700464 */
465 @Test
466 public void testSocketResourceTrackerLimitation() throws Exception {
467 List<IpSecUdpEncapResponse> openUdpEncapSockets = new ArrayList<IpSecUdpEncapResponse>();
468 // Reserve sockets until it fails.
469 for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) {
470 IpSecUdpEncapResponse newUdpEncapSocket =
471 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
472 assertNotNull(newUdpEncapSocket);
473 if (IpSecManager.Status.OK != newUdpEncapSocket.status) {
474 break;
475 }
476 openUdpEncapSockets.add(newUdpEncapSocket);
477 }
478 // Assert that the total sockets quota has a reasonable limit.
ludi6b7fb6b2017-11-15 14:05:16 -0800479 assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty());
ludiaa5c1dc2017-10-16 15:09:41 -0700480 assertTrue(
ludi6b7fb6b2017-11-15 14:05:16 -0800481 "Number of open UDP encap sockets is out of bound",
482 openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
ludiaa5c1dc2017-10-16 15:09:41 -0700483
484 // Try to reserve one more UDP encapsulation socket, and should fail.
485 IpSecUdpEncapResponse extraUdpEncapSocket =
486 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
487 assertNotNull(extraUdpEncapSocket);
488 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
489
ludi6b7fb6b2017-11-15 14:05:16 -0800490 // Close one of the open UDP encapsulation sockets.
ludiaa5c1dc2017-10-16 15:09:41 -0700491 mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
492 openUdpEncapSockets.get(0).fileDescriptor.close();
493 openUdpEncapSockets.remove(0);
494
495 // Try to reserve one more UDP encapsulation socket, and should be successful.
496 extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
497 assertNotNull(extraUdpEncapSocket);
498 assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status);
499 openUdpEncapSockets.add(extraUdpEncapSocket);
500
501 // Close open UDP sockets.
502 for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) {
503 mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId);
504 openSocket.fileDescriptor.close();
505 }
506 }
507
508 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800509 * This function checks if the number of SPI that one UID can reserve has a reasonable limit.
510 * This test does not test for both address families or duplicate SPIs because resource tracking
511 * code does not depend on them.
ludiaa5c1dc2017-10-16 15:09:41 -0700512 */
513 @Test
514 public void testSpiResourceTrackerLimitation() throws Exception {
515 List<IpSecSpiResponse> reservedSpis = new ArrayList<IpSecSpiResponse>();
516 // Return the same SPI for all SPI allocation since IpSecService only
517 // tracks the resource ID.
518 when(mMockNetd.ipSecAllocateSpi(
519 anyInt(),
ludiaa5c1dc2017-10-16 15:09:41 -0700520 anyString(),
521 eq(InetAddress.getLoopbackAddress().getHostAddress()),
522 anyInt()))
523 .thenReturn(DROID_SPI);
524 // Reserve spis until it fails.
525 for (int i = 0; i < MAX_NUM_SPIS; i++) {
526 IpSecSpiResponse newSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800527 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700528 InetAddress.getLoopbackAddress().getHostAddress(),
529 DROID_SPI + i,
530 new Binder());
531 assertNotNull(newSpi);
532 if (IpSecManager.Status.OK != newSpi.status) {
533 break;
534 }
535 reservedSpis.add(newSpi);
536 }
537 // Assert that the SPI quota has a reasonable limit.
538 assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS);
539
540 // Try to reserve one more SPI, and should fail.
541 IpSecSpiResponse extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800542 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700543 InetAddress.getLoopbackAddress().getHostAddress(),
544 DROID_SPI + MAX_NUM_SPIS,
545 new Binder());
546 assertNotNull(extraSpi);
547 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status);
548
549 // Release one reserved spi.
550 mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId);
551 reservedSpis.remove(0);
552
553 // Should successfully reserve one more spi.
554 extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800555 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700556 InetAddress.getLoopbackAddress().getHostAddress(),
557 DROID_SPI + MAX_NUM_SPIS,
558 new Binder());
559 assertNotNull(extraSpi);
560 assertEquals(IpSecManager.Status.OK, extraSpi.status);
561
562 // Release reserved SPIs.
563 for (IpSecSpiResponse spiResp : reservedSpis) {
564 mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
565 }
566 }
Benedict Wongbabe5d72017-12-03 19:42:36 -0800567
568 @Test
569 public void testUidFdtagger() throws Exception {
570 SocketTagger actualSocketTagger = SocketTagger.get();
571
572 try {
573 FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
574
575 // Has to be done after socket creation because BlockGuardOS calls tag on new sockets
576 SocketTagger mockSocketTagger = mock(SocketTagger.class);
577 SocketTagger.set(mockSocketTagger);
578
579 mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID);
580 verify(mockSocketTagger).tag(eq(sockFd));
581 } finally {
582 SocketTagger.set(actualSocketTagger);
583 }
584 }
585
586 /**
587 * Checks if two file descriptors point to the same file.
588 *
589 * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated
590 * file descriptors is to check their inode and device. These two entries uniquely identify any
591 * file.
592 */
593 private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) {
594 try {
595 StructStat fd1Stat = Os.fstat(fd1);
596 StructStat fd2Stat = Os.fstat(fd2);
597
598 return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev;
599 } catch (ErrnoException e) {
600 return false;
601 }
602 }
603
604 @Test
605 public void testOpenUdpEncapSocketTagsSocket() throws Exception {
606 IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
607 IpSecService testIpSecService =
608 new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
609
610 IpSecUdpEncapResponse udpEncapResp =
611 testIpSecService.openUdpEncapsulationSocket(0, new Binder());
612 assertNotNull(udpEncapResp);
613 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
614
615 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
616 ArgumentMatcher<FileDescriptor> fdMatcher =
617 (argFd) -> {
618 return fileDescriptorsEqual(sockFd, argFd);
619 };
620 verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid()));
621
622 testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
623 udpEncapResp.fileDescriptor.close();
624 }
Benedict Wongba8d3132017-12-06 21:56:35 -0800625
626 @Test
627 public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception {
628 IpSecUdpEncapResponse udpEncapResp =
629 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
630
631 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
Luke Huange8e522b2018-11-23 12:01:41 +0800632 ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
Benedict Wongba8d3132017-12-06 21:56:35 -0800633 try {
634 StructStat sockStat = Os.fstat(sockFd);
Luke Huange8e522b2018-11-23 12:01:41 +0800635 StructStat argStat = Os.fstat(arg.getFileDescriptor());
Benedict Wongba8d3132017-12-06 21:56:35 -0800636
637 return sockStat.st_ino == argStat.st_ino
638 && sockStat.st_dev == argStat.st_dev;
639 } catch (ErrnoException e) {
640 return false;
641 }
642 };
643
644 verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
645 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
646 }
Benedict Wong8149f6e2018-01-18 18:31:45 -0800647
648 @Test
649 public void testReserveNetId() {
650 int start = mIpSecService.TUN_INTF_NETID_START;
651 for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
652 assertEquals(start + i, mIpSecService.reserveNetId());
653 }
654
655 // Check that resource exhaustion triggers an exception
656 try {
657 mIpSecService.reserveNetId();
658 fail("Did not throw error for all netIds reserved");
659 } catch (IllegalStateException expected) {
660 }
661
662 // Now release one and try again
663 int releasedNetId =
664 mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
665 mIpSecService.releaseNetId(releasedNetId);
666 assertEquals(releasedNetId, mIpSecService.reserveNetId());
667 }
ludi1a06aa72017-05-12 09:15:00 -0700668}