git ssb

0+

cel / sslh



Tree: ab3324be477b2663196e0cc73d96aa38d59da65a

Files: ab3324be477b2663196e0cc73d96aa38d59da65a / sslh-main.c

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

Built with git-ssb-web