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