Skip to content

Commit

Permalink
libmicrohttpd: sanitize the path before parsing it
Browse files Browse the repository at this point in the history
Fix a path traversal attack. Previous version had the possibility to
retrieve all files from the file system.
  • Loading branch information
lynxis committed May 6, 2020
1 parent 4bd2f00 commit 5ccffbe
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LDLIBS=-lmicrohttpd
STRIP=yes

NDS_OBJS=src/auth.o src/client_list.o src/commandline.o src/conf.o \
src/debug.o src/fw_iptables.o src/main.o src/http_microhttpd.o src/http_microhttpd_utils.o \
src/debug.o src/fw_iptables.o src/path.o src/main.o src/http_microhttpd.o src/http_microhttpd_utils.o \
src/ndsctl_thread.o src/safe.o src/tc.o src/util.o src/template.o

.PHONY: all clean install checkastyle fixstyle deb
Expand Down
7 changes: 6 additions & 1 deletion src/http_microhttpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "http_microhttpd_utils.h"
#include "fw_iptables.h"
#include "mimetypes.h"
#include "path.h"
#include "safe.h"
#include "template.h"
#include "util.h"
Expand Down Expand Up @@ -333,7 +334,7 @@ get_client_ip(char ip_addr[INET6_ADDRSTRLEN], struct MHD_Connection *connection)
int
libmicrohttpd_cb(void *cls,
struct MHD_Connection *connection,
const char *url,
const char *_url,
const char *method,
const char *version,
const char *upload_data, size_t *upload_data_size, void **ptr)
Expand All @@ -342,8 +343,12 @@ libmicrohttpd_cb(void *cls,
t_client *client;
char ip[INET6_ADDRSTRLEN+1];
char mac[18];
char url[PATH_MAX] = { 0 };
int rc = 0;

/* path sanitaze */
buffer_path_simplify(url, _url);

debug(LOG_DEBUG, "access: %s %s", method, url);

// only allow get
Expand Down
166 changes: 166 additions & 0 deletions src/path.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/* libmicrohttpd 1.4.55
*
* Copyright (c) 2004, Jan Kneschke, incremental
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - 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.
*
* - Neither the name of the 'incremental' nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

/* - special case: empty string returns empty string
* - on windows or cygwin: replace \ with /
* - strip leading spaces
* - prepends "/" if not present already
* - resolve "/../", "//" and "/./" the usual way:
* the first one removes a preceding component, the other two
* get compressed to "/".
* - "/." and "/.." at the end are similar, but always leave a trailing
* "/"
*
* /blah/.. gets /
* /blah/../foo gets /foo
* /abc/./xyz gets /abc/xyz
* /abc//xyz gets /abc/xyz
*
* NOTE: src and dest can point to the same buffer, in which case,
* the operation is performed in-place.
*/

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define force_assert(a, ...)

void buffer_path_simplify(char *dest, const char *src)
{
/* current character, the one before, and the one before that from input */
char c, pre1, pre2;
char *start, *slash, *out;
const char *walk;

force_assert(NULL != dest && NULL != src);

if (strlen(src) == 0) {
strcpy(dest, "");
return;
}

force_assert('\0' == src->ptr[src->used-1]);

#if defined(__WIN32) || defined(__CYGWIN__)
/* cygwin is treating \ and / the same, so we have to that too */
{
char *p;
for (p = src->ptr; *p; p++) {
if (*p == '\\') *p = '/';
}
}
#endif

walk = src;
start = dest;
out = dest;
slash = dest;

/* skip leading spaces */
while (*walk == ' ') {
walk++;
}
if (*walk == '.') {
if (walk[1] == '/' || walk[1] == '\0')
++walk;
else if (walk[1] == '.' && (walk[2] == '/' || walk[2] == '\0'))
walk+=2;
}

pre1 = 0;
c = *(walk++);

while (c != '\0') {
/* assert((src != dest || out <= walk) && slash <= out); */
/* the following comments about out and walk are only interesting if
* src == dest; otherwise the memory areas don't overlap anyway.
*/
pre2 = pre1;
pre1 = c;

/* possibly: out == walk - need to read first */
c = *walk;
*out = pre1;

out++;
walk++;
/* (out <= walk) still true; also now (slash < out) */

if (c == '/' || c == '\0') {
const size_t toklen = out - slash;
if (toklen == 3 && pre2 == '.' && pre1 == '.' && *slash == '/') {
/* "/../" or ("/.." at end of string) */
out = slash;
/* if there is something before "/..", there is at least one
* component, which needs to be removed */
if (out > start) {
out--;
while (out > start && *out != '/') out--;
}

/* don't kill trailing '/' at end of path */
if (c == '\0') out++;
/* slash < out before, so out_new <= slash + 1 <= out_before <= walk */
} else if (toklen == 1 || (pre2 == '/' && pre1 == '.')) {
/* "//" or "/./" or (("/" or "/.") at end of string) */
out = slash;
/* don't kill trailing '/' at end of path */
if (c == '\0') out++;
/* slash < out before, so out_new <= slash + 1 <= out_before <= walk */
}

slash = out;
}
}

dest[out - start] = '\0';
}

#ifdef _TESTS
int _buffer_path_simplify(char *input)
{
char dest[4096] = { 0 };
buffer_path_simplify(&dest[0], input);
printf("'%s' = '%s'\n", dest, input);
return 1;
}

int test_buffer_path_simplify()
{
_buffer_path_simplify("");
_buffer_path_simplify("/");
_buffer_path_simplify("/../");
return 1;
}
#endif /* _TESTS */
2 changes: 2 additions & 0 deletions src/path.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

void buffer_path_simplify(char *dest, const char *src);

0 comments on commit 5ccffbe

Please sign in to comment.