git ssb

0+

cel / sslh



Tree: e7d3133ba5bed4d03677431e1a5e769114a3979b

Files: e7d3133ba5bed4d03677431e1a5e769114a3979b / sslh-main.c

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

Built with git-ssb-web