mirror of
https://github.com/curl/curl.git
synced 2026-07-24 08:47:50 +03:00
curl_get_line: fix potential infinite loop when filename is a directory
Fix potential inifinite loop reading file content with `Curl_get_line()`
when a filename passed via these options are pointing to a directory
entry (on non-Windows):
- `--alt-svc` / `CURLOPT_ALTSVC`
- `-b` / `--cookie` / `CURLOPT_COOKIEFILE`
- `--hsts` / `CURLOPT_HSTS`
- `--netrc-file` / `CURLOPT_NETRC_FILE`
Fix by checking for this condition and silently skipping such filename
without attempting to read content. Add test 1713 to verify.
Mention in cookie documentation as an accepted case, also show a verbose
message when a directory is detected. Extend test 46 to verify if such
failure lets the logic continue to the next cookie file.
Reported-and-based-on-patch-by: Richard Tollerton
Fixes #20823
Closes #20826 (originally-based-on)
Follow-up to 769ccb4d42 #19140
Closes #20873
This commit is contained in:
parent
6d87eb2878
commit
e76968e20d
9 changed files with 116 additions and 61 deletions
|
|
@ -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
|
||||
|
|
|
|||
29
lib/altsvc.c
29
lib/altsvc.c
|
|
@ -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;
|
||||
|
|
|
|||
13
lib/cookie.c
13
lib/cookie.c
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
38
lib/hsts.c
38
lib/hsts.c
|
|
@ -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);
|
||||
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;
|
||||
/* 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 */
|
||||
hsts_add(h, lineptr);
|
||||
}
|
||||
} while(!result && !eof);
|
||||
curlx_dyn_free(&buf); /* free the line buffer */
|
||||
}
|
||||
curlx_fclose(fp);
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
52
lib/netrc.c
52
lib/netrc.c
|
|
@ -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 \
|
||||
\
|
||||
|
|
|
|||
36
tests/data/test1713
Normal file
36
tests/data/test1713
Normal file
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue