1 /*
2  * Copyright (C) 2021 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 
17 package com.android.server.permission.access
18 
19 import android.util.Log
20 import com.android.modules.utils.BinaryXmlPullParser
21 import com.android.modules.utils.BinaryXmlSerializer
22 import com.android.server.SystemConfig
23 import com.android.server.permission.access.appop.PackageAppOpPolicy
24 import com.android.server.permission.access.appop.UidAppOpPolicy
25 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
26 import com.android.server.permission.access.permission.UidPermissionPolicy
27 import com.android.server.permission.access.util.forEachTag
28 import com.android.server.permission.access.util.tag
29 import com.android.server.permission.access.util.tagName
30 import com.android.server.pm.permission.PermissionAllowlist
31 import com.android.server.pm.pkg.PackageState
32 
33 class AccessPolicy private constructor(
34     private val schemePolicies: IndexedMap<String, IndexedMap<String, SchemePolicy>>
35 ) {
36     constructor() : this(
37         IndexedMap<String, IndexedMap<String, SchemePolicy>>().apply {
38             fun addPolicy(policy: SchemePolicy) =
39                 getOrPut(policy.subjectScheme) { IndexedMap() }.put(policy.objectScheme, policy)
40             addPolicy(UidPermissionPolicy())
41             addPolicy(UidAppOpPolicy())
42             addPolicy(PackageAppOpPolicy())
43         }
44     )
45 
46     fun getSchemePolicy(subjectScheme: String, objectScheme: String): SchemePolicy =
47         checkNotNull(schemePolicies[subjectScheme]?.get(objectScheme)) {
48             "Scheme policy for $subjectScheme and $objectScheme does not exist"
49         }
50 
51     fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int =
52         with(getSchemePolicy(subject, `object`)){ getDecision(subject, `object`) }
53 
54     fun MutateStateScope.setDecision(subject: AccessUri, `object`: AccessUri, decision: Int) {
55         with(getSchemePolicy(subject, `object`)) { setDecision(subject, `object`, decision) }
56     }
57 
58     fun initialize(
59         state: AccessState,
60         userIds: IntSet,
61         packageStates: Map<String, PackageState>,
62         disabledSystemPackageStates: Map<String, PackageState>,
63         knownPackages: IntMap<Array<String>>,
64         isLeanback: Boolean,
65         configPermissions: Map<String, SystemConfig.PermissionEntry>,
66         privilegedPermissionAllowlistPackages: IndexedListSet<String>,
67         permissionAllowlist: PermissionAllowlist,
68         implicitToSourcePermissions: IndexedMap<String, IndexedListSet<String>>
69     ) {
70         state.systemState.apply {
71             this.userIds += userIds
72             this.packageStates = packageStates
73             this.disabledSystemPackageStates = disabledSystemPackageStates
74             packageStates.forEach { (_, packageState) ->
75                 appIds.getOrPut(packageState.appId) { IndexedListSet() }
76                     .add(packageState.packageName)
77             }
78             this.knownPackages = knownPackages
79             this.isLeanback = isLeanback
80             this.configPermissions = configPermissions
81             this.privilegedPermissionAllowlistPackages = privilegedPermissionAllowlistPackages
82             this.permissionAllowlist = permissionAllowlist
83             this.implicitToSourcePermissions = implicitToSourcePermissions
84         }
85         state.userStates.apply {
86             userIds.forEachIndexed { _, userId ->
87                 this[userId] = UserState()
88             }
89         }
90     }
91 
92     fun GetStateScope.onStateMutated() {
93         forEachSchemePolicy {
94             with(it) { onStateMutated() }
95         }
96     }
97 
98     fun MutateStateScope.onInitialized() {
99         forEachSchemePolicy {
100             with(it) { onInitialized() }
101         }
102     }
103 
104     fun MutateStateScope.onUserAdded(userId: Int) {
105         newState.systemState.userIds += userId
106         newState.userStates[userId] = UserState()
107         forEachSchemePolicy {
108             with(it) { onUserAdded(userId) }
109         }
110     }
111 
112     fun MutateStateScope.onUserRemoved(userId: Int) {
113         newState.systemState.userIds -= userId
114         newState.userStates -= userId
115         forEachSchemePolicy {
116             with(it) { onUserRemoved(userId) }
117         }
118     }
119 
120     fun MutateStateScope.onStorageVolumeMounted(
121         packageStates: Map<String, PackageState>,
122         disabledSystemPackageStates: Map<String, PackageState>,
123         knownPackages: IntMap<Array<String>>,
124         volumeUuid: String?,
125         isSystemUpdated: Boolean
126     ) {
127         val addedAppIds = IntSet()
128         newState.systemState.apply {
129             this.packageStates = packageStates
130             this.disabledSystemPackageStates = disabledSystemPackageStates
131             packageStates.forEach { (packageName, packageState) ->
132                 if (packageState.volumeUuid == volumeUuid) {
133                     val appId = packageState.appId
134                     appIds.getOrPut(appId) {
135                         addedAppIds += appId
136                         IndexedListSet()
137                     } += packageName
138                 }
139             }
140             this.knownPackages = knownPackages
141         }
142         addedAppIds.forEachIndexed { _, appId ->
143             forEachSchemePolicy {
144                 with(it) { onAppIdAdded(appId) }
145             }
146         }
147         forEachSchemePolicy {
148             with(it) { onStorageVolumeMounted(volumeUuid, isSystemUpdated) }
149         }
150     }
151 
152     fun MutateStateScope.onPackageAdded(
153         packageStates: Map<String, PackageState>,
154         disabledSystemPackageStates: Map<String, PackageState>,
155         knownPackages: IntMap<Array<String>>,
156         packageName: String
157     ) {
158         val packageState = packageStates[packageName]
159         // TODO(zhanghai): STOPSHIP: Remove check before feature enable.
160         checkNotNull(packageState) {
161             "Added package $packageName isn't found in packageStates in onPackageAdded()"
162         }
163         val appId = packageState.appId
164         var isAppIdAdded = false
165         newState.systemState.apply {
166             this.packageStates = packageStates
167             this.disabledSystemPackageStates = disabledSystemPackageStates
168             appIds.getOrPut(appId) {
169                 isAppIdAdded = true
170                 IndexedListSet()
171             } += packageName
172             this.knownPackages = knownPackages
173         }
174         if (isAppIdAdded) {
175             forEachSchemePolicy {
176                 with(it) { onAppIdAdded(appId) }
177             }
178         }
179         forEachSchemePolicy {
180             with(it) { onPackageAdded(packageState) }
181         }
182     }
183 
184     fun MutateStateScope.onPackageRemoved(
185         packageStates: Map<String, PackageState>,
186         disabledSystemPackageStates: Map<String, PackageState>,
187         knownPackages: IntMap<Array<String>>,
188         packageName: String,
189         appId: Int
190     ) {
191         // TODO(zhanghai): STOPSHIP: Remove check before feature enable.
192         check(packageName !in packageStates) {
193             "Removed package $packageName is still in packageStates in onPackageRemoved()"
194         }
195         var isAppIdRemoved = false
196         newState.systemState.apply {
197             this.packageStates = packageStates
198             this.disabledSystemPackageStates = disabledSystemPackageStates
199             appIds[appId]?.apply {
200                 this -= packageName
201                 if (isEmpty()) {
202                     appIds -= appId
203                     isAppIdRemoved = true
204                 }
205             }
206             this.knownPackages = knownPackages
207         }
208         forEachSchemePolicy {
209             with(it) { onPackageRemoved(packageName, appId) }
210         }
211         if (isAppIdRemoved) {
212             forEachSchemePolicy {
213                 with(it) { onAppIdRemoved(appId) }
214             }
215         }
216     }
217 
218     fun MutateStateScope.onPackageInstalled(
219         packageStates: Map<String, PackageState>,
220         disabledSystemPackageStates: Map<String, PackageState>,
221         knownPackages: IntMap<Array<String>>,
222         packageName: String,
223         userId: Int
224     ) {
225         newState.systemState.apply {
226             this.packageStates = packageStates
227             this.disabledSystemPackageStates = disabledSystemPackageStates
228             this.knownPackages = knownPackages
229         }
230         val packageState = packageStates[packageName]
231         // TODO(zhanghai): STOPSHIP: Remove check before feature enable.
232         checkNotNull(packageState) {
233             "Installed package $packageName isn't found in packageStates in onPackageInstalled()"
234         }
235         forEachSchemePolicy {
236             with(it) { onPackageInstalled(packageState, userId) }
237         }
238     }
239 
240     fun MutateStateScope.onPackageUninstalled(
241         packageStates: Map<String, PackageState>,
242         disabledSystemPackageStates: Map<String, PackageState>,
243         knownPackages: IntMap<Array<String>>,
244         packageName: String,
245         appId: Int,
246         userId: Int
247     ) {
248         newState.systemState.apply {
249             this.packageStates = packageStates
250             this.disabledSystemPackageStates = disabledSystemPackageStates
251             this.knownPackages = knownPackages
252         }
253         forEachSchemePolicy {
254             with(it) { onPackageUninstalled(packageName, appId, userId) }
255         }
256     }
257 
258     fun MutateStateScope.onSystemReady() {
259         newState.systemState.isSystemReady = true
260         forEachSchemePolicy {
261             with(it) { onSystemReady() }
262         }
263     }
264 
265     fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
266         forEachTag {
267             when (tagName) {
268                 TAG_ACCESS -> {
269                     forEachTag {
270                         forEachSchemePolicy {
271                             with(it) { parseSystemState(state) }
272                         }
273                     }
274                 }
275                 else -> Log.w(LOG_TAG, "Ignoring unknown tag $tagName when parsing system state")
276             }
277         }
278     }
279 
280     fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {
281         tag(TAG_ACCESS) {
282             forEachSchemePolicy {
283                 with(it) { serializeSystemState(state) }
284             }
285         }
286     }
287 
288     fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
289         forEachTag {
290             when (tagName) {
291                 TAG_ACCESS -> {
292                     forEachTag {
293                         forEachSchemePolicy {
294                             with(it) { parseUserState(state, userId) }
295                         }
296                     }
297                 }
298                 else -> {
299                     Log.w(
300                         LOG_TAG,
301                         "Ignoring unknown tag $tagName when parsing user state for user $userId"
302                     )
303                 }
304             }
305         }
306     }
307 
308     fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
309         tag(TAG_ACCESS) {
310             forEachSchemePolicy {
311                 with(it) { serializeUserState(state, userId) }
312             }
313         }
314     }
315 
316     private fun getSchemePolicy(subject: AccessUri, `object`: AccessUri): SchemePolicy =
317         getSchemePolicy(subject.scheme, `object`.scheme)
318 
319     private inline fun forEachSchemePolicy(action: (SchemePolicy) -> Unit) {
320         schemePolicies.forEachValueIndexed { _, objectSchemePolicies ->
321             objectSchemePolicies.forEachValueIndexed { _, schemePolicy ->
322                 action(schemePolicy)
323             }
324         }
325     }
326 
327     companion object {
328         private val LOG_TAG = AccessPolicy::class.java.simpleName
329 
330         private const val TAG_ACCESS = "access"
331     }
332 }
333 
334 abstract class SchemePolicy {
335     abstract val subjectScheme: String
336 
337     abstract val objectScheme: String
338 
339     abstract fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int
340 
341     abstract fun MutateStateScope.setDecision(
342         subject: AccessUri,
343         `object`: AccessUri,
344         decision: Int
345     )
346 
347     open fun GetStateScope.onStateMutated() {}
348 
349     open fun MutateStateScope.onInitialized() {}
350 
351     open fun MutateStateScope.onUserAdded(userId: Int) {}
352 
353     open fun MutateStateScope.onUserRemoved(userId: Int) {}
354 
355     open fun MutateStateScope.onAppIdAdded(appId: Int) {}
356 
357     open fun MutateStateScope.onAppIdRemoved(appId: Int) {}
358 
359     open fun MutateStateScope.onStorageVolumeMounted(
360         volumeUuid: String?,
361         isSystemUpdated: Boolean
362     ) {}
363 
364     open fun MutateStateScope.onPackageAdded(packageState: PackageState) {}
365 
366     open fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {}
367 
368     open fun MutateStateScope.onPackageInstalled(packageState: PackageState, userId: Int) {}
369 
370     open fun MutateStateScope.onPackageUninstalled(packageName: String, appId: Int, userId: Int) {}
371 
372     open fun MutateStateScope.onSystemReady() {}
373 
374     open fun BinaryXmlPullParser.parseSystemState(state: AccessState) {}
375 
376     open fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {}
377 
378     open fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {}
379 
380     open fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {}
381 }
382