1/*
2 * Copyright (C) 2006 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.server.am;
18
19import android.content.Intent;
20import android.os.Binder;
21import android.os.UserHandle;
22import android.util.ArraySet;
23import android.util.Log;
24import android.util.Slog;
25
26import com.android.server.am.ActivityManagerService.GrantUri;
27import com.google.android.collect.Sets;
28
29import java.io.PrintWriter;
30import java.util.Comparator;
31
32/**
33 * Description of a permission granted to an app to access a particular URI.
34 *
35 * CTS tests for this functionality can be run with "runtest cts-appsecurity".
36 *
37 * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
38 *      src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
39 */
40final class UriPermission {
41    private static final String TAG = "UriPermission";
42
43    public static final int STRENGTH_NONE = 0;
44    public static final int STRENGTH_OWNED = 1;
45    public static final int STRENGTH_GLOBAL = 2;
46    public static final int STRENGTH_PERSISTABLE = 3;
47
48    final int targetUserId;
49    final String sourcePkg;
50    final String targetPkg;
51
52    /** Cached UID of {@link #targetPkg}; should not be persisted */
53    final int targetUid;
54
55    final GrantUri uri;
56
57    /**
58     * Allowed modes. All permission enforcement should use this field. Must
59     * always be a combination of {@link #ownedModeFlags},
60     * {@link #globalModeFlags}, {@link #persistableModeFlags}, and
61     * {@link #persistedModeFlags}. Mutations <em>must</em> only be performed by
62     * the owning class.
63     */
64    int modeFlags = 0;
65
66    /** Allowed modes with active owner. */
67    int ownedModeFlags = 0;
68    /** Allowed modes without explicit owner. */
69    int globalModeFlags = 0;
70    /** Allowed modes that have been offered for possible persisting. */
71    int persistableModeFlags = 0;
72
73    /** Allowed modes that should be persisted across device boots. */
74    int persistedModeFlags = 0;
75
76    /**
77     * Timestamp when {@link #persistedModeFlags} was first defined in
78     * {@link System#currentTimeMillis()} time base.
79     */
80    long persistedCreateTime = INVALID_TIME;
81
82    private static final long INVALID_TIME = Long.MIN_VALUE;
83
84    private ArraySet<UriPermissionOwner> mReadOwners;
85    private ArraySet<UriPermissionOwner> mWriteOwners;
86
87    private String stringName;
88
89    UriPermission(String sourcePkg, String targetPkg, int targetUid, GrantUri uri) {
90        this.targetUserId = UserHandle.getUserId(targetUid);
91        this.sourcePkg = sourcePkg;
92        this.targetPkg = targetPkg;
93        this.targetUid = targetUid;
94        this.uri = uri;
95    }
96
97    private void updateModeFlags() {
98        final int oldModeFlags = modeFlags;
99        modeFlags = ownedModeFlags | globalModeFlags | persistableModeFlags | persistedModeFlags;
100
101        if (Log.isLoggable(TAG, Log.VERBOSE) && (modeFlags != oldModeFlags)) {
102            Slog.d(TAG,
103                    "Permission for " + targetPkg + " to " + uri + " is changing from 0x"
104                            + Integer.toHexString(oldModeFlags) + " to 0x"
105                            + Integer.toHexString(modeFlags) + " via calling UID "
106                            + Binder.getCallingUid() + " PID " + Binder.getCallingPid(),
107                    new Throwable());
108        }
109    }
110
111    /**
112     * Initialize persisted modes as read from file. This doesn't issue any
113     * global or owner grants.
114     */
115    void initPersistedModes(int modeFlags, long createdTime) {
116        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
117                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
118
119        persistableModeFlags = modeFlags;
120        persistedModeFlags = modeFlags;
121        persistedCreateTime = createdTime;
122
123        updateModeFlags();
124    }
125
126    void grantModes(int modeFlags, UriPermissionOwner owner) {
127        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
128        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
129                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
130
131        if (persistable) {
132            persistableModeFlags |= modeFlags;
133        }
134
135        if (owner == null) {
136            globalModeFlags |= modeFlags;
137        } else {
138            if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
139                addReadOwner(owner);
140            }
141            if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
142                addWriteOwner(owner);
143            }
144        }
145
146        updateModeFlags();
147    }
148
149    /**
150     * @return if mode changes should trigger persisting.
151     */
152    boolean takePersistableModes(int modeFlags) {
153        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
154                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
155
156        if ((modeFlags & persistableModeFlags) != modeFlags) {
157            Slog.w(TAG, "Requested flags 0x"
158                    + Integer.toHexString(modeFlags) + ", but only 0x"
159                    + Integer.toHexString(persistableModeFlags) + " are allowed");
160            return false;
161        }
162
163        final int before = persistedModeFlags;
164        persistedModeFlags |= (persistableModeFlags & modeFlags);
165
166        if (persistedModeFlags != 0) {
167            persistedCreateTime = System.currentTimeMillis();
168        }
169
170        updateModeFlags();
171        return persistedModeFlags != before;
172    }
173
174    boolean releasePersistableModes(int modeFlags) {
175        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
176                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
177
178        final int before = persistedModeFlags;
179
180        persistableModeFlags &= ~modeFlags;
181        persistedModeFlags &= ~modeFlags;
182
183        if (persistedModeFlags == 0) {
184            persistedCreateTime = INVALID_TIME;
185        }
186
187        updateModeFlags();
188        return persistedModeFlags != before;
189    }
190
191    /**
192     * @return if mode changes should trigger persisting.
193     */
194    boolean revokeModes(int modeFlags, boolean includingOwners) {
195        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
196        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
197                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
198
199        final int before = persistedModeFlags;
200
201        if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
202            if (persistable) {
203                persistableModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
204                persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
205            }
206            globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
207            if (mReadOwners != null && includingOwners) {
208                ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
209                for (UriPermissionOwner r : mReadOwners) {
210                    r.removeReadPermission(this);
211                }
212                mReadOwners = null;
213            }
214        }
215        if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
216            if (persistable) {
217                persistableModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
218                persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
219            }
220            globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
221            if (mWriteOwners != null && includingOwners) {
222                ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
223                for (UriPermissionOwner r : mWriteOwners) {
224                    r.removeWritePermission(this);
225                }
226                mWriteOwners = null;
227            }
228        }
229
230        if (persistedModeFlags == 0) {
231            persistedCreateTime = INVALID_TIME;
232        }
233
234        updateModeFlags();
235        return persistedModeFlags != before;
236    }
237
238    /**
239     * Return strength of this permission grant for the given flags.
240     */
241    public int getStrength(int modeFlags) {
242        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
243                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
244        if ((persistableModeFlags & modeFlags) == modeFlags) {
245            return STRENGTH_PERSISTABLE;
246        } else if ((globalModeFlags & modeFlags) == modeFlags) {
247            return STRENGTH_GLOBAL;
248        } else if ((ownedModeFlags & modeFlags) == modeFlags) {
249            return STRENGTH_OWNED;
250        } else {
251            return STRENGTH_NONE;
252        }
253    }
254
255    private void addReadOwner(UriPermissionOwner owner) {
256        if (mReadOwners == null) {
257            mReadOwners = Sets.newArraySet();
258            ownedModeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
259            updateModeFlags();
260        }
261        if (mReadOwners.add(owner)) {
262            owner.addReadPermission(this);
263        }
264    }
265
266    /**
267     * Remove given read owner, updating {@Link #modeFlags} as needed.
268     */
269    void removeReadOwner(UriPermissionOwner owner) {
270        if (!mReadOwners.remove(owner)) {
271            Slog.wtf(TAG, "Unknown read owner " + owner + " in " + this);
272        }
273        if (mReadOwners.size() == 0) {
274            mReadOwners = null;
275            ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
276            updateModeFlags();
277        }
278    }
279
280    private void addWriteOwner(UriPermissionOwner owner) {
281        if (mWriteOwners == null) {
282            mWriteOwners = Sets.newArraySet();
283            ownedModeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
284            updateModeFlags();
285        }
286        if (mWriteOwners.add(owner)) {
287            owner.addWritePermission(this);
288        }
289    }
290
291    /**
292     * Remove given write owner, updating {@Link #modeFlags} as needed.
293     */
294    void removeWriteOwner(UriPermissionOwner owner) {
295        if (!mWriteOwners.remove(owner)) {
296            Slog.wtf(TAG, "Unknown write owner " + owner + " in " + this);
297        }
298        if (mWriteOwners.size() == 0) {
299            mWriteOwners = null;
300            ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
301            updateModeFlags();
302        }
303    }
304
305    @Override
306    public String toString() {
307        if (stringName != null) {
308            return stringName;
309        }
310        StringBuilder sb = new StringBuilder(128);
311        sb.append("UriPermission{");
312        sb.append(Integer.toHexString(System.identityHashCode(this)));
313        sb.append(' ');
314        sb.append(uri);
315        sb.append('}');
316        return stringName = sb.toString();
317    }
318
319    void dump(PrintWriter pw, String prefix) {
320        pw.print(prefix);
321        pw.print("targetUserId=" + targetUserId);
322        pw.print(" sourcePkg=" + sourcePkg);
323        pw.println(" targetPkg=" + targetPkg);
324
325        pw.print(prefix);
326        pw.print("mode=0x" + Integer.toHexString(modeFlags));
327        pw.print(" owned=0x" + Integer.toHexString(ownedModeFlags));
328        pw.print(" global=0x" + Integer.toHexString(globalModeFlags));
329        pw.print(" persistable=0x" + Integer.toHexString(persistableModeFlags));
330        pw.print(" persisted=0x" + Integer.toHexString(persistedModeFlags));
331        if (persistedCreateTime != INVALID_TIME) {
332            pw.print(" persistedCreate=" + persistedCreateTime);
333        }
334        pw.println();
335
336        if (mReadOwners != null) {
337            pw.print(prefix);
338            pw.println("readOwners:");
339            for (UriPermissionOwner owner : mReadOwners) {
340                pw.print(prefix);
341                pw.println("  * " + owner);
342            }
343        }
344        if (mWriteOwners != null) {
345            pw.print(prefix);
346            pw.println("writeOwners:");
347            for (UriPermissionOwner owner : mReadOwners) {
348                pw.print(prefix);
349                pw.println("  * " + owner);
350            }
351        }
352    }
353
354    public static class PersistedTimeComparator implements Comparator<UriPermission> {
355        @Override
356        public int compare(UriPermission lhs, UriPermission rhs) {
357            return Long.compare(lhs.persistedCreateTime, rhs.persistedCreateTime);
358        }
359    }
360
361    /**
362     * Snapshot of {@link UriPermission} with frozen
363     * {@link UriPermission#persistedModeFlags} state.
364     */
365    public static class Snapshot {
366        final int targetUserId;
367        final String sourcePkg;
368        final String targetPkg;
369        final GrantUri uri;
370        final int persistedModeFlags;
371        final long persistedCreateTime;
372
373        private Snapshot(UriPermission perm) {
374            this.targetUserId = perm.targetUserId;
375            this.sourcePkg = perm.sourcePkg;
376            this.targetPkg = perm.targetPkg;
377            this.uri = perm.uri;
378            this.persistedModeFlags = perm.persistedModeFlags;
379            this.persistedCreateTime = perm.persistedCreateTime;
380        }
381    }
382
383    public Snapshot snapshot() {
384        return new Snapshot(this);
385    }
386
387    public android.content.UriPermission buildPersistedPublicApiObject() {
388        return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime);
389    }
390}
391