FreeRDP/libfreerdp/crypto/cert_common.c
akallabeth 6a7c375831 [crypto,cert] add better logging of certificate read
add missing log entries for possible failures due to invalid data
received. This allows better debugging if a server sends garbage or the
crypto routines have direct RSA routine access disabled.
2024-03-07 14:59:32 +01:00

230 lines
4.8 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Certificate Handling
*
* Copyright 2011 Jiten Pathy
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* Licensed 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.
*/
#include <freerdp/config.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <winpr/assert.h>
#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/crypto.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include "cert_common.h"
#include "crypto.h"
#include "opensslcompat.h"
#define TAG FREERDP_TAG("core")
static BOOL cert_info_allocate(rdpCertInfo* info, size_t size);
BOOL read_bignum(BYTE** dst, UINT32* length, const BIGNUM* num, BOOL alloc)
{
WINPR_ASSERT(dst);
WINPR_ASSERT(length);
WINPR_ASSERT(num);
if (alloc)
{
free(*dst);
*dst = NULL;
*length = 0;
}
const int len = BN_num_bytes(num);
if (len < 0)
return FALSE;
if (!alloc)
{
if (*length < (UINT32)len)
return FALSE;
}
if (len > 0)
{
if (alloc)
{
*dst = malloc((size_t)len);
if (!*dst)
return FALSE;
}
BN_bn2bin(num, *dst);
crypto_reverse(*dst, (size_t)len);
*length = (UINT32)len;
}
return TRUE;
}
BOOL cert_info_create(rdpCertInfo* dst, const BIGNUM* rsa, const BIGNUM* rsa_e)
{
const rdpCertInfo empty = { 0 };
WINPR_ASSERT(dst);
WINPR_ASSERT(rsa);
*dst = empty;
if (!read_bignum(&dst->Modulus, &dst->ModulusLength, rsa, TRUE))
goto fail;
UINT32 len = sizeof(dst->exponent);
BYTE* ptr = &dst->exponent[0];
if (!read_bignum(&ptr, &len, rsa_e, FALSE))
goto fail;
return TRUE;
fail:
cert_info_free(dst);
return FALSE;
}
BOOL cert_info_clone(rdpCertInfo* dst, const rdpCertInfo* src)
{
WINPR_ASSERT(dst);
WINPR_ASSERT(src);
*dst = *src;
dst->Modulus = NULL;
dst->ModulusLength = 0;
if (src->ModulusLength > 0)
{
dst->Modulus = malloc(src->ModulusLength);
if (!dst->Modulus)
return FALSE;
memcpy(dst->Modulus, src->Modulus, src->ModulusLength);
dst->ModulusLength = src->ModulusLength;
}
return TRUE;
}
void cert_info_free(rdpCertInfo* info)
{
WINPR_ASSERT(info);
free(info->Modulus);
info->ModulusLength = 0;
info->Modulus = NULL;
}
BOOL cert_info_allocate(rdpCertInfo* info, size_t size)
{
WINPR_ASSERT(info);
cert_info_free(info);
info->Modulus = (BYTE*)malloc(size);
if (!info->Modulus && (size > 0))
{
WLog_ERR(TAG, "Failed to allocate info->Modulus of size %" PRIuz, size);
return FALSE;
}
info->ModulusLength = (UINT32)size;
return TRUE;
}
BOOL cert_info_read_modulus(rdpCertInfo* info, size_t size, wStream* s)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
return FALSE;
if (size > UINT32_MAX)
{
WLog_ERR(TAG, "modulus size %" PRIuz " exceeds limit of %" PRIu32, size, UINT32_MAX);
return FALSE;
}
if (!cert_info_allocate(info, size))
return FALSE;
Stream_Read(s, info->Modulus, info->ModulusLength);
return TRUE;
}
BOOL cert_info_read_exponent(rdpCertInfo* info, size_t size, wStream* s)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
return FALSE;
if (size > 4)
{
WLog_ERR(TAG, "exponent size %" PRIuz " exceeds limit of %" PRIu32, size, 4);
return FALSE;
}
if (!info->Modulus || (info->ModulusLength == 0))
{
WLog_ERR(TAG, "invalid modulus=%p [%" PRIu32 "]", info->Modulus, info->ModulusLength);
return FALSE;
}
Stream_Read(s, &info->exponent[4 - size], size);
crypto_reverse(info->Modulus, info->ModulusLength);
crypto_reverse(info->exponent, 4);
return TRUE;
}
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
X509* x509_from_rsa(const RSA* rsa)
{
EVP_PKEY* pubkey = NULL;
X509* x509 = NULL;
BIO* bio = BIO_new(
#if defined(LIBRESSL_VERSION_NUMBER)
BIO_s_mem()
#else
BIO_s_secmem()
#endif
);
if (!bio)
return NULL;
const int rc = PEM_write_bio_RSA_PUBKEY(bio, (RSA*)rsa);
if (rc != 1)
goto fail;
pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
if (!pubkey)
goto fail;
x509 = X509_new();
if (!x509)
goto fail;
const int res = X509_set_pubkey(x509, pubkey);
if (res != 1)
{
X509_free(x509);
x509 = NULL;
goto fail;
}
fail:
BIO_free_all(bio);
EVP_PKEY_free(pubkey);
return x509;
}
#endif