/* sslio for coronita -- a tiny webserver Version 1.0 Copyright (c) 2005 by Erik Grziwotz and Klaus Ripke, partner@malete.org some lines of code from matrixssl/examples Copyright (c) PeerSec Networks, 2002-2004. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA see README for more information for a statically linked <50KB executable on linux, use: diet -Os gcc -I/usr/local/matrixssl \ -O2 -fomit-frame-pointer -fno-strict-aliasing -s -o sslio \ sslio.c -L/usr/local/matrixssl/src -lmatrixssl where /usr/local/matrixssl is patched from matrixssl-1.2.2 according to http://malete.org/tar/matrixssl-1.2.2-diet.patch : in src/Makefile and examples/Makefile: - use CC from env (export CC='diet -Os gcc') - use SO = .a - blank out SHARED - remove -lpthread from LDFLAGS - build the $(LIBRARY): from $(OBJECTS) by ar cru $@ $^ in src/matrixConfig.h: - undef USE_CLIENT_SIDE_SSL - undef USE_MULTITHREADING (or get it patched from http://malete.org/tar/matrixssl-1.2.2-diet.tar.bz) sslio expects to inherit - a socket with SSL encrypted data on fd 0 - a socket with plaintext on fd 1 to turn a SSL socket on fd 0 into cleartext: const char *filter[] = {"./sslio", 0}; // or add args for certfile, keyfile int sp[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp)) DIE(sock); if (!fork()) { // if our original socket is on int fd intead of 0: // dup2(fd, 0); close(fd); dup2(sp[0],1); close(sp[1]); execve(*filter, filter, environ); } dup2(sp[1],0); close(sp[1}); // or just use as is on sp[1] and close(fd) close(sp[0]); */ /* $Id: sslio.c,v 1.2 2005/04/13 10:52:49 krip Exp $ */ #include /* exit */ #include /* memcpy et al */ #include /* matrixssl uses stdio anyways :( */ #include #include #include #include #include #include #define CLIENT 0 /* clients ssl fd -- do not change */ #define SERVER 1 /* fd to our server -- do not change */ #define BOTH 2 /* select's n for both == 1+max(CLIENT,SERVER) */ /* server-side plaintext buffer */ static char tos[2*SSL_MAX_PLAINTEXT_LEN], froms[SSL_MAX_PLAINTEXT_LEN]; /* 16K */ /* client-side ciphertext buffer */ static char toc[SSL_MAX_BUF_SIZE], fromc[SSL_MAX_BUF_SIZE]; /* ~18K */ /* ======================= sslSocket.c ================================= */ /* * Copyright (c) PeerSec Networks, 2002-2004. All Rights Reserved. * The latest version of this code is available at http://www.matrixssl.org * * This software is open source; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This General Public License does NOT permit incorporating this software * into proprietary programs. If you are unable to comply with the GPL, a * commercial license for this software may be purchased from PeerSec Networks * at http://www.peersec.com * * This program is distributed in WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * http://www.gnu.org/copyleft/gpl.html */ /******************************************************************************/ static ssl_t *ssl; static sslBuf_t pb; static sslBuf_t ib; static sslBuf_t ob; #ifdef DEBUG # define D(format, args...) fprintf(stderr, format "\n" , ## args) static int toti, toto; #else # define D(format, args...) #endif static void die (int code) { printf("%d:%d", code, errno); exit(code); } #define DIE(code) die(DIE_##code) #define DIE2(code, err) do {errno = err; DIE(code);} while (0) /* 2 die 4 */ enum { /* why die? -- must keep in sync */ DIE_ok, /* do NOT change */ DIE_args, DIE_chroot, DIE_ssl, DIE_select, DIE_read, DIE_write, DIE_exec, DIE_hard }; static int sslRead (int tryread) { unsigned char error, alertLevel, alertDesc; if (ib.end == ib.start) { /* out of ciphertext */ int bytes; ib.start = ib.end = ib.buf; readMore: if (0 >= (bytes = recv(CLIENT, ib.end, ib.buf+ib.size-ib.end, 0))) DIE(read); D("recv %d bytes", bytes); ib.end += bytes; } pb.start = pb.end = pb.buf; error = alertLevel = alertDesc = 0; switch (matrixSslDecode(ssl, &ib, &pb, &error, &alertLevel, &alertDesc)) { case SSL_PROCESS_DATA: /* decoded an application data record */ return pb.end - pb.start; case SSL_SEND_RESPONSE: /* We've decoded a record that requires a response into tmp If there is no data to be flushed in the out buffer, we can write out the contents of the tmp buffer. Otherwise, we need to append the data to the outgoing data buffer and flush it out. */ D("responding %d", pb.end - pb.start); if (pb.end-pb.start != send(CLIENT, pb.start, pb.end-pb.start, 0)) DIE(write); pb.start = pb.end = pb.buf; case SSL_SUCCESS: /* record did not return data or require a response */ return 0; case SSL_PARTIAL: /* need more ciphertext */ if (ib.end-ib.start < ib.size) { if (ib.buf < ib.start) { memmove(ib.buf, ib.start, ib.end - ib.start); ib.end -= (ib.start - ib.buf); ib.start = ib.buf; } if (tryread) goto readMore; return 0; } DIE2(ssl, EINPROGRESS); /* basically a protocol error */ case SSL_ALERT: /* We've decoded an alert. The level and description passed into matrixSslDecode are filled in with the specifics. */ if (SSL_ALERT_CLOSE_NOTIFY == alertDesc) break; D("SSL: Closing on client alert %d: %d", alertLevel, alertDesc); DIE2(ssl, EPIPE); case SSL_FULL: DIE2(ssl, EMSGSIZE); default: case SSL_ERROR: /* There was an error decoding the data, or encoding the out buffer. There may be a response data in the out buffer, so try to send. We try a single hail-mary send of the data, and then close the socket. don't worry too much about a clean flush. */ D("SSL: Closing on protocol error %d", error); if (pb.start < pb.end) send(CLIENT, pb.start, pb.end - pb.start, MSG_DONTWAIT); DIE2(ssl, EBADMSG); } return -1; /* proper "EOF" */ } /* ======================= sslSocket.c ================================= */ static char *certfile = "cert.pem"; static char *privfile = "priv.pem"; static char usage[] = "usage: sslio [certfile [keyfile ]]\n" "certfile may be cert.pem;ca.pem... for ca chain\n\n"; static int len; static fd_set readable, writable; static void croak (int err) { write(2, usage, sizeof usage-1); DIE2(args, err); } int main (int argc, char **argv) { if (1 < argc) certfile = argv[1]; if (2 < argc) privfile = argv[2]; if (0 > matrixSslOpen()) DIE2(ssl, ENOSYS); { sslKeys_t *keys; if (0 > matrixSslReadKeys(&keys, certfile, privfile, 0, 0)) croak(ENOENT); if (0 > matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER)) DIE(ssl); } if (!geteuid()) { if (!chdir("ssl") && chroot(".")) DIE(chroot); if (setuid(getuid())) DIE(chroot); } ib.start = ib.end = ib.buf = fromc; ib.size = sizeof fromc; ob.size = sizeof toc; pb.start = pb.end = pb.buf = tos; pb.size = sizeof tos; /* Reading handshake records should always return 0 bytes, we aren't expecting any data yet. */ do if (sslRead(!0)) DIE(ssl); while (!matrixSslHandshakeIsComplete(ssl)); FD_ZERO(&readable); FD_ZERO(&writable); for (;;) { int tosl = 0, tosd = 0, tryread = !0, wrote; /* read from both */ D("waiting on both"); FD_SET(SERVER, &readable); FD_SET(CLIENT, &readable); if (0 >= select(BOTH, &readable, 0,0,0)) DIE(select); if (FD_ISSET(CLIENT, &readable)) { /* or closed connection ... */ decodeMore: if (0 > (tosl = sslRead(tryread))) break; D("from client: %d %d", tosl, toti += tosl); FD_CLR(CLIENT, &readable); } for (;;) { /* read/write server */ if (FD_ISSET(SERVER, &readable)) { D("server is readable"); /* as long as server is readable, we blocking copy to client */ while (0 < (len = recv(SERVER, froms, sizeof froms, MSG_DONTWAIT))) { D("from server: %d %d", len, toto += tosl); ob.start = ob.end = ob.buf = toc; len = matrixSslEncode(ssl, (unsigned char *)froms, len, &ob); if (0 >= len || len != send(CLIENT, ob.start, len, 0)) DIE(write); } D("from server done %d %d", len, errno); if (!len || EAGAIN != errno) { ob.start = ob.end = ob.buf = toc; matrixSslEncodeClosureAlert(ssl, &ob); send(CLIENT, ob.start, (ob.end - ob.start), MSG_DONTWAIT); exit(0); } } /* nothing more to read from server */ if (tosl <= tosd) break; D("writing server"); if (0 < (wrote = send(SERVER, pb.start+tosd, tosl-tosd, MSG_DONTWAIT))) { D("to server: %d", wrote); if (tosl <= (tosd += wrote)) { if (ib.start < ib.end) { /* try to decode more */ tosd = tryread = 0; /* but w/o reading from socket */ FD_CLR(SERVER, &readable); goto decodeMore; } break; /* again select on both */ } } else if (EAGAIN != errno) DIE(write); D("waiting server"); FD_SET(SERVER, &readable); FD_SET(SERVER, &writable); if (0 >= select(SERVER+1, &readable, &writable,0,0)) DIE(select); } } D("client closed conn"); exit(0); return 42; }