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