X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fexecute.c;h=f07d018a37a29468707f0956fc2e9d3cdea7fc3b;hp=ee05e994450de54375aec6229097790fdcc7d840;hb=4c12626c8e3491570b395d68380543e10c98ad33;hpb=8c7be95e5a80c4bd82d86e9640a071fd98618172 diff --git a/src/execute.c b/src/execute.c index ee05e9944..f07d018a3 100644 --- a/src/execute.c +++ b/src/execute.c @@ -55,6 +55,7 @@ #include "exit-status.h" #include "missing.h" #include "utmp-wtmp.h" +#include "def.h" /* This assumes there is a 'tty' group */ #define TTY_MODE 0620 @@ -139,6 +140,19 @@ static const char *tty_path(const ExecContext *context) { return "/dev/console"; } +void exec_context_tty_reset(const ExecContext *context) { + assert(context); + + if (context->tty_vhangup) + terminal_vhangup(tty_path(context)); + + if (context->tty_reset) + reset_terminal(tty_path(context)); + + if (context->tty_vt_disallocate && context->tty_path) + vt_disallocate(context->tty_path); +} + static int open_null_as(int flags, int nfd) { int fd, r; @@ -173,9 +187,9 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons zero(sa); sa.sa.sa_family = AF_UNIX; - strncpy(sa.un.sun_path+1, LOGGER_SOCKET, sizeof(sa.un.sun_path)-1); + strncpy(sa.un.sun_path, LOGGER_SOCKET, sizeof(sa.un.sun_path)); - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + sizeof(LOGGER_SOCKET) - 1) < 0) { + if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + sizeof(LOGGER_SOCKET) - 1) < 0) { close_nointr_nofail(fd); return -errno; } @@ -535,82 +549,6 @@ static int restore_confirm_stdio(const ExecContext *context, return 0; } -static int get_group_creds(const char *groupname, gid_t *gid) { - struct group *g; - unsigned long lu; - - assert(groupname); - assert(gid); - - /* We enforce some special rules for gid=0: in order to avoid - * NSS lookups for root we hardcode its data. */ - - if (streq(groupname, "root") || streq(groupname, "0")) { - *gid = 0; - return 0; - } - - if (safe_atolu(groupname, &lu) >= 0) { - errno = 0; - g = getgrgid((gid_t) lu); - } else { - errno = 0; - g = getgrnam(groupname); - } - - if (!g) - return errno != 0 ? -errno : -ESRCH; - - *gid = g->gr_gid; - return 0; -} - -static int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) { - struct passwd *p; - unsigned long lu; - - assert(username); - assert(*username); - assert(uid); - assert(gid); - assert(home); - - /* We enforce some special rules for uid=0: in order to avoid - * NSS lookups for root we hardcode its data. */ - - if (streq(*username, "root") || streq(*username, "0")) { - *username = "root"; - *uid = 0; - *gid = 0; - *home = "/root"; - return 0; - } - - if (safe_atolu(*username, &lu) >= 0) { - errno = 0; - p = getpwuid((uid_t) lu); - - /* If there are multiple users with the same id, make - * sure to leave $USER to the configured value instead - * of the first occurrence in the database. However if - * the uid was configured by a numeric uid, then let's - * pick the real username from /etc/passwd. */ - if (*username && p) - *username = p->pw_name; - } else { - errno = 0; - p = getpwnam(*username); - } - - if (!p) - return errno != 0 ? -errno : -ESRCH; - - *uid = p->pw_uid; - *gid = p->pw_gid; - *home = p->pw_dir; - return 0; -} - static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) { bool keep_groups = false; int r; @@ -622,9 +560,12 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ if (context->group || username) { - if (context->group) - if ((r = get_group_creds(context->group, &gid)) < 0) + if (context->group) { + const char *g = context->group; + + if ((r = get_group_creds(&g, &gid)) < 0) return r; + } /* First step, initialize groups from /etc/groups */ if (username && gid != 0) { @@ -645,7 +586,7 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ char **i; /* Final step, initialize any manually set supplementary groups */ - ngroups_max = (int) sysconf(_SC_NGROUPS_MAX); + assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0); if (!(gids = new(gid_t, ngroups_max))) return -ENOMEM; @@ -659,13 +600,16 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ k = 0; STRV_FOREACH(i, context->supplementary_groups) { + const char *g; if (k >= ngroups_max) { free(gids); return -E2BIG; } - if ((r = get_group_creds(*i, gids+k)) < 0) { + g = *i; + r = get_group_creds(&g, gids+k); + if (r < 0) { free(gids); return r; } @@ -803,9 +747,6 @@ static int setup_pam( close_session = true; - if ((pam_code = pam_setcred(handle, PAM_ESTABLISH_CRED | PAM_SILENT)) != PAM_SUCCESS) - goto fail; - if ((!(e = pam_getenvlist(handle)))) { pam_code = PAM_BUF_ERR; goto fail; @@ -832,7 +773,7 @@ static int setup_pam( /* This string must fit in 10 chars (i.e. the length * of "/sbin/init") */ - rename_process("sd:pam"); + rename_process("sd(PAM)"); /* Make sure we don't keep open the passed fds in this child. We assume that otherwise only those fds are @@ -850,13 +791,20 @@ static int setup_pam( /* Check if our parent process might already have * died? */ if (getppid() == parent_pid) { - if (sigwait(&ss, &sig) < 0) - goto child_finish; + for (;;) { + if (sigwait(&ss, &sig) < 0) { + if (errno == EINTR) + continue; + + goto child_finish; + } - assert(sig == SIGTERM); + assert(sig == SIGTERM); + break; + } } - /* Only if our parent died we'll end the session */ + /* If our parent died we'll end the session */ if (getppid() != parent_pid) if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS) goto child_finish; @@ -872,7 +820,7 @@ static int setup_pam( * cleanups, so forget about the handle here. */ handle = NULL; - /* Unblock SIGSUR1 again in the parent */ + /* Unblock SIGTERM again in the parent */ if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0) goto fail; @@ -880,6 +828,9 @@ static int setup_pam( * might have opened it, but we don't want this fd around. */ closelog(); + *pam_env = e; + e = NULL; + return 0; fail: @@ -903,6 +854,71 @@ fail: } #endif +static int do_capability_bounding_set_drop(uint64_t drop) { + unsigned long i; + cap_t old_cap = NULL, new_cap = NULL; + cap_flag_value_t fv; + int r; + + /* If we are run as PID 1 we will lack CAP_SETPCAP by default + * in the effective set (yes, the kernel drops that when + * executing init!), so get it back temporarily so that we can + * call PR_CAPBSET_DROP. */ + + old_cap = cap_get_proc(); + if (!old_cap) + return -errno; + + if (cap_get_flag(old_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) { + r = -errno; + goto finish; + } + + if (fv != CAP_SET) { + static const cap_value_t v = CAP_SETPCAP; + + new_cap = cap_dup(old_cap); + if (!new_cap) { + r = -errno; + goto finish; + } + + if (cap_set_flag(new_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { + r = -errno; + goto finish; + } + + if (cap_set_proc(new_cap) < 0) { + r = -errno; + goto finish; + } + } + + for (i = 0; i <= MAX(63LU, (unsigned long) CAP_LAST_CAP); i++) + if (drop & ((uint64_t) 1ULL << (uint64_t) i)) { + if (prctl(PR_CAPBSET_DROP, i) < 0) { + if (errno == EINVAL) + break; + + r = -errno; + goto finish; + } + } + + r = 0; + +finish: + if (new_cap) + cap_free(new_cap); + + if (old_cap) { + cap_set_proc(old_cap); + cap_free(old_cap); + } + + return r; +} + int exec_spawn(ExecCommand *command, char **argv, const ExecContext *context, @@ -980,7 +996,7 @@ int exec_spawn(ExecCommand *command, /* This string must fit in 10 chars (i.e. the length * of "/sbin/init") */ - rename_process("sd:exec"); + rename_process("sd(EXEC)"); /* We reset exactly these signals, since they are the * only ones we set to SIG_IGN in the main daemon. All @@ -1026,6 +1042,8 @@ int exec_spawn(ExecCommand *command, } } + exec_context_tty_reset(context); + /* We skip the confirmation step if we shall not apply the TTY */ if (confirm_spawn && (!is_terminal_input(context->std_input) || apply_tty_stdin)) { @@ -1105,7 +1123,8 @@ int exec_spawn(ExecCommand *command, snprintf(t, sizeof(t), "%i", adj); char_array_0(t); - if (write_one_line_file("/proc/self/oom_adj", t) < 0) { + if (write_one_line_file("/proc/self/oom_adj", t) < 0 + && errno != EACCES) { r = EXIT_OOM_ADJUST; goto fail_child; } @@ -1164,16 +1183,14 @@ int exec_spawn(ExecCommand *command, r = EXIT_STDIN; goto fail_child; } - } -#ifdef HAVE_PAM - if (context->pam_name && username) { - if (setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds) < 0) { - r = EXIT_PAM; - goto fail_child; - } + if (cgroup_bondings && context->control_group_modify) + if (cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid) < 0 || + cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid) < 0) { + r = EXIT_CGROUP; + goto fail_child; + } } -#endif if (apply_permissions) if (enforce_groups(context, username, uid) < 0) { @@ -1183,6 +1200,15 @@ int exec_spawn(ExecCommand *command, umask(context->umask); +#ifdef HAVE_PAM + if (context->pam_name && username) { + if (setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds) != 0) { + r = EXIT_PAM; + goto fail_child; + } + } +#endif + if (strv_length(context->read_write_dirs) > 0 || strv_length(context->read_only_dirs) > 0 || strv_length(context->inaccessible_dirs) > 0 || @@ -1248,6 +1274,12 @@ int exec_spawn(ExecCommand *command, } } + if (context->capability_bounding_set_drop) + if (do_capability_bounding_set_drop(context->capability_bounding_set_drop) < 0) { + r = EXIT_CAPABILITIES; + goto fail_child; + } + if (context->user) if (enforce_user(context, uid) < 0) { r = EXIT_USER; @@ -1370,7 +1402,7 @@ fail_parent: void exec_context_init(ExecContext *c) { assert(c); - c->umask = 0002; + c->umask = 0022; c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); c->cpu_sched_policy = SCHED_OTHER; c->syslog_priority = LOG_DAEMON|LOG_INFO; @@ -1561,12 +1593,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sWorkingDirectory: %s\n" "%sRootDirectory: %s\n" "%sNonBlocking: %s\n" - "%sPrivateTmp: %s\n", + "%sPrivateTmp: %s\n" + "%sControlGroupModify: %s\n", prefix, c->umask, prefix, c->working_directory ? c->working_directory : "/", prefix, c->root_directory ? c->root_directory : "/", prefix, yes_no(c->non_blocking), - prefix, yes_no(c->private_tmp)); + prefix, yes_no(c->private_tmp), + prefix, yes_no(c->control_group_modify)); STRV_FOREACH(e, c->environment) fprintf(f, "%sEnvironment: %s\n", prefix, *e); @@ -1630,8 +1664,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->tty_path) fprintf(f, - "%sTTYPath: %s\n", - prefix, c->tty_path); + "%sTTYPath: %s\n" + "%sTTYReset: %s\n" + "%sTTYVHangup: %s\n" + "%sTTYVTDisallocate: %s\n", + prefix, c->tty_path, + prefix, yes_no(c->tty_reset), + prefix, yes_no(c->tty_vhangup), + prefix, yes_no(c->tty_vt_disallocate)); if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || @@ -1640,7 +1680,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sSyslogFacility: %s\n" "%sSyslogLevel: %s\n", - prefix, log_facility_to_string(LOG_FAC(c->syslog_priority)), + prefix, log_facility_unshifted_to_string(c->syslog_priority >> 3), prefix, log_level_to_string(LOG_PRI(c->syslog_priority))); if (c->capabilities) { @@ -1663,15 +1703,16 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : ""); if (c->capability_bounding_set_drop) { - fprintf(f, "%sCapabilityBoundingSetDrop:", prefix); + unsigned long l; + fprintf(f, "%sCapabilityBoundingSet:", prefix); - for (i = 0; i <= CAP_LAST_CAP; i++) - if (c->capability_bounding_set_drop & (1 << i)) { + for (l = 0; l <= (unsigned long) CAP_LAST_CAP; l++) + if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) { char *t; - if ((t = cap_to_name(i))) { + if ((t = cap_to_name(l))) { fprintf(f, " %s", t); - free(t); + cap_free(t); } } @@ -1732,7 +1773,7 @@ void exec_status_start(ExecStatus *s, pid_t pid) { dual_timestamp_get(&s->start_timestamp); } -void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char *utmp_id) { +void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) { assert(s); if ((s->pid && s->pid != pid) || @@ -1745,8 +1786,12 @@ void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char s->code = code; s->status = status; - if (utmp_id) - utmp_put_dead_process(utmp_id, pid, code, status); + if (context) { + if (context->utmp_id) + utmp_put_dead_process(context->utmp_id, pid, code, status); + + exec_context_tty_reset(context); + } } void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { @@ -1923,7 +1968,6 @@ DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); static const char* const kill_mode_table[_KILL_MODE_MAX] = { [KILL_CONTROL_GROUP] = "control-group", - [KILL_PROCESS_GROUP] = "process-group", [KILL_PROCESS] = "process", [KILL_NONE] = "none" };