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