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