int cg_split_spec(const char *spec, char **controller, char **path) { const char *e; char *t = NULL, *u = NULL; assert(spec); if (*spec == '/') { if (!path_is_safe(spec)) return -EINVAL; if (path) { t = strdup(spec); if (!t) return -ENOMEM; *path = t; } if (controller) *controller = NULL; return 0; } e = strchr(spec, ':'); if (!e) { if (!filename_is_safe(spec)) return -EINVAL; if (controller) { t = strdup(spec); if (!t) return -ENOMEM; *controller = t; } if (path) *path = NULL; return 0; } t = strndup(spec, e-spec); if (!t) return -ENOMEM; if (!filename_is_safe(t)) { free(t); return -EINVAL; } u = strdup(e+1); if (!u) { free(t); return -ENOMEM; } if (!path_is_safe(u)) { free(t); free(u); return -EINVAL; } if (controller) *controller = t; else free(t); if (path) *path = u; else free(u); 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; } 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; }