<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

#define _POSIX_C_SOURCE 1

#include <proton/ssl.h>
#include "./ssl-internal.h"
#include <proton/engine.h>
#include "../engine/engine-internal.h"
#include "../util.h"

#include <openssl/ssl.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>


/** @file
 * SSL/TLS support API.
 *
 * This file contains an OpenSSL-based implemention of the SSL/TLS API.
 */

static int ssl_initialized;

typedef enum { UNKNOWN_CONNECTION, SSL_CONNECTION, CLEAR_CONNECTION } connection_mode_t;

struct pn_ssl_t {
  SSL_CTX *ctx;
  SSL *ssl;
  pn_ssl_mode_t mode;
  bool allow_unsecured; // allow non-SSL connections
  bool has_ca_db;       // true when CA database configured
  bool has_certificate; // true when certificate configured
  char *keyfile_pw;
  pn_ssl_verify_mode_t verify_mode;
  char *trusted_CAs;

  pn_transport_t *transport;

  BIO *bio_ssl;         // i/o from/to SSL socket layer
  BIO *bio_ssl_io;      // SSL "half" of network-facing BIO
  BIO *bio_net_io;      // socket-side "half" of network-facing BIO
  bool ssl_shutdown;    // BIO_ssl_shutdown() called on socket.
  bool ssl_closed;      // shutdown complete, or SSL error
  ssize_t app_input_closed;   // error code returned by upper layer process input
  ssize_t app_output_closed;  // error code returned by upper layer process output

  bool read_blocked;    // SSL blocked until more network data is read
  bool write_blocked;   // SSL blocked until data is written to network

  // buffers for holding I/O from "applications" above SSL
#define APP_BUF_SIZE    (4*1024)
  char outbuf[APP_BUF_SIZE];
  size_t out_count;
  char inbuf[APP_BUF_SIZE];
  size_t in_count;

  // process cleartext i/o "above" the SSL layer
  ssize_t (*process_input)(pn_transport_t *, const char *, size_t);
  ssize_t (*process_output)(pn_transport_t *, char *, size_t);

  pn_trace_t trace;
};

// define two sets of allowable ciphers: those that require authentication, and those
// that do not require authentication (anonymous).  See ciphers(1).
#define CIPHERS_AUTHENTICATE    "ALL:!aNULL:!eNULL:@STRENGTH"
#define CIPHERS_ANONYMOUS       "ALL:aNULL:!eNULL:@STRENGTH"

/* */
static int keyfile_pw_cb(char *buf, int size, int rwflag, void *userdata);
static ssize_t process_input_ssl( pn_transport_t *transport, const char *input_data, size_t len);
static ssize_t process_output_ssl( pn_transport_t *transport, char *input_data, size_t len);
static ssize_t process_input_cleartext(pn_transport_t *transport, const char *input_data, size_t len);
static ssize_t process_output_cleartext(pn_transport_t *transport, char *buffer, size_t max_len);
static ssize_t process_input_unknown(pn_transport_t *transport, const char *input_data, size_t len);
static ssize_t process_output_unknown(pn_transport_t *transport, char *input_data, size_t len);
static connection_mode_t check_for_ssl_connection( const char *data, size_t len );
static int init_ssl_socket( pn_ssl_t * );


// @todo: used to avoid littering the code with calls to printf...
static void _log_error(const char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
}

// @todo: used to avoid littering the code with calls to printf...
static void _log(pn_ssl_t *ssl, const char *fmt, ...)
{
  if (PN_TRACE_DRV & ssl->trace) {
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
  }
}

// log an error and dump the SSL error stack
static void _log_ssl_error(pn_ssl_t *ssl, const char *fmt, ...)
{
  char buf[128];        // see "man ERR_error_string_n()"
  va_list ap;

  if (fmt) {
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
  }

  unsigned long err = ERR_get_error();
  while (err) {
    ERR_error_string_n(err, buf, sizeof(buf));
    _log_error("%s\n", buf);
    err = ERR_get_error();
  }
}

static void _log_clear_data(pn_ssl_t *ssl, const char *data, size_t len)
{
  if (PN_TRACE_RAW & ssl->trace) {
    fprintf(stderr, "SSL decrypted data: \"");
    pn_fprint_data( stderr, data, len );
    fprintf(stderr, "\"\n");
  }
}

// unrecoverable SSL failure occured, notify transport and generate error code.
static int ssl_failed(pn_ssl_t *ssl)
{
  // try to grab the first SSL error to add to the failure log
  char buf[128] = "Unknown error.";
  unsigned long ssl_err = ERR_get_error();
  if (ssl_err) {
    ERR_error_string_n( ssl_err, buf, sizeof(buf) );
  }
  return pn_error_format( ssl->transport->error, PN_ERR, "SSL Failure: %s", buf );
}

// @todo replace with a "reasonable" default (?), allow application to register its own
// callback.
#if 0
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
    fprintf(stderr, "VERIFY_CALLBACK: pre-verify-ok=%d\n", preverify_ok);

           char    buf[256];<commit_msg>NO-JIRA: force both input and output to close if SSL hard failure detected.<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

#define _POSIX_C_SOURCE 1

#include <proton/ssl.h>
#include "./ssl-internal.h"
#include <proton/engine.h>
#include "../engine/engine-internal.h"
#include "../util.h"

#include <openssl/ssl.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>


/** @file
 * SSL/TLS support API.
 *
 * This file contains an OpenSSL-based implemention of the SSL/TLS API.
 */

static int ssl_initialized;

typedef enum { UNKNOWN_CONNECTION, SSL_CONNECTION, CLEAR_CONNECTION } connection_mode_t;

struct pn_ssl_t {
  SSL_CTX *ctx;
  SSL *ssl;
  pn_ssl_mode_t mode;
  bool allow_unsecured; // allow non-SSL connections
  bool has_ca_db;       // true when CA database configured
  bool has_certificate; // true when certificate configured
  char *keyfile_pw;
  pn_ssl_verify_mode_t verify_mode;
  char *trusted_CAs;

  pn_transport_t *transport;

  BIO *bio_ssl;         // i/o from/to SSL socket layer
  BIO *bio_ssl_io;      // SSL "half" of network-facing BIO
  BIO *bio_net_io;      // socket-side "half" of network-facing BIO
  bool ssl_shutdown;    // BIO_ssl_shutdown() called on socket.
  bool ssl_closed;      // shutdown complete, or SSL error
  ssize_t app_input_closed;   // error code returned by upper layer process input
  ssize_t app_output_closed;  // error code returned by upper layer process output

  bool read_blocked;    // SSL blocked until more network data is read
  bool write_blocked;   // SSL blocked until data is written to network

  // buffers for holding I/O from "applications" above SSL
#define APP_BUF_SIZE    (4*1024)
  char outbuf[APP_BUF_SIZE];
  size_t out_count;
  char inbuf[APP_BUF_SIZE];
  size_t in_count;

  // process cleartext i/o "above" the SSL layer
  ssize_t (*process_input)(pn_transport_t *, const char *, size_t);
  ssize_t (*process_output)(pn_transport_t *, char *, size_t);

  pn_trace_t trace;
};

// define two sets of allowable ciphers: those that require authentication, and those
// that do not require authentication (anonymous).  See ciphers(1).
#define CIPHERS_AUTHENTICATE    "ALL:!aNULL:!eNULL:@STRENGTH"
#define CIPHERS_ANONYMOUS       "ALL:aNULL:!eNULL:@STRENGTH"

/* */
static int keyfile_pw_cb(char *buf, int size, int rwflag, void *userdata);
static ssize_t process_input_ssl( pn_transport_t *transport, const char *input_data, size_t len);
static ssize_t process_output_ssl( pn_transport_t *transport, char *input_data, size_t len);
static ssize_t process_input_cleartext(pn_transport_t *transport, const char *input_data, size_t len);
static ssize_t process_output_cleartext(pn_transport_t *transport, char *buffer, size_t max_len);
static ssize_t process_input_unknown(pn_transport_t *transport, const char *input_data, size_t len);
static ssize_t process_output_unknown(pn_transport_t *transport, char *input_data, size_t len);
static connection_mode_t check_for_ssl_connection( const char *data, size_t len );
static int init_ssl_socket( pn_ssl_t * );


// @todo: used to avoid littering the code with calls to printf...
static void _log_error(const char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
}

// @todo: used to avoid littering the code with calls to printf...
static void _log(pn_ssl_t *ssl, const char *fmt, ...)
{
  if (PN_TRACE_DRV & ssl->trace) {
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
  }
}

// log an error and dump the SSL error stack
static void _log_ssl_error(pn_ssl_t *ssl, const char *fmt, ...)
{
  char buf[128];        // see "man ERR_error_string_n()"
  va_list ap;

  if (fmt) {
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
  }

  unsigned long err = ERR_get_error();
  while (err) {
    ERR_error_string_n(err, buf, sizeof(buf));
    _log_error("%s\n", buf);
    err = ERR_get_error();
  }
}

static void _log_clear_data(pn_ssl_t *ssl, const char *data, size_t len)
{
  if (PN_TRACE_RAW & ssl->trace) {
    fprintf(stderr, "SSL decrypted data: \"");
    pn_fprint_data( stderr, data, len );
    fprintf(stderr, "\"\n");
  }
}

// unrecoverable SSL failure occured, notify transport and generate error code.
static int ssl_failed(pn_ssl_t *ssl)
{
  ssl->ssl_closed = true;
  ssl->app_input_closed = ssl->app_output_closed = PN_ERR;
  // try to grab the first SSL error to add to the failure log
  char buf[128] = "Unknown error.";
  unsigned long ssl_err = ERR_get_error();
  if (ssl_err) {
    ERR_error_string_n( ssl_err, buf, sizeof(buf) );
  }
  _log_ssl_error(ssl, NULL);    // spit out any remaining errors to the log file
  return pn_error_format( ssl->transport->error, PN_ERR, "SSL Failure: %s", buf );
}

// @todo replace with a "reasonable" default (?), allow application to register its own
// callback.
#if 0
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
    fprintf(stderr, "VERIFY_CALLBACK: pre-verify-ok=%d\n", preverify_ok);

           char    buf[256];
{'fraction_non_alphanumeric': 0.10490723079590775, 'fraction_numerical': 0.0041616091555401425, 'mean_word_length': 4.0, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2614103', 'n_tokens_mistral': 3969, 'n_tokens_neox': 3862, 'n_words': 1235}