Compare commits

..
3 Commits
Author SHA1 Message Date
Yves Rutschle 3386a64a4d v1.3 2013-07-10 23:07:49 +02:00
Yves Rutschle b49617923f v1.2 2013-07-10 23:07:20 +02:00
Yves Rutschle d0c0689e3c v1.1 2013-07-10 23:06:51 +02:00
+140 -46
View File
@@ -1,7 +1,7 @@
/*
Reimplementation of sslh in C
# Copyright (C) 2007 Yves Rutschle
# Copyright (C) 2007-2008 Yves Rutschle
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
@@ -28,9 +28,34 @@ Solaris:
LynxOS:
gcc -o tcproxy tcproxy.c -lnetinet
Linux:
cc -o sslh sslh.c -lnet
HISTORY
v1.3: 14MAY2008
Added parsing for local interface to listen on
Changed default SSL connexion to port 442 (443 doesn't make
sense as a default as we're already listening on 443)
Syslog incoming connexions
v1.2: 12MAY2008
Fixed compilation warning for AMD64 (Thx Daniel Lange)
v1.1: 21MAY2007
Making sslhc more like a real daemon:
* If $PIDFILE is defined, write first PID to it upon startup
* Fork at startup (detach from terminal)
(thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist)
* Less memory usage (?)
v1.0:
* Basic functionality: privilege dropping, target hostnames and ports
configurable.
*/
#define VERSION "1.0"
#define VERSION "1.3"
#include <sys/types.h>
#include <fcntl.h>
@@ -40,10 +65,12 @@ LynxOS:
#include <stdio.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <syslog.h>
#define CHECK_RES_DIE(res, str) \
if (res == -1) { \
@@ -51,15 +78,26 @@ if (res == -1) { \
exit(1); \
}
#define USAGE_STRING "usage:\n\tsslh [-t <timeout>] -u <username> -p <listenport> -s [sshhost:]port -l [sslhost:]port [-v]\n"
#define USAGE_STRING \
"sslh v" VERSION "\n" \
"usage:\n" \
"\texport PIDFILE=/var/run/sslhc.pid\n" \
"\tsslh [-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
"\t\t-s [sshhost:]port -l [sslhost:]port [-v]\n\n" \
"-v: verbose\n" \
"-p: address and port to listen on. default: 0.0.0.0:443\n" \
"-s: SSH address: where to connect an SSH connexion. default: localhost:22\n" \
"-l: SSL address: where to connect an SSL connexion.\n" \
""
int verbose = 0; /* That's really quite global */
/* Starts a listening socket on specified port.
/* Starts a listening socket on specified address.
Returns file descriptor
*/
int start_listen_socket(int port)
int start_listen_socket(struct sockaddr *addr)
{
struct sockaddr_in saddr;
struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
int sockfd, res, reuse;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -69,10 +107,7 @@ int start_listen_socket(int port)
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
CHECK_RES_DIE(res, "setsockopt");
memset(&saddr, 0, sizeof(saddr));
saddr.sin_port = htons(port);
res = bind (sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
res = bind (sockfd, (struct sockaddr*)saddr, sizeof(*saddr));
CHECK_RES_DIE(res, "bind");
res = listen (sockfd, 5);
@@ -128,7 +163,7 @@ int shovel(int fd1, int fd2)
if (FD_ISSET(fd1, &fds)) {
res = fd2fd(fd2, fd1);
if (!res) {
printf("client socket closed\n");
if (verbose) fprintf(stderr, "client socket closed\n");
return res;
}
}
@@ -136,31 +171,28 @@ int shovel(int fd1, int fd2)
if (FD_ISSET(fd2, &fds)) {
res = fd2fd(fd1, fd2);
if (!res) {
printf("server socket closed\n");
if (verbose) fprintf(stderr, "server socket closed\n");
return res;
}
}
}
}
/* returns a static string that prints the IP and port of the sockaddr */
char* get_addr(struct sockaddr* s)
/* returns a string that prints the IP and port of the sockaddr */
char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
{
char addr_str[1024];
static char addr_name[1024];
inet_ntop(AF_INET, &((struct sockaddr_in*)s)->sin_addr, addr_str, sizeof(addr_str));
snprintf(addr_name, sizeof(addr_name), "%s:%d", addr_str,ntohs(((struct sockaddr_in*)s)->sin_port));
return addr_name;
snprintf(buf, size, "%s:%d", addr_str, ntohs(((struct sockaddr_in*)s)->sin_port));
return buf;
}
/* turns a "hostname:port" string into a struct sockaddr;
sock: socket address to which to copy the addr
fullname: input string -- it gets clobbered
serv: default service/port
(defaults don't work yet)
*/
int resolve_name(struct sockaddr *sock, char* fullname, int port) {
void resolve_name(struct sockaddr *sock, char* fullname) {
struct addrinfo *addr, hint;
char *serv, *host;
int res;
@@ -170,7 +202,7 @@ int resolve_name(struct sockaddr *sock, char* fullname, int port) {
if (!sep) /* No separator: parameter is just a port */
{
serv = fullname;
fprintf(stderr, "names must be fully specified as hostname:port for the moment\n");
fprintf(stderr, "names must be fully specified as hostname:port\n");
exit(1);
}
else {
@@ -194,14 +226,29 @@ int resolve_name(struct sockaddr *sock, char* fullname, int port) {
memcpy(sock, addr->ai_addr, sizeof(*sock));
}
/* syslogs who connected to where */
void log_connexion(int socket, char* target)
{
struct sockaddr peeraddr;
socklen_t size = sizeof(peeraddr);
char buf[64];
int res;
res = getpeername(socket, &peeraddr, &size);
CHECK_RES_DIE(res, "getpeername");
syslog(LOG_INFO, "connexion from %s forwarded to %s\n",
sprintaddr(buf, sizeof(buf), &peeraddr), target);
}
/*
* Settings that depend on the command line.
* Settings that depend on the command line. That's less global than verbose * :-)
* They're set in main(), but also used in start_shoveler(), and it'd be heavy-handed
* to pass it all as parameters
*/
int verbose = 0;
int timeout = 2;
int listen_port = 443;
struct sockaddr addr_listen;
struct sockaddr addr_ssl, addr_ssh;
@@ -214,6 +261,7 @@ void start_shoveler(int in_socket)
struct sockaddr *saddr;
int res;
int out_socket;
char *target;
FD_ZERO(&fds);
FD_SET(in_socket, &fds);
@@ -227,15 +275,15 @@ void start_shoveler(int in_socket)
if (FD_ISSET(in_socket, &fds)) {
/* The client wrote something to the socket: it's an SSL connection */
saddr = &addr_ssl;
if (verbose)
fprintf(stderr, "Forwarding to SSL\n");
target = "SSL";
} else {
/* The client hasn't written anything and we timed out: connect to SSH */
saddr = &addr_ssh;
if (verbose)
fprintf(stderr, "Forwarding to SSH\n");
target = "SSH";
}
log_connexion(in_socket, target);
/* Connect the target socket */
out_socket = socket(AF_INET, SOCK_STREAM, 0);
res = connect(out_socket, saddr, sizeof(addr_ssl));
@@ -264,10 +312,13 @@ void child_handler(int signo)
}
void setup_signals(void)
{
int res;
void* res;
res = (int)signal(SIGCHLD, &child_handler);
CHECK_RES_DIE(res, "signal");
res = signal(SIGCHLD, &child_handler);
if (res == SIG_ERR) {
perror("signal");
exit(1);
}
}
@@ -289,6 +340,39 @@ void drop_privileges(char* user_name)
CHECK_RES_DIE(res, "setuid");
}
/* Writes my PID if $PIDFILE is defined */
void write_pid_file(void)
{
char *pidfile = getenv("PIDFILE");
FILE *f;
if (!pidfile)
return;
f = fopen(pidfile, "w");
if (!f) {
perror(pidfile);
exit(1);
}
fprintf(f, "%d\n", getpid());
fclose(f);
}
void printsettings(void)
{
char buf[64];
fprintf(
stderr,
"SSL addr: %s (after timeout %ds)\n",
sprintaddr(buf, sizeof(buf), &addr_ssl),
timeout
);
fprintf(stderr, "SSH addr: %s\n", sprintaddr(buf, sizeof(buf), &addr_ssh));
fprintf(stderr, "listening on %s\n", sprintaddr(buf, sizeof(buf), &addr_listen));
}
int main(int argc, char *argv[])
{
@@ -296,14 +380,17 @@ int main(int argc, char *argv[])
extern int optind;
int c, res;
int in_socket, out_socket, listen_socket;
int in_socket, listen_socket;
/* Init defaults */
char *user_name = "nobody";
char ssl_str[] = "localhost:443"; /* need to copy -- Linux doesn't let write to BSS? */
char listen_str[] = "0.0.0.0:443";
char ssl_str[] = "localhost:442";
char ssh_str[] = "localhost:22";
resolve_name(&addr_ssl, ssl_str, 443);
resolve_name(&addr_ssh, ssh_str, 22);
resolve_name(&addr_listen, listen_str);
resolve_name(&addr_ssl, ssl_str);
resolve_name(&addr_ssh, ssh_str);
while ((c = getopt(argc, argv, "t:l:s:p:vu:")) != EOF) {
switch (c) {
@@ -313,15 +400,15 @@ int main(int argc, char *argv[])
break;
case 'p':
listen_port = atoi(optarg);
resolve_name(&addr_listen, optarg);
break;
case 'l':
resolve_name(&addr_ssl, optarg, 443);
resolve_name(&addr_ssl, optarg);
break;
case 's':
resolve_name(&addr_ssh, optarg, 22);
resolve_name(&addr_ssh, optarg);
break;
case 'v':
@@ -338,24 +425,31 @@ int main(int argc, char *argv[])
}
}
if (verbose) {
fprintf(stderr, "SSL addr: %s (after timeout %ds)\n", get_addr(&addr_ssl), timeout);
fprintf(stderr, "SSH addr: %s\n", get_addr(&addr_ssh));
fprintf(stderr, "listening on port %d\n", listen_port);
}
if (verbose)
printsettings();
setup_signals();
listen_socket = start_listen_socket(listen_port);
listen_socket = start_listen_socket(&addr_listen);
if (fork() > 0) exit(0); /* Detach */
write_pid_file();
drop_privileges(user_name);
/* New session -- become group leader */
res = setsid();
CHECK_RES_DIE(res, "setsid: already process leader");
/* Open syslog connexion */
openlog(argv[0], LOG_CONS, LOG_AUTH);
/* Main server loop: accept connections, find what they are, fork shovelers */
while (1)
{
in_socket = accept(listen_socket, 0, 0);
fprintf(stderr, "accepted fd %d\n", in_socket);
if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
if (!fork())
{