diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 5 | ||||
-rw-r--r-- | lib/Makefile.m32 | 2 | ||||
-rw-r--r-- | lib/README.ares | 69 | ||||
-rw-r--r-- | lib/README.curl_off_t | 68 | ||||
-rw-r--r-- | lib/README.curlx | 61 | ||||
-rw-r--r-- | lib/README.encoding | 60 | ||||
-rw-r--r-- | lib/README.hostip | 35 | ||||
-rw-r--r-- | lib/README.httpauth | 74 | ||||
-rw-r--r-- | lib/README.memoryleak | 55 | ||||
-rw-r--r-- | lib/README.multi_socket | 53 | ||||
-rw-r--r-- | lib/cookie.c | 9 | ||||
-rw-r--r-- | lib/ftp.c | 4 | ||||
-rw-r--r-- | lib/http.c | 26 | ||||
-rw-r--r-- | lib/http_proxy.c | 22 | ||||
-rw-r--r-- | lib/http_proxy.h | 7 | ||||
-rw-r--r-- | lib/multi.c | 8 | ||||
-rw-r--r-- | lib/rtsp.c | 9 | ||||
-rw-r--r-- | lib/security.c | 89 | ||||
-rw-r--r-- | lib/smb.c | 12 | ||||
-rw-r--r-- | lib/transfer.c | 3 | ||||
-rw-r--r-- | lib/urldata.h | 3 | ||||
-rw-r--r-- | lib/vtls/openssl.c | 21 | ||||
-rw-r--r-- | lib/vtls/schannel.c | 279 | ||||
-rw-r--r-- | lib/vtls/schannel.h | 1 |
24 files changed, 304 insertions, 671 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 1bef388df..a2c3dc56c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -21,9 +21,6 @@ ########################################################################### AUTOMAKE_OPTIONS = foreign nostdinc -DOCS = README.encoding README.memoryleak README.ares README.curlx \ - README.hostip README.multi_socket README.httpauth README.curl_off_t - CMAKE_DIST = CMakeLists.txt curl_config.h.cmake EXTRA_DIST = Makefile.b32 Makefile.m32 Makefile.vc6 config-win32.h \ @@ -31,7 +28,7 @@ EXTRA_DIST = Makefile.b32 Makefile.m32 Makefile.vc6 config-win32.h \ makefile.dj config-dos.h libcurl.plist libcurl.rc config-amigaos.h \ makefile.amiga Makefile.netware nwlib.c nwos.c config-win32ce.h \ config-os400.h setup-os400.h config-symbian.h Makefile.Watcom \ - config-tpf.h $(DOCS) mk-ca-bundle.pl mk-ca-bundle.vbs $(CMAKE_DIST) \ + config-tpf.h mk-ca-bundle.pl mk-ca-bundle.vbs $(CMAKE_DIST) \ firefox-db2pem.sh config-vxworks.h Makefile.vxworks checksrc.pl \ objnames-test08.sh objnames-test10.sh objnames.inc checksrc.whitelist diff --git a/lib/Makefile.m32 b/lib/Makefile.m32 index ee47d6708..5f88ab724 100644 --- a/lib/Makefile.m32 +++ b/lib/Makefile.m32 @@ -58,7 +58,7 @@ CC = $(CROSSPREFIX)gcc CFLAGS = $(CURL_CFLAG_EXTRAS) -g -O2 -Wall CFLAGS += -fno-strict-aliasing # comment LDFLAGS below to keep debug info -LDFLAGS = -s +LDFLAGS = $(CURL_LDFLAG_EXTRAS) -s AR = $(CROSSPREFIX)ar RANLIB = $(CROSSPREFIX)ranlib RC = $(CROSSPREFIX)windres diff --git a/lib/README.ares b/lib/README.ares deleted file mode 100644 index 8c77937eb..000000000 --- a/lib/README.ares +++ /dev/null @@ -1,69 +0,0 @@ - _ _ ____ _ - ___| | | | _ \| | - / __| | | | |_) | | - | (__| |_| | _ <| |___ - \___|\___/|_| \_\_____| - - How To Build libcurl to Use c-ares For Asynch Name Resolves - =========================================================== - -c-ares: - http://c-ares.haxx.se/ - -NOTE - The latest libcurl version requires c-ares 1.6.0 or later. - - Once upon the time libcurl built fine with the "original" ares. That is no - longer true. You need to use c-ares. - -Build c-ares -============ - -1. unpack the c-ares archive -2. cd c-ares-dir -3. ./configure -4. make -5. make install - -Build libcurl to use c-ares in the curl source tree -=================================================== - -1. name or symlink the c-ares source directory 'ares' in the curl source - directory -2. ./configure --enable-ares - - Optionally, you can point out the c-ares install tree root with the the - --enable-ares option. - -3. make - -Build libcurl to use an installed c-ares -======================================== - -1. ./configure --enable-ares=/path/to/ares/install -2. make - -c-ares on win32 -=============== -(description brought by Dominick Meglio) - -First I compiled c-ares. I changed the default C runtime library to be the -single-threaded rather than the multi-threaded (this seems to be required to -prevent linking errors later on). Then I simply build the areslib project (the -other projects adig/ahost seem to fail under MSVC). - -Next was libcurl. I opened lib/config-win32.h and I added a: - #define USE_ARES 1 - -Next thing I did was I added the path for the ares includes to the include -path, and the libares.lib to the libraries. - -Lastly, I also changed libcurl to be single-threaded rather than -multi-threaded, again this was to prevent some duplicate symbol errors. I'm -not sure why I needed to change everything to single-threaded, but when I -didn't I got redefinition errors for several CRT functions (malloc, stricmp, -etc.) - -I would have modified the MSVC++ project files, but I only have VC.NET and it -uses a different format than VC6.0 so I didn't want to go and change -everything and remove VC6.0 support from libcurl. diff --git a/lib/README.curl_off_t b/lib/README.curl_off_t deleted file mode 100644 index 923b2774c..000000000 --- a/lib/README.curl_off_t +++ /dev/null @@ -1,68 +0,0 @@ - - curl_off_t explained - ==================== - -curl_off_t is a data type provided by the external libcurl include headers. It -is the type meant to be used for the curl_easy_setopt() options that end with -LARGE. The type is 64bit large on most modern platforms. - -Transition from < 7.19.0 to >= 7.19.0 -------------------------------------- - -Applications that used libcurl before 7.19.0 that are rebuilt with a libcurl -that is 7.19.0 or later may or may not have to worry about anything of -this. We have made a significant effort to make the transition really seamless -and transparent. - -You have have to take notice if you are in one of the following situations: - -o Your app is using or will after the transition use a libcurl that is built - with LFS (large file support) disabled even though your system otherwise - supports it. - -o Your app is using or will after the transition use a libcurl that doesn't - support LFS at all, but your system and compiler support 64bit data types. - -In both these cases, the curl_off_t type will now (after the transition) be -64bit where it previously was 32bit. This will cause a binary incompatibility -that you MAY need to deal with. - -Benefits --------- - -This new way has several benefits: - -o Platforms without LFS support can still use libcurl to do >32 bit file - transfers and range operations etc as long as they have >32 bit data-types - supported. - -o Applications will no longer easily build with the curl_off_t size - mismatched, which has been a very frequent (and annoying) problem with - libcurl <= 7.18.2 - -Historically ------------- - -Previously, before 7.19.0, the curl_off_t type would be rather strongly -connected to the size of the system off_t type, where currently curl_off_t is -independent of that. - -The strong connection to off_t made it troublesome for application authors -since when they did mistakes, they could get curl_off_t type of different -sizes in the app vs libcurl, and that caused strange effects that were hard to -track and detect by users of libcurl. - -SONAME ------- - -We opted to not bump the soname for the library unconditionally, simply -because soname bumping is causing a lot of grief and moaning all over the -community so we try to keep that at minimum. Also, our selected design path -should be 100% backwards compatible for the vast majority of all libcurl -users. - -Enforce SONAME bump -------------------- - -If configure doesn't detect your case where a bump is necessary, re-run it -with the --enable-soname-bump command line option! diff --git a/lib/README.curlx b/lib/README.curlx deleted file mode 100644 index 5375b0d1d..000000000 --- a/lib/README.curlx +++ /dev/null @@ -1,61 +0,0 @@ - _ _ ____ _ - ___| | | | _ \| | - / __| | | | |_) | | - | (__| |_| | _ <| |___ - \___|\___/|_| \_\_____| - - Source Code Functions Apps Might Use - ==================================== - -The libcurl source code offers a few functions by source only. They are not -part of the official libcurl API, but the source files might be useful for -others so apps can optionally compile/build with these sources to gain -additional functions. - -We provide them through a single header file for easy access for apps: -"curlx.h" - - curlx_strtoofft() - - A macro that converts a string containing a number to a curl_off_t number. - This might use the curlx_strtoll() function which is provided as source - code in strtoofft.c. Note that the function is only provided if no - strtoll() (or equivalent) function exist on your platform. If curl_off_t - is only a 32 bit number on your platform, this macro uses strtol(). - - curlx_tvnow() - - returns a struct timeval for the current time. - - curlx_tvdiff() - - returns the difference between two timeval structs, in number of - milliseconds. - - curlx_tvdiff_secs() - - returns the same as curlx_tvdiff but with full usec resolution (as a - double) - -FUTURE -====== - - Several functions will be removed from the public curl_ name space in a - future libcurl release. They will then only become available as curlx_ - functions instead. To make the transition easier, we already today provide - these functions with the curlx_ prefix to allow sources to get built properly - with the new function names. The functions this concerns are: - - curlx_getenv - curlx_strequal - curlx_strnequal - curlx_mvsnprintf - curlx_msnprintf - curlx_maprintf - curlx_mvaprintf - curlx_msprintf - curlx_mprintf - curlx_mfprintf - curlx_mvsprintf - curlx_mvprintf - curlx_mvfprintf diff --git a/lib/README.encoding b/lib/README.encoding deleted file mode 100644 index 1012bb9ec..000000000 --- a/lib/README.encoding +++ /dev/null @@ -1,60 +0,0 @@ - - Content Encoding Support for libcurl - -* About content encodings: - -HTTP/1.1 [RFC 2616] specifies that a client may request that a server encode -its response. This is usually used to compress a response using one of a set -of commonly available compression techniques. These schemes are `deflate' (the -zlib algorithm), `gzip' and `compress' [sec 3.5, RFC 2616]. A client requests -that the sever perform an encoding by including an Accept-Encoding header in -the request document. The value of the header should be one of the recognized -tokens `deflate', ... (there's a way to register new schemes/tokens, see sec -3.5 of the spec). A server MAY honor the client's encoding request. When a -response is encoded, the server includes a Content-Encoding header in the -response. The value of the Content-Encoding header indicates which scheme was -used to encode the data. - -A client may tell a server that it can understand several different encoding -schemes. In this case the server may choose any one of those and use it to -encode the response (indicating which one using the Content-Encoding header). -It's also possible for a client to attach priorities to different schemes so -that the server knows which it prefers. See sec 14.3 of RFC 2616 for more -information on the Accept-Encoding header. - -* Current support for content encoding: - -Support for the 'deflate' and 'gzip' content encoding are supported by -libcurl. Both regular and chunked transfers should work fine. The library -zlib is required for this feature. 'deflate' support was added by James -Gallagher, and support for the 'gzip' encoding was added by Dan Fandrich. - -* The libcurl interface: - -To cause libcurl to request a content encoding use: - - curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, <string>) - -where <string> is the intended value of the Accept-Encoding header. - -Currently, libcurl only understands how to process responses that use the -"deflate" or "gzip" Content-Encoding, so the only values for -CURLOPT_ACCEPT_ENCODING that will work (besides "identity," which does -nothing) are "deflate" and "gzip" If a response is encoded using the -"compress" or methods, libcurl will return an error indicating that the -response could not be decoded. If <string> is NULL no Accept-Encoding header -is generated. If <string> is a zero-length string, then an Accept-Encoding -header containing all supported encodings will be generated. - -The CURLOPT_ACCEPT_ENCODING must be set to any non-NULL value for content to -be automatically decoded. If it is not set and the server still sends encoded -content (despite not having been asked), the data is returned in its raw form -and the Content-Encoding type is not checked. - -* The curl interface: - -Use the --compressed option with curl to cause it to ask servers to compress -responses using any format supported by curl. - -James Gallagher <jgallagher@gso.uri.edu> -Dan Fandrich <dan@coneharvesters.com> diff --git a/lib/README.hostip b/lib/README.hostip deleted file mode 100644 index d5688fff1..000000000 --- a/lib/README.hostip +++ /dev/null @@ -1,35 +0,0 @@ - hostip.c explained - ================== - - The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c - source file are these: - - CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use - that. The host may not be able to resolve IPv6, but we don't really have to - take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4 - defined. - - CURLRES_ARES - is defined if libcurl is built to use c-ares for asynchronous - name resolves. This can be Windows or *nix. - - CURLRES_THREADED - is defined if libcurl is built to use threading for - asynchronous name resolves. The name resolve will be done in a new thread, - and the supported asynch API will be the same as for ares-builds. This is - the default under (native) Windows. - - If any of the two previous are defined, CURLRES_ASYNCH is defined too. If - libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is - defined. - - The host*.c sources files are split up like this: - - hostip.c - method-independent resolver functions and utility functions - hostasyn.c - functions for asynchronous name resolves - hostsyn.c - functions for synchronous name resolves - asyn-ares.c - functions for asynchronous name resolves using c-ares - asyn-thread.c - functions for asynchronous name resolves using threads - hostip4.c - IPv4 specific functions - hostip6.c - IPv6 specific functions - - The hostip.h is the single united header file for all this. It defines the - CURLRES_* defines based on the config*.h and curl_setup.h defines. diff --git a/lib/README.httpauth b/lib/README.httpauth deleted file mode 100644 index 960504510..000000000 --- a/lib/README.httpauth +++ /dev/null @@ -1,74 +0,0 @@ - -1. PUT/POST without a known auth to use (possibly no auth required): - - (When explicitly set to use a multi-pass auth when doing a POST/PUT, - libcurl should immediately go the Content-Length: 0 bytes route to avoid - the first send all data phase, step 2. If told to use a single-pass auth, - goto step 3.) - - Issue the proper PUT/POST request immediately, with the correct - Content-Length and Expect: headers. - - If a 100 response is received or the wait for one times out, start sending - the request-body. - - If a 401 (or 407 when talking through a proxy) is received, then: - - If we have "more than just a little" data left to send, close the - connection. Exactly what "more than just a little" means will have to be - determined. Possibly the current transfer speed should be taken into - account as well. - - NOTE: if the size of the POST data is less than MAX_INITIAL_POST_SIZE (when - CURLOPT_POSTFIELDS is used), libcurl will send everything in one single - write() (all request-headers and request-body) and thus it will - unconditionally send the full post data here. - -2. PUT/POST with multi-pass auth but not yet completely negotiated: - - Send a PUT/POST request, we know that it will be rejected and thus we claim - Content-Length zero to avoid having to send the request-body. (This seems - to be what IE does.) - -3. PUT/POST as the last step in the auth negotiation, that is when we have - what we believe is a completed negotiation: - - Send a full and proper PUT/POST request (again) with the proper - Content-Length and a following request-body. - - NOTE: this may very well be the second (or even third) time the whole or at - least parts of the request body is sent to the server. Since the data may - be provided to libcurl with a callback, we need a way to tell the app that - the upload is to be restarted so that the callback will provide data from - the start again. This requires an API method/mechanism that libcurl - doesn't have today. See below. - -Data Rewind - - It will be troublesome for some apps to deal with a rewind like this in all - circumstances. I'm thinking for example when using 'curl' to upload data - from stdin. If libcurl ends up having to rewind the reading for a request - to succeed, of course a lack of this callback or if it returns failure, will - cause the request to fail completely. - - The new callback is set with CURLOPT_IOCTLFUNCTION (in an attempt to add a - more generic function that might be used for other IO-related controls in - the future): - - curlioerr curl_ioctl(CURL *handle, curliocmd cmd, void *clientp); - - And in the case where the read is to be rewinded, it would be called with a - cmd named CURLIOCMD_RESTARTREAD. The callback would then return CURLIOE_OK, - if things are fine, or CURLIOE_FAILRESTART if not. - -Backwards Compatibility - - The approach used until now, that issues a HEAD on the given URL to trigger - the auth negotiation could still be supported and encouraged, but it would - be up to the app to first fetch a URL with GET/HEAD to negotiate on, since - then a following PUT/POST wouldn't need to negotiate authentication and - thus avoid double-sending data. - - Optionally, we keep the current approach if some option is set - (CURLOPT_HEADBEFOREAUTH or similar), since it seems to work fairly well for - POST on most servers. diff --git a/lib/README.memoryleak b/lib/README.memoryleak deleted file mode 100644 index 166177794..000000000 --- a/lib/README.memoryleak +++ /dev/null @@ -1,55 +0,0 @@ - _ _ ____ _ - ___| | | | _ \| | - / __| | | | |_) | | - | (__| |_| | _ <| |___ - \___|\___/|_| \_\_____| - - How To Track Down Suspected Memory Leaks in libcurl - =================================================== - -Single-threaded - - Please note that this memory leak system is not adjusted to work in more - than one thread. If you want/need to use it in a multi-threaded app. Please - adjust accordingly. - - -Build - - Rebuild libcurl with -DCURLDEBUG (usually, rerunning configure with - --enable-debug fixes this). 'make clean' first, then 'make' so that all - files actually are rebuilt properly. It will also make sense to build - libcurl with the debug option (usually -g to the compiler) so that debugging - it will be easier if you actually do find a leak in the library. - - This will create a library that has memory debugging enabled. - -Modify Your Application - - Add a line in your application code: - - curl_memdebug("dump"); - - This will make the malloc debug system output a full trace of all resource - using functions to the given file name. Make sure you rebuild your program - and that you link with the same libcurl you built for this purpose as - described above. - -Run Your Application - - Run your program as usual. Watch the specified memory trace file grow. - - Make your program exit and use the proper libcurl cleanup functions etc. So - that all non-leaks are returned/freed properly. - -Analyze the Flow - - Use the tests/memanalyze.pl perl script to analyze the dump file: - - tests/memanalyze.pl dump - - This now outputs a report on what resources that were allocated but never - freed etc. This report is very fine for posting to the list! - - If this doesn't produce any output, no leak was detected in libcurl. Then - the leak is mostly likely to be in your code. diff --git a/lib/README.multi_socket b/lib/README.multi_socket deleted file mode 100644 index d91e1d9f2..000000000 --- a/lib/README.multi_socket +++ /dev/null @@ -1,53 +0,0 @@ -Implementation of the curl_multi_socket API - - The main ideas of the new API are simply: - - 1 - The application can use whatever event system it likes as it gets info - from libcurl about what file descriptors libcurl waits for what action - on. (The previous API returns fd_sets which is very select()-centric). - - 2 - When the application discovers action on a single socket, it calls - libcurl and informs that there was action on this particular socket and - libcurl can then act on that socket/transfer only and not care about - any other transfers. (The previous API always had to scan through all - the existing transfers.) - - The idea is that curl_multi_socket_action() calls a given callback with - information about what socket to wait for what action on, and the callback - only gets called if the status of that socket has changed. - - We also added a timer callback that makes libcurl call the application when - the timeout value changes, and you set that with curl_multi_setopt() and the - CURLMOPT_TIMERFUNCTION option. To get this to work, Internally, there's an - added a struct to each easy handle in which we store an "expire time" (if - any). The structs are then "splay sorted" so that we can add and remove - times from the linked list and yet somewhat swiftly figure out both how long - time there is until the next nearest timer expires and which timer (handle) - we should take care of now. Of course, the upside of all this is that we get - a curl_multi_timeout() that should also work with old-style applications - that use curl_multi_perform(). - - We created an internal "socket to easy handles" hash table that given - a socket (file descriptor) return the easy handle that waits for action on - that socket. This hash is made using the already existing hash code - (previously only used for the DNS cache). - - To make libcurl able to report plain sockets in the socket callback, we had - to re-organize the internals of the curl_multi_fdset() etc so that the - conversion from sockets to fd_sets for that function is only done in the - last step before the data is returned. I also had to extend c-ares to get a - function that can return plain sockets, as that library too returned only - fd_sets and that is no longer good enough. The changes done to c-ares are - available in c-ares 1.3.1 and later. - - We have done a test runs with up to 9000 connections (with a single active - one). The curl_multi_socket_action() invoke then takes less than 10 - microseconds in average (using the read-only-1-byte-at-a-time hack). We are - now below the 60 microseconds "per socket action" goal (the extra 50 is the - time libevent needs). - -Documentation - - http://curl.haxx.se/libcurl/c/curl_multi_socket_action.html - http://curl.haxx.se/libcurl/c/curl_multi_timeout.html - http://curl.haxx.se/libcurl/c/curl_multi_setopt.html diff --git a/lib/cookie.c b/lib/cookie.c index 94f2a8b85..22730cff4 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -1274,9 +1274,8 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) "# http://curl.haxx.se/docs/http-cookies.html\n" "# This file was generated by libcurl! Edit at your own risk.\n\n", out); - co = c->cookies; - while(co) { + for(co = c->cookies; co; co = co->next) { if(!co->domain) continue; format_ptr = get_netscape_format(co); @@ -1288,7 +1287,6 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere) } fprintf(out, "%s\n", format_ptr); free(format_ptr); - co=co->next; } } @@ -1309,9 +1307,7 @@ struct curl_slist *Curl_cookie_list(struct SessionHandle *data) (data->cookies->numcookies == 0)) return NULL; - c = data->cookies->cookies; - - while(c) { + for(c = data->cookies->cookies; c; c = c->next) { if(!c->domain) continue; line = get_netscape_format(c); @@ -1326,7 +1322,6 @@ struct curl_slist *Curl_cookie_list(struct SessionHandle *data) return NULL; } list = beg; - c = c->next; } return list; @@ -1887,7 +1887,7 @@ static CURLcode proxy_magic(struct connectdata *conn, memset(&http_proxy, 0, sizeof(http_proxy)); data->req.protop = &http_proxy; - result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); + result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport, TRUE); data->req.protop = ftp_save; @@ -3645,7 +3645,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) { /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port aren't used so we blank their arguments. TODO: make this nicer */ - result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0); + result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0, FALSE); return result; } diff --git a/lib/http.c b/lib/http.c index e3cb8370a..f64a56546 100644 --- a/lib/http.c +++ b/lib/http.c @@ -427,8 +427,8 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) /* figure out how much data we are expected to send */ switch(data->set.httpreq) { case HTTPREQ_POST: - if(data->set.postfieldsize != -1) - expectsend = data->set.postfieldsize; + if(data->state.infilesize != -1) + expectsend = data->state.infilesize; else if(data->set.postfields) expectsend = (curl_off_t)strlen(data->set.postfields); break; @@ -2317,20 +2317,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) te ); - /* - * Free userpwd for Negotiate/NTLM. Cannot reuse as it is associated with - * the connection and shouldn't be repeated over it either. - */ - switch (data->state.authhost.picked) { - case CURLAUTH_NEGOTIATE: - case CURLAUTH_NTLM: - case CURLAUTH_NTLM_WB: - Curl_safefree(conn->allocptr.userpwd); - break; - } + /* clear userpwd to avoid re-using credentials from re-used connections */ + Curl_safefree(conn->allocptr.userpwd); /* - * Same for proxyuserpwd + * Free proxyuserpwd for Negotiate/NTLM. Cannot reuse as it is associated + * with the connection and shouldn't be repeated over it either. */ switch (data->state.authproxy.picked) { case CURLAUTH_NEGOTIATE: @@ -2577,8 +2569,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) postsize = 0; else { /* figure out the size of the postfields */ - postsize = (data->set.postfieldsize != -1)? - data->set.postfieldsize: + postsize = (data->state.infilesize != -1)? + data->state.infilesize: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):-1); } @@ -2701,7 +2693,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } - else if(data->set.postfieldsize) { + else if(data->state.infilesize) { /* set the upload size to the progress meter */ Curl_pgrsSetUploadSize(data, postsize?postsize:-1); diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 5ab9915a6..4373d6284 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -68,7 +68,7 @@ CURLcode Curl_proxy_connect(struct connectdata *conn) conn->data->req.protop = &http_proxy; connkeep(conn, "HTTP proxy CONNECT"); result = Curl_proxyCONNECT(conn, FIRSTSOCKET, - conn->host.name, conn->remote_port); + conn->host.name, conn->remote_port, FALSE); conn->data->req.protop = prot_save; if(CURLE_OK != result) return result; @@ -85,12 +85,16 @@ CURLcode Curl_proxy_connect(struct connectdata *conn) * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This * function will issue the necessary commands to get a seamless tunnel through * this proxy. After that, the socket can be used just as a normal socket. + * + * 'blocking' set to TRUE means that this function will do the entire CONNECT + * + response in a blocking fashion. Should be avoided! */ CURLcode Curl_proxyCONNECT(struct connectdata *conn, int sockindex, const char *hostname, - int remote_port) + int remote_port, + bool blocking) { int subversion=0; struct SessionHandle *data=conn->data; @@ -225,12 +229,14 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, return CURLE_RECV_ERROR; } - if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0)) - /* return so we'll be called again polling-style */ - return CURLE_OK; - else { - DEBUGF(infof(data, - "Read response immediately from proxy CONNECT\n")); + if(!blocking) { + if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0)) + /* return so we'll be called again polling-style */ + return CURLE_OK; + else { + DEBUGF(infof(data, + "Read response immediately from proxy CONNECT\n")); + } } /* at this point, the tunnel_connecting phase is over. */ diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 2b5e9c9b4..9c4f0207d 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,7 +26,8 @@ /* ftp can use this as well */ CURLcode Curl_proxyCONNECT(struct connectdata *conn, int tunnelsocket, - const char *hostname, int remote_port); + const char *hostname, int remote_port, + bool blocking); /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600*1000) @@ -34,7 +35,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, CURLcode Curl_proxy_connect(struct connectdata *conn); #else -#define Curl_proxyCONNECT(x,y,z,w) CURLE_NOT_BUILT_IN +#define Curl_proxyCONNECT(x,y,z,w,v) CURLE_NOT_BUILT_IN #define Curl_proxy_connect(x) CURLE_OK #endif diff --git a/lib/multi.c b/lib/multi.c index 85a563d55..d4a00172e 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -400,7 +400,10 @@ CURLMcode curl_multi_add_handle(CURLM *multi_handle, /* Point to the multi's connection cache */ data->state.conn_cache = &multi->conn_cache; - data->state.infilesize = data->set.filesize; + if(data->set.httpreq == HTTPREQ_PUT) + data->state.infilesize = data->set.filesize; + else + data->state.infilesize = data->set.postfieldsize; /* This adds the new entry at the 'end' of the doubly-linked circular list of SessionHandle structs to try and maintain a FIFO queue so @@ -1529,9 +1532,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; } - DEBUGF(infof(data, "multi_runsingle:%d call Curl_readwrite\n", - __LINE__)); - /* read/write data if it is ready to do so */ result = Curl_readwrite(data->easy_conn, data, &done); diff --git a/lib/rtsp.c b/lib/rtsp.c index c5ca75723..c30afd39d 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -322,7 +322,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(!p_session_id && (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", - p_request ? p_request : ""); + p_request); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -440,8 +440,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) Curl_add_bufferf(req_buffer, "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ "CSeq: %ld\r\n", /* CSeq */ - (p_request ? p_request : ""), p_stream_uri, - rtsp->CSeq_sent); + p_request, p_stream_uri, rtsp->CSeq_sent); if(result) return result; @@ -495,8 +494,8 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } else { - postsize = (data->set.postfieldsize != -1)? - data->set.postfieldsize: + postsize = (data->state.infilesize != -1)? + data->state.infilesize: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); data->set.httpreq = HTTPREQ_POST; } diff --git a/lib/security.c b/lib/security.c index 1bea669d5..014bbf1b8 100644 --- a/lib/security.c +++ b/lib/security.c @@ -480,56 +480,54 @@ static CURLcode choose_mech(struct connectdata *conn) void *tmp_allocation; const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech; - do { - tmp_allocation = realloc(conn->app_data, mech->size); - if(tmp_allocation == NULL) { - failf(data, "Failed realloc of size %u", mech->size); - mech = NULL; - return CURLE_OUT_OF_MEMORY; - } - conn->app_data = tmp_allocation; - - if(mech->init) { - ret = mech->init(conn->app_data); - if(ret) { - infof(data, "Failed initialization for %s. Skipping it.\n", - mech->name); - continue; - } + tmp_allocation = realloc(conn->app_data, mech->size); + if(tmp_allocation == NULL) { + failf(data, "Failed realloc of size %u", mech->size); + mech = NULL; + return CURLE_OUT_OF_MEMORY; + } + conn->app_data = tmp_allocation; + + if(mech->init) { + ret = mech->init(conn->app_data); + if(ret) { + infof(data, "Failed initialization for %s. Skipping it.\n", + mech->name); + return CURLE_FAILED_INIT; } + } - infof(data, "Trying mechanism %s...\n", mech->name); - ret = ftp_send_command(conn, "AUTH %s", mech->name); - if(ret < 0) - /* FIXME: This error is too generic but it is OK for now. */ - return CURLE_COULDNT_CONNECT; - - if(ret/100 != 3) { - switch(ret) { - case 504: - infof(data, "Mechanism %s is not supported by the server (server " - "returned ftp code: 504).\n", mech->name); - break; - case 534: - infof(data, "Mechanism %s was rejected by the server (server returned " - "ftp code: 534).\n", mech->name); - break; - default: - if(ret/100 == 5) { - infof(data, "server does not support the security extensions\n"); - return CURLE_USE_SSL_FAILED; - } - break; + infof(data, "Trying mechanism %s...\n", mech->name); + ret = ftp_send_command(conn, "AUTH %s", mech->name); + if(ret < 0) + /* FIXME: This error is too generic but it is OK for now. */ + return CURLE_COULDNT_CONNECT; + + if(ret/100 != 3) { + switch(ret) { + case 504: + infof(data, "Mechanism %s is not supported by the server (server " + "returned ftp code: 504).\n", mech->name); + break; + case 534: + infof(data, "Mechanism %s was rejected by the server (server returned " + "ftp code: 534).\n", mech->name); + break; + default: + if(ret/100 == 5) { + infof(data, "server does not support the security extensions\n"); + return CURLE_USE_SSL_FAILED; } - continue; + break; } + return CURLE_LOGIN_DENIED; + } - /* Authenticate */ - ret = mech->auth(conn->app_data, conn); + /* Authenticate */ + ret = mech->auth(conn->app_data, conn); - if(ret == AUTH_CONTINUE) - continue; - else if(ret != AUTH_OK) { + if(ret != AUTH_CONTINUE) { + if(ret != AUTH_OK) { /* Mechanism has dumped the error to stderr, don't error here. */ return -1; } @@ -545,8 +543,7 @@ static CURLcode choose_mech(struct connectdata *conn) /* Set the requested protection level */ /* BLOCKING */ (void)sec_set_protection_level(conn); - - } WHILE_FALSE; + } return CURLE_OK; } @@ -783,9 +783,15 @@ static CURLcode smb_request_state(struct connectdata *conn, bool *done) off = Curl_read16_le(((unsigned char *) msg) + sizeof(struct smb_header) + 13); if(len > 0) { - result = Curl_client_write(conn, CLIENTWRITE_BODY, - (char *)msg + off + sizeof(unsigned int), - len); + struct smb_conn *smbc = &conn->proto.smbc; + if(off + sizeof(unsigned int) + len > smbc->got) { + failf(conn->data, "Invalid input packet"); + result = CURLE_RECV_ERROR; + } + else + result = Curl_client_write(conn, CLIENTWRITE_BODY, + (char *)msg + off + sizeof(unsigned int), + len); if(result) { req->result = result; next_state = SMB_CLOSE; diff --git a/lib/transfer.c b/lib/transfer.c index 42d38b51a..28cc61ecc 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1053,9 +1053,6 @@ CURLcode Curl_readwrite(struct connectdata *conn, return CURLE_SEND_ERROR; } - DEBUGF(infof(data, "Curl_readwrite: keepon: %x select_res %x\n", k->keepon, - select_res)); - /* We go ahead and do a read if we have a readable socket or if the stream was rewound (in which case we have data in a buffer) */ diff --git a/lib/urldata.h b/lib/urldata.h index 5a2a07d9e..59c704e0d 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -324,6 +324,9 @@ struct ssl_connect_data { size_t encdata_offset, decdata_offset; unsigned char *encdata_buffer, *decdata_buffer; unsigned long req_flags, ret_flags; + CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ + bool recv_sspi_close_notify; /* true if connection closed by close_notify */ + bool recv_connection_closed; /* true if connection closed, regardless how */ #endif /* USE_SCHANNEL */ #ifdef USE_DARWINSSL SSLContextRef ssl_ctx; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index d1ea5fbf1..37d50cb60 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -136,6 +136,11 @@ #define CONF_modules_load_file(a,b,c) #endif +#ifdef OPENSSL_IS_BORINGSSL +/* not present in BoringSSL */ +#define OPENSSL_load_builtin_modules(x) +#endif + /* * Number of bytes to read from the random number seed file. This must be * a finite value (because some entropy "files" like /dev/urandom have @@ -1427,8 +1432,10 @@ static const char *ssl_msg_type(int ssl_ver, int msg) return "Client hello"; case SSL3_MT_SERVER_HELLO: return "Server hello"; +#ifdef SSL3_MT_NEWSESSION_TICKET case SSL3_MT_NEWSESSION_TICKET: return "Newsession Ticket"; +#endif case SSL3_MT_CERTIFICATE: return "Certificate"; case SSL3_MT_SERVER_KEY_EXCHANGE: @@ -2130,10 +2137,9 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) else { /* untreated error */ unsigned long errdetail; - char error_buffer[256]; /* OpenSSL documents that this must be at least - 256 bytes long. */ + char error_buffer[256]=""; /* OpenSSL documents that this must be at + least 256 bytes long. */ CURLcode result; - const char *cert_problem = NULL; long lerr; connssl->connecting_state = ssl_connect_2; /* the connection failed, @@ -2165,9 +2171,10 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) X509_verify_cert_error_string(lerr)); } else - cert_problem = "SSL certificate problem, verify that the CA cert is" - " OK."; - + /* strcpy() is fine here as long as the string fits within + error_buffer */ + strcpy(error_buffer, + "SSL certificate problem, check your CA cert"); break; default: result = CURLE_SSL_CONNECT_ERROR; @@ -2188,7 +2195,7 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) } /* Could be a CERT problem */ - failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); + failf(data, "%s", error_buffer); return result; } diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index b02e42ecc..19aff8f07 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -268,6 +268,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) infof(data, "schannel: sent initial handshake data: " "sent %zd bytes\n", written); + connssl->recv_unrecoverable_err = CURLE_OK; + connssl->recv_sspi_close_notify = false; + connssl->recv_connection_closed = false; + /* continue to second handshake step */ connssl->connecting_state = ssl_connect_2; @@ -300,6 +304,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) if(!connssl->cred || !connssl->ctxt) return CURLE_SSL_CONNECT_ERROR; + /* buffer to store previously received and decrypted data */ + if(connssl->decdata_buffer == NULL) { + connssl->decdata_offset = 0; + connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + connssl->decdata_buffer = malloc(connssl->decdata_length); + if(connssl->decdata_buffer == NULL) { + failf(data, "schannel: unable to allocate memory"); + return CURLE_OUT_OF_MEMORY; + } + } + /* buffer to store previously received and encrypted data */ if(connssl->encdata_buffer == NULL) { connssl->encdata_offset = 0; @@ -403,6 +418,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) return CURLE_OK; } + /* If the server has requested a client certificate, attempt to continue + the handshake without one. This will allow connections to servers which + request a client certificate but do not require it. */ + if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && + !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { + connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; + connssl->connecting_state = ssl_connect_2_writing; + infof(data, "schannel: a client certificate has been requested\n"); + return CURLE_OK; + } + /* check if the handshake needs to be continued */ if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { for(i = 0; i < 3; i++) { @@ -823,8 +849,7 @@ schannel_recv(struct connectdata *conn, int sockindex, char *buf, size_t len, CURLcode *err) { size_t size = 0; - ssize_t nread = 0; - CURLcode result; + ssize_t nread = -1; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; unsigned char *reallocated_buffer; @@ -833,72 +858,103 @@ schannel_recv(struct connectdata *conn, int sockindex, SecBuffer inbuf[4]; SecBufferDesc inbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; + /* we want the length of the encrypted buffer to be at least large enough + that it can hold all the bytes requested and some TLS record overhead. */ + size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; + + /**************************************************************************** + * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup. + * The pattern for return error is set *err, optional infof, goto cleanup. + * + * Our priority is to always return as much decrypted data to the caller as + * possible, even if an error occurs. The state of the decrypted buffer must + * always be valid. Transfer of decrypted data to the caller's buffer is + * handled in the cleanup. + */ infof(data, "schannel: client wants to read %zu bytes\n", len); *err = CURLE_OK; - /* buffer to store previously received and decrypted data */ - if(connssl->decdata_buffer == NULL) { - connssl->decdata_offset = 0; - connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - connssl->decdata_buffer = malloc(connssl->decdata_length); - if(connssl->decdata_buffer == NULL) { - failf(data, "schannel: unable to allocate memory"); - *err = CURLE_OUT_OF_MEMORY; - return -1; - } + if(len && len <= connssl->decdata_offset) { + infof(data, "schannel: enough decrypted data is already available\n"); + goto cleanup; } + else if(connssl->recv_unrecoverable_err) { + *err = connssl->recv_unrecoverable_err; + infof(data, "schannel: an unrecoverable error occurred in a prior call\n"); + goto cleanup; + } + else if(connssl->recv_sspi_close_notify) { + /* once a server has indicated shutdown there is no more encrypted data */ + infof(data, "schannel: server indicated shutdown in a prior call\n"); + goto cleanup; + } + else if(!len) { + /* It's debatable what to return when !len. Regardless we can't return + immediately because there may be data to decrypt (in the case we want to + decrypt all encrypted cached data) so handle !len later in cleanup. + */ + ; /* do nothing */ + } + else if(!connssl->recv_connection_closed) { + /* increase enc buffer in order to fit the requested amount of data */ + size = connssl->encdata_length - connssl->encdata_offset; + if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || + connssl->encdata_length < min_encdata_length) { + reallocated_length = connssl->encdata_offset + + CURL_SCHANNEL_BUFFER_FREE_SIZE; + if(reallocated_length < min_encdata_length) { + reallocated_length = min_encdata_length; + } + reallocated_buffer = realloc(connssl->encdata_buffer, + reallocated_length); + if(reallocated_buffer == NULL) { + *err = CURLE_OUT_OF_MEMORY; + failf(data, "schannel: unable to re-allocate memory"); + goto cleanup; + } - /* increase buffer in order to fit the requested amount of data */ - if(connssl->encdata_length - connssl->encdata_offset < - CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) { - /* increase internal encrypted data buffer */ - reallocated_length = connssl->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - /* make sure that the requested amount of data fits */ - if(reallocated_length < len) { - reallocated_length = len; - } - reallocated_buffer = realloc(connssl->encdata_buffer, - reallocated_length); - - if(reallocated_buffer == NULL) { - failf(data, "schannel: unable to re-allocate memory"); - *err = CURLE_OUT_OF_MEMORY; - return -1; - } - else { connssl->encdata_buffer = reallocated_buffer; connssl->encdata_length = reallocated_length; + size = connssl->encdata_length - connssl->encdata_offset; + infof(data, "schannel: encdata_buffer resized %zu\n", + connssl->encdata_length); } - } - /* read encrypted data from socket */ - infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", - connssl->encdata_offset, connssl->encdata_length); - size = connssl->encdata_length - connssl->encdata_offset; - if(size > 0) { + infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", + connssl->encdata_offset, connssl->encdata_length); + + /* read encrypted data from socket */ *err = Curl_read_plain(conn->sock[sockindex], - (char *) (connssl->encdata_buffer + - connssl->encdata_offset), + (char *)(connssl->encdata_buffer + + connssl->encdata_offset), size, &nread); - /* check for received data */ - if(*err != CURLE_OK) { - return -1; + if(*err) { + nread = -1; + if(*err == CURLE_AGAIN) + infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n"); + else if(*err == CURLE_RECV_ERROR) + infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n"); + else + infof(data, "schannel: Curl_read_plain returned error %d\n", *err); + } + else if(nread == 0) { + connssl->recv_connection_closed = true; + infof(data, "schannel: server closed the connection\n"); } else if(nread > 0) { - /* increase encrypted data buffer offset */ - connssl->encdata_offset += nread; + connssl->encdata_offset += (size_t)nread; + infof(data, "schannel: encrypted data got %zd\n", nread); } - infof(data, "schannel: encrypted data got %zd\n", nread); } infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", connssl->encdata_offset, connssl->encdata_length); - /* check if we still have some data in our buffers */ + /* decrypt loop */ while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK && - connssl->decdata_offset < len) { + (!len || connssl->decdata_offset < len || + connssl->recv_connection_closed)) { /* prepare data buffer for DecryptMessage call */ InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer, curlx_uztoul(connssl->encdata_offset)); @@ -913,13 +969,6 @@ schannel_recv(struct connectdata *conn, int sockindex, sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle, &inbuf_desc, 0, NULL); - /* check if we need more data */ - if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - infof(data, "schannel: failed to decrypt data, need more data\n"); - *err = CURLE_AGAIN; - return -1; - } - /* check if everything went fine (server may want to renegotiate or shutdown the connection context) */ if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE || @@ -932,7 +981,7 @@ schannel_recv(struct connectdata *conn, int sockindex, /* increase buffer in order to fit the received amount of data */ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? - inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; + inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; if(connssl->decdata_length - connssl->decdata_offset < size || connssl->decdata_length < len) { /* increase internal decrypted data buffer */ @@ -943,21 +992,18 @@ schannel_recv(struct connectdata *conn, int sockindex, } reallocated_buffer = realloc(connssl->decdata_buffer, reallocated_length); - if(reallocated_buffer == NULL) { - failf(data, "schannel: unable to re-allocate memory"); *err = CURLE_OUT_OF_MEMORY; - return -1; - } - else { - connssl->decdata_buffer = reallocated_buffer; - connssl->decdata_length = reallocated_length; + failf(data, "schannel: unable to re-allocate memory"); + goto cleanup; } + connssl->decdata_buffer = reallocated_buffer; + connssl->decdata_length = reallocated_length; } /* copy decrypted data to internal buffer */ size = inbuf[1].cbBuffer; - if(size > 0) { + if(size) { memcpy(connssl->decdata_buffer + connssl->decdata_offset, inbuf[1].pvBuffer, size); connssl->decdata_offset += size; @@ -996,56 +1042,117 @@ schannel_recv(struct connectdata *conn, int sockindex, /* check if server wants to renegotiate the connection context */ if(sspi_status == SEC_I_RENEGOTIATE) { infof(data, "schannel: remote party requests renegotiation\n"); - + if(*err && *err != CURLE_AGAIN) { + infof(data, "schannel: can't renogotiate, an error is pending\n"); + goto cleanup; + } + if(connssl->encdata_offset) { + *err = CURLE_RECV_ERROR; + infof(data, "schannel: can't renogotiate, " + "encrypted data available\n"); + goto cleanup; + } /* begin renegotiation */ infof(data, "schannel: renegotiating SSL/TLS connection\n"); connssl->state = ssl_connection_negotiating; connssl->connecting_state = ssl_connect_2_writing; - result = schannel_connect_common(conn, sockindex, FALSE, &done); - if(result) - *err = result; - else { - infof(data, "schannel: SSL/TLS connection renegotiated\n"); - /* now retry receiving data */ - return schannel_recv(conn, sockindex, buf, len, err); + *err = schannel_connect_common(conn, sockindex, FALSE, &done); + if(*err) { + infof(data, "schannel: renegotiation failed\n"); + goto cleanup; } + /* now retry receiving data */ + sspi_status = SEC_E_OK; + infof(data, "schannel: SSL/TLS connection renegotiated\n"); + continue; } + /* check if the server closed the connection */ + else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { + /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not + returned so we have to work around that in cleanup. */ + connssl->recv_sspi_close_notify = true; + if(!connssl->recv_connection_closed) { + connssl->recv_connection_closed = true; + infof(data, "schannel: server closed the connection\n"); + } + goto cleanup; + } + } + else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { + if(!*err) + *err = CURLE_AGAIN; + infof(data, "schannel: failed to decrypt data, need more data\n"); + goto cleanup; } else { - /* something went wrong and we need to return an error */ + *err = CURLE_RECV_ERROR; infof(data, "schannel: failed to read data from server: %s\n", Curl_sspi_strerror(conn, sspi_status)); - *err = CURLE_RECV_ERROR; - return -1; + goto cleanup; } } + infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", + connssl->encdata_offset, connssl->encdata_length); + infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n", connssl->decdata_offset, connssl->decdata_length); - /* copy requested decrypted data to supplied buffer */ +cleanup: + /* Warning- there is no guarantee the encdata state is valid at this point */ + infof(data, "schannel: schannel_recv cleanup\n"); + + /* Error if the connection has closed without a close_notify. + Behavior here is a matter of debate. We don't want to be vulnerable to a + truncation attack however there's some browser precedent for ignoring the + close_notify for compatibility reasons. + Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't + return close_notify. In that case if the connection was closed we assume it + was graceful (close_notify) since there doesn't seem to be a way to tell. + */ + if(len && !connssl->decdata_offset && connssl->recv_connection_closed && + !connssl->recv_sspi_close_notify) { + DWORD winver_full, winver_major, winver_minor; + winver_full = GetVersion(); + winver_major = (DWORD)(LOBYTE(LOWORD(winver_full))); + winver_minor = (DWORD)(HIBYTE(LOWORD(winver_full))); + + if(winver_major == 5 && winver_minor == 0 && sspi_status == SEC_E_OK) + connssl->recv_sspi_close_notify = true; + else { + *err = CURLE_RECV_ERROR; + infof(data, "schannel: server closed abruptly (missing close_notify)\n"); + } + } + + /* Any error other than CURLE_AGAIN is an unrecoverable error. */ + if(*err && *err != CURLE_AGAIN) + connssl->recv_unrecoverable_err = *err; + size = len < connssl->decdata_offset ? len : connssl->decdata_offset; - if(size > 0) { + if(size) { memcpy(buf, connssl->decdata_buffer, size); - - /* move remaining decrypted data forward to the beginning of buffer */ memmove(connssl->decdata_buffer, connssl->decdata_buffer + size, connssl->decdata_offset - size); connssl->decdata_offset -= size; - infof(data, "schannel: decrypted data returned %zd\n", size); + infof(data, "schannel: decrypted data returned %zu\n", size); infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n", connssl->decdata_offset, connssl->decdata_length); - } - /* check if the server closed the connection, */ - /* including special check for Windows 2000 Professional */ - else if(sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK && - connssl->encdata_offset && connssl->encdata_buffer[0] == 0x15)) { - infof(data, "schannel: server closed the connection\n"); *err = CURLE_OK; + return (ssize_t)size; } - return size; + if(!*err && !connssl->recv_connection_closed) + *err = CURLE_AGAIN; + + /* It's debatable what to return when !len. We could return whatever error we + got from decryption but instead we override here so the return is consistent. + */ + if(!len) + *err = CURLE_OK; + + return *err ? -1 : 0; } CURLcode diff --git a/lib/vtls/schannel.h b/lib/vtls/schannel.h index e019a8606..532958483 100644 --- a/lib/vtls/schannel.h +++ b/lib/vtls/schannel.h @@ -72,6 +72,7 @@ #define SECBUFFER_ALERT 17 #endif +/* Both schannel buffer sizes must be > 0 */ #define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 #define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 |