[hackers] [quark] http: fix default index serving

2021-01-17 Thread Quentin Rameau
The previous code would find and stat the default index file,
but would not append it to the file served, resulting in
giving back a 0-length content but with a Content-Length
header of the size of the index.
---
 http.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/http.c b/http.c
index dc32290..96dd540 100644
--- a/http.c
+++ b/http.c
@@ -761,6 +761,11 @@ http_prepare_response(const struct request *req, struct 
response *res,
goto err;
}
}
+   /* copy the found index back to the final path */
+   if (esnprintf(res->path, sizeof(res->path), "%s", tmpuri)) {
+   s = S_REQUEST_TOO_LARGE;
+   goto err;
+   }
}
 
/* modified since */
-- 
2.28.0




[hackers] [quark][PATCH] Add a config switch to enable/disable NPROC limit

2021-01-17 Thread Giulio Picierro
Quoting the book "The Linux Programming Interface" from Micheal Kerrisk:
"the RLIMIT_NPROC limit, which places a limit on the number of processes that
can be created, is measured against not just that process’s consumption of the
corresponding resource, but also against the sum of resources consumed by all
processes with the same real user ID."

This leads quark to easily fail on Linux when launched with the same userid of
a logged user.

For example if the user 'giulio' has an active desktop session, the following
command:

$ sudo ./quark -p 8080 -u giulio -g giulio -l

fails with the following error:

$ ./quark: pthread_create: Resource temporarily unavailable

No error occour if instead quark is launched with an userid that does not have
a session, such as the 'http' user, usually reserved for web servers.

I don't know if this is expected or this could be considered a bug: in the end
for production servers we could expect that the limit works correctly.

In any case, the least invasive way that I have found to solve the issue is to
introduce a config switch to disable the limit, retaining it enabled by default.
---
 Makefile | 2 +-
 config.def.h | 2 ++
 main.c   | 3 +++
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index da0e458..61da150 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ all: quark
 
 data.o: data.c data.h http.h util.h config.mk
 http.o: http.c config.h http.h util.h config.mk
-main.o: main.c arg.h data.h http.h queue.h sock.h util.h config.mk
+main.o: config.h main.c arg.h data.h http.h queue.h sock.h util.h config.mk
 sock.o: sock.c sock.h util.h config.mk
 util.o: util.c util.h config.mk
 
diff --git a/config.def.h b/config.def.h
index 56f62aa..7cccad2 100644
--- a/config.def.h
+++ b/config.def.h
@@ -4,6 +4,8 @@
 #define BUFFER_SIZE 4096
 #define FIELD_MAX   200
 
+#define ENABLE_NPROC_LIMIT 1
+
 /* mime-types */
 static const struct {
char *ext;
diff --git a/main.c b/main.c
index 86b2d0c..f09b5db 100644
--- a/main.c
+++ b/main.c
@@ -19,6 +19,7 @@
 
 #include "arg.h"
 #include "data.h"
+#include "config.h"
 #include "http.h"
 #include "queue.h"
 #include "sock.h"
@@ -646,6 +647,7 @@ main(int argc, char *argv[])
}
 
/* set the thread limit (2 + nthreads) */
+#if ENABLE_NPROC_LIMIT
rlim.rlim_cur = rlim.rlim_max = 2 + nthreads;
if (setrlimit(RLIMIT_NPROC, ) < 0) {
if (errno == EPERM) {
@@ -655,6 +657,7 @@ main(int argc, char *argv[])
die("setrlimit:");
}
}
+#endif
 
/* limit ourselves to reading the servedir and block further 
unveils */
eunveil(servedir, "r");
-- 
2.30.0




Re: [hackers] [quark] Use epoll/kqueue and worker threads to handle connections || Laslo Hunhold

2021-01-17 Thread Laslo Hunhold
On Sun, 17 Jan 2021 12:48:38 +0100
Hiltjo Posthuma  wrote:

Dear Hiltjo,

> This does not work on OpenBSD and it does not compile.

thanks for letting me know! I didn't come around to testing it on
OpenBSD yet, but did it now and pushed a fix[0].

With best regards

Laslo

[0]:https://git.suckless.org/quark/commit/959c855734e3af12f35532d76deb1ab85474f8f4.html



[hackers] [quark] Fix compilation on OpenBSD || Laslo Hunhold

2021-01-17 Thread git
commit 959c855734e3af12f35532d76deb1ab85474f8f4
Author: Laslo Hunhold 
AuthorDate: Sun Jan 17 13:22:53 2021 +0100
Commit: Laslo Hunhold 
CommitDate: Sun Jan 17 13:22:53 2021 +0100

Fix compilation on OpenBSD

The OpenBSD-code was written "blindly" only with the manuals. The
errors that occured are now fixed. It shows how well-written the
OpenBSD manuals are that you can write such a nontrivial state-machine
and it just works (once the syntax-errors are fixed).

Signed-off-by: Laslo Hunhold 

diff --git a/main.c b/main.c
index 2e8f3e7..86b2d0c 100644
--- a/main.c
+++ b/main.c
@@ -243,7 +243,7 @@ thread_method(void *data)
 
/* handle events */
for (i = 0; i < (size_t)nready; i++) {
-   if (event[i].events & (EPOLLERR | EPOLLHUP)) {
+   if (queue_event_is_dropped([i])) {
fd = queue_event_get_fd([i]);
 
if (fd != d->insock) {
diff --git a/queue.c b/queue.c
index b8267c8..fce38e2 100644
--- a/queue.c
+++ b/queue.c
@@ -80,11 +80,11 @@ queue_add_fd(int qfd, int fd, enum queue_event_type t, int 
shared,
return 1;
}
#else
-   kevent e;
+   struct kevent e;
int events;
 
/* prepare event flag */
-   event = (shared) ? 0 : EV_CLEAR;
+   events = (shared) ? 0 : EV_CLEAR;
 
switch (t) {
case QUEUE_EVENT_IN:
@@ -95,7 +95,7 @@ queue_add_fd(int qfd, int fd, enum queue_event_type t, int 
shared,
break;
}
 
-   EV_SET(, fd, events, EV_ADD, 0, 0, 0);
+   EV_SET(, fd, events, EV_ADD, 0, 0, (void *)data);
 
if (kevent(qfd, , 1, NULL, 0, NULL) < 0) {
warn("kevent:");
@@ -139,7 +139,7 @@ queue_mod_fd(int qfd, int fd, enum queue_event_type t, 
const void *data)
return 1;
}
#else
-   kevent e;
+   struct kevent e;
int events;
 
events = EV_CLEAR;
@@ -153,7 +153,7 @@ queue_mod_fd(int qfd, int fd, enum queue_event_type t, 
const void *data)
break;
}
 
-   EV_SET(, fd, events, EV_ADD, 0, 0, 0);
+   EV_SET(, fd, events, EV_ADD, 0, 0, (void *)data);
 
if (kevent(qfd, , 1, NULL, 0, NULL) < 0) {
warn("kevent:");
@@ -175,7 +175,7 @@ queue_rem_fd(int qfd, int fd)
return 1;
}
#else
-   kevent e;
+   struct kevent e;
 
EV_SET(, fd, 0, EV_DELETE, 0, 0, 0);
 
@@ -199,7 +199,7 @@ queue_wait(int qfd, queue_event *e, size_t elen)
return -1;
}
#else
-   if ((nready = kevent(qfd, NULL, 0, e, elen, NULL) < 0) {
+   if ((nready = kevent(qfd, NULL, 0, e, elen, NULL)) < 0) {
warn("kevent:");
return 1;
}
@@ -227,3 +227,13 @@ queue_event_get_ptr(const queue_event *e)
return e->udata;
#endif
 }
+
+int
+queue_event_is_dropped(const queue_event *e)
+{
+   #ifdef __linux__
+   return (e->events & (EPOLLERR | EPOLLHUP)) ? 1 : 0;
+   #else
+   return (e->flags & EV_EOF) ? 1 : 0;
+   #endif
+}
diff --git a/queue.h b/queue.h
index 06ab167..a59ab6f 100644
--- a/queue.h
+++ b/queue.h
@@ -29,4 +29,6 @@ ssize_t queue_wait(int, queue_event *, size_t);
 int queue_event_get_fd(const queue_event *);
 void *queue_event_get_ptr(const queue_event *);
 
+int queue_event_is_dropped(const queue_event *e);
+
 #endif /* QUEUE_H */



[hackers] [quark] Also add a proper warning with a hint when bind() fails || Laslo Hunhold

2021-01-17 Thread git
commit a9164839e6f091ff66b6684c65d55ed7f5a09ebb
Author: Laslo Hunhold 
AuthorDate: Sun Jan 17 13:19:45 2021 +0100
Commit: Laslo Hunhold 
CommitDate: Sun Jan 17 13:19:45 2021 +0100

Also add a proper warning with a hint when bind() fails

Signed-off-by: Laslo Hunhold 

diff --git a/sock.c b/sock.c
index 676f604..fd16547 100644
--- a/sock.c
+++ b/sock.c
@@ -1,5 +1,6 @@
 /* See LICENSE file for copyright and license details. */
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -73,7 +74,13 @@ sock_get_ips_arr(const char *host, const char* port, int 
*sockfd,
freeaddrinfo(ai);
if (!p) {
/* we exhaustet the addrinfo-list and found no connection */
-   warn("bind:");
+   if (errno == EACCES) {
+   warn("You need to run as root or have "
+"CAP_NET_BIND_SERVICE set to bind to "
+"privileged ports");
+   } else {
+   warn("bind:");
+   }
return 1;
}
 



[hackers] [quark] Update license || Laslo Hunhold

2021-01-17 Thread git
commit ed712285707ecba554d718ef28ec2fd83fb846be
Author: Laslo Hunhold 
AuthorDate: Sun Jan 17 12:34:23 2021 +0100
Commit: Laslo Hunhold 
CommitDate: Sun Jan 17 12:34:23 2021 +0100

Update license

Signed-off-by: Laslo Hunhold 

diff --git a/LICENSE b/LICENSE
index 8af9075..d211f9d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 ISC-License
 
-Copyright 2016-2020 Laslo Hunhold 
+Copyright 2016-2021 Laslo Hunhold 
 
 Copyright 2004 Ted Unangst 
 Copyright 2004 Todd C. Miller 



Re: [hackers] [quark] Use epoll/kqueue and worker threads to handle connections || Laslo Hunhold

2021-01-17 Thread Hiltjo Posthuma
On Sun, Nov 01, 2020 at 12:34:52AM +0100, g...@suckless.org wrote:
> commit dff98c0bcaef7be220c563ebaebd66f8c6704197
> Author: Laslo Hunhold 
> AuthorDate: Sun Nov 1 00:27:46 2020 +0100
> Commit: Laslo Hunhold 
> CommitDate: Sun Nov 1 00:27:46 2020 +0100
> 
> Use epoll/kqueue and worker threads to handle connections
> 
> This adds quite a bit of code, but is the culmination of the previous
> restructurizations. Each worker thread has a connection pool and the
> interesting part is that it's 100% nonblocking. If reading or writing
> blocks at any point, the worker thread can just drop it and continue
> with something else. This is especially powerful against attacks like
> slow loris, which cannot be caught with a forking-model and could easily
> be used in a DoS against a quark instance.
> 
> There are no memory allocations at runtime, unless you use dirlistings,
> whose libc-allocations you can't work around.
> 
> In case the connection pool is exhausted due to a lot of slow lorises,
> we still hit a DoS, but at least it can now be possible to assess the
> connection pool and just drop another connection that can be
> heuristically assessed as a "malicious" one (e.g. many connections from
> one client, long time in one state or something using a monotonic
> clock).
> 
> Given we still sadly don't have kqueue in linux, which is 1000x times
> better than epoll, which is deeply flawed, I wrote a very thin wrapper
> in queue.{c,h} which exposes the necessary functions in a common
> interface.
> 
> Signed-off-by: Laslo Hunhold 
> 
> diff --git a/Makefile b/Makefile
> index 548e6aa..da0e458 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -4,13 +4,13 @@
>  
>  include config.mk
>  
> -COMPONENTS = data http sock util
> +COMPONENTS = data http queue sock util
>  
>  all: quark
>  
>  data.o: data.c data.h http.h util.h config.mk
>  http.o: http.c config.h http.h util.h config.mk
> -main.o: main.c arg.h data.h http.h sock.h util.h config.mk
> +main.o: main.c arg.h data.h http.h queue.h sock.h util.h config.mk
>  sock.o: sock.c sock.h util.h config.mk
>  util.o: util.c util.h config.mk
>  
> diff --git a/config.mk b/config.mk
> index 7056241..cedb3e7 100644
> --- a/config.mk
> +++ b/config.mk
> @@ -10,7 +10,7 @@ MANPREFIX = $(PREFIX)/share/man
>  # flags
>  CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 
> -D_BSD_SOURCE
>  CFLAGS   = -std=c99 -pedantic -Wall -Wextra -Os
> -LDFLAGS  = -s
> +LDFLAGS  = -lpthread -s
>  
>  # compiler and linker
>  CC = cc
> diff --git a/main.c b/main.c
> index d64774b..e26cc77 100644
> --- a/main.c
> +++ b/main.c
> @@ -3,6 +3,7 @@
>  #include 
>  #include 
>  #include 
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -19,6 +20,7 @@
>  #include "arg.h"
>  #include "data.h"
>  #include "http.h"
> +#include "queue.h"
>  #include "sock.h"
>  #include "util.h"
>  
> @@ -48,16 +50,20 @@ logmsg(const struct connection *c)
>  }
>  
>  static void
> -serve(struct connection *c, const struct server *srv)
> +close_connection(struct connection *c)
> +{
> + if (c != NULL) {
> + close(c->fd);
> + memset(c, 0, sizeof(*c));
> + }
> +}
> +
> +static void
> +serve_connection(struct connection *c, const struct server *srv)
>  {
>   enum status s;
>   int done;
>  
> - /* set connection timeout */
> - if (sock_set_timeout(c->fd, 30)) {
> - warn("sock_set_timeout: Failed");
> - }
> -
>   switch (c->state) {
>   case C_VACANT:
>   /*
> @@ -145,12 +151,212 @@ response:
>   }
>  err:
>   logmsg(c);
> + close_connection(c);
> +}
> +
> +struct connection *
> +accept_connection(int insock, struct connection *connection,
> +  size_t nslots)
> +{
> + struct connection *c = NULL;
> + size_t j;
> +
> + /* find vacant connection (i.e. one with no fd assigned to it) */
> + for (j = 0; j < nslots; j++) {
> + if (connection[j].fd == 0) {
> + c = [j];
> + break;
> + }
> + }
> + if (j == nslots) {
> + /* nothing available right now, return without accepting */
> +
> + /* 
> +  * NOTE: This is currently still not the best option, but
> +  * at least we now have control over it and can reap a
> +  * connection from our pool instead of previously when
> +  * we were forking and were more or less on our own in
> +  * each process
> +  */
> + return NULL;
> + }
> +
> + /* accept connection */
> + if ((c->fd = accept(insock, (struct sockaddr *)>ia,
> + &(socklen_t){sizeof(c->ia)})) < 0) {
> + if (errno != EAGAIN && errno != EWOULDBLOCK) {
> + /* not much we can do here */
> + warn("accept:");
>