diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/java/android/content/ContentProvider.java | 136 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 98 | ||||
| -rw-r--r-- | core/java/android/content/pm/PathPermission.java | 68 | ||||
| -rw-r--r-- | core/java/android/content/pm/ProviderInfo.java | 12 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 23 | ||||
| -rw-r--r-- | core/res/res/values/attrs_manifest.xml | 14 | 
6 files changed, 327 insertions, 24 deletions
| diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 5cc5730..6b50405 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -17,6 +17,7 @@  package android.content;  import android.content.pm.PackageManager; +import android.content.pm.PathPermission;  import android.content.pm.ProviderInfo;  import android.content.res.AssetFileDescriptor;  import android.content.res.Configuration; @@ -29,6 +30,7 @@ import android.database.SQLException;  import android.net.Uri;  import android.os.Binder;  import android.os.ParcelFileDescriptor; +import android.os.Process;  import java.io.File;  import java.io.FileNotFoundException; @@ -65,8 +67,10 @@ import java.io.FileNotFoundException;   */  public abstract class ContentProvider implements ComponentCallbacks {      private Context mContext = null; +    private int mMyUid;      private String mReadPermission;      private String mWritePermission; +    private PathPermission[] mPathPermissions;      private Transport mTransport = new Transport(); @@ -108,24 +112,20 @@ public abstract class ContentProvider implements ComponentCallbacks {          public IBulkCursor bulkQuery(Uri uri, String[] projection,                  String selection, String[] selectionArgs, String sortOrder,                  IContentObserver observer, CursorWindow window) { -            checkReadPermission(uri); +            enforceReadPermission(uri);              Cursor cursor = ContentProvider.this.query(uri, projection,                      selection, selectionArgs, sortOrder);              if (cursor == null) {                  return null;              } -            String wperm = getWritePermission();              return new CursorToBulkCursorAdaptor(cursor, observer,                      ContentProvider.this.getClass().getName(), -                    wperm == null || -                    getContext().checkCallingOrSelfPermission(getWritePermission()) -                            == PackageManager.PERMISSION_GRANTED, -                    window); +                    hasWritePermission(uri), window);          }          public Cursor query(Uri uri, String[] projection,                  String selection, String[] selectionArgs, String sortOrder) { -            checkReadPermission(uri); +            enforceReadPermission(uri);              return ContentProvider.this.query(uri, projection, selection,                      selectionArgs, sortOrder);          } @@ -136,55 +136,84 @@ public abstract class ContentProvider implements ComponentCallbacks {          public Uri insert(Uri uri, ContentValues initialValues) { -            checkWritePermission(uri); +            enforceWritePermission(uri);              return ContentProvider.this.insert(uri, initialValues);          }          public int bulkInsert(Uri uri, ContentValues[] initialValues) { -            checkWritePermission(uri); +            enforceWritePermission(uri);              return ContentProvider.this.bulkInsert(uri, initialValues);          }          public int delete(Uri uri, String selection, String[] selectionArgs) { -            checkWritePermission(uri); +            enforceWritePermission(uri);              return ContentProvider.this.delete(uri, selection, selectionArgs);          }          public int update(Uri uri, ContentValues values, String selection,                  String[] selectionArgs) { -            checkWritePermission(uri); +            enforceWritePermission(uri);              return ContentProvider.this.update(uri, values, selection, selectionArgs);          }          public ParcelFileDescriptor openFile(Uri uri, String mode)                  throws FileNotFoundException { -            if (mode != null && mode.startsWith("rw")) checkWritePermission(uri); -            else checkReadPermission(uri); +            if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); +            else enforceReadPermission(uri);              return ContentProvider.this.openFile(uri, mode);          }          public AssetFileDescriptor openAssetFile(Uri uri, String mode)                  throws FileNotFoundException { -            if (mode != null && mode.startsWith("rw")) checkWritePermission(uri); -            else checkReadPermission(uri); +            if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); +            else enforceReadPermission(uri);              return ContentProvider.this.openAssetFile(uri, mode);          }          public ISyncAdapter getSyncAdapter() { -            checkWritePermission(null); +            enforceWritePermission(null);              SyncAdapter sa = ContentProvider.this.getSyncAdapter();              return sa != null ? sa.getISyncAdapter() : null;          } -        private void checkReadPermission(Uri uri) { +        private void enforceReadPermission(Uri uri) { +            final int uid = Binder.getCallingUid(); +            if (uid == mMyUid) { +                return; +            } +             +            final Context context = getContext();              final String rperm = getReadPermission();              final int pid = Binder.getCallingPid(); -            final int uid = Binder.getCallingUid(); -            if (getContext().checkUriPermission(uri, rperm, null, pid, uid, +            if (rperm == null +                    || context.checkPermission(rperm, pid, uid) +                    == PackageManager.PERMISSION_GRANTED) { +                return; +            } +             +            PathPermission[] pps = getPathPermissions(); +            if (pps != null) { +                final String path = uri.getPath(); +                int i = pps.length; +                while (i > 0) { +                    i--; +                    final PathPermission pp = pps[i]; +                    final String pprperm = pp.getReadPermission(); +                    if (pprperm != null && pp.match(path)) { +                        if (context.checkPermission(pprperm, pid, uid) +                                == PackageManager.PERMISSION_GRANTED) { +                            return; +                        } +                    } +                } +            } +             +            if (context.checkUriPermission(uri, pid, uid,                      Intent.FLAG_GRANT_READ_URI_PERMISSION)                      == PackageManager.PERMISSION_GRANTED) {                  return;              } +                          String msg = "Permission Denial: reading "                      + ContentProvider.this.getClass().getName()                      + " uri " + uri + " from pid=" + Binder.getCallingPid() @@ -193,20 +222,57 @@ public abstract class ContentProvider implements ComponentCallbacks {              throw new SecurityException(msg);          } -        private void checkWritePermission(Uri uri) { +        private boolean hasWritePermission(Uri uri) { +            final int uid = Binder.getCallingUid(); +            if (uid == mMyUid) { +                return true; +            } +             +            final Context context = getContext();              final String wperm = getWritePermission();              final int pid = Binder.getCallingPid(); -            final int uid = Binder.getCallingUid(); -            if (getContext().checkUriPermission(uri, null, wperm, pid, uid, +            if (wperm == null +                    || context.checkPermission(wperm, pid, uid) +                    == PackageManager.PERMISSION_GRANTED) { +                return true; +            } +             +            PathPermission[] pps = getPathPermissions(); +            if (pps != null) { +                final String path = uri.getPath(); +                int i = pps.length; +                while (i > 0) { +                    i--; +                    final PathPermission pp = pps[i]; +                    final String ppwperm = pp.getWritePermission(); +                    if (ppwperm != null && pp.match(path)) { +                        if (context.checkPermission(ppwperm, pid, uid) +                                == PackageManager.PERMISSION_GRANTED) { +                            return true; +                        } +                    } +                } +            } +             +            if (context.checkUriPermission(uri, pid, uid,                      Intent.FLAG_GRANT_WRITE_URI_PERMISSION)                      == PackageManager.PERMISSION_GRANTED) { +                return true; +            } +             +            return false; +        } +         +        private void enforceWritePermission(Uri uri) { +            if (hasWritePermission(uri)) {                  return;              } +                          String msg = "Permission Denial: writing "                      + ContentProvider.this.getClass().getName()                      + " uri " + uri + " from pid=" + Binder.getCallingPid()                      + ", uid=" + Binder.getCallingUid() -                    + " requires " + wperm; +                    + " requires " + getWritePermission();              throw new SecurityException(msg);          }      } @@ -266,6 +332,28 @@ public abstract class ContentProvider implements ComponentCallbacks {      }      /** +     * Change the path-based permission required to read and/or write data in +     * the content provider.  This is normally set for you from its manifest +     * information when the provider is first created. +     * +     * @param permissions Array of path permission descriptions. +     */ +    protected final void setPathPermissions(PathPermission[] permissions) { +        mPathPermissions = permissions; +    } + +    /** +     * Return the path-based permissions required for read and/or write access to +     * this content provider.  This method can be called from multiple +     * threads, as described in +     * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: +     * Processes and Threads</a>. +     */ +    public final PathPermission[] getPathPermissions() { +        return mPathPermissions; +    } + +    /**       * Called when the provider is being started.       *       * @return true if the provider was successfully loaded, false otherwise @@ -600,9 +688,11 @@ public abstract class ContentProvider implements ComponentCallbacks {           */          if (mContext == null) {              mContext = context; +            mMyUid = Process.myUid();              if (info != null) {                  setReadPermission(info.readPermission);                  setWritePermission(info.writePermission); +                setPathPermissions(info.pathPermissions);              }              ContentProvider.this.onCreate();          } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index b293636..0e2deed 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1918,6 +1918,7 @@ public class PackageParser {                          outInfo.metaData, outError)) == null) {                      return false;                  } +                              } else if (parser.getName().equals("grant-uri-permission")) {                  TypedArray sa = res.obtainAttributes(attrs,                          com.android.internal.R.styleable.AndroidManifestGrantUriPermission); @@ -1941,7 +1942,7 @@ public class PackageParser {                  if (str != null) {                      pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);                  } - +                                  sa.recycle();                  if (pa != null) { @@ -1956,6 +1957,101 @@ public class PackageParser {                          outInfo.info.uriPermissionPatterns = newp;                      }                      outInfo.info.grantUriPermissions = true; +                } else { +                    if (!RIGID_PARSER) { +                        Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); +                        Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>"); +                        XmlUtils.skipCurrentTag(parser); +                        continue; +                    } +                    outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>"; +                    return false; +                } +                XmlUtils.skipCurrentTag(parser); + +            } else if (parser.getName().equals("path-permission")) { +                TypedArray sa = res.obtainAttributes(attrs, +                        com.android.internal.R.styleable.AndroidManifestPathPermission); + +                PathPermission pa = null; + +                String permission = sa.getNonResourceString( +                        com.android.internal.R.styleable.AndroidManifestPathPermission_permission); +                String readPermission = sa.getNonResourceString( +                        com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission); +                if (readPermission == null) { +                    readPermission = permission; +                } +                String writePermission = sa.getNonResourceString( +                        com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission); +                if (writePermission == null) { +                    writePermission = permission; +                } +                 +                boolean havePerm = false; +                if (readPermission != null) { +                    readPermission = readPermission.intern(); +                    havePerm = true; +                } +                if (writePermission != null) { +                    writePermission = readPermission.intern(); +                    havePerm = true; +                } + +                if (!havePerm) { +                    if (!RIGID_PARSER) { +                        Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); +                        Log.w(TAG, "No readPermission or writePermssion for <path-permission>"); +                        XmlUtils.skipCurrentTag(parser); +                        continue; +                    } +                    outError[0] = "No readPermission or writePermssion for <path-permission>"; +                    return false; +                } +                 +                String path = sa.getNonResourceString( +                        com.android.internal.R.styleable.AndroidManifestPathPermission_path); +                if (path != null) { +                    pa = new PathPermission(path, +                            PatternMatcher.PATTERN_LITERAL, readPermission, writePermission); +                } + +                path = sa.getNonResourceString( +                        com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix); +                if (path != null) { +                    pa = new PathPermission(path, +                            PatternMatcher.PATTERN_PREFIX, readPermission, writePermission); +                } + +                path = sa.getNonResourceString( +                        com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern); +                if (path != null) { +                    pa = new PathPermission(path, +                            PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission); +                } + +                sa.recycle(); + +                if (pa != null) { +                    if (outInfo.info.pathPermissions == null) { +                        outInfo.info.pathPermissions = new PathPermission[1]; +                        outInfo.info.pathPermissions[0] = pa; +                    } else { +                        final int N = outInfo.info.pathPermissions.length; +                        PathPermission[] newp = new PathPermission[N+1]; +                        System.arraycopy(outInfo.info.pathPermissions, 0, newp, 0, N); +                        newp[N] = pa; +                        outInfo.info.pathPermissions = newp; +                    } +                } else { +                    if (!RIGID_PARSER) { +                        Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); +                        Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>"); +                        XmlUtils.skipCurrentTag(parser); +                        continue; +                    } +                    outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>"; +                    return false;                  }                  XmlUtils.skipCurrentTag(parser); diff --git a/core/java/android/content/pm/PathPermission.java b/core/java/android/content/pm/PathPermission.java new file mode 100644 index 0000000..7e49d7d --- /dev/null +++ b/core/java/android/content/pm/PathPermission.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; + +/** + * Description of permissions needed to access a particular path + * in a {@link ProviderInfo}. + */ +public class PathPermission extends PatternMatcher { +    private final String mReadPermission; +    private final String mWritePermission; +     +    public PathPermission(String pattern, int type, String readPermission, +            String writePermission) { +        super(pattern, type); +        mReadPermission = readPermission; +        mWritePermission = writePermission; +    } +     +    public String getReadPermission() { +        return mReadPermission; +    } +     +    public String getWritePermission() { +        return mWritePermission; +    } +     +    public void writeToParcel(Parcel dest, int flags) { +        super.writeToParcel(dest, flags); +        dest.writeString(mReadPermission); +        dest.writeString(mWritePermission); +    } +     +    public PathPermission(Parcel src) { +        super(src); +        mReadPermission = src.readString(); +        mWritePermission = src.readString(); +    } +     +    public static final Parcelable.Creator<PathPermission> CREATOR +            = new Parcelable.Creator<PathPermission>() { +        public PathPermission createFromParcel(Parcel source) { +            return new PathPermission(source); +        } + +        public PathPermission[] newArray(int size) { +            return new PathPermission[size]; +        } +    }; +}
\ No newline at end of file diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java index b67ddf6..d01460e 100644 --- a/core/java/android/content/pm/ProviderInfo.java +++ b/core/java/android/content/pm/ProviderInfo.java @@ -28,6 +28,7 @@ import android.os.PatternMatcher;   */  public final class ProviderInfo extends ComponentInfo          implements Parcelable { +          /** The name provider is published under content:// */      public String authority = null; @@ -56,6 +57,14 @@ public final class ProviderInfo extends ComponentInfo       */      public PatternMatcher[] uriPermissionPatterns = null; +    /** +     * If non-null, these are path-specific permissions that are allowed for +     * accessing the provider.  Any permissions listed here will allow a +     * holding client to access the provider, and the provider will check +     * the URI it provides when making calls against the patterns here. +     */ +    public PathPermission[] pathPermissions = null; +          /** If true, this content provider allows multiple instances of itself       *  to run in different process.  If false, a single instances is always       *  run in {@link #processName}. */ @@ -78,6 +87,7 @@ public final class ProviderInfo extends ComponentInfo          writePermission = orig.writePermission;          grantUriPermissions = orig.grantUriPermissions;          uriPermissionPatterns = orig.uriPermissionPatterns; +        pathPermissions = orig.pathPermissions;          multiprocess = orig.multiprocess;          initOrder = orig.initOrder;          isSyncable = orig.isSyncable; @@ -94,6 +104,7 @@ public final class ProviderInfo extends ComponentInfo          out.writeString(writePermission);          out.writeInt(grantUriPermissions ? 1 : 0);          out.writeTypedArray(uriPermissionPatterns, parcelableFlags); +        out.writeTypedArray(pathPermissions, parcelableFlags);          out.writeInt(multiprocess ? 1 : 0);          out.writeInt(initOrder);          out.writeInt(isSyncable ? 1 : 0); @@ -122,6 +133,7 @@ public final class ProviderInfo extends ComponentInfo          writePermission = in.readString();          grantUriPermissions = in.readInt() != 0;          uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR); +        pathPermissions = in.createTypedArray(PathPermission.CREATOR);          multiprocess = in.readInt() != 0;          initOrder = in.readInt();          isSyncable = in.readInt() != 0; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 599360f..23967f4 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -995,6 +995,29 @@          android:description="@string/permdesc_changeBackgroundDataSetting"          android:label="@string/permlab_changeBackgroundDataSetting" /> +    <!-- This permission can be used on content providers to allow the global +         search system to access their data.  Typically it used when the +         provider has some permissions protecting it (which global search +         would not be expected to hold), and added as a read-only permission +         to the path in the provider where global search queries are +         performed.  This permission can not be held by regular applications; +         it is used by applications to protect themselves from everyone else +         besides global search. --> +    <permission android:name="android.permission.GLOBAL_SEARCH" +        android:permissionGroup="android.permission-group.SYSTEM_TOOLS" +        android:protectionLevel="signatureOrSystem" /> + +    <!-- Internal permission protecting access to the global search +         system: ensures that only the system can access the provider +         to perform queries (since this otherwise provides unrestricted +         access to a variety of content providers), and to write the +         search statistics (to keep applications from gaming the source +         ranking). +         @hide --> +    <permission android:name="android.permission.GLOBAL_SEARCH_CONTROL" +        android:permissionGroup="android.permission-group.SYSTEM_TOOLS" +        android:protectionLevel="signature" /> +      <application android:process="system"                   android:persistent="true"                   android:hasCode="false" diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 7571e24..12a76ba 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -953,6 +953,20 @@          <attr name="pathPattern" format="string" />      </declare-styleable> +    <!-- Attributes that can be supplied in an AndroidManifest.xml +         <code>path-permission</code> tag, a child of the +         {@link #AndroidManifestProvider provider} tag, describing a permission +         that allows access to a specific path in the provider.  This tag can be +         specified multiple time to supply multiple paths. --> +    <declare-styleable name="AndroidManifestPathPermission"  parent="AndroidManifestProvider"> +        <attr name="path" /> +        <attr name="pathPrefix" /> +        <attr name="pathPattern" /> +        <attr name="permission" /> +        <attr name="readPermission" /> +        <attr name="writePermission" /> +    </declare-styleable> +          <!-- The <code>service</code> tag declares a           {@link android.app.Service} class that is available           as part of the package's application components, implementing | 
