diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp index b12fdfc9999b9..0a97f3922113e 100644 --- a/core/jni/android_os_SELinux.cpp +++ b/core/jni/android_os_SELinux.cpp @@ -23,428 +23,407 @@ #include "selinux/selinux.h" #include "selinux/android.h" #include +#include +#include +#include namespace android { - static jboolean isSELinuxDisabled = true; +struct SecurityContext_Delete { + void operator()(security_context_t p) const { + freecon(p); + } +}; +typedef UniquePtr Unique_SecurityContext; - static void throw_NullPointerException(JNIEnv *env, const char* msg) { - jclass clazz; - clazz = env->FindClass("java/lang/NullPointerException"); - env->ThrowNew(clazz, msg); - } - - /* - * Function: isSELinuxEnabled - * Purpose: checks whether SELinux is enabled/disbaled - * Parameters: none - * Return value : true (enabled) or false (disabled) - * Exceptions: none - */ - static jboolean isSELinuxEnabled(JNIEnv *env, jobject classz) { +static jboolean isSELinuxDisabled = true; +/* + * Function: isSELinuxEnabled + * Purpose: checks whether SELinux is enabled/disbaled + * Parameters: none + * Return value : true (enabled) or false (disabled) + * Exceptions: none + */ +static jboolean isSELinuxEnabled(JNIEnv *env, jobject) { return !isSELinuxDisabled; - } +} - /* - * Function: isSELinuxEnforced - * Purpose: return the current SELinux enforce mode - * Parameters: none - * Return value: true (enforcing) or false (permissive) - * Exceptions: none - */ - static jboolean isSELinuxEnforced(JNIEnv *env, jobject clazz) { +/* + * Function: isSELinuxEnforced + * Purpose: return the current SELinux enforce mode + * Parameters: none + * Return value: true (enforcing) or false (permissive) + * Exceptions: none + */ +static jboolean isSELinuxEnforced(JNIEnv *env, jobject) { return (security_getenforce() == 1) ? true : false; - } +} - /* - * Function: setSELinuxEnforce - * Purpose: set the SE Linux enforcing mode - * Parameters: true (enforcing) or false (permissive) - * Return value: true (success) or false (fail) - * Exceptions: none - */ - static jboolean setSELinuxEnforce(JNIEnv *env, jobject clazz, jboolean value) { - if (isSELinuxDisabled) - return false; +/* + * Function: setSELinuxEnforce + * Purpose: set the SE Linux enforcing mode + * Parameters: true (enforcing) or false (permissive) + * Return value: true (success) or false (fail) + * Exceptions: none + */ +static jboolean setSELinuxEnforce(JNIEnv *env, jobject, jboolean value) { + if (isSELinuxDisabled) { + return false; + } - int enforce = (value) ? 1 : 0; + int enforce = value ? 1 : 0; return (security_setenforce(enforce) != -1) ? true : false; - } +} - /* - * Function: getPeerCon - * Purpose: retrieves security context of peer socket - * Parameters: - * fileDescriptor: peer socket file as a FileDescriptor object - * Returns: jstring representing the security_context of socket or NULL if error - * Exceptions: NullPointerException if fileDescriptor object is NULL - */ - static jstring getPeerCon(JNIEnv *env, jobject clazz, jobject fileDescriptor) { - if (isSELinuxDisabled) - return NULL; +/* + * Function: getPeerCon + * Purpose: retrieves security context of peer socket + * Parameters: + * fileDescriptor: peer socket file as a FileDescriptor object + * Returns: jstring representing the security_context of socket or NULL if error + * Exceptions: NullPointerException if fileDescriptor object is NULL + */ +static jstring getPeerCon(JNIEnv *env, jobject, jobject fileDescriptor) { + if (isSELinuxDisabled) { + return NULL; + } if (fileDescriptor == NULL) { - throw_NullPointerException(env, "Trying to check security context of a null peer socket."); - return NULL; + jniThrowNullPointerException(env, + "Trying to check security context of a null peer socket."); + return NULL; } - security_context_t context = NULL; - jstring securityString = NULL; - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (env->ExceptionOccurred() != NULL) { - ALOGE("There was an issue with retrieving the file descriptor"); - goto bail; + ALOGE("getPeerCon => getFD for %p failed", fileDescriptor); + return NULL; } - if (getpeercon(fd, &context) == -1) - goto bail; + security_context_t tmp; + int ret = getpeercon(fd, &tmp); + Unique_SecurityContext context(tmp); - ALOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context); - - securityString = env->NewStringUTF(context); - - bail: - if (context != NULL) - freecon(context); - - return securityString; - } - - /* - * Function: setFSCreateCon - * Purpose: set security context used for creating a new file system object - * Parameters: - * context: security_context_t representing the new context of a file system object, - * set to NULL to return to the default policy behavior - * Returns: true on success, false on error - * Exception: none - */ - static jboolean setFSCreateCon(JNIEnv *env, jobject clazz, jstring context) { - if (isSELinuxDisabled) - return false; - - char * securityContext = NULL; - const char *constant_securityContext = NULL; - - if (context != NULL) { - constant_securityContext = env->GetStringUTFChars(context, NULL); - - // GetStringUTFChars returns const char * yet setfscreatecon needs char * - securityContext = const_cast(constant_securityContext); + ScopedLocalRef contextStr(env, NULL); + if (ret != -1) { + contextStr.reset(env->NewStringUTF(context.get())); } - int ret; - if ((ret = setfscreatecon(securityContext)) == -1) - goto bail; + ALOGV("getPeerCon(%d) => %s", fd, contextStr.get()); + return contextStr.release(); +} - ALOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context); +/* + * Function: setFSCreateCon + * Purpose: set security context used for creating a new file system object + * Parameters: + * context: security_context_t representing the new context of a file system object, + * set to NULL to return to the default policy behavior + * Returns: true on success, false on error + * Exception: none + */ +static jboolean setFSCreateCon(JNIEnv *env, jobject, jstring contextStr) { + if (isSELinuxDisabled) { + return false; + } - bail: - if (constant_securityContext != NULL) - env->ReleaseStringUTFChars(context, constant_securityContext); + UniquePtr context; + const char* context_c_str = NULL; + if (contextStr != NULL) { + context.reset(new ScopedUtfChars(env, contextStr)); + context_c_str = context->c_str(); + if (context_c_str == NULL) { + return false; + } + } + + int ret = setfscreatecon(const_cast(context_c_str)); + + ALOGV("setFSCreateCon(%s) => %d", context_c_str, ret); return (ret == 0) ? true : false; - } +} - /* - * Function: setFileCon - * Purpose: set the security context of a file object - * Parameters: - * path: the location of the file system object - * con: the new security context of the file system object - * Returns: true on success, false on error - * Exception: NullPointerException is thrown if either path or context strign are NULL - */ - static jboolean setFileCon(JNIEnv *env, jobject clazz, jstring path, jstring con) { - if (isSELinuxDisabled) - return false; - - if (path == NULL) { - throw_NullPointerException(env, "Trying to change the security context of a NULL file object."); - return false; +/* + * Function: setFileCon + * Purpose: set the security context of a file object + * Parameters: + * path: the location of the file system object + * context: the new security context of the file system object + * Returns: true on success, false on error + * Exception: NullPointerException is thrown if either path or context strign are NULL + */ +static jboolean setFileCon(JNIEnv *env, jobject, jstring pathStr, jstring contextStr) { + if (isSELinuxDisabled) { + return false; } - if (con == NULL) { - throw_NullPointerException(env, "Trying to set the security context of a file object with NULL."); - return false; + ScopedUtfChars path(env, pathStr); + if (path.c_str() == NULL) { + return false; } - const char *objectPath = env->GetStringUTFChars(path, NULL); - const char *constant_con = env->GetStringUTFChars(con, NULL); + ScopedUtfChars context(env, contextStr); + if (context.c_str() == NULL) { + return false; + } // GetStringUTFChars returns const char * yet setfilecon needs char * - char *newCon = const_cast(constant_con); + char *tmp = const_cast(context.c_str()); + int ret = setfilecon(path.c_str(), tmp); - int ret; - if ((ret = setfilecon(objectPath, newCon)) == -1) - goto bail; - - ALOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath); - - bail: - env->ReleaseStringUTFChars(path, objectPath); - env->ReleaseStringUTFChars(con, constant_con); + ALOGV("setFileCon(%s, %s) => %d", path.c_str(), context.c_str(), ret); return (ret == 0) ? true : false; - } +} - /* - * Function: getFileCon - * Purpose: retrieves the context associated with the given path in the file system - * Parameters: - * path: given path in the file system - * Returns: - * string representing the security context string of the file object - * the string may be NULL if an error occured - * Exceptions: NullPointerException if the path object is null - */ - static jstring getFileCon(JNIEnv *env, jobject clazz, jstring path) { - if (isSELinuxDisabled) - return NULL; - - if (path == NULL) { - throw_NullPointerException(env, "Trying to check security context of a null path."); - return NULL; +/* + * Function: getFileCon + * Purpose: retrieves the context associated with the given path in the file system + * Parameters: + * path: given path in the file system + * Returns: + * string representing the security context string of the file object + * the string may be NULL if an error occured + * Exceptions: NullPointerException if the path object is null + */ +static jstring getFileCon(JNIEnv *env, jobject, jstring pathStr) { + if (isSELinuxDisabled) { + return NULL; } - const char *objectPath = env->GetStringUTFChars(path, NULL); + ScopedUtfChars path(env, pathStr); + if (path.c_str() == NULL) { + return NULL; + } - security_context_t context = NULL; - jstring securityString = NULL; + security_context_t tmp; + int ret = getfilecon(path.c_str(), &tmp); + Unique_SecurityContext context(tmp); - if (getfilecon(objectPath, &context) == -1) - goto bail; + ScopedLocalRef securityString(env, NULL); + if (ret != -1) { + securityString.reset(env->NewStringUTF(context.get())); + } - ALOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath); + ALOGV("getFileCon(%s) => %s", path.c_str(), context.get()); + return securityString.release(); +} - securityString = env->NewStringUTF(context); +/* + * Function: getCon + * Purpose: Get the context of the current process. + * Parameters: none + * Returns: a jstring representing the security context of the process, + * the jstring may be NULL if there was an error + * Exceptions: none + */ +static jstring getCon(JNIEnv *env, jobject) { + if (isSELinuxDisabled) { + return NULL; + } - bail: - if (context != NULL) - freecon(context); + security_context_t tmp; + int ret = getcon(&tmp); + Unique_SecurityContext context(tmp); - env->ReleaseStringUTFChars(path, objectPath); + ScopedLocalRef securityString(env, NULL); + if (ret != -1) { + securityString.reset(env->NewStringUTF(context.get())); + } - return securityString; - } + ALOGV("getCon() => %s", context.get()); + return securityString.release(); +} - /* - * Function: getCon - * Purpose: Get the context of the current process. - * Parameters: none - * Returns: a jstring representing the security context of the process, - * the jstring may be NULL if there was an error - * Exceptions: none - */ - static jstring getCon(JNIEnv *env, jobject clazz) { - if (isSELinuxDisabled) - return NULL; +/* + * Function: getPidCon + * Purpose: Get the context of a process identified by its pid + * Parameters: + * pid: a jint representing the process + * Returns: a jstring representing the security context of the pid, + * the jstring may be NULL if there was an error + * Exceptions: none + */ +static jstring getPidCon(JNIEnv *env, jobject, jint pid) { + if (isSELinuxDisabled) { + return NULL; + } - security_context_t context = NULL; - jstring securityString = NULL; + security_context_t tmp; + int ret = getpidcon(static_cast(pid), &tmp); + Unique_SecurityContext context(tmp); - if (getcon(&context) == -1) - goto bail; + ScopedLocalRef securityString(env, NULL); + if (ret != -1) { + securityString.reset(env->NewStringUTF(context.get())); + } - ALOGV("getCon: Successfully retrieved context '%s'", context); + ALOGV("getPidCon(%d) => %s", pid, context.get()); + return securityString.release(); +} - securityString = env->NewStringUTF(context); - - bail: - if (context != NULL) - freecon(context); - - return securityString; - } - - /* - * Function: getPidCon - * Purpose: Get the context of a process identified by its pid - * Parameters: - * pid: a jint representing the process - * Returns: a jstring representing the security context of the pid, - * the jstring may be NULL if there was an error - * Exceptions: none - */ - static jstring getPidCon(JNIEnv *env, jobject clazz, jint pid) { - if (isSELinuxDisabled) - return NULL; - - security_context_t context = NULL; - jstring securityString = NULL; - - pid_t checkPid = (pid_t)pid; - - if (getpidcon(checkPid, &context) == -1) - goto bail; - - ALOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid); - - securityString = env->NewStringUTF(context); - - bail: - if (context != NULL) - freecon(context); - - return securityString; - } - - /* - * Function: getBooleanNames - * Purpose: Gets a list of the SELinux boolean names. - * Parameters: None - * Returns: an array of strings containing the SELinux boolean names. - * returns NULL string on error - * Exceptions: None - */ - static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv clazz) { - if (isSELinuxDisabled) - return NULL; +/* + * Function: getBooleanNames + * Purpose: Gets a list of the SELinux boolean names. + * Parameters: None + * Returns: an array of strings containing the SELinux boolean names. + * returns NULL string on error + * Exceptions: None + */ +static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv) { + if (isSELinuxDisabled) { + return NULL; + } char **list; - int i, len, ret; - jclass stringClass; - jobjectArray stringArray = NULL; + int len; + if (security_get_boolean_names(&list, &len) == -1) { + return NULL; + } - if (security_get_boolean_names(&list, &len) == -1) - return NULL; - - stringClass = env->FindClass("java/lang/String"); - stringArray = env->NewObjectArray(len, stringClass, env->NewStringUTF("")); - for (i = 0; i < len; i++) { - jstring obj; - obj = env->NewStringUTF(list[i]); - env->SetObjectArrayElement(stringArray, i, obj); - env->DeleteLocalRef(obj); - free(list[i]); + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray stringArray = env->NewObjectArray(len, stringClass, NULL); + for (int i = 0; i < len; i++) { + ScopedLocalRef obj(env, env->NewStringUTF(list[i])); + env->SetObjectArrayElement(stringArray, i, obj.get()); + free(list[i]); } free(list); return stringArray; - } +} - /* - * Function: getBooleanValue - * Purpose: Gets the value for the given SELinux boolean name. - * Parameters: - * String: The name of the SELinux boolean. - * Returns: a boolean: (true) boolean is set or (false) it is not. - * Exceptions: None - */ - static jboolean getBooleanValue(JNIEnv *env, jobject clazz, jstring name) { - if (isSELinuxDisabled) - return false; +/* + * Function: getBooleanValue + * Purpose: Gets the value for the given SELinux boolean name. + * Parameters: + * String: The name of the SELinux boolean. + * Returns: a boolean: (true) boolean is set or (false) it is not. + * Exceptions: None + */ +static jboolean getBooleanValue(JNIEnv *env, jobject, jstring nameStr) { + if (isSELinuxDisabled) { + return false; + } - const char *boolean_name; - int ret; + if (nameStr == NULL) { + return false; + } - if (name == NULL) - return false; - boolean_name = env->GetStringUTFChars(name, NULL); - ret = security_get_boolean_active(boolean_name); - env->ReleaseStringUTFChars(name, boolean_name); + ScopedUtfChars name(env, nameStr); + int ret = security_get_boolean_active(name.c_str()); + + ALOGV("getBooleanValue(%s) => %d", name.c_str(), ret); return (ret == 1) ? true : false; - } +} - /* - * Function: setBooleanNames - * Purpose: Sets the value for the given SELinux boolean name. - * Parameters: - * String: The name of the SELinux boolean. - * Boolean: The new value of the SELinux boolean. - * Returns: a boolean indicating whether or not the operation succeeded. - * Exceptions: None - */ - static jboolean setBooleanValue(JNIEnv *env, jobject clazz, jstring name, jboolean value) { - if (isSELinuxDisabled) - return false; +/* + * Function: setBooleanNames + * Purpose: Sets the value for the given SELinux boolean name. + * Parameters: + * String: The name of the SELinux boolean. + * Boolean: The new value of the SELinux boolean. + * Returns: a boolean indicating whether or not the operation succeeded. + * Exceptions: None + */ +static jboolean setBooleanValue(JNIEnv *env, jobject, jstring nameStr, jboolean value) { + if (isSELinuxDisabled) { + return false; + } - const char *boolean_name = NULL; - int ret; + if (nameStr == NULL) { + return false; + } - if (name == NULL) - return false; - boolean_name = env->GetStringUTFChars(name, NULL); - ret = security_set_boolean(boolean_name, (value) ? 1 : 0); - env->ReleaseStringUTFChars(name, boolean_name); - if (ret) - return false; + ScopedUtfChars name(env, nameStr); + int ret = security_set_boolean(name.c_str(), value ? 1 : 0); + if (ret) { + return false; + } - if (security_commit_booleans() == -1) - return false; + if (security_commit_booleans() == -1) { + return false; + } return true; - } +} - /* - * Function: checkSELinuxAccess - * Purpose: Check permissions between two security contexts. - * Parameters: scon: subject security context as a string - * tcon: object security context as a string - * tclass: object's security class name as a string - * perm: permission name as a string - * Returns: boolean: (true) if permission was granted, (false) otherwise - * Exceptions: None - */ - static jboolean checkSELinuxAccess(JNIEnv *env, jobject clazz, jstring scon, jstring tcon, jstring tclass, jstring perm) { - if (isSELinuxDisabled) - return true; +/* + * Function: checkSELinuxAccess + * Purpose: Check permissions between two security contexts. + * Parameters: subjectContextStr: subject security context as a string + * objectContextStr: object security context as a string + * objectClassStr: object's security class name as a string + * permissionStr: permission name as a string + * Returns: boolean: (true) if permission was granted, (false) otherwise + * Exceptions: None + */ +static jboolean checkSELinuxAccess(JNIEnv *env, jobject, jstring subjectContextStr, + jstring objectContextStr, jstring objectClassStr, jstring permissionStr) { + if (isSELinuxDisabled) { + return true; + } - int accessGranted = -1; + ScopedUtfChars subjectContext(env, subjectContextStr); + if (subjectContext.c_str() == NULL) { + return false; + } - const char *const_scon, *const_tcon, *mytclass, *myperm; - char *myscon, *mytcon; + ScopedUtfChars objectContext(env, objectContextStr); + if (objectContext.c_str() == NULL) { + return false; + } - if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL) - goto bail; + ScopedUtfChars objectClass(env, objectClassStr); + if (objectClass.c_str() == NULL) { + return false; + } - const_scon = env->GetStringUTFChars(scon, NULL); - const_tcon = env->GetStringUTFChars(tcon, NULL); - mytclass = env->GetStringUTFChars(tclass, NULL); - myperm = env->GetStringUTFChars(perm, NULL); + ScopedUtfChars permission(env, permissionStr); + if (permission.c_str() == NULL) { + return false; + } - // selinux_check_access needs char* for some - myscon = const_cast(const_scon); - mytcon = const_cast(const_tcon); + char *tmp1 = const_cast(subjectContext.c_str()); + char *tmp2 = const_cast(objectContext.c_str()); + int accessGranted = selinux_check_access(tmp1, tmp2, objectClass.c_str(), permission.c_str(), + NULL); - accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL); + ALOGV("checkSELinuxAccess(%s, %s, %s, %s) => %d", subjectContext.c_str(), objectContext.c_str(), + objectClass.c_str(), permission.c_str(), accessGranted); - ALOGV("selinux_check_access returned %d", accessGranted); - - env->ReleaseStringUTFChars(scon, const_scon); - env->ReleaseStringUTFChars(tcon, const_tcon); - env->ReleaseStringUTFChars(tclass, mytclass); - env->ReleaseStringUTFChars(perm, myperm); - - bail: return (accessGranted == 0) ? true : false; - } +} - /* - * Function: native_restorecon - * Purpose: restore default SELinux security context - * Parameters: pathname: the pathname for the file to be relabeled - * Returns: boolean: (true) file label successfully restored, (false) otherwise - * Exceptions: none - */ - static jboolean native_restorecon(JNIEnv *env, jobject clazz, jstring pathname) { - if (isSELinuxDisabled) - return true; +/* + * Function: native_restorecon + * Purpose: restore default SELinux security context + * Parameters: pathname: the pathname for the file to be relabeled + * Returns: boolean: (true) file label successfully restored, (false) otherwise + * Exceptions: none + */ +static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr) { + if (isSELinuxDisabled) { + return true; + } - const char *file = const_cast(env->GetStringUTFChars(pathname, NULL)); - int ret = selinux_android_restorecon(file); - env->ReleaseStringUTFChars(pathname, file); + ScopedUtfChars pathname(env, pathnameStr); + if (pathname.c_str() == NULL) { + ALOGV("restorecon(%p) => threw exception", pathname); + return false; + } + + int ret = selinux_android_restorecon(pathname.c_str()); + ALOGV("restorecon(%s) => %d", pathname.c_str(), ret); return (ret == 0); - } - - /* - * JNI registration. - */ - static JNINativeMethod method_table[] = { +} +/* + * JNI registration. + */ +static JNINativeMethod method_table[] = { /* name, signature, funcPtr */ { "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess }, { "getBooleanNames" , "()[Ljava/lang/String;" , (void*)getBooleanNames }, @@ -460,25 +439,25 @@ namespace android { { "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon }, { "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon }, { "setSELinuxEnforce" , "(Z)Z" , (void*)setSELinuxEnforce}, - }; +}; - static int log_callback(int type, const char *fmt, ...) { +static int log_callback(int type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); LOG_PRI_VA(ANDROID_LOG_ERROR, "SELinux", fmt, ap); va_end(ap); return 0; - } +} - int register_android_os_SELinux(JNIEnv *env) { +int register_android_os_SELinux(JNIEnv *env) { union selinux_callback cb; cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false; - return AndroidRuntime::registerNativeMethods( - env, "android/os/SELinux", - method_table, NELEM(method_table)); - } + return AndroidRuntime::registerNativeMethods(env, "android/os/SELinux", method_table, + NELEM(method_table)); +} + }