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