Introduction
Introduction Statistics Contact Development Disclaimer Help
quark-digestauth-20200916-5d0221d.diff - sites - public wiki contents of suckle…
git clone git://git.suckless.org/sites
Log
Files
Refs
---
quark-digestauth-20200916-5d0221d.diff (25577B)
---
1 From e0efcece3647fad31ca2750aaf59dd39dd192496 Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Jos=C3=A9=20Miguel=20S=C3=A1nchez=20Garc=C3=ADa?=
3 <[email protected]>
4 Date: Thu, 29 Oct 2020 10:05:27 +0000
5 Subject: [PATCH] Add Digest auth support
6
7 This follows RFC 7616, but only MD5 algorithm and auth qop is supported.
8 ---
9 Makefile | 3 +-
10 config.def.h | 2 +-
11 http.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++--
12 http.h | 28 ++++-
13 main.c | 77 ++++++++++++--
14 md5.c | 148 ++++++++++++++++++++++++++
15 md5.h | 18 ++++
16 quark.1 | 26 +++++
17 util.h | 14 +++
18 9 files changed, 584 insertions(+), 23 deletions(-)
19 create mode 100644 md5.c
20 create mode 100644 md5.h
21
22 diff --git a/Makefile b/Makefile
23 index 548e6aa..6c9e442 100644
24 --- a/Makefile
25 +++ b/Makefile
26 @@ -4,13 +4,14 @@
27
28 include config.mk
29
30 -COMPONENTS = data http sock util
31 +COMPONENTS = data http md5 sock util
32
33 all: quark
34
35 data.o: data.c data.h http.h util.h config.mk
36 http.o: http.c config.h http.h util.h config.mk
37 main.o: main.c arg.h data.h http.h sock.h util.h config.mk
38 +md5.o: md5.c md5.h config.mk
39 sock.o: sock.c sock.h util.h config.mk
40 util.o: util.c util.h config.mk
41
42 diff --git a/config.def.h b/config.def.h
43 index 56f62aa..a322e7a 100644
44 --- a/config.def.h
45 +++ b/config.def.h
46 @@ -2,7 +2,7 @@
47 #define CONFIG_H
48
49 #define BUFFER_SIZE 4096
50 -#define FIELD_MAX 200
51 +#define FIELD_MAX 500
52
53 /* mime-types */
54 static const struct {
55 diff --git a/http.c b/http.c
56 index f1e15a4..1862dc4 100644
57 --- a/http.c
58 +++ b/http.c
59 @@ -17,13 +17,16 @@
60 #include <unistd.h>
61
62 #include "config.h"
63 +#include "data.h"
64 #include "http.h"
65 +#include "md5.h"
66 #include "util.h"
67
68 const char *req_field_str[] = {
69 [REQ_HOST] = "Host",
70 [REQ_RANGE] = "Range",
71 [REQ_IF_MODIFIED_SINCE] = "If-Modified-Since",
72 + [REQ_AUTHORIZATION] = "Authorization",
73 };
74
75 const char *req_method_str[] = {
76 @@ -37,6 +40,7 @@ const char *status_str[] = {
77 [S_MOVED_PERMANENTLY] = "Moved Permanently",
78 [S_NOT_MODIFIED] = "Not Modified",
79 [S_BAD_REQUEST] = "Bad Request",
80 + [S_UNAUTHORIZED] = "Unauthorized",
81 [S_FORBIDDEN] = "Forbidden",
82 [S_NOT_FOUND] = "Not Found",
83 [S_METHOD_NOT_ALLOWED] = "Method Not Allowed",
84 @@ -55,6 +59,7 @@ const char *res_field_str[] = {
85 [RES_CONTENT_LENGTH] = "Content-Length",
86 [RES_CONTENT_RANGE] = "Content-Range",
87 [RES_CONTENT_TYPE] = "Content-Type",
88 + [RES_AUTHENTICATE] = "WWW-Authenticate",
89 };
90
91 enum status
92 @@ -75,8 +80,9 @@ http_prepare_header_buf(const struct response *res, st…
93 if (buffer_appendf(buf,
94 "HTTP/1.1 %d %s\r\n"
95 "Date: %s\r\n"
96 - "Connection: close\r\n",
97 - res->status, status_str[res->status], tstmp)…
98 + "Connection: %s\r\n",
99 + res->status, status_str[res->status], tstmp,
100 + res->keep_alive ? "keep-alive" : "close")) {
101 goto err;
102 }
103
104 @@ -527,21 +533,197 @@ parse_range(const char *str, size_t size, size_t …
105 return 0;
106 }
107
108 +static enum status
109 +parse_auth(const char *str, struct auth *auth)
110 +{
111 + const char *p;
112 + char *q;
113 +
114 + /* done if no range-string is given */
115 + if (str == NULL || *str == '\0') {
116 + return 0;
117 + }
118 +
119 + /* skip method authentication statement */
120 + if (strncmp(str, "Digest", sizeof("Digest") - 1)) {
121 + return S_BAD_REQUEST;
122 + }
123 +
124 + p = str + (sizeof("Digest") - 1);
125 +
126 + /*
127 + * Almost all the fields are quoted-string with no restrictions.
128 + *
129 + * However, some of them require special parsing, which is done…
130 + * and continue the loop early, before reaching the quoted-stri…
131 + * parser.
132 + */
133 + while (*p) {
134 + /* skip leading whitespace */
135 + for (++p; *p == ' ' || *p == '\t'; p++)
136 + ;
137 +
138 + if (!strncmp("qop=", p,
139 + sizeof("qop=") - 1)) {
140 + p += sizeof("qop=") - 1;
141 + q = auth->qop;
142 + /* "qop" is handled differently */
143 + while (*p && *p != ',') {
144 + if (*p == '\\') {
145 + ++p;
146 + }
147 + *q++ = *p++;
148 + }
149 + *q = '\0';
150 +
151 + continue;
152 + } else if (!strncmp("algorithm=", p,
153 + sizeof("algorithm=") - 1)) {
154 + p += sizeof("algorithm=") - 1;
155 + q = auth->algorithm;
156 + /* "algorithm" is handled differently */
157 + while (*p && *p != ',') {
158 + if (*p == '\\') {
159 + ++p;
160 + }
161 + *q++ = *p++;
162 + }
163 + *q = '\0';
164 +
165 + continue;
166 + } else if (!strncmp("nc=", p,
167 + sizeof("nc=") - 1)) {
168 + p += sizeof("nc=") - 1;
169 + q = auth->nc;
170 + /* "nc" is handled differently */
171 + while (*p && *p != ',') {
172 + if (*p < '0' || *p > '9') {
173 + return S_BAD_REQUEST;
174 + }
175 + *q++ = *p++;
176 + }
177 + *q = '\0';
178 +
179 + continue;
180 + /* these all are quoted-string */
181 + } else if (!strncmp("response=\"", p,
182 + sizeof("response=\"") - 1)) {
183 + p += sizeof("response=\"") - 1;
184 + q = auth->response;
185 + } else if (!strncmp("username=\"", p,
186 + sizeof("username=\"") - 1)) {
187 + p += sizeof("username=\"") - 1;
188 + q = auth->username;
189 + } else if (!strncmp("realm=\"", p,
190 + sizeof("realm=\"") - 1)) {
191 + p += sizeof("realm=\"") - 1;
192 + q = auth->realm;
193 + } else if (!strncmp("uri=\"", p,
194 + sizeof("uri=\"") - 1)) {
195 + p += sizeof("uri=\"") - 1;
196 + q = auth->uri;
197 + } else if (!strncmp("cnonce=\"", p,
198 + sizeof("cnonce=\"") - 1)) {
199 + p += sizeof("cnonce=\"") - 1;
200 + q = auth->cnonce;
201 + } else if (!strncmp("nonce=\"", p,
202 + sizeof("nonce=\"") - 1)) {
203 + p += sizeof("nonce=\"") - 1;
204 + q = auth->nonce;
205 + } else {
206 + return S_BAD_REQUEST;
207 + }
208 +
209 + /* parse quoted-string */
210 + while (*p != '"') {
211 + if (*p == '\\') {
212 + ++p;
213 + }
214 + if (!*p) {
215 + return S_BAD_REQUEST;
216 + }
217 + *q++ = *p++;
218 + }
219 + *q = '\0';
220 + ++p;
221 + }
222 +
223 + /* skip trailing whitespace */
224 + for (++p; *p == ' ' || *p == '\t'; p++)
225 + ;
226 +
227 + if (*p) {
228 + return S_BAD_REQUEST;
229 + }
230 +
231 + return 0;
232 +}
233 +
234 +static enum status
235 +prepare_digest(char response[MD5_DIGEST_LENGTH * 2 + 1],
236 + enum req_method method, const char *uri, const uint8_t *…
237 + const char *nonce, const char *nc, const char *cnonce,
238 + const char *qop)
239 +{
240 + uint8_t a2[MD5_DIGEST_LENGTH], kdr[MD5_DIGEST_LENGTH];
241 + char scratch[FIELD_MAX];
242 + struct md5 md5;
243 + unsigned int i;
244 + char *p;
245 +
246 + /* calculate H(A2) */
247 + if (esnprintf(scratch, sizeof(scratch), "%s:%s",
248 + req_method_str[method], uri)) {
249 + return S_INTERNAL_SERVER_ERROR;
250 + }
251 +
252 + md5_init(&md5);
253 + md5_update(&md5, scratch, strlen(scratch));
254 + md5_sum(&md5, a2);
255 +
256 + /* calculate response */
257 + if (esnprintf(scratch, sizeof(scratch), "%s:%s:%s:%s:%s:%-*x",
258 + a1, nonce, nc, cnonce, qop,
259 + MD5_DIGEST_LENGTH * 2 + 1, 0)) {
260 + return S_INTERNAL_SERVER_ERROR;
261 + }
262 +
263 + /* replace trailing string of '-' inside scratch with actual H(…
264 + p = &scratch[strlen(scratch) - (MD5_DIGEST_LENGTH * 2 + 1)];
265 + for (i = 0; i < sizeof(a2); i++) {
266 + sprintf(&p[i << 1], "%02x", a2[i]);
267 + }
268 +
269 + md5_init(&md5);
270 + md5_update(&md5, scratch, strlen(scratch));
271 + md5_sum(&md5, kdr);
272 +
273 + for (i = 0; i < sizeof(kdr); i++) {
274 + sprintf(&response[i << 1], "%02x", kdr[i]);
275 + }
276 +
277 + return 0;
278 +}
279 +
280 #undef RELPATH
281 #define RELPATH(x) ((!*(x) || !strcmp(x, "/")) ? "." : ((x) + 1))
282
283 void
284 -http_prepare_response(const struct request *req, struct response *res,
285 - const struct server *srv)
286 +http_prepare_response(struct request *req, struct response *res,
287 + char nonce[FIELD_MAX], const struct server *srv)
288 {
289 enum status s;
290 struct in6_addr addr;
291 struct stat st;
292 struct tm tm = { 0 };
293 + struct auth auth = { 0 };
294 struct vhost *vhost;
295 + struct realm *realm;
296 + struct account *account;
297 size_t len, i;
298 int hasport, ipv6host;
299 static char realuri[PATH_MAX], tmpuri[PATH_MAX];
300 + char response[MD5_DIGEST_LENGTH * 2 + 1];
301 char *p, *mime;
302 const char *targethost;
303
304 @@ -787,14 +969,63 @@ http_prepare_response(const struct request *req, s…
305 }
306 }
307
308 - /* fill response struct */
309 - res->type = RESTYPE_FILE;
310 -
311 /* check if file is readable */
312 res->status = (access(res->path, R_OK)) ? S_FORBIDDEN :
313 (req->field[REQ_RANGE][0] != '\0') ?
314 S_PARTIAL_CONTENT : S_OK;
315
316 + /* check if the client is authorized */
317 + realm = NULL;
318 + if (srv->realm) {
319 + for (i = 0; i < srv->realm_len; i++) {
320 + if (srv->realm[i].gid == st.st_gid) {
321 + realm = &(srv->realm[i]);
322 + break;
323 + }
324 + }
325 + req->realm = realm;
326 + /* if the file belongs to a realm */
327 + if (i < srv->realm_len) {
328 + if (req->field[REQ_AUTHORIZATION][0] == '\0') {
329 + s = S_UNAUTHORIZED;
330 + goto err;
331 + }
332 + if ((s = parse_auth(req->field[REQ_AUTHORIZATIO…
333 + &auth))) {
334 + goto err;
335 + }
336 + /* look for the requested user */
337 + for (i = 0; i < realm->account_len; i++) {
338 + if (!strcmp(auth.username,
339 + realm->account[i].username)…
340 + account = &(realm->account[i]);
341 + break;
342 + }
343 + }
344 + if (i == realm->account_len) {
345 + s = S_UNAUTHORIZED;
346 + goto err;
347 + }
348 + if ((s = prepare_digest(response, req->method,
349 + auth.uri,
350 + (uint8_t *)account->cry…
351 + nonce, auth.nc,
352 + auth.cnonce, auth.qop))…
353 + goto err;
354 + }
355 + if (strcmp(auth.nonce, nonce)) {
356 + req->stale = 1;
357 + }
358 + if (strncmp(response, auth.response, sizeof(res…
359 + s = S_UNAUTHORIZED;
360 + goto err;
361 + }
362 + }
363 + }
364 +
365 + /* fill response struct */
366 + res->type = RESTYPE_FILE;
367 +
368 if (esnprintf(res->field[RES_ACCEPT_RANGES],
369 sizeof(res->field[RES_ACCEPT_RANGES]),
370 "%s", "bytes")) {
371 @@ -832,17 +1063,22 @@ http_prepare_response(const struct request *req, …
372
373 return;
374 err:
375 - http_prepare_error_response(req, res, s);
376 + http_prepare_error_response(req, res, nonce, s);
377 }
378
379 void
380 http_prepare_error_response(const struct request *req,
381 - struct response *res, enum status s)
382 + struct response *res, char nonce[FIELD_MAX],
383 + enum status s)
384 {
385 + struct timespec ts;
386 + struct buffer buf;
387 + size_t progress;
388 +
389 /* used later */
390 (void)req;
391
392 - /* empty all response fields */
393 + /* empty all fields */
394 memset(res, 0, sizeof(*res));
395
396 res->type = RESTYPE_ERROR;
397 @@ -861,4 +1097,39 @@ http_prepare_error_response(const struct request *…
398 res->status = S_INTERNAL_SERVER_ERROR;
399 }
400 }
401 +
402 + if (res->status == S_UNAUTHORIZED) {
403 + clock_gettime(CLOCK_MONOTONIC, &ts);
404 + if (esnprintf(nonce, FIELD_MAX,
405 + "%lus, %luns, %s",
406 + ts.tv_sec, ts.tv_nsec,
407 + req->realm->name)) {
408 + res->status = S_INTERNAL_SERVER_ERROR;
409 + }
410 + if (esnprintf(res->field[RES_AUTHENTICATE],
411 + sizeof(res->field[RES_AUTHENTICATE]),
412 + "Digest "
413 + "realm=\"%s\", "
414 + "qop=\"auth\", "
415 + "algorithm=MD5, "
416 + "stale=%s, "
417 + "nonce=\"%s\"",
418 + req->realm->name,
419 + req->stale ? "true" : "false",
420 + nonce)) {
421 + res->status = S_INTERNAL_SERVER_ERROR;
422 + } else {
423 + res->keep_alive = 1;
424 + }
425 + }
426 +
427 + progress = 0;
428 + if (data_prepare_error_buf(res, &buf, &progress)
429 + || esnprintf(res->field[RES_CONTENT_LENGTH],
430 + sizeof(res->field[RES_CONTENT_LENGTH]),
431 + "%zu", buf.len)) {
432 + res->field[RES_CONTENT_LENGTH][0] = '\0';
433 + res->keep_alive = 0;
434 + s = S_INTERNAL_SERVER_ERROR;
435 + }
436 }
437 diff --git a/http.h b/http.h
438 index bfaa807..215bb8f 100644
439 --- a/http.h
440 +++ b/http.h
441 @@ -12,6 +12,7 @@ enum req_field {
442 REQ_HOST,
443 REQ_RANGE,
444 REQ_IF_MODIFIED_SINCE,
445 + REQ_AUTHORIZATION,
446 NUM_REQ_FIELDS,
447 };
448
449 @@ -28,6 +29,8 @@ extern const char *req_method_str[];
450 struct request {
451 enum req_method method;
452 char uri[PATH_MAX];
453 + struct realm *realm;
454 + int stale;
455 char field[NUM_REQ_FIELDS][FIELD_MAX];
456 };
457
458 @@ -37,6 +40,7 @@ enum status {
459 S_MOVED_PERMANENTLY = 301,
460 S_NOT_MODIFIED = 304,
461 S_BAD_REQUEST = 400,
462 + S_UNAUTHORIZED = 401,
463 S_FORBIDDEN = 403,
464 S_NOT_FOUND = 404,
465 S_METHOD_NOT_ALLOWED = 405,
466 @@ -57,6 +61,7 @@ enum res_field {
467 RES_CONTENT_LENGTH,
468 RES_CONTENT_RANGE,
469 RES_CONTENT_TYPE,
470 + RES_AUTHENTICATE,
471 NUM_RES_FIELDS,
472 };
473
474 @@ -72,6 +77,7 @@ enum res_type {
475 struct response {
476 enum res_type type;
477 enum status status;
478 + int keep_alive;
479 char field[NUM_RES_FIELDS][FIELD_MAX];
480 char uri[PATH_MAX];
481 char path[PATH_MAX];
482 @@ -83,6 +89,7 @@ struct response {
483
484 enum conn_state {
485 C_VACANT,
486 + C_START,
487 C_RECV_HEADER,
488 C_SEND_HEADER,
489 C_SEND_BODY,
490 @@ -91,6 +98,7 @@ enum conn_state {
491
492 struct connection {
493 enum conn_state state;
494 + char nonce[FIELD_MAX];
495 int fd;
496 struct sockaddr_storage ia;
497 struct request req;
498 @@ -99,13 +107,25 @@ struct connection {
499 size_t progress;
500 };
501
502 +struct auth {
503 + char response[FIELD_MAX];
504 + char username[FIELD_MAX];
505 + char realm[FIELD_MAX];
506 + char uri[FIELD_MAX];
507 + char qop[FIELD_MAX];
508 + char cnonce[FIELD_MAX];
509 + char nonce[FIELD_MAX];
510 + char algorithm[FIELD_MAX];
511 + char nc[FIELD_MAX];
512 +};
513 +
514 enum status http_prepare_header_buf(const struct response *, struct buf…
515 enum status http_send_buf(int, struct buffer *);
516 enum status http_recv_header(int, struct buffer *, int *);
517 enum status http_parse_header(const char *, struct request *);
518 -void http_prepare_response(const struct request *, struct response *,
519 - const struct server *);
520 -void http_prepare_error_response(const struct request *,
521 - struct response *, enum status);
522 +void http_prepare_response(struct request *, struct response *,
523 + char nonce[FIELD_MAX], const struct server *…
524 +void http_prepare_error_response(const struct request *, struct respons…
525 + char nonce[FIELD_MAX], enum status);
526
527 #endif /* HTTP_H */
528 diff --git a/main.c b/main.c
529 index d64774b..b45ad15 100644
530 --- a/main.c
531 +++ b/main.c
532 @@ -60,11 +60,17 @@ serve(struct connection *c, const struct server *srv)
533
534 switch (c->state) {
535 case C_VACANT:
536 + /* we were passed a "fresh" connection, reset all state…
537 +
538 + c->state = C_START;
539 + /* fallthrough */
540 + case C_START:
541 /*
542 - * we were passed a "fresh" connection which should now
543 - * try to receive the header, reset buf beforehand
544 + * we start handling a request, so we first must try to
545 + * receive the header, reset buf beforehand
546 */
547 memset(&c->buf, 0, sizeof(c->buf));
548 + c->progress = 0;
549
550 c->state = C_RECV_HEADER;
551 /* fallthrough */
552 @@ -72,7 +78,7 @@ serve(struct connection *c, const struct server *srv)
553 /* receive header */
554 done = 0;
555 if ((s = http_recv_header(c->fd, &c->buf, &done))) {
556 - http_prepare_error_response(&c->req, &c->res, s…
557 + http_prepare_error_response(&c->req, &c->res, c…
558 goto response;
559 }
560 if (!done) {
561 @@ -82,16 +88,16 @@ serve(struct connection *c, const struct server *srv)
562
563 /* parse header */
564 if ((s = http_parse_header(c->buf.data, &c->req))) {
565 - http_prepare_error_response(&c->req, &c->res, s…
566 + http_prepare_error_response(&c->req, &c->res, c…
567 goto response;
568 }
569
570 /* prepare response struct */
571 - http_prepare_response(&c->req, &c->res, srv);
572 + http_prepare_response(&c->req, &c->res, c->nonce, srv);
573 response:
574 /* generate response header */
575 if ((s = http_prepare_header_buf(&c->res, &c->buf))) {
576 - http_prepare_error_response(&c->req, &c->res, s…
577 + http_prepare_error_response(&c->req, &c->res, c…
578 if ((s = http_prepare_header_buf(&c->res, &c->b…
579 /* couldn't generate the header, we fai…
580 c->res.status = s;
581 @@ -146,6 +152,20 @@ response:
582 err:
583 logmsg(c);
584
585 + /* don't cleanup if we keep the connection alive */
586 + if (c->res.keep_alive) {
587 + /*
588 + * if the length is unspecified, a keep-alive connectio…
589 + * wait timeout: kill the connection to avoid it
590 + */
591 + if (c->res.field[RES_CONTENT_LENGTH][0] == '\0') {
592 + c->res.status = S_INTERNAL_SERVER_ERROR;
593 + } else {
594 + c->state = C_START;
595 + return;
596 + }
597 + }
598 +
599 /* clean up and finish */
600 shutdown(c->fd, SHUT_RD);
601 shutdown(c->fd, SHUT_WR);
602 @@ -257,7 +277,8 @@ static void
603 usage(void)
604 {
605 const char *opts = "[-u user] [-g group] [-n num] [-d dir] [-l]…
606 - "[-i file] [-v vhost] ... [-m map] ...";
607 + "[-i file] [-v vhost] ... [-m map] ... "
608 + "[-r realm] ... [-a account] ...";
609
610 die("usage: %s -p port [-h host] %s\n"
611 " %s -U file [-p port] %s", argv0,
612 @@ -273,6 +294,7 @@ main(int argc, char *argv[])
613 struct server srv = {
614 .docindex = "index.html",
615 };
616 + struct realm *realm;
617 size_t i;
618 int insock, status = 0;
619 const char *err;
620 @@ -285,6 +307,29 @@ main(int argc, char *argv[])
621 char *group = "nogroup";
622
623 ARGBEGIN {
624 + case 'a':
625 + if (spacetok(EARGF(usage()), tok, 3) || !tok[0] || !tok…
626 + !tok[2]) {
627 + usage();
628 + }
629 + realm = NULL;
630 + for (i = 0; i < srv.realm_len; i++) {
631 + if (!strcmp(srv.realm[i].name, tok[0])) {
632 + realm = &(srv.realm[i]);
633 + break;
634 + }
635 + }
636 + if (!realm) {
637 + die("Realm '%s' not found", tok[0]);
638 + }
639 + if (!(realm->account = reallocarray(realm->account,
640 + ++realm->account_len,
641 + sizeof(struct account)…
642 + die("reallocarray:");
643 + }
644 + realm->account[realm->account_len - 1].username = tok[1…
645 + realm->account[realm->account_len - 1].crypt = tok[2…
646 + break;
647 case 'd':
648 servedir = EARGF(usage());
649 break;
650 @@ -324,6 +369,24 @@ main(int argc, char *argv[])
651 case 'p':
652 srv.port = EARGF(usage());
653 break;
654 + case 'r':
655 + if (spacetok(EARGF(usage()), tok, 2) || !tok[0] || !tok…
656 + usage();
657 + }
658 + errno = 0;
659 + if (!(grp = getgrnam(tok[0]))) {
660 + die("getgrnam '%s': %s", tok[0] ? tok[0] : "nul…
661 + errno ? strerror(errno) : "Entry not found"…
662 + }
663 + if (!(srv.realm = reallocarray(srv.realm, ++srv.realm_l…
664 + sizeof(struct realm)))) {
665 + die("reallocarray:");
666 + }
667 + srv.realm[srv.realm_len - 1].gid = grp->gr_gid;
668 + srv.realm[srv.realm_len - 1].name = tok[1];
669 + srv.realm[srv.realm_len - 1].account = NULL;
670 + srv.realm[srv.realm_len - 1].account_len = 0;
671 + break;
672 case 'U':
673 udsname = EARGF(usage());
674 break;
675 diff --git a/md5.c b/md5.c
676 new file mode 100644
677 index 0000000..f56a501
678 --- /dev/null
679 +++ b/md5.c
680 @@ -0,0 +1,148 @@
681 +/* public domain md5 implementation based on rfc1321 and libtomcrypt */
682 +#include <stdint.h>
683 +#include <string.h>
684 +
685 +#include "md5.h"
686 +
687 +static uint32_t rol(uint32_t n, int k) { return (n << k) | (n >> (32-k)…
688 +#define F(x,y,z) (z ^ (x & (y ^ z)))
689 +#define G(x,y,z) (y ^ (z & (y ^ x)))
690 +#define H(x,y,z) (x ^ y ^ z)
691 +#define I(x,y,z) (y ^ (x | ~z))
692 +#define FF(a,b,c,d,w,s,t) a += F(b,c,d) + w + t; a = rol(a,s) + b
693 +#define GG(a,b,c,d,w,s,t) a += G(b,c,d) + w + t; a = rol(a,s) + b
694 +#define HH(a,b,c,d,w,s,t) a += H(b,c,d) + w + t; a = rol(a,s) + b
695 +#define II(a,b,c,d,w,s,t) a += I(b,c,d) + w + t; a = rol(a,s) + b
696 +
697 +static const uint32_t tab[64] = {
698 + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4…
699 + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xf…
700 + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x0…
701 + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xf…
702 + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4…
703 + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe…
704 + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8…
705 + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xb…
706 +};
707 +
708 +static void
709 +processblock(struct md5 *s, const uint8_t *buf)
710 +{
711 + uint32_t i, W[16], a, b, c, d;
712 +
713 + for (i = 0; i < 16; i++) {
714 + W[i] = buf[4*i];
715 + W[i] |= (uint32_t)buf[4*i+1]<<8;
716 + W[i] |= (uint32_t)buf[4*i+2]<<16;
717 + W[i] |= (uint32_t)buf[4*i+3]<<24;
718 + }
719 +
720 + a = s->h[0];
721 + b = s->h[1];
722 + c = s->h[2];
723 + d = s->h[3];
724 +
725 + i = 0;
726 + while (i < 16) {
727 + FF(a,b,c,d, W[i], 7, tab[i]); i++;
728 + FF(d,a,b,c, W[i], 12, tab[i]); i++;
729 + FF(c,d,a,b, W[i], 17, tab[i]); i++;
730 + FF(b,c,d,a, W[i], 22, tab[i]); i++;
731 + }
732 + while (i < 32) {
733 + GG(a,b,c,d, W[(5*i+1)%16], 5, tab[i]); i++;
734 + GG(d,a,b,c, W[(5*i+1)%16], 9, tab[i]); i++;
735 + GG(c,d,a,b, W[(5*i+1)%16], 14, tab[i]); i++;
736 + GG(b,c,d,a, W[(5*i+1)%16], 20, tab[i]); i++;
737 + }
738 + while (i < 48) {
739 + HH(a,b,c,d, W[(3*i+5)%16], 4, tab[i]); i++;
740 + HH(d,a,b,c, W[(3*i+5)%16], 11, tab[i]); i++;
741 + HH(c,d,a,b, W[(3*i+5)%16], 16, tab[i]); i++;
742 + HH(b,c,d,a, W[(3*i+5)%16], 23, tab[i]); i++;
743 + }
744 + while (i < 64) {
745 + II(a,b,c,d, W[7*i%16], 6, tab[i]); i++;
746 + II(d,a,b,c, W[7*i%16], 10, tab[i]); i++;
747 + II(c,d,a,b, W[7*i%16], 15, tab[i]); i++;
748 + II(b,c,d,a, W[7*i%16], 21, tab[i]); i++;
749 + }
750 +
751 + s->h[0] += a;
752 + s->h[1] += b;
753 + s->h[2] += c;
754 + s->h[3] += d;
755 +}
756 +
757 +static void
758 +pad(struct md5 *s)
759 +{
760 + unsigned r = s->len % 64;
761 +
762 + s->buf[r++] = 0x80;
763 + if (r > 56) {
764 + memset(s->buf + r, 0, 64 - r);
765 + r = 0;
766 + processblock(s, s->buf);
767 + }
768 + memset(s->buf + r, 0, 56 - r);
769 + s->len *= 8;
770 + s->buf[56] = s->len;
771 + s->buf[57] = s->len >> 8;
772 + s->buf[58] = s->len >> 16;
773 + s->buf[59] = s->len >> 24;
774 + s->buf[60] = s->len >> 32;
775 + s->buf[61] = s->len >> 40;
776 + s->buf[62] = s->len >> 48;
777 + s->buf[63] = s->len >> 56;
778 + processblock(s, s->buf);
779 +}
780 +
781 +void
782 +md5_init(void *ctx)
783 +{
784 + struct md5 *s = ctx;
785 + s->len = 0;
786 + s->h[0] = 0x67452301;
787 + s->h[1] = 0xefcdab89;
788 + s->h[2] = 0x98badcfe;
789 + s->h[3] = 0x10325476;
790 +}
791 +
792 +void
793 +md5_sum(void *ctx, uint8_t md[MD5_DIGEST_LENGTH])
794 +{
795 + struct md5 *s = ctx;
796 + int i;
797 +
798 + pad(s);
799 + for (i = 0; i < 4; i++) {
800 + md[4*i] = s->h[i];
801 + md[4*i+1] = s->h[i] >> 8;
802 + md[4*i+2] = s->h[i] >> 16;
803 + md[4*i+3] = s->h[i] >> 24;
804 + }
805 +}
806 +
807 +void
808 +md5_update(void *ctx, const void *m, unsigned long len)
809 +{
810 + struct md5 *s = ctx;
811 + const uint8_t *p = m;
812 + unsigned r = s->len % 64;
813 +
814 + s->len += len;
815 + if (r) {
816 + if (len < 64 - r) {
817 + memcpy(s->buf + r, p, len);
818 + return;
819 + }
820 + memcpy(s->buf + r, p, 64 - r);
821 + len -= 64 - r;
822 + p += 64 - r;
823 + processblock(s, s->buf);
824 + }
825 + for (; len >= 64; len -= 64, p += 64)
826 + processblock(s, p);
827 + memcpy(s->buf, p, len);
828 +}
829 diff --git a/md5.h b/md5.h
830 new file mode 100644
831 index 0000000..0b5005e
832 --- /dev/null
833 +++ b/md5.h
834 @@ -0,0 +1,18 @@
835 +/* public domain md5 implementation based on rfc1321 and libtomcrypt */
836 +
837 +struct md5 {
838 + uint64_t len; /* processed message length */
839 + uint32_t h[4]; /* hash state */
840 + uint8_t buf[64]; /* message block buffer */
841 +};
842 +
843 +enum { MD5_DIGEST_LENGTH = 16 };
844 +
845 +/* reset state */
846 +void md5_init(void *ctx);
847 +/* process message */
848 +void md5_update(void *ctx, const void *m, unsigned long len);
849 +/* get message digest */
850 +/* state is ruined after sum, keep a copy if multiple sum is needed */
851 +/* part of the message might be left in s, zero it if secrecy is needed…
852 +void md5_sum(void *ctx, uint8_t md[MD5_DIGEST_LENGTH]);
853 diff --git a/quark.1 b/quark.1
854 index 6e0e5f8..3394639 100644
855 --- a/quark.1
856 +++ b/quark.1
857 @@ -16,6 +16,8 @@
858 .Op Fl i Ar file
859 .Oo Fl v Ar vhost Oc ...
860 .Oo Fl m Ar map Oc ...
861 +.Oo Fl r Ar realm Oc ...
862 +.Oo Fl a Ar account Oc ...
863 .Nm
864 .Fl U Ar file
865 .Op Fl p Ar port
866 @@ -27,6 +29,8 @@
867 .Op Fl i Ar file
868 .Oo Fl v Ar vhost Oc ...
869 .Oo Fl m Ar map Oc ...
870 +.Oo Fl r Ar realm Oc ...
871 +.Oo Fl a Ar account Oc ...
872 .Sh DESCRIPTION
873 .Nm
874 is a simple HTTP GET/HEAD-only web server for static content.
875 @@ -36,11 +40,26 @@ explicit redirects (see
876 .Fl m ) ,
877 directory listings (see
878 .Fl l ) ,
879 +Digest authentication (RFC 7616, see
880 +.Fl r
881 +and
882 +.Fl a ) ,
883 conditional "If-Modified-Since"-requests (RFC 7232), range requests
884 (RFC 7233) and well-known URIs (RFC 8615), while refusing to serve
885 hidden files and directories.
886 .Sh OPTIONS
887 .Bl -tag -width Ds
888 +.It Fl a Ar account
889 +Add the account specified by
890 +.Ar account ,
891 +which has the form
892 +.Qq Pa realm username crypt ,
893 +where each element is separated with spaces (0x20) that can be
894 +escaped with '\\'. The
895 +.Pa crypt
896 +parameter can be generated as follows:
897 +.Pp
898 +echo -n 'username:realm:password' | md5sum | awk '{ print $1 }'
899 .It Fl d Ar dir
900 Serve
901 .Ar dir
902 @@ -92,6 +111,13 @@ In socket mode, use
903 .Ar port
904 for constructing proper virtual host
905 redirects on non-standard ports.
906 +.It Fl r Ar realm
907 +Add mapping from group to realm as specified by
908 +.Ar realm ,
909 +which has the form
910 +.Qq Pa group name ,
911 +where each element is separated with spaces (0x20) that can be
912 +escaped with '\\'.
913 .It Fl U Ar file
914 Create the UNIX-domain socket
915 .Ar file ,
916 diff --git a/util.h b/util.h
917 index 983abd2..0307a34 100644
918 --- a/util.h
919 +++ b/util.h
920 @@ -23,6 +23,18 @@ struct map {
921 char *to;
922 };
923
924 +struct account {
925 + char *username;
926 + char *crypt;
927 +};
928 +
929 +struct realm {
930 + gid_t gid;
931 + char *name;
932 + struct account *account;
933 + size_t account_len;
934 +};
935 +
936 struct server {
937 char *host;
938 char *port;
939 @@ -32,6 +44,8 @@ struct server {
940 size_t vhost_len;
941 struct map *map;
942 size_t map_len;
943 + struct realm *realm;
944 + size_t realm_len;
945 };
946
947 /* general purpose buffer */
948 --
949 2.29.0
950
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.