UiccCarrierPrivilegeRules.java revision 8071f9cde13df71af0c4c8b3810d24740279da9a
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
17package com.android.internal.telephony.uicc;
18
19import android.content.Intent;
20import android.content.pm.PackageInfo;
21import android.content.pm.PackageManager;
22import android.content.pm.ResolveInfo;
23import android.content.pm.Signature;
24import android.os.AsyncResult;
25import android.os.Binder;
26import android.os.Handler;
27import android.os.Message;
28import android.telephony.Rlog;
29import android.telephony.TelephonyManager;
30
31import com.android.internal.telephony.CommandsInterface;
32import com.android.internal.telephony.uicc.IccUtils;
33
34import java.io.ByteArrayInputStream;
35import java.lang.IllegalArgumentException;
36import java.security.MessageDigest;
37import java.security.NoSuchAlgorithmException;
38import java.security.cert.Certificate;
39import java.security.cert.CertificateException;
40import java.security.cert.CertificateFactory;
41import java.security.cert.X509Certificate;
42import java.util.ArrayList;
43import java.util.Arrays;
44import java.util.List;
45import java.util.Locale;
46import 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 */
65public 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
181    public UiccCarrierPrivilegeRules(UiccCard uiccCard) {
182        Rlog.d(LOG_TAG, "Creating UiccCarrierPrivilegeRules");
183        mUiccCard = uiccCard;
184        mState = new AtomicInteger(STATE_LOADING);
185
186        // Start loading the rules.
187        mUiccCard.iccOpenLogicalChannel(AID,
188            obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
189    }
190
191    /**
192     * Returns the status of the carrier privileges for the input certificate and package name.
193     *
194     * @param signature The signature of the certificate.
195     * @param packageName name of the package.
196     * @return Access status.
197     */
198    public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
199        Rlog.d(LOG_TAG, "hasCarrierPrivileges: " + signature + " : " + packageName);
200        int state = mState.get();
201        if (state == STATE_LOADING) {
202            Rlog.d(LOG_TAG, "Rules not loaded.");
203            return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
204        } else if (state == STATE_ERROR) {
205            Rlog.d(LOG_TAG, "Error loading rules.");
206            return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES;
207        }
208
209        byte[] certHash = getCertHash(signature);
210        if (certHash == null) {
211          return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
212        }
213        Rlog.e(LOG_TAG, "Checking: " + IccUtils.bytesToHexString(certHash) + " : " + packageName);
214
215        for (AccessRule ar : mAccessRules) {
216            if (ar.matches(certHash, packageName)) {
217                Rlog.d(LOG_TAG, "Match found!");
218                return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
219            }
220        }
221
222        Rlog.d(LOG_TAG, "No matching rule found. Returning false.");
223        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
224    }
225
226    /**
227     * Returns the status of the carrier privileges for the input package name.
228     *
229     * @param packageManager PackageManager for getting signatures.
230     * @param packageName name of the package.
231     * @return Access status.
232     */
233    public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
234        try {
235            PackageInfo pInfo = packageManager.getPackageInfo(packageName,
236                PackageManager.GET_SIGNATURES);
237            Signature[] signatures = pInfo.signatures;
238            for (Signature sig : signatures) {
239                int accessStatus = getCarrierPrivilegeStatus(sig, pInfo.packageName);
240                if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
241                    return accessStatus;
242                }
243            }
244        } catch (PackageManager.NameNotFoundException ex) {
245            Rlog.e(LOG_TAG, "NameNotFoundException", ex);
246        }
247        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
248    }
249
250    /**
251     * Returns the status of the carrier privileges for the caller of the current transaction.
252     *
253     * @param packageManager PackageManager for getting signatures and package names.
254     * @return Access status.
255     */
256    public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
257        String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid());
258
259        for (String pkg : packages) {
260            int accessStatus = getCarrierPrivilegeStatus(packageManager, pkg);
261            if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
262                return accessStatus;
263            }
264        }
265        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
266    }
267
268    /**
269     * Returns the package name of the carrier app that should handle the input intent.
270     *
271     * @param packageManager PackageManager for getting receivers.
272     * @param intent Intent that will be broadcast.
273     * @return list of carrier app package names that can handle the intent.
274     *         Returns null if there is an error and an empty list if there
275     *         are no matching packages.
276     */
277    public List<String> getCarrierPackageNamesForBroadcastIntent(
278            PackageManager packageManager, Intent intent) {
279        List<String> packages = new ArrayList<String>();
280        List<ResolveInfo> receivers = packageManager.queryBroadcastReceivers(intent, 0);
281        for (ResolveInfo resolveInfo : receivers) {
282            if (resolveInfo.activityInfo == null) {
283                continue;
284            }
285            String packageName = resolveInfo.activityInfo.packageName;
286            int status = getCarrierPrivilegeStatus(packageManager, packageName);
287            if (status == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
288                packages.add(packageName);
289            } else if (status != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
290                // Any status apart from HAS_ACCESS and NO_ACCESS is considered an error.
291                return null;
292            }
293        }
294
295        return packages;
296    }
297
298    @Override
299    public void handleMessage(Message msg) {
300        AsyncResult ar;
301
302        switch (msg.what) {
303
304          case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
305              Rlog.d(LOG_TAG, "EVENT_OPEN_LOGICAL_CHANNEL_DONE");
306              ar = (AsyncResult) msg.obj;
307              if (ar.exception == null && ar.result != null) {
308                  int channelId = ((int[]) ar.result)[0];
309                  mUiccCard.iccTransmitApduLogicalChannel(channelId, CLA, COMMAND, P1, P2, P3, DATA,
310                      obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(channelId)));
311              } else {
312                  Rlog.e(LOG_TAG, "Error opening channel");
313                  mState.set(STATE_ERROR);
314              }
315              break;
316
317          case EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE:
318              Rlog.d(LOG_TAG, "EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
319              ar = (AsyncResult) msg.obj;
320              if (ar.exception == null && ar.result != null) {
321                  IccIoResult response = (IccIoResult) ar.result;
322                  if (response.payload != null && response.sw1 == 0x90 && response.sw2 == 0x00) {
323                      try {
324                          mAccessRules = parseRules(IccUtils.bytesToHexString(response.payload));
325                          mState.set(STATE_LOADED);
326                      } catch (IllegalArgumentException ex) {
327                          Rlog.e(LOG_TAG, "Error parsing rules: " + ex);
328                          mState.set(STATE_ERROR);
329                      }
330                   } else {
331                      Rlog.e(LOG_TAG, "Invalid response: payload=" + response.payload +
332                              " sw1=" + response.sw1 + " sw2=" + response.sw2);
333                   }
334              } else {
335                  Rlog.e(LOG_TAG, "Error reading value from SIM.");
336                  mState.set(STATE_ERROR);
337              }
338
339              int channelId = (Integer) ar.userObj;
340              mUiccCard.iccCloseLogicalChannel(channelId, obtainMessage(
341                      EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
342              break;
343
344          case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
345              Rlog.d(LOG_TAG, "EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
346              break;
347
348          default:
349              Rlog.e(LOG_TAG, "Unknown event " + msg.what);
350        }
351    }
352
353    /*
354     * Parses the rules from the input string.
355     */
356    private static List<AccessRule> parseRules(String rules) {
357        rules = rules.toUpperCase(Locale.US);
358        Rlog.d(LOG_TAG, "Got rules: " + rules);
359
360        TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO); //FF40
361        allRefArDo.parse(rules, true);
362
363        String arDos = allRefArDo.value;
364        List<AccessRule> accessRules = new ArrayList<AccessRule>();
365        while (!arDos.isEmpty()) {
366            TLV refArDo = new TLV(TAG_REF_AR_DO); //E2
367            arDos = refArDo.parse(arDos, false);
368            AccessRule accessRule = parseRefArdo(refArDo.value);
369            if (accessRule != null) {
370                accessRules.add(accessRule);
371            } else {
372              Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
373            }
374        }
375        return accessRules;
376    }
377
378    /*
379     * Parses a single rule.
380     */
381    private static AccessRule parseRefArdo(String rule) {
382        Rlog.d(LOG_TAG, "Got rule: " + rule);
383
384        String certificateHash = null;
385        String packageName = null;
386        String tmp = null;
387        long accessType = 0;
388
389        while (!rule.isEmpty()) {
390            if (rule.startsWith(TAG_REF_DO)) {
391                TLV refDo = new TLV(TAG_REF_DO); //E1
392                rule = refDo.parse(rule, false);
393
394                // Skip unrelated rules.
395                if (!refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
396                    return null;
397                }
398
399                TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
400                tmp = deviceDo.parse(refDo.value, false);
401                certificateHash = deviceDo.value;
402
403                if (!tmp.isEmpty()) {
404                  if (!tmp.startsWith(TAG_PKG_REF_DO)) {
405                      return null;
406                  }
407                  TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
408                  pkgDo.parse(tmp, true);
409                  packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
410                } else {
411                  packageName = null;
412                }
413            } else if (rule.startsWith(TAG_AR_DO)) {
414                TLV arDo = new TLV(TAG_AR_DO); //E3
415                rule = arDo.parse(rule, false);
416
417                // Skip unrelated rules.
418                if (!arDo.value.startsWith(TAG_PERM_AR_DO)) {
419                    return null;
420                }
421
422                TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
423                permDo.parse(arDo.value, true);
424                Rlog.e(LOG_TAG, permDo.value);
425            } else  {
426                // Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
427                throw new RuntimeException("Invalid Rule type");
428            }
429        }
430
431        Rlog.e(LOG_TAG, "Adding: " + certificateHash + " : " + packageName + " : " + accessType);
432
433        AccessRule accessRule = new AccessRule(IccUtils.hexStringToBytes(certificateHash),
434            packageName, accessType);
435        Rlog.e(LOG_TAG, "Parsed rule: " + accessRule);
436        return accessRule;
437    }
438
439    /*
440     * Converts a Signature into a Certificate hash usable for comparison.
441     */
442    private static byte[] getCertHash(Signature signature) {
443        // TODO: Is the following sufficient.
444        try {
445            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
446            X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
447                    new ByteArrayInputStream(signature.toByteArray()));
448
449            MessageDigest md = MessageDigest.getInstance("SHA");
450            return md.digest(cert.getEncoded());
451        } catch (CertificateException ex) {
452            Rlog.e(LOG_TAG, "CertificateException: " + ex);
453        } catch (NoSuchAlgorithmException ex) {
454            Rlog.e(LOG_TAG, "NoSuchAlgorithmException: " + ex);
455        }
456
457        Rlog.e(LOG_TAG, "Cannot compute cert hash");
458        return null;
459    }
460}
461