13d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra/* 23d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * Copyright (C) 2012 The Android Open Source Project 33d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * 43d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * Licensed under the Apache License, Version 2.0 (the "License"); 53d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * you may not use this file except in compliance with the License. 63d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * You may obtain a copy of the License at 73d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * 83d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * http://www.apache.org/licenses/LICENSE-2.0 93d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * 103d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * Unless required by applicable law or agreed to in writing, software 113d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * distributed under the License is distributed on an "AS IS" BASIS, 123d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * See the License for the specific language governing permissions and 143d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * limitations under the License. 153d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra */ 163d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 173d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condrapackage com.android.server; 183d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 193d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport android.content.Context; 203d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport android.content.ContentResolver; 213d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport android.database.ContentObserver; 223d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport android.os.Binder; 233d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport android.os.FileUtils; 243d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport android.provider.Settings; 253d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport android.util.Slog; 263d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 273d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport java.io.File; 283d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport java.io.FileOutputStream; 293d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport java.io.IOException; 303d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 313d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condraimport libcore.io.IoUtils; 323d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 333d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra/** 343d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * <p>CertBlacklister provides a simple mechanism for updating the platform blacklists for SSL 353d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra * certificate public keys and serial numbers. 363d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra */ 373d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condrapublic class CertBlacklister extends Binder { 383d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 393d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private static final String TAG = "CertBlacklister"; 403d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 413d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private static final String BLACKLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/"; 423d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 433d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public static final String PUBKEY_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt"; 443d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public static final String SERIAL_PATH = BLACKLIST_ROOT + "serial_blacklist.txt"; 453d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 463d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public static final String PUBKEY_BLACKLIST_KEY = "pubkey_blacklist"; 473d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public static final String SERIAL_BLACKLIST_KEY = "serial_blacklist"; 483d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 493d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private static class BlacklistObserver extends ContentObserver { 503d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 513d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private final String mKey; 523d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private final String mName; 533d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private final String mPath; 543d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private final File mTmpDir; 553d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private final ContentResolver mContentResolver; 563d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 573d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public BlacklistObserver(String key, String name, String path, ContentResolver cr) { 583d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra super(null); 593d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra mKey = key; 603d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra mName = name; 613d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra mPath = path; 623d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra mTmpDir = new File(mPath).getParentFile(); 633d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra mContentResolver = cr; 643d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 653d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 663d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra @Override 673d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public void onChange(boolean selfChange) { 683d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra super.onChange(selfChange); 693d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra writeBlacklist(); 703d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 713d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 723d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public String getValue() { 733d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra return Settings.Secure.getString(mContentResolver, mKey); 743d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 753d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 763d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private void writeBlacklist() { 773d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra new Thread("BlacklistUpdater") { 783d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public void run() { 793d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra synchronized(mTmpDir) { 803d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra String blacklist = getValue(); 813d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra if (blacklist != null) { 823d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra Slog.i(TAG, "Certificate blacklist changed, updating..."); 833d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra FileOutputStream out = null; 843d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra try { 853d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra // create a temporary file 863d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra File tmp = File.createTempFile("journal", "", mTmpDir); 873d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra // mark it -rw-r--r-- 883d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra tmp.setReadable(true, false); 893d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra // write to it 903d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra out = new FileOutputStream(tmp); 913d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra out.write(blacklist.getBytes()); 923d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra // sync to disk 933d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra FileUtils.sync(out); 943d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra // atomic rename 953d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra tmp.renameTo(new File(mPath)); 963d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra Slog.i(TAG, "Certificate blacklist updated"); 973d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } catch (IOException e) { 983d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra Slog.e(TAG, "Failed to write blacklist", e); 993d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } finally { 1003d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra IoUtils.closeQuietly(out); 1013d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1023d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1033d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1043d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1053d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra }.start(); 1063d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1073d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1083d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 1093d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra public CertBlacklister(Context context) { 1103d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra registerObservers(context.getContentResolver()); 1113d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1123d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 1133d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private BlacklistObserver buildPubkeyObserver(ContentResolver cr) { 1143d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra return new BlacklistObserver(PUBKEY_BLACKLIST_KEY, 1153d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra "pubkey", 1163d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra PUBKEY_PATH, 1173d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra cr); 1183d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1193d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 1203d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private BlacklistObserver buildSerialObserver(ContentResolver cr) { 1213d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra return new BlacklistObserver(SERIAL_BLACKLIST_KEY, 1223d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra "serial", 1233d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra SERIAL_PATH, 1243d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra cr); 1253d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1263d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 1273d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra private void registerObservers(ContentResolver cr) { 1283d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra // set up the public key blacklist observer 1293d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra cr.registerContentObserver( 1303d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra Settings.Secure.getUriFor(PUBKEY_BLACKLIST_KEY), 1313d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra true, 1323d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra buildPubkeyObserver(cr) 1333d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra ); 1343d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra 1353d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra // set up the serial number blacklist observer 1363d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra cr.registerContentObserver( 1373d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra Settings.Secure.getUriFor(SERIAL_BLACKLIST_KEY), 1383d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra true, 1393d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra buildSerialObserver(cr) 1403d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra ); 1413d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra } 1423d33c268cc7f08ec3d2ec1aa535fa86dec458b2eGeremy Condra} 143