Rearrange su daemon routine
This commit is contained in:
parent
ab90901793
commit
5fcd629f16
@ -94,15 +94,6 @@ static void setup_sighandlers(void (*handler)(int)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default values
|
|
||||||
su_req_base::su_req_base()
|
|
||||||
: uid(UID_ROOT), login(false), keepenv(false), mount_master(false) {}
|
|
||||||
su_request::su_request()
|
|
||||||
: shell(DEFAULT_SHELL), command("") {}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Connect daemon, send argc, argv, cwd, pts slave
|
|
||||||
*/
|
|
||||||
int su_client_main(int argc, char *argv[]) {
|
int su_client_main(int argc, char *argv[]) {
|
||||||
int c;
|
int c;
|
||||||
struct option long_opts[] = {
|
struct option long_opts[] = {
|
||||||
@ -189,18 +180,18 @@ int su_client_main(int argc, char *argv[]) {
|
|||||||
// Tell the daemon we are su
|
// Tell the daemon we are su
|
||||||
write_int(fd, SUPERUSER);
|
write_int(fd, SUPERUSER);
|
||||||
|
|
||||||
// Wait for ack from daemon
|
|
||||||
if (read_int(fd)) {
|
|
||||||
// Fast fail
|
|
||||||
fprintf(stderr, "%s\n", strerror(EACCES));
|
|
||||||
return DENY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send su_request
|
// Send su_request
|
||||||
xwrite(fd, &su_req, sizeof(su_req_base));
|
xwrite(fd, &su_req, sizeof(su_req_base));
|
||||||
write_string(fd, su_req.shell);
|
write_string(fd, su_req.shell);
|
||||||
write_string(fd, su_req.command);
|
write_string(fd, su_req.command);
|
||||||
|
|
||||||
|
// Wait for ack from daemon
|
||||||
|
if (read_int(fd)) {
|
||||||
|
// Fast fail
|
||||||
|
fprintf(stderr, "%s\n", strerror(EACCES));
|
||||||
|
return EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine which one of our streams are attached to a TTY
|
// Determine which one of our streams are attached to a TTY
|
||||||
int atty = 0;
|
int atty = 0;
|
||||||
if (isatty(STDIN_FILENO)) atty |= ATTY_IN;
|
if (isatty(STDIN_FILENO)) atty |= ATTY_IN;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <db.h>
|
#include <db.h>
|
||||||
@ -40,18 +39,25 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct su_req_base {
|
struct su_req_base {
|
||||||
unsigned uid;
|
unsigned uid = UID_ROOT;
|
||||||
bool login;
|
bool login = false;
|
||||||
bool keepenv;
|
bool keepenv = false;
|
||||||
bool mount_master;
|
bool mount_master = false;
|
||||||
protected:
|
|
||||||
su_req_base();
|
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct su_request : public su_req_base {
|
struct su_request : public su_req_base {
|
||||||
const char *shell;
|
const char *shell = DEFAULT_SHELL;
|
||||||
const char *command;
|
const char *command = "";
|
||||||
su_request();
|
su_request(bool dyn = false) : dyn(dyn) {}
|
||||||
|
~su_request() {
|
||||||
|
if (dyn) {
|
||||||
|
free(const_cast<char*>(shell));
|
||||||
|
free(const_cast<char*>(command));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool dyn;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct su_context {
|
struct su_context {
|
||||||
@ -60,8 +66,6 @@ struct su_context {
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
};
|
};
|
||||||
|
|
||||||
// connect.c
|
|
||||||
|
|
||||||
void app_log(const su_context &ctx);
|
void app_log(const su_context &ctx);
|
||||||
void app_notify(const su_context &ctx);
|
void app_notify(const su_context &ctx);
|
||||||
void app_connect(const char *socket, const std::shared_ptr<su_info> &info);
|
void app_connect(const char *socket, const std::shared_ptr<su_info> &info);
|
||||||
|
@ -101,27 +101,38 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
|
|||||||
|
|
||||||
// Lock before the policy is determined
|
// Lock before the policy is determined
|
||||||
info->lock();
|
info->lock();
|
||||||
|
RunFinally unlock([&] {
|
||||||
|
info->unlock();
|
||||||
|
});
|
||||||
|
|
||||||
if (info->access.policy == QUERY) {
|
if (info->access.policy == QUERY) {
|
||||||
// Not cached, get data from database
|
// Not cached, get data from database
|
||||||
database_check(info);
|
database_check(info);
|
||||||
|
|
||||||
|
// If it's root or the manager, allow it silently
|
||||||
|
if (info->uid == UID_ROOT || (info->uid % 100000) == (info->mgr_st.st_uid % 100000)) {
|
||||||
|
info->access = SILENT_SU_ACCESS;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
// Check su access settings
|
// Check su access settings
|
||||||
switch (info->cfg[ROOT_ACCESS]) {
|
switch (info->cfg[ROOT_ACCESS]) {
|
||||||
case ROOT_ACCESS_DISABLED:
|
case ROOT_ACCESS_DISABLED:
|
||||||
LOGW("Root access is disabled!\n");
|
LOGW("Root access is disabled!\n");
|
||||||
info->access = NO_SU_ACCESS;
|
info->access = NO_SU_ACCESS;
|
||||||
break;
|
return info;
|
||||||
case ROOT_ACCESS_ADB_ONLY:
|
case ROOT_ACCESS_ADB_ONLY:
|
||||||
if (info->uid != UID_SHELL) {
|
if (info->uid != UID_SHELL) {
|
||||||
LOGW("Root access limited to ADB only!\n");
|
LOGW("Root access limited to ADB only!\n");
|
||||||
info->access = NO_SU_ACCESS;
|
info->access = NO_SU_ACCESS;
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ROOT_ACCESS_APPS_ONLY:
|
case ROOT_ACCESS_APPS_ONLY:
|
||||||
if (info->uid == UID_SHELL) {
|
if (info->uid == UID_SHELL) {
|
||||||
LOGW("Root access is disabled for ADB!\n");
|
LOGW("Root access is disabled for ADB!\n");
|
||||||
info->access = NO_SU_ACCESS;
|
info->access = NO_SU_ACCESS;
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ROOT_ACCESS_APPS_AND_ADB:
|
case ROOT_ACCESS_APPS_AND_ADB:
|
||||||
@ -129,22 +140,17 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's the manager, allow it silently
|
if (info->access.policy != QUERY)
|
||||||
if ((info->uid % 100000) == (info->mgr_st.st_uid % 100000))
|
return info;
|
||||||
info->access = SILENT_SU_ACCESS;
|
|
||||||
|
|
||||||
// Allow if it's root
|
|
||||||
if (info->uid == UID_ROOT)
|
|
||||||
info->access = SILENT_SU_ACCESS;
|
|
||||||
|
|
||||||
// If still not determined, check if manager exists
|
// If still not determined, check if manager exists
|
||||||
if (info->access.policy == QUERY && info->str[SU_MANAGER][0] == '\0')
|
if (info->str[SU_MANAGER][0] == '\0') {
|
||||||
info->access = NO_SU_ACCESS;
|
info->access = NO_SU_ACCESS;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If still not determined, ask manager
|
// If still not determined, ask manager
|
||||||
if (info->access.policy == QUERY) {
|
|
||||||
// Create random socket
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
int sockfd = create_rand_socket(&addr);
|
int sockfd = create_rand_socket(&addr);
|
||||||
|
|
||||||
@ -160,10 +166,6 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock
|
|
||||||
info->unlock();
|
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@ -187,24 +189,31 @@ static void set_identity(unsigned uid) {
|
|||||||
void su_daemon_handler(int client, struct ucred *credential) {
|
void su_daemon_handler(int client, struct ucred *credential) {
|
||||||
LOGD("su: request from pid=[%d], client=[%d]\n", credential->pid, client);
|
LOGD("su: request from pid=[%d], client=[%d]\n", credential->pid, client);
|
||||||
|
|
||||||
auto info = get_su_info(credential->uid);
|
su_context ctx = {
|
||||||
|
.info = get_su_info(credential->uid),
|
||||||
|
.req = su_request(true),
|
||||||
|
.pid = credential->pid
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read su_request
|
||||||
|
xxread(client, &ctx.req, sizeof(su_req_base));
|
||||||
|
ctx.req.shell = read_string(client);
|
||||||
|
ctx.req.command = read_string(client);
|
||||||
|
|
||||||
|
if (ctx.info->access.log)
|
||||||
|
app_log(ctx);
|
||||||
|
else if (ctx.info->access.notify)
|
||||||
|
app_notify(ctx);
|
||||||
|
|
||||||
// Fail fast
|
// Fail fast
|
||||||
if (info->access.policy == DENY && info->str[SU_MANAGER][0] == '\0') {
|
if (ctx.info->access.policy == DENY) {
|
||||||
LOGD("su: fast deny\n");
|
LOGW("su: request rejected (%u)", ctx.info->uid);
|
||||||
|
ctx.info.reset();
|
||||||
write_int(client, DENY);
|
write_int(client, DENY);
|
||||||
close(client);
|
close(client);
|
||||||
return;
|
return;
|
||||||
}
|
} else if (int child = xfork(); child) {
|
||||||
|
ctx.info.reset();
|
||||||
/* Fork a new process, the child process will need to setsid,
|
|
||||||
* open a pseudo-terminal if needed, and will eventually run exec
|
|
||||||
* The parent process will wait for the result and
|
|
||||||
* send the return code back to our client
|
|
||||||
*/
|
|
||||||
int child = xfork();
|
|
||||||
if (child) {
|
|
||||||
info.reset();
|
|
||||||
|
|
||||||
// Wait result
|
// Wait result
|
||||||
LOGD("su: waiting child pid=[%d]\n", child);
|
LOGD("su: waiting child pid=[%d]\n", child);
|
||||||
@ -221,27 +230,23 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The child process will need to setsid, open a pseudo-terminal
|
||||||
|
* if needed, and will eventually run exec.
|
||||||
|
* The parent process will wait for the result and
|
||||||
|
* send the return code back to our client
|
||||||
|
*/
|
||||||
|
|
||||||
LOGD("su: fork handler\n");
|
LOGD("su: fork handler\n");
|
||||||
|
|
||||||
// Abort upon any error occurred
|
// Abort upon any error occurred
|
||||||
log_cb.ex = exit;
|
log_cb.ex = exit;
|
||||||
|
|
||||||
su_context ctx = {
|
|
||||||
.info = info,
|
|
||||||
.pid = credential->pid
|
|
||||||
};
|
|
||||||
|
|
||||||
// ack
|
// ack
|
||||||
write_int(client, 0);
|
write_int(client, 0);
|
||||||
|
|
||||||
// Become session leader
|
// Become session leader
|
||||||
xsetsid();
|
xsetsid();
|
||||||
|
|
||||||
// Read su_request
|
|
||||||
xxread(client, &ctx.req, sizeof(su_req_base));
|
|
||||||
ctx.req.shell = read_string(client);
|
|
||||||
ctx.req.command = read_string(client);
|
|
||||||
|
|
||||||
// Get pts_slave
|
// Get pts_slave
|
||||||
char *pts_slave = read_string(client);
|
char *pts_slave = read_string(client);
|
||||||
|
|
||||||
@ -257,7 +262,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
|||||||
xstat(pts_slave, &st);
|
xstat(pts_slave, &st);
|
||||||
|
|
||||||
// If caller is not root, ensure the owner of pts_slave is the caller
|
// If caller is not root, ensure the owner of pts_slave is the caller
|
||||||
if(st.st_uid != info->uid && info->uid != 0)
|
if(st.st_uid != ctx.info->uid && ctx.info->uid != 0)
|
||||||
LOGE("su: Wrong permission of pts_slave");
|
LOGE("su: Wrong permission of pts_slave");
|
||||||
|
|
||||||
// Opening the TTY has to occur after the
|
// Opening the TTY has to occur after the
|
||||||
@ -292,8 +297,8 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
|||||||
|
|
||||||
// Handle namespaces
|
// Handle namespaces
|
||||||
if (ctx.req.mount_master)
|
if (ctx.req.mount_master)
|
||||||
info->cfg[SU_MNT_NS] = NAMESPACE_MODE_GLOBAL;
|
ctx.info->cfg[SU_MNT_NS] = NAMESPACE_MODE_GLOBAL;
|
||||||
switch (info->cfg[SU_MNT_NS]) {
|
switch (ctx.info->cfg[SU_MNT_NS]) {
|
||||||
case NAMESPACE_MODE_GLOBAL:
|
case NAMESPACE_MODE_GLOBAL:
|
||||||
LOGD("su: use global namespace\n");
|
LOGD("su: use global namespace\n");
|
||||||
break;
|
break;
|
||||||
@ -309,12 +314,6 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->access.log)
|
|
||||||
app_log(ctx);
|
|
||||||
else if (info->access.notify)
|
|
||||||
app_notify(ctx);
|
|
||||||
|
|
||||||
if (info->access.policy == ALLOW) {
|
|
||||||
const char *argv[] = { nullptr, nullptr, nullptr, nullptr };
|
const char *argv[] = { nullptr, nullptr, nullptr, nullptr };
|
||||||
|
|
||||||
argv[0] = ctx.req.login ? "-" : ctx.req.shell;
|
argv[0] = ctx.req.login ? "-" : ctx.req.shell;
|
||||||
@ -358,9 +357,4 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
|||||||
fprintf(stderr, "Cannot execute %s: %s\n", ctx.req.shell, strerror(errno));
|
fprintf(stderr, "Cannot execute %s: %s\n", ctx.req.shell, strerror(errno));
|
||||||
PLOGE("exec");
|
PLOGE("exec");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
} else {
|
|
||||||
LOGW("su: request rejected (%u->%u)", info->uid, ctx.req.uid);
|
|
||||||
fprintf(stderr, "%s\n", strerror(EACCES));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user