From: Daniel Stenberg Date: Tue, 31 May 2022 07:04:56 +0000 (+0200) Subject: netrc: support quoted strings X-Git-Tag: curl-7_84_0~120 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eeaae10c0fb27aa066fdc296074edeacfdeb6522;p=thirdparty%2Fcurl.git 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 --- 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