Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e46f9d51 authored by Stephen Smalley's avatar Stephen Smalley
Browse files

Extend init and ueventd for SE Android.

Add SE Android support for init and ueventd.

init:
- Load policy at boot.
- Set the security context for service daemons and their sockets.
- New built-in commands: setcon, setenforce, restorecon, setsebool.
- New option for services: seclabel.

ueventd:
- Set the security context for device directories and nodes.

Change-Id: I98ed752cde503c94d99dfa5b5a47e3c33db16aac
parent 0458d373
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -30,6 +30,12 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)

LOCAL_STATIC_LIBRARIES := libcutils libc

ifeq ($(HAVE_SELINUX),true)
LOCAL_STATIC_LIBRARIES += libselinux
LOCAL_C_INCLUDES := external/libselinux/include
LOCAL_CFLAGS += -DHAVE_SELINUX
endif

include $(BUILD_EXECUTABLE)

# Make a symlink from /sbin/ueventd to /init
+85 −0
Original line number Diff line number Diff line
@@ -33,6 +33,11 @@
#include <cutils/partition_utils.h>
#include <sys/system_properties.h>

#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
#endif

#include "init.h"
#include "keywords.h"
#include "property_service.h"
@@ -436,6 +441,28 @@ exit_success:

}

int do_setcon(int nargs, char **args) {
#ifdef HAVE_SELINUX
    if (is_selinux_enabled() <= 0)
        return 0;
    if (setcon(args[1]) < 0) {
        return -errno;
    }
#endif
    return 0;
}

int do_setenforce(int nargs, char **args) {
#ifdef HAVE_SELINUX
    if (is_selinux_enabled() <= 0)
        return 0;
    if (security_setenforce(atoi(args[1])) < 0) {
        return -errno;
    }
#endif
    return 0;
}

int do_setkey(int nargs, char **args)
{
    struct kbentry kbe;
@@ -649,6 +676,64 @@ int do_chmod(int nargs, char **args) {
    return 0;
}

int do_restorecon(int nargs, char **args) {
#ifdef HAVE_SELINUX
    char *secontext = NULL;
    struct stat sb;
    int i;

    if (is_selinux_enabled() <= 0 || !sehandle)
        return 0;

    for (i = 1; i < nargs; i++) {
        if (lstat(args[i], &sb) < 0)
            return -errno;
        if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0)
            return -errno;
        if (lsetfilecon(args[i], secontext) < 0) {
            freecon(secontext);
            return -errno;
        }
        freecon(secontext);
    }
#endif
    return 0;
}

int do_setsebool(int nargs, char **args) {
#ifdef HAVE_SELINUX
    SELboolean *b = alloca(nargs * sizeof(SELboolean));
    char *v;
    int i;

    if (is_selinux_enabled() <= 0)
        return 0;

    for (i = 1; i < nargs; i++) {
        char *name = args[i];
        v = strchr(name, '=');
        if (!v) {
            ERROR("setsebool: argument %s had no =\n", name);
            return -EINVAL;
        }
        *v++ = 0;
        b[i-1].name = name;
        if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
            b[i-1].value = 1;
        else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
            b[i-1].value = 0;
        else {
            ERROR("setsebool: invalid value %s\n", v);
            return -EINVAL;
        }
    }

    if (security_set_boolean_list(nargs - 1, b, 0) < 0)
        return -errno;
#endif
    return 0;
}

int do_loglevel(int nargs, char **args) {
    if (nargs == 2) {
        klog_set_level(atoi(args[1]));
+70 −12
Original line number Diff line number Diff line
@@ -29,6 +29,12 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <linux/netlink.h>

#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
#endif

#include <private/android_filesystem_config.h>
#include <sys/time.h>
#include <asm/page.h>
@@ -45,6 +51,10 @@
#define FIRMWARE_DIR1   "/etc/firmware"
#define FIRMWARE_DIR2   "/vendor/firmware"

#ifdef HAVE_SELINUX
static struct selabel_handle *sehandle;
#endif

static int device_fd = -1;

struct uevent {
@@ -180,8 +190,17 @@ static void make_device(const char *path,
    unsigned gid;
    mode_t mode;
    dev_t dev;
#ifdef HAVE_SELINUX
    char *secontext = NULL;
#endif

    mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
#ifdef HAVE_SELINUX
    if (sehandle) {
        selabel_lookup(sehandle, &secontext, path, mode);
        setfscreatecon(secontext);
    }
#endif
    dev = makedev(major, minor);
    /* Temporarily change egid to avoid race condition setting the gid of the
     * device node. Unforunately changing the euid would prevent creation of
@@ -192,8 +211,40 @@ static void make_device(const char *path,
    mknod(path, mode, dev);
    chown(path, uid, -1);
    setegid(AID_ROOT);
#ifdef HAVE_SELINUX
    if (secontext) {
        freecon(secontext);
        setfscreatecon(NULL);
    }
#endif
}


static int make_dir(const char *path, mode_t mode)
{
    int rc;

#ifdef HAVE_SELINUX
    char *secontext = NULL;

    if (sehandle) {
        selabel_lookup(sehandle, &secontext, path, mode);
        setfscreatecon(secontext);
    }
#endif

    rc = mkdir(path, mode);

#ifdef HAVE_SELINUX
    if (secontext) {
        freecon(secontext);
        setfscreatecon(NULL);
    }
#endif
    return rc;
}


static void add_platform_device(const char *name)
{
    int name_len = strlen(name);
@@ -506,7 +557,7 @@ static void handle_block_device_event(struct uevent *uevent)
        return;

    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
    mkdir(base, 0755);
    make_dir(base, 0755);

    if (!strncmp(uevent->path, "/devices/platform/", 18))
        links = parse_platform_block_device(uevent);
@@ -535,10 +586,10 @@ static void handle_generic_device_event(struct uevent *uevent)
             int bus_id = uevent->minor / 128 + 1;
             int device_id = uevent->minor % 128 + 1;
             /* build directories */
             mkdir("/dev/bus", 0755);
             mkdir("/dev/bus/usb", 0755);
             make_dir("/dev/bus", 0755);
             make_dir("/dev/bus/usb", 0755);
             snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
             mkdir(devpath, 0755);
             make_dir(devpath, 0755);
             snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
         } else {
             /* ignore other USB events */
@@ -546,29 +597,29 @@ static void handle_generic_device_event(struct uevent *uevent)
         }
     } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
         base = "/dev/graphics/";
         mkdir(base, 0755);
         make_dir(base, 0755);
     } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
         base = "/dev/oncrpc/";
         mkdir(base, 0755);
         make_dir(base, 0755);
     } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
         base = "/dev/adsp/";
         mkdir(base, 0755);
         make_dir(base, 0755);
     } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
         base = "/dev/msm_camera/";
         mkdir(base, 0755);
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "input", 5)) {
         base = "/dev/input/";
         mkdir(base, 0755);
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
         base = "/dev/mtd/";
         mkdir(base, 0755);
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "sound", 5)) {
         base = "/dev/snd/";
         mkdir(base, 0755);
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "misc", 4) &&
                 !strncmp(name, "log_", 4)) {
         base = "/dev/log/";
         mkdir(base, 0755);
         make_dir(base, 0755);
         name += 4;
     } else
         base = "/dev/";
@@ -819,7 +870,14 @@ void device_init(void)
    suseconds_t t0, t1;
    struct stat info;
    int fd;
#ifdef HAVE_SELINUX
    struct selinux_opt seopts[] = {
        { SELABEL_OPT_PATH, "/file_contexts" }
    };

    if (is_selinux_enabled() > 0)
        sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
#endif
    /* is 64K enough? udev uses 16MB! */
    device_fd = uevent_open_socket(64*1024, true);
    if(device_fd < 0)
+175 −1
Original line number Diff line number Diff line
@@ -31,6 +31,13 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#ifdef HAVE_SELINUX
#include <sys/mman.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
#endif

#include <libgen.h>

#include <cutils/list.h>
@@ -52,6 +59,10 @@
#include "util.h"
#include "ueventd.h"

#ifdef HAVE_SELINUX
struct selabel_handle *sehandle;
#endif

static int property_triggers_enabled = 0;

#if BOOTCHART
@@ -64,6 +75,11 @@ static char hardware[32];
static unsigned revision = 0;
static char qemu[32];

#ifdef HAVE_SELINUX
static int selinux_enabled = 1;
static int selinux_enforcing = 0;
#endif

static struct action *cur_action = NULL;
static struct command *cur_command = NULL;
static struct listnode *command_queue = NULL;
@@ -145,7 +161,10 @@ void service_start(struct service *svc, const char *dynamic_args)
    pid_t pid;
    int needs_console;
    int n;

#ifdef HAVE_SELINUX
    char *scon = NULL;
    int rc;
#endif
        /* starting a service removes it from the disabled or reset
         * state and immediately takes it out of the restarting
         * state if it was in there
@@ -182,6 +201,34 @@ void service_start(struct service *svc, const char *dynamic_args)
        return;
    }

#ifdef HAVE_SELINUX
    if (is_selinux_enabled() > 0) {
        char *mycon = NULL, *fcon = NULL;

        INFO("computing context for service '%s'\n", svc->args[0]);
        rc = getcon(&mycon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            return;
        }

        rc = getfilecon(svc->args[0], &fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            freecon(mycon);
            return;
        }

        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
        freecon(mycon);
        freecon(fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            return;
        }
    }
#endif

    NOTICE("starting '%s'\n", svc->name);

    pid = fork();
@@ -201,6 +248,10 @@ void service_start(struct service *svc, const char *dynamic_args)
        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);

#ifdef HAVE_SELINUX
        setsockcreatecon(scon);
#endif

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
@@ -212,6 +263,12 @@ void service_start(struct service *svc, const char *dynamic_args)
            }
        }

#ifdef HAVE_SELINUX
        freecon(scon);
        scon = NULL;
        setsockcreatecon(NULL);
#endif

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
@@ -257,6 +314,15 @@ void service_start(struct service *svc, const char *dynamic_args)
            }
        }

#ifdef HAVE_SELINUX
        if (svc->seclabel) {
            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127);
            }
        }
#endif

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
@@ -282,6 +348,10 @@ void service_start(struct service *svc, const char *dynamic_args)
        _exit(127);
    }

#ifdef HAVE_SELINUX
    freecon(scon);
#endif

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
@@ -531,6 +601,14 @@ static void import_kernel_nv(char *name, int for_emulator)
    *value++ = 0;
    if (name_len == 0) return;

#ifdef HAVE_SELINUX
    if (!strcmp(name,"enforcing")) {
        selinux_enforcing = atoi(value);
    } else if (!strcmp(name,"selinux")) {
        selinux_enabled = atoi(value);
    }
#endif

    if (for_emulator) {
        /* in the emulator, export any kernel option with the
         * ro.kernel. prefix */
@@ -678,6 +756,97 @@ static int bootchart_init_action(int nargs, char **args)
}
#endif

#ifdef HAVE_SELINUX
void selinux_load_policy(void)
{
    const char path_prefix[] = "/sepolicy";
    struct selinux_opt seopts[] = {
        { SELABEL_OPT_PATH, "/file_contexts" }
    };
    char path[PATH_MAX];
    int fd, rc, vers;
    struct stat sb;
    void *map;

    sehandle = NULL;
    if (!selinux_enabled) {
        INFO("SELinux:  Disabled by command line option\n");
        return;
    }

    mkdir(SELINUXMNT, 0755);
    if (mount("selinuxfs", SELINUXMNT, "selinuxfs", 0, NULL)) {
        if (errno == ENODEV) {
            /* SELinux not enabled in kernel */
            return;
        }
        ERROR("SELinux:  Could not mount selinuxfs:  %s\n",
              strerror(errno));
        return;
    }
    set_selinuxmnt(SELINUXMNT);

    vers = security_policyvers();
    if (vers <= 0) {
        ERROR("SELinux:  Unable to read policy version\n");
        return;
    }
    INFO("SELinux:  Maximum supported policy version:  %d\n", vers);

    snprintf(path, sizeof(path), "%s.%d",
             path_prefix, vers);
    fd = open(path, O_RDONLY);
    while (fd < 0 && errno == ENOENT && --vers) {
        snprintf(path, sizeof(path), "%s.%d",
                 path_prefix, vers);
        fd = open(path, O_RDONLY);
    }
    if (fd < 0) {
        ERROR("SELinux:  Could not open %s:  %s\n",
              path, strerror(errno));
        return;
    }
    if (fstat(fd, &sb) < 0) {
        ERROR("SELinux:  Could not stat %s:  %s\n",
              path, strerror(errno));
        return;
    }
    map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (map == MAP_FAILED) {
        ERROR("SELinux:  Could not map %s:  %s\n",
              path, strerror(errno));
        return;
    }

    rc = security_load_policy(map, sb.st_size);
    if (rc < 0) {
        ERROR("SELinux:  Could not load policy:  %s\n",
              strerror(errno));
        return;
    }

    rc = security_setenforce(selinux_enforcing);
    if (rc < 0) {
        ERROR("SELinux:  Could not set enforcing mode to %s:  %s\n",
              selinux_enforcing ? "enforcing" : "permissive", strerror(errno));
        return;
    }

    munmap(map, sb.st_size);
    close(fd);
    INFO("SELinux: Loaded policy from %s\n", path);

    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
    if (!sehandle) {
        ERROR("SELinux:  Could not load file_contexts:  %s\n",
              strerror(errno));
        return;
    }
    INFO("SELinux: Loaded file contexts from %s\n", seopts[0].value);
    return;
}
#endif

int main(int argc, char **argv)
{
    int fd_count = 0;
@@ -728,6 +897,11 @@ int main(int argc, char **argv)

    process_kernel_cmdline();

#ifdef HAVE_SELINUX
    INFO("loading selinux policy\n");
    selinux_load_policy();
#endif

    is_charger = !strcmp(bootmode, "charger");

    INFO("property init\n");
+8 −0
Original line number Diff line number Diff line
@@ -95,6 +95,10 @@ struct service {
    gid_t supp_gids[NR_SVC_SUPP_GIDS];
    size_t nr_supp_gids;

#ifdef HAVE_SELINUX
    char *seclabel;
#endif

    struct socketinfo *sockets;
    struct svcenvinfo *envvars;

@@ -132,4 +136,8 @@ void property_changed(const char *name, const char *value);

int load_565rle_image( char *file_name );

#ifdef HAVE_SELINUX
extern struct selabel_handle *sehandle;
#endif

#endif	/* _INIT_INIT_H */
Loading