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