diff options
Diffstat (limited to 'cpukit/httpd/websSSL.c')
-rw-r--r-- | cpukit/httpd/websSSL.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/cpukit/httpd/websSSL.c b/cpukit/httpd/websSSL.c new file mode 100644 index 0000000000..6c2e4ce5da --- /dev/null +++ b/cpukit/httpd/websSSL.c @@ -0,0 +1,706 @@ +/* + * 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); +} + +/******************************************************************************/ |