git ssb

0+

cel / sslh



Tree: 5cd1fa18753c479f7eaef26b893016a37646364f

Files: 5cd1fa18753c479f7eaef26b893016a37646364f / sslh-main.c

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

Built with git-ssb-web