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