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

Commit 3e36d7cb authored by Alex Johnston's avatar Alex Johnston Committed by Android (Google) Code Review
Browse files

Merge "Add credential management app access control DPM"

parents 238be814 dfdbd2e9
Loading
Loading
Loading
Loading
+106 −30
Original line number Diff line number Diff line
@@ -5240,9 +5240,22 @@ public class DevicePolicyManager {
    }
    /**
     * Called by a device or profile owner, or delegated certificate installer, to install a
     * certificate and corresponding private key. All apps within the profile will be able to access
     * the certificate and use the private key, given direct user approval.
     * This API can be called by the following to install a certificate and corresponding
     * private key:
     * <ul>
     *    <li>Device owner</li>
     *    <li>Profile owner</li>
     *    <li>Delegated certificate installer</li>
     *    <li>Credential management app</li>
     * </ul>
     * All apps within the profile will be able to access the certificate and use the private key,
     * given direct user approval.
     *
     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
     * can call this API. However, this API sets the key pair as user selectable by default,
     * which is not permitted when called by the credential management app. Instead,
     * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)} should be
     * called with {@link #INSTALLKEY_SET_USER_SELECTABLE} not set as a flag.
     *
     * <p>Access to the installed credentials will not be granted to the caller of this API without
     * direct user approval. This is for security - should a certificate installer become
@@ -5273,10 +5286,23 @@ public class DevicePolicyManager {
    }
    /**
     * Called by a device or profile owner, or delegated certificate installer, to install a
     * certificate chain and corresponding private key for the leaf certificate. All apps within the
     * profile will be able to access the certificate chain and use the private key, given direct
     * user approval.
     * This API can be called by the following to install a certificate chain and corresponding
     * private key for the leaf certificate:
     * <ul>
     *    <li>Device owner</li>
     *    <li>Profile owner</li>
     *    <li>Delegated certificate installer</li>
     *    <li>Credential management app</li>
     * </ul>
     * All apps within the profile will be able to access the certificate chain and use the private
     * key, given direct user approval.
     *
     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
     * can call this API. However, this API sets the key pair as user selectable by default,
     * which is not permitted when called by the credential management app. Instead,
     * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)} should be
     * called with {@link #INSTALLKEY_SET_USER_SELECTABLE} not set as a flag.
     * Note, there can only be a credential management app on an unmanaged device.
     *
     * <p>The caller of this API may grant itself access to the certificate and private key
     * immediately, without user approval. It is a best practice not to request this unless strictly
@@ -5314,10 +5340,26 @@ public class DevicePolicyManager {
    }
    /**
     * Called by a device or profile owner, or delegated certificate installer, to install a
     * certificate chain and corresponding private key for the leaf certificate. All apps within the
     * profile will be able to access the certificate chain and use the private key, given direct
     * user approval (if the user is allowed to select the private key).
     * This API can be called by the following to install a certificate chain and corresponding
     * private key for the leaf certificate:
     * <ul>
     *    <li>Device owner</li>
     *    <li>Profile owner</li>
     *    <li>Delegated certificate installer</li>
     *    <li>Credential management app</li>
     * </ul>
     * All apps within the profile will be able to access the certificate chain and use the
     * private key, given direct user approval (if the user is allowed to select the private key).
     *
     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
     * can call this API. If called by the credential management app:
     * <ul>
     *    <li>The componentName must be {@code null}r</li>
     *    <li>The alias must exist in the credential management app's
     *    {@link android.security.AppUriAuthenticationPolicy}</li>
     *    <li>The key pair must not be user selectable</li>
     * </ul>
     * Note, there can only be a credential management app on an unmanaged device.
     *
     * <p>The caller of this API may grant itself access to the certificate and private key
     * immediately, without user approval. It is a best practice not to request this unless strictly
@@ -5343,7 +5385,8 @@ public class DevicePolicyManager {
     *        {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}.
     * @return {@code true} if the keys were installed, {@code false} otherwise.
     * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
     *         owner.
     *         owner, or {@code admin} is null but the calling application is not a delegated
     *         certificate installer or credential management app.
     * @see android.security.KeyChain#getCertificateChain
     * @see #setDelegatedScopes
     * @see #DELEGATION_CERT_INSTALL
@@ -5376,15 +5419,26 @@ public class DevicePolicyManager {
    }
    /**
     * Called by a device or profile owner, or delegated certificate installer, to remove a
     * certificate and private key pair installed under a given alias.
     * This API can be called by the following to remove a certificate and private key pair
     * installed under a given alias:
     * <ul>
     *    <li>Device owner</li>
     *    <li>Profile owner</li>
     *    <li>Delegated certificate installer</li>
     *    <li>Credential management app</li>
     * </ul>
     *
     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
     * can call this API. If called by the credential management app, the componentName must be
     * {@code null}. Note, there can only be a credential management app on an unmanaged device.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
     *        {@code null} if calling from a delegated certificate installer.
     * @param alias The private key alias under which the certificate is installed.
     * @return {@code true} if the private key alias no longer exists, {@code false} otherwise.
     * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
     *         owner.
     *         owner, or {@code admin} is null but the calling application is not a delegated
     *         certificate installer or credential management app.
     * @see #setDelegatedScopes
     * @see #DELEGATION_CERT_INSTALL
     */
@@ -5419,10 +5473,20 @@ public class DevicePolicyManager {
    }
    /**
     * Called by a device or profile owner, or delegated certificate installer, to generate a
     * new private/public key pair. If the device supports key generation via secure hardware,
     * this method is useful for creating a key in KeyChain that never left the secure hardware.
     * Access to the key is controlled the same way as in {@link #installKeyPair}.
     * This API can be called by the following to generate a new private/public key pair:
     * <ul>
     *    <li>Device owner</li>
     *    <li>Profile owner</li>
     *    <li>Delegated certificate installer</li>
     *    <li>Credential management app</li>
     * </ul>
     * If the device supports key generation via secure hardware, this method is useful for
     * creating a key in KeyChain that never left the secure hardware. Access to the key is
     * controlled the same way as in {@link #installKeyPair}.
     *
     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
     * can call this API. If called by the credential management app, the componentName must be
     * {@code null}. Note, there can only be a credential management app on an unmanaged device.
     *
     * <p>Because this method might take several seconds to complete, it should only be called from
     * a worker thread. This method returns {@code null} when called from the main thread.
@@ -5445,9 +5509,10 @@ public class DevicePolicyManager {
     * supports these features, refer to {@link #isDeviceIdAttestationSupported()} and
     * {@link #isUniqueDeviceAttestationSupported()}.
     *
     * <p>Device owner, profile owner and their delegated certificate installer can use
     * {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device information
     * including manufacturer, model, brand, device and product in the attestation record.
     * <p>Device owner, profile owner, their delegated certificate installer and the credential
     * management app can use {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device
     * information including manufacturer, model, brand, device and product in the attestation
     * record.
     * Only device owner, profile owner on an organization-owned device and their delegated
     * certificate installers can use {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and
     * {@link #ID_TYPE_MEID} to request unique device identifiers to be attested (the serial number,
@@ -5482,9 +5547,11 @@ public class DevicePolicyManager {
     *        {@code keySpec}.
     * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise.
     * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
     *         owner. If Device ID attestation is requested (using {@link #ID_TYPE_SERIAL},
     *         {@link #ID_TYPE_IMEI} or {@link #ID_TYPE_MEID}), the caller must be the Device Owner
     *         or the Certificate Installer delegate.
     *         owner, or {@code admin} is null but the calling application is not a delegated
     *         certificate installer or credential management app. If Device ID attestation is
     *         requested (using {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} or
     *         {@link #ID_TYPE_MEID}), the caller must be the Device Owner or the Certificate
     *         Installer delegate.
     * @throws IllegalArgumentException in the following cases:
     *         <p>
     *         <ul>
@@ -5647,10 +5714,19 @@ public class DevicePolicyManager {
    }
    /**
     * Called by a device or profile owner, or delegated certificate installer, to associate
     * certificates with a key pair that was generated using {@link #generateKeyPair}, and
     * set whether the key is available for the user to choose in the certificate selection
     * prompt.
     * This API can be called by the following to associate certificates with a key pair that was
     * generated using {@link #generateKeyPair}, and set whether the key is available for the user
     * to choose in the certificate selection prompt:
     * <ul>
     *    <li>Device owner</li>
     *    <li>Profile owner</li>
     *    <li>Delegated certificate installer</li>
     *    <li>Credential management app</li>
     * </ul>
     *
     * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app
     * can call this API. If called by the credential management app, the componentName must be
     * {@code null}. Note, there can only be a credential management app on an unmanaged device.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
     *            {@code null} if calling from a delegated certificate installer.
@@ -5668,7 +5744,7 @@ public class DevicePolicyManager {
     *        successfully associated with it, {@code false} otherwise.
     * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
     *         owner, or {@code admin} is null but the calling application is not a delegated
     *         certificate installer.
     *         certificate installer or credential management app.
     */
    public boolean setKeyPairCertificate(@Nullable ComponentName admin,
            @NonNull String alias, @NonNull List<Certificate> certs, boolean isUserSelectable) {
+77 −4
Original line number Diff line number Diff line
@@ -233,6 +233,7 @@ import android.provider.ContactsInternal;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Telephony;
import android.security.AppUriAuthenticationPolicy;
import android.security.IKeyChainAliasCallback;
import android.security.IKeyChainService;
import android.security.KeyChain;
@@ -1154,6 +1155,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            return LocalServices.getService(PersistentDataBlockManagerInternal.class);
        }
        AppOpsManager getAppOpsManager() {
            return mContext.getSystemService(AppOpsManager.class);
        }
        LockSettingsInternal getLockSettingsInternal() {
            return LocalServices.getService(LockSettingsInternal.class);
        }
@@ -5031,7 +5036,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        final CallerIdentity caller = getCallerIdentity(who, callerPackage);
        Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                && (isProfileOwner(caller) || isDeviceOwner(caller)))
                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
                || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
                || isCredentialManagementApp(caller, alias, isUserSelectable))));
        final long id = mInjector.binderClearCallingIdentity();
        try {
@@ -5071,7 +5077,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        final CallerIdentity caller = getCallerIdentity(who, callerPackage);
        Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                && (isProfileOwner(caller) || isDeviceOwner(caller)))
                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
                || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
                || isCredentialManagementApp(caller, alias))));
        final long id = Binder.clearCallingIdentity();
        try {
@@ -5275,7 +5282,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        } else {
            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                    && (isProfileOwner(caller) || isDeviceOwner(caller)))
                    || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
                    || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
                    || isCredentialManagementApp(caller, alias))));
        }
        // As the caller will be granted access to the key, ensure no UID was specified, as
@@ -5371,7 +5379,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        final CallerIdentity caller = getCallerIdentity(who, callerPackage);
        Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                && (isProfileOwner(caller) || isDeviceOwner(caller)))
                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
                || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL)
                || isCredentialManagementApp(caller, alias))));
        final long id = mInjector.binderClearCallingIdentity();
        try (final KeyChainConnection keyChainConnection =
@@ -5806,6 +5815,70 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        }
    }
    /**
     * Check whether a caller application is the credential management app, which can access
     * privileged APIs.
     * <p>
     * This is done by checking that the calling package is authorized to perform the app operation
     * {@link android.app.AppOpsManager#OP_MANAGE_CREDENTIALS}. The alias provided must be contained
     * in the aliases specified in the credential management app's authentication policy. The
     * key pair to install must not be user selectable.
     *
     * @param caller the calling identity
     * @return {@code true} if the calling process is the credential management app.
     */
    private boolean isCredentialManagementApp(CallerIdentity caller, String alias,
            boolean isUserSelectable) {
        // Should not be user selectable
        if (isUserSelectable) {
            Log.e(LOG_TAG, "The credential management app is not allowed to install a "
                    + "user selectable key pair");
            return false;
        }
        return isCredentialManagementApp(caller, alias);
    }
    /**
     * Check whether a caller application is the credential mangement app, which can access
     * privileged APIs.
     * <p>
     * This is done by checking that the calling package is authorized to perform the app operation
     * {@link android.app.AppOpsManager#OP_MANAGE_CREDENTIALS}. The alias provided must be contained
     * in the aliases specified in the credential management app's authentication policy.
     *
     * @param caller the calling identity
     * @return {@code true} if the calling process is the credential management app.
     */
    private boolean isCredentialManagementApp(CallerIdentity caller, String alias) {
        // Should include alias in authentication policy
        try (KeyChainConnection connection = KeyChain.bindAsUser(mContext,
                caller.getUserHandle())) {
            if (!containsAlias(connection.getService().getCredentialManagementAppPolicy(), alias)) {
                return false;
            }
        } catch (RemoteException | InterruptedException e) {
            return false;
        }
        AppOpsManager appOpsManager = mInjector.getAppOpsManager();
        return appOpsManager != null
                ? appOpsManager.noteOpNoThrow(AppOpsManager.OP_MANAGE_CREDENTIALS, caller.getUid(),
                caller.getPackageName(), null, null) == AppOpsManager.MODE_ALLOWED
                : false;
    }
    private static boolean containsAlias(AppUriAuthenticationPolicy policy, String alias) {
        for (Map.Entry<String, Map<Uri, String>> appsToUris :
                policy.getAppAndUriMappings().entrySet()) {
            for (Map.Entry<Uri, String> urisToAliases : appsToUris.getValue().entrySet()) {
                if (urisToAliases.getValue().equals(alias)) {
                    return true;
                }
            }
        }
        return false;
    }
    @Override
    public void setCertInstallerPackage(ComponentName who, String installerPackage)
            throws SecurityException {