+
+static int
+tls_negotiate(void)
+{
+ BIO *ssl_bio;
+
+ if (! sslctx) {
+ const SSL_METHOD *method;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ method = TLSv1_client_method(); /* Not sure about this */
+
+ /* Older ssl takes a non-const arg. */
+ sslctx = SSL_CTX_new((SSL_METHOD *) method);
+
+ if (! sslctx) {
+ sm_end(NOTOK);
+ return sm_ierror("Unable to initialize OpenSSL context: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+ }
+
+ ssl = SSL_new(sslctx);
+
+ if (! ssl) {
+ sm_end(NOTOK);
+ return sm_ierror("Unable to create SSL connection: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ sbior = BIO_new_socket(fileno(sm_rfp), BIO_NOCLOSE);
+ sbiow = BIO_new_socket(fileno(sm_wfp), BIO_NOCLOSE);
+
+ if (sbior == NULL || sbiow == NULL) {
+ sm_end(NOTOK);
+ return sm_ierror("Unable to create BIO endpoints: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ SSL_set_bio(ssl, sbior, sbiow);
+ SSL_set_connect_state(ssl);
+
+ /*
+ * Set up a BIO to handle buffering for us
+ */
+
+ io = BIO_new(BIO_f_buffer());
+
+ if (! io) {
+ sm_end(NOTOK);
+ return sm_ierror("Unable to create a buffer BIO: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ ssl_bio = BIO_new(BIO_f_ssl());
+
+ if (! ssl_bio) {
+ sm_end(NOTOK);
+ return sm_ierror("Unable to create a SSL BIO: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
+ BIO_push(io, ssl_bio);
+
+ /*
+ * Try doing the handshake now
+ */
+
+ if (BIO_do_handshake(io) < 1) {
+ sm_end(NOTOK);
+ return sm_ierror("Unable to negotiate SSL connection: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ if (sm_debug) {
+ const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
+ printf("SSL negotiation successful: %s(%d) %s\n",
+ SSL_CIPHER_get_name(cipher),
+ SSL_CIPHER_get_bits(cipher, NULL),
+ SSL_CIPHER_get_version(cipher));
+
+ }
+
+ tls_active = 1;
+
+ return RP_OK;
+}
+#endif /* TLS_SUPPORT */
+
+/*
+ * Convenience functions to replace occurences of fputs() and fputc()
+ */
+
+static int
+sm_fputs(char *buffer)
+{
+ return sm_fwrite(buffer, strlen(buffer));
+}
+
+static int
+sm_fputc(int c)
+{
+ char h = c;
+
+ return sm_fwrite(&h, 1);
+}
+
+/*
+ * Flush out any pending data on the connection
+ */
+
+static void
+sm_fflush(void)
+{
+#ifdef CYRUS_SASL
+ const char *output;
+ unsigned int outputlen;
+ int result;
+
+ if (sasl_complete == 1 && sasl_ssf > 0 && sasl_outbuflen > 0) {
+ result = sasl_encode(conn, sasl_outbuffer, sasl_outbuflen,
+ &output, &outputlen);
+ if (result != SASL_OK) {
+ sm_ierror("Unable to SASL encode connection data: %s",
+ sasl_errdetail(conn));
+ return;
+ }
+
+ fwrite(output, sizeof(*output), outputlen, sm_wfp);
+ sasl_outbuflen = 0;
+ }
+#endif /* CYRUS_SASL */
+
+#ifdef TLS_SUPPORT
+ if (tls_active) {
+ (void) BIO_flush(io);
+ }
+#endif /* TLS_SUPPORT */
+
+ fflush(sm_wfp);
+}