Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
  • Loading branch information...
commit 75ca568fa1c19de4c5358fed246686de8467c238 1 parent db1a856
Daniel Stenberg authored December 23, 2011
63  lib/escape.c
@@ -31,6 +31,7 @@
31 31
 #include "urldata.h"
32 32
 #include "warnless.h"
33 33
 #include "non-ascii.h"
  34
+#include "escape.h"
34 35
 
35 36
 #define _MPRINTF_REPLACE /* use our functions only */
36 37
 #include <curl/mprintf.h>
@@ -84,7 +85,7 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength)
84 85
   char *testing_ptr = NULL;
85 86
   unsigned char in; /* we need to treat the characters unsigned */
86 87
   size_t newlen = alloc;
87  
-  int strindex=0;
  88
+  size_t strindex=0;
88 89
   size_t length;
89 90
   CURLcode res;
90 91
 
@@ -132,23 +133,29 @@ char *curl_easy_escape(CURL *handle, const char *string, int inlength)
132 133
 }
133 134
 
134 135
 /*
135  
- * Unescapes the given URL escaped string of given length. Returns a
136  
- * pointer to a malloced string with length given in *olen.
137  
- * If length == 0, the length is assumed to be strlen(string).
138  
- * If olen == NULL, no output length is stored.
  136
+ * Curl_urldecode() URL decodes the given string.
  137
+ *
  138
+ * Optionally detects control characters (byte codes lower than 32) in the
  139
+ * data and rejects such data.
  140
+ *
  141
+ * Returns a pointer to a malloced string in *ostring with length given in
  142
+ * *olen. If length == 0, the length is assumed to be strlen(string).
  143
+ *
139 144
  */
140  
-char *curl_easy_unescape(CURL *handle, const char *string, int length,
141  
-                         int *olen)
  145
+CURLcode Curl_urldecode(struct SessionHandle *data,
  146
+                        const char *string, size_t length,
  147
+                        char **ostring, size_t *olen,
  148
+                        bool reject_ctrl)
142 149
 {
143  
-  int alloc = (length?length:(int)strlen(string))+1;
  150
+  size_t alloc = (length?length:strlen(string))+1;
144 151
   char *ns = malloc(alloc);
145 152
   unsigned char in;
146  
-  int strindex=0;
  153
+  size_t strindex=0;
147 154
   unsigned long hex;
148 155
   CURLcode res;
149 156
 
150 157
   if(!ns)
151  
-    return NULL;
  158
+    return CURLE_OUT_OF_MEMORY;
152 159
 
153 160
   while(--alloc > 0) {
154 161
     in = *string;
@@ -164,16 +171,20 @@ char *curl_easy_unescape(CURL *handle, const char *string, int length,
164 171
 
165 172
       in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
166 173
 
167  
-      res = Curl_convert_from_network(handle, &in, 1);
  174
+      res = Curl_convert_from_network(data, &in, 1);
168 175
       if(res) {
169 176
         /* Curl_convert_from_network calls failf if unsuccessful */
170 177
         free(ns);
171  
-        return NULL;
  178
+        return res;
172 179
       }
173 180
 
174 181
       string+=2;
175 182
       alloc-=2;
176 183
     }
  184
+    if(reject_ctrl && (in < 0x20)) {
  185
+      free(ns);
  186
+      return CURLE_URL_MALFORMAT;
  187
+    }
177 188
 
178 189
     ns[strindex++] = in;
179 190
     string++;
@@ -183,7 +194,33 @@ char *curl_easy_unescape(CURL *handle, const char *string, int length,
183 194
   if(olen)
184 195
     /* store output size */
185 196
     *olen = strindex;
186  
-  return ns;
  197
+
  198
+  if(ostring)
  199
+    /* store output string */
  200
+    *ostring = ns;
  201
+
  202
+  return CURLE_OK;
  203
+}
  204
+
  205
+/*
  206
+ * Unescapes the given URL escaped string of given length. Returns a
  207
+ * pointer to a malloced string with length given in *olen.
  208
+ * If length == 0, the length is assumed to be strlen(string).
  209
+ * If olen == NULL, no output length is stored.
  210
+ */
  211
+char *curl_easy_unescape(CURL *handle, const char *string, int length,
  212
+                         int *olen)
  213
+{
  214
+  char *str = NULL;
  215
+  size_t inputlen = length;
  216
+  size_t outputlen;
  217
+  CURLcode res = Curl_urldecode(handle, string, inputlen, &str, &outputlen,
  218
+                                FALSE);
  219
+  if(res)
  220
+    return NULL;
  221
+  if(olen)
  222
+    *olen = curlx_uztosi(outputlen);
  223
+  return str;
187 224
 }
188 225
 
189 226
 /* For operating systems/environments that use different malloc/free
10  lib/escape.h
... ...
@@ -1,5 +1,5 @@
1  
-#ifndef __ESCAPE_H
2  
-#define __ESCAPE_H
  1
+#ifndef HEADER_CURL_ESCAPE_H
  2
+#define HEADER_CURL_ESCAPE_H
3 3
 
4 4
 /***************************************************************************
5 5
  *                                  _   _ ____  _
@@ -8,7 +8,7 @@
8 8
  *                            | (__| |_| |  _ <| |___
9 9
  *                             \___|\___/|_| \_\_____|
10 10
  *
11  
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
  11
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
12 12
  *
13 13
  * This software is licensed as described in the file COPYING, which
14 14
  * you should have received as part of this distribution. The terms
@@ -25,5 +25,9 @@
25 25
 /* Escape and unescape URL encoding in strings. The functions return a new
26 26
  * allocated string or NULL if an error occurred.  */
27 27
 
  28
+CURLcode Curl_urldecode(struct SessionHandle *data,
  29
+                        const char *string, size_t length,
  30
+                        char **ostring, size_t *olen,
  31
+                        bool reject_crlf);
28 32
 
29 33
 #endif
7  lib/imap.c
@@ -953,17 +953,12 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
953 953
   struct imap_conn *imapc = &conn->proto.imapc;
954 954
   struct SessionHandle *data = conn->data;
955 955
   const char *path = data->state.path;
956  
-  int len;
957 956
 
958 957
   if(!*path)
959 958
     path = "INBOX";
960 959
 
961 960
   /* url decode the path and use this mailbox */
962  
-  imapc->mailbox = curl_easy_unescape(data, path, 0, &len);
963  
-  if(!imapc->mailbox)
964  
-    return CURLE_OUT_OF_MEMORY;
965  
-
966  
-  return CURLE_OK;
  961
+  return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE);
967 962
 }
968 963
 
969 964
 /* call this when the DO phase has completed */
6  lib/pop3.c
@@ -914,11 +914,7 @@ static CURLcode pop3_parse_url_path(struct connectdata *conn)
914 914
   const char *path = data->state.path;
915 915
 
916 916
   /* url decode the path and use this mailbox */
917  
-  pop3c->mailbox = curl_easy_unescape(data, path, 0, NULL);
918  
-  if(!pop3c->mailbox)
919  
-    return CURLE_OUT_OF_MEMORY;
920  
-
921  
-  return CURLE_OK;
  917
+  return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE);
922 918
 }
923 919
 
924 920
 /* call this when the DO phase has completed */
7  lib/smtp.c
@@ -1244,7 +1244,6 @@ static CURLcode smtp_connect(struct connectdata *conn,
1244 1244
   struct SessionHandle *data = conn->data;
1245 1245
   struct pingpong *pp = &smtpc->pp;
1246 1246
   const char *path = conn->data->state.path;
1247  
-  int len;
1248 1247
   char localhost[HOSTNAME_MAX + 1];
1249 1248
 
1250 1249
   *done = FALSE; /* default to not done yet */
@@ -1316,9 +1315,9 @@ static CURLcode smtp_connect(struct connectdata *conn,
1316 1315
   }
1317 1316
 
1318 1317
   /* url decode the path and use it as domain with EHLO */
1319  
-  smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len);
1320  
-  if(!smtpc->domain)
1321  
-    return CURLE_OUT_OF_MEMORY;
  1318
+  result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
  1319
+  if(result)
  1320
+    return result;
1322 1321
 
1323 1322
   /* When we connect, we start in the state where we await the server greeting
1324 1323
    */

0 notes on commit 75ca568

Please sign in to comment.
Something went wrong with that request. Please try again.