1 /* 2 * Copyright (C) 2012 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 #define LOG_TAG "SELinuxJNI" 18 #include <utils/Log.h> 19 20 #include "JNIHelp.h" 21 #include "jni.h" 22 #include "core_jni_helpers.h" 23 #include "selinux/selinux.h" 24 #include "selinux/android.h" 25 #include <errno.h> 26 #include <ScopedLocalRef.h> 27 #include <ScopedUtfChars.h> 28 #include <UniquePtr.h> 29 30 namespace android { 31 32 struct SecurityContext_Delete { 33 void operator()(security_context_t p) const { 34 freecon(p); 35 } 36 }; 37 typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext; 38 39 static jboolean isSELinuxDisabled = true; 40 41 /* 42 * Function: isSELinuxEnabled 43 * Purpose: checks whether SELinux is enabled/disbaled 44 * Parameters: none 45 * Return value : true (enabled) or false (disabled) 46 * Exceptions: none 47 */ 48 static jboolean isSELinuxEnabled(JNIEnv *env, jobject) { 49 return !isSELinuxDisabled; 50 } 51 52 /* 53 * Function: isSELinuxEnforced 54 * Purpose: return the current SELinux enforce mode 55 * Parameters: none 56 * Return value: true (enforcing) or false (permissive) 57 * Exceptions: none 58 */ 59 static jboolean isSELinuxEnforced(JNIEnv *env, jobject) { 60 return (security_getenforce() == 1) ? true : false; 61 } 62 63 /* 64 * Function: getPeerCon 65 * Purpose: retrieves security context of peer socket 66 * Parameters: 67 * fileDescriptor: peer socket file as a FileDescriptor object 68 * Returns: jstring representing the security_context of socket or NULL if error 69 * Exceptions: NullPointerException if fileDescriptor object is NULL 70 */ 71 static jstring getPeerCon(JNIEnv *env, jobject, jobject fileDescriptor) { 72 if (isSELinuxDisabled) { 73 return NULL; 74 } 75 76 if (fileDescriptor == NULL) { 77 jniThrowNullPointerException(env, 78 "Trying to check security context of a null peer socket."); 79 return NULL; 80 } 81 82 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 83 if (env->ExceptionCheck()) { 84 ALOGE("getPeerCon => getFD for %p failed", fileDescriptor); 85 return NULL; 86 } 87 88 security_context_t tmp = NULL; 89 int ret = getpeercon(fd, &tmp); 90 Unique_SecurityContext context(tmp); 91 92 ScopedLocalRef<jstring> contextStr(env, NULL); 93 if (ret != -1) { 94 contextStr.reset(env->NewStringUTF(context.get())); 95 } 96 97 ALOGV("getPeerCon(%d) => %s", fd, context.get()); 98 return contextStr.release(); 99 } 100 101 /* 102 * Function: setFSCreateCon 103 * Purpose: set security context used for creating a new file system object 104 * Parameters: 105 * context: security_context_t representing the new context of a file system object, 106 * set to NULL to return to the default policy behavior 107 * Returns: true on success, false on error 108 * Exception: none 109 */ 110 static jboolean setFSCreateCon(JNIEnv *env, jobject, jstring contextStr) { 111 if (isSELinuxDisabled) { 112 return false; 113 } 114 115 UniquePtr<ScopedUtfChars> context; 116 const char* context_c_str = NULL; 117 if (contextStr != NULL) { 118 context.reset(new ScopedUtfChars(env, contextStr)); 119 context_c_str = context->c_str(); 120 if (context_c_str == NULL) { 121 return false; 122 } 123 } 124 125 int ret = setfscreatecon(const_cast<char *>(context_c_str)); 126 127 ALOGV("setFSCreateCon(%s) => %d", context_c_str, ret); 128 129 return (ret == 0) ? true : false; 130 } 131 132 /* 133 * Function: setFileCon 134 * Purpose: set the security context of a file object 135 * Parameters: 136 * path: the location of the file system object 137 * context: the new security context of the file system object 138 * Returns: true on success, false on error 139 * Exception: NullPointerException is thrown if either path or context strign are NULL 140 */ 141 static jboolean setFileCon(JNIEnv *env, jobject, jstring pathStr, jstring contextStr) { 142 if (isSELinuxDisabled) { 143 return false; 144 } 145 146 ScopedUtfChars path(env, pathStr); 147 if (path.c_str() == NULL) { 148 return false; 149 } 150 151 ScopedUtfChars context(env, contextStr); 152 if (context.c_str() == NULL) { 153 return false; 154 } 155 156 // GetStringUTFChars returns const char * yet setfilecon needs char * 157 char *tmp = const_cast<char *>(context.c_str()); 158 int ret = setfilecon(path.c_str(), tmp); 159 160 ALOGV("setFileCon(%s, %s) => %d", path.c_str(), context.c_str(), ret); 161 return (ret == 0) ? true : false; 162 } 163 164 /* 165 * Function: getFileCon 166 * Purpose: retrieves the context associated with the given path in the file system 167 * Parameters: 168 * path: given path in the file system 169 * Returns: 170 * string representing the security context string of the file object 171 * the string may be NULL if an error occured 172 * Exceptions: NullPointerException if the path object is null 173 */ 174 static jstring getFileCon(JNIEnv *env, jobject, jstring pathStr) { 175 if (isSELinuxDisabled) { 176 return NULL; 177 } 178 179 ScopedUtfChars path(env, pathStr); 180 if (path.c_str() == NULL) { 181 return NULL; 182 } 183 184 security_context_t tmp = NULL; 185 int ret = getfilecon(path.c_str(), &tmp); 186 Unique_SecurityContext context(tmp); 187 188 ScopedLocalRef<jstring> securityString(env, NULL); 189 if (ret != -1) { 190 securityString.reset(env->NewStringUTF(context.get())); 191 } 192 193 ALOGV("getFileCon(%s) => %s", path.c_str(), context.get()); 194 return securityString.release(); 195 } 196 197 /* 198 * Function: getCon 199 * Purpose: Get the context of the current process. 200 * Parameters: none 201 * Returns: a jstring representing the security context of the process, 202 * the jstring may be NULL if there was an error 203 * Exceptions: none 204 */ 205 static jstring getCon(JNIEnv *env, jobject) { 206 if (isSELinuxDisabled) { 207 return NULL; 208 } 209 210 security_context_t tmp = NULL; 211 int ret = getcon(&tmp); 212 Unique_SecurityContext context(tmp); 213 214 ScopedLocalRef<jstring> securityString(env, NULL); 215 if (ret != -1) { 216 securityString.reset(env->NewStringUTF(context.get())); 217 } 218 219 ALOGV("getCon() => %s", context.get()); 220 return securityString.release(); 221 } 222 223 /* 224 * Function: getPidCon 225 * Purpose: Get the context of a process identified by its pid 226 * Parameters: 227 * pid: a jint representing the process 228 * Returns: a jstring representing the security context of the pid, 229 * the jstring may be NULL if there was an error 230 * Exceptions: none 231 */ 232 static jstring getPidCon(JNIEnv *env, jobject, jint pid) { 233 if (isSELinuxDisabled) { 234 return NULL; 235 } 236 237 security_context_t tmp = NULL; 238 int ret = getpidcon(static_cast<pid_t>(pid), &tmp); 239 Unique_SecurityContext context(tmp); 240 241 ScopedLocalRef<jstring> securityString(env, NULL); 242 if (ret != -1) { 243 securityString.reset(env->NewStringUTF(context.get())); 244 } 245 246 ALOGV("getPidCon(%d) => %s", pid, context.get()); 247 return securityString.release(); 248 } 249 250 /* 251 * Function: checkSELinuxAccess 252 * Purpose: Check permissions between two security contexts. 253 * Parameters: subjectContextStr: subject security context as a string 254 * objectContextStr: object security context as a string 255 * objectClassStr: object's security class name as a string 256 * permissionStr: permission name as a string 257 * Returns: boolean: (true) if permission was granted, (false) otherwise 258 * Exceptions: None 259 */ 260 static jboolean checkSELinuxAccess(JNIEnv *env, jobject, jstring subjectContextStr, 261 jstring objectContextStr, jstring objectClassStr, jstring permissionStr) { 262 if (isSELinuxDisabled) { 263 return true; 264 } 265 266 ScopedUtfChars subjectContext(env, subjectContextStr); 267 if (subjectContext.c_str() == NULL) { 268 return false; 269 } 270 271 ScopedUtfChars objectContext(env, objectContextStr); 272 if (objectContext.c_str() == NULL) { 273 return false; 274 } 275 276 ScopedUtfChars objectClass(env, objectClassStr); 277 if (objectClass.c_str() == NULL) { 278 return false; 279 } 280 281 ScopedUtfChars permission(env, permissionStr); 282 if (permission.c_str() == NULL) { 283 return false; 284 } 285 286 char *tmp1 = const_cast<char *>(subjectContext.c_str()); 287 char *tmp2 = const_cast<char *>(objectContext.c_str()); 288 int accessGranted = selinux_check_access(tmp1, tmp2, objectClass.c_str(), permission.c_str(), 289 NULL); 290 291 ALOGV("checkSELinuxAccess(%s, %s, %s, %s) => %d", subjectContext.c_str(), objectContext.c_str(), 292 objectClass.c_str(), permission.c_str(), accessGranted); 293 294 return (accessGranted == 0) ? true : false; 295 } 296 297 /* 298 * Function: native_restorecon 299 * Purpose: restore default SELinux security context 300 * Parameters: pathname: the pathname for the file to be relabeled 301 * Returns: boolean: (true) file label successfully restored, (false) otherwise 302 * Exceptions: none 303 */ 304 static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr, jint flags) { 305 if (isSELinuxDisabled) { 306 return true; 307 } 308 309 ScopedUtfChars pathname(env, pathnameStr); 310 if (pathname.c_str() == NULL) { 311 ALOGV("restorecon(%p) => threw exception", pathnameStr); 312 return false; 313 } 314 315 int ret = selinux_android_restorecon(pathname.c_str(), flags); 316 ALOGV("restorecon(%s) => %d", pathname.c_str(), ret); 317 return (ret == 0); 318 } 319 320 /* 321 * JNI registration. 322 */ 323 static const JNINativeMethod method_table[] = { 324 /* name, signature, funcPtr */ 325 { "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess }, 326 { "getContext" , "()Ljava/lang/String;" , (void*)getCon }, 327 { "getFileContext" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)getFileCon }, 328 { "getPeerContext" , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon }, 329 { "getPidContext" , "(I)Ljava/lang/String;" , (void*)getPidCon }, 330 { "isSELinuxEnforced" , "()Z" , (void*)isSELinuxEnforced}, 331 { "isSELinuxEnabled" , "()Z" , (void*)isSELinuxEnabled }, 332 { "native_restorecon" , "(Ljava/lang/String;I)Z" , (void*)native_restorecon}, 333 { "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon }, 334 { "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon }, 335 }; 336 337 static int log_callback(int type, const char *fmt, ...) { 338 va_list ap; 339 int priority; 340 341 switch (type) { 342 case SELINUX_WARNING: 343 priority = ANDROID_LOG_WARN; 344 break; 345 case SELINUX_INFO: 346 priority = ANDROID_LOG_INFO; 347 break; 348 default: 349 priority = ANDROID_LOG_ERROR; 350 break; 351 } 352 va_start(ap, fmt); 353 LOG_PRI_VA(priority, "SELinux", fmt, ap); 354 va_end(ap); 355 return 0; 356 } 357 358 int register_android_os_SELinux(JNIEnv *env) { 359 union selinux_callback cb; 360 cb.func_log = log_callback; 361 selinux_set_callback(SELINUX_CB_LOG, cb); 362 363 isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false; 364 365 return RegisterMethodsOrDie(env, "android/os/SELinux", method_table, NELEM(method_table)); 366 } 367 368 } 369