From 7c279a22551935244f392540f0005cc80b121665 Mon Sep 17 00:00:00 2001 From: Sergey Pinaev Date: Wed, 12 Sep 2018 19:34:48 +0200 Subject: [PATCH 01/10] Get process information from linux /proc Commit represents sslsplit-0.5.3-linux-pid-privsep.patch submitted by e-mail message <20180912115150.be832d62518edb080019e25b@antex.ru>. --- main.c | 4 + privsep.c | 341 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ privsep.h | 5 + proc.c | 30 +++++ proc.h | 13 ++- 5 files changed, 391 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 25ea86a7..9c972246 100644 --- a/main.c +++ b/main.c @@ -826,6 +826,10 @@ main(int argc, char *argv[]) goto out_sslreinit_failed; } +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) + linux_proc_init(clisock[1]); +#endif + /* Post-privdrop/chroot/detach initialization, thread spawning */ if (log_init(opts, proxy, &clisock[1]) == -1) { fprintf(stderr, "%s: failed to init log facility: %s\n", diff --git a/privsep.c b/privsep.c index c049cd3c..6ed4a60a 100644 --- a/privsep.c +++ b/privsep.c @@ -58,13 +58,21 @@ /* maximal message sizes */ #define PRIVSEP_MAX_REQ_SIZE 512 /* arbitrary limit */ +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#define PRIVSEP_MAX_ANS_SIZE PATH_MAX + 255 +#else #define PRIVSEP_MAX_ANS_SIZE (1+sizeof(int)) +#endif /* command byte */ #define PRIVSEP_REQ_CLOSE 0 /* closing command socket */ #define PRIVSEP_REQ_OPENFILE 1 /* open content log file */ #define PRIVSEP_REQ_OPENFILE_P 2 /* open content log file w/mkpath */ #define PRIVSEP_REQ_OPENSOCK 3 /* open socket and pass fd */ #define PRIVSEP_REQ_CERTFILE 4 /* open cert file in certgendir */ +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#define PRIVSEP_REQ_GETPID 5 /* find pid for address */ +#define PRIVSEP_REQ_GETINFO 6 /* get info for pid */ +#endif /* response byte */ #define PRIVSEP_ANS_SUCCESS 0 /* success */ @@ -308,6 +316,193 @@ privsep_server_certfile(const char *fn) return fd; } +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) + +#include +#include +#include +#include + +enum { ST_START, ST_NUM, ST_BEFORE_IP, ST_LOCAL_IP, ST_LOCAL_PORT }; + +int ishex(int c) { + return (isdigit(c) != 0 || (c >= 65 && c <= 70) || (c >= 97 && c <= 102)) ? 1 : 0; +} + +int hextoi(int c) { + if(c >= 65 && c <= 70) { + return c - 55; + } + if(c >= 97 && c <= 102) { + return c - 87; + } + return c - 48; +} + +pid_t find_pid(ino_t inode) +{ + DIR *d = opendir("/proc"), *dfd; + struct dirent entry, *res; + struct stat sbuf; + char *ep, fdn[1024], fdf[1024]; + pid_t pid; + + if(d != NULL) { + while(1) { + readdir_r(d, &entry, &res); + if(res == NULL) { + break; + } + if(entry.d_type == DT_DIR) { + pid = strtol(entry.d_name, &ep, 10); + if(ep[0] == '\0') { + snprintf(fdn, sizeof(fdn), "/proc/%s/fd", entry.d_name); + dfd = opendir(fdn); + if(dfd != NULL) { + while(1) { + readdir_r(dfd, &entry, &res); + if(res == NULL) { + break; + } + snprintf(fdf, sizeof(fdf), "%s/%s", fdn, entry.d_name); + if(stat(fdf, &sbuf) == 0) { + if((sbuf.st_mode & S_IFMT) == S_IFSOCK && sbuf.st_ino == inode) { + closedir(dfd); + closedir(d); + return pid; + } + } + } + closedir(dfd); + } + } + } + } + closedir(d); + } + return -1; +} + +static pid_t WUNRES +privsep_server_get_pid(uint32_t src_addr, in_port_t src_port) +{ + int fd = open("/proc/net/tcp", O_RDONLY); + if (fd != -1) { + char bufc[4096], inode[64]; + char *buf; + int i, state, sh = 0; + uint32_t s_addr; + in_port_t sin_port = 0; + char c; + FILE *file = fdopen(fd, "r"); + + if(file != NULL) { + while(fgets(bufc, sizeof(bufc), file) != NULL) { + state = ST_START; + buf = (char *)&bufc; + for(c = *buf; c != 0; buf++, c = *buf) { + if(state == ST_START) { + if(c == ' ') { + continue; + } + if(isdigit(c) == 0) { + break; + } + state = ST_NUM; + } else if(state == ST_NUM) { + if(c == ':') { + state = ST_BEFORE_IP; + continue; + } + if(isdigit(c) == 0) { + break; + } + } else if(state == ST_BEFORE_IP) { + if(c == ' ') { + continue; + } + if(ishex(c) != 0) { + state = ST_LOCAL_IP; + sh = 24; + s_addr = hextoi(c) << 28; + continue; + } + break; + } else if(state == ST_LOCAL_IP) { + if(c == ':') { + state = ST_LOCAL_PORT; + sin_port = 0; + sh = 12; + continue; + } + if(ishex(c) != 0) { + s_addr += (hextoi(c) << sh); + sh -= 4; + continue; + } + break; + } else if(state == ST_LOCAL_PORT) { + if(c == ' ') { + if(s_addr == src_addr && htons(sin_port) == src_port) { + /* find inode */ + buf += 72; + for(c = *buf, i = 0; c != 0; i++, buf++, c = *buf) { + if(c == ' ') { + inode[i] = 0; + break; + } + if(isdigit(c) == 0) { + inode[0] = 0; + break; + } + inode[i] = c; + } + if(inode[0] == 0) { + break; + } + return find_pid(atoll(inode)); + } + break; + } + if(ishex(c) != 0) { + sin_port += (hextoi(c) << sh); + sh -= 4; + continue; + } + break; + } + } + } + fclose(file); + } + } + return -1; +} + +static size_t WUNRES +privsep_server_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { + struct stat sbuf; + char dn[1024], exe[PATH_MAX + 1]; + ssize_t n; + + snprintf(dn, sizeof(dn), "/proc/%ld", (long)pid); + if(stat(dn, &sbuf) == 0) { + *uid = sbuf.st_uid; + *gid = sbuf.st_gid; + snprintf(dn, sizeof(dn), "/proc/%ld/exe", (long)pid); + n = readlink(dn, exe, sizeof(exe)); + if(n != -1) { + exe[n < PATH_MAX ? n : PATH_MAX] = 0; + *path = strdup(exe); + return n < PATH_MAX ? n : PATH_MAX; + } + } + + return 0; +} + +#endif + /* * Handle a single request on a readable server socket. * Returns 0 on success, 1 on EOF and -1 on error. @@ -341,6 +536,44 @@ privsep_server_handle_req(opts_t *opts, int srvsock) /* client indicates EOF through close message */ return 1; } +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) + case PRIVSEP_REQ_GETPID: { + ans[0] = PRIVSEP_ANS_SUCCESS; + *(pid_t *)(&ans[1]) = privsep_server_get_pid(*(uint32_t *)(&req[1]), + *(in_port_t *)(&req[1 + sizeof(uint32_t)])); + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(pid_t), -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } + case PRIVSEP_REQ_GETINFO: { + char *path; + uid_t uid; + gid_t gid; + size_t anssz = 1, plen; + + ans[0] = PRIVSEP_ANS_SUCCESS; + if((plen = privsep_server_get_info(*(pid_t *)(&req[1]), &path, &uid, &gid)) != 0) { + anssz = 1 + sizeof(size_t) + plen + sizeof(uid_t) + sizeof(gid_t); + *(size_t *)(&ans[1]) = plen; + memcpy((void *)(ans + 1 + sizeof(size_t)), (void *)path, plen); + free(path); + *(uid_t *)(&ans[1 + sizeof(size_t) + plen]) = uid; + *(gid_t *)(&ans[1 + sizeof(size_t) + plen] + sizeof(gid_t)) = gid; + } else { + *(size_t *)(&ans[1]) = 0; + anssz = 1 + sizeof(size_t); + } + if (sys_sendmsgfd(srvsock, ans, anssz, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } +#endif case PRIVSEP_REQ_OPENFILE_P: mkpath = 1; /* fall through */ @@ -847,6 +1080,114 @@ privsep_client_close(int clisock) return 0; } +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) + +pid_t +privsep_client_get_pid(int clisock, uint32_t src_addr, in_port_t src_port) +{ + char req[sizeof(uint32_t) + sizeof(in_port_t) + 1]; + char ans[PRIVSEP_MAX_ANS_SIZE]; + ssize_t n; + int fd = -1; + + req[0] = PRIVSEP_REQ_GETPID; + *(uint32_t *)(&req[1]) = src_addr; + *(in_port_t *)(&req[1 + sizeof(uint32_t)]) = src_port; + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + return -1; + } + + if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + return -1; + } + + if (n < 1) { + errno = EINVAL; + return -1; + } + + switch (ans[0]) { + case PRIVSEP_ANS_SUCCESS: + return *(pid_t*)(&ans[1]); + case PRIVSEP_ANS_DENIED: + errno = EACCES; + return -1; + case PRIVSEP_ANS_SYS_ERR: + if (n < (ssize_t)(1 + sizeof(int))) { + errno = EINVAL; + return -1; + } + errno = *((int*)&ans[1]); + return -1; + case PRIVSEP_ANS_UNK_CMD: + case PRIVSEP_ANS_INVALID: + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +char* +privsep_client_get_info(int clisock, pid_t pid, uid_t *uid, gid_t *gid) +{ + char req[sizeof(pid_t) + 1]; + char ans[PRIVSEP_MAX_ANS_SIZE]; + size_t plen; + char *exe; + ssize_t n; + int fd = -1; + + req[0] = PRIVSEP_REQ_GETINFO; + *(pid_t *)(&req[1]) = pid; + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + return NULL; + } + + if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + return NULL; + } + + if (n < 1) { + errno = EINVAL; + return NULL; + } + + switch (ans[0]) { + case PRIVSEP_ANS_SUCCESS: + plen = *(size_t *)(&ans[1]); + exe = malloc(plen + 1); + if(exe != NULL) { + memcpy((void *)exe, (void *)(ans + 1 + sizeof(size_t)), plen); + exe[plen] = 0; + *uid = *(uid_t *)(&ans[1 + sizeof(size_t) + plen]); + *gid = *(gid_t *)(&ans[1 + sizeof(size_t) + sizeof(uid_t) + plen]); + return exe; + } + errno = ENOMEM; + return NULL; + case PRIVSEP_ANS_DENIED: + errno = EACCES; + return NULL; + case PRIVSEP_ANS_SYS_ERR: + if (n < (ssize_t)(1 + sizeof(int))) { + errno = EINVAL; + return NULL; + } + errno = *((int*)&ans[1]); + return NULL; + case PRIVSEP_ANS_UNK_CMD: + case PRIVSEP_ANS_INVALID: + default: + errno = EINVAL; + return NULL; + } + + return 0; +} +#endif + /* * Fork and set up privilege separated monitor process. * Returns -1 on error before forking, 1 as parent, or 0 as child. diff --git a/privsep.h b/privsep.h index b59dab44..7047a88b 100644 --- a/privsep.h +++ b/privsep.h @@ -39,6 +39,11 @@ int privsep_client_opensock(int, const proxyspec_t *spec); int privsep_client_certfile(int, const char *); int privsep_client_close(int); +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +pid_t privsep_client_get_pid(int, uint32_t, in_port_t); +char* privsep_client_get_info(int , pid_t, uid_t*, gid_t*); +#endif + #endif /* !PRIVSEP_H */ /* vim: set noet ft=c: */ diff --git a/proc.c b/proc.c index 234875d1..2d2bb7ac 100644 --- a/proc.c +++ b/proc.c @@ -456,6 +456,36 @@ proc_darwin_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { #endif /* HAVE_DARWIN_LIBPROC */ +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#include "privsep.h" + +static int proc_clisock = -1; /* privsep client socket for process info */ + +void linux_proc_init(int clisock) { + proc_clisock = clisock; +} + +int +proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, + UNUSED socklen_t src_addrlen) +{ + if (src_addr->sa_family == AF_INET) { + struct sockaddr_in *src_sai = + (struct sockaddr_in *)src_addr; + *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); + return 0; + } + return -1; +} + +int +proc_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { + *path = privsep_client_get_info(proc_clisock, pid, uid, gid); + return path == NULL ? -1 : 0; +} + +#endif + /* vim: set noet ft=c: */ diff --git a/proc.h b/proc.h index 232a416c..0430de2b 100644 --- a/proc.h +++ b/proc.h @@ -36,9 +36,7 @@ #include -#if defined(HAVE_DARWIN_LIBPROC) || defined(__FreeBSD__) #define HAVE_LOCAL_PROCINFO -#endif #ifdef HAVE_DARWIN_LIBPROC #ifndef LOCAL_PROCINFO_STR @@ -60,6 +58,17 @@ int proc_freebsd_pid_for_addr(pid_t *, struct sockaddr *, socklen_t) WUNRES NONN int proc_freebsd_get_info(pid_t, char **, uid_t *, gid_t *) WUNRES NONNULL(2,3,4); #endif /* __FreeBSD__ */ +#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#ifndef LOCAL_PROCINFO_STR +#define LOCAL_PROCINFO_STR "Linux procfs" +#define proc_pid_for_addr(a,b,c) proc_linux_pid_for_addr(a,b,c) +#define proc_get_info(a,b,c,d) proc_linux_get_info(a,b,c,d) +#endif /* LOCAL_PROCINFO_STR */ +void linux_proc_init(int); +int proc_linux_pid_for_addr(pid_t *, struct sockaddr *, socklen_t) WUNRES NONNULL(1,2); +int proc_linux_get_info(pid_t, char **, uid_t *, gid_t *) WUNRES NONNULL(2,3,4); +#endif /* HAVE_DARWIN_LIBPROC */ + #endif /* !PROC_H */ /* vim: set noet ft=c: */ From fbd73acfedb02298e94d578012f833d1ac97962d Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Wed, 12 Sep 2018 21:09:01 +0200 Subject: [PATCH 02/10] Use __linux__ and fix whitespace --- main.c | 6 +- privsep.c | 607 +++++++++++++++++++++++++++--------------------------- privsep.h | 6 +- proc.c | 35 ++-- proc.h | 10 +- 5 files changed, 333 insertions(+), 331 deletions(-) diff --git a/main.c b/main.c index 9c972246..b1d169aa 100644 --- a/main.c +++ b/main.c @@ -826,9 +826,9 @@ main(int argc, char *argv[]) goto out_sslreinit_failed; } -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) - linux_proc_init(clisock[1]); -#endif +#ifdef __linux__ + proc_linux_init(clisock[1]); +#endif /* __linux__ */ /* Post-privdrop/chroot/detach initialization, thread spawning */ if (log_init(opts, proxy, &clisock[1]) == -1) { diff --git a/privsep.c b/privsep.c index 6ed4a60a..7f0e3e72 100644 --- a/privsep.c +++ b/privsep.c @@ -58,21 +58,22 @@ /* maximal message sizes */ #define PRIVSEP_MAX_REQ_SIZE 512 /* arbitrary limit */ -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#ifdef __linux__ #define PRIVSEP_MAX_ANS_SIZE PATH_MAX + 255 -#else +#else /* !__linux__ */ #define PRIVSEP_MAX_ANS_SIZE (1+sizeof(int)) -#endif +#endif /* !__linux__ */ + /* command byte */ #define PRIVSEP_REQ_CLOSE 0 /* closing command socket */ #define PRIVSEP_REQ_OPENFILE 1 /* open content log file */ #define PRIVSEP_REQ_OPENFILE_P 2 /* open content log file w/mkpath */ #define PRIVSEP_REQ_OPENSOCK 3 /* open socket and pass fd */ #define PRIVSEP_REQ_CERTFILE 4 /* open cert file in certgendir */ -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#ifdef __linux__ #define PRIVSEP_REQ_GETPID 5 /* find pid for address */ #define PRIVSEP_REQ_GETINFO 6 /* get info for pid */ -#endif +#endif /* __linux__ */ /* response byte */ #define PRIVSEP_ANS_SUCCESS 0 /* success */ @@ -316,8 +317,7 @@ privsep_server_certfile(const char *fn) return fd; } -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) - +#ifdef __linux__ #include #include #include @@ -325,183 +325,185 @@ privsep_server_certfile(const char *fn) enum { ST_START, ST_NUM, ST_BEFORE_IP, ST_LOCAL_IP, ST_LOCAL_PORT }; -int ishex(int c) { - return (isdigit(c) != 0 || (c >= 65 && c <= 70) || (c >= 97 && c <= 102)) ? 1 : 0; +static int +ishex(int c) +{ + return (isdigit(c) != 0 || (c >= 65 && c <= 70) || (c >= 97 && c <= 102)) ? 1 : 0; } -int hextoi(int c) { - if(c >= 65 && c <= 70) { - return c - 55; - } - if(c >= 97 && c <= 102) { - return c - 87; - } - return c - 48; +static int +hextoi(int c) +{ + if (c >= 65 && c <= 70) { + return c - 55; + } + if (c >= 97 && c <= 102) { + return c - 87; + } + return c - 48; } pid_t find_pid(ino_t inode) { - DIR *d = opendir("/proc"), *dfd; - struct dirent entry, *res; - struct stat sbuf; - char *ep, fdn[1024], fdf[1024]; - pid_t pid; - - if(d != NULL) { - while(1) { - readdir_r(d, &entry, &res); - if(res == NULL) { - break; - } - if(entry.d_type == DT_DIR) { - pid = strtol(entry.d_name, &ep, 10); - if(ep[0] == '\0') { - snprintf(fdn, sizeof(fdn), "/proc/%s/fd", entry.d_name); - dfd = opendir(fdn); - if(dfd != NULL) { - while(1) { - readdir_r(dfd, &entry, &res); - if(res == NULL) { - break; - } - snprintf(fdf, sizeof(fdf), "%s/%s", fdn, entry.d_name); - if(stat(fdf, &sbuf) == 0) { - if((sbuf.st_mode & S_IFMT) == S_IFSOCK && sbuf.st_ino == inode) { - closedir(dfd); - closedir(d); - return pid; - } - } - } - closedir(dfd); - } - } - } - } - closedir(d); - } - return -1; + DIR *d = opendir("/proc"), *dfd; + struct dirent entry, *res; + struct stat sbuf; + char *ep, fdn[1024], fdf[1024]; + pid_t pid; + + if (d != NULL) { + while(1) { + readdir_r(d, &entry, &res); + if (res == NULL) { + break; + } + if (entry.d_type == DT_DIR) { + pid = strtol(entry.d_name, &ep, 10); + if (ep[0] == '\0') { + snprintf(fdn, sizeof(fdn), "/proc/%s/fd", entry.d_name); + dfd = opendir(fdn); + if (dfd != NULL) { + while(1) { + readdir_r(dfd, &entry, &res); + if (res == NULL) { + break; + } + snprintf(fdf, sizeof(fdf), "%s/%s", fdn, entry.d_name); + if (stat(fdf, &sbuf) == 0) { + if ((sbuf.st_mode & S_IFMT) == S_IFSOCK && sbuf.st_ino == inode) { + closedir(dfd); + closedir(d); + return pid; + } + } + } + closedir(dfd); + } + } + } + } + closedir(d); + } + return -1; } static pid_t WUNRES privsep_server_get_pid(uint32_t src_addr, in_port_t src_port) { - int fd = open("/proc/net/tcp", O_RDONLY); - if (fd != -1) { - char bufc[4096], inode[64]; - char *buf; - int i, state, sh = 0; - uint32_t s_addr; - in_port_t sin_port = 0; - char c; - FILE *file = fdopen(fd, "r"); - - if(file != NULL) { - while(fgets(bufc, sizeof(bufc), file) != NULL) { - state = ST_START; - buf = (char *)&bufc; - for(c = *buf; c != 0; buf++, c = *buf) { - if(state == ST_START) { - if(c == ' ') { - continue; - } - if(isdigit(c) == 0) { - break; - } - state = ST_NUM; - } else if(state == ST_NUM) { - if(c == ':') { - state = ST_BEFORE_IP; - continue; - } - if(isdigit(c) == 0) { - break; - } - } else if(state == ST_BEFORE_IP) { - if(c == ' ') { - continue; - } - if(ishex(c) != 0) { - state = ST_LOCAL_IP; - sh = 24; - s_addr = hextoi(c) << 28; - continue; - } - break; - } else if(state == ST_LOCAL_IP) { - if(c == ':') { - state = ST_LOCAL_PORT; - sin_port = 0; - sh = 12; - continue; - } - if(ishex(c) != 0) { - s_addr += (hextoi(c) << sh); - sh -= 4; - continue; - } - break; - } else if(state == ST_LOCAL_PORT) { - if(c == ' ') { - if(s_addr == src_addr && htons(sin_port) == src_port) { - /* find inode */ - buf += 72; - for(c = *buf, i = 0; c != 0; i++, buf++, c = *buf) { - if(c == ' ') { - inode[i] = 0; - break; - } - if(isdigit(c) == 0) { - inode[0] = 0; - break; - } - inode[i] = c; - } - if(inode[0] == 0) { - break; - } - return find_pid(atoll(inode)); - } - break; - } - if(ishex(c) != 0) { - sin_port += (hextoi(c) << sh); - sh -= 4; - continue; - } - break; - } - } - } - fclose(file); - } - } - return -1; + int fd = open("/proc/net/tcp", O_RDONLY); + if (fd != -1) { + char bufc[4096], inode[64]; + char *buf; + int i, state, sh = 0; + uint32_t s_addr; + in_port_t sin_port = 0; + char c; + FILE *file = fdopen(fd, "r"); + + if (file != NULL) { + while (fgets(bufc, sizeof(bufc), file) != NULL) { + state = ST_START; + buf = (char *)&bufc; + for (c = *buf; c != 0; buf++, c = *buf) { + if (state == ST_START) { + if (c == ' ') { + continue; + } + if (isdigit(c) == 0) { + break; + } + state = ST_NUM; + } else if (state == ST_NUM) { + if (c == ':') { + state = ST_BEFORE_IP; + continue; + } + if (isdigit(c) == 0) { + break; + } + } else if (state == ST_BEFORE_IP) { + if (c == ' ') { + continue; + } + if (ishex(c) != 0) { + state = ST_LOCAL_IP; + sh = 24; + s_addr = hextoi(c) << 28; + continue; + } + break; + } else if (state == ST_LOCAL_IP) { + if (c == ':') { + state = ST_LOCAL_PORT; + sin_port = 0; + sh = 12; + continue; + } + if (ishex(c) != 0) { + s_addr += (hextoi(c) << sh); + sh -= 4; + continue; + } + break; + } else if (state == ST_LOCAL_PORT) { + if (c == ' ') { + if (s_addr == src_addr && htons(sin_port) == src_port) { + /* find inode */ + buf += 72; + for (c = *buf, i = 0; c != 0; i++, buf++, c = *buf) { + if (c == ' ') { + inode[i] = 0; + break; + } + if (isdigit(c) == 0) { + inode[0] = 0; + break; + } + inode[i] = c; + } + if (inode[0] == 0) { + break; + } + return find_pid(atoll(inode)); + } + break; + } + if (ishex(c) != 0) { + sin_port += (hextoi(c) << sh); + sh -= 4; + continue; + } + break; + } + } + } + fclose(file); + } + } + return -1; } static size_t WUNRES privsep_server_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { - struct stat sbuf; - char dn[1024], exe[PATH_MAX + 1]; - ssize_t n; - - snprintf(dn, sizeof(dn), "/proc/%ld", (long)pid); - if(stat(dn, &sbuf) == 0) { - *uid = sbuf.st_uid; - *gid = sbuf.st_gid; - snprintf(dn, sizeof(dn), "/proc/%ld/exe", (long)pid); - n = readlink(dn, exe, sizeof(exe)); - if(n != -1) { - exe[n < PATH_MAX ? n : PATH_MAX] = 0; - *path = strdup(exe); - return n < PATH_MAX ? n : PATH_MAX; - } - } - - return 0; -} + struct stat sbuf; + char dn[1024], exe[PATH_MAX + 1]; + ssize_t n; -#endif + snprintf(dn, sizeof(dn), "/proc/%ld", (long)pid); + if (stat(dn, &sbuf) == 0) { + *uid = sbuf.st_uid; + *gid = sbuf.st_gid; + snprintf(dn, sizeof(dn), "/proc/%ld/exe", (long)pid); + n = readlink(dn, exe, sizeof(exe)); + if (n != -1) { + exe[n < PATH_MAX ? n : PATH_MAX] = 0; + *path = strdup(exe); + return n < PATH_MAX ? n : PATH_MAX; + } + } + return -1; +} +#endif /* __linux__ */ /* * Handle a single request on a readable server socket. @@ -536,44 +538,44 @@ privsep_server_handle_req(opts_t *opts, int srvsock) /* client indicates EOF through close message */ return 1; } -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) - case PRIVSEP_REQ_GETPID: { - ans[0] = PRIVSEP_ANS_SUCCESS; - *(pid_t *)(&ans[1]) = privsep_server_get_pid(*(uint32_t *)(&req[1]), - *(in_port_t *)(&req[1 + sizeof(uint32_t)])); - if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(pid_t), -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } - case PRIVSEP_REQ_GETINFO: { - char *path; - uid_t uid; - gid_t gid; - size_t anssz = 1, plen; - - ans[0] = PRIVSEP_ANS_SUCCESS; - if((plen = privsep_server_get_info(*(pid_t *)(&req[1]), &path, &uid, &gid)) != 0) { - anssz = 1 + sizeof(size_t) + plen + sizeof(uid_t) + sizeof(gid_t); - *(size_t *)(&ans[1]) = plen; - memcpy((void *)(ans + 1 + sizeof(size_t)), (void *)path, plen); - free(path); - *(uid_t *)(&ans[1 + sizeof(size_t) + plen]) = uid; - *(gid_t *)(&ans[1 + sizeof(size_t) + plen] + sizeof(gid_t)) = gid; - } else { - *(size_t *)(&ans[1]) = 0; - anssz = 1 + sizeof(size_t); - } - if (sys_sendmsgfd(srvsock, ans, anssz, -1) == -1) { - log_err_printf("Sending message failed: %s (%i" - ")\n", strerror(errno), errno); - return -1; - } - return 0; - } -#endif +#ifdef __linux__ + case PRIVSEP_REQ_GETPID: { + ans[0] = PRIVSEP_ANS_SUCCESS; + *(pid_t *)(&ans[1]) = privsep_server_get_pid(*(uint32_t *)(&req[1]), + *(in_port_t *)(&req[1 + sizeof(uint32_t)])); + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(pid_t), -1) == -1) { + log_err_printf("Sending message failed: %s (%i)\n", + strerror(errno), errno); + return -1; + } + return 0; + } + case PRIVSEP_REQ_GETINFO: { + char *path; + uid_t uid; + gid_t gid; + size_t anssz = 1, plen; + + ans[0] = PRIVSEP_ANS_SUCCESS; + if ((plen = privsep_server_get_info(*(pid_t *)(&req[1]), &path, &uid, &gid)) != 0) { + anssz = 1 + sizeof(size_t) + plen + sizeof(uid_t) + sizeof(gid_t); + *(size_t *)(&ans[1]) = plen; + memcpy((void *)(ans + 1 + sizeof(size_t)), (void *)path, plen); + free(path); + *(uid_t *)(&ans[1 + sizeof(size_t) + plen]) = uid; + *(gid_t *)(&ans[1 + sizeof(size_t) + plen] + sizeof(gid_t)) = gid; + } else { + *(size_t *)(&ans[1]) = 0; + anssz = 1 + sizeof(size_t); + } + if (sys_sendmsgfd(srvsock, ans, anssz, -1) == -1) { + log_err_printf("Sending message failed: %s (%i)\n", + strerror(errno), errno); + return -1; + } + return 0; + } +#endif /* __linux__ */ case PRIVSEP_REQ_OPENFILE_P: mkpath = 1; /* fall through */ @@ -1080,113 +1082,112 @@ privsep_client_close(int clisock) return 0; } -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) - +#ifdef __linux__ pid_t privsep_client_get_pid(int clisock, uint32_t src_addr, in_port_t src_port) { - char req[sizeof(uint32_t) + sizeof(in_port_t) + 1]; - char ans[PRIVSEP_MAX_ANS_SIZE]; - ssize_t n; - int fd = -1; - - req[0] = PRIVSEP_REQ_GETPID; - *(uint32_t *)(&req[1]) = src_addr; - *(in_port_t *)(&req[1 + sizeof(uint32_t)]) = src_port; - if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { - return -1; - } - - if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + char req[sizeof(uint32_t) + sizeof(in_port_t) + 1]; + char ans[PRIVSEP_MAX_ANS_SIZE]; + ssize_t n; + int fd = -1; + + req[0] = PRIVSEP_REQ_GETPID; + *(uint32_t *)(&req[1]) = src_addr; + *(in_port_t *)(&req[1 + sizeof(uint32_t)]) = src_port; + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + return -1; + } + + if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + return -1; + } + + if (n < 1) { + errno = EINVAL; return -1; - } - - if (n < 1) { - errno = EINVAL; - return -1; - } - - switch (ans[0]) { - case PRIVSEP_ANS_SUCCESS: - return *(pid_t*)(&ans[1]); - case PRIVSEP_ANS_DENIED: - errno = EACCES; - return -1; - case PRIVSEP_ANS_SYS_ERR: - if (n < (ssize_t)(1 + sizeof(int))) { - errno = EINVAL; - return -1; - } - errno = *((int*)&ans[1]); - return -1; - case PRIVSEP_ANS_UNK_CMD: - case PRIVSEP_ANS_INVALID: - default: - errno = EINVAL; - return -1; - } - - return 0; + } + + switch (ans[0]) { + case PRIVSEP_ANS_SUCCESS: + return *(pid_t*)(&ans[1]); + case PRIVSEP_ANS_DENIED: + errno = EACCES; + return -1; + case PRIVSEP_ANS_SYS_ERR: + if (n < (ssize_t)(1 + sizeof(int))) { + errno = EINVAL; + return -1; + } + errno = *((int*)&ans[1]); + return -1; + case PRIVSEP_ANS_UNK_CMD: + case PRIVSEP_ANS_INVALID: + default: + errno = EINVAL; + return -1; + } + + return 0; } -char* +char * privsep_client_get_info(int clisock, pid_t pid, uid_t *uid, gid_t *gid) { - char req[sizeof(pid_t) + 1]; - char ans[PRIVSEP_MAX_ANS_SIZE]; - size_t plen; - char *exe; - ssize_t n; - int fd = -1; - - req[0] = PRIVSEP_REQ_GETINFO; - *(pid_t *)(&req[1]) = pid; - if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { - return NULL; - } - - if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + char req[sizeof(pid_t) + 1]; + char ans[PRIVSEP_MAX_ANS_SIZE]; + size_t plen; + char *exe; + ssize_t n; + int fd = -1; + + req[0] = PRIVSEP_REQ_GETINFO; + *(pid_t *)(&req[1]) = pid; + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + return NULL; + } + + if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { return NULL; - } - - if (n < 1) { - errno = EINVAL; - return NULL; - } - - switch (ans[0]) { - case PRIVSEP_ANS_SUCCESS: - plen = *(size_t *)(&ans[1]); - exe = malloc(plen + 1); - if(exe != NULL) { - memcpy((void *)exe, (void *)(ans + 1 + sizeof(size_t)), plen); - exe[plen] = 0; - *uid = *(uid_t *)(&ans[1 + sizeof(size_t) + plen]); - *gid = *(gid_t *)(&ans[1 + sizeof(size_t) + sizeof(uid_t) + plen]); - return exe; - } - errno = ENOMEM; - return NULL; - case PRIVSEP_ANS_DENIED: - errno = EACCES; - return NULL; - case PRIVSEP_ANS_SYS_ERR: - if (n < (ssize_t)(1 + sizeof(int))) { - errno = EINVAL; - return NULL; - } - errno = *((int*)&ans[1]); - return NULL; - case PRIVSEP_ANS_UNK_CMD: - case PRIVSEP_ANS_INVALID: - default: - errno = EINVAL; - return NULL; - } - - return 0; + } + + if (n < 1) { + errno = EINVAL; + return NULL; + } + + switch (ans[0]) { + case PRIVSEP_ANS_SUCCESS: + plen = *(size_t *)(&ans[1]); + exe = malloc(plen + 1); + if (exe != NULL) { + memcpy((void *)exe, (void *)(ans + 1 + sizeof(size_t)), plen); + exe[plen] = 0; + *uid = *(uid_t *)(&ans[1 + sizeof(size_t) + plen]); + *gid = *(gid_t *)(&ans[1 + sizeof(size_t) + sizeof(uid_t) + plen]); + return exe; + } + errno = ENOMEM; + return NULL; + case PRIVSEP_ANS_DENIED: + errno = EACCES; + return NULL; + case PRIVSEP_ANS_SYS_ERR: + if (n < (ssize_t)(1 + sizeof(int))) { + errno = EINVAL; + return NULL; + } + errno = *((int*)&ans[1]); + return NULL; + case PRIVSEP_ANS_UNK_CMD: + case PRIVSEP_ANS_INVALID: + default: + errno = EINVAL; + return NULL; + } + + return 0; } -#endif +#endif /* __linux__ */ /* * Fork and set up privilege separated monitor process. diff --git a/privsep.h b/privsep.h index 7047a88b..2157c89f 100644 --- a/privsep.h +++ b/privsep.h @@ -39,10 +39,10 @@ int privsep_client_opensock(int, const proxyspec_t *spec); int privsep_client_certfile(int, const char *); int privsep_client_close(int); -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#ifdef __linux__ pid_t privsep_client_get_pid(int, uint32_t, in_port_t); -char* privsep_client_get_info(int , pid_t, uid_t*, gid_t*); -#endif +char * privsep_client_get_info(int , pid_t, uid_t *, gid_t *); +#endif /* __linux__ */ #endif /* !PRIVSEP_H */ diff --git a/proc.c b/proc.c index 2d2bb7ac..9632d062 100644 --- a/proc.c +++ b/proc.c @@ -456,37 +456,38 @@ proc_darwin_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { #endif /* HAVE_DARWIN_LIBPROC */ -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) + +#ifdef __linux__ #include "privsep.h" static int proc_clisock = -1; /* privsep client socket for process info */ -void linux_proc_init(int clisock) { - proc_clisock = clisock; +void +proc_linux_init(int clisock) { + /* XXX only use privsep if actually needed */ + proc_clisock = clisock; } int proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, UNUSED socklen_t src_addrlen) { - if (src_addr->sa_family == AF_INET) { - struct sockaddr_in *src_sai = - (struct sockaddr_in *)src_addr; - *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); - return 0; - } - return -1; + if (src_addr->sa_family == AF_INET) { + struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr; + *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); + return 0; + } + /* TODO IPv6 */ + return -1; } int -proc_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { - *path = privsep_client_get_info(proc_clisock, pid, uid, gid); - return path == NULL ? -1 : 0; +proc_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) +{ + *path = privsep_client_get_info(proc_clisock, pid, uid, gid); + return path == NULL ? -1 : 0; } -#endif +#endif /* __linux__ */ /* vim: set noet ft=c: */ - - - diff --git a/proc.h b/proc.h index 0430de2b..859160c5 100644 --- a/proc.h +++ b/proc.h @@ -58,16 +58,16 @@ int proc_freebsd_pid_for_addr(pid_t *, struct sockaddr *, socklen_t) WUNRES NONN int proc_freebsd_get_info(pid_t, char **, uid_t *, gid_t *) WUNRES NONNULL(2,3,4); #endif /* __FreeBSD__ */ -#if !defined(HAVE_DARWIN_LIBPROC) && !defined(__FreeBSD__) +#ifdef __linux__ #ifndef LOCAL_PROCINFO_STR #define LOCAL_PROCINFO_STR "Linux procfs" -#define proc_pid_for_addr(a,b,c) proc_linux_pid_for_addr(a,b,c) -#define proc_get_info(a,b,c,d) proc_linux_get_info(a,b,c,d) +#define proc_pid_for_addr(a,b,c) proc_linux_pid_for_addr(a,b,c) +#define proc_get_info(a,b,c,d) proc_linux_get_info(a,b,c,d) #endif /* LOCAL_PROCINFO_STR */ -void linux_proc_init(int); +void proc_linux_init(int); int proc_linux_pid_for_addr(pid_t *, struct sockaddr *, socklen_t) WUNRES NONNULL(1,2); int proc_linux_get_info(pid_t, char **, uid_t *, gid_t *) WUNRES NONNULL(2,3,4); -#endif /* HAVE_DARWIN_LIBPROC */ +#endif /* __linux__ */ #endif /* !PROC_H */ From d3721598374aa5ca116f0c6e10cd6811b7c38723 Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Wed, 12 Sep 2018 21:14:24 +0200 Subject: [PATCH 03/10] Revert removal of automatic HAVE_LOCAL_PROCINFO detection --- proc.c | 1 - proc.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/proc.c b/proc.c index 9632d062..d321acf2 100644 --- a/proc.c +++ b/proc.c @@ -464,7 +464,6 @@ static int proc_clisock = -1; /* privsep client socket for process info */ void proc_linux_init(int clisock) { - /* XXX only use privsep if actually needed */ proc_clisock = clisock; } diff --git a/proc.h b/proc.h index 859160c5..e785538b 100644 --- a/proc.h +++ b/proc.h @@ -36,7 +36,9 @@ #include +#if defined(HAVE_DARWIN_LIBPROC) || defined(__FreeBSD__) || defined(__linux__) #define HAVE_LOCAL_PROCINFO +#endif #ifdef HAVE_DARWIN_LIBPROC #ifndef LOCAL_PROCINFO_STR From d174f153b49e9302c9fdc85cd54f7ab29f651050 Mon Sep 17 00:00:00 2001 From: Sergey Pinaev Date: Thu, 13 Sep 2018 16:55:15 +0300 Subject: [PATCH 04/10] use separate client socket for proc_*, and lock it before using --- main.c | 6 +++++- proc.c | 28 ++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index b1d169aa..7ef884af 100644 --- a/main.c +++ b/main.c @@ -787,7 +787,11 @@ main(int argc, char *argv[]) * child process doing the actual work. We request 4 privsep client * sockets: three logger threads, and the child process main thread, * which will become the main proxy thread. */ +#ifdef __linux__ + int clisock[5]; +#else /* !__linux__ */ int clisock[4]; +#endif /* !__linux__ */ if (privsep_fork(opts, clisock, sizeof(clisock)/sizeof(clisock[0])) != 0) { /* parent has exited the monitor loop after waiting for child, @@ -827,7 +831,7 @@ main(int argc, char *argv[]) } #ifdef __linux__ - proc_linux_init(clisock[1]); + proc_linux_init(clisock[4]); #endif /* __linux__ */ /* Post-privdrop/chroot/detach initialization, thread spawning */ diff --git a/proc.c b/proc.c index d321acf2..1967b2c0 100644 --- a/proc.c +++ b/proc.c @@ -461,10 +461,16 @@ proc_darwin_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { #include "privsep.h" static int proc_clisock = -1; /* privsep client socket for process info */ +static int proc_clisock_nomutex = 0; +static pthread_mutex_t proc_clisock_mutex; void proc_linux_init(int clisock) { proc_clisock = clisock; + if (pthread_mutex_init(&proc_clisock_mutex, NULL) != 0) { + /* something goes wrong, try to work without mutex */ + proc_clisock_nomutex = 1; + } } int @@ -473,8 +479,15 @@ proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, { if (src_addr->sa_family == AF_INET) { struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr; - *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); - return 0; + if(proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { + *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); + if(proc_clisock_nomutex == 0) { + pthread_mutex_unlock(&proc_clisock_mutex); + } + return 0; + } else { + return -1; + } } /* TODO IPv6 */ return -1; @@ -483,8 +496,15 @@ proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, int proc_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { - *path = privsep_client_get_info(proc_clisock, pid, uid, gid); - return path == NULL ? -1 : 0; + if(proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { + *path = privsep_client_get_info(proc_clisock, pid, uid, gid); + if(proc_clisock_nomutex == 0) { + pthread_mutex_unlock(&proc_clisock_mutex); + } + } else { + *path = NULL; + } + return *path == NULL ? -1 : 0; } #endif /* __linux__ */ From a180119b02bda288871c94e8d92346d157c6477a Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Thu, 13 Sep 2018 19:30:10 +0200 Subject: [PATCH 05/10] Fix whitespace --- proc.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/proc.c b/proc.c index 1967b2c0..b6b97cfe 100644 --- a/proc.c +++ b/proc.c @@ -467,10 +467,10 @@ static pthread_mutex_t proc_clisock_mutex; void proc_linux_init(int clisock) { proc_clisock = clisock; - if (pthread_mutex_init(&proc_clisock_mutex, NULL) != 0) { - /* something goes wrong, try to work without mutex */ - proc_clisock_nomutex = 1; - } + if (pthread_mutex_init(&proc_clisock_mutex, NULL) != 0) { + /* something goes wrong, try to work without mutex */ + proc_clisock_nomutex = 1; + } } int @@ -479,15 +479,15 @@ proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, { if (src_addr->sa_family == AF_INET) { struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr; - if(proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { - *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); - if(proc_clisock_nomutex == 0) { - pthread_mutex_unlock(&proc_clisock_mutex); - } - return 0; - } else { - return -1; - } + if (proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { + *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); + if (proc_clisock_nomutex == 0) { + pthread_mutex_unlock(&proc_clisock_mutex); + } + return 0; + } else { + return -1; + } } /* TODO IPv6 */ return -1; @@ -496,17 +496,16 @@ proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, int proc_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { - if(proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { - *path = privsep_client_get_info(proc_clisock, pid, uid, gid); - if(proc_clisock_nomutex == 0) { - pthread_mutex_unlock(&proc_clisock_mutex); - } - } else { - *path = NULL; - } + if (proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { + *path = privsep_client_get_info(proc_clisock, pid, uid, gid); + if (proc_clisock_nomutex == 0) { + pthread_mutex_unlock(&proc_clisock_mutex); + } + } else { + *path = NULL; + } return *path == NULL ? -1 : 0; } - #endif /* __linux__ */ /* vim: set noet ft=c: */ From dcdb6b9c9bbda69a987b871a5c44ecce67e6afe6 Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Thu, 13 Sep 2018 19:34:26 +0200 Subject: [PATCH 06/10] Fail proc_linux_init() if mutex initialization fails --- main.c | 5 ++++- proc.c | 22 ++++++++-------------- proc.h | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/main.c b/main.c index 7ef884af..275367e7 100644 --- a/main.c +++ b/main.c @@ -831,7 +831,10 @@ main(int argc, char *argv[]) } #ifdef __linux__ - proc_linux_init(clisock[4]); + if (proc_linux_init(clisock[4]) == -1) { + fprintf(stderr, "%s: failed to init proc_linux\n", argv0); + goto out_sslreinit_failed; + } #endif /* __linux__ */ /* Post-privdrop/chroot/detach initialization, thread spawning */ diff --git a/proc.c b/proc.c index b6b97cfe..38497b77 100644 --- a/proc.c +++ b/proc.c @@ -461,16 +461,14 @@ proc_darwin_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { #include "privsep.h" static int proc_clisock = -1; /* privsep client socket for process info */ -static int proc_clisock_nomutex = 0; static pthread_mutex_t proc_clisock_mutex; -void +int proc_linux_init(int clisock) { proc_clisock = clisock; - if (pthread_mutex_init(&proc_clisock_mutex, NULL) != 0) { - /* something goes wrong, try to work without mutex */ - proc_clisock_nomutex = 1; - } + if (pthread_mutex_init(&proc_clisock_mutex, NULL) != 0) + return -1; + return 0; } int @@ -479,11 +477,9 @@ proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, { if (src_addr->sa_family == AF_INET) { struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr; - if (proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { + if (pthread_mutex_lock(&proc_clisock_mutex) == 0) { *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); - if (proc_clisock_nomutex == 0) { - pthread_mutex_unlock(&proc_clisock_mutex); - } + pthread_mutex_unlock(&proc_clisock_mutex); return 0; } else { return -1; @@ -496,11 +492,9 @@ proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, int proc_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { - if (proc_clisock_nomutex == 1 || pthread_mutex_lock(&proc_clisock_mutex) == 0) { + if (pthread_mutex_lock(&proc_clisock_mutex) == 0) { *path = privsep_client_get_info(proc_clisock, pid, uid, gid); - if (proc_clisock_nomutex == 0) { - pthread_mutex_unlock(&proc_clisock_mutex); - } + pthread_mutex_unlock(&proc_clisock_mutex); } else { *path = NULL; } diff --git a/proc.h b/proc.h index e785538b..09cf5925 100644 --- a/proc.h +++ b/proc.h @@ -66,7 +66,7 @@ int proc_freebsd_get_info(pid_t, char **, uid_t *, gid_t *) WUNRES NONNULL(2,3,4 #define proc_pid_for_addr(a,b,c) proc_linux_pid_for_addr(a,b,c) #define proc_get_info(a,b,c,d) proc_linux_get_info(a,b,c,d) #endif /* LOCAL_PROCINFO_STR */ -void proc_linux_init(int); +int proc_linux_init(int); int proc_linux_pid_for_addr(pid_t *, struct sockaddr *, socklen_t) WUNRES NONNULL(1,2); int proc_linux_get_info(pid_t, char **, uid_t *, gid_t *) WUNRES NONNULL(2,3,4); #endif /* __linux__ */ From 0f0b3c517d4bc5bfdf3081a7d6f5fa25a1821b74 Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Sun, 7 Oct 2018 01:27:27 +0200 Subject: [PATCH 07/10] Include limits.h on Linux for PATH_MAX --- privsep.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/privsep.c b/privsep.c index 7f0e3e72..58a9d628 100644 --- a/privsep.c +++ b/privsep.c @@ -47,7 +47,9 @@ #include #include #include - +#ifdef __linux__ +#include +#endif /* __linux__ */ /* * Privilege separation functionality. From a46ca26fae4ae2e5bfbd5589367cd301e495da2a Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Sun, 7 Oct 2018 23:00:34 +0200 Subject: [PATCH 08/10] Make privsep communication IPv4/IPv6-agnostic --- privsep.c | 126 ++++++++++++++++++++++++++++++------------------------ privsep.h | 5 ++- proc.c | 22 +++++----- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/privsep.c b/privsep.c index 58a9d628..c761e72c 100644 --- a/privsep.c +++ b/privsep.c @@ -73,8 +73,8 @@ #define PRIVSEP_REQ_OPENSOCK 3 /* open socket and pass fd */ #define PRIVSEP_REQ_CERTFILE 4 /* open cert file in certgendir */ #ifdef __linux__ -#define PRIVSEP_REQ_GETPID 5 /* find pid for address */ -#define PRIVSEP_REQ_GETINFO 6 /* get info for pid */ +#define PRIVSEP_REQ_LX_GET_PID 5 /* find pid for address */ +#define PRIVSEP_REQ_LX_GET_INFO 6 /* get info for pid */ #endif /* __linux__ */ /* response byte */ @@ -345,7 +345,8 @@ hextoi(int c) return c - 48; } -pid_t find_pid(ino_t inode) +static pid_t +find_pid(ino_t inode) { DIR *d = opendir("/proc"), *dfd; struct dirent entry, *res; @@ -389,9 +390,17 @@ pid_t find_pid(ino_t inode) return -1; } +/* + * This currently only supports IPv4 sockaddrs. + */ static pid_t WUNRES -privsep_server_get_pid(uint32_t src_addr, in_port_t src_port) +privsep_server_linux_get_pid(struct sockaddr *addr) { + if (addr->sa_family != AF_INET) + return -1; + + struct sockaddr_in *sai = (struct sockaddr_in *)addr; + int fd = open("/proc/net/tcp", O_RDONLY); if (fd != -1) { char bufc[4096], inode[64]; @@ -449,7 +458,7 @@ privsep_server_get_pid(uint32_t src_addr, in_port_t src_port) break; } else if (state == ST_LOCAL_PORT) { if (c == ' ') { - if (s_addr == src_addr && htons(sin_port) == src_port) { + if (s_addr == sai->sin_addr.s_addr && htons(sin_port) == sai->sin_port) { /* find inode */ buf += 72; for (c = *buf, i = 0; c != 0; i++, buf++, c = *buf) { @@ -486,16 +495,16 @@ privsep_server_get_pid(uint32_t src_addr, in_port_t src_port) } static size_t WUNRES -privsep_server_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { +privsep_server_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { struct stat sbuf; - char dn[1024], exe[PATH_MAX + 1]; + char dn[32], exe[PATH_MAX + 1]; ssize_t n; - snprintf(dn, sizeof(dn), "/proc/%ld", (long)pid); + snprintf(dn, sizeof(dn), "/proc/%lu", (unsigned long)pid); if (stat(dn, &sbuf) == 0) { *uid = sbuf.st_uid; *gid = sbuf.st_gid; - snprintf(dn, sizeof(dn), "/proc/%ld/exe", (long)pid); + snprintf(dn, sizeof(dn), "/proc/%lu/exe", (unsigned long)pid); n = readlink(dn, exe, sizeof(exe)); if (n != -1) { exe[n < PATH_MAX ? n : PATH_MAX] = 0; @@ -503,7 +512,7 @@ privsep_server_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { return n < PATH_MAX ? n : PATH_MAX; } } - return -1; + return 0; } #endif /* __linux__ */ @@ -540,44 +549,6 @@ privsep_server_handle_req(opts_t *opts, int srvsock) /* client indicates EOF through close message */ return 1; } -#ifdef __linux__ - case PRIVSEP_REQ_GETPID: { - ans[0] = PRIVSEP_ANS_SUCCESS; - *(pid_t *)(&ans[1]) = privsep_server_get_pid(*(uint32_t *)(&req[1]), - *(in_port_t *)(&req[1 + sizeof(uint32_t)])); - if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(pid_t), -1) == -1) { - log_err_printf("Sending message failed: %s (%i)\n", - strerror(errno), errno); - return -1; - } - return 0; - } - case PRIVSEP_REQ_GETINFO: { - char *path; - uid_t uid; - gid_t gid; - size_t anssz = 1, plen; - - ans[0] = PRIVSEP_ANS_SUCCESS; - if ((plen = privsep_server_get_info(*(pid_t *)(&req[1]), &path, &uid, &gid)) != 0) { - anssz = 1 + sizeof(size_t) + plen + sizeof(uid_t) + sizeof(gid_t); - *(size_t *)(&ans[1]) = plen; - memcpy((void *)(ans + 1 + sizeof(size_t)), (void *)path, plen); - free(path); - *(uid_t *)(&ans[1 + sizeof(size_t) + plen]) = uid; - *(gid_t *)(&ans[1 + sizeof(size_t) + plen] + sizeof(gid_t)) = gid; - } else { - *(size_t *)(&ans[1]) = 0; - anssz = 1 + sizeof(size_t); - } - if (sys_sendmsgfd(srvsock, ans, anssz, -1) == -1) { - log_err_printf("Sending message failed: %s (%i)\n", - strerror(errno), errno); - return -1; - } - return 0; - } -#endif /* __linux__ */ case PRIVSEP_REQ_OPENFILE_P: mkpath = 1; /* fall through */ @@ -750,6 +721,46 @@ privsep_server_handle_req(opts_t *opts, int srvsock) /* not reached */ break; } +#ifdef __linux__ + case PRIVSEP_REQ_LX_GET_PID: { + ans[0] = PRIVSEP_ANS_SUCCESS; + *(pid_t *)(&ans[1]) = privsep_server_linux_get_pid( + (struct sockaddr *)(&req[1])); + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(pid_t), -1) == -1) { + log_err_printf("Sending message failed: %s (%i)\n", + strerror(errno), errno); + return -1; + } + return 0; + } + case PRIVSEP_REQ_LX_GET_INFO: { + char *path; + uid_t uid; + gid_t gid; + size_t anssz = 1, plen; + + ans[0] = PRIVSEP_ANS_SUCCESS; + if ((plen = privsep_server_linux_get_info( + *(pid_t *)(&req[1]), + &path, &uid, &gid)) != 0) { + anssz = 1 + sizeof(size_t) + plen + sizeof(uid_t) + sizeof(gid_t); + *(size_t *)(&ans[1]) = plen; + memcpy((void *)(ans + 1 + sizeof(size_t)), (void *)path, plen); + free(path); + *(uid_t *)(&ans[1 + sizeof(size_t) + plen]) = uid; + *(gid_t *)(&ans[1 + sizeof(size_t) + plen] + sizeof(gid_t)) = gid; + } else { + *(size_t *)(&ans[1]) = 0; + anssz = 1 + sizeof(size_t); + } + if (sys_sendmsgfd(srvsock, ans, anssz, -1) == -1) { + log_err_printf("Sending message failed: %s (%i)\n", + strerror(errno), errno); + return -1; + } + return 0; + } +#endif /* __linux__ */ default: ans[0] = PRIVSEP_ANS_UNK_CMD; if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { @@ -1086,16 +1097,21 @@ privsep_client_close(int clisock) #ifdef __linux__ pid_t -privsep_client_get_pid(int clisock, uint32_t src_addr, in_port_t src_port) +privsep_client_linux_get_pid(int clisock, struct sockaddr *addr) { - char req[sizeof(uint32_t) + sizeof(in_port_t) + 1]; + char req[sizeof(struct sockaddr_storage) + 1]; char ans[PRIVSEP_MAX_ANS_SIZE]; ssize_t n; int fd = -1; - req[0] = PRIVSEP_REQ_GETPID; - *(uint32_t *)(&req[1]) = src_addr; - *(in_port_t *)(&req[1 + sizeof(uint32_t)]) = src_port; + req[0] = PRIVSEP_REQ_LX_GET_PID; + if (addr->sa_family == AF_INET) + memcpy(&req[1], addr, sizeof(struct sockaddr_in)); + else if (addr->sa_family == AF_INET6) + memcpy(&req[1], addr, sizeof(struct sockaddr_in6)); + else + return -1; + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { return -1; } @@ -1133,7 +1149,7 @@ privsep_client_get_pid(int clisock, uint32_t src_addr, in_port_t src_port) } char * -privsep_client_get_info(int clisock, pid_t pid, uid_t *uid, gid_t *gid) +privsep_client_linux_get_info(int clisock, pid_t pid, uid_t *uid, gid_t *gid) { char req[sizeof(pid_t) + 1]; char ans[PRIVSEP_MAX_ANS_SIZE]; @@ -1142,7 +1158,7 @@ privsep_client_get_info(int clisock, pid_t pid, uid_t *uid, gid_t *gid) ssize_t n; int fd = -1; - req[0] = PRIVSEP_REQ_GETINFO; + req[0] = PRIVSEP_REQ_LX_GET_INFO; *(pid_t *)(&req[1]) = pid; if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { return NULL; diff --git a/privsep.h b/privsep.h index 2157c89f..3ecf00cb 100644 --- a/privsep.h +++ b/privsep.h @@ -40,8 +40,9 @@ int privsep_client_certfile(int, const char *); int privsep_client_close(int); #ifdef __linux__ -pid_t privsep_client_get_pid(int, uint32_t, in_port_t); -char * privsep_client_get_info(int , pid_t, uid_t *, gid_t *); +#include +pid_t privsep_client_linux_get_pid(int, struct sockaddr *); +char * privsep_client_linux_get_info(int, pid_t, uid_t *, gid_t *); #endif /* __linux__ */ #endif /* !PRIVSEP_H */ diff --git a/proc.c b/proc.c index 38497b77..f9d0b419 100644 --- a/proc.c +++ b/proc.c @@ -475,17 +475,17 @@ int proc_linux_pid_for_addr(pid_t *result, struct sockaddr *src_addr, UNUSED socklen_t src_addrlen) { - if (src_addr->sa_family == AF_INET) { - struct sockaddr_in *src_sai = (struct sockaddr_in *)src_addr; - if (pthread_mutex_lock(&proc_clisock_mutex) == 0) { - *result = privsep_client_get_pid(proc_clisock, src_sai->sin_addr.s_addr, src_sai->sin_port); - pthread_mutex_unlock(&proc_clisock_mutex); - return 0; - } else { - return -1; - } + if (src_addr->sa_family != AF_INET) + return -1; + + if (pthread_mutex_lock(&proc_clisock_mutex) == 0) { + *result = privsep_client_linux_get_pid(proc_clisock, src_addr); + pthread_mutex_unlock(&proc_clisock_mutex); + return 0; + } else { + return -1; } - /* TODO IPv6 */ + return -1; } @@ -493,7 +493,7 @@ int proc_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { if (pthread_mutex_lock(&proc_clisock_mutex) == 0) { - *path = privsep_client_get_info(proc_clisock, pid, uid, gid); + *path = privsep_client_linux_get_info(proc_clisock, pid, uid, gid); pthread_mutex_unlock(&proc_clisock_mutex); } else { *path = NULL; From 887db1cb968e867f66471719409b7a351aea7a17 Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Mon, 8 Oct 2018 23:25:37 +0200 Subject: [PATCH 09/10] Add fastpath to Linux -i --- privsep.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/privsep.c b/privsep.c index c761e72c..3469d831 100644 --- a/privsep.c +++ b/privsep.c @@ -1104,6 +1104,9 @@ privsep_client_linux_get_pid(int clisock, struct sockaddr *addr) ssize_t n; int fd = -1; + if (privsep_fastpath) + return privsep_server_linux_get_pid(addr); + req[0] = PRIVSEP_REQ_LX_GET_PID; if (addr->sa_family == AF_INET) memcpy(&req[1], addr, sizeof(struct sockaddr_in)); @@ -1158,6 +1161,11 @@ privsep_client_linux_get_info(int clisock, pid_t pid, uid_t *uid, gid_t *gid) ssize_t n; int fd = -1; + if (privsep_fastpath) { + plen = privsep_server_linux_get_info(pid, &exe, uid, gid); + return exe; + } + req[0] = PRIVSEP_REQ_LX_GET_INFO; *(pid_t *)(&req[1]) = pid; if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { From 9408b68a8fe9b624c2e6478de51f14da00b2c39a Mon Sep 17 00:00:00 2001 From: Daniel Roethlisberger Date: Fri, 12 Oct 2018 22:04:58 +0200 Subject: [PATCH 10/10] PATH_MAX includes NUL --- privsep.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/privsep.c b/privsep.c index 3469d831..268e7001 100644 --- a/privsep.c +++ b/privsep.c @@ -497,7 +497,7 @@ privsep_server_linux_get_pid(struct sockaddr *addr) static size_t WUNRES privsep_server_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { struct stat sbuf; - char dn[32], exe[PATH_MAX + 1]; + char dn[32], exe[PATH_MAX]; ssize_t n; snprintf(dn, sizeof(dn), "/proc/%lu", (unsigned long)pid); @@ -505,11 +505,11 @@ privsep_server_linux_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) { *uid = sbuf.st_uid; *gid = sbuf.st_gid; snprintf(dn, sizeof(dn), "/proc/%lu/exe", (unsigned long)pid); - n = readlink(dn, exe, sizeof(exe)); + n = readlink(dn, exe, sizeof(exe) - 1); if (n != -1) { - exe[n < PATH_MAX ? n : PATH_MAX] = 0; + exe[n] = 0; *path = strdup(exe); - return n < PATH_MAX ? n : PATH_MAX; + return n; } } return 0;