blob: 30949b261ed39c67bf031e04999e15627689fedb [file] [log] [blame]
#ifndef __ENABLE_MOCANA_SSL_SERVER__
#include "wsIntrn.h"
#include "webs.h"
#include "websSSL.h"
#include "ref_nv_def.h"
#define DEFAULT_KEY_FILE "/etc_ro/certs/cakey.pem"
#define DEFAULT_CA_FILE "/etc_ro/certs/cacert.pem"
#define DEFAULT_PWD_FILE "/etc_ro/certs/password"
#define SSL_PORT 443
#define DEFAULT_KEY_MASK 0x60
typedef struct websSSLKey {
char pwd[17];
unsigned char procType;
unsigned short dekType;
unsigned char dekInfo[16];
unsigned char privateKey[624];
unsigned short certLen;
} websSSLKey;
#ifdef SIGPIPE
#define do_pipe_sig() signal(SIGPIPE,SIG_IGN)
#else
#define do_pipe_sig()
#endif
# 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
#ifdef OPENSSL
#ifndef SSLeay_add_all_algorithms
#define SSLeay_add_all_algorithms() OpenSSL_add_all_algorithms()
#endif
#define SSLC_add_all_algorithms() SSLeay_add_all_algorithms()
#else
extern void SSLC_add_all_algorithms(void);
#endif
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);
static int websSSLSetCertStuff(SSL_CTX *ctx, char *cert_file, char *key_file);
static int sslListenSock = -1;
static SSL_CTX *sslctx = NULL;
static char *sslpwd = NULL;
static websSSLKey sslkey = {0};
static unsigned char *sslcert = NULL;
static int getFileSize(char *lpath)
{
gstat_t s;
if (lpath && stat(lpath, &s) == 0) {
return s.st_size;
}
return 0;
}
int websSSLOpen()
{
SSL_METHOD *meth;
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
#ifdef WEBINSPECT_FIX
meth = TLSv1_2_server_method();
#else
meth = SSLv23_server_method();
#endif
sslctx = SSL_CTX_new(meth);
a_assert(sslctx);
if (sslctx == NULL) {
trace(2, T("SSL: Unable to create SSL context!\n"));
return -1;
}
SSL_CTX_set_quiet_shutdown(sslctx, 1);
SSL_CTX_set_options(sslctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
SSL_CTX_sess_set_cache_size(sslctx, 128);
EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
if (ecdh){
long eret = SSL_CTX_set_tmp_ecdh (sslctx, ecdh);
trace(2, T("SSL: ecdh:%x ret=%d\n"), ecdh, eret);
EC_KEY_free(ecdh);
ecdh = NULL;
}
#ifdef WEBINSPECT_FIX
if(!SSL_CTX_set_cipher_list(sslctx,"ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:!NULL:!RC4:!RC2:!DES:!3DES:!SHA:!SHA256:!SHA384:!MD5+HIGH:+MEDIUM"))
trace(2, T("SSL: Unable to set !DES:!RC4:!SHA1\n"));
#endif
unsigned char webkey_flag = 0;
int retCode = cpnv_NvItemRead(ZPS_REF_MSINFO_WEBKEY_FLAG_BASE_ADDR, &webkey_flag, sizeof(webkey_flag));
if (retCode == CPNV_ERROR)
{
trace(2, T("SSL: read cpnv keyflag error\n"));
websSSLClose();
return -1;
}
trace(2, T("SSL: webkey_flag=%d\n"),webkey_flag);
if (webkey_flag != 1)
{
ssize_t read_len = 0;
char *pwdFile = DEFAULT_PWD_FILE;
int pwdSize = getFileSize(pwdFile);
if(pwdSize > 0)
{
int fd = open(pwdFile, SOCKET_RDONLY | SOCKET_BINARY, 0666);
if(fd >= 0)
{
sslpwd = balloc(B_L,pwdSize + 1);
if(sslpwd != NULL)
{
memset(sslpwd, 0, pwdSize + 1);
read_len = read(fd, sslpwd, pwdSize);
if(read_len < 0)
{
trace(2, T("read len:%d ; errno: %s\n"), read_len, strerror(errno));
}
trace(2, T("SSL: pwd:%s\n"), sslpwd);
SSL_CTX_set_default_passwd_cb_userdata(sslctx, (void *)sslpwd);
}
close(fd);
}
}
//char *certFile = DEFAULT_CA_FILE;
//char *keyFile = DEFAULT_KEY_FILE;// kw OVERWRITE_CONST_CHAR
if (websSSLSetCertStuff(sslctx, DEFAULT_CA_FILE, DEFAULT_KEY_FILE) != 0) {
websSSLClose();
return -1;
}
}
else
{
retCode = cpnv_NvItemRead(ZPS_REF_MSINFO_WEBKEY_DATA_BASE_ADDR, (unsigned char *)&sslkey, sizeof(sslkey));
char i = 0;
if (retCode == CPNV_ERROR)
{
trace(2, T("SSL: read cpnv key error\n"));
websSSLClose();
return -1;
}
trace(2, T("SSL: key procType=%d dekType=%d certLen=%d\n"),sslkey.procType,sslkey.dekType,sslkey.certLen);
sslcert = balloc(B_L, sslkey.certLen);
if(sslcert == NULL)
{
trace(2, T("SSL: alloc cpnv cert fail\n"));
websSSLClose();
return -1;
}
retCode = cpnv_NvItemRead(ZPS_REF_MSINFO_WEBKEY_DATA_BASE_ADDR+sizeof(sslkey), sslcert, sslkey.certLen);
if (retCode == CPNV_ERROR)
{
trace(2, T("SSL: read cpnv cert error\n"));
websSSLClose();
return -1;
}
for(i=0; i < sizeof(sslkey.pwd); i++)
{
if(sslkey.pwd[i] != 0)
{
sslkey.pwd[i] = sslkey.pwd[i] - DEFAULT_KEY_MASK;
}
else
{
break;
}
}
SSL_CTX_set_default_passwd_cb_userdata(sslctx, (void *)sslkey.pwd);
retCode = websSSLSetCertificate(sslctx, sslcert, sslkey.certLen);
if (retCode <= 0)
{
trace(2, T("SSL: websSSLSetCertificate fail ret=%d\n"),retCode);
websSSLClose();
return -1;
}
retCode = websSSLSetPrivateKey(sslctx, &sslkey);
if (retCode <= 0)
{
trace(2, T("SSL: websSSLSetPrivateKey fail ret=%d\n"),retCode);
websSSLClose();
return -1;
}
}
SSL_CTX_set_tmp_rsa_callback(sslctx, websSSLTempRSACallback);
SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, websSSLVerifyCallback);
sslListenSock = socketOpenConnection6(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;
}
int websSSLIsOpen()
{
return (sslListenSock != -1);
}
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);
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));
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;
}
wp->flags |= WEBS_SECURE;
socketCreateHandler(sid, SOCKET_READABLE, websSSLSocketEvent, (int) wp);
wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp);
//trace(8, T("webs: accept request\n"));
return 0;
}
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;
}
if(sslpwd != NULL) {
bfree(B_L, sslpwd);
sslpwd = NULL;
}
#ifdef SSLC
SSL_library_cleanup();
#endif
}
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);
if(sptr == NULL)
return -1;
sock = sptr->sock;
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
#ifdef WEBS_SSL_SUPPORT
websSSLFree(wp->wsp);
wp->wsp = balloc(B_L, sizeof(websSSL_t));
a_assert (wp->wsp);
if(wp->wsp == NULL)
return -1;
(wp->wsp)->bio = bio;
(wp->wsp)->ssl = ssl;
#endif
websReadEvent(wp);
return ret;
}
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 (websValid(wp) && wp->writeSocket) {//cov
(*wp->writeSocket)(wp);
}
}
}
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);
X509_NAME_oneline(X509_get_issuer_name(X509_STORE_CTX_get_current_cert(ctx)), 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;
}
int websSSLSetCertificate(SSL_CTX *ctx, const unsigned char *data, int len)
{
BIO *in;
int ret = 0;
X509 *x = NULL;
in = BIO_new(BIO_s_mem());
if (in == NULL) {
trace(2, T("SSL: cert BIO_new NULL\n"));
goto end;
}
ret = PEM_write_bio(in, PEM_STRING_X509, "", data, len);
if (ret <= 0){
trace(2, T("SSL: cert PEM_write_bio fail ret=%d\n"),ret);
goto end;
}
x = PEM_read_bio_X509(in, NULL,
SSL_CTX_get_default_passwd_cb(ctx),//ctx->default_passwd_callback,
SSL_CTX_get_default_passwd_cb_userdata(ctx));//ctx->default_passwd_callback_userdata);
if (x == NULL) {
trace(2, T("SSL: cert PEM_read_bio_X509 NULL\n"));
goto end;
}
ret = SSL_CTX_use_certificate(ctx, x);
X509_free(x);
end:
if (in != NULL)
BIO_free(in);
return (ret);
}
int websSSLSetPrivateKey(SSL_CTX *ctx, websSSLKey *key)
{
int ret = 0;
BIO *in;
EVP_PKEY *pkey = NULL;
const char *objstr = NULL;
char buf[PEM_BUFSIZE] = {0};
in = BIO_new(BIO_s_mem());
if (in == NULL) {
trace(2, T("SSL: key BIO_new NULL\n"));
goto end;
}
objstr = OBJ_nid2sn(key->dekType);
if (objstr == NULL) {
trace(2, T("SSL: key OBJ_nid2sn NULL type = %d\n"),key->dekType);
goto end;
}
PEM_proc_type(buf, key->procType);
PEM_dek_info(buf, objstr, sizeof(key->dekInfo), (char *)key->dekInfo);
ret = PEM_write_bio(in, PEM_STRING_RSA, buf, key->privateKey, sizeof(key->privateKey));
if (ret <= 0){
trace(2, T("SSL: key PEM_write_bio fail ret=%d\n"),ret);
goto end;
}
pkey = PEM_read_bio_PrivateKey(in, NULL,
SSL_CTX_get_default_passwd_cb(ctx),//ctx->default_passwd_callback,
SSL_CTX_get_default_passwd_cb_userdata(ctx));//ctx->default_passwd_callback_userdata);
if (pkey == NULL) {
trace(2, T("SSL: key PEM_read_bio_PrivateKey NULL\n"));
goto end;
}
ret = SSL_CTX_use_PrivateKey(ctx, pkey);
EVP_PKEY_free(pkey);
end:
if (in != NULL)
BIO_free(in);
return (ret);
}
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;
}
if (!SSL_CTX_check_private_key(ctx)) {
trace(2, T("SSL: Check of private key file <%s> FAILED!\n"),
keyFile);
return -1;
}
}
return 0;
}
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;
}
if (!SSL_CTX_check_private_key(sslctx)) {
return -1;
}
return 0;
}
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;
}
if (!SSL_CTX_check_private_key(sslctx)) {
return -1;
}
return 0;
}
#ifdef SSLC
extern RSA *RSA_new(void);
#endif
int websSSLEof(websSSL_t *wsp)
{
a_assert(wsp);
if ((wsp == NULL) || (wsp->bio == NULL)) {
return -1;
}
return BIO_eof(wsp->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);
}
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;
}
int websSSLFree(websSSL_t *wsp)
{
if (wsp == NULL) {
return -1;
}
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;
}
#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 (len < 1536) {
if ((rc = BIO_read(wsp->bio, &c, 1)) < 0) {
return rc;
}
if (rc == 0) {
if (len > 0 && BIO_eof(wsp->bio)) {
c = '\n';
} else {
return -1;
}
}
if (c == '\n') {
if ((len > 0) && (len < lenBuf)) {
(*buf)[len] = 0;
}
return len;
} else if (c == '\r') {
continue;
}
if (lenBuf == 0 || len >= lenBuf - 1) {
lenBuf += BUF_BLOCK;
*buf = brealloc(B_L, *buf, lenBuf);
}
a_assert(*buf);
if(*buf == NULL)
return -1;
(*buf)[len] = c;
len++;
}
bfreeSafe(B_L,*buf);
*buf = NULL;
return -1;
}
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);
}
int websSSLFlush(websSSL_t *wsp)
{
a_assert(wsp);
if ((wsp == NULL) || (wsp->bio == NULL)) {
return -1;
}
return BIO_flush(wsp->bio);
}
#endif