Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2010 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 #include <android/log.h>
     18 #include <jni.h>
     19 #include <stdio.h>
     20 #include <linux/xattr.h>
     21 #include <sys/types.h>
     22 #include <sys/stat.h>
     23 #include <sys/xattr.h>
     24 #include <sys/capability.h>
     25 #include <grp.h>
     26 #include <pwd.h>
     27 #include <string.h>
     28 #include <ScopedLocalRef.h>
     29 #include <ScopedPrimitiveArray.h>
     30 #include <ScopedUtfChars.h>
     31 
     32 static jfieldID gFileStatusDevFieldID;
     33 static jfieldID gFileStatusInoFieldID;
     34 static jfieldID gFileStatusModeFieldID;
     35 static jfieldID gFileStatusNlinkFieldID;
     36 static jfieldID gFileStatusUidFieldID;
     37 static jfieldID gFileStatusGidFieldID;
     38 static jfieldID gFileStatusSizeFieldID;
     39 static jfieldID gFileStatusBlksizeFieldID;
     40 static jfieldID gFileStatusBlocksFieldID;
     41 static jfieldID gFileStatusAtimeFieldID;
     42 static jfieldID gFileStatusMtimeFieldID;
     43 static jfieldID gFileStatusCtimeFieldID;
     44 
     45 /*
     46  * Native methods used by
     47  * cts/tests/tests/permission/src/android/permission/cts/FileUtils.java
     48  *
     49  * Copied from hidden API: frameworks/base/core/jni/android_os_FileUtils.cpp
     50  */
     51 
     52 jboolean android_permission_cts_FileUtils_getFileStatus(JNIEnv* env,
     53         jobject /* thiz */, jstring path, jobject fileStatus, jboolean statLinks)
     54 {
     55     ScopedUtfChars cPath(env, path);
     56     jboolean ret = false;
     57     struct stat s;
     58 
     59     int res = statLinks == true ? lstat(cPath.c_str(), &s)
     60             : stat(cPath.c_str(), &s);
     61 
     62     if (res == 0) {
     63         ret = true;
     64         if (fileStatus != NULL) {
     65             env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
     66             env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
     67             env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
     68             env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
     69             env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
     70             env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
     71             env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
     72             env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
     73             env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
     74             env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
     75             env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
     76             env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
     77         }
     78     }
     79 
     80     return ret;
     81 }
     82 
     83 jstring android_permission_cts_FileUtils_getUserName(JNIEnv* env,
     84         jobject /* thiz */, jint uid)
     85 {
     86     struct passwd *pwd = getpwuid(uid);
     87     return env->NewStringUTF(pwd->pw_name);
     88 }
     89 
     90 jstring android_permission_cts_FileUtils_getGroupName(JNIEnv* env,
     91         jobject /* thiz */, jint gid)
     92 {
     93     struct group *grp = getgrgid(gid);
     94     return env->NewStringUTF(grp->gr_name);
     95 }
     96 
     97 static jboolean isPermittedCapBitSet(JNIEnv* env, jstring path, size_t capId)
     98 {
     99     struct vfs_cap_data capData;
    100     memset(&capData, 0, sizeof(capData));
    101 
    102     ScopedUtfChars cPath(env, path);
    103     ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &capData,
    104                               sizeof(capData));
    105     if (result <= 0)
    106     {
    107           __android_log_print(ANDROID_LOG_DEBUG, NULL,
    108                   "isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
    109                   "return %d (error: %s (%d))\n",
    110                   cPath.c_str(), result, strerror(errno), errno);
    111           return false;
    112     }
    113 
    114     return (capData.data[CAP_TO_INDEX(capId)].permitted &
    115             CAP_TO_MASK(capId)) != 0;
    116 }
    117 
    118 jboolean android_permission_cts_FileUtils_hasSetUidCapability(JNIEnv* env,
    119         jobject /* clazz */, jstring path)
    120 {
    121     return isPermittedCapBitSet(env, path, CAP_SETUID);
    122 }
    123 
    124 jboolean android_permission_cts_FileUtils_hasSetGidCapability(JNIEnv* env,
    125         jobject /* clazz */, jstring path)
    126 {
    127     return isPermittedCapBitSet(env, path, CAP_SETGID);
    128 }
    129 
    130 static bool throwNamedException(JNIEnv* env, const char* className,
    131         const char* message)
    132 {
    133     ScopedLocalRef<jclass> eClazz(env, env->FindClass(className));
    134     if (eClazz.get() == NULL)
    135     {
    136         __android_log_print(ANDROID_LOG_ERROR, NULL,
    137                 "throwNamedException(): failed to find class %s, cannot throw",
    138                 className);
    139         return false;
    140     }
    141 
    142     env->ThrowNew(eClazz.get(), message);
    143     return true;
    144 }
    145 
    146 // fill vfs_cap_data's permitted caps given a Java int[] of cap ids
    147 static bool fillPermittedCaps(vfs_cap_data* capData, JNIEnv* env, jintArray capIds)
    148 {
    149     ScopedIntArrayRO cCapIds(env, capIds);
    150     const size_t capCount = cCapIds.size();
    151 
    152     for (size_t i = 0; i < capCount; ++i)
    153     {
    154         const jint capId = cCapIds[i];
    155         if (!cap_valid(capId))
    156         {
    157             char message[64];
    158             snprintf(message, sizeof(message),
    159                     "capability id %d out of valid range", capId);
    160             throwNamedException(env, "java/lang/IllegalArgumentException",
    161                     message);
    162 
    163             return false;
    164         }
    165         capData->data[CAP_TO_INDEX(capId)].permitted |= CAP_TO_MASK(capId);
    166     }
    167     return true;
    168 }
    169 
    170 jboolean android_permission_cts_FileUtils_CapabilitySet_fileHasOnly(JNIEnv* env,
    171         jobject /* clazz */, jstring path, jintArray capIds)
    172 {
    173     struct vfs_cap_data expectedCapData;
    174     memset(&expectedCapData, 0, sizeof(expectedCapData));
    175 
    176     expectedCapData.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
    177     if (!fillPermittedCaps(&expectedCapData, env, capIds))
    178     {
    179         // exception thrown
    180         return false;
    181     }
    182 
    183     struct vfs_cap_data actualCapData;
    184     memset(&actualCapData, 0, sizeof(actualCapData));
    185 
    186     ScopedUtfChars cPath(env, path);
    187     ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &actualCapData,
    188             sizeof(actualCapData));
    189     if (result <= 0)
    190     {
    191         __android_log_print(ANDROID_LOG_DEBUG, NULL,
    192                 "fileHasOnly(): getxattr(\"%s\") call failed: "
    193                 "return %d (error: %s (%d))\n",
    194                 cPath.c_str(), result, strerror(errno), errno);
    195         return false;
    196     }
    197 
    198     return (memcmp(&expectedCapData, &actualCapData,
    199             sizeof(struct vfs_cap_data)) == 0);
    200 }
    201 
    202 static JNINativeMethod gMethods[] = {
    203     {  "getFileStatus", "(Ljava/lang/String;Landroid/permission/cts/FileUtils$FileStatus;Z)Z",
    204             (void *) android_permission_cts_FileUtils_getFileStatus  },
    205     {  "getUserName", "(I)Ljava/lang/String;",
    206             (void *) android_permission_cts_FileUtils_getUserName  },
    207     {  "getGroupName", "(I)Ljava/lang/String;",
    208             (void *) android_permission_cts_FileUtils_getGroupName  },
    209     {  "hasSetUidCapability", "(Ljava/lang/String;)Z",
    210             (void *) android_permission_cts_FileUtils_hasSetUidCapability   },
    211     {  "hasSetGidCapability", "(Ljava/lang/String;)Z",
    212             (void *) android_permission_cts_FileUtils_hasSetGidCapability   },
    213 };
    214 
    215 static JNINativeMethod gCapabilitySetMethods[] = {
    216     {  "fileHasOnly", "(Ljava/lang/String;[I)Z",
    217             (void *) android_permission_cts_FileUtils_CapabilitySet_fileHasOnly  },
    218 };
    219 
    220 int register_android_permission_cts_FileUtils(JNIEnv* env)
    221 {
    222     jclass clazz = env->FindClass("android/permission/cts/FileUtils");
    223 
    224     jclass fileStatusClass = env->FindClass("android/permission/cts/FileUtils$FileStatus");
    225     gFileStatusDevFieldID = env->GetFieldID(fileStatusClass, "dev", "I");
    226     gFileStatusInoFieldID = env->GetFieldID(fileStatusClass, "ino", "I");
    227     gFileStatusModeFieldID = env->GetFieldID(fileStatusClass, "mode", "I");
    228     gFileStatusNlinkFieldID = env->GetFieldID(fileStatusClass, "nlink", "I");
    229     gFileStatusUidFieldID = env->GetFieldID(fileStatusClass, "uid", "I");
    230     gFileStatusGidFieldID = env->GetFieldID(fileStatusClass, "gid", "I");
    231     gFileStatusSizeFieldID = env->GetFieldID(fileStatusClass, "size", "J");
    232     gFileStatusBlksizeFieldID = env->GetFieldID(fileStatusClass, "blksize", "I");
    233     gFileStatusBlocksFieldID = env->GetFieldID(fileStatusClass, "blocks", "J");
    234     gFileStatusAtimeFieldID = env->GetFieldID(fileStatusClass, "atime", "J");
    235     gFileStatusMtimeFieldID = env->GetFieldID(fileStatusClass, "mtime", "J");
    236     gFileStatusCtimeFieldID = env->GetFieldID(fileStatusClass, "ctime", "J");
    237 
    238     jint result = env->RegisterNatives(clazz, gMethods,
    239             sizeof(gMethods) / sizeof(JNINativeMethod));
    240     if (result)
    241     {
    242       return result;
    243     }
    244 
    245     // register FileUtils.CapabilitySet native methods
    246     jclass capClazz = env->FindClass("android/permission/cts/FileUtils$CapabilitySet");
    247 
    248     return env->RegisterNatives(capClazz, gCapabilitySetMethods,
    249             sizeof(gCapabilitySetMethods) / sizeof(JNINativeMethod));
    250 }
    251