/* ==================================================================== * Copyright (c) 2000 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ /* * Nuron, a leader in hardware encryption technology, generously * sponsored the development of this demo by Ben Laurie. * * See http://www.nuron.com/. */ /* * the aim of this demo is to provide a fully working state-machine * style SSL implementation, i.e. one where the main loop acquires * some data, then converts it from or to SSL by feeding it into the * SSL state machine. It then does any I/O required by the state machine * and loops. * * In order to keep things as simple as possible, this implementation * listens on a TCP socket, which it expects to get an SSL connection * on (for example, from s_client) and from then on writes decrypted * data to stdout and encrypts anything arriving on stdin. Verbose * commentary is written to stderr. * * This implementation acts as a server, but it can also be done for a client. */ #include #include #include #include #include #include #include #include /* * die_unless is intended to work like assert, except that it happens always, * even if NDEBUG is defined. Use assert as a stopgap. */ #define die_unless(x) assert(x) typedef struct { SSL_CTX *pCtx; BIO *pbioRead; BIO *pbioWrite; SSL *pSSL; } SSLStateMachine; void SSLStateMachine_print_error(SSLStateMachine * pMachine, const char *szErr) { unsigned long l; fprintf(stderr, "%s\n", szErr); while ((l = ERR_get_error())) { char buf[1024]; ERR_error_string_n(l, buf, sizeof buf); fprintf(stderr, "Error %lx: %s\n", l, buf); } } SSLStateMachine *SSLStateMachine_new(const char *szCertificateFile, const char *szKeyFile) { SSLStateMachine *pMachine = malloc(sizeof *pMachine); int n; die_unless(pMachine); pMachine->pCtx = SSL_CTX_new(SSLv23_server_method()); die_unless(pMachine->pCtx); n = SSL_CTX_use_certificate_file(pMachine->pCtx, szCertificateFile, SSL_FILETYPE_PEM); die_unless(n > 0); n = SSL_CTX_use_PrivateKey_file(pMachine->pCtx, szKeyFile, SSL_FILETYPE_PEM); die_unless(n > 0); pMachine->pSSL = SSL_new(pMachine->pCtx); die_unless(pMachine->pSSL); pMachine->pbioRead = BIO_new(BIO_s_mem()); pMachine->pbioWrite = BIO_new(BIO_s_mem()); SSL_set_bio(pMachine->pSSL, pMachine->pbioRead, pMachine->pbioWrite); SSL_set_accept_state(pMachine->pSSL); return pMachine; } void SSLStateMachine_read_inject(SSLStateMachine * pMachine, const unsigned char *aucBuf, int nBuf) { int n = BIO_write(pMachine->pbioRead, aucBuf, nBuf); /* * If it turns out this assert fails, then buffer the data here and just * feed it in in churn instead. Seems to me that it should be guaranteed * to succeed, though. */ assert(n == nBuf); fprintf(stderr, "%d bytes of encrypted data fed to state machine\n", n); } int SSLStateMachine_read_extract(SSLStateMachine * pMachine, unsigned char *aucBuf, int nBuf) { int n; if (!SSL_is_init_finished(pMachine->pSSL)) { fprintf(stderr, "Doing SSL_accept\n"); n = SSL_accept(pMachine->pSSL); if (n == 0) fprintf(stderr, "SSL_accept returned zero\n"); if (n < 0) { int err; if ((err = SSL_get_error(pMachine->pSSL, n)) == SSL_ERROR_WANT_READ) { fprintf(stderr, "SSL_accept wants more data\n"); return 0; } SSLStateMachine_print_error(pMachine, "SSL_accept error"); exit(7); } return 0; } n = SSL_read(pMachine->pSSL, aucBuf, nBuf); if (n < 0) { int err = SSL_get_error(pMachine->pSSL, n); if (err == SSL_ERROR_WANT_READ) { fprintf(stderr, "SSL_read wants more data\n"); return 0; } SSLStateMachine_print_error(pMachine, "SSL_read error"); exit(8); } fprintf(stderr, "%d bytes of decrypted data read from state machine\n", n); return n; } int SSLStateMachine_write_can_extract(SSLStateMachine * pMachine) { int n = BIO_pending(pMachine->pbioWrite); if (n) fprintf(stderr, "There is encrypted data available to write\n"); else fprintf(stderr, "There is no encrypted data available to write\n"); return n; } int SSLStateMachine_write_extract(SSLStateMachine * pMachine, unsigned char *aucBuf, int nBuf) { int n; n = BIO_read(pMachine->pbioWrite, aucBuf, nBuf); fprintf(stderr, "%d bytes of encrypted data read from state machine\n", n); return n; } void SSLStateMachine_write_inject(SSLStateMachine * pMachine, const unsigned char *aucBuf, int nBuf) { int n = SSL_write(pMachine->pSSL, aucBuf, nBuf); /* * If it turns out this assert fails, then buffer the data here and just * feed it in in churn instead. Seems to me that it should be guaranteed * to succeed, though. */ assert(n == nBuf); fprintf(stderr, "%d bytes of unencrypted data fed to state machine\n", n); } int OpenSocket(int nPort) { int nSocket; struct sockaddr_in saServer; struct sockaddr_in saClient; int one = 1; int nSize; int nFD; int nLen; nSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (nSocket < 0) { perror("socket"); exit(1); } if (setsockopt (nSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof one) < 0) { perror("setsockopt"); exit(2); } memset(&saServer, 0, sizeof saServer); saServer.sin_family = AF_INET; saServer.sin_port = htons(nPort); nSize = sizeof saServer; if (bind(nSocket, (struct sockaddr *)&saServer, nSize) < 0) { perror("bind"); exit(3); } if (listen(nSocket, 512) < 0) { perror("listen"); exit(4); } nLen = sizeof saClient; nFD = accept(nSocket, (struct sockaddr *)&saClient, &nLen); if (nFD < 0) { perror("accept"); exit(5); } fprintf(stderr, "Incoming accepted on port %d\n", nPort); return nFD; } int main(int argc, char **argv) { SSLStateMachine *pMachine; int nPort; int nFD; const char *szCertificateFile; const char *szKeyFile; char rbuf[1]; int nrbuf = 0; if (argc != 4) { fprintf(stderr, "%s \n", argv[0]); exit(6); } nPort = atoi(argv[1]); szCertificateFile = argv[2]; szKeyFile = argv[3]; SSL_library_init(); OpenSSL_add_ssl_algorithms(); SSL_load_error_strings(); ERR_load_crypto_strings(); nFD = OpenSocket(nPort); pMachine = SSLStateMachine_new(szCertificateFile, szKeyFile); for (;;) { fd_set rfds, wfds; unsigned char buf[1024]; int n; FD_ZERO(&rfds); FD_ZERO(&wfds); /* Select socket for input */ FD_SET(nFD, &rfds); /* check whether there's decrypted data */ if (!nrbuf) nrbuf = SSLStateMachine_read_extract(pMachine, rbuf, 1); /* if there's decrypted data, check whether we can write it */ if (nrbuf) FD_SET(1, &wfds); /* Select socket for output */ if (SSLStateMachine_write_can_extract(pMachine)) FD_SET(nFD, &wfds); /* Select stdin for input */ FD_SET(0, &rfds); /* Wait for something to do something */ n = select(nFD + 1, &rfds, &wfds, NULL, NULL); assert(n > 0); /* Socket is ready for input */ if (FD_ISSET(nFD, &rfds)) { n = read(nFD, buf, sizeof buf); if (n == 0) { fprintf(stderr, "Got EOF on socket\n"); exit(0); } assert(n > 0); SSLStateMachine_read_inject(pMachine, buf, n); } /* stdout is ready for output (and hence we have some to send it) */ if (FD_ISSET(1, &wfds)) { assert(nrbuf == 1); buf[0] = rbuf[0]; nrbuf = 0; n = SSLStateMachine_read_extract(pMachine, buf + 1, sizeof buf - 1); if (n < 0) { SSLStateMachine_print_error(pMachine, "read extract failed"); break; } assert(n >= 0); ++n; if (n > 0) { /* FIXME: has to be true now */ int w; w = write(1, buf, n); /* FIXME: we should push back any unwritten data */ assert(w == n); } } /* * Socket is ready for output (and therefore we have output to send) */ if (FD_ISSET(nFD, &wfds)) { int w; n = SSLStateMachine_write_extract(pMachine, buf, sizeof buf); assert(n > 0); w = write(nFD, buf, n); /* FIXME: we should push back any unwritten data */ assert(w == n); } /* Stdin is ready for input */ if (FD_ISSET(0, &rfds)) { n = read(0, buf, sizeof buf); if (n == 0) { fprintf(stderr, "Got EOF on stdin\n"); exit(0); } assert(n > 0); SSLStateMachine_write_inject(pMachine, buf, n); } } /* not reached */ return 0; }