From eeaae10c0fb27aa066fdc296074edeacfdeb6522 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 31 May 2022 09:04:56 +0200 Subject: [PATCH] netrc: support quoted strings The .netrc parser now accepts strings within double-quotes in order to deal with for example passwords containing white space - which previously was not possible. A password that starts with a double-quote also ends with one, and double-quotes themselves are escaped with backslashes, like \". It also supports \n, \r and \t for newline, carriage return and tabs respectively. If the password does not start with a double quote, it will end at first white space and no escaping is performed. WARNING: this change is not entirely backwards compatible. If anyone previously used a double-quote as the first letter of their password, the parser will now get it differently compared to before. This is highly unfortunate but hard to avoid. Reported-by: ImpatientHippo on GitHub Fixes #8908 Closes #8937 --- lib/netrc.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++------- lib/url.c | 3 ++- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/lib/netrc.c b/lib/netrc.c index 444e4eebb5..67aa6a62a4 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -78,24 +78,80 @@ static int parsenetrc(const char *host, file = fopen(netrcfile, FOPEN_READTEXT); if(file) { - char *tok; - char *tok_buf; bool done = FALSE; char netrcbuffer[4096]; int netrcbuffsize = (int)sizeof(netrcbuffer); while(!done && fgets(netrcbuffer, netrcbuffsize, file)) { + char *tok; + char *tok_end; + bool quoted; if(state == MACDEF) { if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) state = NOTHING; else continue; } - tok = strtok_r(netrcbuffer, " \t\n", &tok_buf); - if(tok && *tok == '#') - /* treat an initial hash as a comment line */ - continue; + tok = netrcbuffer; while(tok) { + while(ISSPACE(*tok)) + tok++; + /* tok is first non-space letter */ + if(!*tok || (*tok == '#')) + /* end of line or the rest is a comment */ + break; + + /* leading double-quote means quoted string */ + quoted = (*tok == '\"'); + + tok_end = tok; + if(!quoted) { + while(!ISSPACE(*tok_end)) + tok_end++; + *tok_end = 0; + } + 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; + break; + } + *store++ = s; + tok_end++; + } + *store = 0; + if(escape || !endquote) { + /* bad syntax, get out */ + retcode = NETRC_FAILED; + goto out; + } + } + if((login && *login) && (password && *password)) { done = TRUE; break; @@ -183,9 +239,8 @@ static int parsenetrc(const char *host, } break; } /* switch (state) */ - - tok = strtok_r(NULL, " \t\n", &tok_buf); - } /* while(tok) */ + tok = ++tok_end; + } } /* while fgets() */ out: diff --git a/lib/url.c b/lib/url.c index 6907664b71..d45fa57eae 100644 --- a/lib/url.c +++ b/lib/url.c @@ -3011,7 +3011,8 @@ static CURLcode override_login(struct Curl_easy *data, conn->host.name, data->set.str[STRING_NETRC_FILE]); } else if(ret < 0) { - return CURLE_OUT_OF_MEMORY; + failf(data, ".netrc parser error"); + return CURLE_READ_ERROR; } else { /* set bits.netrc TRUE to remember that we got the name from a .netrc -- 2.47.3