nut-debian/server/netssl.c

736 lines
20 KiB
C
Raw Normal View History

2012-08-13 00:39:31 +03:00
/* netssl.c - Interface to OpenSSL for upsd
2010-03-26 01:20:59 +02:00
Copyright (C)
2002 Russell Kroll <rkroll@exploits.org>
2008 Arjen de Korte <adkorte-guest@alioth.debian.org>
based on the original implementation:
Copyright (C) 2002 Technorama Ltd. <oss-list-ups@technorama.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "upsd.h"
#include "neterr.h"
2012-08-13 00:39:31 +03:00
#include "netssl.h"
2022-07-10 10:23:45 +03:00
#include "nut_stdint.h"
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
#ifdef WITH_NSS
#include <pk11pub.h>
#include <prinit.h>
#include <private/pprio.h>
2022-07-10 10:23:45 +03:00
#if defined(NSS_VMAJOR) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && defined(NSS_VMINOR) && NSS_VMINOR >= 39))
#include <keyhi.h>
#include <keythi.h>
#else
2013-11-24 17:00:12 +02:00
#include <key.h>
#include <keyt.h>
2022-07-10 10:23:45 +03:00
#endif /* NSS before 3.39 */
2013-11-24 17:00:12 +02:00
#include <secerr.h>
#include <sslerr.h>
2022-07-10 10:23:45 +03:00
#include <sslproto.h>
2013-11-24 17:00:12 +02:00
#endif /* WITH_NSS */
2010-03-26 01:20:59 +02:00
char *certfile = NULL;
2013-11-24 17:00:12 +02:00
char *certname = NULL;
char *certpasswd = NULL;
2022-07-10 10:23:45 +03:00
/* Warning: in this release of NUT, this feature is disabled by default
* in order to retain compatibility with "least surprise" for earlier
* existing deployments. Over time it can become enabled by default.
* See upsd.conf option DISABLE_WEAK_SSL to toggle this in-vivo.
*/
int disable_weak_ssl = 0;
2013-11-24 17:00:12 +02:00
#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION
int certrequest = 0;
#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */
2010-03-26 01:20:59 +02:00
static int ssl_initialized = 0;
2013-11-24 17:00:12 +02:00
#ifndef WITH_SSL
2010-03-26 01:20:59 +02:00
/* stubs for non-ssl compiles */
2022-07-10 10:23:45 +03:00
void net_starttls(nut_ctype_t *client, size_t numarg, const char **arg)
2010-03-26 01:20:59 +02:00
{
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(client);
NUT_UNUSED_VARIABLE(numarg);
NUT_UNUSED_VARIABLE(arg);
2010-03-26 01:20:59 +02:00
send_err(client, NUT_ERR_FEATURE_NOT_SUPPORTED);
return;
}
2022-07-10 10:23:45 +03:00
ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen)
2010-03-26 01:20:59 +02:00
{
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(client);
NUT_UNUSED_VARIABLE(buf);
NUT_UNUSED_VARIABLE(buflen);
2010-03-26 01:20:59 +02:00
upslogx(LOG_ERR, "ssl_write called but SSL wasn't compiled in");
return -1;
}
2022-07-10 10:23:45 +03:00
ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen)
2010-03-26 01:20:59 +02:00
{
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(client);
NUT_UNUSED_VARIABLE(buf);
NUT_UNUSED_VARIABLE(buflen);
2010-03-26 01:20:59 +02:00
upslogx(LOG_ERR, "ssl_read called but SSL wasn't compiled in");
return -1;
}
void ssl_init(void)
{
ssl_initialized = 0; /* keep gcc quiet */
}
2012-01-24 12:22:33 +02:00
void ssl_finish(nut_ctype_t *client)
2010-03-26 01:20:59 +02:00
{
if (client->ssl) {
upslogx(LOG_ERR, "ssl_finish found active SSL connection but SSL wasn't compiled in");
}
}
2013-11-24 17:00:12 +02:00
void ssl_cleanup(void)
{
}
2010-03-26 01:20:59 +02:00
#else
2013-11-24 17:00:12 +02:00
#ifdef WITH_OPENSSL
2010-03-26 01:20:59 +02:00
static SSL_CTX *ssl_ctx = NULL;
static void ssl_debug(void)
{
2022-07-10 10:23:45 +03:00
unsigned long e;
2010-03-26 01:20:59 +02:00
char errmsg[SMALLBUF];
while ((e = ERR_get_error()) != 0) {
ERR_error_string_n(e, errmsg, sizeof(errmsg));
upsdebugx(1, "ssl_debug: %s", errmsg);
}
}
2022-07-10 10:23:45 +03:00
static int ssl_error(SSL *ssl, ssize_t ret)
2013-11-24 17:00:12 +02:00
{
int e;
2022-07-10 10:23:45 +03:00
if (ret >= INT_MAX) {
upslogx(LOG_ERR, "ssl_error() ret=%zd would not fit in an int", ret);
return -1;
}
e = SSL_get_error(ssl, (int)ret);
2013-11-24 17:00:12 +02:00
switch (e)
{
case SSL_ERROR_WANT_READ:
2022-07-10 10:23:45 +03:00
upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_WANT_READ", ret);
2013-11-24 17:00:12 +02:00
break;
case SSL_ERROR_WANT_WRITE:
2022-07-10 10:23:45 +03:00
upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_WANT_WRITE", ret);
2013-11-24 17:00:12 +02:00
break;
case SSL_ERROR_SYSCALL:
if (ret == 0 && ERR_peek_error() == 0) {
upsdebugx(1, "ssl_error() EOF from client");
} else {
2022-07-10 10:23:45 +03:00
upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_SYSCALL", ret);
2013-11-24 17:00:12 +02:00
}
break;
default:
2022-07-10 10:23:45 +03:00
upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR %d", ret, e);
2013-11-24 17:00:12 +02:00
ssl_debug();
}
return -1;
}
#elif defined(WITH_NSS) /* WITH_OPENSSL */
static CERTCertificate *cert;
static SECKEYPrivateKey *privKey;
2022-07-10 10:23:45 +03:00
static char *nss_password_callback(PK11SlotInfo *slot, PRBool retry,
2013-11-24 17:00:12 +02:00
void *arg)
{
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(arg);
2013-11-24 17:00:12 +02:00
if (retry) {
/* Force not inted to retrieve password many times. */
return NULL;
}
upslogx(LOG_INFO, "Intend to retrieve password for %s / %s: password %sconfigured",
PK11_GetSlotName(slot), PK11_GetTokenName(slot), certpasswd?"":"not ");
return certpasswd ? PL_strdup(certpasswd) : NULL;
}
static void nss_error(const char* text)
{
char buffer[SMALLBUF];
PRInt32 length = PR_GetErrorText(buffer);
if (length > 0 && length < SMALLBUF) {
upsdebugx(1, "nss_error %ld in %s : %s", (long)PR_GetError(), text, buffer);
}else{
upsdebugx(1, "nss_error %ld in %s", (long)PR_GetError(), text);
}
}
2022-07-10 10:23:45 +03:00
static int ssl_error(PRFileDesc *ssl, ssize_t ret)
2013-11-24 17:00:12 +02:00
{
char buffer[256];
PRInt32 length;
PRErrorCode e;
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(ssl);
NUT_UNUSED_VARIABLE(ret);
2013-11-24 17:00:12 +02:00
e = PR_GetError();
length = PR_GetErrorText(buffer);
if (length > 0 && length < 256) {
upsdebugx(1, "ssl_error() ret=%d %*s", e, length, buffer);
} else {
upsdebugx(1, "ssl_error() ret=%d", e);
}
return -1;
}
static SECStatus AuthCertificate(CERTCertDBHandle *arg, PRFileDesc *fd,
PRBool checksig, PRBool isServer)
{
nut_ctype_t *client = (nut_ctype_t *)SSL_RevealPinArg(fd);
SECStatus status = SSL_AuthCertificate(arg, fd, checksig, isServer);
upslogx(LOG_INFO, "Intend to authenticate client %s : %s.",
client?client->addr:"(unnamed)",
status==SECSuccess?"SUCCESS":"FAILED");
return status;
}
static SECStatus BadCertHandler(nut_ctype_t *arg, PRFileDesc *fd)
{
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(fd);
2013-11-24 17:00:12 +02:00
upslogx(LOG_WARNING, "Certificate validation failed for %s",
(arg&&arg->addr)?arg->addr:"<unnamed>");
#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION
/* BadCertHandler is called when the NSS certificate validation is failed.
* If the certificate verification (user conf) is mandatory, reject authentication
* else accept it.
2022-07-10 10:23:45 +03:00
*/
2013-11-24 17:00:12 +02:00
return certrequest==NETSSL_CERTREQ_REQUIRE?SECFailure:SECSuccess;
#else /* WITH_CLIENT_CERTIFICATE_VALIDATION */
/* Always accept clients. */
return SECSuccess;
#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */
}
static void HandshakeCallback(PRFileDesc *fd, nut_ctype_t *client_data)
{
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(fd);
2013-11-24 17:00:12 +02:00
upslogx(LOG_INFO, "SSL handshake done successfully with client %s",
client_data->addr);
}
#endif /* WITH_OPENSSL | WITH_NSS */
2022-07-10 10:23:45 +03:00
void net_starttls(nut_ctype_t *client, size_t numarg, const char **arg)
2010-03-26 01:20:59 +02:00
{
2013-11-24 17:00:12 +02:00
#ifdef WITH_OPENSSL
int ret;
#elif defined(WITH_NSS) /* WITH_OPENSSL */
SECStatus status;
PRFileDesc *socket;
#endif /* WITH_OPENSSL | WITH_NSS */
2022-07-10 10:23:45 +03:00
NUT_UNUSED_VARIABLE(numarg);
NUT_UNUSED_VARIABLE(arg);
2010-03-26 01:20:59 +02:00
if (client->ssl) {
send_err(client, NUT_ERR_ALREADY_SSL_MODE);
return;
}
2013-11-24 17:00:12 +02:00
client->ssl_connected = 0;
if ((!certfile) || (!ssl_initialized)) {
2010-03-26 01:20:59 +02:00
send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED);
return;
}
2022-07-10 10:23:45 +03:00
#ifdef WITH_OPENSSL
if (!ssl_ctx)
2013-11-24 17:00:12 +02:00
#elif defined(WITH_NSS) /* WITH_OPENSSL */
2022-07-10 10:23:45 +03:00
if (!NSS_IsInitialized())
2013-11-24 17:00:12 +02:00
#endif /* WITH_OPENSSL | WITH_NSS */
2022-07-10 10:23:45 +03:00
{
2013-11-24 17:00:12 +02:00
send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED);
ssl_initialized = 0;
return;
}
2022-07-10 10:23:45 +03:00
2010-03-26 01:20:59 +02:00
if (!sendback(client, "OK STARTTLS\n")) {
return;
}
2022-07-10 10:23:45 +03:00
#ifdef WITH_OPENSSL
2013-11-24 17:00:12 +02:00
2010-03-26 01:20:59 +02:00
client->ssl = SSL_new(ssl_ctx);
if (!client->ssl) {
upslog_with_errno(LOG_ERR, "SSL_new failed\n");
ssl_debug();
return;
}
if (SSL_set_fd(client->ssl, client->sock_fd) != 1) {
upslog_with_errno(LOG_ERR, "SSL_set_fd failed\n");
ssl_debug();
2013-11-24 17:00:12 +02:00
return;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
ret = SSL_accept(client->ssl);
switch (ret)
{
case 1:
client->ssl_connected = 1;
2022-07-10 10:23:45 +03:00
upsdebugx(3, "SSL connected (%s)", SSL_get_version(client->ssl));
2013-11-24 17:00:12 +02:00
break;
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
case 0:
upslog_with_errno(LOG_ERR, "SSL_accept do not accept handshake.");
ssl_error(client->ssl, ret);
break;
case -1:
upslog_with_errno(LOG_ERR, "Unknown return value from SSL_accept");
ssl_error(client->ssl, ret);
break;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
#elif defined(WITH_NSS) /* WITH_OPENSSL */
socket = PR_ImportTCPSocket(client->sock_fd);
if (socket == NULL){
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / PR_ImportTCPSocket");
return;
}
client->ssl = SSL_ImportFD(NULL, socket);
if (client->ssl == NULL){
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / SSL_ImportFD");
return;
2010-03-26 01:20:59 +02:00
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
if (SSL_SetPKCS11PinArg(client->ssl, client) == -1){
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / SSL_SetPKCS11PinArg");
return;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
/* Note cast to SSLAuthCertificate to prevent warning due to
* bad function prototype in NSS.
*/
status = SSL_AuthCertificateHook(client->ssl, (SSLAuthCertificate)AuthCertificate, CERT_GetDefaultCertDB());
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / SSL_AuthCertificateHook");
return;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
status = SSL_BadCertHook(client->ssl, (SSLBadCertHandler)BadCertHandler, client);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / SSL_BadCertHook");
return;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
status = SSL_HandshakeCallback(client->ssl, (SSLHandshakeCallback)HandshakeCallback, client);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / SSL_HandshakeCallback");
return;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
status = SSL_ConfigSecureServer(client->ssl, cert, privKey, NSS_FindCertKEAType(cert));
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / SSL_ConfigSecureServer");
return;
}
status = SSL_ResetHandshake(client->ssl, PR_TRUE);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not inialize SSL connection");
nss_error("net_starttls / SSL_ResetHandshake");
return;
}
/* Note: this call can generate memory leaks not resolvable
* by any release function.
* Probably SSL session key object allocation. */
status = SSL_ForceHandshake(client->ssl);
if (status != SECSuccess) {
PRErrorCode code = PR_GetError();
if (code==SSL_ERROR_NO_CERTIFICATE) {
upslogx(LOG_WARNING, "Client %s do not provide certificate.",
client->addr);
} else {
nss_error("net_starttls / SSL_ForceHandshake");
/* TODO : Close the connection. */
return;
}
}
client->ssl_connected = 1;
#endif /* WITH_OPENSSL | WITH_NSS */
2010-03-26 01:20:59 +02:00
}
void ssl_init(void)
{
2013-11-24 17:00:12 +02:00
#ifdef WITH_NSS
SECStatus status;
2022-07-10 10:23:45 +03:00
#if defined(NSS_VMAJOR) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && defined(NSS_VMINOR) && NSS_VMINOR >= 14))
SSLVersionRange range;
2011-01-26 11:35:08 +02:00
#endif
2022-07-10 10:23:45 +03:00
#endif /* WITH_NSS */
2013-11-24 17:00:12 +02:00
2010-03-26 01:20:59 +02:00
if (!certfile) {
return;
}
check_perms(certfile);
2022-07-10 10:23:45 +03:00
if (!disable_weak_ssl)
upslogx(LOG_WARNING, "Warning: DISABLE_WEAK_SSL is not enabled. Please consider enabling to improve network security.");
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
#ifdef WITH_OPENSSL
2022-07-10 10:23:45 +03:00
#if OPENSSL_VERSION_NUMBER < 0x10100000L
2010-03-26 01:20:59 +02:00
SSL_load_error_strings();
2011-01-26 11:35:08 +02:00
SSL_library_init();
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
#else
ssl_ctx = SSL_CTX_new(TLS_server_method());
#endif
if (!ssl_ctx) {
2011-01-26 11:35:08 +02:00
ssl_debug();
2022-07-10 10:23:45 +03:00
fatalx(EXIT_FAILURE, "SSL_CTX_new failed");
2010-03-26 01:20:59 +02:00
}
2022-07-10 10:23:45 +03:00
SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* set minimum protocol TLSv1 */
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
if (disable_weak_ssl) {
#if defined(SSL_OP_NO_TLSv1_2)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
#elif defined(SSL_OP_NO_TLSv1_1)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1);
#endif
}
#else
if (SSL_CTX_set_min_proto_version(ssl_ctx, disable_weak_ssl ? TLS1_2_VERSION : TLS1_VERSION) != 1) {
2010-03-26 01:20:59 +02:00
ssl_debug();
2022-07-10 10:23:45 +03:00
fatalx(EXIT_FAILURE, "SSL_CTX_set_min_proto_version(TLS1_VERSION)");
2010-03-26 01:20:59 +02:00
}
2022-07-10 10:23:45 +03:00
#endif
2010-03-26 01:20:59 +02:00
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) {
2011-01-26 11:35:08 +02:00
ssl_debug();
fatalx(EXIT_FAILURE, "SSL_CTX_use_certificate_chain_file(%s) failed", certfile);
2010-03-26 01:20:59 +02:00
}
2011-01-26 11:35:08 +02:00
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) != 1) {
ssl_debug();
fatalx(EXIT_FAILURE, "SSL_CTX_use_PrivateKey_file(%s) failed", certfile);
2010-03-26 01:20:59 +02:00
}
2011-01-26 11:35:08 +02:00
if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
ssl_debug();
fatalx(EXIT_FAILURE, "SSL_CTX_check_private_key(%s) failed", certfile);
}
2010-03-26 01:20:59 +02:00
if (SSL_CTX_set_cipher_list(ssl_ctx, "HIGH:@STRENGTH") != 1) {
2011-01-26 11:35:08 +02:00
ssl_debug();
fatalx(EXIT_FAILURE, "SSL_CTX_set_cipher_list failed");
2010-03-26 01:20:59 +02:00
}
2011-01-26 11:35:08 +02:00
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
2010-03-26 01:20:59 +02:00
ssl_initialized = 1;
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
#elif defined(WITH_NSS) /* WITH_OPENSSL */
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
if (!certname || certname[0]==0 ) {
upslogx(LOG_ERR, "The SSL certificate name is not specified.");
return;
}
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
PK11_SetPasswordFunc(nss_password_callback);
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
if (certfile)
/* Note: this call can generate memory leaks not resolvable
* by any release function.
* Probably NSS key module object allocation and
* probably NSS key db object allocation too. */
status = NSS_Init(certfile);
else
status = NSS_NoDB_Init(NULL);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not initialize SSL context");
2022-07-10 10:23:45 +03:00
nss_error("upscli_init / NSS_[NoDB]_Init");
2013-11-24 17:00:12 +02:00
return;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
status = NSS_SetDomesticPolicy();
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not initialize SSL policy");
nss_error("upscli_init / NSS_SetDomesticPolicy");
return;
}
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
/* Default server cache config */
status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not initialize SSL server cache");
nss_error("upscli_init / SSL_ConfigServerSessionIDCache");
return;
}
2022-07-10 10:23:45 +03:00
if (!disable_weak_ssl) {
status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not enable SSLv3");
nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_SSL3)");
return;
}
status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not enable TLSv1");
nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)");
return;
}
} else {
#if defined(NSS_VMAJOR) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && defined(NSS_VMINOR) && NSS_VMINOR >= 14))
status = SSL_VersionRangeGetSupported(ssl_variant_stream, &range);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not get versions supported");
nss_error("upscli_init / SSL_VersionRangeGetSupported");
return;
}
range.min = SSL_LIBRARY_VERSION_TLS_1_1;
#ifdef SSL_LIBRARY_VERSION_TLS_1_2
range.min = SSL_LIBRARY_VERSION_TLS_1_2;
#endif
status = SSL_VersionRangeSetDefault(ssl_variant_stream, &range);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not set versions supported");
nss_error("upscli_init / SSL_VersionRangeSetDefault");
return;
}
/* Disable old/weak ciphers */
SSL_CipherPrefSetDefault(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, PR_FALSE);
SSL_CipherPrefSetDefault(TLS_RSA_WITH_3DES_EDE_CBC_SHA, PR_FALSE);
SSL_CipherPrefSetDefault(TLS_RSA_WITH_RC4_128_SHA, PR_FALSE);
SSL_CipherPrefSetDefault(TLS_RSA_WITH_RC4_128_MD5, PR_FALSE);
#else
status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not disable SSLv3");
nss_error("upscli_init / SSL_OptionSetDefault(SSL_DISABLE_SSL3)");
return;
}
status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not enable TLSv1");
nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)");
return;
}
#endif
2013-11-24 17:00:12 +02:00
}
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION
if (certrequest < NETSSL_CERTREQ_NO &&
certrequest > NETSSL_CERTREQ_REQUEST) {
upslogx(LOG_ERR, "Invalid certificate requirement");
return;
}
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
if (certrequest == NETSSL_CERTREQ_REQUEST ||
2013-11-24 17:00:12 +02:00
certrequest == NETSSL_CERTREQ_REQUIRE ) {
status = SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE, PR_TRUE);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not enable certificate request");
nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE)");
return;
2010-03-26 01:20:59 +02:00
}
}
2013-11-24 17:00:12 +02:00
if (certrequest == NETSSL_CERTREQ_REQUIRE ) {
status = SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE, PR_TRUE);
if (status != SECSuccess) {
upslogx(LOG_ERR, "Can not enable certificate requirement");
nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE)");
return;
}
}
#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
cert = PK11_FindCertFromNickname(certname, NULL);
if(cert==NULL) {
upslogx(LOG_ERR, "Can not find server certificate");
nss_error("upscli_init / PK11_FindCertFromNickname");
return;
2010-03-26 01:20:59 +02:00
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
privKey = PK11_FindKeyByAnyCert(cert, NULL);
if(privKey==NULL){
upslogx(LOG_ERR, "Can not find private key associate to server certificate");
nss_error("upscli_init / PK11_FindKeyByAnyCert");
return;
}
2022-07-10 10:23:45 +03:00
2013-11-24 17:00:12 +02:00
ssl_initialized = 1;
#else /* WITH_OPENSSL | WITH_NSS */
upslogx(LOG_ERR, "ssl_init called but SSL wasn't compiled in");
#endif /* WITH_OPENSSL | WITH_NSS */
2010-03-26 01:20:59 +02:00
}
2022-07-10 10:23:45 +03:00
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) )
# pragma GCC diagnostic push
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC
# pragma GCC diagnostic ignored "-Wtype-limits"
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC
# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen)
2010-03-26 01:20:59 +02:00
{
2022-07-10 10:23:45 +03:00
ssize_t ret = -1;
2010-03-26 01:20:59 +02:00
if (!client->ssl_connected) {
2013-11-24 17:00:12 +02:00
return -1;
2010-03-26 01:20:59 +02:00
}
2013-11-24 17:00:12 +02:00
#ifdef WITH_OPENSSL
2022-07-10 10:23:45 +03:00
/* SSL_* routines deal with int type for return and buflen
* We might need to window our I/O if we exceed 2GB (in
* 32-bit builds)... Not likely to exceed in 64-bit builds,
* but smaller systems with 16-bits might be endangered :)
*/
assert(buflen <= INT_MAX);
int iret = SSL_read(client->ssl, buf, (int)buflen);
assert(iret <= SSIZE_MAX);
ret = (ssize_t)iret;
2013-11-24 17:00:12 +02:00
#elif defined(WITH_NSS) /* WITH_OPENSSL */
2022-07-10 10:23:45 +03:00
/* PR_* routines deal in PRInt32 type
* We might need to window our I/O if we exceed 2GB :) */
assert(buflen <= PR_INT32_MAX);
ret = PR_Read(client->ssl, buf, (PRInt32)buflen);
2013-11-24 17:00:12 +02:00
#endif /* WITH_OPENSSL | WITH_NSS */
2010-03-26 01:20:59 +02:00
if (ret < 1) {
ssl_error(client->ssl, ret);
return -1;
}
return ret;
}
2022-07-10 10:23:45 +03:00
ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen)
2010-03-26 01:20:59 +02:00
{
2022-07-10 10:23:45 +03:00
ssize_t ret = -1;
2010-03-26 01:20:59 +02:00
2013-11-24 17:00:12 +02:00
if (!client->ssl_connected) {
return -1;
}
#ifdef WITH_OPENSSL
2022-07-10 10:23:45 +03:00
/* SSL_* routines deal with int type for return and buflen
* We might need to window our I/O if we exceed 2GB (in
* 32-bit builds)... Not likely to exceed in 64-bit builds,
* but smaller systems with 16-bits might be endangered :)
*/
assert(buflen <= INT_MAX);
int iret = SSL_write(client->ssl, buf, (int)buflen);
assert(iret <= SSIZE_MAX);
ret = (ssize_t)iret;
2013-11-24 17:00:12 +02:00
#elif defined(WITH_NSS) /* WITH_OPENSSL */
2022-07-10 10:23:45 +03:00
/* PR_* routines deal in PRInt32 type
* We might need to window our I/O if we exceed 2GB :) */
assert(buflen <= PR_INT32_MAX);
ret = PR_Write(client->ssl, buf, (PRInt32)buflen);
2013-11-24 17:00:12 +02:00
#endif /* WITH_OPENSSL | WITH_NSS */
2010-03-26 01:20:59 +02:00
2022-07-10 10:23:45 +03:00
upsdebugx(5, "ssl_write ret=%zd", ret);
2010-03-26 01:20:59 +02:00
return ret;
}
2022-07-10 10:23:45 +03:00
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) )
# pragma GCC diagnostic pop
#endif
2010-03-26 01:20:59 +02:00
2012-01-24 12:22:33 +02:00
void ssl_finish(nut_ctype_t *client)
2010-03-26 01:20:59 +02:00
{
if (client->ssl) {
2013-11-24 17:00:12 +02:00
#ifdef WITH_OPENSSL
2010-03-26 01:20:59 +02:00
SSL_free(client->ssl);
2013-11-24 17:00:12 +02:00
#elif defined(WITH_NSS)
PR_Shutdown(client->ssl, PR_SHUTDOWN_BOTH);
PR_Close(client->ssl);
#endif /* WITH_OPENSSL | WITH_NSS */
client->ssl_connected = 0;
client->ssl = NULL;
}
}
void ssl_cleanup(void)
{
#ifdef WITH_OPENSSL
if (ssl_ctx) {
SSL_CTX_free(ssl_ctx);
ssl_ctx = NULL;
2010-03-26 01:20:59 +02:00
}
2013-11-24 17:00:12 +02:00
#elif defined(WITH_NSS) /* WITH_OPENSSL */
CERT_DestroyCertificate(cert);
SECKEY_DestroyPrivateKey(privKey);
NSS_Shutdown();
PR_Cleanup();
/* Called to release memory arena used by NSS/NSPR.
* Prevent to show all PL_ArenaAllocate mem alloc as leaks.
* https://developer.mozilla.org/en/NSS_Memory_allocation
*/
PL_ArenaFinish();
#endif /* WITH_OPENSSL | WITH_NSS */
ssl_initialized = 0;
2010-03-26 01:20:59 +02:00
}
2013-11-24 17:00:12 +02:00
#endif /* WITH_SSL */