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 */

View File

@ -47,7 +47,6 @@
#include <cutils/multiuser.h> #include <cutils/multiuser.h>
#endif #endif
#include "binds.h"
#include "su.h" #include "su.h"
#include "utils.h" #include "utils.h"
#include "pts.h" #include "pts.h"
@ -499,12 +498,8 @@ static void prepare_su_bind() {
} }
} }
static void prepare_binds() { static void bind_cb_func(void *arg, int uid, const char *src, const char *dst) {
mkdir("/data/su", 0700); int ret = 0, i = 0;
static int i = 0;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
int ret = 0;
char *tmpfile = NULL; char *tmpfile = NULL;
asprintf(&tmpfile, "/dev/su/bind%d", i++); asprintf(&tmpfile, "/dev/su/bind%d", i++);
@ -541,12 +536,9 @@ static void prepare_binds() {
LOGE("Failed to mount bind"); LOGE("Failed to mount bind");
return; return;
} }
}
bind_foreach(cb, NULL);
} }
static void do_init() { static void init_cb_func(void *arg, int uid, const char *path) {
auto void cb(void *arg, int uid, const char *path) {
int ret = 0; int ret = 0;
int p = fork(); int p = fork();
@ -562,16 +554,12 @@ static void do_init() {
execl("/system/bin/sh", "/system/bin/sh", path, NULL); execl("/system/bin/sh", "/system/bin/sh", path, NULL);
LOGE("Failed to execute %s as shell script", path); LOGE("Failed to execute %s as shell script", path);
_exit(1); _exit(1);
}
init_foreach(cb, NULL);
} }
static void prepare() { static void prepare() {
setfscreatecon("u:object_r:su_daemon:s0"); setfscreatecon("u:object_r:su_daemon:s0");
mkdir("/dev/su", 0700); mkdir("/dev/su", 0700);
prepare_su_bind(); prepare_su_bind();
prepare_binds();
do_init();
setfscreatecon(NULL); setfscreatecon(NULL);
} }

101
su.c
View File

@ -42,7 +42,6 @@
#include "su.h" #include "su.h"
#include "utils.h" #include "utils.h"
#include "binds.h"
extern int is_daemon; extern int is_daemon;
extern int daemon_from_uid; extern int daemon_from_uid;
@ -470,70 +469,6 @@ static void usage(int status) {
exit(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) { static __attribute__ ((noreturn)) void deny(struct su_context *ctx) {
char *cmd = get_command(&ctx->to); char *cmd = get_command(&ctx->to);
@ -575,11 +510,11 @@ static __attribute__ ((noreturn)) void allow(struct su_context *ctx) {
if (send_to_app) if (send_to_app)
send_result(ctx, ALLOW); send_result(ctx, ALLOW);
if(ctx->bind.from[0] && ctx->bind.to[0]) // if(ctx->bind.from[0] && ctx->bind.to[0])
allow_bind(ctx); // allow_bind(ctx);
if(ctx->init[0]) // if(ctx->init[0])
allow_init(ctx); // allow_init(ctx);
char *binary; char *binary;
argc = ctx->to.optind; argc = ctx->to.optind;
@ -777,21 +712,14 @@ int su_main_nodaemon(int argc, char **argv) {
.database_path = REQUESTOR_DATA_PATH REQUESTOR_DATABASE_PATH, .database_path = REQUESTOR_DATA_PATH REQUESTOR_DATABASE_PATH,
.base_path = REQUESTOR_DATA_PATH REQUESTOR .base_path = REQUESTOR_DATA_PATH REQUESTOR
}, },
.bind = {
.from = "",
.to = "",
},
.init = "",
}; };
struct stat st; struct stat st;
int c, socket_serv_fd, fd; int c, socket_serv_fd, fd;
char buf[64], *result; char buf[64], *result;
policy_t dballow; policy_t dballow;
struct option long_opts[] = { struct option long_opts[] = {
{ "bind", required_argument, NULL, 'b' },
{ "command", required_argument, NULL, 'c' }, { "command", required_argument, NULL, 'c' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "init", required_argument, NULL, 'i' },
{ "login", no_argument, NULL, 'l' }, { "login", no_argument, NULL, 'l' },
{ "preserve-environment", no_argument, NULL, 'p' }, { "preserve-environment", no_argument, NULL, 'p' },
{ "shell", required_argument, NULL, 's' }, { "shell", required_argument, NULL, 's' },
@ -800,22 +728,8 @@ int su_main_nodaemon(int argc, char **argv) {
{ NULL, 0, NULL, 0 }, { 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) { 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";
}
}
break;
case 'c': case 'c':
ctx.to.shell = DEFAULT_SHELL; ctx.to.shell = DEFAULT_SHELL;
ctx.to.command = optarg; ctx.to.command = optarg;
@ -823,9 +737,6 @@ int su_main_nodaemon(int argc, char **argv) {
case 'h': case 'h':
usage(EXIT_SUCCESS); usage(EXIT_SUCCESS);
break; break;
case 'i':
ctx.init = optarg;
break;
case 'l': case 'l':
ctx.to.login = 1; ctx.to.login = 1;
break; break;
@ -840,7 +751,7 @@ int su_main_nodaemon(int argc, char **argv) {
printf("%d\n", VERSION_CODE); printf("%d\n", VERSION_CODE);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
case 'v': case 'v':
printf("%s cm-su subind suinit\n", VERSION); printf("%s (topjohnwu v1)\n", VERSION);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
case 'u': case 'u':
switch (get_multiuser_mode()) { switch (get_multiuser_mode()) {

2
su.h
View File

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