• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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