state_machine.c revision 68651
1281681Srpaulo/* ==================================================================== 2281681Srpaulo * Copyright (c) 2000 The OpenSSL Project. All rights reserved. 3281681Srpaulo * 4281681Srpaulo * Redistribution and use in source and binary forms, with or without 5281681Srpaulo * modification, are permitted provided that the following conditions 6281681Srpaulo * are met: 7281681Srpaulo * 8281681Srpaulo * 1. Redistributions of source code must retain the above copyright 9281681Srpaulo * notice, this list of conditions and the following disclaimer. 10281681Srpaulo * 11281681Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 12281681Srpaulo * notice, this list of conditions and the following disclaimer in 13281681Srpaulo * the documentation and/or other materials provided with the 14281681Srpaulo * distribution. 15281681Srpaulo * 16281681Srpaulo * 3. All advertising materials mentioning features or use of this 17281681Srpaulo * software must display the following acknowledgment: 18281681Srpaulo * "This product includes software developed by the OpenSSL Project 19281681Srpaulo * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" 20281681Srpaulo * 21281681Srpaulo * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 22281681Srpaulo * endorse or promote products derived from this software without 23281681Srpaulo * prior written permission. For written permission, please contact 24281681Srpaulo * openssl-core@openssl.org. 25281681Srpaulo * 26281681Srpaulo * 5. Products derived from this software may not be called "OpenSSL" 27281681Srpaulo * nor may "OpenSSL" appear in their names without prior written 28281681Srpaulo * permission of the OpenSSL Project. 29281681Srpaulo * 30281681Srpaulo * 6. Redistributions of any form whatsoever must retain the following 31281681Srpaulo * acknowledgment: 32281681Srpaulo * "This product includes software developed by the OpenSSL Project 33281681Srpaulo * for use in the OpenSSL Toolkit (http://www.openssl.org/)" 34281681Srpaulo * 35351611Scy * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 36351611Scy * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37351611Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 38351611Scy * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 39351611Scy * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40346981Scy * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 41346981Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 42346981Scy * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 43281681Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 44346981Scy * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 45346981Scy * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 46346981Scy * OF THE POSSIBILITY OF SUCH DAMAGE. 47346981Scy * ==================================================================== 48346981Scy * 49281681Srpaulo * This product includes cryptographic software written by Eric Young 50 * (eay@cryptsoft.com). This product includes software written by Tim 51 * Hudson (tjh@cryptsoft.com). 52 * 53 */ 54 55/* 56 * Nuron, a leader in hardware encryption technology, generously 57 * sponsored the development of this demo by Ben Laurie. 58 * 59 * See http://www.nuron.com/. 60 */ 61 62/* 63 * the aim of this demo is to provide a fully working state-machine 64 * style SSL implementation, i.e. one where the main loop acquires 65 * some data, then converts it from or to SSL by feeding it into the 66 * SSL state machine. It then does any I/O required by the state machine 67 * and loops. 68 * 69 * In order to keep things as simple as possible, this implementation 70 * listens on a TCP socket, which it expects to get an SSL connection 71 * on (for example, from s_client) and from then on writes decrypted 72 * data to stdout and encrypts anything arriving on stdin. Verbose 73 * commentary is written to stderr. 74 * 75 * This implementation acts as a server, but it can also be done for a client. */ 76 77#include <openssl/ssl.h> 78#include <assert.h> 79#include <unistd.h> 80#include <string.h> 81#include <openssl/err.h> 82#include <sys/types.h> 83#include <sys/socket.h> 84#include <netinet/in.h> 85 86/* die_unless is intended to work like assert, except that it happens 87 always, even if NDEBUG is defined. Use assert as a stopgap. */ 88 89#define die_unless(x) assert(x) 90 91typedef struct 92 { 93 SSL_CTX *pCtx; 94 BIO *pbioRead; 95 BIO *pbioWrite; 96 SSL *pSSL; 97 } SSLStateMachine; 98 99void SSLStateMachine_print_error(SSLStateMachine *pMachine,const char *szErr) 100 { 101 unsigned long l; 102 103 fprintf(stderr,"%s\n",szErr); 104 while((l=ERR_get_error())) 105 { 106 char buf[1024]; 107 108 ERR_error_string_n(l,buf,sizeof buf); 109 fprintf(stderr,"Error %lx: %s\n",l,buf); 110 } 111 } 112 113SSLStateMachine *SSLStateMachine_new(const char *szCertificateFile, 114 const char *szKeyFile) 115 { 116 SSLStateMachine *pMachine=malloc(sizeof *pMachine); 117 int n; 118 119 die_unless(pMachine); 120 121 pMachine->pCtx=SSL_CTX_new(SSLv23_server_method()); 122 die_unless(pMachine->pCtx); 123 124 n=SSL_CTX_use_certificate_file(pMachine->pCtx,szCertificateFile, 125 SSL_FILETYPE_PEM); 126 die_unless(n > 0); 127 128 n=SSL_CTX_use_PrivateKey_file(pMachine->pCtx,szKeyFile,SSL_FILETYPE_PEM); 129 die_unless(n > 0); 130 131 pMachine->pSSL=SSL_new(pMachine->pCtx); 132 die_unless(pMachine->pSSL); 133 134 pMachine->pbioRead=BIO_new(BIO_s_mem()); 135 136 pMachine->pbioWrite=BIO_new(BIO_s_mem()); 137 138 SSL_set_bio(pMachine->pSSL,pMachine->pbioRead,pMachine->pbioWrite); 139 140 SSL_set_accept_state(pMachine->pSSL); 141 142 return pMachine; 143 } 144 145void SSLStateMachine_read_inject(SSLStateMachine *pMachine, 146 const unsigned char *aucBuf,int nBuf) 147 { 148 int n=BIO_write(pMachine->pbioRead,aucBuf,nBuf); 149 /* If it turns out this assert fails, then buffer the data here 150 * and just feed it in in churn instead. Seems to me that it 151 * should be guaranteed to succeed, though. 152 */ 153 assert(n == nBuf); 154 fprintf(stderr,"%d bytes of encrypted data fed to state machine\n",n); 155 } 156 157int SSLStateMachine_read_extract(SSLStateMachine *pMachine, 158 unsigned char *aucBuf,int nBuf) 159 { 160 int n; 161 162 if(!SSL_is_init_finished(pMachine->pSSL)) 163 { 164 fprintf(stderr,"Doing SSL_accept\n"); 165 n=SSL_accept(pMachine->pSSL); 166 if(n == 0) 167 fprintf(stderr,"SSL_accept returned zero\n"); 168 if(n < 0) 169 { 170 int err; 171 172 if((err=SSL_get_error(pMachine->pSSL,n)) == SSL_ERROR_WANT_READ) 173 { 174 fprintf(stderr,"SSL_accept wants more data\n"); 175 return 0; 176 } 177 178 SSLStateMachine_print_error(pMachine,"SSL_accept error"); 179 exit(7); 180 } 181 return 0; 182 } 183 184 n=SSL_read(pMachine->pSSL,aucBuf,nBuf); 185 if(n < 0) 186 { 187 int err=SSL_get_error(pMachine->pSSL,n); 188 189 if(err == SSL_ERROR_WANT_READ) 190 { 191 fprintf(stderr,"SSL_read wants more data\n"); 192 return 0; 193 } 194 } 195 196 fprintf(stderr,"%d bytes of decrypted data read from state machine\n",n); 197 return n; 198 } 199 200int SSLStateMachine_write_can_extract(SSLStateMachine *pMachine) 201 { 202 int n=BIO_pending(pMachine->pbioWrite); 203 if(n) 204 fprintf(stderr,"There is encrypted data available to write\n"); 205 else 206 fprintf(stderr,"There is no encrypted data available to write\n"); 207 208 return n; 209 } 210 211int SSLStateMachine_write_extract(SSLStateMachine *pMachine, 212 unsigned char *aucBuf,int nBuf) 213 { 214 int n; 215 216 n=BIO_read(pMachine->pbioWrite,aucBuf,nBuf); 217 fprintf(stderr,"%d bytes of encrypted data read from state machine\n",n); 218 return n; 219 } 220 221void SSLStateMachine_write_inject(SSLStateMachine *pMachine, 222 const unsigned char *aucBuf,int nBuf) 223 { 224 int n=SSL_write(pMachine->pSSL,aucBuf,nBuf); 225 /* If it turns out this assert fails, then buffer the data here 226 * and just feed it in in churn instead. Seems to me that it 227 * should be guaranteed to succeed, though. 228 */ 229 assert(n == nBuf); 230 fprintf(stderr,"%d bytes of unencrypted data fed to state machine\n",n); 231 } 232 233int OpenSocket(int nPort) 234 { 235 int nSocket; 236 struct sockaddr_in saServer; 237 struct sockaddr_in saClient; 238 int one=1; 239 int nSize; 240 int nFD; 241 int nLen; 242 243 nSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 244 if(nSocket < 0) 245 { 246 perror("socket"); 247 exit(1); 248 } 249 250 if(setsockopt(nSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof one) < 0) 251 { 252 perror("setsockopt"); 253 exit(2); 254 } 255 256 memset(&saServer,0,sizeof saServer); 257 saServer.sin_family=AF_INET; 258 saServer.sin_port=htons(nPort); 259 nSize=sizeof saServer; 260 if(bind(nSocket,(struct sockaddr *)&saServer,nSize) < 0) 261 { 262 perror("bind"); 263 exit(3); 264 } 265 266 if(listen(nSocket,512) < 0) 267 { 268 perror("listen"); 269 exit(4); 270 } 271 272 nLen=sizeof saClient; 273 nFD=accept(nSocket,(struct sockaddr *)&saClient,&nLen); 274 if(nFD < 0) 275 { 276 perror("accept"); 277 exit(5); 278 } 279 280 fprintf(stderr,"Incoming accepted on port %d\n",nPort); 281 282 return nFD; 283 } 284 285int main(int argc,char **argv) 286 { 287 SSLStateMachine *pMachine; 288 int nPort; 289 int nFD; 290 const char *szCertificateFile; 291 const char *szKeyFile; 292 293 if(argc != 4) 294 { 295 fprintf(stderr,"%s <port> <certificate file> <key file>\n",argv[0]); 296 exit(6); 297 } 298 299 nPort=atoi(argv[1]); 300 szCertificateFile=argv[2]; 301 szKeyFile=argv[3]; 302 303 SSL_library_init(); 304 OpenSSL_add_ssl_algorithms(); 305 SSL_load_error_strings(); 306 ERR_load_crypto_strings(); 307 308 nFD=OpenSocket(nPort); 309 310 pMachine=SSLStateMachine_new(szCertificateFile,szKeyFile); 311 312 for( ; ; ) 313 { 314 fd_set rfds,wfds; 315 unsigned char buf[1024]; 316 int n; 317 318 FD_ZERO(&rfds); 319 FD_ZERO(&wfds); 320 321 /* Select socket for input */ 322 FD_SET(nFD,&rfds); 323 324 /* Select socket for output */ 325 if(SSLStateMachine_write_can_extract(pMachine)) 326 FD_SET(nFD,&wfds); 327 328 /* Select stdin for input */ 329 FD_SET(0,&rfds); 330 331 /* Wait for something to do something */ 332 n=select(nFD+1,&rfds,&wfds,NULL,NULL); 333 assert(n > 0); 334 335 /* Socket is ready for input */ 336 if(FD_ISSET(nFD,&rfds)) 337 { 338 n=read(nFD,buf,sizeof buf); 339 if(n == 0) 340 { 341 fprintf(stderr,"Got EOF on socket\n"); 342 exit(0); 343 } 344 assert(n > 0); 345 346 SSLStateMachine_read_inject(pMachine,buf,n); 347 } 348 349 /* FIXME: we should only extract if stdout is ready */ 350 n=SSLStateMachine_read_extract(pMachine,buf,n); 351 if(n < 0) 352 { 353 SSLStateMachine_print_error(pMachine,"read extract failed"); 354 break; 355 } 356 assert(n >= 0); 357 if(n > 0) 358 { 359 int w; 360 361 w=write(1,buf,n); 362 /* FIXME: we should push back any unwritten data */ 363 assert(w == n); 364 } 365 366 /* Socket is ready for output (and therefore we have output to send) */ 367 if(FD_ISSET(nFD,&wfds)) 368 { 369 int w; 370 371 n=SSLStateMachine_write_extract(pMachine,buf,sizeof buf); 372 assert(n > 0); 373 374 w=write(nFD,buf,n); 375 /* FIXME: we should push back any unwritten data */ 376 assert(w == n); 377 } 378 379 /* Stdin is ready for input */ 380 if(FD_ISSET(0,&rfds)) 381 { 382 n=read(0,buf,sizeof buf); 383 if(n == 0) 384 { 385 fprintf(stderr,"Got EOF on stdin\n"); 386 exit(0); 387 } 388 assert(n > 0); 389 390 SSLStateMachine_write_inject(pMachine,buf,n); 391 } 392 } 393 /* not reached */ 394 return 0; 395 } 396