state_machine.c revision 296465
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