• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 #ifndef CURL_DISABLE_NETRC
27 
28 #ifdef HAVE_PWD_H
29 #include <pwd.h>
30 #endif
31 
32 #include <curl/curl.h>
33 #include "netrc.h"
34 #include "strtok.h"
35 #include "strcase.h"
36 #include "curl_get_line.h"
37 
38 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
41 #include "memdebug.h"
42 
43 /* Get user and password from .netrc when given a machine name */
44 
45 enum host_lookup_state {
46   NOTHING,
47   HOSTFOUND,    /* the 'machine' keyword was found */
48   HOSTVALID,    /* this is "our" machine! */
49   MACDEF
50 };
51 
52 #define NETRC_FILE_MISSING 1
53 #define NETRC_FAILED -1
54 #define NETRC_SUCCESS 0
55 
56 /*
57  * Returns zero on success.
58  */
parsenetrc(const char * host,char ** loginp,char ** passwordp,char * netrcfile)59 static int parsenetrc(const char *host,
60                       char **loginp,
61                       char **passwordp,
62                       char *netrcfile)
63 {
64   FILE *file;
65   int retcode = NETRC_FILE_MISSING;
66   char *login = *loginp;
67   char *password = *passwordp;
68   bool specific_login = (login && *login != 0);
69   bool login_alloc = FALSE;
70   bool password_alloc = FALSE;
71   enum host_lookup_state state = NOTHING;
72 
73   char state_login = 0;      /* Found a login keyword */
74   char state_password = 0;   /* Found a password keyword */
75   int state_our_login = TRUE;  /* With specific_login, found *our* login
76                                   name (or login-less line) */
77 
78   DEBUGASSERT(netrcfile);
79 
80   file = fopen(netrcfile, FOPEN_READTEXT);
81   if(file) {
82     bool done = FALSE;
83     char netrcbuffer[4096];
84     int  netrcbuffsize = (int)sizeof(netrcbuffer);
85 
86     while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) {
87       char *tok;
88       char *tok_end;
89       bool quoted;
90       if(state == MACDEF) {
91         if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
92           state = NOTHING;
93         else
94           continue;
95       }
96       tok = netrcbuffer;
97       while(tok) {
98         while(ISBLANK(*tok))
99           tok++;
100         /* tok is first non-space letter */
101         if(!*tok || (*tok == '#'))
102           /* end of line or the rest is a comment */
103           break;
104 
105         /* leading double-quote means quoted string */
106         quoted = (*tok == '\"');
107 
108         tok_end = tok;
109         if(!quoted) {
110           while(!ISSPACE(*tok_end))
111             tok_end++;
112           *tok_end = 0;
113         }
114         else {
115           bool escape = FALSE;
116           bool endquote = FALSE;
117           char *store = tok;
118           tok_end++; /* pass the leading quote */
119           while(*tok_end) {
120             char s = *tok_end;
121             if(escape) {
122               escape = FALSE;
123               switch(s) {
124               case 'n':
125                 s = '\n';
126                 break;
127               case 'r':
128                 s = '\r';
129                 break;
130               case 't':
131                 s = '\t';
132                 break;
133               }
134             }
135             else if(s == '\\') {
136               escape = TRUE;
137               tok_end++;
138               continue;
139             }
140             else if(s == '\"') {
141               tok_end++; /* pass the ending quote */
142               endquote = TRUE;
143               break;
144             }
145             *store++ = s;
146             tok_end++;
147           }
148           *store = 0;
149           if(escape || !endquote) {
150             /* bad syntax, get out */
151             retcode = NETRC_FAILED;
152             goto out;
153           }
154         }
155 
156         if((login && *login) && (password && *password)) {
157           done = TRUE;
158           break;
159         }
160 
161         switch(state) {
162         case NOTHING:
163           if(strcasecompare("macdef", tok)) {
164             /* Define a macro. A macro is defined with the specified name; its
165                contents begin with the next .netrc line and continue until a
166                null line (consecutive new-line characters) is encountered. */
167             state = MACDEF;
168           }
169           else if(strcasecompare("machine", tok)) {
170             /* the next tok is the machine name, this is in itself the
171                delimiter that starts the stuff entered for this machine,
172                after this we need to search for 'login' and
173                'password'. */
174             state = HOSTFOUND;
175           }
176           else if(strcasecompare("default", tok)) {
177             state = HOSTVALID;
178             retcode = NETRC_SUCCESS; /* we did find our host */
179           }
180           break;
181         case MACDEF:
182           if(!strlen(tok)) {
183             state = NOTHING;
184           }
185           break;
186         case HOSTFOUND:
187           if(strcasecompare(host, tok)) {
188             /* and yes, this is our host! */
189             state = HOSTVALID;
190             retcode = NETRC_SUCCESS; /* we did find our host */
191           }
192           else
193             /* not our host */
194             state = NOTHING;
195           break;
196         case HOSTVALID:
197           /* we are now parsing sub-keywords concerning "our" host */
198           if(state_login) {
199             if(specific_login) {
200               state_our_login = !Curl_timestrcmp(login, tok);
201             }
202             else if(!login || Curl_timestrcmp(login, tok)) {
203               if(login_alloc) {
204                 free(login);
205                 login_alloc = FALSE;
206               }
207               login = strdup(tok);
208               if(!login) {
209                 retcode = NETRC_FAILED; /* allocation failed */
210                 goto out;
211               }
212               login_alloc = TRUE;
213             }
214             state_login = 0;
215           }
216           else if(state_password) {
217             if((state_our_login || !specific_login)
218                && (!password || Curl_timestrcmp(password, tok))) {
219               if(password_alloc) {
220                 free(password);
221                 password_alloc = FALSE;
222               }
223               password = strdup(tok);
224               if(!password) {
225                 retcode = NETRC_FAILED; /* allocation failed */
226                 goto out;
227               }
228               password_alloc = TRUE;
229             }
230             state_password = 0;
231           }
232           else if(strcasecompare("login", tok))
233             state_login = 1;
234           else if(strcasecompare("password", tok))
235             state_password = 1;
236           else if(strcasecompare("machine", tok)) {
237             /* ok, there's machine here go => */
238             state = HOSTFOUND;
239             state_our_login = FALSE;
240           }
241           else if(strcasecompare("default", tok)) {
242             state = HOSTVALID;
243             retcode = NETRC_SUCCESS; /* we did find our host */
244             Curl_safefree(password);
245             if(!specific_login)
246               if(login_alloc) {
247                 free(login);
248                 login_alloc = FALSE;
249               }
250           }
251           break;
252         } /* switch (state) */
253         tok = ++tok_end;
254       }
255     } /* while Curl_get_line() */
256 
257 out:
258     if(!retcode && !password && state_our_login) {
259       /* success without a password, set a blank one */
260       password = strdup("");
261       if(!password)
262         retcode = 1; /* out of memory */
263     }
264     if(!retcode) {
265       /* success */
266       if(login_alloc) {
267         if(*loginp)
268           free(*loginp);
269         *loginp = login;
270       }
271       if(password_alloc) {
272         if(*passwordp)
273           free(*passwordp);
274         *passwordp = password;
275       }
276     }
277     else {
278       if(login_alloc)
279         free(login);
280       if(password_alloc)
281         free(password);
282     }
283     fclose(file);
284   }
285 
286   return retcode;
287 }
288 
289 /*
290  * @unittest: 1304
291  *
292  * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
293  * in.
294  */
Curl_parsenetrc(const char * host,char ** loginp,char ** passwordp,char * netrcfile)295 int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
296                     char *netrcfile)
297 {
298   int retcode = 1;
299   char *filealloc = NULL;
300 
301   if(!netrcfile) {
302 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
303     char pwbuf[1024];
304 #endif
305     char *home = NULL;
306     char *homea = curl_getenv("HOME"); /* portable environment reader */
307     if(homea) {
308       home = homea;
309 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
310     }
311     else {
312       struct passwd pw, *pw_res;
313       if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
314          && pw_res) {
315         home = pw.pw_dir;
316       }
317 #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
318     }
319     else {
320       struct passwd *pw;
321       pw = getpwuid(geteuid());
322       if(pw) {
323         home = pw->pw_dir;
324       }
325 #elif defined(_WIN32)
326     }
327     else {
328       homea = curl_getenv("USERPROFILE");
329       if(homea) {
330         home = homea;
331       }
332 #endif
333     }
334 
335     if(!home)
336       return retcode; /* no home directory found (or possibly out of
337                          memory) */
338 
339     filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
340     if(!filealloc) {
341       free(homea);
342       return -1;
343     }
344     retcode = parsenetrc(host, loginp, passwordp, filealloc);
345     free(filealloc);
346 #ifdef _WIN32
347     if(retcode == NETRC_FILE_MISSING) {
348       /* fallback to the old-style "_netrc" file */
349       filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
350       if(!filealloc) {
351         free(homea);
352         return -1;
353       }
354       retcode = parsenetrc(host, loginp, passwordp, filealloc);
355       free(filealloc);
356     }
357 #endif
358     free(homea);
359   }
360   else
361     retcode = parsenetrc(host, loginp, passwordp, netrcfile);
362   return retcode;
363 }
364 
365 #endif
366