1 /*
2  * Copyright (c) 2015-2021 Free Software Foundation, Inc.
3  *
4  * This file is part of libwget.
5  *
6  * Libwget is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Libwget is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
18  *
19  *
20  * .netrc routines
21  *
22  * Changelog
23  * 01.11.2015  Tim Ruehsen  created
24  *
25  */
26 
27 #include <config.h>
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <time.h>
34 #include <errno.h>
35 
36 #include <wget.h>
37 #include "private.h"
38 
39 struct wget_netrc_db_st {
40 	wget_hashmap *
41 		machines;
42 };
43 
44 #ifdef __clang__
45 __attribute__((no_sanitize("integer")))
46 #endif
47 WGET_GCC_PURE
hash_netrc(const wget_netrc * netrc)48 static unsigned int hash_netrc(const wget_netrc *netrc)
49 {
50 	unsigned int hash = 0;
51 	const unsigned char *p;
52 
53 	for (p = (unsigned char *)netrc->host; *p; p++)
54 		hash = hash * 101 + *p;
55 
56 	return hash;
57 }
58 
59 WGET_GCC_NONNULL_ALL WGET_GCC_PURE
compare_netrc(const wget_netrc * h1,const wget_netrc * h2)60 static int compare_netrc(const wget_netrc *h1, const wget_netrc *h2)
61 {
62 	return wget_strcmp(h1->host, h2->host);
63 }
64 
wget_netrc_init(wget_netrc * netrc)65 wget_netrc *wget_netrc_init(wget_netrc *netrc)
66 {
67 	if (!netrc) {
68 		if (!(netrc = wget_calloc(1, sizeof(wget_netrc))))
69 			return NULL;
70 	} else
71 		memset(netrc, 0, sizeof(*netrc));
72 
73 	return netrc;
74 }
75 
wget_netrc_deinit(wget_netrc * netrc)76 void wget_netrc_deinit(wget_netrc *netrc)
77 {
78 	if (netrc) {
79 		xfree(netrc->host);
80 		xfree(netrc->login);
81 		xfree(netrc->password);
82 	}
83 }
84 
wget_netrc_free(wget_netrc * netrc)85 void wget_netrc_free(wget_netrc *netrc)
86 {
87 	if (netrc) {
88 		wget_netrc_deinit(netrc);
89 		xfree(netrc);
90 	}
91 }
92 
wget_netrc_new(const char * machine,const char * login,const char * password)93 wget_netrc *wget_netrc_new(const char *machine, const char *login, const char *password)
94 {
95 	wget_netrc *netrc = wget_netrc_init(NULL);
96 
97 	if (netrc) {
98 		netrc->host = wget_strdup(machine);
99 		netrc->login = wget_strdup(login);
100 		netrc->password = wget_strdup(password);
101 	}
102 
103 	return netrc;
104 }
105 
wget_netrc_get(const wget_netrc_db * netrc_db,const char * host)106 wget_netrc *wget_netrc_get(const wget_netrc_db *netrc_db, const char *host)
107 {
108 	if (netrc_db) {
109 		wget_netrc netrc, *netrcp;
110 
111 		// look for an exact match
112 		netrc.host = host;
113 
114 		if (wget_hashmap_get(netrc_db->machines, &netrc, &netrcp))
115 			return netrcp;
116 	}
117 
118 	return NULL;
119 }
120 
wget_netrc_db_init(wget_netrc_db * netrc_db)121 wget_netrc_db *wget_netrc_db_init(wget_netrc_db *netrc_db)
122 {
123 	wget_hashmap *machines = wget_hashmap_create(16, (wget_hashmap_hash_fn *) hash_netrc, (wget_hashmap_compare_fn *) compare_netrc);
124 
125 	if (!machines)
126 		return NULL;
127 
128 	if (!netrc_db) {
129 		if (!(netrc_db = wget_calloc(1, sizeof(wget_netrc_db)))) {
130 			wget_hashmap_free(&machines);
131 			return NULL;
132 		}
133 	} else
134 		memset(netrc_db, 0, sizeof(*netrc_db));
135 
136 	wget_hashmap_set_key_destructor(machines, (wget_hashmap_key_destructor *) wget_netrc_free);
137 	wget_hashmap_set_value_destructor(machines, (wget_hashmap_value_destructor *) wget_netrc_free);
138 	netrc_db->machines = machines;
139 
140 	return netrc_db;
141 }
142 
wget_netrc_db_deinit(wget_netrc_db * netrc_db)143 void wget_netrc_db_deinit(wget_netrc_db *netrc_db)
144 {
145 	if (netrc_db) {
146 		wget_hashmap_free(&netrc_db->machines);
147 	}
148 }
149 
wget_netrc_db_free(wget_netrc_db ** netrc_db)150 void wget_netrc_db_free(wget_netrc_db **netrc_db)
151 {
152 	if (netrc_db) {
153 		wget_netrc_db_deinit(*netrc_db);
154 		xfree(*netrc_db);
155 	}
156 }
157 
wget_netrc_db_add(wget_netrc_db * netrc_db,wget_netrc * netrc)158 void wget_netrc_db_add(wget_netrc_db *netrc_db, wget_netrc *netrc)
159 {
160 	if (!netrc)
161 		return;
162 
163 	if (!netrc_db) {
164 		wget_netrc_free(netrc);
165 		return;
166 	}
167 
168 	// key and value are the same to make wget_hashmap_get() return old 'netrc'
169 	debug_printf("add .netrc %s (login=%s, password=*)\n", netrc->host, netrc->login);
170 	wget_hashmap_put(netrc_db->machines, netrc, netrc);
171 	// no need to free anything here
172 }
173 
174 // load the .netrc file
175 // not thread-save
176 
wget_netrc_db_load(wget_netrc_db * netrc_db,const char * fname)177 int wget_netrc_db_load(wget_netrc_db *netrc_db, const char *fname)
178 {
179 	FILE *fp;
180 
181 	if (!netrc_db || !fname || !*fname)
182 		return WGET_E_INVALID;
183 
184 	if (!(fp = fopen(fname, "r")))
185 		return WGET_E_OPEN;
186 
187 	wget_netrc netrc;
188 	char *buf = NULL, *linep, *p, *key = NULL;
189 	size_t bufsize = 0;
190 	ssize_t buflen;
191 	int in_macdef = 0, in_machine = 0, nentries = 0;
192 
193 	while ((buflen = wget_getline(&buf, &bufsize, fp)) >= 0) {
194 		linep = buf;
195 
196 		while (isspace(*linep)) linep++; // ignore leading whitespace
197 
198 		if (*linep == '#')
199 			continue; // skip comments
200 
201 		// strip off \r\n
202 		while (buflen > 0 && (buf[buflen] == '\n' || buf[buflen] == '\r'))
203 			buf[--buflen] = 0;
204 
205 		if (!*linep) {
206 			// empty lines reset macro processing
207 			in_macdef = 0;
208 			continue;
209 		} else if (in_macdef)
210 			continue; // still processing 'macdef' macro
211 
212 		// now we expect key value pairs, e.g.: machine example.com
213 		do {
214 			xfree(key);
215 			while (isspace(*linep)) linep++;
216 			for (p = linep; *linep && !isspace(*linep);) linep++;
217 
218 			if (!(key = wget_strmemdup(p, linep - p))) {
219 				xfree(buf);
220 				fclose(fp);
221 				return WGET_E_MEMORY;
222 			}
223 
224 			if (!strcmp(key, "machine") || !strcmp(key, "default")) {
225 				if (in_machine)
226 					wget_netrc_db_add(netrc_db, wget_memdup(&netrc, sizeof(netrc)));
227 
228 				wget_netrc_init(&netrc);
229 				in_machine = 1;
230 
231 				if (!strcmp(key, "default")) {
232 					netrc.host = wget_strdup("default");
233 					continue;
234 				}
235 			} else if (!in_machine)
236 				continue; // token outside of machine or default
237 
238 			while (isspace(*linep)) linep++;
239 			for (p = linep; *linep && !isspace(*linep);) linep++;
240 
241 			if (!strcmp(key, "machine")) {
242 				if (!netrc.host)
243 					netrc.host = wget_strmemdup(p, linep - p);
244 			} else if (!strcmp(key, "login")) {
245 				if (!netrc.login)
246 					netrc.login = wget_strmemdup(p, linep - p);
247 			} else if (!strcmp(key, "password")) {
248 				if (!netrc.password)
249 					netrc.password = wget_strmemdup(p, linep - p);
250 			} else if (!strcmp(key, "port")) { // GNU extension
251 				netrc.port = (uint16_t) atoi(p);
252 			} else if (!strcmp(key, "force")) { // GNU extension
253 				netrc.force = !wget_strncasecmp_ascii("yes", p, 3);
254 			} else if (!strcmp(key, "macdef")) {
255 				in_macdef = 1; // the above code skips until next empty line
256 			}
257 		} while (*linep);
258 
259 		xfree(key);
260 	}
261 
262 	if (in_machine)
263 		wget_netrc_db_add(netrc_db, wget_memdup(&netrc, sizeof(netrc)));
264 
265 	xfree(buf);
266 	fclose(fp);
267 
268 	nentries = wget_hashmap_size(netrc_db->machines);
269 
270 	debug_printf("loaded %d .netrc %s\n", nentries, nentries != 1 ? "entries" : "entry");
271 
272 	return nentries;
273 }
274