• 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 #undef __NO_NET_API /* required for AmigaOS to declare getpwuid() */
30 #include <pwd.h>
31 #define __NO_NET_API
32 #endif
33 
34 #include <curl/curl.h>
35 #include "netrc.h"
36 #include "strcase.h"
37 #include "curl_get_line.h"
38 
39 /* The last 3 #include files should be in this order */
40 #include "curl_printf.h"
41 #include "curl_memory.h"
42 #include "memdebug.h"
43 
44 /* Get user and password from .netrc when given a machine name */
45 
46 enum host_lookup_state {
47   NOTHING,
48   HOSTFOUND,    /* the 'machine' keyword was found */
49   HOSTVALID,    /* this is "our" machine! */
50   MACDEF
51 };
52 
53 enum found_state {
54   NONE,
55   LOGIN,
56   PASSWORD
57 };
58 
59 #define FOUND_LOGIN    1
60 #define FOUND_PASSWORD 2
61 
62 #define NETRC_FILE_MISSING 1
63 #define NETRC_FAILED -1
64 #define NETRC_SUCCESS 0
65 
66 #define MAX_NETRC_LINE 16384
67 #define MAX_NETRC_FILE (128*1024)
68 #define MAX_NETRC_TOKEN 4096
69 
file2memory(const char * filename,struct dynbuf * filebuf)70 static CURLcode file2memory(const char *filename, struct dynbuf *filebuf)
71 {
72   CURLcode result = CURLE_OK;
73   FILE *file = fopen(filename, FOPEN_READTEXT);
74   struct dynbuf linebuf;
75   Curl_dyn_init(&linebuf, MAX_NETRC_LINE);
76 
77   if(file) {
78     while(Curl_get_line(&linebuf, file)) {
79       const char *line = Curl_dyn_ptr(&linebuf);
80       /* skip comments on load */
81       while(ISBLANK(*line))
82         line++;
83       if(*line == '#')
84         continue;
85       result = Curl_dyn_add(filebuf, line);
86       if(result)
87         goto done;
88     }
89   }
90 done:
91   Curl_dyn_free(&linebuf);
92   if(file)
93     fclose(file);
94   return result;
95 }
96 
97 /*
98  * Returns zero on success.
99  */
parsenetrc(struct store_netrc * store,const char * host,char ** loginp,char ** passwordp,const char * netrcfile)100 static int parsenetrc(struct store_netrc *store,
101                       const char *host,
102                       char **loginp, /* might point to a username */
103                       char **passwordp,
104                       const char *netrcfile)
105 {
106   int retcode = NETRC_FILE_MISSING;
107   char *login = *loginp;
108   char *password = NULL;
109   bool specific_login = !!login; /* points to something */
110   enum host_lookup_state state = NOTHING;
111   enum found_state keyword = NONE;
112   unsigned char found = 0; /* login + password found bits, as they can come in
113                               any order */
114   bool our_login = FALSE;  /* found our login name */
115   bool done = FALSE;
116   char *netrcbuffer;
117   struct dynbuf token;
118   struct dynbuf *filebuf = &store->filebuf;
119   DEBUGASSERT(!*passwordp);
120   Curl_dyn_init(&token, MAX_NETRC_TOKEN);
121 
122   if(!store->loaded) {
123     if(file2memory(netrcfile, filebuf))
124       return NETRC_FAILED;
125     store->loaded = TRUE;
126   }
127 
128   netrcbuffer = Curl_dyn_ptr(filebuf);
129 
130   while(!done) {
131     char *tok = netrcbuffer;
132     while(tok && !done) {
133       char *tok_end;
134       bool quoted;
135       Curl_dyn_reset(&token);
136       while(ISBLANK(*tok))
137         tok++;
138       /* tok is first non-space letter */
139       if(state == MACDEF) {
140         if((*tok == '\n') || (*tok == '\r'))
141           state = NOTHING; /* end of macro definition */
142       }
143 
144       if(!*tok || (*tok == '\n'))
145         /* end of line  */
146         break;
147 
148       /* leading double-quote means quoted string */
149       quoted = (*tok == '\"');
150 
151       tok_end = tok;
152       if(!quoted) {
153         size_t len = 0;
154         while(!ISSPACE(*tok_end)) {
155           tok_end++;
156           len++;
157         }
158         if(!len || Curl_dyn_addn(&token, tok, len)) {
159           retcode = NETRC_FAILED;
160           goto out;
161         }
162       }
163       else {
164         bool escape = FALSE;
165         bool endquote = FALSE;
166         tok_end++; /* pass the leading quote */
167         while(*tok_end) {
168           char s = *tok_end;
169           if(escape) {
170             escape = FALSE;
171             switch(s) {
172             case 'n':
173               s = '\n';
174               break;
175             case 'r':
176               s = '\r';
177               break;
178             case 't':
179               s = '\t';
180               break;
181             }
182           }
183           else if(s == '\\') {
184             escape = TRUE;
185             tok_end++;
186             continue;
187           }
188           else if(s == '\"') {
189             tok_end++; /* pass the ending quote */
190             endquote = TRUE;
191             break;
192           }
193           if(Curl_dyn_addn(&token, &s, 1)) {
194             retcode = NETRC_FAILED;
195             goto out;
196           }
197           tok_end++;
198         }
199         if(escape || !endquote) {
200           /* bad syntax, get out */
201           retcode = NETRC_FAILED;
202           goto out;
203         }
204       }
205 
206       tok = Curl_dyn_ptr(&token);
207 
208       switch(state) {
209       case NOTHING:
210         if(strcasecompare("macdef", tok))
211           /* Define a macro. A macro is defined with the specified name; its
212              contents begin with the next .netrc line and continue until a
213              null line (consecutive new-line characters) is encountered. */
214           state = MACDEF;
215         else if(strcasecompare("machine", tok)) {
216           /* the next tok is the machine name, this is in itself the delimiter
217              that starts the stuff entered for this machine, after this we
218              need to search for 'login' and 'password'. */
219           state = HOSTFOUND;
220           keyword = NONE;
221           found = 0;
222           our_login = FALSE;
223           Curl_safefree(password);
224           if(!specific_login)
225             Curl_safefree(login);
226         }
227         else if(strcasecompare("default", tok)) {
228           state = HOSTVALID;
229           retcode = NETRC_SUCCESS; /* we did find our host */
230         }
231         break;
232       case MACDEF:
233         if(!*tok)
234           state = NOTHING;
235         break;
236       case HOSTFOUND:
237         if(strcasecompare(host, tok)) {
238           /* and yes, this is our host! */
239           state = HOSTVALID;
240           retcode = NETRC_SUCCESS; /* we did find our host */
241         }
242         else
243           /* not our host */
244           state = NOTHING;
245         break;
246       case HOSTVALID:
247         /* we are now parsing sub-keywords concerning "our" host */
248         if(keyword == LOGIN) {
249           if(specific_login)
250             our_login = !Curl_timestrcmp(login, tok);
251           else {
252             our_login = TRUE;
253             free(login);
254             login = strdup(tok);
255             if(!login) {
256               retcode = NETRC_FAILED; /* allocation failed */
257               goto out;
258             }
259           }
260           found |= FOUND_LOGIN;
261           keyword = NONE;
262         }
263         else if(keyword == PASSWORD) {
264           free(password);
265           password = strdup(tok);
266           if(!password) {
267             retcode = NETRC_FAILED; /* allocation failed */
268             goto out;
269           }
270           if(!specific_login || our_login)
271             found |= FOUND_PASSWORD;
272           keyword = NONE;
273         }
274         else if(strcasecompare("login", tok))
275           keyword = LOGIN;
276         else if(strcasecompare("password", tok))
277           keyword = PASSWORD;
278         else if(strcasecompare("machine", tok)) {
279           /* a new machine here */
280           if(found & FOUND_PASSWORD) {
281             done = TRUE;
282             break;
283           }
284           state = HOSTFOUND;
285           keyword = NONE;
286           found = 0;
287           Curl_safefree(password);
288           if(!specific_login)
289             Curl_safefree(login);
290         }
291         else if(strcasecompare("default", tok)) {
292           state = HOSTVALID;
293           retcode = NETRC_SUCCESS; /* we did find our host */
294           Curl_safefree(password);
295           if(!specific_login)
296             Curl_safefree(login);
297         }
298         if((found == (FOUND_PASSWORD|FOUND_LOGIN)) && our_login) {
299           done = TRUE;
300           break;
301         }
302         break;
303       } /* switch (state) */
304       tok = ++tok_end;
305     }
306     if(!done) {
307       char *nl = NULL;
308       if(tok)
309         nl = strchr(tok, '\n');
310       if(!nl)
311         break;
312       /* point to next line */
313       netrcbuffer = &nl[1];
314     }
315   } /* while !done */
316 
317 out:
318   Curl_dyn_free(&token);
319   if(!retcode) {
320     if(!password && our_login) {
321       /* success without a password, set a blank one */
322       password = strdup("");
323       if(!password)
324         retcode = 1; /* out of memory */
325     }
326     else if(!login && !password)
327       /* a default with no credentials */
328       retcode = NETRC_FILE_MISSING;
329   }
330   if(!retcode) {
331     /* success */
332     if(!specific_login)
333       *loginp = login;
334     *passwordp = password;
335   }
336   else {
337     Curl_dyn_free(filebuf);
338     if(!specific_login)
339       free(login);
340     free(password);
341   }
342 
343   return retcode;
344 }
345 
346 /*
347  * @unittest: 1304
348  *
349  * *loginp and *passwordp MUST be allocated if they are not NULL when passed
350  * in.
351  */
Curl_parsenetrc(struct store_netrc * store,const char * host,char ** loginp,char ** passwordp,char * netrcfile)352 int Curl_parsenetrc(struct store_netrc *store, const char *host,
353                     char **loginp, char **passwordp,
354                     char *netrcfile)
355 {
356   int retcode = 1;
357   char *filealloc = NULL;
358 
359   if(!netrcfile) {
360 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
361     char pwbuf[1024];
362 #endif
363     char *home = NULL;
364     char *homea = curl_getenv("HOME"); /* portable environment reader */
365     if(homea) {
366       home = homea;
367 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
368     }
369     else {
370       struct passwd pw, *pw_res;
371       if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
372          && pw_res) {
373         home = pw.pw_dir;
374       }
375 #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
376     }
377     else {
378       struct passwd *pw;
379       pw = getpwuid(geteuid());
380       if(pw) {
381         home = pw->pw_dir;
382       }
383 #elif defined(_WIN32)
384     }
385     else {
386       homea = curl_getenv("USERPROFILE");
387       if(homea) {
388         home = homea;
389       }
390 #endif
391     }
392 
393     if(!home)
394       return retcode; /* no home directory found (or possibly out of
395                          memory) */
396 
397     filealloc = aprintf("%s%s.netrc", home, DIR_CHAR);
398     if(!filealloc) {
399       free(homea);
400       return -1;
401     }
402     retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
403     free(filealloc);
404 #ifdef _WIN32
405     if((retcode == NETRC_FILE_MISSING) || (retcode == NETRC_FAILED)) {
406       /* fallback to the old-style "_netrc" file */
407       filealloc = aprintf("%s%s_netrc", home, DIR_CHAR);
408       if(!filealloc) {
409         free(homea);
410         return -1;
411       }
412       retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
413       free(filealloc);
414     }
415 #endif
416     free(homea);
417   }
418   else
419     retcode = parsenetrc(store, host, loginp, passwordp, netrcfile);
420   return retcode;
421 }
422 
Curl_netrc_init(struct store_netrc * s)423 void Curl_netrc_init(struct store_netrc *s)
424 {
425   Curl_dyn_init(&s->filebuf, MAX_NETRC_FILE);
426   s->loaded = FALSE;
427 }
Curl_netrc_cleanup(struct store_netrc * s)428 void Curl_netrc_cleanup(struct store_netrc *s)
429 {
430   Curl_dyn_free(&s->filebuf);
431   s->loaded = FALSE;
432 }
433 #endif
434