acl/lib_tls/tls/tls_misc.c

868 lines
25 KiB
C
Raw Normal View History

/*++
2014-11-19 00:25:21 +08:00
* NAME
* tls_misc 3
* SUMMARY
* miscellaneous TLS support routines
* SYNOPSIS
* #include <tls.h>
* #include <tls_private.h>
*
* char *var_tls_high_clist;
* char *var_tls_medium_clist;
* char *var_tls_low_clist;
* char *var_tls_export_clist;
* char *var_tls_null_clist;
* char *var_tls_eecdh_strong;
* char *var_tls_eecdh_ultra;
* int var_tls_daemon_rand_bytes;
*
* TLS_APPL_STATE *tls_alloc_app_context(ssl_ctx)
* SSL_CTX *ssl_ctx;
*
* void tls_free_app_context(app_ctx)
* void *app_ctx;
*
* TLS_SESS_STATE *tls_alloc_sess_context(log_level, namaddr)
* int log_level;
* const char *namaddr;
*
* void tls_free_context(TLScontext)
* TLS_SESS_STATE *TLScontext;
*
* void tls_check_version()
*
* long tls_bug_bits()
*
* void tls_param_init()
*
* int tls_protocol_mask(plist)
* const char *plist;
*
* int tls_cipher_grade(name)
* const char *name;
*
* const char *str_tls_cipher_grade(grade)
* int grade;
*
* const char *tls_set_ciphers(app_ctx, context, grade, exclusions)
* TLS_APPL_STATE *app_ctx;
* const char *context;
* int grade;
* const char *exclusions;
*
* void tls_print_errors()
*
* void tls_info_callback(ssl, where, ret)
* const SSL *ssl; #unused
* int where;
* int ret;
*
* long tls_bio_dump_cb(bio, cmd, argp, argi, argl, ret)
* BIO *bio;
* int cmd;
* const char *argp;
* int argi;
* long argl; #unused
* long ret;
* DESCRIPTION
* This module implements routines that support the TLS client
* and server internals.
*
* tls_alloc_app_context() creates an application context that
* holds the SSL context for the application and related cached state.
*
* tls_free_app_context() deallocates the application context and its
* contents (the application context is stored outside the TLS library).
*
* tls_alloc_sess_context() creates an initialized TLS session context
* structure with the specified log mask and peer name[addr].
*
* tls_free_context() destroys a TLScontext structure
* together with OpenSSL structures that are attached to it.
*
* tls_check_version() logs a warning when the run-time OpenSSL
* library differs in its major, minor or micro number from
* the compile-time OpenSSL headers.
*
* tls_bug_bits() returns the bug compatibility mask appropriate
* for the run-time library. Some of the bug work-arounds are
* not appropriate for some library versions.
*
* tls_param_init() loads main.cf parameters used internally in
* TLS library. Any errors are fatal.
*
* tls_protocol_mask() returns a bitmask of excluded protocols, given
* a list (plist) of protocols to include or (preceded by a '!') exclude.
* If "plist" contains invalid protocol names, TLS_PROTOCOL_INVALID is
* returned and no warning is logged.
*
* tls_cipher_grade() converts a case-insensitive cipher grade
* name (high, medium, low, export, null) to the corresponding
* TLS_CIPHER_ constant. When the input specifies an unrecognized
* grade, tls_cipher_grade() logs no warning, and returns
* TLS_CIPHER_NONE.
*
* str_tls_cipher_grade() converts a cipher grade to a name.
* When the input specifies an undefined grade, str_tls_cipher_grade()
* logs no warning, returns a null pointer.
*
* tls_set_ciphers() generates a cipher list from the specified
* grade, minus any ciphers specified via a list of exclusions.
* The cipherlist is applied to the supplied SSL context if it
* is different from the most recently applied value. The return
* value is the cipherlist used and is overwritten upon each call.
* When the input is invalid, tls_set_ciphers() logs a warning with
* the specified context, and returns a null pointer result.
*
* tls_print_errors() queries the OpenSSL error stack,
* logs the error messages, and clears the error stack.
*
* tls_info_callback() is a call-back routine for the
* SSL_CTX_set_info_callback() routine. It logs SSL events
* to the Postfix logfile.
*
* tls_bio_dump_cb() is a call-back routine for the
* BIO_set_callback() routine. It logs SSL content to the
* Postfix logfile.
* LICENSE
* .ad
* .fi
* This software is free. You can do with it whatever you want.
* The original author kindly requests that you acknowledge
* the use of his software.
* AUTHOR(S)
* Originally written by:
* Lutz Jaenicke
* BTU Cottbus
* Allgemeine Elektrotechnik
* Universitaetsplatz 3-4
* D-03044 Cottbus, Germany
*
* Updated by:
* Wietse Venema
* IBM T.J. Watson Research
* P.O. Box 704
* Yorktown Heights, NY 10598, USA
*
* Victor Duchovni
* Morgan Stanley
*--*/
/* System library. */
#include "StdAfx.h"
#include <ctype.h>
#include <string.h>
#ifdef USE_TLS
/*
* TLS library.
*/
#include "tls.h"
#include "tls_params.h"
#include "tls_private.h"
/* Application-specific. */
/*
* Index to attach TLScontext pointers to SSL objects, so that they can be
* accessed by call-back routines.
*/
int TLScontext_index = -1;
/*
* Protocol name <=> mask conversion.
*/
static const NAME_CODE protocol_table[] = {
{ SSL_TXT_SSLV2, TLS_PROTOCOL_SSLv2 },
{ SSL_TXT_SSLV3, TLS_PROTOCOL_SSLv3 },
{ SSL_TXT_TLSV1, TLS_PROTOCOL_TLSv1 },
{ 0, TLS_PROTOCOL_INVALID },
};
/*
* Ciphersuite name <=> code conversion.
*/
const NAME_CODE tls_cipher_grade_table[] = {
{ "high", TLS_CIPHER_HIGH },
{ "medium", TLS_CIPHER_MEDIUM },
{ "low", TLS_CIPHER_LOW },
{ "export", TLS_CIPHER_EXPORT },
{ "null", TLS_CIPHER_NULL },
{ "invalid", TLS_CIPHER_NONE },
{ 0, TLS_CIPHER_NONE },
};
/*
* Parsed OpenSSL version number.
*/
typedef struct {
int major;
int minor;
int micro;
int patch;
int status;
} TLS_VINFO;
/*
* OpenSSL adopted the cipher selection patch, so we don't expect any more
* broken ciphers other than AES and CAMELLIA.
*/
typedef struct {
const char *ssl_name;
const int alg_bits;
const char *evp_name;
} cipher_probe_t;
static const cipher_probe_t cipher_probes[] = {
{ "AES", 256, "AES-256-CBC" },
{ "CAMELLIA", 256, "CAMELLIA-256-CBC" },
{ 0, 0, 0 },
};
static void free_argv_fn(void *arg)
{
ACL_ARGV *argv = (ACL_ARGV*) arg;
acl_argv_free(argv);
}
/* tls_exclude_missing - Append exclusions for missing ciphers */
static const char *tls_exclude_missing(SSL_CTX *ctx, ACL_VSTRING *buf)
{
const char *myname = "tls_exclude_missing";
static __thread ACL_ARGV *exclude; /* Cached */
SSL *s = 0;
STACK_OF(SSL_CIPHER) * ciphers;
SSL_CIPHER *c;
const cipher_probe_t *probe;
int alg_bits;
int num;
int i;
/*
* Process a list of probes which specify:
*
* An SSL cipher-suite name for a family of ciphers that use the same
* symmetric algorithm at two or more key sizes, typically 128/256 bits.
*
* The key size (typically 256) that OpenSSL fails to check, and assumes
* available when another key size (typically 128) is usable.
*
* The OpenSSL name of the symmetric algorithm associated with the SSL
* cipher-suite. Typically, this is MUMBLE-256-CBC, where "MUMBLE" is the
* name of the SSL cipher-suite that use the MUMBLE symmetric algorithm.
* On systems that support the required encryption algorithm, the name is
* listed in the output of "openssl list-cipher-algorithms".
*
* When an encryption algorithm is not available at the given key size but
* the corresponding OpenSSL cipher-suite contains ciphers that have have
* this key size, the problem ciphers are explicitly disabled in Postfix.
* The list is cached in the static "exclude" array.
*/
if (exclude == 0) {
exclude = acl_argv_alloc(1);
acl_pthread_atexit_add(exclude, free_argv_fn);
/*
* Iterate over the probe list
*/
for (probe = cipher_probes; probe->ssl_name; ++probe) {
/* No exclusions if evp_name is a valid algorithm */
if (EVP_get_cipherbyname(probe->evp_name))
continue;
/*
* Sadly there is no SSL_CTX_get_ciphers() interface, so we are
* forced to allocate and free an SSL object. Fatal error if we
* can't allocate the SSL object.
*/
ERR_clear_error();
if (s == 0 && (s = SSL_new(ctx)) == 0) {
tls_print_errors();
acl_msg_fatal("%s: error allocating SSL object", myname);
}
/*
* Cipher is not supported by libcrypto, nothing to do if also
* not supported by libssl. Flush the OpenSSL error stack.
*
* XXX: There may be additional places in pre-existing code where
* SSL errors are generated and ignored, that require a similar
* "flush". Better yet, is to always flush before calls that run
* tls_print_errors() on failure.
*
* Contrary to documentation, on SunOS 5.10 SSL_set_cipher_list()
* returns success with no ciphers selected, when this happens
* SSL_get_ciphers() produces a stack with 0 elements!
*/
if (SSL_set_cipher_list(s, probe->ssl_name) == 0
|| (ciphers = SSL_get_ciphers(s)) == 0
|| (num = sk_SSL_CIPHER_num(ciphers)) == 0) {
ERR_clear_error(); /* flush any generated errors */
continue;
}
for (i = 0; i < num; ++i) {
c = sk_SSL_CIPHER_value(ciphers, i);
(void) SSL_CIPHER_get_bits(c, &alg_bits);
if (alg_bits == probe->alg_bits)
acl_argv_add(exclude, SSL_CIPHER_get_name(c), ACL_ARGV_END);
}
}
if (s != 0)
SSL_free(s);
}
for (i = 0; i < exclude->argc; ++i)
acl_vstring_sprintf_append(buf, ":!%s", exclude->argv[i]);
return (acl_vstring_str(buf));
}
/* tls_apply_cipher_list - update SSL_CTX cipher list */
static const char *tls_apply_cipher_list(TLS_APPL_STATE *app_ctx,
const char *context, ACL_VSTRING *spec)
{
const char *new = tls_exclude_missing(app_ctx->ssl_ctx, spec);
ERR_clear_error();
if (SSL_CTX_set_cipher_list(app_ctx->ssl_ctx, new) == 0) {
tls_print_errors();
acl_vstring_sprintf(app_ctx->why, "invalid %s cipher list: \"%s\"",
context, new);
return (0);
}
return (new);
}
/* tls_protocol_mask - Bitmask of protocols to exclude */
int tls_protocol_mask(const char *plist)
{
char *save;
char *tok;
char *cp;
int code;
int exclude = 0;
int include = 0;
save = cp = acl_mystrdup(plist);
while ((tok = acl_mystrtok(&cp, "\t\n\r ,:")) != 0) {
if (*tok == '!')
exclude |= code =
name_code(protocol_table, NAME_CODE_FLAG_NONE, ++tok);
else
include |= code =
name_code(protocol_table, NAME_CODE_FLAG_NONE, tok);
if (code == TLS_PROTOCOL_INVALID)
return TLS_PROTOCOL_INVALID;
}
acl_myfree(save);
/*
* When the include list is empty, use only the explicit exclusions.
* Otherwise, also exclude the complement of the include list from the
* built-in list of known protocols. There is no way to exclude protocols
* we don't know about at compile time, and this is unavoidable because
* the OpenSSL API works with compile-time *exclusion* bit-masks.
*/
return (include ? (exclude | (TLS_KNOWN_PROTOCOLS & ~include)) : exclude);
}
static void free_vstring_fn(void *arg)
{
ACL_VSTRING *s = (ACL_VSTRING*) arg;
acl_vstring_free(s);
}
/* tls_set_ciphers - Set SSL context cipher list */
const char *tls_set_ciphers(TLS_APPL_STATE *app_ctx, const char *context,
const char *grade, const char *exclusions)
{
const char *myname = "tls_set_ciphers";
static __thread ACL_VSTRING *buf;
int new_grade;
char *save;
char *cp;
char *tok;
const char *new_list;
new_grade = tls_cipher_grade(grade);
if (new_grade == TLS_CIPHER_NONE) {
acl_vstring_sprintf(app_ctx->why, "invalid %s cipher grade: \"%s\"",
context, grade);
return (0);
}
if (buf == 0) {
buf = acl_vstring_alloc(10);
acl_pthread_atexit_add(buf, free_vstring_fn);
}
ACL_VSTRING_RESET(buf);
/*
* Given cached state and identical input, we return the same result.
*/
if (app_ctx->cipher_list) {
if (new_grade == app_ctx->cipher_grade
&& strcmp(app_ctx->cipher_exclusions, exclusions) == 0)
return (app_ctx->cipher_list);
/* Change required, flush cached state */
app_ctx->cipher_grade = TLS_CIPHER_NONE;
acl_myfree(app_ctx->cipher_exclusions);
app_ctx->cipher_exclusions = 0;
acl_myfree(app_ctx->cipher_list);
app_ctx->cipher_list = 0;
}
switch (new_grade) {
case TLS_CIPHER_HIGH:
acl_vstring_strcpy(buf, var_tls_high_clist);
break;
case TLS_CIPHER_MEDIUM:
acl_vstring_strcpy(buf, var_tls_medium_clist);
break;
case TLS_CIPHER_LOW:
acl_vstring_strcpy(buf, var_tls_low_clist);
break;
case TLS_CIPHER_EXPORT:
acl_vstring_strcpy(buf, var_tls_export_clist);
break;
case TLS_CIPHER_NULL:
acl_vstring_strcpy(buf, var_tls_null_clist);
break;
default:
/*
* The caller MUST provide a valid cipher grade
*/
acl_msg_panic("invalid %s cipher grade: %d", context, new_grade);
}
/*
* The base lists for each grade can't be empty.
*/
if (ACL_VSTRING_LEN(buf) == 0)
acl_msg_panic("%s: empty \"%s\" cipherlist", myname, grade);
/*
* Apply locally-specified exclusions.
*/
#define CIPHER_SEP "\t\n\r ,:"
if (exclusions != 0) {
cp = save = acl_mystrdup(exclusions);
while ((tok = acl_mystrtok(&cp, CIPHER_SEP)) != 0) {
/*
* Can't exclude ciphers that start with modifiers.
*/
if (strchr("!+-@", *tok)) {
acl_vstring_sprintf(app_ctx->why,
"invalid unary '!+-@' in %s cipher "
"exclusion: \"%s\"", context, tok);
return (0);
}
acl_vstring_sprintf_append(buf, ":!%s", tok);
}
acl_myfree(save);
}
if ((new_list = tls_apply_cipher_list(app_ctx, context, buf)) == 0)
return (0);
/* Cache new state */
app_ctx->cipher_grade = new_grade;
app_ctx->cipher_exclusions = acl_mystrdup(exclusions);
return (app_ctx->cipher_list = acl_mystrdup(new_list));
}
/* tls_alloc_app_context - allocate TLS application context */
TLS_APPL_STATE *tls_alloc_app_context(SSL_CTX *ssl_ctx)
{
TLS_APPL_STATE *app_ctx;
app_ctx = (TLS_APPL_STATE *) acl_mymalloc(sizeof(*app_ctx));
memset((char *) app_ctx, 0, sizeof(*app_ctx));
app_ctx->ssl_ctx = ssl_ctx;
/* See also: cache purging code in tls_set_ciphers(). */
app_ctx->cipher_grade = TLS_CIPHER_NONE;
app_ctx->cipher_exclusions = 0;
app_ctx->cipher_list = 0;
app_ctx->cache_type = 0;
app_ctx->why = acl_vstring_alloc(1);
return (app_ctx);
}
/* tls_free_app_context - Free TLS application context */
void tls_free_app_context(TLS_APPL_STATE *app_ctx)
{
if (app_ctx->ssl_ctx)
SSL_CTX_free(app_ctx->ssl_ctx);
if (app_ctx->cache_type)
acl_myfree(app_ctx->cache_type);
/* See also: cache purging code in tls_set_ciphers(). */
if (app_ctx->cipher_exclusions)
acl_myfree(app_ctx->cipher_exclusions);
if (app_ctx->cipher_list)
acl_myfree(app_ctx->cipher_list);
if (app_ctx->why)
acl_vstring_free(app_ctx->why);
acl_myfree(app_ctx);
}
/* tls_alloc_sess_context - allocate TLS session context */
TLS_SESS_STATE *tls_alloc_sess_context(int log_level, const char *namaddr)
{
TLS_SESS_STATE *TLScontext;
/*
* PORTABILITY: Do not assume that null pointers are all-zero bits. Use
* explicit assignments to initialize pointers.
*
* See the C language FAQ item 5.17, or if you have time to burn,
* http://www.google.com/search?q=zero+bit+null+pointer
*
* However, it's OK to use memset() to zero integer values.
*/
TLScontext = (TLS_SESS_STATE *) acl_mymalloc(sizeof(TLS_SESS_STATE));
memset((char *) TLScontext, 0, sizeof(*TLScontext));
TLScontext->con = 0;
TLScontext->internal_bio = 0;
TLScontext->network_bio = 0;
TLScontext->cache_type = 0;
TLScontext->serverid = 0;
TLScontext->peer_CN = 0;
TLScontext->issuer_CN = 0;
TLScontext->peer_fingerprint = 0;
TLScontext->protocol = 0;
TLScontext->cipher_name = 0;
TLScontext->log_level = log_level;
TLScontext->namaddr = acl_lowercase(acl_mystrdup(namaddr));
return (TLScontext);
}
/* tls_free_context - deallocate TLScontext and members */
void tls_free_context(TLS_SESS_STATE *TLScontext)
{
/*
* Free the SSL structure and the BIOs. Warning: the internal_bio is
* connected to the SSL structure and is automatically freed with it. Do
* not free it again (core dump)!! Only free the network_bio.
*/
if (TLScontext->con != 0)
SSL_free(TLScontext->con);
if (TLScontext->network_bio)
BIO_free(TLScontext->network_bio);
if (TLScontext->namaddr)
acl_myfree(TLScontext->namaddr);
if (TLScontext->serverid)
acl_myfree(TLScontext->serverid);
if (TLScontext->peer_CN)
acl_myfree(TLScontext->peer_CN);
if (TLScontext->issuer_CN)
acl_myfree(TLScontext->issuer_CN);
if (TLScontext->peer_fingerprint)
acl_myfree(TLScontext->peer_fingerprint);
acl_myfree(TLScontext);
}
/* tls_version_split - Split OpenSSL version number into major, minor, ... */
static void tls_version_split(long version, TLS_VINFO *info)
{
/*
* OPENSSL_VERSION_NUMBER(3):
*
* OPENSSL_VERSION_NUMBER is a numeric release version identifier:
*
* MMNNFFPPS: major minor fix patch status
*
* The status nibble has one of the values 0 for development, 1 to e for
* betas 1 to 14, and f for release. Parsed OpenSSL version number. for
* example
*
* 0x000906000 == 0.9.6 dev 0x000906023 == 0.9.6b beta 3 0x00090605f ==
* 0.9.6e release
*
* Versions prior to 0.9.3 have identifiers < 0x0930. Versions between
* 0.9.3 and 0.9.5 had a version identifier with this interpretation:
*
* MMNNFFRBB major minor fix final beta/patch
*
* for example
*
* 0x000904100 == 0.9.4 release 0x000905000 == 0.9.5 dev
*
* Version 0.9.5a had an interim interpretation that is like the current
* one, except the patch level got the highest bit set, to keep continu-
* ity. The number was therefore 0x0090581f.
*/
if (version < 0x0930) {
info->status = 0;
info->patch = version & 0x0f;
version >>= 4;
info->micro = version & 0x0f;
version >>= 4;
info->minor = version & 0x0f;
version >>= 4;
info->major = version & 0x0f;
} else if (version < 0x00905800L) {
info->patch = version & 0xff;
version >>= 8;
info->status = version & 0xf;
version >>= 4;
info->micro = version & 0xff;
version >>= 8;
info->minor = version & 0xff;
version >>= 8;
info->major = version & 0xff;
} else {
info->status = version & 0xf;
version >>= 4;
info->patch = version & 0xff;
version >>= 8;
info->micro = version & 0xff;
version >>= 8;
info->minor = version & 0xff;
version >>= 8;
info->major = version & 0xff;
if (version < 0x00906000L)
info->patch &= ~0x80;
}
}
/* tls_check_version - Detect mismatch between headers and library. */
void tls_check_version(void)
{
const char *myname = "tls_check_version";
TLS_VINFO hdr_info;
TLS_VINFO lib_info;
tls_version_split(OPENSSL_VERSION_NUMBER, &hdr_info);
tls_version_split(SSLeay(), &lib_info);
if (lib_info.major != hdr_info.major
|| lib_info.minor != hdr_info.minor
|| lib_info.micro != hdr_info.micro)
acl_msg_warn("%s: run-time library vs. compile-time header version mismatch: "
"OpenSSL %d.%d.%d may not be compatible with OpenSSL %d.%d.%d",
myname, lib_info.major, lib_info.minor, lib_info.micro,
hdr_info.major, hdr_info.minor, hdr_info.micro);
}
/* tls_bug_bits - SSL bug compatibility bits for this OpenSSL version */
long tls_bug_bits(void)
{
long bits = SSL_OP_ALL; /* Work around all known bugs */
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
long lib_version = SSLeay();
/*
* In OpenSSL 0.9.8[ab], enabling zlib compression breaks the padding bug
* work-around, leading to false positives and failed connections. We may
* not interoperate with systems with the bug, but this is better than
* breaking on all 0.9.8[ab] systems that have zlib support enabled.
*/
if (lib_version >= 0x00908000L && lib_version <= 0x0090802fL) {
STACK_OF(SSL_COMP) * comp_methods;
comp_methods = SSL_COMP_get_compression_methods();
if (comp_methods != 0 && sk_SSL_COMP_num(comp_methods) > 0)
bits &= ~SSL_OP_TLS_BLOCK_PADDING_BUG;
}
#endif
return (bits);
}
/* tls_print_errors - print and clear the error stack */
void tls_print_errors(void)
{
const char *myname = "tls_print_errors";
unsigned long err;
char buffer[1024]; /* XXX */
const char *file;
const char *data;
int line;
int flags;
unsigned long thread;
/*
while ((err = ERR_get_error()) != 0) {
ERR_error_string(err, buffer);
acl_msg_warn("%s: TLS library warning: %lu|%s", __FUNCTION__, err, buffer);
}
*/
thread = CRYPTO_thread_id();
while ((err = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
ERR_error_string_n(err, buffer, sizeof(buffer));
if (flags & ERR_TXT_STRING)
acl_msg_warn("%s: TLS library problem: %lu|%s|%s|%d|%s|",
myname, thread, buffer, file, line, data);
else
acl_msg_warn("%s: TLS library problem: %lu|%s|%s|%d|",
myname, thread, buffer, file, line);
}
}
/* tls_info_callback - callback for logging SSL events via Postfix */
void tls_info_callback(const SSL *s, int where, int ret)
{
char *str;
int w;
/* Adapted from OpenSSL apps/s_cb.c. */
w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT)
str = "SSL_connect";
else if (w & SSL_ST_ACCEPT)
str = "SSL_accept";
else
str = "unknown";
if (where & SSL_CB_LOOP) {
acl_msg_info("%s:%s", str, SSL_state_string_long((const SSL *) s));
} else if (where & SSL_CB_ALERT) {
str = (where & SSL_CB_READ) ? "read" : "write";
if ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY)
acl_msg_info("SSL3 alert %s:%s:%s", str,
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
} else if (where & SSL_CB_EXIT) {
if (ret == 0)
acl_msg_info("%s:failed in %s",
str, SSL_state_string_long((const SSL *) s));
else if (ret < 0) {
#ifndef LOG_NON_ERROR_STATES
switch (SSL_get_error((SSL *) s, ret)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* Don't log non-error states. */
break;
default:
#endif
acl_msg_info("%s:error in %s",
str, SSL_state_string_long((SSL *) s));
#ifndef LOG_NON_ERROR_STATES
}
#endif
}
}
}
/*
* taken from OpenSSL crypto/bio/b_dump.c.
*
* Modified to save a lot of strcpy and strcat by Matti Aarnio.
*
* Rewritten by Wietse to elimate fixed-size stack buffer, array index
* multiplication and division, sprintf() and strcpy(), and lots of strlen()
* calls. We could make it a little faster by using a fixed-size stack-based
* buffer.
*
* 200412 - use %lx to print pointers, after casting them to unsigned long.
*/
#define TRUNCATE_SPACE_NULL
#define DUMP_WIDTH 16
#define VERT_SPLIT 7
static void tls_dump_buffer(const unsigned char *start, int len)
{
ACL_VSTRING *buf = acl_vstring_alloc(100);
const unsigned char *last = start + len - 1;
const unsigned char *row;
const unsigned char *col;
int ch;
#ifdef TRUNCATE_SPACE_NULL
while (last >= start && (*last == ' ' || *last == 0))
last--;
#endif
for (row = start; row <= last; row += DUMP_WIDTH) {
ACL_VSTRING_RESET(buf);
acl_vstring_sprintf(buf, "%04lx ", (unsigned long) (row - start));
for (col = row; col < row + DUMP_WIDTH; col++) {
if (col > last) {
acl_vstring_strcat(buf, " ");
} else {
ch = *col;
acl_vstring_sprintf_append(buf, "%02x%c",
ch, col - row == VERT_SPLIT ? '|' : ' ');
}
}
ACL_VSTRING_ADDCH(buf, ' ');
for (col = row; col < row + DUMP_WIDTH; col++) {
if (col > last)
break;
ch = *col;
if (!ACL_ISPRINT(ch))
ch = '.';
ACL_VSTRING_ADDCH(buf, ch);
if (col - row == VERT_SPLIT)
ACL_VSTRING_ADDCH(buf, ' ');
}
ACL_VSTRING_TERMINATE(buf);
acl_msg_info("%s", acl_vstring_str(buf));
}
#ifdef TRUNCATE_SPACE_NULL
if ((last + 1) - start < len)
acl_msg_info("%04lx - <SPACES/NULLS>",
(unsigned long) ((last + 1) - start));
#endif
acl_vstring_free(buf);
}
/* taken from OpenSSL apps/s_cb.c */
long tls_bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi,
long unused_argl acl_unused, long ret)
{
if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
acl_msg_info("read from %08lX [%08lX] (%d bytes => %ld (0x%lX))",
(unsigned long) bio, (unsigned long) argp, argi,
ret, (unsigned long) ret);
tls_dump_buffer((const unsigned char *) argp, (int) ret);
} else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
acl_msg_info("write to %08lX [%08lX] (%d bytes => %ld (0x%lX))",
(unsigned long) bio, (unsigned long) argp, argi,
ret, (unsigned long) ret);
tls_dump_buffer((const unsigned char *) argp, (int) ret);
}
return (ret);
}
#else
/*
* Broken linker workaround.
*/
int tls_dummy_for_broken_linkers;
#endif