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.content.pm.PackageManager.PERMISSION_DENIED;
20 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
21 import static android.net.INetd.IF_STATE_DOWN;
22 import static android.net.INetd.IF_STATE_UP;
23 import static android.net.IpSecManager.DIRECTION_FWD;
24 import static android.net.IpSecManager.DIRECTION_IN;
25 import static android.net.IpSecManager.DIRECTION_OUT;
26 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
27 import static android.system.OsConstants.AF_INET;
28 import static android.system.OsConstants.AF_INET6;
29 
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertNotNull;
32 import static org.junit.Assert.fail;
33 import static org.mockito.ArgumentMatchers.argThat;
34 import static org.mockito.Matchers.anyInt;
35 import static org.mockito.Matchers.anyString;
36 import static org.mockito.Matchers.eq;
37 import static org.mockito.Mockito.mock;
38 import static org.mockito.Mockito.times;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.when;
41 
42 import android.app.AppOpsManager;
43 import android.content.Context;
44 import android.content.pm.PackageManager;
45 import android.net.ConnectivityManager;
46 import android.net.INetd;
47 import android.net.InetAddresses;
48 import android.net.InterfaceConfigurationParcel;
49 import android.net.IpSecAlgorithm;
50 import android.net.IpSecConfig;
51 import android.net.IpSecManager;
52 import android.net.IpSecSpiResponse;
53 import android.net.IpSecTransform;
54 import android.net.IpSecTransformResponse;
55 import android.net.IpSecTunnelInterfaceResponse;
56 import android.net.IpSecUdpEncapResponse;
57 import android.net.LinkAddress;
58 import android.net.LinkProperties;
59 import android.net.Network;
60 import android.os.Binder;
61 import android.os.Build;
62 import android.os.ParcelFileDescriptor;
63 import android.system.Os;
64 import android.test.mock.MockContext;
65 import android.util.ArraySet;
66 
67 import androidx.test.filters.SmallTest;
68 
69 import com.android.server.IpSecService.TunnelInterfaceRecord;
70 import com.android.testutils.DevSdkIgnoreRule;
71 
72 import org.junit.Before;
73 import org.junit.Ignore;
74 import org.junit.Rule;
75 import org.junit.Test;
76 import org.junit.runner.RunWith;
77 import org.junit.runners.Parameterized;
78 
79 import java.net.Inet4Address;
80 import java.net.Socket;
81 import java.util.Arrays;
82 import java.util.Collection;
83 import java.util.Set;
84 
85 /** Unit tests for {@link IpSecService}. */
86 @SmallTest
87 @RunWith(Parameterized.class)
88 public class IpSecServiceParameterizedTest {
89     @Rule
90     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(
91             Build.VERSION_CODES.R /* ignoreClassUpTo */);
92 
93     private static final int TEST_SPI = 0xD1201D;
94 
95     private final String mSourceAddr;
96     private final String mDestinationAddr;
97     private final LinkAddress mLocalInnerAddress;
98     private final int mFamily;
99 
100     private static final int[] ADDRESS_FAMILIES =
101             new int[] {AF_INET, AF_INET6};
102 
103     @Parameterized.Parameters
104     public static Collection ipSecConfigs() {
105         return Arrays.asList(
106                 new Object[][] {
107                 {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET},
108                 {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6}
109         });
110     }
111 
112     private static final byte[] AEAD_KEY = {
113         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
114         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
115         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
116         0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
117         0x73, 0x61, 0x6C, 0x74
118     };
119     private static final byte[] CRYPT_KEY = {
120         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
121         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
122         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
123         0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
124     };
125     private static final byte[] AUTH_KEY = {
126         0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
128         0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
130     };
131 
132     AppOpsManager mMockAppOps = mock(AppOpsManager.class);
133     ConnectivityManager mMockConnectivityMgr = mock(ConnectivityManager.class);
134 
135     TestContext mTestContext = new TestContext();
136 
137     private class TestContext extends MockContext {
138         private Set<String> mAllowedPermissions = new ArraySet<>(Arrays.asList(
139                 android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
140                 android.Manifest.permission.NETWORK_STACK,
141                 PERMISSION_MAINLINE_NETWORK_STACK));
142 
143         private void setAllowedPermissions(String... permissions) {
144             mAllowedPermissions = new ArraySet<>(permissions);
145         }
146 
147         @Override
148         public Object getSystemService(String name) {
149             switch(name) {
150                 case Context.APP_OPS_SERVICE:
151                     return mMockAppOps;
152                 case Context.CONNECTIVITY_SERVICE:
153                     return mMockConnectivityMgr;
154                 default:
155                     return null;
156             }
157         }
158 
159         @Override
160         public String getSystemServiceName(Class<?> serviceClass) {
161             if (ConnectivityManager.class == serviceClass) {
162                 return Context.CONNECTIVITY_SERVICE;
163             }
164             return null;
165         }
166 
167         @Override
168         public PackageManager getPackageManager() {
169             return mMockPkgMgr;
170         }
171 
172         @Override
173         public void enforceCallingOrSelfPermission(String permission, String message) {
174             if (mAllowedPermissions.contains(permission)) {
175                 return;
176             } else {
177                 throw new SecurityException("Unavailable permission requested");
178             }
179         }
180 
181         @Override
182         public int checkCallingOrSelfPermission(String permission) {
183             if (mAllowedPermissions.contains(permission)) {
184                 return PERMISSION_GRANTED;
185             } else {
186                 return PERMISSION_DENIED;
187             }
188         }
189     }
190 
191     INetd mMockNetd;
192     PackageManager mMockPkgMgr;
193     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
194     IpSecService mIpSecService;
195     Network fakeNetwork = new Network(0xAB);
196     int mUid = Os.getuid();
197 
198     private static final IpSecAlgorithm AUTH_ALGO =
199             new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
200     private static final IpSecAlgorithm CRYPT_ALGO =
201             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
202     private static final IpSecAlgorithm AEAD_ALGO =
203             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
204     private static final int REMOTE_ENCAP_PORT = 4500;
205 
206     private static final String BLESSED_PACKAGE = "blessedPackage";
207     private static final String SYSTEM_PACKAGE = "systemPackage";
208     private static final String BAD_PACKAGE = "badPackage";
209 
210     public IpSecServiceParameterizedTest(
211             String sourceAddr, String destAddr, String localInnerAddr, int family) {
212         mSourceAddr = sourceAddr;
213         mDestinationAddr = destAddr;
214         mLocalInnerAddress = new LinkAddress(localInnerAddr);
215         mFamily = family;
216     }
217 
218     @Before
219     public void setUp() throws Exception {
220         mMockNetd = mock(INetd.class);
221         mMockPkgMgr = mock(PackageManager.class);
222         mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
223         mIpSecService = new IpSecService(mTestContext, mMockIpSecSrvConfig);
224 
225         // Injecting mock netd
226         when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
227 
228         // PackageManager should always return true (feature flag tests in IpSecServiceTest)
229         when(mMockPkgMgr.hasSystemFeature(anyString())).thenReturn(true);
230 
231         // A package granted the AppOp for MANAGE_IPSEC_TUNNELS will be MODE_ALLOWED.
232         when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BLESSED_PACKAGE)))
233                 .thenReturn(AppOpsManager.MODE_ALLOWED);
234         // A system package will not be granted the app op, so this should fall back to
235         // a permissions check, which should pass.
236         when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(SYSTEM_PACKAGE)))
237                 .thenReturn(AppOpsManager.MODE_DEFAULT);
238         // A mismatch between the package name and the UID will return MODE_IGNORED.
239         when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BAD_PACKAGE)))
240                 .thenReturn(AppOpsManager.MODE_IGNORED);
241     }
242 
243     //TODO: Add a test to verify SPI.
244 
245     @Test
246     public void testIpSecServiceReserveSpi() throws Exception {
247         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
248                 .thenReturn(TEST_SPI);
249 
250         IpSecSpiResponse spiResp =
251                 mIpSecService.allocateSecurityParameterIndex(
252                         mDestinationAddr, TEST_SPI, new Binder());
253         assertEquals(IpSecManager.Status.OK, spiResp.status);
254         assertEquals(TEST_SPI, spiResp.spi);
255     }
256 
257     @Test
258     public void testReleaseSecurityParameterIndex() throws Exception {
259         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
260                 .thenReturn(TEST_SPI);
261 
262         IpSecSpiResponse spiResp =
263                 mIpSecService.allocateSecurityParameterIndex(
264                         mDestinationAddr, TEST_SPI, new Binder());
265 
266         mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
267 
268         verify(mMockNetd)
269                 .ipSecDeleteSecurityAssociation(
270                         eq(mUid),
271                         anyString(),
272                         anyString(),
273                         eq(TEST_SPI),
274                         anyInt(),
275                         anyInt(),
276                         anyInt());
277 
278         // Verify quota and RefcountedResource objects cleaned up
279         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
280         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
281         try {
282             userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
283             fail("Expected IllegalArgumentException on attempt to access deleted resource");
284         } catch (IllegalArgumentException expected) {
285 
286         }
287     }
288 
289     @Test
290     public void testSecurityParameterIndexBinderDeath() throws Exception {
291         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
292                 .thenReturn(TEST_SPI);
293 
294         IpSecSpiResponse spiResp =
295                 mIpSecService.allocateSecurityParameterIndex(
296                         mDestinationAddr, TEST_SPI, new Binder());
297 
298         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
299         IpSecService.RefcountedResource refcountedRecord =
300                 userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
301 
302         refcountedRecord.binderDied();
303 
304         verify(mMockNetd)
305                 .ipSecDeleteSecurityAssociation(
306                         eq(mUid),
307                         anyString(),
308                         anyString(),
309                         eq(TEST_SPI),
310                         anyInt(),
311                         anyInt(),
312                         anyInt());
313 
314         // Verify quota and RefcountedResource objects cleaned up
315         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
316         try {
317             userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
318             fail("Expected IllegalArgumentException on attempt to access deleted resource");
319         } catch (IllegalArgumentException expected) {
320 
321         }
322     }
323 
324     private int getNewSpiResourceId(String remoteAddress, int returnSpi) throws Exception {
325         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), anyString(), anyInt()))
326                 .thenReturn(returnSpi);
327 
328         IpSecSpiResponse spi =
329                 mIpSecService.allocateSecurityParameterIndex(
330                         InetAddresses.parseNumericAddress(remoteAddress).getHostAddress(),
331                         IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
332                         new Binder());
333         return spi.resourceId;
334     }
335 
336     private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception {
337         config.setSpiResourceId(getNewSpiResourceId(mDestinationAddr, TEST_SPI));
338         config.setSourceAddress(mSourceAddr);
339         config.setDestinationAddress(mDestinationAddr);
340     }
341 
342     private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception {
343         config.setEncryption(CRYPT_ALGO);
344         config.setAuthentication(AUTH_ALGO);
345     }
346 
347     private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception {
348         config.setEncapType(IpSecTransform.ENCAP_ESPINUDP);
349         config.setEncapSocketResourceId(resourceId);
350         config.setEncapRemotePort(REMOTE_ENCAP_PORT);
351     }
352 
353     private void verifyTransformNetdCalledForCreatingSA(
354             IpSecConfig config, IpSecTransformResponse resp) throws Exception {
355         verifyTransformNetdCalledForCreatingSA(config, resp, 0);
356     }
357 
358     private void verifyTransformNetdCalledForCreatingSA(
359             IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception {
360         IpSecAlgorithm auth = config.getAuthentication();
361         IpSecAlgorithm crypt = config.getEncryption();
362         IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption();
363 
364         verify(mMockNetd, times(1))
365                 .ipSecAddSecurityAssociation(
366                         eq(mUid),
367                         eq(config.getMode()),
368                         eq(config.getSourceAddress()),
369                         eq(config.getDestinationAddress()),
370                         eq((config.getNetwork() != null) ? config.getNetwork().netId : 0),
371                         eq(TEST_SPI),
372                         eq(0),
373                         eq(0),
374                         eq((auth != null) ? auth.getName() : ""),
375                         eq((auth != null) ? auth.getKey() : new byte[] {}),
376                         eq((auth != null) ? auth.getTruncationLengthBits() : 0),
377                         eq((crypt != null) ? crypt.getName() : ""),
378                         eq((crypt != null) ? crypt.getKey() : new byte[] {}),
379                         eq((crypt != null) ? crypt.getTruncationLengthBits() : 0),
380                         eq((authCrypt != null) ? authCrypt.getName() : ""),
381                         eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}),
382                         eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
383                         eq(config.getEncapType()),
384                         eq(encapSocketPort),
385                         eq(config.getEncapRemotePort()),
386                         eq(config.getXfrmInterfaceId()));
387     }
388 
389     @Test
390     public void testCreateTransform() throws Exception {
391         IpSecConfig ipSecConfig = new IpSecConfig();
392         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
393         addAuthAndCryptToIpSecConfig(ipSecConfig);
394 
395         IpSecTransformResponse createTransformResp =
396                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
397         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
398 
399         verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
400     }
401 
402     @Test
403     public void testCreateTransformAead() throws Exception {
404         IpSecConfig ipSecConfig = new IpSecConfig();
405         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
406 
407         ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
408 
409         IpSecTransformResponse createTransformResp =
410                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
411         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
412 
413         verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
414     }
415 
416     @Test
417     public void testCreateTransportModeTransformWithEncap() throws Exception {
418         IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
419 
420         IpSecConfig ipSecConfig = new IpSecConfig();
421         ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT);
422         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
423         addAuthAndCryptToIpSecConfig(ipSecConfig);
424         addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
425 
426         if (mFamily == AF_INET) {
427             IpSecTransformResponse createTransformResp =
428                     mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
429             assertEquals(IpSecManager.Status.OK, createTransformResp.status);
430 
431             verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
432         } else {
433             try {
434                 IpSecTransformResponse createTransformResp =
435                         mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
436                 fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
437             } catch (IllegalArgumentException expected) {
438             }
439         }
440     }
441 
442     @Test
443     public void testCreateTunnelModeTransformWithEncap() throws Exception {
444         IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
445 
446         IpSecConfig ipSecConfig = new IpSecConfig();
447         ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
448         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
449         addAuthAndCryptToIpSecConfig(ipSecConfig);
450         addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
451 
452         if (mFamily == AF_INET) {
453             IpSecTransformResponse createTransformResp =
454                     mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
455             assertEquals(IpSecManager.Status.OK, createTransformResp.status);
456 
457             verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
458         } else {
459             try {
460                 IpSecTransformResponse createTransformResp =
461                         mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
462                 fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
463             } catch (IllegalArgumentException expected) {
464             }
465         }
466     }
467 
468     @Test
469     public void testCreateTwoTransformsWithSameSpis() throws Exception {
470         IpSecConfig ipSecConfig = new IpSecConfig();
471         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
472         addAuthAndCryptToIpSecConfig(ipSecConfig);
473 
474         IpSecTransformResponse createTransformResp =
475                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
476         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
477 
478         // Attempting to create transform a second time with the same SPIs should throw an error...
479         try {
480             mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
481                 fail("IpSecService should have thrown an error for reuse of SPI");
482         } catch (IllegalStateException expected) {
483         }
484 
485         // ... even if the transform is deleted
486         mIpSecService.deleteTransform(createTransformResp.resourceId);
487         try {
488             mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
489                 fail("IpSecService should have thrown an error for reuse of SPI");
490         } catch (IllegalStateException expected) {
491         }
492     }
493 
494     @Test
495     public void testReleaseOwnedSpi() throws Exception {
496         IpSecConfig ipSecConfig = new IpSecConfig();
497         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
498         addAuthAndCryptToIpSecConfig(ipSecConfig);
499 
500         IpSecTransformResponse createTransformResp =
501                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
502         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
503         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
504         mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
505         verify(mMockNetd, times(0))
506                 .ipSecDeleteSecurityAssociation(
507                         eq(mUid),
508                         anyString(),
509                         anyString(),
510                         eq(TEST_SPI),
511                         anyInt(),
512                         anyInt(),
513                         anyInt());
514         // quota is not released until the SPI is released by the Transform
515         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
516     }
517 
518     @Test
519     public void testDeleteTransform() throws Exception {
520         IpSecConfig ipSecConfig = new IpSecConfig();
521         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
522         addAuthAndCryptToIpSecConfig(ipSecConfig);
523 
524         IpSecTransformResponse createTransformResp =
525                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
526         mIpSecService.deleteTransform(createTransformResp.resourceId);
527 
528         verify(mMockNetd, times(1))
529                 .ipSecDeleteSecurityAssociation(
530                         eq(mUid),
531                         anyString(),
532                         anyString(),
533                         eq(TEST_SPI),
534                         anyInt(),
535                         anyInt(),
536                         anyInt());
537 
538         // Verify quota and RefcountedResource objects cleaned up
539         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
540         assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
541         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
542 
543         mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
544         // Verify that ipSecDeleteSa was not called when the SPI was released because the
545         // ownedByTransform property should prevent it; (note, the called count is cumulative).
546         verify(mMockNetd, times(1))
547                 .ipSecDeleteSecurityAssociation(
548                         anyInt(),
549                         anyString(),
550                         anyString(),
551                         anyInt(),
552                         anyInt(),
553                         anyInt(),
554                         anyInt());
555         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
556 
557         try {
558             userRecord.mTransformRecords.getRefcountedResourceOrThrow(
559                     createTransformResp.resourceId);
560             fail("Expected IllegalArgumentException on attempt to access deleted resource");
561         } catch (IllegalArgumentException expected) {
562 
563         }
564     }
565 
566     @Test
567     public void testTransportModeTransformBinderDeath() throws Exception {
568         IpSecConfig ipSecConfig = new IpSecConfig();
569         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
570         addAuthAndCryptToIpSecConfig(ipSecConfig);
571 
572         IpSecTransformResponse createTransformResp =
573                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
574 
575         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
576         IpSecService.RefcountedResource refcountedRecord =
577                 userRecord.mTransformRecords.getRefcountedResourceOrThrow(
578                         createTransformResp.resourceId);
579 
580         refcountedRecord.binderDied();
581 
582         verify(mMockNetd)
583                 .ipSecDeleteSecurityAssociation(
584                         eq(mUid),
585                         anyString(),
586                         anyString(),
587                         eq(TEST_SPI),
588                         anyInt(),
589                         anyInt(),
590                         anyInt());
591 
592         // Verify quota and RefcountedResource objects cleaned up
593         assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
594         try {
595             userRecord.mTransformRecords.getRefcountedResourceOrThrow(
596                     createTransformResp.resourceId);
597             fail("Expected IllegalArgumentException on attempt to access deleted resource");
598         } catch (IllegalArgumentException expected) {
599 
600         }
601     }
602 
603     @Test
604     public void testApplyTransportModeTransform() throws Exception {
605         verifyApplyTransportModeTransformCommon(false);
606     }
607 
608     @Test
609     public void testApplyTransportModeTransformReleasedSpi() throws Exception {
610         verifyApplyTransportModeTransformCommon(true);
611     }
612 
613     public void verifyApplyTransportModeTransformCommon(
614                 boolean closeSpiBeforeApply) throws Exception {
615         IpSecConfig ipSecConfig = new IpSecConfig();
616         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
617         addAuthAndCryptToIpSecConfig(ipSecConfig);
618 
619         IpSecTransformResponse createTransformResp =
620                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
621 
622         if (closeSpiBeforeApply) {
623             mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
624         }
625 
626         Socket socket = new Socket();
627         socket.bind(null);
628         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
629 
630         int resourceId = createTransformResp.resourceId;
631         mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
632 
633         verify(mMockNetd)
634                 .ipSecApplyTransportModeTransform(
635                         eq(pfd),
636                         eq(mUid),
637                         eq(IpSecManager.DIRECTION_OUT),
638                         anyString(),
639                         anyString(),
640                         eq(TEST_SPI));
641     }
642 
643     @Test
644     public void testApplyTransportModeTransformWithClosedSpi() throws Exception {
645         IpSecConfig ipSecConfig = new IpSecConfig();
646         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
647         addAuthAndCryptToIpSecConfig(ipSecConfig);
648 
649         IpSecTransformResponse createTransformResp =
650                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
651 
652         // Close SPI record
653         mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
654 
655         Socket socket = new Socket();
656         socket.bind(null);
657         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
658 
659         int resourceId = createTransformResp.resourceId;
660         mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
661 
662         verify(mMockNetd)
663                 .ipSecApplyTransportModeTransform(
664                         eq(pfd),
665                         eq(mUid),
666                         eq(IpSecManager.DIRECTION_OUT),
667                         anyString(),
668                         anyString(),
669                         eq(TEST_SPI));
670     }
671 
672     @Test
673     public void testRemoveTransportModeTransform() throws Exception {
674         Socket socket = new Socket();
675         socket.bind(null);
676         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
677         mIpSecService.removeTransportModeTransforms(pfd);
678 
679         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
680     }
681 
682     private IpSecTunnelInterfaceResponse createAndValidateTunnel(
683             String localAddr, String remoteAddr, String pkgName) throws Exception {
684         final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel();
685         config.flags = new String[] {IF_STATE_DOWN};
686         when(mMockNetd.interfaceGetCfg(anyString())).thenReturn(config);
687         IpSecTunnelInterfaceResponse createTunnelResp =
688                 mIpSecService.createTunnelInterface(
689                         mSourceAddr, mDestinationAddr, fakeNetwork, new Binder(), pkgName);
690 
691         assertNotNull(createTunnelResp);
692         assertEquals(IpSecManager.Status.OK, createTunnelResp.status);
693         for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT, DIRECTION_FWD}) {
694             for (int selAddrFamily : ADDRESS_FAMILIES) {
695                 verify(mMockNetd).ipSecAddSecurityPolicy(
696                         eq(mUid),
697                         eq(selAddrFamily),
698                         eq(direction),
699                         anyString(),
700                         anyString(),
701                         eq(0),
702                         anyInt(), // iKey/oKey
703                         anyInt(), // mask
704                         eq(createTunnelResp.resourceId));
705             }
706         }
707 
708         return createTunnelResp;
709     }
710 
711     @Test
712     public void testCreateTunnelInterface() throws Exception {
713         IpSecTunnelInterfaceResponse createTunnelResp =
714                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
715 
716         // Check that we have stored the tracking object, and retrieve it
717         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
718         IpSecService.RefcountedResource refcountedRecord =
719                 userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
720                         createTunnelResp.resourceId);
721 
722         assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
723         verify(mMockNetd)
724                 .ipSecAddTunnelInterface(
725                         eq(createTunnelResp.interfaceName),
726                         eq(mSourceAddr),
727                         eq(mDestinationAddr),
728                         anyInt(),
729                         anyInt(),
730                         anyInt());
731         verify(mMockNetd).interfaceSetCfg(argThat(
732                 config -> Arrays.asList(config.flags).contains(IF_STATE_UP)));
733     }
734 
735     @Test
736     public void testDeleteTunnelInterface() throws Exception {
737         IpSecTunnelInterfaceResponse createTunnelResp =
738                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
739 
740         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
741 
742         mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, BLESSED_PACKAGE);
743 
744         // Verify quota and RefcountedResource objects cleaned up
745         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
746         verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
747         try {
748             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
749                     createTunnelResp.resourceId);
750             fail("Expected IllegalArgumentException on attempt to access deleted resource");
751         } catch (IllegalArgumentException expected) {
752         }
753     }
754 
755     private Network createFakeUnderlyingNetwork(String interfaceName) {
756         final Network fakeNetwork = new Network(1000);
757         final LinkProperties fakeLp = new LinkProperties();
758         fakeLp.setInterfaceName(interfaceName);
759         when(mMockConnectivityMgr.getLinkProperties(eq(fakeNetwork))).thenReturn(fakeLp);
760         return fakeNetwork;
761     }
762 
763     @Test
764     public void testSetNetworkForTunnelInterface() throws Exception {
765         final IpSecTunnelInterfaceResponse createTunnelResp =
766                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
767         final Network newFakeNetwork = createFakeUnderlyingNetwork("newFakeNetworkInterface");
768         final int tunnelIfaceResourceId = createTunnelResp.resourceId;
769         mIpSecService.setNetworkForTunnelInterface(
770                 tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE);
771 
772         final IpSecService.UserRecord userRecord =
773                 mIpSecService.mUserResourceTracker.getUserRecord(mUid);
774         assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
775 
776         final TunnelInterfaceRecord tunnelInterfaceInfo =
777                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId);
778         assertEquals(newFakeNetwork, tunnelInterfaceInfo.getUnderlyingNetwork());
779     }
780 
781     @Test
782     public void testSetNetworkForTunnelInterfaceFailsForInvalidResourceId() throws Exception {
783         final IpSecTunnelInterfaceResponse createTunnelResp =
784                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
785         final Network newFakeNetwork = new Network(1000);
786 
787         try {
788             mIpSecService.setNetworkForTunnelInterface(
789                     IpSecManager.INVALID_RESOURCE_ID, newFakeNetwork, BLESSED_PACKAGE);
790             fail("Expected an IllegalArgumentException for invalid resource ID.");
791         } catch (IllegalArgumentException expected) {
792         }
793     }
794 
795     @Test
796     public void testSetNetworkForTunnelInterfaceFailsWhenSettingTunnelNetwork() throws Exception {
797         final IpSecTunnelInterfaceResponse createTunnelResp =
798                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
799         final int tunnelIfaceResourceId = createTunnelResp.resourceId;
800         final IpSecService.UserRecord userRecord =
801                 mIpSecService.mUserResourceTracker.getUserRecord(mUid);
802         final TunnelInterfaceRecord tunnelInterfaceInfo =
803                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId);
804 
805         final Network newFakeNetwork =
806                 createFakeUnderlyingNetwork(tunnelInterfaceInfo.getInterfaceName());
807 
808         try {
809             mIpSecService.setNetworkForTunnelInterface(
810                     tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE);
811             fail(
812                     "Expected an IllegalArgumentException because the underlying network is the"
813                             + " network being exposed by this tunnel.");
814         } catch (IllegalArgumentException expected) {
815         }
816     }
817 
818     @Test
819     public void testTunnelInterfaceBinderDeath() throws Exception {
820         IpSecTunnelInterfaceResponse createTunnelResp =
821                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
822 
823         IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
824         IpSecService.RefcountedResource refcountedRecord =
825                 userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
826                         createTunnelResp.resourceId);
827 
828         refcountedRecord.binderDied();
829 
830         // Verify quota and RefcountedResource objects cleaned up
831         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
832         verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
833         try {
834             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
835                     createTunnelResp.resourceId);
836             fail("Expected IllegalArgumentException on attempt to access deleted resource");
837         } catch (IllegalArgumentException expected) {
838         }
839     }
840 
841     @Test
842     public void testApplyTunnelModeTransformOutbound() throws Exception {
843         verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT);
844     }
845 
846     @Test
847     public void testApplyTunnelModeTransformOutboundNonNetworkStack() throws Exception {
848         mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS);
849         verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT);
850     }
851 
852     @Test
853     public void testApplyTunnelModeTransformOutboundReleasedSpi() throws Exception {
854         verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_OUT);
855     }
856 
857     @Test
858     public void testApplyTunnelModeTransformInbound() throws Exception {
859         verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN);
860     }
861 
862     @Test
863     public void testApplyTunnelModeTransformInboundNonNetworkStack() throws Exception {
864         mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS);
865         verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN);
866     }
867 
868     @Test
869     public void testApplyTunnelModeTransformForward() throws Exception {
870         verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD);
871     }
872 
873     @Test
874     public void testApplyTunnelModeTransformForwardNonNetworkStack() throws Exception {
875         mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS);
876 
877         try {
878             verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD);
879             fail("Expected security exception due to use of forward policies without NETWORK_STACK"
880                      + " or MAINLINE_NETWORK_STACK permission");
881         } catch (SecurityException expected) {
882         }
883     }
884 
885     public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply, int direction)
886             throws Exception {
887         IpSecConfig ipSecConfig = new IpSecConfig();
888         ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
889         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
890         addAuthAndCryptToIpSecConfig(ipSecConfig);
891 
892         IpSecTransformResponse createTransformResp =
893                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
894         IpSecTunnelInterfaceResponse createTunnelResp =
895                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
896 
897         if (closeSpiBeforeApply) {
898             mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
899         }
900 
901         int transformResourceId = createTransformResp.resourceId;
902         int tunnelResourceId = createTunnelResp.resourceId;
903         mIpSecService.applyTunnelModeTransform(
904                 tunnelResourceId, direction, transformResourceId, BLESSED_PACKAGE);
905 
906         for (int selAddrFamily : ADDRESS_FAMILIES) {
907             verify(mMockNetd)
908                     .ipSecUpdateSecurityPolicy(
909                             eq(mUid),
910                             eq(selAddrFamily),
911                             eq(direction),
912                             anyString(),
913                             anyString(),
914                             eq(direction == DIRECTION_OUT ? TEST_SPI : 0),
915                             anyInt(), // iKey/oKey
916                             anyInt(), // mask
917                             eq(tunnelResourceId));
918         }
919 
920         ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
921         verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
922     }
923 
924 
925     @Test
926     public void testApplyTunnelModeTransformWithClosedSpi() throws Exception {
927         IpSecConfig ipSecConfig = new IpSecConfig();
928         ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
929         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
930         addAuthAndCryptToIpSecConfig(ipSecConfig);
931 
932         IpSecTransformResponse createTransformResp =
933                 mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
934         IpSecTunnelInterfaceResponse createTunnelResp =
935                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
936 
937         // Close SPI record
938         mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
939 
940         int transformResourceId = createTransformResp.resourceId;
941         int tunnelResourceId = createTunnelResp.resourceId;
942         mIpSecService.applyTunnelModeTransform(
943                 tunnelResourceId, IpSecManager.DIRECTION_OUT, transformResourceId, BLESSED_PACKAGE);
944 
945         for (int selAddrFamily : ADDRESS_FAMILIES) {
946             verify(mMockNetd)
947                     .ipSecUpdateSecurityPolicy(
948                             eq(mUid),
949                             eq(selAddrFamily),
950                             eq(IpSecManager.DIRECTION_OUT),
951                             anyString(),
952                             anyString(),
953                             eq(TEST_SPI),
954                             anyInt(), // iKey/oKey
955                             anyInt(), // mask
956                             eq(tunnelResourceId));
957         }
958 
959         ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
960         verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
961     }
962 
963     @Test
964     public void testAddRemoveAddressFromTunnelInterface() throws Exception {
965         for (String pkgName : new String[] {BLESSED_PACKAGE, SYSTEM_PACKAGE}) {
966             IpSecTunnelInterfaceResponse createTunnelResp =
967                     createAndValidateTunnel(mSourceAddr, mDestinationAddr, pkgName);
968             mIpSecService.addAddressToTunnelInterface(
969                     createTunnelResp.resourceId, mLocalInnerAddress, pkgName);
970             verify(mMockNetd, times(1))
971                     .interfaceAddAddress(
972                             eq(createTunnelResp.interfaceName),
973                             eq(mLocalInnerAddress.getAddress().getHostAddress()),
974                             eq(mLocalInnerAddress.getPrefixLength()));
975             mIpSecService.removeAddressFromTunnelInterface(
976                     createTunnelResp.resourceId, mLocalInnerAddress, pkgName);
977             verify(mMockNetd, times(1))
978                     .interfaceDelAddress(
979                             eq(createTunnelResp.interfaceName),
980                             eq(mLocalInnerAddress.getAddress().getHostAddress()),
981                             eq(mLocalInnerAddress.getPrefixLength()));
982             mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, pkgName);
983         }
984     }
985 
986     @Ignore
987     @Test
988     public void testAddTunnelFailsForBadPackageName() throws Exception {
989         try {
990             IpSecTunnelInterfaceResponse createTunnelResp =
991                     createAndValidateTunnel(mSourceAddr, mDestinationAddr, BAD_PACKAGE);
992             fail("Expected a SecurityException for badPackage.");
993         } catch (SecurityException expected) {
994         }
995     }
996 
997     @Test
998     public void testFeatureFlagVerification() throws Exception {
999         when(mMockPkgMgr.hasSystemFeature(eq(PackageManager.FEATURE_IPSEC_TUNNELS)))
1000                 .thenReturn(false);
1001 
1002         try {
1003             String addr = Inet4Address.getLoopbackAddress().getHostAddress();
1004             mIpSecService.createTunnelInterface(
1005                     addr, addr, new Network(0), new Binder(), BLESSED_PACKAGE);
1006             fail("Expected UnsupportedOperationException for disabled feature");
1007         } catch (UnsupportedOperationException expected) {
1008         }
1009     }
1010 }
1011