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 if (ret == 0) {
243 ALOGV("setFileCon(%s, %s) => %d", path.c_str(), context.c_str(), ret);
244 return true;
245 }
246 ALOGE("setFileCon(%s, %s) => %d, err: %s", path.c_str(), context.c_str(), ret, strerror(errno));
247 return false;
248 }
249
250 /*
251 * Function: getFileCon
252 * Purpose: retrieves the context associated with the given path in the file system
253 * Parameters:
254 * path: given path in the file system
255 * Returns:
256 * string representing the security context string of the file object
257 * the string may be NULL if an error occured
258 * Exceptions: NullPointerException if the path object is null
259 */
getFileCon(JNIEnv * env,jobject,jstring pathStr)260 static jstring getFileCon(JNIEnv *env, jobject, jstring pathStr) {
261 if (isSELinuxDisabled) {
262 return NULL;
263 }
264
265 ScopedUtfChars path(env, pathStr);
266 if (path.c_str() == NULL) {
267 return NULL;
268 }
269
270 security_context_t tmp = NULL;
271 int ret = getfilecon(path.c_str(), &tmp);
272 Unique_SecurityContext context(tmp);
273
274 ScopedLocalRef<jstring> securityString(env, NULL);
275 if (ret != -1) {
276 securityString.reset(env->NewStringUTF(context.get()));
277 }
278
279 ALOGV("getFileCon(%s) => %s", path.c_str(), context.get());
280 return securityString.release();
281 }
282
283 /*
284 * Function: getCon
285 * Purpose: Get the context of the current process.
286 * Parameters: none
287 * Returns: a jstring representing the security context of the process,
288 * the jstring may be NULL if there was an error
289 * Exceptions: none
290 */
getCon(JNIEnv * env,jobject)291 static jstring getCon(JNIEnv *env, jobject) {
292 if (isSELinuxDisabled) {
293 return NULL;
294 }
295
296 security_context_t tmp = NULL;
297 int ret = getcon(&tmp);
298 Unique_SecurityContext context(tmp);
299
300 ScopedLocalRef<jstring> securityString(env, NULL);
301 if (ret != -1) {
302 securityString.reset(env->NewStringUTF(context.get()));
303 }
304
305 ALOGV("getCon() => %s", context.get());
306 return securityString.release();
307 }
308
309 /*
310 * Function: getPidCon
311 * Purpose: Get the context of a process identified by its pid
312 * Parameters:
313 * pid: a jint representing the process
314 * Returns: a jstring representing the security context of the pid,
315 * the jstring may be NULL if there was an error
316 * Exceptions: none
317 */
getPidCon(JNIEnv * env,jobject,jint pid)318 static jstring getPidCon(JNIEnv *env, jobject, jint pid) {
319 if (isSELinuxDisabled) {
320 return NULL;
321 }
322
323 security_context_t tmp = NULL;
324 int ret = getpidcon(static_cast<pid_t>(pid), &tmp);
325 Unique_SecurityContext context(tmp);
326
327 ScopedLocalRef<jstring> securityString(env, NULL);
328 if (ret != -1) {
329 securityString.reset(env->NewStringUTF(context.get()));
330 }
331
332 ALOGV("getPidCon(%d) => %s", pid, context.get());
333 return securityString.release();
334 }
335
336 /*
337 * Function: checkSELinuxAccess
338 * Purpose: Check permissions between two security contexts.
339 * Parameters: subjectContextStr: subject security context as a string
340 * objectContextStr: object security context as a string
341 * objectClassStr: object's security class name as a string
342 * permissionStr: permission name as a string
343 * Returns: boolean: (true) if permission was granted, (false) otherwise
344 * Exceptions: None
345 */
checkSELinuxAccess(JNIEnv * env,jobject,jstring subjectContextStr,jstring objectContextStr,jstring objectClassStr,jstring permissionStr)346 static jboolean checkSELinuxAccess(JNIEnv *env, jobject, jstring subjectContextStr,
347 jstring objectContextStr, jstring objectClassStr, jstring permissionStr) {
348 if (isSELinuxDisabled) {
349 return true;
350 }
351
352 ScopedUtfChars subjectContext(env, subjectContextStr);
353 if (subjectContext.c_str() == NULL) {
354 return false;
355 }
356
357 ScopedUtfChars objectContext(env, objectContextStr);
358 if (objectContext.c_str() == NULL) {
359 return false;
360 }
361
362 ScopedUtfChars objectClass(env, objectClassStr);
363 if (objectClass.c_str() == NULL) {
364 return false;
365 }
366
367 ScopedUtfChars permission(env, permissionStr);
368 if (permission.c_str() == NULL) {
369 return false;
370 }
371
372 char *tmp1 = const_cast<char *>(subjectContext.c_str());
373 char *tmp2 = const_cast<char *>(objectContext.c_str());
374 int accessGranted = selinux_check_access(tmp1, tmp2, objectClass.c_str(), permission.c_str(),
375 NULL);
376
377 ALOGV("checkSELinuxAccess(%s, %s, %s, %s) => %d", subjectContext.c_str(), objectContext.c_str(),
378 objectClass.c_str(), permission.c_str(), accessGranted);
379
380 return (accessGranted == 0) ? true : false;
381 }
382
383 /*
384 * Function: native_restorecon
385 * Purpose: restore default SELinux security context
386 * Parameters: pathname: the pathname for the file to be relabeled
387 * Returns: boolean: (true) file label successfully restored, (false) otherwise
388 * Exceptions: none
389 */
native_restorecon(JNIEnv * env,jobject,jstring pathnameStr,jint flags)390 static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr, jint flags) {
391 if (isSELinuxDisabled) {
392 return true;
393 }
394
395 ScopedUtfChars pathname(env, pathnameStr);
396 if (pathname.c_str() == NULL) {
397 ALOGV("restorecon(%p) => threw exception", pathnameStr);
398 return false;
399 }
400
401 int ret = selinux_android_restorecon(pathname.c_str(), flags);
402 ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
403 return (ret == 0);
404 }
405
406 /*
407 * JNI registration.
408 */
409 static const JNINativeMethod method_table[] = {
410 /* name, signature, funcPtr */
411 { "checkSELinuxAccess" , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
412 { "getContext" , "()Ljava/lang/String;" , (void*)getCon },
413 { "getFileContext" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)getFileCon },
414 { "getPeerContext" , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon },
415 { "getFileContext" , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getFdCon },
416 { "getPidContext" , "(I)Ljava/lang/String;" , (void*)getPidCon },
417 { "isSELinuxEnforced" , "()Z" , (void*)isSELinuxEnforced},
418 { "isSELinuxEnabled" , "()Z" , (void*)isSELinuxEnabled },
419 { "native_restorecon" , "(Ljava/lang/String;I)Z" , (void*)native_restorecon},
420 { "setFileContext" , "(Ljava/lang/String;Ljava/lang/String;)Z" , (void*)setFileCon },
421 { "setFSCreateContext" , "(Ljava/lang/String;)Z" , (void*)setFSCreateCon },
422 { "fileSelabelLookup" , "(Ljava/lang/String;)Ljava/lang/String;" , (void*)fileSelabelLookup},
423 };
424
log_callback(int type,const char * fmt,...)425 static int log_callback(int type, const char *fmt, ...) {
426 va_list ap;
427 int priority;
428
429 switch (type) {
430 case SELINUX_WARNING:
431 priority = ANDROID_LOG_WARN;
432 break;
433 case SELINUX_INFO:
434 priority = ANDROID_LOG_INFO;
435 break;
436 default:
437 priority = ANDROID_LOG_ERROR;
438 break;
439 }
440 va_start(ap, fmt);
441 LOG_PRI_VA(priority, "SELinux", fmt, ap);
442 va_end(ap);
443 return 0;
444 }
445
register_android_os_SELinux(JNIEnv * env)446 int register_android_os_SELinux(JNIEnv *env) {
447 union selinux_callback cb;
448 cb.func_log = log_callback;
449 selinux_set_callback(SELINUX_CB_LOG, cb);
450
451 isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
452
453 return RegisterMethodsOrDie(env, "android/os/SELinux", method_table, NELEM(method_table));
454 }
455
456 }
457