| /* Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "system.h" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <net/if.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/prctl.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "util.h" |
| |
| #ifdef HAVE_SECUREBITS_H |
| #include <linux/securebits.h> |
| #else |
| #define SECURE_ALL_BITS 0x55 |
| #define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1) |
| #endif |
| |
| #define SECURE_BITS_NO_AMBIENT 0x15 |
| #define SECURE_LOCKS_NO_AMBIENT (SECURE_BITS_NO_AMBIENT << 1) |
| |
| /* |
| * Assert the value of SECURE_ALL_BITS at compile-time. |
| * Android devices are currently compiled against 4.4 kernel headers. Kernel 4.3 |
| * added a new securebit. |
| * When a new securebit is added, the new SECURE_ALL_BITS mask will return EPERM |
| * when used on older kernels. The compile-time assert will catch this situation |
| * at compile time. |
| */ |
| #if defined(__ANDROID__) |
| _Static_assert(SECURE_ALL_BITS == 0x55, "SECURE_ALL_BITS == 0x55."); |
| #endif |
| |
| int lock_securebits(uint64_t skip_mask) |
| { |
| /* |
| * Ambient capabilities can only be raised if they're already present |
| * in the permitted *and* inheritable set. Therefore, we don't really |
| * need to lock the NO_CAP_AMBIENT_RAISE securebit, since we are already |
| * configuring the permitted and inheritable set. |
| */ |
| unsigned long securebits = |
| (SECURE_BITS_NO_AMBIENT | SECURE_LOCKS_NO_AMBIENT) & ~skip_mask; |
| if (!securebits) { |
| return 0; |
| } |
| int securebits_ret = prctl(PR_SET_SECUREBITS, securebits); |
| if (securebits_ret < 0) { |
| pwarn("prctl(PR_SET_SECUREBITS) failed"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int write_proc_file(pid_t pid, const char *content, const char *basename) |
| { |
| int fd, ret; |
| size_t sz, len; |
| ssize_t written; |
| char filename[32]; |
| |
| sz = sizeof(filename); |
| ret = snprintf(filename, sz, "/proc/%d/%s", pid, basename); |
| if (ret < 0 || (size_t)ret >= sz) { |
| warn("failed to generate %s filename", basename); |
| return -1; |
| } |
| |
| fd = open(filename, O_WRONLY | O_CLOEXEC); |
| if (fd < 0) { |
| pwarn("failed to open '%s'", filename); |
| return -errno; |
| } |
| |
| len = strlen(content); |
| written = write(fd, content, len); |
| if (written < 0) { |
| pwarn("failed to write '%s'", filename); |
| return -1; |
| } |
| |
| if ((size_t)written < len) { |
| warn("failed to write %zu bytes to '%s'", len, filename); |
| return -1; |
| } |
| close(fd); |
| return 0; |
| } |
| |
| /* |
| * We specifically do not use cap_valid() as that only tells us the last |
| * valid cap we were *compiled* against (i.e. what the version of kernel |
| * headers says). If we run on a different kernel version, then it's not |
| * uncommon for that to be less (if an older kernel) or more (if a newer |
| * kernel). |
| * Normally, we suck up the answer via /proc. On Android, not all processes are |
| * guaranteed to be able to access '/proc/sys/kernel/cap_last_cap' so we |
| * programmatically find the value by calling prctl(PR_CAPBSET_READ). |
| */ |
| unsigned int get_last_valid_cap(void) |
| { |
| unsigned int last_valid_cap = 0; |
| if (is_android()) { |
| for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; |
| ++last_valid_cap) |
| ; |
| |
| /* |last_valid_cap| will be the first failing value. */ |
| if (last_valid_cap > 0) { |
| last_valid_cap--; |
| } |
| } else { |
| const char cap_file[] = "/proc/sys/kernel/cap_last_cap"; |
| FILE *fp = fopen(cap_file, "re"); |
| if (fscanf(fp, "%u", &last_valid_cap) != 1) |
| pdie("fscanf(%s)", cap_file); |
| fclose(fp); |
| } |
| return last_valid_cap; |
| } |
| |
| int cap_ambient_supported(void) |
| { |
| return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= |
| 0; |
| } |
| |
| int config_net_loopback(void) |
| { |
| const char ifname[] = "lo"; |
| int sock; |
| struct ifreq ifr; |
| |
| /* Make sure people don't try to add really long names. */ |
| _Static_assert(sizeof(ifname) <= IFNAMSIZ, "interface name too long"); |
| |
| sock = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); |
| if (sock < 0) { |
| pwarn("socket(AF_LOCAL) failed"); |
| return -1; |
| } |
| |
| /* |
| * Do the equiv of `ip link set up lo`. The kernel will assign |
| * IPv4 (127.0.0.1) & IPv6 (::1) addresses automatically! |
| */ |
| strcpy(ifr.ifr_name, ifname); |
| if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { |
| pwarn("ioctl(SIOCGIFFLAGS) failed"); |
| return -1; |
| } |
| |
| /* The kernel preserves ifr.ifr_name for use. */ |
| ifr.ifr_flags |= IFF_UP | IFF_RUNNING; |
| if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) { |
| pwarn("ioctl(SIOCSIFFLAGS) failed"); |
| return -1; |
| } |
| |
| close(sock); |
| return 0; |
| } |
| |
| int setup_pipe_end(int fds[2], size_t index) |
| { |
| if (index > 1) |
| return -1; |
| |
| close(fds[1 - index]); |
| return fds[index]; |
| } |
| |
| int setup_and_dupe_pipe_end(int fds[2], size_t index, int fd) |
| { |
| if (index > 1) |
| return -1; |
| |
| close(fds[1 - index]); |
| /* dup2(2) the corresponding end of the pipe into |fd|. */ |
| return dup2(fds[index], fd); |
| } |
| |
| int write_pid_to_path(pid_t pid, const char *path) |
| { |
| FILE *fp = fopen(path, "we"); |
| |
| if (!fp) { |
| pwarn("failed to open '%s'", path); |
| return -errno; |
| } |
| if (fprintf(fp, "%d\n", (int)pid) < 0) { |
| /* fprintf(3) does not set errno on failure. */ |
| warn("fprintf(%s) failed", path); |
| return -1; |
| } |
| if (fclose(fp)) { |
| pwarn("fclose(%s) failed", path); |
| return -errno; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * setup_mount_destination: Ensures the mount target exists. |
| * Creates it if needed and possible. |
| */ |
| int setup_mount_destination(const char *source, const char *dest, uid_t uid, |
| uid_t gid, bool bind) |
| { |
| int rc; |
| struct stat st_buf; |
| bool domkdir; |
| |
| rc = stat(dest, &st_buf); |
| if (rc == 0) /* destination exists */ |
| return 0; |
| |
| /* |
| * Try to create the destination. |
| * Either make a directory or touch a file depending on the source type. |
| * |
| * If the source isn't an absolute path, assume it is a filesystem type |
| * such as "tmpfs" and create a directory to mount it on. The dest will |
| * be something like "none" or "proc" which we shouldn't be checking. |
| */ |
| if (source[0] == '/') { |
| /* The source is an absolute path -- it better exist! */ |
| rc = stat(source, &st_buf); |
| if (rc) |
| return -errno; |
| |
| /* |
| * If bind mounting, we only create a directory if the source |
| * is a directory, else we always bind mount it as a file to |
| * support device nodes, sockets, etc... |
| * |
| * For all other mounts, we assume a block/char source is |
| * going to want a directory to mount to. If the source is |
| * something else (e.g. a fifo or socket), this probably will |
| * not do the right thing, but we'll fail later on when we try |
| * to mount(), so shouldn't be a big deal. |
| */ |
| domkdir = S_ISDIR(st_buf.st_mode) || |
| (!bind && (S_ISBLK(st_buf.st_mode) || |
| S_ISCHR(st_buf.st_mode))); |
| } else { |
| /* The source is a relative path -- assume it's a pseudo fs. */ |
| |
| /* Disallow relative bind mounts. */ |
| if (bind) |
| return -EINVAL; |
| |
| domkdir = true; |
| } |
| |
| /* Now that we know what we want to do, do it! */ |
| if (domkdir) { |
| if (mkdir(dest, 0700)) |
| return -errno; |
| } else { |
| int fd = open(dest, O_RDWR | O_CREAT | O_CLOEXEC, 0700); |
| if (fd < 0) |
| return -errno; |
| close(fd); |
| } |
| return chown(dest, uid, gid); |
| } |