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")); }
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); }
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); }
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; }
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); }
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; }
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; }