1 /*
2  * Copyright (C) 2000-2008 - Shaun Clowes <delius@progsoc.org>
3  * 				 2008-2011 - Robert Hogan <robert@roberthogan.net>
4  * 				 	  2013 - David Goulet <dgoulet@ev0ke.net>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License, version 2 only, as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <arpa/inet.h>
21 #include <assert.h>
22 #include <limits.h>
23 #include <netinet/in.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/socket.h>
27 
28 #include "config-file.h"
29 #include "log.h"
30 #include "utils.h"
31 
32 /*
33  * These are the torsocks.conf string values.
34  */
35 static const char *conf_toraddr_str = "TorAddress";
36 static const char *conf_torport_str = "TorPort";
37 static const char *conf_onion_str = "OnionAddrRange";
38 static const char *conf_socks5_user_str = "SOCKS5Username";
39 static const char *conf_socks5_pass_str = "SOCKS5Password";
40 static const char *conf_allow_inbound_str = "AllowInbound";
41 static const char *conf_allow_outbound_localhost_str = "AllowOutboundLocalhost";
42 static const char *conf_isolate_pid_str = "IsolatePID";
43 
44 /*
45  * Once this value reaches 2, it means both user and password for a SOCKS5
46  * connection has been set thus use them./
47  */
48 static unsigned int both_socks5_pass_user_set;
49 
50 /*
51  * Username format for the IsolatePID option. Format is:
52  *   'torsocks-' PID ':' TIME
53  */
54 static const char *isolate_username_fmt = "torsocks-%ld:%lld";
55 /* Default password for the IsolatePID option. */
56 static const char *isolate_password = "0";
57 
58 /*
59  * Set the onion pool address range in the configuration object using the value
60  * found in the conf file.
61  *
62  * Return 0 on success or else a negative value.
63  */
set_onion_info(const char * addr,struct configuration * config)64 static int set_onion_info(const char *addr, struct configuration *config)
65 {
66 	int ret;
67 	unsigned long bit_mask;
68 	char *ip = NULL, *mask = NULL;
69 	in_addr_t net;
70 
71 	assert(addr);
72 	assert(config);
73 
74 	ip = strchr(addr, '/');
75 	if (!ip) {
76 		ERR("[config] Invalid %s value for %s", addr, conf_onion_str);
77 		ret = -EINVAL;
78 		goto error;
79 	}
80 
81 	mask = strdup(addr + (ip - addr) + 1);
82 	ip = strndup(addr, ip - addr);
83 	if (!ip || !mask) {
84 		PERROR("[config] strdup onion addr");
85 		ret = -ENOMEM;
86 		goto error;
87 	}
88 
89 	net = inet_addr(ip);
90 	if (net == INADDR_NONE) {
91 		ERR("[config] Invalid IP subnet %s for %s", ip, conf_onion_str);
92 		ret = -EINVAL;
93 		goto error;
94 	}
95 
96 	/* Expressed in base 10. */
97 	bit_mask = strtoul(mask, NULL, 10);
98 	if (bit_mask == ULONG_MAX) {
99 		ERR("[config] Invalid mask %s for %s", mask, conf_onion_str);
100 		ret = -EINVAL;
101 		goto error;
102 	}
103 
104 	memcpy(&config->conf_file.onion_base, &net,
105 			sizeof(config->conf_file.onion_base));
106 	config->conf_file.onion_mask = (uint8_t) bit_mask;
107 
108 	DBG("[config] Onion address range set to %s", addr);
109 	ret = 0;
110 
111 error:
112 	free(ip);
113 	free(mask);
114 	return ret;
115 }
116 
117 /*
118  * Parse a single line from a configuration file and set the value found in
119  * the configuration object.
120  *
121  * Return 0 on success or else a negative value.
122  */
parse_config_line(const char * line,struct configuration * config)123 static int parse_config_line(const char *line, struct configuration *config)
124 {
125 	int ret, nb_token;
126 	char *tokens[DEFAULT_MAX_CONF_TOKEN];
127 
128 	assert(line);
129 	assert(config);
130 
131 	/*
132 	 * The line is tokenized and each token is NULL terminated.
133 	 */
134 	nb_token = utils_tokenize_ignore_comments(line, DEFAULT_MAX_CONF_TOKEN,
135 			tokens);
136 	if (nb_token <= 0) {
137 		/* Nothing on this line that is useful to parse. */
138 		ret = 0;
139 		goto end;
140 	}
141 
142 	if (!strcmp(tokens[0], conf_toraddr_str)) {
143 		ret = conf_file_set_tor_address(tokens[1], config);
144 		if (ret < 0) {
145 			goto error;
146 		}
147 	} else if (!strcmp(tokens[0], conf_torport_str)) {
148 		ret = conf_file_set_tor_port(tokens[1], config);
149 		if (ret < 0) {
150 			goto error;
151 		}
152 	} else if (!strcmp(tokens[0], conf_onion_str)) {
153 		ret = set_onion_info(tokens[1], config);
154 		if (ret < 0) {
155 			goto error;
156 		}
157 	} else if (!strcmp(tokens[0], conf_socks5_user_str)) {
158 		ret = conf_file_set_socks5_user(tokens[1], config);
159 		if (ret < 0) {
160 			goto error;
161 		}
162 	} else if (!strcmp(tokens[0], conf_socks5_pass_str)) {
163 		ret = conf_file_set_socks5_pass(tokens[1], config);
164 		if (ret < 0) {
165 			goto error;
166 		}
167 	} else if (!strcmp(tokens[0], conf_allow_inbound_str)) {
168 		ret = conf_file_set_allow_inbound(tokens[1], config);
169 		if (ret < 0) {
170 			goto error;
171 		}
172 	} else if (!strcmp(tokens[0], conf_allow_outbound_localhost_str)) {
173 		ret = conf_file_set_allow_outbound_localhost(tokens[1], config);
174 		if (ret < 0) {
175 			goto error;
176 		}
177 	} else if (!strcmp(tokens[0], conf_isolate_pid_str)) {
178 		ret = conf_file_set_isolate_pid(tokens[1], config);
179 		if (ret < 0) {
180 			goto error;
181 		}
182 	} else {
183 		WARN("Config file contains unknown value: %s", line);
184 	}
185 
186 	/* Everything went well. */
187 	ret = 0;
188 
189 end:
190 error:
191 	return ret;
192 }
193 
194 /*
195  * Parse the configuration file with the given file pointer into the
196  * configuration object.
197  *
198  * Return 0 on success or else a negative value.
199  */
parse_config_file(FILE * fp,struct configuration * config)200 static int parse_config_file(FILE *fp, struct configuration *config)
201 {
202 	int ret = -1;
203 	/* Usually, this value is 8192 on most Unix systems. */
204 	char line[BUFSIZ];
205 
206 	assert(fp);
207 	assert(config);
208 
209 	while (fgets(line, sizeof(line), fp) != NULL) {
210 		/*
211 		 * Remove the \n at the end of the buffer and replace it by a NULL
212 		 * bytes so we handle the line without this useless char.
213 		 */
214 		if (strlen(line) > 0) {
215 			line[strlen(line) - 1] = '\0';
216 		}
217 
218 		ret = parse_config_line(line, config);
219 		if (ret < 0) {
220 			goto error;
221 		}
222 	}
223 
224 error:
225 	return ret;
226 }
227 
228 /*
229  * Set the given string port in a configuration object.
230  *
231  * Return 0 on success or else a negative EINVAL if the port is equal to 0 or
232  * over 65535.
233  */
234 ATTR_HIDDEN
conf_file_set_tor_port(const char * port,struct configuration * config)235 int conf_file_set_tor_port(const char *port, struct configuration *config)
236 {
237 	int ret = 0;
238 	char *endptr;
239 	unsigned long _port;
240 
241 	assert(port);
242 	assert(config);
243 
244 	/* Let's avoid an integer overflow here ;). */
245 	_port = strtoul(port, &endptr, 10);
246 	if (_port == 0 || _port > 65535) {
247 		ret = -EINVAL;
248 		ERR("Config file invalid port: %s", port);
249 		goto error;
250 	}
251 
252 	config->conf_file.tor_port = (in_port_t) _port;
253 
254 	DBG("Config file setting tor port to %lu", _port);
255 
256 error:
257 	return ret;
258 }
259 
260 /*
261  * Set the given string address in a configuration object.
262  *
263  * Return 0 on success or else a negative value. On error, the address was not
264  * recognized.
265  */
266 ATTR_HIDDEN
conf_file_set_tor_address(const char * addr,struct configuration * config)267 int conf_file_set_tor_address(const char *addr, struct configuration *config)
268 {
269 	int ret;
270 
271 	assert(addr);
272 	assert(config);
273 
274 	ret = utils_is_address_ipv4(addr);
275 	if (ret == 1 ) {
276 		config->conf_file.tor_domain = CONNECTION_DOMAIN_INET;
277 	} else {
278 		ret = utils_is_address_ipv6(addr);
279 		if (ret != 1) {
280 			/* At this point, the addr is either v4 nor v6 so error. */
281 			ERR("Config file unknown tor address: %s", addr);
282 			goto error;
283 		}
284 		config->conf_file.tor_domain = CONNECTION_DOMAIN_INET6;
285 	}
286 
287 	if (config->conf_file.tor_address != NULL) {
288 		free(config->conf_file.tor_address);
289 		config->conf_file.tor_address = NULL;
290 	}
291 
292 	config->conf_file.tor_address = strdup(addr);
293 	if (config->conf_file.tor_address == NULL) {
294 		ret = -ENOMEM;
295 		goto error;
296 	}
297 
298 	DBG("Config file setting tor address to %s", addr);
299 	ret = 0;
300 
301 error:
302 	return ret;
303 }
304 
305 /*
306  * Set the SOCKS5 username to the given configuration.
307  *
308  * Return 0 on success else a negative value.
309  */
310 ATTR_HIDDEN
conf_file_set_socks5_user(const char * username,struct configuration * config)311 int conf_file_set_socks5_user(const char *username,
312 		struct configuration *config)
313 {
314 	int ret;
315 
316 	assert(username);
317 	assert(config);
318 
319 	if (strlen(username) > sizeof(config->conf_file.socks5_username)) {
320 		ERR("[config] Invalid %s value for %s", username,
321 				conf_socks5_user_str);
322 		ret = -EINVAL;
323 		goto error;
324 	}
325 
326 	strncpy(config->conf_file.socks5_username, username, strlen(username));
327 	if (++both_socks5_pass_user_set == 2) {
328 		config->socks5_use_auth = 1;
329 	}
330 	DBG("[config] %s set to %s", conf_socks5_user_str, username);
331 	return 0;
332 
333 error:
334 	return ret;
335 }
336 
337 /*
338  * Set the SOCKS5 password to the given configuration.
339  *
340  * Return 0 on success else a negative value.
341  */
342 ATTR_HIDDEN
conf_file_set_socks5_pass(const char * password,struct configuration * config)343 int conf_file_set_socks5_pass(const char *password,
344 		struct configuration *config)
345 {
346 	int ret;
347 
348 	assert(password);
349 	assert(config);
350 
351 	if (strlen(password) > sizeof(config->conf_file.socks5_password)) {
352 		ERR("[config] Invalid %s value for %s", password,
353 				conf_socks5_pass_str);
354 		ret = -EINVAL;
355 		goto error;
356 	}
357 
358 	strncpy(config->conf_file.socks5_password, password, strlen(password));
359 	if (++both_socks5_pass_user_set == 2) {
360 		config->socks5_use_auth = 1;
361 	}
362 	DBG("[config] %s set to %s", conf_socks5_pass_str, password);
363 	return 0;
364 
365 error:
366 	return ret;
367 }
368 
369 /*
370  * Set the allow inbound option for the given config.
371  *
372  * Return 0 if option is off, 1 if on and negative value on error.
373  */
374 ATTR_HIDDEN
conf_file_set_allow_inbound(const char * val,struct configuration * config)375 int conf_file_set_allow_inbound(const char *val, struct configuration *config)
376 {
377 	int ret;
378 
379 	assert(val);
380 	assert(config);
381 
382 	ret = atoi(val);
383 	if (ret == 0) {
384 		config->allow_inbound = 0;
385 		DBG("[config] Inbound connections disallowed.");
386 	} else if (ret == 1) {
387 		config->allow_inbound = 1;
388 		DBG("[config] Inbound connections allowed.");
389 	} else {
390 		ERR("[config] Invalid %s value for %s", val, conf_allow_inbound_str);
391 		ret = -EINVAL;
392 	}
393 
394 	return ret;
395 }
396 
397 /*
398  * Set the allow outbound localhost option for the given config.
399  *
400  * Return 0 if option is off, 1 if on and negative value on error.
401  */
402 ATTR_HIDDEN
conf_file_set_allow_outbound_localhost(const char * val,struct configuration * config)403 int conf_file_set_allow_outbound_localhost(const char *val,
404 		struct configuration *config)
405 {
406 	int ret;
407 
408 	assert(val);
409 	assert(config);
410 
411 	ret = atoi(val);
412 	if (ret == 0) {
413 		config->allow_outbound_localhost = 0;
414 		DBG("[config] Outbound localhost connections disallowed.");
415 	} else if (ret == 1) {
416 		config->allow_outbound_localhost = 1;
417 		DBG("[config] Outbound localhost connections allowed.");
418 	} else if (ret == 2) {
419 		config->allow_outbound_localhost = 2;
420 		DBG("[config] Outbound localhost connections + UDP allowed.");
421 	} else {
422 		ERR("[config] Invalid %s value for %s", val,
423 				conf_allow_outbound_localhost_str);
424 		ret = -EINVAL;
425 	}
426 
427 	return ret;
428 }
429 
430 /*
431  * Set the isolate PID option for the given config.
432  *
433  * Return 0 if optiuon is off, 1 if on and negative value on error.
434  */
435 ATTR_HIDDEN
conf_file_set_isolate_pid(const char * val,struct configuration * config)436 int conf_file_set_isolate_pid(const char *val, struct configuration *config)
437 {
438 	int ret;
439 
440 	assert(val);
441 	assert(config);
442 
443 	ret = atoi(val);
444 	if (ret == 0) {
445 		config->isolate_pid = 0;
446 		DBG("[config] PID isolation disabled.");
447 	} else if (ret == 1) {
448 		config->isolate_pid = 1;
449 		DBG("[config] PID isolation enabled.");
450 	} else {
451 		ERR("[config] Invalid %s value for %s", val,
452 				conf_isolate_pid_str);
453 		ret = -EINVAL;
454 	}
455 
456 	return ret;
457 }
458 
459 /*
460  * Applies the SOCKS authentication configuration and sets the final SOCKS
461  * username and password.
462  *
463  * Return 0 if successful, and negative value on error.
464  */
465 ATTR_HIDDEN
conf_apply_socks_auth(struct configuration * config)466 int conf_apply_socks_auth(struct configuration *config)
467 {
468 	int ret;
469 	pid_t pid;
470 	time_t now;
471 
472 	assert(config);
473 
474 	if (!config->socks5_use_auth && !config->isolate_pid) {
475 		/* No auth specified at all. */
476 		ret = 0;
477 		goto end;
478 	} else if (config->socks5_use_auth && !config->isolate_pid) {
479 		/* SOCKS5 auth specified by user, already setup. */
480 		ret = 0;
481 		goto end;
482 	} else if (config->socks5_use_auth && config->isolate_pid) {
483 		ERR("[config] %s and SOCKS5 auth both set.", conf_isolate_pid_str);
484 		ret = -EINVAL;
485 		goto end;
486 	}
487 
488 
489 	/* PID based isolation requested.
490 	 *   Username: 'torsocks-' PID ':' TIME
491 	 *   Password: '0'
492 	 */
493 
494 	pid = getpid();
495 	now = time(NULL);
496 
497 	ret = snprintf(config->conf_file.socks5_username,
498 			sizeof(config->conf_file.socks5_username), isolate_username_fmt,
499 			(long) pid, (long long int) now);
500 	if (ret < 0 || ret >= (int) sizeof(config->conf_file.socks5_username)) {
501 		ret = -ENOBUFS;
502 		goto end;
503 	}
504 
505 	ret = snprintf(config->conf_file.socks5_password,
506 			sizeof(config->conf_file.socks5_password), "%s", isolate_password);
507 	if (ret < 0 || ret >= (int) sizeof(config->conf_file.socks5_password)) {
508 		ret = -ENOBUFS;
509 		goto end;
510 	}
511 
512 	DBG("[config]: %s: '%s'/'%s'", conf_isolate_pid_str,
513 			config->conf_file.socks5_username,
514 			config->conf_file.socks5_password);
515 
516 	config->socks5_use_auth = 1;
517 	ret = 0;
518 
519 end:
520 	return ret;
521 }
522 
523 /*
524  * Read and populate the given config parsed data structure.
525  *
526  * Return 0 on success or else a negative value.
527  */
528 ATTR_HIDDEN
config_file_read(const char * filename,struct configuration * config)529 int config_file_read(const char *filename, struct configuration *config)
530 {
531 	int ret;
532 	FILE *fp;
533 
534 	assert(config);
535 
536 	/* Clear out the structure */
537 	memset(config, 0x0, sizeof(*config));
538 
539 	/* If a filename wasn't provided, use the default. */
540 	if (!filename) {
541 		filename = DEFAULT_CONF_FILE;
542 		DBG("Config file not provided by TORSOCKS_CONF_FILE. Using default %s",
543 				filename);
544 	}
545 
546 	fp = fopen(filename, "r");
547 	if (!fp) {
548 		WARN("Config file not found: %s. Using default for Tor", filename);
549 		(void) conf_file_set_tor_address(DEFAULT_TOR_ADDRESS, config);
550 		/*
551 		 * We stringify the default value here so we can print the debug
552 		 * statement in the function call to set port.
553 		 */
554 		(void) conf_file_set_tor_port(XSTR(DEFAULT_TOR_PORT), config);
555 
556 		ret = set_onion_info(
557 				DEFAULT_ONION_ADDR_RANGE "/" DEFAULT_ONION_ADDR_MASK, config);
558 		if (!ret) {
559 			/* ENOMEM is probably the only case here. */
560 			goto error;
561 		}
562 
563 		config->allow_inbound = 0;
564 		goto end;
565 	}
566 
567 	ret = parse_config_file(fp, config);
568 	if (ret < 0) {
569 		goto error;
570 	}
571 
572 	DBG("Config file %s opened and parsed.", filename);
573 
574 end:
575 error:
576 	if (fp) {
577 		fclose(fp);
578 	}
579 	return ret;
580 }
581 
582 /*
583  * Free everything inside a configuration file object. It is the caller
584  * responsability to free the object if needed.
585  */
586 ATTR_HIDDEN
config_file_destroy(struct config_file * conf)587 void config_file_destroy(struct config_file *conf)
588 {
589 	assert(conf);
590 
591 	free(conf->tor_address);
592 }
593