1 /* 2 * Copyright (C) 2009 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.certinstaller; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.net.Uri; 22 import android.os.Bundle; 23 import android.os.UserManager; 24 import android.preference.PreferenceActivity; 25 import android.provider.DocumentsContract; 26 import android.security.Credentials; 27 import android.security.KeyChain; 28 import android.util.Log; 29 import android.widget.Toast; 30 31 import libcore.io.IoUtils; 32 import libcore.io.Streams; 33 34 import java.io.IOException; 35 import java.io.InputStream; 36 37 /** 38 * The main class for installing certificates to the system keystore. It reacts 39 * to the public {@link Credentials#INSTALL_ACTION} intent. 40 */ 41 public class CertInstallerMain extends PreferenceActivity { 42 private static final String TAG = "CertInstaller"; 43 44 private static final int REQUEST_INSTALL = 1; 45 private static final int REQUEST_OPEN_DOCUMENT = 2; 46 47 private static final String INSTALL_CERT_AS_USER_CLASS = ".InstallCertAsUser"; 48 49 private static final String[] ACCEPT_MIME_TYPES = { 50 "application/x-pkcs12", 51 "application/x-x509-ca-cert", 52 "application/x-x509-user-cert", 53 "application/x-x509-server-cert", 54 "application/x-pem-file", 55 "application/pkix-cert" 56 }; 57 58 @Override onCreate(Bundle savedInstanceState)59 protected void onCreate(Bundle savedInstanceState) { 60 super.onCreate(savedInstanceState); 61 62 setResult(RESULT_CANCELED); 63 64 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 65 if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS)) { 66 finish(); 67 return; 68 } 69 70 final Intent intent = getIntent(); 71 final String action = intent.getAction(); 72 73 if (Credentials.INSTALL_ACTION.equals(action) 74 || Credentials.INSTALL_AS_USER_ACTION.equals(action)) { 75 Bundle bundle = intent.getExtras(); 76 77 /* 78 * There is a special INSTALL_AS_USER action that this activity is 79 * aliased to, but you have to have a permission to call it. If the 80 * caller got here any other way, remove the extra that we allow in 81 * that INSTALL_AS_USER path. 82 */ 83 String calledClass = intent.getComponent().getClassName(); 84 String installAsUserClassName = getPackageName() + INSTALL_CERT_AS_USER_CLASS; 85 if (bundle != null && !installAsUserClassName.equals(calledClass)) { 86 bundle.remove(Credentials.EXTRA_INSTALL_AS_UID); 87 } 88 89 // If bundle is empty of any actual credentials, ask user to open. 90 // Otherwise, pass extras to CertInstaller to install those credentials. 91 // Either way, we use KeyChain.EXTRA_NAME as the default name if available. 92 if (bundle == null 93 || bundle.isEmpty() 94 || (bundle.size() == 1 95 && (bundle.containsKey(KeyChain.EXTRA_NAME) 96 || bundle.containsKey(Credentials.EXTRA_INSTALL_AS_UID)))) { 97 final Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 98 openIntent.setType("*/*"); 99 openIntent.putExtra(Intent.EXTRA_MIME_TYPES, ACCEPT_MIME_TYPES); 100 openIntent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true); 101 startActivityForResult(openIntent, REQUEST_OPEN_DOCUMENT); 102 } else { 103 final Intent installIntent = new Intent(this, CertInstaller.class); 104 installIntent.putExtras(intent); 105 startActivityForResult(installIntent, REQUEST_INSTALL); 106 } 107 } else if (Intent.ACTION_VIEW.equals(action)) { 108 startInstallActivity(intent.getType(), intent.getData()); 109 } 110 } 111 startInstallActivity(String mimeType, Uri uri)112 private void startInstallActivity(String mimeType, Uri uri) { 113 if (mimeType == null) { 114 mimeType = getContentResolver().getType(uri); 115 } 116 117 InputStream in = null; 118 try { 119 in = getContentResolver().openInputStream(uri); 120 121 final byte[] raw = Streams.readFully(in); 122 startInstallActivity(mimeType, raw); 123 124 } catch (IOException e) { 125 Log.e(TAG, "Failed to read certificate: " + e); 126 Toast.makeText(this, R.string.cert_read_error, Toast.LENGTH_LONG).show(); 127 } finally { 128 IoUtils.closeQuietly(in); 129 } 130 } 131 startInstallActivity(String mimeType, byte[] value)132 private void startInstallActivity(String mimeType, byte[] value) { 133 Intent intent = new Intent(this, CertInstaller.class); 134 if ("application/x-pkcs12".equals(mimeType)) { 135 intent.putExtra(KeyChain.EXTRA_PKCS12, value); 136 } else if ("application/x-x509-ca-cert".equals(mimeType) 137 || "application/x-x509-user-cert".equals(mimeType) 138 || "application/x-x509-server-cert".equals(mimeType) 139 || "application/x-pem-file".equals(mimeType) 140 || "application/pkix-cert".equals(mimeType)) { 141 intent.putExtra(KeyChain.EXTRA_CERTIFICATE, value); 142 } else { 143 throw new IllegalArgumentException("Unknown MIME type: " + mimeType); 144 } 145 146 startActivityForResult(intent, REQUEST_INSTALL); 147 } 148 149 @Override onActivityResult(int requestCode, int resultCode, Intent data)150 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 151 if (requestCode == REQUEST_OPEN_DOCUMENT) { 152 if (resultCode == RESULT_OK) { 153 startInstallActivity(null, data.getData()); 154 } else { 155 finish(); 156 } 157 } else if (requestCode == REQUEST_INSTALL) { 158 setResult(resultCode); 159 finish(); 160 } else { 161 Log.w(TAG, "unknown request code: " + requestCode); 162 } 163 } 164 } 165