1/*
2 * Copyright (C) 2012 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;
18
19import android.content.Context;
20import android.content.ContentResolver;
21import android.database.ContentObserver;
22import android.os.Binder;
23import android.os.FileUtils;
24import android.provider.Settings;
25import android.util.Slog;
26
27import java.io.File;
28import java.io.FileOutputStream;
29import java.io.IOException;
30
31import libcore.io.IoUtils;
32
33/**
34 * <p>CertBlacklister provides a simple mechanism for updating the platform blacklists for SSL
35 * certificate public keys and serial numbers.
36 */
37public class CertBlacklister extends Binder {
38
39    private static final String TAG = "CertBlacklister";
40
41    private static final String BLACKLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
42
43    public static final String PUBKEY_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt";
44    public static final String SERIAL_PATH = BLACKLIST_ROOT + "serial_blacklist.txt";
45
46    public static final String PUBKEY_BLACKLIST_KEY = "pubkey_blacklist";
47    public static final String SERIAL_BLACKLIST_KEY = "serial_blacklist";
48
49    private static class BlacklistObserver extends ContentObserver {
50
51        private final String mKey;
52        private final String mName;
53        private final String mPath;
54        private final File mTmpDir;
55        private final ContentResolver mContentResolver;
56
57        public BlacklistObserver(String key, String name, String path, ContentResolver cr) {
58            super(null);
59            mKey = key;
60            mName = name;
61            mPath = path;
62            mTmpDir = new File(mPath).getParentFile();
63            mContentResolver = cr;
64        }
65
66        @Override
67        public void onChange(boolean selfChange) {
68            super.onChange(selfChange);
69            writeBlacklist();
70        }
71
72        public String getValue() {
73            return Settings.Secure.getString(mContentResolver, mKey);
74        }
75
76        private void writeBlacklist() {
77            new Thread("BlacklistUpdater") {
78                public void run() {
79                    synchronized(mTmpDir) {
80                        String blacklist = getValue();
81                        if (blacklist != null) {
82                            Slog.i(TAG, "Certificate blacklist changed, updating...");
83                            FileOutputStream out = null;
84                            try {
85                                // create a temporary file
86                                File tmp = File.createTempFile("journal", "", mTmpDir);
87                                // mark it -rw-r--r--
88                                tmp.setReadable(true, false);
89                                // write to it
90                                out = new FileOutputStream(tmp);
91                                out.write(blacklist.getBytes());
92                                // sync to disk
93                                FileUtils.sync(out);
94                                // atomic rename
95                                tmp.renameTo(new File(mPath));
96                                Slog.i(TAG, "Certificate blacklist updated");
97                            } catch (IOException e) {
98                                Slog.e(TAG, "Failed to write blacklist", e);
99                            } finally {
100                                IoUtils.closeQuietly(out);
101                            }
102                        }
103                    }
104                }
105            }.start();
106        }
107    }
108
109    public CertBlacklister(Context context) {
110        registerObservers(context.getContentResolver());
111    }
112
113    private BlacklistObserver buildPubkeyObserver(ContentResolver cr) {
114        return new BlacklistObserver(PUBKEY_BLACKLIST_KEY,
115                    "pubkey",
116                    PUBKEY_PATH,
117                    cr);
118    }
119
120    private BlacklistObserver buildSerialObserver(ContentResolver cr) {
121        return new BlacklistObserver(SERIAL_BLACKLIST_KEY,
122                    "serial",
123                    SERIAL_PATH,
124                    cr);
125    }
126
127    private void registerObservers(ContentResolver cr) {
128        // set up the public key blacklist observer
129        cr.registerContentObserver(
130            Settings.Secure.getUriFor(PUBKEY_BLACKLIST_KEY),
131            true,
132            buildPubkeyObserver(cr)
133        );
134
135        // set up the serial number blacklist observer
136        cr.registerContentObserver(
137            Settings.Secure.getUriFor(SERIAL_BLACKLIST_KEY),
138            true,
139            buildSerialObserver(cr)
140        );
141    }
142}
143