UiccCarrierPrivilegeRules.java revision c9394399180abbc32d04f6a3652ce22d5931e0b8
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.concurrent.atomic.AtomicInteger;
44import java.util.Arrays;
45import java.util.List;
46import java.util.Locale;
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 *     TODO: Put link here.
56 *
57 *
58 * TODO: Notifications.
59 *
60 * {@hide}
61 */
62public class UiccCarrierPrivilegeRules extends Handler {
63    private static final String LOG_TAG = "UiccCarrierPrivilegeRules";
64
65    // TODO: These are temporary values. Put real ones here.
66    private static final String AID = "A0000000015141434D";
67    private static final int CLA = 0x80;
68    private static final int COMMAND = 0xCA;
69    private static final int P1 = 0xFF;
70    private static final int P2 = 0x40;
71    private static final int P3 = 0x00;
72    private static final String DATA = "";
73
74    // Values from the data standard.
75    private static final String TAG_ALL_REF_AR_DO = "FF40";
76    private static final String TAG_REF_AR_DO = "E2";
77    private static final String TAG_REF_DO = "E1";
78    private static final String TAG_AR_DO = "E3";
79    private static final String TAG_DEVICE_APP_ID_REF_DO = "C1";
80    private static final String TAG_PERM_AR_DO = "DB";
81    private static final String TAG_PKG_REF_DO = "CA";
82
83    private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
84    private static final int EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE = 2;
85    private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 3;
86
87    // State of the object.
88    private static final int STATE_LOADING  = 0;
89    private static final int STATE_LOADED   = 1;
90    private static final int STATE_ERROR    = 2;
91
92    // Describes a single rule.
93    private static class AccessRule {
94        public byte[] certificateHash;
95        public String packageName;
96        public long accessType;   // This bit is not currently used, but reserved for future use.
97
98        AccessRule(byte[] certificateHash, String packageName, long accessType) {
99            this.certificateHash = certificateHash;
100            this.packageName = packageName;
101            this.accessType = accessType;
102        }
103
104        boolean matches(byte[] certHash, String packageName) {
105          return certHash != null && Arrays.equals(this.certificateHash, certHash) &&
106                (this.packageName == null || this.packageName.equals(packageName));
107        }
108
109        @Override
110        public String toString() {
111            return "cert: " + certificateHash + " pkg: " + packageName +
112                " access: " + accessType;
113        }
114    }
115
116    // Used for parsing the data from the UICC.
117    private static class TLV {
118        private String tag;
119        private Integer length;
120        private String value;
121
122        public TLV(String tag) {
123            this.tag = tag;
124        }
125
126        public String parse(String data, boolean shouldConsumeAll) {
127            if (!data.startsWith(tag)) {
128                throw new IllegalArgumentException("Tags don't match.");
129            }
130            int index = tag.length();
131            if (index + 2 > data.length()) {
132                throw new IllegalArgumentException("No length.");
133            }
134            length = new Integer(2 * Integer.parseInt(
135                    data.substring(index, index + 2), 16));
136            index += 2;
137
138            int remainingLength = data.length() - (index + length);
139            if (remainingLength < 0) {
140                throw new IllegalArgumentException("Not enough data.");
141            }
142            if (shouldConsumeAll && (remainingLength != 0)) {
143                throw new IllegalArgumentException("Did not consume all.");
144            }
145            value = data.substring(index, index + length);
146
147            Rlog.e(LOG_TAG, "Got TLV: " + tag + "," + length + "," + value);
148
149            return data.substring(index + length);
150        }
151    }
152
153    private UiccCard mUiccCard;  // Parent
154    private AtomicInteger mState;
155    private List<AccessRule> mAccessRules;
156
157    public UiccCarrierPrivilegeRules(UiccCard uiccCard) {
158        Rlog.d(LOG_TAG, "Creating UiccCarrierPrivilegeRules");
159        mUiccCard = uiccCard;
160        mState = new AtomicInteger(STATE_LOADING);
161
162        // Start loading the rules.
163        mUiccCard.iccOpenLogicalChannel(AID,
164            obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
165    }
166
167    /**
168     * Returns the status of the carrier privileges for the input certificate and package name.
169     *
170     * @param signature The signature of the certificate.
171     * @param packageName name of the package.
172     * @return Access status.
173     */
174    public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
175        Rlog.d(LOG_TAG, "hasCarrierPrivileges: " + signature + " : " + packageName);
176        int state = mState.get();
177        if (state == STATE_LOADING) {
178            Rlog.d(LOG_TAG, "Rules not loaded.");
179            return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
180        } else if (state == STATE_ERROR) {
181            Rlog.d(LOG_TAG, "Error loading rules.");
182            return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES;
183        }
184
185        byte[] certHash = getCertHash(signature);
186        if (certHash == null) {
187          return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
188        }
189        Rlog.e(LOG_TAG, "Checking: " + IccUtils.bytesToHexString(certHash) + " : " + packageName);
190
191        for (AccessRule ar : mAccessRules) {
192            if (ar.matches(certHash, packageName)) {
193                return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
194            }
195        }
196
197        Rlog.d(LOG_TAG, "No matching rule found. Returning false.");
198        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
199    }
200
201    /**
202     * Returns the status of the carrier privileges for the input package name.
203     *
204     * @param packageManager PackageManager for getting signatures.
205     * @param packageName name of the package.
206     * @return Access status.
207     */
208    public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
209        try {
210            PackageInfo pInfo = packageManager.getPackageInfo(packageName,
211                PackageManager.GET_SIGNATURES);
212            Signature[] signatures = pInfo.signatures;
213            for (Signature sig : signatures) {
214                int accessStatus = getCarrierPrivilegeStatus(sig, pInfo.packageName);
215                if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
216                    return accessStatus;
217                }
218            }
219        } catch (PackageManager.NameNotFoundException ex) {
220            Rlog.e(LOG_TAG, "NameNotFoundException", ex);
221        }
222        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
223    }
224
225    /**
226     * Returns the status of the carrier privileges for the caller of the current transaction.
227     *
228     * @param packageManager PackageManager for getting signatures and package names.
229     * @return Access status.
230     */
231    public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
232        String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid());
233
234        for (String pkg : packages) {
235            int accessStatus = getCarrierPrivilegeStatus(packageManager, pkg);
236            if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
237                return accessStatus;
238            }
239        }
240        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
241    }
242
243    /**
244     * Given an intent, returns the package name of the carrier app that should handle the intent.
245     *
246     * @param packageManager PackageManager for getting receivers.
247     * @param intent Intent that will be broadcast.
248     * @return packageName name of package that should handle the intent. Will return an empty
249     *         string if no matching package is found.
250     */
251    public String getCarrierPackageNameForBroadcastIntent(
252            PackageManager packageManager, Intent intent) {
253        List<ResolveInfo> receivers = packageManager.queryBroadcastReceivers(intent, 0);
254        for (ResolveInfo resolveInfo : receivers) {
255            if (resolveInfo.activityInfo == null) {
256                continue;
257            }
258            String packageName = resolveInfo.activityInfo.packageName;
259            if (getCarrierPrivilegeStatus(packageManager, packageName) ==
260                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
261                return packageName;
262            }
263        }
264
265        // Return an empty package name so that no packages match.
266        // TODO: This creates an unnecessary ordered broadcast that can be avoided.
267        return "";
268    }
269
270    @Override
271    public void handleMessage(Message msg) {
272        AsyncResult ar;
273
274        switch (msg.what) {
275
276          case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
277              Rlog.d(LOG_TAG, "EVENT_OPEN_LOGICAL_CHANNEL_DONE");
278              ar = (AsyncResult) msg.obj;
279              if (ar.exception == null && ar.result != null) {
280                  int channelId = ((int[]) ar.result)[0];
281                  mUiccCard.iccTransmitApduLogicalChannel(channelId, CLA, COMMAND, P1, P2, P3, DATA,
282                      obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(channelId)));
283              } else {
284                  Rlog.e(LOG_TAG, "Error opening channel");
285                  mState.set(STATE_ERROR);
286              }
287              break;
288
289          case EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE:
290              Rlog.d(LOG_TAG, "EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
291              ar = (AsyncResult) msg.obj;
292              if (ar.exception == null && ar.result != null) {
293                  IccIoResult response = (IccIoResult) ar.result;
294                  if (response.payload != null && response.sw1 == 0x90 && response.sw2 == 0x00) {
295                      try {
296                          mAccessRules = parseRules(IccUtils.bytesToHexString(response.payload));
297                          mState.set(STATE_LOADED);
298                      } catch (IllegalArgumentException ex) {
299                          Rlog.e(LOG_TAG, "Error parsing rules: " + ex);
300                          mState.set(STATE_ERROR);
301                      }
302                   } else {
303                      Rlog.e(LOG_TAG, "Invalid response: payload=" + response.payload +
304                              " sw1=" + response.sw1 + " sw2=" + response.sw2);
305                   }
306              } else {
307                  Rlog.e(LOG_TAG, "Error reading value from SIM.");
308                  mState.set(STATE_ERROR);
309              }
310
311              int channelId = (Integer) ar.userObj;
312              mUiccCard.iccCloseLogicalChannel(channelId, obtainMessage(
313                      EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
314              break;
315
316          case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
317              Rlog.d(LOG_TAG, "EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
318              break;
319
320          default:
321              Rlog.e(LOG_TAG, "Unknown event " + msg.what);
322        }
323    }
324
325    /*
326     * Parses the rules from the input string.
327     */
328    private static List<AccessRule> parseRules(String rules) {
329        rules = rules.toUpperCase(Locale.US);
330        Rlog.d(LOG_TAG, "Got rules: " + rules);
331
332        /*
333         * Rules format.
334         *   ALL_REF_AR_DO = TAG_ALL_REF_AR_DO + len + [REF_AR_DO]xn
335         *   REF_AR_DO = TAG_REF_AR_DO + len + REF-DO | AR-DO
336         */
337        TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO);
338        allRefArDo.parse(rules, true);
339
340        String arDos = allRefArDo.value;
341        List<AccessRule> accessRules = new ArrayList<AccessRule>();
342        while (!arDos.isEmpty()) {
343            TLV refArDo = new TLV(TAG_REF_AR_DO);
344            arDos = refArDo.parse(arDos, false);
345            accessRules.add(parseRefArdo(refArDo.value));
346        }
347        return accessRules;
348    }
349
350    /*
351     * Parses a single rule.
352     */
353    private static AccessRule parseRefArdo(String rule) {
354        Rlog.d(LOG_TAG, "Got rule: " + rule);
355
356        /*
357         *   REF_AR_DO = TAG_REF_AR_DO + len + REF-DO | AR-DO
358         *   REF_DO = TAG_REF_DO + len + DEVICE_APP_ID_REF_DO | PKG_REF_DO
359         *   AR_DO = TAG_AR_DO + len + PERM_AR_DO
360         *   DEVICE_APP_ID_REF_DO = TAG_DEVICE_APP_ID_REF_DO + 20 | 0 + 20 byte hash (padded with FF)
361         *   PKG_REF_DO = TAG_PKG_REF_DO + 20 + 20 byte hash of package name (padded with FF)
362         *   PERM_AR_DO = TAG_PERM_AR_DO + 8 + 8 bytes
363         */
364
365        String certificateHash = null;
366        String packageName = null;
367        long accessType = 0;
368
369        while (!rule.isEmpty()) {
370            if (rule.startsWith(TAG_REF_DO)) {
371                TLV refDo = new TLV(TAG_REF_DO);
372                rule = refDo.parse(rule, false);
373
374                if (refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
375                    TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO);
376                    deviceDo.parse(refDo.value, true);
377                    certificateHash = deviceDo.value;
378                } else if (refDo.value.startsWith(TAG_PKG_REF_DO)) {
379                    TLV pkgDo = new TLV(TAG_PKG_REF_DO);
380                    pkgDo.parse(refDo.value, true);
381                    packageName = pkgDo.value;
382                } else {
383                    throw new IllegalArgumentException(
384                        "Invalid REF_DO value tag: " + refDo.value);
385                }
386            } else if (rule.startsWith(TAG_AR_DO)) {
387                TLV arDo = new TLV(TAG_AR_DO);
388                rule = arDo.parse(rule, false);
389
390                TLV permDo = new TLV(TAG_PERM_AR_DO);
391                permDo.parse(arDo.value, true);
392                Rlog.e(LOG_TAG, permDo.value);
393            } else  {
394                // TODO: Mayabe just throw away rules that are not parseable.
395                throw new RuntimeException("Invalid Rule type");
396            }
397        }
398
399        Rlog.e(LOG_TAG, "Adding: " + certificateHash + " : " + packageName + " : " + accessType);
400
401        AccessRule accessRule = new AccessRule(IccUtils.hexStringToBytes(certificateHash),
402            packageName, accessType);
403        Rlog.e(LOG_TAG, "Parsed rule: " + accessRule);
404        return accessRule;
405    }
406
407    /*
408     * Converts a Signature into a Certificate hash usable for comparison.
409     */
410    private static byte[] getCertHash(Signature signature) {
411        // TODO: Is the following sufficient.
412        try {
413            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
414            X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
415                    new ByteArrayInputStream(signature.toByteArray()));
416
417            MessageDigest md = MessageDigest.getInstance("SHA");
418            return md.digest(cert.getEncoded());
419        } catch (CertificateException ex) {
420            Rlog.e(LOG_TAG, "CertificateException: " + ex);
421        } catch (NoSuchAlgorithmException ex) {
422            Rlog.e(LOG_TAG, "NoSuchAlgorithmException: " + ex);
423        }
424
425        Rlog.e(LOG_TAG, "Cannot compute cert hash");
426        return null;
427    }
428}
429