Esempio n. 1
0
static void test_string_has_cc(void) {
        assert_se(string_has_cc("abc\1", NULL));
        assert_se(string_has_cc("abc\x7f", NULL));
        assert_se(string_has_cc("abc\x7f", NULL));
        assert_se(string_has_cc("abc\t\x7f", "\t"));
        assert_se(string_has_cc("abc\t\x7f", "\t"));
        assert_se(string_has_cc("\x7f", "\t"));
        assert_se(string_has_cc("\x7f", "\t\a"));

        assert_se(!string_has_cc("abc\t\t", "\t"));
        assert_se(!string_has_cc("abc\t\t\a", "\t\a"));
        assert_se(!string_has_cc("a\ab\tc", "\t\a"));
}
Esempio n. 2
0
static void write_env_var(FILE *f, const char *v) {
        const char *p;

        p = strchr(v, '=');
        if (!p) {
                /* Fallback */
                fputs(v, f);
                fputc('\n', f);
                return;
        }

        p++;
        fwrite(v, 1, p-v, f);

        if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
                fputc('\"', f);

                for (; *p; p++) {
                        if (strchr("\'\"\\`$", *p))
                                fputc('\\', f);

                        fputc(*p, f);
                }

                fputc('\"', f);
        } else
                fputs(p, f);

        fputc('\n', f);
}
Esempio n. 3
0
static void write_env_var(FILE *f, const char *v) {
        const char *p;

        p = strchr(v, '=');
        if (!p) {
                /* Fallback */
                fputs_unlocked(v, f);
                fputc_unlocked('\n', f);
                return;
        }

        p++;
        fwrite_unlocked(v, 1, p-v, f);

        if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
                fputc_unlocked('\"', f);

                for (; *p; p++) {
                        if (strchr(SHELL_NEED_ESCAPE, *p))
                                fputc_unlocked('\\', f);

                        fputc_unlocked(*p, f);
                }

                fputc_unlocked('\"', f);
        } else
                fputs_unlocked(p, f);

        fputc_unlocked('\n', f);
}
Esempio n. 4
0
bool env_value_is_valid(const char *e) {
        if (!e)
                return false;

        if (!utf8_is_valid(e))
                return false;

        /* bash allows tabs in environment variables, and so should
         * we */
        if (string_has_cc(e, "\t"))
                return false;

        /* POSIX says the overall size of the environment block cannot
         * be > ARG_MAX, an individual assignment hence cannot be
         * either. Discounting the shortest possible variable name of
         * length 1, the equal sign and trailing NUL this hence leaves
         * ARG_MAX-3 as longest possible variable value. */
        if (strlen(e) > ARG_MAX - 3)
                return false;

        return true;
}
Esempio n. 5
0
static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
    int interactive;
    const char *name;
    int r;

    assert(c);
    assert(m);

    r = sd_bus_message_read(m, "sb", &name, &interactive);
    if (r < 0)
        return r;

    if (isempty(name))
        name = NULL;

    if (streq_ptr(name, c->data[prop]))
        return sd_bus_reply_method_return(m, NULL);

    /* Since the pretty hostname should always be changed at the
     * same time as the static one, use the same policy action for
     * both... */

    r = bus_verify_polkit_async(
            m,
            CAP_SYS_ADMIN,
            prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
            NULL,
            interactive,
            UID_INVALID,
            &c->polkit_registry,
            error);
    if (r < 0)
        return r;
    if (r == 0)
        return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */

    if (isempty(name)) {
        c->data[prop] = mfree(c->data[prop]);
    } else {
        char *h;

        /* The icon name might ultimately be used as file
         * name, so better be safe than sorry */

        if (prop == PROP_ICON_NAME && !filename_is_valid(name))
            return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
        if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
            return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
        if (prop == PROP_CHASSIS && !valid_chassis(name))
            return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
        if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
            return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
        if (prop == PROP_LOCATION && string_has_cc(name, NULL))
            return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);

        h = strdup(name);
        if (!h)
            return -ENOMEM;

        free(c->data[prop]);
        c->data[prop] = h;
    }

    r = context_write_data_machine_info(c);
    if (r < 0) {
        log_error_errno(r, "Failed to write machine info: %m");
        return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r));
    }

    log_info("Changed %s to '%s'",
             prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
             prop == PROP_DEPLOYMENT ? "deployment" :
             prop == PROP_LOCATION ? "location" :
             prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));

    (void) sd_bus_emit_properties_changed(
        sd_bus_message_get_bus(m),
        "/org/freedesktop/hostname1",
        "org.freedesktop.hostname1",
        prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
        prop == PROP_DEPLOYMENT ? "Deployment" :
        prop == PROP_LOCATION ? "Location" :
        prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);

    return sd_bus_reply_method_return(m, NULL);
}
Esempio n. 6
0
int take_etc_passwd_lock(const char *root) {

        struct flock flock = {
                .l_type = F_WRLCK,
                .l_whence = SEEK_SET,
                .l_start = 0,
                .l_len = 0,
        };

        const char *path;
        int fd, r;

        /* This is roughly the same as lckpwdf(), but not as awful. We
         * don't want to use alarm() and signals, hence we implement
         * our own trivial version of this.
         *
         * Note that shadow-utils also takes per-database locks in
         * addition to lckpwdf(). However, we don't given that they
         * are redundant as they invoke lckpwdf() first and keep
         * it during everything they do. The per-database locks are
         * awfully racy, and thus we just won't do them. */

        if (root)
                path = prefix_roota(root, "/etc/.pwd.lock");
        else
                path = "/etc/.pwd.lock";

        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
        if (fd < 0)
                return -errno;

        r = fcntl(fd, F_SETLKW, &flock);
        if (r < 0) {
                safe_close(fd);
                return -errno;
        }

        return fd;
}

bool valid_user_group_name(const char *u) {
        const char *i;
        long sz;

        /* Checks if the specified name is a valid user/group name. */

        if (isempty(u))
                return false;

        if (!(u[0] >= 'a' && u[0] <= 'z') &&
            !(u[0] >= 'A' && u[0] <= 'Z') &&
            u[0] != '_')
                return false;

        for (i = u+1; *i; i++) {
                if (!(*i >= 'a' && *i <= 'z') &&
                    !(*i >= 'A' && *i <= 'Z') &&
                    !(*i >= '0' && *i <= '9') &&
                    *i != '_' &&
                    *i != '-')
                        return false;
        }

        sz = sysconf(_SC_LOGIN_NAME_MAX);
        assert_se(sz > 0);

        if ((size_t) (i-u) > (size_t) sz)
                return false;

        if ((size_t) (i-u) > UT_NAMESIZE - 1)
                return false;

        return true;
}

bool valid_user_group_name_or_id(const char *u) {

        /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
         * range, and not the invalid user ids. */

        if (isempty(u))
                return false;

        if (valid_user_group_name(u))
                return true;

        return parse_uid(u, NULL) >= 0;
}

bool valid_gecos(const char *d) {

        if (!d)
                return false;

        if (!utf8_is_valid(d))
                return false;

        if (string_has_cc(d, NULL))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(d, ':'))
                return false;

        return true;
}

bool valid_home(const char *p) {

        if (isempty(p))
                return false;

        if (!utf8_is_valid(p))
                return false;

        if (string_has_cc(p, NULL))
                return false;

        if (!path_is_absolute(p))
                return false;

        if (!path_is_safe(p))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(p, ':'))
                return false;

        return true;
}

int maybe_setgroups(size_t size, const gid_t *list) {
        int r;

        /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
        if (size == 0) { /* Dropping all aux groups? */
                _cleanup_free_ char *setgroups_content = NULL;
                bool can_setgroups;

                r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
                if (r == -ENOENT)
                        /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
                        can_setgroups = true;
                else if (r < 0)
                        return r;
                else
                        can_setgroups = streq(setgroups_content, "allow");

                if (!can_setgroups) {
                        log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
                        return 0;
                }
        }

        if (setgroups(size, list) < 0)
                return -errno;

        return 0;
}
Esempio n. 7
0
int take_etc_passwd_lock(const char *root) {

        struct flock flock = {
                .l_type = F_WRLCK,
                .l_whence = SEEK_SET,
                .l_start = 0,
                .l_len = 0,
        };

        const char *path;
        int fd, r;

        /* This is roughly the same as lckpwdf(), but not as awful. We
         * don't want to use alarm() and signals, hence we implement
         * our own trivial version of this.
         *
         * Note that shadow-utils also takes per-database locks in
         * addition to lckpwdf(). However, we don't given that they
         * are redundant as they invoke lckpwdf() first and keep
         * it during everything they do. The per-database locks are
         * awfully racy, and thus we just won't do them. */

        if (root)
                path = prefix_roota(root, "/etc/.pwd.lock");
        else
                path = "/etc/.pwd.lock";

        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
        if (fd < 0)
                return -errno;

        r = fcntl(fd, F_SETLKW, &flock);
        if (r < 0) {
                safe_close(fd);
                return -errno;
        }

        return fd;
}

bool valid_user_group_name(const char *u) {
        const char *i;
        long sz;

        /* Checks if the specified name is a valid user/group name. */

        if (isempty(u))
                return false;

        if (!(u[0] >= 'a' && u[0] <= 'z') &&
            !(u[0] >= 'A' && u[0] <= 'Z') &&
            u[0] != '_')
                return false;

        for (i = u+1; *i; i++) {
                if (!(*i >= 'a' && *i <= 'z') &&
                    !(*i >= 'A' && *i <= 'Z') &&
                    !(*i >= '0' && *i <= '9') &&
                    *i != '_' &&
                    *i != '-')
                        return false;
        }

        sz = sysconf(_SC_LOGIN_NAME_MAX);
        assert_se(sz > 0);

        if ((size_t) (i-u) > (size_t) sz)
                return false;

        if ((size_t) (i-u) > UT_NAMESIZE - 1)
                return false;

        return true;
}

bool valid_user_group_name_or_id(const char *u) {

        /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
         * range, and not the invalid user ids. */

        if (isempty(u))
                return false;

        if (valid_user_group_name(u))
                return true;

        return parse_uid(u, NULL) >= 0;
}

bool valid_gecos(const char *d) {

        if (!d)
                return false;

        if (!utf8_is_valid(d))
                return false;

        if (string_has_cc(d, NULL))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(d, ':'))
                return false;

        return true;
}

bool valid_home(const char *p) {

        if (isempty(p))
                return false;

        if (!utf8_is_valid(p))
                return false;

        if (string_has_cc(p, NULL))
                return false;

        if (!path_is_absolute(p))
                return false;

        if (!path_is_safe(p))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(p, ':'))
                return false;

        return true;
}