git ssb

0+

cel / sslh



Tree: f02ce3821c018719536971dbb1bc1ed1517530a2

Files: f02ce3821c018719536971dbb1bc1ed1517530a2 / sslh-main.c

20112 bytesRaw
1/*
2# main: processing of config file, command line options and start the main
3# loop.
4#
5# Copyright (C) 2007-2016 Yves Rutschle
6#
7# This program is free software; you can redistribute it
8# and/or modify it under the terms of the GNU General Public
9# License as published by the Free Software Foundation; either
10# version 2 of the License, or (at your option) any later
11# version.
12#
13# This program is distributed in the hope that it will be
14# useful, but WITHOUT ANY WARRANTY; without even the implied
15# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16# PURPOSE. See the GNU General Public License for more
17# details.
18#
19# The full text for the General Public License is here:
20# http://www.gnu.org/licenses/gpl.html
21
22*/
23
24#define _GNU_SOURCE
25#ifdef LIBCONFIG
26#include <libconfig.h>
27#endif
28#ifdef ENABLE_REGEX
29#ifdef LIBPCRE
30#include <pcreposix.h>
31#else
32#include <regex.h>
33#endif
34#endif
35
36#include "common.h"
37#include "probe.h"
38
39const char* USAGE_STRING =
40"sslh " VERSION "\n" \
41"usage:\n" \
42"\tsslh [-v] [-i] [-V] [-f] [-n] [--transparent] [-F<file>]\n"
43"\t[-t <timeout>] [-P <pidfile>] -u <username> -p <add> [-p <addr> ...] \n" \
44"%s\n\n" /* Dynamically built list of builtin protocols */ \
45"\t[--on-timeout <addr>]\n" \
46"-v: verbose\n" \
47"-V: version\n" \
48"-f: foreground\n" \
49"-n: numeric output\n" \
50"-u: specify under which user to run\n" \
51"--transparent: behave as a transparent proxy\n" \
52"-F: use configuration file (warning: no space between -F and file name!)\n" \
53"--on-timeout: connect to specified address upon timeout (default: ssh address)\n" \
54"-t: seconds to wait before connecting to --on-timeout address.\n" \
55"-p: address and port to listen on.\n Can be used several times to bind to several addresses.\n" \
56"--[ssh,ssl,...]: where to connect connections from corresponding protocol.\n" \
57"-P: PID file.\n" \
58"-i: Run as a inetd service.\n" \
59"";
60
61/* Constants for options that have no one-character shorthand */
62#define OPT_ONTIMEOUT 257
63
64/* Global setting for transparent proxying */
65int g_transparent = 0;
66
67static struct option const_options[] = {
68 { "inetd", no_argument, &inetd, 1 },
69 { "foreground", no_argument, &foreground, 1 },
70 { "background", no_argument, &background, 1 },
71 { "transparent", no_argument, &g_transparent, 1 },
72 { "numeric", no_argument, &numeric, 1 },
73 { "verbose", no_argument, &verbose, 1 },
74 { "user", required_argument, 0, 'u' },
75 { "config", optional_argument, 0, 'F' },
76 { "pidfile", required_argument, 0, 'P' },
77 { "timeout", required_argument, 0, 't' },
78 { "on-timeout", required_argument, 0, OPT_ONTIMEOUT },
79 { "listen", required_argument, 0, 'p' },
80 {}
81};
82static struct option* all_options;
83static struct proto* builtins;
84static const char *optstr = "vt:T:p:VP:F::";
85
86
87
88static void print_usage(void)
89{
90 struct proto *p;
91 int i;
92 int res;
93 char *prots = "";
94
95 p = get_builtins();
96 for (i = 0; i < get_num_builtins(); i++) {
97 res = asprintf(&prots, "%s\t[--%s <addr>]\n", prots, p[i].description);
98 CHECK_RES_DIE(res, "asprintf");
99 }
100
101 fprintf(stderr, USAGE_STRING, prots);
102}
103
104static void printcaps(void) {
105#ifdef LIBCAP
106 cap_t caps;
107 char* desc;
108 ssize_t len;
109
110 caps = cap_get_proc();
111
112 desc = cap_to_text(caps, &len);
113
114 fprintf(stderr, "capabilities: %s\n", desc);
115
116 cap_free(caps);
117 cap_free(desc);
118#endif
119}
120
121static void printsettings(void)
122{
123 char buf[NI_MAXHOST];
124 struct addrinfo *a;
125 struct proto *p;
126
127 for (p = get_first_protocol(); p; p = p->next) {
128 fprintf(stderr,
129 "%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s%s]\n",
130 p->description,
131 sprintaddr(buf, sizeof(buf), p->saddr),
132 p->service,
133 p->log_level,
134 p->saddr->ai_family,
135 p->saddr->ai_addr->sa_family,
136 p->keepalive ? "keepalive " : "",
137 p->transparent ? "transparent" : ""
138 );
139 }
140 fprintf(stderr, "listening on:\n");
141 for (a = addr_listen; a; a = a->ai_next) {
142 fprintf(stderr,
143 "\t%s\t[%s]\n",
144 sprintaddr(buf, sizeof(buf), a),
145 a->ai_flags & SO_KEEPALIVE ? "keepalive" : "");
146 }
147 fprintf(stderr, "timeout: %d\non-timeout: %s\n", probing_timeout,
148 timeout_protocol()->description);
149}
150
151
152/* Extract configuration on addresses and ports on which to listen.
153 * out: newly allocated list of addrinfo to listen to
154 */
155#ifdef LIBCONFIG
156static int config_listen(config_t *config, struct addrinfo **listen)
157{
158 config_setting_t *setting, *addr;
159 int len, i, keepalive;
160 const char *hostname, *port;
161
162 setting = config_lookup(config, "listen");
163 if (setting) {
164 len = config_setting_length(setting);
165 for (i = 0; i < len; i++) {
166 addr = config_setting_get_elem(setting, i);
167 if (! (config_setting_lookup_string(addr, "host", &hostname) &&
168 config_setting_lookup_string(addr, "port", &port))) {
169 fprintf(stderr,
170 "line %d:Incomplete specification (hostname and port required)\n",
171 config_setting_source_line(addr));
172 return -1;
173 }
174
175 keepalive = 0;
176 config_setting_lookup_bool(addr, "keepalive", &keepalive);
177
178 resolve_split_name(listen, hostname, port);
179
180 /* getaddrinfo returned a list of addresses corresponding to the
181 * specification; move the pointer to the end of that list before
182 * processing the next specification, while setting flags for
183 * start_listen_sockets() through ai_flags (which is not meant for
184 * that, but is only used as hint in getaddrinfo, so it's OK) */
185 for (; *listen; listen = &((*listen)->ai_next)) {
186 if (keepalive)
187 (*listen)->ai_flags = SO_KEEPALIVE;
188 }
189 }
190 }
191
192 return 0;
193}
194#endif
195
196
197
198#ifdef LIBCONFIG
199static void setup_regex_probe(struct proto *p, config_setting_t* probes)
200{
201#ifdef ENABLE_REGEX
202 int num_probes, errsize, i, res;
203 char *err;
204 const char * expr;
205 regex_t** probe_list;
206
207 num_probes = config_setting_length(probes);
208 if (!num_probes) {
209 fprintf(stderr, "%s: no probes specified\n", p->description);
210 exit(1);
211 }
212
213 p->probe = get_probe("regex");
214 probe_list = calloc(num_probes + 1, sizeof(*probe_list));
215 p->data = (void*)probe_list;
216
217 for (i = 0; i < num_probes; i++) {
218 probe_list[i] = malloc(sizeof(*(probe_list[i])));
219 expr = config_setting_get_string_elem(probes, i);
220 res = regcomp(probe_list[i], expr, 0);
221 if (res) {
222 err = malloc(errsize = regerror(res, probe_list[i], NULL, 0));
223 regerror(res, probe_list[i], err, errsize);
224 fprintf(stderr, "%s:%s\n", expr, err);
225 free(err);
226 exit(1);
227 }
228 }
229#else
230 fprintf(stderr, "line %d: regex probe specified but not compiled in\n", config_setting_source_line(probes));
231 exit(5);
232#endif
233}
234#endif
235
236#ifdef LIBCONFIG
237static void setup_sni_alpn_list(struct proto *p, config_setting_t* config_items, const char* name, int alpn)
238{
239 int num_probes, i, max_server_name_len, server_name_len;
240 const char * config_item;
241 char** sni_hostname_list;
242
243 num_probes = config_setting_length(config_items);
244 if (!num_probes) {
245 fprintf(stderr, "%s: no %s specified\n", p->description, name);
246 return;
247 }
248
249 max_server_name_len = 0;
250 for (i = 0; i < num_probes; i++) {
251 server_name_len = strlen(config_setting_get_string_elem(config_items, i));
252 if(server_name_len > max_server_name_len)
253 max_server_name_len = server_name_len;
254 }
255
256 sni_hostname_list = calloc(num_probes + 1, ++max_server_name_len);
257
258 for (i = 0; i < num_probes; i++) {
259 config_item = config_setting_get_string_elem(config_items, i);
260 sni_hostname_list[i] = malloc(max_server_name_len);
261 strcpy (sni_hostname_list[i], config_item);
262 if(verbose) fprintf(stderr, "%s: %s[%d]: %s\n", p->description, name, i, sni_hostname_list[i]);
263 }
264
265 p->data = (void*)tls_data_set_list(p->data, alpn, sni_hostname_list);
266}
267
268static void setup_sni_alpn(struct proto *p, config_setting_t* prot)
269{
270 config_setting_t *sni_hostnames, *alpn_protocols;
271
272 p->data = (void*)new_tls_data();
273 sni_hostnames = config_setting_get_member(prot, "sni_hostnames");
274 alpn_protocols = config_setting_get_member(prot, "alpn_protocols");
275
276 if(sni_hostnames && config_setting_is_array(sni_hostnames)) {
277 p->probe = get_probe("sni_alpn");
278 setup_sni_alpn_list(p, sni_hostnames, "sni_hostnames", 0);
279 }
280 if(alpn_protocols && config_setting_is_array(alpn_protocols)) {
281 p->probe = get_probe("sni_alpn");
282 setup_sni_alpn_list(p, alpn_protocols, "alpn_protocols", 1);
283 }
284}
285#endif
286
287/* Extract configuration for protocols to connect to.
288 * out: newly-allocated list of protocols
289 */
290#ifdef LIBCONFIG
291static int config_protocols(config_t *config, struct proto **prots)
292{
293 config_setting_t *setting, *prot, *patterns;
294 const char *hostname, *port, *name;
295 int i, num_prots;
296 struct proto *p, *prev = NULL;
297
298 setting = config_lookup(config, "protocols");
299 if (setting) {
300 num_prots = config_setting_length(setting);
301 for (i = 0; i < num_prots; i++) {
302 p = calloc(1, sizeof(*p));
303 if (i == 0) *prots = p;
304 if (prev) prev->next = p;
305 prev = p;
306
307 prot = config_setting_get_elem(setting, i);
308 if ((config_setting_lookup_string(prot, "name", &name) &&
309 config_setting_lookup_string(prot, "host", &hostname) &&
310 config_setting_lookup_string(prot, "port", &port)
311 )) {
312 p->description = name;
313 config_setting_lookup_string(prot, "service", &(p->service));
314 config_setting_lookup_bool(prot, "keepalive", &p->keepalive);
315 config_setting_lookup_bool(prot, "transparent", &p->transparent);
316
317 if (config_setting_lookup_int(prot, "log_level", &p->log_level) == CONFIG_FALSE) {
318 p->log_level = 1;
319 }
320
321 resolve_split_name(&(p->saddr), hostname, port);
322
323 p->probe = get_probe(name);
324 if (!p->probe || !strcmp(name, "sni_alpn")) {
325 fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), name);
326 exit(1);
327 }
328
329 /* Probe-specific options: regex patterns */
330 if (!strcmp(name, "regex")) {
331 patterns = config_setting_get_member(prot, "regex_patterns");
332 if (patterns && config_setting_is_array(patterns)) {
333 setup_regex_probe(p, patterns);
334 }
335 }
336
337 /* Probe-specific options: SNI/ALPN */
338 if (!strcmp(name, "tls")) {
339 setup_sni_alpn(p, prot);
340 }
341
342 }
343 }
344 }
345
346 return 0;
347}
348#endif
349
350/* Parses a config file
351 * in: *filename
352 * out: *listen, a newly-allocated linked list of listen addrinfo
353 * *prots, a newly-allocated linked list of protocols
354 * 1 on error, 0 on success
355 */
356#ifdef LIBCONFIG
357static int config_parse(char *filename, struct addrinfo **listen, struct proto **prots)
358{
359 config_t config;
360 int timeout;
361 const char* str;
362
363 config_init(&config);
364 if (config_read_file(&config, filename) == CONFIG_FALSE) {
365 /* If it's a parse error then there will be a line number for the failure
366 * an I/O error (such as non-existent file) will have the error line as 0
367 */
368 if (config_error_line(&config) != 0) {
369 fprintf(stderr, "%s:%d:%s\n",
370 filename,
371 config_error_line(&config),
372 config_error_text(&config));
373 exit(1);
374 }
375 fprintf(stderr, "%s:%s\n",
376 filename,
377 config_error_text(&config));
378 return 1;
379 }
380
381 config_lookup_bool(&config, "verbose", &verbose);
382 config_lookup_bool(&config, "inetd", &inetd);
383 config_lookup_bool(&config, "foreground", &foreground);
384 config_lookup_bool(&config, "numeric", &numeric);
385 config_lookup_bool(&config, "transparent", &g_transparent);
386
387 if (config_lookup_int(&config, "timeout", (int *)&timeout) == CONFIG_TRUE) {
388 probing_timeout = timeout;
389 }
390
391 if (config_lookup_string(&config, "on-timeout", &str)) {
392 set_ontimeout(str);
393 }
394
395 config_lookup_string(&config, "user", &user_name);
396 config_lookup_string(&config, "pidfile", &pid_file);
397
398 config_listen(&config, listen);
399 config_protocols(&config, prots);
400
401 return 0;
402}
403#endif
404
405/* Adds protocols to the list of options, so command-line parsing uses the
406 * protocol definition array
407 * options: array of options to add to; must be big enough
408 * n_opts: number of options in *options before calling (i.e. where to append)
409 * prot: array of protocols
410 * n_prots: number of protocols in *prot
411 * */
412static void append_protocols(struct option *options, int n_opts, struct proto *prot , int n_prots)
413{
414 int o, p;
415
416 for (o = n_opts, p = 0; p < n_prots; o++, p++) {
417 options[o].name = prot[p].description;
418 options[o].has_arg = required_argument;
419 options[o].flag = 0;
420 options[o].val = p + PROT_SHIFT;
421 }
422}
423
424static void make_alloptions(void)
425{
426 builtins = get_builtins();
427
428 /* Create all_options, composed of const_options followed by one option per
429 * known protocol */
430 all_options = calloc(ARRAY_SIZE(const_options) + get_num_builtins(), sizeof(struct option));
431 memcpy(all_options, const_options, sizeof(const_options));
432 append_protocols(all_options, ARRAY_SIZE(const_options) - 1, builtins, get_num_builtins());
433}
434
435/* Performs a first scan of command line options to see if a configuration file
436 * is specified. If there is one, parse it now before all other options (so
437 * configuration file settings can be overridden from the command line).
438 *
439 * prots: newly-allocated list of configured protocols, if any.
440 */
441static void cmdline_config(int argc, char* argv[], struct proto** prots)
442{
443#ifdef LIBCONFIG
444 int c, res;
445 char *config_filename;
446#endif
447
448 make_alloptions();
449
450#ifdef LIBCONFIG
451 optind = 1;
452 opterr = 0; /* we're missing protocol options at this stage so don't output errors */
453 while ((c = getopt_long_only(argc, argv, optstr, all_options, NULL)) != -1) {
454 if (c == 'v') {
455 verbose++;
456 }
457 if (c == 'F') {
458 config_filename = optarg;
459 if (config_filename) {
460 res = config_parse(config_filename, &addr_listen, prots);
461 } else {
462 /* No configuration file specified -- try default file locations */
463 res = config_parse("/etc/sslh/sslh.cfg", &addr_listen, prots);
464 if (!res && verbose) fprintf(stderr, "Using /etc/sslh/sslh.cfg\n");
465 if (res) {
466 res = config_parse("/etc/sslh.cfg", &addr_listen, prots);
467 if (!res && verbose) fprintf(stderr, "Using /etc/sslh.cfg\n");
468 }
469 }
470 if (res)
471 exit(4);
472 break;
473 }
474 }
475#endif
476}
477
478
479/* Parse command-line options. prots points to a list of configured protocols,
480 * potentially non-allocated */
481static void parse_cmdline(int argc, char* argv[], struct proto* prots)
482{
483 int c;
484 struct addrinfo **a;
485 struct proto *p;
486
487 optind = 1;
488 opterr = 1;
489next_arg:
490 while ((c = getopt_long_only(argc, argv, optstr, all_options, NULL)) != -1) {
491 if (c == 0) continue;
492
493 if (c >= PROT_SHIFT) {
494 if (prots)
495 for (p = prots; p && p->next; p = p->next) {
496 /* override if protocol was already defined by config file
497 * (note it only overrides address and use builtin probe) */
498 if (!strcmp(p->description, builtins[c-PROT_SHIFT].description)) {
499 resolve_name(&(p->saddr), optarg);
500 p->probe = builtins[c-PROT_SHIFT].probe;
501 goto next_arg;
502 }
503 }
504 /* At this stage, it's a new protocol: add it to the end of the
505 * list */
506 if (!prots) {
507 /* No protocols yet -- create the list */
508 p = prots = calloc(1, sizeof(*p));
509 } else {
510 p->next = calloc(1, sizeof(*p));
511 p = p->next;
512 }
513 memcpy(p, &builtins[c-PROT_SHIFT], sizeof(*p));
514 resolve_name(&(p->saddr), optarg);
515 continue;
516 }
517
518 switch (c) {
519
520 case 'F':
521 /* Legal option, but do nothing, it was already processed in
522 * cmdline_config() */
523#ifndef LIBCONFIG
524 fprintf(stderr, "Built without libconfig support: configuration file not available.\n");
525 exit(1);
526#endif
527 break;
528
529 case 't':
530 probing_timeout = atoi(optarg);
531 break;
532
533 case OPT_ONTIMEOUT:
534 set_ontimeout(optarg);
535 break;
536
537 case 'p':
538 /* find the end of the listen list */
539 for (a = &addr_listen; *a; a = &((*a)->ai_next));
540 /* append the specified addresses */
541 resolve_name(a, optarg);
542
543 break;
544
545 case 'V':
546 printf("%s %s\n", server_type, VERSION);
547 exit(0);
548
549 case 'u':
550 user_name = optarg;
551 break;
552
553 case 'P':
554 pid_file = optarg;
555 break;
556
557 case 'v':
558 verbose++;
559 break;
560
561 default:
562 print_usage();
563 exit(2);
564 }
565 }
566
567 if (!prots) {
568 fprintf(stderr, "At least one target protocol must be specified.\n");
569 exit(2);
570 }
571
572 set_protocol_list(prots);
573
574/* If compiling with systemd socket support no need to require listen address */
575#ifndef SYSTEMD
576 if (!addr_listen && !inetd) {
577 fprintf(stderr, "No listening address specified; use at least one -p option\n");
578 exit(1);
579 }
580#endif
581
582 /* Did command-line override foreground setting? */
583 if (background)
584 foreground = 0;
585
586}
587
588int main(int argc, char *argv[])
589{
590
591 extern char *optarg;
592 extern int optind;
593 int res, num_addr_listen;
594 struct proto* protocols = NULL;
595
596 int *listen_sockets;
597
598 /* Init defaults */
599 pid_file = NULL;
600 user_name = NULL;
601
602 cmdline_config(argc, argv, &protocols);
603 parse_cmdline(argc, argv, protocols);
604
605 if (inetd)
606 {
607 verbose = 0;
608 start_shoveler(0);
609 exit(0);
610 }
611
612 if (verbose)
613 printsettings();
614
615 num_addr_listen = start_listen_sockets(&listen_sockets, addr_listen);
616
617#ifdef SYSTEMD
618 if (num_addr_listen < 1) {
619 fprintf(stderr, "No listening sockets found, restart sockets or specify addresses in config\n");
620 exit(1);
621 }
622#endif
623
624 if (!foreground) {
625 if (fork() > 0) exit(0); /* Detach */
626
627 /* New session -- become group leader */
628 if (getuid() == 0) {
629 res = setsid();
630 CHECK_RES_DIE(res, "setsid: already process leader");
631 }
632 }
633
634 setup_signals();
635
636 if (pid_file)
637 write_pid_file(pid_file);
638
639 if (user_name)
640 drop_privileges(user_name);
641
642
643 /* Open syslog connection */
644 setup_syslog(argv[0]);
645
646 if (verbose)
647 printcaps();
648
649 main_loop(listen_sockets, num_addr_listen);
650
651 return 0;
652}
653

Built with git-ssb-web