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
19 #include <errno.h>
20 #include <fcntl.h>
21
22 #include <utils/Log.h>
23
24 #include <nativehelper/JNIPlatformHelp.h>
25 #include "jni.h"
26 #include "core_jni_helpers.h"
27 #include "selinux/selinux.h"
28 #include "selinux/android.h"
29 #include <memory>
30 #include <atomic>
31 #include <nativehelper/ScopedLocalRef.h>
32 #include <nativehelper/ScopedUtfChars.h>
33
34 namespace android {
35 namespace {
36 std::atomic<selabel_handle*> sehandle{nullptr};
37
GetSELabelHandle()38 selabel_handle* GetSELabelHandle() {
39 selabel_handle* h = sehandle.load();
40 if (h != nullptr) {
41 return h;
42 }
43
44 h = selinux_android_file_context_handle();
45 selabel_handle* expected = nullptr;
46 if (!sehandle.compare_exchange_strong(expected, h)) {
47 selabel_close(h);
48 return sehandle.load();
49 }
50 return h;
51 }
52
53 }
54
55 struct SecurityContext_Delete {
operator ()android::SecurityContext_Delete56 void operator()(security_context_t p) const {
57 freecon(p);
58 }
59 };
60 typedef std::unique_ptr<char[], SecurityContext_Delete> Unique_SecurityContext;
61
62 static jboolean isSELinuxDisabled = true;
63
64 /*
65 * Function: isSELinuxEnabled
66 * Purpose: checks whether SELinux is enabled/disbaled
67 * Parameters: none
68 * Return value : true (enabled) or false (disabled)
69 * Exceptions: none
70 */
isSELinuxEnabled(JNIEnv * env,jobject)71 static jboolean isSELinuxEnabled(JNIEnv *env, jobject) {
72 return !isSELinuxDisabled;
73 }
74
75 /*
76 * Function: isSELinuxEnforced
77 * Purpose: return the current SELinux enforce mode
78 * Parameters: none
79 * Return value: true (enforcing) or false (permissive)
80 * Exceptions: none
81 */
isSELinuxEnforced(JNIEnv * env,jobject)82 static jboolean isSELinuxEnforced(JNIEnv *env, jobject) {
83 return (security_getenforce() == 1) ? true : false;
84 }
85
fileSelabelLookup(JNIEnv * env,jobject,jstring pathStr)86 static jstring fileSelabelLookup(JNIEnv* env, jobject, jstring pathStr) {
87 if (isSELinuxDisabled) {
88 ALOGE("fileSelabelLookup => SELinux is disabled");
89 return NULL;
90 }
91
92 if (pathStr == NULL) {
93 ALOGE("fileSelabelLookup => got null path.");
94 jniThrowNullPointerException(
95 env, "Trying to get security context of a null path.");
96 return NULL;
97 }
98
99 ScopedUtfChars path(env, pathStr);
100 const char* path_c_str = path.c_str();
101 if (path_c_str == NULL) {
102 ALOGE("fileSelabelLookup => Got null path");
103 jniThrowNullPointerException(
104 env, "Trying to get security context of a null path.");
105 return NULL;
106 }
107
108 auto* selabel_handle = GetSELabelHandle();
109 if (selabel_handle == NULL) {
110 ALOGE("fileSelabelLookup => Failed to get SEHandle");
111 return NULL;
112 }
113
114 security_context_t tmp = NULL;
115 if (selabel_lookup(selabel_handle, &tmp, path_c_str, S_IFREG) != 0) {
116 ALOGE("fileSelabelLookup => selabel_lookup for %s failed: %d", path_c_str, errno);
117 return NULL;
118 }
119
120 Unique_SecurityContext context(tmp);
121 return env->NewStringUTF(context.get());
122 }
123
getFdConInner(JNIEnv * env,jobject fileDescriptor,bool isSocket)124 static jstring getFdConInner(JNIEnv *env, jobject fileDescriptor, bool isSocket) {
125 if (isSELinuxDisabled) {
126 return NULL;
127 }
128
129 if (fileDescriptor == NULL) {
130 jniThrowNullPointerException(env,
131 "Trying to check security context of a null FileDescriptor.");
132 return NULL;
133 }
134
135 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
136 if (env->ExceptionCheck()) {
137 ALOGE("getFdCon => getFD for %p failed", fileDescriptor);
138 return NULL;
139 }
140
141 security_context_t tmp = NULL;
142 int ret;
143 if (isSocket) {
144 ret = getpeercon(fd, &tmp);
145 } else{
146 ret = fgetfilecon(fd, &tmp);
147 }
148 Unique_SecurityContext context(tmp);
149
150 ScopedLocalRef<jstring> contextStr(env, NULL);
151 if (ret != -1) {
152 contextStr.reset(env->NewStringUTF(context.get()));
153 }
154
155 ALOGV("getFdCon(%d) => %s", fd, context.get());
156 return contextStr.release();
157 }
158
159 /*
160 * Function: getPeerCon
161 * Purpose: retrieves security context of peer socket
162 * Parameters:
163 * fileDescriptor: peer socket file as a FileDescriptor object
164 * Returns: jstring representing the security_context of socket or NULL if error
165 * Exceptions: NullPointerException if fileDescriptor object is NULL
166 */
getPeerCon(JNIEnv * env,jobject,jobject fileDescriptor)167 static jstring getPeerCon(JNIEnv *env, jobject, jobject fileDescriptor) {
168 return getFdConInner(env, fileDescriptor, true);
169 }
170
171 /*
172 * Function: getFdCon
173 * Purpose: retrieves security context of a file descriptor.
174 * Parameters:
175 * fileDescriptor: a FileDescriptor object
176 * Returns: jstring representing the security_context of socket or NULL if error
177 * Exceptions: NullPointerException if fileDescriptor object is NULL
178 */
getFdCon(JNIEnv * env,jobject,jobject fileDescriptor)179 static jstring getFdCon(JNIEnv *env, jobject, jobject fileDescriptor) {
180 return getFdConInner(env, fileDescriptor, false);
181 }
182
183 /*
184 * Function: setFSCreateCon
185 * Purpose: set security context used for creating a new file system object
186 * Parameters:
187 * context: security_context_t representing the new context of a file system object,
188 * set to NULL to return to the default policy behavior
189 * Returns: true on success, false on error
190 * Exception: none
191 */
setFSCreateCon(JNIEnv * env,jobject,jstring contextStr)192 static jboolean setFSCreateCon(JNIEnv *env, jobject, jstring contextStr) {
193 if (isSELinuxDisabled) {
194 return false;
195 }
196
197 std::unique_ptr<ScopedUtfChars> context;
198 const char* context_c_str = NULL;
199 if (contextStr != NULL) {
200 context.reset(new ScopedUtfChars(env, contextStr));
201 context_c_str = context->c_str();
202 if (context_c_str == NULL) {
203 return false;
204 }
205 }
206
207 int ret = setfscreatecon(const_cast<char *>(context_c_str));
208
209 ALOGV("setFSCreateCon(%s) => %d", context_c_str, ret);
210
211 return (ret == 0) ? true : false;
212 }
213
214 /*
215 * Function: setFileCon
216 * Purpose: set the security context of a file object
217 * Parameters:
218 * path: the location of the file system object
219 * context: the new security context of the file system object
220 * Returns: true on success, false on error
221 * Exception: NullPointerException is thrown if either path or context strign are NULL
222 */
setFileCon(JNIEnv * env,jobject,jstring pathStr,jstring contextStr)223 static jboolean setFileCon(JNIEnv *env, jobject, jstring pathStr, jstring contextStr) {
224 if (isSELinuxDisabled) {
225 return false;
226 }
227
228 ScopedUtfChars path(env, pathStr);
229 if (path.c_str() == NULL) {
230 return false;
231 }
232
233 ScopedUtfChars context(env, contextStr);
234 if (context.c_str() == NULL) {
235 return false;
236 }
237
238 // GetStringUTFChars returns const char * yet setfilecon needs char *
239 char *tmp = const_cast<char *>(context.c_str());
240 int ret = setfilecon(path.c_str(), tmp);
241
242 ALOGV("setFileCon(%s, %s) => %d", path.c_str(), context.c_str(), ret);
243 return (ret == 0) ? true : false;
244 }
245
246 /*
247 * Function: getFileCon
248 * Purpose: retrieves the context associated with the given path in the file system
249 * Parameters:
250 * path: given path in the file system
251 * Returns:
252 * string representing the security context string of the file object
253 * the string may be NULL if an error occured
254 * Exceptions: NullPointerException if the path object is null
255 */
getFileCon(JNIEnv * env,jobject,jstring pathStr)256 static jstring getFileCon(JNIEnv *env, jobject, jstring pathStr) {
257 if (isSELinuxDisabled) {
258 return NULL;
259 }
260
261 ScopedUtfChars path(env, pathStr);
262 if (path.c_str() == NULL) {
263 return NULL;
264 }
265
266 security_context_t tmp = NULL;
267 int ret = getfilecon(path.c_str(), &tmp);
268 Unique_SecurityContext context(tmp);
269
270 ScopedLocalRef<jstring> securityString(env, NULL);
271 if (ret != -1) {
272 securityString.reset(env->NewStringUTF(context.get()));
273 }
274
275 ALOGV("getFileCon(%s) => %s", path.c_str(), context.get());
276 return securityString.release();
277 }
278
279 /*
280 * Function: getCon
281 * Purpose: Get the context of the current process.
282 * Parameters: none
283 * Returns: a jstring representing the security context of the process,
284 * the jstring may be NULL if there was an error
285 * Exceptions: none
286 */
getCon(JNIEnv * env,jobject)287 static jstring getCon(JNIEnv *env, jobject) {
288 if (isSELinuxDisabled) {
289 return NULL;
290 }
291
292 security_context_t tmp = NULL;
293 int ret = getcon(&tmp);
294 Unique_SecurityContext context(tmp);
295
296 ScopedLocalRef<jstring> securityString(env, NULL);
297 if (ret != -1) {
298 securityString.reset(env->NewStringUTF(context.get()));
299 }
300
301 ALOGV("getCon() => %s", context.get());
302 return securityString.release();
303 }
304
305 /*
306 * Function: getPidCon
307 * Purpose: Get the context of a process identified by its pid
308 * Parameters:
309 * pid: a jint representing the process
310 * Returns: a jstring representing the security context of the pid,
311 * the jstring may be NULL if there was an error
312 * Exceptions: none
313 */
getPidCon(JNIEnv * env,jobject,jint pid)314 static jstring getPidCon(JNIEnv *env, jobject, jint pid) {
315 if (isSELinuxDisabled) {
316 return NULL;
317 }
318
319 security_context_t tmp = NULL;
320 int ret = getpidcon(static_cast<pid_t>(pid), &tmp);
321 Unique_SecurityContext context(tmp);
322
323 ScopedLocalRef<jstring> securityString(env, NULL);
324 if (ret != -1) {
325 securityString.reset(env->NewStringUTF(context.get()));
326 }
327
328 ALOGV("getPidCon(%d) => %s", pid, context.get());
329 return securityString.release();
330 }
331
332 /*
333 * Function: checkSELinuxAccess
334 * Purpose: Check permissions between two security contexts.
335 * Parameters: subjectContextStr: subject security context as a string
336 * objectContextStr: object security context as a string
337 * objectClassStr: object's security class name as a string
338 * permissionStr: permission name as a string
339 * Returns: boolean: (true) if permission was granted, (false) otherwise
340 * Exceptions: None
341 */
checkSELinuxAccess(JNIEnv * env,jobject,jstring subjectContextStr,jstring objectContextStr,jstring objectClassStr,jstring permissionStr)342 static jboolean checkSELinuxAccess(JNIEnv *env, jobject, jstring subjectContextStr,
343 jstring objectContextStr, jstring objectClassStr, jstring permissionStr) {
344 if (isSELinuxDisabled) {
345 return true;
346 }
347
348 ScopedUtfChars subjectContext(env, subjectContextStr);
349 if (subjectContext.c_str() == NULL) {
350 return false;
351 }
352
353 ScopedUtfChars objectContext(env, objectContextStr);
354 if (objectContext.c_str() == NULL) {
355 return false;
356 }
357
358 ScopedUtfChars objectClass(env, objectClassStr);
359 if (objectClass.c_str() == NULL) {
360 return false;
361 }
362
363 ScopedUtfChars permission(env, permissionStr);
364 if (permission.c_str() == NULL) {
365 return false;
366 }
367
368 char *tmp1 = const_cast<char *>(subjectContext.c_str());
369 char *tmp2 = const_cast<char *>(objectContext.c_str());
370 int accessGranted = selinux_check_access(tmp1, tmp2, objectClass.c_str(), permission.c_str(),
371 NULL);
372
373 ALOGV("checkSELinuxAccess(%s, %s, %s, %s) => %d", subjectContext.c_str(), objectContext.c_str(),
374 objectClass.c_str(), permission.c_str(), accessGranted);
375
376 return (accessGranted == 0) ? true : false;
377 }
378
379 /*
380 * Function: native_restorecon
381 * Purpose: restore default SELinux security context
382 * Parameters: pathname: the pathname for the file to be relabeled
383 * Returns: boolean: (true) file label successfully restored, (false) otherwise
384 * Exceptions: none
385 */
native_restorecon(JNIEnv * env,jobject,jstring pathnameStr,jint flags)386 static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr, jint flags) {
387 if (isSELinuxDisabled) {
388 return true;
389 }
390
391 ScopedUtfChars pathname(env, pathnameStr);
392 if (pathname.c_str() == NULL) {
393 ALOGV("restorecon(%p) => threw exception", pathnameStr);
394 return false;
395 }
396
397 int ret = selinux_android_restorecon(pathname.c_str(), flags);
398 ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
399 return (ret == 0);
400 }
401
402 /*
403 * JNI registration.
404 */
405 static const JNINativeMethod method_table[] = {
406 /* name, signature, funcPtr */
407 { "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
408 { "getContext" , "()Ljava/lang/String;" , (void*)getCon },
409 { "getFileContext" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)getFileCon },
410 { "getPeerContext" , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon },
411 { "getFileContext" , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getFdCon },
412 { "getPidContext" , "(I)Ljava/lang/String;" , (void*)getPidCon },
413 { "isSELinuxEnforced" , "()Z" , (void*)isSELinuxEnforced},
414 { "isSELinuxEnabled" , "()Z" , (void*)isSELinuxEnabled },
415 { "native_restorecon" , "(Ljava/lang/String;I)Z" , (void*)native_restorecon},
416 { "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon },
417 { "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon },
418 { "fileSelabelLookup" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)fileSelabelLookup},
419 };
420
log_callback(int type,const char * fmt,...)421 static int log_callback(int type, const char *fmt, ...) {
422 va_list ap;
423 int priority;
424
425 switch (type) {
426 case SELINUX_WARNING:
427 priority = ANDROID_LOG_WARN;
428 break;
429 case SELINUX_INFO:
430 priority = ANDROID_LOG_INFO;
431 break;
432 default:
433 priority = ANDROID_LOG_ERROR;
434 break;
435 }
436 va_start(ap, fmt);
437 LOG_PRI_VA(priority, "SELinux", fmt, ap);
438 va_end(ap);
439 return 0;
440 }
441
register_android_os_SELinux(JNIEnv * env)442 int register_android_os_SELinux(JNIEnv *env) {
443 union selinux_callback cb;
444 cb.func_log = log_callback;
445 selinux_set_callback(SELINUX_CB_LOG, cb);
446
447 isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
448
449 return RegisterMethodsOrDie(env, "android/os/SELinux", method_table, NELEM(method_table));
450 }
451
452 }
453