xref: /third_party/curl/lib/netrc.c (revision 13498266)
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
45enum 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 */
59static 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
247out:
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 */
279int 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