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