Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f55a9e37 authored by Junda Liu's avatar Junda Liu Committed by Android (Google) Code Review
Browse files

Merge "Support file based carrier privilege rules." into nyc-dev

parents 1eddcf6a a4113cd0
Loading
Loading
Loading
Loading
+35 −13
Original line number Diff line number Diff line
@@ -57,14 +57,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 * The rules are read when the class is created, hence it should only be created
 * after the UICC can be read. And it should be deleted when a UICC is changed.
 *
 * The spec for the rules:
 *     GP Secure Element Access Control:
 *     http://www.globalplatform.org/specifications/review/GPD_SE_Access_Control_v1.0.20.pdf
 *     Extension spec:
 *     https://code.google.com/p/seek-for-android/
 *
 *
 * TODO: Notifications.
 * Document: https://source.android.com/devices/tech/config/uicc.html
 *
 * {@hide}
 */
@@ -114,6 +107,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
    private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
    private static final int EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE = 2;
    private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 3;
    private static final int EVENT_PKCS15_READ_DONE = 4;

    // State of the object.
    private static final int STATE_LOADING  = 0;
@@ -149,7 +143,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
    }

    // Used for parsing the data from the UICC.
    private static class TLV {
    public static class TLV {
        private static final int SINGLE_BYTE_MAX_LENGTH = 0x80;
        private String tag;
        // Length encoding is in GPC_Specification_2.2.1: 11.1.5 APDU Message and Data Length.
@@ -165,6 +159,11 @@ public class UiccCarrierPrivilegeRules extends Handler {
            this.tag = tag;
        }

        public String getValue() {
            if (value == null) return "";
            return value;
        }

        public String parseLength(String data) {
            int offset = tag.length();
            int firstByte = Integer.parseInt(data.substring(offset, offset + 2), 16);
@@ -210,6 +209,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
    }

    private UiccCard mUiccCard;  // Parent
    private UiccPkcs15 mUiccPkcs15; // ARF fallback
    private AtomicInteger mState;
    private List<AccessRule> mAccessRules;
    private String mRules;
@@ -237,6 +237,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
        mStatusMessage = "Not loaded.";
        mLoadedCallback = loadedCallback;
        mRules = "";
        mAccessRules = new ArrayList<AccessRule>();

        openChannel();
    }
@@ -280,13 +281,10 @@ public class UiccCarrierPrivilegeRules extends Handler {
     * @return Access status.
     */
    public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
        log("hasCarrierPrivileges: " + signature + " : " + packageName);
        int state = mState.get();
        if (state == STATE_LOADING) {
            log("Rules not loaded.");
            return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
        } else if (state == STATE_ERROR) {
            log("Error loading rules.");
            return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES;
        }

@@ -440,7 +438,11 @@ public class UiccCarrierPrivilegeRules extends Handler {
                      removeCallbacks(mRetryRunnable);
                      postDelayed(mRetryRunnable, RETRY_INTERVAL_MS);
                  } else {
                      updateState(STATE_ERROR, "Error opening channel: " + ar.exception);
                      // if rules cannot be read from ARA applet,
                      // fallback to PKCS15-based ARF.
                      log("No ARA, try ARF next.");
                      mUiccPkcs15 = new UiccPkcs15(mUiccCard,
                              obtainMessage(EVENT_PKCS15_READ_DONE));
                  }
              }
              break;
@@ -485,6 +487,20 @@ public class UiccCarrierPrivilegeRules extends Handler {
              log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
              break;

          case EVENT_PKCS15_READ_DONE:
              log("EVENT_PKCS15_READ_DONE");
              if (mUiccPkcs15 == null || mUiccPkcs15.getRules() == null) {
                  updateState(STATE_ERROR, "No ARA or ARF.");
              } else {
                  for (String cert : mUiccPkcs15.getRules()) {
                      AccessRule accessRule = new AccessRule(
                              IccUtils.hexStringToBytes(cert), "", 0x00);
                      mAccessRules.add(accessRule);
                  }
                  updateState(STATE_LOADED, "Success!");
              }
              break;

          default:
              Rlog.e(LOG_TAG, "Unknown event " + msg.what);
        }
@@ -638,6 +654,12 @@ public class UiccCarrierPrivilegeRules extends Handler {
        } else {
            pw.println(" mAccessRules: null");
        }
        if (mUiccPkcs15 != null) {
            pw.println(" mUiccPkcs15: " + mUiccPkcs15);
            mUiccPkcs15.dump(fd, pw, args);
        } else {
            pw.println(" mUiccPkcs15: null");
        }
        pw.flush();
    }

+340 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.internal.telephony.uicc;

import android.os.AsyncResult;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;

import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCarrierPrivilegeRules.TLV;

import java.io.ByteArrayInputStream;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.IllegalArgumentException;
import java.lang.IndexOutOfBoundsException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Class that reads PKCS15-based rules for carrier privileges.
 *
 * The spec for the rules:
 *     GP Secure Element Access Control:
 *     https://www.globalplatform.org/specificationsdevice.asp
 *
 * The UiccPkcs15 class handles overall flow of finding/selecting PKCS15 applet
 * and reading/parsing each file. Because PKCS15 can be selected in 2 different ways:
 * via logical channel or EF_DIR, PKCS15Selector is a handler to encapsulate the flow.
 * Similarly, FileHandler is used for selecting/reading each file, so common codes are
 * all in same place.
 *
 * {@hide}
 */
public class UiccPkcs15 extends Handler {
    private static final String LOG_TAG = "UiccPkcs15";
    private static final boolean DBG = true;

    // File handler for PKCS15 files, select file and read binary,
    // convert to String then send to callback message.
    private class FileHandler extends Handler {
        // EF path for PKCS15 root, eg. "3F007F50"
        // null if logical channel is used for PKCS15 access.
        private final String mPkcs15Path;
        // Message to send when file has been parsed.
        private Message mCallback;
        // File id to read data from, eg. "5031"
        private String mFileId;

        // async events for the sequence of select and read
        static protected final int EVENT_SELECT_FILE_DONE = 101;
        static protected final int EVENT_READ_BINARY_DONE = 102;

        // pkcs15Path is nullable when using logical channel
        public FileHandler(String pkcs15Path) {
            log("Creating FileHandler, pkcs15Path: " + pkcs15Path);
            mPkcs15Path = pkcs15Path;
        }

        public boolean loadFile(String fileId, Message callBack) {
            log("loadFile: " + fileId);
            if (fileId == null || callBack == null) return false;
            mFileId = fileId;
            mCallback = callBack;
            selectFile();
            return true;
        }

        private void selectFile() {
            if (mChannelId >= 0) {
                mUiccCard.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xA4, 0x00, 0x04, 0x02,
                        mFileId, obtainMessage(EVENT_SELECT_FILE_DONE));
            } else {
                log("EF based");
            }
        }

        private void readBinary() {
            if (mChannelId >=0 ) {
                mUiccCard.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00,
                        "", obtainMessage(EVENT_READ_BINARY_DONE));
            } else {
                log("EF based");
            }
        }

        @Override
        public void handleMessage(Message msg) {
            log("handleMessage: " + msg.what);
            AsyncResult ar = (AsyncResult) msg.obj;
            if (ar.exception != null || ar.result == null) {
                log("Error: " + ar.exception);
                AsyncResult.forMessage(mCallback, null, ar.exception);
                mCallback.sendToTarget();
                return;
            }

            switch (msg.what) {
                case EVENT_SELECT_FILE_DONE:
                    readBinary();
                    break;

                case EVENT_READ_BINARY_DONE:
                    IccIoResult response = (IccIoResult) ar.result;
                    String result = IccUtils.bytesToHexString(response.payload);
                    log("IccIoResult: " + response + " payload: " + result);
                    AsyncResult.forMessage(mCallback, result, (result == null) ?
                            new IccException("Error: null response for " + mFileId) : null);
                    mCallback.sendToTarget();
                    break;

                default:
                    log("Unknown event" + msg.what);
            }
        }
    }

    private class Pkcs15Selector extends Handler {
        private static final String PKCS15_AID = "A000000063504B43532D3135";
        private Message mCallback;
        private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 201;

        public Pkcs15Selector(Message callBack) {
            mCallback = callBack;
            mUiccCard.iccOpenLogicalChannel(PKCS15_AID,
                    obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE));
        }

        @Override
        public void handleMessage(Message msg) {
            log("handleMessage: " + msg.what);
            AsyncResult ar;

            switch (msg.what) {
              case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
                  ar = (AsyncResult) msg.obj;
                  if (ar.exception == null && ar.result != null) {
                      mChannelId = ((int[]) ar.result)[0];
                      log("mChannelId: " + mChannelId);
                      AsyncResult.forMessage(mCallback, null, null);
                  } else {
                      log("error: " + ar.exception);
                      AsyncResult.forMessage(mCallback, null, ar.exception);
                      // TODO: don't sendToTarget and read EF_DIR to find PKCS15
                  }
                  mCallback.sendToTarget();
                  break;

              default:
                  log("Unknown event" + msg.what);
            }
        }
    }

    private UiccCard mUiccCard;  // Parent
    private Message mLoadedCallback;
    private int mChannelId = -1; // Channel Id for communicating with UICC.
    private List<String> mRules = new ArrayList<String>();
    private Pkcs15Selector mPkcs15Selector;
    private FileHandler mFh;

    private static final int EVENT_SELECT_PKCS15_DONE = 1;
    private static final int EVENT_LOAD_ODF_DONE = 2;
    private static final int EVENT_LOAD_DODF_DONE = 3;
    private static final int EVENT_LOAD_ACMF_DONE = 4;
    private static final int EVENT_LOAD_ACRF_DONE = 5;
    private static final int EVENT_LOAD_ACCF_DONE = 6;
    private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 7;

    public UiccPkcs15(UiccCard uiccCard, Message loadedCallback) {
        log("Creating UiccPkcs15");
        mUiccCard = uiccCard;
        mLoadedCallback = loadedCallback;
        mPkcs15Selector = new Pkcs15Selector(obtainMessage(EVENT_SELECT_PKCS15_DONE));
    }

    @Override
    public void handleMessage(Message msg) {
        log("handleMessage: " + msg.what);
        AsyncResult ar = (AsyncResult) msg.obj;

        switch (msg.what) {
          case EVENT_SELECT_PKCS15_DONE:
              if (ar.exception == null) {
                  // ar.result is null if using logical channel,
                  // or string for pkcs15 path if using file access.
                  mFh = new FileHandler((String)ar.result);
                  if (!mFh.loadFile(ID_ACRF, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
                      cleanUp();
                  }
              } else {
                  log("select pkcs15 failed: " + ar.exception);
                  // select PKCS15 failed, notify uiccCarrierPrivilegeRules
                  mLoadedCallback.sendToTarget();
              }
              break;

          case EVENT_LOAD_ACRF_DONE:
              if (ar.exception == null && ar.result != null) {
                  String idAccf = parseAcrf((String)ar.result);
                  if (!mFh.loadFile(idAccf, obtainMessage(EVENT_LOAD_ACCF_DONE))) {
                      cleanUp();
                  }
              }
              break;

          case EVENT_LOAD_ACCF_DONE:
              if (ar.exception == null && ar.result != null) {
                  parseAccf((String)ar.result);
              }
              // We are done here, no more file to read
              cleanUp();
              break;

          case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
              break;

          default:
              Rlog.e(LOG_TAG, "Unknown event " + msg.what);
        }
    }

    private void cleanUp() {
        log("cleanUp");
        if (mChannelId >= 0) {
            mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
                    EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
            mChannelId = -1;
        }
        mLoadedCallback.sendToTarget();
    }

    // Constants defined in specs, needed for parsing
    private static final String CARRIER_RULE_AID = "FFFFFFFFFFFF"; // AID for carrier privilege rule
    private static final String ID_ACRF = "4300";
    private static final String TAG_ASN_SEQUENCE = "30";
    private static final String TAG_ASN_OCTET_STRING = "04";
    private static final String TAG_TARGET_AID = "A0";

    // parse ACRF file to get file id for ACCF file
    // data is hex string, return file id if parse success, null otherwise
    private String parseAcrf(String data) {
        String ret = null;

        String acRules = data;
        while (!acRules.isEmpty()) {
            TLV tlvRule = new TLV(TAG_ASN_SEQUENCE);
            try {
                acRules = tlvRule.parse(acRules, false);
                String ruleString = tlvRule.getValue();
                if (ruleString.startsWith(TAG_TARGET_AID)) {
                    // rule string consists of target AID + path, example:
                    // [A0] 08 [04] 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10
                    // bytes in [] are tags for the data
                    TLV tlvTarget = new TLV(TAG_TARGET_AID); // A0
                    TLV tlvAid = new TLV(TAG_ASN_OCTET_STRING); // 04
                    TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
                    TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING);  // 04

                    // populate tlvTarget.value with aid data,
                    // ruleString has remaining data for path
                    ruleString = tlvTarget.parse(ruleString, false);
                    // parse tlvTarget.value to get actual strings for AID.
                    // no other tags expected so shouldConsumeAll is true.
                    tlvAid.parse(tlvTarget.getValue(), true);

                    if (CARRIER_RULE_AID.equals(tlvAid.getValue())) {
                        tlvAsnPath.parse(ruleString, true);
                        tlvPath.parse(tlvAsnPath.getValue(), true);
                        ret = tlvPath.getValue();
                    }
                }
                continue; // skip current rule as it doesn't have expected TAG
            } catch (IllegalArgumentException|IndexOutOfBoundsException ex) {
                log("Error: " + ex);
                break; // Bad data, ignore all remaining ACRules
            }
        }
        return ret;
    }

    // parse ACCF and add to mRules
    private void parseAccf(String data) {
        String acCondition = data;
        while (!acCondition.isEmpty()) {
            TLV tlvCondition = new TLV(TAG_ASN_SEQUENCE);
            TLV tlvCert = new TLV(TAG_ASN_OCTET_STRING);
            try {
                acCondition = tlvCondition.parse(acCondition, false);
                tlvCert.parse(tlvCondition.getValue(), true);
                if (!tlvCert.getValue().isEmpty()) {
                    mRules.add(tlvCert.getValue());
                }
            } catch (IllegalArgumentException|IndexOutOfBoundsException ex) {
                log("Error: " + ex);
                break; // Bad data, ignore all remaining acCondition data
            }
        }
    }

    public List<String> getRules() {
        return mRules;
    }

    private static void log(String msg) {
        if (DBG) Rlog.d(LOG_TAG, msg);
    }

    /**
     * Dumps info to Dumpsys - useful for debugging.
     */
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        if (mRules != null) {
            pw.println(" mRules:");
            for (String cert : mRules) {
                pw.println("  " + cert);
            }
        }
    }
}