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