]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
netrc: support quoted strings
authorDaniel Stenberg <daniel@haxx.se>
Tue, 31 May 2022 07:04:56 +0000 (09:04 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 31 May 2022 07:04:56 +0000 (09:04 +0200)
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
lib/url.c

index 444e4eebb5ea5438859363f2a264cddc57b4da70..67aa6a62a48a9c9118e68f21c86f85251a70ad51 100644 (file)
@@ -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:
index 6907664b718828c756846c415ac762ddd93efc66..d45fa57eaee2218d3697c9cc9fbd6387f3e55ff0 100644 (file)
--- 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