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 "android_runtime/AndroidRuntime.h"
23#ifdef HAVE_SELINUX
24#include "selinux/selinux.h"
25#include "selinux/android.h"
26#endif
27#include <errno.h>
28
29namespace android {
30
31  static jboolean isSELinuxDisabled = true;
32
33  static void throw_NullPointerException(JNIEnv *env, const char* msg) {
34    jclass clazz;
35    clazz = env->FindClass("java/lang/NullPointerException");
36    env->ThrowNew(clazz, msg);
37  }
38
39  /*
40   * Function: isSELinuxEnabled
41   * Purpose:  checks whether SELinux is enabled/disbaled
42   * Parameters: none
43   * Return value : true (enabled) or false (disabled)
44   * Exceptions: none
45   */
46  static jboolean isSELinuxEnabled(JNIEnv *env, jobject classz) {
47
48    return !isSELinuxDisabled;
49  }
50
51  /*
52   * Function: isSELinuxEnforced
53   * Purpose: return the current SELinux enforce mode
54   * Parameters: none
55   * Return value: true (enforcing) or false (permissive)
56   * Exceptions: none
57   */
58  static jboolean isSELinuxEnforced(JNIEnv *env, jobject clazz) {
59#ifdef HAVE_SELINUX
60    return (security_getenforce() == 1) ? true : false;
61#else
62    return false;
63#endif
64  }
65
66  /*
67   * Function: setSELinuxEnforce
68   * Purpose: set the SE Linux enforcing mode
69   * Parameters: true (enforcing) or false (permissive)
70   * Return value: true (success) or false (fail)
71   * Exceptions: none
72   */
73  static jboolean setSELinuxEnforce(JNIEnv *env, jobject clazz, jboolean value) {
74#ifdef HAVE_SELINUX
75    if (isSELinuxDisabled)
76      return false;
77
78    int enforce = (value) ? 1 : 0;
79
80    return (security_setenforce(enforce) != -1) ? true : false;
81#else
82    return false;
83#endif
84  }
85
86  /*
87   * Function: getPeerCon
88   * Purpose: retrieves security context of peer socket
89   * Parameters:
90   *        fileDescriptor: peer socket file as a FileDescriptor object
91   * Returns: jstring representing the security_context of socket or NULL if error
92   * Exceptions: NullPointerException if fileDescriptor object is NULL
93   */
94  static jstring getPeerCon(JNIEnv *env, jobject clazz, jobject fileDescriptor) {
95#ifdef HAVE_SELINUX
96    if (isSELinuxDisabled)
97      return NULL;
98
99    if (fileDescriptor == NULL) {
100      throw_NullPointerException(env, "Trying to check security context of a null peer socket.");
101      return NULL;
102    }
103
104    security_context_t context = NULL;
105    jstring securityString = NULL;
106
107    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
108
109    if (env->ExceptionOccurred() != NULL) {
110      ALOGE("There was an issue with retrieving the file descriptor");
111      goto bail;
112    }
113
114    if (getpeercon(fd, &context) == -1)
115      goto bail;
116
117    ALOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context);
118
119    securityString = env->NewStringUTF(context);
120
121  bail:
122    if (context != NULL)
123      freecon(context);
124
125    return securityString;
126#else
127    return NULL;
128#endif
129  }
130
131  /*
132   * Function: setFSCreateCon
133   * Purpose: set security context used for creating a new file system object
134   * Parameters:
135   *       context: security_context_t representing the new context of a file system object,
136   *                set to NULL to return to the default policy behavior
137   * Returns: true on success, false on error
138   * Exception: none
139   */
140  static jboolean setFSCreateCon(JNIEnv *env, jobject clazz, jstring context) {
141#ifdef HAVE_SELINUX
142    if (isSELinuxDisabled)
143      return false;
144
145    char * securityContext = NULL;
146    const char *constant_securityContext = NULL;
147
148    if (context != NULL) {
149      constant_securityContext = env->GetStringUTFChars(context, NULL);
150
151      // GetStringUTFChars returns const char * yet setfscreatecon needs char *
152      securityContext = const_cast<char *>(constant_securityContext);
153    }
154
155    int ret;
156    if ((ret = setfscreatecon(securityContext)) == -1)
157      goto bail;
158
159    ALOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context);
160
161  bail:
162    if (constant_securityContext != NULL)
163      env->ReleaseStringUTFChars(context, constant_securityContext);
164
165    return (ret == 0) ? true : false;
166#else
167    return false;
168#endif
169  }
170
171  /*
172   * Function: setFileCon
173   * Purpose:  set the security context of a file object
174   * Parameters:
175   *       path: the location of the file system object
176   *       con: the new security context of the file system object
177   * Returns: true on success, false on error
178   * Exception: NullPointerException is thrown if either path or context strign are NULL
179   */
180  static jboolean setFileCon(JNIEnv *env, jobject clazz, jstring path, jstring con) {
181#ifdef HAVE_SELINUX
182    if (isSELinuxDisabled)
183      return false;
184
185    if (path == NULL) {
186      throw_NullPointerException(env, "Trying to change the security context of a NULL file object.");
187      return false;
188    }
189
190    if (con == NULL) {
191      throw_NullPointerException(env, "Trying to set the security context of a file object with NULL.");
192      return false;
193    }
194
195    const char *objectPath = env->GetStringUTFChars(path, NULL);
196    const char *constant_con = env->GetStringUTFChars(con, NULL);
197
198    // GetStringUTFChars returns const char * yet setfilecon needs char *
199    char *newCon = const_cast<char *>(constant_con);
200
201    int ret;
202    if ((ret = setfilecon(objectPath, newCon)) == -1)
203      goto bail;
204
205    ALOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath);
206
207  bail:
208    env->ReleaseStringUTFChars(path, objectPath);
209    env->ReleaseStringUTFChars(con, constant_con);
210    return (ret == 0) ? true : false;
211#else
212    return false;
213#endif
214  }
215
216  /*
217   * Function: getFileCon
218   * Purpose: retrieves the context associated with the given path in the file system
219   * Parameters:
220   *        path: given path in the file system
221   * Returns:
222   *        string representing the security context string of the file object
223   *        the string may be NULL if an error occured
224   * Exceptions: NullPointerException if the path object is null
225   */
226  static jstring getFileCon(JNIEnv *env, jobject clazz, jstring path) {
227#ifdef HAVE_SELINUX
228    if (isSELinuxDisabled)
229      return NULL;
230
231    if (path == NULL) {
232      throw_NullPointerException(env, "Trying to check security context of a null path.");
233      return NULL;
234    }
235
236    const char *objectPath = env->GetStringUTFChars(path, NULL);
237
238    security_context_t context = NULL;
239    jstring securityString = NULL;
240
241    if (getfilecon(objectPath, &context) == -1)
242      goto bail;
243
244    ALOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath);
245
246    securityString = env->NewStringUTF(context);
247
248  bail:
249    if (context != NULL)
250      freecon(context);
251
252    env->ReleaseStringUTFChars(path, objectPath);
253
254    return securityString;
255#else
256    return NULL;
257#endif
258  }
259
260  /*
261   * Function: getCon
262   * Purpose: Get the context of the current process.
263   * Parameters: none
264   * Returns: a jstring representing the security context of the process,
265   *          the jstring may be NULL if there was an error
266   * Exceptions: none
267   */
268  static jstring getCon(JNIEnv *env, jobject clazz) {
269#ifdef HAVE_SELINUX
270    if (isSELinuxDisabled)
271      return NULL;
272
273    security_context_t context = NULL;
274    jstring securityString = NULL;
275
276    if (getcon(&context) == -1)
277      goto bail;
278
279    ALOGV("getCon: Successfully retrieved context '%s'", context);
280
281    securityString = env->NewStringUTF(context);
282
283  bail:
284    if (context != NULL)
285      freecon(context);
286
287    return securityString;
288#else
289    return NULL;
290#endif
291  }
292
293  /*
294   * Function: getPidCon
295   * Purpose: Get the context of a process identified by its pid
296   * Parameters:
297   *            pid: a jint representing the process
298   * Returns: a jstring representing the security context of the pid,
299   *          the jstring may be NULL if there was an error
300   * Exceptions: none
301   */
302  static jstring getPidCon(JNIEnv *env, jobject clazz, jint pid) {
303#ifdef HAVE_SELINUX
304    if (isSELinuxDisabled)
305      return NULL;
306
307    security_context_t context = NULL;
308    jstring securityString = NULL;
309
310    pid_t checkPid = (pid_t)pid;
311
312    if (getpidcon(checkPid, &context) == -1)
313      goto bail;
314
315    ALOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid);
316
317    securityString = env->NewStringUTF(context);
318
319  bail:
320    if (context != NULL)
321      freecon(context);
322
323    return securityString;
324#else
325    return NULL;
326#endif
327  }
328
329  /*
330   * Function: getBooleanNames
331   * Purpose: Gets a list of the SELinux boolean names.
332   * Parameters: None
333   * Returns: an array of strings  containing the SELinux boolean names.
334   *          returns NULL string on error
335   * Exceptions: None
336   */
337  static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv clazz) {
338#ifdef HAVE_SELINUX
339    if (isSELinuxDisabled)
340      return NULL;
341
342    char **list;
343    int i, len, ret;
344    jclass stringClass;
345    jobjectArray stringArray = NULL;
346
347    if (security_get_boolean_names(&list, &len) == -1)
348      return NULL;
349
350    stringClass = env->FindClass("java/lang/String");
351    stringArray = env->NewObjectArray(len, stringClass, env->NewStringUTF(""));
352    for (i = 0; i < len; i++) {
353      jstring obj;
354      obj = env->NewStringUTF(list[i]);
355      env->SetObjectArrayElement(stringArray, i, obj);
356      env->DeleteLocalRef(obj);
357      free(list[i]);
358    }
359    free(list);
360
361    return stringArray;
362#else
363    return NULL;
364#endif
365  }
366
367  /*
368   * Function: getBooleanValue
369   * Purpose: Gets the value for the given SELinux boolean name.
370   * Parameters:
371   *            String: The name of the SELinux boolean.
372   * Returns: a boolean: (true) boolean is set or (false) it is not.
373   * Exceptions: None
374   */
375  static jboolean getBooleanValue(JNIEnv *env, jobject clazz, jstring name) {
376#ifdef HAVE_SELINUX
377    if (isSELinuxDisabled)
378      return false;
379
380    const char *boolean_name;
381    int ret;
382
383    if (name == NULL)
384      return false;
385    boolean_name = env->GetStringUTFChars(name, NULL);
386    ret = security_get_boolean_active(boolean_name);
387    env->ReleaseStringUTFChars(name, boolean_name);
388    return (ret == 1) ? true : false;
389#else
390    return false;
391#endif
392  }
393
394  /*
395   * Function: setBooleanNames
396   * Purpose: Sets the value for the given SELinux boolean name.
397   * Parameters:
398   *            String: The name of the SELinux boolean.
399   *            Boolean: The new value of the SELinux boolean.
400   * Returns: a boolean indicating whether or not the operation succeeded.
401   * Exceptions: None
402   */
403  static jboolean setBooleanValue(JNIEnv *env, jobject clazz, jstring name, jboolean value) {
404#ifdef HAVE_SELINUX
405    if (isSELinuxDisabled)
406      return false;
407
408    const char *boolean_name = NULL;
409    int ret;
410
411    if (name == NULL)
412      return false;
413    boolean_name = env->GetStringUTFChars(name, NULL);
414    ret = security_set_boolean(boolean_name, (value) ? 1 : 0);
415    env->ReleaseStringUTFChars(name, boolean_name);
416    if (ret)
417      return false;
418
419    if (security_commit_booleans() == -1)
420      return false;
421
422    return true;
423#else
424    return false;
425#endif
426  }
427
428  /*
429   * Function: checkSELinuxAccess
430   * Purpose: Check permissions between two security contexts.
431   * Parameters: scon: subject security context as a string
432   *             tcon: object security context as a string
433   *             tclass: object's security class name as a string
434   *             perm: permission name as a string
435   * Returns: boolean: (true) if permission was granted, (false) otherwise
436   * Exceptions: None
437   */
438  static jboolean checkSELinuxAccess(JNIEnv *env, jobject clazz, jstring scon, jstring tcon, jstring tclass, jstring perm) {
439#ifdef HAVE_SELINUX
440    if (isSELinuxDisabled)
441      return true;
442
443    int accessGranted = -1;
444
445    const char *const_scon, *const_tcon, *mytclass, *myperm;
446    char *myscon, *mytcon;
447
448    if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL)
449      goto bail;
450
451    const_scon = env->GetStringUTFChars(scon, NULL);
452    const_tcon = env->GetStringUTFChars(tcon, NULL);
453    mytclass   = env->GetStringUTFChars(tclass, NULL);
454    myperm     = env->GetStringUTFChars(perm, NULL);
455
456    // selinux_check_access needs char* for some
457    myscon = const_cast<char *>(const_scon);
458    mytcon = const_cast<char *>(const_tcon);
459
460    accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL);
461
462    ALOGV("selinux_check_access returned %d", accessGranted);
463
464    env->ReleaseStringUTFChars(scon, const_scon);
465    env->ReleaseStringUTFChars(tcon, const_tcon);
466    env->ReleaseStringUTFChars(tclass, mytclass);
467    env->ReleaseStringUTFChars(perm, myperm);
468
469  bail:
470    return (accessGranted == 0) ? true : false;
471
472#else
473    return true;
474#endif
475  }
476
477  /*
478   * Function: native_restorecon
479   * Purpose: restore default SELinux security context
480   * Parameters: pathname: the pathname for the file to be relabeled
481   * Returns: boolean: (true) file label successfully restored, (false) otherwise
482   * Exceptions: none
483   */
484  static jboolean native_restorecon(JNIEnv *env, jobject clazz, jstring pathname) {
485#ifdef HAVE_SELINUX
486    if (isSELinuxDisabled)
487      return true;
488
489    const char *file = const_cast<char *>(env->GetStringUTFChars(pathname, NULL));
490    int ret = selinux_android_restorecon(file);
491    env->ReleaseStringUTFChars(pathname, file);
492    return (ret == 0);
493#else
494    return true;
495#endif
496  }
497
498  /*
499   * JNI registration.
500   */
501  static JNINativeMethod method_table[] = {
502
503    /* name,                     signature,                    funcPtr */
504    { "checkSELinuxAccess"       , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
505    { "getBooleanNames"          , "()[Ljava/lang/String;"                        , (void*)getBooleanNames  },
506    { "getBooleanValue"          , "(Ljava/lang/String;)Z"                        , (void*)getBooleanValue  },
507    { "getContext"               , "()Ljava/lang/String;"                         , (void*)getCon           },
508    { "getFileContext"           , "(Ljava/lang/String;)Ljava/lang/String;"       , (void*)getFileCon       },
509    { "getPeerContext"           , "(Ljava/io/FileDescriptor;)Ljava/lang/String;" , (void*)getPeerCon       },
510    { "getPidContext"            , "(I)Ljava/lang/String;"                        , (void*)getPidCon        },
511    { "isSELinuxEnforced"        , "()Z"                                          , (void*)isSELinuxEnforced},
512    { "isSELinuxEnabled"         , "()Z"                                          , (void*)isSELinuxEnabled },
513    { "native_restorecon"        , "(Ljava/lang/String;)Z"                        , (void*)native_restorecon},
514    { "setBooleanValue"          , "(Ljava/lang/String;Z)Z"                       , (void*)setBooleanValue  },
515    { "setFileContext"           , "(Ljava/lang/String;Ljava/lang/String;)Z"      , (void*)setFileCon       },
516    { "setFSCreateContext"       , "(Ljava/lang/String;)Z"                        , (void*)setFSCreateCon   },
517    { "setSELinuxEnforce"        , "(Z)Z"                                         , (void*)setSELinuxEnforce},
518  };
519
520  static int log_callback(int type, const char *fmt, ...) {
521    va_list ap;
522    va_start(ap, fmt);
523    LOG_PRI_VA(ANDROID_LOG_ERROR, "SELinux", fmt, ap);
524    va_end(ap);
525    return 0;
526  }
527
528  int register_android_os_SELinux(JNIEnv *env) {
529#ifdef HAVE_SELINUX
530    union selinux_callback cb;
531    cb.func_log = log_callback;
532    selinux_set_callback(SELINUX_CB_LOG, cb);
533
534    isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
535
536#endif
537    return AndroidRuntime::registerNativeMethods(
538         env, "android/os/SELinux",
539         method_table, NELEM(method_table));
540  }
541}
542