| #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 |