• 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           break;
242         } /* switch (state) */
243         tok = ++tok_end;
244       }
245     } /* while Curl_get_line() */
246 
247 out:
248     if(!retcode) {
249       /* success */
250       if(login_alloc) {
251         if(*loginp)
252           free(*loginp);
253         *loginp = login;
254       }
255       if(password_alloc) {
256         if(*passwordp)
257           free(*passwordp);
258         *passwordp = password;
259       }
260     }
261     else {
262       if(login_alloc)
263         free(login);
264       if(password_alloc)
265         free(password);
266     }
267     fclose(file);
268   }
269 
270   return retcode;
271 }
272 
273 /*
274  * @unittest: 1304
275  *
276  * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
277  * in.
278  */
Curl_parsenetrc(const char * host,char ** loginp,char ** passwordp,char * netrcfile)279 int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
280                     char *netrcfile)
281 {
282   int retcode = 1;
283   char *filealloc = NULL;
284 
285   if(!netrcfile) {
286 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
287     char pwbuf[1024];
288 #endif
289     char *home = NULL;
290     char *homea = curl_getenv("HOME"); /* portable environment reader */
291     if(homea) {
292       home = homea;
293 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
294     }
295     else {
296       struct passwd pw, *pw_res;
297       if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
298          && pw_res) {
299         home = pw.pw_dir;
300       }
301 #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
302     }
303     else {
304       struct passwd *pw;
305       pw = getpwuid(geteuid());
306       if(pw) {
307         home = pw->pw_dir;
308       }
309 #elif defined(_WIN32)
310     }
311     else {
312       homea = curl_getenv("USERPROFILE");
313       if(homea) {
314         home = homea;
315       }
316 #endif
317     }
318 
319     if(!home)
320       return retcode; /* no home directory found (or possibly out of
321                          memory) */
322 
323     filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
324     if(!filealloc) {
325       free(homea);
326       return -1;
327     }
328     retcode = parsenetrc(host, loginp, passwordp, filealloc);
329     free(filealloc);
330 #ifdef WIN32
331     if(retcode == NETRC_FILE_MISSING) {
332       /* fallback to the old-style "_netrc" file */
333       filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
334       if(!filealloc) {
335         free(homea);
336         return -1;
337       }
338       retcode = parsenetrc(host, loginp, passwordp, filealloc);
339       free(filealloc);
340     }
341 #endif
342     free(homea);
343   }
344   else
345     retcode = parsenetrc(host, loginp, passwordp, netrcfile);
346   return retcode;
347 }
348 
349 #endif
350