1 /*
2  * Copyright (C) 2015 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.pm;
18 
19 import android.os.UserHandle;
20 import android.util.ArrayMap;
21 import android.util.ArraySet;
22 
23 import android.util.SparseArray;
24 import com.android.internal.util.ArrayUtils;
25 
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Set;
31 
32 /**
33  * This class encapsulates the permissions for a package or a shared user.
34  * <p>
35  * There are two types of permissions: install (granted at installation)
36  * and runtime (granted at runtime). Install permissions are granted to
37  * all device users while runtime permissions are granted explicitly to
38  * specific users.
39  * </p>
40  * <p>
41  * The permissions are kept on a per device user basis. For example, an
42  * application may have some runtime permissions granted under the device
43  * owner but not granted under the secondary user.
44  * <p>
45  * This class is also responsible for keeping track of the Linux gids per
46  * user for a package or a shared user. The gids are computed as a set of
47  * the gids for all granted permissions' gids on a per user basis.
48  * </p>
49  */
50 public final class PermissionsState {
51 
52     /** The permission operation failed. */
53     public static final int PERMISSION_OPERATION_FAILURE = -1;
54 
55     /** The permission operation succeeded and no gids changed. */
56     public static final int PERMISSION_OPERATION_SUCCESS = 0;
57 
58     /** The permission operation succeeded and gids changed. */
59     public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
60 
61     private static final int[] NO_GIDS = {};
62 
63     private ArrayMap<String, PermissionData> mPermissions;
64 
65     private int[] mGlobalGids = NO_GIDS;
66 
PermissionsState()67     public PermissionsState() {
68         /* do nothing */
69     }
70 
PermissionsState(PermissionsState prototype)71     public PermissionsState(PermissionsState prototype) {
72         copyFrom(prototype);
73     }
74 
75     /**
76      * Sets the global gids, applicable to all users.
77      *
78      * @param globalGids The global gids.
79      */
setGlobalGids(int[] globalGids)80     public void setGlobalGids(int[] globalGids) {
81         if (!ArrayUtils.isEmpty(globalGids)) {
82             mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
83         }
84     }
85 
86     /**
87      * Initialized this instance from another one.
88      *
89      * @param other The other instance.
90      */
copyFrom(PermissionsState other)91     public void copyFrom(PermissionsState other) {
92         if (other == this) {
93             return;
94         }
95         if (mPermissions != null) {
96             if (other.mPermissions == null) {
97                 mPermissions = null;
98             } else {
99                 mPermissions.clear();
100             }
101         }
102         if (other.mPermissions != null) {
103             if (mPermissions == null) {
104                 mPermissions = new ArrayMap<>();
105             }
106             final int permissionCount = other.mPermissions.size();
107             for (int i = 0; i < permissionCount; i++) {
108                 String name = other.mPermissions.keyAt(i);
109                 PermissionData permissionData = other.mPermissions.valueAt(i);
110                 mPermissions.put(name, new PermissionData(permissionData));
111             }
112         }
113 
114         mGlobalGids = NO_GIDS;
115         if (other.mGlobalGids != NO_GIDS) {
116             mGlobalGids = Arrays.copyOf(other.mGlobalGids,
117                     other.mGlobalGids.length);
118         }
119     }
120 
121     /**
122      * Grant an install permission.
123      *
124      * @param permission The permission to grant.
125      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
126      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
127      *     #PERMISSION_OPERATION_FAILURE}.
128      */
grantInstallPermission(BasePermission permission)129     public int grantInstallPermission(BasePermission permission) {
130         return grantPermission(permission, UserHandle.USER_ALL);
131     }
132 
133     /**
134      * Revoke an install permission.
135      *
136      * @param permission The permission to revoke.
137      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
138      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
139      *     #PERMISSION_OPERATION_FAILURE}.
140      */
revokeInstallPermission(BasePermission permission)141     public int revokeInstallPermission(BasePermission permission) {
142         return revokePermission(permission, UserHandle.USER_ALL);
143     }
144 
145     /**
146      * Grant a runtime permission for a given device user.
147      *
148      * @param permission The permission to grant.
149      * @param userId The device user id.
150      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
151      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
152      *     #PERMISSION_OPERATION_FAILURE}.
153      */
grantRuntimePermission(BasePermission permission, int userId)154     public int grantRuntimePermission(BasePermission permission, int userId) {
155         enforceValidUserId(userId);
156         if (userId == UserHandle.USER_ALL) {
157             return PERMISSION_OPERATION_FAILURE;
158         }
159         return grantPermission(permission, userId);
160     }
161 
162     /**
163      *  Revoke a runtime permission for a given device user.
164      *
165      * @param permission The permission to revoke.
166      * @param userId The device user id.
167      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
168      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
169      *     #PERMISSION_OPERATION_FAILURE}.
170      */
revokeRuntimePermission(BasePermission permission, int userId)171     public int revokeRuntimePermission(BasePermission permission, int userId) {
172         enforceValidUserId(userId);
173         if (userId == UserHandle.USER_ALL) {
174             return PERMISSION_OPERATION_FAILURE;
175         }
176         return revokePermission(permission, userId);
177     }
178 
179     /**
180      * Gets whether this state has a given runtime permission for a
181      * given device user id.
182      *
183      * @param name The permission name.
184      * @param userId The device user id.
185      * @return Whether this state has the permission.
186      */
hasRuntimePermission(String name, int userId)187     public boolean hasRuntimePermission(String name, int userId) {
188         enforceValidUserId(userId);
189         return !hasInstallPermission(name) && hasPermission(name, userId);
190     }
191 
192     /**
193      * Gets whether this state has a given install permission.
194      *
195      * @param name The permission name.
196      * @return Whether this state has the permission.
197      */
hasInstallPermission(String name)198     public boolean hasInstallPermission(String name) {
199         return hasPermission(name, UserHandle.USER_ALL);
200     }
201 
202     /**
203      * Gets whether the state has a given permission for the specified
204      * user, regardless if this is an install or a runtime permission.
205      *
206      * @param name The permission name.
207      * @param userId The device user id.
208      * @return Whether the user has the permission.
209      */
hasPermission(String name, int userId)210     public boolean hasPermission(String name, int userId) {
211         enforceValidUserId(userId);
212 
213         if (mPermissions == null) {
214             return false;
215         }
216 
217         PermissionData permissionData = mPermissions.get(name);
218         return permissionData != null && permissionData.isGranted(userId);
219     }
220 
221     /**
222      * Returns whether the state has any known request for the given permission name,
223      * whether or not it has been granted.
224      */
hasRequestedPermission(ArraySet<String> names)225     public boolean hasRequestedPermission(ArraySet<String> names) {
226         if (mPermissions == null) {
227             return false;
228         }
229         for (int i=names.size()-1; i>=0; i--) {
230             if (mPermissions.get(names.valueAt(i)) != null) {
231                 return true;
232             }
233         }
234         return false;
235     }
236 
237     /**
238      * Gets all permissions for a given device user id regardless if they
239      * are install time or runtime permissions.
240      *
241      * @param userId The device user id.
242      * @return The permissions or an empty set.
243      */
getPermissions(int userId)244     public Set<String> getPermissions(int userId) {
245         enforceValidUserId(userId);
246 
247         if (mPermissions == null) {
248             return Collections.emptySet();
249         }
250 
251         Set<String> permissions = new ArraySet<>();
252 
253         final int permissionCount = mPermissions.size();
254         for (int i = 0; i < permissionCount; i++) {
255             String permission = mPermissions.keyAt(i);
256 
257             if (hasInstallPermission(permission)) {
258                 permissions.add(permission);
259             }
260 
261             if (userId != UserHandle.USER_ALL) {
262                 if (hasRuntimePermission(permission, userId)) {
263                     permissions.add(permission);
264                 }
265             }
266         }
267 
268         return permissions;
269     }
270 
271     /**
272      * Gets the state for an install permission or null if no such.
273      *
274      * @param name The permission name.
275      * @return The permission state.
276      */
getInstallPermissionState(String name)277     public PermissionState getInstallPermissionState(String name) {
278         return getPermissionState(name, UserHandle.USER_ALL);
279     }
280 
281     /**
282      * Gets the state for a runtime permission or null if no such.
283      *
284      * @param name The permission name.
285      * @param userId The device user id.
286      * @return The permission state.
287      */
getRuntimePermissionState(String name, int userId)288     public PermissionState getRuntimePermissionState(String name, int userId) {
289         enforceValidUserId(userId);
290         return getPermissionState(name, userId);
291     }
292 
293     /**
294      * Gets all install permission states.
295      *
296      * @return The permission states or an empty set.
297      */
getInstallPermissionStates()298     public List<PermissionState> getInstallPermissionStates() {
299         return getPermissionStatesInternal(UserHandle.USER_ALL);
300     }
301 
302     /**
303      * Gets all runtime permission states.
304      *
305      * @return The permission states or an empty set.
306      */
getRuntimePermissionStates(int userId)307     public List<PermissionState> getRuntimePermissionStates(int userId) {
308         enforceValidUserId(userId);
309         return getPermissionStatesInternal(userId);
310     }
311 
312     /**
313      * Gets the flags for a permission regardless if it is install or
314      * runtime permission.
315      *
316      * @param name The permission name.
317      * @return The permission state or null if no such.
318      */
getPermissionFlags(String name, int userId)319     public int getPermissionFlags(String name, int userId) {
320         PermissionState installPermState = getInstallPermissionState(name);
321         if (installPermState != null) {
322             return installPermState.getFlags();
323         }
324         PermissionState runtimePermState = getRuntimePermissionState(name, userId);
325         if (runtimePermState != null) {
326             return runtimePermState.getFlags();
327         }
328         return 0;
329     }
330 
331     /**
332      * Update the flags associated with a given permission.
333      * @param permission The permission whose flags to update.
334      * @param userId The user for which to update.
335      * @param flagMask Mask for which flags to change.
336      * @param flagValues New values for the mask flags.
337      * @return Whether the permission flags changed.
338      */
updatePermissionFlags(BasePermission permission, int userId, int flagMask, int flagValues)339     public boolean updatePermissionFlags(BasePermission permission, int userId,
340             int flagMask, int flagValues) {
341         enforceValidUserId(userId);
342 
343         final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
344 
345         if (mPermissions == null) {
346             if (!mayChangeFlags) {
347                 return false;
348             }
349             ensurePermissionData(permission);
350         }
351 
352         PermissionData permissionData = mPermissions.get(permission.name);
353         if (permissionData == null) {
354             if (!mayChangeFlags) {
355                 return false;
356             }
357             permissionData = ensurePermissionData(permission);
358         }
359 
360         return permissionData.updateFlags(userId, flagMask, flagValues);
361     }
362 
updatePermissionFlagsForAllPermissions( int userId, int flagMask, int flagValues)363     public boolean updatePermissionFlagsForAllPermissions(
364             int userId, int flagMask, int flagValues) {
365         enforceValidUserId(userId);
366 
367         if (mPermissions == null) {
368             return false;
369         }
370         boolean changed = false;
371         final int permissionCount = mPermissions.size();
372         for (int i = 0; i < permissionCount; i++) {
373             PermissionData permissionData = mPermissions.valueAt(i);
374             changed |= permissionData.updateFlags(userId, flagMask, flagValues);
375         }
376         return changed;
377     }
378 
379     /**
380      * Compute the Linux gids for a given device user from the permissions
381      * granted to this user. Note that these are computed to avoid additional
382      * state as they are rarely accessed.
383      *
384      * @param userId The device user id.
385      * @return The gids for the device user.
386      */
computeGids(int userId)387     public int[] computeGids(int userId) {
388         enforceValidUserId(userId);
389 
390         int[] gids = mGlobalGids;
391 
392         if (mPermissions != null) {
393             final int permissionCount = mPermissions.size();
394             for (int i = 0; i < permissionCount; i++) {
395                 String permission = mPermissions.keyAt(i);
396                 if (!hasPermission(permission, userId)) {
397                     continue;
398                 }
399                 PermissionData permissionData = mPermissions.valueAt(i);
400                 final int[] permGids = permissionData.computeGids(userId);
401                 if (permGids != NO_GIDS) {
402                     gids = appendInts(gids, permGids);
403                 }
404             }
405         }
406 
407         return gids;
408     }
409 
410     /**
411      * Compute the Linux gids for all device users from the permissions
412      * granted to these users.
413      *
414      * @return The gids for all device users.
415      */
computeGids(int[] userIds)416     public int[] computeGids(int[] userIds) {
417         int[] gids = mGlobalGids;
418 
419         for (int userId : userIds) {
420             final int[] userGids = computeGids(userId);
421             gids = appendInts(gids, userGids);
422         }
423 
424         return gids;
425     }
426 
427     /**
428      * Resets the internal state of this object.
429      */
reset()430     public void reset() {
431         mGlobalGids = NO_GIDS;
432         mPermissions = null;
433     }
434 
getPermissionState(String name, int userId)435     private PermissionState getPermissionState(String name, int userId) {
436         if (mPermissions == null) {
437             return null;
438         }
439         PermissionData permissionData = mPermissions.get(name);
440         if (permissionData == null) {
441             return null;
442         }
443         return permissionData.getPermissionState(userId);
444     }
445 
getPermissionStatesInternal(int userId)446     private List<PermissionState> getPermissionStatesInternal(int userId) {
447         enforceValidUserId(userId);
448 
449         if (mPermissions == null) {
450             return Collections.emptyList();
451         }
452 
453         List<PermissionState> permissionStates = new ArrayList<>();
454 
455         final int permissionCount = mPermissions.size();
456         for (int i = 0; i < permissionCount; i++) {
457             PermissionData permissionData = mPermissions.valueAt(i);
458 
459             PermissionState permissionState = permissionData.getPermissionState(userId);
460             if (permissionState != null) {
461                 permissionStates.add(permissionState);
462             }
463         }
464 
465         return permissionStates;
466     }
467 
grantPermission(BasePermission permission, int userId)468     private int grantPermission(BasePermission permission, int userId) {
469         if (hasPermission(permission.name, userId)) {
470             return PERMISSION_OPERATION_FAILURE;
471         }
472 
473         final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
474         final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
475 
476         PermissionData permissionData = ensurePermissionData(permission);
477 
478         if (!permissionData.grant(userId)) {
479             return PERMISSION_OPERATION_FAILURE;
480         }
481 
482         if (hasGids) {
483             final int[] newGids = computeGids(userId);
484             if (oldGids.length != newGids.length) {
485                 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
486             }
487         }
488 
489         return PERMISSION_OPERATION_SUCCESS;
490     }
491 
revokePermission(BasePermission permission, int userId)492     private int revokePermission(BasePermission permission, int userId) {
493         if (!hasPermission(permission.name, userId)) {
494             return PERMISSION_OPERATION_FAILURE;
495         }
496 
497         final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
498         final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
499 
500         PermissionData permissionData = mPermissions.get(permission.name);
501 
502         if (!permissionData.revoke(userId)) {
503             return PERMISSION_OPERATION_FAILURE;
504         }
505 
506         if (permissionData.isDefault()) {
507             ensureNoPermissionData(permission.name);
508         }
509 
510         if (hasGids) {
511             final int[] newGids = computeGids(userId);
512             if (oldGids.length != newGids.length) {
513                 return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
514             }
515         }
516 
517         return PERMISSION_OPERATION_SUCCESS;
518     }
519 
appendInts(int[] current, int[] added)520     private static int[] appendInts(int[] current, int[] added) {
521         if (current != null && added != null) {
522             for (int guid : added) {
523                 current = ArrayUtils.appendInt(current, guid);
524             }
525         }
526         return current;
527     }
528 
enforceValidUserId(int userId)529     private static void enforceValidUserId(int userId) {
530         if (userId != UserHandle.USER_ALL && userId < 0) {
531             throw new IllegalArgumentException("Invalid userId:" + userId);
532         }
533     }
534 
ensurePermissionData(BasePermission permission)535     private PermissionData ensurePermissionData(BasePermission permission) {
536         if (mPermissions == null) {
537             mPermissions = new ArrayMap<>();
538         }
539         PermissionData permissionData = mPermissions.get(permission.name);
540         if (permissionData == null) {
541             permissionData = new PermissionData(permission);
542             mPermissions.put(permission.name, permissionData);
543         }
544         return permissionData;
545     }
546 
ensureNoPermissionData(String name)547     private void ensureNoPermissionData(String name) {
548         if (mPermissions == null) {
549             return;
550         }
551         mPermissions.remove(name);
552         if (mPermissions.isEmpty()) {
553             mPermissions = null;
554         }
555     }
556 
557     private static final class PermissionData {
558         private final BasePermission mPerm;
559         private SparseArray<PermissionState> mUserStates = new SparseArray<>();
560 
PermissionData(BasePermission perm)561         public PermissionData(BasePermission perm) {
562             mPerm = perm;
563         }
564 
PermissionData(PermissionData other)565         public PermissionData(PermissionData other) {
566             this(other.mPerm);
567             final int otherStateCount = other.mUserStates.size();
568             for (int i = 0; i < otherStateCount; i++) {
569                 final int otherUserId = other.mUserStates.keyAt(i);
570                 PermissionState otherState = other.mUserStates.valueAt(i);
571                 mUserStates.put(otherUserId, new PermissionState(otherState));
572             }
573         }
574 
computeGids(int userId)575         public int[] computeGids(int userId) {
576             return mPerm.computeGids(userId);
577         }
578 
isGranted(int userId)579         public boolean isGranted(int userId) {
580             if (isInstallPermission()) {
581                 userId = UserHandle.USER_ALL;
582             }
583 
584             PermissionState userState = mUserStates.get(userId);
585             if (userState == null) {
586                 return false;
587             }
588 
589             return userState.mGranted;
590         }
591 
grant(int userId)592         public boolean grant(int userId) {
593             if (!isCompatibleUserId(userId)) {
594                 return false;
595             }
596 
597             if (isGranted(userId)) {
598                 return false;
599             }
600 
601             PermissionState userState = mUserStates.get(userId);
602             if (userState == null) {
603                 userState = new PermissionState(mPerm.name);
604                 mUserStates.put(userId, userState);
605             }
606 
607             userState.mGranted = true;
608 
609             return true;
610         }
611 
revoke(int userId)612         public boolean revoke(int userId) {
613             if (!isCompatibleUserId(userId)) {
614                 return false;
615             }
616 
617             if (!isGranted(userId)) {
618                 return false;
619             }
620 
621             PermissionState userState = mUserStates.get(userId);
622             userState.mGranted = false;
623 
624             if (userState.isDefault()) {
625                 mUserStates.remove(userId);
626             }
627 
628             return true;
629         }
630 
getPermissionState(int userId)631         public PermissionState getPermissionState(int userId) {
632             return mUserStates.get(userId);
633         }
634 
getFlags(int userId)635         public int getFlags(int userId) {
636             PermissionState userState = mUserStates.get(userId);
637             if (userState != null) {
638                 return userState.mFlags;
639             }
640             return 0;
641         }
642 
isDefault()643         public boolean isDefault() {
644             return mUserStates.size() <= 0;
645         }
646 
isInstallPermissionKey(int userId)647         public static boolean isInstallPermissionKey(int userId) {
648             return userId == UserHandle.USER_ALL;
649         }
650 
updateFlags(int userId, int flagMask, int flagValues)651         public boolean updateFlags(int userId, int flagMask, int flagValues) {
652             if (isInstallPermission()) {
653                 userId = UserHandle.USER_ALL;
654             }
655 
656             if (!isCompatibleUserId(userId)) {
657                 return false;
658             }
659 
660             final int newFlags = flagValues & flagMask;
661 
662             PermissionState userState = mUserStates.get(userId);
663             if (userState != null) {
664                 final int oldFlags = userState.mFlags;
665                 userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
666                 if (userState.isDefault()) {
667                     mUserStates.remove(userId);
668                 }
669                 return userState.mFlags != oldFlags;
670             } else if (newFlags != 0) {
671                 userState = new PermissionState(mPerm.name);
672                 userState.mFlags = newFlags;
673                 mUserStates.put(userId, userState);
674                 return true;
675             }
676 
677             return false;
678         }
679 
isCompatibleUserId(int userId)680         private boolean isCompatibleUserId(int userId) {
681             return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
682         }
683 
isInstallPermission()684         private boolean isInstallPermission() {
685             return mUserStates.size() == 1
686                     && mUserStates.get(UserHandle.USER_ALL) != null;
687         }
688     }
689 
690     public static final class PermissionState {
691         private final String mName;
692         private boolean mGranted;
693         private int mFlags;
694 
PermissionState(String name)695         public PermissionState(String name) {
696             mName = name;
697         }
698 
PermissionState(PermissionState other)699         public PermissionState(PermissionState other) {
700             mName = other.mName;
701             mGranted = other.mGranted;
702             mFlags = other.mFlags;
703         }
704 
isDefault()705         public boolean isDefault() {
706             return !mGranted && mFlags == 0;
707         }
708 
getName()709         public String getName() {
710             return mName;
711         }
712 
isGranted()713         public boolean isGranted() {
714             return mGranted;
715         }
716 
getFlags()717         public int getFlags() {
718             return mFlags;
719         }
720     }
721 }
722