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/* 87 * die_unless is intended to work like assert, except that it happens always, 88 * even if NDEBUG is defined. Use assert as a stopgap. 89 */ 90 91#define die_unless(x) assert(x) 92 93typedef struct { 94 SSL_CTX *pCtx; 95 BIO *pbioRead; 96 BIO *pbioWrite; 97 SSL *pSSL; 98} SSLStateMachine; 99 100void SSLStateMachine_print_error(SSLStateMachine * pMachine, 101 const char *szErr) 102{ 103 unsigned long l; 104 105 fprintf(stderr, "%s\n", szErr); 106 while ((l = ERR_get_error())) { 107 char buf[1024]; 108 109 ERR_error_string_n(l, buf, sizeof buf); 110 fprintf(stderr, "Error %lx: %s\n", l, buf); 111 } 112} 113 114SSLStateMachine *SSLStateMachine_new(const char *szCertificateFile, 115 const char *szKeyFile) 116{ 117 SSLStateMachine *pMachine = malloc(sizeof *pMachine); 118 int n; 119 120 die_unless(pMachine); 121 122 pMachine->pCtx = SSL_CTX_new(SSLv23_server_method()); 123 die_unless(pMachine->pCtx); 124 125 n = SSL_CTX_use_certificate_file(pMachine->pCtx, szCertificateFile, 126 SSL_FILETYPE_PEM); 127 die_unless(n > 0); 128 129 n = SSL_CTX_use_PrivateKey_file(pMachine->pCtx, szKeyFile, 130 SSL_FILETYPE_PEM); 131 die_unless(n > 0); 132 133 pMachine->pSSL = SSL_new(pMachine->pCtx); 134 die_unless(pMachine->pSSL); 135 136 pMachine->pbioRead = BIO_new(BIO_s_mem()); 137 138 pMachine->pbioWrite = BIO_new(BIO_s_mem()); 139 140 SSL_set_bio(pMachine->pSSL, pMachine->pbioRead, pMachine->pbioWrite); 141 142 SSL_set_accept_state(pMachine->pSSL); 143 144 return pMachine; 145} 146 147void SSLStateMachine_read_inject(SSLStateMachine * pMachine, 148 const unsigned char *aucBuf, int nBuf) 149{ 150 int n = BIO_write(pMachine->pbioRead, aucBuf, nBuf); 151 /* 152 * If it turns out this assert fails, then buffer the data here and just 153 * feed it in in churn instead. Seems to me that it should be guaranteed 154 * to succeed, though. 155 */ 156 assert(n == nBuf); 157 fprintf(stderr, "%d bytes of encrypted data fed to state machine\n", n); 158} 159 160int SSLStateMachine_read_extract(SSLStateMachine * pMachine, 161 unsigned char *aucBuf, int nBuf) 162{ 163 int n; 164 165 if (!SSL_is_init_finished(pMachine->pSSL)) { 166 fprintf(stderr, "Doing SSL_accept\n"); 167 n = SSL_accept(pMachine->pSSL); 168 if (n == 0) 169 fprintf(stderr, "SSL_accept returned zero\n"); 170 if (n < 0) { 171 int err; 172 173 if ((err = 174 SSL_get_error(pMachine->pSSL, n)) == SSL_ERROR_WANT_READ) { 175 fprintf(stderr, "SSL_accept wants more data\n"); 176 return 0; 177 } 178 179 SSLStateMachine_print_error(pMachine, "SSL_accept error"); 180 exit(7); 181 } 182 return 0; 183 } 184 185 n = SSL_read(pMachine->pSSL, aucBuf, nBuf); 186 if (n < 0) { 187 int err = SSL_get_error(pMachine->pSSL, n); 188 189 if (err == SSL_ERROR_WANT_READ) { 190 fprintf(stderr, "SSL_read wants more data\n"); 191 return 0; 192 } 193 194 SSLStateMachine_print_error(pMachine, "SSL_read error"); 195 exit(8); 196 } 197 198 fprintf(stderr, "%d bytes of decrypted data read from state machine\n", 199 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", 221 n); 222 return n; 223} 224 225void SSLStateMachine_write_inject(SSLStateMachine * pMachine, 226 const unsigned char *aucBuf, int nBuf) 227{ 228 int n = SSL_write(pMachine->pSSL, aucBuf, nBuf); 229 /* 230 * If it turns out this assert fails, then buffer the data here and just 231 * feed it in in churn instead. Seems to me that it should be guaranteed 232 * to succeed, though. 233 */ 234 assert(n == nBuf); 235 fprintf(stderr, "%d bytes of unencrypted data fed to state machine\n", n); 236} 237 238int OpenSocket(int nPort) 239{ 240 int nSocket; 241 struct sockaddr_in saServer; 242 struct sockaddr_in saClient; 243 int one = 1; 244 int nSize; 245 int nFD; 246 int nLen; 247 248 nSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 249 if (nSocket < 0) { 250 perror("socket"); 251 exit(1); 252 } 253 254 if (setsockopt 255 (nSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof one) < 0) { 256 perror("setsockopt"); 257 exit(2); 258 } 259 260 memset(&saServer, 0, sizeof saServer); 261 saServer.sin_family = AF_INET; 262 saServer.sin_port = htons(nPort); 263 nSize = sizeof saServer; 264 if (bind(nSocket, (struct sockaddr *)&saServer, nSize) < 0) { 265 perror("bind"); 266 exit(3); 267 } 268 269 if (listen(nSocket, 512) < 0) { 270 perror("listen"); 271 exit(4); 272 } 273 274 nLen = sizeof saClient; 275 nFD = accept(nSocket, (struct sockaddr *)&saClient, &nLen); 276 if (nFD < 0) { 277 perror("accept"); 278 exit(5); 279 } 280 281 fprintf(stderr, "Incoming accepted on port %d\n", nPort); 282 283 return nFD; 284} 285 286int main(int argc, char **argv) 287{ 288 SSLStateMachine *pMachine; 289 int nPort; 290 int nFD; 291 const char *szCertificateFile; 292 const char *szKeyFile; 293 char rbuf[1]; 294 int nrbuf = 0; 295 296 if (argc != 4) { 297 fprintf(stderr, "%s <port> <certificate file> <key file>\n", argv[0]); 298 exit(6); 299 } 300 301 nPort = atoi(argv[1]); 302 szCertificateFile = argv[2]; 303 szKeyFile = argv[3]; 304 305 SSL_library_init(); 306 OpenSSL_add_ssl_algorithms(); 307 SSL_load_error_strings(); 308 ERR_load_crypto_strings(); 309 310 nFD = OpenSocket(nPort); 311 312 pMachine = SSLStateMachine_new(szCertificateFile, szKeyFile); 313 314 for (;;) { 315 fd_set rfds, wfds; 316 unsigned char buf[1024]; 317 int n; 318 319 FD_ZERO(&rfds); 320 FD_ZERO(&wfds); 321 322 /* Select socket for input */ 323 FD_SET(nFD, &rfds); 324 325 /* check whether there's decrypted data */ 326 if (!nrbuf) 327 nrbuf = SSLStateMachine_read_extract(pMachine, rbuf, 1); 328 329 /* if there's decrypted data, check whether we can write it */ 330 if (nrbuf) 331 FD_SET(1, &wfds); 332 333 /* Select socket for output */ 334 if (SSLStateMachine_write_can_extract(pMachine)) 335 FD_SET(nFD, &wfds); 336 337 /* Select stdin for input */ 338 FD_SET(0, &rfds); 339 340 /* Wait for something to do something */ 341 n = select(nFD + 1, &rfds, &wfds, NULL, NULL); 342 assert(n > 0); 343 344 /* Socket is ready for input */ 345 if (FD_ISSET(nFD, &rfds)) { 346 n = read(nFD, buf, sizeof buf); 347 if (n == 0) { 348 fprintf(stderr, "Got EOF on socket\n"); 349 exit(0); 350 } 351 assert(n > 0); 352 353 SSLStateMachine_read_inject(pMachine, buf, n); 354 } 355 356 /* stdout is ready for output (and hence we have some to send it) */ 357 if (FD_ISSET(1, &wfds)) { 358 assert(nrbuf == 1); 359 buf[0] = rbuf[0]; 360 nrbuf = 0; 361 362 n = SSLStateMachine_read_extract(pMachine, buf + 1, 363 sizeof buf - 1); 364 if (n < 0) { 365 SSLStateMachine_print_error(pMachine, "read extract failed"); 366 break; 367 } 368 assert(n >= 0); 369 ++n; 370 if (n > 0) { /* FIXME: has to be true now */ 371 int w; 372 373 w = write(1, buf, n); 374 /* FIXME: we should push back any unwritten data */ 375 assert(w == n); 376 } 377 } 378 379 /* 380 * Socket is ready for output (and therefore we have output to send) 381 */ 382 if (FD_ISSET(nFD, &wfds)) { 383 int w; 384 385 n = SSLStateMachine_write_extract(pMachine, buf, sizeof buf); 386 assert(n > 0); 387 388 w = write(nFD, buf, n); 389 /* FIXME: we should push back any unwritten data */ 390 assert(w == n); 391 } 392 393 /* Stdin is ready for input */ 394 if (FD_ISSET(0, &rfds)) { 395 n = read(0, buf, sizeof buf); 396 if (n == 0) { 397 fprintf(stderr, "Got EOF on stdin\n"); 398 exit(0); 399 } 400 assert(n > 0); 401 402 SSLStateMachine_write_inject(pMachine, buf, n); 403 } 404 } 405 /* not reached */ 406 return 0; 407} 408