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