/* httpd.h -- Common state for HTTP/RSS/xDAV/JMAP/TZdist/iSchedule daemon
*
* Copyright (c) 1994-2017 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef HTTPD_H
#define HTTPD_H
#include <sasl/sasl.h>
#include <libxml/tree.h>
#include <libxml/uri.h>
#include <libical/ical.h>
#include "annotate.h" /* for strlist */
#include "hash.h"
#include "http_client.h"
#include "mailbox.h"
#include "prometheus.h"
#include "spool.h"
#define MAX_REQ_LINE 8000 /* minimum size per RFC 7230 */
#define MARKUP_INDENT 2 /* # spaces to indent each line of markup */
#define GZIP_MIN_LEN 300 /* minimum length of data to gzip */
#define COMPRESS_START (1<<0)
#define COMPRESS_END (1<<1)
#define DFLAG_UNBIND "DAV:unbind"
#define DFLAG_UNCHANGED "DAV:unchanged"
/* XML namespace URIs */
#define XML_NS_CYRUS "http://cyrusimap.org/ns/"
/* Supported TLS version for Upgrade */
#define TLS_VERSION "TLS/1.2"
/* Supported HTML DOCTYPE */
#define HTML_DOCTYPE \
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" " \
"\"http://www.w3.org/TR/html4/loose.dtd\">"
#define XML_DECLARATION \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
/* Macro to return proper response code when user privileges are insufficient */
#define HTTP_NO_PRIVS \
(httpd_userid && !is_userid_anonymous(httpd_userid) ? \
HTTP_FORBIDDEN : HTTP_UNAUTHORIZED)
/* Macro to access query part of URI */
#if LIBXML_VERSION >= 20700
#define URI_QUERY(uri) uri->query_raw
#else
#define URI_QUERY(uri) uri->query
#endif
/* SASL usage based on availability */
#if defined(SASL_NEED_HTTP) && defined(SASL_HTTP_REQUEST)
#define HTTP_DIGEST_MECH "DIGEST-MD5"
#define SASL_USAGE_FLAGS (SASL_NEED_HTTP | SASL_SUCCESS_DATA)
#else
#define HTTP_DIGEST_MECH NULL /* not supported by our SASL version */
#define SASL_USAGE_FLAGS SASL_SUCCESS_DATA
#endif /* SASL_NEED_HTTP */
/* Array of HTTP methods known by our server. */
struct known_meth_t {
const char *name;
unsigned flags;
enum prom_labelled_metric metric;
};
extern const struct known_meth_t http_methods[];
extern struct namespace_t *http_namespaces[];
/* Flags for known methods*/
enum {
METH_NOBODY = (1<<0), /* Method does not expect a body */
METH_SAFE = (1<<1), /* Method is "safe" */
};
/* Path namespaces */
enum {
URL_NS_DEFAULT = 0,
URL_NS_PRINCIPAL,
URL_NS_NOTIFY,
URL_NS_CALENDAR,
URL_NS_FREEBUSY,
URL_NS_ADDRESSBOOK,
URL_NS_DRIVE,
URL_NS_ISCHEDULE,
URL_NS_DOMAINKEY,
URL_NS_TZDIST,
URL_NS_RSS,
URL_NS_DBLOOKUP,
#ifdef WITH_JMAP
URL_NS_JMAP,
#endif
URL_NS_ADMIN,
URL_NS_APPLEPUSH,
URL_NS_PROMETHEUS,
URL_NS_CGI,
};
/* Bitmask of features/methods to allow, based on URL */
enum {
ALLOW_READ = (1<<0), /* Read resources/properties */
ALLOW_POST = (1<<1), /* Post to a URL */
ALLOW_WRITE = (1<<2), /* Create/modify/lock resources */
ALLOW_PATCH = (1<<3), /* Patch resources */
ALLOW_DELETE = (1<<4), /* Delete resources/collections */
ALLOW_TRACE = (1<<5), /* TRACE a request */
ALLOW_CONNECT = (1<<6), /* Establish a tunnel */
ALLOW_DAV = (1<<8), /* WebDAV specific methods/features */
ALLOW_PROPPATCH = (1<<9), /* Modify properties */
ALLOW_MKCOL = (1<<10),/* Create collections */
ALLOW_ACL = (1<<11),/* Modify access control list */
ALLOW_USERDATA = (1<<12),/* Store per-user data for resource */
ALLOW_CAL = (1<<16),/* CalDAV specific methods/features */
ALLOW_CAL_SCHED = (1<<17),/* CalDAV Scheduling specific features */
ALLOW_CAL_AVAIL = (1<<18),/* CalDAV Availability specific features */
ALLOW_CAL_NOTZ = (1<<19),/* CalDAV TZ by Ref specific features */
ALLOW_CAL_ATTACH = (1<<20),/* CalDAV Managed Attachments features */
ALLOW_CARD = (1<<24),/* CardDAV specific methods/features */
ALLOW_READONLY = (1<<30),/* Allow "unsafe" methods when readonly */
ALLOW_ISCHEDULE = (1<<31) /* iSchedule specific methods/features */
};
#define ALLOW_READ_MASK ~(ALLOW_POST|ALLOW_WRITE|ALLOW_DELETE|ALLOW_PATCH\
|ALLOW_PROPPATCH|ALLOW_MKCOL|ALLOW_ACL)
typedef struct transaction_t txn_t;
struct auth_scheme_t {
unsigned id; /* Identifier of the scheme */
const char *name; /* HTTP auth scheme name */
const char *saslmech; /* Corresponding SASL mech name */
unsigned flags; /* Bitmask of requirements/features */
/* Optional function to send success data */
};
/* Auth scheme identifiers */
enum {
AUTH_BASIC = (1<<0),
AUTH_DIGEST = (1<<1),
AUTH_SPNEGO = (1<<2),
AUTH_NTLM = (1<<3),
AUTH_BEARER = (1<<4),
AUTH_SCRAM_SHA1 = (1<<5),
AUTH_SCRAM_SHA256 = (1<<6)
};
/* Auth scheme flags */
enum {
AUTH_NEED_PERSIST = (1<<0), /* Persistent connection required */
AUTH_NEED_REQUEST = (1<<1), /* Request-line required */
AUTH_SERVER_FIRST = (1<<2), /* SASL mech is server-first */
AUTH_BASE64 = (1<<3), /* Base64 encode/decode challenge/response */
AUTH_REALM_PARAM = (1<<4), /* Need "realm" parameter in initial challenge */
AUTH_DATA_PARAM = (1<<5), /* Challenge/credentials use auth-params */
AUTH_SUCCESS_WWW = (1<<6) /* Success data uses WWW-Authenticate header */
};
#define AUTH_SCHEME_BASIC { AUTH_BASIC, "Basic", NULL, \
AUTH_SERVER_FIRST | AUTH_REALM_PARAM | AUTH_BASE64 }
/* List of HTTP auth schemes that we support */
extern struct auth_scheme_t auth_schemes[];
/* Request-line context */
struct request_line_t {
char buf[MAX_REQ_LINE+1]; /* working copy of request-line */
const char *meth; /* method */
const char *uri; /* request-target */
const char *ver; /* HTTP-version */
};
/* Request target context */
struct request_target_t {
char path[MAX_MAILBOX_PATH+1]; /* working copy of URL path */
char *tail; /* tail of original request path */
const struct namespace_t *namespace; /* namespace of path */
char *userid; /* owner of collection (needs freeing) */
char *collection; /* ptr to collection name */
size_t collen;
char *resource; /* ptr to resource name */
size_t reslen;
unsigned flags; /* target-specific flags/meta-data */
unsigned long allow; /* bitmask of allowed features/methods */
mbentry_t *mbentry; /* mboxlist entry of target collection */
const char *mboxprefix; /* mailbox prefix */
};
/* Request target flags */
enum {
TGT_SERVER_INFO = 1,
TGT_DAV_SHARED,
TGT_SCHED_INBOX,
TGT_SCHED_OUTBOX,
TGT_MANAGED_ATTACH,
TGT_DRIVE_ROOT,
TGT_DRIVE_USER,
TGT_USER_ZZZZ
};
/* Function to parse URI path and generate a mailbox name */
typedef int (*parse_path_t)(const char *path, struct request_target_t *tgt,
const char **resultstr);
/* Auth challenge context */
struct auth_challenge_t {
struct auth_scheme_t *scheme; /* Selected AUTH scheme */
const char *param; /* Server challenge */
};
/* Meta-data for error response */
struct error_t {
const char *desc; /* Error description */
unsigned precond; /* [Cal]DAV precondition */
xmlNodePtr node; /* XML node to be added to error */
const char *resource; /* Resource href to be added to error */
int rights; /* Privileges needed by resource */
};
struct range {
unsigned long first;
unsigned long last;
struct range *next;
};
struct patch_doc_t {
const char *format; /* MIME format of patch document */
int (*proc)(); /* Function to parse and apply doc */
};
typedef int (*encode_proc_t)(struct transaction_t *txn,
unsigned flags, const char *buf, unsigned len);
/* Meta-data for response body (payload & representation headers) */
struct resp_body_t {
ulong len; /* Content-Length */
struct range *range; /* Content-Range */
struct {
const char *fname;
unsigned attach : 1;
} dispo; /* Content-Dispo */
struct {
unsigned char type;
encode_proc_t proc;
} enc; /* Content-Encoding */
const char *lang; /* Content-Language */
const char *loc; /* Content-Location */
const u_char *md5; /* Content-MD5 */
const char *type; /* Content-Type */
const struct patch_doc_t *patch; /* Accept-Patch */
unsigned prefs; /* Prefer */
strarray_t links; /* Link(s) */
const char *lock; /* Lock-Token */
const char *ctag; /* CTag */
const char *etag; /* ETag */
time_t lastmod; /* Last-Modified */
time_t maxage; /* Expires */
const char *stag; /* Schedule-Tag */
const char *cmid; /* Cal-Managed-ID */
time_t iserial; /* iSched serial# */
hdrcache_t extra_hdrs; /* Extra headers */
struct buf payload; /* Payload */
};
/* Transaction flags */
struct txn_flags_t {
unsigned long ver : 2; /* HTTP version of request */
unsigned long conn : 3; /* Connection opts on req/resp */
unsigned long upgrade : 3; /* Upgrade protocols */
unsigned long override : 1; /* HTTP method override */
unsigned long cors : 3; /* Cross-Origin Resource Sharing */
unsigned long mime : 1; /* MIME-conformant response */
unsigned long te : 3; /* Transfer-Encoding for resp */
unsigned long cc : 8; /* Cache-Control directives for resp */
unsigned long ranges : 1; /* Accept range requests for resource */
unsigned long vary : 6; /* Headers on which response can vary */
unsigned long trailer : 3; /* Headers which will be in trailer */
unsigned long redirect : 1; /* CGI local redirect */
};
/* HTTP connection context */
struct http_connection {
struct protstream *pin; /* Input protstream */
struct protstream *pout; /* Output protstream */
const char *clienthost; /* Name of client host */
int logfd; /* Telemetry log file */
struct buf logbuf; /* Telemetry log buffer */
void *tls_ctx; /* TLS context */
void *sess_ctx; /* HTTP/2+ session context */
xmlParserCtxtPtr xml; /* XML parser content */
};
/* Transaction context */
struct transaction_t {
struct http_connection *conn; /* Global connection context */
void *strm_ctx; /* HTTP/2+ stream context */
void *ws_ctx; /* WebSocket channel context */
unsigned meth; /* Index of Method to be performed */
struct txn_flags_t flags; /* Flags for this txn */
struct request_line_t req_line; /* Parsed request-line */
xmlURIPtr req_uri; /* Parsed request-target URI */
struct request_target_t req_tgt; /* Parsed request-target path */
hash_table req_qparams; /* Parsed query params */
hdrcache_t req_hdrs; /* Cached HTTP headers */
struct body_t req_body; /* Buffered request body */
struct auth_challenge_t auth_chal; /* Authentication challenge */
const char *location; /* Location of resource */
struct error_t error; /* Error response meta-data */
struct resp_body_t resp_body; /* Response body meta-data */
struct buf zbuf; /* Compression buffer */
struct buf buf; /* Working buffer - currently used for:
httpd:
- telemetry of auth'd request
- error desc string
- Location hdr on redirects
- Etag for static docs
http_rss:
- Content-Type for MIME parts
- URL for feed & items
http_caldav:
- precond error resource URL
http_ischedule:
- error desc string
*/
void *zstrm; /* Zlib compression context */
void *brotli; /* Brotli compression context */
void *zstd; /* Zstandard compression context */
};
/* HTTP version flags */
enum {
VER_1_0 = 0,
VER_1_1 = 1,
VER_2 = 2
};
/* Connection token flags */
enum {
CONN_CLOSE = (1<<0),
CONN_UPGRADE = (1<<1),
CONN_KEEPALIVE = (1<<2)
};
/* Upgrade protocol flags */
enum {
UPGRADE_TLS = (1<<0),
UPGRADE_HTTP2 = (1<<1),
UPGRADE_WS = (1<<2)
};
/* Cross-Origin Resource Sharing flags */
enum {
CORS_NONE = 0,
CORS_SIMPLE = 1,
CORS_PREFLIGHT = 2
};
/* Content-Encoding flags (coding of representation) */
enum {
CE_IDENTITY = 0, /* no encoding */
CE_DEFLATE = (1<<0), /* ZLIB - RFC 1950 */
CE_GZIP = (1<<1), /* GZIP - RFC 1952 */
CE_BR = (1<<2), /* Brotli - RFC 7932 */
CE_ZSTD = (1<<3) /* Zstandard - RFC 8478 */
};
/* Cache-Control directive flags */
enum {
CC_REVALIDATE = (1<<0),
CC_NOCACHE = (1<<1),
CC_NOSTORE = (1<<2),
CC_NOTRANSFORM = (1<<3),
CC_PUBLIC = (1<<4),
CC_PRIVATE = (1<<5),
CC_MAXAGE = (1<<6),
CC_IMMUTABLE = (1<<7), /* RFC 8246 */
};
/* Vary header flags (headers used in selecting/producing representation) */
enum {
VARY_ACCEPT = (1<<0),
VARY_AE = (1<<1), /* Accept-Encoding */
VARY_BRIEF = (1<<2),
VARY_PREFER = (1<<3),
VARY_IFNONE = (1<<4), /* If-None-Match */
VARY_CALTZ = (1<<5) /* CalDAV-Timezones */
};
/* Trailer header flags */
enum {
TRAILER_CMD5 = (1<<0), /* Content-MD5 will be generated */
TRAILER_CTAG = (1<<1), /* CTag will be returned */
TRAILER_PROXY = (1<<2) /* Trailer(s) will be proxied from origin */
};
typedef int (*premethod_proc_t)(struct transaction_t *txn);
typedef int (*method_proc_t)(struct transaction_t *txn, void *params);
struct method_t {
method_proc_t proc; /* Function to perform the method */
void *params; /* Parameters to pass to the method */
};
struct connect_params {
/* WebSocket parameters */
const char *endpoint;
const char *subprotocol;
int (*data_cb)(struct buf *inbuf, struct buf *outbuf,
struct buf *logbuf, void **rock);
};
struct namespace_t {
unsigned id; /* Namespace identifier */
unsigned enabled; /* Is this namespace enabled? */
const char *name; /* Text name of this namespace ([A-Z][a-z][0-9]+) */
const char *prefix; /* Prefix of URL path denoting namespace */
const char *well_known; /* Any /.well-known/ URI */
int (*need_auth)(txn_t *); /* Function run prior to unauthorized requests */
unsigned auth_schemes; /* Bitmask of allowed auth schemes, 0 for any */
int mboxtype; /* What mbtype can be seen in this namespace? */
unsigned long allow; /* Bitmask of allowed features/methods */
void (*init)(struct buf *); /* Function run during service startup */
int (*auth)(const char *); /* Function run after authentication */
void (*reset)(void); /* Function run before change in auth */
void (*shutdown)(void); /* Function run during service shutdown */
int (*premethod)(txn_t *); /* Function run prior to any method */
int (*bearer)(const char *, /* Function run to authenticate Bearer token */
char *, size_t);
struct method_t methods[]; /* Array of functions to perform HTTP methods.
* MUST be an entry for EACH method listed,
* and in the SAME ORDER in which they appear
* in the http_methods[] array.
* If the method is not supported,
* the function pointer MUST be NULL.
*/
};
struct accept {
char *token;
float qual;
struct accept *next;
};
extern struct namespace_t namespace_default;
extern struct namespace_t namespace_principal;
extern struct namespace_t namespace_notify;
extern struct namespace_t namespace_calendar;
extern struct namespace_t namespace_freebusy;
extern struct namespace_t namespace_addressbook;
extern struct namespace_t namespace_drive;
extern struct namespace_t namespace_ischedule;
extern struct namespace_t namespace_domainkey;
extern struct namespace_t namespace_tzdist;
#ifdef WITH_JMAP
extern struct namespace_t namespace_jmap;
#endif
extern struct namespace_t namespace_rss;
extern struct namespace_t namespace_dblookup;
extern struct namespace_t namespace_admin;
extern struct namespace_t namespace_applepush;
extern struct namespace_t namespace_prometheus;
extern struct namespace_t namespace_cgi;
/* XXX These should be included in struct transaction_t */
extern struct buf serverinfo;
extern struct backend **backend_cached;
extern struct protstream *httpd_in;
extern struct protstream *httpd_out;
extern int https;
extern sasl_conn_t *httpd_saslconn;
extern int httpd_timeout;
extern int httpd_userisadmin;
extern int httpd_userisproxyadmin;
extern int httpd_userisanonymous;
extern char *httpd_userid;
extern char *httpd_extrafolder;
extern char *httpd_extradomain;
extern struct auth_state *httpd_authstate;
extern struct namespace httpd_namespace;
extern const char *httpd_localip, *httpd_remoteip;
extern unsigned long config_httpmodules;
extern int config_httpprettytelemetry;
extern strarray_t *httpd_log_headers;
extern int ignorequota;
extern int apns_enabled;
extern xmlURIPtr parse_uri(unsigned meth, const char *uri, unsigned path_reqd,
const char **errstr);
extern struct accept *parse_accept(const char **hdr);
extern void parse_query_params(struct transaction_t *txn, const char *query);
extern time_t calc_compile_time(const char *time, const char *date);
extern const char *http_statusline(unsigned ver, long code);
extern char *httpdate_gen(char *buf, size_t len, time_t t);
extern void begin_resp_headers(struct transaction_t *txn, long code);
extern int end_resp_headers(struct transaction_t *txn, long code);
extern void simple_hdr(struct transaction_t *txn,
const char *name, const char *value, ...)
__attribute__((format(printf, 3, 4)));
extern void content_md5_hdr(struct transaction_t *txn,
const unsigned char *md5);
extern void comma_list_hdr(struct transaction_t *txn,
const char *hdr, const char *vals[],
unsigned flags, ...);
extern void response_header(long code, struct transaction_t *txn);
extern void buf_printf_markup(struct buf *buf, unsigned level,
const char *fmt, ...)
__attribute__((format(printf, 3, 4)));
extern void keepalive_response(struct transaction_t *txn);
extern void error_response(long code, struct transaction_t *txn);
extern void html_response(long code, struct transaction_t *txn, xmlDocPtr html);
extern void xml_response(long code, struct transaction_t *txn, xmlDocPtr xml);
extern void xml_partial_response(struct transaction_t *txn,
xmlDocPtr doc, xmlNodePtr node,
unsigned level, xmlBufferPtr *buf);
extern void write_body(long code, struct transaction_t *txn,
const char *buf, unsigned len);
extern void write_multipart_body(long code, struct transaction_t *txn,
const char *buf, unsigned len,
const char *part_headers);
extern int meth_connect(struct transaction_t *txn, void *params);
extern int meth_options(struct transaction_t *txn, void *params);
extern int meth_trace(struct transaction_t *txn, void *params);
extern int etagcmp(const char *hdr, const char *etag);
extern int check_precond(struct transaction_t *txn,
const char *etag, time_t lastmod);
extern void log_cachehdr(const char *name, const char *contents,
const char *raw, void *rock);
extern int examine_request(struct transaction_t *txn, const char *uri);
extern int process_request(struct transaction_t *txn);
extern void transaction_free(struct transaction_t *txn);
extern int httpd_myrights(struct auth_state *authstate, const mbentry_t *mbentry);
extern int http_allow_noauth(struct transaction_t *txn);
extern int http_allow_noauth_get(struct transaction_t *txn);
extern int http_read_req_body(struct transaction_t *txn);
extern void *zlib_init();
extern int zlib_compress(struct transaction_t *txn, unsigned flags,
const char *buf, unsigned len);
extern void *zstd_init();
extern void *brotli_init();
#endif /* HTTPD_H */