Example #1
0
/*
   Any time an access gets denied this callback will be called
   with the audit data.  We then need to just copy the audit data into the msgbuf.
*/
static int audit_callback(
                void *auditdata,
                security_class_t cls,
                char *msgbuf,
                size_t msgbufsize) {

        const struct audit_info *audit = auditdata;
        uid_t uid = 0, login_uid = 0;
        gid_t gid = 0;
        char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
        char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
        char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a";

        if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
                xsprintf(login_uid_buf, UID_FMT, login_uid);
        if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
                xsprintf(uid_buf, UID_FMT, uid);
        if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
                xsprintf(gid_buf, GID_FMT, gid);

        snprintf(msgbuf, msgbufsize,
                 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
                 login_uid_buf, uid_buf, gid_buf,
                 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
                 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");

        return 0;
}
Example #2
0
void server_forward_kmsg(
        Server *s,
        int priority,
        const char *identifier,
        const char *message,
        const struct ucred *ucred) {

        struct iovec iovec[5];
        char header_priority[DECIMAL_STR_MAX(priority) + 3],
             header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
        int n = 0;
        char *ident_buf = NULL;

        assert(s);
        assert(priority >= 0);
        assert(priority <= 999);
        assert(message);

        if (_unlikely_(LOG_PRI(priority) > s->max_level_kmsg))
                return;

        if (_unlikely_(s->dev_kmsg_fd < 0))
                return;

        /* Never allow messages with kernel facility to be written to
         * kmsg, regardless where the data comes from. */
        priority = syslog_fixup_facility(priority);

        /* First: priority field */
        xsprintf(header_priority, "<%i>", priority);
        IOVEC_SET_STRING(iovec[n++], header_priority);

        /* Second: identifier and PID */
        if (ucred) {
                if (!identifier) {
                        get_process_comm(ucred->pid, &ident_buf);
                        identifier = ident_buf;
                }

                xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);

                if (identifier)
                        IOVEC_SET_STRING(iovec[n++], identifier);

                IOVEC_SET_STRING(iovec[n++], header_pid);
        } else if (identifier) {
                IOVEC_SET_STRING(iovec[n++], identifier);
                IOVEC_SET_STRING(iovec[n++], ": ");
        }

        /* Fourth: message */
        IOVEC_SET_STRING(iovec[n++], message);
        IOVEC_SET_STRING(iovec[n++], "\n");

        if (writev(s->dev_kmsg_fd, iovec, n) < 0)
                log_debug_errno(errno, "Failed to write to /dev/kmsg for logging: %m");

        free(ident_buf);
}
Example #3
0
static void server_process_entry_meta(
                const char *p, size_t l,
                const struct ucred *ucred,
                int *priority,
                char **identifier,
                char **message,
                pid_t *object_pid) {

        /* We need to determine the priority of this entry for the rate limiting logic */

        if (l == 10 &&
            startswith(p, "PRIORITY=") &&
            p[9] >= '0' && p[9] <= '9')
                *priority = (*priority & LOG_FACMASK) | (p[9] - '0');

        else if (l == 17 &&
                 startswith(p, "SYSLOG_FACILITY=") &&
                 p[16] >= '0' && p[16] <= '9')
                *priority = (*priority & LOG_PRIMASK) | ((p[16] - '0') << 3);

        else if (l == 18 &&
                 startswith(p, "SYSLOG_FACILITY=") &&
                 p[16] >= '0' && p[16] <= '9' &&
                 p[17] >= '0' && p[17] <= '9')
                *priority = (*priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);

        else if (l >= 19 &&
                 startswith(p, "SYSLOG_IDENTIFIER=")) {
                char *t;

                t = strndup(p + 18, l - 18);
                if (t) {
                        free(*identifier);
                        *identifier = t;
                }

        } else if (l >= 8 &&
                   startswith(p, "MESSAGE=")) {
                char *t;

                t = strndup(p + 8, l - 8);
                if (t) {
                        free(*message);
                        *message = t;
                }

        } else if (l > STRLEN("OBJECT_PID=") &&
                   l < STRLEN("OBJECT_PID=")  + DECIMAL_STR_MAX(pid_t) &&
                   startswith(p, "OBJECT_PID=") &&
                   allow_object_pid(ucred)) {
                char buf[DECIMAL_STR_MAX(pid_t)];
                memcpy(buf, p + STRLEN("OBJECT_PID="),
                       l - STRLEN("OBJECT_PID="));
                buf[l-STRLEN("OBJECT_PID=")] = '\0';

                (void) parse_pid(buf, object_pid);
        }
}
Example #4
0
static int write_to_kmsg(
                int level,
                int error,
                const char *file,
                int line,
                const char *func,
                const char *object_field,
                const char *object,
                const char *buffer) {

        char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
             header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
        struct iovec iovec[5] = {};

        if (kmsg_fd < 0)
                return 0;

        xsprintf(header_priority, "<%i>", level);
        xsprintf(header_pid, "["PID_FMT"]: ", getpid());

        IOVEC_SET_STRING(iovec[0], header_priority);
        IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
        IOVEC_SET_STRING(iovec[2], header_pid);
        IOVEC_SET_STRING(iovec[3], buffer);
        IOVEC_SET_STRING(iovec[4], "\n");

        if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
                return -errno;

        return 1;
}
Example #5
0
static int write_to_syslog(
                int level,
                int error,
                const char *file,
                int line,
                const char *func,
                const char *buffer) {

        char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
             header_time[64],
             header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
        struct iovec iovec[5] = {};
        struct msghdr msghdr = {
                .msg_iov = iovec,
                .msg_iovlen = ELEMENTSOF(iovec),
        };
        time_t t;
        struct tm *tm;

        if (syslog_fd < 0)
                return 0;

        xsprintf(header_priority, "<%i>", level);

        t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
        tm = localtime(&t);
        if (!tm)
                return -EINVAL;

        if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
                return -EINVAL;

        xsprintf(header_pid, "["PID_FMT"]: ", getpid());

        IOVEC_SET_STRING(iovec[0], header_priority);
        IOVEC_SET_STRING(iovec[1], header_time);
        IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
        IOVEC_SET_STRING(iovec[3], header_pid);
        IOVEC_SET_STRING(iovec[4], buffer);

        /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
        if (syslog_is_stream)
                iovec[4].iov_len++;

        for (;;) {
                ssize_t n;

                n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
                if (n < 0)
                        return -errno;

                if (!syslog_is_stream ||
                    (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
                        break;

                IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
        }

        return 1;
}
Example #6
0
static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
        char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
        _cleanup_strv_free_ char **a = NULL;
        _cleanup_free_ char *s = NULL;
        int r;

        assert_return(ifindex > 0, -EINVAL);
        assert_return(ret, -EINVAL);

        xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
        r = parse_env_file(path, NEWLINE, key, &s, NULL);
        if (r == -ENOENT)
                return -ENODATA;
        if (r < 0)
                return r;
        if (isempty(s)) {
                *ret = NULL;
                return 0;
        }

        a = strv_split(s, " ");
        if (!a)
                return -ENOMEM;

        strv_uniq(a);
        r = strv_length(a);

        *ret = TAKE_PTR(a);

        return r;
}
Example #7
0
static void test_get_process_comm(pid_t pid) {
        struct stat st;
        _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL;
        _cleanup_free_ char *env = NULL;
        char path[strlen("/proc//comm") + DECIMAL_STR_MAX(pid_t)];
        pid_t e;
        uid_t u;
        gid_t g;
        dev_t h;
        int r;

        xsprintf(path, "/proc/"PID_FMT"/comm", pid);

        if (stat(path, &st) == 0) {
                assert_se(get_process_comm(pid, &a) >= 0);
                log_info("PID"PID_FMT" comm: '%s'", pid, a);
        } else
                log_warning("%s not exist.", path);

        assert_se(get_process_cmdline(pid, 0, true, &c) >= 0);
        log_info("PID"PID_FMT" cmdline: '%s'", pid, c);

        assert_se(get_process_cmdline(pid, 8, false, &d) >= 0);
        log_info("PID"PID_FMT" cmdline truncated to 8: '%s'", pid, d);

        free(d);
        assert_se(get_process_cmdline(pid, 1, false, &d) >= 0);
        log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);

        assert_se(get_process_ppid(pid, &e) >= 0);
        log_info("PID"PID_FMT" PPID: "PID_FMT, pid, e);
        assert_se(pid == 1 ? e == 0 : e > 0);

        assert_se(is_kernel_thread(pid) == 0 || pid != 1);

        r = get_process_exe(pid, &f);
        assert_se(r >= 0 || r == -EACCES);
        log_info("PID"PID_FMT" exe: '%s'", pid, strna(f));

        assert_se(get_process_uid(pid, &u) == 0);
        log_info("PID"PID_FMT" UID: "UID_FMT, pid, u);
        assert_se(u == 0 || pid != 1);

        assert_se(get_process_gid(pid, &g) == 0);
        log_info("PID"PID_FMT" GID: "GID_FMT, pid, g);
        assert_se(g == 0 || pid != 1);

        r = get_process_environ(pid, &env);
        assert_se(r >= 0 || r == -EACCES);
        log_info("PID"PID_FMT" strlen(environ): %zi", pid, env ? (ssize_t)strlen(env) : (ssize_t)-errno);

        if (!detect_container())
                assert_se(get_ctty_devnr(pid, &h) == -ENXIO || pid != 1);

        getenv_for_pid(pid, "PATH", &i);
        log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
}
Example #8
0
int sync_cgroup(pid_t pid, CGroupUnified unified_requested) {
        _cleanup_free_ char *cgroup = NULL;
        char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
        bool undo_mount = false;
        const char *fn;
        int unified, r;

        unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
        if (unified < 0)
                return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");

        if ((unified > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
                return 0;

        /* When the host uses the legacy cgroup setup, but the
         * container shall use the unified hierarchy, let's make sure
         * we copy the path from the name=systemd hierarchy into the
         * unified hierarchy. Similar for the reverse situation. */

        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
        if (r < 0)
                return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid);

        /* In order to access the unified hierarchy we need to mount it */
        if (!mkdtemp(tree))
                return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");

        if (unified)
                r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
        else
                r = mount("cgroup", tree, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
        if (r < 0) {
                r = log_error_errno(errno, "Failed to mount unified hierarchy: %m");
                goto finish;
        }

        undo_mount = true;

        fn = strjoina(tree, cgroup, "/cgroup.procs");
        (void) mkdir_parents(fn, 0755);

        sprintf(pid_string, PID_FMT, pid);
        r = write_string_file(fn, pid_string, 0);
        if (r < 0)
                log_error_errno(r, "Failed to move process: %m");

finish:
        if (undo_mount)
                (void) umount(tree);

        (void) rmdir(tree);
        return r;
}
Example #9
0
static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
        char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
        _cleanup_free_ int *ifis = NULL;
        _cleanup_free_ char *s = NULL;
        size_t allocated = 0, c = 0;
        const char *x;
        int r;

        assert_return(ifindex > 0, -EINVAL);
        assert_return(ret, -EINVAL);

        xsprintf(path, "/run/systemd/netif/links/%i", ifindex);
        r = parse_env_file(path, NEWLINE, key, &s, NULL);
        if (r == -ENOENT)
                return -ENODATA;
        if (r < 0)
                return r;
        if (isempty(s)) {
                *ret = NULL;
                return 0;
        }

        x = s;
        for (;;) {
                _cleanup_free_ char *word = NULL;

                r = extract_first_word(&x, &word, NULL, 0);
                if (r < 0)
                        return r;
                if (r == 0)
                        break;

                r = parse_ifindex(word, &ifindex);
                if (r < 0)
                        return r;

                if (!GREEDY_REALLOC(ifis, allocated, c + 1))
                        return -ENOMEM;

                ifis[c++] = ifindex;
        }

        if (!GREEDY_REALLOC(ifis, allocated, c + 1))
                return -ENOMEM;
        ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/

        *ret = ifis;
        ifis = NULL;

        return c;
}
Example #10
0
int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
        char num[DECIMAL_STR_MAX(usec_t)];
        usec_t usec_initialized;
        int r;

        assert(device);

        if (device_old && device_old->usec_initialized > 0)
                usec_initialized = device_old->usec_initialized;
        else
                usec_initialized = now(CLOCK_MONOTONIC);

        r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
        if (r < 0)
                return -errno;

        r = device_set_usec_initialized(device, num);
        if (r < 0)
                return r;

        return 0;
}
Example #11
0
static int network_link_get_string(int ifindex, const char *field, char **ret) {
        char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
        _cleanup_free_ char *s = NULL;
        int r;

        assert_return(ifindex > 0, -EINVAL);
        assert_return(ret, -EINVAL);

        xsprintf(path, "/run/systemd/netif/links/%i", ifindex);

        r = parse_env_file(path, NEWLINE, field, &s, NULL);
        if (r == -ENOENT)
                return -ENODATA;
        if (r < 0)
                return r;
        if (isempty(s))
                return -ENODATA;

        *ret = TAKE_PTR(s);

        return 0;
}
Example #12
0
static int make_uid_symlinks(uid_t uid, const char *name, bool b) {

        char path1[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
        const char *path2;
        int r = 0, k;

        /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
         * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
         * would be its own client then). We hence keep these world-readable symlinks in place, so that the
         * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
         * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
         * on them and as those may be taken by any user with read access we can't make them world-readable. */

        xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
        if (unlink(path1) < 0 && errno != ENOENT)
                r = -errno;

        if (b && symlink(name, path1) < 0) {
                k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1);
                if (r == 0)
                        r = k;
        }

        path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
        if (unlink(path2) < 0 && errno != ENOENT) {
                k = -errno;
                if (r == 0)
                        r = k;
        }

        if (b && symlink(path1 + STRLEN("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
                k = log_warning_errno(errno,  "Failed to symlink \"%s\": %m", path2);
                if (r == 0)
                        r = k;
        }

        return r;
}
Example #13
0
void server_process_native_message(
                Server *s,
                const void *buffer, size_t buffer_size,
                const struct ucred *ucred,
                const struct timeval *tv,
                const char *label, size_t label_len) {

        struct iovec *iovec = NULL;
        unsigned n = 0, j, tn = (unsigned) -1;
        const char *p;
        size_t remaining, m = 0, entry_size = 0;
        int priority = LOG_INFO;
        char *identifier = NULL, *message = NULL;
        pid_t object_pid = 0;

        assert(s);
        assert(buffer || buffer_size == 0);

        p = buffer;
        remaining = buffer_size;

        while (remaining > 0) {
                const char *e, *q;

                e = memchr(p, '\n', remaining);

                if (!e) {
                        /* Trailing noise, let's ignore it, and flush what we collected */
                        log_debug("Received message with trailing noise, ignoring.");
                        break;
                }

                if (e == p) {
                        /* Entry separator */

                        if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
                                log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size);
                                continue;
                        }

                        server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
                        n = 0;
                        priority = LOG_INFO;
                        entry_size = 0;

                        p++;
                        remaining--;
                        continue;
                }

                if (*p == '.' || *p == '#') {
                        /* Ignore control commands for now, and
                         * comments too. */
                        remaining -= (e - p) + 1;
                        p = e + 1;
                        continue;
                }

                /* A property follows */

                /* n existing properties, 1 new, +1 for _TRANSPORT */
                if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) {
                        log_oom();
                        break;
                }

                q = memchr(p, '=', e - p);
                if (q) {
                        if (valid_user_field(p, q - p, false)) {
                                size_t l;

                                l = e - p;

                                /* If the field name starts with an
                                 * underscore, skip the variable,
                                 * since that indidates a trusted
                                 * field */
                                iovec[n].iov_base = (char*) p;
                                iovec[n].iov_len = l;
                                entry_size += iovec[n].iov_len;
                                n++;

                                /* We need to determine the priority
                                 * of this entry for the rate limiting
                                 * logic */
                                if (l == 10 &&
                                    startswith(p, "PRIORITY=") &&
                                    p[9] >= '0' && p[9] <= '9')
                                        priority = (priority & LOG_FACMASK) | (p[9] - '0');

                                else if (l == 17 &&
                                         startswith(p, "SYSLOG_FACILITY=") &&
                                         p[16] >= '0' && p[16] <= '9')
                                        priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);

                                else if (l == 18 &&
                                         startswith(p, "SYSLOG_FACILITY=") &&
                                         p[16] >= '0' && p[16] <= '9' &&
                                         p[17] >= '0' && p[17] <= '9')
                                        priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);

                                else if (l >= 19 &&
                                         startswith(p, "SYSLOG_IDENTIFIER=")) {
                                        char *t;

                                        t = strndup(p + 18, l - 18);
                                        if (t) {
                                                free(identifier);
                                                identifier = t;
                                        }

                                } else if (l >= 8 &&
                                           startswith(p, "MESSAGE=")) {
                                        char *t;

                                        t = strndup(p + 8, l - 8);
                                        if (t) {
                                                free(message);
                                                message = t;
                                        }

                                } else if (l > strlen("OBJECT_PID=") &&
                                           l < strlen("OBJECT_PID=")  + DECIMAL_STR_MAX(pid_t) &&
                                           startswith(p, "OBJECT_PID=") &&
                                           allow_object_pid(ucred)) {
                                        char buf[DECIMAL_STR_MAX(pid_t)];
                                        memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
                                        buf[l-strlen("OBJECT_PID=")] = '\0';

                                        /* ignore error */
                                        parse_pid(buf, &object_pid);
                                }
                        }

                        remaining -= (e - p) + 1;
                        p = e + 1;
                        continue;
                } else {
                        le64_t l_le;
                        uint64_t l;
                        char *k;

                        if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
                                log_debug("Failed to parse message, ignoring.");
                                break;
                        }

                        memcpy(&l_le, e + 1, sizeof(uint64_t));
                        l = le64toh(l_le);

                        if (l > DATA_SIZE_MAX) {
                                log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
                                break;
                        }

                        if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
                            e[1+sizeof(uint64_t)+l] != '\n') {
                                log_debug("Failed to parse message, ignoring.");
                                break;
                        }

                        k = malloc((e - p) + 1 + l);
                        if (!k) {
                                log_oom();
                                break;
                        }

                        memcpy(k, p, e - p);
                        k[e - p] = '=';
                        memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);

                        if (valid_user_field(p, e - p, false)) {
                                iovec[n].iov_base = k;
                                iovec[n].iov_len = (e - p) + 1 + l;
                                entry_size += iovec[n].iov_len;
                                n++;
                        } else
                                free(k);

                        remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
                        p = e + 1 + sizeof(uint64_t) + l + 1;
                }
        }

        if (n <= 0)
                goto finish;

        tn = n++;
        IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
        entry_size += strlen("_TRANSPORT=journal");

        if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
                log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
                          n, entry_size);
                goto finish;
        }

        if (message) {
                if (s->forward_to_syslog)
                        server_forward_syslog(s, priority, identifier, message, ucred, tv);

                if (s->forward_to_kmsg)
                        server_forward_kmsg(s, priority, identifier, message, ucred);

                if (s->forward_to_console)
                        server_forward_console(s, priority, identifier, message, ucred);

                if (s->forward_to_wall)
                        server_forward_wall(s, priority, identifier, message, ucred);
        }

        server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);

finish:
        for (j = 0; j < n; j++)  {
                if (j == tn)
                        continue;

                if (iovec[j].iov_base < buffer ||
                    (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
                        free(iovec[j].iov_base);
        }

        free(iovec);
        free(identifier);
        free(message);
}
Example #14
0
void server_forward_console(
                Server *s,
                int priority,
                const char *identifier,
                const char *message,
                const struct ucred *ucred) {

        struct iovec iovec[5];
        struct timespec ts;
        char tbuf[sizeof("[] ")-1 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1];
        char header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t)];
        int n = 0, fd;
        _cleanup_free_ char *ident_buf = NULL;
        const char *tty;

        assert(s);
        assert(message);

        if (LOG_PRI(priority) > s->max_level_console)
                return;

        /* First: timestamp */
        if (prefix_timestamp()) {
                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
                xsprintf(tbuf, "[%5"PRI_TIME".%06ld] ",
                         ts.tv_sec,
                         ts.tv_nsec / 1000);
                IOVEC_SET_STRING(iovec[n++], tbuf);
        }

        /* Second: identifier and PID */
        if (ucred) {
                if (!identifier) {
                        get_process_comm(ucred->pid, &ident_buf);
                        identifier = ident_buf;
                }

                xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);

                if (identifier)
                        IOVEC_SET_STRING(iovec[n++], identifier);

                IOVEC_SET_STRING(iovec[n++], header_pid);
        } else if (identifier) {
                IOVEC_SET_STRING(iovec[n++], identifier);
                IOVEC_SET_STRING(iovec[n++], ": ");
        }

        /* Fourth: message */
        IOVEC_SET_STRING(iovec[n++], message);
        IOVEC_SET_STRING(iovec[n++], "\n");

        tty = s->tty_path ? s->tty_path : "/dev/console";

        fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC);
        if (fd < 0) {
                log_debug_errno(errno, "Failed to open %s for logging: %m", tty);
                return;
        }

        if (writev(fd, iovec, n) < 0)
                log_debug_errno(errno, "Failed to write to %s for logging: %m", tty);

        safe_close(fd);
}
Example #15
0
static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {

        /* Find a suitable free UID. We use the following strategy to find a suitable UID:
         *
         * 1. Initially, we try to read the UID of a number of specified paths. If any of these UIDs works, we use
         *    them. We use in order to increase the chance of UID reuse, if StateDirectory=, CacheDirectory= or
         *    LogsDirectory= are used, as reusing the UID these directories are owned by saves us from having to
         *    recursively chown() them to new users.
         *
         * 2. If that didn't yield a currently unused UID, we hash the user name, and try to use that. This should be
         *    pretty good, as the use ris by default derived from the unit name, and hence the same service and same
         *    user should usually get the same UID as long as our hashing doesn't clash.
         *
         * 3. Finally, if that didn't work, we randomly pick UIDs, until we find one that is empty.
         *
         * Since the dynamic UID space is relatively small we'll stop trying after 100 iterations, giving up. */

        enum {
                PHASE_SUGGESTED,  /* the first phase, reusing directory ownership UIDs */
                PHASE_HASHED,     /* the second phase, deriving a UID from the username by hashing */
                PHASE_RANDOM,     /* the last phase, randomly picking UIDs */
        } phase = PHASE_SUGGESTED;

        static const uint8_t hash_key[] = {
                0x37, 0x53, 0x7e, 0x31, 0xcf, 0xce, 0x48, 0xf5,
                0x8a, 0xbb, 0x39, 0x57, 0x8d, 0xd9, 0xec, 0x59
        };

        unsigned n_tries = 100, current_suggested = 0;
        int r;

        (void) mkdir("/run/systemd/dynamic-uid", 0755);

        for (;;) {
                char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
                _cleanup_close_ int lock_fd = -1;
                uid_t candidate;
                ssize_t l;

                if (--n_tries <= 0) /* Give up retrying eventually */
                        return -EBUSY;

                switch (phase) {

                case PHASE_SUGGESTED: {
                        struct stat st;

                        if (!suggested_paths || !suggested_paths[current_suggested]) {
                                /* We reached the end of the suggested paths list, let's try by hashing the name */
                                phase = PHASE_HASHED;
                                continue;
                        }

                        if (stat(suggested_paths[current_suggested++], &st) < 0)
                                continue; /* We can't read the UID of this path, but that doesn't matter, just try the next */

                        candidate = st.st_uid;
                        break;
                }

                case PHASE_HASHED:
                        /* A static user by this name does not exist yet. Let's find a free ID then, and use that. We
                         * start with a UID generated as hash from the user name. */
                        candidate = UID_CLAMP_INTO_RANGE(siphash24(name, strlen(name), hash_key));

                        /* If this one fails, we should proceed with random tries */
                        phase = PHASE_RANDOM;
                        break;

                case PHASE_RANDOM:

                        /* Pick another random UID, and see if that works for us. */
                        random_bytes(&candidate, sizeof(candidate));
                        candidate = UID_CLAMP_INTO_RANGE(candidate);
                        break;

                default:
                        assert_not_reached("unknown phase");
                }

                /* Make sure whatever we picked here actually is in the right range */
                if (!uid_is_dynamic(candidate))
                        continue;

                xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate);

                for (;;) {
                        struct stat st;

                        lock_fd = open(lock_path, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
                        if (lock_fd < 0)
                                return -errno;

                        r = flock(lock_fd, LOCK_EX|LOCK_NB); /* Try to get a BSD file lock on the UID lock file */
                        if (r < 0) {
                                if (IN_SET(errno, EBUSY, EAGAIN))
                                        goto next; /* already in use */

                                return -errno;
                        }

                        if (fstat(lock_fd, &st) < 0)
                                return -errno;
                        if (st.st_nlink > 0)
                                break;

                        /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
                         * got the lock. Close it, and try again. */
                        lock_fd = safe_close(lock_fd);
                }

                /* Some superficial check whether this UID/GID might already be taken by some static user */
                if (getpwuid(candidate) ||
                    getgrgid((gid_t) candidate) ||
                    search_ipc(candidate, (gid_t) candidate) != 0) {
                        (void) unlink(lock_path);
                        continue;
                }

                /* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */
                l = pwritev(lock_fd,
                            (struct iovec[2]) {
                                    IOVEC_INIT_STRING(name),
                                    IOVEC_INIT((char[1]) { '\n' }, 1),
                            }, 2, 0);
Example #16
0
static void automount_enter_waiting(Automount *a) {
        _cleanup_close_ int ioctl_fd = -1;
        int p[2] = { -1, -1 };
        char name[sizeof("systemd-")-1 + DECIMAL_STR_MAX(pid_t) + 1];
        char options[sizeof("fd=,pgrp=,minproto=5,maxproto=5,direct")-1
                     + DECIMAL_STR_MAX(int) + DECIMAL_STR_MAX(gid_t) + 1];
        bool mounted = false;
        int r, dev_autofs_fd;
        struct stat st;

        assert(a);
        assert(a->pipe_fd < 0);
        assert(a->where);

        set_clear(a->tokens);

        r = unit_fail_if_symlink(UNIT(a), a->where);
        if (r < 0)
                goto fail;

        (void) mkdir_p_label(a->where, 0555);

        unit_warn_if_dir_nonempty(UNIT(a), a->where);

        dev_autofs_fd = open_dev_autofs(UNIT(a)->manager);
        if (dev_autofs_fd < 0) {
                r = dev_autofs_fd;
                goto fail;
        }

        if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
                r = -errno;
                goto fail;
        }

        xsprintf(options, "fd=%i,pgrp="PID_FMT",minproto=5,maxproto=5,direct", p[1], getpgrp());
        xsprintf(name, "systemd-"PID_FMT, getpid());
        if (mount(name, a->where, "autofs", 0, options) < 0) {
                r = -errno;
                goto fail;
        }

        mounted = true;

        p[1] = safe_close(p[1]);

        if (stat(a->where, &st) < 0) {
                r = -errno;
                goto fail;
        }

        ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev);
        if (ioctl_fd < 0) {
                r = ioctl_fd;
                goto fail;
        }

        r = autofs_protocol(dev_autofs_fd, ioctl_fd);
        if (r < 0)
                goto fail;

        r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec);
        if (r < 0)
                goto fail;

        /* Autofs fun fact:
         *
         * Unless we close the ioctl fd here, for some weird reason
         * the direct mount will not receive events from the
         * kernel. */

        r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a);
        if (r < 0)
                goto fail;

        (void) sd_event_source_set_description(a->pipe_event_source, "automount-io");

        a->pipe_fd = p[0];
        a->dev_id = st.st_dev;

        automount_set_state(a, AUTOMOUNT_WAITING);

        return;

fail:
        log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");

        safe_close_pair(p);

        if (mounted) {
                r = repeat_unmount(a->where, MNT_DETACH);
                if (r < 0)
                        log_error_errno(r, "Failed to unmount, ignoring: %m");
        }

        automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
}
Example #17
0
void server_forward_console(
                Server *s,
                int priority,
                const char *identifier,
                const char *message,
                const struct ucred *ucred) {

        struct iovec iovec[5];
        struct timespec ts;
        char tbuf[sizeof("[] ")-1 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1];
        char header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t)];
        int n = 0, fd;
        _cleanup_free_ char *ident_buf = NULL;
        const char *tty;

        assert(s);
        assert(message);

        if (LOG_PRI(priority) > s->max_level_console)
                return;

        /* First: timestamp */
        if (prefix_timestamp()) {
                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
                xsprintf(tbuf, "[%5"PRI_TIME".%06ld] ",
                         ts.tv_sec,
                         ts.tv_nsec / 1000);
                IOVEC_SET_STRING(iovec[n++], tbuf);
        }

        /* Second: identifier and PID */
        if (ucred) {
                if (!identifier) {
                        get_process_comm(ucred->pid, &ident_buf);
                        identifier = ident_buf;
                }

                xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);

                if (identifier)
                        IOVEC_SET_STRING(iovec[n++], identifier);

                IOVEC_SET_STRING(iovec[n++], header_pid);
        } else if (identifier) {
                IOVEC_SET_STRING(iovec[n++], identifier);
                IOVEC_SET_STRING(iovec[n++], ": ");
        }

        /* Fourth: message */
        IOVEC_SET_STRING(iovec[n++], message);
        IOVEC_SET_STRING(iovec[n++], "\n");

        tty = s->tty_path ? s->tty_path : "/dev/console";

        /* Before you ask: yes, on purpose we open/close the console for each log line we write individually. This is a
         * good strategy to avoid journald getting killed by the kernel's SAK concept (it doesn't fix this entirely,
         * but minimizes the time window the kernel might end up killing journald due to SAK). It also makes things
         * easier for us so that we don't have to recover from hangups and suchlike triggered on the console. */

        fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC);
        if (fd < 0) {
                log_debug_errno(fd, "Failed to open %s for logging: %m", tty);
                return;
        }

        if (writev(fd, iovec, n) < 0)
                log_debug_errno(errno, "Failed to write to %s for logging: %m", tty);

        safe_close(fd);
}
Example #18
0
void server_forward_console(
                Server *s,
                int priority,
                const char *identifier,
                const char *message,
                struct ucred *ucred) {

        struct iovec iovec[5];
        char header_pid[16];
        struct timespec ts;
        char tbuf[4 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1];
        int n = 0, fd;
        char *ident_buf = NULL;
        const char *tty;

        assert(s);
        assert(message);

        if (LOG_PRI(priority) > s->max_level_console)
                return;

        /* First: timestamp */
        if (prefix_timestamp()) {
                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
                snprintf(tbuf, sizeof(tbuf), "[%5llu.%06llu] ",
                         (unsigned long long) ts.tv_sec,
                         (unsigned long long) ts.tv_nsec / 1000);
                IOVEC_SET_STRING(iovec[n++], tbuf);
        }

        /* Second: identifier and PID */
        if (ucred) {
                if (!identifier) {
                        get_process_comm(ucred->pid, &ident_buf);
                        identifier = ident_buf;
                }

                snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
                char_array_0(header_pid);

                if (identifier)
                        IOVEC_SET_STRING(iovec[n++], identifier);

                IOVEC_SET_STRING(iovec[n++], header_pid);
        } else if (identifier) {
                IOVEC_SET_STRING(iovec[n++], identifier);
                IOVEC_SET_STRING(iovec[n++], ": ");
        }

        /* Fourth: message */
        IOVEC_SET_STRING(iovec[n++], message);
        IOVEC_SET_STRING(iovec[n++], "\n");

        tty = s->tty_path ? s->tty_path : "/dev/console";

        fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC);
        if (fd < 0) {
                log_debug("Failed to open %s for logging: %s", tty, strerror(errno));
                goto finish;
        }

        if (writev(fd, iovec, n) < 0)
                log_debug("Failed to write to %s for logging: %s", tty, strerror(errno));

        safe_close(fd);

finish:
        free(ident_buf);
}
Example #19
0
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
        _cleanup_free_ char *cgroup = NULL;
        char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
        bool undo_mount = false;
        const char *fn;
        int r, unified_controller;

        unified_controller = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
        if (unified_controller < 0)
                return log_error_errno(unified_controller, "Failed to determine whether the systemd hierarchy is unified: %m");
        if ((unified_controller > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
                return 0;

        /* When the host uses the legacy cgroup setup, but the
         * container shall use the unified hierarchy, let's make sure
         * we copy the path from the name=systemd hierarchy into the
         * unified hierarchy. Similar for the reverse situation. */

        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
        if (r < 0)
                return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid);

        /* In order to access the unified hierarchy we need to mount it */
        if (!mkdtemp(tree))
                return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");

        if (unified_controller > 0)
                r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup",
                                  MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
        else
                r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup2",
                                  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
        if (r < 0)
                goto finish;

        undo_mount = true;

        /* If nspawn dies abruptly the cgroup hierarchy created below
         * its unit isn't cleaned up. So, let's remove it
         * https://github.com/systemd/systemd/pull/4223#issuecomment-252519810 */
        fn = strjoina(tree, cgroup);
        (void) rm_rf(fn, REMOVE_ROOT|REMOVE_ONLY_DIRECTORIES);

        fn = strjoina(tree, cgroup, "/cgroup.procs");
        (void) mkdir_parents(fn, 0755);

        sprintf(pid_string, PID_FMT, pid);
        r = write_string_file(fn, pid_string, 0);
        if (r < 0) {
                log_error_errno(r, "Failed to move process: %m");
                goto finish;
        }

        fn = strjoina(tree, cgroup);
        r = chown_cgroup_path(fn, uid_shift);
        if (r < 0)
                log_error_errno(r, "Failed to chown() cgroup %s: %m", fn);
finish:
        if (undo_mount)
                (void) umount_verbose(tree);

        (void) rmdir(tree);
        return r;
}