]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-ssl-iostream: Add ssl_iostream_get_state()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 8 Aug 2025 11:45:50 +0000 (14:45 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 11 Aug 2025 07:47:35 +0000 (07:47 +0000)
src/lib-ssl-iostream/iostream-openssl.c
src/lib-ssl-iostream/iostream-openssl.h
src/lib-ssl-iostream/iostream-ssl-private.h
src/lib-ssl-iostream/iostream-ssl.c
src/lib-ssl-iostream/iostream-ssl.h

index cb05fcbf60c7bd0e71c93312c59ccefbc91d7c53..1988edab77f3fca9fe9537e716f54584943edf94 100644 (file)
@@ -112,6 +112,7 @@ openssl_iostream_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx)
                ssl_io->cert_broken = TRUE;
                if (!ssl_io->allow_invalid_cert) {
                        ssl_io->handshake_failed = TRUE;
+                       ssl_io->state = SSL_IOSTREAM_STATE_INVALID_CERT;
                        return 0;
                }
        }
@@ -181,6 +182,7 @@ openssl_iostream_create(struct ssl_iostream_context *ctx,
        ssl_io->plain_output = *output;
        ssl_io->connected_host = i_strdup(host);
        ssl_io->event = event_create(event_parent);
+       ssl_io->state = SSL_IOSTREAM_STATE_HANDSHAKING;
        ssl_io->allow_invalid_cert = ctx->allow_invalid_cert ||
                (flags & SSL_IOSTREAM_FLAG_ALLOW_INVALID_CERT) != 0;
        if (client)
@@ -618,15 +620,30 @@ static int openssl_iostream_handshake(struct ssl_iostream *ssl_io)
        (void)openssl_iostream_bio_sync(ssl_io, OPENSSL_IOSTREAM_SYNC_TYPE_HANDSHAKE);
 
        if (ssl_io->handshake_callback != NULL) {
-               if (ssl_io->handshake_callback(&error, ssl_io->handshake_context) != SSL_IOSTREAM_STATE_OK) {
+               ssl_io->state = ssl_io->handshake_callback(&error, ssl_io->handshake_context);
+               if (ssl_io->state != SSL_IOSTREAM_STATE_OK) {
                        i_assert(error != NULL);
                        openssl_iostream_set_error(ssl_io, error);
                        ssl_io->handshake_failed = TRUE;
                }
        } else if (ssl_io->connected_host != NULL && !ssl_io->handshake_failed &&
                   !ssl_io->allow_invalid_cert) {
-               if (ssl_iostream_check_cert_validity(ssl_io, ssl_io->connected_host,
-                                                    &reason) != SSL_IOSTREAM_CERT_VALIDITY_OK) {
+               enum ssl_iostream_cert_validity validity =
+                       ssl_iostream_check_cert_validity(ssl_io,
+                               ssl_io->connected_host, &reason);
+               switch (validity) {
+               case SSL_IOSTREAM_CERT_VALIDITY_OK:
+                       ssl_io->state = SSL_IOSTREAM_STATE_OK;
+                       break;
+               case SSL_IOSTREAM_CERT_VALIDITY_NO_CERT:
+               case SSL_IOSTREAM_CERT_VALIDITY_INVALID:
+                       ssl_io->state = SSL_IOSTREAM_STATE_INVALID_CERT;
+                       break;
+               case SSL_IOSTREAM_CERT_VALIDITY_NAME_MISMATCH:
+                       ssl_io->state = SSL_IOSTREAM_STATE_NAME_MISMATCH;
+                       break;
+               }
+               if (validity != SSL_IOSTREAM_CERT_VALIDITY_OK) {
                        openssl_iostream_set_error(ssl_io, reason);
                        ssl_io->handshake_failed = TRUE;
                }
@@ -638,6 +655,7 @@ static int openssl_iostream_handshake(struct ssl_iostream *ssl_io)
        }
        i_free_and_null(ssl_io->last_error);
        ssl_io->handshaked = TRUE;
+       ssl_io->state = SSL_IOSTREAM_STATE_OK;
 
        const char *alpn_proto = ssl_iostream_get_application_protocol(ssl_io);
        if (alpn_proto != NULL && *alpn_proto != '\0')
@@ -683,6 +701,12 @@ static void openssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io,
        event_set_append_log_prefix(ssl_io->event, prefix);
 }
 
+static enum ssl_iostream_state
+openssl_iostream_get_state(const struct ssl_iostream *ssl_io)
+{
+       return ssl_io->state;
+}
+
 static bool openssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
 {
        return ssl_io->handshaked;
@@ -1084,6 +1108,7 @@ static const struct iostream_ssl_vfuncs ssl_vfuncs = {
        .change_context = openssl_iostream_change_context,
 
        .set_log_prefix = openssl_iostream_set_log_prefix,
+       .get_state = openssl_iostream_get_state,
        .is_handshaked = openssl_iostream_is_handshaked,
        .has_handshake_failed = openssl_iostream_has_handshake_failed,
        .has_valid_client_cert = openssl_iostream_has_valid_client_cert,
index fb18159928ea9bda7d06dc0fee7172f630aac4bc..43519be5d81e4d7ea2a73f7d19ece87c22010ff4 100644 (file)
@@ -43,6 +43,7 @@ struct ssl_iostream {
        SSL *ssl;
        BIO *bio_ext;
 
+       enum ssl_iostream_state state;
        struct istream *plain_input;
        struct ostream *plain_output;
        struct istream *ssl_input;
index d62e7168df1221e25e4202f8670d5aad179d3395..6b06f9fa470409a485c3d9b218ce11c4f79daf7e 100644 (file)
@@ -36,6 +36,7 @@ struct iostream_ssl_vfuncs {
                               struct ssl_iostream_context *ctx);
 
        void (*set_log_prefix)(struct ssl_iostream *ssl_io, const char *prefix);
+       enum ssl_iostream_state (*get_state)(const struct ssl_iostream *ssl_io);
        bool (*is_handshaked)(const struct ssl_iostream *ssl_io);
        bool (*has_handshake_failed)(const struct ssl_iostream *ssl_io);
        bool (*has_valid_client_cert)(const struct ssl_iostream *ssl_io);
index 58f9de3011b6a4b882ca35a291044da6a5ee2f9f..d33b0571f2226e7e4fc383f49b3611f2d39b2544 100644 (file)
@@ -258,6 +258,12 @@ void ssl_iostream_change_context(struct ssl_iostream *ssl_io,
        ssl_vfuncs->change_context(ssl_io, ctx);
 }
 
+enum ssl_iostream_state
+ssl_iostream_get_state(const struct ssl_iostream *ssl_io)
+{
+       return ssl_vfuncs->get_state(ssl_io);
+}
+
 bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
 {
        return ssl_vfuncs->is_handshaked(ssl_io);
index c9d9357416730b88ab8fe62643354cd5ff04f924..fffa63722b142056baaf542ab4d455dcbd6a3412 100644 (file)
@@ -52,6 +52,10 @@ enum ssl_iostream_cert_validity {
 enum ssl_iostream_state {
        /* Handshake is finished and successful. */
        SSL_IOSTREAM_STATE_OK,
+       /* Handshake has not finished yet. */
+       SSL_IOSTREAM_STATE_HANDSHAKING,
+       /* Handshake failed due to some error unrelated to SSL certificate. */
+       SSL_IOSTREAM_STATE_HANDSHAKE_FAILURE,
        /* SSL certificate is missing/invalid/untrusted. */
        SSL_IOSTREAM_STATE_INVALID_CERT,
        /* SSL certificate is valid, but it doesn't match the name. */
@@ -194,6 +198,10 @@ void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
 void ssl_iostream_change_context(struct ssl_iostream *ssl_io,
                                 struct ssl_iostream_context *ctx);
 
+/* Returns the SSL iostream (handshake) state. */
+enum ssl_iostream_state
+ssl_iostream_get_state(const struct ssl_iostream *ssl_io);
+
 bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io);
 /* Returns TRUE if the remote cert is invalid, or handshake callback returned
    failure. */