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