Home | History | Annotate | Download | only in security
      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  * Copyright (c) 2014-2017, The Linux Foundation.
     18  */
     19 
     20 /*
     21  * Copyright 2012 Giesecke & Devrient GmbH.
     22  *
     23  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
     24  * use this file except in compliance with the License. You may obtain a copy of
     25  * the License at
     26  *
     27  * http://www.apache.org/licenses/LICENSE-2.0
     28  *
     29  * Unless required by applicable law or agreed to in writing, software
     30  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     31  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     32  * License for the specific language governing permissions and limitations under
     33  * the License.
     34  */
     35 
     36 package com.android.se.security;
     37 
     38 import android.content.pm.PackageInfo;
     39 import android.content.pm.PackageManager;
     40 import android.content.pm.PackageManager.NameNotFoundException;
     41 import android.content.pm.Signature;
     42 import android.os.Build;
     43 import android.os.SystemProperties;
     44 import android.util.Log;
     45 
     46 import com.android.se.Channel;
     47 import com.android.se.SecureElementService;
     48 import com.android.se.Terminal;
     49 import com.android.se.security.ChannelAccess.ACCESS;
     50 import com.android.se.security.ara.AraController;
     51 import com.android.se.security.arf.ArfController;
     52 
     53 import java.io.ByteArrayInputStream;
     54 import java.io.IOException;
     55 import java.io.PrintWriter;
     56 import java.security.AccessControlException;
     57 import java.security.MessageDigest;
     58 import java.security.NoSuchAlgorithmException;
     59 import java.security.cert.Certificate;
     60 import java.security.cert.CertificateEncodingException;
     61 import java.security.cert.CertificateException;
     62 import java.security.cert.CertificateFactory;
     63 import java.security.cert.X509Certificate;
     64 import java.util.ArrayList;
     65 import java.util.MissingResourceException;
     66 import java.util.NoSuchElementException;
     67 
     68 /** Reads and Maintains the ARF and ARA access control for a particular Secure Element */
     69 public class AccessControlEnforcer {
     70 
     71     private final String mTag = "SecureElement-AccessControlEnforcer";
     72     private PackageManager mPackageManager = null;
     73     private AraController mAraController = null;
     74     private boolean mUseAra = true;
     75     private ArfController mArfController = null;
     76     private boolean mUseArf = false;
     77     private AccessRuleCache mAccessRuleCache = null;
     78     private boolean mRulesRead = false;
     79     private Terminal mTerminal = null;
     80     private ChannelAccess mInitialChannelAccess = new ChannelAccess();
     81     private boolean mFullAccess = false;
     82 
     83     public AccessControlEnforcer(Terminal terminal) {
     84 
     85         mTerminal = terminal;
     86         mAccessRuleCache = new AccessRuleCache();
     87     }
     88 
     89     public static byte[] getDefaultAccessControlAid() {
     90         return AraController.getAraMAid();
     91     }
     92 
     93     private static Certificate decodeCertificate(byte[] certData) throws CertificateException {
     94         CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
     95         X509Certificate cert =
     96                 (X509Certificate) certFactory.generateCertificate(
     97                         new ByteArrayInputStream(certData));
     98         return cert;
     99     }
    100 
    101     /** Returns the Hash of the Application */
    102     public static byte[] getAppCertHash(Certificate appCert) throws CertificateEncodingException {
    103         MessageDigest md = null;
    104         try {
    105             md = MessageDigest.getInstance("SHA");
    106         } catch (NoSuchAlgorithmException e) {
    107             throw new AccessControlException("Exception getting SHA for the signature");
    108         }
    109         if (md == null) {
    110             throw new AccessControlException("Hash can not be computed");
    111         }
    112         return md.digest(appCert.getEncoded());
    113     }
    114 
    115     public PackageManager getPackageManager() {
    116         return mPackageManager;
    117     }
    118 
    119     public void setPackageManager(PackageManager packageManager) {
    120         mPackageManager = packageManager;
    121     }
    122 
    123     public Terminal getTerminal() {
    124         return mTerminal;
    125     }
    126 
    127     public AccessRuleCache getAccessRuleCache() {
    128         return mAccessRuleCache;
    129     }
    130 
    131     /** Resets the Access Control for the Secure Element */
    132     public synchronized void reset() {
    133         // Destroy any previous Controler
    134         // in order to reset the ACE
    135         Log.i(mTag, "Reset the ACE for terminal:" + mTerminal.getName());
    136         mAccessRuleCache.reset();
    137         mAraController = null;
    138         mArfController = null;
    139     }
    140 
    141     /** Initializes the Access Control for the Secure Element */
    142     public synchronized void initialize() throws IOException, MissingResourceException {
    143         boolean status = true;
    144         String denyMsg = "";
    145         // allow access to set up access control for a channel
    146         mInitialChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
    147         mInitialChannelAccess.setNFCEventAccess(ChannelAccess.ACCESS.ALLOWED);
    148         mInitialChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
    149 
    150         readSecurityProfile();
    151 
    152         if (!mTerminal.getName().startsWith(SecureElementService.UICC_TERMINAL)) {
    153             // When SE is not the UICC then it's allowed to grant full access if no
    154             // rules can be retreived.
    155             mFullAccess = true;
    156         }
    157 
    158         // 1 - Let's try to use ARA
    159         if (mUseAra && mAraController == null) {
    160             mAraController = new AraController(mAccessRuleCache, mTerminal);
    161         }
    162 
    163         if (mUseAra && mAraController != null) {
    164             try {
    165                 mAraController.initialize();
    166                 Log.i(mTag, "ARA applet is used for:" + mTerminal.getName());
    167                 // disable other access methods
    168                 mUseArf = false;
    169                 mFullAccess = false;
    170             } catch (IOException | MissingResourceException e) {
    171                 throw e;
    172             } catch (Exception e) {
    173                 // ARA cannot be used since we got an exception during initialization
    174                 mUseAra = false;
    175                 denyMsg = e.getLocalizedMessage();
    176                 if (e instanceof NoSuchElementException) {
    177                     Log.i(mTag, "No ARA applet found in: " + mTerminal.getName());
    178                 } else if (mTerminal.getName().startsWith(SecureElementService.UICC_TERMINAL)) {
    179                     // A possible explanation could simply be due to the fact that the UICC is old
    180                     // and does not support logical channel (and is not compliant with GP spec).
    181                     // We should simply act as if no ARA was available in this case.
    182                     if (!mUseArf) {
    183                         // Only ARA was the candidate to retrieve access rules,
    184                         // but it is not 100% sure if the expected ARA really does not exist.
    185                         // Full access should not be granted in this case.
    186                         mFullAccess = false;
    187                         status = false;
    188                     }
    189                 } else {
    190                     // ARA is available but doesn't work properly.
    191                     // We are going to disable everything per security req.
    192                     mUseArf = false;
    193                     mFullAccess = false;
    194                     status = false;
    195                     Log.i(mTag, "Problem accessing ARA, Access DENIED "
    196                             + e.getLocalizedMessage());
    197                 }
    198             }
    199         }
    200 
    201         // 2 - Let's try to use ARF since ARA cannot be used
    202         if (mUseArf && !mTerminal.getName().startsWith(SecureElementService.UICC_TERMINAL)) {
    203             Log.i(mTag, "Disable ARF for terminal: " + mTerminal.getName()
    204                     + " (ARF is only available for UICC)");
    205             mUseArf = false; // Arf is only supproted on UICC
    206         }
    207 
    208         if (mUseArf && mArfController == null) {
    209             mArfController = new ArfController(mAccessRuleCache, mTerminal);
    210         }
    211 
    212         if (mUseArf && mArfController != null) {
    213             try {
    214                 mArfController.initialize();
    215                 // disable other access methods
    216                 Log.i(mTag, "ARF rules are used for:" + mTerminal.getName());
    217                 mFullAccess = false;
    218             } catch (IOException | MissingResourceException e) {
    219                 throw e;
    220             } catch (Exception e) {
    221                 // ARF cannot be used since we got an exception
    222                 mUseArf = false;
    223                 denyMsg = e.getLocalizedMessage();
    224                 Log.e(mTag, e.getMessage());
    225                 if (mFullAccess) {
    226                     if (!(e instanceof NoSuchElementException)) {
    227                         // It is not 100% sure if the expected ARF really does not exist.
    228                         // No ARF might be due to a kind of temporary problem like missing resource,
    229                         // so full access should not be granted in this case.
    230                         mFullAccess = false;
    231                         status = false;
    232                     }
    233                 } else {
    234                     status = false;
    235                 }
    236             }
    237         }
    238 
    239         /* 4 - Let's block everything since neither ARA, ARF or fullaccess can be used */
    240         if (!mUseArf && !mUseAra && !mFullAccess) {
    241             mInitialChannelAccess.setApduAccess(ChannelAccess.ACCESS.DENIED);
    242             mInitialChannelAccess.setNFCEventAccess(ChannelAccess.ACCESS.DENIED);
    243             mInitialChannelAccess.setAccess(ChannelAccess.ACCESS.DENIED, denyMsg);
    244             Log.i(mTag, "Deny any access to:" + mTerminal.getName());
    245         }
    246 
    247         mRulesRead = status;
    248     }
    249 
    250     /** Check if the Channel has permission for the given APDU */
    251     public synchronized void checkCommand(Channel channel, byte[] command) {
    252         ChannelAccess ca = channel.getChannelAccess();
    253         if (ca == null) {
    254             throw new AccessControlException(mTag + "Channel access not set");
    255         }
    256         String reason = ca.getReason();
    257         if (reason.length() == 0) {
    258             reason = "Command not allowed!";
    259         }
    260         if (ca.getAccess() != ACCESS.ALLOWED) {
    261             throw new AccessControlException(mTag + reason);
    262         }
    263         if (ca.isUseApduFilter()) {
    264             ApduFilter[] accessConditions = ca.getApduFilter();
    265             if (accessConditions == null || accessConditions.length == 0) {
    266                 throw new AccessControlException(mTag + "Access Rule not available:"
    267                         + reason);
    268             }
    269             for (ApduFilter ac : accessConditions) {
    270                 if (CommandApdu.compareHeaders(command, ac.getMask(), ac.getApdu())) {
    271                     return;
    272                 }
    273             }
    274             throw new AccessControlException(mTag + "Access Rule does not match: "
    275                     + reason);
    276         }
    277         if (ca.getApduAccess() == ChannelAccess.ACCESS.ALLOWED) {
    278             return;
    279         } else {
    280             throw new AccessControlException(mTag + "APDU access NOT allowed");
    281         }
    282     }
    283 
    284     /** Sets up the Channel Access for the given Package */
    285     public ChannelAccess setUpChannelAccess(byte[] aid, String packageName, boolean checkRefreshTag)
    286             throws IOException, MissingResourceException {
    287         ChannelAccess channelAccess = null;
    288         // check result of channel access during initialization procedure
    289         if (mInitialChannelAccess.getAccess() == ChannelAccess.ACCESS.DENIED) {
    290             throw new AccessControlException(
    291                     mTag + "access denied: " + mInitialChannelAccess.getReason());
    292         }
    293         // this is the new GP Access Control Enforcer implementation
    294         if (mUseAra || mUseArf) {
    295             channelAccess = internal_setUpChannelAccess(aid, packageName,
    296                     checkRefreshTag);
    297         }
    298         if (channelAccess == null || (channelAccess.getApduAccess() != ChannelAccess.ACCESS.ALLOWED
    299                 && !channelAccess.isUseApduFilter())) {
    300             if (mFullAccess) {
    301                 // if full access is set then we reuse the initial channel access,
    302                 // since we got so far it allows everything with a descriptive reason.
    303                 channelAccess = mInitialChannelAccess;
    304             } else {
    305                 throw new AccessControlException(mTag + "no APDU access allowed!");
    306             }
    307         }
    308         channelAccess.setPackageName(packageName);
    309         return channelAccess.clone();
    310     }
    311 
    312     private synchronized ChannelAccess internal_setUpChannelAccess(byte[] aid,
    313             String packageName, boolean checkRefreshTag) throws IOException,
    314             MissingResourceException {
    315         if (packageName == null || packageName.isEmpty()) {
    316             throw new AccessControlException("package names must be specified");
    317         }
    318         try {
    319             // estimate SHA-1 hash value of the device application's certificate.
    320             Certificate[] appCerts = getAPPCerts(packageName);
    321             // APP certificates must be available => otherwise Exception
    322             if (appCerts == null || appCerts.length == 0) {
    323                 throw new AccessControlException(
    324                         "Application certificates are invalid or do not exist.");
    325             }
    326             if (checkRefreshTag) {
    327                 updateAccessRuleIfNeed();
    328             }
    329             return getAccessRule(aid, appCerts);
    330         } catch (IOException | MissingResourceException e) {
    331             throw e;
    332         } catch (Throwable exp) {
    333             throw new AccessControlException(exp.getMessage());
    334         }
    335     }
    336 
    337     /** Fetches the Access Rules for the given application and AID pair */
    338     public ChannelAccess getAccessRule(
    339             byte[] aid, Certificate[] appCerts)
    340             throws AccessControlException, CertificateEncodingException {
    341         ChannelAccess channelAccess = null;
    342         // if read all is true get rule from cache.
    343         if (mRulesRead) {
    344             // get rules from internal storage
    345             channelAccess = mAccessRuleCache.findAccessRule(aid, appCerts);
    346         }
    347         // if no rule was found return an empty access rule
    348         // with all access denied.
    349         if (channelAccess == null) {
    350             channelAccess = new ChannelAccess();
    351             channelAccess.setAccess(ChannelAccess.ACCESS.DENIED, "no access rule found!");
    352             channelAccess.setApduAccess(ChannelAccess.ACCESS.DENIED);
    353             channelAccess.setNFCEventAccess(ChannelAccess.ACCESS.DENIED);
    354         }
    355         return channelAccess;
    356     }
    357 
    358     /**
    359      * Returns Certificate chain for one package.
    360      */
    361     private Certificate[] getAPPCerts(String packageName)
    362             throws CertificateException, NoSuchAlgorithmException, AccessControlException {
    363         if (packageName == null || packageName.length() == 0) {
    364             throw new AccessControlException("Package Name not defined");
    365         }
    366         PackageInfo foundPkgInfo;
    367         try {
    368             foundPkgInfo = mPackageManager.getPackageInfo(packageName,
    369                     PackageManager.GET_SIGNATURES);
    370         } catch (NameNotFoundException ne) {
    371             throw new AccessControlException("Package does not exist");
    372         }
    373         if (foundPkgInfo == null) {
    374             throw new AccessControlException("Package does not exist");
    375         }
    376         ArrayList<Certificate> appCerts = new ArrayList<Certificate>();
    377         for (Signature signature : foundPkgInfo.signatures) {
    378             appCerts.add(decodeCertificate(signature.toByteArray()));
    379         }
    380         return appCerts.toArray(new Certificate[appCerts.size()]);
    381     }
    382 
    383     /** Returns true if the given application is allowed to recieve NFC Events */
    384     public synchronized boolean[] isNfcEventAllowed(byte[] aid,
    385             String[] packageNames, boolean checkRefreshTag) {
    386         if (mUseAra || mUseArf) {
    387             return internal_isNfcEventAllowed(aid, packageNames, checkRefreshTag);
    388         } else {
    389             // if ARA and ARF is not available and
    390             // - terminal DOES NOT belong to a UICC -> mFullAccess is true
    391             // - terminal belongs to a UICC -> mFullAccess is false
    392             boolean[] ret = new boolean[packageNames.length];
    393             for (int i = 0; i < ret.length; i++) {
    394                 ret[i] = mFullAccess;
    395             }
    396             return ret;
    397         }
    398     }
    399 
    400     private synchronized boolean[] internal_isNfcEventAllowed(byte[] aid,
    401             String[] packageNames, boolean checkRefreshTag) {
    402         if (checkRefreshTag) {
    403             try {
    404                 updateAccessRuleIfNeed();
    405             } catch (IOException | MissingResourceException e) {
    406                 throw new AccessControlException("Access-Control not found in "
    407                         + mTerminal.getName());
    408             }
    409         }
    410 
    411         int i = 0;
    412         boolean[] nfcEventFlags = new boolean[packageNames.length];
    413         for (String packageName : packageNames) {
    414             // estimate SHA-1 hash value of the device application's certificate.
    415             try {
    416                 Certificate[] appCerts = getAPPCerts(packageName);
    417                 // APP certificates must be available => otherwise Exception
    418                 if (appCerts == null || appCerts.length == 0) {
    419                     nfcEventFlags[i] = false;
    420                 } else {
    421                     ChannelAccess channelAccess = getAccessRule(aid, appCerts);
    422                     nfcEventFlags[i] =
    423                             (channelAccess.getNFCEventAccess() == ChannelAccess.ACCESS.ALLOWED);
    424                 }
    425             } catch (Exception e) {
    426                 Log.w(mTag, " Access Rules for NFC: " + e.getLocalizedMessage());
    427                 nfcEventFlags[i] = false;
    428             }
    429             i++;
    430         }
    431         return nfcEventFlags;
    432     }
    433 
    434     private void updateAccessRuleIfNeed() throws IOException {
    435         if (mUseAra && mAraController != null) {
    436             try {
    437                 mAraController.initialize();
    438                 mUseArf = false;
    439                 mFullAccess = false;
    440             } catch (IOException | MissingResourceException e) {
    441                 // There was a communication error between the terminal and the secure element
    442                 // or failure in retrieving rules due to the lack of a new logical channel.
    443                 // These errors must be distinguished from other ones.
    444                 throw e;
    445             } catch (Exception e) {
    446                 throw new AccessControlException("No ARA applet found in " + mTerminal.getName());
    447             }
    448         } else if (mUseArf && mArfController != null) {
    449             try {
    450                 mArfController.initialize();
    451             } catch (IOException | MissingResourceException e) {
    452                 // There was a communication error between the terminal and the secure element
    453                 // or failure in retrieving rules due to the lack of a new logical channel.
    454                 // These errors must be distinguished from other ones.
    455                 throw e;
    456             } catch (Exception e) {
    457                 Log.e(mTag, e.getMessage());
    458             }
    459         }
    460     }
    461 
    462     /** Debug information to be used by dumpsys */
    463     public void dump(PrintWriter writer) {
    464         writer.println(mTag + ":");
    465 
    466         writer.println("mUseArf: " + mUseArf);
    467         writer.println("mUseAra: " + mUseAra);
    468         writer.println("mInitialChannelAccess:");
    469         writer.println(mInitialChannelAccess.toString());
    470         writer.println();
    471 
    472         /* Dump the access rule cache */
    473         if (mAccessRuleCache != null) mAccessRuleCache.dump(writer);
    474     }
    475 
    476     private void readSecurityProfile() {
    477         if (!Build.IS_DEBUGGABLE) {
    478             mUseArf = true;
    479             mUseAra = true;
    480             mFullAccess = false; // Per default we don't grant full access.
    481         } else {
    482             String level = SystemProperties.get("service.seek", "useara usearf");
    483             level = SystemProperties.get("persist.service.seek", level);
    484 
    485             if (level.contains("usearf")) {
    486                 mUseArf = true;
    487             } else {
    488                 mUseArf = false;
    489             }
    490             if (level.contains("useara")) {
    491                 mUseAra = true;
    492             } else {
    493                 mUseAra = false;
    494             }
    495             if (level.contains("fullaccess")) {
    496                 mFullAccess = true;
    497             } else {
    498                 mFullAccess = false;
    499             }
    500         }
    501         Log.i(
    502                 mTag,
    503                 "Allowed ACE mode: ara=" + mUseAra + " arf=" + mUseArf + " fullaccess="
    504                         + mFullAccess);
    505     }
    506 }
    507