git ssb

0+

cel / sslh



Tree: 66f85dc608a67e113b5436f258a8b1b27bfc13c4

Files: 66f85dc608a67e113b5436f258a8b1b27bfc13c4 / sslh-main.c

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

Built with git-ssb-web