Adapt make_safe_uri() to work in-place.

We're currently failing some tests.
This commit is contained in:
Emil Mikulic 2006-12-13 08:30:21 +00:00
parent 97ce3cb5d0
commit 4b90b875c6
1 changed files with 52 additions and 59 deletions

View File

@ -520,31 +520,38 @@ static void consolidate_slashes(char *s)
/* --------------------------------------------------------------------------- /* ---------------------------------------------------------------------------
* Resolve /./ and /../ in a URI, returning a new, safe URI, or NULL if the * Resolve /./ and /../ in a URI, in-place. Returns NULL if the URI is
* URI is invalid/unsafe. Returned buffer needs to be deallocated. * invalid/unsafe, or the original buffer if successful.
*/ */
static char *make_safe_uri(char *uri) static char *make_safe_uri(char *uri)
{ {
char **elem, *out; struct {
unsigned int slashes = 0, elements = 0; char *start;
size_t len;
} *chunks;
unsigned int num_slashes, num_chunks;
size_t urilen, i, j, pos; size_t urilen, i, j, pos;
int ends_in_slash;
assert(uri != NULL); assert(uri != NULL);
if (uri[0] != '/') return NULL; if (uri[0] != '/') return NULL;
consolidate_slashes(uri); consolidate_slashes(uri);
urilen = strlen(uri); urilen = strlen(uri);
if (urilen > 0)
ends_in_slash = (uri[urilen-1] == '/');
else
ends_in_slash = 1;
/* count the slashes */ /* count the slashes */
for (i=0, slashes=0; i<urilen; i++) for (i=0, num_slashes=0; i<urilen; i++)
if (uri[i] == '/') slashes++; if (uri[i] == '/') num_slashes++;
/* make an array for the URI elements */ /* make an array for the URI elements */
elem = xmalloc(sizeof(char*) * slashes); chunks = xmalloc(sizeof(*chunks) * num_slashes);
for (i=0; i<slashes; i++) elem[i] = NULL;
/* split by slashes and build elem[] array */ /* split by slashes and build chunks array */
for (i=1; i<urilen;) num_chunks = 0;
{ for (i=1; i<urilen;) {
/* look for the next slash */ /* look for the next slash */
for (j=i; j<urilen && uri[j] != '/'; j++) for (j=i; j<urilen && uri[j] != '/'; j++)
; ;
@ -552,53 +559,42 @@ static char *make_safe_uri(char *uri)
/* process uri[i,j) */ /* process uri[i,j) */
if ((j == i+1) && (uri[i] == '.')) if ((j == i+1) && (uri[i] == '.'))
/* "." */; /* "." */;
else if ((j == i+2) && (uri[i] == '.') && (uri[i+1] == '.')) else if ((j == i+2) && (uri[i] == '.') && (uri[i+1] == '.')) {
{
/* ".." */ /* ".." */
if (elements == 0) if (num_chunks == 0) {
{ /* unsafe string so free chunks */
/* unsafe string so free elem[]; all its elements are free at free(chunks);
* this point. return (NULL);
*/ } else
free(elem); num_chunks--;
return NULL; } else {
} chunks[num_chunks].start = uri+i;
else chunks[num_chunks].len = j-i;
{ num_chunks++;
elements--;
free(elem[elements]);
}
} }
else elem[elements++] = split_string(uri, i, j);
i = j + 1; /* uri[j] is a slash - move along one */ i = j + 1; /* uri[j] is a slash - move along one */
} }
/* reassemble */ /* reassemble in-place */
out = xmalloc(urilen+1); /* it won't expand */
pos = 0; pos = 0;
for (i=0; i<elements; i++) for (i=0; i<num_chunks; i++) {
{
size_t delta = strlen(elem[i]);
assert(pos <= urilen); assert(pos <= urilen);
out[pos++] = '/'; uri[pos++] = '/';
assert(pos+delta <= urilen); assert(pos + chunks[i].len <= urilen);
memcpy(out+pos, elem[i], delta); assert(uri + pos <= chunks[i].start);
free(elem[i]);
pos += delta; if (uri+pos < chunks[i].start)
memmove(uri+pos, chunks[i].start, chunks[i].len);
pos += chunks[i].len;
} }
free(elem); free(chunks);
if ((elements == 0) || (uri[urilen-1] == '/')) out[pos++] = '/'; if (ends_in_slash) uri[pos++] = '/';
assert(pos <= urilen); assert(pos <= urilen);
out[pos] = '\0'; uri[pos] = '\0';
return uri;
/* shorten buffer if necessary */
if (pos != urilen) out = xrealloc(out, strlen(out)+1);
return out;
} }
@ -1761,7 +1757,7 @@ static void generate_dir_listing(struct connection *conn, const char *path)
*/ */
static void process_get(struct connection *conn) static void process_get(struct connection *conn)
{ {
char *decoded_url, *safe_url, *target, *if_mod_since; char *decoded_url, *target, *if_mod_since;
char date[DATE_LEN], lastmod[DATE_LEN]; char date[DATE_LEN], lastmod[DATE_LEN];
const char *mimetype = NULL; const char *mimetype = NULL;
struct stat filestat; struct stat filestat;
@ -1770,36 +1766,33 @@ static void process_get(struct connection *conn)
decoded_url = urldecode(conn->uri); decoded_url = urldecode(conn->uri);
/* make sure it's safe */ /* make sure it's safe */
safe_url = make_safe_uri(decoded_url); if (make_safe_uri(decoded_url) == NULL) {
free(decoded_url);
if (safe_url == NULL)
{
default_reply(conn, 400, "Bad Request", default_reply(conn, 400, "Bad Request",
"You requested an invalid URI: %s", conn->uri); "You requested an invalid URI: %s", conn->uri);
return; return;
} }
/* does it end in a slash? serve up url/index_name */ /* does it end in a slash? serve up url/index_name */
if (safe_url[strlen(safe_url)-1] == '/') if (decoded_url[strlen(decoded_url)-1] == '/')
{ {
xasprintf(&target, "%s%s%s", wwwroot, safe_url, index_name); xasprintf(&target, "%s%s%s", wwwroot, decoded_url, index_name);
if (!file_exists(target)) if (!file_exists(target))
{ {
free(target); free(target);
xasprintf(&target, "%s%s", wwwroot, safe_url); xasprintf(&target, "%s%s", wwwroot, decoded_url);
generate_dir_listing(conn, target); generate_dir_listing(conn, target);
free(target); free(target);
free(safe_url); free(decoded_url);
return; return;
} }
mimetype = uri_content_type(index_name); mimetype = uri_content_type(index_name);
} }
else /* points to a file */ else /* points to a file */
{ {
xasprintf(&target, "%s%s", wwwroot, safe_url); xasprintf(&target, "%s%s", wwwroot, decoded_url);
mimetype = uri_content_type(safe_url); mimetype = uri_content_type(decoded_url);
} }
free(safe_url); free(decoded_url);
if (debug) printf("uri=%s, target=%s, content-type=%s\n", if (debug) printf("uri=%s, target=%s, content-type=%s\n",
conn->uri, target, mimetype); conn->uri, target, mimetype);
@ -2076,7 +2069,7 @@ static void poll_send_header(struct connection *conn)
/* --------------------------------------------------------------------------- /* ---------------------------------------------------------------------------
* Send chunk on socket <s> from FILE *fp, starting at <ofs> and of size * Send chunk on socket <s> from FILE *fp, starting at <ofs> and of size
* <size>. Use sendfile() is possible since it's zero-copy on some platforms. * <size>. Use sendfile() if possible since it's zero-copy on some platforms.
* Returns the number of bytes sent, 0 on closure, -1 if send() failed, -2 if * Returns the number of bytes sent, 0 on closure, -1 if send() failed, -2 if
* read error. * read error.
*/ */