git ssb

0+

cel / sslh



Commit d7e9cbb655626718177c36988d3b63a579dbfb66

sslh 1.0

Yves Rutschle committed on 7/10/2013, 9:05:41 PM

Files changed

sslh.cadded
sslh.cView
@@ -1,0 +1,371 @@
1+/*
2+ Reimplementation of sslh in C
3+
4+# Copyright (C) 2007 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+Comments? questions? sslh@rutschle.net
22+
23+Compilation instructions:
24+
25+Solaris:
26+ cc -o sslh sslh.c -lresolv -lsocket -lnsl
27+
28+LynxOS:
29+ gcc -o tcproxy tcproxy.c -lnetinet
30+
31+*/
32+
33+#define VERSION "1.0"
34+
35+#include <sys/types.h>
36+#include <fcntl.h>
37+#include <string.h>
38+#include <unistd.h>
39+#include <stdlib.h>
40+#include <stdio.h>
41+#include <signal.h>
42+#include <sys/socket.h>
43+#include <netinet/in.h>
44+#include <arpa/inet.h>
45+#include <netdb.h>
46+#include <pwd.h>
47+
48+#define CHECK_RES_DIE(res, str) \
49+if (res == -1) { \
50+ perror(str); \
51+ exit(1); \
52+}
53+
54+#define USAGE_STRING "usage:\n\tsslh [-t <timeout>] -u <username> -p <listenport> -s [sshhost:]port -l [sslhost:]port [-v]\n"
55+
56+
57+/* Starts a listening socket on specified port.
58+ Returns file descriptor
59+ */
60+int start_listen_socket(int port)
61+{
62+ struct sockaddr_in saddr;
63+ int sockfd, res, reuse;
64+
65+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
66+ CHECK_RES_DIE(sockfd, "socket");
67+
68+ reuse = 1;
69+ res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
70+ CHECK_RES_DIE(res, "setsockopt");
71+
72+ memset(&saddr, 0, sizeof(saddr));
73+ saddr.sin_port = htons(port);
74+
75+ res = bind (sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
76+ CHECK_RES_DIE(res, "bind");
77+
78+ res = listen (sockfd, 5);
79+ CHECK_RES_DIE(res, "listen");
80+
81+ return sockfd;
82+}
83+
84+
85+/*
86+ * moves data from one fd to other
87+ * returns 0 if incoming socket closed, size moved otherwise
88+ */
89+int fd2fd(int target, int from)
90+{
91+ char buffer[BUFSIZ];
92+ int size;
93+
94+ size = read(from, buffer, sizeof(buffer));
95+ CHECK_RES_DIE(size, "read");
96+
97+ if (size == 0)
98+ return 0;
99+
100+ size = write(target, buffer, size);
101+ CHECK_RES_DIE(size, "write");
102+
103+ return size;
104+}
105+
106+/* shovels data from one fd to the other and vice-versa
107+ returns after one socket closed
108+ */
109+int shovel(int fd1, int fd2)
110+{
111+ fd_set fds;
112+ int res;
113+
114+ FD_ZERO(&fds);
115+ while (1) {
116+ FD_SET(fd1, &fds);
117+ FD_SET(fd2, &fds);
118+
119+ res = select(
120+ (fd1 > fd2 ? fd1 : fd2 ) + 1,
121+ &fds,
122+ NULL,
123+ NULL,
124+ NULL
125+ );
126+ CHECK_RES_DIE(res, "select");
127+
128+ if (FD_ISSET(fd1, &fds)) {
129+ res = fd2fd(fd2, fd1);
130+ if (!res) {
131+ printf("client socket closed\n");
132+ return res;
133+ }
134+ }
135+
136+ if (FD_ISSET(fd2, &fds)) {
137+ res = fd2fd(fd1, fd2);
138+ if (!res) {
139+ printf("server socket closed\n");
140+ return res;
141+ }
142+ }
143+ }
144+}
145+
146+/* returns a static string that prints the IP and port of the sockaddr */
147+char* get_addr(struct sockaddr* s)
148+{
149+ char addr_str[1024];
150+ static char addr_name[1024];
151+
152+ inet_ntop(AF_INET, &((struct sockaddr_in*)s)->sin_addr, addr_str, sizeof(addr_str));
153+ snprintf(addr_name, sizeof(addr_name), "%s:%d", addr_str,ntohs(((struct sockaddr_in*)s)->sin_port));
154+ return addr_name;
155+}
156+
157+/* turns a "hostname:port" string into a struct sockaddr;
158+sock: socket address to which to copy the addr
159+fullname: input string -- it gets clobbered
160+serv: default service/port
161+(defaults don't work yet)
162+*/
163+int resolve_name(struct sockaddr *sock, char* fullname, int port) {
164+ struct addrinfo *addr, hint;
165+ char *serv, *host;
166+ int res;
167+
168+ char *sep = strchr(fullname, ':');
169+
170+ if (!sep) /* No separator: parameter is just a port */
171+ {
172+ serv = fullname;
173+ fprintf(stderr, "names must be fully specified as hostname:port for the moment\n");
174+ exit(1);
175+ }
176+ else {
177+ host = fullname;
178+ serv = sep+1;
179+ *sep = 0;
180+ }
181+
182+ memset(&hint, 0, sizeof(hint));
183+ hint.ai_family = PF_INET;
184+ hint.ai_socktype = SOCK_STREAM;
185+
186+ res = getaddrinfo(host, serv, &hint, &addr);
187+ if (res) {
188+ fprintf(stderr, "%s\n", gai_strerror(res));
189+ if (res == EAI_SERVICE)
190+ fprintf(stderr, "(Check you have specified all ports)\n");
191+ exit(1);
192+ }
193+
194+ memcpy(sock, addr->ai_addr, sizeof(*sock));
195+}
196+
197+/*
198+ * Settings that depend on the command line.
199+ * They're set in main(), but also used in start_shoveler(), and it'd be heavy-handed
200+ * to pass it all as parameters
201+ */
202+int verbose = 0;
203+int timeout = 2;
204+int listen_port = 443;
205+struct sockaddr addr_ssl, addr_ssh;
206+
207+
208+/* Child process that finds out what to connect to and proxies
209+ */
210+void start_shoveler(int in_socket)
211+{
212+ fd_set fds;
213+ struct timeval tv;
214+ struct sockaddr *saddr;
215+ int res;
216+ int out_socket;
217+
218+ FD_ZERO(&fds);
219+ FD_SET(in_socket, &fds);
220+ memset(&tv, 0, sizeof(tv));
221+ tv.tv_sec = timeout;
222+ res = select(in_socket + 1, &fds, NULL, NULL, &tv);
223+ if (res == -1)
224+ perror("select");
225+
226+ /* Pick the target address depending on whether we timed out or not */
227+ if (FD_ISSET(in_socket, &fds)) {
228+ /* The client wrote something to the socket: it's an SSL connection */
229+ saddr = &addr_ssl;
230+ if (verbose)
231+ fprintf(stderr, "Forwarding to SSL\n");
232+ } else {
233+ /* The client hasn't written anything and we timed out: connect to SSH */
234+ saddr = &addr_ssh;
235+ if (verbose)
236+ fprintf(stderr, "Forwarding to SSH\n");
237+ }
238+
239+ /* Connect the target socket */
240+ out_socket = socket(AF_INET, SOCK_STREAM, 0);
241+ res = connect(out_socket, saddr, sizeof(addr_ssl));
242+ CHECK_RES_DIE(res, "connect");
243+ if (verbose)
244+ fprintf(stderr, "connected to something\n");
245+
246+ shovel(in_socket, out_socket);
247+
248+ close(in_socket);
249+ close(out_socket);
250+
251+ if (verbose)
252+ fprintf(stderr, "connection closed down\n");
253+
254+ exit(0);
255+}
256+
257+/* SIGCHLD handling:
258+ * we need to reap our children
259+ */
260+void child_handler(int signo)
261+{
262+ signal(SIGCHLD, &child_handler);
263+ wait(NULL);
264+}
265+void setup_signals(void)
266+{
267+ int res;
268+
269+ res = (int)signal(SIGCHLD, &child_handler);
270+ CHECK_RES_DIE(res, "signal");
271+}
272+
273+
274+/* We don't want to run as root -- drop priviledges if required */
275+void drop_privileges(char* user_name)
276+{
277+ int res;
278+ struct passwd *pw = getpwnam(user_name);
279+ if (!pw) {
280+ fprintf(stderr, "%s: not found\n", user_name);
281+ exit(1);
282+ }
283+ if (verbose)
284+ fprintf(stderr, "turning into %s\n", user_name);
285+
286+ res = setgid(pw->pw_gid);
287+ CHECK_RES_DIE(res, "setgid");
288+ setuid(pw->pw_uid);
289+ CHECK_RES_DIE(res, "setuid");
290+}
291+
292+int main(int argc, char *argv[])
293+{
294+
295+ extern char *optarg;
296+ extern int optind;
297+ int c, res;
298+
299+ int in_socket, out_socket, listen_socket;
300+
301+ /* Init defaults */
302+ char *user_name = "nobody";
303+ char ssl_str[] = "localhost:443"; /* need to copy -- Linux doesn't let write to BSS? */
304+ char ssh_str[] = "localhost:22";
305+ resolve_name(&addr_ssl, ssl_str, 443);
306+ resolve_name(&addr_ssh, ssh_str, 22);
307+
308+ while ((c = getopt(argc, argv, "t:l:s:p:vu:")) != EOF) {
309+ switch (c) {
310+
311+ case 't':
312+ timeout = atoi(optarg);
313+ break;
314+
315+ case 'p':
316+ listen_port = atoi(optarg);
317+ break;
318+
319+ case 'l':
320+ resolve_name(&addr_ssl, optarg, 443);
321+ break;
322+
323+ case 's':
324+ resolve_name(&addr_ssh, optarg, 22);
325+ break;
326+
327+ case 'v':
328+ verbose += 1;
329+ break;
330+
331+ case 'u':
332+ user_name = optarg;
333+ break;
334+
335+ default:
336+ fprintf(stderr, USAGE_STRING);
337+ exit(2);
338+ }
339+ }
340+
341+ if (verbose) {
342+ fprintf(stderr, "SSL addr: %s (after timeout %ds)\n", get_addr(&addr_ssl), timeout);
343+ fprintf(stderr, "SSH addr: %s\n", get_addr(&addr_ssh));
344+ fprintf(stderr, "listening on port %d\n", listen_port);
345+ }
346+
347+
348+ setup_signals();
349+
350+ listen_socket = start_listen_socket(listen_port);
351+
352+ drop_privileges(user_name);
353+
354+ /* Main server loop: accept connections, find what they are, fork shovelers */
355+ while (1)
356+ {
357+ in_socket = accept(listen_socket, 0, 0);
358+ fprintf(stderr, "accepted fd %d\n", in_socket);
359+
360+ if (!fork())
361+ {
362+ start_shoveler(in_socket);
363+ exit(0);
364+ }
365+ close(in_socket);
366+ }
367+
368+ return 0;
369+}
370+
371+

Built with git-ssb-web