1/* ==================================================================== 2 * Copyright (c) 2000 The OpenSSL Project. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * 3. All advertising materials mentioning features or use of this 17 * software must display the following acknowledgment: 18 * "This product includes software developed by the OpenSSL Project 19 * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" 20 * 21 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For written permission, please contact 24 * openssl-core@openssl.org. 25 * 26 * 5. Products derived from this software may not be called "OpenSSL" 27 * nor may "OpenSSL" appear in their names without prior written 28 * permission of the OpenSSL Project. 29 * 30 * 6. Redistributions of any form whatsoever must retain the following 31 * acknowledgment: 32 * "This product includes software developed by the OpenSSL Project 33 * for use in the OpenSSL Toolkit (http://www.openssl.org/)" 34 * 35 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 36 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 38 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 41 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 42 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 44 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 45 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 46 * OF THE POSSIBILITY OF SUCH DAMAGE. 47 * ==================================================================== 48 * 49 * 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 SSLStateMachine_print_error(pMachine,"SSL_read error"); 196 exit(8); 197 } 198 199 fprintf(stderr,"%d bytes of decrypted data read from state machine\n",n); 200 return n; 201 } 202 203int SSLStateMachine_write_can_extract(SSLStateMachine *pMachine) 204 { 205 int n=BIO_pending(pMachine->pbioWrite); 206 if(n) 207 fprintf(stderr,"There is encrypted data available to write\n"); 208 else 209 fprintf(stderr,"There is no encrypted data available to write\n"); 210 211 return n; 212 } 213 214int SSLStateMachine_write_extract(SSLStateMachine *pMachine, 215 unsigned char *aucBuf,int nBuf) 216 { 217 int n; 218 219 n=BIO_read(pMachine->pbioWrite,aucBuf,nBuf); 220 fprintf(stderr,"%d bytes of encrypted data read from state machine\n",n); 221 return n; 222 } 223 224void SSLStateMachine_write_inject(SSLStateMachine *pMachine, 225 const unsigned char *aucBuf,int nBuf) 226 { 227 int n=SSL_write(pMachine->pSSL,aucBuf,nBuf); 228 /* If it turns out this assert fails, then buffer the data here 229 * and just feed it in in churn instead. Seems to me that it 230 * should be guaranteed to succeed, though. 231 */ 232 assert(n == nBuf); 233 fprintf(stderr,"%d bytes of unencrypted data fed to state machine\n",n); 234 } 235 236int OpenSocket(int nPort) 237 { 238 int nSocket; 239 struct sockaddr_in saServer; 240 struct sockaddr_in saClient; 241 int one=1; 242 int nSize; 243 int nFD; 244 int nLen; 245 246 nSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 247 if(nSocket < 0) 248 { 249 perror("socket"); 250 exit(1); 251 } 252 253 if(setsockopt(nSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof one) < 0) 254 { 255 perror("setsockopt"); 256 exit(2); 257 } 258 259 memset(&saServer,0,sizeof saServer); 260 saServer.sin_family=AF_INET; 261 saServer.sin_port=htons(nPort); 262 nSize=sizeof saServer; 263 if(bind(nSocket,(struct sockaddr *)&saServer,nSize) < 0) 264 { 265 perror("bind"); 266 exit(3); 267 } 268 269 if(listen(nSocket,512) < 0) 270 { 271 perror("listen"); 272 exit(4); 273 } 274 275 nLen=sizeof saClient; 276 nFD=accept(nSocket,(struct sockaddr *)&saClient,&nLen); 277 if(nFD < 0) 278 { 279 perror("accept"); 280 exit(5); 281 } 282 283 fprintf(stderr,"Incoming accepted on port %d\n",nPort); 284 285 return nFD; 286 } 287 288int main(int argc,char **argv) 289 { 290 SSLStateMachine *pMachine; 291 int nPort; 292 int nFD; 293 const char *szCertificateFile; 294 const char *szKeyFile; 295 char rbuf[1]; 296 int nrbuf=0; 297 298 if(argc != 4) 299 { 300 fprintf(stderr,"%s <port> <certificate file> <key file>\n",argv[0]); 301 exit(6); 302 } 303 304 nPort=atoi(argv[1]); 305 szCertificateFile=argv[2]; 306 szKeyFile=argv[3]; 307 308 SSL_library_init(); 309 OpenSSL_add_ssl_algorithms(); 310 SSL_load_error_strings(); 311 ERR_load_crypto_strings(); 312 313 nFD=OpenSocket(nPort); 314 315 pMachine=SSLStateMachine_new(szCertificateFile,szKeyFile); 316 317 for( ; ; ) 318 { 319 fd_set rfds,wfds; 320 unsigned char buf[1024]; 321 int n; 322 323 FD_ZERO(&rfds); 324 FD_ZERO(&wfds); 325 326 /* Select socket for input */ 327 FD_SET(nFD,&rfds); 328 329 /* check whether there's decrypted data */ 330 if(!nrbuf) 331 nrbuf=SSLStateMachine_read_extract(pMachine,rbuf,1); 332 333 /* if there's decrypted data, check whether we can write it */ 334 if(nrbuf) 335 FD_SET(1,&wfds); 336 337 /* Select socket for output */ 338 if(SSLStateMachine_write_can_extract(pMachine)) 339 FD_SET(nFD,&wfds); 340 341 /* Select stdin for input */ 342 FD_SET(0,&rfds); 343 344 /* Wait for something to do something */ 345 n=select(nFD+1,&rfds,&wfds,NULL,NULL); 346 assert(n > 0); 347 348 /* Socket is ready for input */ 349 if(FD_ISSET(nFD,&rfds)) 350 { 351 n=read(nFD,buf,sizeof buf); 352 if(n == 0) 353 { 354 fprintf(stderr,"Got EOF on socket\n"); 355 exit(0); 356 } 357 assert(n > 0); 358 359 SSLStateMachine_read_inject(pMachine,buf,n); 360 } 361 362 /* stdout is ready for output (and hence we have some to send it) */ 363 if(FD_ISSET(1,&wfds)) 364 { 365 assert(nrbuf == 1); 366 buf[0]=rbuf[0]; 367 nrbuf=0; 368 369 n=SSLStateMachine_read_extract(pMachine,buf+1,sizeof buf-1); 370 if(n < 0) 371 { 372 SSLStateMachine_print_error(pMachine,"read extract failed"); 373 break; 374 } 375 assert(n >= 0); 376 ++n; 377 if(n > 0) /* FIXME: has to be true now */ 378 { 379 int w; 380 381 w=write(1,buf,n); 382 /* FIXME: we should push back any unwritten data */ 383 assert(w == n); 384 } 385 } 386 387 /* Socket is ready for output (and therefore we have output to send) */ 388 if(FD_ISSET(nFD,&wfds)) 389 { 390 int w; 391 392 n=SSLStateMachine_write_extract(pMachine,buf,sizeof buf); 393 assert(n > 0); 394 395 w=write(nFD,buf,n); 396 /* FIXME: we should push back any unwritten data */ 397 assert(w == n); 398 } 399 400 /* Stdin is ready for input */ 401 if(FD_ISSET(0,&rfds)) 402 { 403 n=read(0,buf,sizeof buf); 404 if(n == 0) 405 { 406 fprintf(stderr,"Got EOF on stdin\n"); 407 exit(0); 408 } 409 assert(n > 0); 410 411 SSLStateMachine_write_inject(pMachine,buf,n); 412 } 413 } 414 /* not reached */ 415 return 0; 416 } 417