WvStreams
wvsslstream.cc
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>
16 
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 #endif
31 
32 WV_LINK(WvSSLStream);
33 
34 static IWvStream *creator(WvStringParm s, IObject *_obj)
35 {
36  return new WvSSLStream(IWvStream::create(s, _obj), NULL, 0, false);
37 }
38 
39 static IWvStream *screator(WvStringParm s, IObject *_obj)
40 {
41  return new WvSSLStream(IWvStream::create(s, _obj),
43  0, true);
44 }
45 
47 {
48  WvX509Mgr *m;
49  WvString s;
50 
51  /* Kind of necessary; the WvX509Mgr object here is meant to be passed into
52  * a WvSSLStream, which will addRef() the object. Thus, once the stream
53  * has been created, we need to release() it here, so that once the stream
54  * itself falls into oblivion, we have no hanging references.
55  */
57  { WVRELEASE(m); }
58 };
59 
60 static WvTclParseValues *parse_wvtcl_sslcert(WvStringParm s)
61 {
62  /* The idea here is that we've got s, which is a TclStyle string of the
63  * format (without the quotes, of course, but escaped):
64  * "PEM-encoded SSL cert" "PEM-encoded private RSA key" "connection moniker"
65  */
67  wvtcl_decode(l, s);
68  if (l.count() > 3 || l.count() < 2)
69  return NULL; /* we fscked up, no clue how to recover */
70  // in the case of '2', 'obj' had better be set to the calling function
71 
73  p->m = new WvX509Mgr;
74  p->m->decode(WvX509::CertPEM, *l.first());
75  l.unlink_first();
76  p->m->decode(WvRSAKey::RsaPEM, *l.first());
77  l.unlink_first();
78  if (!p->m->test()) { /* RSA key and certificate don't match up?? */
79  delete p;
80  return NULL;
81  }
82 
83  if (l.count())
84  p->s = *l.first();
85 
86  return p;
87 }
88 
89 static IWvStream *sslcertcreator(WvStringParm s, IObject *_obj)
90 {
91  WvTclParseValues *p = parse_wvtcl_sslcert(s);
92  if (!p) {
93  WVRELEASE(_obj);
94  return NULL;
95  }
96 
97  WvSSLStream *ret = new WvSSLStream(IWvStream::create(p->s, _obj), p->m,
98  0, false);
99  delete p;
100  return ret;
101 }
102 
103 static IWvStream *sslcertscreator(WvStringParm s, IObject *_obj)
104 {
105  WvTclParseValues *p = parse_wvtcl_sslcert(s);
106  if (!p) {
107  WVRELEASE(_obj);
108  return NULL;
109  }
110 
111  WvSSLStream *ret = new WvSSLStream(IWvStream::create(p->s, _obj), p->m,
112  0, true);
113  delete p;
114  return ret;
115 }
116 
117 static WvMoniker<IWvStream> reg("ssl", creator);
118 static WvMoniker<IWvStream> sreg("sslserv", screator);
119 static WvMoniker<IWvStream> sslcertreg("sslcert", sslcertcreator);
120 static WvMoniker<IWvStream> sslcertsreg("sslcertserv", sslcertscreator);
121 
122 static IWvListener *listener(WvStringParm s, IObject *obj)
123 {
124  IWvListener *l = IWvListener::create(s, obj);
125  if (l)
126  l->addwrap(wv::bind(&IWvStream::create, "sslserv", _1));
127  return l;
128 }
129 
130 static IWvListener *sslcertlistener(WvStringParm s, IObject *obj)
131 {
132  WvList<WvString> li;
133  wvtcl_decode(li, s);
134  WvString connmoniker;
135 
136  if (li.count() == 3) {
137  // We have a connection moniker as well as SSL information
138  connmoniker = *li.last();
139  li.unlink(li.last());
140  } else if (li.count() != 2) {
141  // something went very wrong
142  WVRELEASE(obj);
143  return NULL;
144  }
145 
146  IWvListener *l = IWvListener::create(connmoniker, obj);
147  if (l)
148  l->addwrap(wv::bind(&IWvStream::create,
149  WvString("sslcertserv:%s", wvtcl_encode(li)), _1));
150  return l;
151 }
152 
153 static WvMoniker<IWvListener> lreg("ssl", listener);
154 static WvMoniker<IWvListener> lsslcertreg("sslcert", sslcertlistener);
155 
156 #define MAX_BOUNCE_AMOUNT (16384) // 1 SSLv3/TLSv1 record
157 
158 static int ssl_stream_count = 0;
159 
160 static int wv_verify_cb(int preverify_ok, X509_STORE_CTX *ctx)
161 {
162  // This is just returns true, since what we really want
163  // is for the WvSSLValidateCallback to do this work
164  return 1;
165 }
166 
167 WvSSLGlobalValidateCallback WvSSLStream::global_vcb = 0;
168 
170  WvSSLValidateCallback _vcb, bool _is_server) :
171  WvStreamClone(_slave),
172  debug(WvString("WvSSLStream %s", ++ssl_stream_count), WvLog::Debug5),
173  write_bouncebuf(MAX_BOUNCE_AMOUNT), write_eat(0),
174  read_bouncebuf(MAX_BOUNCE_AMOUNT), read_pending(false)
175 {
176  x509 = _x509;
177  if (x509)
178  x509->addRef(); // openssl may keep a pointer to this object
179 
180  vcb = _vcb;
181  if (!vcb && global_vcb)
182  vcb = wv::bind(global_vcb, _1, this);;
183 
184  is_server = _is_server;
185  ctx = NULL;
186  ssl = NULL;
187  //meth = NULL;
188  sslconnected = ssl_stop_read = ssl_stop_write = false;
189 
190  wvssl_init();
191 
192  if (x509 && !x509->isok())
193  {
194  seterr("Certificate + key pair invalid.");
195  return;
196  }
197 
198  if (is_server && !x509)
199  {
200  seterr("Certificate not available: server mode not possible!");
201  return;
202  }
203 
204  if (is_server)
205  {
206  debug("Configured algorithms and methods for server mode.\n");
207 
208  ctx = SSL_CTX_new(SSLv23_server_method());
209  if (!ctx)
210  {
211  ERR_print_errors_fp(stderr);
212  debug("Can't get SSL context! Error: %s\n",
213  ERR_reason_error_string(ERR_get_error()));
214  seterr("Can't get SSL context!");
215  return;
216  }
217 
218  // Allow SSL Writes to only write part of a request...
219  SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
220 
221  // Tell SSL to use 128 bit or better ciphers - this appears to
222  // be necessary for some reason... *sigh*
223  SSL_CTX_set_cipher_list(ctx, "HIGH");
224 
225  // Enable the workarounds for broken clients and servers
226  // and disable the insecure SSLv2 protocol
227  SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
228 
229  if (!x509->bind_ssl(ctx))
230  {
231  seterr("Unable to bind Certificate to SSL Context!");
232  return;
233  }
234 
235  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
236  wv_verify_cb);
237 
238  debug("Server mode ready.\n");
239  }
240  else
241  {
242  debug("Configured algorithms and methods for client mode.\n");
243 
244  ctx = SSL_CTX_new(SSLv23_client_method());
245  if (!ctx)
246  {
247  seterr("Can't get SSL context!");
248  return;
249  }
250  if (x509 && !x509->bind_ssl(ctx))
251  {
252  seterr("Unable to bind Certificate to SSL Context!");
253  return;
254  }
255  }
256 
257  //SSL_CTX_set_read_ahead(ctx, 1);
258 
259  ERR_clear_error();
260  ssl = SSL_new(ctx);
261  if (!ssl)
262  {
263  seterr("Can't create SSL object!");
264  return;
265  }
266 
267  // If we set this, it seems we always verify the client... security hole,
268  // no? Well, if we don't set it, the server doesn't even ask the client
269  // for a certificate, so, ya know, it's not actually any more secure.
270  // Client doesn't need this (unless vcb is set), since it always asks the
271  // server for a cert anyway
272  if (!!vcb || is_server)
273  SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
274  wv_verify_cb);
275 
276  connect_wants.readable = true;
277  connect_wants.writable = true; // force ssl initiation ASAP
278  connect_wants.isexception = false;
279  debug("SSL stream initialized.\n");
280 }
281 
282 
284 {
285  close();
286 
287  debug("Deleting SSL connection.\n");
288  if (geterr())
289  debug("Error was: %s\n", errstr());
290 
291  WVRELEASE(x509);
292  wvssl_free();
293 }
294 
295 
296 void WvSSLStream::printerr(WvStringParm func)
297 {
298  unsigned long l = ERR_get_error();
299  char buf[121]; // man ERR_error_string says must be > 120.
300 
301  while (l)
302  {
303  ERR_error_string(l, buf);
304  debug("%s error: %s\n", func, buf);
305  l = ERR_get_error();
306  }
307 }
308 
309 
310 size_t WvSSLStream::uread(void *buf, size_t len)
311 {
312  if (!sslconnected)
313  return 0;
314  if (len == 0) return 0;
315 
316  // if SSL buffers stuff on its own, select() may not wake us up
317  // the next time around unless we're sure there is nothing left
318  read_pending = true;
319 
320  size_t total = 0;
321  for (;;)
322  {
323  // handle SSL_read quirk
324  if (read_bouncebuf.used() != 0)
325  {
326  // copy out cached data
327  size_t amount = len < read_bouncebuf.used() ?
328  len : read_bouncebuf.used();
329  read_bouncebuf.move(buf, amount);
330 
331  // locate next chunk in buffer
332  len -= amount;
333  total += amount;
334  if (len == 0)
335  {
336  read_pending = false;
337  break;
338  }
339  buf = (unsigned char *)buf + amount;
340 
341  // FIXME: this shouldn't be necessary, but it resolves weird
342  // problems when the other end disconnects in the middle of
343  // SSL negotiation, but only on emakela's machine. I don't
344  // know why. -- apenwarr (2004/02/10)
345  break;
346  }
347 
348  // attempt to read
349  read_bouncebuf.zap(); // force use of same position in buffer
350  size_t avail = read_bouncebuf.free();
351  unsigned char *data = read_bouncebuf.alloc(avail);
352 
353  ERR_clear_error();
354  int result = SSL_read(ssl, data, avail);
355  // debug("<< SSL_read result %s for %s bytes (wanted %s)\n",
356  // result, avail, len);
357  if (result <= 0)
358  {
359  error_t err = errno;
360  read_bouncebuf.unalloc(avail);
361  int sslerrcode = SSL_get_error(ssl, result);
362  switch (sslerrcode)
363  {
364  case SSL_ERROR_WANT_READ:
365  debug("<< SSL_read() needs to wait for writable.\n");
366  break; // wait for later
367  case SSL_ERROR_WANT_WRITE:
368  debug("<< SSL_read() needs to wait for readable.\n");
369  break; // wait for later
370 
371  case SSL_ERROR_NONE:
372  break; // no error, but can't make progress
373 
374  case SSL_ERROR_ZERO_RETURN:
375  debug("<< EOF: zero return\n");
376 
377  // don't do this if we're returning nonzero!
378  // (SSL has no way to do a one-way shutdown, so if SSL
379  // detects a read problem, it's also a write problem.)
380  if (!total) { noread(); nowrite(); }
381  break;
382 
383  case SSL_ERROR_SYSCALL:
384  if (!err)
385  {
386  if (result == 0)
387  {
388  debug("<< EOF: syscall error "
389  "(%s/%s, %s/%s) total=%s\n",
390  stop_read, stop_write,
391  isok(), cloned && cloned->isok(), total);
392 
393  // don't do this if we're returning nonzero!
394  // (SSL has no way to do a one-way shutdown, so
395  // if SSL detects a read problem, it's also a
396  // write problem.)
397  if (!total) { noread(); nowrite(); }
398  }
399  }
400  else
401  {
402  debug("<< SSL_read() err=%s (%s)\n",
403  err, strerror(err));
404  seterr_both(err, WvString("SSL read: %s",
405  strerror(err)));
406  }
407  break;
408 
409  default:
410  printerr("SSL_read");
411  seterr("SSL read error #%s", sslerrcode);
412  break;
413  }
414  read_pending = false;
415  break; // wait for next iteration
416  }
417  // debug("<< read result was %s\n", result);
418 
419  if (result < 0)
420  result = 0;
421  read_bouncebuf.unalloc(avail - result);
422  }
423 
424  // debug("<< read %s bytes (%s, %s)\n",
425  // total, isok(), cloned && cloned->isok());
426  return total;
427 }
428 
429 
430 size_t WvSSLStream::uwrite(const void *buf, size_t len)
431 {
432  if (!sslconnected)
433  {
434  debug(">> writing, but not connected yet (%s); enqueue.\n", getwfd());
435  unconnected_buf.put(buf, len);
436  return len;
437  }
438 
439  if (len == 0) return 0;
440 
441 // debug(">> I want to write %s bytes.\n", len);
442 
443  size_t total = 0;
444 
445  // eat any data that was precached and already written
446  if (write_eat >= len)
447  {
448  write_eat -= len;
449  total = len;
450  len = 0;
451  }
452  else
453  {
454  buf = (const unsigned char *)buf + write_eat;
455  total = write_eat;
456  len -= write_eat;
457  write_eat = 0;
458  }
459 
460  // FIXME: WOW!!! Ummm... hope this never spins...
461  //
462  for (;;)
463  {
464  // handle SSL_write quirk
465  if (write_bouncebuf.used() == 0)
466  {
467  if (len == 0) break;
468 
469  // copy new data into the bounce buffer only if empty
470  // if it were not empty, then SSL_write probably returned
471  // SSL_ERROR_WANT_WRITE on the previous call and we
472  // must invoke it with precisely the same arguments
473  size_t amount = len < write_bouncebuf.free() ?
474  len : write_bouncebuf.free();
475  write_bouncebuf.put(buf, amount);
476  // note: we don't adjust the total yet...
477  } // otherwise we use what we cached last time in bounce buffer
478 
479  // attempt to write
480  size_t used = write_bouncebuf.used();
481  const unsigned char *data = write_bouncebuf.get(used);
482 
483  ERR_clear_error();
484  int result = SSL_write(ssl, data, used);
485  // debug("<< SSL_write result %s for %s bytes\n",
486  // result, used);
487  if (result <= 0)
488  {
489  int sslerrcode = SSL_get_error(ssl, result);
490  write_bouncebuf.unget(used);
491  switch (sslerrcode)
492  {
493  case SSL_ERROR_WANT_READ:
494  debug(">> SSL_write() needs to wait for readable.\n");
495  break; // wait for later
496  case SSL_ERROR_WANT_WRITE:
497  // debug(">> SSL_write() needs to wait for writable.\n");
498  break; // wait for later
499 
500  case SSL_ERROR_SYSCALL:
501  debug(">> ERROR: SSL_write() failed on socket error.\n");
502  seterr(WvString("SSL write error: %s", strerror(errno)));
503  break;
504 
505  // This case can cause truncated web pages... give more info
506  case SSL_ERROR_SSL:
507  debug(">> ERROR: SSL_write() failed on internal error.\n");
508  seterr(WvString("SSL write error: %s",
509  ERR_error_string(ERR_get_error(), NULL)));
510  break;
511 
512  case SSL_ERROR_NONE:
513  break; // no error, but can't make progress
514 
515  case SSL_ERROR_ZERO_RETURN:
516  debug(">> SSL_write zero return: EOF\n");
517  close(); // EOF
518  break;
519 
520  default:
521  printerr("SSL_write");
522  seterr(WvString("SSL write error #%s", sslerrcode));
523  break;
524  }
525  break; // wait for next iteration
526  }
527  else
528  assert((size_t)result == used);
529  write_bouncebuf.zap(); // force use of same position in buffer
530 
531  // locate next chunk to be written
532  // note: we assume that initial contents of buf and of the
533  // bouncebuf match since if we got SSL_ERROR_WANT_WRITE
534  // we did not claim to actually have written the chunk
535  // that we cached so we will have gotten it again here
536  if (size_t(result) >= len)
537  {
538  // if we cached more previously than we were given, claim
539  // we wrote what we got and remember to eat the rest later
540  write_eat = result - len;
541  total += len;
542  break;
543  }
544  total += size_t(result);
545  len -= size_t(result);
546  buf = (const unsigned char *)buf + size_t(result);
547  }
548 
549  //debug(">> wrote %s bytes\n", total);
550  return total;
551 }
552 
554 {
555  debug("Closing SSL connection (ok=%s,sr=%s,sw=%s,child=%s).\n",
556  isok(), stop_read, stop_write, cloned && cloned->isok());
557 
558  if (ssl)
559  {
560  ERR_clear_error();
561  SSL_shutdown(ssl);
562  SSL_free(ssl);
563  ssl = NULL;
564  sslconnected = false;
565  }
566 
568 
569  if (ctx)
570  {
571  SSL_CTX_free(ctx);
572  ctx = NULL;
573  }
574 }
575 
576 
577 bool WvSSLStream::isok() const
578 {
579  return ssl && WvStreamClone::isok();
580 }
581 
582 
584 {
585  // WARNING: openssl always needs two-way socket communications even for
586  // one-way encrypted communications, so we don't pass noread/nowrite
587  // along to the child stream. This should be mostly okay, though,
588  // because we'll still send it close() once we have both noread() and
589  // nowrite().
590  ssl_stop_read = true;
591  if (ssl_stop_write)
592  {
595  }
596 }
597 
598 
600 {
601  // WARNING: see note in noread()
602  ssl_stop_write = true;
603  if (ssl_stop_read)
604  {
607  }
608 }
609 
610 
612 {
613  SelectRequest oldwant = si.wants;
614  bool oldinherit = si.inherit_request;
615  if (!sslconnected)
616  {
617  si.wants = connect_wants;
618  si.inherit_request = true; // ignore force_select() until connected
619  }
620 
621  // the SSL library might be keeping its own internal buffers
622  // or we might have left buffered data behind deliberately
623  if (si.wants.readable && (read_pending || read_bouncebuf.used()))
624  {
625  // debug("pre_select: try reading again immediately.\n");
626  si.msec_timeout = 0;
627  si.inherit_request = oldinherit;
628  si.wants = oldwant;
629  return;
630  }
631 
633  si.inherit_request = oldinherit;
634  si.wants = oldwant;
635 }
636 
637 
639 {
640  SelectRequest oldwant = si.wants;
641  bool oldinherit = si.inherit_request;
642 
643  if (!sslconnected)
644  {
645  si.wants = connect_wants;
646  si.inherit_request = true; // ignore force_select() until connected
647  }
648 
649  bool result = WvStreamClone::post_select(si);
650  si.wants = oldwant;
651  si.inherit_request = oldinherit;
652 
653  // SSL takes a few round trips to
654  // initialize itself, and we mustn't block in the constructor, so keep
655  // trying here... it is also turning into a rather cool place
656  // to do the validation of the connection ;)
657  if (!sslconnected && cloned && cloned->isok() && result)
658  {
659  debug("!sslconnected in post_select (r=%s/%s, w=%s/%s, t=%s)\n",
660  cloned->isreadable(), si.wants.readable,
661  cloned->iswritable(), si.wants.writable,
662  si.msec_timeout);
663 
664  connect_wants.writable = false;
665 
666  // for ssl streams to work, we have to be cloning a stream that
667  // actually uses a single, valid fd.
668  WvFDStream *fdstream = static_cast<WvFDStream*>(cloned);
669  int fd = fdstream->getfd();
670  assert(fd >= 0);
671  ERR_clear_error();
672  SSL_set_fd(ssl, fd);
673 // debug("SSL connected on fd %s.\n", fd);
674 
675  int err;
676 
677  if (is_server)
678  {
679  // If we are a server, get ready to accept an incoming SSL
680  // connection
681  err = SSL_accept(ssl);
682  }
683  else
684  err = SSL_connect(ssl);
685 
686  if (err < 0)
687  {
688  if (errno == EAGAIN)
689  debug("Still waiting for SSL negotiation.\n");
690  else if (!errno)
691  {
692  printerr(is_server ? "SSL_accept" : "SSL_connect");
693  seterr(WvString("SSL negotiation failed (%s)!", err));
694  }
695  else
696  {
697  printerr(is_server ? "SSL_accept" : "SSL_connect");
698  seterr(errno);
699  }
700  }
701  else // We're connected, so let's do some checks ;)
702  {
703  debug("SSL connection using cipher %s.\n", SSL_get_cipher(ssl));
704 
705  WvX509 *peercert = new WvX509(SSL_get_peer_certificate(ssl));
706  //Should we try to validate before storing, or not?
707  if (peercert->isok() && peercert->validate())
708  setattr("peercert", peercert->encode(WvX509::CertPEM));
709  if (!!vcb)
710  {
711  debug("SSL Peer is: %s\n", peercert->get_subject());
712  if (peercert->isok() && peercert->validate() && vcb(peercert))
713  {
714  setconnected(true);
715  debug("SSL finished negotiating - certificate is valid.\n");
716  }
717  else
718  {
719  if (!peercert->isok())
720  seterr("Peer cert: %s", peercert->errstr());
721  else
722  seterr("Peer certificate is invalid!");
723  }
724  }
725  else
726  {
727  setconnected(true);
728  debug("SSL finished negotiating "
729  "- certificate validation disabled.\n");
730  }
731  WVRELEASE(peercert);
732  }
733 
734  return false;
735  }
736 
737  if ((si.wants.readable || readcb)
738  && (read_pending || read_bouncebuf.used()))
739  result = true;
740 
741  return result;
742 }
743 
744 
745 void WvSSLStream::setconnected(bool conn)
746 {
747  sslconnected = conn;
748  if (conn) write(unconnected_buf);
749 }
750