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 = NULL; 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, context.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 = NULL; 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 = NULL; 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 = NULL; 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, jint flags) { 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", pathnameStr); 415 return false; 416 } 417 418 int ret = selinux_android_restorecon(pathname.c_str(), flags); 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;I)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 int priority; 447 448 switch (type) { 449 case SELINUX_WARNING: 450 priority = ANDROID_LOG_WARN; 451 break; 452 case SELINUX_INFO: 453 priority = ANDROID_LOG_INFO; 454 break; 455 default: 456 priority = ANDROID_LOG_ERROR; 457 break; 458 } 459 va_start(ap, fmt); 460 LOG_PRI_VA(priority, "SELinux", fmt, ap); 461 va_end(ap); 462 return 0; 463 } 464 465 int register_android_os_SELinux(JNIEnv *env) { 466 union selinux_callback cb; 467 cb.func_log = log_callback; 468 selinux_set_callback(SELINUX_CB_LOG, cb); 469 470 isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false; 471 472 return AndroidRuntime::registerNativeMethods(env, "android/os/SELinux", method_table, 473 NELEM(method_table)); 474 } 475 476 } 477