git ssb

0+

cel / sslh



Tree: 1814bcb43cdf455b847c17f1313eb35d73f94716

Files: 1814bcb43cdf455b847c17f1313eb35d73f94716 / common.c

18649 bytesRaw
1/* Code and variables that is common to both fork and select-based
2 * servers.
3 *
4 * No code here should assume whether sockets are blocking or not.
5 **/
6
7#define _GNU_SOURCE
8#include <stdarg.h>
9#include <grp.h>
10
11#include "common.h"
12#include "probe.h"
13
14/* Added to make the code compilable under CYGWIN
15 * */
16#ifndef SA_NOCLDWAIT
17#define SA_NOCLDWAIT 0
18#endif
19
20/*
21 * Settings that depend on the command line. They're set in main(), but also
22 * used in other places in common.c, and it'd be heavy-handed to pass it all as
23 * parameters
24 */
25int verbose = 0;
26int probing_timeout = 2;
27int inetd = 0;
28int foreground = 0;
29int background = 0;
30int transparent = 0;
31int numeric = 0;
32const char *user_name, *pid_file;
33
34struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
35
36#ifdef LIBWRAP
37#include <tcpd.h>
38int allow_severity =0, deny_severity = 0;
39#endif
40
41/* check result and die, printing the offending address and error */
42void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall)
43{
44 char buf[NI_MAXHOST];
45
46 if (res == -1) {
47 fprintf(stderr, "%s:%s: %s\n",
48 sprintaddr(buf, sizeof(buf), addr),
49 syscall,
50 strerror(errno));
51 exit(1);
52 }
53}
54
55/* Starts listening sockets on specified addresses.
56 * IN: addr[], num_addr
57 * OUT: *sockfd[] pointer to newly-allocated array of file descriptors
58 * Returns number of addresses bound
59 * Bound file descriptors are returned in newly-allocated *sockfd pointer
60 */
61int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list)
62{
63 struct sockaddr_storage *saddr;
64 struct addrinfo *addr;
65 int i, res, one;
66 int num_addr = 0;
67
68 for (addr = addr_list; addr; addr = addr->ai_next)
69 num_addr++;
70
71 if (verbose)
72 fprintf(stderr, "listening to %d addresses\n", num_addr);
73
74 *sockfd = malloc(num_addr * sizeof(*sockfd[0]));
75
76 for (i = 0, addr = addr_list; i < num_addr && addr; i++, addr = addr->ai_next) {
77 if (!addr) {
78 fprintf(stderr, "FATAL: Inconsistent listen number. This should not happen.\n");
79 exit(1);
80 }
81 saddr = (struct sockaddr_storage*)addr->ai_addr;
82
83 (*sockfd)[i] = socket(saddr->ss_family, SOCK_STREAM, 0);
84 check_res_dumpdie((*sockfd)[i], addr, "socket");
85
86 one = 1;
87 res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
88 check_res_dumpdie(res, addr, "setsockopt(SO_REUSEADDR)");
89
90 if (IP_FREEBIND) {
91 res = setsockopt((*sockfd)[i], IPPROTO_IP, IP_FREEBIND, (char*)&one, sizeof(one));
92 check_res_dumpdie(res, addr, "setsockopt(IP_FREEBIND)");
93 }
94
95 res = bind((*sockfd)[i], addr->ai_addr, addr->ai_addrlen);
96 check_res_dumpdie(res, addr, "bind");
97
98 res = listen ((*sockfd)[i], 50);
99 check_res_dumpdie(res, addr, "listen");
100
101 }
102
103 return num_addr;
104}
105
106/* Transparent proxying: bind the peer address of fd to the peer address of
107 * fd_from */
108#define IP_TRANSPARENT 19
109int bind_peer(int fd, int fd_from)
110{
111 struct addrinfo from;
112 struct sockaddr_storage ss;
113 int res, trans = 1;
114
115 memset(&from, 0, sizeof(from));
116 from.ai_addr = (struct sockaddr*)&ss;
117 from.ai_addrlen = sizeof(ss);
118
119 /* getpeername can fail with ENOTCONN if connection was dropped before we
120 * got here */
121 res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
122 CHECK_RES_RETURN(res, "getpeername");
123#ifndef IP_BINDANY /* use IP_TRANSPARENT */
124 res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
125 CHECK_RES_DIE(res, "setsockopt");
126#else
127 if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */
128 res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans));
129 CHECK_RES_RETURN(res, "setsockopt IP_BINDANY");
130#ifdef IPV6_BINDANY
131 } else { /* IPv6 */
132 res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans));
133 CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY");
134#endif /* IPV6_BINDANY */
135 }
136#endif /* IP_TRANSPARENT / IP_BINDANY */
137 res = bind(fd, from.ai_addr, from.ai_addrlen);
138 CHECK_RES_RETURN(res, "bind");
139
140 return 0;
141}
142
143/* Connect to first address that works and returns a file descriptor, or -1 if
144 * none work.
145 * If transparent proxying is on, use fd_from peer address on external address
146 * of new file descriptor. */
147int connect_addr(struct connection *cnx, int fd_from)
148{
149 struct addrinfo *a, from;
150 struct sockaddr_storage ss;
151 char buf[NI_MAXHOST];
152 int fd, res;
153
154 memset(&from, 0, sizeof(from));
155 from.ai_addr = (struct sockaddr*)&ss;
156 from.ai_addrlen = sizeof(ss);
157
158 res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
159 CHECK_RES_RETURN(res, "getpeername");
160
161 for (a = cnx->proto->saddr; a; a = a->ai_next) {
162 /* When transparent, make sure both connections use the same address family */
163 if (transparent && a->ai_family != from.ai_addr->sa_family)
164 continue;
165 if (verbose)
166 fprintf(stderr, "connecting to %s family %d len %d\n",
167 sprintaddr(buf, sizeof(buf), a),
168 a->ai_addr->sa_family, a->ai_addrlen);
169
170 /* XXX Needs to match ai_family from fd_from when being transparent! */
171 fd = socket(a->ai_family, SOCK_STREAM, 0);
172 if (fd == -1) {
173 log_message(LOG_ERR, "forward to %s failed:socket: %s\n",
174 cnx->proto->description, strerror(errno));
175 } else {
176 if (transparent) {
177 res = bind_peer(fd, fd_from);
178 CHECK_RES_RETURN(res, "bind_peer");
179 }
180 res = connect(fd, a->ai_addr, a->ai_addrlen);
181 if (res == -1) {
182 log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
183 cnx->proto->description, strerror(errno));
184 close(fd);
185 } else {
186 return fd;
187 }
188 }
189 }
190 return -1;
191}
192
193/* Store some data to write to the queue later */
194int defer_write(struct queue *q, void* data, int data_size)
195{
196 char *p;
197 if (verbose)
198 fprintf(stderr, "**** writing deferred on fd %d\n", q->fd);
199
200 p = realloc(q->begin_deferred_data, q->deferred_data_size + data_size);
201 if (!p) {
202 perror("realloc");
203 exit(1);
204 }
205
206 q->deferred_data = q->begin_deferred_data = p;
207 p += q->deferred_data_size;
208 q->deferred_data_size += data_size;
209 memcpy(p, data, data_size);
210
211 return 0;
212}
213
214/* tries to flush some of the data for specified queue
215 * Upon success, the number of bytes written is returned.
216 * Upon failure, -1 returned (e.g. connexion closed)
217 * */
218int flush_deferred(struct queue *q)
219{
220 int n;
221
222 if (verbose)
223 fprintf(stderr, "flushing deferred data to fd %d\n", q->fd);
224
225 n = write(q->fd, q->deferred_data, q->deferred_data_size);
226 if (n == -1)
227 return n;
228
229 if (n == q->deferred_data_size) {
230 /* All has been written -- release the memory */
231 free(q->begin_deferred_data);
232 q->begin_deferred_data = NULL;
233 q->deferred_data = NULL;
234 q->deferred_data_size = 0;
235 } else {
236 /* There is data left */
237 q->deferred_data += n;
238 q->deferred_data_size -= n;
239 }
240
241 return n;
242}
243
244
245void init_cnx(struct connection *cnx)
246{
247 memset(cnx, 0, sizeof(*cnx));
248 cnx->q[0].fd = -1;
249 cnx->q[1].fd = -1;
250 cnx->proto = get_first_protocol();
251}
252
253void dump_connection(struct connection *cnx)
254{
255 printf("state: %d\n", cnx->state);
256 printf("fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size);
257 printf("fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size);
258}
259
260
261/*
262 * moves data from one fd to other
263 *
264 * returns number of bytes copied if success
265 * returns 0 (FD_CNXCLOSED) if incoming socket closed
266 * returns FD_NODATA if no data was available
267 * returns FD_STALLED if data was read, could not be written, and has been
268 * stored in temporary buffer.
269 */
270int fd2fd(struct queue *target_q, struct queue *from_q)
271{
272 char buffer[BUFSIZ];
273 int target, from, size_r, size_w;
274
275 target = target_q->fd;
276 from = from_q->fd;
277
278 size_r = read(from, buffer, sizeof(buffer));
279 if (size_r == -1) {
280 switch (errno) {
281 case EAGAIN:
282 if (verbose)
283 fprintf(stderr, "reading 0 from %d\n", from);
284 return FD_NODATA;
285
286 case ECONNRESET:
287 case EPIPE:
288 return FD_CNXCLOSED;
289 }
290 }
291
292 CHECK_RES_RETURN(size_r, "read");
293
294 if (size_r == 0)
295 return FD_CNXCLOSED;
296
297 size_w = write(target, buffer, size_r);
298 /* process -1 when we know how to deal with it */
299 if (size_w == -1) {
300 switch (errno) {
301 case EAGAIN:
302 /* write blocked: Defer data */
303 defer_write(target_q, buffer, size_r);
304 return FD_STALLED;
305
306 case ECONNRESET:
307 case EPIPE:
308 /* remove end closed -- drop the connection */
309 return FD_CNXCLOSED;
310 }
311 } else if (size_w < size_r) {
312 /* incomplete write -- defer the rest of the data */
313 defer_write(target_q, buffer + size_w, size_r - size_w);
314 return FD_STALLED;
315 }
316
317 CHECK_RES_RETURN(size_w, "write");
318
319 return size_w;
320}
321
322/* returns a string that prints the IP and port of the sockaddr */
323char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
324{
325 char host[NI_MAXHOST], serv[NI_MAXSERV];
326 int res;
327
328 res = getnameinfo(a->ai_addr, a->ai_addrlen,
329 host, sizeof(host),
330 serv, sizeof(serv),
331 numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
332
333 if (res) {
334 log_message(LOG_ERR, "sprintaddr:getnameinfo: %s\n", gai_strerror(res));
335 /* Name resolution failed: do it numerically instead */
336 res = getnameinfo(a->ai_addr, a->ai_addrlen,
337 host, sizeof(host),
338 serv, sizeof(serv),
339 NI_NUMERICHOST | NI_NUMERICSERV);
340 /* should not fail but... */
341 if (res) {
342 log_message(LOG_ERR, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res));
343 strcpy(host, "?");
344 strcpy(serv, "?");
345 }
346 }
347
348 snprintf(buf, size, "%s:%s", host, serv);
349
350 return buf;
351}
352
353/* Turns a hostname and port (or service) into a list of struct addrinfo
354 * returns 0 on success, -1 otherwise and logs error
355 **/
356int resolve_split_name(struct addrinfo **out, const char* host, const char* serv)
357{
358 struct addrinfo hint;
359 int res;
360
361 memset(&hint, 0, sizeof(hint));
362 hint.ai_family = PF_UNSPEC;
363 hint.ai_socktype = SOCK_STREAM;
364
365 res = getaddrinfo(host, serv, &hint, out);
366 if (res)
367 log_message(LOG_ERR, "%s `%s:%s'\n", gai_strerror(res), host, serv);
368 return res;
369}
370
371/* turns a "hostname:port" string into a list of struct addrinfo;
372out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done
373fullname: input string -- it gets clobbered
374*/
375void resolve_name(struct addrinfo **out, char* fullname)
376{
377 char *serv, *host, *end;
378 int res;
379
380 /* Find port */
381 char *sep = strrchr(fullname, ':');
382 if (!sep) { /* No separator: parameter is just a port */
383 fprintf(stderr, "%s: names must be fully specified as hostname:port\n", fullname);
384 exit(1);
385 }
386 serv = sep+1;
387 *sep = 0;
388
389 host = fullname;
390
391 /* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets
392 * around IP address */
393 if (host[0] == '[') {
394 end = strrchr(host, ']');
395 if (!end) {
396 fprintf(stderr, "%s: no closing bracket in IPv6 address?\n", host);
397 }
398 host++; /* skip first bracket */
399 *end = 0; /* remove last bracket */
400 }
401
402 res = resolve_split_name(out, host, serv);
403 if (res) {
404 fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname);
405 if (res == EAI_SERVICE)
406 fprintf(stderr, "(Check you have specified all ports)\n");
407 exit(4);
408 }
409}
410
411/* Log to syslog or stderr if foreground */
412void log_message(int type, char* msg, ...)
413{
414 va_list ap;
415
416 va_start(ap, msg);
417 if (foreground)
418 vfprintf(stderr, msg, ap);
419 else
420 vsyslog(type, msg, ap);
421 va_end(ap);
422}
423
424/* syslogs who connected to where */
425void log_connection(struct connection *cnx)
426{
427 struct addrinfo addr;
428 struct sockaddr_storage ss;
429#define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1)
430 char peer[MAX_NAMELENGTH], service[MAX_NAMELENGTH],
431 local[MAX_NAMELENGTH], target[MAX_NAMELENGTH];
432 int res;
433
434 if (cnx->proto->log_level < 1)
435 return;
436
437 addr.ai_addr = (struct sockaddr*)&ss;
438 addr.ai_addrlen = sizeof(ss);
439
440 res = getpeername(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
441 if (res == -1) return; /* Can happen if connection drops before we get here.
442 In that case, don't log anything (there is no connection) */
443 sprintaddr(peer, sizeof(peer), &addr);
444
445 addr.ai_addrlen = sizeof(ss);
446 res = getsockname(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
447 if (res == -1) return;
448 sprintaddr(service, sizeof(service), &addr);
449
450 addr.ai_addrlen = sizeof(ss);
451 res = getpeername(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
452 if (res == -1) return;
453 sprintaddr(target, sizeof(target), &addr);
454
455 addr.ai_addrlen = sizeof(ss);
456 res = getsockname(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
457 if (res == -1) return;
458 sprintaddr(local, sizeof(local), &addr);
459
460 log_message(LOG_INFO, "%s:connection from %s to %s forwarded from %s to %s\n",
461 cnx->proto->description,
462 peer,
463 service,
464 local,
465 target);
466}
467
468
469/* libwrap (tcpd): check the connection is legal. This is necessary because
470 * the actual server will only see a connection coming from localhost and can't
471 * apply the rules itself.
472 *
473 * Returns -1 if access is denied, 0 otherwise
474 */
475int check_access_rights(int in_socket, const char* service)
476{
477#ifdef LIBWRAP
478 union {
479 struct sockaddr saddr;
480 struct sockaddr_storage ss;
481 } peer;
482 socklen_t size = sizeof(peer);
483 char addr_str[NI_MAXHOST], host[NI_MAXHOST];
484 int res;
485
486 res = getpeername(in_socket, &peer.saddr, &size);
487 CHECK_RES_RETURN(res, "getpeername");
488
489 /* extract peer address */
490 res = getnameinfo(&peer.saddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
491 if (res) {
492 if (verbose)
493 fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
494 strcpy(addr_str, STRING_UNKNOWN);
495 }
496 /* extract peer name */
497 strcpy(host, STRING_UNKNOWN);
498 if (!numeric) {
499 res = getnameinfo(&peer.saddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
500 if (res) {
501 if (verbose)
502 fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
503 }
504 }
505
506 if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
507 if (verbose)
508 fprintf(stderr, "access denied\n");
509 log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str);
510 close(in_socket);
511 return -1;
512 }
513#endif
514 return 0;
515}
516
517void setup_signals(void)
518{
519 int res;
520 struct sigaction action;
521
522 /* Request no SIGCHLD is sent upon termination of
523 * the children */
524 memset(&action, 0, sizeof(action));
525 action.sa_handler = NULL;
526 action.sa_flags = SA_NOCLDWAIT;
527 res = sigaction(SIGCHLD, &action, NULL);
528 CHECK_RES_DIE(res, "sigaction");
529
530 /* Set SIGTERM to exit. For some reason if it's not set explicitly,
531 * coverage information is lost when killing the process */
532 memset(&action, 0, sizeof(action));
533 action.sa_handler = exit;
534 res = sigaction(SIGTERM, &action, NULL);
535 CHECK_RES_DIE(res, "sigaction");
536
537 /* Ignore SIGPIPE . */
538 action.sa_handler = SIG_IGN;
539 res = sigaction(SIGPIPE, &action, NULL);
540 CHECK_RES_DIE(res, "sigaction");
541
542}
543
544/* Open syslog connection with appropriate banner;
545 * banner is made up of basename(bin_name)+"[pid]" */
546void setup_syslog(const char* bin_name) {
547 char *name1, *name2;
548 int res;
549
550 name1 = strdup(bin_name);
551 res = asprintf(&name2, "%s[%d]", basename(name1), getpid());
552 CHECK_RES_DIE(res, "asprintf");
553 openlog(name2, LOG_CONS, LOG_AUTH);
554 free(name1);
555 /* Don't free name2, as openlog(3) uses it (at least in glibc) */
556
557 log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
558}
559
560/* Ask OS to keep capabilities over a setuid(nonzero) */
561void set_keepcaps(int val) {
562#ifdef LIBCAP
563 int res;
564 res = prctl(PR_SET_KEEPCAPS, val, 0, 0, 0);
565 if (res) {
566 perror("prctl");
567 exit(1);
568 }
569#endif
570}
571
572/* set needed capabilities for effective and permitted, clear rest */
573void set_capabilities(void) {
574#ifdef LIBCAP
575 int res;
576 cap_t caps;
577 cap_value_t cap_list[10];
578 int ncap = 0;
579
580 if (transparent)
581 cap_list[ncap++] = CAP_NET_ADMIN;
582
583 caps = cap_init();
584
585#define _cap_set_flag(flag) do { \
586 res = cap_clear_flag(caps, flag); \
587 CHECK_RES_DIE(res, "cap_clear_flag(" #flag ")"); \
588 if (ncap > 0) { \
589 res = cap_set_flag(caps, flag, ncap, cap_list, CAP_SET); \
590 CHECK_RES_DIE(res, "cap_set_flag(" #flag ")"); \
591 } \
592 } while(0)
593
594 _cap_set_flag(CAP_EFFECTIVE);
595 _cap_set_flag(CAP_PERMITTED);
596
597#undef _cap_set_flag
598
599 res = cap_set_proc(caps);
600 CHECK_RES_DIE(res, "cap_set_proc");
601
602 res = cap_free(caps);
603 if (res) {
604 perror("cap_free");
605 exit(1);
606 }
607#endif
608}
609
610/* We don't want to run as root -- drop privileges if required */
611void drop_privileges(const char* user_name)
612{
613 int res;
614 struct passwd *pw = getpwnam(user_name);
615 if (!pw) {
616 fprintf(stderr, "%s: not found\n", user_name);
617 exit(2);
618 }
619 if (verbose)
620 fprintf(stderr, "turning into %s\n", user_name);
621
622 set_keepcaps(1);
623
624 /* remove extraneous groups in case we belong to several extra groups that
625 * may have unwanted rights. If non-root when calling setgroups(), it
626 * fails, which is fine because... we have no unwanted rights
627 * (see POS36-C for security context)
628 * */
629 setgroups(0, NULL);
630
631 res = setgid(pw->pw_gid);
632 CHECK_RES_DIE(res, "setgid");
633 res = setuid(pw->pw_uid);
634 CHECK_RES_DIE(res, "setuid");
635
636 set_capabilities();
637 set_keepcaps(0);
638}
639
640/* Writes my PID */
641void write_pid_file(const char* pidfile)
642{
643 FILE *f;
644
645 f = fopen(pidfile, "w");
646 if (!f) {
647 perror(pidfile);
648 exit(3);
649 }
650
651 fprintf(f, "%d\n", getpid());
652 fclose(f);
653}
654
655

Built with git-ssb-web