5 #define OPENSSL_NO_KRB5
6 #include "wvsslstream.h"
9 #include "wvlistener.h"
11 #include "wvmoniker.h"
12 #include "wvlinkerhack.h"
13 #include <openssl/ssl.h>
14 #include <openssl/err.h>
27 #define errno GetLastError()
29 #define EAGAIN WSAEWOULDBLOCK
36 return new WvSSLStream(IWvStream::create(s, _obj), NULL, 0,
false);
126 l->
addwrap(wv::bind(&IWvStream::create,
"sslserv", _1));
136 if (li.
count() == 3) {
138 connmoniker = *li.
last();
140 }
else if (li.
count() != 2) {
146 IWvListener *l = IWvListener::create(connmoniker, obj);
148 l->
addwrap(wv::bind(&IWvStream::create,
156 #define MAX_BOUNCE_AMOUNT (16384) // 1 SSLv3/TLSv1 record
158 static int ssl_stream_count = 0;
160 static int wv_verify_cb(
int preverify_ok, X509_STORE_CTX *ctx)
167 WvSSLGlobalValidateCallback WvSSLStream::global_vcb = 0;
170 WvSSLValidateCallback _vcb,
bool _is_server) :
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)
181 if (!vcb && global_vcb)
182 vcb = wv::bind(global_vcb, _1,
this);;
184 is_server = _is_server;
188 sslconnected = ssl_stop_read = ssl_stop_write =
false;
192 if (x509 && !x509->
isok())
194 seterr(
"Certificate + key pair invalid.");
198 if (is_server && !x509)
200 seterr(
"Certificate not available: server mode not possible!");
206 debug(
"Configured algorithms and methods for server mode.\n");
208 ctx = SSL_CTX_new(SSLv23_server_method());
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!");
219 SSL_CTX_set_mode(
ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
223 SSL_CTX_set_cipher_list(
ctx,
"HIGH");
227 SSL_CTX_set_options(
ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
231 seterr(
"Unable to bind Certificate to SSL Context!");
235 SSL_CTX_set_verify(
ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
238 debug(
"Server mode ready.\n");
242 debug(
"Configured algorithms and methods for client mode.\n");
244 ctx = SSL_CTX_new(SSLv23_client_method());
247 seterr(
"Can't get SSL context!");
252 seterr(
"Unable to bind Certificate to SSL Context!");
263 seterr(
"Can't create SSL object!");
272 if (!!vcb || is_server)
273 SSL_set_verify(
ssl, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
276 connect_wants.readable =
true;
277 connect_wants.writable =
true;
278 connect_wants.isexception =
false;
279 debug(
"SSL stream initialized.\n");
287 debug(
"Deleting SSL connection.\n");
289 debug(
"Error was: %s\n", errstr());
296 void WvSSLStream::printerr(WvStringParm func)
298 unsigned long l = ERR_get_error();
303 ERR_error_string(l, buf);
304 debug(
"%s error: %s\n", func, buf);
314 if (len == 0)
return 0;
324 if (read_bouncebuf.
used() != 0)
327 size_t amount = len < read_bouncebuf.
used() ?
328 len : read_bouncebuf.
used();
329 read_bouncebuf.move(buf, amount);
336 read_pending =
false;
339 buf = (
unsigned char *)buf + amount;
349 read_bouncebuf.
zap();
350 size_t avail = read_bouncebuf.
free();
351 unsigned char *data = read_bouncebuf.
alloc(avail);
354 int result = SSL_read(
ssl, data, avail);
361 int sslerrcode = SSL_get_error(
ssl, result);
364 case SSL_ERROR_WANT_READ:
365 debug(
"<< SSL_read() needs to wait for writable.\n");
367 case SSL_ERROR_WANT_WRITE:
368 debug(
"<< SSL_read() needs to wait for readable.\n");
374 case SSL_ERROR_ZERO_RETURN:
375 debug(
"<< EOF: zero return\n");
383 case SSL_ERROR_SYSCALL:
388 debug(
"<< EOF: syscall error "
389 "(%s/%s, %s/%s) total=%s\n",
391 isok(), cloned && cloned->
isok(), total);
402 debug(
"<< SSL_read() err=%s (%s)\n",
404 seterr_both(err,
WvString(
"SSL read: %s",
410 printerr(
"SSL_read");
411 seterr(
"SSL read error #%s", sslerrcode);
414 read_pending =
false;
421 read_bouncebuf.
unalloc(avail - result);
434 debug(
">> writing, but not connected yet (%s); enqueue.\n", getwfd());
435 unconnected_buf.put(buf, len);
439 if (len == 0)
return 0;
446 if (write_eat >= len)
454 buf = (
const unsigned char *)buf + write_eat;
465 if (write_bouncebuf.
used() == 0)
473 size_t amount = len < write_bouncebuf.
free() ?
474 len : write_bouncebuf.
free();
475 write_bouncebuf.put(buf, amount);
480 size_t used = write_bouncebuf.
used();
481 const unsigned char *data = write_bouncebuf.
get(used);
484 int result = SSL_write(
ssl, data, used);
489 int sslerrcode = SSL_get_error(
ssl, result);
490 write_bouncebuf.
unget(used);
493 case SSL_ERROR_WANT_READ:
494 debug(
">> SSL_write() needs to wait for readable.\n");
496 case SSL_ERROR_WANT_WRITE:
500 case SSL_ERROR_SYSCALL:
501 debug(
">> ERROR: SSL_write() failed on socket error.\n");
507 debug(
">> ERROR: SSL_write() failed on internal error.\n");
509 ERR_error_string(ERR_get_error(), NULL)));
515 case SSL_ERROR_ZERO_RETURN:
516 debug(
">> SSL_write zero return: EOF\n");
521 printerr(
"SSL_write");
528 assert((
size_t)result == used);
529 write_bouncebuf.
zap();
536 if (
size_t(result) >= len)
540 write_eat = result - len;
544 total += size_t(result);
545 len -= size_t(result);
546 buf = (
const unsigned char *)buf +
size_t(result);
555 debug(
"Closing SSL connection (ok=%s,sr=%s,sw=%s,child=%s).\n",
564 sslconnected =
false;
590 ssl_stop_read =
true;
602 ssl_stop_write =
true;
614 bool oldinherit = si.inherit_request;
617 si.wants = connect_wants;
618 si.inherit_request =
true;
623 if (si.wants.readable && (read_pending || read_bouncebuf.
used()))
627 si.inherit_request = oldinherit;
633 si.inherit_request = oldinherit;
641 bool oldinherit = si.inherit_request;
645 si.wants = connect_wants;
646 si.inherit_request =
true;
651 si.inherit_request = oldinherit;
657 if (!sslconnected && cloned && cloned->
isok() && result)
659 debug(
"!sslconnected in post_select (r=%s/%s, w=%s/%s, t=%s)\n",
664 connect_wants.writable =
false;
669 int fd = fdstream->
getfd();
681 err = SSL_accept(
ssl);
684 err = SSL_connect(
ssl);
689 debug(
"Still waiting for SSL negotiation.\n");
692 printerr(is_server ?
"SSL_accept" :
"SSL_connect");
697 printerr(is_server ?
"SSL_accept" :
"SSL_connect");
703 debug(
"SSL connection using cipher %s.\n", SSL_get_cipher(
ssl));
708 setattr(
"peercert", peercert->
encode(WvX509::CertPEM));
711 debug(
"SSL Peer is: %s\n", peercert->
get_subject());
712 if (peercert->
isok() && peercert->
validate() && vcb(peercert))
715 debug(
"SSL finished negotiating - certificate is valid.\n");
719 if (!peercert->
isok())
722 seterr(
"Peer certificate is invalid!");
728 debug(
"SSL finished negotiating "
729 "- certificate validation disabled.\n");
737 if ((si.wants.readable || readcb)
738 && (read_pending || read_bouncebuf.
used()))
745 void WvSSLStream::setconnected(
bool conn)
748 if (conn)
write(unconnected_buf);
const T * get(size_t count)
Reads exactly the specified number of elements and returns a pointer to a storage location owned by t...
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
WvString fqdomainname()
Get the fqdn of the local host, using gethostbyname() and gethostname()
virtual void addwrap(IWvListenerWrapper _wrapper)=0
Add a wrapper function for this stream: something that accept() will call to possibly wrap the stream...
virtual size_t uwrite(const void *buf, size_t len)
unbuffered I/O functions; these ignore the buffer, which is handled by write().
virtual bool isreadable()=0
Returns true if the stream is readable.
void unget(size_t count)
Ungets exactly the specified number of elements by returning them to the buffer for subsequent reads.
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
T * last() const
Returns a pointer to the last element in the linked list.
X509 Class to handle certificates and their related functions.
T * alloc(size_t count)
Allocates exactly the specified number of elements and returns a pointer to an UNINITIALIZED storage ...
void unalloc(size_t count)
Unallocates exactly the specified number of elements by removing them from the buffer and releasing t...
virtual bool iswritable()=0
Returns true if the stream is writable (without using the outbuf).
virtual void close()
Close this stream.
size_t free() const
Returns the number of elements that the buffer can currently accept for writing.
SSL Stream, handles SSLv2, SSLv3, and TLS Methods - If you want it to be a server,...
void unlink(T *data)
Unlinks the specified element from the list.
bool bind_ssl(SSL_CTX *ctx)
Avoid a lot of ugliness by having it so that we are binding to the SSL context, and not the other way...
static WvString strerror(int errnum)
A replacement for the operating system ::strerror() function that can map more kinds of error strings...
T * first() const
Returns a pointer to the first element in the linked list.
bool validate(WvX509 *cacert=NULL) const
Function to verify the validity of a certificate that has been placed in cert.
virtual bool isok() const
Is the certificate object valid?
virtual void noread()
Shuts down the reading side of the stream.
virtual WvString errstr() const
Returns an error string if isok() is not true.
WvString encode_hostname_as_DN(WvStringParm hostname)
Example: encode_hostname_as_DN("www.fizzle.com") will result in dc=www,dc=fizzle,dc=com,...
virtual bool isok() const
Says if this certificate+key pair is good for use.
virtual void seterr(int _errnum)
Override seterr() from WvError so that it auto-closes the stream.
WvSSLStream(IWvStream *_slave, WvX509Mgr *_x509=NULL, WvSSLValidateCallback _vcb=0, bool _is_server=false)
Start an SSL connection on the stream _slave.
WvString is an implementation of a simple and efficient printable-string class.
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
WvString wvtcl_encode(WvList< WvString > &l, const WvStringMask &nasties=WVTCL_NASTY_SPACES, const WvStringMask &splitchars=WVTCL_SPLITCHARS)
encode a tcl-style list.
the data structure used by pre_select()/post_select() and internally by select().
virtual void nowrite()
Shuts down the writing side of the stream.
A type-safe version of WvMonikerBase that lets you provide create functions for object types other th...
virtual void nowrite()
Shuts down the writing side of the stream.
virtual void decode(const WvX509::DumpMode mode, WvStringParm encoded)
Load the information from the format requested by mode into the class - this overwrites the certifica...
WvStreamClone simply forwards all requests to the "cloned" stream.
virtual size_t uread(void *buf, size_t len)
unbuffered I/O functions; these ignore the buffer, which is handled by read().
void zap()
Clears the buffer.
int getfd() const
Returns the Unix file descriptor for reading and writing.
virtual bool isok() const =0
By default, returns true if geterr() == 0.
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
virtual int geterr() const
If isok() is false, return the system error number corresponding to the error, -1 for a special error...
A SelectRequest is a convenient way to remember what we want to do to a particular stream: read from ...
virtual bool isok() const
return true if the stream is actually usable right now
size_t count() const
Returns the number of elements in the list.
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Base class for streams built on Unix file descriptors.
virtual void noread()
Shuts down the reading side of the stream.
size_t used() const
Returns the number of elements in the buffer currently available for reading.
SSL_CTX * ctx
SSL Context - used to create SSL Object.
virtual bool isok() const
return true if the stream is actually usable right now
bool test() const
Test to make sure that a certificate and a keypair go together.
virtual void close()
Close this stream.
WvString get_subject() const
get and set the Subject field of the certificate
void wvtcl_decode(WvList< WvString > &l, WvStringParm _s, const WvStringMask &splitchars=WVTCL_SPLITCHARS, bool do_unescape=true)
split a tcl-style list.
A linked list container class.
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
virtual unsigned int addRef()=0
Indicate you are using this object.
void unlink_first()
Unlinks the first element from the list.
virtual ~WvSSLStream()
Cleans up everything (calls close + frees up the SSL Objects used)
bool stop_read
True if noread()/nowrite()/close() have been called, respectively.
WvString encode(const DumpMode mode) const
Return the information requested by mode.
SSL * ssl
Main SSL Object - after SSL_set_fd() we make all calls through the connection through here.