blob: 478337809ea1aeb6f50f9cbff869471defe4ef95 [file] [log] [blame]
Eran Messeri5844b632017-11-03 10:28:34 +00001/*
2 * Copyright (C) 2017 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.keychain.internal;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.database.Cursor;
23import android.database.DatabaseUtils;
24import android.database.sqlite.SQLiteDatabase;
25import android.database.sqlite.SQLiteOpenHelper;
26import android.util.Log;
27
28public class GrantsDatabase {
29 private static final String TAG = "KeyChain";
30
31 private static final String DATABASE_NAME = "grants.db";
32 private static final int DATABASE_VERSION = 1;
33 private static final String TABLE_GRANTS = "grants";
34 private static final String GRANTS_ALIAS = "alias";
35 private static final String GRANTS_GRANTEE_UID = "uid";
36
37 private static final String SELECTION_COUNT_OF_MATCHING_GRANTS =
38 "SELECT COUNT(*) FROM "
39 + TABLE_GRANTS
40 + " WHERE "
41 + GRANTS_GRANTEE_UID
42 + "=? AND "
43 + GRANTS_ALIAS
44 + "=?";
45
46 private static final String SELECT_GRANTS_BY_UID_AND_ALIAS =
47 GRANTS_GRANTEE_UID + "=? AND " + GRANTS_ALIAS + "=?";
48
49 private static final String SELECTION_GRANTS_BY_UID = GRANTS_GRANTEE_UID + "=?";
50
51 private static final String SELECTION_GRANTS_BY_ALIAS = GRANTS_ALIAS + "=?";
52
Eran Messeri133b2722017-11-07 17:51:50 +000053 private static final String TABLE_SELECTABLE = "userselectable";
54 private static final String SELECTABLE_IS_SELECTABLE = "is_selectable";
55 private static final String COUNT_SELECTABILITY_FOR_ALIAS =
56 "SELECT COUNT(*) FROM " + TABLE_SELECTABLE + " WHERE " + GRANTS_ALIAS + "=?";
57
Eran Messeri5844b632017-11-03 10:28:34 +000058 public DatabaseHelper mDatabaseHelper;
59
60 private class DatabaseHelper extends SQLiteOpenHelper {
61 public DatabaseHelper(Context context) {
62 super(context, DATABASE_NAME, null /* CursorFactory */, DATABASE_VERSION);
63 }
64
65 @Override
66 public void onCreate(final SQLiteDatabase db) {
67 db.execSQL(
68 "CREATE TABLE "
69 + TABLE_GRANTS
70 + " ( "
71 + GRANTS_ALIAS
72 + " STRING NOT NULL, "
73 + GRANTS_GRANTEE_UID
74 + " INTEGER NOT NULL, "
75 + "UNIQUE ("
76 + GRANTS_ALIAS
77 + ","
78 + GRANTS_GRANTEE_UID
79 + "))");
Eran Messeri133b2722017-11-07 17:51:50 +000080
81 db.execSQL(
82 "CREATE TABLE "
83 + TABLE_SELECTABLE
84 + " ( "
85 + GRANTS_ALIAS
86 + " STRING NOT NULL, "
87 + SELECTABLE_IS_SELECTABLE
88 + " STRING NOT NULL, "
89 + "UNIQUE ("
90 + GRANTS_ALIAS
91 + "))");
Eran Messeri5844b632017-11-03 10:28:34 +000092 }
93
94 @Override
95 public void onUpgrade(final SQLiteDatabase db, int oldVersion, final int newVersion) {
96 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
97
98 if (oldVersion == 1) {
99 // the first upgrade step goes here
100 oldVersion++;
101 }
102 }
103 }
104
105 public GrantsDatabase(Context context) {
106 mDatabaseHelper = new DatabaseHelper(context);
107 }
108
109 public void destroy() {
110 mDatabaseHelper.close();
111 mDatabaseHelper = null;
112 }
113
114 boolean hasGrantInternal(final SQLiteDatabase db, final int uid, final String alias) {
115 final long numMatches =
116 DatabaseUtils.longForQuery(
117 db,
118 SELECTION_COUNT_OF_MATCHING_GRANTS,
119 new String[] {String.valueOf(uid), alias});
120 return numMatches > 0;
121 }
122
123 public boolean hasGrant(final int uid, final String alias) {
124 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
125 return hasGrantInternal(db, uid, alias);
126 }
127
128 public void setGrant(final int uid, final String alias, final boolean value) {
129 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
130 if (value) {
131 if (!hasGrantInternal(db, uid, alias)) {
132 final ContentValues values = new ContentValues();
133 values.put(GRANTS_ALIAS, alias);
134 values.put(GRANTS_GRANTEE_UID, uid);
135 db.insert(TABLE_GRANTS, GRANTS_ALIAS, values);
136 }
137 } else {
138 db.delete(
139 TABLE_GRANTS,
140 SELECT_GRANTS_BY_UID_AND_ALIAS,
141 new String[] {String.valueOf(uid), alias});
142 }
143 }
144
145 public void removeGrantsForAlias(String alias) {
146 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
147 db.delete(TABLE_GRANTS, SELECTION_GRANTS_BY_ALIAS, new String[] {alias});
148 }
149
150 public void removeAllGrants() {
151 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
152 db.delete(TABLE_GRANTS, null /* whereClause */, null /* whereArgs */);
153 }
154
155 public void purgeOldGrants(PackageManager pm) {
156 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
157 Cursor cursor = null;
158 db.beginTransaction();
159 try {
160 cursor =
161 db.query(
162 TABLE_GRANTS,
163 new String[] {GRANTS_GRANTEE_UID},
164 null,
165 null,
166 GRANTS_GRANTEE_UID,
167 null,
168 null);
169 while (cursor.moveToNext()) {
170 final int uid = cursor.getInt(0);
171 final boolean packageExists = pm.getPackagesForUid(uid) != null;
172 if (packageExists) {
173 continue;
174 }
175 Log.d(TAG, String.format(
176 "deleting grants for UID %d because its package is no longer installed",
177 uid));
178 db.delete(
179 TABLE_GRANTS,
180 SELECTION_GRANTS_BY_UID,
181 new String[] {Integer.toString(uid)});
182 }
183 db.setTransactionSuccessful();
184 } finally {
185 if (cursor != null) {
186 cursor.close();
187 }
188 db.endTransaction();
189 }
190 }
Eran Messeri133b2722017-11-07 17:51:50 +0000191
192 public void setIsUserSelectable(final String alias, final boolean userSelectable) {
193 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
194 final ContentValues values = new ContentValues();
195 values.put(GRANTS_ALIAS, alias);
196 values.put(SELECTABLE_IS_SELECTABLE, Boolean.toString(userSelectable));
197
198 db.replace(TABLE_SELECTABLE, null, values);
199 }
200
201 public boolean isUserSelectable(final String alias) {
202 final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
203 try (Cursor res =
204 db.query(
205 TABLE_SELECTABLE,
206 new String[] {SELECTABLE_IS_SELECTABLE},
207 SELECTION_GRANTS_BY_ALIAS,
208 new String[] {alias},
209 null /* group by */,
210 null /* having */,
211 null /* order by */)) {
212 if (res == null || !res.moveToNext()) {
213 return false;
214 }
215
216 boolean isSelectable = Boolean.parseBoolean(res.getString(0));
217 if (!res.isAfterLast()) {
218 // BUG! Should not have more than one result for any given alias.
219 Log.w(TAG, String.format("Have more than one result for alias %s", alias));
220 }
221 return isSelectable;
222 }
223 }
Eran Messeri5844b632017-11-03 10:28:34 +0000224}