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