git ssb

0+

cel / sslh



Tree: 3aefaf300478cd6fbc4892d5baaf70521ed323af

Files: 3aefaf300478cd6fbc4892d5baaf70521ed323af / sslh-main.c

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

Built with git-ssb-web