Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83856fca authored by Janis Danisevskis's avatar Janis Danisevskis Committed by Automerger Merge Worker
Browse files

Merge "Keystore 2.0: Make VPN Keystore 2.0 ready." into sc-dev am: f778d605

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13714578

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I5fe8043031846defc92febd0a6a40a209b571ffe
parents 3da629f9 f778d605
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -584,6 +584,7 @@ java_library {
        "android.security.apc-java",
        "android.security.authorization-java",
        "android.security.usermanager-java",
        "android.security.vpnprofilestore-java",
        "android.system.keystore2-V1-java",
        "android.system.suspend.control.internal-java",
        "cameraprotosnano",
+19 −23
Original line number Diff line number Diff line
@@ -24,10 +24,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.content.pm.PackageManager;
import android.os.Process;
import android.security.Credentials;
import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreProvider;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnProfile;
@@ -35,7 +32,9 @@ import com.android.internal.net.VpnProfile;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
@@ -66,6 +65,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
    /** Prefix for when a Private Key is stored directly in the profile @hide */
    public static final String PREFIX_INLINE = "INLINE:";

    private static final String ANDROID_KEYSTORE_PROVIDER = "AndroidKeyStore";
    private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
    private static final String EMPTY_CERT = "";

@@ -430,32 +430,31 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
        return profile;
    }

    /**
     * Constructs a Ikev2VpnProfile from an internal-use VpnProfile instance.
     *
     * <p>Redundant authentication information (not related to profile type) will be discarded.
     *
     * @hide
     */
    @NonNull
    public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
            throws IOException, GeneralSecurityException {
        return fromVpnProfile(profile, null);
    private static PrivateKey getPrivateKeyFromAndroidKeystore(String alias) {
        try {
            final KeyStore keystore = KeyStore.getInstance(ANDROID_KEYSTORE_PROVIDER);
            keystore.load(null);
            final Key key = keystore.getKey(alias, null);
            if (!(key instanceof PrivateKey)) {
                throw new IllegalStateException(
                        "Unexpected key type returned from android keystore.");
            }
            return (PrivateKey) key;
        } catch (Exception e) {
            throw new IllegalStateException("Failed to load key from android keystore.", e);
        }
    }

    /**
     * Builds the Ikev2VpnProfile from the given profile.
     *
     * @param profile the source VpnProfile to build from
     * @param keyStore the Android Keystore instance to use to retrieve the private key, or null if
     *     the private key is PEM-encoded into the profile.
     * @return The IKEv2/IPsec VPN profile
     * @hide
     */
    @NonNull
    public static Ikev2VpnProfile fromVpnProfile(
            @NonNull VpnProfile profile, @Nullable KeyStore keyStore)
            throws IOException, GeneralSecurityException {
    public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
            throws GeneralSecurityException {
        final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
        builder.setProxy(profile.proxy);
        builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
@@ -479,12 +478,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
            case TYPE_IKEV2_IPSEC_RSA:
                final PrivateKey key;
                if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
                    Objects.requireNonNull(keyStore, "Missing Keystore for aliased PrivateKey");

                    final String alias =
                            profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
                    key = AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
                            keyStore, alias, Process.myUid());
                    key = getPrivateKeyFromAndroidKeystore(alias);
                } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
                    key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
                } else {
+142 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.security;

import android.annotation.NonNull;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.vpnprofilestore.IVpnProfileStore;
import android.util.Log;

/**
 * @hide This class allows legacy VPN access to its profiles that were stored in Keystore.
 * The storage of unstructured blobs in Android Keystore is going away, because there is no
 * architectural or security benefit of storing profiles in keystore over storing them
 * in the file system. This class allows access to the blobs that still exist in keystore.
 * And it stores new blob in a database that is still owned by Android Keystore.
 */
public class LegacyVpnProfileStore {
    private static final String TAG = "LegacyVpnProfileStore";

    public static final int SYSTEM_ERROR = IVpnProfileStore.ERROR_SYSTEM_ERROR;
    public static final int PROFILE_NOT_FOUND = IVpnProfileStore.ERROR_PROFILE_NOT_FOUND;

    private static final String VPN_PROFILE_STORE_SERVICE_NAME = "android.security.vpnprofilestore";

    private static IVpnProfileStore getService() {
        return IVpnProfileStore.Stub.asInterface(
                    ServiceManager.checkService(VPN_PROFILE_STORE_SERVICE_NAME));
    }

    /**
     * Stores the profile under the alias in the profile database. Existing profiles by the
     * same name will be replaced.
     * @param alias The name of the profile
     * @param profile The profile.
     * @return true if the profile was successfully added. False otherwise.
     * @hide
     */
    public static boolean put(@NonNull String alias, @NonNull byte[] profile) {
        try {
            if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
                getService().put(alias, profile);
                return true;
            } else {
                return KeyStore.getInstance().put(
                        alias, profile, KeyStore.UID_SELF, 0);
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to put vpn profile.", e);
            return false;
        }
    }

    /**
     * Retrieves a profile by the name alias from the profile database.
     * @param alias Name of the profile to retrieve.
     * @return The unstructured blob, that is the profile that was stored using
     *         LegacyVpnProfileStore#put or with
     *         android.security.Keystore.put(Credentials.VPN + alias).
     *         Returns null if no profile was found.
     * @hide
     */
    public static byte[] get(@NonNull String alias) {
        try {
            if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
                return getService().get(alias);
            } else {
                return KeyStore.getInstance().get(alias, true /* suppressKeyNotFoundWarning */);
            }
        } catch (ServiceSpecificException e) {
            if (e.errorCode != PROFILE_NOT_FOUND) {
                Log.e(TAG, "Failed to get vpn profile.", e);
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to get vpn profile.", e);
        }
        return null;
    }

    /**
     * Removes a profile by the name alias from the profile database.
     * @param alias Name of the profile to be removed.
     * @return True if a profile was removed. False if no such profile was found.
     * @hide
     */
    public static boolean remove(@NonNull String alias) {
        try {
            if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
                getService().remove(alias);
                return true;
            } else {
                return KeyStore.getInstance().delete(alias);
            }
        } catch (ServiceSpecificException e) {
            if (e.errorCode != PROFILE_NOT_FOUND) {
                Log.e(TAG, "Failed to remove vpn profile.", e);
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to remove vpn profile.", e);
        }
        return false;
    }

    /**
     * Lists the vpn profiles stored in the database.
     * @return An array of strings representing the aliases stored in the profile database.
     *         The return value may be empty but never null.
     * @hide
     */
    public static @NonNull String[] list(@NonNull String prefix) {
        try {
            if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
                final String[] aliases = getService().list(prefix);
                for (int i = 0; i < aliases.length; ++i) {
                    aliases[i] = aliases[i].substring(prefix.length());
                }
                return aliases;
            } else {
                final String[] result = KeyStore.getInstance().list(prefix);
                return result != null ? result : new String[0];
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to list vpn profiles.", e);
        }
        return new String[0];
    }
}
+23 −22
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -60,6 +59,7 @@ import com.android.internal.net.VpnProfile;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.VpnProfileStore;
import com.android.server.net.LockdownVpnTracker;

import java.io.FileDescriptor;
@@ -83,7 +83,7 @@ public class VpnManagerService extends IVpnManager.Stub {
    private final Dependencies mDeps;

    private final ConnectivityManager mCm;
    private final KeyStore mKeyStore;
    private final VpnProfileStore mVpnProfileStore;
    private final INetworkManagementService mNMS;
    private final INetd mNetd;
    private final UserManager mUserManager;
@@ -114,9 +114,9 @@ public class VpnManagerService extends IVpnManager.Stub {
            return new HandlerThread("VpnManagerService");
        }

        /** Returns the KeyStore instance to be used by this class. */
        public KeyStore getKeyStore() {
            return KeyStore.getInstance();
        /** Return the VpnProfileStore to be used by this class */
        public VpnProfileStore getVpnProfileStore() {
            return new VpnProfileStore();
        }

        public INetd getNetd() {
@@ -135,7 +135,7 @@ public class VpnManagerService extends IVpnManager.Stub {
        mHandlerThread = mDeps.makeHandlerThread();
        mHandlerThread.start();
        mHandler = mHandlerThread.getThreadHandler();
        mKeyStore = mDeps.getKeyStore();
        mVpnProfileStore = mDeps.getVpnProfileStore();
        mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
        mCm = mContext.getSystemService(ConnectivityManager.class);
        mNMS = mDeps.getINetworkManagementService();
@@ -289,7 +289,7 @@ public class VpnManagerService extends IVpnManager.Stub {
    public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
        final int user = UserHandle.getUserId(mDeps.getCallingUid());
        synchronized (mVpns) {
            return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
            return mVpns.get(user).provisionVpnProfile(packageName, profile);
        }
    }

@@ -307,7 +307,7 @@ public class VpnManagerService extends IVpnManager.Stub {
    public void deleteVpnProfile(@NonNull String packageName) {
        final int user = UserHandle.getUserId(mDeps.getCallingUid());
        synchronized (mVpns) {
            mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
            mVpns.get(user).deleteVpnProfile(packageName);
        }
    }

@@ -325,7 +325,7 @@ public class VpnManagerService extends IVpnManager.Stub {
        final int user = UserHandle.getUserId(mDeps.getCallingUid());
        synchronized (mVpns) {
            throwIfLockdownEnabled();
            mVpns.get(user).startVpnProfile(packageName, mKeyStore);
            mVpns.get(user).startVpnProfile(packageName);
        }
    }

@@ -358,7 +358,7 @@ public class VpnManagerService extends IVpnManager.Stub {
        }
        synchronized (mVpns) {
            throwIfLockdownEnabled();
            mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
            mVpns.get(user).startLegacyVpn(profile, null /* underlying */, egress);
        }
    }

@@ -396,7 +396,7 @@ public class VpnManagerService extends IVpnManager.Stub {
    }

    private boolean isLockdownVpnEnabled() {
        return mKeyStore.contains(Credentials.LOCKDOWN_VPN);
        return mVpnProfileStore.get(Credentials.LOCKDOWN_VPN) != null;
    }

    @Override
@@ -417,14 +417,14 @@ public class VpnManagerService extends IVpnManager.Stub {
                return true;
            }

            byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
            byte[] profileTag = mVpnProfileStore.get(Credentials.LOCKDOWN_VPN);
            if (profileTag == null) {
                loge("Lockdown VPN configured but cannot be read from keystore");
                return false;
            }
            String profileName = new String(profileTag);
            final VpnProfile profile = VpnProfile.decode(
                    profileName, mKeyStore.get(Credentials.VPN + profileName));
                    profileName, mVpnProfileStore.get(Credentials.VPN + profileName));
            if (profile == null) {
                loge("Lockdown VPN configured invalid profile " + profileName);
                setLockdownTracker(null);
@@ -437,7 +437,7 @@ public class VpnManagerService extends IVpnManager.Stub {
                return false;
            }
            setLockdownTracker(
                    new LockdownVpnTracker(mContext, mHandler, mKeyStore, vpn,  profile));
                    new LockdownVpnTracker(mContext, mHandler, vpn,  profile));
        }

        return true;
@@ -495,7 +495,7 @@ public class VpnManagerService extends IVpnManager.Stub {
                return false;
            }

            return vpn.startAlwaysOnVpn(mKeyStore);
            return vpn.startAlwaysOnVpn();
        }
    }

@@ -510,7 +510,7 @@ public class VpnManagerService extends IVpnManager.Stub {
                logw("User " + userId + " has no Vpn configuration");
                return false;
            }
            return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
            return vpn.isAlwaysOnPackageSupported(packageName);
        }
    }

@@ -531,11 +531,11 @@ public class VpnManagerService extends IVpnManager.Stub {
                logw("User " + userId + " has no Vpn configuration");
                return false;
            }
            if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownAllowlist, mKeyStore)) {
            if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownAllowlist)) {
                return false;
            }
            if (!startAlwaysOnVpn(userId)) {
                vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
                vpn.setAlwaysOnPackage(null, false, null);
                return false;
            }
        }
@@ -705,7 +705,8 @@ public class VpnManagerService extends IVpnManager.Stub {
                loge("Starting user already has a VPN");
                return;
            }
            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId,
                    new VpnProfileStore());
            mVpns.put(userId, userVpn);
            if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
                updateLockdownVpn();
@@ -777,7 +778,7 @@ public class VpnManagerService extends IVpnManager.Stub {
            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
                log("Restarting always-on VPN package " + packageName + " for user "
                        + userId);
                vpn.startAlwaysOnVpn(mKeyStore);
                vpn.startAlwaysOnVpn();
            }
        }
    }
@@ -798,7 +799,7 @@ public class VpnManagerService extends IVpnManager.Stub {
            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
                log("Removing always-on VPN package " + packageName + " for user "
                        + userId);
                vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
                vpn.setAlwaysOnPackage(null, false, null);
            }
        }
    }
@@ -843,7 +844,7 @@ public class VpnManagerService extends IVpnManager.Stub {
            if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
                final long ident = Binder.clearCallingIdentity();
                try {
                    mKeyStore.delete(Credentials.LOCKDOWN_VPN);
                    mVpnProfileStore.remove(Credentials.LOCKDOWN_VPN);
                    mLockdownEnabled = false;
                    setLockdownTracker(null);
                } finally {
+142 −73

File changed.

Preview size limit exceeded, changes collapsed.

Loading