FreeRDP/libfreerdp/crypto/certificate_store.c
akallabeth dac0ae4976
[crypto,cert] extend certificate store API
Use the certificate PEM without trustchain in the local trust store, but
keep the full PEM with chain for the user facing callbacks.
2024-08-13 15:56:08 +02:00

208 lines
5.2 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Certificate Handling
*
* Copyright 2011 Jiten Pathy
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.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 <winpr/assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <winpr/crypto.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/path.h>
#include <freerdp/settings.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/crypto/certificate_store.h>
#include <freerdp/log.h>
#define TAG FREERDP_TAG("crypto")
struct rdp_certificate_store
{
char* certs_path;
char* server_path;
};
static const char certificate_store_dir[] = "certs";
static const char certificate_server_dir[] = "server";
static char* freerdp_certificate_store_file_path(const rdpCertificateStore* store, const char* hash)
{
const char* hosts = freerdp_certificate_store_get_hosts_path(store);
if (!hosts || !hash)
return NULL;
return GetCombinedPath(hosts, hash);
}
freerdp_certificate_store_result
freerdp_certificate_store_contains_data(rdpCertificateStore* store, const rdpCertificateData* data)
{
freerdp_certificate_store_result rc = CERT_STORE_NOT_FOUND;
const char* host = freerdp_certificate_data_get_host(data);
const UINT16 port = freerdp_certificate_data_get_port(data);
rdpCertificateData* loaded = freerdp_certificate_store_load_data(store, host, port);
if (!loaded)
goto fail;
rc = freerdp_certificate_data_equal(data, loaded) ? CERT_STORE_MATCH : CERT_STORE_MISMATCH;
fail:
freerdp_certificate_data_free(loaded);
return rc;
}
BOOL freerdp_certificate_store_remove_data(rdpCertificateStore* store,
const rdpCertificateData* data)
{
BOOL rc = TRUE;
WINPR_ASSERT(store);
const char* hash = freerdp_certificate_data_get_hash(data);
if (!hash)
return FALSE;
char* path = freerdp_certificate_store_file_path(store, hash);
if (!path)
return FALSE;
if (winpr_PathFileExists(path))
rc = winpr_DeleteFile(path);
free(path);
return rc;
}
BOOL freerdp_certificate_store_save_data(rdpCertificateStore* store, const rdpCertificateData* data)
{
BOOL rc = FALSE;
const char* base = freerdp_certificate_store_get_hosts_path(store);
const char* hash = freerdp_certificate_data_get_hash(data);
char* path = freerdp_certificate_store_file_path(store, hash);
FILE* fp = NULL;
if (!winpr_PathFileExists(base))
{
if (!winpr_PathMakePath(base, NULL))
goto fail;
}
fp = winpr_fopen(path, "w");
if (!fp)
goto fail;
fprintf(fp, "%s", freerdp_certificate_data_get_pem_ex(data, FALSE));
rc = TRUE;
fail:
if (fp)
fclose(fp);
free(path);
return rc;
}
rdpCertificateData* freerdp_certificate_store_load_data(rdpCertificateStore* store,
const char* host, UINT16 port)
{
char* path = NULL;
rdpCertificateData* data = NULL;
WINPR_ASSERT(store);
path = freerdp_certificate_store_get_cert_path(store, host, port);
if (!path)
goto fail;
data = freerdp_certificate_data_new_from_file(host, port, path);
fail:
free(path);
return data;
}
rdpCertificateStore* freerdp_certificate_store_new(const rdpSettings* settings)
{
rdpCertificateStore* store = (rdpCertificateStore*)calloc(1, sizeof(rdpCertificateStore));
if (!store)
return NULL;
const char* base = freerdp_settings_get_string(settings, FreeRDP_ConfigPath);
if (!base)
goto fail;
store->certs_path = GetCombinedPath(base, certificate_store_dir);
store->server_path = GetCombinedPath(base, certificate_server_dir);
if (!store->certs_path || !store->server_path)
goto fail;
return store;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
freerdp_certificate_store_free(store);
WINPR_PRAGMA_DIAG_POP
return NULL;
}
void freerdp_certificate_store_free(rdpCertificateStore* store)
{
if (!store)
return;
free(store->certs_path);
free(store->server_path);
free(store);
}
const char* freerdp_certificate_store_get_certs_path(const rdpCertificateStore* store)
{
WINPR_ASSERT(store);
return store->certs_path;
}
const char* freerdp_certificate_store_get_hosts_path(const rdpCertificateStore* store)
{
WINPR_ASSERT(store);
return store->server_path;
}
char* freerdp_certificate_store_get_cert_path(const rdpCertificateStore* store, const char* host,
UINT16 port)
{
WINPR_ASSERT(store);
char* hash = freerdp_certificate_data_hash(host, port);
if (!hash)
return NULL;
char* path = freerdp_certificate_store_file_path(store, hash);
free(hash);
return path;
}