diff --git a/docs/cmdline-opts/cookie.md b/docs/cmdline-opts/cookie.md
index 50f977e70d..30288fbcba 100644
--- a/docs/cmdline-opts/cookie.md
+++ b/docs/cmdline-opts/cookie.md
@@ -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
diff --git a/docs/libcurl/opts/CURLOPT_COOKIEFILE.md b/docs/libcurl/opts/CURLOPT_COOKIEFILE.md
index 36403f9a4d..bf2d374464 100644
--- a/docs/libcurl/opts/CURLOPT_COOKIEFILE.md
+++ b/docs/libcurl/opts/CURLOPT_COOKIEFILE.md
@@ -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
diff --git a/lib/altsvc.c b/lib/altsvc.c
index 34da3e1ac8..20705d1159 100644
--- a/lib/altsvc.c
+++ b/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;
diff --git a/lib/cookie.c b/lib/cookie.c
index d52fa78b3c..92f7935cca 100644
--- a/lib/cookie.c
+++ b/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;
+ }
}
}
diff --git a/lib/hsts.c b/lib/hsts.c
index 03f51f8109..3585af422a 100644
--- a/lib/hsts.c
+++ b/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;
diff --git a/lib/netrc.c b/lib/netrc.c
index 1073cc5365..d88609e358 100644
--- a/lib/netrc.c
+++ b/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;
}
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 65846e52ed..52bee86739 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -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 \
\
diff --git a/tests/data/test1713 b/tests/data/test1713
new file mode 100644
index 0000000000..9727ff615f
--- /dev/null
+++ b/tests/data/test1713
@@ -0,0 +1,36 @@
+
+
+
+
+HTTP
+HTTP GET
+Alt-Svc
+cookies
+HSTS
+netrc
+
+
+
+# Client-side
+
+
+Filenames pointing to directory failing gracefully
+
+
+http://invalid.invalid/%TESTNUMBER --alt-svc %LOGDIR --cookie %LOGDIR --hsts %LOGDIR --netrc-file %LOGDIR
+
+
+alt-svc
+cookies
+HSTS
+netrc
+
+
+
+
+# 26 = CURLE_READ_ERROR
+
+26
+
+
+
diff --git a/tests/data/test46 b/tests/data/test46
index 1b0c8e515d..b46b0e41a6 100644
--- a/tests/data/test46
+++ b/tests/data/test46
@@ -48,7 +48,7 @@ HTTP with bad domain name, get cookies and store in cookie jar
TZ=GMT
-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
# Netscape HTTP Cookie File