summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/CertBlacklister.java
blob: 8b167d7b0c90d8ee810590ce14153aacc2dec516 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * Copyright (C) 2012 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;

import android.content.Context;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.os.Binder;
import android.os.FileUtils;
import android.provider.Settings;
import android.util.Slog;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import libcore.io.IoUtils;

/**
 * <p>CertBlacklister provides a simple mechanism for updating the platform blacklists for SSL
 * certificate public keys and serial numbers.
 */
public class CertBlacklister extends Binder {

    private static final String TAG = "CertBlacklister";

    private static final String BLACKLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";

    public static final String PUBKEY_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt";
    public static final String SERIAL_PATH = BLACKLIST_ROOT + "serial_blacklist.txt";

    public static final String PUBKEY_BLACKLIST_KEY = "pubkey_blacklist";
    public static final String SERIAL_BLACKLIST_KEY = "serial_blacklist";

    private static class BlacklistObserver extends ContentObserver {

        private final String mKey;
        private final String mName;
        private final String mPath;
        private final File mTmpDir;
        private final ContentResolver mContentResolver;

        public BlacklistObserver(String key, String name, String path, ContentResolver cr) {
            super(null);
            mKey = key;
            mName = name;
            mPath = path;
            mTmpDir = new File(mPath).getParentFile();
            mContentResolver = cr;
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            writeBlacklist();
        }

        public String getValue() {
            return Settings.Secure.getString(mContentResolver, mKey);
        }

        private void writeBlacklist() {
            new Thread("BlacklistUpdater") {
                public void run() {
                    synchronized(mTmpDir) {
                        String blacklist = getValue();
                        if (blacklist != null) {
                            Slog.i(TAG, "Certificate blacklist changed, updating...");
                            FileOutputStream out = null;
                            try {
                                // create a temporary file
                                File tmp = File.createTempFile("journal", "", mTmpDir);
                                // mark it -rw-r--r--
                                tmp.setReadable(true, false);
                                // write to it
                                out = new FileOutputStream(tmp);
                                out.write(blacklist.getBytes());
                                // sync to disk
                                FileUtils.sync(out);
                                // atomic rename
                                tmp.renameTo(new File(mPath));
                                Slog.i(TAG, "Certificate blacklist updated");
                            } catch (IOException e) {
                                Slog.e(TAG, "Failed to write blacklist", e);
                            } finally {
                                IoUtils.closeQuietly(out);
                            }
                        }
                    }
                }
            }.start();
        }
    }

    public CertBlacklister(Context context) {
        registerObservers(context.getContentResolver());
    }

    private BlacklistObserver buildPubkeyObserver(ContentResolver cr) {
        return new BlacklistObserver(PUBKEY_BLACKLIST_KEY,
                    "pubkey",
                    PUBKEY_PATH,
                    cr);
    }

    private BlacklistObserver buildSerialObserver(ContentResolver cr) {
        return new BlacklistObserver(SERIAL_BLACKLIST_KEY,
                    "serial",
                    SERIAL_PATH,
                    cr);
    }

    private void registerObservers(ContentResolver cr) {
        // set up the public key blacklist observer
        cr.registerContentObserver(
            Settings.Secure.getUriFor(PUBKEY_BLACKLIST_KEY),
            true,
            buildPubkeyObserver(cr)
        );

        // set up the serial number blacklist observer
        cr.registerContentObserver(
            Settings.Secure.getUriFor(SERIAL_BLACKLIST_KEY),
            true,
            buildSerialObserver(cr)
        );
    }
}