diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-09-27 14:58:44 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2010-10-05 14:09:08 -0700 |
commit | 90f4aafa336d9f2f07281ead3c846d323a710015 (patch) | |
tree | 68c03063b31b671be8b2a5b58607472f7003a9cb | |
parent | e21635571eec6600abbfa2cd926383e788b12e12 (diff) | |
download | frameworks_base-90f4aafa336d9f2f07281ead3c846d323a710015.zip frameworks_base-90f4aafa336d9f2f07281ead3c846d323a710015.tar.gz frameworks_base-90f4aafa336d9f2f07281ead3c846d323a710015.tar.bz2 |
Implement permission granting in clipboard.
Change-Id: I9a7a949d1aaf4b3beabceaf807fb7d3b040e4ea8
7 files changed, 256 insertions, 26 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b558318..b34c243 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1341,6 +1341,18 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case CHECK_GRANT_URI_PERMISSION_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + int callingUid = data.readInt(); + String targetPkg = data.readString(); + Uri uri = Uri.CREATOR.createFromParcel(data); + int modeFlags = data.readInt(); + int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags); + reply.writeNoException(); + reply.writeInt(res); + return true; + } + case DUMP_HEAP_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String process = data.readString(); @@ -2998,6 +3010,23 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public int checkGrantUriPermission(int callingUid, String targetPkg, + Uri uri, int modeFlags) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(callingUid); + data.writeString(targetPkg); + uri.writeToParcel(data, 0); + data.writeInt(modeFlags); + mRemote.transact(CHECK_GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0); + reply.readException(); + int res = reply.readInt(); + data.recycle(); + reply.recycle(); + return res; + } + public boolean dumpHeap(String process, boolean managed, String path, ParcelFileDescriptor fd) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 4d73817..cd229e3 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -326,6 +326,9 @@ public interface IActivityManager extends IInterface { public void revokeUriPermissionFromOwner(IBinder owner, Uri uri, int mode) throws RemoteException; + public int checkGrantUriPermission(int callingUid, String targetPkg, + Uri uri, int modeFlags) throws RemoteException; + // Cause the specified process to dump the specified heap. public boolean dumpHeap(String process, boolean managed, String path, ParcelFileDescriptor fd) throws RemoteException; @@ -540,5 +543,6 @@ public interface IActivityManager extends IInterface { int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115; int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116; int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117; - int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118; + int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118; + int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119; } diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index 0ea0648..85a6765 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -108,7 +108,7 @@ public class ClipboardManager extends android.text.ClipboardManager { */ public ClipData getPrimaryClip() { try { - return getService().getPrimaryClip(); + return getService().getPrimaryClip(mContext.getPackageName()); } catch (RemoteException e) { return null; } diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl index 3e1fe55..254901b 100644 --- a/core/java/android/content/IClipboard.aidl +++ b/core/java/android/content/IClipboard.aidl @@ -27,7 +27,7 @@ import android.content.IOnPrimaryClipChangedListener; */ interface IClipboard { void setPrimaryClip(in ClipData clip); - ClipData getPrimaryClip(); + ClipData getPrimaryClip(String pkg); ClipDescription getPrimaryClipDescription(); boolean hasPrimaryClip(); void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener); diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java index 308c9c0..bdf313c 100644 --- a/services/java/com/android/server/ClipboardService.java +++ b/services/java/com/android/server/ClipboardService.java @@ -16,32 +16,81 @@ package com.android.server; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.content.ClipData; import android.content.ClipDescription; import android.content.IClipboard; import android.content.IOnPrimaryClipChangedListener; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.util.Pair; +import android.util.Slog; + +import java.util.HashSet; /** * Implementation of the clipboard for copy and paste. */ public class ClipboardService extends IClipboard.Stub { - private ClipData mPrimaryClip; + private final Context mContext; + private final IActivityManager mAm; + private final PackageManager mPm; + private final IBinder mPermissionOwner; + private final RemoteCallbackList<IOnPrimaryClipChangedListener> mPrimaryClipListeners = new RemoteCallbackList<IOnPrimaryClipChangedListener>(); + private ClipData mPrimaryClip; + + private final HashSet<String> mActivePermissionOwners + = new HashSet<String>(); + /** * Instantiates the clipboard. */ - public ClipboardService(Context context) { } + public ClipboardService(Context context) { + mContext = context; + mAm = ActivityManagerNative.getDefault(); + mPm = context.getPackageManager(); + IBinder permOwner = null; + try { + permOwner = mAm.newUriPermissionOwner("clipboard"); + } catch (RemoteException e) { + Slog.w("clipboard", "AM dead", e); + } + mPermissionOwner = permOwner; + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + Slog.w("clipboard", "Exception: ", e); + throw e; + } + + } public void setPrimaryClip(ClipData clip) { synchronized (this) { if (clip != null && clip.getItemCount() <= 0) { throw new IllegalArgumentException("No items"); } + checkDataOwnerLocked(clip, Binder.getCallingUid()); + clearActiveOwnersLocked(); mPrimaryClip = clip; final int n = mPrimaryClipListeners.beginBroadcast(); for (int i = 0; i < n; i++) { @@ -57,8 +106,9 @@ public class ClipboardService extends IClipboard.Stub { } } - public ClipData getPrimaryClip() { + public ClipData getPrimaryClip(String pkg) { synchronized (this) { + addActiveOwnerLocked(Binder.getCallingUid(), pkg); return mPrimaryClip; } } @@ -96,4 +146,110 @@ public class ClipboardService extends IClipboard.Stub { return false; } } + + private final void checkUriOwnerLocked(Uri uri, int uid) { + if (!"content".equals(uri.getScheme())) { + return; + } + long ident = Binder.clearCallingIdentity(); + boolean allowed = false; + try { + // This will throw SecurityException for us. + mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private final void checkItemOwnerLocked(ClipData.Item item, int uid) { + if (item.getUri() != null) { + checkUriOwnerLocked(item.getUri(), uid); + } + Intent intent = item.getIntent(); + if (intent != null && intent.getData() != null) { + checkUriOwnerLocked(intent.getData(), uid); + } + } + + private final void checkDataOwnerLocked(ClipData data, int uid) { + final int N = data.getItemCount(); + for (int i=0; i<N; i++) { + checkItemOwnerLocked(data.getItem(i), uid); + } + } + + private final void grantUriLocked(Uri uri, String pkg) { + long ident = Binder.clearCallingIdentity(); + try { + mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private final void grantItemLocked(ClipData.Item item, String pkg) { + if (item.getUri() != null) { + grantUriLocked(item.getUri(), pkg); + } + Intent intent = item.getIntent(); + if (intent != null && intent.getData() != null) { + grantUriLocked(intent.getData(), pkg); + } + } + + private final void addActiveOwnerLocked(int uid, String pkg) { + PackageInfo pi; + try { + pi = mPm.getPackageInfo(pkg, 0); + if (pi.applicationInfo.uid != uid) { + throw new SecurityException("Calling uid " + uid + + " does not own package " + pkg); + } + } catch (NameNotFoundException e) { + throw new IllegalArgumentException("Unknown package " + pkg, e); + } + if (!mActivePermissionOwners.contains(pkg)) { + final int N = mPrimaryClip.getItemCount(); + for (int i=0; i<N; i++) { + grantItemLocked(mPrimaryClip.getItem(i), pkg); + } + mActivePermissionOwners.add(pkg); + } + } + + private final void revokeUriLocked(Uri uri) { + long ident = Binder.clearCallingIdentity(); + try { + mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private final void revokeItemLocked(ClipData.Item item) { + if (item.getUri() != null) { + revokeUriLocked(item.getUri()); + } + Intent intent = item.getIntent(); + if (intent != null && intent.getData() != null) { + revokeUriLocked(intent.getData()); + } + } + + private final void clearActiveOwnersLocked() { + mActivePermissionOwners.clear(); + if (mPrimaryClip == null) { + return; + } + final int N = mPrimaryClip.getItemCount(); + for (int i=0; i<N; i++) { + revokeItemLocked(mPrimaryClip.getItem(i)); + } + } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 1eab7fc..d008c90 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -4350,8 +4350,10 @@ public final class ActivityManagerService extends ActivityManagerNative return -1; } - if (DEBUG_URI_PERMISSION) Slog.v(TAG, - "Checking grant " + targetPkg + " permission to " + uri); + if (targetPkg != null) { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Checking grant " + targetPkg + " permission to " + uri); + } final IPackageManager pm = AppGlobals.getPackageManager(); @@ -4380,23 +4382,45 @@ public final class ActivityManagerService extends ActivityManagerNative } int targetUid; - try { - targetUid = pm.getPackageUid(targetPkg); - if (targetUid < 0) { - if (DEBUG_URI_PERMISSION) Slog.v(TAG, - "Can't grant URI permission no uid for: " + targetPkg); + if (targetPkg != null) { + try { + targetUid = pm.getPackageUid(targetPkg); + if (targetUid < 0) { + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Can't grant URI permission no uid for: " + targetPkg); + return -1; + } + } catch (RemoteException ex) { return -1; } - } catch (RemoteException ex) { - return -1; + } else { + targetUid = -1; } - // First... does the target actually need this permission? - if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) { - // No need to grant the target this permission. - if (DEBUG_URI_PERMISSION) Slog.v(TAG, - "Target " + targetPkg + " already has full permission to " + uri); - return -1; + if (targetUid >= 0) { + // First... does the target actually need this permission? + if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) { + // No need to grant the target this permission. + if (DEBUG_URI_PERMISSION) Slog.v(TAG, + "Target " + targetPkg + " already has full permission to " + uri); + return -1; + } + } else { + // First... there is no target package, so can anyone access it? + boolean allowed = pi.exported; + if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { + if (pi.readPermission != null) { + allowed = false; + } + } + if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { + if (pi.writePermission != null) { + allowed = false; + } + } + if (allowed) { + return -1; + } } // Second... is the provider allowing granting of URI permissions? @@ -4426,16 +4450,25 @@ public final class ActivityManagerService extends ActivityManagerNative // Third... does the caller itself have permission to access // this uri? - if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) { - if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) { - throw new SecurityException("Uid " + callingUid - + " does not have permission to uri " + uri); + if (callingUid != Process.myUid()) { + if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) { + if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) { + throw new SecurityException("Uid " + callingUid + + " does not have permission to uri " + uri); + } } } return targetUid; } + public int checkGrantUriPermission(int callingUid, String targetPkg, + Uri uri, int modeFlags) { + synchronized(this) { + return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags); + } + } + void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) { modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -4478,6 +4511,10 @@ public final class ActivityManagerService extends ActivityManagerNative void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) { + if (targetPkg == null) { + throw new NullPointerException("targetPkg"); + } + int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags); if (targetUid < 0) { return; @@ -4496,6 +4533,10 @@ public final class ActivityManagerService extends ActivityManagerNative + " from " + intent + "; flags=0x" + Integer.toHexString(intent != null ? intent.getFlags() : 0)); + if (targetPkg == null) { + throw new NullPointerException("targetPkg"); + } + if (intent == null) { return -1; } diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java index 99c82e6..68a2e0f 100644 --- a/services/java/com/android/server/am/UriPermissionOwner.java +++ b/services/java/com/android/server/am/UriPermissionOwner.java @@ -45,7 +45,7 @@ class UriPermissionOwner { } Binder getExternalTokenLocked() { - if (externalToken != null) { + if (externalToken == null) { externalToken = new ExternalToken(); } return externalToken; |