• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.os.Build.VERSION_CODES.R;
20 
21 import static com.android.testutils.ContextUtils.mockService;
22 import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
23 import static com.android.testutils.MiscAsserts.assertThrows;
24 
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertNull;
29 import static org.junit.Assert.assertTrue;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.Mockito.doReturn;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 
37 import android.annotation.UserIdInt;
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.pm.PackageManager;
42 import android.net.ConnectivityManager;
43 import android.net.INetd;
44 import android.net.Uri;
45 import android.os.Handler;
46 import android.os.HandlerThread;
47 import android.os.INetworkManagementService;
48 import android.os.Looper;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.security.Credentials;
52 
53 import androidx.test.filters.SmallTest;
54 
55 import com.android.internal.net.VpnProfile;
56 import com.android.server.connectivity.Vpn;
57 import com.android.server.connectivity.VpnProfileStore;
58 import com.android.server.net.LockdownVpnTracker;
59 import com.android.testutils.DevSdkIgnoreRule;
60 import com.android.testutils.DevSdkIgnoreRunner;
61 import com.android.testutils.HandlerUtils;
62 
63 import org.junit.Before;
64 import org.junit.Rule;
65 import org.junit.Test;
66 import org.junit.runner.RunWith;
67 import org.mockito.ArgumentCaptor;
68 import org.mockito.Mock;
69 import org.mockito.MockitoAnnotations;
70 
71 import java.nio.charset.StandardCharsets;
72 import java.util.List;
73 
74 @RunWith(DevSdkIgnoreRunner.class)
75 @IgnoreUpTo(R) // VpnManagerService is not available before R
76 @SmallTest
77 public class VpnManagerServiceTest extends VpnTestBase {
78     private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER";
79 
80     @Rule
81     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
82 
83     private static final int TIMEOUT_MS = 2_000;
84 
85     @Mock Context mContext;
86     @Mock Context mContextWithoutAttributionTag;
87     @Mock Context mSystemContext;
88     @Mock Context mUserAllContext;
89     private HandlerThread mHandlerThread;
90     @Mock private Vpn mVpn;
91     @Mock private INetworkManagementService mNms;
92     @Mock private ConnectivityManager mCm;
93     @Mock private UserManager mUserManager;
94     @Mock private INetd mNetd;
95     @Mock private PackageManager mPackageManager;
96     @Mock private VpnProfileStore mVpnProfileStore;
97     @Mock private LockdownVpnTracker mLockdownVpnTracker;
98 
99     private VpnManagerServiceDependencies mDeps;
100     private VpnManagerService mService;
101     private BroadcastReceiver mUserPresentReceiver;
102     private BroadcastReceiver mIntentReceiver;
103     private final String mNotMyVpnPkg = "com.not.my.vpn";
104 
105     class VpnManagerServiceDependencies extends VpnManagerService.Dependencies {
106         @Override
makeHandlerThread()107         public HandlerThread makeHandlerThread() {
108             return mHandlerThread;
109         }
110 
111         @Override
getINetworkManagementService()112         public INetworkManagementService getINetworkManagementService() {
113             return mNms;
114         }
115 
116         @Override
getNetd()117         public INetd getNetd() {
118             return mNetd;
119         }
120 
121         @Override
createVpn(Looper looper, Context context, INetworkManagementService nms, INetd netd, @UserIdInt int userId)122         public Vpn createVpn(Looper looper, Context context, INetworkManagementService nms,
123                 INetd netd, @UserIdInt int userId) {
124             return mVpn;
125         }
126 
127         @Override
getVpnProfileStore()128         public VpnProfileStore getVpnProfileStore() {
129             return mVpnProfileStore;
130         }
131 
132         @Override
createLockDownVpnTracker(Context context, Handler handler, Vpn vpn, VpnProfile profile)133         public LockdownVpnTracker createLockDownVpnTracker(Context context, Handler handler,
134                 Vpn vpn, VpnProfile profile) {
135             return mLockdownVpnTracker;
136         }
137 
138         @Override
getMainUserId()139         public @UserIdInt int getMainUserId() {
140             return UserHandle.USER_SYSTEM;
141         }
142     }
143 
144     @Before
setUp()145     public void setUp() throws Exception {
146         MockitoAnnotations.initMocks(this);
147 
148         mHandlerThread = new HandlerThread("TestVpnManagerService");
149         mDeps = new VpnManagerServiceDependencies();
150 
151         // The attribution tag is a dependency for IKE library to collect VPN metrics correctly
152         // and thus should not be changed without updating the IKE code.
153         doReturn(mContext)
154                 .when(mContextWithoutAttributionTag)
155                 .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
156 
157         doReturn(mUserAllContext).when(mContext).createContextAsUser(UserHandle.ALL, 0);
158         doReturn(mSystemContext).when(mContext).createContextAsUser(UserHandle.SYSTEM, 0);
159         doReturn(mPackageManager).when(mContext).getPackageManager();
160         setMockedPackages(mPackageManager, sPackages);
161 
162         mockService(mContext, ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mCm);
163         mockService(mContext, UserManager.class, Context.USER_SERVICE, mUserManager);
164         doReturn(SYSTEM_USER).when(mUserManager).getUserInfo(eq(SYSTEM_USER_ID));
165 
166         mService = new VpnManagerService(mContextWithoutAttributionTag, mDeps);
167         mService.systemReady();
168 
169         final ArgumentCaptor<BroadcastReceiver> intentReceiverCaptor =
170                 ArgumentCaptor.forClass(BroadcastReceiver.class);
171         final ArgumentCaptor<BroadcastReceiver> userPresentReceiverCaptor =
172                 ArgumentCaptor.forClass(BroadcastReceiver.class);
173         verify(mSystemContext).registerReceiver(
174                 userPresentReceiverCaptor.capture(), any(), any(), any());
175         verify(mUserAllContext, times(2)).registerReceiver(
176                 intentReceiverCaptor.capture(), any(), any(), any());
177         mUserPresentReceiver = userPresentReceiverCaptor.getValue();
178         mIntentReceiver = intentReceiverCaptor.getValue();
179 
180         // Add user to create vpn in mVpn
181         onUserStarted(SYSTEM_USER_ID);
182         assertNotNull(mService.mVpns.get(SYSTEM_USER_ID));
183     }
184 
185     @Test
testUpdateAppExclusionList()186     public void testUpdateAppExclusionList() {
187         // Start vpn
188         mService.startVpnProfile(TEST_VPN_PKG);
189         verify(mVpn).startVpnProfile(eq(TEST_VPN_PKG));
190 
191         // Remove package due to package replaced.
192         onPackageRemoved(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
193         verify(mVpn, never()).refreshPlatformVpnAppExclusionList();
194 
195         // Add package due to package replaced.
196         onPackageAdded(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
197         verify(mVpn, never()).refreshPlatformVpnAppExclusionList();
198 
199         // Remove package
200         onPackageRemoved(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
201         verify(mVpn).refreshPlatformVpnAppExclusionList();
202 
203         // Add the package back
204         onPackageAdded(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
205         verify(mVpn, times(2)).refreshPlatformVpnAppExclusionList();
206     }
207 
208     @Test
testStartVpnProfileFromDiffPackage()209     public void testStartVpnProfileFromDiffPackage() {
210         assertThrows(
211                 SecurityException.class, () -> mService.startVpnProfile(mNotMyVpnPkg));
212     }
213 
214     @Test
testStopVpnProfileFromDiffPackage()215     public void testStopVpnProfileFromDiffPackage() {
216         assertThrows(SecurityException.class, () -> mService.stopVpnProfile(mNotMyVpnPkg));
217     }
218 
219     @Test
testGetProvisionedVpnProfileStateFromDiffPackage()220     public void testGetProvisionedVpnProfileStateFromDiffPackage() {
221         assertThrows(SecurityException.class, () ->
222                 mService.getProvisionedVpnProfileState(mNotMyVpnPkg));
223     }
224 
225     @Test
testGetProvisionedVpnProfileState()226     public void testGetProvisionedVpnProfileState() {
227         mService.getProvisionedVpnProfileState(TEST_VPN_PKG);
228         verify(mVpn).getProvisionedVpnProfileState(TEST_VPN_PKG);
229     }
230 
buildIntent(String action, String packageName, int userId, int uid, boolean isReplacing)231     private Intent buildIntent(String action, String packageName, int userId, int uid,
232             boolean isReplacing) {
233         final Intent intent = new Intent(action);
234         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
235         intent.putExtra(Intent.EXTRA_UID, uid);
236         intent.putExtra(Intent.EXTRA_REPLACING, isReplacing);
237         if (packageName != null) {
238             intent.setData(Uri.fromParts("package" /* scheme */, packageName, null /* fragment */));
239         }
240 
241         return intent;
242     }
243 
sendIntent(Intent intent)244     private void sendIntent(Intent intent) {
245         sendIntent(mIntentReceiver, mContext, intent);
246     }
247 
sendIntent(BroadcastReceiver receiver, Context context, Intent intent)248     private void sendIntent(BroadcastReceiver receiver, Context context, Intent intent) {
249         final Handler h = mHandlerThread.getThreadHandler();
250 
251         // Send in handler thread.
252         h.post(() -> receiver.onReceive(context, intent));
253         HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS);
254     }
255 
onUserStarted(int userId)256     private void onUserStarted(int userId) {
257         sendIntent(buildIntent(Intent.ACTION_USER_STARTED,
258                 null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
259     }
260 
onUserUnlocked(int userId)261     private void onUserUnlocked(int userId) {
262         sendIntent(buildIntent(Intent.ACTION_USER_UNLOCKED,
263                 null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
264     }
265 
onUserStopped(int userId)266     private void onUserStopped(int userId) {
267         sendIntent(buildIntent(Intent.ACTION_USER_STOPPED,
268                 null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
269     }
270 
onLockDownReset()271     private void onLockDownReset() {
272         sendIntent(buildIntent(LockdownVpnTracker.ACTION_LOCKDOWN_RESET, null /* packageName */,
273                 UserHandle.USER_SYSTEM, -1 /* uid */, false /* isReplacing */));
274     }
275 
onPackageAdded(String packageName, int userId, int uid, boolean isReplacing)276     private void onPackageAdded(String packageName, int userId, int uid, boolean isReplacing) {
277         sendIntent(buildIntent(Intent.ACTION_PACKAGE_ADDED, packageName, userId, uid, isReplacing));
278     }
279 
onPackageAdded(String packageName, int uid, boolean isReplacing)280     private void onPackageAdded(String packageName, int uid, boolean isReplacing) {
281         onPackageAdded(packageName, UserHandle.USER_SYSTEM, uid, isReplacing);
282     }
283 
onPackageRemoved(String packageName, int userId, int uid, boolean isReplacing)284     private void onPackageRemoved(String packageName, int userId, int uid, boolean isReplacing) {
285         sendIntent(buildIntent(Intent.ACTION_PACKAGE_REMOVED, packageName, userId, uid,
286                 isReplacing));
287     }
288 
onPackageRemoved(String packageName, int uid, boolean isReplacing)289     private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
290         onPackageRemoved(packageName, UserHandle.USER_SYSTEM, uid, isReplacing);
291     }
292 
293     @Test
testReceiveIntentFromNonHandlerThread()294     public void testReceiveIntentFromNonHandlerThread() {
295         assertThrows(IllegalStateException.class, () ->
296                 mIntentReceiver.onReceive(mContext, buildIntent(Intent.ACTION_PACKAGE_REMOVED,
297                         PKGS[0], UserHandle.USER_SYSTEM, PKG_UIDS[0], true /* isReplacing */)));
298 
299         assertThrows(IllegalStateException.class, () ->
300                 mUserPresentReceiver.onReceive(mContext, new Intent(Intent.ACTION_USER_PRESENT)));
301     }
302 
setupLockdownVpn(String packageName)303     private void setupLockdownVpn(String packageName) {
304         final byte[] profileTag = packageName.getBytes(StandardCharsets.UTF_8);
305         doReturn(profileTag).when(mVpnProfileStore).get(Credentials.LOCKDOWN_VPN);
306     }
307 
setupVpnProfile(String profileName)308     private void setupVpnProfile(String profileName) {
309         final VpnProfile profile = new VpnProfile(profileName);
310         profile.name = profileName;
311         profile.server = "192.0.2.1";
312         profile.dnsServers = "8.8.8.8";
313         profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
314         final byte[] encodedProfile = profile.encode();
315         doReturn(encodedProfile).when(mVpnProfileStore).get(Credentials.VPN + profileName);
316     }
317 
318     @Test
testUserPresent()319     public void testUserPresent() {
320         // Verify that LockDownVpnTracker is not created.
321         verify(mLockdownVpnTracker, never()).init();
322 
323         setupLockdownVpn(TEST_VPN_PKG);
324         setupVpnProfile(TEST_VPN_PKG);
325 
326         // mUserPresentReceiver only registers ACTION_USER_PRESENT intent and does no verification
327         // on action, so an empty intent is enough.
328         sendIntent(mUserPresentReceiver, mSystemContext, new Intent());
329 
330         verify(mLockdownVpnTracker).init();
331         verify(mSystemContext).unregisterReceiver(mUserPresentReceiver);
332         verify(mUserAllContext, never()).unregisterReceiver(any());
333     }
334 
335     @Test
testUpdateLockdownVpn()336     public void testUpdateLockdownVpn() {
337         setupLockdownVpn(TEST_VPN_PKG);
338         onUserUnlocked(SYSTEM_USER_ID);
339 
340         // Will not create lockDownVpnTracker w/o valid profile configured in the keystore
341         verify(mLockdownVpnTracker, never()).init();
342 
343         setupVpnProfile(TEST_VPN_PKG);
344 
345         // Remove the user from mVpns
346         onUserStopped(SYSTEM_USER_ID);
347         onUserUnlocked(SYSTEM_USER_ID);
348         verify(mLockdownVpnTracker, never()).init();
349 
350         // Add user back
351         onUserStarted(SYSTEM_USER_ID);
352         verify(mLockdownVpnTracker).init();
353 
354         // Trigger another update. The existing LockDownVpnTracker should be shut down and
355         // initialize another one.
356         onUserUnlocked(SYSTEM_USER_ID);
357         verify(mLockdownVpnTracker).shutdown();
358         verify(mLockdownVpnTracker, times(2)).init();
359     }
360 
361     @Test
testLockdownReset()362     public void testLockdownReset() {
363         // Init LockdownVpnTracker
364         setupLockdownVpn(TEST_VPN_PKG);
365         setupVpnProfile(TEST_VPN_PKG);
366         onUserUnlocked(SYSTEM_USER_ID);
367         verify(mLockdownVpnTracker).init();
368 
369         onLockDownReset();
370         verify(mLockdownVpnTracker).reset();
371     }
372 
373     @Test
testLockdownResetWhenLockdownVpnTrackerIsNotInit()374     public void testLockdownResetWhenLockdownVpnTrackerIsNotInit() {
375         setupLockdownVpn(TEST_VPN_PKG);
376         setupVpnProfile(TEST_VPN_PKG);
377 
378         onLockDownReset();
379 
380         // LockDownVpnTracker is not created. Lockdown reset will not take effect.
381         verify(mLockdownVpnTracker, never()).reset();
382     }
383 
384     @Test
testIsVpnLockdownEnabled()385     public void testIsVpnLockdownEnabled() {
386         // Vpn is created but the VPN lockdown is not enabled.
387         assertFalse(mService.isVpnLockdownEnabled(SYSTEM_USER_ID));
388 
389         // Set lockdown for the SYSTEM_USER_ID VPN.
390         doReturn(true).when(mVpn).getLockdown();
391         assertTrue(mService.isVpnLockdownEnabled(SYSTEM_USER_ID));
392 
393         // Even lockdown is enabled but no Vpn is created for SECONDARY_USER.
394         assertFalse(mService.isVpnLockdownEnabled(SECONDARY_USER.id));
395     }
396 
397     @Test
testGetVpnLockdownAllowlist()398     public void testGetVpnLockdownAllowlist() {
399         doReturn(null).when(mVpn).getLockdownAllowlist();
400         assertNull(mService.getVpnLockdownAllowlist(SYSTEM_USER_ID));
401 
402         final List<String> expected = List.of(PKGS);
403         doReturn(expected).when(mVpn).getLockdownAllowlist();
404         assertEquals(expected, mService.getVpnLockdownAllowlist(SYSTEM_USER_ID));
405 
406         // Even lockdown is enabled but no Vpn is created for SECONDARY_USER.
407         assertNull(mService.getVpnLockdownAllowlist(SECONDARY_USER.id));
408     }
409 }
410