+else
+ {
+ state = &state_server;
+ memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init));
+ state->tlsp = &tls_in;
+ DEBUG(D_tls) debug_printf("initialising GnuTLS server session\n");
+ rc = gnutls_init(&state->session, GNUTLS_SERVER);
+ }
+exim_gnutls_err_check(US"gnutls_init");
+
+state->host = host;
+
+state->tls_certificate = certificate;
+state->tls_privatekey = privatekey;
+state->tls_require_ciphers = require_ciphers;
+state->tls_sni = sni;
+state->tls_verify_certificates = cas;
+state->tls_crl = crl;
+
+/* This handles the variables that might get re-expanded after TLS SNI;
+that's tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */
+
+DEBUG(D_tls)
+ debug_printf("Expanding various TLS configuration options for session credentials.\n");
+rc = tls_expand_session_files(state);
+if (rc != OK) return rc;
+
+/* These are all other parts of the x509_cred handling, since SNI in GnuTLS
+requires a new structure afterwards. */
+
+rc = tls_set_remaining_x509(state);
+if (rc != OK) return rc;
+
+/* set SNI in client, only */
+if (host)
+ {
+ if (!expand_check(state->tlsp->sni, "tls_out_sni", &state->exp_tls_sni))
+ return DEFER;
+ if (state->exp_tls_sni && *state->exp_tls_sni)
+ {
+ DEBUG(D_tls)
+ debug_printf("Setting TLS client SNI to \"%s\"\n", state->exp_tls_sni);
+ sz = Ustrlen(state->exp_tls_sni);
+ rc = gnutls_server_name_set(state->session,
+ GNUTLS_NAME_DNS, state->exp_tls_sni, sz);
+ exim_gnutls_err_check(US"gnutls_server_name_set");
+ }
+ }
+else if (state->tls_sni)
+ DEBUG(D_tls) debug_printf("*** PROBABLY A BUG *** " \
+ "have an SNI set for a client [%s]\n", state->tls_sni);
+
+/* This is the priority string support,
+http://www.gnu.org/software/gnutls/manual/html_node/Priority-Strings.html
+and replaces gnutls_require_kx, gnutls_require_mac & gnutls_require_protocols.
+This was backwards incompatible, but means Exim no longer needs to track
+all algorithms and provide string forms for them. */
+
+want_default_priorities = TRUE;
+
+if (state->tls_require_ciphers && *state->tls_require_ciphers)
+ {
+ if (!expand_check_tlsvar(tls_require_ciphers))
+ return DEFER;
+ if (state->exp_tls_require_ciphers && *state->exp_tls_require_ciphers)
+ {
+ DEBUG(D_tls) debug_printf("GnuTLS session cipher/priority \"%s\"\n",
+ state->exp_tls_require_ciphers);
+
+ rc = gnutls_priority_init(&state->priority_cache,
+ CS state->exp_tls_require_ciphers, &errpos);
+ want_default_priorities = FALSE;
+ p = state->exp_tls_require_ciphers;
+ }
+ }
+if (want_default_priorities)
+ {
+ DEBUG(D_tls)
+ debug_printf("GnuTLS using default session cipher/priority \"%s\"\n",
+ exim_default_gnutls_priority);
+ rc = gnutls_priority_init(&state->priority_cache,
+ exim_default_gnutls_priority, &errpos);
+ p = US exim_default_gnutls_priority;
+ }
+
+exim_gnutls_err_check(string_sprintf(
+ "gnutls_priority_init(%s) failed at offset %ld, \"%.6s..\"",
+ p, errpos - CS p, errpos));
+
+rc = gnutls_priority_set(state->session, state->priority_cache);
+exim_gnutls_err_check(US"gnutls_priority_set");
+
+gnutls_db_set_cache_expiration(state->session, ssl_session_timeout);
+
+/* Reduce security in favour of increased compatibility, if the admin
+decides to make that trade-off. */
+if (gnutls_compat_mode)
+ {
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020104
+ DEBUG(D_tls) debug_printf("lowering GnuTLS security, compatibility mode\n");
+ gnutls_session_enable_compatibility_mode(state->session);
+#else
+ DEBUG(D_tls) debug_printf("Unable to set gnutls_compat_mode - GnuTLS version too old\n");
+#endif
+ }
+
+*caller_state = state;
+return OK;