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