diff options
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/macro.h | 21 | ||||
-rw-r--r-- | src/shared/util.c | 265 | ||||
-rw-r--r-- | src/shared/util.h | 54 |
3 files changed, 287 insertions, 53 deletions
diff --git a/src/shared/macro.h b/src/shared/macro.h index e8c564837b..cea13182ec 100644 --- a/src/shared/macro.h +++ b/src/shared/macro.h @@ -37,6 +37,10 @@ #define _public_ __attribute__ ((visibility("default"))) #define _cleanup_(x) __attribute__((cleanup(x))) +/* Temporarily disable some warnings */ +#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") #define DISABLE_WARNING_FORMAT_NONLITERAL \ _Pragma("GCC diagnostic push"); \ @@ -103,6 +107,23 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ } while (false) +#if defined(static_assert) +/* static_assert() is sometimes defined in a way that trips up + * -Wdeclaration-after-statement, hence let's temporarily turn off + * this warning around it. */ +#define assert_cc(expr) \ + DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ + static_assert(expr, #expr); \ + REENABLE_WARNING +#else +#define assert_cc(expr) \ + DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ + struct CONCATENATE(_assert_struct_, __LINE__) { \ + char x[(expr) ? 0 : -1]; \ + }; \ + REENABLE_WARNING +#endif + #define PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) #define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) diff --git a/src/shared/util.c b/src/shared/util.c index bbe18ecf31..61513bade5 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -180,6 +180,34 @@ int unlink_noerrno(const char *path) { return 0; } +int parse_uid(const char *s, uid_t* ret_uid) { + unsigned long ul = 0; + uid_t uid; + int r; + + assert(s); + assert(ret_uid); + + r = safe_atolu(s, &ul); + if (r < 0) + return r; + + uid = (uid_t) ul; + + if ((unsigned long) uid != ul) + return -ERANGE; + + /* Some libc APIs use (uid_t) -1 as special placeholder */ + if (uid == (uid_t) 0xFFFFFFFF) + return -ENXIO; + + /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ + if (uid == (uid_t) 0xFFFF) + return -ENXIO; + + *ret_uid = uid; + return 0; +} int safe_atou(const char *s, unsigned *ret_u) { char *x = NULL; unsigned long l; @@ -353,6 +381,69 @@ char *strappend(const char *s, const char *suffix) { return strnappend(s, suffix, suffix ? strlen(suffix) : 0); } +int rmdir_parents(const char *path, const char *stop) { + size_t l; + int r = 0; + + assert(path); + assert(stop); + + l = strlen(path); + + /* Skip trailing slashes */ + while (l > 0 && path[l-1] == '/') + l--; + + while (l > 0) { + char *t; + + /* Skip last component */ + while (l > 0 && path[l-1] != '/') + l--; + + /* Skip trailing slashes */ + while (l > 0 && path[l-1] == '/') + l--; + + if (l <= 0) + break; + + if (!(t = strndup(path, l))) + return -ENOMEM; + + if (path_startswith(stop, t)) { + free(t); + return 0; + } + + r = rmdir(t); + free(t); + + if (r < 0) + if (errno != ENOENT) + return -errno; + } + + return 0; +} + +int dev_urandom(void *p, size_t n) { + _cleanup_close_ int fd; + ssize_t k; + + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return errno == ENOENT ? -ENOSYS : -errno; + + k = loop_read(fd, p, n, true); + if (k < 0) + return (int) k; + if ((size_t) k != n) + return -EIO; + + return 0; +} + char hexchar(int x) { static const char table[16] = "0123456789abcdef"; @@ -578,6 +669,43 @@ int flush_fd(int fd) { } } +int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { + FILE *f; + char *t; + int fd; + + assert(path); + assert(_f); + assert(_temp_path); + + t = tempfn_xxxxxx(path); + if (!t) + return -ENOMEM; + +#if HAVE_DECL_MKOSTEMP + fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC); +#else + fd = mkstemp_safe(t); + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + if (fd < 0) { + free(t); + return -errno; + } + + f = fdopen(fd, "we"); + if (!f) { + unlink(t); + free(t); + return -errno; + } + + *_f = f; + *_temp_path = t; + + return 0; +} + ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { uint8_t *p = buf; ssize_t n = 0; @@ -613,23 +741,6 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { return n; } -int dev_urandom(void *p, size_t n) { - _cleanup_close_ int fd; - ssize_t k; - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - k = loop_read(fd, p, n, true); - if (k < 0) - return (int) k; - if ((size_t) k != n) - return -EIO; - - return 0; -} - void random_bytes(void *p, size_t n) { static bool srand_called = false; uint8_t *q; @@ -755,39 +866,107 @@ bool nulstr_contains(const char*nulstr, const char *needle) { return false; } -int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { - FILE *f; - char *t; - int fd; +int get_user_creds( + const char **username, + uid_t *uid, gid_t *gid, + const char **home, + const char **shell) { - assert(path); - assert(_f); - assert(_temp_path); + struct passwd *p; + uid_t u; - t = tempfn_xxxxxx(path); - if (!t) - return -ENOMEM; + assert(username); + assert(*username); -#if HAVE_DECL_MKOSTEMP - fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC); -#else - fd = mkstemp_safe(t); - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif - if (fd < 0) { - free(t); - return -errno; + /* We enforce some special rules for uid=0: in order to avoid + * NSS lookups for root we hardcode its data. */ + + if (streq(*username, "root") || streq(*username, "0")) { + *username = "root"; + + if (uid) + *uid = 0; + + if (gid) + *gid = 0; + + if (home) + *home = "/root"; + + if (shell) + *shell = "/bin/sh"; + + return 0; } - f = fdopen(fd, "we"); - if (!f) { - unlink(t); - free(t); - return -errno; + if (parse_uid(*username, &u) >= 0) { + errno = 0; + p = getpwuid(u); + + /* If there are multiple users with the same id, make + * sure to leave $USER to the configured value instead + * of the first occurrence in the database. However if + * the uid was configured by a numeric uid, then let's + * pick the real username from /etc/passwd. */ + if (p) + *username = p->pw_name; + } else { + errno = 0; + p = getpwnam(*username); } - *_f = f; - *_temp_path = t; + if (!p) + return errno > 0 ? -errno : -ESRCH; + + if (uid) + *uid = p->pw_uid; + + if (gid) + *gid = p->pw_gid; + + if (home) + *home = p->pw_dir; + + if (shell) + *shell = p->pw_shell; + + return 0; +} + +int get_group_creds(const char **groupname, gid_t *gid) { + struct group *g; + gid_t id; + + assert(groupname); + + /* We enforce some special rules for gid=0: in order to avoid + * NSS lookups for root we hardcode its data. */ + + if (streq(*groupname, "root") || streq(*groupname, "0")) { + *groupname = "root"; + + if (gid) + *gid = 0; + + return 0; + } + + if (parse_gid(*groupname, &id) >= 0) { + errno = 0; + g = getgrgid(id); + + if (g) + *groupname = g->gr_name; + } else { + errno = 0; + g = getgrnam(*groupname); + } + + if (!g) + return errno > 0 ? -errno : -ESRCH; + + if (gid) + *gid = g->gr_gid; return 0; } diff --git a/src/shared/util.h b/src/shared/util.h index d5c6705497..60ea971dc3 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -132,6 +132,8 @@ char *endswith(const char *s, const char *postfix) _pure_; int close_nointr(int fd); int safe_close(int fd); +int parse_uid(const char *s, uid_t* ret_uid); +#define parse_gid(s, ret_uid) parse_uid(s, ret_uid) int safe_atou(const char *s, unsigned *ret_u); int safe_atoi(const char *s, int *ret_i); @@ -139,6 +141,31 @@ int safe_atoi(const char *s, int *ret_i); int safe_atollu(const char *s, unsigned long long *ret_u); int safe_atolli(const char *s, long long int *ret_i); + +#if __WORDSIZE == 32 +static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned)); + return safe_atou(s, (unsigned*) ret_u); +} +static inline int safe_atoli(const char *s, long int *ret_u) { + assert_cc(sizeof(long int) == sizeof(int)); + return safe_atoi(s, (int*) ret_u); +} +#else +static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +} +static inline int safe_atoli(const char *s, long int *ret_u) { + assert_cc(sizeof(long int) == sizeof(long long int)); + return safe_atolli(s, (long long int*) ret_u); +} +#endif + +static inline int safe_atou64(const char *s, uint64_t *ret_u) { + assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +} const char* split(const char **state, size_t *l, const char *separator, bool quoted); #define FOREACH_WORD_QUOTED(word, length, s, state) \ @@ -152,6 +179,7 @@ char *strnappend(const char *s, const char *suffix, size_t length); char *truncate_nl(char *s); +int rmdir_parents(const char *path, const char *stop); char hexchar(int x) _const_; char octchar(int x) _const_; @@ -235,6 +263,9 @@ int null_or_empty_fd(int fd); bool nulstr_contains(const char*nulstr, const char *needle); +int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); +int get_group_creds(const char **groupname, gid_t *gid); + char *strjoin(const char *x, ...) _sentinel_; bool is_main_thread(void); @@ -338,16 +369,19 @@ static inline void _reset_errno_(int *saved_errno) { int unlink_noerrno(const char *path); -#define strappenda(a, b) \ - ({ \ - const char *_a_ = (a), *_b_ = (b); \ - char *_c_; \ - size_t _x_, _y_; \ - _x_ = strlen(_a_); \ - _y_ = strlen(_b_); \ - _c_ = alloca(_x_ + _y_ + 1); \ - strcpy(stpcpy(_c_, _a_), _b_); \ - _c_; \ +#define strappenda(a, ...) \ + ({ \ + int _len = strlen(a); \ + unsigned _i; \ + char *_d_, *_p_; \ + const char *_appendees_[] = { __VA_ARGS__ }; \ + for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \ + _len += strlen(_appendees_[_i]); \ + _d_ = alloca(_len + 1); \ + _p_ = stpcpy(_d_, a); \ + for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \ + _p_ = stpcpy(_p_, _appendees_[_i]); \ + _d_; \ }) static inline void qsort_safe(void *base, size_t nmemb, size_t size, |