curl_get_line: fix potential infinite loop when filename is a directory
No binary (build failed)
—
(log yok)
| @@ -54,7 +54,8 @@ the Netscape format. | |
| Users often want to both read cookies from a file and write updated cookies | |
| back to a file, so using both --cookie and --cookie-jar in the same command | |
| - | line is common. |
| + | line is common. curl ignores filenames specified with --cookie which do not |
| + | exist or point to a directory. |
| If curl is built with PSL (**Public Suffix List**) support, it detects and | |
| discards cookies that are specified for such suffix domains that should not be | |
| @@ -58,6 +58,8 @@ list of files to read cookies from. | |
| The cookies are loaded from the specified file(s) when the transfer starts, | |
| not when this option is set. | |
| + | libcurl ignores filenames which do not exist or point to a directory. |
| + | |
| # SECURITY CONCERNS | |
| This document previously mentioned how specifying a non-existing file can also | |
| @@ -208,19 +208,22 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) | |
| fp = curlx_fopen(file, FOPEN_READTEXT); | |
| if(fp) { | |
| - | bool eof = FALSE; |
| - | struct dynbuf buf; |
| - | curlx_dyn_init(&buf, MAX_ALTSVC_LINE); |
| - | do { |
| - | result = Curl_get_line(&buf, fp, &eof); |
| - | if(!result) { |
| - | const char *lineptr = curlx_dyn_ptr(&buf); |
| - | curlx_str_passblanks(&lineptr); |
| - | if(curlx_str_single(&lineptr, '#')) |
| - | altsvc_add(asi, lineptr); |
| - | } |
| - | } while(!result && !eof); |
| - | curlx_dyn_free(&buf); /* free the line buffer */ |
| + | curlx_struct_stat stat; |
| + | if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { |
| + | bool eof = FALSE; |
| + | struct dynbuf buf; |
| + | curlx_dyn_init(&buf, MAX_ALTSVC_LINE); |
| + | do { |
| + | result = Curl_get_line(&buf, fp, &eof); |
| + | if(!result) { |
| + | const char *lineptr = curlx_dyn_ptr(&buf); |
| + | curlx_str_passblanks(&lineptr); |
| + | if(curlx_str_single(&lineptr, '#')) |
| + | altsvc_add(asi, lineptr); |
| + | } |
| + | } while(!result && !eof); |
| + | curlx_dyn_free(&buf); /* free the line buffer */ |
| + | } |
| curlx_fclose(fp); | |
| } | |
| return result; | |
| @@ -1106,8 +1106,17 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file, | |
| fp = curlx_fopen(file, "rb"); | |
| if(!fp) | |
| infof(data, "WARNING: failed to open cookie file \"%s\"", file); | |
| - | else |
| - | handle = fp; |
| + | else { |
| + | curlx_struct_stat stat; |
| + | if((curlx_fstat(fileno(fp), &stat) != -1) && S_ISDIR(stat.st_mode)) { |
| + | curlx_fclose(fp); |
| + | fp = NULL; |
| + | infof(data, "WARNING: cookie filename points to a directory: \"%s\"", |
| + | file); |
| + | } |
| + | else |
| + | handle = fp; |
| + | } |
| } | |
| } | |
| @@ -505,26 +505,28 @@ static CURLcode hsts_load(struct hsts *h, const char *file) | |
| fp = curlx_fopen(file, FOPEN_READTEXT); | |
| if(fp) { | |
| - | struct dynbuf buf; |
| - | bool eof = FALSE; |
| - | curlx_dyn_init(&buf, MAX_HSTS_LINE); |
| - | do { |
| - | result = Curl_get_line(&buf, fp, &eof); |
| - | if(!result) { |
| - | const char *lineptr = curlx_dyn_ptr(&buf); |
| - | curlx_str_passblanks(&lineptr); |
| - | |
| - | /* |
| - | * Skip empty or commented lines, since we know the line will have a |
| - | * trailing newline from Curl_get_line we can treat length 1 as empty. |
| - | */ |
| - | if((*lineptr == '#') || strlen(lineptr) <= 1) |
| - | continue; |
| - | |
| - | hsts_add(h, lineptr); |
| - | } |
| - | } while(!result && !eof); |
| - | curlx_dyn_free(&buf); /* free the line buffer */ |
| + | curlx_struct_stat stat; |
| + | if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { |
| + | struct dynbuf buf; |
| + | bool eof = FALSE; |
| + | curlx_dyn_init(&buf, MAX_HSTS_LINE); |
| + | do { |
| + | result = Curl_get_line(&buf, fp, &eof); |
| + | if(!result) { |
| + | const char *lineptr = curlx_dyn_ptr(&buf); |
| + | curlx_str_passblanks(&lineptr); |
| + | |
| + | /* Skip empty or commented lines, since we know the line will have |
| + | a trailing newline from Curl_get_line we can treat length 1 as |
| + | empty. */ |
| + | if((*lineptr == '#') || strlen(lineptr) <= 1) |
| + | continue; |
| + | |
| + | hsts_add(h, lineptr); |
| + | } |
| + | } while(!result && !eof); |
| + | curlx_dyn_free(&buf); /* free the line buffer */ |
| + | } |
| curlx_fclose(fp); | |
| } | |
| return result; | |
| @@ -72,34 +72,36 @@ static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) | |
| { | |
| NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ | |
| FILE *file = curlx_fopen(filename, FOPEN_READTEXT); | |
| - | struct dynbuf linebuf; |
| - | curlx_dyn_init(&linebuf, MAX_NETRC_LINE); |
| if(file) { | |
| - | CURLcode result = CURLE_OK; |
| - | bool eof; |
| - | ret = NETRC_OK; |
| - | do { |
| - | const char *line; |
| - | result = Curl_get_line(&linebuf, file, &eof); |
| - | if(!result) { |
| - | line = curlx_dyn_ptr(&linebuf); |
| - | /* skip comments on load */ |
| - | curlx_str_passblanks(&line); |
| - | if(*line == '#') |
| - | continue; |
| - | result = curlx_dyn_add(filebuf, line); |
| - | } |
| - | if(result) { |
| - | curlx_dyn_free(filebuf); |
| - | ret = curl2netrc(result); |
| - | break; |
| - | } |
| - | } while(!eof); |
| - | } |
| - | curlx_dyn_free(&linebuf); |
| - | if(file) |
| + | curlx_struct_stat stat; |
| + | if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { |
| + | CURLcode result = CURLE_OK; |
| + | bool eof; |
| + | struct dynbuf linebuf; |
| + | curlx_dyn_init(&linebuf, MAX_NETRC_LINE); |
| + | ret = NETRC_OK; |
| + | do { |
| + | const char *line; |
| + | result = Curl_get_line(&linebuf, file, &eof); |
| + | if(!result) { |
| + | line = curlx_dyn_ptr(&linebuf); |
| + | /* skip comments on load */ |
| + | curlx_str_passblanks(&line); |
| + | if(*line == '#') |
| + | continue; |
| + | result = curlx_dyn_add(filebuf, line); |
| + | } |
| + | if(result) { |
| + | curlx_dyn_free(filebuf); |
| + | ret = curl2netrc(result); |
| + | break; |
| + | } |
| + | } while(!eof); |
| + | curlx_dyn_free(&linebuf); |
| + | } |
| curlx_fclose(file); | |
| + | } |
| return ret; | |
| } | |
| @@ -229,7 +229,7 @@ test1670 test1671 \ | |
| test1680 test1681 test1682 test1683 \ | |
| \ | |
| test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ | |
| - | test1708 test1709 test1710 test1711 test1712 \ |
| + | test1708 test1709 test1710 test1711 test1712 test1713 \ |
| \ | |
| test1800 test1801 test1802 test1847 test1848 test1849 test1850 \ | |
| \ | |
| @@ -0,0 +1,36 @@ | |
| + | <?xml version="1.0" encoding="US-ASCII"?> |
| + | <testcase> |
| + | <info> |
| + | <keywords> |
| + | HTTP |
| + | HTTP GET |
| + | Alt-Svc |
| + | cookies |
| + | HSTS |
| + | netrc |
| + | </keywords> |
| + | </info> |
| + | |
| + | # Client-side |
| + | <client> |
| + | <name> |
| + | Filenames pointing to directory failing gracefully |
| + | </name> |
| + | <command> |
| + | http://invalid.invalid/%TESTNUMBER --alt-svc %LOGDIR --cookie %LOGDIR --hsts %LOGDIR --netrc-file %LOGDIR |
| + | </command> |
| + | <features> |
| + | alt-svc |
| + | cookies |
| + | HSTS |
| + | netrc |
| + | </features> |
| + | </client> |
| + | |
| + | <verify> |
| + | # 26 = CURLE_READ_ERROR |
| + | <errorcode> |
| + | 26 |
| + | </errorcode> |
| + | </verify> |
| + | </testcase> |
| @@ -48,7 +48,7 @@ HTTP with bad domain name, get cookies and store in cookie jar | |
| TZ=GMT | |
| </setenv> | |
| <command> | |
| - | domain..tld:%HTTPPORT/want/%TESTNUMBER --resolve domain..tld:%HTTPPORT:%HOSTIP -c %LOGDIR/jar%TESTNUMBER -b %LOGDIR/injar%TESTNUMBER |
| + | domain..tld:%HTTPPORT/want/%TESTNUMBER --resolve domain..tld:%HTTPPORT:%HOSTIP -c %LOGDIR/jar%TESTNUMBER -b %LOGDIR -b %LOGDIR/injar%TESTNUMBER |
| </command> | |
| <file name="%LOGDIR/injar%TESTNUMBER"> | |
| # Netscape HTTP Cookie File | |