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