blob: 5be7c7bb204066bd134c8cc6ede696db6f0b793e [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
ludi1a06aa72017-05-12 09:15:00 -0700108 static {
109 try {
110 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
111 } catch (UnknownHostException e) {
112 throw new RuntimeException(e);
113 }
114 }
115
ludi1a06aa72017-05-12 09:15:00 -0700116 Context mMockContext;
117 INetd mMockNetd;
118 IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
119 IpSecService mIpSecService;
120
121 @Before
122 public void setUp() throws Exception {
123 mMockContext = mock(Context.class);
124 mMockNetd = mock(INetd.class);
125 mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
126 mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
127
128 // Injecting mock netd
129 when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
130 }
131
132 @Test
133 public void testIpSecServiceCreate() throws InterruptedException {
134 IpSecService ipSecSrv = IpSecService.create(mMockContext);
135 assertNotNull(ipSecSrv);
136 }
137
138 @Test
ludi1a06aa72017-05-12 09:15:00 -0700139 public void testReleaseInvalidSecurityParameterIndex() throws Exception {
140 try {
141 mIpSecService.releaseSecurityParameterIndex(1);
142 fail("IllegalArgumentException not thrown");
143 } catch (IllegalArgumentException e) {
144 }
145 }
146
147 /** This function finds an available port */
148 int findUnusedPort() throws Exception {
149 // Get an available port.
150 ServerSocket s = new ServerSocket(0);
151 int port = s.getLocalPort();
152 s.close();
153 return port;
154 }
155
156 @Test
157 public void testOpenAndCloseUdpEncapsulationSocket() throws Exception {
158 int localport = findUnusedPort();
159
160 IpSecUdpEncapResponse udpEncapResp =
161 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
162 assertNotNull(udpEncapResp);
163 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
164 assertEquals(localport, udpEncapResp.port);
165
166 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
167 udpEncapResp.fileDescriptor.close();
168
Benedict Wong4f9fb812017-12-13 17:16:53 -0800169 // Verify quota and RefcountedResource objects cleaned up
Benedict Wong344bd622017-11-16 15:27:22 -0800170 IpSecService.UserRecord userRecord =
171 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
172 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
173 try {
174 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
175 fail("Expected IllegalArgumentException on attempt to access deleted resource");
176 } catch (IllegalArgumentException expected) {
177
178 }
179 }
180
181 @Test
182 public void testUdpEncapsulationSocketBinderDeath() throws Exception {
Benedict Wong344bd622017-11-16 15:27:22 -0800183 IpSecUdpEncapResponse udpEncapResp =
Benedict Wong4f9fb812017-12-13 17:16:53 -0800184 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
Benedict Wong344bd622017-11-16 15:27:22 -0800185
186 IpSecService.UserRecord userRecord =
187 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
188 IpSecService.RefcountedResource refcountedRecord =
189 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
190 udpEncapResp.resourceId);
191
192 refcountedRecord.binderDied();
193
Benedict Wong4f9fb812017-12-13 17:16:53 -0800194 // Verify quota and RefcountedResource objects cleaned up
Benedict Wong344bd622017-11-16 15:27:22 -0800195 assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
196 try {
197 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
198 fail("Expected IllegalArgumentException on attempt to access deleted resource");
199 } catch (IllegalArgumentException expected) {
200
201 }
ludi1a06aa72017-05-12 09:15:00 -0700202 }
203
204 @Test
205 public void testOpenUdpEncapsulationSocketAfterClose() throws Exception {
206 int localport = findUnusedPort();
207 IpSecUdpEncapResponse udpEncapResp =
208 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
209 assertNotNull(udpEncapResp);
210 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
211 assertEquals(localport, udpEncapResp.port);
212
213 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
214 udpEncapResp.fileDescriptor.close();
215
216 /** Check if localport is available. */
217 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
218 Os.bind(newSocket, INADDR_ANY, localport);
219 Os.close(newSocket);
220 }
221
222 /**
223 * This function checks if the IpSecService holds the reserved port. If
224 * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete.
225 */
226 @Test
227 public void testUdpEncapPortNotReleased() throws Exception {
228 int localport = findUnusedPort();
229 IpSecUdpEncapResponse udpEncapResp =
230 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
231 assertNotNull(udpEncapResp);
232 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
233 assertEquals(localport, udpEncapResp.port);
234
235 udpEncapResp.fileDescriptor.close();
236
237 FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
238 try {
239 Os.bind(newSocket, INADDR_ANY, localport);
240 fail("ErrnoException not thrown");
241 } catch (ErrnoException e) {
242 assertEquals(EADDRINUSE, e.errno);
243 }
244 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
245 }
246
247 @Test
248 public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception {
249 IpSecUdpEncapResponse udpEncapResp =
250 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
251 assertNotNull(udpEncapResp);
252 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
Benedict Wongf186d672017-10-10 20:44:28 -0700253 assertNotEquals(0, udpEncapResp.port);
ludi1a06aa72017-05-12 09:15:00 -0700254 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
255 udpEncapResp.fileDescriptor.close();
256 }
257
258 @Test
259 public void testOpenUdpEncapsulationSocketPortRange() throws Exception {
260 try {
261 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder());
262 fail("IllegalArgumentException not thrown");
263 } catch (IllegalArgumentException e) {
264 }
265
266 try {
267 mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder());
268 fail("IllegalArgumentException not thrown");
269 } catch (IllegalArgumentException e) {
270 }
271 }
272
273 @Test
274 public void testOpenUdpEncapsulationSocketTwice() throws Exception {
275 int localport = findUnusedPort();
276
277 IpSecUdpEncapResponse udpEncapResp =
278 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
279 assertNotNull(udpEncapResp);
280 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
281 assertEquals(localport, udpEncapResp.port);
282 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
283
284 IpSecUdpEncapResponse testUdpEncapResp =
285 mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
286 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status);
287
288 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
289 udpEncapResp.fileDescriptor.close();
290 }
291
292 @Test
293 public void testCloseInvalidUdpEncapsulationSocket() throws Exception {
294 try {
295 mIpSecService.closeUdpEncapsulationSocket(1);
296 fail("IllegalArgumentException not thrown");
297 } catch (IllegalArgumentException e) {
298 }
299 }
300
ludi1a06aa72017-05-12 09:15:00 -0700301 @Test
Benedict Wong4f255702017-11-06 20:49:10 -0800302 public void testValidateAlgorithmsAuth() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800303 // Validate that correct algorithm type succeeds
304 IpSecConfig config = new IpSecConfig();
305 config.setAuthentication(AUTH_ALGO);
306 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800307
Nathan Harold5676f5f2018-01-16 19:34:01 -0800308 // Validate that incorrect algorithm types fails
309 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
310 try {
311 config = new IpSecConfig();
312 config.setAuthentication(algo);
313 mIpSecService.validateAlgorithms(config);
314 fail("Did not throw exception on invalid algorithm type");
315 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800316 }
317 }
318 }
319
320 @Test
321 public void testValidateAlgorithmsCrypt() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800322 // Validate that correct algorithm type succeeds
323 IpSecConfig config = new IpSecConfig();
324 config.setEncryption(CRYPT_ALGO);
325 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800326
Nathan Harold5676f5f2018-01-16 19:34:01 -0800327 // Validate that incorrect algorithm types fails
328 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
329 try {
330 config = new IpSecConfig();
331 config.setEncryption(algo);
332 mIpSecService.validateAlgorithms(config);
333 fail("Did not throw exception on invalid algorithm type");
334 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800335 }
336 }
337 }
338
339 @Test
340 public void testValidateAlgorithmsAead() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800341 // Validate that correct algorithm type succeeds
342 IpSecConfig config = new IpSecConfig();
343 config.setAuthenticatedEncryption(AEAD_ALGO);
344 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800345
Nathan Harold5676f5f2018-01-16 19:34:01 -0800346 // Validate that incorrect algorithm types fails
347 for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
348 try {
349 config = new IpSecConfig();
350 config.setAuthenticatedEncryption(algo);
351 mIpSecService.validateAlgorithms(config);
352 fail("Did not throw exception on invalid algorithm type");
353 } catch (IllegalArgumentException expected) {
Benedict Wong4f255702017-11-06 20:49:10 -0800354 }
355 }
356 }
357
358 @Test
359 public void testValidateAlgorithmsAuthCrypt() {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800360 // Validate that correct algorithm type succeeds
361 IpSecConfig config = new IpSecConfig();
362 config.setAuthentication(AUTH_ALGO);
363 config.setEncryption(CRYPT_ALGO);
364 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800365 }
366
367 @Test
368 public void testValidateAlgorithmsNoAlgorithms() {
369 IpSecConfig config = new IpSecConfig();
370 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800371 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800372 fail("Expected exception; no algorithms specified");
373 } catch (IllegalArgumentException expected) {
374 }
375 }
376
377 @Test
378 public void testValidateAlgorithmsAeadWithAuth() {
379 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800380 config.setAuthenticatedEncryption(AEAD_ALGO);
381 config.setAuthentication(AUTH_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800382 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800383 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800384 fail("Expected exception; both AEAD and auth algorithm specified");
385 } catch (IllegalArgumentException expected) {
386 }
387 }
388
389 @Test
390 public void testValidateAlgorithmsAeadWithCrypt() {
391 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800392 config.setAuthenticatedEncryption(AEAD_ALGO);
393 config.setEncryption(CRYPT_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800394 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800395 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800396 fail("Expected exception; both AEAD and crypt algorithm specified");
397 } catch (IllegalArgumentException expected) {
398 }
399 }
400
401 @Test
402 public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
403 IpSecConfig config = new IpSecConfig();
Nathan Harold5676f5f2018-01-16 19:34:01 -0800404 config.setAuthenticatedEncryption(AEAD_ALGO);
405 config.setAuthentication(AUTH_ALGO);
406 config.setEncryption(CRYPT_ALGO);
Benedict Wong4f255702017-11-06 20:49:10 -0800407 try {
Nathan Harold5676f5f2018-01-16 19:34:01 -0800408 mIpSecService.validateAlgorithms(config);
Benedict Wong4f255702017-11-06 20:49:10 -0800409 fail("Expected exception; AEAD, auth and crypt algorithm specified");
410 } catch (IllegalArgumentException expected) {
411 }
412 }
413
414 @Test
Benedict Wongf33f03132018-01-18 14:38:16 -0800415 public void testDeleteInvalidTransform() throws Exception {
ludi1a06aa72017-05-12 09:15:00 -0700416 try {
Benedict Wongf33f03132018-01-18 14:38:16 -0800417 mIpSecService.deleteTransform(1);
ludi1a06aa72017-05-12 09:15:00 -0700418 fail("IllegalArgumentException not thrown");
419 } catch (IllegalArgumentException e) {
420 }
421 }
422
423 @Test
ludi1a06aa72017-05-12 09:15:00 -0700424 public void testRemoveTransportModeTransform() throws Exception {
Josh Gao42bd8e12018-10-31 12:26:40 -0700425 Socket socket = new Socket();
426 socket.bind(null);
427 ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
Nathan Haroldf73d2522018-01-17 01:00:20 -0800428 mIpSecService.removeTransportModeTransforms(pfd);
ludi1a06aa72017-05-12 09:15:00 -0700429
Luke Huange8e522b2018-11-23 12:01:41 +0800430 verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
ludi1a06aa72017-05-12 09:15:00 -0700431 }
Nathan Harolda10003d2017-08-23 13:46:33 -0700432
433 @Test
434 public void testValidateIpAddresses() throws Exception {
435 String[] invalidAddresses =
436 new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""};
437 for (String address : invalidAddresses) {
438 try {
439 IpSecSpiResponse spiResp =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800440 mIpSecService.allocateSecurityParameterIndex(
Nathan Harolda2523312018-01-05 19:25:13 -0800441 address, DROID_SPI, new Binder());
Nathan Harolda10003d2017-08-23 13:46:33 -0700442 fail("Invalid address was passed through IpSecService validation: " + address);
443 } catch (IllegalArgumentException e) {
444 } catch (Exception e) {
445 fail(
446 "Invalid InetAddress was not caught in validation: "
447 + address
448 + ", Exception: "
449 + e);
450 }
451 }
452 }
ludiaa5c1dc2017-10-16 15:09:41 -0700453
454 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800455 * This function checks if the number of encap UDP socket that one UID can reserve has a
456 * reasonable limit.
ludiaa5c1dc2017-10-16 15:09:41 -0700457 */
458 @Test
459 public void testSocketResourceTrackerLimitation() throws Exception {
460 List<IpSecUdpEncapResponse> openUdpEncapSockets = new ArrayList<IpSecUdpEncapResponse>();
461 // Reserve sockets until it fails.
462 for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) {
463 IpSecUdpEncapResponse newUdpEncapSocket =
464 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
465 assertNotNull(newUdpEncapSocket);
466 if (IpSecManager.Status.OK != newUdpEncapSocket.status) {
467 break;
468 }
469 openUdpEncapSockets.add(newUdpEncapSocket);
470 }
471 // Assert that the total sockets quota has a reasonable limit.
ludi6b7fb6b2017-11-15 14:05:16 -0800472 assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty());
ludiaa5c1dc2017-10-16 15:09:41 -0700473 assertTrue(
ludi6b7fb6b2017-11-15 14:05:16 -0800474 "Number of open UDP encap sockets is out of bound",
475 openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
ludiaa5c1dc2017-10-16 15:09:41 -0700476
477 // Try to reserve one more UDP encapsulation socket, and should fail.
478 IpSecUdpEncapResponse extraUdpEncapSocket =
479 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
480 assertNotNull(extraUdpEncapSocket);
481 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
482
ludi6b7fb6b2017-11-15 14:05:16 -0800483 // Close one of the open UDP encapsulation sockets.
ludiaa5c1dc2017-10-16 15:09:41 -0700484 mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
485 openUdpEncapSockets.get(0).fileDescriptor.close();
486 openUdpEncapSockets.remove(0);
487
488 // Try to reserve one more UDP encapsulation socket, and should be successful.
489 extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
490 assertNotNull(extraUdpEncapSocket);
491 assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status);
492 openUdpEncapSockets.add(extraUdpEncapSocket);
493
494 // Close open UDP sockets.
495 for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) {
496 mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId);
497 openSocket.fileDescriptor.close();
498 }
499 }
500
501 /**
ludi6b7fb6b2017-11-15 14:05:16 -0800502 * This function checks if the number of SPI that one UID can reserve has a reasonable limit.
503 * This test does not test for both address families or duplicate SPIs because resource tracking
504 * code does not depend on them.
ludiaa5c1dc2017-10-16 15:09:41 -0700505 */
506 @Test
507 public void testSpiResourceTrackerLimitation() throws Exception {
508 List<IpSecSpiResponse> reservedSpis = new ArrayList<IpSecSpiResponse>();
509 // Return the same SPI for all SPI allocation since IpSecService only
510 // tracks the resource ID.
511 when(mMockNetd.ipSecAllocateSpi(
512 anyInt(),
ludiaa5c1dc2017-10-16 15:09:41 -0700513 anyString(),
514 eq(InetAddress.getLoopbackAddress().getHostAddress()),
515 anyInt()))
516 .thenReturn(DROID_SPI);
517 // Reserve spis until it fails.
518 for (int i = 0; i < MAX_NUM_SPIS; i++) {
519 IpSecSpiResponse newSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800520 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700521 InetAddress.getLoopbackAddress().getHostAddress(),
522 DROID_SPI + i,
523 new Binder());
524 assertNotNull(newSpi);
525 if (IpSecManager.Status.OK != newSpi.status) {
526 break;
527 }
528 reservedSpis.add(newSpi);
529 }
530 // Assert that the SPI quota has a reasonable limit.
531 assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS);
532
533 // Try to reserve one more SPI, and should fail.
534 IpSecSpiResponse extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800535 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700536 InetAddress.getLoopbackAddress().getHostAddress(),
537 DROID_SPI + MAX_NUM_SPIS,
538 new Binder());
539 assertNotNull(extraSpi);
540 assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status);
541
542 // Release one reserved spi.
543 mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId);
544 reservedSpis.remove(0);
545
546 // Should successfully reserve one more spi.
547 extraSpi =
Jonathan Basseri5fb92902017-11-16 10:58:01 -0800548 mIpSecService.allocateSecurityParameterIndex(
ludiaa5c1dc2017-10-16 15:09:41 -0700549 InetAddress.getLoopbackAddress().getHostAddress(),
550 DROID_SPI + MAX_NUM_SPIS,
551 new Binder());
552 assertNotNull(extraSpi);
553 assertEquals(IpSecManager.Status.OK, extraSpi.status);
554
555 // Release reserved SPIs.
556 for (IpSecSpiResponse spiResp : reservedSpis) {
557 mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
558 }
559 }
Benedict Wongbabe5d72017-12-03 19:42:36 -0800560
561 @Test
562 public void testUidFdtagger() throws Exception {
563 SocketTagger actualSocketTagger = SocketTagger.get();
564
565 try {
566 FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
567
568 // Has to be done after socket creation because BlockGuardOS calls tag on new sockets
569 SocketTagger mockSocketTagger = mock(SocketTagger.class);
570 SocketTagger.set(mockSocketTagger);
571
572 mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID);
573 verify(mockSocketTagger).tag(eq(sockFd));
574 } finally {
575 SocketTagger.set(actualSocketTagger);
576 }
577 }
578
579 /**
580 * Checks if two file descriptors point to the same file.
581 *
582 * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated
583 * file descriptors is to check their inode and device. These two entries uniquely identify any
584 * file.
585 */
586 private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) {
587 try {
588 StructStat fd1Stat = Os.fstat(fd1);
589 StructStat fd2Stat = Os.fstat(fd2);
590
591 return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev;
592 } catch (ErrnoException e) {
593 return false;
594 }
595 }
596
597 @Test
598 public void testOpenUdpEncapSocketTagsSocket() throws Exception {
599 IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
600 IpSecService testIpSecService =
601 new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
602
603 IpSecUdpEncapResponse udpEncapResp =
604 testIpSecService.openUdpEncapsulationSocket(0, new Binder());
605 assertNotNull(udpEncapResp);
606 assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
607
608 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
609 ArgumentMatcher<FileDescriptor> fdMatcher =
610 (argFd) -> {
611 return fileDescriptorsEqual(sockFd, argFd);
612 };
613 verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid()));
614
615 testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
616 udpEncapResp.fileDescriptor.close();
617 }
Benedict Wongba8d3132017-12-06 21:56:35 -0800618
619 @Test
620 public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception {
621 IpSecUdpEncapResponse udpEncapResp =
622 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
623
624 FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
Luke Huange8e522b2018-11-23 12:01:41 +0800625 ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
Benedict Wongba8d3132017-12-06 21:56:35 -0800626 try {
627 StructStat sockStat = Os.fstat(sockFd);
Luke Huange8e522b2018-11-23 12:01:41 +0800628 StructStat argStat = Os.fstat(arg.getFileDescriptor());
Benedict Wongba8d3132017-12-06 21:56:35 -0800629
630 return sockStat.st_ino == argStat.st_ino
631 && sockStat.st_dev == argStat.st_dev;
632 } catch (ErrnoException e) {
633 return false;
634 }
635 };
636
637 verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
638 mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
639 }
Benedict Wong8149f6e2018-01-18 18:31:45 -0800640
641 @Test
642 public void testReserveNetId() {
643 int start = mIpSecService.TUN_INTF_NETID_START;
644 for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
645 assertEquals(start + i, mIpSecService.reserveNetId());
646 }
647
648 // Check that resource exhaustion triggers an exception
649 try {
650 mIpSecService.reserveNetId();
651 fail("Did not throw error for all netIds reserved");
652 } catch (IllegalStateException expected) {
653 }
654
655 // Now release one and try again
656 int releasedNetId =
657 mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
658 mIpSecService.releaseNetId(releasedNetId);
659 assertEquals(releasedNetId, mIpSecService.reserveNetId());
660 }
ludi1a06aa72017-05-12 09:15:00 -0700661}