abduco

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | LICENSE

commit 2dd042f9742f7f18b61044a91effa331b56d30e0
parent 91b7338f9dbdb746b834395f1efca86066b75076
Author: Marc André Tanner <mat@brain-dump.org>
Date:   Wed, 25 Feb 2015 23:16:51 +0100

Fix access permissions on non-Linux systems

Create sockets in a per user directory with proper permissions
either in

 $HOME/.abduco/session-name

or if not possible in

 /tmp/abduco/$USER/session-name

Diffstat:
abduco.1 | 4++--
abduco.c | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
config.mk | 2+-
3 files changed, 74 insertions(+), 22 deletions(-)

diff --git a/abduco.1 b/abduco.1 @@ -60,9 +60,9 @@ is executed. By default all session related information is stored in .B $HOME/.abduco with -.BR $TMPDIR/.abduco +.BR $TMPDIR/abduco/$USER as a fallback and -.BR /tmp/.abduco +.BR /tmp/abduco/$USER as a last resort. However if a given session name represents either a relative or absolute path diff --git a/abduco.c b/abduco.c @@ -28,6 +28,7 @@ #include <termios.h> #include <time.h> #include <unistd.h> +#include <pwd.h> #include <sys/select.h> #include <sys/stat.h> #include <sys/ioctl.h> @@ -219,35 +220,90 @@ static void usage(void) { exit(EXIT_FAILURE); } -static int create_socket_dir(struct sockaddr_un *sockaddr) { +static bool xsnprintf(char *buf, size_t size, const char *fmt, ...) { + va_list ap; + if (size > INT_MAX) + return false; + va_start(ap, fmt); + int n = vsnprintf(buf, size, fmt, ap); + va_end(ap); + if (n == -1) + return false; + if (n >= size) { + errno = ENAMETOOLONG; + return false; + } + return true; +} + +static bool create_socket_dir(struct sockaddr_un *sockaddr) { + sockaddr->sun_path[0] = '\0'; + uid_t uid = getuid(); size_t maxlen = sizeof(sockaddr->sun_path); char *dirs[] = { getenv("HOME"), getenv("TMPDIR"), "/tmp" }; int socketfd = socket(AF_UNIX, SOCK_STREAM, 0); if (socketfd == -1) - return -1; + return false; + struct passwd *pw = getpwuid(uid); + if ((!dirs[0] || !dirs[0][0]) && pw) + dirs[0] = pw->pw_dir; + for (unsigned int i = 0; i < countof(dirs); i++) { char *dir = dirs[i]; + struct stat sb; + bool ishome = (i == 0); if (!dir) continue; - int len = snprintf(sockaddr->sun_path, maxlen, "%s/.%s/", dir, server.name); - if (len < 0 || (size_t)len >= maxlen) + if (!xsnprintf(sockaddr->sun_path, maxlen, "%s/%s%s/", dir, ishome ? "." : "", server.name)) continue; - if (mkdir(sockaddr->sun_path, 0750) == -1 && errno != EEXIST) + mode_t mask = umask(0); + int r = mkdir(sockaddr->sun_path, ishome ? S_IRWXU : S_IRWXU|S_IRWXG|S_IRWXO|S_ISVTX); + umask(mask); + if (r != 0 && errno != EEXIST) + continue; + if (lstat(sockaddr->sun_path, &sb) != 0) + continue; + if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + continue; + } + + size_t dirlen = strlen(sockaddr->sun_path); + if (!ishome) { + if (pw && !xsnprintf(sockaddr->sun_path+dirlen, maxlen-dirlen, "%s/", pw->pw_name)) + continue; + if (!pw && !xsnprintf(sockaddr->sun_path+dirlen, maxlen-dirlen, "%d/", uid)) + continue; + if (mkdir(sockaddr->sun_path, S_IRWXU) != 0 && errno != EEXIST) + continue; + if (lstat(sockaddr->sun_path, &sb) != 0) + continue; + if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + continue; + } + dirlen = strlen(sockaddr->sun_path); + } + + if (sb.st_uid != uid || sb.st_mode & (S_IRWXG|S_IRWXO)) { + errno = EACCES; continue; - int len2 = snprintf(sockaddr->sun_path, maxlen, "%s/.%s/.abduco-%d", dir, server.name, getpid()); - if (len2 < 0 || (size_t)len2 >= maxlen) + } + + if (!xsnprintf(sockaddr->sun_path+dirlen, maxlen-dirlen, ".abduco-%d", getpid())) continue; + socklen_t socklen = offsetof(struct sockaddr_un, sun_path) + strlen(sockaddr->sun_path) + 1; if (bind(socketfd, (struct sockaddr*)sockaddr, socklen) == -1) continue; unlink(sockaddr->sun_path); close(socketfd); - sockaddr->sun_path[len] = '\0'; - return len; + sockaddr->sun_path[dirlen] = '\0'; + return true; } close(socketfd); - return -1; + return false; } static bool set_socket_name(struct sockaddr_un *sockaddr, const char *name) { @@ -262,16 +318,12 @@ static bool set_socket_name(struct sockaddr_un *sockaddr, const char *name) { char buf[maxlen], *cwd = getcwd(buf, sizeof buf); if (!cwd) return false; - int len = snprintf(sockaddr->sun_path, maxlen, "%s/%s", cwd, name); - if (len < 0) + if (!xsnprintf(sockaddr->sun_path, maxlen, "%s/%s", cwd, name)) return false; - if ((size_t)len >= maxlen) { - errno = ENAMETOOLONG; - return false; - } } else { - int dir_len = create_socket_dir(sockaddr); - if (dir_len == -1 || dir_len + strlen(name) + strlen(server.host) >= maxlen) { + if (!create_socket_dir(sockaddr)) + return false; + if (strlen(sockaddr->sun_path) + strlen(name) + strlen(server.host) >= maxlen) { errno = ENAMETOOLONG; return false; } @@ -448,7 +500,7 @@ static int session_comparator(const struct dirent **a, const struct dirent **b) } static int list_session(void) { - if (create_socket_dir(&sockaddr) == -1) + if (!create_socket_dir(&sockaddr)) return 1; chdir(sockaddr.sun_path); struct dirent **namelist; diff --git a/config.mk b/config.mk @@ -9,7 +9,7 @@ MANPREFIX = ${PREFIX}/share/man INCS = -I. LIBS = -lc -lutil -CPPFLAGS = -D_POSIX_C_SOURCE=200809L +CPPFLAGS = -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 CFLAGS += -std=c99 -pedantic -Wall ${INCS} -DVERSION=\"${VERSION}\" -DNDEBUG ${CPPFLAGS} LDFLAGS += ${LIBS}