quark-noroot-20260211-5ad0df9.diff (10649B)
1 From 8d41e52f7a22b13008e111dbd5edbfc190f253ad Mon Sep 17 00:00:00 2001 2 From: Rogo <goryachev.romochka@gmail.com> 3 Date: Wed, 11 Feb 2026 02:31:33 +0300 4 Subject: [PATCH 1/4] applying quark-noroot-20191003-3c7049e.diff 5 6 --- 7 main.c | 92 ++------------------------------------------------------- 8 quark.1 | 21 +------------ 9 sock.c | 15 ++-------- 10 sock.h | 2 +- 11 4 files changed, 7 insertions(+), 123 deletions(-) 12 13 diff --git a/main.c b/main.c 14 index 1e88536..d4969fb 100644 15 --- a/main.c 16 +++ b/main.c 17 @@ -1,8 +1,7 @@ 18 /* See LICENSE file for copyright and license details. */ 19 #include <errno.h> 20 -#include <grp.h> 21 #include <limits.h> 22 -#include <pwd.h> 23 +#include <netinet/in.h> 24 #include <regex.h> 25 #include <signal.h> 26 #include <stddef.h> 27 @@ -54,7 +53,7 @@ handlesignals(void(*hdl)(int)) 28 static void 29 usage(void) 30 { 31 - const char *opts = "[-u user] [-g group] [-n num] [-d dir] [-l] " 32 + const char *opts = "[-n num] [-d dir] [-l] " 33 "[-i file] [-v vhost] ... [-m map] ..."; 34 35 die("usage: %s -p port [-h host] %s\n" 36 @@ -65,9 +64,6 @@ usage(void) 37 int 38 main(int argc, char *argv[]) 39 { 40 - struct group *grp = NULL; 41 - struct passwd *pwd = NULL; 42 - struct rlimit rlim; 43 struct server srv = { 44 .docindex = "index.html", 45 }; 46 @@ -80,16 +76,11 @@ main(int argc, char *argv[]) 47 size_t nthreads = 4; 48 size_t nslots = 64; 49 char *servedir = "."; 50 - char *user = "nobody"; 51 - char *group = "nogroup"; 52 53 ARGBEGIN { 54 case 'd': 55 servedir = EARGF(usage()); 56 break; 57 - case 'g': 58 - group = EARGF(usage()); 59 - break; 60 case 'h': 61 srv.host = EARGF(usage()); 62 break; 63 @@ -134,9 +125,6 @@ main(int argc, char *argv[]) 64 case 'U': 65 udsname = EARGF(usage()); 66 break; 67 - case 'u': 68 - user = EARGF(usage()); 69 - break; 70 case 'v': 71 if (spacetok(EARGF(usage()), tok, 4) || !tok[0] || !tok[1] || 72 !tok[2]) { 73 @@ -178,42 +166,10 @@ main(int argc, char *argv[]) 74 } 75 } 76 77 - /* validate user and group */ 78 - errno = 0; 79 - if (!user || !(pwd = getpwnam(user))) { 80 - die("getpwnam '%s': %s", user ? user : "null", 81 - errno ? strerror(errno) : "Entry not found"); 82 - } 83 - errno = 0; 84 - if (!group || !(grp = getgrnam(group))) { 85 - die("getgrnam '%s': %s", group ? group : "null", 86 - errno ? strerror(errno) : "Entry not found"); 87 - } 88 - 89 /* open a new process group */ 90 setpgid(0, 0); 91 92 handlesignals(sigcleanup); 93 - 94 - /* 95 - * set the maximum number of open file descriptors as needed 96 - * - 3 initial fd's 97 - * - nthreads fd's for the listening socket 98 - * - (nthreads * nslots) fd's for the connection-fd 99 - * - (5 * nthreads) fd's for general purpose thread-use 100 - */ 101 - rlim.rlim_cur = rlim.rlim_max = 3 + nthreads + nthreads * nslots + 102 - 5 * nthreads; 103 - if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { 104 - if (errno == EPERM) { 105 - die("You need to run as root or have " 106 - "CAP_SYS_RESOURCE set, or are asking for more " 107 - "file descriptors than the system can offer"); 108 - } else { 109 - die("setrlimit:"); 110 - } 111 - } 112 - 113 /* 114 * create the (non-blocking) listening socket 115 * 116 @@ -228,7 +184,7 @@ main(int argc, char *argv[]) 117 * kernel by changing epoll-sheduling from a FIFO- to a 118 * LIFO-model, especially as it doesn't affect performance 119 */ 120 - insock = udsname ? sock_get_uds(udsname, pwd->pw_uid, grp->gr_gid) : 121 + insock = udsname ? sock_get_uds(udsname) : 122 sock_get_ips(srv.host, srv.port); 123 if (sock_set_nonblocking(insock)) { 124 return 1; 125 @@ -287,51 +243,9 @@ main(int argc, char *argv[]) 126 eunveil(servedir, "r"); 127 eunveil(NULL, NULL); 128 129 - /* chroot */ 130 if (chdir(servedir) < 0) { 131 die("chdir '%s':", servedir); 132 } 133 - if (chroot(".") < 0) { 134 - if (errno == EPERM) { 135 - die("You need to run as root or have " 136 - "CAP_SYS_CHROOT set"); 137 - } else { 138 - die("chroot:"); 139 - } 140 - } 141 - 142 - /* drop root */ 143 - if (pwd->pw_uid == 0 || grp->gr_gid == 0) { 144 - die("Won't run under root %s for hopefully obvious reasons", 145 - (pwd->pw_uid == 0) ? (grp->gr_gid == 0) ? 146 - "user and group" : "user" : "group"); 147 - } 148 - 149 - if (setgroups(1, &(grp->gr_gid)) < 0) { 150 - if (errno == EPERM) { 151 - die("You need to run as root or have " 152 - "CAP_SETGID set"); 153 - } else { 154 - die("setgroups:"); 155 - } 156 - } 157 - if (setgid(grp->gr_gid) < 0) { 158 - if (errno == EPERM) { 159 - die("You need to run as root or have " 160 - "CAP_SETGID set"); 161 - } else { 162 - die("setgid:"); 163 - } 164 - 165 - } 166 - if (setuid(pwd->pw_uid) < 0) { 167 - if (errno == EPERM) { 168 - die("You need to run as root or have " 169 - "CAP_SETUID set"); 170 - } else { 171 - die("setuid:"); 172 - } 173 - } 174 175 if (udsname) { 176 epledge("stdio rpath proc unix", NULL); 177 diff --git a/quark.1 b/quark.1 178 index d752cc7..93126b5 100644 179 --- a/quark.1 180 +++ b/quark.1 181 @@ -46,13 +46,8 @@ hidden files and directories. 182 .It Fl d Ar dir 183 Serve 184 .Ar dir 185 -after chrooting into it. 186 +after changing into it. 187 The default is ".". 188 -.It Fl g Ar group 189 -Set group ID when dropping privileges, and in socket mode the group of the 190 -socket file, to the ID of 191 -.Ar group . 192 -The default is "nogroup". 193 .It Fl h Ar host 194 Use 195 .Ar host 196 @@ -94,20 +89,6 @@ redirects on non-standard ports. 197 Create the UNIX-domain socket 198 .Ar file , 199 listen on it for incoming connections and remove it on exit. 200 -.It Fl s Ar num 201 -Set the number of connection slots per worker thread to 202 -.Ar num . 203 -The default is 64. 204 -.It Fl t Ar num 205 -Set the number of worker threads to 206 -.Ar num . 207 -The default is 4. 208 -.It Fl u Ar user 209 -Set user ID when dropping privileges, 210 -and in socket mode the user of the socket file, 211 -to the ID of 212 -.Ar user . 213 -The default is "nobody". 214 .It Fl v Ar vhost 215 Add the virtual host specified by 216 .Ar vhost , 217 diff --git a/sock.c b/sock.c 218 index ebbbf65..bff1b38 100644 219 --- a/sock.c 220 +++ b/sock.c 221 @@ -70,14 +70,13 @@ sock_get_ips(const char *host, const char* port) 222 } 223 224 int 225 -sock_get_uds(const char *udsname, uid_t uid, gid_t gid) 226 +sock_get_uds(const char *udsname) 227 { 228 struct sockaddr_un addr = { 229 .sun_family = AF_UNIX, 230 }; 231 size_t udsnamelen; 232 - int insock, sockmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | 233 - S_IROTH | S_IWOTH; 234 + int insock; 235 236 if ((insock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { 237 die("socket:"); 238 @@ -97,16 +96,6 @@ sock_get_uds(const char *udsname, uid_t uid, gid_t gid) 239 die("listen:"); 240 } 241 242 - if (chmod(udsname, sockmode) < 0) { 243 - sock_rem_uds(udsname); 244 - die("chmod '%s':", udsname); 245 - } 246 - 247 - if (chown(udsname, uid, gid) < 0) { 248 - sock_rem_uds(udsname); 249 - die("chown '%s':", udsname); 250 - } 251 - 252 return insock; 253 } 254 255 diff --git a/sock.h b/sock.h 256 index 22ae303..765016d 100644 257 --- a/sock.h 258 +++ b/sock.h 259 @@ -7,8 +7,8 @@ 260 #include <sys/types.h> 261 262 int sock_get_ips(const char *, const char *); 263 -int sock_get_uds(const char *, uid_t, gid_t); 264 void sock_rem_uds(const char *); 265 +int sock_get_uds(const char *); 266 int sock_set_timeout(int, int); 267 int sock_set_nonblocking(int); 268 int sock_get_inaddr_str(const struct sockaddr_storage *, char *, size_t); 269 -- 270 2.52.0 271 272 273 From 56bde27402884fd051cbe9e5def222b1d4fb5fdb Mon Sep 17 00:00:00 2001 274 From: Rogo <goryachev.romochka@gmail.com> 275 Date: Wed, 11 Feb 2026 02:44:40 +0300 276 Subject: [PATCH 2/4] Remove setrlimit() code 277 278 User other tools to setup process limits, capabilities, credentials. 279 --- 280 main.c | 28 ---------------------------- 281 1 file changed, 28 deletions(-) 282 283 diff --git a/main.c b/main.c 284 index d4969fb..8bb7023 100644 285 --- a/main.c 286 +++ b/main.c 287 @@ -211,34 +211,6 @@ main(int argc, char *argv[]) 288 die("signal: Failed to set SIG_IGN on SIGPIPE"); 289 } 290 291 - /* 292 - * try increasing the thread-limit by the number 293 - * of threads we need (which is the only reliable 294 - * workaround I know given the thread-limit is per user 295 - * rather than per process), but ignore EPERM errors, 296 - * because this most probably means the user has already 297 - * set the value to the kernel's limit, and there's not 298 - * much we can do in any other case. 299 - * There's also no danger of overflow as the value 300 - * returned by getrlimit() is way below the limits of the 301 - * rlim_t datatype. 302 - */ 303 - if (getrlimit(RLIMIT_NPROC, &rlim) < 0) { 304 - die("getrlimit:"); 305 - } 306 - if (rlim.rlim_max == RLIM_INFINITY) { 307 - if (rlim.rlim_cur != RLIM_INFINITY) { 308 - /* try increasing current limit by nthreads */ 309 - rlim.rlim_cur += nthreads; 310 - } 311 - } else { 312 - /* try increasing current and hard limit by nthreads */ 313 - rlim.rlim_cur = rlim.rlim_max += nthreads; 314 - } 315 - if (setrlimit(RLIMIT_NPROC, &rlim) < 0 && errno != EPERM) { 316 - die("setrlimit()"); 317 - } 318 - 319 /* limit ourselves to reading the servedir and block further unveils */ 320 eunveil(servedir, "r"); 321 eunveil(NULL, NULL); 322 -- 323 2.52.0 324 325 326 From a31e262d003b0ffdc4bdeff8d6690cd11d5e2f45 Mon Sep 17 00:00:00 2001 327 From: Rogo <goryachev.romochka@gmail.com> 328 Date: Wed, 11 Feb 2026 07:13:49 +0300 329 Subject: [PATCH 3/4] Make internal_path relative to "." or vhost dir 330 331 Absolute pathnames for filesystem paths only work with chroot. 332 333 Without even dealing with other problems that abuse of chroot for 334 "sandboxing" introduces, it makes impossible to run quark as unpriviliged 335 user. URL path is already sanitized for ".." dirs, and quark doesn't 336 create new files and links. 337 --- 338 http.c | 8 ++------ 339 1 file changed, 2 insertions(+), 6 deletions(-) 340 341 diff --git a/http.c b/http.c 342 index 36f8b1c..9d17cfb 100644 343 --- a/http.c 344 +++ b/http.c 345 @@ -776,16 +776,12 @@ http_prepare_response(const struct request *req, struct response *res, 346 * path and the virtual host while ignoring query and fragment 347 * (valid according to RFC 3986) 348 */ 349 - if (esnprintf(res->internal_path, sizeof(res->internal_path), "/%s/%s", 350 - (srv->vhost && res->vhost) ? res->vhost->dir : "", 351 + if (esnprintf(res->internal_path, sizeof(res->internal_path), "%s/%s", 352 + (srv->vhost && res->vhost) ? res->vhost->dir : ".", 353 res->path)) { 354 s = S_REQUEST_TOO_LARGE; 355 goto err; 356 } 357 - if ((tmps = path_normalize(res->internal_path, NULL))) { 358 - s = tmps; 359 - goto err; 360 - } 361 if (stat(res->internal_path, &st) < 0) { 362 s = (errno == EACCES) ? S_FORBIDDEN : S_NOT_FOUND; 363 goto err; 364 -- 365 2.52.0 366 367 368 From c67c92dcbdc00f5a16179da8d46525c70a28d219 Mon Sep 17 00:00:00 2001 369 From: Rogo <goryachev.romochka@gmail.com> 370 Date: Wed, 11 Feb 2026 07:25:23 +0300 371 Subject: [PATCH 4/4] Remove "setpgid(0, 0)"; stop breaking ^C behavior in 372 shell 373 374 --- 375 main.c | 3 --- 376 1 file changed, 3 deletions(-) 377 378 diff --git a/main.c b/main.c 379 index 8bb7023..776fb13 100644 380 --- a/main.c 381 +++ b/main.c 382 @@ -166,9 +166,6 @@ main(int argc, char *argv[]) 383 } 384 } 385 386 - /* open a new process group */ 387 - setpgid(0, 0); 388 - 389 handlesignals(sigcleanup); 390 /* 391 * create the (non-blocking) listening socket 392 -- 393 2.52.0 394