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