openssl/crypto/err/err_prn.c
Richard Levitte 71f2994b15 ERR: special case system errors
Because system errors can be any positive number that fits in an 'int'
according to POSIX, we can't reasonably expect them to be in the 1..127
range, even though that's the most usual.

Instead of packing them into the OpenSSL error code structure, we
recognise them as a special case and mark them as such by storing them
in our error queue with the highest bit set.  We make OpenSSL specific
error records have their highest bit cleared, and in doing so, we
shift down the library section of the code by one bit.  This still
leaves a very large section for the reason codes.

Of course, we must adapt the error code and reason string extraction
and printing functions accordingly.

With this, we also thrown away the pre-loaded array of system error
strings, and extract them from the system when needed instead, i.e.
when we create error strings.

Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/12343)
2020-07-05 21:13:03 +02:00

207 lines
6.5 KiB
C

/*
* Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/* TODO: When ERR_STATE becomes opaque, this musts be removed */
#define OSSL_FORCE_ERR_STATE
#include <stdio.h>
#include "internal/cryptlib.h"
#include <openssl/crypto.h>
#include <openssl/buffer.h>
#include <openssl/err.h>
#include "err_local.h"
#define ERR_PRINT_BUF_SIZE 4096
void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u),
void *u)
{
CRYPTO_THREAD_ID tid = CRYPTO_THREAD_get_current_id();
unsigned long l;
const char *file, *data, *func;
int line, flags;
while ((l = ERR_get_error_all(&file, &line, &func, &data, &flags)) != 0) {
char buf[ERR_PRINT_BUF_SIZE], *hex;
const char *lib, *reason = NULL;
char rsbuf[256];
unsigned long r = ERR_GET_REASON(l);
lib = ERR_lib_error_string(l);
/*
* ERR_reason_error_string() can't safely return system error strings,
* since it would call openssl_strerror_r(), which needs a buffer for
* thread safety. So for system errors, we call openssl_strerror_r()
* directly instead.
*/
if (ERR_SYSTEM_ERROR(l)) {
if (openssl_strerror_r(r, rsbuf, sizeof(rsbuf)))
reason = rsbuf;
} else {
reason = ERR_reason_error_string(l);
}
if (func == NULL)
func = "unknown function";
if (reason == NULL) {
BIO_snprintf(rsbuf, sizeof(rsbuf), "reason(%lu)", r);
reason = rsbuf;
}
if ((flags & ERR_TXT_STRING) == 0)
data = "";
hex = openssl_buf2hexstr_sep((const unsigned char *)&tid, sizeof(tid),
'\0');
BIO_snprintf(buf, sizeof(buf), "%s:error::%s:%s:%s:%s:%d:%s\n",
hex == NULL ? "<null>" : hex, lib, func, reason, file,
line, data);
OPENSSL_free(hex);
if (cb(buf, strlen(buf), u) <= 0)
break; /* abort outputting the error report */
}
}
/* auxiliary function for incrementally reporting texts via the error queue */
static void put_error(int lib, const char *func, int reason,
const char *file, int line)
{
ERR_new();
ERR_set_debug(file, line, func);
ERR_set_error(lib, reason, NULL /* no data here, so fmt is NULL */);
}
#define TYPICAL_MAX_OUTPUT_BEFORE_DATA 100
#define MAX_DATA_LEN (ERR_PRINT_BUF_SIZE - TYPICAL_MAX_OUTPUT_BEFORE_DATA)
void ERR_add_error_txt(const char *separator, const char *txt)
{
const char *file = NULL;
int line;
const char *func = NULL;
const char *data = NULL;
int flags;
unsigned long err = ERR_peek_last_error();
if (separator == NULL)
separator = "";
if (err == 0)
put_error(ERR_LIB_NONE, NULL, 0, "", 0);
do {
size_t available_len, data_len;
const char *curr = txt, *next = txt;
const char *leading_separator = separator;
int trailing_separator = 0;
char *tmp;
ERR_peek_last_error_all(&file, &line, &func, &data, &flags);
if ((flags & ERR_TXT_STRING) == 0) {
data = "";
leading_separator = "";
}
data_len = strlen(data);
/* workaround for limit of ERR_print_errors_cb() */
if (data_len >= MAX_DATA_LEN
|| strlen(separator) >= (size_t)(MAX_DATA_LEN - data_len))
available_len = 0;
else
available_len = MAX_DATA_LEN - data_len - strlen(separator) - 1;
/* MAX_DATA_LEN > available_len >= 0 */
if (*separator == '\0') {
const size_t len_next = strlen(next);
if (len_next <= available_len) {
next += len_next;
curr = NULL; /* no need to split */
} else {
next += available_len;
curr = next; /* will split at this point */
}
} else {
while (*next != '\0' && (size_t)(next - txt) <= available_len) {
curr = next;
next = strstr(curr, separator);
if (next != NULL) {
next += strlen(separator);
trailing_separator = *next == '\0';
} else {
next = curr + strlen(curr);
}
}
if ((size_t)(next - txt) <= available_len)
curr = NULL; /* the above loop implies *next == '\0' */
}
if (curr != NULL) {
/* split error msg at curr since error data would get too long */
if (curr != txt) {
tmp = OPENSSL_strndup(txt, curr - txt);
if (tmp == NULL)
return;
ERR_add_error_data(2, separator, tmp);
OPENSSL_free(tmp);
}
put_error(ERR_GET_LIB(err), func, err, file, line);
txt = curr;
} else {
if (trailing_separator) {
tmp = OPENSSL_strndup(txt, next - strlen(separator) - txt);
if (tmp == NULL)
return;
/* output txt without the trailing separator */
ERR_add_error_data(2, leading_separator, tmp);
OPENSSL_free(tmp);
} else {
ERR_add_error_data(2, leading_separator, txt);
}
txt = next; /* finished */
}
} while (*txt != '\0');
}
void ERR_add_error_mem_bio(const char *separator, BIO *bio)
{
if (bio != NULL) {
char *str;
long len = BIO_get_mem_data(bio, &str);
if (len > 0) {
if (str[len - 1] != '\0') {
if (BIO_write(bio, "", 1) <= 0)
return;
len = BIO_get_mem_data(bio, &str);
}
if (len > 1)
ERR_add_error_txt(separator, str);
}
}
}
static int print_bio(const char *str, size_t len, void *bp)
{
return BIO_write((BIO *)bp, str, len);
}
void ERR_print_errors(BIO *bp)
{
ERR_print_errors_cb(print_bio, bp);
}
#ifndef OPENSSL_NO_STDIO
void ERR_print_errors_fp(FILE *fp)
{
BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);
if (bio == NULL)
return;
ERR_print_errors_cb(print_bio, bio);
BIO_free(bio);
}
#endif