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