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 #ifdef HAVE_SELINUX
     24 #include "selinux/selinux.h"
     25 #include "selinux/android.h"
     26 #endif
     27 #include <errno.h>
     28 
     29 namespace android {
     30 
     31   static jboolean isSELinuxDisabled = true;
     32 
     33   static void throw_NullPointerException(JNIEnv *env, const char* msg) {
     34     jclass clazz;
     35     clazz = env->FindClass("java/lang/NullPointerException");
     36     env->ThrowNew(clazz, msg);
     37   }
     38 
     39   /*
     40    * Function: isSELinuxEnabled
     41    * Purpose:  checks whether SELinux is enabled/disbaled
     42    * Parameters: none
     43    * Return value : true (enabled) or false (disabled)
     44    * Exceptions: none
     45    */
     46   static jboolean isSELinuxEnabled(JNIEnv *env, jobject classz) {
     47 
     48     return !isSELinuxDisabled;
     49   }
     50 
     51   /*
     52    * Function: isSELinuxEnforced
     53    * Purpose: return the current SELinux enforce mode
     54    * Parameters: none
     55    * Return value: true (enforcing) or false (permissive)
     56    * Exceptions: none
     57    */
     58   static jboolean isSELinuxEnforced(JNIEnv *env, jobject clazz) {
     59 #ifdef HAVE_SELINUX
     60     return (security_getenforce() == 1) ? true : false;
     61 #else
     62     return false;
     63 #endif
     64   }
     65 
     66   /*
     67    * Function: setSELinuxEnforce
     68    * Purpose: set the SE Linux enforcing mode
     69    * Parameters: true (enforcing) or false (permissive)
     70    * Return value: true (success) or false (fail)
     71    * Exceptions: none
     72    */
     73   static jboolean setSELinuxEnforce(JNIEnv *env, jobject clazz, jboolean value) {
     74 #ifdef HAVE_SELINUX
     75     if (isSELinuxDisabled)
     76       return false;
     77 
     78     int enforce = (value) ? 1 : 0;
     79 
     80     return (security_setenforce(enforce) != -1) ? true : false;
     81 #else
     82     return false;
     83 #endif
     84   }
     85 
     86   /*
     87    * Function: getPeerCon
     88    * Purpose: retrieves security context of peer socket
     89    * Parameters:
     90    *        fileDescriptor: peer socket file as a FileDescriptor object
     91    * Returns: jstring representing the security_context of socket or NULL if error
     92    * Exceptions: NullPointerException if fileDescriptor object is NULL
     93    */
     94   static jstring getPeerCon(JNIEnv *env, jobject clazz, jobject fileDescriptor) {
     95 #ifdef HAVE_SELINUX
     96     if (isSELinuxDisabled)
     97       return NULL;
     98 
     99     if (fileDescriptor == NULL) {
    100       throw_NullPointerException(env, "Trying to check security context of a null peer socket.");
    101       return NULL;
    102     }
    103 
    104     security_context_t context = NULL;
    105     jstring securityString = NULL;
    106 
    107     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    108 
    109     if (env->ExceptionOccurred() != NULL) {
    110       ALOGE("There was an issue with retrieving the file descriptor");
    111       goto bail;
    112     }
    113 
    114     if (getpeercon(fd, &context) == -1)
    115       goto bail;
    116 
    117     ALOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context);
    118 
    119     securityString = env->NewStringUTF(context);
    120 
    121   bail:
    122     if (context != NULL)
    123       freecon(context);
    124 
    125     return securityString;
    126 #else
    127     return NULL;
    128 #endif
    129   }
    130 
    131   /*
    132    * Function: setFSCreateCon
    133    * Purpose: set security context used for creating a new file system object
    134    * Parameters:
    135    *       context: security_context_t representing the new context of a file system object,
    136    *                set to NULL to return to the default policy behavior
    137    * Returns: true on success, false on error
    138    * Exception: none
    139    */
    140   static jboolean setFSCreateCon(JNIEnv *env, jobject clazz, jstring context) {
    141 #ifdef HAVE_SELINUX
    142     if (isSELinuxDisabled)
    143       return false;
    144 
    145     char * securityContext = NULL;
    146     const char *constant_securityContext = NULL;
    147 
    148     if (context != NULL) {
    149       constant_securityContext = env->GetStringUTFChars(context, NULL);
    150 
    151       // GetStringUTFChars returns const char * yet setfscreatecon needs char *
    152       securityContext = const_cast<char *>(constant_securityContext);
    153     }
    154 
    155     int ret;
    156     if ((ret = setfscreatecon(securityContext)) == -1)
    157       goto bail;
    158 
    159     ALOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context);
    160 
    161   bail:
    162     if (constant_securityContext != NULL)
    163       env->ReleaseStringUTFChars(context, constant_securityContext);
    164 
    165     return (ret == 0) ? true : false;
    166 #else
    167     return false;
    168 #endif
    169   }
    170 
    171   /*
    172    * Function: setFileCon
    173    * Purpose:  set the security context of a file object
    174    * Parameters:
    175    *       path: the location of the file system object
    176    *       con: the new security context of the file system object
    177    * Returns: true on success, false on error
    178    * Exception: NullPointerException is thrown if either path or context strign are NULL
    179    */
    180   static jboolean setFileCon(JNIEnv *env, jobject clazz, jstring path, jstring con) {
    181 #ifdef HAVE_SELINUX
    182     if (isSELinuxDisabled)
    183       return false;
    184 
    185     if (path == NULL) {
    186       throw_NullPointerException(env, "Trying to change the security context of a NULL file object.");
    187       return false;
    188     }
    189 
    190     if (con == NULL) {
    191       throw_NullPointerException(env, "Trying to set the security context of a file object with NULL.");
    192       return false;
    193     }
    194 
    195     const char *objectPath = env->GetStringUTFChars(path, NULL);
    196     const char *constant_con = env->GetStringUTFChars(con, NULL);
    197 
    198     // GetStringUTFChars returns const char * yet setfilecon needs char *
    199     char *newCon = const_cast<char *>(constant_con);
    200 
    201     int ret;
    202     if ((ret = setfilecon(objectPath, newCon)) == -1)
    203       goto bail;
    204 
    205     ALOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath);
    206 
    207   bail:
    208     env->ReleaseStringUTFChars(path, objectPath);
    209     env->ReleaseStringUTFChars(con, constant_con);
    210     return (ret == 0) ? true : false;
    211 #else
    212     return false;
    213 #endif
    214   }
    215 
    216   /*
    217    * Function: getFileCon
    218    * Purpose: retrieves the context associated with the given path in the file system
    219    * Parameters:
    220    *        path: given path in the file system
    221    * Returns:
    222    *        string representing the security context string of the file object
    223    *        the string may be NULL if an error occured
    224    * Exceptions: NullPointerException if the path object is null
    225    */
    226   static jstring getFileCon(JNIEnv *env, jobject clazz, jstring path) {
    227 #ifdef HAVE_SELINUX
    228     if (isSELinuxDisabled)
    229       return NULL;
    230 
    231     if (path == NULL) {
    232       throw_NullPointerException(env, "Trying to check security context of a null path.");
    233       return NULL;
    234     }
    235 
    236     const char *objectPath = env->GetStringUTFChars(path, NULL);
    237 
    238     security_context_t context = NULL;
    239     jstring securityString = NULL;
    240 
    241     if (getfilecon(objectPath, &context) == -1)
    242       goto bail;
    243 
    244     ALOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath);
    245 
    246     securityString = env->NewStringUTF(context);
    247 
    248   bail:
    249     if (context != NULL)
    250       freecon(context);
    251 
    252     env->ReleaseStringUTFChars(path, objectPath);
    253 
    254     return securityString;
    255 #else
    256     return NULL;
    257 #endif
    258   }
    259 
    260   /*
    261    * Function: getCon
    262    * Purpose: Get the context of the current process.
    263    * Parameters: none
    264    * Returns: a jstring representing the security context of the process,
    265    *          the jstring may be NULL if there was an error
    266    * Exceptions: none
    267    */
    268   static jstring getCon(JNIEnv *env, jobject clazz) {
    269 #ifdef HAVE_SELINUX
    270     if (isSELinuxDisabled)
    271       return NULL;
    272 
    273     security_context_t context = NULL;
    274     jstring securityString = NULL;
    275 
    276     if (getcon(&context) == -1)
    277       goto bail;
    278 
    279     ALOGV("getCon: Successfully retrieved context '%s'", context);
    280 
    281     securityString = env->NewStringUTF(context);
    282 
    283   bail:
    284     if (context != NULL)
    285       freecon(context);
    286 
    287     return securityString;
    288 #else
    289     return NULL;
    290 #endif
    291   }
    292 
    293   /*
    294    * Function: getPidCon
    295    * Purpose: Get the context of a process identified by its pid
    296    * Parameters:
    297    *            pid: a jint representing the process
    298    * Returns: a jstring representing the security context of the pid,
    299    *          the jstring may be NULL if there was an error
    300    * Exceptions: none
    301    */
    302   static jstring getPidCon(JNIEnv *env, jobject clazz, jint pid) {
    303 #ifdef HAVE_SELINUX
    304     if (isSELinuxDisabled)
    305       return NULL;
    306 
    307     security_context_t context = NULL;
    308     jstring securityString = NULL;
    309 
    310     pid_t checkPid = (pid_t)pid;
    311 
    312     if (getpidcon(checkPid, &context) == -1)
    313       goto bail;
    314 
    315     ALOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid);
    316 
    317     securityString = env->NewStringUTF(context);
    318 
    319   bail:
    320     if (context != NULL)
    321       freecon(context);
    322 
    323     return securityString;
    324 #else
    325     return NULL;
    326 #endif
    327   }
    328 
    329   /*
    330    * Function: getBooleanNames
    331    * Purpose: Gets a list of the SELinux boolean names.
    332    * Parameters: None
    333    * Returns: an array of strings  containing the SELinux boolean names.
    334    *          returns NULL string on error
    335    * Exceptions: None
    336    */
    337   static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv clazz) {
    338 #ifdef HAVE_SELINUX
    339     if (isSELinuxDisabled)
    340       return NULL;
    341 
    342     char **list;
    343     int i, len, ret;
    344     jclass stringClass;
    345     jobjectArray stringArray = NULL;
    346 
    347     if (security_get_boolean_names(&list, &len) == -1)
    348       return NULL;
    349 
    350     stringClass = env->FindClass("java/lang/String");
    351     stringArray = env->NewObjectArray(len, stringClass, env->NewStringUTF(""));
    352     for (i = 0; i < len; i++) {
    353       jstring obj;
    354       obj = env->NewStringUTF(list[i]);
    355       env->SetObjectArrayElement(stringArray, i, obj);
    356       env->DeleteLocalRef(obj);
    357       free(list[i]);
    358     }
    359     free(list);
    360 
    361     return stringArray;
    362 #else
    363     return NULL;
    364 #endif
    365   }
    366 
    367   /*
    368    * Function: getBooleanValue
    369    * Purpose: Gets the value for the given SELinux boolean name.
    370    * Parameters:
    371    *            String: The name of the SELinux boolean.
    372    * Returns: a boolean: (true) boolean is set or (false) it is not.
    373    * Exceptions: None
    374    */
    375   static jboolean getBooleanValue(JNIEnv *env, jobject clazz, jstring name) {
    376 #ifdef HAVE_SELINUX
    377     if (isSELinuxDisabled)
    378       return false;
    379 
    380     const char *boolean_name;
    381     int ret;
    382 
    383     if (name == NULL)
    384       return false;
    385     boolean_name = env->GetStringUTFChars(name, NULL);
    386     ret = security_get_boolean_active(boolean_name);
    387     env->ReleaseStringUTFChars(name, boolean_name);
    388     return (ret == 1) ? true : false;
    389 #else
    390     return false;
    391 #endif
    392   }
    393 
    394   /*
    395    * Function: setBooleanNames
    396    * Purpose: Sets the value for the given SELinux boolean name.
    397    * Parameters:
    398    *            String: The name of the SELinux boolean.
    399    *            Boolean: The new value of the SELinux boolean.
    400    * Returns: a boolean indicating whether or not the operation succeeded.
    401    * Exceptions: None
    402    */
    403   static jboolean setBooleanValue(JNIEnv *env, jobject clazz, jstring name, jboolean value) {
    404 #ifdef HAVE_SELINUX
    405     if (isSELinuxDisabled)
    406       return false;
    407 
    408     const char *boolean_name = NULL;
    409     int ret;
    410 
    411     if (name == NULL)
    412       return false;
    413     boolean_name = env->GetStringUTFChars(name, NULL);
    414     ret = security_set_boolean(boolean_name, (value) ? 1 : 0);
    415     env->ReleaseStringUTFChars(name, boolean_name);
    416     if (ret)
    417       return false;
    418 
    419     if (security_commit_booleans() == -1)
    420       return false;
    421 
    422     return true;
    423 #else
    424     return false;
    425 #endif
    426   }
    427 
    428   /*
    429    * Function: checkSELinuxAccess
    430    * Purpose: Check permissions between two security contexts.
    431    * Parameters: scon: subject security context as a string
    432    *             tcon: object security context as a string
    433    *             tclass: object's security class name as a string
    434    *             perm: permission name as a string
    435    * Returns: boolean: (true) if permission was granted, (false) otherwise
    436    * Exceptions: None
    437    */
    438   static jboolean checkSELinuxAccess(JNIEnv *env, jobject clazz, jstring scon, jstring tcon, jstring tclass, jstring perm) {
    439 #ifdef HAVE_SELINUX
    440     if (isSELinuxDisabled)
    441       return true;
    442 
    443     int accessGranted = -1;
    444 
    445     const char *const_scon, *const_tcon, *mytclass, *myperm;
    446     char *myscon, *mytcon;
    447 
    448     if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL)
    449       goto bail;
    450 
    451     const_scon = env->GetStringUTFChars(scon, NULL);
    452     const_tcon = env->GetStringUTFChars(tcon, NULL);
    453     mytclass   = env->GetStringUTFChars(tclass, NULL);
    454     myperm     = env->GetStringUTFChars(perm, NULL);
    455 
    456     // selinux_check_access needs char* for some
    457     myscon = const_cast<char *>(const_scon);
    458     mytcon = const_cast<char *>(const_tcon);
    459 
    460     accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL);
    461 
    462     ALOGV("selinux_check_access returned %d", accessGranted);
    463 
    464     env->ReleaseStringUTFChars(scon, const_scon);
    465     env->ReleaseStringUTFChars(tcon, const_tcon);
    466     env->ReleaseStringUTFChars(tclass, mytclass);
    467     env->ReleaseStringUTFChars(perm, myperm);
    468 
    469   bail:
    470     return (accessGranted == 0) ? true : false;
    471 
    472 #else
    473     return true;
    474 #endif
    475   }
    476 
    477   /*
    478    * Function: native_restorecon
    479    * Purpose: restore default SELinux security context
    480    * Parameters: pathname: the pathname for the file to be relabeled
    481    * Returns: boolean: (true) file label successfully restored, (false) otherwise
    482    * Exceptions: none
    483    */
    484   static jboolean native_restorecon(JNIEnv *env, jobject clazz, jstring pathname) {
    485 #ifdef HAVE_SELINUX
    486     if (isSELinuxDisabled)
    487       return true;
    488 
    489     const char *file = const_cast<char *>(env->GetStringUTFChars(pathname, NULL));
    490     int ret = selinux_android_restorecon(file);
    491     env->ReleaseStringUTFChars(pathname, file);
    492     return (ret == 0);
    493 #else
    494     return true;
    495 #endif
    496   }
    497 
    498   /*
    499    * JNI registration.
    500    */
    501   static JNINativeMethod method_table[] = {
    502 
    503     /* name,                     signature,                    funcPtr */
    504     { "checkSELinuxAccess"       , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
    505     { "getBooleanNames"          , "()[Ljava/lang/String;"                        , (void*)getBooleanNames  },
    506     { "getBooleanValue"          , "(Ljava/lang/String;)Z"                        , (void*)getBooleanValue  },
    507     { "getContext"               , "()Ljava/lang/String;"                         , (void*)getCon           },
    508     { "getFileContext"           , "(Ljava/lang/String;)Ljava/lang/String;"       , (void*)getFileCon       },
    509     { "getPeerContext"           , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon       },
    510     { "getPidContext"            , "(I)Ljava/lang/String;"                        , (void*)getPidCon        },
    511     { "isSELinuxEnforced"        , "()Z"                                          , (void*)isSELinuxEnforced},
    512     { "isSELinuxEnabled"         , "()Z"                                          , (void*)isSELinuxEnabled },
    513     { "native_restorecon"        , "(Ljava/lang/String;)Z"                        , (void*)native_restorecon},
    514     { "setBooleanValue"          , "(Ljava/lang/String;Z)Z"                       , (void*)setBooleanValue  },
    515     { "setFileContext"           , "(Ljava/lang/String;Ljava/lang/String;)Z"      , (void*)setFileCon       },
    516     { "setFSCreateContext"       , "(Ljava/lang/String;)Z"                        , (void*)setFSCreateCon   },
    517     { "setSELinuxEnforce"        , "(Z)Z"                                         , (void*)setSELinuxEnforce},
    518   };
    519 
    520   static int log_callback(int type, const char *fmt, ...) {
    521     va_list ap;
    522     va_start(ap, fmt);
    523     LOG_PRI_VA(ANDROID_LOG_ERROR, "SELinux", fmt, ap);
    524     va_end(ap);
    525     return 0;
    526   }
    527 
    528   int register_android_os_SELinux(JNIEnv *env) {
    529 #ifdef HAVE_SELINUX
    530     union selinux_callback cb;
    531     cb.func_log = log_callback;
    532     selinux_set_callback(SELINUX_CB_LOG, cb);
    533 
    534     isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
    535 
    536 #endif
    537     return AndroidRuntime::registerNativeMethods(
    538          env, "android/os/SELinux",
    539          method_table, NELEM(method_table));
    540   }
    541 }
    542