blob: 49eec3f68cd82a1c2c378188e9cfe5f58f0ce92c [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;
23import static org.junit.Assert.assertEquals;
Benedict Wongf186d672017-10-10 20:44:28 -070024import static org.junit.Assert.assertNotEquals;
ludi1a06aa72017-05-12 09:15:00 -070025import static org.junit.Assert.assertNotNull;
ludiaa5c1dc2017-10-16 15:09:41 -070026import static org.junit.Assert.assertTrue;
ludi1a06aa72017-05-12 09:15:00 -070027import static org.junit.Assert.fail;
ludiaa5c1dc2017-10-16 15:09:41 -070028import static org.mockito.Matchers.anyInt;
29import static org.mockito.Matchers.anyString;
Benedict Wongbabe5d72017-12-03 19:42:36 -080030import static org.mockito.Matchers.argThat;
ludiaa5c1dc2017-10-16 15:09:41 -070031import static org.mockito.Matchers.eq;
ludi1a06aa72017-05-12 09:15:00 -070032import static org.mockito.Mockito.mock;
33import static org.mockito.Mockito.verify;
34import static org.mockito.Mockito.when;
35
36import android.content.Context;
37import android.net.INetd;
Benedict Wong4f255702017-11-06 20:49:10 -080038import android.net.IpSecAlgorithm;
39import android.net.IpSecConfig;
ludi1a06aa72017-05-12 09:15:00 -070040import android.net.IpSecManager;
41import android.net.IpSecSpiResponse;
42import android.net.IpSecTransform;
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.support.test.filters.SmallTest;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090048import android.support.test.runner.AndroidJUnit4;
ludi1a06aa72017-05-12 09:15:00 -070049import android.system.ErrnoException;
50import android.system.Os;
Benedict Wongbabe5d72017-12-03 19:42:36 -080051import android.system.StructStat;
52
53import dalvik.system.SocketTagger;
Nathan Harold2e9a5202017-09-26 11:44:23 -070054
ludi1a06aa72017-05-12 09:15:00 -070055import java.io.FileDescriptor;
56import java.net.InetAddress;
57import java.net.ServerSocket;
58import java.net.Socket;
59import java.net.UnknownHostException;
ludiaa5c1dc2017-10-16 15:09:41 -070060import java.util.ArrayList;
61import java.util.List;
Nathan Harold2e9a5202017-09-26 11:44:23 -070062
ludi1a06aa72017-05-12 09:15:00 -070063import org.junit.Before;
64import org.junit.Test;
65import org.junit.runner.RunWith;
Benedict Wongbabe5d72017-12-03 19:42:36 -080066import org.mockito.ArgumentMatcher;
ludi1a06aa72017-05-12 09:15:00 -070067
68/** Unit tests for {@link IpSecService}. */
69@SmallTest
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090070@RunWith(AndroidJUnit4.class)
ludi1a06aa72017-05-12 09:15:00 -070071public class IpSecServiceTest {
72
73 private static final int DROID_SPI = 0xD1201D;
ludiaa5c1dc2017-10-16 15:09:41 -070074 private static final int MAX_NUM_ENCAP_SOCKETS = 100;
75 private static final int MAX_NUM_SPIS = 100;
ludi1a06aa72017-05-12 09:15:00 -070076 private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
77 private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
ludi1a06aa72017-05-12 09:15:00 -070078
79 private static final InetAddress INADDR_ANY;
80
Benedict Wong4f255702017-11-06 20:49:10 -080081 private static final byte[] AEAD_KEY = {
82 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
83 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
84 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
85 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
86 0x73, 0x61, 0x6C, 0x74
87 };
88 private static final byte[] CRYPT_KEY = {
89 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
90 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
91 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
92 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
93 };
94 private static final byte[] AUTH_KEY = {
95 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
97 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
99 };
100
101 private static final IpSecAlgorithm AUTH_ALGO =
102 new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
103 private static final IpSecAlgorithm CRYPT_ALGO =
104 new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
105 private static final IpSecAlgorithm AEAD_ALGO =
106 new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
107
108 private static final int[] DIRECTIONS =
109 new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
110
ludi1a06aa72017-05-12 09:15:00 -0700111 static {
112 try {
113 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
114 } catch (UnknownHostException e) {
115 throw new RuntimeException(e);
116 }
117 }
118
ludi1a06aa72017-05-12 09:15:00 -0700119 Context mMockContext;
120 INetd mMockNetd;
121 IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
122 IpSecService mIpSecService;
123
124 @Before
125 public void setUp() throws Exception {
126 mMockContext = mock(Context.class);
127 mMockNetd = mock(INetd.class);
128 mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
129 mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
130
131 // Injecting mock netd
132 when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
133 }
134
135 @Test
136 public void testIpSecServiceCreate() throws InterruptedException {
137 IpSecService ipSecSrv = IpSecService.create(mMockContext);
138 assertNotNull(ipSecSrv);
139 }
140
141 @Test
ludi1a06aa72017-05-12 09:15:00 -0700142 public void testReleaseInvalidSecurityParameterIndex() throws Exception {
143 try {
144 mIpSecService.releaseSecurityParameterIndex(1);
145 fail("IllegalArgumentException not thrown");
146 } catch (IllegalArgumentException e) {
147 }
148 }
149
150 /** This function finds an available port */
151 int findUnusedPort() throws Exception {
152 // Get an available port.
153 ServerSocket s = new ServerSocket(0);
154 int port = s.getLocalPort();
155 s.close();
156 return port;
157 }
158
159 @Test
160 public void testOpenAndCloseUdpEncapsulationSocket() throws Exception {
161 int localport = findUnusedPort();
162
163 IpSecUdpEncapResponse udpEncapResp =
164 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
165 assertNotNull(udpEncapResp);
166 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
167 assertEquals(localport, udpEncapResp.port);
168
169 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
170 udpEncapResp.fileDescriptor.close();
171
Benedict Wong344bd622017-11-16 15:27:22 -0800172 IpSecService.UserRecord userRecord =
173 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
174 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
175 try {
176 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
177 fail("Expected IllegalArgumentException on attempt to access deleted resource");
178 } catch (IllegalArgumentException expected) {
179
180 }
181 }
182
183 @Test
184 public void testUdpEncapsulationSocketBinderDeath() throws Exception {
185 int localport = findUnusedPort();
186
187 IpSecUdpEncapResponse udpEncapResp =
188 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
189
190 IpSecService.UserRecord userRecord =
191 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
192 IpSecService.RefcountedResource refcountedRecord =
193 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
194 udpEncapResp.resourceId);
195
196 refcountedRecord.binderDied();
197
198 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
199 try {
200 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
201 fail("Expected IllegalArgumentException on attempt to access deleted resource");
202 } catch (IllegalArgumentException expected) {
203
204 }
ludi1a06aa72017-05-12 09:15:00 -0700205 }
206
207 @Test
208 public void testOpenUdpEncapsulationSocketAfterClose() throws Exception {
209 int localport = findUnusedPort();
210 IpSecUdpEncapResponse udpEncapResp =
211 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
212 assertNotNull(udpEncapResp);
213 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
214 assertEquals(localport, udpEncapResp.port);
215
216 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
217 udpEncapResp.fileDescriptor.close();
218
219 /** Check if localport is available. */
220 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
221 Os.bind(newSocket, INADDR_ANY, localport);
222 Os.close(newSocket);
223 }
224
225 /**
226 * This function checks if the IpSecService holds the reserved port. If
227 * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete.
228 */
229 @Test
230 public void testUdpEncapPortNotReleased() throws Exception {
231 int localport = findUnusedPort();
232 IpSecUdpEncapResponse udpEncapResp =
233 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
234 assertNotNull(udpEncapResp);
235 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
236 assertEquals(localport, udpEncapResp.port);
237
238 udpEncapResp.fileDescriptor.close();
239
240 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
241 try {
242 Os.bind(newSocket, INADDR_ANY, localport);
243 fail("ErrnoException not thrown");
244 } catch (ErrnoException e) {
245 assertEquals(EADDRINUSE, e.errno);
246 }
247 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
248 }
249
250 @Test
251 public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception {
252 IpSecUdpEncapResponse udpEncapResp =
253 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
254 assertNotNull(udpEncapResp);
255 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
Benedict Wongf186d672017-10-10 20:44:28 -0700256 assertNotEquals(0, udpEncapResp.port);
ludi1a06aa72017-05-12 09:15:00 -0700257 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
258 udpEncapResp.fileDescriptor.close();
259 }
260
261 @Test
262 public void testOpenUdpEncapsulationSocketPortRange() throws Exception {
263 try {
264 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder());
265 fail("IllegalArgumentException not thrown");
266 } catch (IllegalArgumentException e) {
267 }
268
269 try {
270 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder());
271 fail("IllegalArgumentException not thrown");
272 } catch (IllegalArgumentException e) {
273 }
274 }
275
276 @Test
277 public void testOpenUdpEncapsulationSocketTwice() throws Exception {
278 int localport = findUnusedPort();
279
280 IpSecUdpEncapResponse udpEncapResp =
281 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
282 assertNotNull(udpEncapResp);
283 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
284 assertEquals(localport, udpEncapResp.port);
285 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
286
287 IpSecUdpEncapResponse testUdpEncapResp =
288 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
289 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status);
290
291 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
292 udpEncapResp.fileDescriptor.close();
293 }
294
295 @Test
296 public void testCloseInvalidUdpEncapsulationSocket() throws Exception {
297 try {
298 mIpSecService.closeUdpEncapsulationSocket(1);
299 fail("IllegalArgumentException not thrown");
300 } catch (IllegalArgumentException e) {
301 }
302 }
303
ludi1a06aa72017-05-12 09:15:00 -0700304 @Test
Benedict Wong4f255702017-11-06 20:49:10 -0800305 public void testValidateAlgorithmsAuth() {
306 for (int direction : DIRECTIONS) {
307 // Validate that correct algorithm type succeeds
308 IpSecConfig config = new IpSecConfig();
309 config.setAuthentication(direction, AUTH_ALGO);
310 mIpSecService.validateAlgorithms(config, direction);
311
312 // Validate that incorrect algorithm types fails
313 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
314 try {
315 config = new IpSecConfig();
316 config.setAuthentication(direction, algo);
317 mIpSecService.validateAlgorithms(config, direction);
318 fail("Did not throw exception on invalid algorithm type");
319 } catch (IllegalArgumentException expected) {
320 }
321 }
322 }
323 }
324
325 @Test
326 public void testValidateAlgorithmsCrypt() {
327 for (int direction : DIRECTIONS) {
328 // Validate that correct algorithm type succeeds
329 IpSecConfig config = new IpSecConfig();
330 config.setEncryption(direction, CRYPT_ALGO);
331 mIpSecService.validateAlgorithms(config, direction);
332
333 // Validate that incorrect algorithm types fails
334 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
335 try {
336 config = new IpSecConfig();
337 config.setEncryption(direction, algo);
338 mIpSecService.validateAlgorithms(config, direction);
339 fail("Did not throw exception on invalid algorithm type");
340 } catch (IllegalArgumentException expected) {
341 }
342 }
343 }
344 }
345
346 @Test
347 public void testValidateAlgorithmsAead() {
348 for (int direction : DIRECTIONS) {
349 // Validate that correct algorithm type succeeds
350 IpSecConfig config = new IpSecConfig();
351 config.setAuthenticatedEncryption(direction, AEAD_ALGO);
352 mIpSecService.validateAlgorithms(config, direction);
353
354 // Validate that incorrect algorithm types fails
355 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
356 try {
357 config = new IpSecConfig();
358 config.setAuthenticatedEncryption(direction, algo);
359 mIpSecService.validateAlgorithms(config, direction);
360 fail("Did not throw exception on invalid algorithm type");
361 } catch (IllegalArgumentException expected) {
362 }
363 }
364 }
365 }
366
367 @Test
368 public void testValidateAlgorithmsAuthCrypt() {
369 for (int direction : DIRECTIONS) {
370 // Validate that correct algorithm type succeeds
371 IpSecConfig config = new IpSecConfig();
372 config.setAuthentication(direction, AUTH_ALGO);
373 config.setEncryption(direction, CRYPT_ALGO);
374 mIpSecService.validateAlgorithms(config, direction);
375 }
376 }
377
378 @Test
379 public void testValidateAlgorithmsNoAlgorithms() {
380 IpSecConfig config = new IpSecConfig();
381 try {
382 mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
383 fail("Expected exception; no algorithms specified");
384 } catch (IllegalArgumentException expected) {
385 }
386 }
387
388 @Test
389 public void testValidateAlgorithmsAeadWithAuth() {
390 IpSecConfig config = new IpSecConfig();
391 config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
392 config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
393 try {
394 mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
395 fail("Expected exception; both AEAD and auth algorithm specified");
396 } catch (IllegalArgumentException expected) {
397 }
398 }
399
400 @Test
401 public void testValidateAlgorithmsAeadWithCrypt() {
402 IpSecConfig config = new IpSecConfig();
403 config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
404 config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
405 try {
406 mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
407 fail("Expected exception; both AEAD and crypt algorithm specified");
408 } catch (IllegalArgumentException expected) {
409 }
410 }
411
412 @Test
413 public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
414 IpSecConfig config = new IpSecConfig();
415 config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
416 config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
417 config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
418 try {
419 mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
420 fail("Expected exception; AEAD, auth and crypt algorithm specified");
421 } catch (IllegalArgumentException expected) {
422 }
423 }
424
425 @Test
ludi1a06aa72017-05-12 09:15:00 -0700426 public void testDeleteInvalidTransportModeTransform() throws Exception {
427 try {
428 mIpSecService.deleteTransportModeTransform(1);
429 fail("IllegalArgumentException not thrown");
430 } catch (IllegalArgumentException e) {
431 }
432 }
433
434 @Test
ludi1a06aa72017-05-12 09:15:00 -0700435 public void testRemoveTransportModeTransform() throws Exception {
436 ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
Nathan Harolda2523312018-01-05 19:25:13 -0800437 mIpSecService.removeTransportModeTransforms(pfd, 1);
ludi1a06aa72017-05-12 09:15:00 -0700438
439 verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
440 }
Nathan Harolda10003d2017-08-23 13:46:33 -0700441
442 @Test
443 public void testValidateIpAddresses() throws Exception {
444 String[] invalidAddresses =
445 new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
446 for (String address : invalidAddresses) {
447 try {
448 IpSecSpiResponse spiResp =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800449 mIpSecService.allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -0800450 address, DROID_SPI, new Binder());
Nathan Harolda10003d2017-08-23 13:46:33 -0700451 fail("Invalid address was passed through IpSecService validation: " + address);
452 } catch (IllegalArgumentException e) {
453 } catch (Exception e) {
454 fail(
455 "Invalid InetAddress was not caught in validation: "
456 + address
457 + ", Exception: "
458 + e);
459 }
460 }
461 }
ludiaa5c1dc2017-10-16 15:09:41 -0700462
463 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800464 * This function checks if the number of encap UDP socket that one UID can reserve has a
465 * reasonable limit.
ludiaa5c1dc2017-10-16 15:09:41 -0700466 */
467 @Test
468 public void testSocketResourceTrackerLimitation() throws Exception {
469 List<IpSecUdpEncapResponse> openUdpEncapSockets = new ArrayList<IpSecUdpEncapResponse>();
470 // Reserve sockets until it fails.
471 for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) {
472 IpSecUdpEncapResponse newUdpEncapSocket =
473 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
474 assertNotNull(newUdpEncapSocket);
475 if (IpSecManager.Status.OK != newUdpEncapSocket.status) {
476 break;
477 }
478 openUdpEncapSockets.add(newUdpEncapSocket);
479 }
480 // Assert that the total sockets quota has a reasonable limit.
ludi6b7fb6b2017-11-15 14:05:16 -0800481 assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty());
ludiaa5c1dc2017-10-16 15:09:41 -0700482 assertTrue(
ludi6b7fb6b2017-11-15 14:05:16 -0800483 "Number of open UDP encap sockets is out of bound",
484 openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
ludiaa5c1dc2017-10-16 15:09:41 -0700485
486 // Try to reserve one more UDP encapsulation socket, and should fail.
487 IpSecUdpEncapResponse extraUdpEncapSocket =
488 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
489 assertNotNull(extraUdpEncapSocket);
490 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
491
ludi6b7fb6b2017-11-15 14:05:16 -0800492 // Close one of the open UDP encapsulation sockets.
ludiaa5c1dc2017-10-16 15:09:41 -0700493 mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
494 openUdpEncapSockets.get(0).fileDescriptor.close();
495 openUdpEncapSockets.remove(0);
496
497 // Try to reserve one more UDP encapsulation socket, and should be successful.
498 extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
499 assertNotNull(extraUdpEncapSocket);
500 assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status);
501 openUdpEncapSockets.add(extraUdpEncapSocket);
502
503 // Close open UDP sockets.
504 for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) {
505 mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId);
506 openSocket.fileDescriptor.close();
507 }
508 }
509
510 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800511 * This function checks if the number of SPI that one UID can reserve has a reasonable limit.
512 * This test does not test for both address families or duplicate SPIs because resource tracking
513 * code does not depend on them.
ludiaa5c1dc2017-10-16 15:09:41 -0700514 */
515 @Test
516 public void testSpiResourceTrackerLimitation() throws Exception {
517 List<IpSecSpiResponse> reservedSpis = new ArrayList<IpSecSpiResponse>();
518 // Return the same SPI for all SPI allocation since IpSecService only
519 // tracks the resource ID.
520 when(mMockNetd.ipSecAllocateSpi(
521 anyInt(),
ludiaa5c1dc2017-10-16 15:09:41 -0700522 anyString(),
523 eq(InetAddress.getLoopbackAddress().getHostAddress()),
524 anyInt()))
525 .thenReturn(DROID_SPI);
526 // Reserve spis until it fails.
527 for (int i = 0; i < MAX_NUM_SPIS; i++) {
528 IpSecSpiResponse newSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800529 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700530 InetAddress.getLoopbackAddress().getHostAddress(),
531 DROID_SPI + i,
532 new Binder());
533 assertNotNull(newSpi);
534 if (IpSecManager.Status.OK != newSpi.status) {
535 break;
536 }
537 reservedSpis.add(newSpi);
538 }
539 // Assert that the SPI quota has a reasonable limit.
540 assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS);
541
542 // Try to reserve one more SPI, and should fail.
543 IpSecSpiResponse extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800544 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700545 InetAddress.getLoopbackAddress().getHostAddress(),
546 DROID_SPI + MAX_NUM_SPIS,
547 new Binder());
548 assertNotNull(extraSpi);
549 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status);
550
551 // Release one reserved spi.
552 mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId);
553 reservedSpis.remove(0);
554
555 // Should successfully reserve one more spi.
556 extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800557 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700558 InetAddress.getLoopbackAddress().getHostAddress(),
559 DROID_SPI + MAX_NUM_SPIS,
560 new Binder());
561 assertNotNull(extraSpi);
562 assertEquals(IpSecManager.Status.OK, extraSpi.status);
563
564 // Release reserved SPIs.
565 for (IpSecSpiResponse spiResp : reservedSpis) {
566 mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
567 }
568 }
Benedict Wongbabe5d72017-12-03 19:42:36 -0800569
570 @Test
571 public void testUidFdtagger() throws Exception {
572 SocketTagger actualSocketTagger = SocketTagger.get();
573
574 try {
575 FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
576
577 // Has to be done after socket creation because BlockGuardOS calls tag on new sockets
578 SocketTagger mockSocketTagger = mock(SocketTagger.class);
579 SocketTagger.set(mockSocketTagger);
580
581 mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID);
582 verify(mockSocketTagger).tag(eq(sockFd));
583 } finally {
584 SocketTagger.set(actualSocketTagger);
585 }
586 }
587
588 /**
589 * Checks if two file descriptors point to the same file.
590 *
591 * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated
592 * file descriptors is to check their inode and device. These two entries uniquely identify any
593 * file.
594 */
595 private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) {
596 try {
597 StructStat fd1Stat = Os.fstat(fd1);
598 StructStat fd2Stat = Os.fstat(fd2);
599
600 return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev;
601 } catch (ErrnoException e) {
602 return false;
603 }
604 }
605
606 @Test
607 public void testOpenUdpEncapSocketTagsSocket() throws Exception {
608 IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
609 IpSecService testIpSecService =
610 new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
611
612 IpSecUdpEncapResponse udpEncapResp =
613 testIpSecService.openUdpEncapsulationSocket(0, new Binder());
614 assertNotNull(udpEncapResp);
615 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
616
617 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
618 ArgumentMatcher<FileDescriptor> fdMatcher =
619 (argFd) -> {
620 return fileDescriptorsEqual(sockFd, argFd);
621 };
622 verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid()));
623
624 testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
625 udpEncapResp.fileDescriptor.close();
626 }
Benedict Wongba8d3132017-12-06 21:56:35 -0800627
628 @Test
629 public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception {
630 IpSecUdpEncapResponse udpEncapResp =
631 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
632
633 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
634 ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
635 try {
636 StructStat sockStat = Os.fstat(sockFd);
637 StructStat argStat = Os.fstat(arg);
638
639 return sockStat.st_ino == argStat.st_ino
640 && sockStat.st_dev == argStat.st_dev;
641 } catch (ErrnoException e) {
642 return false;
643 }
644 };
645
646 verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
647 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
648 }
ludi1a06aa72017-05-12 09:15:00 -0700649}