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