Saner handling if config.mk doesn't exist: use a default config.defaults.mk.
[wvstreams.git] / crypto / wvsslstream.cc
blobceb60c8fef9bd0076018e77ab72d86d01ae318f1
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 */
5 #define OPENSSL_NO_KRB5
6 #include "wvsslstream.h"
7 #include "wvx509mgr.h"
8 #include "wvcrypto.h"
9 #include "wvlistener.h"
10 #include "wvstrutils.h"
11 #include "wvmoniker.h"
12 #include "wvlinkerhack.h"
13 #include <openssl/ssl.h>
14 #include <openssl/err.h>
15 #include <assert.h>
17 #ifndef _WIN32
18 # if HAVE_ARGZ_H
19 # include <argz.h>
20 # else
21 # if HAVE_ERRNO_H
22 # include <errno.h>
23 # endif
24 # endif
25 #else
26 #undef errno
27 #define errno GetLastError()
28 #undef EAGAIN
29 #define EAGAIN WSAEWOULDBLOCK
30 #define error_t long
31 #endif
33 WV_LINK(WvSSLStream);
35 static IWvStream *creator(WvStringParm s, IObject *_obj)
37 return new WvSSLStream(IWvStream::create(s, _obj), NULL, 0, false);
40 static IWvStream *screator(WvStringParm s, IObject *_obj)
42 return new WvSSLStream(IWvStream::create(s, _obj),
43 new WvX509Mgr(encode_hostname_as_DN(fqdomainname()), 1024),
44 0, true);
47 static WvMoniker<IWvStream> reg("ssl", creator);
48 static WvMoniker<IWvStream> sreg("sslserv", screator);
50 static IWvListener *listener(WvStringParm s, IObject *obj)
52 IWvListener *l = IWvListener::create(s, obj);
53 if (l)
54 l->addwrap(wv::bind(&IWvStream::create, "sslserv", _1));
55 return l;
58 static WvMoniker<IWvListener> lreg("ssl", listener);
61 #define MAX_BOUNCE_AMOUNT (16384) // 1 SSLv3/TLSv1 record
63 static int ssl_stream_count = 0;
65 static int wv_verify_cb(int preverify_ok, X509_STORE_CTX *ctx)
67 // This is just returns true, since what we really want
68 // is for the WvSSLValidateCallback to do this work
69 return 1;
72 WvSSLStream::WvSSLStream(IWvStream *_slave, WvX509Mgr *_x509,
73 WvSSLValidateCallback _vcb, bool _is_server) :
74 WvStreamClone(_slave),
75 debug(WvString("WvSSLStream %s", ++ssl_stream_count), WvLog::Debug5),
76 write_bouncebuf(MAX_BOUNCE_AMOUNT), write_eat(0),
77 read_bouncebuf(MAX_BOUNCE_AMOUNT), read_pending(false)
79 x509 = _x509;
80 if (x509)
81 x509->addRef(); // openssl may keep a pointer to this object
83 vcb = _vcb;
84 is_server = _is_server;
85 ctx = NULL;
86 ssl = NULL;
87 //meth = NULL;
88 sslconnected = ssl_stop_read = ssl_stop_write = false;
90 wvssl_init();
92 if (x509 && !x509->isok())
94 seterr("Certificate + key pair invalid.");
95 return;
98 if (is_server && !x509)
100 seterr("Certificate not available: server mode not possible!");
101 return;
104 if (is_server)
106 debug("Configured algorithms and methods for server mode.\n");
108 ctx = SSL_CTX_new(SSLv23_server_method());
109 if (!ctx)
111 ERR_print_errors_fp(stderr);
112 debug("Can't get SSL context! Error: %s\n",
113 ERR_reason_error_string(ERR_get_error()));
114 seterr("Can't get SSL context!");
115 return;
118 // Allow SSL Writes to only write part of a request...
119 SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
121 // Tell SSL to use 128 bit or better ciphers - this appears to
122 // be necessary for some reason... *sigh*
123 SSL_CTX_set_cipher_list(ctx, "HIGH");
125 // Enable the workarounds for broken clients and servers
126 // and disable the insecure SSLv2 protocol
127 SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
129 if (!x509->bind_ssl(ctx))
131 seterr("Unable to bind Certificate to SSL Context!");
132 return;
135 if (!!vcb)
136 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
137 wv_verify_cb);
139 debug("Server mode ready.\n");
141 else
143 debug("Configured algorithms and methods for client mode.\n");
145 ctx = SSL_CTX_new(SSLv23_client_method());
146 if (!ctx)
148 seterr("Can't get SSL context!");
149 return;
151 if (x509 && !x509->bind_ssl(ctx))
153 seterr("Unable to bind Certificate to SSL Context!");
154 return;
158 //SSL_CTX_set_read_ahead(ctx, 1);
160 ERR_clear_error();
161 ssl = SSL_new(ctx);
162 if (!ssl)
164 seterr("Can't create SSL object!");
165 return;
168 if (!!vcb)
169 SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
170 wv_verify_cb);
172 connect_wants.readable = true;
173 connect_wants.writable = true; // force ssl initiation ASAP
174 connect_wants.isexception = false;
175 debug("SSL stream initialized.\n");
179 WvSSLStream::~WvSSLStream()
181 close();
183 debug("Deleting SSL connection.\n");
184 if (geterr())
185 debug("Error was: %s\n", errstr());
187 WVRELEASE(x509);
188 wvssl_free();
192 void WvSSLStream::printerr(WvStringParm func)
194 unsigned long l = ERR_get_error();
195 char buf[121]; // man ERR_error_string says must be > 120.
197 SSL_load_error_strings();
198 while (l)
200 ERR_error_string(l, buf);
201 debug("%s error: %s\n", func, buf);
202 l = ERR_get_error();
204 ERR_free_strings();
208 size_t WvSSLStream::uread(void *buf, size_t len)
210 if (!sslconnected)
211 return 0;
212 if (len == 0) return 0;
214 // if SSL buffers stuff on its own, select() may not wake us up
215 // the next time around unless we're sure there is nothing left
216 read_pending = true;
218 size_t total = 0;
219 for (;;)
221 // handle SSL_read quirk
222 if (read_bouncebuf.used() != 0)
224 // copy out cached data
225 size_t amount = len < read_bouncebuf.used() ?
226 len : read_bouncebuf.used();
227 read_bouncebuf.move(buf, amount);
229 // locate next chunk in buffer
230 len -= amount;
231 total += amount;
232 if (len == 0)
234 read_pending = false;
235 break;
237 buf = (unsigned char *)buf + amount;
239 // FIXME: this shouldn't be necessary, but it resolves weird
240 // problems when the other end disconnects in the middle of
241 // SSL negotiation, but only on emakela's machine. I don't
242 // know why. -- apenwarr (2004/02/10)
243 break;
246 // attempt to read
247 read_bouncebuf.zap(); // force use of same position in buffer
248 size_t avail = read_bouncebuf.free();
249 unsigned char *data = read_bouncebuf.alloc(avail);
251 ERR_clear_error();
252 int result = SSL_read(ssl, data, avail);
253 // debug("<< SSL_read result %s for %s bytes (wanted %s)\n",
254 // result, avail, len);
255 if (result <= 0)
257 error_t err = errno;
258 read_bouncebuf.unalloc(avail);
259 int sslerrcode = SSL_get_error(ssl, result);
260 switch (sslerrcode)
262 case SSL_ERROR_WANT_READ:
263 debug("<< SSL_read() needs to wait for writable.\n");
264 break; // wait for later
265 case SSL_ERROR_WANT_WRITE:
266 debug("<< SSL_read() needs to wait for readable.\n");
267 break; // wait for later
269 case SSL_ERROR_NONE:
270 break; // no error, but can't make progress
272 case SSL_ERROR_ZERO_RETURN:
273 debug("<< EOF: zero return\n");
275 // don't do this if we're returning nonzero!
276 // (SSL has no way to do a one-way shutdown, so if SSL
277 // detects a read problem, it's also a write problem.)
278 if (!total) { noread(); nowrite(); }
279 break;
281 case SSL_ERROR_SYSCALL:
282 if (!err)
284 if (result == 0)
286 debug("<< EOF: syscall error "
287 "(%s/%s, %s/%s) total=%s\n",
288 stop_read, stop_write,
289 isok(), cloned && cloned->isok(), total);
291 // don't do this if we're returning nonzero!
292 // (SSL has no way to do a one-way shutdown, so
293 // if SSL detects a read problem, it's also a
294 // write problem.)
295 if (!total) { noread(); nowrite(); }
298 else
300 debug("<< SSL_read() err=%s (%s)\n",
301 err, strerror(err));
302 seterr_both(err, WvString("SSL read: %s",
303 strerror(err)));
305 break;
307 default:
308 printerr("SSL_read");
309 seterr("SSL read error #%s", sslerrcode);
310 break;
312 read_pending = false;
313 break; // wait for next iteration
315 // debug("<< read result was %s\n", result);
317 if (result < 0)
318 result = 0;
319 read_bouncebuf.unalloc(avail - result);
322 // debug("<< read %s bytes (%s, %s)\n",
323 // total, isok(), cloned && cloned->isok());
324 return total;
328 size_t WvSSLStream::uwrite(const void *buf, size_t len)
330 if (!sslconnected)
332 debug(">> writing, but not connected yet (%s); enqueue.\n", getwfd());
333 unconnected_buf.put(buf, len);
334 return len;
337 if (len == 0) return 0;
339 // debug(">> I want to write %s bytes.\n", len);
341 size_t total = 0;
343 // eat any data that was precached and already written
344 if (write_eat >= len)
346 write_eat -= len;
347 total = len;
348 len = 0;
350 else
352 buf = (const unsigned char *)buf + write_eat;
353 total = write_eat;
354 len -= write_eat;
355 write_eat = 0;
358 // FIXME: WOW!!! Ummm... hope this never spins...
360 for (;;)
362 // handle SSL_write quirk
363 if (write_bouncebuf.used() == 0)
365 if (len == 0) break;
367 // copy new data into the bounce buffer only if empty
368 // if it were not empty, then SSL_write probably returned
369 // SSL_ERROR_WANT_WRITE on the previous call and we
370 // must invoke it with precisely the same arguments
371 size_t amount = len < write_bouncebuf.free() ?
372 len : write_bouncebuf.free();
373 write_bouncebuf.put(buf, amount);
374 // note: we don't adjust the total yet...
375 } // otherwise we use what we cached last time in bounce buffer
377 // attempt to write
378 size_t used = write_bouncebuf.used();
379 const unsigned char *data = write_bouncebuf.get(used);
381 ERR_clear_error();
382 int result = SSL_write(ssl, data, used);
383 // debug("<< SSL_write result %s for %s bytes\n",
384 // result, used);
385 if (result <= 0)
387 int sslerrcode = SSL_get_error(ssl, result);
388 write_bouncebuf.unget(used);
389 switch (sslerrcode)
391 case SSL_ERROR_WANT_READ:
392 debug(">> SSL_write() needs to wait for readable.\n");
393 break; // wait for later
394 case SSL_ERROR_WANT_WRITE:
395 // debug(">> SSL_write() needs to wait for writable.\n");
396 break; // wait for later
398 case SSL_ERROR_SYSCALL:
399 debug(">> ERROR: SSL_write() failed on socket error.\n");
400 seterr(WvString("SSL write error: %s", strerror(errno)));
401 break;
403 // This case can cause truncated web pages... give more info
404 case SSL_ERROR_SSL:
405 debug(">> ERROR: SSL_write() failed on internal error.\n");
406 seterr(WvString("SSL write error: %s",
407 ERR_error_string(ERR_get_error(), NULL)));
408 break;
410 case SSL_ERROR_NONE:
411 break; // no error, but can't make progress
413 case SSL_ERROR_ZERO_RETURN:
414 debug(">> SSL_write zero return: EOF\n");
415 close(); // EOF
416 break;
418 default:
419 printerr("SSL_write");
420 seterr(WvString("SSL write error #%s", sslerrcode));
421 break;
423 break; // wait for next iteration
425 else
426 assert((size_t)result == used);
427 write_bouncebuf.zap(); // force use of same position in buffer
429 // locate next chunk to be written
430 // note: we assume that initial contents of buf and of the
431 // bouncebuf match since if we got SSL_ERROR_WANT_WRITE
432 // we did not claim to actually have written the chunk
433 // that we cached so we will have gotten it again here
434 if (size_t(result) >= len)
436 // if we cached more previously than we were given, claim
437 // we wrote what we got and remember to eat the rest later
438 write_eat = result - len;
439 total += len;
440 break;
442 total += size_t(result);
443 len -= size_t(result);
444 buf = (const unsigned char *)buf + size_t(result);
447 //debug(">> wrote %s bytes\n", total);
448 return total;
451 void WvSSLStream::close()
453 debug("Closing SSL connection (ok=%s,sr=%s,sw=%s,child=%s).\n",
454 isok(), stop_read, stop_write, cloned && cloned->isok());
456 if (ssl)
458 ERR_clear_error();
459 SSL_shutdown(ssl);
460 SSL_free(ssl);
461 ssl = NULL;
462 sslconnected = false;
465 WvStreamClone::close();
467 if (ctx)
469 SSL_CTX_free(ctx);
470 ctx = NULL;
475 bool WvSSLStream::isok() const
477 return ssl && WvStreamClone::isok();
481 void WvSSLStream::noread()
483 // WARNING: openssl always needs two-way socket communications even for
484 // one-way encrypted communications, so we don't pass noread/nowrite
485 // along to the child stream. This should be mostly okay, though,
486 // because we'll still send it close() once we have both noread() and
487 // nowrite().
488 ssl_stop_read = true;
489 if (ssl_stop_write)
491 WvStreamClone::nowrite();
492 WvStreamClone::noread();
497 void WvSSLStream::nowrite()
499 // WARNING: see note in noread()
500 ssl_stop_write = true;
501 if (ssl_stop_read)
503 WvStreamClone::noread();
504 WvStreamClone::nowrite();
509 void WvSSLStream::pre_select(SelectInfo &si)
511 SelectRequest oldwant = si.wants;
512 bool oldinherit = si.inherit_request;
513 if (!sslconnected)
515 si.wants = connect_wants;
516 si.inherit_request = true; // ignore force_select() until connected
519 // the SSL library might be keeping its own internal buffers
520 // or we might have left buffered data behind deliberately
521 if (si.wants.readable && (read_pending || read_bouncebuf.used()))
523 // debug("pre_select: try reading again immediately.\n");
524 si.msec_timeout = 0;
525 si.inherit_request = oldinherit;
526 si.wants = oldwant;
527 return;
530 WvStreamClone::pre_select(si);
531 si.inherit_request = oldinherit;
532 si.wants = oldwant;
536 bool WvSSLStream::post_select(SelectInfo &si)
538 SelectRequest oldwant = si.wants;
539 bool oldinherit = si.inherit_request;
541 if (!sslconnected)
543 si.wants = connect_wants;
544 si.inherit_request = true; // ignore force_select() until connected
547 bool result = WvStreamClone::post_select(si);
548 si.wants = oldwant;
549 si.inherit_request = oldinherit;
551 // SSL takes a few round trips to
552 // initialize itself, and we mustn't block in the constructor, so keep
553 // trying here... it is also turning into a rather cool place
554 // to do the validation of the connection ;)
555 if (!sslconnected && cloned && cloned->isok() && result)
557 debug("!sslconnected in post_select (r=%s/%s, w=%s/%s, t=%s)\n",
558 cloned->isreadable(), si.wants.readable,
559 cloned->iswritable(), si.wants.writable,
560 si.msec_timeout);
562 connect_wants.writable = false;
564 // for ssl streams to work, we have to be cloning a stream that
565 // actually uses a single, valid fd.
566 WvFDStream *fdstream = static_cast<WvFDStream*>(cloned);
567 int fd = fdstream->getfd();
568 assert(fd >= 0);
569 ERR_clear_error();
570 SSL_set_fd(ssl, fd);
571 // debug("SSL connected on fd %s.\n", fd);
573 int err;
575 if (is_server)
577 // If we are a server, get ready to accept an incoming SSL
578 // connection
579 err = SSL_accept(ssl);
581 else
582 err = SSL_connect(ssl);
584 if (err < 0)
586 if (errno == EAGAIN)
587 debug("Still waiting for SSL negotiation.\n");
588 else if (!errno)
590 printerr(is_server ? "SSL_accept" : "SSL_connect");
591 seterr(WvString("SSL negotiation failed (%s)!", err));
593 else
595 printerr(is_server ? "SSL_accept" : "SSL_connect");
596 seterr(errno);
599 else // We're connected, so let's do some checks ;)
601 debug("SSL connection using cipher %s.\n", SSL_get_cipher(ssl));
602 if (!!vcb)
604 WvX509 *peercert = new WvX509(SSL_get_peer_certificate(ssl));
605 debug("SSL Peer is: %s\n", peercert->get_subject());
606 if (peercert->isok() && peercert->validate() && vcb(peercert))
608 setconnected(true);
609 debug("SSL finished negotiating - certificate is valid.\n");
611 else
613 if (!peercert->isok())
614 seterr("Peer cert: %s", peercert->errstr());
615 else
616 seterr("Peer certificate is invalid!");
618 WVRELEASE(peercert);
620 else
622 setconnected(true);
623 debug("SSL finished negotiating "
624 "- certificate validation disabled.\n");
628 return false;
631 if ((si.wants.readable || readcb)
632 && (read_pending || read_bouncebuf.used()))
633 result = true;
635 return result;
639 void WvSSLStream::setconnected(bool conn)
641 sslconnected = conn;
642 if (conn) write(unconnected_buf);