diff options
author | rajesh.nyamagoud <nyamagoud@google.com> | 2021-11-23 06:00:31 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-11-23 06:00:31 +0000 |
commit | e0808a31dc8239d74f0ae0e66a2ff0ca1422ed85 (patch) | |
tree | 557723747884587579a8dc47165b90811a4cabfd | |
parent | cbaf5356e26b3949f1b9b297e3390b0f7ddd29a2 (diff) | |
parent | 6085656b8d3676ab0234b5ce07b75f3b045d57f2 (diff) | |
download | SecureElement-e0808a31dc8239d74f0ae0e66a2ff0ca1422ed85.tar.gz |
UUID based access rules support for SE am: 10b0a90cc7 am: 22aa6a4fc9 am: a25fa592a6 am: 6085656b8d
Original change: https://android-review.googlesource.com/c/platform/packages/apps/SecureElement/+/1769148
Change-Id: I8ca7b33fac9c9ed50823c4e3bc46f5b7bad8b55a
-rw-r--r-- | etc/hal_uuid_map_config.xml | 41 | ||||
-rw-r--r-- | src/com/android/se/SecureElementService.java | 47 | ||||
-rw-r--r-- | src/com/android/se/Terminal.java | 65 | ||||
-rw-r--r-- | src/com/android/se/security/AccessControlEnforcer.java | 21 | ||||
-rw-r--r-- | src/com/android/se/security/HalRefDoParser.java | 250 |
5 files changed, 395 insertions, 29 deletions
diff --git a/etc/hal_uuid_map_config.xml b/etc/hal_uuid_map_config.xml new file mode 100644 index 0000000..5388697 --- /dev/null +++ b/etc/hal_uuid_map_config.xml @@ -0,0 +1,41 @@ +<!-- Vendor mapping file --> +<!-- Sample UUID to list of UIDs mapping file --> + +<!-- UUID: Universally Unique IDentifier --> +<!-- 16 Byte UUID need to be generated by vendors to add new entry --> +<!-- As per global platform access control spec, UUID is expected to be of --> +<!-- length 20 bytes. While using this UUID, it is expected to be --> +<!-- automatically padded with ffffffff in initial 4 bytes of 20 Byte length --> + +<!-- UID: user identifier of the service --> + +<!-- This mapping file should contain an entry for VTS tests, since VTS --> +<!-- tests run as root, user identifier 0 should be mapped to its --> +<!-- corresponding UUID to allow VTS tests to access secure element --> +<!-- For VTS tests use UID: 0 and UUID: 9f36407ead0639fc966f14dde7970f68 --> + +<ref_do> + <!-- mapping entries to map unique identifiers to device hal services --> + <!-- uids --> + + <!-- UUID would be automatically padding with ffffffff to fulfill 20 --> + <!-- bytes in access rule. For example for --> + <!-- UUID:9f36407ead0639fc966f14dde7970f68 after padding it should look --> + <!-- like ffffffff9f36407ead0639fc966f14dde7970f68 --> + <uuid_ref_do> + <uids> + <uid>0</uid> + </uids> + <uuid>9f36407ead0639fc966f14dde7970f68</uuid> + </uuid_ref_do> + + <!-- Sample mapping entry with UIDs:1096 and 1097 mapped to --> + <!-- UUID:9f36407ead0639fc966f14dde7970f68 --> + <uuid_ref_do> + <uids> + <uid>1096</uid> + <uid>1097</uid> + </uids> + <uuid>a9b7ba70783b317e9998dc4dd82eb3c5</uuid> + </uuid_ref_do> +</ref_do> diff --git a/src/com/android/se/SecureElementService.java b/src/com/android/se/SecureElementService.java index 38899fa..c7187e1 100644 --- a/src/com/android/se/SecureElementService.java +++ b/src/com/android/se/SecureElementService.java @@ -45,6 +45,7 @@ import android.util.Log; import com.android.se.Terminal.SecureElementReader; import com.android.se.internal.ByteArrayConverter; +import com.android.se.security.HalRefDoParser; import java.io.FileDescriptor; import java.io.IOException; @@ -244,6 +245,16 @@ public final class SecureElementService extends Service { throw new AccessControlException("PackageName can not be determined"); } + private byte[] getUUIDFromCallingUid(int uid) { + byte[] uuid = HalRefDoParser.getInstance().findUUID(Binder.getCallingUid()); + + if (uuid != null) { + return uuid; + } + + return null; + } + final class SecureElementSession extends ISecureElementSession.Stub { private final SecureElementReader mReader; @@ -326,12 +337,26 @@ public final class SecureElementService extends Service { + String.format("%02x ", p2 & 0xFF)); } - String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + String packageName = null; + byte[] uuid = null; + try { + packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + } catch (AccessControlException e) { + // Since packageName not found for calling process, try to find UUID mapping + // provided by vendors for the calling process UID + // (vendor provide UUID mapping for native services to access secure element) + Log.d(mTag, "openBasicChannel() trying to find mapping uuid"); + uuid = getUUIDFromCallingUid(Binder.getCallingUid()); + if (uuid == null) { + Log.e(mTag, "openBasicChannel() uuid mapping for calling uid is not found"); + throw e; + } + } Channel channel = null; try { channel = mReader.getTerminal().openBasicChannel(this, aid, p2, listener, - packageName, Binder.getCallingPid()); + packageName, uuid, Binder.getCallingPid()); } catch (IOException e) { throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage()); } catch (NoSuchElementException e) { @@ -368,12 +393,26 @@ public final class SecureElementService extends Service { + String.format("%02x ", p2 & 0xFF)); } - String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + String packageName = null; + byte[] uuid = null; + try { + packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + } catch (AccessControlException e) { + // Since packageName not found for calling process, try to find UUID mapping + // provided by vendors for the calling process UID + // (vendor provide UUID mapping for native services to access secure element) + Log.d(mTag, "openLogicalChannel() trying to find mapping uuid"); + uuid = getUUIDFromCallingUid(Binder.getCallingUid()); + if (uuid == null) { + Log.e(mTag, "openLogicalChannel() uuid mapping for calling uid is not found"); + throw e; + } + } Channel channel = null; try { channel = mReader.getTerminal().openLogicalChannel(this, aid, p2, listener, - packageName, Binder.getCallingPid()); + packageName, uuid, Binder.getCallingPid()); } catch (IOException e) { throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage()); } catch (NoSuchElementException e) { diff --git a/src/com/android/se/Terminal.java b/src/com/android/se/Terminal.java index d97dd2f..1c055a0 100644 --- a/src/com/android/se/Terminal.java +++ b/src/com/android/se/Terminal.java @@ -32,6 +32,7 @@ import android.hardware.secure_element.V1_0.ISecureElement; import android.hardware.secure_element.V1_0.ISecureElementHalCallback; import android.hardware.secure_element.V1_0.LogicalChannelResponse; import android.hardware.secure_element.V1_0.SecureElementStatus; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.HwBinder; @@ -432,9 +433,11 @@ public class Terminal { /** * Opens a Basic Channel with the given AID and P2 paramters + * with the given device app reference package name or uuid */ public Channel openBasicChannel(SecureElementSession session, byte[] aid, byte p2, - ISecureElementListener listener, String packageName, int pid) throws IOException, + ISecureElementListener listener, String packageName, byte[] uuid, + int pid) throws IOException, NoSuchElementException { if (aid != null && aid.length == 0) { aid = null; @@ -446,19 +449,31 @@ public class Terminal { ChannelAccess channelAccess = null; if (packageName != null) { - Log.w(mTag, "Enable access control on basic channel for " + packageName); + Log.w(mTag, "Enable access control on basic channel for package name: " + + packageName); SecureElementStatsLog.write( SecureElementStatsLog.SE_OMAPI_REPORTED, SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, mName, packageName); - try { - // For application without privilege permission or carrier privilege, - // openBasicChannel with UICC terminals should be rejected. - channelAccess = setUpChannelAccess(aid, packageName, pid, true); - } catch (MissingResourceException e) { - return null; + } else if (uuid != null) { + Log.w(mTag, "Enable access control on basic channel for uid: " + + Binder.getCallingUid() + + " UUID: " + Arrays.toString(uuid)); + SecureElementStatsLog.write( + SecureElementStatsLog.SE_OMAPI_REPORTED, + SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, + mName, + Arrays.toString(uuid)); + } + try { + // For application without privilege permission or carrier privilege, + // openBasicChannel with UICC terminals should be rejected. + if (packageName != null || uuid != null) { + channelAccess = setUpChannelAccess(aid, packageName, uuid, pid, true); } + } catch (MissingResourceException e) { + return null; } synchronized (mLock) { @@ -516,14 +531,15 @@ public class Terminal { */ public Channel openLogicalChannelWithoutChannelAccess(byte[] aid) throws IOException, NoSuchElementException { - return openLogicalChannel(null, aid, (byte) 0x00, null, null, 0); + return openLogicalChannel(null, aid, (byte) 0x00, null, null, null, 0); } /** - * Opens a logical Channel with AID. + * Opens a logical Channel with AID for the given package name or uuid */ public Channel openLogicalChannel(SecureElementSession session, byte[] aid, byte p2, - ISecureElementListener listener, String packageName, int pid) throws IOException, + ISecureElementListener listener, String packageName, + byte[] uuid, int pid) throws IOException, NoSuchElementException { if (aid != null && aid.length == 0) { aid = null; @@ -541,11 +557,22 @@ public class Terminal { SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, mName, packageName); - try { - channelAccess = setUpChannelAccess(aid, packageName, pid, false); - } catch (MissingResourceException | UnsupportedOperationException e) { - return null; + } else if (uuid != null) { + Log.w(mTag, "Enable access control on logical channel for uid: " + + Binder.getCallingUid() + + " UUID: " + Arrays.toString(uuid)); + SecureElementStatsLog.write( + SecureElementStatsLog.SE_OMAPI_REPORTED, + SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, + mName, + Arrays.toString(uuid)); + } + try { + if (packageName != null || uuid != null) { + channelAccess = setUpChannelAccess(aid, packageName, uuid, pid, false); } + } catch (MissingResourceException | UnsupportedOperationException e) { + return null; } synchronized (mLock) { @@ -745,10 +772,10 @@ public class Terminal { /** * Initialize the Access Control and set up the channel access. */ - private ChannelAccess setUpChannelAccess(byte[] aid, String packageName, int pid, + private ChannelAccess setUpChannelAccess(byte[] aid, String packageName, byte[] uuid, int pid, boolean isBasicChannel) throws IOException, MissingResourceException { boolean checkRefreshTag = true; - if (isPrivilegedApplication(packageName)) { + if (packageName != null && isPrivilegedApplication(packageName)) { return ChannelAccess.getPrivilegeAccess(packageName, pid); } // Attempt to initialize the access control enforcer if it failed @@ -764,7 +791,7 @@ public class Terminal { mAccessControlEnforcer.setPackageManager(mContext.getPackageManager()); // Check carrier privilege when AID is not ISD-R - if (getName().startsWith(SecureElementService.UICC_TERMINAL) + if (packageName != null && getName().startsWith(SecureElementService.UICC_TERMINAL) && !Arrays.equals(aid, ISD_R_AID)) { try { PackageManager pm = mContext.getPackageManager(); @@ -794,7 +821,7 @@ public class Terminal { synchronized (mLock) { try { ChannelAccess channelAccess = - mAccessControlEnforcer.setUpChannelAccess(aid, packageName, + mAccessControlEnforcer.setUpChannelAccess(aid, packageName, uuid, checkRefreshTag); channelAccess.setCallingPid(pid); return channelAccess; diff --git a/src/com/android/se/security/AccessControlEnforcer.java b/src/com/android/se/security/AccessControlEnforcer.java index 6d595d8..e6f08cf 100644 --- a/src/com/android/se/security/AccessControlEnforcer.java +++ b/src/com/android/se/security/AccessControlEnforcer.java @@ -271,8 +271,8 @@ public class AccessControlEnforcer { } /** Sets up the Channel Access for the given Package */ - public ChannelAccess setUpChannelAccess(byte[] aid, String packageName, boolean checkRefreshTag) - throws IOException, MissingResourceException { + public ChannelAccess setUpChannelAccess(byte[] aid, String packageName, byte[] uuid, + boolean checkRefreshTag) throws IOException, MissingResourceException { ChannelAccess channelAccess = null; // check result of channel access during initialization procedure if (mInitialChannelAccess.getAccess() == ChannelAccess.ACCESS.DENIED) { @@ -281,7 +281,7 @@ public class AccessControlEnforcer { } // this is the new GP Access Control Enforcer implementation if (mUseAra || mUseArf) { - channelAccess = internal_setUpChannelAccess(aid, packageName, + channelAccess = internal_setUpChannelAccess(aid, packageName, uuid, checkRefreshTag); } if (channelAccess == null || (channelAccess.getApduAccess() != ChannelAccess.ACCESS.ALLOWED @@ -299,14 +299,23 @@ public class AccessControlEnforcer { } private synchronized ChannelAccess internal_setUpChannelAccess(byte[] aid, - String packageName, boolean checkRefreshTag) throws IOException, + String packageName, byte[] uuid, boolean checkRefreshTag) throws IOException, MissingResourceException { - if (packageName == null || packageName.isEmpty()) { + if (uuid == null && (packageName == null || packageName.isEmpty())) { throw new AccessControlException("package names must be specified"); } try { // estimate SHA-1 and SHA-256 hash values of the device application's certificate. - List<byte[]> appCertHashes = getAppCertHashes(packageName); + List<byte[]> appCertHashes = null; + if (packageName != null) { + appCertHashes = getAppCertHashes(packageName); + } else { + if (uuid != null) { + appCertHashes = new ArrayList<byte[]>(); + appCertHashes.add(uuid); + } + } + // APP certificates must be available => otherwise Exception if (appCertHashes == null || appCertHashes.size() == 0) { throw new AccessControlException( diff --git a/src/com/android/se/security/HalRefDoParser.java b/src/com/android/se/security/HalRefDoParser.java new file mode 100644 index 0000000..40ea7de --- /dev/null +++ b/src/com/android/se/security/HalRefDoParser.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2021 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 com.android.se.security; + +import android.os.Build; +import android.os.SystemProperties; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Parser for HAL_UID_MAP.XML + * Parses the xml file and collects HAL references (UUID) to identify the corresponding + * access rules for the HAL services. + */ +public class HalRefDoParser { + + private static final boolean DEBUG = Build.IS_DEBUGGABLE; + private final String mTag = "SecureElement-HalRefDoParser"; + + private static final String PROP_PRODUCT_HARDWARE_SKU = "ro.boot.product.hardware.sku"; + private static final String UUID_MAPPING_CONFIG_PREFIX = "hal_uuid_map_"; + private static final String UUID_MAPPING_CONFIG_EXT = ".xml"; + private static final String[] UUID_MAPPING_CONFIG_PATHS = {"/odm/etc/", "/vendor/etc/", + "/etc/"}; + + // Holds UUID to UIDs mapping + private final Map<Integer, byte[]> mUUIDMap = new HashMap<Integer, byte[]>(); + + private static final String REF_DO = "ref_do"; + private static final String UUID_REF_DO = "uuid_ref_do"; + private static final String UUID = "uuid"; + private static final String UIDS = "uids"; + private static final String UID = "uid"; + + private static final byte[] PADDING_BYTES = { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + + private HalRefDoParser() { + parseUuidMappings(); + } + + private static class HalRefParserSingleton { + private static final HalRefDoParser INSTANCE = new HalRefDoParser(); + } + + public static HalRefDoParser getInstance() { + return HalRefParserSingleton.INSTANCE; + } + + private File getUuidMapConfigFile() { + // default file name: hal_uuid_map_config.xml + String uuid_map_config_file_name = UUID_MAPPING_CONFIG_PREFIX + + SystemProperties.get(PROP_PRODUCT_HARDWARE_SKU, "config") + + UUID_MAPPING_CONFIG_EXT; + String uuid_map_config_path = null; + + try { + // Search in predefined folders + for (String path : UUID_MAPPING_CONFIG_PATHS) { + uuid_map_config_path = path + uuid_map_config_file_name; + File confFile = new File(uuid_map_config_path); + if (confFile.exists()) { + Log.d(mTag, "UUID mapping config file path: " + uuid_map_config_path); + return confFile; + } + } + } catch (Exception e) { + Log.e(mTag, "Error in finding UUID mapping config file path: " + uuid_map_config_path); + } + + return null; + } + + /** + * Parses the below mapping structure - + * + * <ref_do> + * <uuid_ref_do> + * <uids> + * <uid>1000</uid> + * </uids> + * <uuid>a9b7ba70783b317e9998dc4dd82eb3c5</uuid> + * </uuid_ref_do> + * </ref_do> + */ + private void parse(InputStream is) { + try { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser parser = factory.newPullParser(); + String text = null; + List<Integer> uids = null; + byte[] uuid = null; + + parser.setInput(is, null); + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + String tagname = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + if (tagname.equalsIgnoreCase(UIDS)) { + uids = new ArrayList<Integer>(); + } else if (tagname.equalsIgnoreCase(UUID)) { + uuid = null; + } + break; + + case XmlPullParser.TEXT: + text = parser.getText(); + break; + + case XmlPullParser.END_TAG: + if (tagname.equalsIgnoreCase(UUID_REF_DO)) { + if (uuid != null) { + for (int uid : uids) { + mUUIDMap.put(uid, uuid); + } + } + } else if (tagname.equalsIgnoreCase(UID)) { + uids.add(Integer.parseInt(text)); + } else if (tagname.equalsIgnoreCase(UUID)) { + byte[] uuidValue = decodeHexUUID(text); + uuid = new byte[uuidValue.length + PADDING_BYTES.length]; + System.arraycopy(PADDING_BYTES, 0, uuid, 0, PADDING_BYTES.length); + System.arraycopy(uuidValue, 0, uuid, PADDING_BYTES.length, + uuidValue.length); + + } + break; + + default: + break; + } + eventType = parser.next(); + } + + } catch (XmlPullParserException e) { + Log.e(mTag, "Error while parsing hal uuid mappings"); + Log.e(mTag, e.getMessage()); + } catch (IOException e) { + Log.e(mTag, "IO error while parsing hal uuid mappings"); + Log.e(mTag, e.getMessage()); + } + } + + /** + * Finds the uuid mapping config file path from predefined folders + * Parses the uuid mapping config file + */ + private void parseUuidMappings() { + try { + File uuid_map_file = getUuidMapConfigFile(); + if (uuid_map_file == null) { + Log.e(mTag, "Unable to determine UUID mapping config file path"); + return; + } + + parse(new FileInputStream(uuid_map_file)); + } catch (Exception e) { + Log.e(mTag, "Unable to parse hal uuid mappings"); + Log.e(mTag, e.getMessage()); + } + + if (DEBUG) { + for (Map.Entry<Integer, byte[]> entry : mUUIDMap.entrySet()) { + Log.d(mTag, "UID: " + entry.getKey()); + Log.d(mTag, "UUID: " + Arrays.toString(entry.getValue())); + } + } + } + + /** + * Finds UUID for the give UID + */ + public byte[] findUUID(int uid) { + return mUUIDMap.get(uid); + } + + /** + * Convert char to hex digit + * @param hexChar + * @return hex digit + */ + private int toDigit(char hexChar) { + int digit = Character.digit(hexChar, 16); + if (digit == -1) { + throw new IllegalArgumentException( + "Invalid Hexadecimal Character: " + hexChar); + } + return digit; + } + + /** + * Convert hex digits string to bytes + * @param hextText + * @return hex byte + */ + private byte hexToByte(char ch1, char ch2) { + int firstDigit = toDigit(ch1); + int secondDigit = toDigit(ch2); + return (byte) ((firstDigit << 4) + secondDigit); + } + + /** + * Convert hex string to hex byte array + * @param hextText + * @return hex bytes + */ + private byte[] decodeHexUUID(String hextText) { + if (hextText == null || hextText.length() != 32) { + throw new IllegalArgumentException( + "Invalid UUID supplied"); + } + + byte[] bytes = new byte[hextText.length() / 2]; + for (int i = 0; i < hextText.length(); i += 2) { + bytes[i / 2] = hexToByte(hextText.charAt(i), hextText.charAt(i + 1)); + } + return bytes; + } + +} |