diff options
Diffstat (limited to 'crypto/openssl/demos/state_machine/state_machine.c')
-rw-r--r-- | crypto/openssl/demos/state_machine/state_machine.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/crypto/openssl/demos/state_machine/state_machine.c b/crypto/openssl/demos/state_machine/state_machine.c new file mode 100644 index 0000000..0140fbc --- /dev/null +++ b/crypto/openssl/demos/state_machine/state_machine.c @@ -0,0 +1,395 @@ +/* ==================================================================== + * 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 <openssl/ssl.h> +#include <assert.h> +#include <unistd.h> +#include <string.h> +#include <openssl/err.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +/* 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; + } + } + + 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; + + if(argc != 4) + { + fprintf(stderr,"%s <port> <certificate file> <key file>\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); + + /* 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); + } + + /* FIXME: we should only extract if stdout is ready */ + n=SSLStateMachine_read_extract(pMachine,buf,n); + if(n < 0) + { + SSLStateMachine_print_error(pMachine,"read extract failed"); + break; + } + assert(n >= 0); + if(n > 0) + { + 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; + } |