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