git ssb

0+

cel / sslh



Tree: aa06261d70509eb5a571937d5e3d5c41e11f193d

Files: aa06261d70509eb5a571937d5e3d5c41e11f193d / sslh-main.c

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

Built with git-ssb-web