13e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom/*
23e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * Copyright (C) 2011 The Android Open Source Project
33e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom *
43e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * Licensed under the Apache License, Version 2.0 (the "License");
53e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * you may not use this file except in compliance with the License.
63e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * You may obtain a copy of the License at
73e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom *
83e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom *      http://www.apache.org/licenses/LICENSE-2.0
93e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom *
103e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * Unless required by applicable law or agreed to in writing, software
113e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * distributed under the License is distributed on an "AS IS" BASIS,
123e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * See the License for the specific language governing permissions and
143e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom * limitations under the License.
153e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom */
163e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
173e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrompackage com.android.keychain;
183e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
19fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanaimport android.app.IntentService;
20fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanaimport android.content.ContentValues;
213e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport android.content.Context;
223e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport android.content.Intent;
23fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanaimport android.content.pm.PackageManager;
24fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanaimport android.database.Cursor;
25fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanaimport android.database.DatabaseUtils;
26fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanaimport android.database.sqlite.SQLiteDatabase;
27fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanaimport android.database.sqlite.SQLiteOpenHelper;
286f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Rootimport android.os.Binder;
293e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport android.os.IBinder;
306f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Rootimport android.os.Process;
313e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport android.security.Credentials;
323e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport android.security.IKeyChainService;
3339e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurunimport android.security.KeyChain;
343e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport android.security.KeyStore;
353e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport android.util.Log;
363e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport java.io.ByteArrayInputStream;
37a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstromimport java.io.IOException;
383e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport java.security.cert.CertificateException;
393e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport java.security.cert.CertificateFactory;
403e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstromimport java.security.cert.X509Certificate;
41fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana
42a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstromimport org.apache.harmony.xnet.provider.jsse.TrustedCertificateStore;
433e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
44fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintanapublic class KeyChainService extends IntentService {
4539e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun
46fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String TAG = "KeyChain";
47fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana
48fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String DATABASE_NAME = "grants.db";
49fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final int DATABASE_VERSION = 1;
50fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String TABLE_GRANTS = "grants";
51fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String GRANTS_ALIAS = "alias";
52fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String GRANTS_GRANTEE_UID = "uid";
533e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
54fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    /** created in onCreate(), closed in onDestroy() */
55fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    public DatabaseHelper mDatabaseHelper;
563e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
57fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String SELECTION_COUNT_OF_MATCHING_GRANTS =
58fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            "SELECT COUNT(*) FROM " + TABLE_GRANTS
59fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                    + " WHERE " + GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
603e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
61fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String SELECT_GRANTS_BY_UID_AND_ALIAS =
62fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
63fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana
64fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private static final String SELECTION_GRANTS_BY_UID = GRANTS_GRANTEE_UID + "=?";
65fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana
66fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    public KeyChainService() {
67fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        super(KeyChainService.class.getSimpleName());
68fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
693e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
703e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom    @Override public void onCreate() {
713e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        super.onCreate();
72fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        mDatabaseHelper = new DatabaseHelper(this);
733e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom    }
743e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
75fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    @Override
76fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    public void onDestroy() {
77fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        super.onDestroy();
78fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        mDatabaseHelper.close();
79fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        mDatabaseHelper = null;
80fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
813e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
82fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
833e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        private final KeyStore mKeyStore = KeyStore.getInstance();
84a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom        private final TrustedCertificateStore mTrustedCertificateStore
85a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                = new TrustedCertificateStore();
863e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
876f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root        @Override
886f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root        public String requestPrivateKey(String alias) {
896f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            checkArgs(alias);
906f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root
916f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
926f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            final int uid = Binder.getCallingUid();
936f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            if (!mKeyStore.grant(keystoreAlias, uid)) {
946f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root                return null;
956f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            }
966f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root
976f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            final StringBuilder sb = new StringBuilder();
986f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            sb.append(Process.SYSTEM_UID);
996f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            sb.append('_');
1006f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            sb.append(keystoreAlias);
1016f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root
1026f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            return sb.toString();
1033e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
1043e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
105fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        @Override public byte[] getCertificate(String alias) {
1066f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            checkArgs(alias);
1076f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root            return mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
1083e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
1093e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
1106f1f03bcae70792bbd8bc0aecb90c7b9c43b76b5Kenny Root        private void checkArgs(String alias) {
1113e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom            if (alias == null) {
1123e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom                throw new NullPointerException("alias == null");
1133e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom            }
1143e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom            if (!isKeyStoreUnlocked()) {
115c8b0463643df1f1a4035d641e155f5e1cef8e1b0Nick Kralevich                throw new IllegalStateException("keystore is "
116c8b0463643df1f1a4035d641e155f5e1cef8e1b0Nick Kralevich                        + mKeyStore.state().toString());
1173e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom            }
118c8b0463643df1f1a4035d641e155f5e1cef8e1b0Nick Kralevich
119fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            final int callingUid = getCallingUid();
120fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            if (!hasGrantInternal(mDatabaseHelper.getReadableDatabase(), callingUid, alias)) {
121fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                throw new IllegalStateException("uid " + callingUid
122fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                        + " doesn't have permission to access the requested alias");
1233e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom            }
1243e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
1253e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
1265aeadd9be22ea51ea2d638f7090618448ecc8ac7Brian Carlstrom        private boolean isKeyStoreUnlocked() {
127e3b3390d3b1c96097c8e7cbd4c0eb51715677739Brian Carlstrom            return (mKeyStore.state() == KeyStore.State.UNLOCKED);
1283e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
129a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom
130a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom        @Override public void installCaCertificate(byte[] caCertificate) {
13143f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            checkCertInstallerOrSystemCaller();
132a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            try {
133a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                synchronized (mTrustedCertificateStore) {
134a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                    mTrustedCertificateStore.installCertificate(parseCertificate(caCertificate));
135a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                }
136a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            } catch (IOException e) {
137a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                throw new IllegalStateException(e);
138a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            } catch (CertificateException e) {
139a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                throw new IllegalStateException(e);
140a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            }
14139e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            broadcastStorageChange();
142a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom        }
1435aeadd9be22ea51ea2d638f7090618448ecc8ac7Brian Carlstrom
1445aeadd9be22ea51ea2d638f7090618448ecc8ac7Brian Carlstrom        private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
1455aeadd9be22ea51ea2d638f7090618448ecc8ac7Brian Carlstrom            CertificateFactory cf = CertificateFactory.getInstance("X.509");
1465aeadd9be22ea51ea2d638f7090618448ecc8ac7Brian Carlstrom            return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
1475aeadd9be22ea51ea2d638f7090618448ecc8ac7Brian Carlstrom        }
1485aeadd9be22ea51ea2d638f7090618448ecc8ac7Brian Carlstrom
149a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom        @Override public boolean reset() {
150a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            // only Settings should be able to reset
15143f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            checkSystemCaller();
152fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            removeAllGrants(mDatabaseHelper.getWritableDatabase());
153a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            boolean ok = true;
154a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            synchronized (mTrustedCertificateStore) {
155a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                // delete user-installed CA certs
156a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                for (String alias : mTrustedCertificateStore.aliases()) {
157a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                    if (TrustedCertificateStore.isUser(alias)) {
15843f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                        if (!deleteCertificateEntry(alias)) {
159a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                            ok = false;
160a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                        }
161a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                    }
162a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom                }
163a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom            }
16439e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            broadcastStorageChange();
16539e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            return ok;
166a58db5485e7b47880d9d565b036ae8b894ffdc48Brian Carlstrom        }
16743f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom
16843f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        @Override public boolean deleteCaCertificate(String alias) {
16943f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            // only Settings should be able to delete
17043f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            checkSystemCaller();
17139e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            boolean ok = true;
17239e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            synchronized (mTrustedCertificateStore) {
17339e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun                ok = deleteCertificateEntry(alias);
17439e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            }
17539e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            broadcastStorageChange();
17639e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            return ok;
17743f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        }
17843f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom
17943f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        private boolean deleteCertificateEntry(String alias) {
18043f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            try {
18143f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                mTrustedCertificateStore.deleteCertificateEntry(alias);
18243f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                return true;
18343f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            } catch (IOException e) {
18443f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                Log.w(TAG, "Problem removing CA certificate " + alias, e);
18543f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                return false;
18643f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            } catch (CertificateException e) {
18743f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                Log.w(TAG, "Problem removing CA certificate " + alias, e);
18843f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                return false;
18943f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            }
19043f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        }
19143f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom
19243f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        private void checkCertInstallerOrSystemCaller() {
19343f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            String actual = checkCaller("com.android.certinstaller");
19443f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            if (actual == null) {
19543f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                return;
19643f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            }
19743f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            checkSystemCaller();
19843f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        }
19943f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        private void checkSystemCaller() {
20043f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            String actual = checkCaller("android.uid.system:1000");
20143f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            if (actual != null) {
20243f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom                throw new IllegalStateException(actual);
20343f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            }
20443f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        }
20543f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        /**
20643f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom         * Returns null if actually caller is expected, otherwise return bad package to report
20743f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom         */
20843f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        private String checkCaller(String expectedPackage) {
20943f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            String actualPackage = getPackageManager().getNameForUid(getCallingUid());
21043f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom            return (!expectedPackage.equals(actualPackage)) ? actualPackage : null;
21143f5b77dbbff264f7f521dbf5361f07a5e253c70Brian Carlstrom        }
2123e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
213fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        @Override public boolean hasGrant(int uid, String alias) {
214fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            checkSystemCaller();
215fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            return hasGrantInternal(mDatabaseHelper.getReadableDatabase(), uid, alias);
2163e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
2173e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
218fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        @Override public void setGrant(int uid, String alias, boolean value) {
219fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            checkSystemCaller();
220fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            setGrantInternal(mDatabaseHelper.getWritableDatabase(), uid, alias, value);
22139e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun            broadcastStorageChange();
2223e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
223fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    };
2243e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
225fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private boolean hasGrantInternal(final SQLiteDatabase db, final int uid, final String alias) {
226fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        final long numMatches = DatabaseUtils.longForQuery(db, SELECTION_COUNT_OF_MATCHING_GRANTS,
227fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                new String[]{String.valueOf(uid), alias});
228fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        return numMatches > 0;
229fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
2303e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
231fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private void setGrantInternal(final SQLiteDatabase db,
232fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            final int uid, final String alias, final boolean value) {
233fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        if (value) {
234fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            if (!hasGrantInternal(db, uid, alias)) {
235fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                final ContentValues values = new ContentValues();
236fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                values.put(GRANTS_ALIAS, alias);
237fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                values.put(GRANTS_GRANTEE_UID, uid);
238fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                db.insert(TABLE_GRANTS, GRANTS_ALIAS, values);
239fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            }
240fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        } else {
241fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            db.delete(TABLE_GRANTS, SELECT_GRANTS_BY_UID_AND_ALIAS,
242fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                    new String[]{String.valueOf(uid), alias});
2433e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
244fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
2453e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
246fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private void removeAllGrants(final SQLiteDatabase db) {
247fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        db.delete(TABLE_GRANTS, null /* whereClause */, null /* whereArgs */);
248fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
2493e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
250fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private class DatabaseHelper extends SQLiteOpenHelper {
251fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        public DatabaseHelper(Context context) {
252fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            super(context, DATABASE_NAME, null /* CursorFactory */, DATABASE_VERSION);
2533e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
2543e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
255fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        @Override
256fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        public void onCreate(final SQLiteDatabase db) {
257fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
258fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                    + GRANTS_ALIAS + " STRING NOT NULL,  "
259fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
260fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                    + "UNIQUE (" + GRANTS_ALIAS + "," + GRANTS_GRANTEE_UID + "))");
2613e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
2623e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
263fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        @Override
264fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        public void onUpgrade(final SQLiteDatabase db, int oldVersion, final int newVersion) {
265fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
2663e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom
267fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            if (oldVersion == 1) {
268fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                // the first upgrade step goes here
269fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                oldVersion++;
2703e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom            }
2717037b73962c34e884467d3d4a871ecdab9797fc3Brian Carlstrom        }
272fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
273fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana
274fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    @Override public IBinder onBind(Intent intent) {
2757037b73962c34e884467d3d4a871ecdab9797fc3Brian Carlstrom        if (IKeyChainService.class.getName().equals(intent.getAction())) {
2763e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom            return mIKeyChainService;
2773e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        }
2783e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom        return null;
2793e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom    }
280fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana
281fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    @Override
282fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    protected void onHandleIntent(final Intent intent) {
283fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
284fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            purgeOldGrants();
285fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        }
286fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
287fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana
288fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    private void purgeOldGrants() {
289fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        final PackageManager packageManager = getPackageManager();
290fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
291fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        Cursor cursor = null;
292fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        db.beginTransaction();
293fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        try {
294fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            cursor = db.query(TABLE_GRANTS,
295fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                    new String[]{GRANTS_GRANTEE_UID}, null, null, GRANTS_GRANTEE_UID, null, null);
296fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            while (cursor.moveToNext()) {
297fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                final int uid = cursor.getInt(0);
298fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                final boolean packageExists = packageManager.getPackagesForUid(uid) != null;
299fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                if (packageExists) {
300fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                    continue;
301fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                }
302fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                Log.d(TAG, "deleting grants for UID " + uid
303fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                        + " because its package is no longer installed");
304fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_UID,
305fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                        new String[]{Integer.toString(uid)});
306fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            }
307fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            db.setTransactionSuccessful();
308fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        } finally {
309fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            if (cursor != null) {
310fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana                cursor.close();
311fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            }
312fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana            db.endTransaction();
313fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana        }
314fb2e18e112f9fb9f0620c0c0ff06377f52fe39a4Fred Quintana    }
31539e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun
31639e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun    private void broadcastStorageChange() {
31739e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun        Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
31839e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun        sendBroadcast(intent);
31939e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun    }
32039e36e58a7fd0a4520af5467719dac73afc205b4Selim Gurun
3213e6251dedc92654476c70bdc413f24a4b31ce6a4Brian Carlstrom}
322