/* ==================================================================== * 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; }