forked from Imagelibrary/rtems
* rtems_webserver/cgi.c, rtems_webserver/sockGen.c, rtems_webserver/umui.c, rtems_webserver/websSSL.c, rtems_webserver/websSSL.h, rtems_webserver/websda.c, rtems_webserver/websda.h: New files. Not included in previous commit.
707 lines
14 KiB
C
707 lines
14 KiB
C
/*
|
|
* 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);
|
|
}
|
|
|
|
/******************************************************************************/
|