| Message ID | 20250214050035.1049565-1-yogita.urade@windriver.com |
|---|---|
| State | Changes Requested |
| Delegated to: | Steve Sakoman |
| Headers | show |
| Series | [scarthgap,1/3] curl: fix CVE-2024-11053 | expand |
Hi Yogita,
This series caused a number of autobuilder failures.
There were failing curl ptests:
AssertionError:
Failed ptests:
{'curl': ['257_-_HTTP_Location:_following_with_--netrc-optional_-_protocol',
'478_-_.netrc_with_multiple_accounts_for_same_host_-_protocol',
'479_-_.netrc_with_redirect_and_default_without_password_-_protocol',
'486_-_.netrc_with_redirect_and_"default"_with_no_password_or_login_-_protocol',
'2005_-_netrc_match_with_password_only_in_file,_no_username._machine_follows_-_protocol']}
And there were also failures in the sdk test, with the following error message:
fatal: unable to access
'https://git.yoctoproject.org/git/kernel-module-hello-world/': .netrc
parser error
Steve
On Thu, Feb 13, 2025 at 9:01 PM Urade, Yogita via
lists.openembedded.org
<Yogita.Urade=windriver.com@lists.openembedded.org> wrote:
>
> From: Yogita Urade <yogita.urade@windriver.com>
>
> When asked to both use a `.netrc` file for credentials and to
> follow HTTP redirects, curl could leak the password used for
> the first host to the followed-to host under certain circumstances.
>
> This flaw only manifests itself if the netrc file has an entry
> that matches the redirect target hostname but the entry either
> omits just the password or omits both login and password.
>
> CVE-2024-11053-0001 is the dependent commit and CVE-2024-11053-0002
> is actual CVE fix. CVE-2024-11053-0003 fixes the regression caused by
> CVE-2024-11053-0002.
>
> Reference:
> https://curl.se/docs/CVE-2024-11053.html
>
> Upstream patches:
> https://github.com/curl/curl/commit/3b43a05e000aa8f65bda513f733a73fefe35d5ca
> https://github.com/curl/curl/commit/e9b9bbac22c26cf67316fa8e6c6b9e831af31949
> https://github.com/curl/curl/commit/9fce2c55d4b0273ac99b59bd8cb982a6d96b88cf
>
> Signed-off-by: Yogita Urade <yogita.urade@windriver.com>
> ---
> .../curl/curl/CVE-2024-11053-0001.patch | 801 ++++++++++++++++++
> .../curl/curl/CVE-2024-11053-0002.patch | 734 ++++++++++++++++
> .../curl/curl/CVE-2024-11053-0003.patch | 130 +++
> meta/recipes-support/curl/curl_8.7.1.bb | 3 +
> 4 files changed, 1668 insertions(+)
> create mode 100644 meta/recipes-support/curl/curl/CVE-2024-11053-0001.patch
> create mode 100644 meta/recipes-support/curl/curl/CVE-2024-11053-0002.patch
> create mode 100644 meta/recipes-support/curl/curl/CVE-2024-11053-0003.patch
>
> diff --git a/meta/recipes-support/curl/curl/CVE-2024-11053-0001.patch b/meta/recipes-support/curl/curl/CVE-2024-11053-0001.patch
> new file mode 100644
> index 0000000000..c65c3c9417
> --- /dev/null
> +++ b/meta/recipes-support/curl/curl/CVE-2024-11053-0001.patch
> @@ -0,0 +1,801 @@
> +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
> +
> +CVE: CVE-2024-11053
> +Upstream-Status: Backport [https://github.com/curl/curl/commit/3b43a05e000aa8f65bda513f733a73fefe35d5ca]
> +
> +Signed-off-by: Yogita Urade <yogita.urade@windriver.com>
> +---
> + lib/multi.c | 3 +
> + lib/netrc.c | 406 ++++++++++++++++++++++++------------------
> + 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 ed9cac7..8a05b37 100644
> +--- a/lib/multi.c
> ++++ b/lib/multi.c
> +@@ -800,6 +800,9 @@ static CURLcode multi_done(struct Curl_easy *data,
> + data->state.lastconnect_id = -1;
> + }
> +
> ++ /* flush the netrc cache */
> ++ Curl_netrc_cleanup(&data->state.netrc);
> ++
> + return result;
> + }
> +
> +diff --git a/lib/netrc.c b/lib/netrc.c
> +index cd2a284..d72959b 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,211 @@ 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;
> ++ if(!*tok || (*tok == '\n'))
> ++ /* end of line */
> ++ break;
> +
> +- /* leading double-quote means quoted string */
> +- quoted = (*tok == '\"');
> ++ /* leading double-quote means quoted string */
> ++ quoted = (*tok == '\"');
> +
> +- tok_end = tok;
> +- if(!quoted) {
> +- while(!ISSPACE(*tok_end))
> +- tok_end++;
> +- *tok_end = 0;
> ++ 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;
> + }
> +- *store = 0;
> +- if(escape || !endquote) {
> +- /* bad syntax, get out */
> ++ else if(s == '\"') {
> ++ tok_end++; /* pass the ending quote */
> ++ endquote = TRUE;
> ++ break;
> ++ }
> ++ 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_alloc = FALSE;
> +- }
> +- login = strdup(tok);
> +- if(!login) {
> +- retcode = NETRC_FAILED; /* allocation failed */
> +- goto out;
> +- }
> +- login_alloc = TRUE;
> ++ else if(!login || Curl_timestrcmp(login, tok)) {
> ++ if(login_alloc)
> ++ free(login);
> ++ login = strdup(tok);
> ++ if(!login) {
> ++ retcode = NETRC_FAILED; /* allocation failed */
> ++ goto out;
> + }
> +- 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 +321,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 +371,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 +381,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 9f2815f..fac223a 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 224b9f3..5cdf83c 100644
> +--- a/lib/url.c
> ++++ b/lib/url.c
> +@@ -329,6 +329,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;
> + }
> +@@ -532,6 +533,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) {
> +@@ -2736,7 +2738,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 ce28f25..165136e 100644
> +--- a/lib/urldata.h
> ++++ b/lib/urldata.h
> +@@ -144,6 +144,7 @@ typedef unsigned int curl_prot_t;
> + #include "dynbuf.h"
> + #include "dynhds.h"
> + #include "request.h"
> ++#include "netrc.h"
> +
> + /* return the count of bytes sent, or -1 on error */
> + typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */
> +@@ -1324,6 +1325,10 @@ struct UrlState {
> + struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */
> + #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 0288562..34cfb7a 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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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.40.0
> diff --git a/meta/recipes-support/curl/curl/CVE-2024-11053-0002.patch b/meta/recipes-support/curl/curl/CVE-2024-11053-0002.patch
> new file mode 100644
> index 0000000000..f066b0b1d5
> --- /dev/null
> +++ b/meta/recipes-support/curl/curl/CVE-2024-11053-0002.patch
> @@ -0,0 +1,734 @@
> +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
> +
> +Conflicts:
> +- Use old variable names password and loging instead of s_password and s_login.
> +- Test files are added in Makefile.inc.
> +
> +CVE: CVE-2024-11053
> +Upstream-Status: Backport [https://github.com/curl/curl/commit/e9b9bbac22c26cf67316fa8e6c6b9e831af31949]
> +
> +Signed-off-by: Yogita Urade <yogita.urade@windriver.com>
> +---
> + lib/netrc.c | 113 +++++++++++++++++++++++-----------------
> + lib/url.c | 60 ++++++++++++++-------
> + tests/data/Makefile.inc | 2 +-
> + tests/data/test478 | 73 ++++++++++++++++++++++++++
> + tests/data/test479 | 107 +++++++++++++++++++++++++++++++++++++
> + tests/data/test480 | 38 ++++++++++++++
> + tests/unit/unit1304.c | 75 ++++++++------------------
> + 7 files changed, 345 insertions(+), 123 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 d72959b..8b0192a 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);
> +@@ -197,11 +200,6 @@ static int parsenetrc(struct store_netrc *store,
> + }
> + }
> +
> +- if((login && *login) && (password && *password)) {
> +- done = TRUE;
> +- break;
> +- }
> +-
> + tok = Curl_dyn_ptr(&token);
> +
> + switch(state) {
> +@@ -211,11 +209,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 */
> +@@ -237,44 +242,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) */
> +@@ -293,23 +308,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 5cdf83c..247a8f9 100644
> +--- a/lib/url.c
> ++++ b/lib/url.c
> +@@ -2703,6 +2703,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.
> +@@ -2733,29 +2744,40 @@ static CURLcode override_login(struct Curl_easy *data,
> + bool url_provided = FALSE;
> +
> + if(data->state.aptr.user) {
> +- /* 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 d89e565..7a6b153 100644
> +--- a/tests/data/Makefile.inc
> ++++ b/tests/data/Makefile.inc
> +@@ -73,7 +73,7 @@ test426 test427 test428 test429 test430 test431 test432 test433 test434 \
> + test435 test436 test437 test438 test439 test440 test441 test442 test443 \
> + test444 test445 test446 test447 test448 test449 test450 test451 test452 \
> + test453 test454 test455 test456 test457 test458 test459 test460 test461 \
> +-test462 test463 test467 test468 \
> ++test462 test463 test467 test468 test478 test479 test480 \
> + \
> + test490 test491 test492 test493 test494 test495 test496 test497 test498 \
> + test499 test500 test501 test502 test503 test504 test505 test506 test507 \
> +diff --git a/tests/data/test478 b/tests/data/test478
> +new file mode 100644
> +index 0000000..7d7454d
> +--- /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 0000000..48bcdfe
> +--- /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 0000000..aab889f
> +--- /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 34cfb7a..db2a2c0 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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_password, arg);
> +--
> +2.40.0
> diff --git a/meta/recipes-support/curl/curl/CVE-2024-11053-0003.patch b/meta/recipes-support/curl/curl/CVE-2024-11053-0003.patch
> new file mode 100644
> index 0000000000..ec55a68c5b
> --- /dev/null
> +++ b/meta/recipes-support/curl/curl/CVE-2024-11053-0003.patch
> @@ -0,0 +1,130 @@
> +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
> +
> +CVE: CVE-2024-11053
> +Upstream-Status: Backport [https://github.com/curl/curl/commit/9fce2c55d4b0273ac99]
> +
> +Signed-off-by: Yogita Urade <yogita.urade@windriver.com>
> +---
> + 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 8b0192a..85b72ba 100644
> +--- a/lib/netrc.c
> ++++ b/lib/netrc.c
> +@@ -264,7 +264,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))
> +@@ -273,6 +274,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 7a6b153..32d8fee 100644
> +--- a/tests/data/Makefile.inc
> ++++ b/tests/data/Makefile.inc
> +@@ -230,7 +230,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 0000000..69e14d9
> +--- /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.40.0
> diff --git a/meta/recipes-support/curl/curl_8.7.1.bb b/meta/recipes-support/curl/curl_8.7.1.bb
> index 439fcb7881..81243d1873 100644
> --- a/meta/recipes-support/curl/curl_8.7.1.bb
> +++ b/meta/recipes-support/curl/curl_8.7.1.bb
> @@ -20,6 +20,9 @@ SRC_URI = " \
> file://CVE-2024-7264-2.patch \
> file://CVE-2024-8096.patch \
> file://CVE-2024-9681.patch \
> + file://CVE-2024-11053-0001.patch \
> + file://CVE-2024-11053-0002.patch \
> + file://CVE-2024-11053-0003.patch \
> "
> SRC_URI[sha256sum] = "6fea2aac6a4610fbd0400afb0bcddbe7258a64c63f1f68e5855ebc0c659710cd"
>
> --
> 2.40.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#211372): https://lists.openembedded.org/g/openembedded-core/message/211372
> Mute This Topic: https://lists.openembedded.org/mt/111177797/3620601
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [steve@sakoman.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
diff --git a/meta/recipes-support/curl/curl/CVE-2024-11053-0001.patch b/meta/recipes-support/curl/curl/CVE-2024-11053-0001.patch new file mode 100644 index 0000000000..c65c3c9417 --- /dev/null +++ b/meta/recipes-support/curl/curl/CVE-2024-11053-0001.patch @@ -0,0 +1,801 @@ +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 + +CVE: CVE-2024-11053 +Upstream-Status: Backport [https://github.com/curl/curl/commit/3b43a05e000aa8f65bda513f733a73fefe35d5ca] + +Signed-off-by: Yogita Urade <yogita.urade@windriver.com> +--- + lib/multi.c | 3 + + lib/netrc.c | 406 ++++++++++++++++++++++++------------------ + 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 ed9cac7..8a05b37 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -800,6 +800,9 @@ static CURLcode multi_done(struct Curl_easy *data, + data->state.lastconnect_id = -1; + } + ++ /* flush the netrc cache */ ++ Curl_netrc_cleanup(&data->state.netrc); ++ + return result; + } + +diff --git a/lib/netrc.c b/lib/netrc.c +index cd2a284..d72959b 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,211 @@ 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; ++ if(!*tok || (*tok == '\n')) ++ /* end of line */ ++ break; + +- /* leading double-quote means quoted string */ +- quoted = (*tok == '\"'); ++ /* leading double-quote means quoted string */ ++ quoted = (*tok == '\"'); + +- tok_end = tok; +- if(!quoted) { +- while(!ISSPACE(*tok_end)) +- tok_end++; +- *tok_end = 0; ++ 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; + } +- *store = 0; +- if(escape || !endquote) { +- /* bad syntax, get out */ ++ else if(s == '\"') { ++ tok_end++; /* pass the ending quote */ ++ endquote = TRUE; ++ break; ++ } ++ 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_alloc = FALSE; +- } +- login = strdup(tok); +- if(!login) { +- retcode = NETRC_FAILED; /* allocation failed */ +- goto out; +- } +- login_alloc = TRUE; ++ else if(!login || Curl_timestrcmp(login, tok)) { ++ if(login_alloc) ++ free(login); ++ login = strdup(tok); ++ if(!login) { ++ retcode = NETRC_FAILED; /* allocation failed */ ++ goto out; + } +- 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 +321,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 +371,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 +381,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 9f2815f..fac223a 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 224b9f3..5cdf83c 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -329,6 +329,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; + } +@@ -532,6 +533,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) { +@@ -2736,7 +2738,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 ce28f25..165136e 100644 +--- a/lib/urldata.h ++++ b/lib/urldata.h +@@ -144,6 +144,7 @@ typedef unsigned int curl_prot_t; + #include "dynbuf.h" + #include "dynhds.h" + #include "request.h" ++#include "netrc.h" + + /* return the count of bytes sent, or -1 on error */ + typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ +@@ -1324,6 +1325,10 @@ struct UrlState { + struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */ + #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 0288562..34cfb7a 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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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.40.0 diff --git a/meta/recipes-support/curl/curl/CVE-2024-11053-0002.patch b/meta/recipes-support/curl/curl/CVE-2024-11053-0002.patch new file mode 100644 index 0000000000..f066b0b1d5 --- /dev/null +++ b/meta/recipes-support/curl/curl/CVE-2024-11053-0002.patch @@ -0,0 +1,734 @@ +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 + +Conflicts: +- Use old variable names password and loging instead of s_password and s_login. +- Test files are added in Makefile.inc. + +CVE: CVE-2024-11053 +Upstream-Status: Backport [https://github.com/curl/curl/commit/e9b9bbac22c26cf67316fa8e6c6b9e831af31949] + +Signed-off-by: Yogita Urade <yogita.urade@windriver.com> +--- + lib/netrc.c | 113 +++++++++++++++++++++++----------------- + lib/url.c | 60 ++++++++++++++------- + tests/data/Makefile.inc | 2 +- + tests/data/test478 | 73 ++++++++++++++++++++++++++ + tests/data/test479 | 107 +++++++++++++++++++++++++++++++++++++ + tests/data/test480 | 38 ++++++++++++++ + tests/unit/unit1304.c | 75 ++++++++------------------ + 7 files changed, 345 insertions(+), 123 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 d72959b..8b0192a 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); +@@ -197,11 +200,6 @@ static int parsenetrc(struct store_netrc *store, + } + } + +- if((login && *login) && (password && *password)) { +- done = TRUE; +- break; +- } +- + tok = Curl_dyn_ptr(&token); + + switch(state) { +@@ -211,11 +209,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 */ +@@ -237,44 +242,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) */ +@@ -293,23 +308,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 5cdf83c..247a8f9 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -2703,6 +2703,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. +@@ -2733,29 +2744,40 @@ static CURLcode override_login(struct Curl_easy *data, + bool url_provided = FALSE; + + if(data->state.aptr.user) { +- /* 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 d89e565..7a6b153 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -73,7 +73,7 @@ test426 test427 test428 test429 test430 test431 test432 test433 test434 \ + test435 test436 test437 test438 test439 test440 test441 test442 test443 \ + test444 test445 test446 test447 test448 test449 test450 test451 test452 \ + test453 test454 test455 test456 test457 test458 test459 test460 test461 \ +-test462 test463 test467 test468 \ ++test462 test463 test467 test468 test478 test479 test480 \ + \ + test490 test491 test492 test493 test494 test495 test496 test497 test498 \ + test499 test500 test501 test502 test503 test504 test505 test506 test507 \ +diff --git a/tests/data/test478 b/tests/data/test478 +new file mode 100644 +index 0000000..7d7454d +--- /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 0000000..48bcdfe +--- /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 0000000..aab889f +--- /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 34cfb7a..db2a2c0 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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_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", &s_login, &s_password, arg); +-- +2.40.0 diff --git a/meta/recipes-support/curl/curl/CVE-2024-11053-0003.patch b/meta/recipes-support/curl/curl/CVE-2024-11053-0003.patch new file mode 100644 index 0000000000..ec55a68c5b --- /dev/null +++ b/meta/recipes-support/curl/curl/CVE-2024-11053-0003.patch @@ -0,0 +1,130 @@ +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 + +CVE: CVE-2024-11053 +Upstream-Status: Backport [https://github.com/curl/curl/commit/9fce2c55d4b0273ac99] + +Signed-off-by: Yogita Urade <yogita.urade@windriver.com> +--- + 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 8b0192a..85b72ba 100644 +--- a/lib/netrc.c ++++ b/lib/netrc.c +@@ -264,7 +264,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)) +@@ -273,6 +274,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 7a6b153..32d8fee 100644 +--- a/tests/data/Makefile.inc ++++ b/tests/data/Makefile.inc +@@ -230,7 +230,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 0000000..69e14d9 +--- /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.40.0 diff --git a/meta/recipes-support/curl/curl_8.7.1.bb b/meta/recipes-support/curl/curl_8.7.1.bb index 439fcb7881..81243d1873 100644 --- a/meta/recipes-support/curl/curl_8.7.1.bb +++ b/meta/recipes-support/curl/curl_8.7.1.bb @@ -20,6 +20,9 @@ SRC_URI = " \ file://CVE-2024-7264-2.patch \ file://CVE-2024-8096.patch \ file://CVE-2024-9681.patch \ + file://CVE-2024-11053-0001.patch \ + file://CVE-2024-11053-0002.patch \ + file://CVE-2024-11053-0003.patch \ " SRC_URI[sha256sum] = "6fea2aac6a4610fbd0400afb0bcddbe7258a64c63f1f68e5855ebc0c659710cd"