Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d33c268 authored by Geremy Condra's avatar Geremy Condra
Browse files

Adds support for the CertBlacklister.

The CertBlacklister is a mechanism for allowing is to use gservices
to blacklist certs by serial number or public key.

Change-Id: Ie4b0c966a8a43c9823fb550c0b1691204f133ae7
parent 9d7bbcb8
Loading
Loading
Loading
Loading
+142 −0
Original line number Diff line number Diff line
/*
 * 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)
        );
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -624,6 +624,13 @@ class ServerThread extends Thread {
                reportWtf("starting CommonTimeManagementService service", e);
            }

            try {
                Slog.i(TAG, "CertBlacklister");
                CertBlacklister blacklister = new CertBlacklister(context);
            } catch (Throwable e) {
                reportWtf("starting CertBlacklister", e);
            }
            
            if (context.getResources().getBoolean(
                    com.android.internal.R.bool.config_enableDreams)) {
                try {
+169 −0
Original line number Diff line number Diff line
/*
 * 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.Intent;
import android.test.AndroidTestCase;
import android.provider.Settings;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashSet;

import libcore.io.IoUtils;

/**
 * Tests for {@link com.android.server.CertBlacklister}
 */
public class CertBlacklisterTest extends AndroidTestCase {

    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_KEY = "pubkey_blacklist";
    public static final String SERIAL_KEY = "serial_blacklist";

    private void overrideSettings(String key, String value) throws Exception {
        Settings.Secure.putString(mContext.getContentResolver(), key, value);
        Thread.sleep(1000);
    }

    public void testClearBlacklistPubkey() throws Exception {
        // clear the gservices setting for a clean slate
        overrideSettings(PUBKEY_KEY, "");
        // read the contents of the pubkey blacklist
        String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
        // Verify that it's empty
        assertEquals("", blacklist);
    }

    public void testSetBlacklistPubkey() throws Exception {
        // build a new thing to blacklist
        String badPubkey = "7ccabd7db47e94a5759901b6a7dfd45d1c091ccc";
        // add the gservices override
        overrideSettings(PUBKEY_KEY, badPubkey);
        // check the contents again
        String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
        // make sure that we're equal to the string we sent out
        assertEquals(badPubkey, blacklist);
    }

    public void testChangeBlacklistPubkey() throws Exception {
        String badPubkey = "6ccabd7db47e94a5759901b6a7dfd45d1c091ccc";
        overrideSettings(PUBKEY_KEY, badPubkey);
        badPubkey = "6ccabd7db47e94a5759901b6a7dfd45d1c091cce";
        overrideSettings(PUBKEY_KEY, badPubkey);
        String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
        assertEquals(badPubkey, blacklist);
    }

    public void testMultiBlacklistPubkey() throws Exception {
        String badPubkey = "6ccabd7db47e94a5759901b6a7dfd45d1c091ccc,6ccabd7db47e94a5759901b6a7dfd45d1c091ccd";
        overrideSettings(PUBKEY_KEY, badPubkey);
        String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
        assertEquals(badPubkey, blacklist);
    }

    public void testInvalidMultiBlacklistPubkey() throws Exception {
        String badPubkey = "6ccabd7db47e94a5759901b6a7dfd45d1c091ccc,ZZZZZ,6ccabd7db47e94a5759901b6a7dfd45d1c091ccd";
        overrideSettings(PUBKEY_KEY, badPubkey);
        String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
        assertEquals(badPubkey, blacklist);
    }

    public void testInvalidCharsBlacklistPubkey() throws Exception {
        String badPubkey = "\n6ccabd7db47e94a5759901b6a7dfd45d1c091ccc,-ZZZZZ,+6ccabd7db47e94a5759901b6a7dfd45d1c091ccd";
        overrideSettings(PUBKEY_KEY, badPubkey);
        String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
        assertEquals(badPubkey, blacklist);
    }

    public void testLotsOfBlacklistedPubkeys() throws Exception {
        StringBuilder bl = new StringBuilder();
        for (int i=0; i < 1000; i++) {
            bl.append("6ccabd7db47e94a5759901b6a7dfd45d1c091ccc,");
        }
        overrideSettings(PUBKEY_KEY, bl.toString());
        String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
        assertEquals(bl.toString(), blacklist);
    }

    public void testClearBlacklistSerial() throws Exception {
        // clear the gservices setting for a clean slate
        overrideSettings(SERIAL_KEY, "");
        // read the contents of the pubkey blacklist
        String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
        // Verify that it's empty
        assertEquals("", blacklist);
    }

    public void testSetBlacklistSerial() throws Exception {
        // build a new thing to blacklist
        String badSerial = "22e514121e61c643b1e9b06bd4b9f7d0";
        // add the gservices override
        overrideSettings(SERIAL_KEY, badSerial);
        // check the contents again
        String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
        // make sure that we're equal to the string we sent out
        assertEquals(badSerial, blacklist);
    }

    public void testChangeBlacklistSerial() throws Exception {
        String badSerial = "22e514121e61c643b1e9b06bd4b9f7d0";
        overrideSettings(SERIAL_KEY, badSerial);
        badSerial = "22e514121e61c643b1e9b06bd4b9f7d1";
        overrideSettings(SERIAL_KEY, badSerial);
        String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
        assertEquals(badSerial, blacklist);
    }

    public void testMultiBlacklistSerial() throws Exception {
        String badSerial = "22e514121e61c643b1e9b06bd4b9f7d0,22e514121e61c643b1e9b06bd4b9f7d1";
        overrideSettings(SERIAL_KEY, badSerial);
        String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
        assertEquals(badSerial, blacklist);
    }

    public void testInvalidMultiBlacklistSerial() throws Exception {
        String badSerial = "22e514121e61c643b1e9b06bd4b9f7d0,ZZZZ,22e514121e61c643b1e9b06bd4b9f7d1";
        overrideSettings(SERIAL_KEY, badSerial);
        String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
        assertEquals(badSerial, blacklist);
    }

    public void testInvalidCharsBlacklistSerial() throws Exception {
        String badSerial = "\n22e514121e61c643b1e9b06bd4b9f7d0,-ZZZZ,+22e514121e61c643b1e9b06bd4b9f7d1";
        overrideSettings(SERIAL_KEY, badSerial);
        String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
        assertEquals(badSerial, blacklist);
    }

    public void testLotsOfBlacklistedSerials() throws Exception {
        StringBuilder bl = new StringBuilder();
        for (int i=0; i < 1000; i++) {
            bl.append("22e514121e61c643b1e9b06bd4b9f7d0,");
        }
        overrideSettings(SERIAL_KEY, bl.toString());
        String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
        assertEquals(bl.toString(), blacklist);
    }
}