Beispiel #1
0
static int fix_acl(int fd, uid_t uid) {

#if HAVE_ACL
        _cleanup_(acl_freep) acl_t acl = NULL;
        acl_entry_t entry;
        acl_permset_t permset;
        int r;

        assert(fd >= 0);

        if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
                return 0;

        /* Make sure normal users can read (but not write or delete)
         * their own coredumps */

        acl = acl_get_fd(fd);
        if (!acl)
                return log_error_errno(errno, "Failed to get ACL: %m");

        if (acl_create_entry(&acl, &entry) < 0 ||
            acl_set_tag_type(entry, ACL_USER) < 0 ||
            acl_set_qualifier(entry, &uid) < 0)
                return log_error_errno(errno, "Failed to patch ACL: %m");

        if (acl_get_permset(entry, &permset) < 0 ||
            acl_add_perm(permset, ACL_READ) < 0)
                return log_warning_errno(errno, "Failed to patch ACL: %m");

        r = calc_acl_mask_if_needed(&acl);
        if (r < 0)
                return log_warning_errno(r, "Failed to patch ACL: %m");

        if (acl_set_fd(fd, acl) < 0)
                return log_error_errno(errno, "Failed to apply ACL: %m");
#endif

        return 0;
}
Beispiel #2
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);