summaryrefslogtreecommitdiff
path: root/src/mod_webdav.c
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2021-04-01 11:25:42 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2021-04-05 13:24:51 -0400
commit7283c435668258b25b6261d3018d1a8eabafcf82 (patch)
treeccc8acc2524fe8f0c6191dd5ec19825aa77f597f /src/mod_webdav.c
parentc6362e127fb3bac68855e9c1deab1c928d01484e (diff)
downloadlighttpd-git-7283c435668258b25b6261d3018d1a8eabafcf82.tar.gz
[mod_webdav] limit mem use under extreme condition
limit memory use under extreme conditions (edge cases)
Diffstat (limited to 'src/mod_webdav.c')
-rw-r--r--src/mod_webdav.c139
1 files changed, 82 insertions, 57 deletions
diff --git a/src/mod_webdav.c b/src/mod_webdav.c
index a690f179..8ff22216 100644
--- a/src/mod_webdav.c
+++ b/src/mod_webdav.c
@@ -230,6 +230,7 @@
#include "buffer.h"
#include "chunk.h"
#include "fdevent.h"
+#include "http_chunk.h"
#include "http_date.h"
#include "http_etag.h"
#include "http_header.h"
@@ -621,6 +622,19 @@ URIHANDLER_FUNC(mod_webdav_uri_handler)
}
+static void
+webdav_double_buffer (request_st * const r, buffer * const b)
+{
+ /* send parts of XML to r->write_queue; surrounding XML tags added later.
+ * http_chunk_append_buffer() is safe to use here since r->resp_body_started
+ * has not been set, so r->resp_send_chunked can not be set yet */
+ if (buffer_string_length(b) > 60000) {
+ http_chunk_append_buffer(r, b); /*(might move/steal/reset buffer)*/
+ buffer_clear(b);
+ }
+}
+
+
#ifdef USE_LOCKS
typedef struct webdav_lockdata_wr {
@@ -858,16 +872,20 @@ webdav_xml_propstat (buffer * const b, buffer * const value, const int status)
__attribute_cold__
static void
-webdav_xml_response_status (buffer * const b,
+webdav_xml_response_status (request_st * const r,
const buffer * const href,
const int status)
{
+ buffer * const b = chunk_buffer_acquire();
buffer_append_string_len(b, CONST_STR_LEN(
"<D:response>\n"));
webdav_xml_href(b, href);
webdav_xml_status(b, status);
buffer_append_string_len(b, CONST_STR_LEN(
"</D:response>\n"));
+ /*(under extreme error conditions, write() to tempfile for each error)*/
+ http_chunk_append_buffer(r, b); /*(might move/steal/reset buffer)*/
+ chunk_buffer_release(b);
}
@@ -940,8 +958,7 @@ webdav_xml_activelock (buffer * const b,
static void
webdav_xml_doc_multistatus (request_st * const r,
- const plugin_config * const pconf,
- buffer * const ms)
+ const plugin_config * const pconf)
{
http_status_set_fin(r, 207); /* Multi-status */
@@ -951,7 +968,7 @@ webdav_xml_doc_multistatus (request_st * const r,
buffer_append_string_len(b, CONST_STR_LEN(
"<D:multistatus xmlns:D=\"DAV:\">\n"));
chunkqueue_prepend_buffer_commit(cq);
- chunkqueue_append_buffer(cq, ms); /*(might move/steal/reset buffer)*/
+
chunkqueue_append_mem(cq, CONST_STR_LEN(
"</D:multistatus>\n"));
@@ -1083,28 +1100,25 @@ webdav_xml_doc_423_locked (request_st * const r, buffer * const hrefs,
http_status_set(r, 423); /* Locked */
r->resp_body_finished = 1;
- buffer * const b = /*(optimization; buf extended as needed)*/
- chunkqueue_append_buffer_open_sz(&r->write_queue, 256 + hrefs->used);
-
+ chunkqueue * const cq = &r->write_queue;
+ buffer * const b = chunkqueue_prepend_buffer_open(cq);
webdav_xml_doctype(b, r);
- struct const_iovec iov[] = {
- { CONST_STR_LEN(
+ buffer_append_str3(b,
+ CONST_STR_LEN(
"<D:error xmlns:D=\"DAV:\">\n"
- "<D:") }
- ,{ errtag, errtaglen }
- ,{ CONST_STR_LEN(
- ">\n") }
- ,{ CONST_BUF_LEN(hrefs) }
- ,{ CONST_STR_LEN(
- "</D:") }
- ,{ errtag, errtaglen }
- ,{ CONST_STR_LEN(
+ "<D:"),
+ errtag, errtaglen,
+ CONST_STR_LEN(
+ ">\n"));
+ chunkqueue_prepend_buffer_commit(cq);
+ buffer_append_str3(hrefs,
+ CONST_STR_LEN(
+ "</D:"),
+ errtag, errtaglen,
+ CONST_STR_LEN(
">\n"
- "</D:error>\n") }
- };
- buffer_append_iovec(b, iov, sizeof(iov)/sizeof(*iov));
-
- chunkqueue_append_buffer_commit(&r->write_queue);
+ "</D:error>\n"));
+ chunkqueue_append_buffer(cq, hrefs); /*(might move/steal/reset buffer)*/
}
#endif
@@ -2367,7 +2381,7 @@ webdav_delete_file (const plugin_config * const pconf,
static int
webdav_delete_dir (const plugin_config * const pconf,
physical_st * const dst,
- buffer * const b,
+ request_st * const r,
const int flags)
{
int multi_status = 0;
@@ -2380,7 +2394,7 @@ webdav_delete_dir (const plugin_config * const pconf,
#endif
if (NULL == dir) {
if (dfd >= 0) close(dfd);
- webdav_xml_response_status(b, &dst->rel_path, 403);
+ webdav_xml_response_status(r, &dst->rel_path, 403);
return 1;
}
@@ -2436,13 +2450,13 @@ webdav_delete_dir (const plugin_config * const pconf,
if (s_isdir) {
buffer_append_string_len(&dst->path, CONST_STR_LEN("/"));
buffer_append_string_len(&dst->rel_path, CONST_STR_LEN("/"));
- multi_status |= webdav_delete_dir(pconf, dst, b, flags);
+ multi_status |= webdav_delete_dir(pconf, dst, r, flags);
}
else {
int status =
webdav_unlinkat(pconf, dst, dfd, de->d_name);
if (0 != status) {
- webdav_xml_response_status(b, &dst->rel_path, status);
+ webdav_xml_response_status(r, &dst->rel_path, status);
multi_status = 1;
}
}
@@ -2465,7 +2479,7 @@ webdav_delete_dir (const plugin_config * const pconf,
}
}
if (0 != rmdir_status) {
- webdav_xml_response_status(b, &dst->rel_path, rmdir_status);
+ webdav_xml_response_status(r, &dst->rel_path, rmdir_status);
multi_status = 1;
}
}
@@ -2739,7 +2753,7 @@ static int
webdav_copymove_dir (const plugin_config * const pconf,
physical_st * const src,
physical_st * const dst,
- buffer * const b,
+ request_st * const r,
int flags)
{
/* NOTE: merging collections is NON-CONFORMANT behavior
@@ -2795,7 +2809,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
#ifndef RENAME_NOREPLACE /*(renameat2() not well-supported yet)*/
if (!overwrite) {
if (0 == lstat(dst->path.ptr, &st) || errno != ENOENT) {
- webdav_xml_response_status(b, &src->rel_path, 412);
+ webdav_xml_response_status(r, &src->rel_path, 412);
return 412; /* Precondition Failed */
}
/* TOC-TOU race between lstat() and rename(),
@@ -2818,14 +2832,14 @@ webdav_copymove_dir (const plugin_config * const pconf,
case ENOTEMPTY:
#endif
if (!overwrite) {
- webdav_xml_response_status(b, &src->rel_path, 412);
+ webdav_xml_response_status(r, &src->rel_path, 412);
return 412; /* Precondition Failed */
}
make_destdir = 0;
break;
case ENOTDIR:
if (!overwrite) {
- webdav_xml_response_status(b, &src->rel_path, 409);
+ webdav_xml_response_status(r, &src->rel_path, 409);
return 409; /* Conflict */
}
@@ -2852,7 +2866,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
dst->path.ptr[dst->path.used-2] = '/'; /*(restore slash)*/
dst->rel_path.ptr[dst->rel_path.used-2] = '/';
if (0 != status) {
- webdav_xml_response_status(b, &src->rel_path, status);
+ webdav_xml_response_status(r, &src->rel_path, status);
return status;
}
@@ -2883,7 +2897,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
if (make_destdir) {
if (0 != (status = webdav_mkdir(pconf, dst, overwrite))) {
- webdav_xml_response_status(b, &src->rel_path, status);
+ webdav_xml_response_status(r, &src->rel_path, status);
return status;
}
}
@@ -2908,7 +2922,7 @@ webdav_copymove_dir (const plugin_config * const pconf,
#endif
if (NULL == srcdir) {
if (dfd >= 0) close(dfd);
- webdav_xml_response_status(b, &src->rel_path, 403);
+ webdav_xml_response_status(r, &src->rel_path, 403);
return 403; /* Forbidden */
}
mode_t d_type;
@@ -2963,14 +2977,14 @@ webdav_copymove_dir (const plugin_config * const pconf,
buffer_append_string_len(&dst->path, CONST_STR_LEN("/"));
buffer_append_string_len(&src->rel_path, CONST_STR_LEN("/"));
buffer_append_string_len(&dst->rel_path, CONST_STR_LEN("/"));
- status = webdav_copymove_dir(pconf, src, dst, b, flags);
+ status = webdav_copymove_dir(pconf, src, dst, r, flags);
if (0 != status)
multi_status = 1;
}
else if (S_ISREG(d_type)) {
status = webdav_copymove_file(pconf, src, dst, &flags);
if (0 != status)
- webdav_xml_response_status(b, &src->rel_path, status);
+ webdav_xml_response_status(r, &src->rel_path, status);
}
#if 0
else if (S_ISLNK(d_type)) {
@@ -2998,9 +3012,9 @@ webdav_copymove_dir (const plugin_config * const pconf,
if (0 == multi_status) {
if (flags & (WEBDAV_FLAG_MOVE_RENAME|WEBDAV_FLAG_MOVE_XDEV)) {
- status = webdav_delete_dir(pconf, src, b, flags); /* content */
+ status = webdav_delete_dir(pconf, src, r, flags); /* content */
if (0 != status) {
- webdav_xml_response_status(b, &src->rel_path, status);
+ webdav_xml_response_status(r, &src->rel_path, status);
multi_status = 1;
}
}
@@ -3383,6 +3397,8 @@ webdav_propfind_resource_403 (const webdav_propfind_bufs * const restrict pb)
buffer_append_string_len(b, CONST_STR_LEN(
"</D:propstat>\n"
"</D:response>\n"));
+
+ webdav_double_buffer(pb->r, b);
}
@@ -3423,6 +3439,8 @@ webdav_propfind_resource (const webdav_propfind_bufs * const restrict pb)
webdav_xml_propstat(b, b_404, 404);
buffer_append_string_len(b, CONST_STR_LEN(
"</D:response>\n"));
+
+ webdav_double_buffer(pb->r, b);
}
@@ -3671,6 +3689,7 @@ struct webdav_lock_token_submitted_st {
int size;
const buffer *authn_user;
buffer *b;
+ request_st *r;
int nlocks;
int slocks;
int smatch;
@@ -3722,8 +3741,10 @@ webdav_lock_token_submitted_cb (void * const vdata,
}
/* no match with lock tokens in request */
- if (!shared)
+ if (!shared) {
webdav_xml_href(cbdata->b, &lockdata->lockroot);
+ webdav_double_buffer(cbdata->r, cbdata->b);
+ }
}
@@ -3745,6 +3766,7 @@ webdav_has_lock (request_st * const r,
struct webdav_lock_token_submitted_st cbdata;
cbdata.b = chunk_buffer_acquire();
+ cbdata.r = r;
cbdata.tokens = NULL;
cbdata.used = 0;
cbdata.size = 0;
@@ -3874,7 +3896,7 @@ webdav_has_lock (request_st * const r,
int has_lock = 1;
- if (0 != cbdata.b->used)
+ if (0 != cbdata.b->used || !chunkqueue_is_empty(&r->write_queue))
has_lock = 0;
else if (0 == cbdata.nlocks) { /* resource is not locked at all */
/* error if lock provided on source and no locks present on source;
@@ -4118,10 +4140,11 @@ mod_webdav_propfind (request_st * const r, const plugin_config * const pconf)
pb.r = r;
pb.pconf = pconf;
pb.dst = &r->physical;
- pb.b = /*(optimization; buf extended as needed)*/
- chunkqueue_append_buffer_open_sz(&r->write_queue, 8192);
+ pb.b = chunk_buffer_acquire();
pb.b_200 = chunk_buffer_acquire();
pb.b_404 = chunk_buffer_acquire();
+ /*(optimization; buf extended as needed)*/
+ chunk_buffer_prepare_append(pb.b, 8192);
webdav_xml_doctype(pb.b, r);
buffer_append_string_len(pb.b, CONST_STR_LEN(
@@ -4135,7 +4158,8 @@ mod_webdav_propfind (request_st * const r, const plugin_config * const pconf)
buffer_append_string_len(pb.b, CONST_STR_LEN(
"</D:multistatus>\n"));
- chunkqueue_append_buffer_commit(&r->write_queue);
+ http_chunk_append_buffer(r, pb.b); /*(might move/steal/reset buffer)*/
+ chunk_buffer_release(pb.b);
http_status_set_fin(r, 207); /* Multi-status */
chunk_buffer_release(pb.b_404);
@@ -4220,12 +4244,10 @@ mod_webdav_delete (request_st * const r, const plugin_config * const pconf)
return HANDLER_FINISHED;
}
- buffer * const ms = chunk_buffer_acquire(); /* multi-status */
-
const int flags = (r->conf.force_lowercase_filenames)
? WEBDAV_FLAG_LC_NAMES
: 0;
- if (0 == webdav_delete_dir(pconf, &r->physical, ms, flags)) {
+ if (0 == webdav_delete_dir(pconf, &r->physical, r, flags)) {
/* Note: this does not destroy locks if an error occurs,
* which is not a problem if lock is only on the collection
* being moved, but might need finer updates if there are
@@ -4234,10 +4256,9 @@ mod_webdav_delete (request_st * const r, const plugin_config * const pconf)
http_status_set_fin(r, 204); /* No Content */
}
else {
- webdav_xml_doc_multistatus(r, pconf, ms); /* 207 Multi-status */
+ webdav_xml_doc_multistatus(r, pconf); /* 207 Multi-status */
}
- chunk_buffer_release(ms);
/* invalidate stat cache of src if DELETE, whether or not successful */
stat_cache_delete_dir(CONST_BUF_LEN(&r->physical.path));
}
@@ -4962,8 +4983,7 @@ mod_webdav_copymove_b (request_st * const r, const plugin_config * const pconf,
return HANDLER_FINISHED;
}
- buffer * const ms = chunk_buffer_acquire(); /* multi-status */
- if (0 == webdav_copymove_dir(pconf, &r->physical, dst, ms, flags)) {
+ if (0 == webdav_copymove_dir(pconf, &r->physical, dst, r, flags)) {
if (r->http_method == HTTP_METHOD_MOVE)
webdav_lock_delete_uri_col(pconf, &r->physical.rel_path);
/*(requiring lock on destination requires MKCOL create dst first)
@@ -4976,9 +4996,8 @@ mod_webdav_copymove_b (request_st * const r, const plugin_config * const pconf,
* which is not a problem if lock is only on the collection
* being moved, but might need finer updates if there are
* locks on internal elements that are successfully moved */
- webdav_xml_doc_multistatus(r, pconf, ms); /* 207 Multi-status */
+ webdav_xml_doc_multistatus(r, pconf); /* 207 Multi-status */
}
- chunk_buffer_release(ms);
/* invalidate stat cache of src if MOVE, whether or not successful */
if (r->http_method == HTTP_METHOD_MOVE)
stat_cache_delete_dir(CONST_BUF_LEN(&r->physical.path));
@@ -5212,6 +5231,7 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
"no namespace for: %s", prop->name);
if (!ms) ms = chunk_buffer_acquire(); /* Unprocessable Entity */
webdav_xml_propstat_status(ms, "", (char *)prop->name, 422);
+ webdav_double_buffer(r, ms);
continue;
}
@@ -5230,6 +5250,7 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
if (!ms) ms = chunk_buffer_acquire();
webdav_xml_propstat_protected(ms, (char *)prop->name,
namelen, 403); /* Forbidden */
+ webdav_double_buffer(r, ms);
continue;
}
}
@@ -5264,8 +5285,8 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
/* workaround Microsoft-WebDAV-MiniRedir bug; 204 not handled */
/* 200 without response body or 204 both incorrectly interpreted
* as 507 Insufficient Storage by Microsoft-WebDAV-MiniRedir. */
- ms = chunk_buffer_acquire(); /* 207 Multi-status */
- webdav_xml_response_status(ms, &r->physical.path, 200);
+ ms = chunk_buffer_acquire(); /* 207 Multi-status */ /*(flag)*/
+ webdav_xml_response_status(r, &r->physical.path, 200);
}
}
if (NULL == ms)
@@ -5289,6 +5310,7 @@ mod_webdav_proppatch (request_st * const r, const plugin_config * const pconf)
struct webdav_conflicting_lock_st {
webdav_lockdata *lockdata;
buffer *b;
+ request_st *r;
};
@@ -5301,8 +5323,10 @@ webdav_conflicting_lock_cb (void * const vdata,
struct webdav_conflicting_lock_st * const cbdata =
(struct webdav_conflicting_lock_st *)vdata;
if (lockdata->lockscope->used == sizeof("exclusive")
- || cbdata->lockdata->lockscope->used == sizeof("exclusive"))
+ || cbdata->lockdata->lockscope->used == sizeof("exclusive")) {
webdav_xml_href(cbdata->b, &lockdata->lockroot);
+ webdav_double_buffer(cbdata->r, cbdata->b);
+ }
}
@@ -5471,10 +5495,11 @@ mod_webdav_lock (request_st * const r, const plugin_config * const pconf)
struct webdav_conflicting_lock_st cbdata;
cbdata.lockdata = &lockdata;
cbdata.b = chunk_buffer_acquire();
+ cbdata.r = r;
webdav_lock_activelocks(pconf, &lockdata.lockroot,
(0 == lockdata.depth ? 1 : -1),
webdav_conflicting_lock_cb, &cbdata);
- if (0 != cbdata.b->used) {
+ if (0 != cbdata.b->used || !chunkqueue_is_empty(&r->write_queue)) {
/* 423 Locked */
webdav_xml_doc_error_no_conflicting_lock(r, cbdata.b);
chunk_buffer_release(cbdata.b);