From 8d41e52f7a22b13008e111dbd5edbfc190f253ad Mon Sep 17 00:00:00 2001 From: Rogo Date: Wed, 11 Feb 2026 02:31:33 +0300 Subject: [PATCH 1/4] applying quark-noroot-20191003-3c7049e.diff --- main.c | 92 ++------------------------------------------------------- quark.1 | 21 +------------ sock.c | 15 ++-------- sock.h | 2 +- 4 files changed, 7 insertions(+), 123 deletions(-) diff --git a/main.c b/main.c index 1e88536..d4969fb 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,7 @@ /* See LICENSE file for copyright and license details. */ #include -#include #include -#include +#include #include #include #include @@ -54,7 +53,7 @@ handlesignals(void(*hdl)(int)) static void usage(void) { - const char *opts = "[-u user] [-g group] [-n num] [-d dir] [-l] " + const char *opts = "[-n num] [-d dir] [-l] " "[-i file] [-v vhost] ... [-m map] ..."; die("usage: %s -p port [-h host] %s\n" @@ -65,9 +64,6 @@ usage(void) int main(int argc, char *argv[]) { - struct group *grp = NULL; - struct passwd *pwd = NULL; - struct rlimit rlim; struct server srv = { .docindex = "index.html", }; @@ -80,16 +76,11 @@ main(int argc, char *argv[]) size_t nthreads = 4; size_t nslots = 64; char *servedir = "."; - char *user = "nobody"; - char *group = "nogroup"; ARGBEGIN { case 'd': servedir = EARGF(usage()); break; - case 'g': - group = EARGF(usage()); - break; case 'h': srv.host = EARGF(usage()); break; @@ -134,9 +125,6 @@ main(int argc, char *argv[]) case 'U': udsname = EARGF(usage()); break; - case 'u': - user = EARGF(usage()); - break; case 'v': if (spacetok(EARGF(usage()), tok, 4) || !tok[0] || !tok[1] || !tok[2]) { @@ -178,42 +166,10 @@ main(int argc, char *argv[]) } } - /* validate user and group */ - errno = 0; - if (!user || !(pwd = getpwnam(user))) { - die("getpwnam '%s': %s", user ? user : "null", - errno ? strerror(errno) : "Entry not found"); - } - errno = 0; - if (!group || !(grp = getgrnam(group))) { - die("getgrnam '%s': %s", group ? group : "null", - errno ? strerror(errno) : "Entry not found"); - } - /* open a new process group */ setpgid(0, 0); handlesignals(sigcleanup); - - /* - * set the maximum number of open file descriptors as needed - * - 3 initial fd's - * - nthreads fd's for the listening socket - * - (nthreads * nslots) fd's for the connection-fd - * - (5 * nthreads) fd's for general purpose thread-use - */ - rlim.rlim_cur = rlim.rlim_max = 3 + nthreads + nthreads * nslots + - 5 * nthreads; - if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { - if (errno == EPERM) { - die("You need to run as root or have " - "CAP_SYS_RESOURCE set, or are asking for more " - "file descriptors than the system can offer"); - } else { - die("setrlimit:"); - } - } - /* * create the (non-blocking) listening socket * @@ -228,7 +184,7 @@ main(int argc, char *argv[]) * kernel by changing epoll-sheduling from a FIFO- to a * LIFO-model, especially as it doesn't affect performance */ - insock = udsname ? sock_get_uds(udsname, pwd->pw_uid, grp->gr_gid) : + insock = udsname ? sock_get_uds(udsname) : sock_get_ips(srv.host, srv.port); if (sock_set_nonblocking(insock)) { return 1; @@ -287,51 +243,9 @@ main(int argc, char *argv[]) eunveil(servedir, "r"); eunveil(NULL, NULL); - /* chroot */ if (chdir(servedir) < 0) { die("chdir '%s':", servedir); } - if (chroot(".") < 0) { - if (errno == EPERM) { - die("You need to run as root or have " - "CAP_SYS_CHROOT set"); - } else { - die("chroot:"); - } - } - - /* drop root */ - if (pwd->pw_uid == 0 || grp->gr_gid == 0) { - die("Won't run under root %s for hopefully obvious reasons", - (pwd->pw_uid == 0) ? (grp->gr_gid == 0) ? - "user and group" : "user" : "group"); - } - - if (setgroups(1, &(grp->gr_gid)) < 0) { - if (errno == EPERM) { - die("You need to run as root or have " - "CAP_SETGID set"); - } else { - die("setgroups:"); - } - } - if (setgid(grp->gr_gid) < 0) { - if (errno == EPERM) { - die("You need to run as root or have " - "CAP_SETGID set"); - } else { - die("setgid:"); - } - - } - if (setuid(pwd->pw_uid) < 0) { - if (errno == EPERM) { - die("You need to run as root or have " - "CAP_SETUID set"); - } else { - die("setuid:"); - } - } if (udsname) { epledge("stdio rpath proc unix", NULL); diff --git a/quark.1 b/quark.1 index d752cc7..93126b5 100644 --- a/quark.1 +++ b/quark.1 @@ -46,13 +46,8 @@ hidden files and directories. .It Fl d Ar dir Serve .Ar dir -after chrooting into it. +after changing into it. The default is ".". -.It Fl g Ar group -Set group ID when dropping privileges, and in socket mode the group of the -socket file, to the ID of -.Ar group . -The default is "nogroup". .It Fl h Ar host Use .Ar host @@ -94,20 +89,6 @@ redirects on non-standard ports. Create the UNIX-domain socket .Ar file , listen on it for incoming connections and remove it on exit. -.It Fl s Ar num -Set the number of connection slots per worker thread to -.Ar num . -The default is 64. -.It Fl t Ar num -Set the number of worker threads to -.Ar num . -The default is 4. -.It Fl u Ar user -Set user ID when dropping privileges, -and in socket mode the user of the socket file, -to the ID of -.Ar user . -The default is "nobody". .It Fl v Ar vhost Add the virtual host specified by .Ar vhost , diff --git a/sock.c b/sock.c index ebbbf65..bff1b38 100644 --- a/sock.c +++ b/sock.c @@ -70,14 +70,13 @@ sock_get_ips(const char *host, const char* port) } int -sock_get_uds(const char *udsname, uid_t uid, gid_t gid) +sock_get_uds(const char *udsname) { struct sockaddr_un addr = { .sun_family = AF_UNIX, }; size_t udsnamelen; - int insock, sockmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | - S_IROTH | S_IWOTH; + int insock; if ((insock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { die("socket:"); @@ -97,16 +96,6 @@ sock_get_uds(const char *udsname, uid_t uid, gid_t gid) die("listen:"); } - if (chmod(udsname, sockmode) < 0) { - sock_rem_uds(udsname); - die("chmod '%s':", udsname); - } - - if (chown(udsname, uid, gid) < 0) { - sock_rem_uds(udsname); - die("chown '%s':", udsname); - } - return insock; } diff --git a/sock.h b/sock.h index 22ae303..765016d 100644 --- a/sock.h +++ b/sock.h @@ -7,8 +7,8 @@ #include int sock_get_ips(const char *, const char *); -int sock_get_uds(const char *, uid_t, gid_t); void sock_rem_uds(const char *); +int sock_get_uds(const char *); int sock_set_timeout(int, int); int sock_set_nonblocking(int); int sock_get_inaddr_str(const struct sockaddr_storage *, char *, size_t); -- 2.52.0 From 56bde27402884fd051cbe9e5def222b1d4fb5fdb Mon Sep 17 00:00:00 2001 From: Rogo Date: Wed, 11 Feb 2026 02:44:40 +0300 Subject: [PATCH 2/4] Remove setrlimit() code User other tools to setup process limits, capabilities, credentials. --- main.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/main.c b/main.c index d4969fb..8bb7023 100644 --- a/main.c +++ b/main.c @@ -211,34 +211,6 @@ main(int argc, char *argv[]) die("signal: Failed to set SIG_IGN on SIGPIPE"); } - /* - * try increasing the thread-limit by the number - * of threads we need (which is the only reliable - * workaround I know given the thread-limit is per user - * rather than per process), but ignore EPERM errors, - * because this most probably means the user has already - * set the value to the kernel's limit, and there's not - * much we can do in any other case. - * There's also no danger of overflow as the value - * returned by getrlimit() is way below the limits of the - * rlim_t datatype. - */ - if (getrlimit(RLIMIT_NPROC, &rlim) < 0) { - die("getrlimit:"); - } - if (rlim.rlim_max == RLIM_INFINITY) { - if (rlim.rlim_cur != RLIM_INFINITY) { - /* try increasing current limit by nthreads */ - rlim.rlim_cur += nthreads; - } - } else { - /* try increasing current and hard limit by nthreads */ - rlim.rlim_cur = rlim.rlim_max += nthreads; - } - if (setrlimit(RLIMIT_NPROC, &rlim) < 0 && errno != EPERM) { - die("setrlimit()"); - } - /* limit ourselves to reading the servedir and block further unveils */ eunveil(servedir, "r"); eunveil(NULL, NULL); -- 2.52.0 From a31e262d003b0ffdc4bdeff8d6690cd11d5e2f45 Mon Sep 17 00:00:00 2001 From: Rogo Date: Wed, 11 Feb 2026 07:13:49 +0300 Subject: [PATCH 3/4] Make internal_path relative to "." or vhost dir Absolute pathnames for filesystem paths only work with chroot. Without even dealing with other problems that abuse of chroot for "sandboxing" introduces, it makes impossible to run quark as unpriviliged user. URL path is already sanitized for ".." dirs, and quark doesn't create new files and links. --- http.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/http.c b/http.c index 36f8b1c..9d17cfb 100644 --- a/http.c +++ b/http.c @@ -776,16 +776,12 @@ http_prepare_response(const struct request *req, struct response *res, * path and the virtual host while ignoring query and fragment * (valid according to RFC 3986) */ - if (esnprintf(res->internal_path, sizeof(res->internal_path), "/%s/%s", - (srv->vhost && res->vhost) ? res->vhost->dir : "", + if (esnprintf(res->internal_path, sizeof(res->internal_path), "%s/%s", + (srv->vhost && res->vhost) ? res->vhost->dir : ".", res->path)) { s = S_REQUEST_TOO_LARGE; goto err; } - if ((tmps = path_normalize(res->internal_path, NULL))) { - s = tmps; - goto err; - } if (stat(res->internal_path, &st) < 0) { s = (errno == EACCES) ? S_FORBIDDEN : S_NOT_FOUND; goto err; -- 2.52.0 From c67c92dcbdc00f5a16179da8d46525c70a28d219 Mon Sep 17 00:00:00 2001 From: Rogo Date: Wed, 11 Feb 2026 07:25:23 +0300 Subject: [PATCH 4/4] Remove "setpgid(0, 0)"; stop breaking ^C behavior in shell --- main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/main.c b/main.c index 8bb7023..776fb13 100644 --- a/main.c +++ b/main.c @@ -166,9 +166,6 @@ main(int argc, char *argv[]) } } - /* open a new process group */ - setpgid(0, 0); - handlesignals(sigcleanup); /* * create the (non-blocking) listening socket -- 2.52.0