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
45 enum 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 */
parsenetrc(const char * host,char ** loginp,char ** passwordp,char * netrcfile)59 static 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 else if(strcasecompare("default", tok)) {
242 state = HOSTVALID;
243 retcode = NETRC_SUCCESS; /* we did find our host */
244 Curl_safefree(password);
245 if(!specific_login)
246 if(login_alloc) {
247 free(login);
248 login_alloc = FALSE;
249 }
250 }
251 break;
252 } /* switch (state) */
253 tok = ++tok_end;
254 }
255 } /* while Curl_get_line() */
256
257 out:
258 if(!retcode && !password && state_our_login) {
259 /* success without a password, set a blank one */
260 password = strdup("");
261 if(!password)
262 retcode = 1; /* out of memory */
263 }
264 if(!retcode) {
265 /* success */
266 if(login_alloc) {
267 if(*loginp)
268 free(*loginp);
269 *loginp = login;
270 }
271 if(password_alloc) {
272 if(*passwordp)
273 free(*passwordp);
274 *passwordp = password;
275 }
276 }
277 else {
278 if(login_alloc)
279 free(login);
280 if(password_alloc)
281 free(password);
282 }
283 fclose(file);
284 }
285
286 return retcode;
287 }
288
289 /*
290 * @unittest: 1304
291 *
292 * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
293 * in.
294 */
Curl_parsenetrc(const char * host,char ** loginp,char ** passwordp,char * netrcfile)295 int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
296 char *netrcfile)
297 {
298 int retcode = 1;
299 char *filealloc = NULL;
300
301 if(!netrcfile) {
302 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
303 char pwbuf[1024];
304 #endif
305 char *home = NULL;
306 char *homea = curl_getenv("HOME"); /* portable environment reader */
307 if(homea) {
308 home = homea;
309 #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
310 }
311 else {
312 struct passwd pw, *pw_res;
313 if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
314 && pw_res) {
315 home = pw.pw_dir;
316 }
317 #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
318 }
319 else {
320 struct passwd *pw;
321 pw = getpwuid(geteuid());
322 if(pw) {
323 home = pw->pw_dir;
324 }
325 #elif defined(_WIN32)
326 }
327 else {
328 homea = curl_getenv("USERPROFILE");
329 if(homea) {
330 home = homea;
331 }
332 #endif
333 }
334
335 if(!home)
336 return retcode; /* no home directory found (or possibly out of
337 memory) */
338
339 filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
340 if(!filealloc) {
341 free(homea);
342 return -1;
343 }
344 retcode = parsenetrc(host, loginp, passwordp, filealloc);
345 free(filealloc);
346 #ifdef _WIN32
347 if(retcode == NETRC_FILE_MISSING) {
348 /* fallback to the old-style "_netrc" file */
349 filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
350 if(!filealloc) {
351 free(homea);
352 return -1;
353 }
354 retcode = parsenetrc(host, loginp, passwordp, filealloc);
355 free(filealloc);
356 }
357 #endif
358 free(homea);
359 }
360 else
361 retcode = parsenetrc(host, loginp, passwordp, netrcfile);
362 return retcode;
363 }
364
365 #endif
366