1 /*
2  * Copyright (C) 2022 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.devicepolicy;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.admin.Authority;
22 import android.app.admin.DeviceAdminAuthority;
23 import android.app.admin.DpcAuthority;
24 import android.app.admin.RoleAuthority;
25 import android.app.admin.UnknownAuthority;
26 import android.content.ComponentName;
27 import android.os.UserHandle;
28 
29 import com.android.modules.utils.TypedXmlPullParser;
30 import com.android.modules.utils.TypedXmlSerializer;
31 import com.android.role.RoleManagerLocal;
32 import com.android.server.LocalManagerRegistry;
33 import com.android.server.utils.Slogf;
34 
35 import org.xmlpull.v1.XmlPullParserException;
36 
37 import java.io.IOException;
38 import java.util.HashSet;
39 import java.util.Map;
40 import java.util.Objects;
41 import java.util.Set;
42 
43 /**
44  * {@code EnforcingAdmins} can have the following authority types:
45  *
46  * <ul>
47  *     <li> {@link #DPC_AUTHORITY} meaning it's an enterprise admin (e.g. PO, DO, COPE)
48  *     <li> {@link #DEVICE_ADMIN_AUTHORITY} which is a legacy non enterprise admin
49  *     <li> Or a role authority, in which case {@link #mAuthorities} contains a list of all roles
50  *     held by the given {@code packageName}
51  * </ul>
52  *
53  */
54 final class EnforcingAdmin {
55 
56     static final String TAG = "EnforcingAdmin";
57 
58     static final String ROLE_AUTHORITY_PREFIX = "role:";
59     static final String DPC_AUTHORITY = "enterprise";
60     static final String DEVICE_ADMIN_AUTHORITY = "device_admin";
61     static final String DEFAULT_AUTHORITY = "default";
62 
63     private static final String ATTR_PACKAGE_NAME = "package-name";
64     private static final String ATTR_CLASS_NAME = "class-name";
65     private static final String ATTR_AUTHORITIES = "authorities";
66     private static final String ATTR_AUTHORITIES_SEPARATOR = ";";
67     private static final String ATTR_USER_ID = "user-id";
68     private static final String ATTR_IS_ROLE = "is-role";
69 
70     private final String mPackageName;
71     // This is needed for DPCs and active admins
72     private final ComponentName mComponentName;
73     private Set<String> mAuthorities;
74     private final int mUserId;
75     private final boolean mIsRoleAuthority;
76     private final ActiveAdmin mActiveAdmin;
77 
createEnforcingAdmin(@onNull String packageName, int userId, ActiveAdmin admin)78     static EnforcingAdmin createEnforcingAdmin(@NonNull String packageName, int userId,
79             ActiveAdmin admin) {
80         Objects.requireNonNull(packageName);
81         return new EnforcingAdmin(packageName, userId, admin);
82     }
83 
createEnterpriseEnforcingAdmin( @onNull ComponentName componentName, int userId)84     static EnforcingAdmin createEnterpriseEnforcingAdmin(
85             @NonNull ComponentName componentName, int userId) {
86         Objects.requireNonNull(componentName);
87         return new EnforcingAdmin(
88                 componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId,
89                 /* activeAdmin=*/ null);
90     }
91 
createEnterpriseEnforcingAdmin( @onNull ComponentName componentName, int userId, ActiveAdmin activeAdmin)92     static EnforcingAdmin createEnterpriseEnforcingAdmin(
93             @NonNull ComponentName componentName, int userId, ActiveAdmin activeAdmin) {
94         Objects.requireNonNull(componentName);
95         return new EnforcingAdmin(
96                 componentName.getPackageName(), componentName, Set.of(DPC_AUTHORITY), userId,
97                 activeAdmin);
98     }
99 
createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId, ActiveAdmin activeAdmin)100     static EnforcingAdmin createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId,
101             ActiveAdmin activeAdmin) {
102         Objects.requireNonNull(componentName);
103         return new EnforcingAdmin(
104                 componentName.getPackageName(), componentName, Set.of(DEVICE_ADMIN_AUTHORITY),
105                 userId, activeAdmin);
106     }
107 
getRoleAuthorityOf(String roleName)108     static String getRoleAuthorityOf(String roleName) {
109         return ROLE_AUTHORITY_PREFIX + roleName;
110     }
111 
getParcelableAuthority(String authority)112     static Authority getParcelableAuthority(String authority) {
113         if (authority == null || authority.isEmpty()) {
114             return UnknownAuthority.UNKNOWN_AUTHORITY;
115         }
116         if (DPC_AUTHORITY.equals(authority)) {
117             return DpcAuthority.DPC_AUTHORITY;
118         }
119         if (DEVICE_ADMIN_AUTHORITY.equals(authority)) {
120             return DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY;
121         }
122         if (authority.startsWith(ROLE_AUTHORITY_PREFIX)) {
123             String role = authority.substring(ROLE_AUTHORITY_PREFIX.length());
124             return new RoleAuthority(Set.of(role));
125         }
126         return UnknownAuthority.UNKNOWN_AUTHORITY;
127     }
128 
EnforcingAdmin( String packageName, @Nullable ComponentName componentName, Set<String> authorities, int userId, @Nullable ActiveAdmin activeAdmin)129     private EnforcingAdmin(
130             String packageName, @Nullable ComponentName componentName, Set<String> authorities,
131             int userId, @Nullable ActiveAdmin activeAdmin) {
132         Objects.requireNonNull(packageName);
133         Objects.requireNonNull(authorities);
134 
135         // Role authorities should not be using this constructor
136         mIsRoleAuthority = false;
137         mPackageName = packageName;
138         mComponentName = componentName;
139         mAuthorities = new HashSet<>(authorities);
140         mUserId = userId;
141         mActiveAdmin = activeAdmin;
142     }
143 
EnforcingAdmin(String packageName, int userId, ActiveAdmin activeAdmin)144     private EnforcingAdmin(String packageName, int userId, ActiveAdmin activeAdmin) {
145         Objects.requireNonNull(packageName);
146 
147         // Only role authorities use this constructor.
148         mIsRoleAuthority = true;
149         mPackageName = packageName;
150         mUserId = userId;
151         mComponentName = null;
152         // authorities will be loaded when needed
153         mAuthorities = null;
154         mActiveAdmin = activeAdmin;
155     }
156 
getRoleAuthoritiesOrDefault(String packageName, int userId)157     private static Set<String> getRoleAuthoritiesOrDefault(String packageName, int userId) {
158         Set<String> roles = getRoles(packageName, userId);
159         Set<String> authorities = new HashSet<>();
160         for (String role : roles) {
161             authorities.add(ROLE_AUTHORITY_PREFIX + role);
162         }
163         return authorities.isEmpty() ? Set.of(DEFAULT_AUTHORITY) : authorities;
164     }
165 
166     // TODO(b/259042794): move this logic to RoleManagerLocal
getRoles(String packageName, int userId)167     private static Set<String> getRoles(String packageName, int userId) {
168         RoleManagerLocal roleManagerLocal = LocalManagerRegistry.getManager(
169                 RoleManagerLocal.class);
170         Set<String> roles = new HashSet<>();
171         Map<String, Set<String>> rolesAndHolders = roleManagerLocal.getRolesAndHolders(userId);
172         for (String role : rolesAndHolders.keySet()) {
173             if (rolesAndHolders.get(role).contains(packageName)) {
174                 roles.add(role);
175             }
176         }
177         return roles;
178     }
179 
getAuthorities()180     private Set<String> getAuthorities() {
181         if (mAuthorities == null && mIsRoleAuthority) {
182             mAuthorities = getRoleAuthoritiesOrDefault(mPackageName, mUserId);
183         }
184         return mAuthorities;
185     }
186 
reloadRoleAuthorities()187     void reloadRoleAuthorities() {
188         if (mIsRoleAuthority) {
189             mAuthorities = getRoleAuthoritiesOrDefault(mPackageName, mUserId);
190         }
191     }
192 
hasAuthority(String authority)193     boolean hasAuthority(String authority) {
194         return getAuthorities().contains(authority);
195     }
196 
197     @NonNull
getPackageName()198     String getPackageName() {
199         return mPackageName;
200     }
201 
getUserId()202     int getUserId() {
203         return mUserId;
204     }
205 
206     @Nullable
getActiveAdmin()207     public ActiveAdmin getActiveAdmin() {
208         return mActiveAdmin;
209     }
210 
211     @NonNull
getParcelableAdmin()212     android.app.admin.EnforcingAdmin getParcelableAdmin() {
213         Authority authority;
214         if (mIsRoleAuthority) {
215             Set<String> roles = getRoles(mPackageName, mUserId);
216             if (roles.isEmpty()) {
217                 authority = UnknownAuthority.UNKNOWN_AUTHORITY;
218             } else {
219                 authority = new RoleAuthority(roles);
220             }
221         } else if (mAuthorities.contains(DPC_AUTHORITY)) {
222             authority = DpcAuthority.DPC_AUTHORITY;
223         } else if (mAuthorities.contains(DEVICE_ADMIN_AUTHORITY)) {
224             authority = DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY;
225         } else {
226             authority = UnknownAuthority.UNKNOWN_AUTHORITY;
227         }
228         return new android.app.admin.EnforcingAdmin(
229                 mPackageName,
230                 authority,
231                 UserHandle.of(mUserId),
232                 mComponentName);
233     }
234 
235     /**
236      * For two EnforcingAdmins to be equal they must:
237      *
238      * <ul>
239      *     <li> have the same package names and component names and either
240      *     <li> have exactly the same authorities ({@link #DPC_AUTHORITY} or
241      *     {@link #DEVICE_ADMIN_AUTHORITY}), or have any role or default authorities.
242      * </ul>
243      *
244      * <p>EnforcingAdmins are considered equal if they have any role authority as they can have
245      * roles granted/revoked between calls.
246      */
247     @Override
equals(@ullable Object o)248     public boolean equals(@Nullable Object o) {
249         if (this == o) return true;
250         if (o == null || getClass() != o.getClass()) return false;
251         EnforcingAdmin other = (EnforcingAdmin) o;
252         return Objects.equals(mPackageName, other.mPackageName)
253                 && Objects.equals(mComponentName, other.mComponentName)
254                 && Objects.equals(mIsRoleAuthority, other.mIsRoleAuthority)
255                 && hasMatchingAuthorities(this, other);
256     }
257 
hasMatchingAuthorities(EnforcingAdmin admin1, EnforcingAdmin admin2)258     private static boolean hasMatchingAuthorities(EnforcingAdmin admin1, EnforcingAdmin admin2) {
259         if (admin1.mIsRoleAuthority && admin2.mIsRoleAuthority) {
260             return true;
261         }
262         return admin1.getAuthorities().equals(admin2.getAuthorities());
263     }
264 
265     @Override
hashCode()266     public int hashCode() {
267         if (mIsRoleAuthority) {
268             return Objects.hash(mPackageName, mUserId);
269         } else {
270             return Objects.hash(
271                     mComponentName == null ? mPackageName : mComponentName,
272                     mUserId,
273                     getAuthorities());
274         }
275     }
276 
saveToXml(TypedXmlSerializer serializer)277     void saveToXml(TypedXmlSerializer serializer) throws IOException {
278         serializer.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, mPackageName);
279         serializer.attributeBoolean(/* namespace= */ null, ATTR_IS_ROLE, mIsRoleAuthority);
280         serializer.attributeInt(/* namespace= */ null, ATTR_USER_ID, mUserId);
281         if (!mIsRoleAuthority) {
282             if (mComponentName != null) {
283                 serializer.attribute(
284                         /* namespace= */ null, ATTR_CLASS_NAME, mComponentName.getClassName());
285             }
286             // Role authorities get recomputed on load so no need to save them.
287             serializer.attribute(
288                     /* namespace= */ null,
289                     ATTR_AUTHORITIES,
290                     String.join(ATTR_AUTHORITIES_SEPARATOR, getAuthorities()));
291         }
292     }
293 
294     @Nullable
readFromXml(TypedXmlPullParser parser)295     static EnforcingAdmin readFromXml(TypedXmlPullParser parser)
296             throws XmlPullParserException {
297         String packageName = parser.getAttributeValue(/* namespace= */ null, ATTR_PACKAGE_NAME);
298         boolean isRoleAuthority = parser.getAttributeBoolean(/* namespace= */ null, ATTR_IS_ROLE);
299         String authoritiesStr = parser.getAttributeValue(/* namespace= */ null, ATTR_AUTHORITIES);
300         int userId = parser.getAttributeInt(/* namespace= */ null, ATTR_USER_ID);
301 
302         if (isRoleAuthority) {
303             if (packageName == null) {
304                 Slogf.wtf(TAG, "Error parsing EnforcingAdmin with RoleAuthority, packageName is "
305                         + "null.");
306                 return null;
307             }
308             // TODO(b/281697976): load active admin
309             return new EnforcingAdmin(packageName, userId, null);
310         } else {
311             if (packageName == null || authoritiesStr == null) {
312                 Slogf.wtf(TAG, "Error parsing EnforcingAdmin, packageName is "
313                         + (packageName == null ? "null" : packageName) + ", and authorities is "
314                         + (authoritiesStr == null ? "null" : authoritiesStr) + ".");
315                 return null;
316             }
317             String className = parser.getAttributeValue(/* namespace= */ null, ATTR_CLASS_NAME);
318             ComponentName componentName = className == null
319                     ? null :  new ComponentName(packageName, className);
320             Set<String> authorities = Set.of(authoritiesStr.split(ATTR_AUTHORITIES_SEPARATOR));
321             // TODO(b/281697976): load active admin
322             return new EnforcingAdmin(packageName, componentName, authorities, userId, null);
323         }
324     }
325 
326     @Override
toString()327     public String toString() {
328         return "EnforcingAdmin { mPackageName= " + mPackageName + ", mComponentName= "
329                 + mComponentName + ", mAuthorities= " + mAuthorities + ", mUserId= "
330                 + mUserId + ", mIsRoleAuthority= " + mIsRoleAuthority + " }";
331     }
332 }
333