From e782d9cf91c4b5b92897032277c795126d404889 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Thu, 19 Oct 2023 06:36:35 +0000 Subject: Fix custom response, get rid of secondary static stralloc Signed-off-by: Laurent Bercot --- doc/tipidee-config.html | 2 +- doc/tipidee.conf.html | 45 ++++++++++++++++++++++++++++++++++++++- src/config/lexparse.c | 39 +++++++++++++++++++++++++++++---- src/libtipidee/tipidee_conf_get.c | 2 ++ src/tipideed/responses.c | 25 +++++++++++++++++----- src/tipideed/tipideed.c | 19 ++++++++--------- 6 files changed, 111 insertions(+), 21 deletions(-) diff --git a/doc/tipidee-config.html b/doc/tipidee-config.html index c56c248..4e3433d 100644 --- a/doc/tipidee-config.html +++ b/doc/tipidee-config.html @@ -108,7 +108,7 @@ static — and that means incompressible RAM. By contrast, a cdb file is mapped read-only, so its pages are shared clean, which means it's essentially free.
  • tipideed is exposed to the network. You -want to its attack surface to be as small as possible. Taking the parsing code +want its attack surface to be as small as possible. Taking the parsing code out of it goes a long way — admittedly, having to parse HTTP in the first place is more attack surface than a simple config file can ever hope to be, but every little bit helps.
  • diff --git a/doc/tipidee.conf.html b/doc/tipidee.conf.html index 483d9d0..4af3b8b 100644 --- a/doc/tipidee.conf.html +++ b/doc/tipidee.conf.html @@ -447,7 +447,7 @@ to clients with the Content-Type: type header.
  • Example: content-type text/html .html .htm means that files ending in .html or .htm should be served as text/html.
  • tipidee already comes with a -large +large list of default Content-Type mappings; this directive should only be necessary if you're serving files with uncommon extensions or have specific needs.
  • @@ -705,5 +705,48 @@ requests received on port 443). But if you declare a redirection under the example.com domain, it will apply to requests received on any port. +
    +

    custom-response

    +
    + +

    + custom-response status file +

    + + + diff --git a/src/config/lexparse.c b/src/config/lexparse.c index d7d9e1b..88a9157 100644 --- a/src/config/lexparse.c +++ b/src/config/lexparse.c @@ -55,7 +55,8 @@ enum token_e T_NONNPH, T_BASICAUTH, T_NOAUTH, - T_FILETYPE + T_FILETYPE, + T_CUSTOMRESPONSE } ; struct directive_s @@ -200,7 +201,7 @@ static inline void parse_redirect (char const *s, size_t const *word, size_t n, if (!domain) strerr_dief6x(1, "redirection", " without a domain directive", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; if (s[word[0]] != '/') - strerr_dief5x(1, "redirected resource must start with /", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + strerr_dief6x(1, "redirected resource", " must start with /", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; if (!uint320_scan(s + word[1], &code)) strerr_dief6x(1, "invalid redirection code ", s + word[1], " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; for (; i < 4 ; i++) if (code == codes[i]) break ; @@ -237,7 +238,7 @@ static void parse_bitattr (char const *s, size_t const *word, size_t n, char con if (!domain) strerr_dief7x(1, "resource attribute ", "definition", " without a domain directive", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; if (s[*word] != '/') - strerr_dief5x(1, "resource must start with /", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + strerr_dief6x(1, "resource", " must start with /", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; { confnode const *oldnode ; size_t arglen = strlen(s + *word) ; @@ -279,7 +280,7 @@ static inline void parse_filetype (char const *s, size_t const *word, size_t n, if (!domain) strerr_dief7x(1, "file-type", " definition", " without a domain directive", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; if (s[word[0]] != '/') - strerr_dief5x(1, "resource must start with /", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + strerr_dief6x(1, "resource", " must start with /", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; { confnode const *oldnode ; size_t arglen = strlen(s + word[0]) ; @@ -321,6 +322,32 @@ static inline void parse_filetype (char const *s, size_t const *word, size_t n, } } +static inline void parse_customresponse (char const *s, size_t const *word, size_t n, char const *domain, size_t domainlen, mdt const *md) +{ + uint32_t status ; + size_t word1, len ; + char key[7 + domainlen] ; + + if (n != 2) + strerr_dief8x(1, "too ", n > 2 ? "many" : "few", " arguments to directive ", "custom-response", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + if (!domain) + strerr_dief7x(1, "custom-response", " definition", " without a domain directive", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + if (!uint320_scan(s + word[0], &status) || status < 100 || status > 999) + strerr_dief6x(1, "invalid status", " for custom-response", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + word1 = word[1] ; + while (s[word1] == '/') word1++ ; + len = strlen(s + word1) ; + if (!len || !strncmp(s + word1, "../", 3) || strstr(s + word1, "/../") || (len >= 3 && !strcmp(s + word1 + len - 3, "/.."))) + strerr_dief6x(1, "invalid file", " for custom-response", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ; + key[0] = 'E' ; + key[1] = ':' ; + uint32_fmt(key + 2, status) ; + key[5] = ':' ; + memcpy(key + 6, domain, domainlen) ; + key[6 + domainlen] = 0 ; + add_unique(key, s + word1, len + 1, md) ; +} + static inline void process_line (char const *s, size_t const *word, size_t n, stralloc *domain, mdt *md) { static struct directive_s const directives[] = @@ -329,6 +356,7 @@ static inline void process_line (char const *s, size_t const *word, size_t n, st { .s = "basic-auth", .token = T_BASICAUTH }, { .s = "cgi", .token = T_CGI }, { .s = "content-type", .token = T_CONTENTTYPE }, + { .s = "custom-response", .token = T_CUSTOMRESPONSE }, { .s = "domain", .token = T_DOMAIN }, { .s = "file-type", .token = T_FILETYPE }, { .s = "global", .token = T_GLOBAL }, @@ -420,6 +448,9 @@ static inline void process_line (char const *s, size_t const *word, size_t n, st case T_FILETYPE : parse_filetype(s, word, n, domain->s, domain->len, md) ; break ; + case T_CUSTOMRESPONSE : + parse_customresponse(s, word, n, domain->s, domain->len, md) ; + break ; } } diff --git a/src/libtipidee/tipidee_conf_get.c b/src/libtipidee/tipidee_conf_get.c index 2c96d2b..b056fd6 100644 --- a/src/libtipidee/tipidee_conf_get.c +++ b/src/libtipidee/tipidee_conf_get.c @@ -4,12 +4,14 @@ #include #include +#include #include int tipidee_conf_get (tipidee_conf const *conf, char const *key, cdb_data *data) { size_t keylen = strlen(key) ; + LOLDEBUG("tipidee_conf_get: looking up %s", key) ; if (keylen > TIPIDEE_CONF_KEY_MAXLEN) return (errno = EINVAL, 0) ; switch (cdb_find(&conf->c, data, key, keylen)) { diff --git a/src/tipideed/responses.c b/src/tipideed/responses.c index 0d0840c..70dcddf 100644 --- a/src/tipideed/responses.c +++ b/src/tipideed/responses.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,14 @@ void response_error (tipidee_rql const *rql, char const *docroot, unsigned int s { tain deadline ; tipidee_defaulttext dt ; - char const *file = tipidee_conf_get_errorfile(&g.conf, docroot, status) ; + char const *file ; + size_t salen = g.sa.len ; + if (sarealpath(&g.sa, docroot) == -1 || !stralloc_0(&g.sa)) + die500sys(rql, 111, docroot, "realpath ", docroot) ; + if (strncmp(g.sa.s + salen, g.sa.s, g.cwdlen) || g.sa.s[salen + g.cwdlen] != '/') + die500x(rql, 102, docroot, "docroot ", docroot, " points outside of the server's root") ; + file = tipidee_conf_get_errorfile(&g.conf, g.sa.s + salen + g.cwdlen + 1, status) ; + g.sa.len = salen ; if (!tipidee_util_defaulttext(status, &dt)) { char fmt[UINT_FMT] ; @@ -47,20 +55,27 @@ void response_error (tipidee_rql const *rql, char const *docroot, unsigned int s if (file) { - int fd = open_read(file) ; - if (fd == -1) strerr_warnwu3sys("open ", "custom error file ", file) ; + int fd ; + if (file[0] == '/') + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, status)] = 0 ; + strerr_dief4x(102, "bad configuration: absolute path for custom ", fmt, " file: ", file) ; + } + fd = open_read(file) ; + if (fd == -1) strerr_warnwu3sys("open ", "custom response file ", file) ; else { struct stat st ; if (fstat(fd, &st) == -1) { fd_close(fd) ; - strerr_warnwu3sys("stat ", "custom error file ", file) ; + strerr_warnwu3sys("stat ", "custom response file ", file) ; } else if (!S_ISREG(st.st_mode)) { fd_close(fd) ; - strerr_warnw3x("custom error file ", file, " is not a regular file") ; + strerr_warnw3x("custom response file ", file, " is not a regular file") ; } else { diff --git a/src/tipideed/tipideed.c b/src/tipideed/tipideed.c index b004782..b499b2a 100644 --- a/src/tipideed/tipideed.c +++ b/src/tipideed/tipideed.c @@ -195,18 +195,17 @@ static inline unsigned int indexify (tipidee_rql const *rql, char const *docroot static inline void get_resattr (tipidee_rql const *rql, char const *docroot, char const *res, tipidee_resattr *ra) { - static stralloc sa = STRALLOC_ZERO ; - sa.len = 0 ; - if (sarealpath(&sa, res) == -1 || !stralloc_0(&sa)) die500sys(rql, 111, docroot, "realpath ", res) ; - if (strncmp(sa.s, g.sa.s, g.cwdlen) || sa.s[g.cwdlen] != '/') + size_t pos = g.sa.len ; + if (sarealpath(&g.sa, res) == -1 || !stralloc_0(&g.sa)) die500sys(rql, 111, docroot, "realpath ", res) ; + if (strncmp(g.sa.s + pos, g.sa.s, g.cwdlen) || g.sa.s[pos + g.cwdlen] != '/') die500x(rql, 102, docroot, "resource ", res, " points outside of the server's root") ; { char const *attr = 0 ; - size_t len = sa.len - g.cwdlen + 1 ; + size_t len = g.sa.len - pos - g.cwdlen + 1 ; char key[len + 1] ; key[0] = 'A' ; key[1] = ':' ; - memcpy(key + 2, sa.s + 1 + g.cwdlen, sa.len - 1 - g.cwdlen) ; + memcpy(key + 2, g.sa.s + pos + 1 + g.cwdlen, len - 2) ; key[len] = '/' ; errno = ENOENT ; while (!attr) @@ -233,18 +232,18 @@ static inline void get_resattr (tipidee_rql const *rql, char const *docroot, cha nphprefix = tipidee_conf_get_string(&g.conf, key) ; if (nphprefix) { - char const *base = strrchr(sa.s + g.cwdlen, '/') ; + char const *base = strrchr(g.sa.s + pos + g.cwdlen, '/') ; if (str_start(base + 1, nphprefix)) ra->isnph = 1 ; } } } } - if (!ra->iscgi && !ra->content_type) { - ra->content_type = tipidee_conf_get_content_type(&g.conf, sa.s + g.cwdlen) ; - if (!ra->content_type) die500sys(rql, 111, docroot, "get content type for ", sa.s + g.cwdlen) ; + ra->content_type = tipidee_conf_get_content_type(&g.conf, g.sa.s + pos + g.cwdlen) ; + if (!ra->content_type) die500sys(rql, 111, docroot, "get content type for ", g.sa.s + pos + g.cwdlen) ; } + g.sa.len = pos ; } static inline void force_redirect (tipidee_rql const *rql, char const *fn) -- cgit v1.2.3