openssl: guard against OOM on context creation
[platform/upstream/curl.git] / lib / http_proxy.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #include "http_proxy.h"
26
27 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
28
29 #include <curl/curl.h>
30 #include "sendf.h"
31 #include "http.h"
32 #include "url.h"
33 #include "select.h"
34 #include "progress.h"
35 #include "non-ascii.h"
36 #include "connect.h"
37 #include "curlx.h"
38 #include "vtls/vtls.h"
39
40 /* The last 3 #include files should be in this order */
41 #include "curl_printf.h"
42 #include "curl_memory.h"
43 #include "memdebug.h"
44
45 /*
46  * Perform SSL initialization for HTTPS proxy.  Sets
47  * proxy_ssl_connected connection bit when complete.  Can be
48  * called multiple times.
49  */
50 static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
51 {
52 #ifdef USE_SSL
53   CURLcode result = CURLE_OK;
54   DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
55   if(!conn->bits.proxy_ssl_connected[sockindex]) {
56     /* perform SSL initialization for this socket */
57     result =
58       Curl_ssl_connect_nonblocking(conn, sockindex,
59                                    &conn->bits.proxy_ssl_connected[sockindex]);
60     if(result)
61       /* a failed connection is marked for closure to prevent (bad) re-use or
62          similar */
63       connclose(conn, "TLS handshake failed");
64   }
65   return result;
66 #else
67   (void) conn;
68   (void) sockindex;
69   return CURLE_NOT_BUILT_IN;
70 #endif
71 }
72
73 CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
74 {
75   struct Curl_easy *data = conn->data;
76   if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
77     const CURLcode result = https_proxy_connect(conn, sockindex);
78     if(result)
79       return result;
80     if(!conn->bits.proxy_ssl_connected[sockindex])
81       return result; /* wait for HTTPS proxy SSL initialization to complete */
82   }
83
84   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
85 #ifndef CURL_DISABLE_PROXY
86     /* for [protocol] tunneled through HTTP proxy */
87     struct HTTP http_proxy;
88     void *prot_save;
89     const char *hostname;
90     int remote_port;
91     CURLcode result;
92
93     /* BLOCKING */
94     /* We want "seamless" operations through HTTP proxy tunnel */
95
96     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
97      * member conn->proto.http; we want [protocol] through HTTP and we have
98      * to change the member temporarily for connecting to the HTTP
99      * proxy. After Curl_proxyCONNECT we have to set back the member to the
100      * original pointer
101      *
102      * This function might be called several times in the multi interface case
103      * if the proxy's CONNECT response is not instant.
104      */
105     prot_save = conn->data->req.protop;
106     memset(&http_proxy, 0, sizeof(http_proxy));
107     conn->data->req.protop = &http_proxy;
108     connkeep(conn, "HTTP proxy CONNECT");
109
110     /* for the secondary socket (FTP), use the "connect to host"
111      * but ignore the "connect to port" (use the secondary port)
112      */
113
114     if(conn->bits.conn_to_host)
115       hostname = conn->conn_to_host.name;
116     else if(sockindex == SECONDARYSOCKET)
117       hostname = conn->secondaryhostname;
118     else
119       hostname = conn->host.name;
120
121     if(sockindex == SECONDARYSOCKET)
122       remote_port = conn->secondary_port;
123     else if(conn->bits.conn_to_port)
124       remote_port = conn->conn_to_port;
125     else
126       remote_port = conn->remote_port;
127     result = Curl_proxyCONNECT(conn, sockindex, hostname, remote_port);
128     conn->data->req.protop = prot_save;
129     if(CURLE_OK != result)
130       return result;
131     Curl_safefree(data->state.aptr.proxyuserpwd);
132 #else
133     return CURLE_NOT_BUILT_IN;
134 #endif
135   }
136   /* no HTTP tunnel proxy, just return */
137   return CURLE_OK;
138 }
139
140 bool Curl_connect_complete(struct connectdata *conn)
141 {
142   return !conn->connect_state ||
143     (conn->connect_state->tunnel_state == TUNNEL_COMPLETE);
144 }
145
146 bool Curl_connect_ongoing(struct connectdata *conn)
147 {
148   return conn->connect_state &&
149     (conn->connect_state->tunnel_state != TUNNEL_COMPLETE);
150 }
151
152 static CURLcode connect_init(struct connectdata *conn, bool reinit)
153 {
154   struct http_connect_state *s;
155   if(!reinit) {
156     DEBUGASSERT(!conn->connect_state);
157     s = calloc(1, sizeof(struct http_connect_state));
158     if(!s)
159       return CURLE_OUT_OF_MEMORY;
160     infof(conn->data, "allocate connect buffer!\n");
161     conn->connect_state = s;
162     Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
163   }
164   else {
165     DEBUGASSERT(conn->connect_state);
166     s = conn->connect_state;
167     Curl_dyn_reset(&s->rcvbuf);
168   }
169   s->tunnel_state = TUNNEL_INIT;
170   s->keepon = TRUE;
171   s->cl = 0;
172   s->close_connection = FALSE;
173   return CURLE_OK;
174 }
175
176 static void connect_done(struct connectdata *conn)
177 {
178   struct http_connect_state *s = conn->connect_state;
179   s->tunnel_state = TUNNEL_COMPLETE;
180   Curl_dyn_free(&s->rcvbuf);
181   infof(conn->data, "CONNECT phase completed!\n");
182 }
183
184 static CURLcode CONNECT(struct connectdata *conn,
185                         int sockindex,
186                         const char *hostname,
187                         int remote_port)
188 {
189   int subversion = 0;
190   struct Curl_easy *data = conn->data;
191   struct SingleRequest *k = &data->req;
192   CURLcode result;
193   curl_socket_t tunnelsocket = conn->sock[sockindex];
194   struct http_connect_state *s = conn->connect_state;
195   char *linep;
196   size_t perline;
197
198 #define SELECT_OK      0
199 #define SELECT_ERROR   1
200
201   if(Curl_connect_complete(conn))
202     return CURLE_OK; /* CONNECT is already completed */
203
204   conn->bits.proxy_connect_closed = FALSE;
205
206   do {
207     timediff_t check;
208     if(TUNNEL_INIT == s->tunnel_state) {
209       /* BEGIN CONNECT PHASE */
210       char *host_port;
211       struct dynbuf req;
212
213       infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
214             hostname, remote_port);
215
216         /* This only happens if we've looped here due to authentication
217            reasons, and we don't really use the newly cloned URL here
218            then. Just free() it. */
219       free(data->req.newurl);
220       data->req.newurl = NULL;
221
222       host_port = aprintf("%s:%d", hostname, remote_port);
223       if(!host_port)
224         return CURLE_OUT_OF_MEMORY;
225
226       /* initialize a dynamic send-buffer */
227       Curl_dyn_init(&req, DYN_HTTP_REQUEST);
228
229       /* Setup the proxy-authorization header, if any */
230       result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
231
232       free(host_port);
233
234       if(!result) {
235         char *host = NULL;
236         const char *proxyconn = "";
237         const char *useragent = "";
238         const char *httpv =
239           (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
240         bool ipv6_ip = conn->bits.ipv6_ip;
241         char *hostheader;
242
243         /* the hostname may be different */
244         if(hostname != conn->host.name)
245           ipv6_ip = (strchr(hostname, ':') != NULL);
246         hostheader = /* host:port with IPv6 support */
247           aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
248                   remote_port);
249         if(!hostheader) {
250           Curl_dyn_free(&req);
251           return CURLE_OUT_OF_MEMORY;
252         }
253
254         if(!Curl_checkProxyheaders(conn, "Host")) {
255           host = aprintf("Host: %s\r\n", hostheader);
256           if(!host) {
257             free(hostheader);
258             Curl_dyn_free(&req);
259             return CURLE_OUT_OF_MEMORY;
260           }
261         }
262         if(!Curl_checkProxyheaders(conn, "Proxy-Connection"))
263           proxyconn = "Proxy-Connection: Keep-Alive\r\n";
264
265         if(!Curl_checkProxyheaders(conn, "User-Agent") &&
266            data->set.str[STRING_USERAGENT])
267           useragent = data->state.aptr.uagent;
268
269         result =
270           Curl_dyn_addf(&req,
271                         "CONNECT %s HTTP/%s\r\n"
272                         "%s"  /* Host: */
273                         "%s"  /* Proxy-Authorization */
274                         "%s"  /* User-Agent */
275                         "%s", /* Proxy-Connection */
276                         hostheader,
277                         httpv,
278                         host?host:"",
279                         data->state.aptr.proxyuserpwd?
280                         data->state.aptr.proxyuserpwd:"",
281                         useragent,
282                         proxyconn);
283
284         if(host)
285           free(host);
286         free(hostheader);
287
288         if(!result)
289           result = Curl_add_custom_headers(conn, TRUE, &req);
290
291         if(!result)
292           /* CRLF terminate the request */
293           result = Curl_dyn_add(&req, "\r\n");
294
295         if(!result) {
296           /* Send the connect request to the proxy */
297           /* BLOCKING */
298           result = Curl_buffer_send(&req, conn, &data->info.request_size, 0,
299                                     sockindex);
300         }
301         if(result)
302           failf(data, "Failed sending CONNECT to proxy");
303       }
304
305       Curl_dyn_free(&req);
306       if(result)
307         return result;
308
309       s->tunnel_state = TUNNEL_CONNECT;
310     } /* END CONNECT PHASE */
311
312     check = Curl_timeleft(data, NULL, TRUE);
313     if(check <= 0) {
314       failf(data, "Proxy CONNECT aborted due to timeout");
315       return CURLE_OPERATION_TIMEDOUT;
316     }
317
318     if(!Curl_conn_data_pending(conn, sockindex))
319       /* return so we'll be called again polling-style */
320       return CURLE_OK;
321
322     /* at this point, the tunnel_connecting phase is over. */
323
324     { /* READING RESPONSE PHASE */
325       int error = SELECT_OK;
326
327       while(s->keepon) {
328         ssize_t gotbytes;
329         char byte;
330
331         /* Read one byte at a time to avoid a race condition. Wait at most one
332            second before looping to ensure continuous pgrsUpdates. */
333         result = Curl_read(conn, tunnelsocket, &byte, 1, &gotbytes);
334         if(result == CURLE_AGAIN)
335           /* socket buffer drained, return */
336           return CURLE_OK;
337
338         if(Curl_pgrsUpdate(conn))
339           return CURLE_ABORTED_BY_CALLBACK;
340
341         if(result) {
342           s->keepon = FALSE;
343           break;
344         }
345         else if(gotbytes <= 0) {
346           if(data->set.proxyauth && data->state.authproxy.avail) {
347             /* proxy auth was requested and there was proxy auth available,
348                then deem this as "mere" proxy disconnect */
349             conn->bits.proxy_connect_closed = TRUE;
350             infof(data, "Proxy CONNECT connection closed\n");
351           }
352           else {
353             error = SELECT_ERROR;
354             failf(data, "Proxy CONNECT aborted");
355           }
356           s->keepon = FALSE;
357           break;
358         }
359
360         if(s->keepon > TRUE) {
361           /* This means we are currently ignoring a response-body */
362
363           if(s->cl) {
364             /* A Content-Length based body: simply count down the counter
365                and make sure to break out of the loop when we're done! */
366             s->cl--;
367             if(s->cl <= 0) {
368               s->keepon = FALSE;
369               s->tunnel_state = TUNNEL_COMPLETE;
370               break;
371             }
372           }
373           else {
374             /* chunked-encoded body, so we need to do the chunked dance
375                properly to know when the end of the body is reached */
376             CHUNKcode r;
377             CURLcode extra;
378             ssize_t tookcareof = 0;
379
380             /* now parse the chunked piece of data so that we can
381                properly tell when the stream ends */
382             r = Curl_httpchunk_read(conn, &byte, 1, &tookcareof, &extra);
383             if(r == CHUNKE_STOP) {
384               /* we're done reading chunks! */
385               infof(data, "chunk reading DONE\n");
386               s->keepon = FALSE;
387               /* we did the full CONNECT treatment, go COMPLETE */
388               s->tunnel_state = TUNNEL_COMPLETE;
389             }
390           }
391           continue;
392         }
393
394         if(Curl_dyn_addn(&s->rcvbuf, &byte, 1)) {
395           failf(data, "CONNECT response too large!");
396           return CURLE_RECV_ERROR;
397         }
398
399         /* if this is not the end of a header line then continue */
400         if(byte != 0x0a)
401           continue;
402
403         linep = Curl_dyn_ptr(&s->rcvbuf);
404         perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */
405
406         /* convert from the network encoding */
407         result = Curl_convert_from_network(data, linep, perline);
408         /* Curl_convert_from_network calls failf if unsuccessful */
409         if(result)
410           return result;
411
412         /* output debug if that is requested */
413         if(data->set.verbose)
414           Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
415
416         if(!data->set.suppress_connect_headers) {
417           /* send the header to the callback */
418           int writetype = CLIENTWRITE_HEADER;
419           if(data->set.include_header)
420             writetype |= CLIENTWRITE_BODY;
421
422           result = Curl_client_write(conn, writetype, linep, perline);
423           if(result)
424             return result;
425         }
426
427         data->info.header_size += (long)perline;
428
429         /* Newlines are CRLF, so the CR is ignored as the line isn't
430            really terminated until the LF comes. Treat a following CR
431            as end-of-headers as well.*/
432
433         if(('\r' == linep[0]) ||
434            ('\n' == linep[0])) {
435           /* end of response-headers from the proxy */
436
437           if((407 == k->httpcode) && !data->state.authproblem) {
438             /* If we get a 407 response code with content length
439                when we have no auth problem, we must ignore the
440                whole response-body */
441             s->keepon = 2;
442
443             if(s->cl) {
444               infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
445                     " bytes of response-body\n", s->cl);
446             }
447             else if(s->chunked_encoding) {
448               CHUNKcode r;
449               CURLcode extra;
450
451               infof(data, "Ignore chunked response-body\n");
452
453               /* We set ignorebody true here since the chunked decoder
454                  function will acknowledge that. Pay attention so that this is
455                  cleared again when this function returns! */
456               k->ignorebody = TRUE;
457
458               if(linep[1] == '\n')
459                 /* this can only be a LF if the letter at index 0 was a CR */
460                 linep++;
461
462               /* now parse the chunked piece of data so that we can properly
463                  tell when the stream ends */
464               r = Curl_httpchunk_read(conn, linep + 1, 1, &gotbytes,
465                                       &extra);
466               if(r == CHUNKE_STOP) {
467                 /* we're done reading chunks! */
468                 infof(data, "chunk reading DONE\n");
469                 s->keepon = FALSE;
470                 /* we did the full CONNECT treatment, go to COMPLETE */
471                 s->tunnel_state = TUNNEL_COMPLETE;
472               }
473             }
474             else {
475               /* without content-length or chunked encoding, we
476                  can't keep the connection alive since the close is
477                  the end signal so we bail out at once instead */
478               s->keepon = FALSE;
479             }
480           }
481           else
482             s->keepon = FALSE;
483           if(!s->cl)
484             /* we did the full CONNECT treatment, go to COMPLETE */
485             s->tunnel_state = TUNNEL_COMPLETE;
486           continue;
487         }
488
489         if((checkprefix("WWW-Authenticate:", linep) &&
490             (401 == k->httpcode)) ||
491            (checkprefix("Proxy-authenticate:", linep) &&
492             (407 == k->httpcode))) {
493
494           bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
495           char *auth = Curl_copy_header_value(linep);
496           if(!auth)
497             return CURLE_OUT_OF_MEMORY;
498
499           result = Curl_http_input_auth(conn, proxy, auth);
500
501           free(auth);
502
503           if(result)
504             return result;
505         }
506         else if(checkprefix("Content-Length:", linep)) {
507           if(k->httpcode/100 == 2) {
508             /* A client MUST ignore any Content-Length or Transfer-Encoding
509                header fields received in a successful response to CONNECT.
510                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
511             infof(data, "Ignoring Content-Length in CONNECT %03d response\n",
512                   k->httpcode);
513           }
514           else {
515             (void)curlx_strtoofft(linep +
516                                   strlen("Content-Length:"), NULL, 10, &s->cl);
517           }
518         }
519         else if(Curl_compareheader(linep, "Connection:", "close"))
520           s->close_connection = TRUE;
521         else if(checkprefix("Transfer-Encoding:", linep)) {
522           if(k->httpcode/100 == 2) {
523             /* A client MUST ignore any Content-Length or Transfer-Encoding
524                header fields received in a successful response to CONNECT.
525                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
526             infof(data, "Ignoring Transfer-Encoding in "
527                   "CONNECT %03d response\n", k->httpcode);
528           }
529           else if(Curl_compareheader(linep,
530                                      "Transfer-Encoding:", "chunked")) {
531             infof(data, "CONNECT responded chunked\n");
532             s->chunked_encoding = TRUE;
533             /* init our chunky engine */
534             Curl_httpchunk_init(conn);
535           }
536         }
537         else if(Curl_compareheader(linep, "Proxy-Connection:", "close"))
538           s->close_connection = TRUE;
539         else if(2 == sscanf(linep, "HTTP/1.%d %d",
540                             &subversion,
541                             &k->httpcode)) {
542           /* store the HTTP code from the proxy */
543           data->info.httpproxycode = k->httpcode;
544         }
545
546         Curl_dyn_reset(&s->rcvbuf);
547       } /* while there's buffer left and loop is requested */
548
549       if(Curl_pgrsUpdate(conn))
550         return CURLE_ABORTED_BY_CALLBACK;
551
552       if(error)
553         return CURLE_RECV_ERROR;
554
555       if(data->info.httpproxycode/100 != 2) {
556         /* Deal with the possibly already received authenticate
557            headers. 'newurl' is set to a new URL if we must loop. */
558         result = Curl_http_auth_act(conn);
559         if(result)
560           return result;
561
562         if(conn->bits.close)
563           /* the connection has been marked for closure, most likely in the
564              Curl_http_auth_act() function and thus we can kill it at once
565              below */
566           s->close_connection = TRUE;
567       }
568
569       if(s->close_connection && data->req.newurl) {
570         /* Connection closed by server. Don't use it anymore */
571         Curl_closesocket(conn, conn->sock[sockindex]);
572         conn->sock[sockindex] = CURL_SOCKET_BAD;
573         break;
574       }
575     } /* END READING RESPONSE PHASE */
576
577     /* If we are supposed to continue and request a new URL, which basically
578      * means the HTTP authentication is still going on so if the tunnel
579      * is complete we start over in INIT state */
580     if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) {
581       connect_init(conn, TRUE); /* reinit */
582     }
583
584   } while(data->req.newurl);
585
586   if(data->info.httpproxycode/100 != 2) {
587     if(s->close_connection && data->req.newurl) {
588       conn->bits.proxy_connect_closed = TRUE;
589       infof(data, "Connect me again please\n");
590       connect_done(conn);
591     }
592     else {
593       free(data->req.newurl);
594       data->req.newurl = NULL;
595       /* failure, close this connection to avoid re-use */
596       streamclose(conn, "proxy CONNECT failure");
597       Curl_closesocket(conn, conn->sock[sockindex]);
598       conn->sock[sockindex] = CURL_SOCKET_BAD;
599     }
600
601     /* to back to init state */
602     s->tunnel_state = TUNNEL_INIT;
603
604     if(conn->bits.proxy_connect_closed)
605       /* this is not an error, just part of the connection negotiation */
606       return CURLE_OK;
607     Curl_dyn_free(&s->rcvbuf);
608     failf(data, "Received HTTP code %d from proxy after CONNECT",
609           data->req.httpcode);
610     return CURLE_RECV_ERROR;
611   }
612
613   s->tunnel_state = TUNNEL_COMPLETE;
614
615   /* If a proxy-authorization header was used for the proxy, then we should
616      make sure that it isn't accidentally used for the document request
617      after we've connected. So let's free and clear it here. */
618   Curl_safefree(data->state.aptr.proxyuserpwd);
619   data->state.aptr.proxyuserpwd = NULL;
620
621   data->state.authproxy.done = TRUE;
622   data->state.authproxy.multipass = FALSE;
623
624   infof(data, "Proxy replied %d to CONNECT request\n",
625         data->info.httpproxycode);
626   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
627   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
628                                          document request  */
629   Curl_dyn_free(&s->rcvbuf);
630   return CURLE_OK;
631 }
632
633 void Curl_connect_free(struct Curl_easy *data)
634 {
635   struct connectdata *conn = data->conn;
636   struct http_connect_state *s = conn->connect_state;
637   if(s) {
638     free(s);
639     conn->connect_state = NULL;
640   }
641 }
642
643 /*
644  * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
645  * function will issue the necessary commands to get a seamless tunnel through
646  * this proxy. After that, the socket can be used just as a normal socket.
647  */
648
649 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
650                            int sockindex,
651                            const char *hostname,
652                            int remote_port)
653 {
654   CURLcode result;
655   if(!conn->connect_state) {
656     result = connect_init(conn, FALSE);
657     if(result)
658       return result;
659   }
660   result = CONNECT(conn, sockindex, hostname, remote_port);
661
662   if(result || Curl_connect_complete(conn))
663     connect_done(conn);
664
665   return result;
666 }
667
668 #else
669 void Curl_connect_free(struct Curl_easy *data)
670 {
671   (void)data;
672 }
673
674 #endif /* CURL_DISABLE_PROXY */