1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2021, 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 ***************************************************************************/
22
23#include "curl_setup.h"
24#ifndef CURL_DISABLE_NETRC
25
26#ifdef HAVE_PWD_H
27#include <pwd.h>
28#endif
29
30#include <curl/curl.h>
31#include "netrc.h"
32#include "strtok.h"
33#include "strcase.h"
34
35/* The last 3 #include files should be in this order */
36#include "curl_printf.h"
37#include "curl_memory.h"
38#include "memdebug.h"
39
40/* Get user and password from .netrc when given a machine name */
41
42enum host_lookup_state {
43 NOTHING,
44 HOSTFOUND, /* the 'machine' keyword was found */
45 HOSTVALID, /* this is "our" machine! */
46 MACDEF
47};
48
49#define NETRC_FILE_MISSING 1
50#define NETRC_FAILED -1
51#define NETRC_SUCCESS 0
52
53/*
54 * Returns zero on success.
55 */
56static int parsenetrc(const char *host,
57 char **loginp,
58 char **passwordp,
59 bool *login_changed,
60 bool *password_changed,
61 char *netrcfile)
62{
63 FILE *file;
64 int retcode = NETRC_FILE_MISSING;
65 char *login = *loginp;
66 char *password = *passwordp;
67 bool specific_login = (login && *login != 0);
68 bool login_alloc = FALSE;
69 bool password_alloc = FALSE;
70 enum host_lookup_state state = NOTHING;
71
72 char state_login = 0; /* Found a login keyword */
73 char state_password = 0; /* Found a password keyword */
74 int state_our_login = FALSE; /* With specific_login, found *our* login
75 name */
76
77 DEBUGASSERT(netrcfile);
78
79 file = fopen(netrcfile, FOPEN_READTEXT);
80 if(file) {
81 char *tok;
82 char *tok_buf;
83 bool done = FALSE;
84 char netrcbuffer[4096];
85 int netrcbuffsize = (int)sizeof(netrcbuffer);
86
87 while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
88 if(state == MACDEF) {
89 if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
90 state = NOTHING;
91 else
92 continue;
93 }
94 tok = strtok_r(netrcbuffer, " \t\n", &tok_buf);
95 if(tok && *tok == '#')
96 /* treat an initial hash as a comment line */
97 continue;
98 while(tok) {
99 if((login && *login) && (password && *password)) {
100 done = TRUE;
101 break;
102 }
103
104 switch(state) {
105 case NOTHING:
106 if(strcasecompare("macdef", tok)) {
107 /* Define a macro. A macro is defined with the specified name; its
108 contents begin with the next .netrc line and continue until a
109 null line (consecutive new-line characters) is encountered. */
110 state = MACDEF;
111 }
112 else if(strcasecompare("machine", tok)) {
113 /* the next tok is the machine name, this is in itself the
114 delimiter that starts the stuff entered for this machine,
115 after this we need to search for 'login' and
116 'password'. */
117 state = HOSTFOUND;
118 }
119 else if(strcasecompare("default", tok)) {
120 state = HOSTVALID;
121 retcode = NETRC_SUCCESS; /* we did find our host */
122 }
123 break;
124 case MACDEF:
125 if(!strlen(tok)) {
126 state = NOTHING;
127 }
128 break;
129 case HOSTFOUND:
130 if(strcasecompare(host, tok)) {
131 /* and yes, this is our host! */
132 state = HOSTVALID;
133 retcode = NETRC_SUCCESS; /* we did find our host */
134 }
135 else
136 /* not our host */
137 state = NOTHING;
138 break;
139 case HOSTVALID:
140 /* we are now parsing sub-keywords concerning "our" host */
141 if(state_login) {
142 if(specific_login) {
143 state_our_login = strcasecompare(login, tok);
144 }
145 else if(!login || strcmp(login, tok)) {
146 if(login_alloc) {
147 free(login);
148 login_alloc = FALSE;
149 }
150 login = strdup(tok);
151 if(!login) {
152 retcode = NETRC_FAILED; /* allocation failed */
153 goto out;
154 }
155 login_alloc = TRUE;
156 }
157 state_login = 0;
158 }
159 else if(state_password) {
160 if((state_our_login || !specific_login)
161 && (!password || strcmp(password, tok))) {
162 if(password_alloc) {
163 free(password);
164 password_alloc = FALSE;
165 }
166 password = strdup(tok);
167 if(!password) {
168 retcode = NETRC_FAILED; /* allocation failed */
169 goto out;
170 }
171 password_alloc = TRUE;
172 }
173 state_password = 0;
174 }
175 else if(strcasecompare("login", tok))
176 state_login = 1;
177 else if(strcasecompare("password", tok))
178 state_password = 1;
179 else if(strcasecompare("machine", tok)) {
180 /* ok, there's machine here go => */
181 state = HOSTFOUND;
182 state_our_login = FALSE;
183 }
184 break;
185 } /* switch (state) */
186
187 tok = strtok_r(NULL, " \t\n", &tok_buf);
188 } /* while(tok) */
189 } /* while fgets() */
190
191 out:
192 if(!retcode) {
193 /* success */
194 *login_changed = FALSE;
195 *password_changed = FALSE;
196 if(login_alloc) {
197 if(*loginp)
198 free(*loginp);
199 *loginp = login;
200 *login_changed = TRUE;
201 }
202 if(password_alloc) {
203 if(*passwordp)
204 free(*passwordp);
205 *passwordp = password;
206 *password_changed = TRUE;
207 }
208 }
209 else {
210 if(login_alloc)
211 free(login);
212 if(password_alloc)
213 free(password);
214 }
215 fclose(file);
216 }
217
218 return retcode;
219}
220
221/*
222 * @unittest: 1304
223 *
224 * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
225 * in.
226 */
227int Curl_parsenetrc(const char *host,
228 char **loginp,
229 char **passwordp,
230 bool *login_changed,
231 bool *password_changed,
232 char *netrcfile)
233{
234 int retcode = 1;
235 char *filealloc = NULL;
236
237 if(!netrcfile) {
238 char *home = NULL;
239 char *homea = curl_getenv("HOME"); /* portable environment reader */
240 if(homea) {
241 home = homea;
242#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
243 }
244 else {
245 struct passwd pw, *pw_res;
246 char pwbuf[1024];
247 if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
248 && pw_res) {
249 home = pw.pw_dir;
250 }
251#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
252 }
253 else {
254 struct passwd *pw;
255 pw = getpwuid(geteuid());
256 if(pw) {
257 home = pw->pw_dir;
258 }
259#endif
260 }
261
262 if(!home)
263 return retcode; /* no home directory found (or possibly out of
264 memory) */
265
266 filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
267 if(!filealloc) {
268 free(homea);
269 return -1;
270 }
271 retcode = parsenetrc(host, loginp, passwordp, login_changed,
272 password_changed, filealloc);
273 free(filealloc);
274#ifdef WIN32
275 if(retcode == NETRC_FILE_MISSING) {
276 /* fallback to the old-style "_netrc" file */
277 filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
278 if(!filealloc) {
279 free(homea);
280 return -1;
281 }
282 retcode = parsenetrc(host, loginp, passwordp, login_changed,
283 password_changed, filealloc);
284 free(filealloc);
285 }
286#endif
287 free(homea);
288 }
289 else
290 retcode = parsenetrc(host, loginp, passwordp, login_changed,
291 password_changed, netrcfile);
292 return retcode;
293}
294
295#endif
296