diff --git a/src/container.c b/src/container.c index 14ba24d..a56f969 100644 --- a/src/container.c +++ b/src/container.c @@ -610,6 +610,11 @@ static int hyper_container_init(void *data) goto fail; } + if (hyper_setup_exec_user(&container->exec) < 0) { + fprintf(stderr, "setup exec user failed\n"); + goto fail; + } + fflush(stdout); if (container_setup_tty(arg->pipe[1], container) < 0) { diff --git a/src/exec.c b/src/exec.c index 3820a7f..cf222eb 100644 --- a/src/exec.c +++ b/src/exec.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "hyper.h" #include "util.h" @@ -185,6 +187,96 @@ struct hyper_event_ops err_ops = { /* don't need write buff, the stderr data is one way */ }; +int hyper_setup_exec_user(struct hyper_exec *exec) +{ + char *user = exec->user == NULL || strlen(exec->user) == 0 ? NULL : exec->user; + char *group = exec->group == NULL || strlen(exec->group) == 0 ? NULL : exec->group; + + // check the config + if (!user) { + if (group || exec->nr_additional_groups > 0) { + fprintf(stderr, "group or additional groups can only be set when user is set\n"); + return -1; + } + return 0; + } + + // get uid + fprintf(stdout, "try to find the user: %s\n", user); + struct passwd *pwd = hyper_getpwnam(user); + if (pwd == NULL) { + perror("can't find the user"); + return -1; + } + uid_t uid = pwd->pw_uid; + + // get gid + gid_t gid = pwd->pw_gid; + if (group) { + fprintf(stdout, "try to find the group: %s\n", group); + struct group *gr = hyper_getgrnam(group); + if (gr == NULL) { + perror("can't find the group"); + return -1; + } + gid = gr->gr_gid; + } + + // get all gids + int i, ngroups = 10; + gid_t *reallocgroups, *groups = malloc(sizeof(gid_t) * ngroups); + if (groups == NULL) + goto fail; + if (hyper_getgrouplist(pwd->pw_name, gid, groups, &ngroups) < 0) { + reallocgroups = realloc(groups, sizeof(gid_t) * ngroups); + if (reallocgroups == NULL) + goto fail; + groups = reallocgroups; + if (hyper_getgrouplist(pwd->pw_name, gid, groups, &ngroups) < 0) + goto fail; + } + reallocgroups = realloc(groups, sizeof(gid_t) * (ngroups + exec->nr_additional_groups)); + if (reallocgroups == NULL) + goto fail; + groups = reallocgroups; + for (i = 0; i < exec->nr_additional_groups; i++) { + fprintf(stdout, "try to find the group: %s\n", exec->additional_groups[i]); + struct group *gr = hyper_getgrnam(exec->additional_groups[i]); + if (gr == NULL) { + perror("can't find the group"); + goto fail; + } + groups[ngroups] = gr->gr_gid; + ngroups++; + } + + // setup the owner of tty + if (exec->tty) { + char ptmx[512]; + sprintf(ptmx, "/dev/pts/%d", exec->ptyno); + chown(ptmx, uid, gid); + } + + // apply + if (setgroups(ngroups, groups) < 0) { + perror("setgroups() fails"); + goto fail; + } + if (setgid(gid) < 0) { + perror("setgid() fails"); + goto fail; + } + if (setuid(uid) < 0) { + perror("setuid() fails"); + goto fail; + } + return 0; + +fail: + free(groups); + return -1; +} + static int hyper_setup_exec_notty(struct hyper_exec *e) { if (e->errseq == 0) @@ -502,6 +594,11 @@ static int hyper_do_exec_cmd(void *data) goto exit; } + if (hyper_setup_exec_user(exec) < 0) { + fprintf(stderr, "setup exec user failed\n"); + goto exit; + } + if (hyper_dup_exec_tty(pipe[1], exec) < 0) { fprintf(stderr, "dup pts to exec stdio failed\n"); goto exit; diff --git a/src/exec.h b/src/exec.h index c84df97..3bb7b08 100644 --- a/src/exec.h +++ b/src/exec.h @@ -28,6 +28,10 @@ struct hyper_exec { // configs char *id; + char *user; + char *group; + char **additional_groups; + int nr_additional_groups; struct env *envs; int envs_num; char **argv; @@ -47,6 +51,7 @@ int hyper_setup_exec_tty(struct hyper_exec *e); int hyper_dup_exec_tty(int fd, struct hyper_exec *e); struct hyper_exec *hyper_find_exec_by_pid(struct list_head *head, int pid); struct hyper_exec *hyper_find_exec_by_seq(struct hyper_pod *pod, uint64_t seq); +int hyper_setup_exec_user(struct hyper_exec *e); int hyper_handle_exec_exit(struct hyper_pod *pod, int pid, uint8_t code); int hyper_watch_exec_pty(struct hyper_exec *exec, struct hyper_pod *pod); void hyper_cleanup_exec(struct hyper_pod *pod); diff --git a/src/parse.c b/src/parse.c index 02a0a57..96d51b2 100644 --- a/src/parse.c +++ b/src/parse.c @@ -130,6 +130,31 @@ int json_token_streq(char *js, jsmntok_t *t, char *s) strlen(s) == (size_t)(t->end - t->start)); } +static int container_parse_additional_groups(struct hyper_exec *exec, char *json, jsmntok_t *toks) +{ + int i = 0, j; + + if (toks[i].type != JSMN_ARRAY) { + fprintf(stdout, "additional groups need array"); + return -1; + } + + exec->nr_additional_groups = toks[i].size; + exec->additional_groups = calloc(exec->nr_additional_groups, sizeof(*exec->additional_groups)); + if (exec->additional_groups == NULL) { + fprintf(stderr, "allocate memory for additional groups failed\n"); + return -1; + } + + i++; + for (j = 0; j < exec->nr_additional_groups; j++, i++) { + exec->additional_groups[j] = (json_token_str(json, &toks[i])); + fprintf(stdout, "container process additional group %d %s\n", j, exec->additional_groups[j]); + } + + return i; +} + static int container_parse_argv(struct hyper_exec *exec, char *json, jsmntok_t *toks) { int i = 0, j; @@ -164,6 +189,16 @@ static void container_cleanup_exec(struct hyper_exec *exec) free(exec->id); exec->id = NULL; + free(exec->user); + exec->user = NULL; + free(exec->group); + exec->group = NULL; + for (i = 0; i < exec->nr_additional_groups; i++) { + free(exec->additional_groups[i]); + } + free(exec->additional_groups); + exec->additional_groups = NULL; + free(exec->workdir); exec->workdir = NULL; @@ -440,7 +475,20 @@ static int hyper_parse_process(struct hyper_exec *exec, char *json, jsmntok_t *t for (j = 0; j < toks_size; j++) { t = &toks[i]; fprintf(stdout, "%d name %s\n", i, json_token_str(json, t)); - if (json_token_streq(json, t, "terminal") && t->size == 1) { + if (json_token_streq(json, t, "user") && t->size == 1) { + exec->user = (json_token_str(json, &toks[++i])); + fprintf(stdout, "container process user %s\n", exec->user); + i++; + } else if (json_token_streq(json, t, "group") && t->size == 1) { + exec->group = (json_token_str(json, &toks[++i])); + fprintf(stdout, "container process group %s\n", exec->group); + i++; + } else if (json_token_streq(json, t, "additionalGroups") && t->size == 1) { + next = container_parse_additional_groups(exec, json, &toks[++i]); + if (next < 0) + return -1; + i += next; + } else if (json_token_streq(json, t, "terminal") && t->size == 1) { if (!json_token_streq(json, &toks[++i], "false")) { exec->tty = 1; fprintf(stdout, "container uses terminal\n"); diff --git a/src/util.c b/src/util.c index 5a8bea4..6d1a278 100644 --- a/src/util.c +++ b/src/util.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "util.h" #include "hyper.h" @@ -145,6 +147,92 @@ int hyper_find_sd(char *addr, char **dev) return 0; } +static unsigned long id_or_max(const char *name) +{ + char *ptr; + long id = strtol(name, &ptr, 10); + if (name == ptr || id < 0 || (errno != 0 && id == 0) || *ptr != '\0') + return ~0UL; + return id; +} + +// the same as getpwnam(), but it only parses /etc/passwd and allows name to be id string +struct passwd *hyper_getpwnam(const char *name) +{ + uid_t uid = (uid_t)id_or_max(name); + FILE *file = fopen("/etc/passwd", "r"); + if (!file) { + perror("faile to open /etc/passwd"); + return NULL; + } + for (;;) { + struct passwd *pwd = fgetpwent(file); + if (!pwd) + break; + if (!strcmp(pwd->pw_name, name) || pwd->pw_uid == uid) { + fclose(file); + return pwd; + } + } + fclose(file); + return NULL; +} + +// the same as getgrnam(), but it only parses /etc/group and allows the name to be id string +struct group *hyper_getgrnam(const char *name) +{ + gid_t gid = (gid_t)id_or_max(name); + FILE *file = fopen("/etc/group", "r"); + if (!file) { + perror("faile to open /etc/group"); + return NULL; + } + for (;;) { + struct group *gr = fgetgrent(file); + if (!gr) + break; + if (!strcmp(gr->gr_name, name) || gr->gr_gid == gid) { + fclose(file); + return gr; + } + } + fclose(file); + return NULL; +} + +// the same as getgrouplist(), but it only parses /etc/group +int hyper_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + int nr = 0, ret; + FILE *file = fopen("/etc/group", "r"); + if (!file) { + perror("faile to open /etc/group"); + return -1; + } + for (;;) { + struct group *gr = fgetgrent(file); + if (!gr) + break; + int j; + for (j = 0; gr->gr_mem && gr->gr_mem[j]; j++) { + if (!strcmp(gr->gr_mem[j], user)) { + if (nr + 1 < *ngroups) + groups[nr] = gr->gr_gid; + nr++; + } + } + } + fclose(file); + if (nr == 0) { + if (nr + 1 < *ngroups) + groups[nr] = group; + nr++; + } + ret = nr <= *ngroups ? nr : -1; + *ngroups = nr; + return ret; +} + int hyper_mkdir(char *hyper_path) { struct stat st; diff --git a/src/util.h b/src/util.h index 5be3327..c81ff60 100644 --- a/src/util.h +++ b/src/util.h @@ -2,6 +2,8 @@ #define _UTIL_H_ #include +#include +#include #include "../config.h" struct hyper_pod; @@ -31,4 +33,7 @@ int hyper_setfd_nonblock(int fd); int hyper_socketpair(int domain, int type, int protocol, int sv[2]); void hyper_shutdown(void); int hyper_insmod(char *module); +struct passwd *hyper_getpwnam(const char *name); +struct group *hyper_getgrnam(const char *name); +int hyper_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups); #endif