xref: /aosp_15_r20/frameworks/base/core/jni/android_os_SELinux.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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