Remove bind, init, and GNU compiler dependancy

This commit is contained in:
topjohnwu 2016-12-30 05:50:08 +08:00
parent 42a66ad49e
commit 7955ddceb2
5 changed files with 96 additions and 476 deletions

246
binds.c
View File

@ -1,246 +0,0 @@
/*
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <strings.h>
#include <stdint.h>
#include <pwd.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <sys/types.h>
#include <selinux/selinux.h>
#include <arpa/inet.h>
#include "binds.h"
int bind_foreach(bind_cb cb, void* arg) {
int res = 0;
char *str = NULL;
int fd = open("/data/su/binds", O_RDONLY);
if(fd<0)
return 1;
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
str = malloc(size);
if(read(fd, str, size) != size)
goto error;
char *base = str;
while(base < str+size) {
char *parse_src, *parse_dst;
int uid;
char *ptr = memchr(base, 0, size-(base-str));
if(ptr == NULL)
goto error;
sscanf(base, "%d", &uid);
parse_src = strchr(base, ':');
if(!parse_src)
goto error;
parse_src++;
parse_dst = strchr(parse_src, ':');
if(!parse_dst)
goto error;
*parse_dst = 0; // Split parse_src string
parse_dst++;
cb(arg, uid, parse_src, parse_dst);
base = ptr+1;
}
res = 1;
error:
if(str) free(str);
close(fd);
return res;
}
int bind_uniq_dst(const char *dst) {
static int _res;
static const char *_dst;
_res = 1;
_dst = dst;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
if(strcmp(dst, _dst) == 0)
_res = 0;
}
if(!bind_foreach(cb, NULL))
return 0;
return _res;
}
void bind_ls(int uid) {
static int _uid;
_uid=uid;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
if(_uid == 0 || _uid == 2000 || _uid == uid) {
fprintf(stderr, "%d %s => %s\n", uid, src, dst);
}
}
bind_foreach(cb, NULL);
}
int bind_remove(const char *path, int uid) {
static int _found = 0;
static const char *_path;
static int _fd;
static int _uid;
_path = path;
_found = 0;
_uid = uid;
unlink("/data/su/bind.new");
_fd = open("/data/su/bind.new", O_WRONLY|O_CREAT, 0600);
if(_fd<0)
return 0;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
//The one we want to drop
if(strcmp(dst, _path) == 0 &&
(_uid == 0 || _uid == 2000 || _uid == uid)) {
_found = 1;
return;
}
char *str = NULL;
int len = asprintf(&str, "%d:%s:%s", uid, src, dst);
write(_fd, str, len+1); //len doesn't include final \0 and we want to write it
free(str);
}
bind_foreach(cb, NULL);
close(_fd);
unlink("/data/su/bind");
rename("/data/su/bind.new", "/data/su/bind");
return _found;
}
int init_foreach(init_cb icb, void* arg) {
int res = 0;
char *str = NULL;
int fd = open("/data/su/init", O_RDONLY);
if(fd<0)
return 1;
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
str = malloc(size);
if(read(fd, str, size) != size)
goto error;
char *base = str;
while(base < str+size) {
char *parsed;
int uid;
char *ptr = memchr(base, 0, size-(base-str));
if(ptr == NULL)
goto error;
sscanf(base, "%d", &uid);
parsed = strchr(base, ':');
if(!parsed)
goto error;
parsed++;
icb(arg, uid, parsed);
base = ptr+1;
}
res = 1;
error:
if(str) free(str);
close(fd);
return res;
}
int init_uniq(const char *path) {
static int _res;
static const char *_path;
_res = 1;
_path = path;
auto void cb(void *arg, int uid, const char *path) {
if(strcmp(path, _path) == 0)
_res = 0;
}
if(!init_foreach(cb, NULL))
return 0;
return _res;
}
int init_remove(const char *path, int uid) {
static int _found = 0;
static const char *_path;
static int fd;
static int _uid;
_path = path;
_found = 0;
_uid = uid;
unlink("/data/su/init.new");
fd = open("/data/su/init.new", O_WRONLY|O_CREAT, 0600);
if(fd<0)
return 0;
auto void cb(void *arg, int uid, const char *path) {
//The one we want to drop
if(strcmp(path, _path) == 0 &&
(_uid == 0 || _uid == 2000 || uid == _uid)) {
_found = 1;
return;
}
char *str = NULL;
int len = asprintf(&str, "%d:%s", uid, path);
write(fd, str, len+1); //len doesn't include final \0 and we want to write it
free(str);
}
init_foreach(cb, NULL);
close(fd);
unlink("/data/su/init");
rename("/data/su/init.new", "/data/su/init");
return _found;
}
void init_ls(int uid) {
static int _uid;
_uid = uid;
auto void cb(void *arg, int uid, const char *path) {
if(_uid == 2000 || _uid == 0 || _uid == uid)
fprintf(stderr, "%d %s\n", uid, path);
}
init_foreach(cb, NULL);
}

33
binds.h
View File

@ -1,33 +0,0 @@
/*
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef BINDSH
#define BINDS_H
typedef void (*bind_cb)(void *arg, int uid, const char *src, const char *dst);
extern int bind_foreach(bind_cb cb, void* arg);
extern int bind_uniq_dst(const char *dst);
extern int bind_remove(const char *path, int uid);
extern void bind_ls(int uid);
typedef void (*init_cb)(void *arg, int uid, const char *path);
extern int init_foreach(init_cb cb, void* arg);
extern int init_uniq(const char *dst);
extern int init_remove(const char *path, int uid);
extern void init_ls(int uid);
#endif /* BINDS_H */

104
daemon.c
View File

@ -47,7 +47,6 @@
#include <cutils/multiuser.h>
#endif
#include "binds.h"
#include "su.h"
#include "utils.h"
#include "pts.h"
@ -499,79 +498,68 @@ static void prepare_su_bind() {
}
}
static void prepare_binds() {
mkdir("/data/su", 0700);
static int i = 0;
static void bind_cb_func(void *arg, int uid, const char *src, const char *dst) {
int ret = 0, i = 0;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
int ret = 0;
char *tmpfile = NULL;
asprintf(&tmpfile, "/dev/su/bind%d", i++);
struct stat stbuf;
ret = stat(src, &stbuf);
if(ret) {
free(tmpfile);
LOGE("Failed to stat src %s file", src);
return;
}
char *tmpfile = NULL;
asprintf(&tmpfile, "/dev/su/bind%d", i++);
struct stat stbuf;
ret = stat(src, &stbuf);
if(ret) {
free(tmpfile);
LOGE("Failed to stat src %s file", src);
return;
}
//Only shell uid is allowed to bind files not his own
if(uid != 2000 && uid != stbuf.st_uid) {
LOGE("File %s has wrong owner: %d vs %d", src, uid, stbuf.st_uid);
return;
}
//Only shell uid is allowed to bind files not his own
if(uid != 2000 && uid != stbuf.st_uid) {
LOGE("File %s has wrong owner: %d vs %d", src, uid, stbuf.st_uid);
return;
}
ret = copy_file(src, tmpfile, 0);
if(ret) {
free(tmpfile);
PLOGE("Failed to copy su");
return;
}
chmod(tmpfile, stbuf.st_mode);
ret = copy_file(src, tmpfile, 0);
if(ret) {
free(tmpfile);
PLOGE("Failed to copy su");
return;
}
chmod(tmpfile, stbuf.st_mode);
ret = setfilecon(tmpfile, "u:object_r:system_file:s0");
if(ret) {
LOGE("Failed to set file context");
return;
}
ret = setfilecon(tmpfile, "u:object_r:system_file:s0");
if(ret) {
LOGE("Failed to set file context");
return;
}
ret = mount(tmpfile, dst, "", MS_BIND, NULL);
if(ret) {
LOGE("Failed to mount bind");
return;
}
}
bind_foreach(cb, NULL);
ret = mount(tmpfile, dst, "", MS_BIND, NULL);
if(ret) {
LOGE("Failed to mount bind");
return;
}
}
static void do_init() {
auto void cb(void *arg, int uid, const char *path) {
int ret = 0;
static void init_cb_func(void *arg, int uid, const char *path) {
int ret = 0;
int p = fork();
if(p)
return;
int p = fork();
if(p)
return;
while(access("/system/bin/sh", R_OK)) sleep(1);
ret = setexeccon("u:r:su:s0");
execl(path, path, NULL);
LOGE("Failed to execute %s. Trying as shell script, ret = %d", path, ret);
while(access("/system/bin/sh", R_OK)) sleep(1);
ret = setexeccon("u:r:su:s0");
execl(path, path, NULL);
LOGE("Failed to execute %s. Trying as shell script, ret = %d", path, ret);
ret = setexeccon("u:r:su:s0");
execl("/system/bin/sh", "/system/bin/sh", path, NULL);
LOGE("Failed to execute %s as shell script", path);
_exit(1);
}
init_foreach(cb, NULL);
ret = setexeccon("u:r:su:s0");
execl("/system/bin/sh", "/system/bin/sh", path, NULL);
LOGE("Failed to execute %s as shell script", path);
_exit(1);
}
static void prepare() {
setfscreatecon("u:object_r:su_daemon:s0");
mkdir("/dev/su", 0700);
prepare_su_bind();
prepare_binds();
do_init();
setfscreatecon(NULL);
}

187
su.c
View File

@ -42,7 +42,6 @@
#include "su.h"
#include "utils.h"
#include "binds.h"
extern int is_daemon;
extern int daemon_from_uid;
@ -470,70 +469,6 @@ static void usage(int status) {
exit(status);
}
static __attribute__ ((noreturn)) void allow_bind(struct su_context *ctx) {
if(ctx->from.uid == 0)
exit(1);
if(ctx->bind.from[0] == '!') {
int ret = bind_remove(ctx->bind.to, ctx->from.uid);
if(!ret) {
fprintf(stderr, "The mentioned bind destination path didn't exist\n");
exit(1);
}
exit(0);
}
if(strcmp("--ls", ctx->bind.from)==0) {
bind_ls(ctx->from.uid);
exit(0);
}
if(!bind_uniq_dst(ctx->bind.to)) {
fprintf(stderr, "BIND: Distant file NOT unique. I refuse.\n");
exit(1);
}
int fd = open("/data/su/binds", O_WRONLY|O_APPEND|O_CREAT, 0600);
if(fd<0) {
fprintf(stderr, "Failed to open binds file\n");
exit(1);
}
char *str = NULL;
int len = asprintf(&str, "%d:%s:%s", ctx->from.uid, ctx->bind.from, ctx->bind.to);
write(fd, str, len+1); //len doesn't include final \0 and we want to write it
free(str);
close(fd);
exit(0);
}
static __attribute__ ((noreturn)) void allow_init(struct su_context *ctx) {
if(ctx->init[0]=='!') {
int ret = init_remove(ctx->init+1, ctx->from.uid);
if(!ret) {
fprintf(stderr, "The mentioned init path didn't exist\n");
exit(1);
}
exit(0);
}
if(strcmp("--ls", ctx->init) == 0) {
init_ls(ctx->from.uid);
exit(0);
}
if(!init_uniq(ctx->init))
//This script is already in init list
exit(1);
int fd = open("/data/su/init", O_WRONLY|O_APPEND|O_CREAT, 0600);
if(fd<0) {
fprintf(stderr, "Failed to open init file\n");
exit(1);
}
char *str = NULL;
int len = asprintf(&str, "%d:%s", ctx->from.uid, ctx->init);
write(fd, str, len+1); //len doesn't include final \0 and we want to write it
free(str);
close(fd);
exit(0);
}
static __attribute__ ((noreturn)) void deny(struct su_context *ctx) {
char *cmd = get_command(&ctx->to);
@ -575,11 +510,11 @@ static __attribute__ ((noreturn)) void allow(struct su_context *ctx) {
if (send_to_app)
send_result(ctx, ALLOW);
if(ctx->bind.from[0] && ctx->bind.to[0])
allow_bind(ctx);
// if(ctx->bind.from[0] && ctx->bind.to[0])
// allow_bind(ctx);
if(ctx->init[0])
allow_init(ctx);
// if(ctx->init[0])
// allow_init(ctx);
char *binary;
argc = ctx->to.optind;
@ -777,21 +712,14 @@ int su_main_nodaemon(int argc, char **argv) {
.database_path = REQUESTOR_DATA_PATH REQUESTOR_DATABASE_PATH,
.base_path = REQUESTOR_DATA_PATH REQUESTOR
},
.bind = {
.from = "",
.to = "",
},
.init = "",
};
struct stat st;
int c, socket_serv_fd, fd;
char buf[64], *result;
policy_t dballow;
struct option long_opts[] = {
{ "bind", required_argument, NULL, 'b' },
{ "command", required_argument, NULL, 'c' },
{ "help", no_argument, NULL, 'h' },
{ "init", required_argument, NULL, 'i' },
{ "login", no_argument, NULL, 'l' },
{ "preserve-environment", no_argument, NULL, 'p' },
{ "shell", required_argument, NULL, 's' },
@ -800,71 +728,54 @@ int su_main_nodaemon(int argc, char **argv) {
{ NULL, 0, NULL, 0 },
};
while ((c = getopt_long(argc, argv, "+b:c:hlmps:Vvuz:", long_opts, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "c:hlmps:Vvuz:", long_opts, NULL)) != -1) {
switch(c) {
case 'b': {
char *s = strdup(optarg);
char *pos = strchr(s, ':');
if(pos) {
pos[0] = 0;
ctx.bind.to = pos + 1;
ctx.bind.from = s;
} else {
ctx.bind.from = "--ls";
ctx.bind.to = "--ls";
}
case 'c':
ctx.to.shell = DEFAULT_SHELL;
ctx.to.command = optarg;
break;
case 'h':
usage(EXIT_SUCCESS);
break;
case 'l':
ctx.to.login = 1;
break;
case 'm':
case 'p':
ctx.to.keepenv = 1;
break;
case 's':
ctx.to.shell = optarg;
break;
case 'V':
printf("%d\n", VERSION_CODE);
exit(EXIT_SUCCESS);
case 'v':
printf("%s (topjohnwu v1)\n", VERSION);
exit(EXIT_SUCCESS);
case 'u':
switch (get_multiuser_mode()) {
case MULTIUSER_MODE_USER:
printf("%s\n", MULTIUSER_VALUE_USER);
break;
case MULTIUSER_MODE_OWNER_MANAGED:
printf("%s\n", MULTIUSER_VALUE_OWNER_MANAGED);
break;
case MULTIUSER_MODE_OWNER_ONLY:
printf("%s\n", MULTIUSER_VALUE_OWNER_ONLY);
break;
case MULTIUSER_MODE_NONE:
printf("%s\n", MULTIUSER_VALUE_NONE);
break;
}
exit(EXIT_SUCCESS);
case 'z':
ctx.to.context = optarg;
break;
case 'c':
ctx.to.shell = DEFAULT_SHELL;
ctx.to.command = optarg;
break;
case 'h':
usage(EXIT_SUCCESS);
break;
case 'i':
ctx.init = optarg;
break;
case 'l':
ctx.to.login = 1;
break;
case 'm':
case 'p':
ctx.to.keepenv = 1;
break;
case 's':
ctx.to.shell = optarg;
break;
case 'V':
printf("%d\n", VERSION_CODE);
exit(EXIT_SUCCESS);
case 'v':
printf("%s cm-su subind suinit\n", VERSION);
exit(EXIT_SUCCESS);
case 'u':
switch (get_multiuser_mode()) {
case MULTIUSER_MODE_USER:
printf("%s\n", MULTIUSER_VALUE_USER);
break;
case MULTIUSER_MODE_OWNER_MANAGED:
printf("%s\n", MULTIUSER_VALUE_OWNER_MANAGED);
break;
case MULTIUSER_MODE_OWNER_ONLY:
printf("%s\n", MULTIUSER_VALUE_OWNER_ONLY);
break;
case MULTIUSER_MODE_NONE:
printf("%s\n", MULTIUSER_VALUE_NONE);
break;
}
exit(EXIT_SUCCESS);
case 'z':
ctx.to.context = optarg;
break;
default:
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf(stderr, "\n");
usage(2);
default:
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf(stderr, "\n");
usage(2);
}
}
hacks_init();

2
su.h
View File

@ -77,7 +77,7 @@
#define str(a) #a
#ifndef VERSION_CODE
#define VERSION_CODE 17
#define VERSION_CODE 1
#endif
#define VERSION xstr(VERSION_CODE) " " REQUESTOR