1
0
mirror of https://github.com/moparisthebest/curl synced 2025-02-28 09:21:50 -05:00

URL sanitize: reject URLs containing bad data

Protocols (IMAP, POP3 and SMTP) that use the path part of a URL in a
decoded manner now use the new Curl_urldecode() function to reject URLs
with embedded control codes (anything that is or decodes to a byte value
less than 32).

URLs containing such codes could easily otherwise be used to do harm and
allow users to do unintended actions with otherwise innocent tools and
applications. Like for example using a URL like
pop3://pop3.example.com/1%0d%0aDELE%201 when the app wants a URL to get
a mail and instead this would delete one.

This flaw is considered a security vulnerability: CVE-2012-0036

Security advisory at: http://curl.haxx.se/docs/adv_20120124.html

Reported by: Dan Fandrich
This commit is contained in:
Daniel Stenberg 2011-12-23 13:24:16 +01:00
parent db1a856b4f
commit 75ca568fa1
5 changed files with 62 additions and 31 deletions

View File

@ -31,6 +31,7 @@
#include "urldata.h" #include "urldata.h"
#include "warnless.h" #include "warnless.h"
#include "non-ascii.h" #include "non-ascii.h"
#include "escape.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -84,7 +85,7 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength)
char *testing_ptr = NULL; char *testing_ptr = NULL;
unsigned char in; /* we need to treat the characters unsigned */ unsigned char in; /* we need to treat the characters unsigned */
size_t newlen = alloc; size_t newlen = alloc;
int strindex=0; size_t strindex=0;
size_t length; size_t length;
CURLcode res; CURLcode res;
@ -132,23 +133,29 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength)
} }
/* /*
* Unescapes the given URL escaped string of given length. Returns a * Curl_urldecode() URL decodes the given string.
* pointer to a malloced string with length given in *olen. *
* If length == 0, the length is assumed to be strlen(string). * Optionally detects control characters (byte codes lower than 32) in the
* If olen == NULL, no output length is stored. * data and rejects such data.
*
* Returns a pointer to a malloced string in *ostring with length given in
* *olen. If length == 0, the length is assumed to be strlen(string).
*
*/ */
char *curl_easy_unescape(CURL *handle, const char *string, int length, CURLcode Curl_urldecode(struct SessionHandle *data,
int *olen) const char *string, size_t length,
char **ostring, size_t *olen,
bool reject_ctrl)
{ {
int alloc = (length?length:(int)strlen(string))+1; size_t alloc = (length?length:strlen(string))+1;
char *ns = malloc(alloc); char *ns = malloc(alloc);
unsigned char in; unsigned char in;
int strindex=0; size_t strindex=0;
unsigned long hex; unsigned long hex;
CURLcode res; CURLcode res;
if(!ns) if(!ns)
return NULL; return CURLE_OUT_OF_MEMORY;
while(--alloc > 0) { while(--alloc > 0) {
in = *string; in = *string;
@ -164,16 +171,20 @@ char *curl_easy_unescape(CURL *handle, const char *string, int length,
in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */ in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
res = Curl_convert_from_network(handle, &in, 1); res = Curl_convert_from_network(data, &in, 1);
if(res) { if(res) {
/* Curl_convert_from_network calls failf if unsuccessful */ /* Curl_convert_from_network calls failf if unsuccessful */
free(ns); free(ns);
return NULL; return res;
} }
string+=2; string+=2;
alloc-=2; alloc-=2;
} }
if(reject_ctrl && (in < 0x20)) {
free(ns);
return CURLE_URL_MALFORMAT;
}
ns[strindex++] = in; ns[strindex++] = in;
string++; string++;
@ -183,7 +194,33 @@ char *curl_easy_unescape(CURL *handle, const char *string, int length,
if(olen) if(olen)
/* store output size */ /* store output size */
*olen = strindex; *olen = strindex;
return ns;
if(ostring)
/* store output string */
*ostring = ns;
return CURLE_OK;
}
/*
* Unescapes the given URL escaped string of given length. Returns a
* pointer to a malloced string with length given in *olen.
* If length == 0, the length is assumed to be strlen(string).
* If olen == NULL, no output length is stored.
*/
char *curl_easy_unescape(CURL *handle, const char *string, int length,
int *olen)
{
char *str = NULL;
size_t inputlen = length;
size_t outputlen;
CURLcode res = Curl_urldecode(handle, string, inputlen, &str, &outputlen,
FALSE);
if(res)
return NULL;
if(olen)
*olen = curlx_uztosi(outputlen);
return str;
} }
/* For operating systems/environments that use different malloc/free /* For operating systems/environments that use different malloc/free

View File

@ -1,5 +1,5 @@
#ifndef __ESCAPE_H #ifndef HEADER_CURL_ESCAPE_H
#define __ESCAPE_H #define HEADER_CURL_ESCAPE_H
/*************************************************************************** /***************************************************************************
* _ _ ____ _ * _ _ ____ _
@ -8,7 +8,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -25,5 +25,9 @@
/* Escape and unescape URL encoding in strings. The functions return a new /* Escape and unescape URL encoding in strings. The functions return a new
* allocated string or NULL if an error occurred. */ * allocated string or NULL if an error occurred. */
CURLcode Curl_urldecode(struct SessionHandle *data,
const char *string, size_t length,
char **ostring, size_t *olen,
bool reject_crlf);
#endif #endif

View File

@ -953,17 +953,12 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
struct imap_conn *imapc = &conn->proto.imapc; struct imap_conn *imapc = &conn->proto.imapc;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
const char *path = data->state.path; const char *path = data->state.path;
int len;
if(!*path) if(!*path)
path = "INBOX"; path = "INBOX";
/* url decode the path and use this mailbox */ /* url decode the path and use this mailbox */
imapc->mailbox = curl_easy_unescape(data, path, 0, &len); return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE);
if(!imapc->mailbox)
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
} }
/* call this when the DO phase has completed */ /* call this when the DO phase has completed */

View File

@ -914,11 +914,7 @@ static CURLcode pop3_parse_url_path(struct connectdata *conn)
const char *path = data->state.path; const char *path = data->state.path;
/* url decode the path and use this mailbox */ /* url decode the path and use this mailbox */
pop3c->mailbox = curl_easy_unescape(data, path, 0, NULL); return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE);
if(!pop3c->mailbox)
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
} }
/* call this when the DO phase has completed */ /* call this when the DO phase has completed */

View File

@ -1244,7 +1244,6 @@ static CURLcode smtp_connect(struct connectdata *conn,
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
struct pingpong *pp = &smtpc->pp; struct pingpong *pp = &smtpc->pp;
const char *path = conn->data->state.path; const char *path = conn->data->state.path;
int len;
char localhost[HOSTNAME_MAX + 1]; char localhost[HOSTNAME_MAX + 1];
*done = FALSE; /* default to not done yet */ *done = FALSE; /* default to not done yet */
@ -1316,9 +1315,9 @@ static CURLcode smtp_connect(struct connectdata *conn,
} }
/* url decode the path and use it as domain with EHLO */ /* url decode the path and use it as domain with EHLO */
smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len); result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
if(!smtpc->domain) if(result)
return CURLE_OUT_OF_MEMORY; return result;
/* When we connect, we start in the state where we await the server greeting /* When we connect, we start in the state where we await the server greeting
*/ */