Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b2d0563 authored by Kenny Root's avatar Kenny Root Committed by android code review
Browse files

Merge "Add JNI bindings for some of the libselinux interfaces."

parents 3e3d641c c07fca38
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
package android.os;

import java.io.FileDescriptor;

/**
 * This class provides access to the centralized jni bindings for
 * SELinux interaction.
 * {@hide}
 */
public class SELinux {

    /**
     * Determine whether SELinux is disabled or enabled.
     * @return a boolean indicating whether SELinux is enabled.
     */
    public static final native boolean isSELinuxEnabled();

    /**
     * Determine whether SELinux is permissive or enforcing.
     * @return a boolean indicating whether SELinux is enforcing.
     */
    public static final native boolean isSELinuxEnforced();

    /**
     * Set whether SELinux is permissive or enforcing.
     * @param boolean representing whether to set SELinux to enforcing
     * @return a boolean representing whether the desired mode was set
     */
    public static final native boolean setSELinuxEnforce(boolean value);

    /**
     * Sets the security context for newly created file objects.
     * @param context a security context given as a String.
     * @return a boolean indicating whether the operation succeeded.
     */
    public static final native boolean setFSCreateContext(String context);

    /**
     * Change the security context of an existing file object.
     * @param path representing the path of file object to relabel.
     * @param con new security context given as a String.
     * @return a boolean indicating whether the operation succeeded.
     */
    public static final native boolean setFileContext(String path, String context);

    /**
     * Get the security context of a file object.
     * @param path the pathname of the file object.
     * @return a security context given as a String.
     */
    public static final native String getFileContext(String path);

    /**
     * Get the security context of a peer socket.
     * @param fd FileDescriptor class of the peer socket.
     * @return a String representing the peer socket security context.
     */
    public static final native String getPeerContext(FileDescriptor fd);

    /**
     * Gets the security context of the current process.
     * @return a String representing the security context of the current process.
     */
    public static final native String getContext();

    /**
     * Gets the security context of a given process id.
     * Use of this function is discouraged for Binder transactions.
     * Use Binder.getCallingSecctx() instead.
     * @param pid an int representing the process id to check.
     * @return a String representing the security context of the given pid.
     */
    public static final native String getPidContext(int pid);

    /**
     * Gets a list of the SELinux boolean names.
     * @return an array of strings containing the SELinux boolean names.
     */
    public static final native String[] getBooleanNames();

    /**
     * Gets the value for the given SELinux boolean name.
     * @param String The name of the SELinux boolean.
     * @return a boolean indicating whether the SELinux boolean is set.
     */
    public static final native boolean getBooleanValue(String name);

    /**
     * Sets the value for the given SELinux boolean name.
     * @param String The name of the SELinux boolean.
     * @param Boolean The new value of the SELinux boolean.
     * @return a boolean indicating whether or not the operation succeeded.
     */
    public static final native boolean setBooleanValue(String name, boolean value);

    /**
     * Check permissions between two security contexts.
     * @param scon The source or subject security context.
     * @param tcon The target or object security context.
     * @param tclass The object security class name.
     * @param perm The permission name.
     * @return a boolean indicating whether permission was granted.
     */
    public static final native boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm);
}
+8 −1
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ LOCAL_SRC_FILES:= \
	android_os_MessageQueue.cpp \
	android_os_ParcelFileDescriptor.cpp \
	android_os_Power.cpp \
	android_os_SELinux.cpp \
	android_os_StatFs.cpp \
	android_os_SystemClock.cpp \
	android_os_SystemProperties.cpp \
@@ -216,7 +217,13 @@ LOCAL_SHARED_LIBRARIES := \
	libnfc_ndef \
	libusbhost \
	libharfbuzz \
	libz \
	libz

ifeq ($(HAVE_SELINUX),true)
LOCAL_C_INCLUDES += external/libselinux/include
LOCAL_SHARED_LIBRARIES += libselinux
LOCAL_CFLAGS += -DHAVE_SELINUX
endif # HAVE_SELINUX

ifeq ($(USE_OPENGL_RENDERER),true)
	LOCAL_SHARED_LIBRARIES += libhwui
+2 −0
Original line number Diff line number Diff line
@@ -135,6 +135,7 @@ extern int register_android_os_Debug(JNIEnv* env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_ParcelFileDescriptor(JNIEnv *env);
extern int register_android_os_Power(JNIEnv *env);
extern int register_android_os_SELinux(JNIEnv* env);
extern int register_android_os_StatFs(JNIEnv *env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
@@ -1153,6 +1154,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_os_MessageQueue),
    REG_JNI(register_android_os_ParcelFileDescriptor),
    REG_JNI(register_android_os_Power),
    REG_JNI(register_android_os_SELinux),
    REG_JNI(register_android_os_StatFs),
    REG_JNI(register_android_os_UEventObserver),
    REG_JNI(register_android_net_LocalSocketImpl),
+502 −0
Original line number Diff line number Diff line
#define LOG_TAG "SELinuxJNI"
#include <utils/Log.h>

#include "JNIHelp.h"
#include "jni.h"
#include "android_runtime/AndroidRuntime.h"
#ifdef HAVE_SELINUX
#include "selinux/selinux.h"
#endif
#include <errno.h>

namespace android {

  static jboolean isSELinuxDisabled = true;

  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) {

    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) {
#ifdef HAVE_SELINUX
    return (security_getenforce() == 1) ? true : false;
#else
    return false;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return false;

    int enforce = (value) ? 1 : 0;

    return (security_setenforce(enforce) != -1) ? true : false;
#else
    return false;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return NULL;

    if (fileDescriptor == NULL) {
      throw_NullPointerException(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) {
      LOGE("There was an issue with retrieving the file descriptor");
      goto bail;
    }

    if (getpeercon(fd, &context) == -1)
      goto bail;

    LOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context);

    securityString = env->NewStringUTF(context);

  bail:
    if (context != NULL)
      freecon(context);

    return securityString;
#else
    return NULL;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    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<char *>(constant_securityContext);
    }

    int ret;
    if ((ret = setfscreatecon(securityContext)) == -1)
      goto bail;

    LOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context);

  bail:
    if (constant_securityContext != NULL)
      env->ReleaseStringUTFChars(context, constant_securityContext);

    return (ret == 0) ? true : false;
#else
    return false;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return false;

    if (path == NULL) {
      throw_NullPointerException(env, "Trying to change the security context of a NULL file object.");
      return false;
    }

    if (con == NULL) {
      throw_NullPointerException(env, "Trying to set the security context of a file object with NULL.");
      return false;
    }

    const char *objectPath = env->GetStringUTFChars(path, NULL);
    const char *constant_con = env->GetStringUTFChars(con, NULL);

    // GetStringUTFChars returns const char * yet setfilecon needs char *
    char *newCon = const_cast<char *>(constant_con);

    int ret;
    if ((ret = setfilecon(objectPath, newCon)) == -1)
      goto bail;

    LOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath);

  bail:
    env->ReleaseStringUTFChars(path, objectPath);
    env->ReleaseStringUTFChars(con, constant_con);
    return (ret == 0) ? true : false;
#else
    return false;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return NULL;

    if (path == NULL) {
      throw_NullPointerException(env, "Trying to check security context of a null path.");
      return NULL;
    }

    const char *objectPath = env->GetStringUTFChars(path, NULL);

    security_context_t context = NULL;
    jstring securityString = NULL;

    if (getfilecon(objectPath, &context) == -1)
      goto bail;

    LOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath);

    securityString = env->NewStringUTF(context);

  bail:
    if (context != NULL)
      freecon(context);

    env->ReleaseStringUTFChars(path, objectPath);

    return securityString;
#else
    return NULL;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return NULL;

    security_context_t context = NULL;
    jstring securityString = NULL;

    if (getcon(&context) == -1)
      goto bail;

    LOGV("getCon: Successfully retrieved context '%s'", context);

    securityString = env->NewStringUTF(context);

  bail:
    if (context != NULL)
      freecon(context);

    return securityString;
#else
    return NULL;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    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;

    LOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid);

    securityString = env->NewStringUTF(context);

  bail:
    if (context != NULL)
      freecon(context);

    return securityString;
#else
    return NULL;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return NULL;

    char **list;
    int i, len, ret;
    jclass stringClass;
    jobjectArray stringArray = 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]);
    }
    free(list);

    return stringArray;
#else
    return NULL;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return false;

    const char *boolean_name;
    int ret;

    if (name == NULL)
      return false;
    boolean_name = env->GetStringUTFChars(name, NULL);
    ret = security_get_boolean_active(boolean_name);
    env->ReleaseStringUTFChars(name, boolean_name);
    return (ret == 1) ? true : false;
#else
    return false;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return false;

    const char *boolean_name = NULL;
    int ret;

    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;

    if (security_commit_booleans() == -1)
      return false;

    return true;
#else
    return false;
#endif
  }

  /*
   * 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) {
#ifdef HAVE_SELINUX
    if (isSELinuxDisabled)
      return true;

    int accessGranted = -1;

    const char *const_scon, *const_tcon, *mytclass, *myperm;
    char *myscon, *mytcon;

    if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL)
      goto bail;

    const_scon = env->GetStringUTFChars(scon, NULL);
    const_tcon = env->GetStringUTFChars(tcon, NULL);
    mytclass   = env->GetStringUTFChars(tclass, NULL);
    myperm     = env->GetStringUTFChars(perm, NULL);

    // selinux_check_access needs char* for some
    myscon = const_cast<char *>(const_scon);
    mytcon = const_cast<char *>(const_tcon);

    accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL);

    LOGV("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;

#else
    return true;
#endif
  }

  /*
   * 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  },
    { "getBooleanValue"          , "(Ljava/lang/String;)Z"                        , (void*)getBooleanValue  },
    { "getContext"               , "()Ljava/lang/String;"                         , (void*)getCon           },
    { "getFileContext"           , "(Ljava/lang/String;)Ljava/lang/String;"       , (void*)getFileCon       },
    { "getPeerContext"           , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon       },
    { "getPidContext"            , "(I)Ljava/lang/String;"                        , (void*)getPidCon        },
    { "isSELinuxEnforced"        , "()Z"                                          , (void*)isSELinuxEnforced},
    { "isSELinuxEnabled"         , "()Z"                                          , (void*)isSELinuxEnabled },
    { "setBooleanValue"          , "(Ljava/lang/String;Z)Z"                       , (void*)setBooleanValue  },
    { "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, ...) {
    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) {
#ifdef HAVE_SELINUX
    union selinux_callback cb;
    cb.func_log = log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;

#endif
    return AndroidRuntime::registerNativeMethods(
         env, "android/os/SELinux",
         method_table, NELEM(method_table));
  }
}
+45 −0
Original line number Diff line number Diff line
package android.os;

import android.os.Process;
import android.os.SELinux;
import android.test.AndroidTestCase;
import static junit.framework.Assert.assertEquals;

public class SELinuxTest extends AndroidTestCase {

    public void testgetFileCon() {
        if(SELinux.isSELinuxEnabled() == false)
            return;

        String ctx = SELinux.getFileContext("/system/bin/toolbox");
        assertEquals(ctx, "u:object_r:system_file:s0");
    }

    public void testgetCon() {
        if(SELinux.isSELinuxEnabled() == false)
            return;

        String mycon = SELinux.getContext();
        assertEquals(mycon, "u:r:untrusted_app:s0:c33");
    }

    public void testgetPidCon() {
        if(SELinux.isSELinuxEnabled() == false)
            return;

        String mycon = SELinux.getPidContext(Process.myPid());
        assertEquals(mycon, "u:r:untrusted_app:s0:c33");
    }

    public void testcheckSELinuxAccess() {
        if(SELinux.isSELinuxEnabled() == false)
            return;

        String mycon = SELinux.getContext();
        boolean ret;
        ret = SELinux.checkSELinuxAccess(mycon, mycon, "process", "fork");
        assertEquals(ret,"true");
        ret = SELinux.checkSELinuxAccess(mycon, mycon, "memprotect", "mmap_zero");
        assertEquals(ret,"true");
    }
}