]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/conn-ssl.c
ConnSSL_HandleError: Code cleanup, more documentation
[ngircd-alex.git] / src / ngircd / conn-ssl.c
index 914d01651235ad16af2e81ebb3d9a045e1d39a37..1721aac560be1b6f8585754a35a42b48c61dc53e 100644 (file)
@@ -54,11 +54,15 @@ static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c ));
 #define DH_BITS 2048
 #define DH_BITS_MIN 1024
 
+#define MAX_HASH_SIZE  64      /* from gnutls-int.h */
+
 static gnutls_certificate_credentials_t x509_cred;
 static gnutls_dh_params_t dh_params;
 static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void ));
 #endif
 
+#define CERTFP_LEN     (20 * 2 + 1)
+
 static bool ConnSSL_Init_SSL PARAMS(( CONNECTION *c ));
 static int ConnectAccept PARAMS(( CONNECTION *c, bool connect ));
 static int ConnSSL_HandleError PARAMS(( CONNECTION *c, const int code, const char *fname ));
@@ -145,6 +149,13 @@ pem_passwd_cb(char *buf, int size, int rwflag, void *password)
        memcpy(buf, (char *)(array_start(pass)), size);
        return size;
 }
+
+
+static int
+Verify_openssl(UNUSED int preverify_ok, UNUSED X509_STORE_CTX *x509_ctx)
+{
+       return 1;
+}
 #endif
 
 
@@ -156,7 +167,7 @@ Load_DH_params(void)
        bool ret = true;
 
        if (!Conf_SSLOptions.DHFile) {
-               Log(LOG_NOTICE, "Configuration option \"SSLDHFile\" not set!");
+               Log(LOG_NOTICE, "Configuration option \"DHFile\" not set!");
                return false;
        }
        fp = fopen(Conf_SSLOptions.DHFile, "r");
@@ -201,7 +212,7 @@ Load_DH_params(void)
        }
        if (need_dhgenerate) {
                Log(LOG_WARNING,
-                   "SSLDHFile not set, generating %u bit DH parameters. This may take a while ...",
+                   "DHFile not set, generating %u bit DH parameters. This may take a while ...",
                    DH_BITS);
                err = gnutls_dh_params_generate2(tmp_dh_params, DH_BITS);
                if (err < 0) {
@@ -223,6 +234,10 @@ void ConnSSL_Free(CONNECTION *c)
                SSL_shutdown(ssl);
                SSL_free(ssl);
                c->ssl_state.ssl = NULL;
+               if (c->ssl_state.fingerprint) {
+                       free(c->ssl_state.fingerprint);
+                       c->ssl_state.fingerprint = NULL;
+               }
        }
 #endif
 #ifdef HAVE_LIBGNUTLS
@@ -241,8 +256,10 @@ void ConnSSL_Free(CONNECTION *c)
 bool
 ConnSSL_InitLibrary( void )
 {
-       if (!array_bytes(&Conf_SSLOptions.ListenPorts))
+       if (!Conf_SSLInUse()) {
+               LogDebug("SSL not in use, skipping initialization.");
                return true;
+       }
 
 #ifdef HAVE_LIBSSL
        SSL_CTX *newctx;
@@ -275,6 +292,7 @@ ConnSSL_InitLibrary( void )
 
        SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2);
        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.", SSLeay_version(SSLEAY_VERSION));
@@ -402,6 +420,7 @@ ConnSSL_Init_SSL(CONNECTION *c)
                return false;
        }
        assert(c->ssl_state.ssl == NULL);
+       assert(c->ssl_state.fingerprint == NULL);
 
        c->ssl_state.ssl = SSL_new(ssl_ctx);
        if (!c->ssl_state.ssl) {
@@ -430,6 +449,7 @@ ConnSSL_Init_SSL(CONNECTION *c)
         * http://www.mail-archive.com/help-gnutls@gnu.org/msg00286.html
         */
        gnutls_transport_set_ptr(c->ssl_state.gnutls_session, (gnutls_transport_ptr_t) (long) c->sock);
+       gnutls_certificate_server_set_request(c->ssl_state.gnutls_session, GNUTLS_CERT_REQUEST);
        ret = gnutls_credentials_set(c->ssl_state.gnutls_session, GNUTLS_CRD_CERTIFICATE, x509_cred);
        if (ret < 0) {
                Log(LOG_ERR, "gnutls_credentials_set: %s", gnutls_strerror(ret));
@@ -468,16 +488,23 @@ ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s)
 }
 
 
-/*
-  Check an Handle Error return code after failed calls to ssl/tls functions.
-  OpenSSL:
-  SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or SSL_write() on ssl.
-  GNUTLS:
-  gnutlsssl_read(), gnutls_write() or gnutls_handshake().
-  Return: -1 on fatal error, 0 if we can try again later.
+/**
+ * Check and handle error return codes after failed calls to SSL/TLS functions.
+ *
+ * OpenSSL:
+ * SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or
+ * SSL_write() on ssl.
+ *
+ * GNUTLS:
+ * gnutlsssl_read(), gnutls_write() or gnutls_handshake().
+ *
+ * @param c The connection handle.
+ * @prarm code The return code.
+ * @param fname The name of the function in which the error occurred.
+ * @return -1 on fatal errors, 0 if we can try again later.
  */
 static int
-ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname )
+ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname)
 {
 #ifdef HAVE_LIBSSL
        int ret = SSL_ERROR_SYSCALL;
@@ -498,21 +525,22 @@ ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname )
        case SSL_ERROR_ZERO_RETURN:
                LogDebug("TLS/SSL connection shut down normally");
                break;
-       /*
-       SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, SSL_ERROR_WANT_X509_LOOKUP
-       */
        case SSL_ERROR_SYSCALL:
+               /* SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT,
+                * and SSL_ERROR_WANT_X509_LOOKUP */
                sslerr = ERR_get_error();
                if (sslerr) {
-                       Log( LOG_ERR, "%s: %s", fname, ERR_error_string(sslerr, NULL ));
+                       Log(LOG_ERR, "%s: %s", fname,
+                           ERR_error_string(sslerr, NULL));
                } else {
 
                        switch (code) { /* EOF that violated protocol */
                        case 0:
-                               Log(LOG_ERR, "%s: Client Disconnected", fname );
+                               Log(LOG_ERR, "%s: Client Disconnected", fname);
                                break;
-                       case -1: /* low level socket I/O error, check errno */
-                               Log(LOG_ERR, "%s: %s", fname, strerror(real_errno));
+                       case -1:        /* low level socket I/O error, check errno */
+                               Log(LOG_ERR, "%s: %s", fname,
+                                   strerror(real_errno));
                        }
                }
                break;
@@ -520,7 +548,7 @@ ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname )
                LogOpenSSLError("TLS/SSL Protocol Error", fname);
                break;
        default:
-               Log( LOG_ERR, "%s: Unknown error %d!", fname, ret);
+               Log(LOG_ERR, "%s: Unknown error %d!", fname, ret);
        }
        ConnSSL_Free(c);
        return -1;
@@ -612,6 +640,77 @@ ConnSSL_Connect( CONNECTION *c )
        return ConnectAccept(c, true);
 }
 
+static int
+ConnSSL_InitCertFp( CONNECTION *c )
+{
+       const char hex[] = "0123456789abcdef";
+       int i;
+
+#ifdef HAVE_LIBSSL
+       unsigned char digest[EVP_MAX_MD_SIZE];
+       unsigned int digest_size;
+       X509 *cert;
+
+       cert = SSL_get_peer_certificate(c->ssl_state.ssl);
+       if (!cert)
+               return 0;
+
+       if (!X509_digest(cert, EVP_sha1(), digest, &digest_size)) {
+               X509_free(cert);
+               return 0;
+       }
+
+       X509_free(cert);
+#endif /* HAVE_LIBSSL */
+#ifdef HAVE_LIBGNUTLS
+       gnutls_x509_crt_t cert;
+       unsigned int cert_list_size;
+       const gnutls_datum_t *cert_list;
+       unsigned char digest[MAX_HASH_SIZE];
+       size_t digest_size;
+
+       if (gnutls_certificate_type_get(c->ssl_state.gnutls_session) != GNUTLS_CRT_X509)
+               return 0;
+
+       if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
+               return 0;
+
+       cert_list_size = 0;
+       cert_list = gnutls_certificate_get_peers(c->ssl_state.gnutls_session,
+                                                &cert_list_size);
+       if (!cert_list) {
+               gnutls_x509_crt_deinit(cert);
+               return 0;
+       }
+       
+       if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
+               gnutls_x509_crt_deinit(cert);
+               return 0;
+       }
+
+       digest_size = sizeof(digest);
+       if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, digest, &digest_size)) {
+               gnutls_x509_crt_deinit(cert);
+               return 0;
+       }
+
+       gnutls_x509_crt_deinit(cert);
+#endif /* HAVE_LIBGNUTLS */
+
+       assert(c->ssl_state.fingerprint == NULL);
+
+       c->ssl_state.fingerprint = malloc(CERTFP_LEN);
+       if (!c->ssl_state.fingerprint)
+               return 0;
+
+       for (i = 0; i < (int)digest_size; i++) {
+               c->ssl_state.fingerprint[i * 2] = hex[digest[i] / 16];
+               c->ssl_state.fingerprint[i * 2 + 1] = hex[digest[i] % 16];
+       }
+       c->ssl_state.fingerprint[i * 2] = '\0';
+
+       return 1;
+}
 
 /* accept/connect wrapper. if connect is true, connect to peer, otherwise wait for incoming connection */
 static int
@@ -632,6 +731,8 @@ ConnectAccept( CONNECTION *c, bool connect)
        if (ret)
                return ConnSSL_HandleError(c, ret, "gnutls_handshake");
 #endif /* _GNUTLS */
+       (void)ConnSSL_InitCertFp(c);
+
        Conn_OPTION_DEL(c, (CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_CONNECT));
        ConnSSL_LogCertInfo(c);
 
@@ -723,6 +824,26 @@ ConnSSL_GetCipherInfo(CONNECTION *c, char *buf, size_t len)
 #endif
 }
 
+char *
+ConnSSL_GetCertFp(CONNECTION *c)
+{
+       return c->ssl_state.fingerprint;
+}
+
+bool
+ConnSSL_SetCertFp(CONNECTION *c, const char *fingerprint)
+{
+       assert (c != NULL);
+       c->ssl_state.fingerprint = strdup(fingerprint);
+       return c->ssl_state.fingerprint != NULL;
+}
+#else
+
+bool
+ConnSSL_InitLibrary(void)
+{
+       return true;
+}
 
 #endif /* SSL_SUPPORT */
 /* -eof- */