blob: e180f5153ad66254a258aa7cfb2a8cf9720cb39c [file] [log] [blame]
/*
* Copyright (C) 2015 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.server.wifi;
import android.app.admin.IDevicePolicyManager;
import android.content.Context;
import android.os.Environment;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import com.android.server.net.DelayedDiskWrite;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Manager class for affiliated Wifi certificates.
*/
public class WifiCertManager {
private static final String TAG = "WifiCertManager";
private static final String SEP = "\n";
private final Context mContext;
private final Set<String> mAffiliatedUserOnlyCerts = new HashSet<String>();
private final String mConfigFile;
private static final String CONFIG_FILE =
Environment.getDataDirectory() + "/misc/wifi/affiliatedcerts.txt";
private final DelayedDiskWrite mWriter = new DelayedDiskWrite();
WifiCertManager(Context context) {
this(context, CONFIG_FILE);
}
WifiCertManager(Context context, String configFile) {
mContext = context;
mConfigFile = configFile;
final byte[] bytes = readConfigFile();
if (bytes == null) {
// Config file does not exist or empty.
return;
}
String[] keys = new String(bytes, StandardCharsets.UTF_8).split(SEP);
for (String key : keys) {
mAffiliatedUserOnlyCerts.add(key);
}
// Remove keys that no longer exist in KeyStore.
if (mAffiliatedUserOnlyCerts.retainAll(Arrays.asList(listClientCertsForAllUsers()))) {
writeConfig();
}
}
/** @param key Unprefixed cert key to hide from unaffiliated users. */
public void hideCertFromUnaffiliatedUsers(String key) {
if (mAffiliatedUserOnlyCerts.add(Credentials.USER_PRIVATE_KEY + key)) {
writeConfig();
}
}
/** @return Prefixed cert keys that are visible to the current user. */
public String[] listClientCertsForCurrentUser() {
HashSet<String> results = new HashSet<String>();
String[] keys = listClientCertsForAllUsers();
if (isAffiliatedUser()) {
return keys;
}
for (String key : keys) {
if (!mAffiliatedUserOnlyCerts.contains(key)) {
results.add(key);
}
}
return results.toArray(new String[results.size()]);
}
private void writeConfig() {
String[] values =
mAffiliatedUserOnlyCerts.toArray(new String[mAffiliatedUserOnlyCerts.size()]);
String value = TextUtils.join(SEP, values);
writeConfigFile(value.getBytes(StandardCharsets.UTF_8));
}
protected byte[] readConfigFile() {
byte[] bytes = null;
try {
final File file = new File(mConfigFile);
final long fileSize = file.exists() ? file.length() : 0;
if (fileSize == 0 || fileSize >= Integer.MAX_VALUE) {
// Config file is empty/corrupted/non-existing.
return bytes;
}
bytes = new byte[(int) file.length()];
final DataInputStream stream = new DataInputStream(new FileInputStream(file));
stream.readFully(bytes);
} catch (IOException e) {
Log.e(TAG, "readConfigFile: failed to read " + e, e);
}
return bytes;
}
protected void writeConfigFile(byte[] payload) {
final byte[] data = payload;
mWriter.write(mConfigFile, new DelayedDiskWrite.Writer() {
public void onWriteCalled(DataOutputStream out) throws IOException {
out.write(data, 0, data.length);
}
});
}
protected String[] listClientCertsForAllUsers() {
return KeyStore.getInstance().list(Credentials.USER_PRIVATE_KEY, UserHandle.myUserId());
}
protected boolean isAffiliatedUser() {
IDevicePolicyManager pm = IDevicePolicyManager.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
boolean result = false;
try {
result = pm.isAffiliatedUser();
} catch (Exception e) {
Log.e(TAG, "failed to check user affiliation", e);
}
return result;
}
}