Skip to content

Conversation

@eehakkin
Copy link

@eehakkin eehakkin commented Jan 22, 2026

The OpenLDAP library has global options (which can be set by passing a NULL LDAP handle to ldap_set_setting()) and connection specific options (which can be set by passing a non-NULL LDAP handle to ldap_set_setting()). For TLS connection, the library uses either a default TLS context (which is based on global TLS options) or a connection specific TLS context (which can be create used the LDAP_OPT_X_TLS_NEWCTX option and which is based on connection specific TLS options).

The Dovecot LDAP libraries set and use the options and TLS contexts inconsistently and therefore it is not possible to set Dovecot LDAP TLS options as documented. Additionally, the ssl_min_protocol setting in handled wrongly.

Problems:

  1. Inconsistent use of LDAP TLS contexts:

    • LDAP connections are create by by the db_ldap_init_ld() in src/auth/db-ldap.c and by the ldap_connection_setup() in src/lib-ldap/ldap-connection.c (using ldap_initialize()). Both of these call the ldap_set_tls_options() to set connection specific TLS options (except for LDAP_OPT_X_TLS_REQUIRE_CERT also the global one).
    • The ldap_connection_setup() in src/lib-ldap/ldap-connection.c also sets the LDAP_OPT_X_TLS_NEWCTX option and thus creates a new connection specific TLS context based on the connection specific TLS options which were set by the ldap_set_tls_options().
    • But the db_ldap_init_ld() in src/auth/db-ldap.c does not set the LDAP_OPT_X_TLS_NEWCTX option and thus uses the default TLS context based on the global TLS options which were not set by the ldap_set_tls_options().

    To fix this disparity, the ldap_set_tls_options() should set the LDAP_OPT_X_TLS_NEWCTX option.

  2. Once the TLS context issue is fixed and only connection specific TLS options are used, it is not longer necessary to set the global LDAP_OPT_X_TLS_REQUIRE_CERT option in the ldap_set_tls_options().

  3. The ldap_set_tls_options() uses the ssl_min_protocol setting string as-is as a LDAP_OPT_X_TLS_PROTOCOL_MIN option but the option is an int option. Typically the string is something like "TLSv1.3" or "\x54\x4C\x53\x76\x31\x2E\x33" which with typical 4 byte little endian ints means TLSv7754572.84 (0x76534C, 0x54) which LDAP servers do not provide.
    To fix this, the ldap_set_tls_options() should convert the ssl_min_protocol setting to LDAP_OPT_X_TLS_PROTOCOL_TLSx_y constants.

  4. The ldap_connection_setup() in src/lib-ldap/ldap-connection.c calls the ldap_set_tls_options() to set TLS options but overrides the LDAP_OPT_X_TLS_PROTOCOL_MIN option with the LDAP_OPT_X_TLS_PROTOCOL_SSL3 value.

Partial work-a-round for the current Dovecot 2.4.x is to configure at least LDAP TLS certificates also using import_environment as in

import_environment {
	LDAPTLS_CACERT    = /path/to/ca.pem
	LDAPTLS_CACERTDIR = /path/to/ca.dir/
	LDAPTLS_CERT      = /path/to/cert.pem
	LDAPTLS_KEY       = /path/to/key.pem
}

/* refuse to connect to SSLv2 as it's completely insecure */
opt = LDAP_OPT_X_TLS_PROTOCOL_SSL3;
ldap_set_option(conn->conn, LDAP_OPT_X_TLS_PROTOCOL_MIN, &opt);
#endif
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done in ldap_set_tls_options().

opt = 0;
ldap_set_option(conn->conn, LDAP_OPT_X_TLS_NEWCTX, &opt);
#endif

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done in ldap_set_tls_options().

int opt = requires ? LDAP_OPT_X_TLS_HARD : LDAP_OPT_X_TLS_ALLOW;

/* required for Bookworm */
if (ldap_set_opt(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &opt,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's required for Bookworm and others to either set the global TLS options (by passing a NULL LDAP handle for ldap_set_option) and not to create a connection specific TLS context (by setting the LDAP_OPT_X_TLS_NEWCTX option) or to set the connection specific TLS options (by passing a non-NULL LDAP handle for ldap_set_option like on RHEL9) and to create a connection specific TLS context (by setting the LDAP_OPT_X_TLS_NEWCTX option).

It's better to set set the connection specific TLS options and to create a connection specific TLS context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in the commit message.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

else if (strcasecmp(ssl_set->ssl_min_protocol, "ANY") == 0 ||
*ssl_set->ssl_min_protocol == '\0')
/* refuse to connect to SSLv2 as it's completely insecure */
opt = LDAP_OPT_X_TLS_PROTOCOL_SSL3;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this minimum be increased to LDAP_OPT_X_TLS_PROTOCOL_TLS1_0?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably disallow empty ssl_min_protocol value entirely (also in lib-ssl-iostream code).

else if (strcasecmp(ssl_set->ssl_min_protocol, "ANY") == 0 ||
*ssl_set->ssl_min_protocol == '\0')
/* refuse to connect to SSLv2 as it's completely insecure */
opt = LDAP_OPT_X_TLS_PROTOCOL_SSL3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably disallow empty ssl_min_protocol value entirely (also in lib-ssl-iostream code).

opt = LDAP_OPT_X_TLS_PROTOCOL_TLS1_2;
else if (strcasecmp(ssl_set->ssl_min_protocol, "TLSv1.3") == 0 ||
strcasecmp(ssl_set->ssl_min_protocol, "LATEST") == 0)
opt = LDAP_OPT_X_TLS_PROTOCOL_TLS1_3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should move this mapping into a table and loop through it, instead of many ifs.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

int opt = requires ? LDAP_OPT_X_TLS_HARD : LDAP_OPT_X_TLS_ALLOW;

/* required for Bookworm */
if (ldap_set_opt(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &opt,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in the commit message.

Additionally, do not set the global peer certificate checking strategy
(the global LDAP_OPT_X_TLS_REQUIRE_CERT option). It is not needed and
has no effect after a new TLS context is created.
@eehakkin eehakkin force-pushed the fix/ldap-set-tls-options branch from e3b3d0c to f043a7a Compare January 26, 2026 20:45
@eehakkin eehakkin requested a review from sirainen January 26, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants