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 <nativehelper/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 <memory>
     27 #include <nativehelper/ScopedLocalRef.h>
     28 #include <nativehelper/ScopedUtfChars.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 std::unique_ptr<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     std::unique_ptr<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