Improve builtin selinux implementation

This commit is contained in:
topjohnwu 2019-03-14 06:34:22 -04:00
parent b7e2e972c7
commit da0a72e8b0
5 changed files with 142 additions and 78 deletions

View File

@ -27,15 +27,17 @@ static int is_excl(const char *name) {
return 0;
}
int fd_getpath(int fd, char *path, size_t size) {
ssize_t fd_path(int fd, char *path, size_t size) {
snprintf(path, size, "/proc/self/fd/%d", fd);
return xreadlink(path, path, size) == -1;
return xreadlink(path, path, size);
}
int fd_getpathat(int dirfd, const char *name, char *path, size_t size) {
if (fd_getpath(dirfd, path, size))
return 1;
snprintf(path, size, "%s/%s", path, name);
int fd_pathat(int dirfd, const char *name, char *path, size_t size) {
ssize_t len = fd_path(dirfd, path, size);
if (len < 0)
return -1;
path[len] = '/';
strlcpy(&path[len + 1], name, size - len - 1);
return 0;
}
@ -85,7 +87,7 @@ void rm_rf(const char *path) {
if (lstat(path, &st) < 0)
return;
if (S_ISDIR(st.st_mode)) {
int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
int fd = open(path, O_RDONLY | O_CLOEXEC);
frm_rf(fd);
close(fd);
}
@ -211,7 +213,7 @@ void clone_dir(int src, int dest) {
case DT_REG:
destfd = xopenat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC);
srcfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
xsendfile(destfd, srcfd, 0, a.st.st_size);
xsendfile(destfd, srcfd, nullptr, a.st.st_size);
fsetattr(destfd, &a);
close(destfd);
close(srcfd);
@ -263,16 +265,21 @@ int getattr(const char *path, struct file_attr *a) {
return 0;
}
int getattrat(int dirfd, const char *pathname, struct file_attr *a) {
char path[PATH_MAX];
fd_getpathat(dirfd, pathname, path, sizeof(path));
int getattrat(int dirfd, const char *name, struct file_attr *a) {
char path[4096];
fd_pathat(dirfd, name, path, sizeof(path));
return getattr(path, a);
}
int fgetattr(int fd, struct file_attr *a) {
char path[PATH_MAX];
fd_getpath(fd, path, sizeof(path));
return getattr(path, a);
if (xfstat(fd, &a->st) < 0)
return -1;
char *con;
if (fgetfilecon(fd, &con) < 0)
return -1;
strcpy(a->con, con);
freecon(con);
return 0;
}
int setattr(const char *path, struct file_attr *a) {
@ -280,21 +287,25 @@ int setattr(const char *path, struct file_attr *a) {
return -1;
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
return -1;
if (strlen(a->con) && lsetfilecon(path, a->con) < 0)
if (a->con[0] && lsetfilecon(path, a->con) < 0)
return -1;
return 0;
}
int setattrat(int dirfd, const char *pathname, struct file_attr *a) {
char path[PATH_MAX];
fd_getpathat(dirfd, pathname, path, sizeof(path));
int setattrat(int dirfd, const char *name, struct file_attr *a) {
char path[4096];
fd_pathat(dirfd, name, path, sizeof(path));
return setattr(path, a);
}
int fsetattr(int fd, struct file_attr *a) {
char path[PATH_MAX];
fd_getpath(fd, path, sizeof(path));
return setattr(path, a);
if (fchmod(fd, a->st.st_mode & 0777) < 0)
return -1;
if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0)
return -1;
if (a->con[0] && fsetfilecon(fd, a->con) < 0)
return -1;
return 0;
}
void clone_attr(const char *source, const char *target) {
@ -346,17 +357,6 @@ void full_read(const char *filename, void **buf, size_t *size) {
close(fd);
}
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size) {
int fd = xopenat(dirfd, filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
*buf = nullptr;
*size = 0;
return;
}
fd_full_read(fd, buf, size);
close(fd);
}
void write_zero(int fd, size_t size) {
char buf[4096] = {0};
size_t len;

View File

@ -8,8 +8,12 @@ extern void (*freecon)(char * con);
extern int (*setcon)(const char *con);
extern int (*getfilecon)(const char *path, char **con);
extern int (*lgetfilecon)(const char *path, char **con);
extern int (*fgetfilecon)(int fd, char **con);
extern int (*setfilecon)(const char *path, const char *con);
extern int (*lsetfilecon)(const char *path, const char *con);
extern int (*fsetfilecon)(int fd, const char *con);
void getfilecon_at(int dirfd, const char *name, char **con);
void setfilecon_at(int dirfd, const char *name, const char *con);
void selinux_builtin_impl();
void dload_selinux();

View File

@ -51,6 +51,7 @@ int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
int xstat(const char *pathname, struct stat *buf);
int xlstat(const char *pathname, struct stat *buf);
int xfstat(int fd, struct stat *buf);
int xdup2(int oldfd, int newfd);
int xdup3(int oldfd, int newfd, int flags);
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz);
@ -103,8 +104,8 @@ struct file_attr {
char con[128];
};
int fd_getpath(int fd, char *path, size_t size);
int fd_getpathat(int dirfd, const char *name, char *path, size_t size);
ssize_t fd_path(int fd, char *path, size_t size);
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
int mkdirs(const char *pathname, mode_t mode);
void post_order_walk(int dirfd, void (*fn)(int, struct dirent *));
void rm_rf(const char *path);
@ -115,17 +116,16 @@ void cp_afc(const char *source, const char *destination);
void link_dir(int src, int dest);
void clone_dir(int src, int dest);
int getattr(const char *path, struct file_attr *a);
int getattrat(int dirfd, const char *pathname, struct file_attr *a);
int getattrat(int dirfd, const char *name, struct file_attr *a);
int fgetattr(int fd, struct file_attr *a);
int setattr(const char *path, struct file_attr *a);
int setattrat(int dirfd, const char *pathname, struct file_attr *a);
int setattrat(int dirfd, const char *name, struct file_attr *a);
int fsetattr(int fd, struct file_attr *a);
void fclone_attr(int sourcefd, int targetfd);
void clone_attr(const char *source, const char *target);
void mmap_ro(const char *filename, void **buf, size_t *size);
void fd_full_read(int fd, void **buf, size_t *size);
void full_read(const char *filename, void **buf, size_t *size);
void full_read_at(int dirfd, const char *filename, void **buf, size_t *size);
void write_zero(int fd, size_t size);
#ifdef __cplusplus

View File

@ -25,6 +25,13 @@ static int stub(const char *, char **ctx) {
return 0;
}
static int stub(int, const char *) { return 0; }
static int stub(int, char **ctx) {
*ctx = strdup("");
return 0;
}
// Builtin implementation
static void __freecon(char *s) {
@ -44,7 +51,10 @@ static int __setcon(const char *ctx) {
static int __getfilecon(const char *path, char **ctx) {
char buf[1024];
int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc > 0)
if (rc < 0) {
errno = -rc;
return -1;
}
*ctx = strdup(buf);
return rc;
}
@ -52,17 +62,50 @@ static int __getfilecon(const char *path, char **ctx) {
static int __lgetfilecon(const char *path, char **ctx) {
char buf[1024];
int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc > 0)
if (rc < 0) {
errno = -rc;
return -1;
}
*ctx = strdup(buf);
return rc;
}
static int __fgetfilecon(int fd, char **ctx) {
char buf[1024];
int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc < 0) {
errno = -rc;
return -1;
}
*ctx = strdup(buf);
return rc;
}
static int __setfilecon(const char *path, const char *ctx) {
return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
int rc = syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
if (rc) {
errno = -rc;
return -1;
}
return 0;
}
static int __lsetfilecon(const char *path, const char *ctx) {
return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
int rc = syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
if (rc) {
errno = -rc;
return -1;
}
return 0;
}
static int __fsetfilecon(int fd, const char *ctx) {
int rc = syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
if (rc) {
errno = -rc;
return -1;
}
return 0;
}
// Function pointers
@ -71,15 +114,32 @@ void (*freecon)(char *) = __freecon;
int (*setcon)(const char *) = stub;
int (*getfilecon)(const char *, char **) = stub;
int (*lgetfilecon)(const char *, char **) = stub;
int (*fgetfilecon)(int, char **) = stub;
int (*setfilecon)(const char *, const char *) = stub;
int (*lsetfilecon)(const char *, const char *) = stub;
int (*fsetfilecon)(int, const char *) = stub;
void getfilecon_at(int dirfd, const char *name, char **con) {
char path[4096];
fd_pathat(dirfd, name, path, sizeof(path));
if (lgetfilecon(path, con))
*con = strdup("");
}
void setfilecon_at(int dirfd, const char *name, const char *con) {
char path[4096];
fd_pathat(dirfd, name, path, sizeof(path));
lsetfilecon(path, con);
}
void selinux_builtin_impl() {
setcon = __setcon;
getfilecon = __getfilecon;
lgetfilecon = __lgetfilecon;
fgetfilecon = __fgetfilecon;
setfilecon = __setfilecon;
lsetfilecon = __lsetfilecon;
fsetfilecon = __fsetfilecon;
}
void dload_selinux() {
@ -95,33 +155,32 @@ void dload_selinux() {
static void restore_syscon(int dirfd) {
struct dirent *entry;
DIR *dir;
char *con;
char path[PATH_MAX], *con;
fd_getpath(dirfd, path, sizeof(path));
size_t len = strlen(path);
getfilecon(path, &con);
fgetfilecon(dirfd, &con);
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
lsetfilecon(path, SYSTEM_CON);
fsetfilecon(dirfd, SYSTEM_CON);
freecon(con);
dir = xfdopendir(dirfd);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
if (entry->d_type == DT_DIR) {
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
restore_syscon(fd);
close(fd);
} else {
path[len] = '/';
strcpy(path + len + 1, entry->d_name);
lgetfilecon(path, &con);
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
lsetfilecon(path, SYSTEM_CON);
} else if (entry->d_type == DT_REG) {
fgetfilecon(fd, &con);
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0)
fsetfilecon(fd, SYSTEM_CON);
freecon(con);
} else if (entry->d_type == DT_LNK) {
getfilecon_at(dirfd, entry->d_name, &con);
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0)
setfilecon_at(dirfd, entry->d_name, con);
freecon(con);
path[len] = '\0';
}
close(fd);
}
}
@ -129,28 +188,21 @@ static void restore_magiskcon(int dirfd) {
struct dirent *entry;
DIR *dir;
char path[PATH_MAX];
fd_getpath(dirfd, path, sizeof(path));
size_t len = strlen(path);
lsetfilecon(path, MAGISK_CON);
lchown(path, 0, 0);
fsetfilecon(dirfd, MAGISK_CON);
fchown(dirfd, 0, 0);
dir = xfdopendir(dirfd);
while ((entry = xreaddir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (entry->d_type == DT_DIR) {
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
if (entry->d_type == DT_DIR) {
restore_magiskcon(fd);
close(fd);
} else {
path[len] = '/';
strcpy(path + len + 1, entry->d_name);
lsetfilecon(path, MAGISK_CON);
lchown(path, 0, 0);
path[len] = '\0';
} else if (entry->d_type) {
fsetfilecon(fd, MAGISK_CON);
fchown(fd, 0, 0);
}
close(fd);
}
}

View File

@ -262,6 +262,14 @@ int xlstat(const char *pathname, struct stat *buf) {
return ret;
}
int xfstat(int fd, struct stat *buf) {
int ret = fstat(fd, buf);
if (ret == -1) {
PLOGE("fstat %d", fd);
}
return ret;
}
int xdup2(int oldfd, int newfd) {
int ret = dup2(oldfd, newfd);
if (ret == -1) {