OpenSSL  1.0.1c
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros
state_machine.c
Go to the documentation of this file.
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
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  * ([email protected]). This product includes software written by Tim
51  * Hudson ([email protected]).
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 
91 typedef struct
92  {
98 
99 void 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 
113 SSLStateMachine *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 
122  die_unless(pMachine->pCtx);
123 
124  n=SSL_CTX_use_certificate_file(pMachine->pCtx,szCertificateFile,
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 
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 
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 
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 
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 
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 
236 int 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 
288 int 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 
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 */
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  }