Home | History | Annotate | Download | only in jni
      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 "android_runtime/AndroidRuntime.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: setSELinuxEnforce
     65  * Purpose: set the SE Linux enforcing mode
     66  * Parameters: true (enforcing) or false (permissive)
     67  * Return value: true (success) or false (fail)
     68  * Exceptions: none
     69  */
     70 static jboolean setSELinuxEnforce(JNIEnv *env, jobject, jboolean value) {
     71     if (isSELinuxDisabled) {
     72         return false;
     73     }
     74 
     75     int enforce = value ? 1 : 0;
     76 
     77     return (security_setenforce(enforce) != -1) ? true : false;
     78 }
     79 
     80 /*
     81  * Function: getPeerCon
     82  * Purpose: retrieves security context of peer socket
     83  * Parameters:
     84  *        fileDescriptor: peer socket file as a FileDescriptor object
     85  * Returns: jstring representing the security_context of socket or NULL if error
     86  * Exceptions: NullPointerException if fileDescriptor object is NULL
     87  */
     88 static jstring getPeerCon(JNIEnv *env, jobject, jobject fileDescriptor) {
     89     if (isSELinuxDisabled) {
     90         return NULL;
     91     }
     92 
     93     if (fileDescriptor == NULL) {
     94         jniThrowNullPointerException(env,
     95                 "Trying to check security context of a null peer socket.");
     96         return NULL;
     97     }
     98 
     99     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    100     if (env->ExceptionOccurred() != NULL) {
    101         ALOGE("getPeerCon => getFD for %p failed", fileDescriptor);
    102         return NULL;
    103     }
    104 
    105     security_context_t tmp;
    106     int ret = getpeercon(fd, &tmp);
    107     Unique_SecurityContext context(tmp);
    108 
    109     ScopedLocalRef<jstring> contextStr(env, NULL);
    110     if (ret != -1) {
    111         contextStr.reset(env->NewStringUTF(context.get()));
    112     }
    113 
    114     ALOGV("getPeerCon(%d) => %s", fd, contextStr.get());
    115     return contextStr.release();
    116 }
    117 
    118 /*
    119  * Function: setFSCreateCon
    120  * Purpose: set security context used for creating a new file system object
    121  * Parameters:
    122  *       context: security_context_t representing the new context of a file system object,
    123  *                set to NULL to return to the default policy behavior
    124  * Returns: true on success, false on error
    125  * Exception: none
    126  */
    127 static jboolean setFSCreateCon(JNIEnv *env, jobject, jstring contextStr) {
    128     if (isSELinuxDisabled) {
    129         return false;
    130     }
    131 
    132     UniquePtr<ScopedUtfChars> context;
    133     const char* context_c_str = NULL;
    134     if (contextStr != NULL) {
    135         context.reset(new ScopedUtfChars(env, contextStr));
    136         context_c_str = context->c_str();
    137         if (context_c_str == NULL) {
    138             return false;
    139         }
    140     }
    141 
    142     int ret = setfscreatecon(const_cast<char *>(context_c_str));
    143 
    144     ALOGV("setFSCreateCon(%s) => %d", context_c_str, ret);
    145 
    146     return (ret == 0) ? true : false;
    147 }
    148 
    149 /*
    150  * Function: setFileCon
    151  * Purpose:  set the security context of a file object
    152  * Parameters:
    153  *       path: the location of the file system object
    154  *       context: the new security context of the file system object
    155  * Returns: true on success, false on error
    156  * Exception: NullPointerException is thrown if either path or context strign are NULL
    157  */
    158 static jboolean setFileCon(JNIEnv *env, jobject, jstring pathStr, jstring contextStr) {
    159     if (isSELinuxDisabled) {
    160         return false;
    161     }
    162 
    163     ScopedUtfChars path(env, pathStr);
    164     if (path.c_str() == NULL) {
    165         return false;
    166     }
    167 
    168     ScopedUtfChars context(env, contextStr);
    169     if (context.c_str() == NULL) {
    170         return false;
    171     }
    172 
    173     // GetStringUTFChars returns const char * yet setfilecon needs char *
    174     char *tmp = const_cast<char *>(context.c_str());
    175     int ret = setfilecon(path.c_str(), tmp);
    176 
    177     ALOGV("setFileCon(%s, %s) => %d", path.c_str(), context.c_str(), ret);
    178     return (ret == 0) ? true : false;
    179 }
    180 
    181 /*
    182  * Function: getFileCon
    183  * Purpose: retrieves the context associated with the given path in the file system
    184  * Parameters:
    185  *        path: given path in the file system
    186  * Returns:
    187  *        string representing the security context string of the file object
    188  *        the string may be NULL if an error occured
    189  * Exceptions: NullPointerException if the path object is null
    190  */
    191 static jstring getFileCon(JNIEnv *env, jobject, jstring pathStr) {
    192     if (isSELinuxDisabled) {
    193         return NULL;
    194     }
    195 
    196     ScopedUtfChars path(env, pathStr);
    197     if (path.c_str() == NULL) {
    198         return NULL;
    199     }
    200 
    201     security_context_t tmp;
    202     int ret = getfilecon(path.c_str(), &tmp);
    203     Unique_SecurityContext context(tmp);
    204 
    205     ScopedLocalRef<jstring> securityString(env, NULL);
    206     if (ret != -1) {
    207         securityString.reset(env->NewStringUTF(context.get()));
    208     }
    209 
    210     ALOGV("getFileCon(%s) => %s", path.c_str(), context.get());
    211     return securityString.release();
    212 }
    213 
    214 /*
    215  * Function: getCon
    216  * Purpose: Get the context of the current process.
    217  * Parameters: none
    218  * Returns: a jstring representing the security context of the process,
    219  *          the jstring may be NULL if there was an error
    220  * Exceptions: none
    221  */
    222 static jstring getCon(JNIEnv *env, jobject) {
    223     if (isSELinuxDisabled) {
    224         return NULL;
    225     }
    226 
    227     security_context_t tmp;
    228     int ret = getcon(&tmp);
    229     Unique_SecurityContext context(tmp);
    230 
    231     ScopedLocalRef<jstring> securityString(env, NULL);
    232     if (ret != -1) {
    233         securityString.reset(env->NewStringUTF(context.get()));
    234     }
    235 
    236     ALOGV("getCon() => %s", context.get());
    237     return securityString.release();
    238 }
    239 
    240 /*
    241  * Function: getPidCon
    242  * Purpose: Get the context of a process identified by its pid
    243  * Parameters:
    244  *            pid: a jint representing the process
    245  * Returns: a jstring representing the security context of the pid,
    246  *          the jstring may be NULL if there was an error
    247  * Exceptions: none
    248  */
    249 static jstring getPidCon(JNIEnv *env, jobject, jint pid) {
    250     if (isSELinuxDisabled) {
    251         return NULL;
    252     }
    253 
    254     security_context_t tmp;
    255     int ret = getpidcon(static_cast<pid_t>(pid), &tmp);
    256     Unique_SecurityContext context(tmp);
    257 
    258     ScopedLocalRef<jstring> securityString(env, NULL);
    259     if (ret != -1) {
    260         securityString.reset(env->NewStringUTF(context.get()));
    261     }
    262 
    263     ALOGV("getPidCon(%d) => %s", pid, context.get());
    264     return securityString.release();
    265 }
    266 
    267 /*
    268  * Function: getBooleanNames
    269  * Purpose: Gets a list of the SELinux boolean names.
    270  * Parameters: None
    271  * Returns: an array of strings  containing the SELinux boolean names.
    272  *          returns NULL string on error
    273  * Exceptions: None
    274  */
    275 static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv) {
    276     if (isSELinuxDisabled) {
    277         return NULL;
    278     }
    279 
    280     char **list;
    281     int len;
    282     if (security_get_boolean_names(&list, &len) == -1) {
    283         return NULL;
    284     }
    285 
    286     jclass stringClass = env->FindClass("java/lang/String");
    287     jobjectArray stringArray = env->NewObjectArray(len, stringClass, NULL);
    288     for (int i = 0; i < len; i++) {
    289         ScopedLocalRef<jstring> obj(env, env->NewStringUTF(list[i]));
    290         env->SetObjectArrayElement(stringArray, i, obj.get());
    291         free(list[i]);
    292     }
    293     free(list);
    294 
    295     return stringArray;
    296 }
    297 
    298 /*
    299  * Function: getBooleanValue
    300  * Purpose: Gets the value for the given SELinux boolean name.
    301  * Parameters:
    302  *            String: The name of the SELinux boolean.
    303  * Returns: a boolean: (true) boolean is set or (false) it is not.
    304  * Exceptions: None
    305  */
    306 static jboolean getBooleanValue(JNIEnv *env, jobject, jstring nameStr) {
    307     if (isSELinuxDisabled) {
    308         return false;
    309     }
    310 
    311     if (nameStr == NULL) {
    312         return false;
    313     }
    314 
    315     ScopedUtfChars name(env, nameStr);
    316     int ret = security_get_boolean_active(name.c_str());
    317 
    318     ALOGV("getBooleanValue(%s) => %d", name.c_str(), ret);
    319     return (ret == 1) ? true : false;
    320 }
    321 
    322 /*
    323  * Function: setBooleanNames
    324  * Purpose: Sets the value for the given SELinux boolean name.
    325  * Parameters:
    326  *            String: The name of the SELinux boolean.
    327  *            Boolean: The new value of the SELinux boolean.
    328  * Returns: a boolean indicating whether or not the operation succeeded.
    329  * Exceptions: None
    330  */
    331 static jboolean setBooleanValue(JNIEnv *env, jobject, jstring nameStr, jboolean value) {
    332     if (isSELinuxDisabled) {
    333         return false;
    334     }
    335 
    336     if (nameStr == NULL) {
    337         return false;
    338     }
    339 
    340     ScopedUtfChars name(env, nameStr);
    341     int ret = security_set_boolean(name.c_str(), value ? 1 : 0);
    342     if (ret) {
    343         return false;
    344     }
    345 
    346     if (security_commit_booleans() == -1) {
    347         return false;
    348     }
    349 
    350     return true;
    351 }
    352 
    353 /*
    354  * Function: checkSELinuxAccess
    355  * Purpose: Check permissions between two security contexts.
    356  * Parameters: subjectContextStr: subject security context as a string
    357  *             objectContextStr: object security context as a string
    358  *             objectClassStr: object's security class name as a string
    359  *             permissionStr: permission name as a string
    360  * Returns: boolean: (true) if permission was granted, (false) otherwise
    361  * Exceptions: None
    362  */
    363 static jboolean checkSELinuxAccess(JNIEnv *env, jobject, jstring subjectContextStr,
    364         jstring objectContextStr, jstring objectClassStr, jstring permissionStr) {
    365     if (isSELinuxDisabled) {
    366         return true;
    367     }
    368 
    369     ScopedUtfChars subjectContext(env, subjectContextStr);
    370     if (subjectContext.c_str() == NULL) {
    371         return false;
    372     }
    373 
    374     ScopedUtfChars objectContext(env, objectContextStr);
    375     if (objectContext.c_str() == NULL) {
    376         return false;
    377     }
    378 
    379     ScopedUtfChars objectClass(env, objectClassStr);
    380     if (objectClass.c_str() == NULL) {
    381         return false;
    382     }
    383 
    384     ScopedUtfChars permission(env, permissionStr);
    385     if (permission.c_str() == NULL) {
    386         return false;
    387     }
    388 
    389     char *tmp1 = const_cast<char *>(subjectContext.c_str());
    390     char *tmp2 = const_cast<char *>(objectContext.c_str());
    391     int accessGranted = selinux_check_access(tmp1, tmp2, objectClass.c_str(), permission.c_str(),
    392             NULL);
    393 
    394     ALOGV("checkSELinuxAccess(%s, %s, %s, %s) => %d", subjectContext.c_str(), objectContext.c_str(),
    395             objectClass.c_str(), permission.c_str(), accessGranted);
    396 
    397     return (accessGranted == 0) ? true : false;
    398 }
    399 
    400 /*
    401  * Function: native_restorecon
    402  * Purpose: restore default SELinux security context
    403  * Parameters: pathname: the pathname for the file to be relabeled
    404  * Returns: boolean: (true) file label successfully restored, (false) otherwise
    405  * Exceptions: none
    406  */
    407 static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr) {
    408     if (isSELinuxDisabled) {
    409         return true;
    410     }
    411 
    412     ScopedUtfChars pathname(env, pathnameStr);
    413     if (pathname.c_str() == NULL) {
    414         ALOGV("restorecon(%p) => threw exception", pathname);
    415         return false;
    416     }
    417 
    418     int ret = selinux_android_restorecon(pathname.c_str());
    419     ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
    420     return (ret == 0);
    421 }
    422 
    423 /*
    424  * JNI registration.
    425  */
    426 static JNINativeMethod method_table[] = {
    427     /* name,                     signature,                    funcPtr */
    428     { "checkSELinuxAccess"       , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
    429     { "getBooleanNames"          , "()[Ljava/lang/String;"                        , (void*)getBooleanNames  },
    430     { "getBooleanValue"          , "(Ljava/lang/String;)Z"                        , (void*)getBooleanValue  },
    431     { "getContext"               , "()Ljava/lang/String;"                         , (void*)getCon           },
    432     { "getFileContext"           , "(Ljava/lang/String;)Ljava/lang/String;"       , (void*)getFileCon       },
    433     { "getPeerContext"           , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon       },
    434     { "getPidContext"            , "(I)Ljava/lang/String;"                        , (void*)getPidCon        },
    435     { "isSELinuxEnforced"        , "()Z"                                          , (void*)isSELinuxEnforced},
    436     { "isSELinuxEnabled"         , "()Z"                                          , (void*)isSELinuxEnabled },
    437     { "native_restorecon"        , "(Ljava/lang/String;)Z"                        , (void*)native_restorecon},
    438     { "setBooleanValue"          , "(Ljava/lang/String;Z)Z"                       , (void*)setBooleanValue  },
    439     { "setFileContext"           , "(Ljava/lang/String;Ljava/lang/String;)Z"      , (void*)setFileCon       },
    440     { "setFSCreateContext"       , "(Ljava/lang/String;)Z"                        , (void*)setFSCreateCon   },
    441     { "setSELinuxEnforce"        , "(Z)Z"                                         , (void*)setSELinuxEnforce},
    442 };
    443 
    444 static int log_callback(int type, const char *fmt, ...) {
    445     va_list ap;
    446     va_start(ap, fmt);
    447     LOG_PRI_VA(ANDROID_LOG_ERROR, "SELinux", fmt, ap);
    448     va_end(ap);
    449     return 0;
    450 }
    451 
    452 int register_android_os_SELinux(JNIEnv *env) {
    453     union selinux_callback cb;
    454     cb.func_log = log_callback;
    455     selinux_set_callback(SELINUX_CB_LOG, cb);
    456 
    457     isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
    458 
    459     return AndroidRuntime::registerNativeMethods(env, "android/os/SELinux", method_table,
    460             NELEM(method_table));
    461 }
    462 
    463 }
    464