void *context ATTR_UNUSED)
{
struct ssl_iostream *ssl_io;
- const char *host;
+ const char *host, *error;
ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
} else if (ssl_io->verbose) {
i_debug("SSL_get_servername() failed");
}
+
+ if (ssl_io->sni_callback != NULL) {
+ if (ssl_io->sni_callback(ssl_io->sni_host, &error,
+ ssl_io->sni_context) < 0) {
+ openssl_iostream_set_error(ssl_io, error);
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ }
return SSL_TLSEXT_ERR_OK;
}
#endif
static void openssl_iostream_free(struct ssl_iostream *ssl_io);
-static void
-openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
+void openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
{
if (ssl_io->verbose) {
/* This error should normally be logged by lib-ssl-iostream's
ssl_io->handshake_context = context;
}
+static void
+openssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
+ ssl_iostream_sni_callback_t *callback,
+ void *context)
+{
+ ssl_io->sni_callback = callback;
+ ssl_io->sni_context = context;
+}
+
+static void
+openssl_iostream_change_context(struct ssl_iostream *ssl_io,
+ struct ssl_iostream_context *ctx)
+{
+ if (ctx != ssl_io->ctx) {
+ SSL_set_SSL_CTX(ssl_io->ssl, ctx->ssl_ctx);
+ ssl_iostream_context_ref(ctx);
+ ssl_iostream_context_unref(&ssl_io->ctx);
+ ssl_io->ctx = ctx;
+ }
+}
+
static void openssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io,
const char *prefix)
{
.handshake = openssl_iostream_handshake,
.set_handshake_callback = openssl_iostream_set_handshake_callback,
+ .set_sni_callback = openssl_iostream_set_sni_callback,
+ .change_context = openssl_iostream_change_context,
.set_log_prefix = openssl_iostream_set_log_prefix,
.is_handshaked = openssl_iostream_is_handshaked,
ssl_iostream_handshake_callback_t *handshake_callback;
void *handshake_context;
+ ssl_iostream_sni_callback_t *sni_callback;
+ void *sni_context;
+
bool handshaked:1;
bool handshake_failed:1;
bool cert_received:1;
enum openssl_iostream_sync_type type,
const char *func_name);
+void openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str);
const char *openssl_iostream_error(void);
const char *openssl_iostream_key_load_error(void);
const char *
void (*set_handshake_callback)(struct ssl_iostream *ssl_io,
ssl_iostream_handshake_callback_t *callback,
void *context);
+ void (*set_sni_callback)(struct ssl_iostream *ssl_io,
+ ssl_iostream_sni_callback_t *callback,
+ void *context);
+ void (*change_context)(struct ssl_iostream *ssl_io,
+ struct ssl_iostream_context *ctx);
void (*set_log_prefix)(struct ssl_iostream *ssl_io, const char *prefix);
bool (*is_handshaked)(const struct ssl_iostream *ssl_io);
ssl_vfuncs->set_handshake_callback(ssl_io, callback, context);
}
+void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
+ ssl_iostream_sni_callback_t *callback,
+ void *context)
+{
+ ssl_vfuncs->set_sni_callback(ssl_io, callback, context);
+}
+
+void ssl_iostream_change_context(struct ssl_iostream *ssl_io,
+ struct ssl_iostream_context *ctx)
+{
+ ssl_vfuncs->change_context(ssl_io, ctx);
+}
+
bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
{
return ssl_vfuncs->is_handshaked(ssl_io);
likely should be calling ssl_iostream_check_cert_validity(). */
typedef int
ssl_iostream_handshake_callback_t(const char **error_r, void *context);
+/* Called when TLS SNI becomes available. */
+typedef int ssl_iostream_sni_callback_t(const char *name, const char **error_r,
+ void *context);
int io_stream_create_ssl_client(struct ssl_iostream_context *ctx, const char *host,
const struct ssl_iostream_settings *set,
void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
ssl_iostream_handshake_callback_t *callback,
void *context);
+/* Call the given callback when client sends SNI. The callback can change the
+ ssl_iostream's context (with different certificates) by using
+ ssl_iostream_change_context(). */
+void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
+ ssl_iostream_sni_callback_t *callback,
+ void *context);
+void ssl_iostream_change_context(struct ssl_iostream *ssl_io,
+ struct ssl_iostream_context *ctx);
bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io);
/* Returns TRUE if the remote cert is invalid, or handshake callback returned