Compare commits

...

3 Commits

Author SHA1 Message Date
Vladimir Dubrovin
5a2e9d27c3 httpsrv: a limit exceeded answered with 503, not ignored
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Authorization reports 10 when a limit of the service was exceeded,
which every other child answers with 503; the http server took it as
success and answered the request anyway. It now says 503 for itself,
after the body of the request is drained as any answer here does.
2026-11-08 14:39:54 +03:00
Vladimir Dubrovin
c0eb7a65c6 httpsrv: a dot-dot segment of a path refused, with either separator
A path ending in a backslashed dot-dot, or carrying one in a form the
checks did not name, was taken as it stood. Any dot-dot segment is now
refused wherever it is and whichever separator carries it, and a path
ending in a bare dot of either kind.
2026-11-08 14:39:50 +03:00
Vladimir Dubrovin
a7e5a8b48b pcre: a rule with no ACL a match data of its own, not itself
A rule whose ACL was missing was handed to the filters as the
connection's data, which they read as a match data block and freed:
a pointer into the configuration, dereferenced as another type and
given back to malloc. A rule without an ACL now skips the check and
goes on with a block of its own, and nothing a filter is given is
ever the rule itself.
2026-11-08 14:39:32 +03:00
2 changed files with 25 additions and 12 deletions

View File

@ -428,11 +428,12 @@ static int targetunsafe(const char *path)
#endif
}
if(!strncmp(path, "./", 2) || !strncmp(path, "../", 3)) return 1;
if(strstr(path, "/./") || strstr(path, "/../")) return 1;
if(strstr(path, "\\.\\") || strstr(path, "\\..\\")) return 1;
/* a dot-dot segment, with either separator, wherever it is in the
path: a walk out of the tree, or a file named to look like one */
if(strstr(path, "/..") || strstr(path, "\\..")) return 1;
if(strstr(path, "/./") || strstr(path, "\\.\\")) return 1;
p = path + strlen(path);
if(p - path >= 2 && !strcmp(p - 2, "/.")) return 1;
if(p - path >= 3 && !strcmp(p - 3, "/..")) return 1;
if(p - path >= 2 && (!strcmp(p - 2, "/.") || !strcmp(p - 2, "\\."))) return 1;
return 0;
}
@ -1475,16 +1476,27 @@ static int httpsrv_request(struct clientparam *param, struct httpreq *r)
return 1;
}
}
if(i && i != 10){
if(i){
/* The body goes before the answer, as for any answer this server
gives: left in the stream it would be read as the next request. */
if(!r->drained){
if(r->contentlen) httpsrv_drain(param, r->contentlen);
r->drained = 1;
}
/* 10: a limit of the service was exceeded, which the other
services answer with 503. */
if(i == 10){
static const char body[] = "exceeded your limits\n";
r->hdrs = NULL;
r->maxage = -1;
r->keepalive = 0;
if(httpsrv_head(r, 503, "text/plain", (int64_t)sizeof(body) - 1, NULL)) return 1;
httpsrv_send(r, body, (int)sizeof(body) - 1);
}
/* 4 no credentials, 5 unknown user, 6 wrong password: all of them
should let the client offer credentials again. */
if(i >= 4 && i <= 6) op_authrequired(r);
else if(i >= 4 && i <= 6) op_authrequired(r);
else op_forbidden(r);
RETURN(i);
}

View File

@ -264,13 +264,14 @@ static FILTER_ACTION pcre_filter_client(void *fo, struct clientparam * param, vo
struct pcre_filter_data *rule = (struct pcre_filter_data *)fo;
struct pcre_client_data *pcd;
*fc = fo;
*fc = NULL;
if(!fo) return PASS;
if(!rule->acl) return CONTINUE;
memset (&tmpace, 0, sizeof(struct ace));
tmpace.src = rule->acl->src;
res = pl->ACLMatches(&tmpace, param);
if(!res) return PASS;
if(rule->acl){
memset (&tmpace, 0, sizeof(struct ace));
tmpace.src = rule->acl->src;
res = pl->ACLMatches(&tmpace, param);
if(!res) return PASS;
}
/* the connection goes on with a match data of its own */
if(!(pcd = pl->mallocfunc(sizeof(struct pcre_client_data)))){
*fc = NULL;