fix CVE-2024-11053

This commit is contained in:
sherlock2010 2025-01-07 08:12:07 +00:00
parent f1d1ed229a
commit 5f58cdffa3
9 changed files with 2771 additions and 1 deletions

View File

@ -0,0 +1,37 @@
From 4b07b7ebadfbff1d26622719b9048673a78f0bf0 Mon Sep 17 00:00:00 2001
From: Viktor Szakats <commit@vsz.me>
Date: Sun, 17 Nov 2024 12:46:25 +0100
Subject: [PATCH] netrc: fix pointer to bool conversion
with MSVC 2008 and 2010:
```
lib/netrc.c(107): error C2440: 'initializing' : cannot convert from 'char *' to 'bool'
```
Ref: https://ci.appveyor.com/project/curlorg/curl/builds/51002792/job/jtoxd4mk984oi6fd#L164
Ref: https://ci.appveyor.com/project/curlorg/curl/builds/51002792/job/0wxlw9a8g04e56vt#L177
Follow-up to e9b9bbac22c26cf67316fa8e6c6b9e831af31949 #15586
Closes #15601
Conflict:NA
Reference:https://github.com/curl/curl/commit/4b07b7ebadfbff1d26622719b9048673a78f0bf0
---
lib/netrc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/netrc.c b/lib/netrc.c
index e787a6ffc..d5ee3c0fd 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -104,7 +104,7 @@ static int parsenetrc(struct store_netrc *store,
int retcode = NETRC_FILE_MISSING;
char *login = *loginp;
char *password = NULL;
- bool specific_login = login; /* points to something */
+ bool specific_login = !!login; /* points to something */
enum host_lookup_state state = NOTHING;
enum found_state keyword = NONE;
unsigned char found = 0; /* login + password found bits, as they can come in
--
2.33.0

View File

@ -0,0 +1,129 @@
From 9fce2c55d4b0273ac99b59bd8cb982a6d96b88cf Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Tue, 17 Dec 2024 23:56:42 +0100
Subject: [PATCH] netrc: fix password-only entries
When a specific hostname matched, and only a password is set before
another machine is specified in the netrc file, the parser would not be
happy and stop there and return the password-only state. It instead
continued and did not return a match.
Add test 2005 to verify this case
Regression from e9b9bba, shipped in 8.11.1.
Reported-by: Ben Zanin
Fixes #15767
Closes #15768
Conflict:context adapt
Reference:https://github.com/curl/curl/commit/9fce2c55d4b0273ac99b59bd8cb982a6d96b88cf
---
lib/netrc.c | 7 +++++-
tests/data/Makefile.inc | 2 +-
tests/data/test2005 | 55 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 62 insertions(+), 2 deletions(-)
create mode 100644 tests/data/test2005
diff --git a/lib/netrc.c b/lib/netrc.c
index cbc86484f..b517c1dfa 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -267,7 +267,8 @@ static int parsenetrc(struct store_netrc *store,
retcode = NETRC_FAILED; /* allocation failed */
goto out;
}
- found |= FOUND_PASSWORD;
+ if(!specific_login || our_login)
+ found |= FOUND_PASSWORD;
keyword = NONE;
}
else if(strcasecompare("login", tok))
@@ -276,6 +277,10 @@ static int parsenetrc(struct store_netrc *store,
keyword = PASSWORD;
else if(strcasecompare("machine", tok)) {
/* a new machine here */
+ if(found & FOUND_PASSWORD) {
+ done = TRUE;
+ break;
+ }
state = HOSTFOUND;
keyword = NONE;
found = 0;
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index bd9a0bbaa..105108309 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -238,7 +238,7 @@ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \
test1955 test1956 test1957 test1958 test1959 test1960 test1964 \
test1970 test1971 test1972 test1973 test1974 test1975 \
\
-test2000 test2001 test2002 test2003 test2004 \
+test2000 test2001 test2002 test2003 test2004 test2005 \
\
test2023 \
test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \
diff --git a/tests/data/test2005 b/tests/data/test2005
new file mode 100644
index 000000000..91e256298
--- /dev/null
+++ b/tests/data/test2005
@@ -0,0 +1,55 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+netrc
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Fri, 05 Aug 2022 10:09:00 GMT
+Server: test-server/fake
+Content-Type: text/plain
+Content-Length: 6
+Connection: close
+
+-foo-
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+netrc match with password only in file, no username. machine follows
+</name>
+<command>
+--netrc-optional --netrc-file %LOGDIR/netrc%TESTNUMBER http://%HOSTIP:%HTTPPORT/
+</command>
+<file name="%LOGDIR/netrc%TESTNUMBER" >
+machine %HOSTIP
+password 5up3r53cr37
+
+machine example.com
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+GET / HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Authorization: Basic %b64[:5up3r53cr37]b64%
+User-Agent: curl/%VERSION
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
--
2.33.0

View File

@ -0,0 +1,453 @@
From 142ac257b3242459b284020c59f1902b9687a954 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Tue, 6 Feb 2024 10:15:52 +0100
Subject: [PATCH] lib: convert Curl_get_line to use dynbuf
Create the line in a dynbuf. Aborts the reading of the file on
errors. Avoids having to always allocate maximum amount from the
start. Avoids direct malloc.
Closes #12846
Conflict:context adapt
Reference:https://github.com/curl/curl/commit/142ac257b3242459b284020c59f1902b9687a954
---
lib/altsvc.c | 18 +++-------
lib/cookie.c | 29 ++++------------
lib/curl_get_line.c | 55 +++++++++++++----------------
lib/curl_get_line.h | 7 ++--
lib/hsts.c | 17 +++------
lib/netrc.c | 10 ++++--
tests/unit/unit3200.c | 80 ++++++++++++++++++++++++-------------------
7 files changed, 96 insertions(+), 120 deletions(-)
diff --git a/lib/altsvc.c b/lib/altsvc.c
index e9f62bf0e..c12d7bda1 100644
--- a/lib/altsvc.c
+++ b/lib/altsvc.c
@@ -209,7 +209,6 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
{
CURLcode result = CURLE_OK;
- char *line = NULL;
FILE *fp;
/* we need a private copy of the file name so that the altsvc cache file
@@ -221,11 +220,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
fp = fopen(file, FOPEN_READTEXT);
if(fp) {
- line = malloc(MAX_ALTSVC_LINE);
- if(!line)
- goto fail;
- while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) {
- char *lineptr = line;
+ struct dynbuf buf;
+ Curl_dyn_init(&buf, MAX_ALTSVC_LINE);
+ while(Curl_get_line(&buf, fp)) {
+ char *lineptr = Curl_dyn_ptr(&buf);
while(*lineptr && ISBLANK(*lineptr))
lineptr++;
if(*lineptr == '#')
@@ -234,16 +232,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
altsvc_add(asi, lineptr);
}
- free(line); /* free the line buffer */
+ Curl_dyn_free(&buf); /* free the line buffer */
fclose(fp);
}
return result;
-
-fail:
- Curl_safefree(asi->filename);
- free(line);
- fclose(fp);
- return CURLE_OUT_OF_MEMORY;
}
/*
diff --git a/lib/cookie.c b/lib/cookie.c
index dc319b611..d10dd572b 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -1205,7 +1205,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
bool newsession)
{
struct CookieInfo *c;
- char *line = NULL;
FILE *handle = NULL;
if(!inc) {
@@ -1241,16 +1240,14 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
c->running = FALSE; /* this is not running, this is init */
if(fp) {
-
- line = malloc(MAX_COOKIE_LINE);
- if(!line)
- goto fail;
- while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
- char *lineptr = line;
+ struct dynbuf buf;
+ Curl_dyn_init(&buf, MAX_COOKIE_LINE);
+ while(Curl_get_line(&buf, fp)) {
+ char *lineptr = Curl_dyn_ptr(&buf);
bool headerline = FALSE;
- if(checkprefix("Set-Cookie:", line)) {
+ if(checkprefix("Set-Cookie:", lineptr)) {
/* This is a cookie line, get it! */
- lineptr = &line[11];
+ lineptr += 11;
headerline = TRUE;
while(*lineptr && ISBLANK(*lineptr))
lineptr++;
@@ -1258,7 +1255,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
}
- free(line); /* free the line buffer */
+ Curl_dyn_free(&buf); /* free the line buffer */
/*
* Remove expired cookies from the hash. We must make sure to run this
@@ -1274,18 +1271,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
c->running = TRUE; /* now, we're running */
return c;
-
-fail:
- free(line);
- /*
- * Only clean up if we allocated it here, as the original could still be in
- * use by a share handle.
- */
- if(!inc)
- Curl_cookie_cleanup(c);
- if(handle)
- fclose(handle);
- return NULL; /* out of memory */
}
/*
diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c
index 686abe751..100207331 100644
--- a/lib/curl_get_line.c
+++ b/lib/curl_get_line.c
@@ -33,14 +33,16 @@
#include "memdebug.h"
/*
- * Curl_get_line() makes sure to only return complete whole lines that fit in
- * 'len' bytes and end with a newline.
+ * Curl_get_line() makes sure to only return complete whole lines that end
+ * newlines.
*/
-char *Curl_get_line(char *buf, int len, FILE *input)
+int Curl_get_line(struct dynbuf *buf, FILE *input)
{
- bool partial = FALSE;
+ CURLcode result;
+ char buffer[128];
+ Curl_dyn_reset(buf);
while(1) {
- char *b = fgets(buf, len, input);
+ char *b = fgets(buffer, sizeof(buffer), input);
if(b) {
size_t rlen = strlen(b);
@@ -48,39 +50,28 @@ char *Curl_get_line(char *buf, int len, FILE *input)
if(!rlen)
break;
- if(b[rlen-1] == '\n') {
- /* b is \n terminated */
- if(partial) {
- partial = FALSE;
- continue;
- }
- return b;
- }
- else if(feof(input)) {
- if(partial)
- /* Line is already too large to return, ignore rest */
- break;
+ result = Curl_dyn_addn(buf, b, rlen);
+ if(result)
+ /* too long line or out of memory */
+ return 0; /* error */
- if(rlen + 1 < (size_t) len) {
- /* b is EOF terminated, insert missing \n */
- b[rlen] = '\n';
- b[rlen + 1] = '\0';
- return b;
- }
- else
- /* Maximum buffersize reached + EOF
- * This line is impossible to add a \n to so we'll ignore it
- */
- break;
+ else if(b[rlen-1] == '\n')
+ /* end of the line */
+ return 1; /* all good */
+
+ else if(feof(input)) {
+ /* append a newline */
+ result = Curl_dyn_addn(buf, "\n", 1);
+ if(result)
+ /* too long line or out of memory */
+ return 0; /* error */
+ return 1; /* all good */
}
- else
- /* Maximum buffersize reached */
- partial = TRUE;
}
else
break;
}
- return NULL;
+ return 0;
}
#endif /* if not disabled */
diff --git a/lib/curl_get_line.h b/lib/curl_get_line.h
index 0ff32c5c2..7907cde88 100644
--- a/lib/curl_get_line.h
+++ b/lib/curl_get_line.h
@@ -24,8 +24,9 @@
*
***************************************************************************/
-/* get_line() makes sure to only return complete whole lines that fit in 'len'
- * bytes and end with a newline. */
-char *Curl_get_line(char *buf, int len, FILE *input);
+#include "dynbuf.h"
+
+/* Curl_get_line() returns complete lines that end with a newline. */
+int Curl_get_line(struct dynbuf *buf, FILE *input);
#endif /* HEADER_CURL_GET_LINE_H */
diff --git a/lib/hsts.c b/lib/hsts.c
index 8725a35c1..607755e6b 100644
--- a/lib/hsts.c
+++ b/lib/hsts.c
@@ -511,7 +511,6 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
static CURLcode hsts_load(struct hsts *h, const char *file)
{
CURLcode result = CURLE_OK;
- char *line = NULL;
FILE *fp;
/* we need a private copy of the file name so that the hsts cache file
@@ -523,11 +522,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
fp = fopen(file, FOPEN_READTEXT);
if(fp) {
- line = malloc(MAX_HSTS_LINE);
- if(!line)
- goto fail;
- while(Curl_get_line(line, MAX_HSTS_LINE, fp)) {
- char *lineptr = line;
+ struct dynbuf buf;
+ Curl_dyn_init(&buf, MAX_HSTS_LINE);
+ while(Curl_get_line(&buf, fp)) {
+ char *lineptr = Curl_dyn_ptr(&buf);
while(*lineptr && ISBLANK(*lineptr))
lineptr++;
if(*lineptr == '#')
@@ -536,15 +534,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
hsts_add(h, lineptr);
}
- free(line); /* free the line buffer */
+ Curl_dyn_free(&buf); /* free the line buffer */
fclose(fp);
}
return result;
-
-fail:
- Curl_safefree(h->filename);
- fclose(fp);
- return CURLE_OUT_OF_MEMORY;
}
/*
diff --git a/lib/netrc.c b/lib/netrc.c
index 038c6dca6..cd2a2844e 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -53,6 +53,8 @@ enum host_lookup_state {
#define NETRC_FAILED -1
#define NETRC_SUCCESS 0
+#define MAX_NETRC_LINE 4096
+
/*
* Returns zero on success.
*/
@@ -80,13 +82,14 @@ static int parsenetrc(const char *host,
file = fopen(netrcfile, FOPEN_READTEXT);
if(file) {
bool done = FALSE;
- char netrcbuffer[4096];
- int netrcbuffsize = (int)sizeof(netrcbuffer);
+ struct dynbuf buf;
+ Curl_dyn_init(&buf, MAX_NETRC_LINE);
- while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) {
+ while(!done && Curl_get_line(&buf, file)) {
char *tok;
char *tok_end;
bool quoted;
+ char *netrcbuffer = Curl_dyn_ptr(&buf);
if(state == MACDEF) {
if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
state = NOTHING;
@@ -245,6 +248,7 @@ static int parsenetrc(const char *host,
} /* while Curl_get_line() */
out:
+ Curl_dyn_free(&buf);
if(!retcode) {
/* success */
if(login_alloc) {
diff --git a/tests/unit/unit3200.c b/tests/unit/unit3200.c
index 0544bcc93..6f508ce07 100644
--- a/tests/unit/unit3200.c
+++ b/tests/unit/unit3200.c
@@ -69,7 +69,7 @@ static const char *filecontents[] = {
"LINE1\n"
C4096 "SOME EXTRA TEXT",
- /* First and third line should be read */
+ /* Only first should be read */
"LINE1\n"
C4096 "SOME EXTRA TEXT\n"
"LINE3\n",
@@ -84,11 +84,13 @@ static const char *filecontents[] = {
UNITTEST_START
size_t i;
+ int rc = 0;
for(i = 0; i < NUMTESTS; i++) {
FILE *fp;
- char buf[4096];
+ struct dynbuf buf;
int len = 4096;
char *line;
+ Curl_dyn_init(&buf, len);
fp = fopen(arg, "wb");
abort_unless(fp != NULL, "Cannot open testfile");
@@ -101,65 +103,73 @@ UNITTEST_START
fprintf(stderr, "Test %zd...", i);
switch(i) {
case 0:
- line = Curl_get_line(buf, len, fp);
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line),
- "First line failed (1)");
- line = Curl_get_line(buf, len, fp);
+ "First line failed (1)");
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE2 NEWLINE\n", line),
- "Second line failed (1)");
- line = Curl_get_line(buf, len, fp);
- abort_unless(line == NULL, "Missed EOF (1)");
+ "Second line failed (1)");
+ rc = Curl_get_line(&buf, fp);
+ abort_unless(!Curl_dyn_len(&buf), "Missed EOF (1)");
break;
case 1:
- line = Curl_get_line(buf, len, fp);
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line),
- "First line failed (2)");
- line = Curl_get_line(buf, len, fp);
+ "First line failed (2)");
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE2 NONEWLINE\n", line),
- "Second line failed (2)");
- line = Curl_get_line(buf, len, fp);
- abort_unless(line == NULL, "Missed EOF (2)");
+ "Second line failed (2)");
+ rc = Curl_get_line(&buf, fp);
+ abort_unless(!Curl_dyn_len(&buf), "Missed EOF (2)");
break;
case 2:
- line = Curl_get_line(buf, len, fp);
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line),
- "First line failed (3)");
- line = Curl_get_line(buf, len, fp);
- fail_unless(line == NULL,
- "Did not detect max read on EOF (3)");
+ "First line failed (3)");
+ rc = Curl_get_line(&buf, fp);
+ fail_unless(!Curl_dyn_len(&buf),
+ "Did not detect max read on EOF (3)");
break;
case 3:
- line = Curl_get_line(buf, len, fp);
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line),
- "First line failed (4)");
- line = Curl_get_line(buf, len, fp);
- fail_unless(line == NULL,
- "Did not ignore partial on EOF (4)");
+ "First line failed (4)");
+ rc = Curl_get_line(&buf, fp);
+ fail_unless(!Curl_dyn_len(&buf),
+ "Did not ignore partial on EOF (4)");
break;
case 4:
- line = Curl_get_line(buf, len, fp);
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line),
- "First line failed (5)");
- line = Curl_get_line(buf, len, fp);
- fail_unless(line && !strcmp("LINE3\n", line),
- "Third line failed (5)");
- line = Curl_get_line(buf, len, fp);
- abort_unless(line == NULL, "Missed EOF (5)");
+ "First line failed (5)");
+ rc = Curl_get_line(&buf, fp);
+ fail_unless(!Curl_dyn_len(&buf),
+ "Did not bail out on too long line");
break;
case 5:
- line = Curl_get_line(buf, len, fp);
+ rc = Curl_get_line(&buf, fp);
+ line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\x1aTEST\n", line),
- "Missed/Misinterpreted ^Z (6)");
- line = Curl_get_line(buf, len, fp);
- abort_unless(line == NULL, "Missed EOF (6)");
+ "Missed/Misinterpreted ^Z (6)");
+ rc = Curl_get_line(&buf, fp);
+ abort_unless(!Curl_dyn_len(&buf), "Missed EOF (6)");
break;
default:
abort_unless(1, "Unknown case");
break;
}
+ Curl_dyn_free(&buf);
fclose(fp);
fprintf(stderr, "OK\n");
}
+ return rc;
UNITTEST_STOP
#else
--
2.33.0

View File

@ -0,0 +1,800 @@
From 3b43a05e000aa8f65bda513f733a73fefe35d5ca Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 10 Oct 2024 18:08:07 +0200
Subject: [PATCH] netrc: cache the netrc file in memory
So that on redirects etc it does not reread the file but just parses it
again.
Reported-by: Pierre-Etienne Meunier
Fixes #15248
Closes #15259
Conflict:context adapt
Reference:https://github.com/curl/curl/commit/3b43a05e000aa8f65bda513f733a73fefe35d5ca
---
lib/multi.c | 2 +
lib/netrc.c | 407 ++++++++++++++++++++++++------------------
lib/netrc.h | 14 +-
lib/url.c | 4 +-
lib/urldata.h | 5 +
tests/unit/unit1304.c | 48 ++++-
6 files changed, 292 insertions(+), 188 deletions(-)
diff --git a/lib/multi.c b/lib/multi.c
index 223c2339c..0f9fedaff 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -757,6 +757,8 @@ static CURLcode multi_done(struct Curl_easy *data,
data->state.lastconnect_id = -1;
}
+ /* flush the netrc cache */
+ Curl_netrc_cleanup(&data->state.netrc);
Curl_safefree(data->state.buffer);
return result;
}
diff --git a/lib/netrc.c b/lib/netrc.c
index 3c0651dcc..c23f927ce 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -31,7 +31,6 @@
#include <curl/curl.h>
#include "netrc.h"
-#include "strtok.h"
#include "strcase.h"
#include "curl_get_line.h"
@@ -49,21 +48,56 @@ enum host_lookup_state {
MACDEF
};
+enum found_state {
+ NONE,
+ LOGIN,
+ PASSWORD
+};
+
#define NETRC_FILE_MISSING 1
#define NETRC_FAILED -1
#define NETRC_SUCCESS 0
#define MAX_NETRC_LINE 4096
+#define MAX_NETRC_FILE (64*1024)
+#define MAX_NETRC_TOKEN 128
+
+static CURLcode file2memory(const char *filename, struct dynbuf *filebuf)
+{
+ CURLcode result = CURLE_OK;
+ FILE *file = fopen(filename, FOPEN_READTEXT);
+ struct dynbuf linebuf;
+ Curl_dyn_init(&linebuf, MAX_NETRC_LINE);
+
+ if(file) {
+ while(Curl_get_line(&linebuf, file)) {
+ const char *line = Curl_dyn_ptr(&linebuf);
+ /* skip comments on load */
+ while(ISBLANK(*line))
+ line++;
+ if(*line == '#')
+ continue;
+ result = Curl_dyn_add(filebuf, line);
+ if(result)
+ goto done;
+ }
+ }
+done:
+ Curl_dyn_free(&linebuf);
+ if(file)
+ fclose(file);
+ return result;
+}
/*
* Returns zero on success.
*/
-static int parsenetrc(const char *host,
+static int parsenetrc(struct store_netrc *store,
+ const char *host,
char **loginp,
char **passwordp,
- char *netrcfile)
+ const char *netrcfile)
{
- FILE *file;
int retcode = NETRC_FILE_MISSING;
char *login = *loginp;
char *password = *passwordp;
@@ -71,204 +105,212 @@ static int parsenetrc(const char *host,
bool login_alloc = FALSE;
bool password_alloc = FALSE;
enum host_lookup_state state = NOTHING;
+ enum found_state found = NONE;
+ bool our_login = TRUE; /* With specific_login, found *our* login name (or
+ login-less line) */
+ bool done = FALSE;
+ char *netrcbuffer;
+ struct dynbuf token;
+ struct dynbuf *filebuf = &store->filebuf;
+ Curl_dyn_init(&token, MAX_NETRC_TOKEN);
- char state_login = 0; /* Found a login keyword */
- char state_password = 0; /* Found a password keyword */
- int state_our_login = TRUE; /* With specific_login, found *our* login
- name (or login-less line) */
-
- DEBUGASSERT(netrcfile);
+ if(!store->loaded) {
+ if(file2memory(netrcfile, filebuf))
+ return NETRC_FAILED;
+ store->loaded = TRUE;
+ }
- file = fopen(netrcfile, FOPEN_READTEXT);
- if(file) {
- bool done = FALSE;
- struct dynbuf buf;
- Curl_dyn_init(&buf, MAX_NETRC_LINE);
+ netrcbuffer = Curl_dyn_ptr(filebuf);
- while(!done && Curl_get_line(&buf, file)) {
- char *tok;
+ while(!done) {
+ char *tok = netrcbuffer;
+ while(tok) {
char *tok_end;
bool quoted;
- char *netrcbuffer = Curl_dyn_ptr(&buf);
+ Curl_dyn_reset(&token);
+ while(ISBLANK(*tok))
+ tok++;
+ /* tok is first non-space letter */
if(state == MACDEF) {
- if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
- state = NOTHING;
- else
- continue;
+ if((*tok == '\n') || (*tok == '\r'))
+ state = NOTHING; /* end of macro definition */
}
- tok = netrcbuffer;
- while(tok) {
- while(ISBLANK(*tok))
- tok++;
- /* tok is first non-space letter */
- if(!*tok || (*tok == '#'))
- /* end of line or the rest is a comment */
- break;
- /* leading double-quote means quoted string */
- quoted = (*tok == '\"');
+ if(!*tok || (*tok == '\n'))
+ /* end of line */
+ break;
- tok_end = tok;
- if(!quoted) {
- while(!ISSPACE(*tok_end))
- tok_end++;
- *tok_end = 0;
+ /* leading double-quote means quoted string */
+ quoted = (*tok == '\"');
+
+ tok_end = tok;
+ if(!quoted) {
+ size_t len = 0;
+ while(!ISSPACE(*tok_end)) {
+ tok_end++;
+ len++;
}
- else {
- bool escape = FALSE;
- bool endquote = FALSE;
- char *store = tok;
- tok_end++; /* pass the leading quote */
- while(*tok_end) {
- char s = *tok_end;
- if(escape) {
- escape = FALSE;
- switch(s) {
- case 'n':
- s = '\n';
- break;
- case 'r':
- s = '\r';
- break;
- case 't':
- s = '\t';
- break;
- }
- }
- else if(s == '\\') {
- escape = TRUE;
- tok_end++;
- continue;
- }
- else if(s == '\"') {
- tok_end++; /* pass the ending quote */
- endquote = TRUE;
+ if(!len || Curl_dyn_addn(&token, tok, len)) {
+ retcode = NETRC_FAILED;
+ goto out;
+ }
+ }
+ else {
+ bool escape = FALSE;
+ bool endquote = FALSE;
+ tok_end++; /* pass the leading quote */
+ while(*tok_end) {
+ char s = *tok_end;
+ if(escape) {
+ escape = FALSE;
+ switch(s) {
+ case 'n':
+ s = '\n';
+ break;
+ case 'r':
+ s = '\r';
+ break;
+ case 't':
+ s = '\t';
break;
}
- *store++ = s;
+ }
+ else if(s == '\\') {
+ escape = TRUE;
tok_end++;
+ continue;
+ }
+ else if(s == '\"') {
+ tok_end++; /* pass the ending quote */
+ endquote = TRUE;
+ break;
}
- *store = 0;
- if(escape || !endquote) {
- /* bad syntax, get out */
+ if(Curl_dyn_addn(&token, &s, 1)) {
retcode = NETRC_FAILED;
goto out;
}
+ tok_end++;
}
-
- if((login && *login) && (password && *password)) {
- done = TRUE;
- break;
+ if(escape || !endquote) {
+ /* bad syntax, get out */
+ retcode = NETRC_FAILED;
+ goto out;
}
+ }
- switch(state) {
- case NOTHING:
- if(strcasecompare("macdef", tok)) {
- /* Define a macro. A macro is defined with the specified name; its
- contents begin with the next .netrc line and continue until a
- null line (consecutive new-line characters) is encountered. */
- state = MACDEF;
- }
- else if(strcasecompare("machine", tok)) {
- /* the next tok is the machine name, this is in itself the
- delimiter that starts the stuff entered for this machine,
- after this we need to search for 'login' and
- 'password'. */
- state = HOSTFOUND;
- }
- else if(strcasecompare("default", tok)) {
- state = HOSTVALID;
- retcode = NETRC_SUCCESS; /* we did find our host */
- }
- break;
- case MACDEF:
- if(!strlen(tok)) {
- state = NOTHING;
- }
- break;
- case HOSTFOUND:
- if(strcasecompare(host, tok)) {
- /* and yes, this is our host! */
- state = HOSTVALID;
- retcode = NETRC_SUCCESS; /* we did find our host */
+ if((login && *login) && (password && *password)) {
+ done = TRUE;
+ break;
+ }
+
+ tok = Curl_dyn_ptr(&token);
+
+ switch(state) {
+ case NOTHING:
+ if(strcasecompare("macdef", tok))
+ /* Define a macro. A macro is defined with the specified name; its
+ contents begin with the next .netrc line and continue until a
+ null line (consecutive new-line characters) is encountered. */
+ state = MACDEF;
+ else if(strcasecompare("machine", tok))
+ /* the next tok is the machine name, this is in itself the delimiter
+ that starts the stuff entered for this machine, after this we
+ need to search for 'login' and 'password'. */
+ state = HOSTFOUND;
+ else if(strcasecompare("default", tok)) {
+ state = HOSTVALID;
+ retcode = NETRC_SUCCESS; /* we did find our host */
+ }
+ break;
+ case MACDEF:
+ if(!*tok)
+ state = NOTHING;
+ break;
+ case HOSTFOUND:
+ if(strcasecompare(host, tok)) {
+ /* and yes, this is our host! */
+ state = HOSTVALID;
+ retcode = NETRC_SUCCESS; /* we did find our host */
+ }
+ else
+ /* not our host */
+ state = NOTHING;
+ break;
+ case HOSTVALID:
+ /* we are now parsing sub-keywords concerning "our" host */
+ if(found == LOGIN) {
+ if(specific_login) {
+ our_login = !Curl_timestrcmp(login, tok);
}
- else
- /* not our host */
- state = NOTHING;
- break;
- case HOSTVALID:
- /* we are now parsing sub-keywords concerning "our" host */
- if(state_login) {
- if(specific_login) {
- state_our_login = !Curl_timestrcmp(login, tok);
+ else if(!login || Curl_timestrcmp(login, tok)) {
+ if(login_alloc)
+ free(login);
+ login = strdup(tok);
+ if(!login) {
+ retcode = NETRC_FAILED; /* allocation failed */
+ goto out;
}
- else if(!login || Curl_timestrcmp(login, tok)) {
- if(login_alloc) {
- free(login);
- login_alloc = FALSE;
- }
- login = strdup(tok);
- if(!login) {
- retcode = NETRC_FAILED; /* allocation failed */
- goto out;
- }
- login_alloc = TRUE;
- }
- state_login = 0;
+ login_alloc = TRUE;
}
- else if(state_password) {
- if((state_our_login || !specific_login)
- && (!password || Curl_timestrcmp(password, tok))) {
- if(password_alloc) {
- free(password);
- password_alloc = FALSE;
- }
- password = strdup(tok);
- if(!password) {
- retcode = NETRC_FAILED; /* allocation failed */
- goto out;
- }
- password_alloc = TRUE;
+ found = NONE;
+ }
+ else if(found == PASSWORD) {
+ if((our_login || !specific_login) &&
+ (!password || Curl_timestrcmp(password, tok))) {
+ if(password_alloc)
+ free(password);
+ password = strdup(tok);
+ if(!password) {
+ retcode = NETRC_FAILED; /* allocation failed */
+ goto out;
}
- state_password = 0;
- }
- else if(strcasecompare("login", tok))
- state_login = 1;
- else if(strcasecompare("password", tok))
- state_password = 1;
- else if(strcasecompare("machine", tok)) {
- /* ok, there's machine here go => */
- state = HOSTFOUND;
- state_our_login = FALSE;
+ password_alloc = TRUE;
}
- break;
- } /* switch (state) */
- tok = ++tok_end;
- }
- } /* while Curl_get_line() */
+ found = NONE;
+ }
+ else if(strcasecompare("login", tok))
+ found = LOGIN;
+ else if(strcasecompare("password", tok))
+ found = PASSWORD;
+ else if(strcasecompare("machine", tok)) {
+ /* ok, there is machine here go => */
+ state = HOSTFOUND;
+ found = NONE;
+ }
+ break;
+ } /* switch (state) */
+ tok = ++tok_end;
+ }
+ if(!done) {
+ char *nl = NULL;
+ if(tok)
+ nl = strchr(tok, '\n');
+ if(!nl)
+ break;
+ /* point to next line */
+ netrcbuffer = &nl[1];
+ }
+ } /* while !done */
out:
- Curl_dyn_free(&buf);
- if(!retcode) {
- /* success */
- if(login_alloc) {
- if(*loginp)
- free(*loginp);
- *loginp = login;
- }
- if(password_alloc) {
- if(*passwordp)
- free(*passwordp);
- *passwordp = password;
- }
+ Curl_dyn_free(&token);
+ if(!retcode) {
+ /* success */
+ if(login_alloc) {
+ free(*loginp);
+ *loginp = login;
}
- else {
- if(login_alloc)
- free(login);
- if(password_alloc)
- free(password);
+ if(password_alloc) {
+ free(*passwordp);
+ *passwordp = password;
}
- fclose(file);
+ }
+ else {
+ Curl_dyn_free(filebuf);
+ if(login_alloc)
+ free(login);
+ if(password_alloc)
+ free(password);
}
return retcode;
@@ -280,7 +322,8 @@ out:
* *loginp and *passwordp MUST be allocated if they aren't NULL when passed
* in.
*/
-int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
+int Curl_parsenetrc(struct store_netrc *store, const char *host,
+ char **loginp, char **passwordp,
char *netrcfile)
{
int retcode = 1;
@@ -329,7 +372,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
free(homea);
return -1;
}
- retcode = parsenetrc(host, loginp, passwordp, filealloc);
+ retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
free(filealloc);
#ifdef WIN32
if(retcode == NETRC_FILE_MISSING) {
@@ -339,15 +382,25 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
free(homea);
return -1;
}
- retcode = parsenetrc(host, loginp, passwordp, filealloc);
+ retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
free(filealloc);
}
#endif
free(homea);
}
else
- retcode = parsenetrc(host, loginp, passwordp, netrcfile);
+ retcode = parsenetrc(store, host, loginp, passwordp, netrcfile);
return retcode;
}
+void Curl_netrc_init(struct store_netrc *s)
+{
+ Curl_dyn_init(&s->filebuf, MAX_NETRC_FILE);
+ s->loaded = FALSE;
+}
+void Curl_netrc_cleanup(struct store_netrc *s)
+{
+ Curl_dyn_free(&s->filebuf);
+ s->loaded = FALSE;
+}
#endif
diff --git a/lib/netrc.h b/lib/netrc.h
index 37c95db5e..0ef9ff78e 100644
--- a/lib/netrc.h
+++ b/lib/netrc.h
@@ -26,9 +26,19 @@
#include "curl_setup.h"
#ifndef CURL_DISABLE_NETRC
+#include "dynbuf.h"
+
+struct store_netrc {
+ struct dynbuf filebuf;
+ char *filename;
+ BIT(loaded);
+};
+
+void Curl_netrc_init(struct store_netrc *s);
+void Curl_netrc_cleanup(struct store_netrc *s);
/* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */
-int Curl_parsenetrc(const char *host, char **loginp,
+int Curl_parsenetrc(struct store_netrc *s, const char *host, char **loginp,
char **passwordp, char *filename);
/* Assume: (*passwordp)[0]=0, host[0] != 0.
* If (*loginp)[0] = 0, search for login and password within a machine
@@ -38,6 +48,8 @@ int Curl_parsenetrc(const char *host, char **loginp,
#else
/* disabled */
#define Curl_parsenetrc(a,b,c,d,e,f) 1
+#define Curl_netrc_init(x)
+#define Curl_netrc_cleanup(x)
#endif
#endif /* HEADER_CURL_NETRC_H */
diff --git a/lib/url.c b/lib/url.c
index a59cb0e34..45745bc60 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -338,6 +338,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_wildcard_dtor(&data->wildcard);
Curl_freeset(data);
Curl_headers_cleanup(data);
+ Curl_netrc_cleanup(&data->state.netrc);
free(data);
return CURLE_OK;
}
@@ -545,6 +546,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
data->progress.flags |= PGRS_HIDE;
data->state.current_speed = -1; /* init to negative == impossible */
+ Curl_netrc_init(&data->state.netrc);
}
if(result) {
@@ -2689,7 +2691,7 @@ static CURLcode override_login(struct Curl_easy *data,
url_provided = TRUE;
}
- ret = Curl_parsenetrc(conn->host.name,
+ ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
userp, passwdp,
data->set.str[STRING_NETRC_FILE]);
if(ret > 0) {
diff --git a/lib/urldata.h b/lib/urldata.h
index 4e0d6ef98..6aa26237d 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -163,6 +163,7 @@ typedef unsigned int curl_prot_t;
#include "splay.h"
#include "dynbuf.h"
#include "dynhds.h"
+#include "netrc.h"
/* return the count of bytes sent, or -1 on error */
typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */
@@ -1313,6 +1314,10 @@ struct UrlState {
CURLcode hresult; /* used to pass return codes back from hyper callbacks */
#endif
+#ifndef CURL_DISABLE_NETRC
+ struct store_netrc netrc;
+#endif
+
/* Dynamically allocated strings, MUST be freed before this struct is
killed. */
struct dynamically_allocated_data {
diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c
index 2171c0736..238d3c0f7 100644
--- a/tests/unit/unit1304.c
+++ b/tests/unit/unit1304.c
@@ -49,17 +49,22 @@ static void unit_stop(void)
}
UNITTEST_START
+{
int result;
+ struct store_netrc store;
/*
* Test a non existent host in our netrc file.
*/
- result = Curl_parsenetrc("test.example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "test.example.com", &login, &password, arg);
fail_unless(result == 1, "Host not found should return 1");
abort_unless(password != NULL, "returned NULL!");
fail_unless(password[0] == 0, "password should not have been changed");
abort_unless(login != NULL, "returned NULL!");
fail_unless(login[0] == 0, "login should not have been changed");
+ Curl_netrc_cleanup(&store);
/*
* Test a non existent login in our netrc file.
@@ -67,13 +72,16 @@ UNITTEST_START
free(login);
login = strdup("me");
abort_unless(login != NULL, "returned NULL!");
- result = Curl_parsenetrc("example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
abort_unless(password != NULL, "returned NULL!");
fail_unless(password[0] == 0, "password should not have been changed");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "me", 2) == 0,
"login should not have been changed");
+ Curl_netrc_cleanup(&store);
/*
* Test a non existent login and host in our netrc file.
@@ -81,13 +89,16 @@ UNITTEST_START
free(login);
login = strdup("me");
abort_unless(login != NULL, "returned NULL!");
- result = Curl_parsenetrc("test.example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "test.example.com", &login, &password, arg);
fail_unless(result == 1, "Host not found should return 1");
abort_unless(password != NULL, "returned NULL!");
fail_unless(password[0] == 0, "password should not have been changed");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "me", 2) == 0,
"login should not have been changed");
+ Curl_netrc_cleanup(&store);
/*
* Test a non existent login (substring of an existing one) in our
@@ -96,13 +107,16 @@ UNITTEST_START
free(login);
login = strdup("admi");
abort_unless(login != NULL, "returned NULL!");
- result = Curl_parsenetrc("example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
abort_unless(password != NULL, "returned NULL!");
fail_unless(password[0] == 0, "password should not have been changed");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "admi", 4) == 0,
"login should not have been changed");
+ Curl_netrc_cleanup(&store);
/*
* Test a non existent login (superstring of an existing one)
@@ -111,13 +125,16 @@ UNITTEST_START
free(login);
login = strdup("adminn");
abort_unless(login != NULL, "returned NULL!");
- result = Curl_parsenetrc("example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
abort_unless(password != NULL, "returned NULL!");
fail_unless(password[0] == 0, "password should not have been changed");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "adminn", 6) == 0,
"login should not have been changed");
+ Curl_netrc_cleanup(&store);
/*
* Test for the first existing host in our netrc file
@@ -126,13 +143,16 @@ UNITTEST_START
free(login);
login = strdup("");
abort_unless(login != NULL, "returned NULL!");
- result = Curl_parsenetrc("example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
abort_unless(password != NULL, "returned NULL!");
fail_unless(strncmp(password, "passwd", 6) == 0,
"password should be 'passwd'");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'");
+ Curl_netrc_cleanup(&store);
/*
* Test for the first existing host in our netrc file
@@ -141,13 +161,16 @@ UNITTEST_START
free(password);
password = strdup("");
abort_unless(password != NULL, "returned NULL!");
- result = Curl_parsenetrc("example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
abort_unless(password != NULL, "returned NULL!");
fail_unless(strncmp(password, "passwd", 6) == 0,
"password should be 'passwd'");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "admin", 5) == 0, "login should be 'admin'");
+ Curl_netrc_cleanup(&store);
/*
* Test for the second existing host in our netrc file
@@ -159,13 +182,16 @@ UNITTEST_START
free(login);
login = strdup("");
abort_unless(login != NULL, "returned NULL!");
- result = Curl_parsenetrc("curl.example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "curl.example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
abort_unless(password != NULL, "returned NULL!");
fail_unless(strncmp(password, "none", 4) == 0,
"password should be 'none'");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'");
+ Curl_netrc_cleanup(&store);
/*
* Test for the second existing host in our netrc file
@@ -174,14 +200,18 @@ UNITTEST_START
free(password);
password = strdup("");
abort_unless(password != NULL, "returned NULL!");
- result = Curl_parsenetrc("curl.example.com", &login, &password, arg);
+ Curl_netrc_init(&store);
+ result = Curl_parsenetrc(&store,
+ "curl.example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
abort_unless(password != NULL, "returned NULL!");
fail_unless(strncmp(password, "none", 4) == 0,
"password should be 'none'");
abort_unless(login != NULL, "returned NULL!");
fail_unless(strncmp(login, "none", 4) == 0, "login should be 'none'");
+ Curl_netrc_cleanup(&store);
+}
UNITTEST_STOP
#else
--
2.33.0

View File

@ -0,0 +1,349 @@
From 9bee39bfed2c413b4cc4eb306a57ac92a1854907 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Sat, 12 Oct 2024 23:54:39 +0200
Subject: [PATCH] url: use same credentials on redirect
Previously it could lose the username and only use the password.
Added test 998 and 999 to verify.
Reported-by: Tobias Bora
Fixes #15262
Closes #15282
Conflict:context adapt
Reference:https://github.com/curl/curl/commit/9bee39bfed2c413b4cc4eb306a57ac92a1854907
---
lib/transfer.c | 3 +
lib/url.c | 19 ++++---
lib/urldata.h | 9 ++-
tests/data/Makefile.inc | 14 +++++++-------
tests/data/test998 | 92 ++++++++++++++++++++++++++++++
tests/data/test999 | 81 ++++++++++++++++++++++++++
6 files changed, 195 insertions(+), 11 deletions(-)
create mode 100644 tests/data/test998
create mode 100644 tests/data/test999
diff --git a/lib/transfer.c b/lib/transfer.c
index 79d648cab..3a9239254 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -679,6 +679,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
}
+ if(data->set.str[STRING_USERNAME] ||
+ data->set.str[STRING_PASSWORD])
+ data->state.creds_from = CREDS_OPTION;
if(!result)
result = Curl_setstropt(&data->state.aptr.user,
data->set.str[STRING_USERNAME]);
diff --git a/lib/url.c b/lib/url.c
index 45745bc60..261f61d8d 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1860,10 +1860,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
return result;
/*
- * User name and password set with their own options override the
- * credentials possibly set in the URL.
+ * username and password set with their own options override the credentials
+ * possibly set in the URL, but netrc does not.
*/
- if(!data->set.str[STRING_PASSWORD]) {
+ if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) {
uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0);
if(!uc) {
char *decoded;
@@ -1876,12 +1876,13 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
result = Curl_setstropt(&data->state.aptr.passwd, decoded);
if(result)
return result;
+ data->state.creds_from = CREDS_URL;
}
else if(uc != CURLUE_NO_PASSWORD)
return Curl_uc_to_curlcode(uc);
}
- if(!data->set.str[STRING_USERNAME]) {
+ if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) {
/* we don't use the URL API's URL decoder option here since it rejects
control codes and we want to allow them for some schemes in the user
and password fields */
@@ -1895,13 +1896,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
return result;
conn->user = decoded;
result = Curl_setstropt(&data->state.aptr.user, decoded);
+ data->state.creds_from = CREDS_URL;
}
else if(uc != CURLUE_NO_USER)
return Curl_uc_to_curlcode(uc);
- else if(data->state.aptr.passwd) {
- /* no user was set but a password, set a blank user */
- result = Curl_setstropt(&data->state.aptr.user, "");
- }
if(result)
return result;
}
@@ -2685,7 +2683,8 @@ static CURLcode override_login(struct Curl_easy *data,
int ret;
bool url_provided = FALSE;
- if(data->state.aptr.user) {
+ if(data->state.aptr.user &&
+ (data->state.creds_from != CREDS_NETRC)) {
/* there was a user name in the URL. Use the URL decoded version */
userp = &data->state.aptr.user;
url_provided = TRUE;
@@ -2733,6 +2732,7 @@ static CURLcode override_login(struct Curl_easy *data,
result = Curl_setstropt(&data->state.aptr.user, *userp);
if(result)
return result;
+ data->state.creds_from = CREDS_NETRC;
}
}
if(data->state.aptr.user) {
@@ -2750,6 +2750,7 @@ static CURLcode override_login(struct Curl_easy *data,
CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp);
if(result)
return result;
+ data->state.creds_from = CREDS_NETRC;
}
if(data->state.aptr.passwd) {
uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD,
diff --git a/lib/urldata.h b/lib/urldata.h
index 6aa26237d..73f662159 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1206,6 +1206,11 @@ struct urlpieces {
char *query;
};
+#define CREDS_NONE 0
+#define CREDS_URL 1 /* from URL */
+#define CREDS_OPTION 2 /* set with a CURLOPT_ */
+#define CREDS_NETRC 3 /* found in netrc */
+
struct UrlState {
/* Points to the connection cache */
struct conncache *conn_cache;
@@ -1344,7 +1349,6 @@ struct UrlState {
char *proxyuser;
char *proxypasswd;
} aptr;
-
unsigned char httpwant; /* when non-zero, a specific HTTP version requested
to be used in the library's request(s) */
unsigned char httpversion; /* the lowest HTTP version*10 reported by any
@@ -1354,6 +1358,9 @@ struct UrlState {
unsigned char dselect_bits; /* != 0 -> bitmask of socket events for this
transfer overriding anything the socket may
report */
+ unsigned int creds_from:2; /* where is the server credentials originating
+ from, see the CREDS_* defines above */
+
#ifdef CURLDEBUG
BIT(conncache_lock);
#endif
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 480a88208..02bf2ae25 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -133,7 +133,7 @@ test961 test962 test963 test964 test965 test966 test967 test968 test969 \
test961 test962 test963 test964 test965 test966 test967 test968 test969 \
test970 test971 test972 test973 test974 test975 test976 test977 test978 \
test979 test980 test981 test982 test983 test984 test985 test986 test987 \
-test988 test989 test990 test991 \
+test988 test989 test990 test991 test998 test999 \
\
test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \
test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \
diff --git a/tests/data/test998 b/tests/data/test998
new file mode 100644
index 000000000..c3a8f5169
--- /dev/null
+++ b/tests/data/test998
@@ -0,0 +1,92 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+--location-trusted
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+HTTP/1.1 301 redirect
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 0
+Connection: close
+Content-Type: text/html
+Location: http://somewhere.else.example/a/path/%TESTNUMBER0002
+
+</data>
+<data2>
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Content-Length: 6
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+</data2>
+
+<datacheck>
+HTTP/1.1 301 redirect
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 0
+Connection: close
+Content-Type: text/html
+Location: http://somewhere.else.example/a/path/%TESTNUMBER0002
+
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Content-Length: 6
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+</datacheck>
+
+</reply>
+
+#
+# Client-side
+<client>
+<features>
+proxy
+</features>
+<server>
+http
+</server>
+<name>
+HTTP with auth in URL redirected to another host
+</name>
+<command>
+-x %HOSTIP:%HTTPPORT http://alberto:einstein@somwhere.example/%TESTNUMBER --location-trusted
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+QUIT
+</strip>
+<protocol>
+GET http://somwhere.example/998 HTTP/1.1
+Host: somwhere.example
+Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg==
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://somewhere.else.example/a/path/9980002 HTTP/1.1
+Host: somewhere.else.example
+Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg==
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test999 b/tests/data/test999
new file mode 100644
index 000000000..990a8d09a
--- /dev/null
+++ b/tests/data/test999
@@ -0,0 +1,81 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+--location-trusted
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Content-Length: 6
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+</data>
+
+<datacheck>
+HTTP/1.1 301 redirect
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 0
+Connection: close
+Content-Type: text/html
+Location: http://somewhere.else.example/a/path/%TESTNUMBER0002
+
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Content-Length: 6
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+</datacheck>
+
+</reply>
+
+#
+# Client-side
+<client>
+<features>
+proxy
+</features>
+<server>
+http
+</server>
+<name>
+HTTP with auth in first URL but not second
+</name>
+<command>
+-x %HOSTIP:%HTTPPORT http://alberto:einstein@somwhere.example/%TESTNUMBER http://somewhere.else.example/%TESTNUMBER
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+QUIT
+</strip>
+<protocol>
+GET http://somwhere.example/%TESTNUMBER HTTP/1.1
+Host: somwhere.example
+Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg==
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://somewhere.else.example/%TESTNUMBER HTTP/1.1
+Host: somewhere.else.example
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
--
2.33.0

View File

@ -0,0 +1,223 @@
From f5c616930b5cf148b1b2632da4f5963ff48bdf88 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 7 Nov 2024 08:52:38 +0100
Subject: [PATCH] duphandle: also init netrc
The netrc init was only done in the Curl_open, meaning that a duplicated
handle would not get inited properly.
Added test 2309 to verify. It does netrc auth with a duplicated handle.
Regression from 3b43a05e000aa8f65bda513f733a
Reported-by: tranzystorekk on github
Fixes #15496
Closes #15503
Conflict:context adapt
Reference:https://github.com/curl/curl/commit/f5c616930b5cf148b1b2632da4f5963ff48bdf88
---
lib/easy.c | 1 +
tests/data/Makefile.inc | 2 ++
tests/data/test2309 | 66 ++++++++++++++++++++++++++++++++++++++
tests/libtest/Makefile.inc | 5 ++-
tests/libtest/lib2309.c | 66 ++++++++++++++++++++++++++++++++++++++
5 files changed, 139 insertions(+), 1 deletions(-)
create mode 100644 tests/data/test2309
create mode 100644 tests/libtest/lib2309.c
diff --git a/lib/easy.c b/lib/easy.c
index d16fa8c07..ac8fab342 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -940,6 +940,7 @@ CURL *curl_easy_duphandle(CURL *d)
goto fail;
Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
+ Curl_netrc_init(&outcurl->state.netrc);
/* the connection cache is setup on demand */
outcurl->state.conn_cache = NULL;
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 02bf2ae25..ea5221c00 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -255,6 +255,8 @@ test2100 \
test2200 test2201 test2202 test2203 test2204 test2205 \
\
test2300 test2301 test2302 test2303 test2304 test2305 test2306 \
+\
+test2309 \
\
test2400 test2401 test2402 test2403 test2404 \
\
diff --git a/tests/data/test2309 b/tests/data/test2309
new file mode 100644
index 000000000..4ba78ee91
--- /dev/null
+++ b/tests/data/test2309
@@ -0,0 +1,66 @@
+<testcase>
+<info>
+<keywords>
+netrc
+HTTP
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes" nocheck="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+proxy
+</features>
+
+# Reproducing issue 15496
+<name>
+HTTP with .netrc using duped easy handle
+</name>
+<tool>
+lib%TESTNUMBER
+</tool>
+<command>
+http://github.com %LOGDIR/netrc%TESTNUMBER http://%HOSTIP:%HTTPPORT/
+</command>
+<file name="%LOGDIR/netrc%TESTNUMBER" >
+
+machine github.com
+
+login daniel
+password $y$j9T$WUVjiVvDbRAWafDLs6cab1$01NX.oaZKf5lw8MR2Nk9Yaxv4CqbE0IaDF.GpGxPul1
+</file>
+</client>
+
+<verify>
+<protocol>
+GET http://github.com/ HTTP/1.1
+Host: github.com
+Authorization: Basic %b64[daniel:$y$j9T$WUVjiVvDbRAWafDLs6cab1$01NX.oaZKf5lw8MR2Nk9Yaxv4CqbE0IaDF.GpGxPul1]b64%
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
index 339a00fc4..8f58fd642 100644
--- a/tests/libtest/Makefile.inc
+++ b/tests/libtest/Makefile.inc
@@ -77,7 +77,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \
lib1945 lib1946 lib1947 lib1948 lib1955 lib1956 lib1957 lib1958 lib1959 \
lib1960 lib1964 \
lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 \
- lib2301 lib2302 lib2304 lib2305 lib2306 \
+ lib2301 lib2302 lib2304 lib2305 lib2306 lib2309 \
lib2402 lib2404 \
lib2502 \
lib3010 lib3025 lib3026 lib3027 \
@@ -683,6 +683,9 @@ lib2306_LDADD = $(TESTUTIL_LIBS)
lib2306_SOURCES = lib2306.c $(SUPPORTFILES)
lib2306_LDADD = $(TESTUTIL_LIBS)
+lib2309_SOURCES = lib2309.c $(SUPPORTFILES)
+lib2309_LDADD = $(TESTUTIL_LIBS)
+
lib2402_SOURCES = lib2402.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib2402_LDADD = $(TESTUTIL_LIBS)
diff --git a/tests/libtest/lib2309.c b/tests/libtest/lib2309.c
new file mode 100644
index 000000000..11f1c1fbd
--- /dev/null
+++ b/tests/libtest/lib2309.c
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 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
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "test.h"
+#include "testtrace.h"
+
+#include <curl/curl.h>
+
+static size_t cb_ignore(char *buffer, size_t size, size_t nmemb, void *userp)
+{
+ (void)buffer;
+ (void)size;
+ (void)nmemb;
+ (void)userp;
+ return CURL_WRITEFUNC_ERROR;
+}
+
+int test(char *URL)
+{
+ CURL *curl;
+ CURL *curldupe;
+ int res = CURLE_OK;
+
+ global_init(CURL_GLOBAL_ALL);
+ curl = curl_easy_init();
+ if(curl) {
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb_ignore);
+ curl_easy_setopt(curl, CURLOPT_URL, URL);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ curl_easy_setopt(curl, CURLOPT_PROXY, libtest_arg3);
+ curl_easy_setopt(curl, CURLOPT_NETRC, (long)CURL_NETRC_REQUIRED);
+ curl_easy_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg2);
+
+ curldupe = curl_easy_duphandle(curl);
+ if(curldupe) {
+ res = curl_easy_perform(curldupe);
+ printf("Returned %d, should be %d.\n", res, CURLE_WRITE_ERROR);
+ fflush(stdout);
+ curl_easy_cleanup(curldupe);
+ }
+ curl_easy_cleanup(curl);
+ }
+ curl_global_cleanup();
+ return 0;
+}
--
2.33.0

View File

@ -0,0 +1,37 @@
From 0cdde0fdfbeb8c35420f6d03fa4b77ed73497694 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 7 Nov 2024 17:03:54 +0100
Subject: [PATCH] netrc: support large file, longer lines, longer tokens
Regression from 3b43a05e000aa8f6 (shipped in 8.11.0)
Reported-by: Moritz
Fixes #15513
Closes #15514
Conflict:NA
Reference:https://github.com/curl/curl/commit/0cdde0fdfbeb8c35420f6d03fa4b77ed73497694
---
lib/netrc.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/netrc.c b/lib/netrc.c
index c23f927ce..034c0307a 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -58,9 +58,9 @@ enum found_state {
#define NETRC_FAILED -1
#define NETRC_SUCCESS 0
-#define MAX_NETRC_LINE 4096
-#define MAX_NETRC_FILE (64*1024)
-#define MAX_NETRC_TOKEN 128
+#define MAX_NETRC_LINE 16384
+#define MAX_NETRC_FILE (128*1024)
+#define MAX_NETRC_TOKEN 4096
static CURLcode file2memory(const char *filename, struct dynbuf *filebuf)
{
--
2.33.0

View File

@ -0,0 +1,728 @@
From e9b9bbac22c26cf67316fa8e6c6b9e831af31949 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Fri, 15 Nov 2024 11:06:36 +0100
Subject: [PATCH] netrc: address several netrc parser flaws
- make sure that a match that returns a username also returns a
password, that should be blank if no password is found
- fix handling of multiple logins for same host where the password/login
order might be reversed.
- reject credentials provided in the .netrc if they contain ASCII control
codes - if the used protocol does not support such (like HTTP and WS do)
Reported-by: Harry Sintonen
Add test 478, 479 and 480 to verify. Updated unit 1304.
Closes #15586
Conflict:context adapt
Reference:https://github.com/curl/curl/e9b9bbac22c26cf67316fa8e6c6b9e831af31949
---
lib/netrc.c | 113 +++++++++++++++++++++++------------------
lib/url.c | 60 +++++++++++++++-------
tests/data/Makefile.inc | 1 +
tests/data/test478 | 73 ++++++++++++++++++++++++++
tests/data/test479 | 107 ++++++++++++++++++++++++++++++++++++++
tests/data/test480 | 38 ++++++++++++++
tests/unit/unit1304.c | 75 ++++++++-------------------
7 files changed, 345 insertions(+), 122 deletions(-)
create mode 100644 tests/data/test478
create mode 100644 tests/data/test479
create mode 100644 tests/data/test480
diff --git a/lib/netrc.c b/lib/netrc.c
index 034c0307a..e787a6ffc 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -54,6 +54,9 @@ enum found_state {
PASSWORD
};
+#define FOUND_LOGIN 1
+#define FOUND_PASSWORD 2
+
#define NETRC_FILE_MISSING 1
#define NETRC_FAILED -1
#define NETRC_SUCCESS 0
@@ -94,24 +97,24 @@ done:
*/
static int parsenetrc(struct store_netrc *store,
const char *host,
- char **loginp,
+ char **loginp, /* might point to a username */
char **passwordp,
const char *netrcfile)
{
int retcode = NETRC_FILE_MISSING;
char *login = *loginp;
- char *password = *passwordp;
- bool specific_login = (login && *login != 0);
- bool login_alloc = FALSE;
- bool password_alloc = FALSE;
+ char *password = NULL;
+ bool specific_login = login; /* points to something */
enum host_lookup_state state = NOTHING;
- enum found_state found = NONE;
- bool our_login = TRUE; /* With specific_login, found *our* login name (or
- login-less line) */
+ enum found_state keyword = NONE;
+ unsigned char found = 0; /* login + password found bits, as they can come in
+ any order */
+ bool our_login = FALSE; /* found our login name */
bool done = FALSE;
char *netrcbuffer;
struct dynbuf token;
struct dynbuf *filebuf = &store->filebuf;
+ DEBUGASSERT(!*passwordp);
Curl_dyn_init(&token, MAX_NETRC_TOKEN);
if(!store->loaded) {
@@ -124,7 +127,7 @@ static int parsenetrc(struct store_netrc *store,
while(!done) {
char *tok = netrcbuffer;
- while(tok) {
+ while(tok && !done) {
char *tok_end;
bool quoted;
Curl_dyn_reset(&token);
@@ -198,11 +201,6 @@ static int parsenetrc(struct store_netrc *store,
}
}
- if((login && *login) && (password && *password)) {
- done = TRUE;
- break;
- }
-
tok = Curl_dyn_ptr(&token);
switch(state) {
@@ -212,11 +210,18 @@ static int parsenetrc(struct store_netrc *store,
contents begin with the next .netrc line and continue until a
null line (consecutive new-line characters) is encountered. */
state = MACDEF;
- else if(strcasecompare("machine", tok))
+ else if(strcasecompare("machine", tok)) {
/* the next tok is the machine name, this is in itself the delimiter
that starts the stuff entered for this machine, after this we
need to search for 'login' and 'password'. */
state = HOSTFOUND;
+ keyword = NONE;
+ found = 0;
+ our_login = FALSE;
+ Curl_safefree(password);
+ if(!specific_login)
+ Curl_safefree(login);
+ }
else if(strcasecompare("default", tok)) {
state = HOSTVALID;
retcode = NETRC_SUCCESS; /* we did find our host */
@@ -238,44 +243,54 @@ static int parsenetrc(struct store_netrc *store,
break;
case HOSTVALID:
/* we are now parsing sub-keywords concerning "our" host */
- if(found == LOGIN) {
- if(specific_login) {
+ if(keyword == LOGIN) {
+ if(specific_login)
our_login = !Curl_timestrcmp(login, tok);
- }
- else if(!login || Curl_timestrcmp(login, tok)) {
- if(login_alloc)
- free(login);
+ else {
+ our_login = TRUE;
+ free(login);
login = strdup(tok);
if(!login) {
retcode = NETRC_FAILED; /* allocation failed */
goto out;
}
- login_alloc = TRUE;
}
- found = NONE;
+ found |= FOUND_LOGIN;
+ keyword = NONE;
}
- else if(found == PASSWORD) {
- if((our_login || !specific_login) &&
- (!password || Curl_timestrcmp(password, tok))) {
- if(password_alloc)
- free(password);
- password = strdup(tok);
- if(!password) {
- retcode = NETRC_FAILED; /* allocation failed */
- goto out;
- }
- password_alloc = TRUE;
+ else if(keyword == PASSWORD) {
+ free(password);
+ password = strdup(tok);
+ if(!password) {
+ retcode = NETRC_FAILED; /* allocation failed */
+ goto out;
}
- found = NONE;
+ found |= FOUND_PASSWORD;
+ keyword = NONE;
}
else if(strcasecompare("login", tok))
- found = LOGIN;
+ keyword = LOGIN;
else if(strcasecompare("password", tok))
- found = PASSWORD;
+ keyword = PASSWORD;
else if(strcasecompare("machine", tok)) {
- /* ok, there is machine here go => */
+ /* a new machine here */
state = HOSTFOUND;
- found = NONE;
+ keyword = NONE;
+ found = 0;
+ Curl_safefree(password);
+ if(!specific_login)
+ Curl_safefree(login);
+ }
+ else if(strcasecompare("default", tok)) {
+ state = HOSTVALID;
+ retcode = NETRC_SUCCESS; /* we did find our host */
+ Curl_safefree(password);
+ if(!specific_login)
+ Curl_safefree(login);
+ }
+ if((found == (FOUND_PASSWORD|FOUND_LOGIN)) && our_login) {
+ done = TRUE;
+ break;
}
break;
} /* switch (state) */
@@ -294,23 +309,23 @@ static int parsenetrc(struct store_netrc *store,
out:
Curl_dyn_free(&token);
+ if(!retcode && !password && our_login) {
+ /* success without a password, set a blank one */
+ password = strdup("");
+ if(!password)
+ retcode = 1; /* out of memory */
+ }
if(!retcode) {
/* success */
- if(login_alloc) {
- free(*loginp);
+ if(!specific_login)
*loginp = login;
- }
- if(password_alloc) {
- free(*passwordp);
- *passwordp = password;
- }
+ *passwordp = password;
}
else {
Curl_dyn_free(filebuf);
- if(login_alloc)
+ if(!specific_login)
free(login);
- if(password_alloc)
- free(password);
+ free(password);
}
return retcode;
diff --git a/lib/url.c b/lib/url.c
index f9bb05f79..436edd891 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -2651,6 +2651,17 @@ static CURLcode parse_remote_port(struct Curl_easy *data,
return CURLE_OK;
}
+static bool str_has_ctrl(const char *input)
+{
+ const unsigned char *str = (const unsigned char *)input;
+ while(*str) {
+ if(*str < 0x20)
+ return TRUE;
+ str++;
+ }
+ return FALSE;
+}
+
/*
* Override the login details from the URL with that in the CURLOPT_USERPWD
* option or a .netrc file, if applicable.
@@ -2682,29 +2693,40 @@ static CURLcode override_login(struct Curl_easy *data,
if(data->state.aptr.user &&
(data->state.creds_from != CREDS_NETRC)) {
- /* there was a user name in the URL. Use the URL decoded version */
+ /* there was a username with a length in the URL. Use the URL decoded
+ version */
userp = &data->state.aptr.user;
url_provided = TRUE;
}
- ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
- userp, passwdp,
- data->set.str[STRING_NETRC_FILE]);
- if(ret > 0) {
- infof(data, "Couldn't find host %s in the %s file; using defaults",
- conn->host.name,
- (data->set.str[STRING_NETRC_FILE] ?
- data->set.str[STRING_NETRC_FILE] : ".netrc"));
- }
- else if(ret < 0) {
- failf(data, ".netrc parser error");
- return CURLE_READ_ERROR;
- }
- else {
- /* set bits.netrc TRUE to remember that we got the name from a .netrc
- file, so that it is safe to use even if we followed a Location: to a
- different host or similar. */
- conn->bits.netrc = TRUE;
+ if(!*passwdp) {
+ ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
+ userp, passwdp,
+ data->set.str[STRING_NETRC_FILE]);
+ if(ret > 0) {
+ infof(data, "Couldn't find host %s in the %s file; using defaults",
+ conn->host.name,
+ (data->set.str[STRING_NETRC_FILE] ?
+ data->set.str[STRING_NETRC_FILE] : ".netrc"));
+ }
+ else if(ret < 0) {
+ failf(data, ".netrc parser error");
+ return CURLE_READ_ERROR;
+ }
+ else {
+ if(!(conn->handler->flags&PROTOPT_USERPWDCTRL)) {
+ /* if the protocol can't handle control codes in credentials, make
+ sure there are none */
+ if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) {
+ failf(data, "control code detected in .netrc credentials");
+ return CURLE_READ_ERROR;
+ }
+ }
+ /* set bits.netrc TRUE to remember that we got the name from a .netrc
+ file, so that it is safe to use even if we followed a Location: to a
+ different host or similar. */
+ conn->bits.netrc = TRUE;
+ }
}
if(url_provided) {
Curl_safefree(conn->user);
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index ea5221c00..53f62c6e2 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -77,6 +77,7 @@ test435 test436 test437 test438 test439 test440 test441 test442 test443 \
test435 test436 test437 test438 test439 test440 test441 test442 test443 \
test444 test445 test446 test447 test448 test449 test450 test451 test452 \
test453 test454 test455 test456 test457 test458 \
+test478 test479 test480 \
\
test490 test491 test492 test493 test494 test495 test496 test497 test498 \
\
diff --git a/tests/data/test478 b/tests/data/test478
new file mode 100644
index 000000000..6558363f5
--- /dev/null
+++ b/tests/data/test478
@@ -0,0 +1,73 @@
+<testcase>
+<info>
+<keywords>
+netrc
+HTTP
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+proxy
+</features>
+<name>
+.netrc with multiple accounts for same host
+</name>
+<command>
+--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER -x http://%HOSTIP:%HTTPPORT/ http://debbie@github.com/
+</command>
+<file name="%LOGDIR/netrc%TESTNUMBER" >
+
+machine github.com
+password weird
+password firstone
+login daniel
+
+machine github.com
+
+machine github.com
+login debbie
+
+machine github.com
+password weird
+password "second\r"
+login debbie
+
+</file>
+</client>
+
+<verify>
+<protocol>
+GET http://github.com/ HTTP/1.1
+Host: github.com
+Authorization: Basic %b64[debbie:second%0D]b64%
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test479 b/tests/data/test479
new file mode 100644
index 000000000..d7ce4652f
--- /dev/null
+++ b/tests/data/test479
@@ -0,0 +1,107 @@
+<testcase>
+<info>
+<keywords>
+netrc
+HTTP
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data crlf="yes">
+HTTP/1.1 301 Follow this you fool
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 6
+Connection: close
+Location: http://b.com/%TESTNUMBER0002
+
+-foo-
+</data>
+
+<data2 crlf="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 7
+Connection: close
+
+target
+</data2>
+
+<datacheck crlf="yes">
+HTTP/1.1 301 Follow this you fool
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 6
+Connection: close
+Location: http://b.com/%TESTNUMBER0002
+
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 7
+Connection: close
+
+target
+</datacheck>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+proxy
+</features>
+<name>
+.netrc with redirect and default without password
+</name>
+<command>
+--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER -L -x http://%HOSTIP:%HTTPPORT/ http://a.com/
+</command>
+<file name="%LOGDIR/netrc%TESTNUMBER" >
+
+machine a.com
+ login alice
+ password alicespassword
+
+default
+ login bob
+
+</file>
+</client>
+
+<verify>
+<protocol>
+GET http://a.com/ HTTP/1.1
+Host: a.com
+Authorization: Basic %b64[alice:alicespassword]b64%
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://b.com/%TESTNUMBER0002 HTTP/1.1
+Host: b.com
+Authorization: Basic %b64[bob:]b64%
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test480 b/tests/data/test480
new file mode 100644
index 000000000..aab889f47
--- /dev/null
+++ b/tests/data/test480
@@ -0,0 +1,38 @@
+<testcase>
+<info>
+<keywords>
+netrc
+pop3
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+pop3
+</server>
+<name>
+Reject .netrc with credentials using CRLF for POP3
+</name>
+<command>
+--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER pop3://%HOSTIP:%POP3PORT/%TESTNUMBER
+</command>
+<file name="%LOGDIR/netrc%TESTNUMBER" >
+machine %HOSTIP
+ login alice
+ password "password\r\ncommand"
+</file>
+</client>
+
+<verify>
+<errorcode>
+26
+</errorcode>
+</verify>
+</testcase>
diff --git a/tests/unit/unit1304.c b/tests/unit/unit1304.c
index 238d3c0f7..817887b94 100644
--- a/tests/unit/unit1304.c
+++ b/tests/unit/unit1304.c
@@ -32,13 +32,8 @@ static char *password;
static CURLcode unit_setup(void)
{
- password = strdup("");
- login = strdup("");
- if(!password || !login) {
- Curl_safefree(password);
- Curl_safefree(login);
- return CURLE_OUT_OF_MEMORY;
- }
+ password = NULL;
+ login = NULL;
return CURLE_OK;
}
@@ -60,89 +55,61 @@ UNITTEST_START
result = Curl_parsenetrc(&store,
"test.example.com", &login, &password, arg);
fail_unless(result == 1, "Host not found should return 1");
- abort_unless(password != NULL, "returned NULL!");
- fail_unless(password[0] == 0, "password should not have been changed");
- abort_unless(login != NULL, "returned NULL!");
- fail_unless(login[0] == 0, "login should not have been changed");
+ abort_unless(password == NULL, "password did not return NULL!");
+ abort_unless(login == NULL, "user did not return NULL!");
Curl_netrc_cleanup(&store);
/*
* Test a non existent login in our netrc file.
*/
- free(login);
- login = strdup("me");
- abort_unless(login != NULL, "returned NULL!");
+ login = (char *)"me";
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
- abort_unless(password != NULL, "returned NULL!");
- fail_unless(password[0] == 0, "password should not have been changed");
- abort_unless(login != NULL, "returned NULL!");
- fail_unless(strncmp(login, "me", 2) == 0,
- "login should not have been changed");
+ abort_unless(password == NULL, "password is not NULL!");
Curl_netrc_cleanup(&store);
/*
* Test a non existent login and host in our netrc file.
*/
- free(login);
- login = strdup("me");
- abort_unless(login != NULL, "returned NULL!");
+ login = (char *)"me";
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"test.example.com", &login, &password, arg);
fail_unless(result == 1, "Host not found should return 1");
- abort_unless(password != NULL, "returned NULL!");
- fail_unless(password[0] == 0, "password should not have been changed");
- abort_unless(login != NULL, "returned NULL!");
- fail_unless(strncmp(login, "me", 2) == 0,
- "login should not have been changed");
+ abort_unless(password == NULL, "password is not NULL!");
Curl_netrc_cleanup(&store);
/*
* Test a non existent login (substring of an existing one) in our
* netrc file.
*/
- free(login);
- login = strdup("admi");
- abort_unless(login != NULL, "returned NULL!");
+ login = (char *)"admi";
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
- abort_unless(password != NULL, "returned NULL!");
- fail_unless(password[0] == 0, "password should not have been changed");
- abort_unless(login != NULL, "returned NULL!");
- fail_unless(strncmp(login, "admi", 4) == 0,
- "login should not have been changed");
+ abort_unless(password == NULL, "password is not NULL!");
Curl_netrc_cleanup(&store);
/*
* Test a non existent login (superstring of an existing one)
* in our netrc file.
*/
- free(login);
- login = strdup("adminn");
- abort_unless(login != NULL, "returned NULL!");
+ login = (char *)"adminn";
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"example.com", &login, &password, arg);
fail_unless(result == 0, "Host should have been found");
- abort_unless(password != NULL, "returned NULL!");
- fail_unless(password[0] == 0, "password should not have been changed");
- abort_unless(login != NULL, "returned NULL!");
- fail_unless(strncmp(login, "adminn", 6) == 0,
- "login should not have been changed");
+ abort_unless(password == NULL, "password is not NULL!");
Curl_netrc_cleanup(&store);
/*
* Test for the first existing host in our netrc file
* with login[0] = 0.
*/
- free(login);
- login = strdup("");
- abort_unless(login != NULL, "returned NULL!");
+ login = NULL;
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"example.com", &login, &password, arg);
@@ -159,8 +126,9 @@ UNITTEST_START
* with login[0] != 0.
*/
free(password);
- password = strdup("");
- abort_unless(password != NULL, "returned NULL!");
+ free(login);
+ password = NULL;
+ login = NULL;
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"example.com", &login, &password, arg);
@@ -177,11 +145,9 @@ UNITTEST_START
* with login[0] = 0.
*/
free(password);
- password = strdup("");
- abort_unless(password != NULL, "returned NULL!");
+ password = NULL;
free(login);
- login = strdup("");
- abort_unless(login != NULL, "returned NULL!");
+ login = NULL;
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"curl.example.com", &login, &password, arg);
@@ -198,8 +164,9 @@ UNITTEST_START
* with login[0] != 0.
*/
free(password);
- password = strdup("");
- abort_unless(password != NULL, "returned NULL!");
+ free(login);
+ password = NULL;
+ login = NULL;
Curl_netrc_init(&store);
result = Curl_parsenetrc(&store,
"curl.example.com", &login, &password, arg);
--
2.33.0

View File

@ -7,7 +7,7 @@
Name: curl
Version: 8.4.0
Release: 13
Release: 14
Summary: Curl is used in command lines or scripts to transfer data
License: curl
URL: https://curl.se/
@ -38,6 +38,14 @@ Patch29: backport-pre-CVE-2024-9681.patch
Patch30: backport-CVE-2024-9681.patch
Patch31: backport-multi-check-that-the-multi-handle-is-valid-in-curl_m.patch
Patch32: backport-cookie-treat-cookie-name-case-sensitively.patch
Patch33: backport-CVE-2024-11053-pre1.patch
Patch34: backport-CVE-2024-11053-pre2.patch
Patch35: backport-CVE-2024-11053-pre3.patch
Patch36: backport-CVE-2024-11053-pre4.patch
Patch37: backport-CVE-2024-11053-pre5.patch
Patch38: backport-CVE-2024-11053.patch
Patch39: backport-CVE-2024-11053-post1.patch
Patch40: backport-CVE-2024-11053-post2.patch
BuildRequires: automake brotli-devel coreutils gcc groff krb5-devel
BuildRequires: libidn2-devel libnghttp2-devel libpsl-devel
@ -223,6 +231,12 @@ rm -rf ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la
%{_mandir}/man3/*
%changelog
* Tue Jan 07 2025 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-14
- Type:CVE
- CVE:CVE-2024-11053
- SUG:NA
- DESC:fix CVE-2024-11053
* Mon Dec 09 2024 zhouyihang <zhouyihang3@h-partners.com> - 8.4.0-13
- Type:bugfix
- CVE:NA