1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, 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.haxx.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
25#ifdef HAVE_PWD_H
26#include <pwd.h>
27#endif
28
29#include <curl/curl.h>
30#include "netrc.h"
31
32#include "strequal.h"
33#include "strtok.h"
34#include "rawstr.h"
35
36/* The last 3 #include files should be in this order */
37#include "curl_printf.h"
38#include "curl_memory.h"
39#include "memdebug.h"
40
41/* Get user and password from .netrc when given a machine name */
42
43enum host_lookup_state {
44  NOTHING,
45  HOSTFOUND,    /* the 'machine' keyword was found */
46  HOSTVALID     /* this is "our" machine! */
47};
48
49/*
50 * @unittest: 1304
51 *
52 * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
53 * in.
54 */
55int Curl_parsenetrc(const char *host,
56                    char **loginp,
57                    char **passwordp,
58                    char *netrcfile)
59{
60  FILE *file;
61  int retcode=1;
62  int specific_login = (*loginp && **loginp != 0);
63  bool netrc_alloc = FALSE;
64  enum host_lookup_state state=NOTHING;
65
66  char state_login=0;      /* Found a login keyword */
67  char state_password=0;   /* Found a password keyword */
68  int state_our_login=FALSE;  /* With specific_login, found *our* login name */
69
70#define NETRC DOT_CHAR "netrc"
71
72  if(!netrcfile) {
73    bool home_alloc = FALSE;
74    char *home = curl_getenv("HOME"); /* portable environment reader */
75    if(home) {
76      home_alloc = TRUE;
77#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
78    }
79    else {
80      struct passwd pw, *pw_res;
81      char pwbuf[1024];
82      if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
83         && pw_res) {
84        home = strdup(pw.pw_dir);
85        if(!home)
86          return CURLE_OUT_OF_MEMORY;
87        home_alloc = TRUE;
88      }
89#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
90    }
91    else {
92      struct passwd *pw;
93      pw= getpwuid(geteuid());
94      if(pw) {
95        home = pw->pw_dir;
96      }
97#endif
98    }
99
100    if(!home)
101      return retcode; /* no home directory found (or possibly out of memory) */
102
103    netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC);
104    if(home_alloc)
105      free(home);
106    if(!netrcfile) {
107      return -1;
108    }
109    netrc_alloc = TRUE;
110  }
111
112  file = fopen(netrcfile, FOPEN_READTEXT);
113  if(netrc_alloc)
114    free(netrcfile);
115  if(file) {
116    char *tok;
117    char *tok_buf;
118    bool done=FALSE;
119    char netrcbuffer[256];
120    int  netrcbuffsize = (int)sizeof(netrcbuffer);
121
122    while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
123      tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
124      while(!done && tok) {
125
126        if((*loginp && **loginp) && (*passwordp && **passwordp)) {
127          done=TRUE;
128          break;
129        }
130
131        switch(state) {
132        case NOTHING:
133          if(Curl_raw_equal("machine", tok)) {
134            /* the next tok is the machine name, this is in itself the
135               delimiter that starts the stuff entered for this machine,
136               after this we need to search for 'login' and
137               'password'. */
138            state=HOSTFOUND;
139          }
140          else if(Curl_raw_equal("default", tok)) {
141            state=HOSTVALID;
142            retcode=0; /* we did find our host */
143          }
144          break;
145        case HOSTFOUND:
146          if(Curl_raw_equal(host, tok)) {
147            /* and yes, this is our host! */
148            state=HOSTVALID;
149            retcode=0; /* we did find our host */
150          }
151          else
152            /* not our host */
153            state=NOTHING;
154          break;
155        case HOSTVALID:
156          /* we are now parsing sub-keywords concerning "our" host */
157          if(state_login) {
158            if(specific_login) {
159              state_our_login = Curl_raw_equal(*loginp, tok);
160            }
161            else {
162              free(*loginp);
163              *loginp = strdup(tok);
164              if(!*loginp) {
165                retcode = -1; /* allocation failed */
166                goto out;
167              }
168            }
169            state_login=0;
170          }
171          else if(state_password) {
172            if(state_our_login || !specific_login) {
173              free(*passwordp);
174              *passwordp = strdup(tok);
175              if(!*passwordp) {
176                retcode = -1; /* allocation failed */
177                goto out;
178              }
179            }
180            state_password=0;
181          }
182          else if(Curl_raw_equal("login", tok))
183            state_login=1;
184          else if(Curl_raw_equal("password", tok))
185            state_password=1;
186          else if(Curl_raw_equal("machine", tok)) {
187            /* ok, there's machine here go => */
188            state = HOSTFOUND;
189            state_our_login = FALSE;
190          }
191          break;
192        } /* switch (state) */
193
194        tok = strtok_r(NULL, " \t\n", &tok_buf);
195      } /* while(tok) */
196    } /* while fgets() */
197
198    out:
199    fclose(file);
200  }
201
202  return retcode;
203}
204