• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2021, 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  ***************************************************************************/
22 
23 #include "curl_setup.h"
24 #ifndef CURL_DISABLE_NETRC
25 
26 #ifdef HAVE_PWD_H
27 #include <pwd.h>
28 #endif
29 
30 #include <curl/curl.h>
31 #include "netrc.h"
32 #include "strtok.h"
33 #include "strcase.h"
34 
35 /* The last 3 #include files should be in this order */
36 #include "curl_printf.h"
37 #include "curl_memory.h"
38 #include "memdebug.h"
39 
40 /* Get user and password from .netrc when given a machine name */
41 
42 enum host_lookup_state {
43   NOTHING,
44   HOSTFOUND,    /* the 'machine' keyword was found */
45   HOSTVALID,    /* this is "our" machine! */
46   MACDEF
47 };
48 
49 #define NETRC_FILE_MISSING 1
50 #define NETRC_FAILED -1
51 #define NETRC_SUCCESS 0
52 
53 /*
54  * Returns zero on success.
55  */
parsenetrc(const char * host,char ** loginp,char ** passwordp,bool * login_changed,bool * password_changed,char * netrcfile)56 static int parsenetrc(const char *host,
57                       char **loginp,
58                       char **passwordp,
59                       bool *login_changed,
60                       bool *password_changed,
61                       char *netrcfile)
62 {
63   FILE *file;
64   int retcode = NETRC_FILE_MISSING;
65   char *login = *loginp;
66   char *password = *passwordp;
67   bool specific_login = (login && *login != 0);
68   bool login_alloc = FALSE;
69   bool password_alloc = FALSE;
70   enum host_lookup_state state = NOTHING;
71 
72   char state_login = 0;      /* Found a login keyword */
73   char state_password = 0;   /* Found a password keyword */
74   int state_our_login = FALSE;  /* With specific_login, found *our* login
75                                    name */
76 
77   DEBUGASSERT(netrcfile);
78 
79   file = fopen(netrcfile, FOPEN_READTEXT);
80   if(file) {
81     char *tok;
82     char *tok_buf;
83     bool done = FALSE;
84     char netrcbuffer[4096];
85     int  netrcbuffsize = (int)sizeof(netrcbuffer);
86 
87     while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
88       if(state == MACDEF) {
89         if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
90           state = NOTHING;
91         else
92           continue;
93       }
94       tok = strtok_r(netrcbuffer, " \t\n", &tok_buf);
95       if(tok && *tok == '#')
96         /* treat an initial hash as a comment line */
97         continue;
98       while(tok) {
99         if((login && *login) && (password && *password)) {
100           done = TRUE;
101           break;
102         }
103 
104         switch(state) {
105         case NOTHING:
106           if(strcasecompare("macdef", tok)) {
107             /* Define a macro. A macro is defined with the specified name; its
108                contents begin with the next .netrc line and continue until a
109                null line (consecutive new-line characters) is encountered. */
110             state = MACDEF;
111           }
112           else if(strcasecompare("machine", tok)) {
113             /* the next tok is the machine name, this is in itself the
114                delimiter that starts the stuff entered for this machine,
115                after this we need to search for 'login' and
116                'password'. */
117             state = HOSTFOUND;
118           }
119           else if(strcasecompare("default", tok)) {
120             state = HOSTVALID;
121             retcode = NETRC_SUCCESS; /* we did find our host */
122           }
123           break;
124         case MACDEF:
125           if(!strlen(tok)) {
126             state = NOTHING;
127           }
128           break;
129         case HOSTFOUND:
130           if(strcasecompare(host, tok)) {
131             /* and yes, this is our host! */
132             state = HOSTVALID;
133             retcode = NETRC_SUCCESS; /* we did find our host */
134           }
135           else
136             /* not our host */
137             state = NOTHING;
138           break;
139         case HOSTVALID:
140           /* we are now parsing sub-keywords concerning "our" host */
141           if(state_login) {
142             if(specific_login) {
143               state_our_login = !Curl_timestrcmp(login, tok);
144             }
145             else if(!login || Curl_timestrcmp(login, tok)) {
146               if(login_alloc) {
147                 free(login);
148                 login_alloc = FALSE;
149               }
150               login = strdup(tok);
151               if(!login) {
152                 retcode = NETRC_FAILED; /* allocation failed */
153                 goto out;
154               }
155               login_alloc = TRUE;
156             }
157             state_login = 0;
158           }
159           else if(state_password) {
160             if((state_our_login || !specific_login)
161                && (!password || Curl_timestrcmp(password, tok))) {
162               if(password_alloc) {
163                 free(password);
164                 password_alloc = FALSE;
165               }
166               password = strdup(tok);
167               if(!password) {
168                 retcode = NETRC_FAILED; /* allocation failed */
169                 goto out;
170               }
171               password_alloc = TRUE;
172             }
173             state_password = 0;
174           }
175           else if(strcasecompare("login", tok))
176             state_login = 1;
177           else if(strcasecompare("password", tok))
178             state_password = 1;
179           else if(strcasecompare("machine", tok)) {
180             /* ok, there's machine here go => */
181             state = HOSTFOUND;
182             state_our_login = FALSE;
183           }
184           break;
185         } /* switch (state) */
186 
187         tok = strtok_r(NULL, " \t\n", &tok_buf);
188       } /* while(tok) */
189     } /* while fgets() */
190 
191     out:
192     if(!retcode) {
193       /* success */
194       *login_changed = FALSE;
195       *password_changed = FALSE;
196       if(login_alloc) {
197         if(*loginp)
198           free(*loginp);
199         *loginp = login;
200         *login_changed = TRUE;
201       }
202       if(password_alloc) {
203         if(*passwordp)
204           free(*passwordp);
205         *passwordp = password;
206         *password_changed = TRUE;
207       }
208     }
209     else {
210       if(login_alloc)
211         free(login);
212       if(password_alloc)
213         free(password);
214     }
215     fclose(file);
216   }
217 
218   return retcode;
219 }
220 
221 /*
222  * @unittest: 1304
223  *
224  * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
225  * in.
226  */
Curl_parsenetrc(const char * host,char ** loginp,char ** passwordp,bool * login_changed,bool * password_changed,char * netrcfile)227 int Curl_parsenetrc(const char *host,
228                     char **loginp,
229                     char **passwordp,
230                     bool *login_changed,
231                     bool *password_changed,
232                     char *netrcfile)
233 {
234   int retcode = 1;
235   char *filealloc = NULL;
236 
237   if(!netrcfile) {
238     char *home = NULL;
239     char *homea = curl_getenv("HOME"); /* portable environment reader */
240     if(homea) {
241       home = homea;
242 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
243     }
244     else {
245       struct passwd pw, *pw_res;
246       char pwbuf[1024];
247       if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
248          && pw_res) {
249         home = pw.pw_dir;
250       }
251 #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
252     }
253     else {
254       struct passwd *pw;
255       pw = getpwuid(geteuid());
256       if(pw) {
257         home = pw->pw_dir;
258       }
259 #endif
260     }
261 
262     if(!home)
263       return retcode; /* no home directory found (or possibly out of
264                          memory) */
265 
266     filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
267     if(!filealloc) {
268       free(homea);
269       return -1;
270     }
271     retcode = parsenetrc(host, loginp, passwordp, login_changed,
272                          password_changed, filealloc);
273     free(filealloc);
274 #ifdef WIN32
275     if(retcode == NETRC_FILE_MISSING) {
276       /* fallback to the old-style "_netrc" file */
277       filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
278       if(!filealloc) {
279         free(homea);
280         return -1;
281       }
282       retcode = parsenetrc(host, loginp, passwordp, login_changed,
283                            password_changed, filealloc);
284       free(filealloc);
285     }
286 #endif
287     free(homea);
288   }
289   else
290     retcode = parsenetrc(host, loginp, passwordp, login_changed,
291                          password_changed, netrcfile);
292   return retcode;
293 }
294 
295 #endif
296