1 /* httpd.c -- HTTP/WebDAV/CalDAV server protocol parsing
2  *
3  * Copyright (c) 1994-2011 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any legal
20  *    details, please contact
21  *      Carnegie Mellon University
22  *      Center for Technology Transfer and Enterprise Creation
23  *      4615 Forbes Avenue
24  *      Suite 302
25  *      Pittsburgh, PA  15213
26  *      (412) 268-7393, fax: (412) 268-7395
27  *      innovation@andrew.cmu.edu
28  *
29  * 4. Redistributions of any form whatsoever must retain the following
30  *    acknowledgment:
31  *    "This product includes software developed by Computing Services
32  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33  *
34  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41  *
42  */
43 
44 #include <config.h>
45 
46 
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #include <stdio.h>
51 #include <errno.h>
52 #include <string.h>
53 #include <fcntl.h>
54 #include <signal.h>
55 #include <sys/types.h>
56 #include <sys/param.h>
57 #include <syslog.h>
58 #include <netdb.h>
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61 #include <arpa/inet.h>
62 #include <ctype.h>
63 #include "prot.h"
64 
65 #include <sasl/sasl.h>
66 #include <sasl/saslutil.h>
67 
68 #include "httpd.h"
69 #include "http_proxy.h"
70 
71 #include "acl.h"
72 #include "assert.h"
73 #include "util.h"
74 #include "iptostring.h"
75 #include "global.h"
76 #include "tls.h"
77 #include "map.h"
78 
79 #include "acl.h"
80 #include "exitcodes.h"
81 #include "imapd.h"
82 #include "proc.h"
83 #include "version.h"
84 #include "xstrlcpy.h"
85 #include "xstrlcat.h"
86 #include "sync_log.h"
87 #include "telemetry.h"
88 #include "backend.h"
89 #include "proxy.h"
90 #include "userdeny.h"
91 #include "message.h"
92 #include "idle.h"
93 #include "times.h"
94 #include "tok.h"
95 #include "wildmat.h"
96 #include "md5.h"
97 
98 /* generated headers are not necessarily in current directory */
99 #include "imap/http_err.h"
100 
101 #ifdef WITH_DAV
102 #include "http_dav.h"
103 #endif
104 
105 #include <libxml/tree.h>
106 #include <libxml/HTMLtree.h>
107 #include <libxml/uri.h>
108 
109 #ifdef HAVE_ZLIB
110 #include <zlib.h>
111 #endif /* HAVE_ZLIB */
112 
113 #ifdef HAVE_BROTLI
114 #include <brotli/encode.h>
115 
brotli_init()116 BrotliEncoderState *brotli_init()
117 {
118     BrotliEncoderState *brotli = BrotliEncoderCreateInstance(NULL, NULL, NULL);
119 
120     if (brotli) {
121         BrotliEncoderSetParameter(brotli, BROTLI_PARAM_MODE,
122                                   BROTLI_DEFAULT_MODE);
123         BrotliEncoderSetParameter(brotli, BROTLI_PARAM_QUALITY,
124                                   BROTLI_DEFAULT_QUALITY);
125         BrotliEncoderSetParameter(brotli, BROTLI_PARAM_LGWIN,
126                                   BROTLI_DEFAULT_WINDOW);
127         BrotliEncoderSetParameter(brotli, BROTLI_PARAM_LGBLOCK, 0);
128     }
129 
130     return brotli;
131 }
132 #endif /* HAVE_BROTLI */
133 
134 
135 static const char tls_message[] =
136     HTML_DOCTYPE
137     "<html>\n<head>\n<title>TLS Required</title>\n</head>\n" \
138     "<body>\n<h2>TLS is required prior to authentication</h2>\n" \
139     "Use <a href=\"%s\">%s</a> instead.\n" \
140     "</body>\n</html>\n";
141 
142 extern int optind;
143 extern char *optarg;
144 extern int opterr;
145 
146 #ifdef HAVE_SSL
147 static SSL *tls_conn;
148 #endif /* HAVE_SSL */
149 
150 sasl_conn_t *httpd_saslconn; /* the sasl connection context */
151 
152 static struct wildmat *allow_cors = NULL;
153 int httpd_timeout, httpd_keepalive;
154 char *httpd_userid = NULL;
155 char *httpd_extrafolder = NULL;
156 char *httpd_extradomain = NULL;
157 struct auth_state *httpd_authstate = 0;
158 int httpd_userisadmin = 0;
159 int httpd_userisproxyadmin = 0;
160 int httpd_userisanonymous = 1;
161 static const char *httpd_clienthost = "[local]";
162 const char *httpd_localip = NULL, *httpd_remoteip = NULL;
163 struct protstream *httpd_out = NULL;
164 struct protstream *httpd_in = NULL;
165 struct protgroup *protin = NULL;
166 static int httpd_logfd = -1;
167 
168 static sasl_ssf_t extprops_ssf = 0;
169 int https = 0;
170 int httpd_tls_done = 0;
171 int httpd_tls_required = 0;
172 unsigned avail_auth_schemes = 0; /* bitmask of available auth schemes */
173 unsigned long config_httpmodules;
174 int config_httpprettytelemetry;
175 
176 static time_t compile_time;
177 struct buf serverinfo = BUF_INITIALIZER;
178 
179 int ignorequota = 0;
180 int apns_enabled = 0;
181 
182 #ifdef HAVE_NGHTTP2
183 
184 static nghttp2_session_callbacks *http2_callbacks = NULL;
185 
186 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
alpn_select_cb(SSL * ssl,const unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)187 static int alpn_select_cb(SSL *ssl __attribute__((unused)),
188                           const unsigned char **out, unsigned char *outlen,
189                           const unsigned char *in, unsigned int inlen,
190                           void *arg)
191 {
192     int *is_h2 = (int *) arg;
193 
194     if (nghttp2_select_next_protocol((u_char **) out, outlen, in, inlen) == 1) {
195         *is_h2 = 1;
196         return SSL_TLSEXT_ERR_OK;
197     }
198 
199     return SSL_TLSEXT_ERR_NOACK;
200 }
201 #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
202 
http2_send_cb(nghttp2_session * session,const uint8_t * data,size_t length,int flags,void * user_data)203 static ssize_t http2_send_cb(nghttp2_session *session __attribute__((unused)),
204                              const uint8_t *data, size_t length,
205                              int flags __attribute__((unused)),
206                              void *user_data)
207 {
208     struct http_connection *conn = (struct http_connection *) user_data;
209     struct protstream *pout = conn->pout;
210     int r;
211 
212     r = prot_write(pout, (const char *) data, length);
213 
214     syslog(LOG_DEBUG, "http2_send_cb(%zu): %d", length, r);
215 
216     if (r) return NGHTTP2_ERR_CALLBACK_FAILURE;
217 
218     return length;
219 }
220 
http2_recv_cb(nghttp2_session * session,uint8_t * buf,size_t length,int flags,void * user_data)221 static ssize_t http2_recv_cb(nghttp2_session *session __attribute__((unused)),
222                              uint8_t *buf, size_t length,
223                              int flags __attribute__((unused)),
224                              void *user_data)
225 {
226     struct http_connection *conn = (struct http_connection *) user_data;
227     struct protstream *pin = conn->pin;
228     ssize_t n;
229 
230 
231     n = prot_read(pin, (char *) buf, length);
232     if (n) {
233         /* We received some data - don't block next time
234            Note: This callback gets called multiple times until it
235            would block.  We don't actually want to block and prevent
236            output from being submitted */
237         prot_NONBLOCK(pin);
238     }
239     else {
240         /* No data -  block next time (for client timeout) */
241         prot_BLOCK(pin);
242 
243         if (pin->eof) n = NGHTTP2_ERR_EOF;
244         else if (pin->error) n = NGHTTP2_ERR_CALLBACK_FAILURE;
245         else n = NGHTTP2_ERR_WOULDBLOCK;
246     }
247 
248     syslog(LOG_DEBUG,
249            "http2_recv_cb(%zu): n = %zd, eof = %d, err = '%s', errno = %d",
250            length, n, pin->eof, pin->error ? pin->error : "", errno);
251 
252     return n;
253 }
254 
http2_data_source_read_cb(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * user_data)255 static ssize_t http2_data_source_read_cb(nghttp2_session *session __attribute__((unused)),
256                                          int32_t stream_id,
257                                          uint8_t *buf, size_t length,
258                                          uint32_t *data_flags,
259                                          nghttp2_data_source *source,
260                                          void *user_data __attribute__((unused)))
261 {
262     struct protstream *s = source->ptr;
263     size_t n = prot_read(s, (char *) buf, length);
264 
265     syslog(LOG_DEBUG,
266            "http2_data_source_read_cb(id=%d, len=%zu): n=%zu, eof=%d",
267            stream_id, length, n, !s->cnt);
268 
269     if (!s->cnt) *data_flags |= NGHTTP2_DATA_FLAG_EOF;
270 
271     return n;
272 }
273 
http2_begin_headers_cb(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)274 static int http2_begin_headers_cb(nghttp2_session *session,
275                                   const nghttp2_frame *frame, void *user_data)
276 {
277     if (frame->hd.type != NGHTTP2_HEADERS ||
278         frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
279         return 0;
280     }
281 
282     syslog(LOG_DEBUG, "http2_begin_headers_cb(id=%d, type=%d)",
283            frame->hd.stream_id, frame->hd.type);
284 
285     struct transaction_t *txn = xzmalloc(sizeof(struct transaction_t));
286 
287     txn->conn = (struct http_connection *) user_data;
288     txn->http2.stream_id = frame->hd.stream_id;
289     txn->meth = METH_UNKNOWN;
290     txn->flags.ver = VER_2;
291     txn->flags.vary = VARY_AE;
292     txn->req_line.ver = HTTP2_VERSION;
293 
294     /* Create header cache */
295     if (!(txn->req_hdrs = spool_new_hdrcache())) {
296         syslog(LOG_ERR, "Unable to create header cache");
297         return NGHTTP2_ERR_CALLBACK_FAILURE;
298     }
299 
300     nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, txn);
301 
302     return 0;
303 }
304 
http2_header_cb(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * user_data)305 static int http2_header_cb(nghttp2_session *session,
306                            const nghttp2_frame *frame,
307                            const uint8_t *name, size_t namelen,
308                            const uint8_t *value, size_t valuelen,
309                            uint8_t flags __attribute__((unused)),
310                            void *user_data __attribute__((unused)))
311 {
312     if (frame->hd.type != NGHTTP2_HEADERS ||
313         frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
314         return 0;
315     }
316 
317     char *my_name, *my_value;
318     struct transaction_t *txn =
319         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
320 
321     if (!txn) return 0;
322 
323     my_name = xstrndup((const char *) name, namelen);
324     my_value = xstrndup((const char *) value, valuelen);
325 
326     syslog(LOG_DEBUG, "http2_header_cb(%s: %s)", my_name, my_value);
327 
328     if (my_name[0] == ':') {
329         switch (my_name[1]) {
330         case 'm': /* :method */
331             if (!strcmp("ethod", my_name+2)) txn->req_line.meth = my_value;
332             break;
333 
334         case 's': /* :scheme */
335             break;
336 
337         case 'a': /* :authority */
338             break;
339 
340         case 'p': /* :path */
341             if (!strcmp("ath", my_name+2)) txn->req_line.uri = my_value;
342             break;
343         }
344     }
345 
346     spool_cache_header(my_name, my_value, txn->req_hdrs);
347 
348     return 0;
349 }
350 
http2_data_chunk_recv_cb(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)351 static int http2_data_chunk_recv_cb(nghttp2_session *session,
352                                     uint8_t flags __attribute__((unused)),
353                                     int32_t stream_id,
354                                     const uint8_t *data, size_t len,
355                                     void *user_data __attribute__((unused)))
356 {
357     struct transaction_t *txn =
358         nghttp2_session_get_stream_user_data(session, stream_id);
359 
360     if (!txn) return 0;
361 
362     syslog(LOG_DEBUG, "http2_data_chunk_recv_cb(id=%d, len=%zu, txnflags=%#x)",
363            stream_id, len, txn->req_body.flags);
364 
365     if (txn->req_body.flags & BODY_DISCARD) return 0;
366 
367     if (len) {
368         txn->req_body.framing = FRAMING_HTTP2;
369         txn->req_body.len += len;
370         buf_appendmap(&txn->req_body.payload, (const char *) data, len);
371     }
372 
373     return 0;
374 }
375 
376 static int examine_request(struct transaction_t *txn);
377 
378 static int client_need_auth(struct transaction_t *txn, int sasl_result);
379 
380 static void transaction_free(struct transaction_t *txn);
381 
http2_frame_recv_cb(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)382 static int http2_frame_recv_cb(nghttp2_session *session,
383                                const nghttp2_frame *frame,
384                                void *user_data __attribute__((unused)))
385 {
386     int ret = 0;
387     struct transaction_t *txn =
388         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
389 
390     if (!txn) return 0;
391 
392     syslog(LOG_DEBUG, "http2_frame_recv_cb(id=%d, type=%d, flags=%#x",
393            frame->hd.stream_id, frame->hd.type, frame->hd.flags);
394 
395     switch (frame->hd.type) {
396     case NGHTTP2_HEADERS:
397         if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
398             /* Examine request */
399             ret = examine_request(txn);
400 
401             if (ret) {
402                 txn->req_body.flags |= BODY_DISCARD;
403                 error_response(ret, txn);
404                 break;
405             }
406 
407             if (txn->req_body.flags & BODY_CONTINUE) {
408                 txn->req_body.flags &= ~BODY_CONTINUE;
409                 response_header(HTTP_CONTINUE, txn);
410                 break;
411             }
412         }
413 
414         GCC_FALLTHROUGH
415 
416     case NGHTTP2_DATA:
417         /* Check that the client request has finished */
418         if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) break;
419 
420         /* Check that we still want to process the request */
421         if (txn->req_body.flags & BODY_DISCARD) break;
422 
423         /* Process the requested method */
424         if (txn->req_tgt.namespace->premethod) {
425             ret = txn->req_tgt.namespace->premethod(txn);
426         }
427         if (!ret) {
428             const struct method_t *meth_t =
429                 &txn->req_tgt.namespace->methods[txn->meth];
430 
431             ret = (*meth_t->proc)(txn, meth_t->params);
432         }
433 
434         if (ret == HTTP_UNAUTHORIZED) {
435             /* User must authenticate */
436             ret = client_need_auth(txn, 0);
437         }
438 
439         /* Handle errors (success responses handled by method functions) */
440         if (ret) error_response(ret, txn);
441 
442         if (txn->flags.conn & CONN_CLOSE) {
443             syslog(LOG_DEBUG, "nghttp2_submit_goaway()");
444 
445             nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE,
446                                   nghttp2_session_get_last_proc_stream_id(
447                                       txn->conn->http2_session),
448                                   NGHTTP2_NO_ERROR, NULL, 0);
449         }
450 
451         break;
452     }
453 
454     return 0;
455 }
456 
http2_stream_close_cb(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)457 static int http2_stream_close_cb(nghttp2_session *session, int32_t stream_id,
458                                  uint32_t error_code __attribute__((unused)),
459                                  void *user_data __attribute__((unused)))
460 {
461     struct transaction_t *txn =
462         nghttp2_session_get_stream_user_data(session, stream_id);
463 
464     syslog(LOG_DEBUG, "http2_stream_close_cb(id=%d)", stream_id);
465 
466     if (txn) {
467         /* Memory cleanup */
468         transaction_free(txn);
469         free(txn);
470     }
471 
472     return 0;
473 }
474 
http2_frame_not_send_cb(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * user_data)475 static int http2_frame_not_send_cb(nghttp2_session *session,
476                                    const nghttp2_frame *frame,
477                                    int lib_error_code,
478                                    void *user_data __attribute__((unused)))
479 {
480     syslog(LOG_DEBUG, "http2_frame_not_send_cb(id=%d)", frame->hd.stream_id);
481 
482     /* Issue RST_STREAM so that stream does not hang around. */
483     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
484                               frame->hd.stream_id, lib_error_code);
485 
486     return 0;
487 }
488 
489 
starthttp2(struct http_connection * conn,struct transaction_t * txn)490 static int starthttp2(struct http_connection *conn, struct transaction_t *txn)
491 {
492     int r;
493     nghttp2_settings_entry iv =
494         { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 };
495 
496     r = nghttp2_session_server_new2(&conn->http2_session,
497                                     http2_callbacks, conn, conn->http2_options);
498     if (r) {
499         syslog(LOG_WARNING,
500                "nghttp2_session_server_new: %s", nghttp2_strerror(r));
501         return r;
502     }
503 
504     if (txn && txn->flags.conn & CONN_UPGRADE) {
505         const char **hdr = spool_getheader(txn->req_hdrs, "HTTP2-Settings");
506         if (!hdr || hdr[1]) return 0;
507 
508         /* base64url decode the settings.
509            Use the SASL base64 decoder after replacing the encoded values
510            for chars 62 and 63 and adding appropriate padding. */
511         unsigned outlen;
512         struct buf buf;
513         buf_init_ro_cstr(&buf, hdr[0]);
514         buf_replace_char(&buf, '-', '+');
515         buf_replace_char(&buf, '_', '/');
516         buf_appendmap(&buf, "==", (4 - (buf_len(&buf) % 4)) % 4);
517         r = sasl_decode64(buf_base(&buf), buf_len(&buf),
518                           (char *) buf_base(&buf), buf_len(&buf), &outlen);
519         if (r != SASL_OK) {
520             syslog(LOG_WARNING, "sasl_decode64 failed: %s",
521                    sasl_errstring(r, NULL, NULL));
522             buf_free(&buf);
523             return r;
524         }
525         r = nghttp2_session_upgrade2(conn->http2_session,
526                                      (const uint8_t *) buf_base(&buf),
527                                      outlen, txn->meth == METH_HEAD, NULL);
528         buf_free(&buf);
529         if (r) {
530             syslog(LOG_WARNING, "nghttp2_session_upgrade: %s",
531                    nghttp2_strerror(r));
532             return r;
533         }
534 
535         /* tell client to start h2c upgrade (RFC 7540) */
536         response_header(HTTP_SWITCH_PROT, txn);
537 
538         txn->flags.ver = VER_2;
539         txn->http2.stream_id =
540             nghttp2_session_get_last_proc_stream_id(conn->http2_session);
541     }
542 
543     r = nghttp2_submit_settings(conn->http2_session, NGHTTP2_FLAG_NONE, &iv, 1);
544     if (r) {
545         syslog(LOG_ERR, "nghttp2_submit_settings: %s", nghttp2_strerror(r));
546         return r;
547     }
548 
549     return 0;
550 }
551 #else
starthttp2(void * conn,struct transaction_t * txn)552 static int starthttp2(void *conn __attribute__((unused)),
553                       struct transaction_t *txn __attribute__((unused)))
554 {
555     fatal("starthttp2() called, but no Nghttp2", EC_SOFTWARE);
556 }
557 #endif /* HAVE_NGHTTP2 */
558 
559 
digest_send_success(struct transaction_t * txn,const char * name,const char * data)560 static void digest_send_success(struct transaction_t *txn,
561                                 const char *name __attribute__((unused)),
562                                 const char *data)
563 {
564     simple_hdr(txn, "Authentication-Info", data);
565 }
566 
567 /* List of HTTP auth schemes that we support */
568 struct auth_scheme_t auth_schemes[] = {
569     { AUTH_BASIC, "Basic", NULL, AUTH_SERVER_FIRST | AUTH_BASE64, NULL, NULL },
570     { AUTH_DIGEST, "Digest", HTTP_DIGEST_MECH, AUTH_NEED_REQUEST|AUTH_SERVER_FIRST,
571       &digest_send_success, digest_recv_success },
572     { AUTH_SPNEGO, "Negotiate", "GSS-SPNEGO", AUTH_BASE64, NULL, NULL },
573     { AUTH_NTLM, "NTLM", "NTLM", AUTH_NEED_PERSIST | AUTH_BASE64, NULL, NULL },
574     { AUTH_BEARER, "Bearer", NULL, AUTH_NEED_REQUEST|AUTH_SERVER_FIRST, NULL, NULL },
575     { -1, NULL, NULL, 0, NULL, NULL }
576 };
577 
578 
579 /* the sasl proxy policy context */
580 static struct proxy_context httpd_proxyctx = {
581     0, 1, &httpd_authstate, &httpd_userisadmin, &httpd_userisproxyadmin
582 };
583 
584 /* signal to config.c */
585 const int config_need_data = CONFIG_NEED_PARTITION_DATA;
586 
587 /* current namespace */
588 HIDDEN struct namespace httpd_namespace;
589 
590 /* PROXY STUFF */
591 /* we want a list of our outgoing connections here and which one we're
592    currently piping */
593 
594 /* the current server most commands go to */
595 struct backend *backend_current = NULL;
596 
597 /* our cached connections */
598 struct backend **backend_cached = NULL;
599 
600 /* end PROXY stuff */
601 
602 static int starttls(struct transaction_t *txn, int *http2);
603 void usage(void);
604 void shut_down(int code) __attribute__ ((noreturn));
605 
606 /* Enable the resetting of a sasl_conn_t */
607 static int reset_saslconn(sasl_conn_t **conn);
608 
609 static void cmdloop(struct http_connection *conn);
610 static int parse_expect(struct transaction_t *txn);
611 static int parse_connection(struct transaction_t *txn);
612 static int parse_ranges(const char *hdr, unsigned long len,
613                         struct range **ranges);
614 static int proxy_authz(const char **authzid, struct transaction_t *txn);
615 static void auth_success(struct transaction_t *txn, const char *userid);
616 static int http_auth(const char *creds, struct transaction_t *txn);
617 
618 static int meth_get(struct transaction_t *txn, void *params);
619 static int meth_propfind_root(struct transaction_t *txn, void *params);
620 
621 
622 static struct {
623     char *ipremoteport;
624     char *iplocalport;
625     sasl_ssf_t ssf;
626     char *authid;
627 } saslprops = {NULL,NULL,0,NULL};
628 
629 static struct sasl_callback mysasl_cb[] = {
630     { SASL_CB_GETOPT, (mysasl_cb_ft *) &mysasl_config, NULL },
631     { SASL_CB_PROXY_POLICY, (mysasl_cb_ft *) &mysasl_proxy_policy, (void*) &httpd_proxyctx },
632     { SASL_CB_CANON_USER, (mysasl_cb_ft *) &mysasl_canon_user, NULL },
633     { SASL_CB_LIST_END, NULL, NULL }
634 };
635 
636 /* Array of HTTP methods known by our server. */
637 const struct known_meth_t http_methods[] = {
638     { "ACL",            0 },
639     { "BIND",           0 },
640     { "COPY",           METH_NOBODY },
641     { "DELETE",         METH_NOBODY },
642     { "GET",            METH_NOBODY },
643     { "HEAD",           METH_NOBODY },
644     { "LOCK",           0 },
645     { "MKCALENDAR",     0 },
646     { "MKCOL",          0 },
647     { "MOVE",           METH_NOBODY },
648     { "OPTIONS",        METH_NOBODY },
649     { "PATCH",          0 },
650     { "POST",           0 },
651     { "PROPFIND",       0 },
652     { "PROPPATCH",      0 },
653     { "PUT",            0 },
654     { "REPORT",         0 },
655     { "TRACE",          METH_NOBODY },
656     { "UNBIND",         0 },
657     { "UNLOCK",         METH_NOBODY },
658     { NULL,             0 }
659 };
660 
661 /* Namespace to fetch static content from filesystem */
662 struct namespace_t namespace_default = {
663     URL_NS_DEFAULT, 1, "", NULL,
664     http_allow_noauth, /*authschemes*/0,
665     /*mbtype*/0,
666     ALLOW_READ,
667     NULL, NULL, NULL, NULL, NULL, NULL,
668     {
669         { NULL,                 NULL },                 /* ACL          */
670         { NULL,                 NULL },                 /* BIND         */
671         { NULL,                 NULL },                 /* COPY         */
672         { NULL,                 NULL },                 /* DELETE       */
673         { &meth_get,            NULL },                 /* GET          */
674         { &meth_get,            NULL },                 /* HEAD         */
675         { NULL,                 NULL },                 /* LOCK         */
676         { NULL,                 NULL },                 /* MKCALENDAR   */
677         { NULL,                 NULL },                 /* MKCOL        */
678         { NULL,                 NULL },                 /* MOVE         */
679         { &meth_options,        NULL },                 /* OPTIONS      */
680         { NULL,                 NULL },                 /* PATCH        */
681         { NULL,                 NULL },                 /* POST         */
682         { &meth_propfind_root,  NULL },                 /* PROPFIND     */
683         { NULL,                 NULL },                 /* PROPPATCH    */
684         { NULL,                 NULL },                 /* PUT          */
685         { NULL,                 NULL },                 /* REPORT       */
686         { &meth_trace,          NULL },                 /* TRACE        */
687         { NULL,                 NULL },                 /* UNBIND       */
688         { NULL,                 NULL },                 /* UNLOCK       */
689     }
690 };
691 
692 /* Array of different namespaces and features supported by the server */
693 struct namespace_t *namespaces[] = {
694     &namespace_tzdist,          /* MUST be before namespace_calendar!! */
695 #ifdef WITH_DAV
696     &namespace_calendar,
697     &namespace_freebusy,
698     &namespace_addressbook,
699     &namespace_drive,
700     &namespace_principal,       /* MUST be after namespace_cal & addr & drive */
701     &namespace_notify,          /* MUST be after namespace_principal */
702     &namespace_applepush,       /* MUST be after namespace_cal & addr */
703 #ifdef HAVE_IANA_PARAMS
704     &namespace_ischedule,
705     &namespace_domainkey,
706 #endif /* HAVE_IANA_PARAMS */
707 #endif /* WITH_DAV */
708     &namespace_rss,
709     &namespace_dblookup,
710     &namespace_admin,
711     &namespace_default,         /* MUST be present and be last!! */
712     NULL,
713 };
714 
715 
httpd_reset(void)716 static void httpd_reset(void)
717 {
718     int i;
719     int bytes_in = 0;
720     int bytes_out = 0;
721 
722     /* Do any namespace specific cleanup */
723     for (i = 0; namespaces[i]; i++) {
724         if (namespaces[i]->enabled && namespaces[i]->reset)
725             namespaces[i]->reset();
726     }
727 
728     /* Reset available authentication schemes */
729     avail_auth_schemes = 0;
730 
731     proc_cleanup();
732 
733     /* close backend connections */
734     i = 0;
735     while (backend_cached && backend_cached[i]) {
736         proxy_downserver(backend_cached[i]);
737         free(backend_cached[i]->context);
738         free(backend_cached[i]);
739         i++;
740     }
741     if (backend_cached) free(backend_cached);
742     backend_cached = NULL;
743     backend_current = NULL;
744 
745     if (httpd_in) {
746         prot_NONBLOCK(httpd_in);
747         prot_fill(httpd_in);
748         bytes_in = prot_bytes_in(httpd_in);
749         prot_free(httpd_in);
750     }
751 
752     if (httpd_out) {
753         prot_flush(httpd_out);
754         bytes_out = prot_bytes_out(httpd_out);
755         prot_free(httpd_out);
756     }
757 
758     if (config_auditlog) {
759         syslog(LOG_NOTICE,
760                "auditlog: traffic sessionid=<%s> bytes_in=<%d> bytes_out=<%d>",
761                session_id(), bytes_in, bytes_out);
762     }
763 
764     httpd_in = httpd_out = NULL;
765 
766     if (protin) protgroup_reset(protin);
767 
768 #ifdef HAVE_SSL
769     if (tls_conn) {
770         tls_reset_servertls(&tls_conn);
771         tls_conn = NULL;
772     }
773 #endif
774 
775     cyrus_reset_stdio();
776 
777     httpd_clienthost = "[local]";
778     if (httpd_logfd != -1) {
779         close(httpd_logfd);
780         httpd_logfd = -1;
781     }
782     if (httpd_userid != NULL) {
783         free(httpd_userid);
784         httpd_userid = NULL;
785     }
786     httpd_userisanonymous = 1;
787     if (httpd_extrafolder != NULL) {
788         free(httpd_extrafolder);
789         httpd_extrafolder = NULL;
790     }
791     if (httpd_extradomain != NULL) {
792         free(httpd_extradomain);
793         httpd_extradomain = NULL;
794     }
795     if (httpd_authstate) {
796         auth_freestate(httpd_authstate);
797         httpd_authstate = NULL;
798     }
799     if (httpd_saslconn) {
800         sasl_dispose(&httpd_saslconn);
801         httpd_saslconn = NULL;
802     }
803     httpd_tls_done = 0;
804 
805     if(saslprops.iplocalport) {
806        free(saslprops.iplocalport);
807        saslprops.iplocalport = NULL;
808     }
809     if(saslprops.ipremoteport) {
810        free(saslprops.ipremoteport);
811        saslprops.ipremoteport = NULL;
812     }
813     if(saslprops.authid) {
814        free(saslprops.authid);
815        saslprops.authid = NULL;
816     }
817     saslprops.ssf = 0;
818 
819     session_new_id();
820 }
821 
822 /*
823  * run once when process is forked;
824  * MUST NOT exit directly; must return with non-zero error code
825  */
service_init(int argc,char ** argv,char ** envp)826 int service_init(int argc __attribute__((unused)),
827                  char **argv __attribute__((unused)),
828                  char **envp __attribute__((unused)))
829 {
830     int r, events, opt, i;
831     int allow_trace = config_getswitch(IMAPOPT_HTTPALLOWTRACE);
832 
833     LIBXML_TEST_VERSION
834 
835     initialize_http_error_table();
836 
837     if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
838     setproctitle_init(argc, argv, envp);
839 
840     /* set signal handlers */
841     signals_set_shutdown(&shut_down);
842     signal(SIGPIPE, SIG_IGN);
843 
844     /* load the SASL plugins */
845     global_sasl_init(1, 1, mysasl_cb);
846 
847     /* open the mboxlist, we'll need it for real work */
848     mboxlist_init(0);
849     mboxlist_open(NULL);
850 
851     /* open the quota db, we'll need it for expunge */
852     quotadb_init(0);
853     quotadb_open(NULL);
854 
855     /* open the user deny db */
856     denydb_init(0);
857     denydb_open(/*create*/0);
858 
859     /* open annotations.db, we'll need it for collection properties */
860     annotatemore_open();
861 
862     /* setup for sending IMAP IDLE notifications */
863     idle_enabled();
864 
865     /* Set namespace */
866     if ((r = mboxname_init_namespace(&httpd_namespace, 1)) != 0) {
867         syslog(LOG_ERR, "%s", error_message(r));
868         fatal(error_message(r), EC_CONFIG);
869     }
870 
871     /* open the mboxevent system */
872     events = mboxevent_init();
873     apns_enabled = (events & EVENT_APPLEPUSHSERVICE_DAV);
874 
875     mboxevent_setnamespace(&httpd_namespace);
876 
877     while ((opt = getopt(argc, argv, "sp:q")) != EOF) {
878         switch(opt) {
879         case 's': /* https (do TLS right away) */
880             https = 1;
881             if (!tls_enabled()) {
882                 syslog(LOG_ERR, "https: required OpenSSL options not present");
883                 fatal("https: required OpenSSL options not present",
884                       EC_CONFIG);
885             }
886             break;
887 
888         case 'q':
889             ignorequota = 1;
890             break;
891 
892         case 'p': /* external protection */
893             extprops_ssf = atoi(optarg);
894             break;
895 
896         default:
897             usage();
898         }
899     }
900 
901     /* Create a protgroup for input from the client and selected backend */
902     protin = protgroup_new(2);
903 
904     config_httpprettytelemetry = config_getswitch(IMAPOPT_HTTPPRETTYTELEMETRY);
905 
906     if (config_getstring(IMAPOPT_HTTPALLOWCORS)) {
907         allow_cors =
908             split_wildmats((char *) config_getstring(IMAPOPT_HTTPALLOWCORS),
909                            NULL);
910     }
911 
912     /* Construct serverinfo string */
913     buf_printf(&serverinfo, "Cyrus-HTTP/%s Cyrus-SASL/%u.%u.%u",
914                cyrus_version(),
915                SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP);
916 #ifdef HAVE_SSL
917     buf_printf(&serverinfo, " OpenSSL/%s", SHLIB_VERSION_NUMBER);
918 #endif
919 
920 #ifdef HAVE_NGHTTP2
921     buf_printf(&serverinfo, " Nghttp2/%s", NGHTTP2_VERSION);
922 
923     /* Setup HTTP/2 callbacks */
924     if ((r = nghttp2_session_callbacks_new(&http2_callbacks))) {
925         syslog(LOG_WARNING,
926                "nghttp2_session_callbacks_new: %s", nghttp2_strerror(r));
927     }
928     else {
929         nghttp2_session_callbacks_set_send_callback(http2_callbacks,
930                                                     &http2_send_cb);
931         nghttp2_session_callbacks_set_recv_callback(http2_callbacks,
932                                                     &http2_recv_cb);
933         nghttp2_session_callbacks_set_on_begin_headers_callback(http2_callbacks,
934                                                                 &http2_begin_headers_cb);
935         nghttp2_session_callbacks_set_on_header_callback(http2_callbacks,
936                                                          http2_header_cb);
937         nghttp2_session_callbacks_set_on_data_chunk_recv_callback(http2_callbacks,
938                                                                   http2_data_chunk_recv_cb);
939         nghttp2_session_callbacks_set_on_frame_recv_callback(http2_callbacks,
940                                                              http2_frame_recv_cb);
941         nghttp2_session_callbacks_set_on_stream_close_callback(http2_callbacks,
942                                                                &http2_stream_close_cb);
943         nghttp2_session_callbacks_set_on_frame_not_send_callback(http2_callbacks,
944                                                                  &http2_frame_not_send_cb);
945     }
946 #endif /* HAVE_NGHTTP2 */
947 
948 #ifdef HAVE_ZLIB
949     buf_printf(&serverinfo, " Zlib/%s", ZLIB_VERSION);
950 #endif
951 #ifdef HAVE_BROTLI
952     uint32_t version = BrotliEncoderVersion();
953     buf_printf(&serverinfo, " Brotli/%u.%u.%u",
954                (version >> 24) & 0xfff, (version >> 12) & 0xfff, version & 0xfff);
955 #endif
956     buf_printf(&serverinfo, " LibXML/%s", LIBXML_DOTTED_VERSION);
957 
958     /* Do any namespace specific initialization */
959     config_httpmodules = config_getbitfield(IMAPOPT_HTTPMODULES);
960     for (i = 0; namespaces[i]; i++) {
961         if (allow_trace) namespaces[i]->allow |= ALLOW_TRACE;
962         if (namespaces[i]->init) namespaces[i]->init(&serverinfo);
963     }
964 
965     compile_time = calc_compile_time(__TIME__, __DATE__);
966 
967     return 0;
968 }
969 
970 
971 static volatile sig_atomic_t gotsigalrm = 0;
972 
sigalrm_handler(int sig)973 static void sigalrm_handler(int sig __attribute__((unused)))
974 {
975     gotsigalrm = 1;
976 }
977 
978 
979 /*
980  * run for each accepted connection
981  */
service_main(int argc,char ** argv,char ** envp)982 int service_main(int argc __attribute__((unused)),
983                  char **argv __attribute__((unused)),
984                  char **envp __attribute__((unused)))
985 {
986     sasl_security_properties_t *secprops=NULL;
987     const char *mechlist, *mech;
988     int mechcount = 0;
989     size_t mechlen;
990     struct auth_scheme_t *scheme;
991     struct http_connection http_conn;
992 
993     session_new_id();
994 
995     signals_poll();
996 
997     sync_log_init();
998 
999     httpd_in = prot_new(0, 0);
1000     httpd_out = prot_new(1, 1);
1001     protgroup_insert(protin, httpd_in);
1002 
1003     /* Find out name of client host */
1004     httpd_clienthost = get_clienthost(0, &httpd_localip, &httpd_remoteip);
1005 
1006     /* other params should be filled in */
1007     if (sasl_server_new("HTTP", config_servername, NULL, NULL, NULL, NULL,
1008                         SASL_USAGE_FLAGS, &httpd_saslconn) != SASL_OK)
1009         fatal("SASL failed initializing: sasl_server_new()",EC_TEMPFAIL);
1010 
1011     /* will always return something valid */
1012     secprops = mysasl_secprops(0);
1013 
1014     /* no HTTP clients seem to use "auth-int" */
1015     secprops->max_ssf = 0;                              /* "auth" only */
1016     secprops->maxbufsize = 0;                           /* don't need maxbuf */
1017     if (sasl_setprop(httpd_saslconn, SASL_SEC_PROPS, secprops) != SASL_OK)
1018         fatal("Failed to set SASL property", EC_TEMPFAIL);
1019     if (sasl_setprop(httpd_saslconn, SASL_SSF_EXTERNAL, &extprops_ssf) != SASL_OK)
1020         fatal("Failed to set SASL property", EC_TEMPFAIL);
1021 
1022     if (httpd_localip) {
1023         sasl_setprop(httpd_saslconn, SASL_IPLOCALPORT, httpd_localip);
1024         saslprops.iplocalport = xstrdup(httpd_localip);
1025     }
1026 
1027     if (httpd_remoteip) {
1028         char hbuf[NI_MAXHOST], *p;
1029 
1030         sasl_setprop(httpd_saslconn, SASL_IPREMOTEPORT, httpd_remoteip);
1031         saslprops.ipremoteport = xstrdup(httpd_remoteip);
1032 
1033         /* Create pre-authentication telemetry log based on client IP */
1034         strlcpy(hbuf, httpd_remoteip, NI_MAXHOST);
1035         if ((p = strchr(hbuf, ';'))) *p = '\0';
1036         httpd_logfd = telemetry_log(hbuf, httpd_in, httpd_out, 0);
1037     }
1038 
1039     /* See which auth schemes are available to us */
1040     avail_auth_schemes = 0; /* Reset auth schemes for each connection */
1041     if ((extprops_ssf >= 2) || config_getswitch(IMAPOPT_ALLOWPLAINTEXT)) {
1042         avail_auth_schemes |= (1 << AUTH_BASIC);
1043     }
1044     sasl_listmech(httpd_saslconn, NULL, NULL, " ", NULL,
1045                   &mechlist, NULL, &mechcount);
1046     for (mech = mechlist; mechcount--; mech += ++mechlen) {
1047         mechlen = strcspn(mech, " \0");
1048         for (scheme = auth_schemes; scheme->name; scheme++) {
1049             if (scheme->saslmech && !strncmp(mech, scheme->saslmech, mechlen)) {
1050                 avail_auth_schemes |= (1 << scheme->idx);
1051                 break;
1052             }
1053         }
1054     }
1055     httpd_tls_required =
1056         config_getswitch(IMAPOPT_TLS_REQUIRED) || !avail_auth_schemes;
1057 
1058     proc_register(config_ident, httpd_clienthost, NULL, NULL, NULL);
1059 
1060     /* Set inactivity timer */
1061     httpd_timeout = config_getint(IMAPOPT_HTTPTIMEOUT);
1062     if (httpd_timeout < 0) httpd_timeout = 0;
1063     httpd_timeout *= 60;
1064     prot_settimeout(httpd_in, httpd_timeout);
1065     prot_setflushonread(httpd_in, httpd_out);
1066 
1067     /* Setup HTTP connection */
1068     memset(&http_conn, 0, sizeof(struct http_connection));
1069     http_conn.pin = httpd_in;
1070     http_conn.pout = httpd_out;
1071 
1072     /* we were connected on https port so we should do
1073        TLS negotiation immediatly */
1074     if (https == 1) {
1075         int r, http2 = 0;
1076 
1077         r = starttls(NULL, &http2);
1078         if (!r && http2) r = starthttp2(&http_conn, NULL);
1079         if (r) shut_down(0);
1080     }
1081 
1082     /* Setup the signal handler for keepalive heartbeat */
1083     httpd_keepalive = config_getint(IMAPOPT_HTTPKEEPALIVE);
1084     if (httpd_keepalive < 0) httpd_keepalive = 0;
1085     if (httpd_keepalive) {
1086         struct sigaction action;
1087 
1088         sigemptyset(&action.sa_mask);
1089         action.sa_flags = 0;
1090 #ifdef SA_RESTART
1091         action.sa_flags |= SA_RESTART;
1092 #endif
1093         action.sa_handler = sigalrm_handler;
1094         if (sigaction(SIGALRM, &action, NULL) < 0) {
1095             syslog(LOG_ERR, "unable to install signal handler for %d: %m", SIGALRM);
1096             httpd_keepalive = 0;
1097         }
1098     }
1099 
1100     if (config_getswitch(IMAPOPT_HTTPALLOWCOMPRESS)) {
1101 #ifdef HAVE_ZLIB
1102         http_conn.zstrm = xzmalloc(sizeof(z_stream));
1103         /* Always use gzip format because IE incorrectly uses raw deflate */
1104         if (deflateInit2(http_conn.zstrm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
1105                          16+MAX_WBITS /* gzip */,
1106                          MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
1107             free(http_conn.zstrm);
1108             http_conn.zstrm = NULL;
1109         }
1110 #endif
1111 #ifdef HAVE_BROTLI
1112         http_conn.brotli = brotli_init();
1113 #endif
1114     }
1115 
1116     cmdloop(&http_conn);
1117 
1118     /* Closing connection */
1119 
1120     /* cleanup */
1121     signal(SIGALRM, SIG_IGN);
1122     httpd_reset();
1123 
1124 #ifdef HAVE_NGHTTP2
1125     nghttp2_option_del(http_conn.http2_options);
1126     nghttp2_session_del(http_conn.http2_session);
1127 #endif
1128 
1129 #ifdef HAVE_ZLIB
1130     if (http_conn.zstrm) {
1131         deflateEnd(http_conn.zstrm);
1132         free(http_conn.zstrm);
1133     }
1134 #endif
1135 #ifdef HAVE_BROTLI
1136     if (http_conn.brotli) BrotliEncoderDestroyInstance(http_conn.brotli);
1137 #endif
1138 
1139     return 0;
1140 }
1141 
1142 
1143 /* Called by service API to shut down the service */
service_abort(int error)1144 void service_abort(int error)
1145 {
1146     shut_down(error);
1147 }
1148 
1149 
usage(void)1150 void usage(void)
1151 {
1152     prot_printf(httpd_out, "%s: usage: httpd [-C <alt_config>] [-s]\r\n",
1153                 error_message(HTTP_SERVER_ERROR));
1154     prot_flush(httpd_out);
1155     exit(EC_USAGE);
1156 }
1157 
1158 
1159 /*
1160  * Cleanly shut down and exit
1161  */
shut_down(int code)1162 void shut_down(int code)
1163 {
1164     int i;
1165     int bytes_in = 0;
1166     int bytes_out = 0;
1167 
1168     in_shutdown = 1;
1169 
1170     if (allow_cors) free_wildmats(allow_cors);
1171 
1172     /* Do any namespace specific cleanup */
1173     for (i = 0; namespaces[i]; i++) {
1174         if (namespaces[i]->enabled && namespaces[i]->shutdown)
1175             namespaces[i]->shutdown();
1176     }
1177 
1178     xmlCleanupParser();
1179 
1180     proc_cleanup();
1181 
1182     /* close backend connections */
1183     i = 0;
1184     while (backend_cached && backend_cached[i]) {
1185         proxy_downserver(backend_cached[i]);
1186         free(backend_cached[i]->context);
1187         free(backend_cached[i]);
1188         i++;
1189     }
1190     if (backend_cached) free(backend_cached);
1191 
1192     sync_log_done();
1193 
1194     mboxlist_close();
1195     mboxlist_done();
1196 
1197     quotadb_close();
1198     quotadb_done();
1199 
1200     denydb_close();
1201     denydb_done();
1202 
1203     annotatemore_close();
1204 
1205     if (httpd_in) {
1206         prot_NONBLOCK(httpd_in);
1207         prot_fill(httpd_in);
1208         bytes_in = prot_bytes_in(httpd_in);
1209         prot_free(httpd_in);
1210     }
1211 
1212     if (httpd_out) {
1213         prot_flush(httpd_out);
1214         bytes_out = prot_bytes_out(httpd_out);
1215         prot_free(httpd_out);
1216     }
1217 
1218     if (protin) protgroup_free(protin);
1219 
1220     if (config_auditlog)
1221         syslog(LOG_NOTICE,
1222                "auditlog: traffic sessionid=<%s> bytes_in=<%d> bytes_out=<%d>",
1223                session_id(), bytes_in, bytes_out);
1224 
1225 #ifdef HAVE_SSL
1226     tls_shutdown_serverengine();
1227 #endif
1228 
1229 #ifdef HAVE_NGHTTP2
1230     nghttp2_session_callbacks_del(http2_callbacks);
1231 #endif
1232 
1233     cyrus_done();
1234 
1235     exit(code);
1236 }
1237 
1238 
fatal(const char * s,int code)1239 void fatal(const char* s, int code)
1240 {
1241     static int recurse_code = 0;
1242 
1243     if (recurse_code) {
1244         /* We were called recursively. Just give up */
1245         proc_cleanup();
1246         exit(recurse_code);
1247     }
1248     recurse_code = code;
1249     if (httpd_out) {
1250         prot_printf(httpd_out,
1251                     "HTTP/1.1 %s\r\n"
1252                     "Content-Type: text/plain\r\n"
1253                     "Connection: close\r\n\r\n"
1254                     "Fatal error: %s\r\n",
1255                     error_message(HTTP_SERVER_ERROR), s);
1256         prot_flush(httpd_out);
1257     }
1258     syslog(LOG_ERR, "Fatal error: %s", s);
1259     shut_down(code);
1260 }
1261 
1262 
1263 #ifdef HAVE_SSL
starttls(struct transaction_t * txn,int * http2)1264 static int starttls(struct transaction_t *txn, int *http2)
1265 {
1266     int https = (txn == NULL);
1267     int result;
1268     int *layerp;
1269     sasl_ssf_t ssf;
1270     char *auth_id;
1271     SSL_CTX *ctx = NULL;
1272 
1273     /* SASL and openssl have different ideas about whether ssf is signed */
1274     layerp = (int *) &ssf;
1275 
1276     result=tls_init_serverengine("http",
1277                                  5,        /* depth to verify */
1278                                  !https,   /* can client auth? */
1279                                  &ctx);
1280 
1281     if (result == -1) {
1282         syslog(LOG_ERR, "error initializing TLS");
1283 
1284         if (txn) txn->error.desc = "Error initializing TLS";
1285         return HTTP_SERVER_ERROR;
1286     }
1287 
1288 #if (defined HAVE_NGHTTP2 && OPENSSL_VERSION_NUMBER >= 0x10002000L)
1289     if (http2_callbacks) {
1290         /* enable TLS ALPN extension */
1291         SSL_CTX_set_alpn_select_cb(ctx, alpn_select_cb, http2);
1292     }
1293 #else
1294     (void) http2; /* silence 'unused variable http2' warning */
1295 #endif
1296 
1297     if (!https) {
1298         /* tell client to start TLS upgrade (RFC 2817) */
1299         response_header(HTTP_SWITCH_PROT, txn);
1300     }
1301 
1302     result=tls_start_servertls(0, /* read */
1303                                1, /* write */
1304                                https ? 180 : httpd_timeout,
1305                                layerp,
1306                                &auth_id,
1307                                &tls_conn);
1308 
1309     /* if error */
1310     if (result == -1) {
1311         syslog(LOG_NOTICE, "starttls failed: %s", httpd_clienthost);
1312 
1313         if (txn) txn->error.desc = "Error negotiating TLS";
1314         return HTTP_BAD_REQUEST;
1315     }
1316 
1317     /* tell SASL about the negotiated layer */
1318     result = sasl_setprop(httpd_saslconn, SASL_SSF_EXTERNAL, &ssf);
1319     if (result == SASL_OK) {
1320         saslprops.ssf = ssf;
1321 
1322         result = sasl_setprop(httpd_saslconn, SASL_AUTH_EXTERNAL, auth_id);
1323     }
1324     if (result != SASL_OK) {
1325         syslog(LOG_NOTICE, "sasl_setprop() failed: starttls()");
1326 
1327         fatal("sasl_setprop() failed: starttls()", EC_TEMPFAIL);
1328     }
1329     if (saslprops.authid) {
1330         free(saslprops.authid);
1331         saslprops.authid = NULL;
1332     }
1333     if (auth_id) saslprops.authid = xstrdup(auth_id);
1334 
1335     /* tell the prot layer about our new layers */
1336     prot_settls(httpd_in, tls_conn);
1337     prot_settls(httpd_out, tls_conn);
1338 
1339     httpd_tls_done = 1;
1340     httpd_tls_required = 0;
1341 
1342     avail_auth_schemes |= (1 << AUTH_BASIC);
1343 
1344     return 0;
1345 }
1346 #else
starttls(struct transaction_t * txn,int * http2)1347 static int starttls(struct transaction_t *txn __attribute__((unused)),
1348                     int *http2 __attribute__((unused)))
1349 {
1350     fatal("starttls() called, but no OpenSSL", EC_SOFTWARE);
1351 }
1352 #endif /* HAVE_SSL */
1353 
1354 
1355 /* Reset the given sasl_conn_t to a sane state */
reset_saslconn(sasl_conn_t ** conn)1356 static int reset_saslconn(sasl_conn_t **conn)
1357 {
1358     int ret;
1359     sasl_security_properties_t *secprops = NULL;
1360 
1361     sasl_dispose(conn);
1362     /* do initialization typical of service_main */
1363     ret = sasl_server_new("HTTP", config_servername, NULL, NULL, NULL, NULL,
1364                           SASL_USAGE_FLAGS, conn);
1365     if(ret != SASL_OK) return ret;
1366 
1367     if(saslprops.ipremoteport)
1368        ret = sasl_setprop(*conn, SASL_IPREMOTEPORT,
1369                           saslprops.ipremoteport);
1370     if(ret != SASL_OK) return ret;
1371 
1372     if(saslprops.iplocalport)
1373        ret = sasl_setprop(*conn, SASL_IPLOCALPORT,
1374                           saslprops.iplocalport);
1375     if(ret != SASL_OK) return ret;
1376     secprops = mysasl_secprops(0);
1377 
1378     /* no HTTP clients seem to use "auth-int" */
1379     secprops->max_ssf = 0;                              /* "auth" only */
1380     secprops->maxbufsize = 0;                           /* don't need maxbuf */
1381     ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
1382     if(ret != SASL_OK) return ret;
1383     /* end of service_main initialization excepting SSF */
1384 
1385     /* If we have TLS/SSL info, set it */
1386     if(saslprops.ssf) {
1387         ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &saslprops.ssf);
1388     } else {
1389         ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &extprops_ssf);
1390     }
1391 
1392     if(ret != SASL_OK) return ret;
1393 
1394     if(saslprops.authid) {
1395        ret = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, saslprops.authid);
1396        if(ret != SASL_OK) return ret;
1397     }
1398     /* End TLS/SSL Info */
1399 
1400     return SASL_OK;
1401 }
1402 
1403 
parse_request_line(struct transaction_t * txn)1404 static int parse_request_line(struct transaction_t *txn)
1405 {
1406     struct request_line_t *req_line = &txn->req_line;
1407     char *p;
1408     tok_t tok;
1409     int ret = 0;
1410 
1411     /* Trim CRLF from request-line */
1412     p = req_line->buf + strlen(req_line->buf);
1413     if (p[-1] == '\n') *--p = '\0';
1414     if (p[-1] == '\r') *--p = '\0';
1415 
1416     /* Parse request-line = method SP request-target SP HTTP-version CRLF */
1417     tok_initm(&tok, req_line->buf, " ", 0);
1418     if (!(req_line->meth = tok_next(&tok))) {
1419         ret = HTTP_BAD_REQUEST;
1420         txn->error.desc = "Missing method in request-line";
1421     }
1422     else if (!(req_line->uri = tok_next(&tok))) {
1423         ret = HTTP_BAD_REQUEST;
1424         txn->error.desc = "Missing request-target in request-line";
1425     }
1426     else if ((size_t) (p - req_line->buf) > MAX_REQ_LINE - 2) {
1427         /* request-line overran the size of our buffer */
1428         ret = HTTP_URI_TOO_LONG;
1429         buf_printf(&txn->buf,
1430                    "Length of request-line MUST be less than %u octets",
1431                    MAX_REQ_LINE);
1432         txn->error.desc = buf_cstring(&txn->buf);
1433     }
1434     else if (!(req_line->ver = tok_next(&tok))) {
1435         ret = HTTP_BAD_REQUEST;
1436         txn->error.desc = "Missing HTTP-version in request-line";
1437     }
1438     else if (tok_next(&tok)) {
1439         ret = HTTP_BAD_REQUEST;
1440         txn->error.desc = "Unexpected extra argument(s) in request-line";
1441     }
1442 
1443     /* Check HTTP-Version - MUST be HTTP/1.x */
1444     else if (strlen(req_line->ver) != HTTP_VERSION_LEN
1445              || strncmp(req_line->ver, HTTP_VERSION, HTTP_VERSION_LEN-1)
1446              || !isdigit(req_line->ver[HTTP_VERSION_LEN-1])) {
1447         ret = HTTP_BAD_VERSION;
1448         buf_printf(&txn->buf,
1449                    "This server only speaks %.*sx",
1450                    HTTP_VERSION_LEN-1, HTTP_VERSION);
1451         txn->error.desc = buf_cstring(&txn->buf);
1452     }
1453     else if (req_line->ver[HTTP_VERSION_LEN-1] == '0') {
1454         /* HTTP/1.0 connection */
1455         txn->flags.ver = VER_1_0;
1456     }
1457     tok_fini(&tok);
1458 
1459     return ret;
1460 }
1461 
1462 
client_need_auth(struct transaction_t * txn,int sasl_result)1463 static int client_need_auth(struct transaction_t *txn, int sasl_result)
1464 {
1465     if (httpd_tls_required) {
1466         /* We only support TLS+Basic, so tell client to use TLS */
1467         const char **hdr;
1468 
1469         /* Check which response is required */
1470         if ((hdr = spool_getheader(txn->req_hdrs, "Upgrade")) &&
1471             strstr(hdr[0], TLS_VERSION)) {
1472             /* Client (Murder proxy) supports RFC 2817 (TLS upgrade) */
1473 
1474             txn->flags.conn |= CONN_UPGRADE;
1475             txn->flags.upgrade = UPGRADE_TLS;
1476             return HTTP_UPGRADE;
1477         }
1478         else {
1479             /* All other clients use RFC 2818 (HTTPS) */
1480             const char *path = txn->req_uri->path;
1481             const char *query = URI_QUERY(txn->req_uri);
1482             struct buf *html = &txn->resp_body.payload;
1483 
1484             /* Create https URL */
1485             hdr = spool_getheader(txn->req_hdrs, "Host");
1486             buf_printf(&txn->buf, "https://%s", hdr[0]);
1487             if (strcmp(path, "*")) {
1488                 buf_appendcstr(&txn->buf, path);
1489                 if (query) buf_printf(&txn->buf, "?%s", query);
1490             }
1491 
1492             txn->location = buf_cstring(&txn->buf);
1493 
1494             /* Create HTML body */
1495             buf_reset(html);
1496             buf_printf(html, tls_message,
1497                        buf_cstring(&txn->buf), buf_cstring(&txn->buf));
1498 
1499             /* Output our HTML response */
1500             txn->resp_body.type = "text/html; charset=utf-8";
1501             return HTTP_MOVED;
1502         }
1503     }
1504     else {
1505         /* Tell client to authenticate */
1506         if (sasl_result == SASL_CONTINUE)
1507             txn->error.desc = "Continue authentication exchange";
1508         else if (sasl_result) txn->error.desc = "Authentication failed";
1509         else txn->error.desc =
1510                  "Must authenticate to access the specified target";
1511 
1512         return HTTP_UNAUTHORIZED;
1513     }
1514 }
1515 
1516 
examine_request(struct transaction_t * txn)1517 static int examine_request(struct transaction_t *txn)
1518 {
1519     int ret = 0, r = 0, i;
1520     const char **hdr, *query;
1521     const struct namespace_t *namespace;
1522     const struct method_t *meth_t;
1523     struct request_line_t *req_line = &txn->req_line;
1524 
1525     /* Check for HTTP method override */
1526     if (!strcmp(req_line->meth, "POST") &&
1527         (hdr = spool_getheader(txn->req_hdrs, "X-HTTP-Method-Override"))) {
1528         txn->flags.override = 1;
1529         req_line->meth = (char *) hdr[0];
1530     }
1531 
1532     /* Check Method against our list of known methods */
1533     for (txn->meth = 0; (txn->meth < METH_UNKNOWN) &&
1534              strcmp(http_methods[txn->meth].name, req_line->meth);
1535          txn->meth++);
1536 
1537     if (txn->meth == METH_UNKNOWN) return HTTP_NOT_IMPLEMENTED;
1538 
1539     /* Parse request-target URI */
1540     if (!(txn->req_uri = parse_uri(txn->meth, req_line->uri, 1,
1541                                    &txn->error.desc))) {
1542         return HTTP_BAD_REQUEST;
1543     }
1544 
1545     /* Check for mandatory Host header (HTTP/1.1+ only) */
1546     if ((hdr = spool_getheader(txn->req_hdrs, "Host")) && hdr[1]) {
1547         txn->error.desc = "Too many Host headers";
1548         return HTTP_BAD_REQUEST;
1549     }
1550     else if (!hdr) {
1551         switch (txn->flags.ver) {
1552         case VER_2:
1553             /* HTTP/2 - create a Host header from :authority */
1554             hdr = spool_getheader(txn->req_hdrs, ":authority");
1555             spool_cache_header(xstrdup("Host"), xstrdup(hdr[0]), txn->req_hdrs);
1556             break;
1557 
1558         case VER_1_0:
1559             /* HTTP/1.0 - create a Host header from URI */
1560             if (txn->req_uri->server) {
1561                 buf_setcstr(&txn->buf, txn->req_uri->server);
1562                 if (txn->req_uri->port)
1563                     buf_printf(&txn->buf, ":%d", txn->req_uri->port);
1564             }
1565             else buf_setcstr(&txn->buf, config_servername);
1566 
1567             spool_cache_header(xstrdup("Host"),
1568                                xstrdup(buf_cstring(&txn->buf)), txn->req_hdrs);
1569             buf_reset(&txn->buf);
1570             break;
1571 
1572         case VER_1_1:
1573         default:
1574             txn->error.desc = "Missing Host header";
1575             return HTTP_BAD_REQUEST;
1576         }
1577     }
1578 
1579     /* Check message framing */
1580     if ((ret = http_parse_framing(txn->flags.ver == VER_2, txn->req_hdrs,
1581                                   &txn->req_body, &txn->error.desc))) return ret;
1582 
1583     /* Check for Expectations */
1584     if ((ret = parse_expect(txn))) return ret;
1585 
1586     /* Check for Connection options */
1587     if ((ret = parse_connection(txn))) return ret;
1588 
1589     syslog(LOG_DEBUG, "conn flags: %#x  upgrade flags: %#x  tls req: %d",
1590            txn->flags.conn, txn->flags.upgrade, httpd_tls_required);
1591     if (txn->flags.conn & CONN_UPGRADE) {
1592         /* Read any request body (can't upgrade in middle of request) */
1593         txn->req_body.flags |= BODY_DECODE;
1594         ret = http_read_body(httpd_in, httpd_out,
1595                              txn->req_hdrs, &txn->req_body, &txn->error.desc);
1596         if (ret) {
1597             txn->flags.conn = CONN_CLOSE;
1598             return ret;
1599         }
1600 
1601         if (txn->flags.upgrade & UPGRADE_TLS) {
1602             int http2 = 0;
1603             if ((ret = starttls(txn, &http2))) {
1604                 txn->flags.conn = CONN_CLOSE;
1605                 return ret;
1606             }
1607             if (http2) txn->flags.upgrade |= UPGRADE_HTTP2;
1608         }
1609 
1610         syslog(LOG_DEBUG, "upgrade flags: %#x  tls req: %d",
1611                txn->flags.upgrade, httpd_tls_required);
1612         if ((txn->flags.upgrade & UPGRADE_HTTP2) && !httpd_tls_required) {
1613             if ((ret = starthttp2(txn->conn, httpd_tls_done ? NULL : txn))) {
1614                 txn->flags.conn = CONN_CLOSE;
1615                 return ret;
1616             }
1617         }
1618 
1619         txn->flags.conn &= ~CONN_UPGRADE;
1620         txn->flags.upgrade = 0;
1621     }
1622     else if (!httpd_tls_done && txn->flags.ver == VER_1_1) {
1623         /* Advertise available upgrade protocols */
1624 #ifdef HAVE_NGHTTP2
1625         txn->flags.conn |= CONN_UPGRADE;
1626         txn->flags.upgrade = UPGRADE_HTTP2;
1627 #endif
1628         if (config_mupdate_server && config_getstring(IMAPOPT_PROXYSERVERS)) {
1629             txn->flags.upgrade |= UPGRADE_TLS;
1630             txn->flags.conn |= CONN_UPGRADE;
1631         }
1632     }
1633 
1634     query = URI_QUERY(txn->req_uri);
1635 
1636     /* Find the namespace of the requested resource */
1637     for (i = 0; namespaces[i]; i++) {
1638         const char *path = txn->req_uri->path;
1639         size_t len;
1640 
1641         /* Skip disabled namespaces */
1642         if (!namespaces[i]->enabled) continue;
1643 
1644         /* Handle any /.well-known/ bootstrapping */
1645         if (namespaces[i]->well_known) {
1646             len = strlen(namespaces[i]->well_known);
1647             if (!strncmp(path, namespaces[i]->well_known, len) &&
1648                 (!path[len] || path[len] == '/')) {
1649 
1650                 hdr = spool_getheader(txn->req_hdrs, "Host");
1651                 buf_reset(&txn->buf);
1652                 buf_printf(&txn->buf, "%s://%s",
1653                            https? "https" : "http", hdr[0]);
1654                 buf_appendcstr(&txn->buf, namespaces[i]->prefix);
1655                 buf_appendcstr(&txn->buf, path + len);
1656                 if (query) buf_printf(&txn->buf, "?%s", query);
1657                 txn->location = buf_cstring(&txn->buf);
1658 
1659                 return HTTP_MOVED;
1660             }
1661         }
1662 
1663         /* See if the prefix matches - terminated with NUL or '/' */
1664         len = strlen(namespaces[i]->prefix);
1665         if (!strncmp(path, namespaces[i]->prefix, len) &&
1666             (!path[len] || (path[len] == '/') || !strcmp(path, "*"))) {
1667             break;
1668         }
1669     }
1670     if ((namespace = namespaces[i])) {
1671         txn->req_tgt.namespace = namespace;
1672         txn->req_tgt.allow = namespace->allow;
1673 
1674         /* Check if method is supported in this namespace */
1675         meth_t = &namespace->methods[txn->meth];
1676         if (!meth_t->proc) return HTTP_NOT_ALLOWED;
1677 
1678         /* Check if method expects a body */
1679         else if ((http_methods[txn->meth].flags & METH_NOBODY) &&
1680                  (txn->req_body.framing != FRAMING_LENGTH ||
1681                   /* XXX  Will break if client sends just a last-chunk */
1682                   txn->req_body.len)) {
1683             return HTTP_BAD_MEDIATYPE;
1684         }
1685     } else {
1686         /* XXX  Should never get here */
1687         return HTTP_SERVER_ERROR;
1688     }
1689 
1690     /* See if this namespace whitelists auth schemes */
1691     if (namespace->auth_schemes) {
1692         avail_auth_schemes = (namespace->auth_schemes & avail_auth_schemes);
1693 
1694         /* Bearer auth must be advertised and supported by the namespace */
1695         if ((namespace->auth_schemes & (1<<AUTH_BEARER)) && namespace->bearer) {
1696             avail_auth_schemes |= (1<<AUTH_BEARER);
1697         }
1698     }
1699 
1700     /* Perform authentication, if necessary */
1701     if ((hdr = spool_getheader(txn->req_hdrs, "Authorization"))) {
1702         if (httpd_userid) {
1703             /* Reauth - reinitialize */
1704             syslog(LOG_DEBUG, "reauth - reinit");
1705             reset_saslconn(&httpd_saslconn);
1706             txn->auth_chal.scheme = NULL;
1707         }
1708 
1709         if (httpd_tls_required) {
1710             /* TLS required - redirect handled below */
1711             ret = HTTP_UNAUTHORIZED;
1712         }
1713         else {
1714             /* Check the auth credentials */
1715             r = http_auth(hdr[0], txn);
1716             if ((r < 0) || !txn->auth_chal.scheme) {
1717                 /* Auth failed - reinitialize */
1718                 syslog(LOG_DEBUG, "auth failed - reinit");
1719                 reset_saslconn(&httpd_saslconn);
1720                 txn->auth_chal.scheme = NULL;
1721                 ret = HTTP_UNAUTHORIZED;
1722             }
1723             else if (r == SASL_CONTINUE) {
1724                 /* Continue with multi-step authentication */
1725                 ret = HTTP_UNAUTHORIZED;
1726             }
1727         }
1728     }
1729     else if (!httpd_userid && txn->auth_chal.scheme) {
1730         /* Started auth exchange, but client didn't engage - reinit */
1731         syslog(LOG_DEBUG, "client didn't complete auth - reinit");
1732         reset_saslconn(&httpd_saslconn);
1733         txn->auth_chal.scheme = NULL;
1734     }
1735 
1736     /* Drop auth credentials, if not a backend in a Murder */
1737     else if (!config_mupdate_server || !config_getstring(IMAPOPT_PROXYSERVERS)) {
1738         syslog(LOG_DEBUG, "drop auth creds");
1739 
1740         free(httpd_userid);
1741         httpd_userid = NULL;
1742 
1743         free(httpd_extrafolder);
1744         httpd_extrafolder = NULL;
1745 
1746         free(httpd_extradomain);
1747         httpd_extradomain = NULL;
1748 
1749         if (httpd_authstate) {
1750             auth_freestate(httpd_authstate);
1751             httpd_authstate = NULL;
1752         }
1753     }
1754 
1755     /* Perform proxy authorization, if necessary */
1756     else if (saslprops.authid &&
1757              (hdr = spool_getheader(txn->req_hdrs, "Authorize-As")) &&
1758              *hdr[0]) {
1759         const char *authzid = hdr[0];
1760 
1761         r = proxy_authz(&authzid, txn);
1762         if (r) {
1763             /* Proxy authz failed - reinitialize */
1764             syslog(LOG_DEBUG, "proxy authz failed - reinit");
1765             reset_saslconn(&httpd_saslconn);
1766             txn->auth_chal.scheme = NULL;
1767             ret = HTTP_UNAUTHORIZED;
1768         }
1769         else {
1770             auth_success(txn, authzid);
1771         }
1772     }
1773 
1774     /* Register service/module and method */
1775     buf_printf(&txn->buf, "%s%s", config_ident,
1776                namespace->well_known ? strrchr(namespace->well_known, '/') :
1777                namespace->prefix);
1778     proc_register(buf_cstring(&txn->buf), httpd_clienthost, httpd_userid,
1779                   txn->req_line.uri, txn->req_line.meth);
1780     buf_reset(&txn->buf);
1781 
1782     /* Request authentication, if necessary */
1783     if (!httpd_userid && namespace->need_auth(txn)) {
1784         ret = HTTP_UNAUTHORIZED;
1785     }
1786 
1787     if (ret) return client_need_auth(txn, r);
1788 
1789     /* Check if this is a Cross-Origin Resource Sharing request */
1790     if (allow_cors && (hdr = spool_getheader(txn->req_hdrs, "Origin"))) {
1791         const char *err = NULL;
1792         xmlURIPtr uri = parse_uri(METH_UNKNOWN, hdr[0], 0, &err);
1793 
1794         if (uri && uri->scheme && uri->server) {
1795             int o_https = !strcasecmp(uri->scheme, "https");
1796 
1797             if ((https == o_https) &&
1798                 !strcasecmp(uri->server,
1799                             *spool_getheader(txn->req_hdrs, "Host"))) {
1800                 txn->flags.cors = CORS_SIMPLE;
1801             }
1802             else {
1803                 struct wildmat *wild;
1804 
1805                 /* Create URI w/o path or default port */
1806                 assert(!buf_len(&txn->buf));
1807                 buf_printf(&txn->buf, "%s://%s",
1808                            lcase(uri->scheme), lcase(uri->server));
1809                 if (uri->port &&
1810                     ((o_https && uri->port != 443) ||
1811                      (!o_https && uri->port != 80))) {
1812                     buf_printf(&txn->buf, ":%d", uri->port);
1813                 }
1814 
1815                 /* Check Origin against the 'httpallowcors' wildmat */
1816                 for (wild = allow_cors; wild->pat; wild++) {
1817                     if (wildmat(buf_cstring(&txn->buf), wild->pat)) {
1818                         /* If we have a non-negative match, allow request */
1819                         if (!wild->not) txn->flags.cors = CORS_SIMPLE;
1820                         break;
1821                     }
1822                 }
1823                 buf_reset(&txn->buf);
1824             }
1825         }
1826         xmlFreeURI(uri);
1827     }
1828 
1829     /* Check if we should compress response body
1830 
1831        XXX  Do we want to support deflate even though M$
1832        doesn't implement it correctly (raw deflate vs. zlib)? */
1833     if (txn->conn->zstrm &&
1834         txn->flags.ver == VER_1_1 &&
1835         (hdr = spool_getheader(txn->req_hdrs, "TE"))) {
1836         struct accept *e, *enc = parse_accept(hdr);
1837 
1838         for (e = enc; e && e->token; e++) {
1839             if (e->qual > 0.0 &&
1840                 (!strcasecmp(e->token, "gzip") ||
1841                  !strcasecmp(e->token, "x-gzip"))) {
1842                 txn->flags.te = TE_GZIP;
1843             }
1844             free(e->token);
1845         }
1846         if (enc) free(enc);
1847     }
1848     else if ((txn->conn->zstrm || txn->conn->brotli) &&
1849              (hdr = spool_getheader(txn->req_hdrs, "Accept-Encoding"))) {
1850         struct accept *e, *enc = parse_accept(hdr);
1851         float qual = 0.0;
1852 
1853         for (e = enc; e && e->token; e++) {
1854             if (e->qual > 0.0) {
1855                 /* Favor Brotli over GZIP if q values are equal */
1856                 if (txn->conn->brotli &&
1857                     (e->qual >= qual) && !strcasecmp(e->token, "br")) {
1858                     txn->resp_body.enc = CE_BR;
1859                     qual = e->qual;
1860                 }
1861                 else if (txn->conn->zstrm &&
1862                          (e->qual > qual) && (!strcasecmp(e->token, "gzip") ||
1863                                               !strcasecmp(e->token, "x-gzip"))) {
1864                     txn->resp_body.enc = CE_GZIP;
1865                     qual = e->qual;
1866                 }
1867             }
1868             free(e->token);
1869         }
1870         if (enc) free(enc);
1871     }
1872 
1873     /* Parse any query parameters */
1874     construct_hash_table(&txn->req_qparams, 10, 1);
1875     if (query) parse_query_params(txn, query);
1876 
1877     return 0;
1878 }
1879 
1880 
transaction_reset(struct transaction_t * txn)1881 static void transaction_reset(struct transaction_t *txn)
1882 {
1883     txn->meth = METH_UNKNOWN;
1884 
1885     memset(&txn->flags, 0, sizeof(struct txn_flags_t));
1886     txn->flags.ver = VER_1_1;
1887     txn->flags.vary = VARY_AE;
1888 
1889     memset(&txn->req_line, 0, sizeof(struct request_line_t));
1890 
1891     /* Reset Bearer auth scheme for each transaction */
1892     avail_auth_schemes &= ~(1 << AUTH_BEARER);
1893 
1894     if (txn->req_uri) xmlFreeURI(txn->req_uri);
1895     txn->req_uri = NULL;
1896 
1897     /* XXX - split this into a req_tgt cleanup */
1898     free(txn->req_tgt.userid);
1899     mboxlist_entry_free(&txn->req_tgt.mbentry);
1900     memset(&txn->req_tgt, 0, sizeof(struct request_target_t));
1901 
1902     free_hash_table(&txn->req_qparams, (void (*)(void *)) &freestrlist);
1903 
1904     if (txn->req_hdrs) spool_free_hdrcache(txn->req_hdrs);
1905     txn->req_hdrs = NULL;
1906 
1907     txn->req_body.flags = 0;
1908     buf_reset(&txn->req_body.payload);
1909 
1910     txn->auth_chal.param = NULL;
1911     txn->location = NULL;
1912     memset(&txn->error, 0, sizeof(struct error_t));
1913 
1914     memset(&txn->resp_body, 0,  /* Don't zero the response payload buffer */
1915            sizeof(struct resp_body_t) - sizeof(struct buf));
1916     buf_reset(&txn->resp_body.payload);
1917 
1918     buf_reset(&txn->buf);
1919 }
1920 
1921 
transaction_free(struct transaction_t * txn)1922 static void transaction_free(struct transaction_t *txn)
1923 {
1924 #ifdef HAVE_NGHTTP2
1925     size_t i;
1926 
1927     for (i = 0; i < HTTP2_MAX_HEADERS; i++) {
1928         free(txn->http2.resp_hdrs[i].value);
1929     }
1930 #endif /* HAVE_NGHTTP2 */
1931 
1932     transaction_reset(txn);
1933 
1934     buf_free(&txn->req_body.payload);
1935     buf_free(&txn->resp_body.payload);
1936     buf_free(&txn->zbuf);
1937     buf_free(&txn->buf);
1938 }
1939 
1940 
1941 /*
1942  * Top-level command loop parsing
1943  */
cmdloop(struct http_connection * conn)1944 static void cmdloop(struct http_connection *conn)
1945 {
1946     int empty = 0;
1947     struct transaction_t txn;
1948 
1949     /* Start with an empty (clean) transaction */
1950     memset(&txn, 0, sizeof(struct transaction_t));
1951     txn.conn = conn;
1952 
1953     /* Pre-allocate our working buffer */
1954     buf_ensure(&txn.buf, 1024);
1955 
1956     for (;;) {
1957         int ret = 0;
1958 
1959         /* Reset txn state */
1960         transaction_reset(&txn);
1961 
1962         /* Check for input from client */
1963         do {
1964             /* Flush any buffered output */
1965 #ifdef HAVE_NGHTTP2
1966             if (conn->http2_session &&
1967                 nghttp2_session_want_write(conn->http2_session)) {
1968                 /* Send queued frame(s) */
1969                 int r = nghttp2_session_send(conn->http2_session);
1970                 if (r) {
1971                     syslog(LOG_ERR,
1972                            "nghttp2_session_send: %s", nghttp2_strerror(r));
1973                     /* XXX  can we do anything else here? */
1974                     transaction_free(&txn);
1975                     return;
1976                 }
1977             }
1978 #endif /* HAVE_NGHTTP2 */
1979 
1980             prot_flush(httpd_out);
1981             if (backend_current) prot_flush(backend_current->out);
1982 
1983             /* Check for shutdown file */
1984             if (shutdown_file(txn.buf.s, txn.buf.alloc) ||
1985                 (httpd_userid &&
1986                  userdeny(httpd_userid, config_ident, txn.buf.s, txn.buf.alloc))) {
1987                 txn.error.desc = txn.buf.s;
1988                 ret = HTTP_UNAVAILABLE;
1989                 break;
1990             }
1991 
1992             signals_poll();
1993 
1994         } while (!proxy_check_input(protin, httpd_in, httpd_out,
1995                                     backend_current ? backend_current->in : NULL,
1996                                     NULL, 0));
1997 
1998 
1999 #ifdef HAVE_NGHTTP2
2000         if (conn->http2_session) {
2001             syslog(LOG_DEBUG, "ret: %d, eof: %d, want read: %d", ret,
2002                    httpd_in->eof, nghttp2_session_want_read(conn->http2_session));
2003             if (nghttp2_session_want_read(conn->http2_session)) {
2004                 if (!ret) {
2005                     /* Read frame(s) */
2006                     int r = nghttp2_session_recv(conn->http2_session);
2007                     if (!r) continue;
2008                     else if (r != NGHTTP2_ERR_EOF) {
2009                         syslog(LOG_WARNING, "nghttp2_session_recv: %s (%s)",
2010                                nghttp2_strerror(r), prot_error(httpd_in));
2011                         txn.error.desc = prot_error(httpd_in);
2012                         ret = HTTP_TIMEOUT;
2013                     }
2014                 }
2015 
2016                 if (ret) {
2017                     /* Tell client we are closing session */
2018                     syslog(LOG_WARNING, "%s, closing connection", txn.error.desc);
2019                     syslog(LOG_DEBUG, "nghttp2_submit_goaway()");
2020                     nghttp2_submit_goaway(conn->http2_session, NGHTTP2_FLAG_NONE,
2021                                           nghttp2_session_get_last_proc_stream_id(
2022                                               conn->http2_session),
2023                                           NGHTTP2_NO_ERROR,
2024                                           (const uint8_t *) txn.error.desc,
2025                                           strlen(txn.error.desc));
2026                     continue;
2027                 }
2028             }
2029             else if (ret) {
2030                 protgroup_free(protin);
2031                 shut_down(0);
2032             }
2033 
2034             /* client closed connection */
2035             syslog(LOG_DEBUG, "client closed connection");
2036             transaction_free(&txn);
2037             return;
2038         }
2039 #endif /* HAVE_NGHTTP2 */
2040 
2041 
2042         if (ret) {
2043             txn.flags.conn = CONN_CLOSE;
2044             error_response(ret, &txn);
2045             protgroup_free(protin);
2046             shut_down(0);
2047         }
2048 
2049         /* Read request-line */
2050         struct request_line_t *req_line = &txn.req_line;
2051         syslog(LOG_DEBUG, "read & parse request-line");
2052         if (!prot_fgets(req_line->buf, MAX_REQ_LINE+1, httpd_in)) {
2053             txn.error.desc = prot_error(httpd_in);
2054             if (txn.error.desc && strcmp(txn.error.desc, PROT_EOF_STRING)) {
2055                 /* client timed out */
2056                 syslog(LOG_WARNING, "%s, closing connection", txn.error.desc);
2057                 ret = HTTP_TIMEOUT;
2058             }
2059             else {
2060                 /* client closed connection */
2061             }
2062 
2063             txn.flags.conn = CONN_CLOSE;
2064             goto done;
2065         }
2066 
2067         /* Ignore 1 empty line before request-line per RFC 7230 Sec 3.5 */
2068         if (!empty++ && !strcspn(req_line->buf, "\r\n")) continue;
2069         empty = 0;
2070 
2071 
2072 #ifdef HAVE_NGHTTP2
2073         /* Check for HTTP/2 client connection preface */
2074         if (http2_callbacks &&
2075             !strncmp(NGHTTP2_CLIENT_MAGIC,
2076                      req_line->buf, strlen(req_line->buf))) {
2077             syslog(LOG_DEBUG, "HTTP/2 client connection preface");
2078 
2079             /* Read remainder of preface */
2080             prot_readbuf(httpd_in, &txn.req_body.payload,
2081                          NGHTTP2_CLIENT_MAGIC_LEN - strlen(req_line->buf));
2082 
2083             /* Tell library not to look for preface */
2084             nghttp2_option_new(&conn->http2_options);
2085             nghttp2_option_set_no_recv_client_magic(conn->http2_options, 1);
2086 
2087             /* Start HTTP/2 */
2088             ret = starthttp2(conn, &txn);
2089             if (ret) {
2090                 /* XXX  what do we do here? */
2091                 transaction_free(&txn);
2092                 return;
2093             }
2094 
2095             continue;
2096         }
2097 #endif /* HAVE_NGHTTP2 */
2098 
2099 
2100         /* Parse request-line = method SP request-target SP HTTP-version CRLF */
2101         ret = parse_request_line(&txn);
2102 
2103         /* Parse headers */
2104         if (!ret) {
2105             ret = http_read_headers(httpd_in, 1 /* read_sep */,
2106                                     &txn.req_hdrs, &txn.error.desc);
2107         }
2108 
2109         if (ret) {
2110             txn.flags.conn = CONN_CLOSE;
2111             goto done;
2112         }
2113 
2114         /* Examine request */
2115         ret = examine_request(&txn);
2116         if (ret) goto done;
2117 
2118         /* Start method processing alarm (HTTP/1.1 only) */
2119         if (txn.flags.ver == VER_1_1) alarm(httpd_keepalive);
2120 
2121         /* Process the requested method */
2122         if (txn.req_tgt.namespace->premethod) {
2123             ret = txn.req_tgt.namespace->premethod(&txn);
2124         }
2125         if (!ret) {
2126             const struct method_t *meth_t =
2127                 &txn.req_tgt.namespace->methods[txn.meth];
2128 
2129             ret = (*meth_t->proc)(&txn, meth_t->params);
2130         }
2131 
2132         if (ret == HTTP_UNAUTHORIZED) {
2133             /* User must authenticate */
2134             ret = client_need_auth(&txn, 0);
2135         }
2136 
2137       done:
2138         /* Handle errors (success responses handled by method functions) */
2139         if (ret) error_response(ret, &txn);
2140 
2141         /* Read and discard any unread request body */
2142         if (!(txn.flags.conn & CONN_CLOSE)) {
2143             txn.req_body.flags |= BODY_DISCARD;
2144             if (http_read_body(httpd_in, httpd_out,
2145                                txn.req_hdrs, &txn.req_body, &txn.error.desc)) {
2146                 txn.flags.conn = CONN_CLOSE;
2147             }
2148         }
2149 
2150         if (txn.flags.conn & CONN_CLOSE) {
2151             /* Memory cleanup */
2152             transaction_free(&txn);
2153             return;
2154         }
2155 
2156         continue;
2157     }
2158 }
2159 
2160 /****************************  Parsing Routines  ******************************/
2161 
2162 /* Parse URI, returning the path */
parse_uri(unsigned meth,const char * uri,unsigned path_reqd,const char ** errstr)2163 EXPORTED xmlURIPtr parse_uri(unsigned meth, const char *uri, unsigned path_reqd,
2164                     const char **errstr)
2165 {
2166     xmlURIPtr p_uri;  /* parsed URI */
2167 
2168     /* Parse entire URI */
2169     if ((p_uri = xmlParseURI(uri)) == NULL) {
2170         *errstr = "Illegal request target URI";
2171         goto bad_request;
2172     }
2173 
2174     if (p_uri->scheme) {
2175         /* Check sanity of scheme */
2176 
2177         if (strcasecmp(p_uri->scheme, "http") &&
2178             strcasecmp(p_uri->scheme, "https")) {
2179             *errstr = "Unsupported URI scheme";
2180             goto bad_request;
2181         }
2182     }
2183 
2184     /* Check sanity of path */
2185     if (path_reqd && (!p_uri->path || !*p_uri->path)) {
2186         *errstr = "Empty path in target URI";
2187         goto bad_request;
2188     }
2189     else if (p_uri->path) {
2190         if ((p_uri->path[0] != '/') &&
2191             (strcmp(p_uri->path, "*") || (meth != METH_OPTIONS))) {
2192             /* No special URLs except for "OPTIONS * HTTP/1.1" */
2193             *errstr = "Illegal request target URI";
2194             goto bad_request;
2195         }
2196         else if (strstr(p_uri->path, "/..")) {
2197             /* Don't allow access up directory tree */
2198             *errstr = "Illegal request target URI";
2199             goto bad_request;
2200         }
2201         else if (strlen(p_uri->path) > MAX_MAILBOX_PATH) {
2202             *errstr = "Request target URI too long";
2203             goto bad_request;
2204         }
2205     }
2206 
2207     return p_uri;
2208 
2209   bad_request:
2210     if (p_uri) xmlFreeURI(p_uri);
2211     return NULL;
2212 }
2213 
2214 
2215 /* Calculate compile time of a file for use as Last-Modified and/or ETag */
calc_compile_time(const char * time,const char * date)2216 EXPORTED time_t calc_compile_time(const char *time, const char *date)
2217 {
2218     struct tm tm;
2219     char month[4];
2220     const char *monthname[] = {
2221         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
2222         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2223     };
2224 
2225     memset(&tm, 0, sizeof(struct tm));
2226     tm.tm_isdst = -1;
2227     sscanf(time, "%02d:%02d:%02d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
2228     sscanf(date, "%3s %2d %4d", month, &tm.tm_mday, &tm.tm_year);
2229     tm.tm_year -= 1900;
2230     for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
2231         if (!strcmp(month, monthname[tm.tm_mon])) break;
2232     }
2233 
2234     return mktime(&tm);
2235 }
2236 
2237 /* Parse Expect header(s) for interesting expectations */
parse_expect(struct transaction_t * txn)2238 static int parse_expect(struct transaction_t *txn)
2239 {
2240     const char **exp = spool_getheader(txn->req_hdrs, "Expect");
2241     int i, ret = 0;
2242 
2243     /* Expect not supported by HTTP/1.0 clients */
2244     if (exp && txn->flags.ver == VER_1_0) return HTTP_EXPECT_FAILED;
2245 
2246     /* Look for interesting expectations.  Unknown == error */
2247     for (i = 0; !ret && exp && exp[i]; i++) {
2248         tok_t tok = TOK_INITIALIZER(exp[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT);
2249         char *token;
2250 
2251         while (!ret && (token = tok_next(&tok))) {
2252             /* Check if client wants acknowledgment before sending body */
2253             if (!strcasecmp(token, "100-continue")) {
2254                 syslog(LOG_DEBUG, "Expect: 100-continue");
2255                 txn->req_body.flags |= BODY_CONTINUE;
2256             }
2257             else {
2258                 txn->error.desc = "Unsupported Expectation";
2259                 ret = HTTP_EXPECT_FAILED;
2260             }
2261         }
2262 
2263         tok_fini(&tok);
2264     }
2265 
2266     return ret;
2267 }
2268 
2269 
2270 /* Parse Connection header(s) for interesting options */
parse_connection(struct transaction_t * txn)2271 static int parse_connection(struct transaction_t *txn)
2272 {
2273     const char **conn = spool_getheader(txn->req_hdrs, "Connection");
2274     int i;
2275 
2276     if (conn && txn->flags.ver == VER_2) {
2277         txn->error.desc = "Connection not allowed in HTTP/2";
2278         return HTTP_BAD_REQUEST;
2279     }
2280 
2281     if (!httpd_timeout || txn->flags.ver == VER_1_0) {
2282         /* Non-persistent connection by default */
2283         txn->flags.conn |= CONN_CLOSE;
2284     }
2285 
2286     /* Look for interesting connection tokens */
2287     for (i = 0; conn && conn[i]; i++) {
2288         tok_t tok = TOK_INITIALIZER(conn[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT);
2289         char *token;
2290 
2291         while ((token = tok_next(&tok))) {
2292             switch (txn->flags.ver) {
2293             case VER_1_1:
2294                 if (!strcasecmp(token, "Upgrade")) {
2295                     /* Client wants to upgrade */
2296                     const char **upgrade =
2297                         spool_getheader(txn->req_hdrs, "Upgrade");
2298 
2299                     if (upgrade && upgrade[0]) {
2300                         syslog(LOG_NOTICE,
2301                                "client requested upgrade to %s", upgrade[0]);
2302 
2303                         if (!httpd_tls_done && tls_enabled() &&
2304                             !strncmp(upgrade[0], TLS_VERSION,
2305                                      strcspn(upgrade[0], " ,"))) {
2306                             /* Upgrade to TLS */
2307                             txn->flags.conn |= CONN_UPGRADE;
2308                             txn->flags.upgrade |= UPGRADE_TLS;
2309                         }
2310 #ifdef HAVE_NGHTTP2
2311                         else if (http2_callbacks &&
2312                                  !strncmp(upgrade[0],
2313                                           NGHTTP2_CLEARTEXT_PROTO_VERSION_ID,
2314                                           strcspn(upgrade[0], " ,"))) {
2315                             /* Upgrade to HTTP/2 */
2316                             txn->flags.conn |= CONN_UPGRADE;
2317                             txn->flags.upgrade |= UPGRADE_HTTP2;
2318                         }
2319 #endif /* HAVE_NGHTTP2 */
2320                         else {
2321                             /* Unknown/unsupported protocol - no upgrade */
2322                         }
2323                     }
2324                 }
2325                 else if (!strcasecmp(token, "close")) {
2326                     /* Non-persistent connection */
2327                     txn->flags.conn |= CONN_CLOSE;
2328                 }
2329                 break;
2330 
2331             case VER_1_0:
2332                 if (httpd_timeout && !strcasecmp(token, "keep-alive")) {
2333                     /* Persistent connection */
2334                     txn->flags.conn = CONN_KEEPALIVE;
2335                 }
2336                 break;
2337             }
2338         }
2339 
2340         tok_fini(&tok);
2341     }
2342 
2343     return 0;
2344 }
2345 
2346 
2347 /* Compare accept quality values so that they sort in descending order */
compare_accept(const struct accept * a1,const struct accept * a2)2348 static int compare_accept(const struct accept *a1, const struct accept *a2)
2349 {
2350     if (a2->qual < a1->qual) return -1;
2351     if (a2->qual > a1->qual) return 1;
2352     return 0;
2353 }
2354 
parse_accept(const char ** hdr)2355 struct accept *parse_accept(const char **hdr)
2356 {
2357     int i, n = 0, alloc = 0;
2358     struct accept *ret = NULL;
2359 #define GROW_ACCEPT 10;
2360 
2361     for (i = 0; hdr && hdr[i]; i++) {
2362         tok_t tok = TOK_INITIALIZER(hdr[i], ";,", TOK_TRIMLEFT|TOK_TRIMRIGHT);
2363         char *token;
2364 
2365         while ((token = tok_next(&tok))) {
2366             if (!strncmp(token, "q=", 2)) {
2367                 if (!ret) break;
2368                 ret[n-1].qual = strtof(token+2, NULL);
2369             }
2370             else {
2371                 if (n + 1 >= alloc)  {
2372                     alloc += GROW_ACCEPT;
2373                     ret = xrealloc(ret, alloc * sizeof(struct accept));
2374                 }
2375                 ret[n].token = xstrdup(token);
2376                 ret[n].qual = 1.0;
2377                 ret[++n].token = NULL;
2378             }
2379         }
2380         tok_fini(&tok);
2381     }
2382 
2383     qsort(ret, n, sizeof(struct accept),
2384           (int (*)(const void *, const void *)) &compare_accept);
2385 
2386     return ret;
2387 }
2388 
2389 
2390 /* Parse the query string and add key/value pairs to hash table */
parse_query_params(struct transaction_t * txn,const char * query)2391 void parse_query_params(struct transaction_t *txn, const char *query)
2392 {
2393     tok_t tok;
2394     char *param;
2395 
2396     assert(!buf_len(&txn->buf));  /* Unescape buffer */
2397 
2398     tok_init(&tok, query, "&", TOK_TRIMLEFT|TOK_TRIMRIGHT|TOK_EMPTY);
2399     while ((param = tok_next(&tok))) {
2400         struct strlist *vals;
2401         char *key, *value;
2402         size_t len;
2403 
2404         /* Split param into key and optional value */
2405         key = param;
2406         value = strchr(param, '=');
2407 
2408         if (!value) value = "";
2409         else *value++ = '\0';
2410         len = strlen(value);
2411         buf_ensure(&txn->buf, len+1);
2412 
2413         vals = hash_lookup(key, &txn->req_qparams);
2414         appendstrlist(&vals, xmlURIUnescapeString(value, len, txn->buf.s));
2415         hash_insert(key, vals, &txn->req_qparams);
2416     }
2417     tok_fini(&tok);
2418 
2419     buf_reset(&txn->buf);
2420 }
2421 
2422 
2423 /****************************  Response Routines  *****************************/
2424 
2425 
2426 /* Create HTTP-date ('buf' must be at least 30 characters) */
httpdate_gen(char * buf,size_t len,time_t t)2427 EXPORTED char *httpdate_gen(char *buf, size_t len, time_t t)
2428 {
2429     static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
2430                              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
2431     static char *wday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
2432 
2433     struct tm *tm = gmtime(&t);
2434 
2435     snprintf(buf, len, "%3s, %02d %3s %4d %02d:%02d:%02d GMT",
2436              wday[tm->tm_wday],
2437              tm->tm_mday, month[tm->tm_mon], tm->tm_year + 1900,
2438              tm->tm_hour, tm->tm_min, tm->tm_sec);
2439 
2440     return buf;
2441 }
2442 
2443 
2444 /* Create an HTTP Status-Line given response code */
http_statusline(long code)2445 EXPORTED const char *http_statusline(long code)
2446 {
2447     static struct buf statline = BUF_INITIALIZER;
2448     static unsigned tail = 0;
2449 
2450     if (!tail) {
2451         buf_setcstr(&statline, HTTP_VERSION);
2452         buf_putc(&statline, ' ');
2453         tail = buf_len(&statline);
2454     }
2455 
2456     buf_truncate(&statline, tail);
2457     buf_appendcstr(&statline, error_message(code));
2458     return buf_cstring(&statline);
2459 }
2460 
2461 
2462 /* Output an HTTP response header.
2463  * 'code' specifies the HTTP Status-Code and Reason-Phrase.
2464  * 'txn' contains the transaction context
2465  */
2466 
simple_hdr(struct transaction_t * txn,const char * name,const char * value,...)2467 EXPORTED void simple_hdr(struct transaction_t *txn,
2468                          const char *name, const char *value, ...)
2469 {
2470     struct buf buf = BUF_INITIALIZER;
2471     va_list args;
2472 
2473     va_start(args, value);
2474     buf_vprintf(&buf, value, args);
2475     va_end(args);
2476 
2477     syslog(LOG_DEBUG, "simple_hdr(%s: %s)", name, buf_cstring(&buf));
2478 
2479 #ifdef HAVE_NGHTTP2
2480     if (txn->flags.ver == VER_2) {
2481         if (txn->http2.num_resp_hdrs >= HTTP2_MAX_HEADERS) {
2482             buf_free(&buf);
2483             return;
2484         }
2485 
2486         nghttp2_nv *nv = &txn->http2.resp_hdrs[txn->http2.num_resp_hdrs];
2487 
2488         free(nv->value);
2489 
2490         nv->namelen = strlen(name);
2491         nv->name = (uint8_t *) name;
2492         nv->valuelen = buf_len(&buf);
2493         nv->value = (uint8_t *) buf_release(&buf);
2494         nv->flags = NGHTTP2_NV_FLAG_NO_COPY_VALUE;
2495 
2496         txn->http2.num_resp_hdrs++;
2497         return;
2498     }
2499 #endif /* HAVE_NGHTTP2 */
2500 
2501     prot_printf(txn->conn->pout, "%c%s: ", toupper(name[0]), name+1);
2502     prot_puts(txn->conn->pout, buf_cstring(&buf));
2503     prot_puts(txn->conn->pout, "\r\n");
2504 
2505     buf_free(&buf);
2506 }
2507 
2508 #define WWW_Authenticate(name, param)                           \
2509     simple_hdr(txn, "WWW-Authenticate", param ? "%s %s" : "%s", name, param)
2510 
2511 #define Access_Control_Expose(hdr)                              \
2512     simple_hdr(txn, "Access-Control-Expose-Headers", hdr)
2513 
comma_list_hdr(struct transaction_t * txn,const char * name,const char * vals[],unsigned flags,...)2514 EXPORTED void comma_list_hdr(struct transaction_t *txn, const char *name,
2515                              const char *vals[], unsigned flags, ...)
2516 {
2517     struct buf buf = BUF_INITIALIZER;
2518     const char *sep = "";
2519     va_list args;
2520     int i;
2521 
2522     va_start(args, flags);
2523 
2524     for (i = 0; vals[i]; i++) {
2525         if (flags & (1 << i)) {
2526             buf_appendcstr(&buf, sep);
2527             buf_vprintf(&buf, vals[i], args);
2528             sep = ", ";
2529         }
2530         else {
2531             /* discard any unused args */
2532             vsnprintf(NULL, 0, vals[i], args);
2533         }
2534     }
2535 
2536     va_end(args);
2537 
2538     simple_hdr(txn, name, buf_cstring(&buf));
2539 
2540     buf_free(&buf);
2541 }
2542 
list_auth_schemes(struct transaction_t * txn)2543 EXPORTED void list_auth_schemes(struct transaction_t *txn)
2544 {
2545     struct auth_challenge_t *auth_chal = &txn->auth_chal;
2546     unsigned conn_close = (txn->flags.conn & CONN_CLOSE);
2547     struct auth_scheme_t *scheme;
2548 
2549     /* Advertise available schemes that can work with the type of connection */
2550     for (scheme = auth_schemes; scheme->name; scheme++) {
2551         if ((avail_auth_schemes & (1 << scheme->idx)) &&
2552             !(conn_close && (scheme->flags & AUTH_NEED_PERSIST))) {
2553             auth_chal->param = NULL;
2554 
2555             if (scheme->flags & AUTH_SERVER_FIRST) {
2556                 /* Generate the initial challenge */
2557                 http_auth(scheme->name, txn);
2558 
2559                 if (!auth_chal->param) continue;  /* If fail, skip it */
2560             }
2561             WWW_Authenticate(scheme->name, auth_chal->param);
2562         }
2563     }
2564 }
2565 
allow_hdr(struct transaction_t * txn,const char * name,unsigned allow)2566 EXPORTED void allow_hdr(struct transaction_t *txn,
2567                         const char *name, unsigned allow)
2568 {
2569     const char *meths[] = {
2570         "OPTIONS, GET, HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", NULL
2571     };
2572 
2573     comma_list_hdr(txn, name, meths, allow);
2574 
2575     if (allow & ALLOW_DAV) {
2576         simple_hdr(txn, name, "PROPFIND, REPORT, COPY%s%s%s%s%s",
2577                    (allow & ALLOW_DELETE)    ? ", MOVE" : "",
2578                    (allow & ALLOW_PROPPATCH) ? ", PROPPATCH" : "",
2579                    (allow & ALLOW_MKCOL)     ? ", MKCOL" : "",
2580                    (allow & ALLOW_WRITE)     ? ", LOCK, UNLOCK" : "",
2581                    (allow & ALLOW_ACL)       ? ", ACL" : "");
2582         if ((allow & ALLOW_CAL) && (allow & ALLOW_MKCOL))
2583             simple_hdr(txn, name, "MKCALENDAR");
2584     }
2585 }
2586 
accept_patch_hdr(struct transaction_t * txn,const struct patch_doc_t * patch)2587 EXPORTED void accept_patch_hdr(struct transaction_t *txn,
2588                                const struct patch_doc_t *patch)
2589 {
2590     struct buf buf = BUF_INITIALIZER;
2591     const char *sep = "";
2592     int i;
2593 
2594     for (i = 0; patch[i].format; i++) {
2595         buf_appendcstr(&buf, sep);
2596         buf_appendcstr(&buf, patch[i].format);
2597         sep = ", ";
2598     }
2599 
2600     simple_hdr(txn, "Accept-Patch", buf_cstring(&buf));
2601 
2602     buf_free(&buf);
2603 }
2604 
2605 #define MD5_BASE64_LEN 25   /* ((MD5_DIGEST_LENGTH / 3) + 1) * 4 */
2606 
content_md5_hdr(struct transaction_t * txn,const unsigned char * md5)2607 EXPORTED void content_md5_hdr(struct transaction_t *txn,
2608                               const unsigned char *md5)
2609 {
2610     char base64[MD5_BASE64_LEN+1];
2611 
2612     sasl_encode64((char *) md5, MD5_DIGEST_LENGTH,
2613                   base64, MD5_BASE64_LEN, NULL);
2614     simple_hdr(txn, "Content-MD5", base64);
2615 }
2616 
begin_resp_headers(struct transaction_t * txn,long code)2617 EXPORTED void begin_resp_headers(struct transaction_t *txn, long code)
2618 {
2619 #ifdef HAVE_NGHTTP2
2620     if (txn->flags.ver == VER_2) {
2621         txn->http2.num_resp_hdrs = 0;
2622         if (code) simple_hdr(txn, ":status", "%.3s", error_message(code));
2623         return;
2624     }
2625 #endif /* HAVE_NGHTTP2 */
2626 
2627     if (code) prot_printf(txn->conn->pout, "%s\r\n", http_statusline(code));
2628     return;
2629 }
2630 
end_resp_headers(struct transaction_t * txn,long code)2631 EXPORTED int end_resp_headers(struct transaction_t *txn, long code)
2632 {
2633 #ifdef HAVE_NGHTTP2
2634     if (txn->flags.ver == VER_2) {
2635         uint8_t flags = NGHTTP2_FLAG_NONE;
2636         int r;
2637 
2638         syslog(LOG_DEBUG,
2639                "end_resp_headers(code = %ld, len = %ld, flags.te = %#x)",
2640                code, txn->resp_body.len, txn->flags.te);
2641 
2642         switch (code) {
2643         case 0:
2644             /* Trailer */
2645             flags = NGHTTP2_FLAG_END_STREAM;
2646             break;
2647 
2648         case HTTP_CONTINUE:
2649         case HTTP_PROCESSING:
2650             /* Provisional response */
2651             break;
2652 
2653         case HTTP_NO_CONTENT:
2654         case HTTP_NOT_MODIFIED:
2655             /* MUST NOT include a body */
2656             flags = NGHTTP2_FLAG_END_STREAM;
2657             break;
2658 
2659         default:
2660             if (txn->meth == METH_HEAD) {
2661                 /* MUST NOT include a body */
2662                 flags = NGHTTP2_FLAG_END_STREAM;
2663             }
2664             else if (!(txn->resp_body.len || (txn->flags.te & TE_CHUNKED))) {
2665                 /* Empty body */
2666                 flags = NGHTTP2_FLAG_END_STREAM;
2667             }
2668             break;
2669         }
2670 
2671         syslog(LOG_DEBUG, "%s(id=%d, flags=%#x)",
2672                code ? "nghttp2_submit headers" : "nghttp2_submit_trailers",
2673                txn->http2.stream_id, flags);
2674 
2675         if (code) {
2676             r = nghttp2_submit_headers(txn->conn->http2_session,
2677                                        flags, txn->http2.stream_id, NULL,
2678                                        txn->http2.resp_hdrs,
2679                                        txn->http2.num_resp_hdrs, NULL);
2680         }
2681         else {
2682             r = nghttp2_submit_trailer(txn->conn->http2_session,
2683                                        txn->http2.stream_id,
2684                                        txn->http2.resp_hdrs,
2685                                        txn->http2.num_resp_hdrs);
2686         }
2687         if (r) {
2688             syslog(LOG_ERR, "%s: %s",
2689                    code ? "nghttp2_submit headers" : "nghttp2_submit_trailers",
2690                    nghttp2_strerror(r));
2691             return r;
2692         }
2693 
2694         return 0;
2695     }
2696 #else
2697     (void) code; /* silence 'unused variable code' warning */
2698 #endif /* HAVE_NGHTTP2 */
2699 
2700     /* CRLF terminating the header block */
2701     prot_puts(txn->conn->pout, "\r\n");
2702 
2703     return 0;
2704 }
2705 
2706 
response_header(long code,struct transaction_t * txn)2707 EXPORTED void response_header(long code, struct transaction_t *txn)
2708 {
2709     time_t now;
2710     char datestr[30];
2711     const char **hdr;
2712     struct auth_challenge_t *auth_chal = &txn->auth_chal;
2713     struct resp_body_t *resp_body = &txn->resp_body;
2714     static struct buf log = BUF_INITIALIZER;
2715 
2716     /* Stop method processing alarm */
2717     alarm(0);
2718     gotsigalrm = 0;
2719 
2720 
2721     /* Status-Line */
2722     begin_resp_headers(txn, code);
2723 
2724 
2725     switch (code) {
2726     default:
2727         /* Final response */
2728         now = time(0);
2729         httpdate_gen(datestr, sizeof(datestr), now);
2730         simple_hdr(txn, "Date", datestr);
2731 
2732         if (txn->flags.ver == VER_2) break;
2733 
2734         /* Fall through and specify connection options - HTTP/1.x only */
2735         GCC_FALLTHROUGH
2736 
2737     case HTTP_SWITCH_PROT:
2738         if (txn->flags.conn) {
2739             /* Construct Connection header */
2740             const char *conn_tokens[] =
2741                 { "close", "Upgrade", "Keep-Alive", NULL };
2742 
2743             comma_list_hdr(txn, "Connection", conn_tokens, txn->flags.conn);
2744 
2745             if (txn->flags.upgrade) {
2746                 /* Construct Upgrade header */
2747                 const char *upgrd_tokens[] =
2748                     { TLS_VERSION,
2749 #ifdef HAVE_NGHTTP2
2750                       NGHTTP2_CLEARTEXT_PROTO_VERSION_ID,
2751 #endif
2752                       NULL };
2753 
2754                 comma_list_hdr(txn, "Upgrade", upgrd_tokens, txn->flags.upgrade);
2755             }
2756             if (txn->flags.conn & CONN_KEEPALIVE) {
2757                 simple_hdr(txn, "Keep-Alive", "timeout=%d", httpd_timeout);
2758             }
2759         }
2760 
2761         if (code != HTTP_SWITCH_PROT) break;
2762 
2763         /* Fall through as provisional response */
2764         GCC_FALLTHROUGH
2765 
2766     case HTTP_CONTINUE:
2767     case HTTP_PROCESSING:
2768         /* Provisional response - nothing else needed */
2769         end_resp_headers(txn, code);
2770 
2771         /* Force the response to the client immediately */
2772         prot_flush(httpd_out);
2773 
2774         return;
2775     }
2776 
2777 
2778     /* Control Data */
2779     if (httpd_tls_done) {
2780         simple_hdr(txn, "Strict-Transport-Security", "max-age=600");
2781     }
2782     if (txn->location) {
2783         simple_hdr(txn, "Location", txn->location);
2784     }
2785     if (txn->flags.mime) {
2786         simple_hdr(txn, "MIME-Version", "1.0");
2787     }
2788     if (txn->flags.cc) {
2789         /* Construct Cache-Control header */
2790         const char *cc_dirs[] =
2791             { "must-revalidate", "no-cache", "no-store", "no-transform",
2792               "public", "private", "max-age=%d", NULL };
2793 
2794         comma_list_hdr(txn, "Cache-Control",
2795                        cc_dirs, txn->flags.cc, resp_body->maxage);
2796 
2797         if (txn->flags.cc & CC_MAXAGE) {
2798             httpdate_gen(datestr, sizeof(datestr), now + resp_body->maxage);
2799             simple_hdr(txn, "Expires", datestr);
2800         }
2801     }
2802     if (txn->flags.cors) {
2803         /* Construct Cross-Origin Resource Sharing headers */
2804         simple_hdr(txn, "Access-Control-Allow-Origin",
2805                       *spool_getheader(txn->req_hdrs, "Origin"));
2806         simple_hdr(txn, "Access-Control-Allow-Credentials", "true");
2807 
2808         if (txn->flags.cors == CORS_PREFLIGHT) {
2809             allow_hdr(txn, "Access-Control-Allow-Methods", txn->req_tgt.allow);
2810 
2811             for (hdr = spool_getheader(txn->req_hdrs,
2812                                        "Access-Control-Request-Headers");
2813                  hdr && *hdr; hdr++) {
2814                 simple_hdr(txn, "Access-Control-Allow-Headers", *hdr);
2815             }
2816             simple_hdr(txn, "Access-Control-Max-Age", "3600");
2817         }
2818     }
2819     if (txn->flags.vary && !(txn->flags.cc & CC_NOCACHE)) {
2820         /* Construct Vary header */
2821         const char *vary_hdrs[] =
2822             { "Accept", "Accept-Encoding", "Brief", "Prefer", NULL };
2823 
2824         comma_list_hdr(txn, "Vary", vary_hdrs, txn->flags.vary);
2825     }
2826 
2827 
2828     /* Authentication Challenges */
2829     if (code == HTTP_UNAUTHORIZED) {
2830         if (!auth_chal->scheme) {
2831             /* Require authentication by advertising all available schemes */
2832             list_auth_schemes(txn);
2833         }
2834         else {
2835             /* Continue with current authentication exchange */
2836             WWW_Authenticate(auth_chal->scheme->name, auth_chal->param);
2837         }
2838     }
2839     else if (auth_chal->param) {
2840         /* Authentication completed with success data */
2841         if (auth_chal->scheme->send_success) {
2842             /* Special handling of success data for this scheme */
2843             auth_chal->scheme->send_success(txn, auth_chal->scheme->name,
2844                                             auth_chal->param);
2845         }
2846         else {
2847             /* Default handling of success data */
2848             WWW_Authenticate(auth_chal->scheme->name, auth_chal->param);
2849         }
2850     }
2851 
2852 
2853     /* Response Context */
2854     if (txn->req_tgt.allow & ALLOW_ISCHEDULE) {
2855         simple_hdr(txn, "iSchedule-Version", "1.0");
2856 
2857         if (resp_body->iserial) {
2858             simple_hdr(txn, "iSchedule-Capabilities", "%ld", resp_body->iserial);
2859         }
2860     }
2861     if (resp_body->link) {
2862         simple_hdr(txn, "Link", resp_body->link);
2863     }
2864     if (resp_body->patch) {
2865         accept_patch_hdr(txn, resp_body->patch);
2866     }
2867 
2868     switch (code) {
2869     case HTTP_OK:
2870         switch (txn->meth) {
2871         case METH_GET:
2872         case METH_HEAD:
2873             /* Construct Accept-Ranges header for GET and HEAD responses */
2874             simple_hdr(txn, "Accept-Ranges",
2875                        txn->flags.ranges ? "bytes" : "none");
2876             break;
2877 
2878         case METH_OPTIONS:
2879             if (config_serverinfo == IMAP_ENUM_SERVERINFO_ON) {
2880                 simple_hdr(txn, "Server", buf_cstring(&serverinfo));
2881             }
2882 
2883             if (!httpd_userid && !auth_chal->scheme) {
2884                 /* Advertise all available auth schemes */
2885                 list_auth_schemes(txn);
2886             }
2887 
2888             if (txn->req_tgt.allow & ALLOW_DAV) {
2889                 /* Construct DAV header(s) based on namespace of request URL */
2890                 simple_hdr(txn, "DAV", "1, 2, 3, access-control,"
2891                            " extended-mkcol, resource-sharing");
2892                 if (txn->req_tgt.allow & ALLOW_CAL) {
2893                     simple_hdr(txn, "DAV", "calendar-access%s%s",
2894                                (txn->req_tgt.allow & ALLOW_CAL_SCHED) ?
2895                                ", calendar-auto-schedule" : "",
2896                                (txn->req_tgt.allow & ALLOW_CAL_NOTZ) ?
2897                                ", calendar-no-timezone" : "");
2898                     simple_hdr(txn, "DAV", "calendar-query-extended%s%s",
2899                                (txn->req_tgt.allow & ALLOW_CAL_AVAIL) ?
2900                                ", calendar-availability" : "",
2901                                (txn->req_tgt.allow & ALLOW_CAL_ATTACH) ?
2902                                ", calendar-managed-attachments" : "");
2903 
2904                     /* Backwards compatibility with older Apple clients */
2905                     simple_hdr(txn, "DAV", "calendarserver-sharing%s",
2906                                (txn->req_tgt.allow &
2907                                 (ALLOW_CAL_AVAIL | ALLOW_CAL_SCHED)) ==
2908                                (ALLOW_CAL_AVAIL | ALLOW_CAL_SCHED) ?
2909                                ", inbox-availability" : "");
2910                 }
2911                 if (txn->req_tgt.allow & ALLOW_CARD) {
2912                     simple_hdr(txn, "DAV", "addressbook");
2913                 }
2914             }
2915 
2916             /* Access-Control-Allow-Methods supersedes Allow */
2917             if (txn->flags.cors != CORS_PREFLIGHT) {
2918                 /* Construct Allow header(s) */
2919                 allow_hdr(txn, "Allow", txn->req_tgt.allow);
2920             }
2921             break;
2922         }
2923         break;
2924 
2925     case HTTP_NOT_ALLOWED:
2926         /* Construct Allow header(s) for 405 response */
2927         allow_hdr(txn, "Allow", txn->req_tgt.allow);
2928         break;
2929 
2930     case HTTP_BAD_CE:
2931         /* Construct Accept-Encoding header for 415 response */
2932 #ifdef HAVE_ZLIB
2933         simple_hdr(txn, "Accept-Encoding", "gzip, deflate");
2934 #else
2935         simple_hdr(txn, "Accept-Encoding", "identity");
2936 #endif
2937         break;
2938     }
2939 
2940 
2941     /* Validators */
2942     if (resp_body->lock) {
2943         simple_hdr(txn, "Lock-Token", "<%s>", resp_body->lock);
2944         if (txn->flags.cors) Access_Control_Expose("Lock-Token");
2945     }
2946     if (resp_body->ctag) {
2947         simple_hdr(txn, "CTag", "%s", resp_body->ctag);
2948         if (txn->flags.cors) Access_Control_Expose("CTag");
2949     }
2950     if (resp_body->stag) {
2951         simple_hdr(txn, "Schedule-Tag", "\"%s\"", resp_body->stag);
2952         if (txn->flags.cors) Access_Control_Expose("Schedule-Tag");
2953     }
2954     if (resp_body->etag) {
2955         simple_hdr(txn, "ETag", "%s\"%s\"",
2956                       resp_body->enc ? "W/" : "", resp_body->etag);
2957         if (txn->flags.cors) Access_Control_Expose("ETag");
2958     }
2959     if (resp_body->lastmod) {
2960         /* Last-Modified MUST NOT be in the future */
2961         resp_body->lastmod = MIN(resp_body->lastmod, now);
2962         httpdate_gen(datestr, sizeof(datestr), resp_body->lastmod);
2963         simple_hdr(txn, "Last-Modified", datestr);
2964     }
2965 
2966 
2967     /* Representation Metadata */
2968     if (resp_body->prefs) {
2969         /* Construct Preference-Applied header */
2970         const char *prefs[] =
2971             { "return=minimal", "return=representation", "depth-noroot", NULL };
2972 
2973         comma_list_hdr(txn, "Preference-Applied", prefs, resp_body->prefs);
2974         if (txn->flags.cors) Access_Control_Expose("Preference-Applied");
2975     }
2976     if (resp_body->cmid) {
2977         simple_hdr(txn, "Cal-Managed-ID", "\"%s\"", resp_body->cmid);
2978         if (txn->flags.cors) Access_Control_Expose("Cal-Managed-ID");
2979     }
2980     if (resp_body->type) {
2981         simple_hdr(txn, "Content-Type", resp_body->type);
2982 
2983         if (resp_body->fname) {
2984             simple_hdr(txn, "Content-Disposition",
2985                        "attachment; filename=\"%s\"", resp_body->fname);
2986         }
2987         if (txn->resp_body.enc) {
2988             /* Construct Content-Encoding header */
2989             const char *ce[] =
2990                 { "deflate", "gzip", "br", NULL };
2991 
2992             comma_list_hdr(txn, "Content-Encoding", ce, txn->resp_body.enc);
2993         }
2994         if (resp_body->lang) {
2995             simple_hdr(txn, "Content-Language", resp_body->lang);
2996         }
2997         if (resp_body->loc) {
2998             simple_hdr(txn, "Content-Location", resp_body->loc);
2999             if (txn->flags.cors) Access_Control_Expose("Content-Location");
3000         }
3001         if (resp_body->md5) {
3002             content_md5_hdr(txn, resp_body->md5);
3003         }
3004     }
3005 
3006 
3007     /* Payload */
3008     switch (code) {
3009     case HTTP_NO_CONTENT:
3010     case HTTP_NOT_MODIFIED:
3011         /* MUST NOT include a body */
3012         resp_body->len = 0;
3013         break;
3014 
3015     case HTTP_PARTIAL:
3016     case HTTP_BAD_RANGE:
3017         if (resp_body->range) {
3018             simple_hdr(txn, "Content-Range", "bytes %lu-%lu/%lu",
3019                        resp_body->range->first, resp_body->range->last,
3020                        resp_body->len);
3021 
3022             /* Set actual content length of range */
3023             resp_body->len =
3024                 resp_body->range->last - resp_body->range->first + 1;
3025 
3026             free(resp_body->range);
3027         }
3028         else {
3029             simple_hdr(txn, "Content-Range", "bytes */%lu", resp_body->len);
3030             resp_body->len = 0;  /* No content */
3031         }
3032 
3033         /* Fall through and specify framing */
3034         GCC_FALLTHROUGH
3035 
3036     default:
3037         if (txn->flags.te) {
3038             /* HTTP/1.1 only - we use close-delimiting for HTTP/1.0 */
3039             if (txn->flags.ver == VER_1_1) {
3040                 /* Construct Transfer-Encoding header */
3041                 const char *te[] =
3042                     { "deflate", "gzip", "chunked", NULL };
3043 
3044                 comma_list_hdr(txn, "Transfer-Encoding", te, txn->flags.te);
3045             }
3046 
3047             if (txn->flags.trailer & ~TRAILER_PROXY) {
3048                 /* Construct Trailer header */
3049                 const char *trailer_hdrs[] = { "Content-MD5", NULL };
3050 
3051                 comma_list_hdr(txn, "Trailer", trailer_hdrs, txn->flags.trailer);
3052             }
3053         }
3054         else if (resp_body->len || txn->meth != METH_HEAD) {
3055             simple_hdr(txn, "Content-Length", "%lu", resp_body->len);
3056         }
3057     }
3058 
3059 
3060     /* End of headers */
3061     end_resp_headers(txn, code);
3062 
3063 
3064     /* Log the client request and our response */
3065     buf_reset(&log);
3066     /* Add client data */
3067     buf_printf(&log, "%s", httpd_clienthost);
3068     if (httpd_userid) buf_printf(&log, " as \"%s\"", httpd_userid);
3069     if (txn->req_hdrs &&
3070         (hdr = spool_getheader(txn->req_hdrs, "User-Agent"))) {
3071         buf_printf(&log, " with \"%s\"", hdr[0]);
3072         if ((hdr = spool_getheader(txn->req_hdrs, "X-Client")))
3073             buf_printf(&log, " by \"%s\"", hdr[0]);
3074         else if ((hdr = spool_getheader(txn->req_hdrs, "X-Requested-With")))
3075             buf_printf(&log, " by \"%s\"", hdr[0]);
3076     }
3077     /* Add request-line */
3078     buf_appendcstr(&log, "; \"");
3079     if (txn->req_line.meth) {
3080         buf_printf(&log, "%s",
3081                    txn->flags.override ? "POST" : txn->req_line.meth);
3082         if (txn->req_line.uri) {
3083             buf_printf(&log, " %s", txn->req_line.uri);
3084             if (txn->req_line.ver) {
3085                 buf_printf(&log, " %s", txn->req_line.ver);
3086                 if (code != HTTP_URI_TOO_LONG && *txn->req_line.buf) {
3087                     char *p = txn->req_line.ver + strlen(txn->req_line.ver) + 1;
3088                     if (*p) buf_printf(&log, " %s", p);
3089                 }
3090             }
3091         }
3092     }
3093     buf_appendcstr(&log, "\"");
3094     if (txn->req_hdrs) {
3095         /* Add any request modifying headers */
3096         const char *sep = " (";
3097 
3098         if (txn->flags.override) {
3099             buf_printf(&log, "%smethod-override=%s", sep, txn->req_line.meth);
3100             sep = "; ";
3101         }
3102         if ((hdr = spool_getheader(txn->req_hdrs, "Origin"))) {
3103             buf_printf(&log, "%sorigin=%s", sep, hdr[0]);
3104             sep = "; ";
3105         }
3106         if ((hdr = spool_getheader(txn->req_hdrs, "Referer"))) {
3107             buf_printf(&log, "%sreferer=%s", sep, hdr[0]);
3108             sep = "; ";
3109         }
3110         if ((hdr = spool_getheader(txn->req_hdrs, "Destination"))) {
3111             buf_printf(&log, "%sdestination=%s", sep, hdr[0]);
3112             sep = "; ";
3113         }
3114         if ((hdr = spool_getheader(txn->req_hdrs, "Lock-Token"))) {
3115             buf_printf(&log, "%slock-token=%s", sep, hdr[0]);
3116             sep = "; ";
3117         }
3118         if ((hdr = spool_getheader(txn->req_hdrs, "If"))) {
3119             buf_printf(&log, "%sif=%s", sep, hdr[0]);
3120             sep = "; ";
3121         }
3122         if ((hdr = spool_getheader(txn->req_hdrs, "If-Schedule-Tag-Match"))) {
3123             buf_printf(&log, "%sif-schedule-tag-match=%s", sep, hdr[0]);
3124             sep = "; ";
3125         }
3126         else if ((hdr = spool_getheader(txn->req_hdrs, "If-Match"))) {
3127             buf_printf(&log, "%sif-match=%s", sep, hdr[0]);
3128             sep = "; ";
3129         }
3130         else if ((hdr = spool_getheader(txn->req_hdrs, "If-Unmodified-Since"))) {
3131             buf_printf(&log, "%sif-unmodified-since=%s", sep, hdr[0]);
3132             sep = "; ";
3133         }
3134         if ((hdr = spool_getheader(txn->req_hdrs, "If-None-Match"))) {
3135             buf_printf(&log, "%sif-none-match=%s", sep, hdr[0]);
3136             sep = "; ";
3137         }
3138         else if ((hdr = spool_getheader(txn->req_hdrs, "If-Modified-Since"))) {
3139             buf_printf(&log, "%sif-modified-since=%s", sep, hdr[0]);
3140             sep = "; ";
3141         }
3142         if ((hdr = spool_getheader(txn->req_hdrs, ":type"))) {
3143             buf_printf(&log, "%stype=%s", sep, hdr[0]);
3144             sep = "; ";
3145         }
3146         if ((hdr = spool_getheader(txn->req_hdrs, "Depth"))) {
3147             buf_printf(&log, "%sdepth=%s", sep, hdr[0]);
3148             sep = "; ";
3149         }
3150         if (*sep == ';') buf_appendcstr(&log, ")");
3151     }
3152     /* Add response */
3153     buf_printf(&log, " => \"%s %s\"",
3154                txn->flags.ver == VER_2 ? HTTP2_VERSION : HTTP_VERSION,
3155                error_message(code));
3156     /* Add any auxiliary response data */
3157     if (txn->location) {
3158         buf_printf(&log, " (location=%s)", txn->location);
3159     }
3160     else if (txn->flags.cors) {
3161         buf_appendcstr(&log, " (allow-origin)");
3162     }
3163     else if (txn->error.desc) {
3164         buf_printf(&log, " (error=%s)", txn->error.desc);
3165     }
3166     syslog(LOG_INFO, "%s", buf_cstring(&log));
3167 }
3168 
3169 
keepalive_response(struct transaction_t * txn)3170 EXPORTED void keepalive_response(struct transaction_t *txn)
3171 {
3172     if (gotsigalrm) {
3173         response_header(HTTP_PROCESSING, txn);
3174         alarm(httpd_keepalive);
3175     }
3176 }
3177 
3178 
3179 /*
3180  * Output an HTTP response with multipart body data.
3181  *
3182  * An initial call with 'code' != 0 will output a response header
3183  * and the preamble.
3184  * All subsequent calls should have 'code' = 0 to output just a body part.
3185  * A final call with 'len' = 0 ends the multipart body.
3186  */
write_multipart_body(long code,struct transaction_t * txn,const char * buf,unsigned len)3187 EXPORTED void write_multipart_body(long code, struct transaction_t *txn,
3188                           const char *buf, unsigned len)
3189 {
3190     static char boundary[100];
3191     struct buf *body = &txn->resp_body.payload;
3192 
3193     if (code) {
3194         const char *preamble =
3195             "This is a message with multiple parts in MIME format.\r\n";
3196 
3197         txn->flags.mime = 1;
3198 
3199         /* Create multipart boundary */
3200         snprintf(boundary, sizeof(boundary), "%s-%ld-%ld-%ld",
3201                  *spool_getheader(txn->req_hdrs, "Host"),
3202                  (long) getpid(), (long) time(0), (long) rand());
3203 
3204         /* Create Content-Type w/ boundary */
3205         assert(!buf_len(&txn->buf));
3206         buf_printf(&txn->buf, "%s; boundary=\"%s\"",
3207                    txn->resp_body.type, boundary);
3208         txn->resp_body.type = buf_cstring(&txn->buf);
3209 
3210         /* Setup for chunked response and begin multipart */
3211         txn->flags.te |= TE_CHUNKED;
3212         if (!buf) {
3213             buf = preamble;
3214             len = strlen(preamble);
3215         }
3216         write_body(code, txn, buf, len);
3217     }
3218     else if (len) {
3219         /* Output delimiter and MIME part-headers */
3220         buf_reset(body);
3221         buf_printf(body, "\r\n--%s\r\n", boundary);
3222         buf_printf(body, "Content-Type: %s\r\n", txn->resp_body.type);
3223         if (txn->resp_body.range) {
3224             buf_printf(body, "Content-Range: bytes %lu-%lu/%lu\r\n",
3225                        txn->resp_body.range->first,
3226                        txn->resp_body.range->last,
3227                        txn->resp_body.len);
3228         }
3229         buf_printf(body, "Content-Length: %d\r\n\r\n", len);
3230         write_body(0, txn, buf_cstring(body), buf_len(body));
3231 
3232         /* Output body-part data */
3233         write_body(0, txn, buf, len);
3234     }
3235     else {
3236         const char *epilogue = "\r\nEnd of MIME multipart body.\r\n";
3237 
3238         /* Output close-delimiter and epilogue */
3239         buf_reset(body);
3240         buf_printf(body, "\r\n--%s--\r\n%s", boundary, epilogue);
3241         write_body(0, txn, buf_cstring(body), buf_len(body));
3242 
3243         /* End of output */
3244         write_body(0, txn, NULL, 0);
3245     }
3246 }
3247 
3248 
3249 /* Output multipart/byteranges */
multipart_byteranges(struct transaction_t * txn,const char * msg_base)3250 static void multipart_byteranges(struct transaction_t *txn,
3251                                  const char *msg_base)
3252 {
3253     /* Save Content-Range and Content-Type pointers */
3254     struct range *range = txn->resp_body.range;
3255     const char *type = txn->resp_body.type;
3256 
3257     /* Start multipart response */
3258     txn->resp_body.range = NULL;
3259     txn->resp_body.type = "multipart/byteranges";
3260     write_multipart_body(HTTP_PARTIAL, txn, NULL, 0);
3261 
3262     txn->resp_body.type = type;
3263     while (range) {
3264         unsigned long offset = range->first;
3265         unsigned long datalen = range->last - range->first + 1;
3266         struct range *next = range->next;
3267 
3268         /* Output range as body part */
3269         txn->resp_body.range = range;
3270         write_multipart_body(0, txn, msg_base + offset, datalen);
3271 
3272         /* Cleanup */
3273         free(range);
3274         range = next;
3275     }
3276 
3277     /* End of multipart body */
3278     write_multipart_body(0, txn, NULL, 0);
3279 }
3280 
3281 
3282 /*
3283  * Output an HTTP response with body data, compressed as necessary.
3284  *
3285  * For chunked body data, an initial call with 'code' != 0 will output
3286  * a response header and the first body chunk.
3287  * All subsequent calls should have 'code' = 0 to output just the body chunk.
3288  * A final call with 'len' = 0 ends the chunked body.
3289  *
3290  * NOTE: HTTP/1.0 clients can't handle chunked encoding,
3291  *       so we use bare chunks and close the connection when done.
3292  */
write_body(long code,struct transaction_t * txn,const char * buf,unsigned len)3293 EXPORTED void write_body(long code, struct transaction_t *txn,
3294                          const char *buf, unsigned len)
3295 {
3296     unsigned outlen = len, offset = 0, last_chunk;
3297     int do_md5 = (txn->meth == METH_HEAD) ? 0 :
3298         config_getswitch(IMAPOPT_HTTPCONTENTMD5);
3299     static MD5_CTX ctx;
3300     static unsigned char md5[MD5_DIGEST_LENGTH];
3301 
3302     syslog(LOG_DEBUG, "write_body(code = %ld, flags.te = %#x, len = %u)",
3303            code, txn->flags.te, len);
3304 
3305     if (txn->flags.te & TE_CHUNKED) last_chunk = !(code || len);
3306     else {
3307         /* Handle static content as last chunk */
3308         last_chunk = 1;
3309 
3310         if (len < GZIP_MIN_LEN) {
3311             /* Don't compress small static content */
3312             txn->resp_body.enc = CE_IDENTITY;
3313             txn->flags.te = TE_NONE;
3314         }
3315     }
3316 
3317     /* Compress data */
3318     if (txn->resp_body.enc == CE_BR) {
3319 #ifdef HAVE_BROTLI
3320         /* Only flush for static content or on last (zero-length) chunk */
3321         unsigned op = last_chunk ?
3322             BROTLI_OPERATION_FINISH : BROTLI_OPERATION_FLUSH;
3323         BrotliEncoderState *brotli = txn->conn->brotli;
3324         const uint8_t *next_in = (const uint8_t *) buf;
3325         size_t avail_in = (size_t) len;
3326 
3327         buf_ensure(&txn->zbuf, BrotliEncoderMaxCompressedSize(avail_in));
3328         buf_reset(&txn->zbuf);
3329 
3330         do {
3331             uint8_t *next_out = (uint8_t *) txn->zbuf.s + txn->zbuf.len;
3332             size_t avail_out = txn->zbuf.alloc - txn->zbuf.len;
3333 
3334             if (!BrotliEncoderCompressStream(brotli, op,
3335                                              &avail_in, &next_in,
3336                                              &avail_out, &next_out, NULL)) {
3337                 syslog(LOG_ERR, "Brotli: Error while compressing data");
3338                 fatal("Brotli: Error while compressing data", EC_SOFTWARE);
3339             }
3340 
3341             txn->zbuf.len = txn->zbuf.alloc - avail_out;
3342         } while (avail_in || BrotliEncoderHasMoreOutput(brotli));
3343 
3344         buf = txn->zbuf.s;
3345         outlen = txn->zbuf.len;
3346 
3347         if (BrotliEncoderIsFinished(brotli)) {
3348             BrotliEncoderDestroyInstance(brotli);
3349             txn->conn->brotli = brotli_init();
3350         }
3351 #else
3352         /* XXX should never get here */
3353         fatal("Brotli Compression requested, but not available", EC_SOFTWARE);
3354 #endif /* HAVE_BROTLI */
3355     }
3356     else if (txn->resp_body.enc || txn->flags.te & ~TE_CHUNKED) {
3357 #ifdef HAVE_ZLIB
3358         /* Only flush for static content or on last (zero-length) chunk */
3359         unsigned flush = last_chunk ? Z_FINISH : Z_NO_FLUSH;
3360         z_stream *zstrm = txn->conn->zstrm;
3361 
3362         if (code) deflateReset(zstrm);
3363 
3364         zstrm->next_in = (Bytef *) buf;
3365         zstrm->avail_in = len;
3366 
3367         buf_ensure(&txn->zbuf, deflateBound(zstrm, zstrm->avail_in));
3368         buf_reset(&txn->zbuf);
3369 
3370         do {
3371 	    int zr;
3372 
3373 	    if (!zstrm->avail_out) {
3374 		unsigned pending;
3375 
3376 		zr = deflatePending(zstrm, &pending, Z_NULL);
3377 		if (zr != Z_OK) {
3378 		    /* something went wrong */
3379 		    syslog(LOG_ERR, "zlib deflate error: %d %s", zr, zstrm->msg);
3380 		    fatal("zlib: Error while compressing data", EC_SOFTWARE);
3381 		}
3382 
3383 		buf_ensure(&txn->zbuf, pending);
3384 	    }
3385 
3386             zstrm->next_out = (Bytef *) txn->zbuf.s + txn->zbuf.len;
3387             zstrm->avail_out = txn->zbuf.alloc - txn->zbuf.len;
3388 
3389             zr = deflate(zstrm, flush);
3390             if (!(zr == Z_OK || zr == Z_STREAM_END || zr == Z_BUF_ERROR)) {
3391 		/* something went wrong */
3392 		syslog(LOG_ERR, "zlib deflate error: %d %s", zr, zstrm->msg);
3393                 fatal("zlib: Error while compressing data", EC_SOFTWARE);
3394             }
3395 
3396             txn->zbuf.len = txn->zbuf.alloc - zstrm->avail_out;
3397 
3398         } while (!zstrm->avail_out);
3399 
3400         buf = txn->zbuf.s;
3401         outlen = txn->zbuf.len;
3402 #else
3403         /* XXX should never get here */
3404         fatal("Compression requested, but no zlib", EC_SOFTWARE);
3405 #endif /* HAVE_ZLIB */
3406     }
3407 
3408     if (code) {
3409         /* Initial call - prepare response header based on CE, TE and version */
3410         if (do_md5) MD5Init(&ctx);
3411 
3412         if (txn->flags.te & ~TE_CHUNKED) {
3413             /* Transfer-Encoded content MUST be chunked */
3414             txn->flags.te |= TE_CHUNKED;
3415         }
3416 
3417         if (!txn->flags.te) {
3418             /* Full/partial body (no encoding).
3419              *
3420              * In all cases, 'resp_body.len' is used to specify complete-length
3421              * In the case of a 206 or 416 response, Content-Length will be
3422              * set accordingly in response_header().
3423              */
3424             txn->resp_body.len = outlen;
3425 
3426             if (code == HTTP_PARTIAL) {
3427                 /* check_precond() tells us that this is a range request */
3428                 code = parse_ranges(*spool_getheader(txn->req_hdrs, "Range"),
3429                                     outlen, &txn->resp_body.range);
3430 
3431                 switch (code) {
3432                 case HTTP_OK:
3433                     /* Full body (unknown range-unit) */
3434                     break;
3435 
3436                 case HTTP_PARTIAL:
3437                     /* One or more range request(s) */
3438                     txn->resp_body.len = outlen;
3439 
3440                     if (txn->resp_body.range->next) {
3441                         /* Multiple ranges */
3442                         multipart_byteranges(txn, buf);
3443                         return;
3444                     }
3445                     else {
3446                         /* Single range - set data parameters accordingly */
3447                         offset += txn->resp_body.range->first;
3448                         outlen = txn->resp_body.range->last -
3449                             txn->resp_body.range->first + 1;
3450                     }
3451                     break;
3452 
3453                 case HTTP_BAD_RANGE:
3454                     /* No valid ranges */
3455                     outlen = 0;
3456                     break;
3457                 }
3458             }
3459 
3460             if (outlen && do_md5) {
3461                 MD5Update(&ctx, buf+offset, outlen);
3462                 MD5Final(md5, &ctx);
3463                 txn->resp_body.md5 = md5;
3464             }
3465         }
3466         else if (txn->flags.ver == VER_1_0) {
3467             /* HTTP/1.0 doesn't support chunked - close-delimit the body */
3468             txn->flags.conn = CONN_CLOSE;
3469         }
3470         else if (do_md5) txn->flags.trailer = TRAILER_CMD5;
3471 
3472         response_header(code, txn);
3473 
3474         /* MUST NOT send a body for 1xx/204/304 response or any HEAD response */
3475         switch (code) {
3476         case HTTP_CONTINUE:
3477         case HTTP_SWITCH_PROT:
3478         case HTTP_PROCESSING:
3479         case HTTP_NO_CONTENT:
3480         case HTTP_NOT_MODIFIED:
3481             return;
3482 
3483         default:
3484             if (txn->meth == METH_HEAD) return;
3485         }
3486     }
3487 
3488     /* Output data */
3489 #ifdef HAVE_NGHTTP2
3490     if (txn->flags.ver == VER_2) {
3491         int r;
3492         uint8_t flags = NGHTTP2_FLAG_END_STREAM;
3493         struct protstream *s = prot_readmap(buf + offset, outlen);
3494         nghttp2_data_provider prd;
3495 
3496         prd.source.ptr = s;
3497         prd.read_callback = http2_data_source_read_cb;
3498 
3499         if (txn->flags.te) {
3500             if (!last_chunk) {
3501                 flags = NGHTTP2_FLAG_NONE;
3502                 if (outlen && (txn->flags.trailer & TRAILER_CMD5)) {
3503                     MD5Update(&ctx, buf + offset, outlen);
3504                 }
3505             }
3506             else if (txn->flags.trailer) {
3507                 flags = NGHTTP2_FLAG_NONE;
3508                 if (txn->flags.trailer & TRAILER_CMD5) MD5Final(md5, &ctx);
3509             }
3510         }
3511 
3512         syslog(LOG_DEBUG,
3513                "nghttp2_submit_data(id=%d, len=%d, outlen=%d, flags=%#x)",
3514                txn->http2.stream_id, len, outlen, flags);
3515 
3516         r = nghttp2_submit_data(txn->conn->http2_session,
3517                                 flags, txn->http2.stream_id, &prd);
3518         if (r) {
3519             syslog(LOG_ERR, "nghttp2_submit_data: %s", nghttp2_strerror(r));
3520         }
3521         else {
3522             r = nghttp2_session_send(txn->conn->http2_session);
3523             if (r) {
3524                 syslog(LOG_ERR, "nghttp2_session_send: %s", nghttp2_strerror(r));
3525             }
3526         }
3527 
3528         if (last_chunk && (txn->flags.trailer & TRAILER_CMD5)) {
3529             begin_resp_headers(txn, 0);
3530             content_md5_hdr(txn, md5);
3531             end_resp_headers(txn, 0);
3532         }
3533 
3534         prot_free(s);
3535         return;
3536     }
3537 #endif /* HAVE_NGHTTP2 */
3538 
3539     if (txn->flags.te && txn->flags.ver == VER_1_1) {
3540         /* HTTP/1.1 chunk */
3541         if (outlen) {
3542             syslog(LOG_DEBUG, "write_body: chunk(%d)", outlen);
3543             prot_printf(httpd_out, "%x\r\n", outlen);
3544             prot_write(httpd_out, buf, outlen);
3545             prot_puts(httpd_out, "\r\n");
3546 
3547             if (txn->flags.trailer & TRAILER_CMD5) MD5Update(&ctx, buf, outlen);
3548         }
3549         if (last_chunk) {
3550             /* Terminate the HTTP/1.1 body with a zero-length chunk */
3551             syslog(LOG_DEBUG, "write_body: last chunk");
3552             prot_puts(httpd_out, "0\r\n");
3553 
3554             /* Trailer */
3555             if (txn->flags.trailer & TRAILER_CMD5) {
3556                 syslog(LOG_DEBUG, "write_body: trailer");
3557                 MD5Final(md5, &ctx);
3558                 content_md5_hdr(txn, md5);
3559             }
3560 
3561             if (txn->flags.trailer != TRAILER_PROXY) {
3562                 syslog(LOG_DEBUG, "write_body: CRLF");
3563                 prot_puts(httpd_out, "\r\n");
3564             }
3565         }
3566     }
3567     else {
3568         /* Full body or HTTP/1.0 close-delimited body */
3569         prot_write(httpd_out, buf + offset, outlen);
3570     }
3571 }
3572 
3573 
3574 /* Output an HTTP response with application/xml body */
xml_response(long code,struct transaction_t * txn,xmlDocPtr xml)3575 EXPORTED void xml_response(long code, struct transaction_t *txn, xmlDocPtr xml)
3576 {
3577     xmlChar *buf;
3578     int bufsiz;
3579 
3580     switch (code) {
3581     case HTTP_OK:
3582     case HTTP_CREATED:
3583     case HTTP_NO_CONTENT:
3584     case HTTP_MULTI_STATUS:
3585         break;
3586 
3587     default:
3588         /* Neither Brief nor Prefer affect error response bodies */
3589         txn->flags.vary &= ~(VARY_BRIEF | VARY_PREFER);
3590         txn->resp_body.prefs = 0;
3591     }
3592 
3593     /* Dump XML response tree into a text buffer */
3594     xmlDocDumpFormatMemoryEnc(xml, &buf, &bufsiz, "utf-8",
3595                               config_httpprettytelemetry);
3596 
3597     if (buf) {
3598         /* Output the XML response */
3599         txn->resp_body.type = "application/xml; charset=utf-8";
3600 
3601         write_body(code, txn, (char *) buf, bufsiz);
3602 
3603         /* Cleanup */
3604         xmlFree(buf);
3605     }
3606     else {
3607         txn->error.precond = 0;
3608         txn->error.desc = "Error dumping XML tree\r\n";
3609         error_response(HTTP_SERVER_ERROR, txn);
3610     }
3611 }
3612 
buf_printf_markup(struct buf * buf,unsigned level,const char * fmt,...)3613 EXPORTED void buf_printf_markup(struct buf *buf, unsigned level, const char *fmt, ...)
3614 {
3615     va_list args;
3616     const char *eol = "\n";
3617 
3618     if (!config_httpprettytelemetry) {
3619         level = 0;
3620         eol = "";
3621     }
3622 
3623     va_start(args, fmt);
3624 
3625     buf_printf(buf, "%*s", level * MARKUP_INDENT, "");
3626     buf_vprintf(buf, fmt, args);
3627     buf_appendcstr(buf, eol);
3628 
3629     va_end(args);
3630 }
3631 
3632 
3633 /* Output an HTTP error response with optional XML or HTML body */
error_response(long code,struct transaction_t * txn)3634 EXPORTED void error_response(long code, struct transaction_t *txn)
3635 {
3636     struct buf *html = &txn->resp_body.payload;
3637 
3638     /* Neither Brief nor Prefer affect error response bodies */
3639     txn->flags.vary &= ~(VARY_BRIEF | VARY_PREFER);
3640     txn->resp_body.prefs = 0;
3641 
3642 #ifdef WITH_DAV
3643     if (code != HTTP_UNAUTHORIZED && txn->error.precond) {
3644         xmlNodePtr root = xml_add_error(NULL, &txn->error, NULL);
3645 
3646         if (root) {
3647             xml_response(code, txn, root->doc);
3648             xmlFreeDoc(root->doc);
3649             return;
3650         }
3651     }
3652 #endif /* WITH_DAV */
3653 
3654     if (!txn->error.desc) {
3655         switch (code) {
3656             /* 4xx codes */
3657         case HTTP_BAD_REQUEST:
3658             txn->error.desc =
3659                 "The request was not understood by this server.";
3660             break;
3661 
3662         case HTTP_NOT_FOUND:
3663             txn->error.desc =
3664                 "The requested URL was not found on this server.";
3665             break;
3666 
3667         case HTTP_NOT_ALLOWED:
3668             txn->error.desc =
3669                 "The requested method is not allowed for the URL.";
3670             break;
3671 
3672         case HTTP_GONE:
3673             txn->error.desc =
3674                 "The requested URL has been removed from this server.";
3675             break;
3676 
3677             /* 5xx codes */
3678         case HTTP_SERVER_ERROR:
3679             txn->error.desc =
3680                 "The server encountered an internal error.";
3681             break;
3682 
3683         case HTTP_NOT_IMPLEMENTED:
3684             txn->error.desc =
3685                 "The requested method is not implemented by this server.";
3686             break;
3687 
3688         case HTTP_UNAVAILABLE:
3689             txn->error.desc =
3690                 "The server is unable to process the request at this time.";
3691             break;
3692         }
3693     }
3694 
3695     if (txn->error.desc) {
3696         const char **hdr, *host = "";
3697         char *port = NULL;
3698         unsigned level = 0;
3699 
3700         if (txn->req_hdrs &&
3701             (hdr = spool_getheader(txn->req_hdrs, "Host")) &&
3702             hdr[0] && *hdr[0]) {
3703             host = (char *) hdr[0];
3704             if ((port = strchr(host, ':'))) *port++ = '\0';
3705         }
3706         else if (config_serverinfo != IMAP_ENUM_SERVERINFO_OFF) {
3707             host = config_servername;
3708         }
3709         if (!port) {
3710             port = (saslprops.iplocalport) ?
3711                 strchr(saslprops.iplocalport, ';')+1 : "";
3712         }
3713 
3714         buf_printf_markup(html, level, HTML_DOCTYPE);
3715         buf_printf_markup(html, level++, "<html>");
3716         buf_printf_markup(html, level++, "<head>");
3717         buf_printf_markup(html, level, "<title>%s</title>",
3718                           error_message(code));
3719         buf_printf_markup(html, --level, "</head>");
3720         buf_printf_markup(html, level++, "<body>");
3721         buf_printf_markup(html, level, "<h1>%s</h1>", error_message(code)+4);
3722         buf_printf_markup(html, level, "<p>%s</p>", txn->error.desc);
3723         buf_printf_markup(html, level, "<hr>");
3724         buf_printf_markup(html, level,
3725                           "<address>%s Server at %s Port %s</address>",
3726                           buf_cstring(&serverinfo), host, port);
3727         buf_printf_markup(html, --level, "</body>");
3728         buf_printf_markup(html, --level, "</html>");
3729 
3730         txn->resp_body.type = "text/html; charset=utf-8";
3731     }
3732 
3733     write_body(code, txn, buf_cstring(html), buf_len(html));
3734 }
3735 
3736 
proxy_authz(const char ** authzid,struct transaction_t * txn)3737 static int proxy_authz(const char **authzid, struct transaction_t *txn)
3738 {
3739     static char authzbuf[MAX_MAILBOX_BUFFER];
3740     unsigned authzlen;
3741     int status;
3742 
3743     syslog(LOG_DEBUG, "proxy_auth: authzid='%s'", *authzid);
3744 
3745     /* Free userid & authstate previously allocated for auth'd user */
3746     if (httpd_userid) {
3747         free(httpd_userid);
3748         httpd_userid = NULL;
3749     }
3750     if (httpd_extrafolder) {
3751         free(httpd_extrafolder);
3752         httpd_extrafolder = NULL;
3753     }
3754     if (httpd_extradomain) {
3755         free(httpd_extradomain);
3756         httpd_extradomain = NULL;
3757     }
3758     if (httpd_authstate) {
3759         auth_freestate(httpd_authstate);
3760         httpd_authstate = NULL;
3761     }
3762 
3763     if (!(config_mupdate_server && config_getstring(IMAPOPT_PROXYSERVERS))) {
3764         /* Not a backend in a Murder - proxy authz is not allowed */
3765         syslog(LOG_NOTICE, "badlogin: %s %s %s %s",
3766                httpd_clienthost, txn->auth_chal.scheme->name, saslprops.authid,
3767                "proxy authz attempted on non-Murder backend");
3768         return SASL_NOAUTHZ;
3769     }
3770 
3771     /* Canonify the authzid */
3772     status = mysasl_canon_user(httpd_saslconn, NULL,
3773                                *authzid, strlen(*authzid),
3774                                SASL_CU_AUTHZID, NULL,
3775                                authzbuf, sizeof(authzbuf), &authzlen);
3776     if (status) {
3777         syslog(LOG_NOTICE, "badlogin: %s %s %s invalid user",
3778                httpd_clienthost, txn->auth_chal.scheme->name,
3779                beautify_string(*authzid));
3780         return status;
3781     }
3782 
3783     /* See if auth'd user is allowed to proxy */
3784     status = mysasl_proxy_policy(httpd_saslconn, &httpd_proxyctx,
3785                                  authzbuf, authzlen,
3786                                  saslprops.authid, strlen(saslprops.authid),
3787                                  NULL, 0, NULL);
3788 
3789     if (status) {
3790         syslog(LOG_NOTICE, "badlogin: %s %s %s %s",
3791                httpd_clienthost, txn->auth_chal.scheme->name, saslprops.authid,
3792                sasl_errdetail(httpd_saslconn));
3793         return status;
3794     }
3795 
3796     *authzid = authzbuf;
3797 
3798     return status;
3799 }
3800 
3801 
3802 /* Write cached header (redacting authorization credentials) to buffer. */
log_cachehdr(const char * name,const char * contents,void * rock)3803 static void log_cachehdr(const char *name, const char *contents, void *rock)
3804 {
3805     struct buf *buf = (struct buf *) rock;
3806 
3807     /* Ignore private headers in our cache */
3808     if (name[0] == ':') return;
3809 
3810     buf_printf(buf, "%c%s: ", toupper(name[0]), name+1);
3811     if (!strcmp(name, "authorization")) {
3812         /* Replace authorization credentials with an ellipsis */
3813         const char *creds = strchr(contents, ' ') + 1;
3814         buf_printf(buf, "%.*s%-*s\r\n", (int) (creds - contents), contents,
3815                    (int) strlen(creds), "...");
3816     }
3817     else buf_printf(buf, "%s\r\n", contents);
3818 }
3819 
3820 
auth_success(struct transaction_t * txn,const char * userid)3821 static void auth_success(struct transaction_t *txn, const char *userid)
3822 {
3823     struct auth_scheme_t *scheme = txn->auth_chal.scheme;
3824     int i;
3825 
3826     httpd_userid = xstrdup(userid);
3827     httpd_userisanonymous = is_userid_anonymous(httpd_userid);
3828 
3829     syslog(LOG_NOTICE, "login: %s %s %s%s %s SESSIONID=<%s>",
3830            httpd_clienthost, httpd_userid, scheme->name,
3831            httpd_tls_done ? "+TLS" : "", "User logged in",
3832            session_id());
3833 
3834 
3835     /* Recreate telemetry log entry for request (w/ credentials redacted) */
3836     assert(!buf_len(&txn->buf));
3837     buf_printf(&txn->buf, "<%ld<", time(NULL));         /* timestamp */
3838     buf_printf(&txn->buf, "%s %s %s\r\n",               /* request-line*/
3839                txn->req_line.meth, txn->req_line.uri, txn->req_line.ver);
3840     spool_enum_hdrcache(txn->req_hdrs,                  /* header fields */
3841                         &log_cachehdr, &txn->buf);
3842     buf_appendcstr(&txn->buf, "\r\n");                  /* CRLF */
3843     buf_append(&txn->buf, &txn->req_body.payload);      /* message body */
3844     buf_appendmap(&txn->buf,                            /* buffered input */
3845                   (const char *) httpd_in->ptr, httpd_in->cnt);
3846 
3847     if (httpd_logfd != -1) {
3848         /* Rewind log to current request and truncate it */
3849         off_t end = lseek(httpd_logfd, 0, SEEK_END);
3850 
3851         ftruncate(httpd_logfd, end - buf_len(&txn->buf));
3852 
3853         /* Close existing telemetry log */
3854         close(httpd_logfd);
3855     }
3856 
3857     prot_setlog(httpd_in, PROT_NO_FD);
3858     prot_setlog(httpd_out, PROT_NO_FD);
3859 
3860     /* Create telemetry log based on new userid */
3861     httpd_logfd = telemetry_log(httpd_userid, httpd_in, httpd_out, 0);
3862 
3863     if (httpd_logfd != -1) {
3864         /* Log credential-redacted request */
3865         write(httpd_logfd, buf_cstring(&txn->buf), buf_len(&txn->buf));
3866     }
3867 
3868     buf_reset(&txn->buf);
3869 
3870     /* Do any namespace specific post-auth processing */
3871     for (i = 0; namespaces[i]; i++) {
3872         if (namespaces[i]->enabled && namespaces[i]->auth)
3873             namespaces[i]->auth(httpd_userid);
3874     }
3875 }
3876 
3877 /* Perform HTTP Authentication based on the given credentials ('creds').
3878  * Returns the selected auth scheme and any server challenge in 'chal'.
3879  * May be called multiple times if auth scheme requires multiple steps.
3880  * SASL status between steps is maintained in 'status'.
3881  */
3882 #define BASE64_BUF_SIZE 21848   /* per RFC 4422: ((16K / 3) + 1) * 4  */
3883 
http_auth(const char * creds,struct transaction_t * txn)3884 static int http_auth(const char *creds, struct transaction_t *txn)
3885 {
3886     struct auth_challenge_t *chal = &txn->auth_chal;
3887     static int status = SASL_OK;
3888     int slen;
3889     const char *clientin = NULL, *realm = NULL, *user, **authzid;
3890     unsigned int clientinlen = 0;
3891     struct auth_scheme_t *scheme;
3892     static char base64[BASE64_BUF_SIZE+1];
3893     const void *canon_user;
3894 
3895     /* Split credentials into auth scheme and response */
3896     slen = strcspn(creds, " \0");
3897     if ((clientin = strchr(creds, ' '))) clientinlen = strlen(++clientin);
3898 
3899     syslog(LOG_DEBUG,
3900            "http_auth: status=%d   scheme='%s'   creds='%.*s%s'",
3901            status, chal->scheme ? chal->scheme->name : "",
3902            slen, creds, clientin ? " <response>" : "");
3903 
3904     /* Free userid & authstate previously allocated for auth'd user */
3905     if (httpd_userid) {
3906         free(httpd_userid);
3907         httpd_userid = NULL;
3908     }
3909     if (httpd_extrafolder) {
3910         free(httpd_extrafolder);
3911         httpd_extrafolder = NULL;
3912     }
3913     if (httpd_extradomain) {
3914         free(httpd_extradomain);
3915         httpd_extradomain = NULL;
3916     }
3917     if (httpd_authstate) {
3918         auth_freestate(httpd_authstate);
3919         httpd_authstate = NULL;
3920     }
3921     chal->param = NULL;
3922 
3923     if (chal->scheme) {
3924         /* Use current scheme, if possible */
3925         scheme = chal->scheme;
3926 
3927         if (strncasecmp(scheme->name, creds, slen)) {
3928             /* Changing auth scheme -> reset state */
3929             syslog(LOG_DEBUG, "http_auth: changing scheme");
3930             reset_saslconn(&httpd_saslconn);
3931             chal->scheme = NULL;
3932             status = SASL_OK;
3933         }
3934     }
3935 
3936     if (!chal->scheme) {
3937         /* Find the client-specified auth scheme */
3938         syslog(LOG_DEBUG, "http_auth: find client scheme");
3939         for (scheme = auth_schemes; scheme->name; scheme++) {
3940             if (slen && !strncasecmp(scheme->name, creds, slen)) {
3941                 /* Found a supported scheme, see if its available */
3942                 if (!(avail_auth_schemes & (1 << scheme->idx))) scheme = NULL;
3943                 break;
3944             }
3945         }
3946         if (!scheme || !scheme->name) {
3947             /* Didn't find a matching scheme that is available */
3948             syslog(LOG_DEBUG, "Unknown auth scheme '%.*s'", slen, creds);
3949             return SASL_NOMECH;
3950         }
3951         /* We found it! */
3952         syslog(LOG_DEBUG, "http_auth: found matching scheme: %s", scheme->name);
3953         chal->scheme = scheme;
3954         status = SASL_OK;
3955     }
3956 
3957     /* Base64 decode any client response, if necesary */
3958     if (clientin && (scheme->flags & AUTH_BASE64)) {
3959         int r = sasl_decode64(clientin, clientinlen,
3960                               base64, BASE64_BUF_SIZE, &clientinlen);
3961         if (r != SASL_OK) {
3962             syslog(LOG_ERR, "Base64 decode failed: %s",
3963                    sasl_errstring(r, NULL, NULL));
3964             return r;
3965         }
3966         clientin = base64;
3967     }
3968 
3969     /* Get realm - based on namespace of URL */
3970     switch (txn->req_tgt.namespace->id) {
3971     case URL_NS_DEFAULT:
3972     case URL_NS_PRINCIPAL:
3973         realm = config_getstring(IMAPOPT_DAV_REALM);
3974         break;
3975 
3976     case URL_NS_CALENDAR:
3977         realm = config_getstring(IMAPOPT_CALDAV_REALM);
3978         break;
3979 
3980     case URL_NS_ADDRESSBOOK:
3981         realm = config_getstring(IMAPOPT_CARDDAV_REALM);
3982         break;
3983 
3984     case URL_NS_RSS:
3985         realm = config_getstring(IMAPOPT_RSS_REALM);
3986         break;
3987     }
3988     if (!realm) realm = config_servername;
3989 
3990 #ifdef SASL_HTTP_REQUEST
3991     /* Setup SASL HTTP request, if necessary */
3992     if (scheme->flags & AUTH_NEED_REQUEST) {
3993         sasl_http_request_t sasl_http_req;
3994 
3995         sasl_http_req.method = txn->req_line.meth;
3996         sasl_http_req.uri = txn->req_line.uri;
3997         sasl_http_req.entity = NULL;
3998         sasl_http_req.elen = 0;
3999         sasl_http_req.non_persist = txn->flags.conn & CONN_CLOSE;
4000         sasl_setprop(httpd_saslconn, SASL_HTTP_REQUEST, &sasl_http_req);
4001     }
4002 #endif /* SASL_HTTP_REQUEST */
4003 
4004     if (scheme->idx == AUTH_BASIC) {
4005         /* Basic (plaintext) authentication */
4006         char *pass;
4007         char *extra;
4008         char *plus;
4009         char *domain;
4010 
4011         if (!clientin) {
4012             /* Create initial challenge (base64 buffer is static) */
4013             snprintf(base64, BASE64_BUF_SIZE, "realm=\"%s\"", realm);
4014             chal->param = base64;
4015             chal->scheme = NULL;  /* make sure we don't reset the SASL ctx */
4016             return status;
4017         }
4018 
4019         /* Split credentials into <user> ':' <pass>.
4020          * We are working with base64 buffer, so we can modify it.
4021          */
4022         user = base64;
4023         pass = strchr(base64, ':');
4024         if (!pass) {
4025             syslog(LOG_ERR, "Basic auth: Missing password");
4026             return SASL_BADPARAM;
4027         }
4028         *pass++ = '\0';
4029         domain = strchr(user, '@');
4030         if (domain) *domain++ = '\0';
4031         extra = strchr(user, '%');
4032         if (extra) *extra++ = '\0';
4033         plus = strchr(user, '+');
4034         if (plus) *plus++ = '\0';
4035         /* Verify the password */
4036         char *realuser = domain ? strconcat(user, "@", domain, (char *)NULL) : xstrdup(user);
4037         status = sasl_checkpass(httpd_saslconn, realuser, strlen(realuser),
4038                                 pass, strlen(pass));
4039         memset(pass, 0, strlen(pass));          /* erase plaintext password */
4040 
4041         if (status) {
4042             syslog(LOG_NOTICE, "badlogin: %s Basic %s %s",
4043                    httpd_clienthost, realuser, sasl_errdetail(httpd_saslconn));
4044             free(realuser);
4045 
4046             /* Don't allow user probing */
4047             if (status == SASL_NOUSER) status = SASL_BADAUTH;
4048             return status;
4049         }
4050         free(realuser);
4051 
4052         /* Successful authentication - fall through */
4053         httpd_extrafolder = xstrdupnull(plus);
4054         httpd_extradomain = xstrdupnull(extra);
4055     }
4056     else if (scheme->idx == AUTH_BEARER) {
4057         /* Bearer authentication */
4058         assert(txn->req_tgt.namespace->bearer);
4059 
4060         if (!clientin) {
4061             /* Create initial challenge (base64 buffer is static) */
4062             snprintf(base64, BASE64_BUF_SIZE, "realm=\"%s\"", realm);
4063             chal->param = base64;
4064             chal->scheme = NULL;  /* make sure we don't reset the SASL ctx */
4065             return status;
4066         }
4067 
4068         /* Call namespace bearer authentication.
4069          * We are working with base64 buffer, so the namespace can
4070          * write the canonicalized userid into the buffer */
4071         base64[0] = 0;
4072         status = txn->req_tgt.namespace->bearer(clientin, base64, BASE64_BUF_SIZE);
4073         if (status) return status;
4074         user = base64;
4075 
4076         /* Successful authentication - fall through */
4077         httpd_extrafolder = NULL;
4078         httpd_extradomain = NULL;
4079         httpd_authstate = auth_newstate(user);
4080     }
4081     else {
4082         /* SASL-based authentication (Digest, Negotiate, NTLM) */
4083         const char *serverout = NULL;
4084         unsigned int serveroutlen = 0;
4085 
4086         if (status == SASL_CONTINUE) {
4087             /* Continue current authentication exchange */
4088             syslog(LOG_DEBUG, "http_auth: continue %s", scheme->saslmech);
4089             status = sasl_server_step(httpd_saslconn, clientin, clientinlen,
4090                                       &serverout, &serveroutlen);
4091         }
4092         else {
4093             /* Start new authentication exchange */
4094             syslog(LOG_DEBUG, "http_auth: start %s", scheme->saslmech);
4095             status = sasl_server_start(httpd_saslconn, scheme->saslmech,
4096                                        clientin, clientinlen,
4097                                        &serverout, &serveroutlen);
4098         }
4099 
4100         /* Failure - probably bad client response */
4101         if ((status != SASL_OK) && (status != SASL_CONTINUE)) {
4102             syslog(LOG_ERR, "SASL failed: %s",
4103                    sasl_errstring(status, NULL, NULL));
4104             return status;
4105         }
4106 
4107         /* Base64 encode any server challenge, if necesary */
4108         if (serverout && (scheme->flags & AUTH_BASE64)) {
4109             int r = sasl_encode64(serverout, serveroutlen,
4110                                    base64, BASE64_BUF_SIZE, NULL);
4111             if (r != SASL_OK) {
4112                 syslog(LOG_ERR, "Base64 encode failed: %s",
4113                        sasl_errstring(r, NULL, NULL));
4114                 return r;
4115             }
4116             serverout = base64;
4117         }
4118 
4119         chal->param = serverout;
4120 
4121         if (status == SASL_CONTINUE) {
4122             /* Need another step to complete authentication */
4123             return status;
4124         }
4125 
4126         /* Successful authentication
4127          *
4128          * HTTP doesn't support security layers,
4129          * so don't attach SASL context to prot layer.
4130          */
4131     }
4132 
4133     if (scheme->idx != AUTH_BEARER) {
4134         /* Get the userid from SASL - already canonicalized */
4135         status = sasl_getprop(httpd_saslconn, SASL_USERNAME, &canon_user);
4136         if (status != SASL_OK) {
4137             syslog(LOG_ERR, "weird SASL error %d getting SASL_USERNAME", status);
4138             return status;
4139         }
4140         user = (const char *) canon_user;
4141     }
4142 
4143     if (saslprops.authid) free(saslprops.authid);
4144     saslprops.authid = xstrdup(user);
4145 
4146     authzid = spool_getheader(txn->req_hdrs, "Authorize-As");
4147     if (authzid && *authzid[0]) {
4148         /* Trying to proxy as another user */
4149         user = authzid[0];
4150 
4151         status = proxy_authz(&user, txn);
4152         if (status) return status;
4153     }
4154 
4155     auth_success(txn, user);
4156 
4157     return status;
4158 }
4159 
4160 
4161 /*************************  Method Execution Routines  ************************/
4162 
4163 
4164 /* Compare an etag in a header to a resource etag.
4165  * Returns 0 if a match, non-zero otherwise.
4166  */
etagcmp(const char * hdr,const char * etag)4167 EXPORTED int etagcmp(const char *hdr, const char *etag)
4168 {
4169     size_t len;
4170 
4171     if (!etag) return -1;               /* no representation       */
4172     if (!strcmp(hdr, "*")) return 0;    /* any representation      */
4173 
4174     len = strlen(etag);
4175     if (!strncmp(hdr, "W/", 2)) hdr+=2; /* skip weak prefix        */
4176     if (*hdr++ != '\"') return 1;       /* match/skip open DQUOTE  */
4177     if (strlen(hdr) != len+1) return 1; /* make sure lengths match */
4178     if (hdr[len] != '\"') return 1;     /* match close DQUOTE      */
4179 
4180     return strncmp(hdr, etag, len);
4181 }
4182 
4183 
4184 /* Compare a resource etag to a comma-separated list and/or multiple headers
4185  * looking for a match.  Returns 1 if a match is found, 0 otherwise.
4186  */
etag_match(const char * hdr[],const char * etag)4187 static unsigned etag_match(const char *hdr[], const char *etag)
4188 {
4189     unsigned i, match = 0;
4190     tok_t tok;
4191     char *token;
4192 
4193     for (i = 0; !match && hdr[i]; i++) {
4194         tok_init(&tok, hdr[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT);
4195         while (!match && (token = tok_next(&tok))) {
4196             if (!etagcmp(token, etag)) match = 1;
4197         }
4198         tok_fini(&tok);
4199     }
4200 
4201     return match;
4202 }
4203 
4204 
parse_ranges(const char * hdr,unsigned long len,struct range ** ranges)4205 static int parse_ranges(const char *hdr, unsigned long len,
4206                         struct range **ranges)
4207 {
4208     int ret = HTTP_BAD_RANGE;
4209     struct range *new, *tail = *ranges = NULL;
4210     tok_t tok;
4211     char *token;
4212 
4213     if (!len) return HTTP_OK;  /* need to know length of representation */
4214 
4215     /* we only handle byte-unit */
4216     if (!hdr || strncmp(hdr, "bytes=", 6)) return HTTP_OK;
4217 
4218     tok_init(&tok, hdr+6, ",", TOK_TRIMLEFT|TOK_TRIMRIGHT);
4219     while ((token = tok_next(&tok))) {
4220         /* default to entire representation */
4221         unsigned long first = 0;
4222         unsigned long last = len - 1;
4223         char *p, *endp;
4224 
4225         if (!(p = strchr(token, '-'))) continue;  /* bad byte-range-set */
4226 
4227         if (p == token) {
4228             /* suffix-byte-range-spec */
4229             unsigned long suffix = strtoul(++p, &endp, 10);
4230 
4231             if (endp == p || *endp) continue;  /* bad suffix-length */
4232             if (!suffix) continue;      /* unsatisfiable suffix-length */
4233 
4234             /* don't start before byte zero */
4235             if (suffix < len) first = len - suffix;
4236         }
4237         else {
4238             /* byte-range-spec */
4239             first = strtoul(token, &endp, 10);
4240             if (endp != p) continue;      /* bad first-byte-pos */
4241             if (first >= len) continue;   /* unsatisfiable first-byte-pos */
4242 
4243             if (*++p) {
4244                 /* last-byte-pos */
4245                 last = strtoul(p, &endp, 10);
4246                 if (*endp || last < first) continue; /* bad last-byte-pos */
4247 
4248                 /* don't go past end of representation */
4249                 if (last >= len) last = len - 1;
4250             }
4251         }
4252 
4253         ret = HTTP_PARTIAL;
4254 
4255         /* Coalesce overlapping ranges, or those with a gap < 80 bytes */
4256         if (tail &&
4257             first >= tail->first && (long) (first - tail->last) < 80) {
4258             tail->last = MAX(last, tail->last);
4259             continue;
4260         }
4261 
4262         /* Create a new range and append it to linked list */
4263         new = xzmalloc(sizeof(struct range));
4264         new->first = first;
4265         new->last = last;
4266 
4267         if (tail) tail->next = new;
4268         else *ranges = new;
4269         tail = new;
4270     }
4271 
4272     tok_fini(&tok);
4273 
4274     return ret;
4275 }
4276 
4277 
4278 /* Check headers for any preconditions.
4279  *
4280  * Interaction is complex and is documented in RFC 7232
4281  */
check_precond(struct transaction_t * txn,const char * etag,time_t lastmod)4282 EXPORTED int check_precond(struct transaction_t *txn,
4283                            const char *etag, time_t lastmod)
4284 {
4285     hdrcache_t hdrcache = txn->req_hdrs;
4286     const char **hdr;
4287     time_t since = 0;
4288 
4289     /* Step 1 */
4290     if ((hdr = spool_getheader(hdrcache, "If-Match"))) {
4291         if (!etag_match(hdr, etag)) return HTTP_PRECOND_FAILED;
4292 
4293         /* Continue to step 3 */
4294     }
4295 
4296     /* Step 2 */
4297     else if ((hdr = spool_getheader(hdrcache, "If-Unmodified-Since"))) {
4298         if (time_from_rfc822(hdr[0], &since) < 0) return HTTP_BAD_REQUEST;
4299 
4300         if (lastmod > since) return HTTP_PRECOND_FAILED;
4301 
4302         /* Continue to step 3 */
4303     }
4304 
4305     /* Step 3 */
4306     if ((hdr = spool_getheader(hdrcache, "If-None-Match"))) {
4307         if (etag_match(hdr, etag)) {
4308             if (txn->meth == METH_GET || txn->meth == METH_HEAD)
4309                 return HTTP_NOT_MODIFIED;
4310             else
4311                 return HTTP_PRECOND_FAILED;
4312         }
4313 
4314         /* Continue to step 5 */
4315     }
4316 
4317     /* Step 4 */
4318     else if ((txn->meth == METH_GET || txn->meth == METH_HEAD) &&
4319              (hdr = spool_getheader(hdrcache, "If-Modified-Since"))) {
4320         if (time_from_rfc822(hdr[0], &since) < 0) return HTTP_BAD_REQUEST;
4321 
4322         if (lastmod <= since) return HTTP_NOT_MODIFIED;
4323 
4324         /* Continue to step 5 */
4325     }
4326 
4327     /* Step 5 */
4328     if (txn->flags.ranges &&  /* Only if we support Range requests */
4329         txn->meth == METH_GET && (hdr = spool_getheader(hdrcache, "Range"))) {
4330 
4331         if ((hdr = spool_getheader(hdrcache, "If-Range"))) {
4332             time_from_rfc822(hdr[0], &since); /* error OK here, could be an etag */
4333         }
4334 
4335         /* Only process Range if If-Range isn't present or validator matches */
4336         if (!hdr || (since && (lastmod <= since)) || !etagcmp(hdr[0], etag))
4337             return HTTP_PARTIAL;
4338     }
4339 
4340     /* Step 6 */
4341     return HTTP_OK;
4342 }
4343 
4344 
4345 const struct mimetype {
4346     const char *ext;
4347     const char *type;
4348     unsigned int compressible;
4349 } mimetypes[] = {
4350     { ".css",  "text/css", 1 },
4351     { ".htm",  "text/html", 1 },
4352     { ".html", "text/html", 1 },
4353     { ".ics",  "text/calendar", 1 },
4354     { ".ifb",  "text/calendar", 1 },
4355     { ".text", "text/plain", 1 },
4356     { ".txt",  "text/plain", 1 },
4357 
4358     { ".cgm",  "image/cgm", 1 },
4359     { ".gif",  "image/gif", 0 },
4360     { ".jpg",  "image/jpeg", 0 },
4361     { ".jpeg", "image/jpeg", 0 },
4362     { ".png",  "image/png", 0 },
4363     { ".svg",  "image/svg+xml", 1 },
4364     { ".tif",  "image/tiff", 1 },
4365     { ".tiff", "image/tiff", 1 },
4366 
4367     { ".aac",  "audio/aac", 0 },
4368     { ".m4a",  "audio/mp4", 0 },
4369     { ".mp3",  "audio/mpeg", 0 },
4370     { ".mpeg", "audio/mpeg", 0 },
4371     { ".oga",  "audio/ogg", 0 },
4372     { ".ogg",  "audio/ogg", 0 },
4373     { ".wav",  "audio/wav", 0 },
4374 
4375     { ".avi",  "video/x-msvideo", 0 },
4376     { ".mov",  "video/quicktime", 0 },
4377     { ".m4v",  "video/mp4", 0 },
4378     { ".ogv",  "video/ogg", 0 },
4379     { ".qt",   "video/quicktime", 0 },
4380     { ".wmv",  "video/x-ms-wmv", 0 },
4381 
4382     { ".bz",   "application/x-bzip", 0 },
4383     { ".bz2",  "application/x-bzip2", 0 },
4384     { ".gz",   "application/gzip", 0 },
4385     { ".gzip", "application/gzip", 0 },
4386     { ".tgz",  "application/gzip", 0 },
4387     { ".zip",  "application/zip", 0 },
4388 
4389     { ".doc",  "application/msword", 1 },
4390     { ".jcs",  "application/calendar+json", 1 },
4391     { ".jfb",  "application/calendar+json", 1 },
4392     { ".js",   "application/javascript", 1 },
4393     { ".json", "application/json", 1 },
4394     { ".pdf",  "application/pdf", 1 },
4395     { ".ppt",  "application/vnd.ms-powerpoint", 1 },
4396     { ".sh",   "application/x-sh", 1 },
4397     { ".tar",  "application/x-tar", 1 },
4398     { ".xcs",  "application/calendar+xml", 1 },
4399     { ".xfb",  "application/calendar+xml", 1 },
4400     { ".xls",  "application/vnd.ms-excel", 1 },
4401     { ".xml",  "application/xml", 1 },
4402 
4403     { NULL, NULL, 0 }
4404 };
4405 
4406 
list_well_known(struct transaction_t * txn)4407 static int list_well_known(struct transaction_t *txn)
4408 {
4409     static struct buf body = BUF_INITIALIZER;
4410     static time_t lastmod = 0;
4411     struct stat sbuf;
4412     int precond;
4413 
4414     /* stat() imapd.conf for Last-Modified and ETag */
4415     stat(config_filename, &sbuf);
4416     assert(!buf_len(&txn->buf));
4417     buf_printf(&txn->buf, "%ld-%ld-%ld",
4418                compile_time, sbuf.st_mtime, sbuf.st_size);
4419     sbuf.st_mtime = MAX(compile_time, sbuf.st_mtime);
4420 
4421     /* Check any preconditions, including range request */
4422     txn->flags.ranges = 1;
4423     precond = check_precond(txn, buf_cstring(&txn->buf), sbuf.st_mtime);
4424 
4425     switch (precond) {
4426     case HTTP_OK:
4427     case HTTP_NOT_MODIFIED:
4428         /* Fill in ETag, Last-Modified, and Expires */
4429         txn->resp_body.etag = buf_cstring(&txn->buf);
4430         txn->resp_body.lastmod = sbuf.st_mtime;
4431         txn->resp_body.maxage = 86400;  /* 24 hrs */
4432         txn->flags.cc |= CC_MAXAGE;
4433 
4434         if (precond != HTTP_NOT_MODIFIED) break;
4435 
4436         GCC_FALLTHROUGH
4437 
4438     default:
4439         /* We failed a precondition - don't perform the request */
4440         return precond;
4441     }
4442 
4443     if (txn->resp_body.lastmod > lastmod) {
4444         const char *proto = NULL, *host = NULL;
4445         unsigned i, level = 0;
4446 
4447         /* Start HTML */
4448         buf_reset(&body);
4449         buf_printf_markup(&body, level, HTML_DOCTYPE);
4450         buf_printf_markup(&body, level++, "<html>");
4451         buf_printf_markup(&body, level++, "<head>");
4452         buf_printf_markup(&body, level,
4453                           "<title>%s</title>", "Well-Known Locations");
4454         buf_printf_markup(&body, --level, "</head>");
4455         buf_printf_markup(&body, level++, "<body>");
4456         buf_printf_markup(&body, level,
4457                           "<h2>%s</h2>", "Well-Known Locations");
4458         buf_printf_markup(&body, level++, "<ul>");
4459 
4460         /* Add the list of enabled /.well-known/ URLs */
4461         http_proto_host(txn->req_hdrs, &proto, &host);
4462         for (i = 0; namespaces[i]; i++) {
4463 
4464             if (namespaces[i]->enabled && namespaces[i]->well_known) {
4465                 buf_printf_markup(&body, level,
4466                                   "<li><a href=\"%s://%s%s\">%s</a></li>",
4467                                   proto, host, namespaces[i]->prefix,
4468                                   namespaces[i]->well_known);
4469             }
4470         }
4471 
4472         /* Finish HTML */
4473         buf_printf_markup(&body, --level, "</ul>");
4474         buf_printf_markup(&body, --level, "</body>");
4475         buf_printf_markup(&body, --level, "</html>");
4476 
4477         lastmod = txn->resp_body.lastmod;
4478     }
4479 
4480     /* Output the HTML response */
4481     txn->resp_body.type = "text/html; charset=utf-8";
4482     write_body(precond, txn, buf_cstring(&body), buf_len(&body));
4483 
4484     return 0;
4485 }
4486 
4487 
4488 #define WELL_KNOWN_PREFIX "/.well-known"
4489 
4490 /* Perform a GET/HEAD request */
meth_get(struct transaction_t * txn,void * params)4491 static int meth_get(struct transaction_t *txn,
4492                     void *params __attribute__((unused)))
4493 {
4494     int ret = 0, r, fd = -1, precond, len;
4495     const char *prefix, *urls, *path, *ext;
4496     static struct buf pathbuf = BUF_INITIALIZER;
4497     struct stat sbuf;
4498     const char *msg_base = NULL;
4499     size_t msg_size = 0;
4500     struct resp_body_t *resp_body = &txn->resp_body;
4501 
4502     /* Check if this is a request for /.well-known/ listing */
4503     len = strlen(WELL_KNOWN_PREFIX);
4504     if (!strncmp(txn->req_uri->path, WELL_KNOWN_PREFIX, len)) {
4505         if (txn->req_uri->path[len] == '/') len++;
4506         if (txn->req_uri->path[len] == '\0') return list_well_known(txn);
4507         else return HTTP_NOT_FOUND;
4508     }
4509 
4510     /* Serve up static pages */
4511     prefix = config_getstring(IMAPOPT_HTTPDOCROOT);
4512     if (!prefix) return HTTP_NOT_FOUND;
4513 
4514     if (*prefix != '/') {
4515         /* Remote content */
4516         struct backend *be;
4517 
4518         be = proxy_findserver(prefix, &http_protocol, httpd_userid,
4519                               &backend_cached, NULL, NULL, httpd_in);
4520         if (!be) return HTTP_UNAVAILABLE;
4521 
4522         return http_pipe_req_resp(be, txn);
4523     }
4524 
4525     /* Local content */
4526     if ((urls = config_getstring(IMAPOPT_HTTPALLOWEDURLS))) {
4527         tok_t tok = TOK_INITIALIZER(urls, " \t", TOK_TRIMLEFT|TOK_TRIMRIGHT);
4528         char *token;
4529 
4530         while ((token = tok_next(&tok)) && strcmp(token, txn->req_uri->path));
4531         tok_fini(&tok);
4532 
4533         if (!token) return HTTP_NOT_FOUND;
4534     }
4535 
4536     buf_setcstr(&pathbuf, prefix);
4537     buf_appendcstr(&pathbuf, txn->req_uri->path);
4538     path = buf_cstring(&pathbuf);
4539 
4540     /* See if path is a directory and look for index.html */
4541     if (!(r = stat(path, &sbuf)) && S_ISDIR(sbuf.st_mode)) {
4542         buf_appendcstr(&pathbuf, "/index.html");
4543         path = buf_cstring(&pathbuf);
4544         r = stat(path, &sbuf);
4545     }
4546 
4547     /* See if file exists and get Content-Length & Last-Modified time */
4548     if (r || !S_ISREG(sbuf.st_mode)) return HTTP_NOT_FOUND;
4549 
4550     if (!resp_body->type) {
4551         /* Caller hasn't specified the Content-Type */
4552         resp_body->type = "application/octet-stream";
4553 
4554         if ((ext = strrchr(path, '.'))) {
4555             /* Try to use filename extension to identity Content-Type */
4556             const struct mimetype *mtype;
4557 
4558             for (mtype = mimetypes; mtype->ext; mtype++) {
4559                 if (!strcasecmp(ext, mtype->ext)) {
4560                     resp_body->type = mtype->type;
4561                     if (!mtype->compressible) {
4562                         /* Never compress non-compressible resources */
4563                         txn->resp_body.enc = CE_IDENTITY;
4564                         txn->flags.te = TE_NONE;
4565                         txn->flags.vary &= ~VARY_AE;
4566                     }
4567                     break;
4568                 }
4569             }
4570         }
4571     }
4572 
4573     /* Generate Etag */
4574     assert(!buf_len(&txn->buf));
4575     buf_printf(&txn->buf, "%ld-%ld", (long) sbuf.st_mtime, (long) sbuf.st_size);
4576 
4577     /* Check any preconditions, including range request */
4578     txn->flags.ranges = 1;
4579     precond = check_precond(txn, buf_cstring(&txn->buf), sbuf.st_mtime);
4580 
4581     switch (precond) {
4582     case HTTP_OK:
4583     case HTTP_PARTIAL:
4584     case HTTP_NOT_MODIFIED:
4585         /* Fill in ETag, Last-Modified, and Expires */
4586         resp_body->etag = buf_cstring(&txn->buf);
4587         resp_body->lastmod = sbuf.st_mtime;
4588         resp_body->maxage = 86400;  /* 24 hrs */
4589         txn->flags.cc |= CC_MAXAGE;
4590         if (!httpd_userisanonymous) txn->flags.cc |= CC_PUBLIC;
4591 
4592         if (precond != HTTP_NOT_MODIFIED) break;
4593 
4594         GCC_FALLTHROUGH
4595 
4596     default:
4597         /* We failed a precondition - don't perform the request */
4598         resp_body->type = NULL;
4599         return precond;
4600     }
4601 
4602     if (txn->meth == METH_GET) {
4603         /* Open and mmap the file */
4604         if ((fd = open(path, O_RDONLY)) == -1) return HTTP_SERVER_ERROR;
4605         map_refresh(fd, 1, &msg_base, &msg_size, sbuf.st_size, path, NULL);
4606     }
4607 
4608     write_body(precond, txn, msg_base, sbuf.st_size);
4609 
4610     if (fd != -1) {
4611         map_free(&msg_base, &msg_size);
4612         close(fd);
4613     }
4614 
4615     return ret;
4616 }
4617 
4618 
4619 /* Perform an OPTIONS request */
meth_options(struct transaction_t * txn,void * params)4620 EXPORTED int meth_options(struct transaction_t *txn, void *params)
4621 {
4622     parse_path_t parse_path = (parse_path_t) params;
4623     int r, i;
4624 
4625     /* Response should not be cached */
4626     txn->flags.cc |= CC_NOCACHE;
4627 
4628     /* Response doesn't have a body, so no Vary */
4629     txn->flags.vary = 0;
4630 
4631     /* Special case "*" - show all features/methods available on server */
4632     if (!strcmp(txn->req_uri->path, "*")) {
4633         for (i = 0; namespaces[i]; i++) {
4634             if (namespaces[i]->enabled)
4635                 txn->req_tgt.allow |= namespaces[i]->allow;
4636         }
4637     }
4638     else {
4639         if (parse_path) {
4640             /* Parse the path */
4641             r = parse_path(txn->req_uri->path, &txn->req_tgt, &txn->error.desc);
4642             if (r) return r;
4643         }
4644 
4645         if (txn->flags.cors) {
4646             const char **hdr =
4647                 spool_getheader(txn->req_hdrs, "Access-Control-Request-Method");
4648 
4649             if (hdr) {
4650                 /* CORS preflight request */
4651                 unsigned meth;
4652 
4653                 txn->flags.cors = CORS_PREFLIGHT;
4654 
4655                 /* Check Method against our list of known methods */
4656                 for (meth = 0; (meth < METH_UNKNOWN) &&
4657                          strcmp(http_methods[meth].name, hdr[0]); meth++);
4658 
4659                 if (meth == METH_UNKNOWN) txn->flags.cors = 0;
4660                 else {
4661                     /* Check Method against those supported by the resource */
4662                     if (!txn->req_tgt.namespace->methods[meth].proc)
4663                         txn->flags.cors = 0;
4664                 }
4665             }
4666         }
4667     }
4668 
4669     response_header(HTTP_OK, txn);
4670     return 0;
4671 }
4672 
4673 
4674 /* Perform an PROPFIND request on "/" iff we support CalDAV */
meth_propfind_root(struct transaction_t * txn,void * params)4675 static int meth_propfind_root(struct transaction_t *txn,
4676                               void *params __attribute__((unused)))
4677 {
4678     assert(txn);
4679 
4680 #ifdef WITH_DAV
4681     /* Apple iCal and Evolution both check "/" */
4682     if (!strcmp(txn->req_uri->path, "/") ||
4683         !strcmp(txn->req_uri->path, "/dav/")) {
4684         /* Array of known "live" properties */
4685         const struct prop_entry root_props[] = {
4686 
4687             /* WebDAV ACL (RFC 3744) properties */
4688             { "principal-collection-set", NS_DAV, PROP_COLLECTION,
4689               propfind_princolset, NULL, NULL },
4690 
4691             /* WebDAV Current Principal (RFC 5397) properties */
4692             { "current-user-principal", NS_DAV, PROP_COLLECTION,
4693               propfind_curprin, NULL, NULL },
4694 
4695             { NULL, 0, 0, NULL, NULL, NULL }
4696         };
4697 
4698         struct meth_params root_params = {
4699             .propfind = { DAV_FINITE_DEPTH, root_props }
4700         };
4701 
4702         /* Make a working copy of target path */
4703         strlcpy(txn->req_tgt.path, txn->req_uri->path,
4704                 sizeof(txn->req_tgt.path));
4705         txn->req_tgt.tail = txn->req_tgt.path + strlen(txn->req_tgt.path);
4706 
4707         txn->req_tgt.allow |= ALLOW_DAV;
4708         return meth_propfind(txn, &root_params);
4709     }
4710 #endif /* WITH_DAV */
4711 
4712     return HTTP_NOT_ALLOWED;
4713 }
4714 
4715 
4716 /* Write cached header to buf, excluding any that might have sensitive data. */
trace_cachehdr(const char * name,const char * contents,void * rock)4717 static void trace_cachehdr(const char *name, const char *contents, void *rock)
4718 {
4719     struct buf *buf = (struct buf *) rock;
4720     const char **hdr, *sensitive[] =
4721         { "authorization", "cookie", "proxy-authorization", NULL };
4722 
4723     /* Ignore private headers in our cache */
4724     if (name[0] == ':') return;
4725 
4726     for (hdr = sensitive; *hdr && strcmp(name, *hdr); hdr++);
4727 
4728     if (!*hdr) buf_printf(buf, "%c%s: %s\r\n",
4729                           toupper(name[0]), name+1, contents);
4730 }
4731 
4732 /* Perform an TRACE request */
meth_trace(struct transaction_t * txn,void * params)4733 EXPORTED int meth_trace(struct transaction_t *txn, void *params)
4734 {
4735     parse_path_t parse_path = (parse_path_t) params;
4736     const char **hdr;
4737     unsigned long max_fwd = -1;
4738     struct buf *msg = &txn->resp_body.payload;
4739 
4740     /* Response should not be cached */
4741     txn->flags.cc |= CC_NOCACHE;
4742 
4743     /* Make sure method is allowed */
4744     if (!(txn->req_tgt.allow & ALLOW_TRACE)) return HTTP_NOT_ALLOWED;
4745 
4746     if ((hdr = spool_getheader(txn->req_hdrs, "Max-Forwards"))) {
4747         max_fwd = strtoul(hdr[0], NULL, 10);
4748     }
4749 
4750     if (max_fwd && parse_path) {
4751         /* Parse the path */
4752         int r;
4753 
4754         if ((r = parse_path(txn->req_uri->path,
4755                             &txn->req_tgt, &txn->error.desc))) return r;
4756 
4757         if (txn->req_tgt.mbentry && txn->req_tgt.mbentry->server) {
4758             /* Remote mailbox */
4759             struct backend *be;
4760 
4761             be = proxy_findserver(txn->req_tgt.mbentry->server,
4762                                   &http_protocol, httpd_userid,
4763                                   &backend_cached, NULL, NULL, httpd_in);
4764             if (!be) return HTTP_UNAVAILABLE;
4765 
4766             return http_pipe_req_resp(be, txn);
4767         }
4768 
4769         /* Local mailbox */
4770     }
4771 
4772     /* Echo the request back to the client as a message/http:
4773      *
4774      * - Piece the Request-line back together
4775      * - Use all non-sensitive cached headers from client
4776      */
4777     buf_reset(msg);
4778     buf_printf(msg, "TRACE %s %s\r\n", txn->req_line.uri, txn->req_line.ver);
4779     spool_enum_hdrcache(txn->req_hdrs, &trace_cachehdr, msg);
4780     buf_appendcstr(msg, "\r\n");
4781 
4782     txn->resp_body.type = "message/http";
4783     txn->resp_body.len = buf_len(msg);
4784 
4785     write_body(HTTP_OK, txn, buf_cstring(msg), buf_len(msg));
4786 
4787     return 0;
4788 }
4789 
4790 /* simple wrapper to implicity add READFB if we have the READ ACL */
httpd_myrights(struct auth_state * authstate,const mbentry_t * mbentry)4791 EXPORTED int httpd_myrights(struct auth_state *authstate, const mbentry_t *mbentry)
4792 {
4793     int rights = 0;
4794 
4795     if (mbentry && mbentry->acl) {
4796         rights = cyrus_acl_myrights(authstate, mbentry->acl);
4797 
4798         if (mbentry->mbtype == MBTYPE_CALENDAR &&
4799             (rights & DACL_READ) == DACL_READ) {
4800             rights |= DACL_READFB;
4801         }
4802     }
4803 
4804     return rights;
4805 }
4806 
4807 /* Allow unauthenticated GET/HEAD, deny all other unauthenticated requests */
http_allow_noauth_get(struct transaction_t * txn)4808 EXPORTED int http_allow_noauth_get(struct transaction_t *txn)
4809 {
4810     /* Inverse logic: True means we *require* authentication */
4811     switch (txn->meth) {
4812     case METH_GET:
4813     case METH_HEAD:
4814         /* Let method processing function decide if auth is needed */
4815         return 0;
4816     default:
4817         return 1;
4818     }
4819 }
4820 
4821 /* Allow unauthenticated requests */
http_allow_noauth(struct transaction_t * txn)4822 EXPORTED int http_allow_noauth(struct transaction_t *txn __attribute__((unused)))
4823 {
4824     return 0;
4825 }
4826