• 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 #define MAX_NETRC_LINE 4096
57 
58 /*
59  * Returns zero on success.
60  */
parsenetrc(const char * host,char ** loginp,char ** passwordp,char * netrcfile)61 static int parsenetrc(const char *host,
62                       char **loginp,
63                       char **passwordp,
64                       char *netrcfile)
65 {
66   FILE *file;
67   int retcode = NETRC_FILE_MISSING;
68   char *login = *loginp;
69   char *password = *passwordp;
70   bool specific_login = (login && *login != 0);
71   bool login_alloc = FALSE;
72   bool password_alloc = FALSE;
73   enum host_lookup_state state = NOTHING;
74 
75   char state_login = 0;      /* Found a login keyword */
76   char state_password = 0;   /* Found a password keyword */
77   int state_our_login = TRUE;  /* With specific_login, found *our* login
78                                   name (or login-less line) */
79 
80   DEBUGASSERT(netrcfile);
81 
82   file = fopen(netrcfile, FOPEN_READTEXT);
83   if(file) {
84     bool done = FALSE;
85     struct dynbuf buf;
86     Curl_dyn_init(&buf, MAX_NETRC_LINE);
87 
88     while(!done && Curl_get_line(&buf, file)) {
89       char *tok;
90       char *tok_end;
91       bool quoted;
92       char *netrcbuffer = Curl_dyn_ptr(&buf);
93       if(state == MACDEF) {
94         if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
95           state = NOTHING;
96         else
97           continue;
98       }
99       tok = netrcbuffer;
100       while(tok) {
101         while(ISBLANK(*tok))
102           tok++;
103         /* tok is first non-space letter */
104         if(!*tok || (*tok == '#'))
105           /* end of line or the rest is a comment */
106           break;
107 
108         /* leading double-quote means quoted string */
109         quoted = (*tok == '\"');
110 
111         tok_end = tok;
112         if(!quoted) {
113           while(!ISSPACE(*tok_end))
114             tok_end++;
115           *tok_end = 0;
116         }
117         else {
118           bool escape = FALSE;
119           bool endquote = FALSE;
120           char *store = tok;
121           tok_end++; /* pass the leading quote */
122           while(*tok_end) {
123             char s = *tok_end;
124             if(escape) {
125               escape = FALSE;
126               switch(s) {
127               case 'n':
128                 s = '\n';
129                 break;
130               case 'r':
131                 s = '\r';
132                 break;
133               case 't':
134                 s = '\t';
135                 break;
136               }
137             }
138             else if(s == '\\') {
139               escape = TRUE;
140               tok_end++;
141               continue;
142             }
143             else if(s == '\"') {
144               tok_end++; /* pass the ending quote */
145               endquote = TRUE;
146               break;
147             }
148             *store++ = s;
149             tok_end++;
150           }
151           *store = 0;
152           if(escape || !endquote) {
153             /* bad syntax, get out */
154             retcode = NETRC_FAILED;
155             goto out;
156           }
157         }
158 
159         if((login && *login) && (password && *password)) {
160           done = TRUE;
161           break;
162         }
163 
164         switch(state) {
165         case NOTHING:
166           if(strcasecompare("macdef", tok)) {
167             /* Define a macro. A macro is defined with the specified name; its
168                contents begin with the next .netrc line and continue until a
169                null line (consecutive new-line characters) is encountered. */
170             state = MACDEF;
171           }
172           else if(strcasecompare("machine", tok)) {
173             /* the next tok is the machine name, this is in itself the
174                delimiter that starts the stuff entered for this machine,
175                after this we need to search for 'login' and
176                'password'. */
177             state = HOSTFOUND;
178           }
179           else if(strcasecompare("default", tok)) {
180             state = HOSTVALID;
181             retcode = NETRC_SUCCESS; /* we did find our host */
182           }
183           break;
184         case MACDEF:
185           if(!strlen(tok)) {
186             state = NOTHING;
187           }
188           break;
189         case HOSTFOUND:
190           if(strcasecompare(host, tok)) {
191             /* and yes, this is our host! */
192             state = HOSTVALID;
193             retcode = NETRC_SUCCESS; /* we did find our host */
194           }
195           else
196             /* not our host */
197             state = NOTHING;
198           break;
199         case HOSTVALID:
200           /* we are now parsing sub-keywords concerning "our" host */
201           if(state_login) {
202             if(specific_login) {
203               state_our_login = !Curl_timestrcmp(login, tok);
204             }
205             else if(!login || Curl_timestrcmp(login, tok)) {
206               if(login_alloc) {
207                 free(login);
208                 login_alloc = FALSE;
209               }
210               login = strdup(tok);
211               if(!login) {
212                 retcode = NETRC_FAILED; /* allocation failed */
213                 goto out;
214               }
215               login_alloc = TRUE;
216             }
217             state_login = 0;
218           }
219           else if(state_password) {
220             if((state_our_login || !specific_login)
221                && (!password || Curl_timestrcmp(password, tok))) {
222               if(password_alloc) {
223                 free(password);
224                 password_alloc = FALSE;
225               }
226               password = strdup(tok);
227               if(!password) {
228                 retcode = NETRC_FAILED; /* allocation failed */
229                 goto out;
230               }
231               password_alloc = TRUE;
232             }
233             state_password = 0;
234           }
235           else if(strcasecompare("login", tok))
236             state_login = 1;
237           else if(strcasecompare("password", tok))
238             state_password = 1;
239           else if(strcasecompare("machine", tok)) {
240             /* ok, there's machine here go => */
241             state = HOSTFOUND;
242             state_our_login = FALSE;
243           }
244           else if(strcasecompare("default", tok)) {
245             state = HOSTVALID;
246             retcode = NETRC_SUCCESS; /* we did find our host */
247             Curl_safefree(password);
248             if(!specific_login)
249               if(login_alloc) {
250                 free(login);
251                 login_alloc = FALSE;
252               }
253           }
254           break;
255         } /* switch (state) */
256         tok = ++tok_end;
257       }
258     } /* while Curl_get_line() */
259 
260 out:
261     Curl_dyn_free(&buf);
262     if(!retcode) {
263       if(!password && state_our_login) {
264         /* success without a password, set a blank one */
265         password = strdup("");
266         if(!password)
267           retcode = 1; /* out of memory */
268       }
269       else if(!login && !password)
270         /* a default with no credentials */
271         retcode = NETRC_FILE_MISSING;
272     }
273     if(!retcode) {
274       /* success */
275       if(login_alloc) {
276         if(*loginp)
277           free(*loginp);
278         *loginp = login;
279       }
280       if(password_alloc) {
281         if(*passwordp)
282           free(*passwordp);
283         *passwordp = password;
284       }
285     }
286     else {
287       if(login_alloc)
288         free(login);
289       if(password_alloc)
290         free(password);
291     }
292     fclose(file);
293   }
294 
295   return retcode;
296 }
297 
298 /*
299  * @unittest: 1304
300  *
301  * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
302  * in.
303  */
Curl_parsenetrc(const char * host,char ** loginp,char ** passwordp,char * netrcfile)304 int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
305                     char *netrcfile)
306 {
307   int retcode = 1;
308   char *filealloc = NULL;
309 
310   if(!netrcfile) {
311 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
312     char pwbuf[1024];
313 #endif
314     char *home = NULL;
315     char *homea = curl_getenv("HOME"); /* portable environment reader */
316     if(homea) {
317       home = homea;
318 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
319     }
320     else {
321       struct passwd pw, *pw_res;
322       if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
323          && pw_res) {
324         home = pw.pw_dir;
325       }
326 #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
327     }
328     else {
329       struct passwd *pw;
330       pw = getpwuid(geteuid());
331       if(pw) {
332         home = pw->pw_dir;
333       }
334 #elif defined(_WIN32)
335     }
336     else {
337       homea = curl_getenv("USERPROFILE");
338       if(homea) {
339         home = homea;
340       }
341 #endif
342     }
343 
344     if(!home)
345       return retcode; /* no home directory found (or possibly out of
346                          memory) */
347 
348     filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
349     if(!filealloc) {
350       free(homea);
351       return -1;
352     }
353     retcode = parsenetrc(host, loginp, passwordp, filealloc);
354     free(filealloc);
355 #ifdef _WIN32
356     if(retcode == NETRC_FILE_MISSING) {
357       /* fallback to the old-style "_netrc" file */
358       filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
359       if(!filealloc) {
360         free(homea);
361         return -1;
362       }
363       retcode = parsenetrc(host, loginp, passwordp, filealloc);
364       free(filealloc);
365     }
366 #endif
367     free(homea);
368   }
369   else
370     retcode = parsenetrc(host, loginp, passwordp, netrcfile);
371   return retcode;
372 }
373 
374 #endif
375