diff options
author | Glenn Strauss <gstrauss@gluelogic.com> | 2021-04-01 11:25:42 -0400 |
---|---|---|
committer | Glenn Strauss <gstrauss@gluelogic.com> | 2021-04-05 13:24:51 -0400 |
commit | 7283c435668258b25b6261d3018d1a8eabafcf82 (patch) | |
tree | ccc8acc2524fe8f0c6191dd5ec19825aa77f597f /src/mod_webdav.c | |
parent | c6362e127fb3bac68855e9c1deab1c928d01484e (diff) | |
download | lighttpd-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.c | 139 |
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); |