summaryrefslogblamecommitdiffstats
path: root/cpukit/httpd/websSSL.c
blob: a3c3fdc232d5911516298b0665696e1993e0d0a0 (plain) (tree)


































































                                                                                

                                                                                        























                                                                                        
 



                                              
                                               



















                                                    
                                                                    
















                                                                       
                                                                                 































                                                                            
                                                            


                                             
                                                                             























                                                                                
                                          














































                                                                                

                                                               








































                                                                                 
         



                                               
         









































































































































                                                                                     
                                                               

                                                                                 
                                          








                                                                                       
                                         


                                  
  
                                                                     
                               


                                                                                    
                                         




















                                                                                
                                                          


                                         
  

























                                                                                  
  






































                                                                                
                          







                               
  

























                                                                                      
         















                                                                                
         























                                                                                
         





                                                           
 




















                                                                                           
                 

























                                                                                
         














                                                                                
         




                                                                                
/*
 * websSSL.c -- SSL envrionment creation
 *
 * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
 *
 * See the file "license.txt" for usage and redistribution license requirements
 *
 * $Id$
 */

/******************************** Description *********************************/

/*
 *	This module implements a patch into SSL implementations for the webs
 *	module.
 */

/********************************* Includes ***********************************/

#include	"wsIntrn.h"
#include	"webs.h"
#include	"websSSL.h"

/******************************* Definitions **********************************/

#define DEFAULT_CERT_FILE	"./server.pem"
#define DEFAULT_KEY_FILE	"./certs/cakey.pem"
#define DEFAULT_CA_FILE		"./certs/cacert.pem"
#define DEFAULT_CA_PATH		"./certs/"
#define SSL_PORT			443

/*
 *	Define the components of the apps_startup() macro
 */

#ifdef SIGPIPE
#define do_pipe_sig()	signal(SIGPIPE,SIG_IGN)
#else
#define do_pipe_sig()
#endif

#ifdef OPENSSL
#define SSLC_add_all_algorithms()	SSLeay_add_all_algorithms()
#else
extern void SSLC_add_all_algorithms(void);
#endif

/*
 *	Define the apps_startup() macro
 */

#  if defined(MSDOS) || defined(WIN16) || defined(WIN32)
#    ifdef _O_BINARY
#      define apps_startup() \
		_fmode=_O_BINARY; do_pipe_sig(); CRYPTO_malloc_init(); \
		SSLC_add_all_algorithms()
#    else
#      define apps_startup() \
		_fmode=O_BINARY; do_pipe_sig(); CRYPTO_malloc_init(); \
		SSLC_add_all_algorithms()
#    endif
#  else
#    define apps_startup()	do_pipe_sig(); SSLC_add_all_algorithms();
#  endif

/*************************** Forward Declarations *****************************/

static int		websSSLSetCertStuff(SSL_CTX *ctx,
									char *cert_file,
									char *key_file);
static int		websSSLVerifyCallback(int ok, X509_STORE_CTX *ctx);
static RSA		*websSSLTempRSACallback(SSL *s, int is_export, int keylength);

static int		websSSLReadEvent (webs_t wp);
static int		websSSLAccept(int sid, char *ipaddr, int port, int listenSid);
static void		websSSLSocketEvent(int sid, int mask, int data);

/*********************************** Locals ***********************************/

static int		sslListenSock = -1;			/* Listen socket */
static SSL_CTX	*sslctx = NULL;

/******************************************************************************/
/*
 *	Start up the SSL Context for the application, and start a listen on the
 *	SSL port (usually 443, and defined by SSL_PORT)
 *	Return 0 on success, -1 on failure.
 */

int websSSLOpen()
{
	char		*certFile, *keyFile, *CApath, *CAfile;
	SSL_METHOD	*meth;

/*
 *	Install and initialize the SSL library
 */
	apps_startup();
	trace(7, T("SSL: Initializing SSL\n"));

#ifdef SSLC
	SSL_library_init();
#endif

	SSL_load_error_strings();

#ifdef OPENSSL
	SSLeay_add_ssl_algorithms();
#endif

/*
 *	Important!  Enable both SSL versions 2 and 3
 */
	meth = SSLv23_server_method();
	sslctx = SSL_CTX_new(meth);

	a_assert(sslctx);

	if (sslctx == NULL) {
		trace(2, T("SSL: Unable to create SSL context!\n"));
		return -1;
	}

/*
 *	Adjust some SSL Context variables
 */
	SSL_CTX_set_quiet_shutdown(sslctx, 1);
	SSL_CTX_set_options(sslctx, 0);
	SSL_CTX_sess_set_cache_size(sslctx, 128);

/*
 *	Set the certificate verification locations
 */
	CApath = DEFAULT_CA_PATH;
	CAfile = DEFAULT_CA_FILE;
	if ((!SSL_CTX_load_verify_locations(sslctx, CAfile, CApath)) ||
		(!SSL_CTX_set_default_verify_paths(sslctx))) {
		trace(2, T("SSL: Unable to set cert verification locations!\n"));
		websSSLClose();
		return -1;
	}

/*
 *	Set the certificate and key files for the SSL context
 */
	certFile = DEFAULT_CERT_FILE;
	keyFile = NULL;
	if (websSSLSetCertStuff(sslctx, certFile, keyFile) != 0) {
		websSSLClose();
		return -1;
	}

/*
 *	Set the RSA callback for the SSL context
 */
	SSL_CTX_set_tmp_rsa_callback(sslctx, websSSLTempRSACallback);

/*
 *	Set the verification callback for the SSL context
 */
	SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, websSSLVerifyCallback);

/*
 *	Set the certificate authority list for the client
 */
	SSL_CTX_set_client_CA_list(sslctx, SSL_load_client_CA_file(CAfile));

/*
 *	Open the socket
 */
	sslListenSock = socketOpenConnection(NULL, SSL_PORT,
		websSSLAccept, SOCKET_BLOCK);

	if (sslListenSock < 0) {
		trace(2, T("SSL: Unable to open SSL socket on port <%d>!\n"),
			SSL_PORT);
		return -1;
	}

	return 0;
}

/******************************************************************************/
/*
 *	Return TRUE if websSSL has been opened
 */

int websSSLIsOpen()
{
	return (sslListenSock != -1);
}

/******************************************************************************/
/*
 *	Stops the SSL
 */

void websSSLClose()
{
	trace(7, T("SSL: Closing SSL\n"));

	if (sslctx != NULL) {
		SSL_CTX_free(sslctx);
		sslctx = NULL;
	}

	if (sslListenSock != -1) {
		socketCloseConnection(sslListenSock);
		sslListenSock = -1;
	}

#ifdef SSLC
	SSL_library_cleanup();
#endif
}

/******************************************************************************/
/*
 *	Accept a connection
 */

int websSSLAccept(int sid, char *ipaddr, int port, int listenSid)
{
	webs_t	wp;
	int		wid;

	a_assert(ipaddr && *ipaddr);
	a_assert(sid >= 0);
	a_assert(port >= 0);

/*
 *	Allocate a new handle for this accepted connection. This will allocate
 *	a webs_t structure in the webs[] list
 */
	if ((wid = websAlloc(sid)) < 0) {
		return -1;
	}
	wp = webs[wid];
	a_assert(wp);
	wp->listenSid = listenSid;

	ascToUni(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr), strlen(ipaddr)+1));

/*
 *	Check if this is a request from a browser on this system. This is useful
 *	to know for permitting administrative operations only for local access
 */
	if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 ||
			gstrcmp(wp->ipaddr, websIpaddr) == 0 ||
			gstrcmp(wp->ipaddr, websHost) == 0) {
		wp->flags |= WEBS_LOCAL_REQUEST;
	}
/*
 *	Since the acceptance came in on this channel, it must be secure
 */
	wp->flags |= WEBS_SECURE;

/*
 *	Arrange for websSocketEvent to be called when read data is available
 */
	socketCreateHandler(sid, SOCKET_READABLE, websSSLSocketEvent, (int) wp);

/*
 *	Arrange for a timeout to kill hung requests
 */
	wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp);
	trace(8, T("webs: accept request\n"));
	return 0;
}

/******************************************************************************/
/*
 *	The webs socket handler.  Called in response to I/O. We just pass control
 *	to the relevant read or write handler. A pointer to the webs structure
 *	is passed as an (int) in iwp.
 */

static void websSSLSocketEvent(int sid, int mask, int iwp)
{
	webs_t	wp;

	wp = (webs_t) iwp;
	a_assert(wp);

	if (! websValid(wp)) {
		return;
	}

	if (mask & SOCKET_READABLE) {
		websSSLReadEvent(wp);
	}
	if (mask & SOCKET_WRITABLE) {
		if (wp->writeSocket) {
			(*wp->writeSocket)(wp);
		}
	}
}

/******************************************************************************/
/*
 *	Handler for SSL Read Events
 */

static int websSSLReadEvent (webs_t wp)
{
	int			ret, sock;
	socket_t	*sptr;
	SSL			*ssl;
	BIO			*bio, *bioSSL, *bioSock;
#ifdef DEV
	const char	*ciphers;
#endif

	a_assert (wp);
	a_assert(websValid(wp));

	sptr = socketPtr(wp->sid);
	a_assert(sptr);

	sock = sptr->sock;

/*
 *	Create a new BIO and SSL session for this web request
 */
	bio = BIO_new(BIO_f_buffer());
	a_assert(bio);

	if (!BIO_set_write_buffer_size(bio, 128)) {
		return -1;
	}

	ssl = (SSL *) SSL_new(sslctx);
	a_assert(ssl);

	if (ssl == NULL) {
		return -1;
	}

	SSL_set_session(ssl, NULL);

	bioSSL =  BIO_new(BIO_f_ssl());
	a_assert(bioSSL);

	bioSock = BIO_new_socket(sock, BIO_NOCLOSE);
	a_assert(bioSock);

	SSL_set_bio(ssl, bioSock, bioSock);
	SSL_set_accept_state(ssl);

	ret = BIO_set_ssl(bioSSL, ssl, BIO_CLOSE);
	BIO_push(bio, bioSSL);

#ifdef DEV
	ciphers = SSL_get_cipher_list(ssl, 10);
#endif

/*
 *	Create the SSL data structure in the wp.
 */
#ifdef WEBS_SSL_SUPPORT
	wp->wsp = balloc(B_L, sizeof(websSSL_t));
	a_assert (wp->wsp);
	(wp->wsp)->bio = bio;
	(wp->wsp)->ssl = ssl;
#endif

/*
 *	Call the default Read Event
 */
	websReadEvent(wp);

	return ret;
}


/******************************************************************************/
/*
 *	SSL Verification Callback
 */

static int sslVerifyDepth = 0;
static int sslVerifyError = X509_V_OK;

int websSSLVerifyCallback(int ok, X509_STORE_CTX *ctx)
{
	char	buf[256];
	X509	*errCert;
	int		err;
	int		depth;

	errCert =	X509_STORE_CTX_get_current_cert(ctx);
	err =		X509_STORE_CTX_get_error(ctx);
	depth =		X509_STORE_CTX_get_error_depth(ctx);

	X509_NAME_oneline(X509_get_subject_name(errCert), buf, 256);

	if (!ok) {
		if (sslVerifyDepth >= depth)	{
			ok = 1;
			sslVerifyError = X509_V_OK;
		} else {
			ok=0;
			sslVerifyError = X509_V_ERR_CERT_CHAIN_TOO_LONG;
		}
	}

	switch (err)	{
	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
#ifdef OPENSSL
		X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
#endif
		break;

	case X509_V_ERR_CERT_NOT_YET_VALID:
	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
	case X509_V_ERR_CERT_HAS_EXPIRED:
	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
		break;
	}

	return ok;
}

/******************************************************************************/
/*
 *	Set the SSL certificate and key for the SSL context
 */

int websSSLSetCertStuff(SSL_CTX *ctx, char *certFile, char *keyFile)
{
	a_assert (ctx);
	a_assert (certFile);

	if (certFile != NULL) {
		if (SSL_CTX_use_certificate_file(ctx, certFile,
			SSL_FILETYPE_PEM) <= 0) {
			trace(2, T("SSL: Unable to set certificate file <%s>\n"),
				certFile);
			return -1;
		}

		if (keyFile == NULL) {
			keyFile = certFile;
		}

		if (SSL_CTX_use_PrivateKey_file(ctx, keyFile, SSL_FILETYPE_PEM) <= 0) {
			trace(2, T("SSL: Unable to set private key file <%s>\n"),
				keyFile);
			return -1;
		}

/*
 *		Now we know that a key and cert have been set against
 *		the SSL context
 */
		if (!SSL_CTX_check_private_key(ctx)) {
			trace(2, T("SSL: Check of private key file <%s> FAILED!\n"),
				keyFile);
			return -1;
		}
	}

	return 0;
}

/******************************************************************************/
/*
 *	Set certificate file for SSL context
 */

int websSSLSetCertFile(char_t *certFile)
{
	a_assert (sslctx);
	a_assert (certFile);

	if (sslctx == NULL) {
		return -1;
	}

	if (SSL_CTX_use_certificate_file(sslctx, certFile,
		SSL_FILETYPE_PEM) <= 0) {
		return -1;
	}
/*
 *	Confirm that the certificate and the private key jive.
 */
	if (!SSL_CTX_check_private_key(sslctx)) {
		return -1;
	}

	return 0;
}

/******************************************************************************/
/*
 *	Set key file for SSL context
 */

int websSSLSetKeyFile(char_t *keyFile)
{
	a_assert (sslctx);
	a_assert (keyFile);

	if (sslctx == NULL) {
		return -1;
	}

	if (SSL_CTX_use_PrivateKey_file(sslctx, keyFile, SSL_FILETYPE_PEM) <= 0) {
		return -1;
	}
/*
 *	Confirm that the certificate and the private key jive.
 */
	if (!SSL_CTX_check_private_key(sslctx)) {
		return -1;
	}

	return 0;
}

#ifdef SSLC
extern RSA *RSA_new(void);
#endif

/******************************************************************************/
/*
 *	the Temporary RSA callback
 */

static RSA *websSSLTempRSACallback(SSL *ssl, int isExport, int keyLength)
{
	static RSA *rsaTemp = NULL;

	if (rsaTemp == NULL) {

#ifdef OPENSSL
		rsaTemp = RSA_generate_key(keyLength, RSA_F4, NULL, NULL);
#endif

#ifdef SSLC
		rsaTemp = RSA_new();
#endif

	}

	return rsaTemp;
}

/******************************************************************************/
/*
 *	Free SSL resources
 */

int websSSLFree(websSSL_t *wsp)
{
	if (wsp == NULL) {
		return -1;
	}

/*
 *	Make sure we re-use sessions
 */
	if (wsp->ssl != NULL) {
		SSL_set_shutdown(wsp->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
	}

	if (wsp->bio != NULL) {
		BIO_free_all(wsp->bio);
	}

	bfree(B_L, wsp);

	return 0;
}

/******************************************************************************/
/*
 *	Return Eof for the SSL BIO
 */

int websSSLEof(websSSL_t *wsp)
{
	a_assert(wsp);

	if ((wsp == NULL) || (wsp->bio == NULL)) {
		return -1;
	}

	return BIO_eof(wsp->bio);
}

/******************************************************************************/
/*
 *	Perform a read of the SSL BIO
 */

int websSSLRead(websSSL_t *wsp, char_t *buf, int len)
{
	a_assert(wsp);
	a_assert(buf);

	if ((wsp == NULL) || (wsp->bio == NULL)) {
		return -1;
	}

	return BIO_read(wsp->bio, buf, len);
}

/******************************************************************************/
/*
 *	Perform a gets of the SSL BIO, returning an balloc'ed string
 */

#define BUF_BLOCK 256

int websSSLGets(websSSL_t *wsp, char_t **buf)
{
	int		rc,	len, lenBuf;
	char	c;

	a_assert(wsp);
	a_assert(buf);

	lenBuf = 0;
	len = 0;

	if ((wsp == NULL) || (wsp->bio == NULL)) {
		return -1;
	}

	while (1) {

		if ((rc = BIO_read(wsp->bio, &c, 1)) < 0) {
			return rc;
		}

		if (rc == 0) {
/*
 *			If there is a partial line and we are at EOF, pretend we saw a '\n'
 */
			if (len > 0 && BIO_eof(wsp->bio)) {
				c = '\n';
			} else {
				return -1;
			}
		}
/*
 *		If a newline is seen, return the data excluding the new line to the
 *		caller. If carriage return is seen, just eat it.
 */
		if (c == '\n') {
			if ((len > 0) && (len < lenBuf)) {
				(*buf)[len] = 0;
			}
			return len;
		} else if (c == '\r') {
			continue;
		}
/*
 *		Append character to buf
 */
		if (len >= lenBuf) {
			lenBuf += BUF_BLOCK;
			*buf = brealloc(B_L, *buf, lenBuf);
		}

		a_assert(*buf);
		(*buf)[len] = c;
		len++;
	}
}

/******************************************************************************/
/*
 *	Perform a write to the SSL BIO
 */

int websSSLWrite(websSSL_t *wsp, char_t *buf, int len)
{
	a_assert(wsp);
	a_assert(buf);

	if ((wsp == NULL) || (wsp->bio == NULL)) {
		return -1;
	}

	return BIO_write(wsp->bio, buf, len);
}

/******************************************************************************/
/*
 *	Perform a flush of the SSL BIO
 */

int websSSLFlush(websSSL_t *wsp)
{
	a_assert(wsp);

	if ((wsp == NULL) || (wsp->bio == NULL)) {
		return -1;
	}

	return BIO_flush(wsp->bio);
}

/******************************************************************************/