diff options
Diffstat (limited to 'src/shared/util.c')
-rw-r--r-- | src/shared/util.c | 265 |
1 files changed, 222 insertions, 43 deletions
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; } |