Home | History | Annotate | Download | only in uicc
      1 /*
      2  * Copyright (C) 2014 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.internal.telephony.uicc;
     18 
     19 import android.content.Intent;
     20 import android.content.pm.PackageInfo;
     21 import android.content.pm.PackageManager;
     22 import android.content.pm.ResolveInfo;
     23 import android.content.pm.Signature;
     24 import android.os.AsyncResult;
     25 import android.os.Binder;
     26 import android.os.Handler;
     27 import android.os.Message;
     28 import android.telephony.Rlog;
     29 import android.telephony.TelephonyManager;
     30 
     31 import com.android.internal.telephony.CommandsInterface;
     32 import com.android.internal.telephony.uicc.IccUtils;
     33 
     34 import java.io.ByteArrayInputStream;
     35 import java.lang.IllegalArgumentException;
     36 import java.security.MessageDigest;
     37 import java.security.NoSuchAlgorithmException;
     38 import java.security.cert.Certificate;
     39 import java.security.cert.CertificateException;
     40 import java.security.cert.CertificateFactory;
     41 import java.security.cert.X509Certificate;
     42 import java.util.ArrayList;
     43 import java.util.Arrays;
     44 import java.util.List;
     45 import java.util.Locale;
     46 import java.util.concurrent.atomic.AtomicInteger;
     47 
     48 /**
     49  * Class that reads and stores the carrier privileged rules from the UICC.
     50  *
     51  * The rules are read when the class is created, hence it should only be created
     52  * after the UICC can be read. And it should be deleted when a UICC is changed.
     53  *
     54  * The spec for the rules:
     55  *     GP Secure Element Access Control:
     56  *     http://www.globalplatform.org/specifications/review/GPD_SE_Access_Control_v1.0.20.pdf
     57  *     Extension spec:
     58  *     https://code.google.com/p/seek-for-android/
     59  *
     60  *
     61  * TODO: Notifications.
     62  *
     63  * {@hide}
     64  */
     65 public class UiccCarrierPrivilegeRules extends Handler {
     66     private static final String LOG_TAG = "UiccCarrierPrivilegeRules";
     67 
     68     private static final String AID = "A00000015141434C00";
     69     private static final int CLA = 0x80;
     70     private static final int COMMAND = 0xCA;
     71     private static final int P1 = 0xFF;
     72     private static final int P2 = 0x40;
     73     private static final int P3 = 0x00;
     74     private static final String DATA = "";
     75 
     76     /*
     77      * Rules format:
     78      *   ALL_REF_AR_DO = TAG_ALL_REF_AR_DO + len + [REF_AR_DO]*n
     79      *   REF_AR_DO = TAG_REF_AR_DO + len + REF-DO + AR-DO
     80      *
     81      *   REF_DO = TAG_REF_DO + len + DEVICE_APP_ID_REF_DO + (optional) PKG_REF_DO
     82      *   AR_DO = TAG_AR_DO + len + PERM_AR_DO
     83      *
     84      *   DEVICE_APP_ID_REF_DO = TAG_DEVICE_APP_ID_REF_DO + len + sha1 hexstring of cert (20 bytes)
     85      *   PKG_REF_DO = TAG_PKG_REF_DO + len + package name
     86      *   PERM_AR_DO = TAG_PERM_AR_DO + len + detailed permission (8 bytes)
     87      *
     88      * Data objects hierarchy by TAG:
     89      * FF40
     90      *   E2
     91      *     E1
     92      *       C1
     93      *       CA
     94      *     E3
     95      *       DB
     96      */
     97     // Values from the data standard.
     98     private static final String TAG_ALL_REF_AR_DO = "FF40";
     99     private static final String TAG_REF_AR_DO = "E2";
    100     private static final String TAG_REF_DO = "E1";
    101     private static final String TAG_DEVICE_APP_ID_REF_DO = "C1";
    102     private static final String TAG_PKG_REF_DO = "CA";
    103     private static final String TAG_AR_DO = "E3";
    104     private static final String TAG_PERM_AR_DO = "DB";
    105 
    106     private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
    107     private static final int EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE = 2;
    108     private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 3;
    109 
    110     // State of the object.
    111     private static final int STATE_LOADING  = 0;
    112     private static final int STATE_LOADED   = 1;
    113     private static final int STATE_ERROR    = 2;
    114 
    115     // Describes a single rule.
    116     private static class AccessRule {
    117         public byte[] certificateHash;
    118         public String packageName;
    119         public long accessType;   // This bit is not currently used, but reserved for future use.
    120 
    121         AccessRule(byte[] certificateHash, String packageName, long accessType) {
    122             this.certificateHash = certificateHash;
    123             this.packageName = packageName;
    124             this.accessType = accessType;
    125         }
    126 
    127         boolean matches(byte[] certHash, String packageName) {
    128           return certHash != null && Arrays.equals(this.certificateHash, certHash) &&
    129                 (this.packageName == null || this.packageName.equals(packageName));
    130         }
    131 
    132         @Override
    133         public String toString() {
    134             return "cert: " + certificateHash + " pkg: " + packageName +
    135                 " access: " + accessType;
    136         }
    137     }
    138 
    139     // Used for parsing the data from the UICC.
    140     private static class TLV {
    141         private String tag;
    142         private Integer length;
    143         private String value;
    144 
    145         public TLV(String tag) {
    146             this.tag = tag;
    147         }
    148 
    149         public String parse(String data, boolean shouldConsumeAll) {
    150             Rlog.d(LOG_TAG, "Parse TLV: " + tag);
    151             if (!data.startsWith(tag)) {
    152                 throw new IllegalArgumentException("Tags don't match.");
    153             }
    154             int index = tag.length();
    155             if (index + 2 > data.length()) {
    156                 throw new IllegalArgumentException("No length.");
    157             }
    158             length = new Integer(2 * Integer.parseInt(
    159                     data.substring(index, index + 2), 16));
    160             index += 2;
    161 
    162             int remainingLength = data.length() - (index + length);
    163             if (remainingLength < 0) {
    164                 throw new IllegalArgumentException("Not enough data.");
    165             }
    166             if (shouldConsumeAll && (remainingLength != 0)) {
    167                 throw new IllegalArgumentException("Did not consume all.");
    168             }
    169             value = data.substring(index, index + length);
    170 
    171             Rlog.d(LOG_TAG, "Got TLV: " + tag + "," + length + "," + value);
    172 
    173             return data.substring(index + length);
    174         }
    175     }
    176 
    177     private UiccCard mUiccCard;  // Parent
    178     private AtomicInteger mState;
    179     private List<AccessRule> mAccessRules;
    180     private Message mLoadedCallback;
    181 
    182     public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
    183         Rlog.d(LOG_TAG, "Creating UiccCarrierPrivilegeRules");
    184         mUiccCard = uiccCard;
    185         mState = new AtomicInteger(STATE_LOADING);
    186         mLoadedCallback = loadedCallback;
    187 
    188         // Start loading the rules.
    189         mUiccCard.iccOpenLogicalChannel(AID,
    190             obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
    191     }
    192 
    193     /**
    194      * Returns true if the carrier privilege rules have finished loading.
    195      */
    196     public boolean areCarrierPriviligeRulesLoaded() {
    197         return mState.get() != STATE_LOADING;
    198     }
    199 
    200     /**
    201      * Returns the status of the carrier privileges for the input certificate and package name.
    202      *
    203      * @param signature The signature of the certificate.
    204      * @param packageName name of the package.
    205      * @return Access status.
    206      */
    207     public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
    208         Rlog.d(LOG_TAG, "hasCarrierPrivileges: " + signature + " : " + packageName);
    209         int state = mState.get();
    210         if (state == STATE_LOADING) {
    211             Rlog.d(LOG_TAG, "Rules not loaded.");
    212             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
    213         } else if (state == STATE_ERROR) {
    214             Rlog.d(LOG_TAG, "Error loading rules.");
    215             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES;
    216         }
    217 
    218         byte[] certHash = getCertHash(signature);
    219         if (certHash == null) {
    220           return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
    221         }
    222         Rlog.e(LOG_TAG, "Checking: " + IccUtils.bytesToHexString(certHash) + " : " + packageName);
    223 
    224         for (AccessRule ar : mAccessRules) {
    225             if (ar.matches(certHash, packageName)) {
    226                 Rlog.d(LOG_TAG, "Match found!");
    227                 return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
    228             }
    229         }
    230 
    231         Rlog.d(LOG_TAG, "No matching rule found. Returning false.");
    232         return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
    233     }
    234 
    235     /**
    236      * Returns the status of the carrier privileges for the input package name.
    237      *
    238      * @param packageManager PackageManager for getting signatures.
    239      * @param packageName name of the package.
    240      * @return Access status.
    241      */
    242     public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
    243         try {
    244             PackageInfo pInfo = packageManager.getPackageInfo(packageName,
    245                 PackageManager.GET_SIGNATURES);
    246             Signature[] signatures = pInfo.signatures;
    247             for (Signature sig : signatures) {
    248                 int accessStatus = getCarrierPrivilegeStatus(sig, pInfo.packageName);
    249                 if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
    250                     return accessStatus;
    251                 }
    252             }
    253         } catch (PackageManager.NameNotFoundException ex) {
    254             Rlog.e(LOG_TAG, "NameNotFoundException", ex);
    255         }
    256         return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
    257     }
    258 
    259     /**
    260      * Returns the status of the carrier privileges for the caller of the current transaction.
    261      *
    262      * @param packageManager PackageManager for getting signatures and package names.
    263      * @return Access status.
    264      */
    265     public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
    266         String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid());
    267 
    268         for (String pkg : packages) {
    269             int accessStatus = getCarrierPrivilegeStatus(packageManager, pkg);
    270             if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
    271                 return accessStatus;
    272             }
    273         }
    274         return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
    275     }
    276 
    277     /**
    278      * Returns the package name of the carrier app that should handle the input intent.
    279      *
    280      * @param packageManager PackageManager for getting receivers.
    281      * @param intent Intent that will be sent.
    282      * @return list of carrier app package names that can handle the intent.
    283      *         Returns null if there is an error and an empty list if there
    284      *         are no matching packages.
    285      */
    286     public List<String> getCarrierPackageNamesForIntent(
    287             PackageManager packageManager, Intent intent) {
    288         List<String> packages = new ArrayList<String>();
    289         List<ResolveInfo> receivers = new ArrayList<ResolveInfo>();
    290         receivers.addAll(packageManager.queryBroadcastReceivers(intent, 0));
    291         receivers.addAll(packageManager.queryIntentContentProviders(intent, 0));
    292         receivers.addAll(packageManager.queryIntentActivities(intent, 0));
    293         receivers.addAll(packageManager.queryIntentServices(intent, 0));
    294 
    295         for (ResolveInfo resolveInfo : receivers) {
    296             if (resolveInfo.activityInfo == null) {
    297                 continue;
    298             }
    299             String packageName = resolveInfo.activityInfo.packageName;
    300             int status = getCarrierPrivilegeStatus(packageManager, packageName);
    301             if (status == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
    302                 packages.add(packageName);
    303             } else if (status != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
    304                 // Any status apart from HAS_ACCESS and NO_ACCESS is considered an error.
    305                 return null;
    306             }
    307         }
    308 
    309         return packages;
    310     }
    311 
    312     @Override
    313     public void handleMessage(Message msg) {
    314         AsyncResult ar;
    315 
    316         switch (msg.what) {
    317 
    318           case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
    319               Rlog.d(LOG_TAG, "EVENT_OPEN_LOGICAL_CHANNEL_DONE");
    320               ar = (AsyncResult) msg.obj;
    321               if (ar.exception == null && ar.result != null) {
    322                   int channelId = ((int[]) ar.result)[0];
    323                   mUiccCard.iccTransmitApduLogicalChannel(channelId, CLA, COMMAND, P1, P2, P3, DATA,
    324                       obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(channelId)));
    325               } else {
    326                   Rlog.e(LOG_TAG, "Error opening channel");
    327                   updateState(STATE_ERROR);
    328               }
    329               break;
    330 
    331           case EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE:
    332               Rlog.d(LOG_TAG, "EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
    333               ar = (AsyncResult) msg.obj;
    334               if (ar.exception == null && ar.result != null) {
    335                   IccIoResult response = (IccIoResult) ar.result;
    336                   if (response.payload != null && response.sw1 == 0x90 && response.sw2 == 0x00) {
    337                       try {
    338                           mAccessRules = parseRules(IccUtils.bytesToHexString(response.payload));
    339                           updateState(STATE_LOADED);
    340                       } catch (IllegalArgumentException ex) {
    341                           Rlog.e(LOG_TAG, "Error parsing rules: " + ex);
    342                           updateState(STATE_ERROR);
    343                       }
    344                    } else {
    345                       Rlog.e(LOG_TAG, "Invalid response: payload=" + response.payload +
    346                               " sw1=" + response.sw1 + " sw2=" + response.sw2);
    347                       updateState(STATE_ERROR);
    348                    }
    349               } else {
    350                   Rlog.e(LOG_TAG, "Error reading value from SIM.");
    351                   updateState(STATE_ERROR);
    352               }
    353 
    354               int channelId = (Integer) ar.userObj;
    355               mUiccCard.iccCloseLogicalChannel(channelId, obtainMessage(
    356                       EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
    357               break;
    358 
    359           case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
    360               Rlog.d(LOG_TAG, "EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
    361               break;
    362 
    363           default:
    364               Rlog.e(LOG_TAG, "Unknown event " + msg.what);
    365         }
    366     }
    367 
    368     /*
    369      * Parses the rules from the input string.
    370      */
    371     private static List<AccessRule> parseRules(String rules) {
    372         rules = rules.toUpperCase(Locale.US);
    373         Rlog.d(LOG_TAG, "Got rules: " + rules);
    374 
    375         TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO); //FF40
    376         allRefArDo.parse(rules, true);
    377 
    378         String arDos = allRefArDo.value;
    379         List<AccessRule> accessRules = new ArrayList<AccessRule>();
    380         while (!arDos.isEmpty()) {
    381             TLV refArDo = new TLV(TAG_REF_AR_DO); //E2
    382             arDos = refArDo.parse(arDos, false);
    383             AccessRule accessRule = parseRefArdo(refArDo.value);
    384             if (accessRule != null) {
    385                 accessRules.add(accessRule);
    386             } else {
    387               Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
    388             }
    389         }
    390         return accessRules;
    391     }
    392 
    393     /*
    394      * Parses a single rule.
    395      */
    396     private static AccessRule parseRefArdo(String rule) {
    397         Rlog.d(LOG_TAG, "Got rule: " + rule);
    398 
    399         String certificateHash = null;
    400         String packageName = null;
    401         String tmp = null;
    402         long accessType = 0;
    403 
    404         while (!rule.isEmpty()) {
    405             if (rule.startsWith(TAG_REF_DO)) {
    406                 TLV refDo = new TLV(TAG_REF_DO); //E1
    407                 rule = refDo.parse(rule, false);
    408 
    409                 // Skip unrelated rules.
    410                 if (!refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
    411                     return null;
    412                 }
    413 
    414                 TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
    415                 tmp = deviceDo.parse(refDo.value, false);
    416                 certificateHash = deviceDo.value;
    417 
    418                 if (!tmp.isEmpty()) {
    419                   if (!tmp.startsWith(TAG_PKG_REF_DO)) {
    420                       return null;
    421                   }
    422                   TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
    423                   pkgDo.parse(tmp, true);
    424                   packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
    425                 } else {
    426                   packageName = null;
    427                 }
    428             } else if (rule.startsWith(TAG_AR_DO)) {
    429                 TLV arDo = new TLV(TAG_AR_DO); //E3
    430                 rule = arDo.parse(rule, false);
    431 
    432                 // Skip unrelated rules.
    433                 if (!arDo.value.startsWith(TAG_PERM_AR_DO)) {
    434                     return null;
    435                 }
    436 
    437                 TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
    438                 permDo.parse(arDo.value, true);
    439                 Rlog.e(LOG_TAG, permDo.value);
    440             } else  {
    441                 // Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
    442                 throw new RuntimeException("Invalid Rule type");
    443             }
    444         }
    445 
    446         Rlog.e(LOG_TAG, "Adding: " + certificateHash + " : " + packageName + " : " + accessType);
    447 
    448         AccessRule accessRule = new AccessRule(IccUtils.hexStringToBytes(certificateHash),
    449             packageName, accessType);
    450         Rlog.e(LOG_TAG, "Parsed rule: " + accessRule);
    451         return accessRule;
    452     }
    453 
    454     /*
    455      * Converts a Signature into a Certificate hash usable for comparison.
    456      */
    457     private static byte[] getCertHash(Signature signature) {
    458         // TODO: Is the following sufficient.
    459         try {
    460             CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    461             X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
    462                     new ByteArrayInputStream(signature.toByteArray()));
    463 
    464             MessageDigest md = MessageDigest.getInstance("SHA");
    465             return md.digest(cert.getEncoded());
    466         } catch (CertificateException ex) {
    467             Rlog.e(LOG_TAG, "CertificateException: " + ex);
    468         } catch (NoSuchAlgorithmException ex) {
    469             Rlog.e(LOG_TAG, "NoSuchAlgorithmException: " + ex);
    470         }
    471 
    472         Rlog.e(LOG_TAG, "Cannot compute cert hash");
    473         return null;
    474     }
    475 
    476     /*
    477      * Updates the state and notifies the UiccCard that the rules have finished loading.
    478      */
    479     private void updateState(int newState) {
    480         mState.set(newState);
    481         if (mLoadedCallback != null) {
    482             mLoadedCallback.sendToTarget();
    483         }
    484     }
    485 }
    486