/* Return the number of bytes read. Return -1 on errors and EOF. Distinguish EOF via mprIsSocketEof. If non-blocking, may return zero if no data or still handshaking. */ static ssize readOss(MprSocket *sp, void *buf, ssize len) { OpenSocket *osp; int rc, error, retries, i; osp = (OpenSocket*) sp->sslSocket; assert(osp); if (osp->handle == 0) { return MPR_ERR_BAD_STATE; } /* Limit retries on WANT_READ. If non-blocking and no data, then this could spin forever. */ retries = 5; for (i = 0; i < retries; i++) { rc = SSL_read(osp->handle, buf, (int) len); if (rc < 0) { error = SSL_get_error(osp->handle, rc); if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_CONNECT || error == SSL_ERROR_WANT_ACCEPT) { continue; } mprLog("info mpr ssl openssl", 5, "SSL_read %s", getOssError(sp)); } break; } if (rc <= 0) { error = SSL_get_error(osp->handle, rc); if (error == SSL_ERROR_WANT_READ) { rc = 0; } else if (error == SSL_ERROR_WANT_WRITE) { rc = 0; } else if (error == SSL_ERROR_ZERO_RETURN) { sp->flags |= MPR_SOCKET_EOF; rc = -1; } else if (error == SSL_ERROR_SYSCALL) { sp->flags |= MPR_SOCKET_EOF; rc = -1; } else if (error != SSL_ERROR_ZERO_RETURN) { /* SSL_ERROR_SSL */ mprLog("info mpr ssl openssl", 4, "%s", getOssError(sp)); rc = -1; sp->flags |= MPR_SOCKET_EOF; } } else { if (!(sp->flags & MPR_SOCKET_SERVER) && !sp->secured) { if (checkPeerCertName(sp) < 0) { return MPR_ERR_BAD_STATE; } } setSecured(sp); if (SSL_pending(osp->handle) > 0) { sp->flags |= MPR_SOCKET_BUFFERED_READ; mprRecallWaitHandlerByFd(sp->fd); } } return rc; }
/* Upgrade a standard socket to use SSL/TLS. Used by both clients and servers to upgrade a socket for SSL. If a client, this may block while connecting. */ static int upgradeOss(MprSocket *sp, MprSsl *ssl, cchar *requiredPeerName) { OpenSocket *osp; OpenConfig *cfg; int rc; assert(sp); if (ssl == 0) { ssl = mprCreateSsl(sp->flags & MPR_SOCKET_SERVER); } if ((osp = (OpenSocket*) mprAllocObj(OpenSocket, manageOpenSocket)) == 0) { return MPR_ERR_MEMORY; } osp->sock = sp; sp->sslSocket = osp; sp->ssl = ssl; lock(ssl); if (configOss(ssl, sp->flags, &sp->errorMsg) < 0) { unlock(ssl); return MPR_ERR_CANT_INITIALIZE; } unlock(ssl); /* Create and configure the SSL struct */ cfg = osp->cfg = sp->ssl->config; if ((osp->handle = (SSL*) SSL_new(cfg->ctx)) == 0) { return MPR_ERR_BAD_STATE; } SSL_set_app_data(osp->handle, (void*) osp); /* Create a socket bio. We don't use the BIO except as storage for the fd */ if ((osp->bio = BIO_new_socket((int) sp->fd, BIO_NOCLOSE)) == 0) { return MPR_ERR_BAD_STATE; } SSL_set_bio(osp->handle, osp->bio, osp->bio); if (sp->flags & MPR_SOCKET_SERVER) { SSL_set_accept_state(osp->handle); } else { if (requiredPeerName) { osp->requiredPeerName = sclone(requiredPeerName); #if OPENSSL_VERSION_NUMBER >= 0x10002000L X509_VERIFY_PARAM *param = param = SSL_get0_param(osp->handle); X509_VERIFY_PARAM_set_hostflags(param, 0); X509_VERIFY_PARAM_set1_host(param, requiredPeerName, 0); #endif } /* Block while connecting */ mprSetSocketBlockingMode(sp, 1); sp->errorMsg = 0; if ((rc = SSL_connect(osp->handle)) < 1) { if (sp->errorMsg) { mprLog("info mpr ssl openssl", 4, "Connect failed: %s", sp->errorMsg); } else { mprLog("info mpr ssl openssl", 4, "Connect failed: error %s", getOssError(sp)); } return MPR_ERR_CANT_CONNECT; } if (rc > 0 && checkPeerCertName(sp) < 0) { return MPR_ERR_CANT_CONNECT; } setSecured(sp); mprSetSocketBlockingMode(sp, 0); } #if defined(ME_MPR_SSL_RENEGOTIATE) && !ME_MPR_SSL_RENEGOTIATE /* Disable renegotiation after the initial handshake if renegotiate is explicitly set to false (CVE-2009-3555). Note: this really is a bogus CVE as disabling renegotiation is not required nor does it enhance security if used with up-to-date (patched) SSL stacks. */ if (osp->handle->s3) { osp->handle->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; } #endif return 0; }
/* Create and initialize an SSL configuration for a route. This configuration is used by all requests for a given route. An application can have different SSL configurations for different routes. There is also a default SSL configuration that is used when a route does not define a configuration and one for clients. */ static OpenConfig *createOpenSslConfig(MprSocket *sp) { MprSsl *ssl; OpenConfig *cfg; X509_STORE *store; SSL_CTX *context; cchar *key; uchar resume[16]; int verifyMode; ssl = sp->ssl; assert(ssl); if ((ssl->config = mprAllocObj(OpenConfig, manageOpenConfig)) == 0) { return 0; } cfg = ssl->config; if ((context = SSL_CTX_new(SSLv23_method())) == 0) { mprLog("error openssl", 0, "Unable to create SSL context"); return 0; } SSL_CTX_set_app_data(context, (void*) ssl); if (ssl->verifyPeer && !(ssl->caFile || ssl->caPath)) { sp->errorMsg = sfmt("Cannot verify peer due to undefined CA certificates"); SSL_CTX_free(context); return 0; } /* Configure the certificates */ if (ssl->certFile) { if (SSL_CTX_use_certificate_chain_file(context, ssl->certFile) <= 0) { if (SSL_CTX_use_certificate_file(context, ssl->certFile, SSL_FILETYPE_ASN1) <= 0) { mprLog("error openssl", 0, "Cannot open certificate file: %s", ssl->certFile); SSL_CTX_free(context); return 0; } } key = (ssl->keyFile == 0) ? ssl->certFile : ssl->keyFile; if (key) { if (SSL_CTX_use_PrivateKey_file(context, key, SSL_FILETYPE_PEM) <= 0) { /* attempt ASN1 for self-signed format */ if (SSL_CTX_use_PrivateKey_file(context, key, SSL_FILETYPE_ASN1) <= 0) { mprLog("error openssl", 0, "Cannot open private key file: %s", key); SSL_CTX_free(context); return 0; } } if (!SSL_CTX_check_private_key(context)) { mprLog("error openssl", 0, "Check of private key file failed: %s", key); SSL_CTX_free(context); return 0; } } } if (ssl->ciphers) { ssl->ciphers = mapCipherNames(ssl->ciphers); } if (!ssl->ciphers && (sp->flags & MPR_SOCKET_SERVER)) { ssl->ciphers = sclone(OPENSSL_DEFAULT_CIPHERS); } if (ssl->ciphers) { mprLog("info openssl", 5, "Using SSL ciphers: %s", ssl->ciphers); if (SSL_CTX_set_cipher_list(context, ssl->ciphers) != 1) { sp->errorMsg = sfmt("Unable to set cipher list \"%s\". %s", ssl->ciphers, getOssError(sp)); SSL_CTX_free(context); return 0; } } verifyMode = ssl->verifyPeer ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE; if (verifyMode != SSL_VERIFY_NONE) { if (!(ssl->caFile || ssl->caPath)) { sp->errorMsg = sclone("No defined certificate authority file"); SSL_CTX_free(context); return 0; } if ((!SSL_CTX_load_verify_locations(context, (char*) ssl->caFile, (char*) ssl->caPath)) || (!SSL_CTX_set_default_verify_paths(context))) { sp->errorMsg = sfmt("Unable to set certificate locations: %s: %s", ssl->caFile, ssl->caPath); SSL_CTX_free(context); return 0; } if (ssl->caFile) { STACK_OF(X509_NAME) *certNames; certNames = SSL_load_client_CA_file(ssl->caFile); if (certNames) { /* Define the list of CA certificates to send to the client before they send their client certificate for validation */ SSL_CTX_set_client_CA_list(context, certNames); } } store = SSL_CTX_get_cert_store(context); if (ssl->revokeList && !X509_STORE_load_locations(store, ssl->revokeList, 0)) { mprLog("error openssl", 0, "Cannot load certificate revoke list: %s", ssl->revokeList); SSL_CTX_free(context); return 0; } if (sp->flags & MPR_SOCKET_SERVER) { SSL_CTX_set_verify_depth(context, ssl->verifyDepth); } } /* Define callbacks */ SSL_CTX_set_verify(context, verifyMode, verifyPeerCertificate); /* Configure DH parameters */ SSL_CTX_set_tmp_dh_callback(context, dhcallback); cfg->dhKey = getDhKey(); /* Define default OpenSSL options */ SSL_CTX_set_options(context, SSL_OP_ALL); /* Ensure we generate a new private key for each connection */ SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE); /* Define a session reuse context */ RAND_bytes(resume, sizeof(resume)); SSL_CTX_set_session_id_context(context, resume, sizeof(resume)); /* Elliptic Curve initialization */ #if SSL_OP_SINGLE_ECDH_USE #ifdef SSL_CTX_set_ecdh_auto SSL_CTX_set_ecdh_auto(context, 1); #else { EC_KEY *ecdh; cchar *name; int nid; name = ME_MPR_SSL_CURVE; if ((nid = OBJ_sn2nid(name)) == 0) { sp->errorMsg = sfmt("Unknown curve name \"%s\"", name); SSL_CTX_free(context); return 0; } if ((ecdh = EC_KEY_new_by_curve_name(nid)) == 0) { sp->errorMsg = sfmt("Unable to create curve \"%s\"", name); SSL_CTX_free(context); return 0; } SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_tmp_ecdh(context, ecdh); EC_KEY_free(ecdh); } #endif #endif SSL_CTX_set_mode(context, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING SSL_CTX_set_options(context, SSL_OP_MSIE_SSLV2_RSA_PADDING); #endif #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode(context, SSL_MODE_RELEASE_BUFFERS); #endif #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE SSL_CTX_set_mode(context, SSL_OP_CIPHER_SERVER_PREFERENCE); #endif /* Select the required protocols Disable SSLv2 and SSLv3 by default -- they are insecure. */ SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); SSL_CTX_set_options(context, SSL_OP_NO_SSLv3); #ifdef SSL_OP_NO_TLSv1 if (!(ssl->protocols & MPR_PROTO_TLSV1)) { SSL_CTX_set_options(context, SSL_OP_NO_TLSv1); } #endif #ifdef SSL_OP_NO_TLSv1_1 if (!(ssl->protocols & MPR_PROTO_TLSV1_1)) { SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_1); } #endif #ifdef SSL_OP_NO_TLSv1_2 if (!(ssl->protocols & MPR_PROTO_TLSV1_2)) { SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_2); } #endif /* Options set via main.me mpr.ssl.* */ #if defined(SSL_OP_NO_TICKET) /* Ticket based session reuse is enabled by default */ #if defined(ME_MPR_SSL_TICKET) if (ME_MPR_SSL_TICKET) { SSL_CTX_clear_options(context, SSL_OP_NO_TICKET); } else { SSL_CTX_set_options(context, SSL_OP_NO_TICKET); } #else SSL_CTX_clear_options(context, SSL_OP_NO_TICKET); #endif #endif #if defined(SSL_OP_NO_COMPRESSION) /* Use of compression is not secure. Disabled by default. */ #if defined(ME_MPR_SSL_COMPRESSION) if (ME_MPR_SSL_COMPRESSION) { SSL_CTX_clear_options(context, SSL_OP_NO_COMPRESSION); } else { SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION); } #else /* CRIME attack targets compression */ SSL_CTX_clear_options(context, SSL_OP_NO_COMPRESSION); #endif #endif #if defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) /* Force a new session on renegotiation. Default to true. */ #if defined(ME_MPR_SSL_RENEGOTIATE) if (ME_MPR_SSL_RENEGOTIATE) { SSL_CTX_clear_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); } else { SSL_CTX_set_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); } #else SSL_CTX_set_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #endif #endif #if defined(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) /* Disables a countermeasure against a SSL 3.0/TLS 1.0 protocol vulnerability affecting CBC ciphers. Defaults to true. */ #if defined(ME_MPR_SSL_EMPTY_FRAGMENTS) if (ME_MPR_SSL_EMPTY_FRAGMENTS) { /* SSL_OP_ALL disables empty fragments. Only needed for ancient browsers like IE-6 on SSL-3.0/TLS-1.0 */ SSL_CTX_clear_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); } else { SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); } #else SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); #endif #endif #if defined(ME_MPR_SSL_CACHE) /* Set the number of sessions supported. Default in OpenSSL is 20K. */ SSL_CTX_sess_set_cache_size(context, ME_MPR_SSL_CACHE); #else SSL_CTX_sess_set_cache_size(context, 1024); #endif cfg->context = context; return cfg; }