diff --git a/toolbox/Android.mk b/toolbox/Android.mk index d7a675a70..9daeed3ee 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -57,6 +57,21 @@ TOOLS := \ touch \ lsof +ifeq ($(HAVE_SELINUX),true) + +TOOLS += \ + getenforce \ + setenforce \ + chcon \ + restorecon \ + runcon \ + getsebool \ + setsebool \ + load_policy + +endif + + ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r endif @@ -68,6 +83,14 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := libcutils libc libusbhost +ifeq ($(HAVE_SELINUX),true) + +LOCAL_CFLAGS += -DHAVE_SELINUX +LOCAL_SHARED_LIBRARIES += libselinux +LOCAL_C_INCLUDES += external/libselinux/include + +endif + LOCAL_MODULE:= toolbox # Including this will define $(intermediates). diff --git a/toolbox/chcon.c b/toolbox/chcon.c new file mode 100644 index 000000000..d594b9b4d --- /dev/null +++ b/toolbox/chcon.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include + +int chcon_main(int argc, char **argv) +{ + int rc, i; + + if (argc < 3) { + fprintf(stderr, "usage: %s context path...\n", argv[0]); + exit(1); + } + + for (i = 2; i < argc; i++) { + rc = setfilecon(argv[i], argv[1]); + if (rc < 0) { + fprintf(stderr, "%s: Could not label %s with %s: %s\n", + argv[0], argv[i], argv[1], strerror(errno)); + exit(2); + } + } + exit(0); +} diff --git a/toolbox/getenforce.c b/toolbox/getenforce.c new file mode 100644 index 000000000..9e7589a48 --- /dev/null +++ b/toolbox/getenforce.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include + +int getenforce_main(int argc, char **argv) +{ + int rc; + + rc = is_selinux_enabled(); + if (rc <= 0) { + printf("Disabled\n"); + return 0; + } + + rc = security_getenforce(); + if (rc < 0) { + fprintf(stderr, "Could not get enforcing status: %s\n", + strerror(errno)); + return 2; + } + + if (rc) + printf("Enforcing\n"); + else + printf("Permissive\n"); + + return 0; +} diff --git a/toolbox/getsebool.c b/toolbox/getsebool.c new file mode 100644 index 000000000..aab520020 --- /dev/null +++ b/toolbox/getsebool.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include + +static void usage(const char *progname) +{ + fprintf(stderr, "usage: %s -a or %s boolean...\n", progname, progname); + exit(1); +} + +int getsebool_main(int argc, char **argv) +{ + int i, get_all = 0, rc = 0, active, pending, len = 0, opt; + char **names; + + while ((opt = getopt(argc, argv, "a")) > 0) { + switch (opt) { + case 'a': + if (argc > 2) + usage(argv[0]); + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "%s: SELinux is disabled\n", + argv[0]); + return 1; + } + errno = 0; + rc = security_get_boolean_names(&names, &len); + if (rc) { + fprintf(stderr, + "%s: Unable to get boolean names: %s\n", + argv[0], strerror(errno)); + return 1; + } + if (!len) { + printf("No booleans\n"); + return 0; + } + get_all = 1; + break; + default: + usage(argv[0]); + } + } + + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "%s: SELinux is disabled\n", argv[0]); + return 1; + } + if (!len) { + if (argc < 2) + usage(argv[0]); + len = argc - 1; + names = malloc(sizeof(char *) * len); + if (!names) { + fprintf(stderr, "%s: out of memory\n", argv[0]); + return 2; + } + for (i = 0; i < len; i++) { + names[i] = strdup(argv[i + 1]); + if (!names[i]) { + fprintf(stderr, "%s: out of memory\n", + argv[0]); + return 2; + } + } + } + + for (i = 0; i < len; i++) { + active = security_get_boolean_active(names[i]); + if (active < 0) { + if (get_all && errno == EACCES) + continue; + fprintf(stderr, "Error getting active value for %s\n", + names[i]); + rc = -1; + goto out; + } + pending = security_get_boolean_pending(names[i]); + if (pending < 0) { + fprintf(stderr, "Error getting pending value for %s\n", + names[i]); + rc = -1; + goto out; + } + if (pending != active) { + printf("%s --> %s pending: %s\n", names[i], + (active ? "on" : "off"), + (pending ? "on" : "off")); + } else { + printf("%s --> %s\n", names[i], + (active ? "on" : "off")); + } + } + +out: + for (i = 0; i < len; i++) + free(names[i]); + free(names); + return rc; +} diff --git a/toolbox/id.c b/toolbox/id.c index bb03cad2a..bc792887c 100644 --- a/toolbox/id.c +++ b/toolbox/id.c @@ -5,6 +5,10 @@ #include #include +#ifdef HAVE_SELINUX +#include +#endif + static void print_uid(uid_t uid) { struct passwd *pw = getpwuid(uid); @@ -30,6 +34,9 @@ int id_main(int argc, char **argv) { gid_t list[64]; int n, max; +#ifdef HAVE_SELINUX + char *secctx; +#endif max = getgroups(64, list); if (max < 0) max = 0; @@ -46,6 +53,12 @@ int id_main(int argc, char **argv) print_gid(list[n]); } } +#ifdef HAVE_SELINUX + if (getcon(&secctx) == 0) { + printf(" context=%s", secctx); + free(secctx); + } +#endif printf("\n"); return 0; } diff --git a/toolbox/load_policy.c b/toolbox/load_policy.c new file mode 100644 index 000000000..eb5aba674 --- /dev/null +++ b/toolbox/load_policy.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int load_policy_main(int argc, char **argv) +{ + int fd, rc, vers; + struct stat sb; + void *map; + const char *path; + + if (argc != 2) { + fprintf(stderr, "usage: %s policy-file\n", argv[0]); + exit(1); + } + + path = argv[1]; + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Could not open %s: %s\n", path, strerror(errno)); + exit(2); + } + + if (fstat(fd, &sb) < 0) { + fprintf(stderr, "Could not stat %s: %s\n", path, strerror(errno)); + exit(3); + } + + map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + fprintf(stderr, "Could not mmap %s: %s\n", path, strerror(errno)); + exit(4); + } + + rc = security_load_policy(map, sb.st_size); + if (rc < 0) { + fprintf(stderr, "Could not load %s: %s\n", path, strerror(errno)); + exit(5); + } + munmap(map, sb.st_size); + close(fd); + exit(0); +} diff --git a/toolbox/ls.c b/toolbox/ls.c index bee365c9d..a4db99ca8 100644 --- a/toolbox/ls.c +++ b/toolbox/ls.c @@ -5,6 +5,10 @@ #include #include +#ifdef HAVE_SELINUX +#include +#endif + #include #include #include @@ -25,6 +29,7 @@ #define LIST_SIZE (1 << 4) #define LIST_LONG_NUMERIC (1 << 5) #define LIST_CLASSIFY (1 << 6) +#define LIST_MACLABEL (1 << 7) // fwd static int listpath(const char *name, int flags); @@ -234,9 +239,75 @@ static int listfile_long(const char *path, int flags) return 0; } +static int listfile_maclabel(const char *path, int flags) +{ + struct stat s; + char mode[16]; + char user[16]; + char group[16]; + char *maclabel = NULL; + const char *name; + + /* name is anything after the final '/', or the whole path if none*/ + name = strrchr(path, '/'); + if(name == 0) { + name = path; + } else { + name++; + } + + if(lstat(path, &s) < 0) { + return -1; + } + +#ifdef HAVE_SELINUX + lgetfilecon(path, &maclabel); +#else + maclabel = strdup("-"); +#endif + if (!maclabel) { + return -1; + } + + mode2str(s.st_mode, mode); + user2str(s.st_uid, user); + group2str(s.st_gid, group); + + switch(s.st_mode & S_IFMT) { + case S_IFLNK: { + char linkto[256]; + int len; + + len = readlink(path, linkto, sizeof(linkto)); + if(len < 0) return -1; + + if(len > sizeof(linkto)-1) { + linkto[sizeof(linkto)-4] = '.'; + linkto[sizeof(linkto)-3] = '.'; + linkto[sizeof(linkto)-2] = '.'; + linkto[sizeof(linkto)-1] = 0; + } else { + linkto[len] = 0; + } + + printf("%s %-8s %-8s %s %s -> %s\n", + mode, user, group, maclabel, name, linkto); + break; + } + default: + printf("%s %-8s %-8s %s %s\n", + mode, user, group, maclabel, name); + + } + + free(maclabel); + + return 0; +} + static int listfile(const char *dirname, const char *filename, int flags) { - if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY)) == 0) { + if ((flags & LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL) == 0) { printf("%s\n", filename); return 0; } @@ -251,7 +322,9 @@ static int listfile(const char *dirname, const char *filename, int flags) pathname = filename; } - if ((flags & LIST_LONG) != 0) { + if ((flags & LIST_MACLABEL) != 0) { + return listfile_maclabel(pathname, flags); + } else if ((flags & LIST_LONG) != 0) { return listfile_long(pathname, flags); } else /*((flags & LIST_SIZE) != 0)*/ { return listfile_size(pathname, filename, flags); @@ -386,6 +459,7 @@ int ls_main(int argc, char **argv) case 's': flags |= LIST_SIZE; break; case 'R': flags |= LIST_RECURSIVE; break; case 'd': flags |= LIST_DIRECTORIES; break; + case 'Z': flags |= LIST_MACLABEL; break; case 'a': flags |= LIST_ALL; break; case 'F': flags |= LIST_CLASSIFY; break; default: diff --git a/toolbox/ps.c b/toolbox/ps.c index 2aa3efb56..7c3de4a39 100644 --- a/toolbox/ps.c +++ b/toolbox/ps.c @@ -13,7 +13,6 @@ #include - static char *nexttoksep(char **strp, char *sep) { char *p = strsep(strp,sep); @@ -28,6 +27,7 @@ static char *nexttok(char **strp) #define SHOW_TIME 2 #define SHOW_POLICY 4 #define SHOW_CPU 8 +#define SHOW_MACLABEL 16 static int display_flags = 0; @@ -35,6 +35,7 @@ static int ps_line(int pid, int tid, char *namefilter) { char statline[1024]; char cmdline[1024]; + char macline[1024]; char user[32]; struct stat stats; int fd, r; @@ -51,9 +52,11 @@ static int ps_line(int pid, int tid, char *namefilter) if(tid) { sprintf(statline, "/proc/%d/task/%d/stat", pid, tid); cmdline[0] = 0; + snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid); } else { sprintf(statline, "/proc/%d/stat", pid); - sprintf(cmdline, "/proc/%d/cmdline", pid); + sprintf(cmdline, "/proc/%d/cmdline", pid); + snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid); fd = open(cmdline, O_RDONLY); if(fd == 0) { r = 0; @@ -142,6 +145,19 @@ static int ps_line(int pid, int tid, char *namefilter) } if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) { + if (display_flags & SHOW_MACLABEL) { + fd = open(macline, O_RDONLY); + strcpy(macline, "-"); + if (fd >= 0) { + r = read(fd, macline, sizeof(macline)-1); + close(fd); + if (r > 0) + macline[r] = 0; + } + printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name); + return 0; + } + printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4); if (display_flags & SHOW_CPU) printf(" %-2d", psr); @@ -206,6 +222,8 @@ int ps_main(int argc, char **argv) threads = 1; } else if(!strcmp(argv[1],"-x")) { display_flags |= SHOW_TIME; + } else if(!strcmp(argv[1], "-Z")) { + display_flags |= SHOW_MACLABEL; } else if(!strcmp(argv[1],"-P")) { display_flags |= SHOW_POLICY; } else if(!strcmp(argv[1],"-p")) { @@ -221,10 +239,14 @@ int ps_main(int argc, char **argv) argv++; } - printf("USER PID PPID VSIZE RSS %s%s %s WCHAN PC NAME\n", - (display_flags&SHOW_CPU)?"CPU ":"", - (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"", - (display_flags&SHOW_POLICY)?"PCY " : ""); + if (display_flags & SHOW_MACLABEL) { + printf("LABEL USER PID PPID NAME\n"); + } else { + printf("USER PID PPID VSIZE RSS %s%s %s WCHAN PC NAME\n", + (display_flags&SHOW_CPU)?"CPU ":"", + (display_flags&SHOW_PRIO)?"PRIO NICE RTPRI SCHED ":"", + (display_flags&SHOW_POLICY)?"PCY " : ""); + } while((de = readdir(d)) != 0){ if(isdigit(de->d_name[0])){ int pid = atoi(de->d_name); diff --git a/toolbox/restorecon.c b/toolbox/restorecon.c new file mode 100644 index 000000000..5ef0ef1fb --- /dev/null +++ b/toolbox/restorecon.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FCPATH "/file_contexts" + +static struct selabel_handle *sehandle; +static const char *progname; +static int nochange; +static int verbose; + +static void usage(void) +{ + fprintf(stderr, "usage: %s [-f file_contexts] [-nrRv] pathname...\n", progname); + exit(1); +} + +static int restore(const char *pathname, const struct stat *sb) +{ + char *oldcontext, *newcontext; + + if (lgetfilecon(pathname, &oldcontext) < 0) { + fprintf(stderr, "Could not get context of %s: %s\n", + pathname, strerror(errno)); + return -1; + } + if (selabel_lookup(sehandle, &newcontext, pathname, sb->st_mode) < 0) { + fprintf(stderr, "Could not lookup context for %s: %s\n", pathname, + strerror(errno)); + return -1; + } + if (strcmp(newcontext, "<>") && + strcmp(oldcontext, newcontext)) { + if (verbose) + printf("Relabeling %s from %s to %s.\n", pathname, oldcontext, newcontext); + if (!nochange) { + if (lsetfilecon(pathname, newcontext) < 0) { + fprintf(stderr, "Could not label %s with %s: %s\n", + pathname, newcontext, strerror(errno)); + return -1; + } + } + } + freecon(oldcontext); + freecon(newcontext); + return 0; +} + +int restorecon_main(int argc, char **argv) +{ + struct selinux_opt seopts[] = { + { SELABEL_OPT_PATH, FCPATH } + }; + int ch, recurse = 0, ftsflags = FTS_PHYSICAL; + + progname = argv[0]; + + do { + ch = getopt(argc, argv, "f:nrRv"); + if (ch == EOF) + break; + switch (ch) { + case 'f': + seopts[0].value = optarg; + break; + case 'n': + nochange = 1; + break; + case 'r': + case 'R': + recurse = 1; + break; + case 'v': + verbose = 1; + break; + default: + usage(); + } + } while (1); + + argc -= optind; + argv += optind; + if (!argc) + usage(); + + sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); + if (!sehandle) { + fprintf(stderr, "Could not load file contexts from %s: %s\n", seopts[0].value, + strerror(errno)); + return -1; + } + + if (recurse) { + FTS *fts; + FTSENT *ftsent; + fts = fts_open(argv, ftsflags, NULL); + if (!fts) { + fprintf(stderr, "Could not traverse filesystems (first was %s): %s\n", + argv[0], strerror(errno)); + return -1; + } + while ((ftsent = fts_read(fts))) { + switch (ftsent->fts_info) { + case FTS_DP: + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + fprintf(stderr, "Could not access %s: %s\n", ftsent->fts_path, + strerror(errno)); + fts_set(fts, ftsent, FTS_SKIP); + break; + default: + if (restore(ftsent->fts_path, ftsent->fts_statp) < 0) + fts_set(fts, ftsent, FTS_SKIP); + break; + } + } + } else { + int i, rc; + struct stat sb; + + for (i = 0; i < argc; i++) { + rc = lstat(argv[i], &sb); + if (rc < 0) { + fprintf(stderr, "Could not stat %s: %s\n", argv[i], + strerror(errno)); + continue; + } + restore(argv[i], &sb); + } + } + + return 0; +} diff --git a/toolbox/runcon.c b/toolbox/runcon.c new file mode 100644 index 000000000..4a57bf316 --- /dev/null +++ b/toolbox/runcon.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include + +int runcon_main(int argc, char **argv) +{ + int rc; + + if (argc < 3) { + fprintf(stderr, "usage: %s context program args...\n", argv[0]); + exit(1); + } + + rc = setexeccon(argv[1]); + if (rc < 0) { + fprintf(stderr, "Could not set context to %s: %s\n", argv[1], strerror(errno)); + exit(2); + } + + argv += 2; + argc -= 2; + execvp(argv[0], argv); + fprintf(stderr, "Could not exec %s: %s\n", argv[0], strerror(errno)); + exit(3); +} diff --git a/toolbox/setenforce.c b/toolbox/setenforce.c new file mode 100644 index 000000000..1b0ea5c66 --- /dev/null +++ b/toolbox/setenforce.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void usage(const char *progname) +{ + fprintf(stderr, "usage: %s [ Enforcing | Permissive | 1 | 0 ]\n", + progname); + exit(1); +} + +int setenforce_main(int argc, char **argv) +{ + int rc = 0; + if (argc != 2) { + usage(argv[0]); + } + + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "%s: SELinux is disabled\n", argv[0]); + return 1; + } + if (strlen(argv[1]) == 1 && (argv[1][0] == '0' || argv[1][0] == '1')) { + rc = security_setenforce(atoi(argv[1])); + } else { + if (strcasecmp(argv[1], "enforcing") == 0) { + rc = security_setenforce(1); + } else if (strcasecmp(argv[1], "permissive") == 0) { + rc = security_setenforce(0); + } else + usage(argv[0]); + } + if (rc < 0) { + fprintf(stderr, "%s: Could not set enforcing status: %s\n", + argv[0], strerror(errno)); + return 2; + } + return 0; +} diff --git a/toolbox/setsebool.c b/toolbox/setsebool.c new file mode 100644 index 000000000..4a3d87d0f --- /dev/null +++ b/toolbox/setsebool.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int do_setsebool(int nargs, char **args) { + 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) { + fprintf(stderr, "setsebool: argument %s had no =\n", name); + return -1; + } + *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 { + fprintf(stderr, "setsebool: invalid value %s\n", v); + return -1; + } + } + + if (security_set_boolean_list(nargs - 1, b, 0) < 0) + { + fprintf(stderr, "setsebool: unable to set booleans: %s", strerror(errno)); + return -1; + } + + return 0; +} + +int setsebool_main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "Usage: %s name=value...\n", argv[0]); + exit(1); + } + + return do_setsebool(argc, argv); +}