@@ -244,7 +244,7 @@ class Socket
244244 bool isConnected ()const ;
245245 void setDebugConnect (bool d);
246246
247- void setSSLConfig_OpenSSL (bool force_ssl, int certverify, const std::string& ca_path, const std::string& ca_file, const std::string& cert_file, const std::string& key_file, const std::string& key_pass);
247+ void setSSLConfig_OpenSSL (bool force_ssl, int certverify, const std::string& ca_path, const std::string& ca_file, const std::string& cert_file, const std::string& key_file, const std::string& key_pass, const std::string& certident_name );
248248 void setSSLConfig_NSS (bool force_ssl, int certverify, const std::string& certstore_path, const std::string& certstore_pass, const std::string& certstore_prefix, const std::string& certhost_name, const std::string& certident_name);
249249
250250 void startTLS ();
@@ -320,13 +320,28 @@ SSL_CTX* Socket::_ssl_ctx = nullptr;
320320
321321/* static*/ int Socket::openssl_password_callback (char *buf, int size, int rwflag, void *userdata) /* pem_passwd_cb, 1.1.0+ */
322322{
323+ /* See https://docs.openssl.org/1.0.2/man3/SSL_CTX_set_default_passwd_cb */
324+ /* is callback used for reading/decryption (rwflag=0) or writing/encryption (rwflag=1)? */
323325 NUT_UNUSED_VARIABLE (rwflag);
326+ /* "userdata" is generally the user-provided password, possibly cached
327+ * from an earlier loop (e.g. to check interactively typing it twice,
328+ * or to probe several items in a loop). */
324329
325- if (size < 1 || !userdata || !*(static_cast <char *>(userdata))) {
330+ if (!buf || size < 1 ) {
331+ /* Can not even set buf[0] */
332+ return 0 ;
333+ }
334+
335+ if (!userdata || !*(static_cast <char *>(userdata))) {
326336 buf[0 ] = ' \0 ' ;
327337 return 0 ;
328338 }
329339
340+ if (strlen ((char *)userdata) >= (size_t )size) {
341+ /* Do not return truncated trash, just say we could not do it */
342+ return 0 ;
343+ }
344+
330345 strncpy (buf, static_cast <char *>(userdata), static_cast <size_t >(size));
331346 buf[size - 1 ] = ' \0 ' ;
332347 return static_cast <int >(strlen (buf));
@@ -861,7 +876,7 @@ bool Socket::isSSL()const
861876#endif
862877}
863878
864- void Socket::setSSLConfig_OpenSSL (bool force_ssl, int certverify, const std::string& ca_path, const std::string& ca_file, const std::string& cert_file, const std::string& key_file, const std::string& key_pass)
879+ void Socket::setSSLConfig_OpenSSL (bool force_ssl, int certverify, const std::string& ca_path, const std::string& ca_file, const std::string& cert_file, const std::string& key_file, const std::string& key_pass, const std::string& certident_name )
865880{
866881 _force_ssl = force_ssl;
867882
@@ -875,6 +890,7 @@ void Socket::setSSLConfig_OpenSSL(bool force_ssl, int certverify, const std::str
875890 _cert_file = cert_file;
876891 _key_file = key_file;
877892 _key_pass = key_pass;
893+ _certident_name = certident_name;
878894
879895 _ssl_configured |= UPSCLI_SSL_CAPS_OPENSSL;
880896#else
@@ -884,6 +900,7 @@ void Socket::setSSLConfig_OpenSSL(bool force_ssl, int certverify, const std::str
884900 NUT_UNUSED_VARIABLE (cert_file);
885901 NUT_UNUSED_VARIABLE (key_file);
886902 NUT_UNUSED_VARIABLE (key_pass);
903+ NUT_UNUSED_VARIABLE (certident_name);
887904
888905 _ssl_configured &= ~UPSCLI_SSL_CAPS_OPENSSL;
889906#endif
@@ -978,7 +995,12 @@ void Socket::startTLS()
978995
979996 if (!_ca_file.empty () || !_ca_path.empty ()) {
980997 if (SSL_CTX_load_verify_locations (_ssl_ctx, _ca_file.empty () ? nullptr : _ca_file.c_str (), _ca_path.empty () ? nullptr : _ca_path.c_str ()) != 1 ) {
981- throw nut::SSLException_OpenSSL (" Failed to load CA verify locations" );
998+ if (!(_ca_file.empty () && !_ca_path.empty ()
999+ /* Retry in case CERTPATH pointed to PEM file */
1000+ && SSL_CTX_load_verify_locations (_ssl_ctx, _ca_path.c_str (), nullptr ) == 1 )
1001+ ) {
1002+ throw nut::SSLException_OpenSSL (" Failed to load CA verify locations" );
1003+ }
9821004 }
9831005 }
9841006 if (_certverify != -1 ) {
@@ -990,6 +1012,15 @@ void Socket::startTLS()
9901012 }
9911013 if (!_key_pass.empty ()) {
9921014# if OPENSSL_VERSION_NUMBER < 0x10100000L
1015+ /* Per https://docs.openssl.org/3.5/man3/SSL_CTX_set_default_passwd_cb,
1016+ * the `SSL_CTX*` variants were added in 1.1.
1017+ * The SSL_set_default_passwd_cb() and SSL_set_default_passwd_cb_userdata()
1018+ * for `SSL*` argument were around since the turn of millennium, approx 0.9.6+
1019+ * per https://github.com/openssl/openssl/commit/66ebbb6a56bc1688fa37878e4feec985b0c260d7
1020+ *
1021+ * But to use those, we would need to get that SSL* (maybe from socket FD?);
1022+ * that would also unlock us using the ssl_error() elsewhere.
1023+ */
9931024 throw nut::SSLException_OpenSSL (" Private key password support not implemented for OpenSSL < 1.1 yet" );
9941025# else
9951026 /* OpenSSL 1.1.0+
@@ -1004,6 +1035,59 @@ void Socket::startTLS()
10041035 if (SSL_CTX_use_PrivateKey_file (_ssl_ctx, _key_file.empty () ? _cert_file.c_str () : _key_file.c_str (), SSL_FILETYPE_PEM) != 1 ) {
10051036 throw nut::SSLException_OpenSSL (" Failed to load client private key file" );
10061037 }
1038+
1039+ if (!_certident_name.empty ()) {
1040+ # if OPENSSL_VERSION_NUMBER >= 0x10002000L
1041+ X509 *x509 = SSL_CTX_get0_certificate (_ssl_ctx);
1042+ if (x509) {
1043+ /* Check if _certident_name matches the host (CN or SAN) */
1044+ if (X509_check_host (x509, _certident_name.c_str (), 0 , 0 , nullptr ) != 1
1045+ && X509_check_ip_asc (x509, _certident_name.c_str (), 0 ) != 1
1046+ ) {
1047+ char *subject = X509_NAME_oneline (X509_get_subject_name (x509), nullptr , 0 );
1048+ char *subject_CN = (subject ? static_cast <char *>(strstr (subject, " CN=" )) + 3 : nullptr );
1049+ size_t certident_len = _certident_name.length ();
1050+
1051+ if (_debugConnect) std::cerr <<
1052+ " [D4] Socket::startTLS(): My certificate subject: '" << (subject ? subject : " unknown" ) <<
1053+ " '; CN: '" << (subject_CN ? subject_CN : " unknown" ) <<
1054+ " '; CERTIDENT: [" << certident_len << " ]'" << _certident_name << " '" <<
1055+ std::endl << std::flush;
1056+
1057+ /* Check if _certident_name matches the whole subject or just .../CN=.../ part as a string */
1058+ if (!subject || !(
1059+ strcmp (subject, _certident_name.c_str ()) == 0
1060+ || (subject_CN && !strncmp (subject_CN, _certident_name.c_str (), certident_len)
1061+ && (subject_CN[certident_len] == ' \0 ' || subject_CN[certident_len] == ' /' ) )
1062+ )) {
1063+ /* This way or that, the names differ */
1064+ std::string err = " Certificate subject (" + std::string (subject ? subject : " unknown" ) + " ) does not match CERTIDENT name (" + _certident_name + " )" ;
1065+ if (subject) {
1066+ OPENSSL_free (subject);
1067+ }
1068+ throw nut::SSLException_OpenSSL (err);
1069+ } else {
1070+ if (_debugConnect) std::cerr <<
1071+ " [D2] Socket::startTLS(): Certificate subject verified against CERTIDENT subject name (" << _certident_name << " )" <<
1072+ std::endl << std::flush;
1073+ }
1074+ if (subject) {
1075+ OPENSSL_free (subject);
1076+ }
1077+ } else {
1078+ if (_debugConnect) std::cerr <<
1079+ " [D2] Socket::startTLS(): Certificate subject verified against CERTIDENT host name (" << _certident_name << " )" <<
1080+ std::endl << std::flush;
1081+ }
1082+ }
1083+ # else
1084+ throw nut::SSLException_OpenSSL (" Can not verify CERTIDENT '" + _certident_name + " ': not supported in this OpenSSL build (too old)" );
1085+ # endif
1086+ }
1087+ } else {
1088+ if (!_certident_name.empty ()) {
1089+ throw nut::SSLException_OpenSSL (" Can not verify CERTIDENT '" + _certident_name + " ': no cert_file was provided" );
1090+ }
10071091 }
10081092
10091093 _ssl = SSL_new (_ssl_ctx);
@@ -1419,7 +1503,7 @@ void SSLConfig::apply(TcpClient& client) const
14191503
14201504void SSLConfig_OpenSSL::apply (TcpClient& client) const
14211505{
1422- client.setSSLConfig_OpenSSL (_force_ssl, _certverify, _ca_path, _ca_file, _cert_file, _key_file, _key_pass);
1506+ client.setSSLConfig_OpenSSL (_force_ssl, _certverify, _ca_path, _ca_file, _cert_file, _key_file, _key_pass, _certident_name );
14231507}
14241508
14251509void SSLConfig_NSS::apply (TcpClient& client) const
@@ -1432,7 +1516,7 @@ void TcpClient::setSSLConfig(const SSLConfig& config)
14321516 config.apply (*this );
14331517}
14341518
1435- void TcpClient::setSSLConfig_OpenSSL (bool force_ssl, int certverify, const char *ca_path, const char *ca_file, const char *cert_file, const char *key_file, const char *key_pass)
1519+ void TcpClient::setSSLConfig_OpenSSL (bool force_ssl, int certverify, const char *ca_path, const char *ca_file, const char *cert_file, const char *key_file, const char *key_pass, const char *certident_name )
14361520{
14371521 _force_ssl = force_ssl;
14381522 _certverify = certverify;
@@ -1441,9 +1525,10 @@ void TcpClient::setSSLConfig_OpenSSL(bool force_ssl, int certverify, const char
14411525 if (cert_file) _cert_file = cert_file;
14421526 if (key_file) _key_file = key_file;
14431527 if (key_pass) _key_pass = key_pass;
1528+ if (certident_name) _certident_name = certident_name;
14441529}
14451530
1446- void TcpClient::setSSLConfig_OpenSSL (bool force_ssl, int certverify, const std::string& ca_path, const std::string& ca_file, const std::string& cert_file, const std::string& key_file, const std::string& key_pass)
1531+ void TcpClient::setSSLConfig_OpenSSL (bool force_ssl, int certverify, const std::string& ca_path, const std::string& ca_file, const std::string& cert_file, const std::string& key_file, const std::string& key_pass, const std::string& certident_name )
14471532{
14481533 _force_ssl = force_ssl;
14491534 _certverify = certverify;
@@ -1452,6 +1537,7 @@ void TcpClient::setSSLConfig_OpenSSL(bool force_ssl, int certverify, const std::
14521537 _cert_file = cert_file;
14531538 _key_file = key_file;
14541539 _key_pass = key_pass;
1540+ _certident_name = certident_name;
14551541}
14561542
14571543void TcpClient::setSSLConfig_NSS (bool force_ssl, int certverify, const char *certstore_path, const char *certstore_pass, const char *certstore_prefix, const char *certhost_name, const char *certident_name)
@@ -1493,8 +1579,8 @@ void TcpClient::connect()
14931579{
14941580 _socket->connect (_host, _port);
14951581 if (_try_ssl || _force_ssl) {
1496- if (!_ca_path.empty () || !_ca_file.empty () || !_cert_file.empty () || !_key_file.empty ()) {
1497- _socket->setSSLConfig_OpenSSL (_force_ssl, _certverify, _ca_path, _ca_file, _cert_file, _key_file, _key_pass);
1582+ if (!_ca_path.empty () || !_ca_file.empty () || !_cert_file.empty () || !_key_file.empty () || !_certident_name. empty () ) {
1583+ _socket->setSSLConfig_OpenSSL (_force_ssl, _certverify, _ca_path, _ca_file, _cert_file, _key_file, _key_pass, _certident_name );
14981584 } else if (!_certstore_path.empty () || !_certstore_prefix.empty () || !_certhost_name.empty () || !_certident_name.empty ()) {
14991585 _socket->setSSLConfig_NSS (_force_ssl, _certverify, _certstore_path, _key_pass, _certstore_prefix, _certhost_name, _certident_name);
15001586 }
@@ -2931,13 +3017,13 @@ NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, uint16_t port)
29313017
29323018int nutclient_tcp_get_ssl_caps (void ) { return nut::TcpClient::getSslCaps (); }
29333019
2934- NUTCLIENT_TCP_t nutclient_tcp_create_client_ssl_OpenSSL (const char * host, uint16_t port, int try_ssl, int force_ssl, int certverify, const char *ca_path, const char *ca_file, const char *cert_file, const char *key_file, const char *key_pass)
3020+ NUTCLIENT_TCP_t nutclient_tcp_create_client_ssl_OpenSSL (const char * host, uint16_t port, int try_ssl, int force_ssl, int certverify, const char *ca_path, const char *ca_file, const char *cert_file, const char *key_file, const char *key_pass, const char *certident_name )
29353021{
29363022 nut::TcpClient* client = new nut::TcpClient;
29373023 try
29383024 {
29393025 client->setSSLConfig (nut::SSLConfig_OpenSSL (
2940- (force_ssl > 0 ), certverify, ca_path, ca_file, cert_file, key_file, key_pass
3026+ (force_ssl > 0 ), certverify, ca_path, ca_file, cert_file, key_file, key_pass, certident_name
29413027 ));
29423028 client->connect (host, port, try_ssl != 0 );
29433029 return static_cast <NUTCLIENT_TCP_t>(client);
@@ -2950,15 +3036,15 @@ NUTCLIENT_TCP_t nutclient_tcp_create_client_ssl_OpenSSL(const char* host, uint16
29503036 }
29513037}
29523038
2953- void nutclient_tcp_set_ssl_config_OpenSSL (NUTCLIENT_TCP_t client, int force_ssl, int certverify, const char *ca_path, const char *ca_file, const char *cert_file, const char *key_file, const char *key_pass)
3039+ void nutclient_tcp_set_ssl_config_OpenSSL (NUTCLIENT_TCP_t client, int force_ssl, int certverify, const char *ca_path, const char *ca_file, const char *cert_file, const char *key_file, const char *key_pass, const char *certident_name )
29543040{
29553041 if (client)
29563042 {
29573043 nut::TcpClient* cl = dynamic_cast <nut::TcpClient*>(static_cast <nut::Client*>(client));
29583044 if (cl)
29593045 {
29603046 cl->setSSLConfig (nut::SSLConfig_OpenSSL ((
2961- force_ssl > 0 ), certverify, ca_path, ca_file, cert_file, key_file, key_pass
3047+ force_ssl > 0 ), certverify, ca_path, ca_file, cert_file, key_file, key_pass, certident_name
29623048 ));
29633049 }
29643050 }
0 commit comments