]> arthur.barton.de Git - ngircd.git/blobdiff - src/ngircd/conn-ssl.c
Change leftover GnuTLS "slot handling" messages to debug level
[ngircd.git] / src / ngircd / conn-ssl.c
index 649c0e6257ee53f4da7ec2767c5ae80987b2b3f3..abcf53c2f7dc811b8a6b5200505109e883fa75ed 100644 (file)
@@ -45,6 +45,8 @@ extern struct SSLOptions Conf_SSLOptions;
 #include <openssl/dh.h>
 #include <openssl/x509v3.h>
 
+#define MAX_CERT_CHAIN_LENGTH  10      /* XXX: do not hardcode */
+
 static SSL_CTX * ssl_ctx;
 static DH *dh_params;
 
@@ -52,8 +54,6 @@ static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c ));
 static bool ConnSSL_SetVerifyProperties_openssl PARAMS((SSL_CTX * c));
 #endif
 
-#define MAX_CERT_CHAIN_LENGTH  10      /* XXX: do not hardcode */
-
 #ifdef HAVE_LIBGNUTLS
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -155,13 +155,13 @@ LogOpenSSL_CertInfo(int level, X509 * cert, const char *msg)
        mem = BIO_new(BIO_s_mem());
        if (!mem)
                return;
-       X509_NAME_print_ex(mem, X509_get_subject_name(cert), 4,
+       X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0,
                           XN_FLAG_ONELINE);
-       X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 4, XN_FLAG_ONELINE);
+       X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_ONELINE);
        if (BIO_write(mem, "", 1) == 1) {
                len = BIO_get_mem_data(mem, &memptr);
                if (memptr && len > 0)
-                       Log(level, "%s: \"%s\"", msg, memptr);
+                       Log(level, "%s: \"%s\".", msg, memptr);
        }
        (void)BIO_set_close(mem, BIO_CLOSE);
        BIO_free(mem);
@@ -211,14 +211,23 @@ pem_passwd_cb(char *buf, int size, int rwflag, void *password)
 static int
 Verify_openssl(int preverify_ok, X509_STORE_CTX * ctx)
 {
-       int err;
-
+#ifdef DEBUG
        if (!preverify_ok) {
-               err = X509_STORE_CTX_get_error(ctx);
-               Log(LOG_ERR, "Certificate validation failed: %s",
-                   X509_verify_cert_error_string(err));
+               int err = X509_STORE_CTX_get_error(ctx);
+               LogDebug("Certificate validation failed: %s",
+                        X509_verify_cert_error_string(err));
        }
-       return preverify_ok;
+#else
+       (void)preverify_ok;
+       (void)ctx;
+#endif
+
+       /* Always(!) return success as we have to deal with invalid
+        * (self-signed, expired, ...) client certificates and with invalid
+        * server certificates when "SSLVerify" is disabled, which we don't
+        * know at this stage. Therefore we postpone this check, it will be
+        * (and has to be!) handled in cb_connserver_login_ssl(). */
+       return 1;
 }
 #endif
 
@@ -401,8 +410,6 @@ ConnSSL_InitLibrary( void )
                            SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 |
                            SSL_OP_NO_COMPRESSION);
        SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
-       SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
-                          Verify_openssl);
        SSL_CTX_free(ssl_ctx);
        ssl_ctx = newctx;
        Log(LOG_INFO, "%s initialized.", OpenSSL_version(OPENSSL_VERSION));
@@ -461,6 +468,9 @@ ConnSSL_SetVerifyProperties_gnutls(void)
        if (!Conf_SSLOptions.CAFile)
                return true;
 
+       x509_cred_slot *slot = array_get(&x509_creds, sizeof(x509_cred_slot), x509_cred_idx);
+       gnutls_certificate_credentials_t x509_cred = slot->x509_cred;
+
        err = gnutls_certificate_set_x509_trust_file(x509_cred,
                                                     Conf_SSLOptions.CAFile,
                                                     GNUTLS_X509_FMT_PEM);
@@ -508,6 +518,7 @@ ConnSSL_LoadServerKey_gnutls(void)
                return false;
 
        gnutls_certificate_set_dh_params(x509_cred, dh_params);
+       gnutls_certificate_set_flags(x509_cred, GNUTLS_CERTIFICATE_VERIFY_CRLS);
 
        cert_file = Conf_SSLOptions.CertFile ?
                        Conf_SSLOptions.CertFile : Conf_SSLOptions.KeyFile;
@@ -558,7 +569,7 @@ ConnSSL_LoadServerKey_gnutls(void)
                        return false;
                }
        }
-       Log(LOG_INFO, "Storing new X509 certificate credentials in slot %zd.", x509_cred_idx);
+       LogDebug("Storing new X509 certificate credentials in slot %zd.", x509_cred_idx);
        slot->x509_cred = x509_cred;
        slot->refcnt = 0;
 
@@ -615,7 +626,6 @@ ConnSSL_SetVerifyProperties_openssl(SSL_CTX * ctx)
 {
        X509_STORE *store = NULL;
        X509_LOOKUP *lookup;
-       int verify_flags = SSL_VERIFY_PEER;
        bool ret = false;
 
        if (!Conf_SSLOptions.CAFile)
@@ -649,7 +659,8 @@ ConnSSL_SetVerifyProperties_openssl(SSL_CTX * ctx)
                }
        }
 
-       SSL_CTX_set_verify(ctx, verify_flags, Verify_openssl);
+       SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
+                          Verify_openssl);
        SSL_CTX_set_verify_depth(ctx, MAX_CERT_CHAIN_LENGTH);
        ret = true;
 out:
@@ -737,6 +748,7 @@ ConnSSL_PrepareConnect(CONNECTION * c, CONF_SERVER * s)
 #ifdef HAVE_LIBGNUTLS
        int err;
 
+       (void)s;
        err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_CLIENT);
        if (err) {
                Log(LOG_ERR, "Failed to initialize new SSL session: %s",
@@ -748,26 +760,27 @@ ConnSSL_PrepareConnect(CONNECTION * c, CONF_SERVER * s)
        if (!ret)
                return false;
        Conn_OPTION_ADD(c, CONN_SSL_CONNECT);
+
 #ifdef HAVE_LIBSSL
        assert(c->ssl_state.ssl);
-       if (s->SSLVerify) {
-               X509_VERIFY_PARAM *param = NULL;
-               param = SSL_get0_param(c->ssl_state.ssl);
-               X509_VERIFY_PARAM_set_hostflags(param,
-                                               X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
-Log(LOG_ERR, "DEBUG: Setting up hostname verification for '%s'", s->host);
-               int err = X509_VERIFY_PARAM_set1_host(param, s->host, 0);
-               if (err != 1) {
-                       Log(LOG_ERR,
-                           "Cannot set up hostname verification for '%s': %u",
-                           s->host, err);
-                       return false;
-               }
+
+       X509_VERIFY_PARAM *param = SSL_get0_param(c->ssl_state.ssl);
+       X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+       int err = X509_VERIFY_PARAM_set1_host(param, s->host, 0);
+       if (err != 1) {
+               Log(LOG_ERR,
+                   "Cannot set up hostname verification for '%s': %u",
+                   s->host, err);
+               return false;
+       }
+
+       if (s->SSLVerify)
                SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_PEER,
                               Verify_openssl);
-       else
+       else
                SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_NONE, NULL);
 #endif
+
        return true;
 }
 
@@ -824,9 +837,12 @@ ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname)
                                    "SSL error, client disconnected [in %s()]!",
                                    fname);
                                break;
-                       case -1:        /* low level socket I/O error, check errno */
-                               Log(LOG_ERR, "SSL error: %s [in %s()]!",
-                                   strerror(real_errno), fname);
+                       case -1:
+                               /* Low level socket I/O error, check errno. But
+                                * we don't need to log this here, the generic
+                                * connection layer will take care of it. */
+                               LogDebug("SSL error: %s [in %s()]!",
+                                        strerror(real_errno), fname);
                        }
                }
                break;
@@ -854,8 +870,10 @@ ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname)
        default:
                assert(code < 0);
                if (gnutls_error_is_fatal(code)) {
-                       Log(LOG_ERR, "SSL error: %s [%s].",
-                           gnutls_strerror(code), fname);
+                       /* We don't need to log this here, the generic
+                        * connection layer will take care of it. */
+                       LogDebug("SSL error: %s [%s].",
+                                gnutls_strerror(code), fname);
                        ConnSSL_Free(c);
                        return -1;
                }
@@ -899,12 +917,12 @@ LogGnuTLS_CertInfo(int level, gnutls_x509_crt_t cert, const char *msg)
        assert(size);
        issuer_dn = LogMalloc(size);
        if (!issuer_dn) {
-               Log(level, "%s: Distinguished Name: %s", msg, dn);
+               Log(level, "%s: Distinguished Name \"%s\".", msg, dn);
                free(dn);
                return;
        }
        gnutls_x509_crt_get_issuer_dn(cert, issuer_dn, &size);
-       Log(level, "%s: Distinguished Name: \"%s\", Issuer \"%s\"", msg, dn,
+       Log(level, "%s: Distinguished Name \"%s\", Issuer \"%s\".", msg, dn,
            issuer_dn);
        free(dn);
        free(issuer_dn);
@@ -936,24 +954,36 @@ ConnSSL_LogCertInfo( CONNECTION * c, bool connect)
        Log(LOG_INFO, "Connection %d: initialized %s using cipher %s, %s.",
            c->sock, SSL_get_version(ssl), SSL_get_cipher(ssl), comp_alg);
        peer_cert = SSL_get_peer_certificate(ssl);
-       if (peer_cert && connect) {
+       if (peer_cert) {
                cert_seen = true;
-               /* Client: Check server certificate */
-               int err = SSL_get_verify_result(ssl);
-               if (err == X509_V_OK) {
-                       const char *peername = SSL_get0_peername(ssl);
-                       if (peername != NULL)
-                               cert_ok = true;
-
-                       Log(LOG_ERR, "X509_V_OK, peername = '%s'", peername);
-
-               } else
-                       Log(LOG_ERR, "Certificate validation failed: %s",
-                           X509_verify_cert_error_string(err));
-               snprintf(msg, sizeof(msg), "%svalid peer certificate",
-                        cert_ok ? "" : "in");
-               LogOpenSSL_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, peer_cert,
-                                   msg);
+
+               if (connect) {
+                       /* Outgoing connection. Verify the remote server! */
+                       int err = SSL_get_verify_result(ssl);
+                       if (err == X509_V_OK) {
+                               const char *peername = SSL_get0_peername(ssl);
+                               if (peername != NULL)
+                                       cert_ok = true;
+                               LogDebug("X509_V_OK, peername = '%s'", peername);
+                       } else
+                               Log(LOG_WARNING, "Certificate validation failed: %s!",
+                                   X509_verify_cert_error_string(err));
+
+                       snprintf(msg, sizeof(msg), "Got %svalid server certificate",
+                                cert_ok ? "" : "in");
+                       LogOpenSSL_CertInfo(LOG_INFO, peer_cert, msg);
+               } else {
+                       /* Incoming connection.
+                        * Accept all certificates, don't depend on their
+                        * validity: for example, we don't know the hostname
+                        * to check, because we not yet even know if this is a
+                        * server connection at all and if so, which one, so we
+                        * don't know a host name to look for. On the other
+                        * hand we want client certificates, for example for
+                        * "CertFP" authentication with services ... */
+                       LogOpenSSL_CertInfo(LOG_INFO, peer_cert,
+                                           "Got unchecked peer certificate");
+               }
 
                X509_free(peer_cert);
        }
@@ -970,40 +1000,16 @@ ConnSSL_LogCertInfo( CONNECTION * c, bool connect)
            gnutls_cipher_get_name(cipher),
            gnutls_mac_get_name(gnutls_mac_get(sess)));
        cred = gnutls_auth_get_type(c->ssl_state.gnutls_session);
-       if (cred == GNUTLS_CRD_CERTIFICATE && connect) {
-               cert_seen = true;
-               int verify =
-                   gnutls_certificate_verify_peers2(c->
-                                                    ssl_state.gnutls_session,
-                                                    &status);
-Log(LOG_ERR, "DEBUG: verify = %d", verify);
-               if (verify < 0) {
-                       Log(LOG_ERR,
-                           "gnutls_certificate_verify_peers2 failed: %s",
-                           gnutls_strerror(verify));
-                       goto done_cn_validation;
-               } else if (status) {
-                       gnutls_datum_t out;
-
-                       if (gnutls_certificate_verification_status_print
-                           (status, gnutls_certificate_type_get(sess), &out,
-                            0) == GNUTLS_E_SUCCESS) {
-                               Log(LOG_ERR,
-                                   "Certificate validation failed: %s",
-                                   out.data);
-                               gnutls_free(out.data);
-                       }
-               }
-Log(LOG_ERR, "DEBUG: status = %d", status);
-
+       if (cred == GNUTLS_CRD_CERTIFICATE) {
                gnutls_x509_crt_t cert;
                unsigned cert_list_size;
                const gnutls_datum_t *cert_list =
                    gnutls_certificate_get_peers(sess, &cert_list_size);
-               if (!cert_list || cert_list_size == 0) {
-                       Log(LOG_ERR, "No certificates found");
+
+               if (!cert_list || cert_list_size == 0)
                        goto done_cn_validation;
-               }
+
+               cert_seen = true;
                int err = gnutls_x509_crt_init(&cert);
                if (err < 0) {
                        Log(LOG_ERR,
@@ -1018,17 +1024,46 @@ Log(LOG_ERR, "DEBUG: status = %d", status);
                            gnutls_strerror(err));
                        goto done_cn_validation;
                }
-               err = gnutls_x509_crt_check_hostname(cert, c->host);
-               if (err == 0)
-                       Log(LOG_ERR,
-                           "Failed to verify the hostname, expected \"%s\"",
-                           c->host);
-               else
-                       cert_ok = verify == 0 && status == 0;
 
-               snprintf(msg, sizeof(msg), "%svalid peer certificate",
-                       cert_ok ? "" : "in");
-               LogGnuTLS_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, cert, msg);
+               if (connect) {
+                       int verify =
+                           gnutls_certificate_verify_peers2(c->
+                                                            ssl_state.gnutls_session,
+                                                            &status);
+                       if (verify < 0) {
+                               Log(LOG_ERR,
+                                   "gnutls_certificate_verify_peers2 failed: %s",
+                                   gnutls_strerror(verify));
+                               goto done_cn_validation;
+                       } else if (status) {
+                               gnutls_datum_t out;
+
+                               if (gnutls_certificate_verification_status_print
+                                   (status, gnutls_certificate_type_get(sess), &out,
+                                    0) == GNUTLS_E_SUCCESS) {
+                                       Log(LOG_ERR,
+                                           "Certificate validation failed: %s",
+                                           out.data);
+                                       gnutls_free(out.data);
+                               }
+                       }
+
+                       err = gnutls_x509_crt_check_hostname(cert, c->host);
+                       if (err == 0)
+                               Log(LOG_ERR,
+                                   "Failed to verify the hostname, expected \"%s\"",
+                                   c->host);
+                       else
+                               cert_ok = verify == 0 && status == 0;
+
+                       snprintf(msg, sizeof(msg), "Got %svalid server certificate",
+                               cert_ok ? "" : "in");
+                       LogGnuTLS_CertInfo(LOG_INFO, cert, msg);
+               } else {
+                       /* Incoming connection. Please see comments for OpenSSL! */
+                       LogGnuTLS_CertInfo(LOG_INFO, cert,
+                                           "Got unchecked peer certificate");
+               }
 
                gnutls_x509_crt_deinit(cert);
 done_cn_validation:
@@ -1043,7 +1078,7 @@ done_cn_validation:
        if (cert_ok)
                Conn_OPTION_ADD(c, CONN_SSL_PEERCERT_OK);
        if (!cert_seen)
-               Log(LOG_INFO, "Peer did not present a certificate");
+               Log(LOG_INFO, "Peer did not present a certificate.");
 }