1 /* 2 * Copyright (C) 2015 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.server.wifi; 18 19 import android.app.admin.IDevicePolicyManager; 20 import android.content.Context; 21 import android.os.Environment; 22 import android.os.ServiceManager; 23 import android.os.UserHandle; 24 import android.security.Credentials; 25 import android.security.KeyStore; 26 import android.text.TextUtils; 27 import android.util.Log; 28 29 import com.android.server.net.DelayedDiskWrite; 30 31 import java.io.DataInputStream; 32 import java.io.DataOutputStream; 33 import java.io.File; 34 import java.io.FileInputStream; 35 import java.io.IOException; 36 import java.nio.charset.StandardCharsets; 37 import java.util.Arrays; 38 import java.util.HashSet; 39 import java.util.Set; 40 41 /** 42 * Manager class for affiliated Wifi certificates. 43 */ 44 public class WifiCertManager { 45 private static final String TAG = "WifiCertManager"; 46 private static final String SEP = "\n"; 47 48 private final Context mContext; 49 private final Set<String> mAffiliatedUserOnlyCerts = new HashSet<String>(); 50 private final String mConfigFile; 51 52 private static final String CONFIG_FILE = 53 Environment.getDataDirectory() + "/misc/wifi/affiliatedcerts.txt"; 54 55 private final DelayedDiskWrite mWriter = new DelayedDiskWrite(); 56 57 WifiCertManager(Context context)58 WifiCertManager(Context context) { 59 this(context, CONFIG_FILE); 60 } 61 WifiCertManager(Context context, String configFile)62 WifiCertManager(Context context, String configFile) { 63 mContext = context; 64 mConfigFile = configFile; 65 final byte[] bytes = readConfigFile(); 66 if (bytes == null) { 67 // Config file does not exist or empty. 68 return; 69 } 70 71 String[] keys = new String(bytes, StandardCharsets.UTF_8).split(SEP); 72 for (String key : keys) { 73 mAffiliatedUserOnlyCerts.add(key); 74 } 75 76 // Remove keys that no longer exist in KeyStore. 77 if (mAffiliatedUserOnlyCerts.retainAll(Arrays.asList(listClientCertsForAllUsers()))) { 78 writeConfig(); 79 } 80 } 81 82 /** @param key Unprefixed cert key to hide from unaffiliated users. */ hideCertFromUnaffiliatedUsers(String key)83 public void hideCertFromUnaffiliatedUsers(String key) { 84 if (mAffiliatedUserOnlyCerts.add(Credentials.USER_PRIVATE_KEY + key)) { 85 writeConfig(); 86 } 87 } 88 89 /** @return Prefixed cert keys that are visible to the current user. */ listClientCertsForCurrentUser()90 public String[] listClientCertsForCurrentUser() { 91 HashSet<String> results = new HashSet<String>(); 92 93 String[] keys = listClientCertsForAllUsers(); 94 if (isAffiliatedUser()) { 95 return keys; 96 } 97 98 for (String key : keys) { 99 if (!mAffiliatedUserOnlyCerts.contains(key)) { 100 results.add(key); 101 } 102 } 103 return results.toArray(new String[results.size()]); 104 } 105 writeConfig()106 private void writeConfig() { 107 String[] values = 108 mAffiliatedUserOnlyCerts.toArray(new String[mAffiliatedUserOnlyCerts.size()]); 109 String value = TextUtils.join(SEP, values); 110 writeConfigFile(value.getBytes(StandardCharsets.UTF_8)); 111 } 112 readConfigFile()113 protected byte[] readConfigFile() { 114 byte[] bytes = null; 115 try { 116 final File file = new File(mConfigFile); 117 final long fileSize = file.exists() ? file.length() : 0; 118 if (fileSize == 0 || fileSize >= Integer.MAX_VALUE) { 119 // Config file is empty/corrupted/non-existing. 120 return bytes; 121 } 122 123 bytes = new byte[(int) file.length()]; 124 final DataInputStream stream = new DataInputStream(new FileInputStream(file)); 125 stream.readFully(bytes); 126 } catch (IOException e) { 127 Log.e(TAG, "readConfigFile: failed to read " + e, e); 128 } 129 return bytes; 130 } 131 writeConfigFile(byte[] payload)132 protected void writeConfigFile(byte[] payload) { 133 final byte[] data = payload; 134 mWriter.write(mConfigFile, new DelayedDiskWrite.Writer() { 135 public void onWriteCalled(DataOutputStream out) throws IOException { 136 out.write(data, 0, data.length); 137 } 138 }); 139 } 140 listClientCertsForAllUsers()141 protected String[] listClientCertsForAllUsers() { 142 return KeyStore.getInstance().list(Credentials.USER_PRIVATE_KEY, UserHandle.myUserId()); 143 } 144 isAffiliatedUser()145 protected boolean isAffiliatedUser() { 146 IDevicePolicyManager pm = IDevicePolicyManager.Stub.asInterface( 147 ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); 148 boolean result = false; 149 try { 150 result = pm.isAffiliatedUser(); 151 } catch (Exception e) { 152 Log.e(TAG, "failed to check user affiliation", e); 153 } 154 return result; 155 } 156 } 157