git ssb

0+

cel / sslh



Tree: 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a

Files: 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a / sslh-main.c

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

Built with git-ssb-web