diff options
7 files changed, 256 insertions, 26 deletions
| diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b558318a0c25..b34c2430d285 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 4d738178bd83..cd229e30f890 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 0ea06482dbe4..85a67658fa14 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 3e1fe55f8b95..254901bc3a5c 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 308c9c0c8c23..bdf313c56aa3 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 1eab7fc7244e..d008c9093af4 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 99c82e6c7ff0..68a2e0fdc426 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; |