diff options
-rw-r--r-- | src/libudev/libudev-util.c | 101 | ||||
-rw-r--r-- | src/shared/macro.h | 21 | ||||
-rw-r--r-- | src/shared/util.c | 265 | ||||
-rw-r--r-- | src/shared/util.h | 54 | ||||
-rw-r--r-- | src/udev/udevadm.c | 2 | ||||
-rw-r--r-- | src/udev/udevd.c | 8 |
6 files changed, 290 insertions, 161 deletions
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c index 6268a8a4a7..00dc6e157e 100644 --- a/src/libudev/libudev-util.c +++ b/src/libudev/libudev-util.c @@ -48,107 +48,6 @@ * Utilities useful when dealing with devices and device node names. */ -int util_delete_path(struct udev *udev, const char *path) -{ - char p[UTIL_PATH_SIZE]; - char *pos; - int err = 0; - - if (path[0] == '/') - while(path[1] == '/') - path++; - strscpy(p, sizeof(p), path); - pos = strrchr(p, '/'); - if (pos == p || pos == NULL) - return 0; - - for (;;) { - *pos = '\0'; - pos = strrchr(p, '/'); - - /* don't remove the last one */ - if ((pos == p) || (pos == NULL)) - break; - - err = rmdir(p); - if (err < 0) { - if (errno == ENOENT) - err = 0; - break; - } - } - return err; -} - -uid_t util_lookup_user(struct udev *udev, const char *user) -{ - char *endptr; - struct passwd pwbuf; - struct passwd *pw; - uid_t uid; - size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); - char *buf; - - if (buflen == -1) - buflen = 1024; - buf = alloca(buflen); - if (streq(user, "root")) - return 0; - uid = strtoul(user, &endptr, 10); - if (endptr[0] == '\0') - return uid; - - errno = getpwnam_r(user, &pwbuf, buf, buflen, &pw); - if (pw != NULL) - return pw->pw_uid; - if (errno == 0 || errno == ENOENT || errno == ESRCH) - udev_err(udev, "specified user '%s' unknown\n", user); - else - udev_err(udev, "error resolving user '%s': %m\n", user); - return 0; -} - -gid_t util_lookup_group(struct udev *udev, const char *group) -{ - char *endptr; - struct group grbuf; - struct group *gr; - gid_t gid = 0; - size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); - char *buf = NULL; - - if (buflen == -1) - buflen = 1024; - if (streq(group, "root")) - return 0; - gid = strtoul(group, &endptr, 10); - if (endptr[0] == '\0') - return gid; - gid = 0; - for (;;) { - char *newbuf; - - newbuf = realloc(buf, buflen); - if (!newbuf) - break; - buf = newbuf; - errno = getgrnam_r(group, &grbuf, buf, buflen, &gr); - if (gr != NULL) { - gid = gr->gr_gid; - } else if (errno == ERANGE) { - buflen *= 2; - continue; - } else if (errno == 0 || errno == ENOENT || errno == ESRCH) { - udev_err(udev, "specified group '%s' unknown\n", group); - } else { - udev_err(udev, "error resolving group '%s': %m\n", group); - } - break; - } - free(buf); - return gid; -} - /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */ int util_resolve_subsys_kernel(struct udev *udev, const char *string, char *result, size_t maxsize, int read_value) 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, diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index aaf94262a8..1d2e7dd5d6 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -76,7 +76,7 @@ static int adm_help(struct udev *udev, int argc, char *argv[]) { static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) { if (cmd->debug) log_set_max_level(LOG_DEBUG); - log_debug("calling: %s\n", cmd->name); + log_debug("calling: %s", cmd->name); return cmd->cmd(udev, argc, argv); } diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 5b0db0a0b1..cd94724f54 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1060,6 +1060,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, 'V' }, {} }; + int c; assert(argc >= 0); @@ -1068,7 +1069,6 @@ static int parse_argv(int argc, char *argv[]) { while ((c = getopt_long(argc, argv, "c:de:DtN:hV", options, NULL)) >= 0) { int r; - switch (c) { case 'd': @@ -1138,7 +1138,6 @@ int main(int argc, char *argv[]) { goto exit; log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); log_open(); udev_set_log_fn(udev, udev_main_log); @@ -1163,10 +1162,7 @@ int main(int argc, char *argv[]) { label_init("/dev"); /* set umask before creating any file/directory */ - if(chdir("/")!= 0) { - log_error("unable to change into directory '/'"); - goto exit; - } + chdir("/"); umask(022); mkdir("/run/udev", 0755); |