Blame libfreerdp/crypto/certificate.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit 1fb8d4
 * Certificate Handling
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2011 Jiten Pathy
Packit 1fb8d4
 * Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <errno.h>
Packit 1fb8d4
#include <stdio.h>
Packit 1fb8d4
#include <string.h>
Packit 1fb8d4
#include <ctype.h>
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crypto.h>
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
#include <winpr/file.h>
Packit 1fb8d4
#include <winpr/path.h>
Packit 1fb8d4
Packit 1fb8d4
#include <openssl/pem.h>
Packit 1fb8d4
#include <openssl/rsa.h>
Packit 1fb8d4
Packit 1fb8d4
static const char certificate_store_dir[] = "certs";
Packit 1fb8d4
static const char certificate_server_dir[] = "server";
Packit 1fb8d4
static const char certificate_known_hosts_file[] = "known_hosts2";
Packit 1fb8d4
static const char certificate_legacy_hosts_file[] = "known_hosts";
Packit 1fb8d4
Packit 1fb8d4
#include <freerdp/log.h>
Packit 1fb8d4
#include <freerdp/crypto/certificate.h>
Packit 1fb8d4
Packit 1fb8d4
#define TAG FREERDP_TAG("crypto")
Packit 1fb8d4
Packit Service 5a9772
static BOOL certificate_split_line(char* line, char** host, UINT16* port, char** subject,
Packit Service 5a9772
                                   char** issuer, char** fingerprint);
Packit 1fb8d4
static BOOL certificate_line_is_comment(const char* line, size_t length)
Packit 1fb8d4
{
Packit Service 5a9772
	while (length > 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		switch (*line)
Packit 1fb8d4
		{
Packit Service 5a9772
			case ' ':
Packit Service 5a9772
			case '\t':
Packit Service 5a9772
				line++;
Packit Service 5a9772
				length--;
Packit Service 5a9772
				break;
Packit Service 5a9772
Packit Service 5a9772
			case '#':
Packit Service 5a9772
				return TRUE;
Packit Service 5a9772
Packit Service 5a9772
			default:
Packit Service 5a9772
				return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL certificate_store_init(rdpCertificateStore* certificate_store)
Packit 1fb8d4
{
Packit 1fb8d4
	char* server_path = NULL;
Packit 1fb8d4
	rdpSettings* settings;
Packit 1fb8d4
	settings = certificate_store->settings;
Packit 1fb8d4
Packit 1fb8d4
	if (!PathFileExistsA(settings->ConfigPath))
Packit 1fb8d4
	{
Packit 1fb8d4
		if (!PathMakePathA(settings->ConfigPath, 0))
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "error creating directory '%s'", settings->ConfigPath);
Packit 1fb8d4
			goto fail;
Packit 1fb8d4
		}
Packit Service 5a9772
Packit 1fb8d4
		WLog_INFO(TAG, "creating directory %s", settings->ConfigPath);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	if (!(certificate_store->path =
Packit Service 5a9772
	          GetCombinedPath(settings->ConfigPath, (char*)certificate_store_dir)))
Packit 1fb8d4
		goto fail;
Packit 1fb8d4
Packit 1fb8d4
	if (!PathFileExistsA(certificate_store->path))
Packit 1fb8d4
	{
Packit 1fb8d4
		if (!PathMakePathA(certificate_store->path, 0))
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "error creating directory [%s]", certificate_store->path);
Packit 1fb8d4
			goto fail;
Packit 1fb8d4
		}
Packit Service 5a9772
Packit 1fb8d4
		WLog_INFO(TAG, "creating directory [%s]", certificate_store->path);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	if (!(server_path = GetCombinedPath(settings->ConfigPath, (char*)certificate_server_dir)))
Packit 1fb8d4
		goto fail;
Packit 1fb8d4
Packit 1fb8d4
	if (!PathFileExistsA(server_path))
Packit 1fb8d4
	{
Packit 1fb8d4
		if (!PathMakePathA(server_path, 0))
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "error creating directory [%s]", server_path);
Packit 1fb8d4
			goto fail;
Packit 1fb8d4
		}
Packit Service 5a9772
Packit 1fb8d4
		WLog_INFO(TAG, "created directory [%s]", server_path);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	if (!(certificate_store->file =
Packit Service 5a9772
	          GetCombinedPath(settings->ConfigPath, (char*)certificate_known_hosts_file)))
Packit 1fb8d4
		goto fail;
Packit 1fb8d4
Packit Service 5a9772
	if (!(certificate_store->legacy_file =
Packit Service 5a9772
	          GetCombinedPath(settings->ConfigPath, (char*)certificate_legacy_hosts_file)))
Packit 1fb8d4
		goto fail;
Packit 1fb8d4
Packit 1fb8d4
	free(server_path);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
fail:
Packit 1fb8d4
	WLog_ERR(TAG, "certificate store initialization failed");
Packit 1fb8d4
	free(server_path);
Packit 1fb8d4
	free(certificate_store->path);
Packit 1fb8d4
	free(certificate_store->file);
Packit 1fb8d4
	certificate_store->path = NULL;
Packit 1fb8d4
	certificate_store->file = NULL;
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
Packit Service 5a9772
                                         rdpCertificateData* certificate_data)
Packit 1fb8d4
{
Packit 1fb8d4
	HANDLE fp;
Packit 1fb8d4
	int match = 1;
Packit 1fb8d4
	char* data;
Packit 1fb8d4
	char* mdata;
Packit 1fb8d4
	char* pline;
Packit 1fb8d4
	char* hostname = NULL;
Packit 1fb8d4
	DWORD lowSize, highSize;
Packit 1fb8d4
	UINT64 size;
Packit 1fb8d4
	size_t length;
Packit 1fb8d4
	DWORD read;
Packit 1fb8d4
	/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
Packit Service 5a9772
	PathCchConvertStyleA(certificate_store->legacy_file, strlen(certificate_store->legacy_file),
Packit Service 5a9772
	                     PATH_STYLE_UNIX);
Packit Service 5a9772
	fp = CreateFileA(certificate_store->legacy_file, GENERIC_READ, FILE_SHARE_READ, NULL,
Packit Service 5a9772
	                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (fp == INVALID_HANDLE_VALUE)
Packit 1fb8d4
		return match;
Packit 1fb8d4
Packit 1fb8d4
	if ((lowSize = GetFileSize(fp, &highSize)) == INVALID_FILE_SIZE)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "GetFileSize(%s) returned %s [0x%08" PRIX32 "]",
Packit Service 5a9772
		         certificate_store->legacy_file, strerror(errno), GetLastError());
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit Service 5a9772
Packit 1fb8d4
	size = (UINT64)lowSize | ((UINT64)highSize << 32);
Packit 1fb8d4
Packit 1fb8d4
	if (size < 1)
Packit 1fb8d4
	{
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	mdata = (char*)malloc(size + 2);
Packit Service 5a9772
Packit 1fb8d4
	if (!mdata)
Packit 1fb8d4
	{
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	data = mdata;
Packit Service 5a9772
Packit 1fb8d4
	if (!ReadFile(fp, data, size, &read, NULL) || (read != size))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(data);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	CloseHandle(fp);
Packit 1fb8d4
	data[size] = '\n';
Packit 1fb8d4
	data[size + 1] = '\0';
Packit 1fb8d4
	pline = StrSep(&data, "\r\n");
Packit 1fb8d4
Packit 1fb8d4
	while (pline != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		length = strlen(pline);
Packit 1fb8d4
Packit 1fb8d4
		if (length > 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			hostname = StrSep(&pline, " \t");
Packit Service 5a9772
Packit 1fb8d4
			if (!hostname || !pline)
Packit Service 5a9772
				WLog_WARN(TAG, "Invalid %s entry %s %s!", certificate_legacy_hosts_file, hostname,
Packit Service 5a9772
				          pline);
Packit 1fb8d4
			else if (strcmp(hostname, certificate_data->hostname) == 0)
Packit 1fb8d4
			{
Packit 1fb8d4
				const int diff = strcmp(pline, certificate_data->fingerprint);
Packit 1fb8d4
				match = (diff == 0) ? 0 : -1;
Packit 1fb8d4
				break;
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		pline = StrSep(&data, "\r\n");
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* Found a valid fingerprint in legacy file,
Packit 1fb8d4
	 * copy to new file in new format. */
Packit 1fb8d4
	if (0 == match)
Packit 1fb8d4
	{
Packit Service 5a9772
		rdpCertificateData* data = certificate_data_new(hostname, certificate_data->port, NULL,
Packit Service 5a9772
		                                                NULL, certificate_data->fingerprint);
Packit Service 5a9772
Packit 1fb8d4
		if (data)
Packit 1fb8d4
		{
Packit Service 5a9772
			free(data->subject);
Packit Service 5a9772
			free(data->issuer);
Packit 1fb8d4
			data->subject = NULL;
Packit 1fb8d4
			data->issuer = NULL;
Packit Service 5a9772
Packit 1fb8d4
			if (certificate_data->subject)
Packit 1fb8d4
			{
Packit 1fb8d4
				data->subject = _strdup(certificate_data->subject);
Packit Service 5a9772
Packit 1fb8d4
				if (!data->subject)
Packit 1fb8d4
					goto out;
Packit 1fb8d4
			}
Packit Service 5a9772
Packit 1fb8d4
			if (certificate_data->issuer)
Packit 1fb8d4
			{
Packit 1fb8d4
				data->issuer = _strdup(certificate_data->issuer);
Packit Service 5a9772
Packit 1fb8d4
				if (!data->issuer)
Packit 1fb8d4
					goto out;
Packit 1fb8d4
			}
Packit Service 5a9772
Packit 1fb8d4
			match = certificate_data_print(certificate_store, data) ? 0 : 1;
Packit 1fb8d4
		}
Packit Service 5a9772
Packit Service 5a9772
	out:
Packit 1fb8d4
		certificate_data_free(data);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	free(mdata);
Packit 1fb8d4
	return match;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
Packit Service 5a9772
                                      rdpCertificateData* certificate_data, char** psubject,
Packit Service 5a9772
                                      char** pissuer, char** fprint)
Packit 1fb8d4
{
Packit 1fb8d4
	BOOL found = FALSE;
Packit 1fb8d4
	HANDLE fp;
Packit 1fb8d4
	size_t length;
Packit 1fb8d4
	char* data;
Packit 1fb8d4
	char* mdata;
Packit 1fb8d4
	char* pline;
Packit 1fb8d4
	int match = 1;
Packit 1fb8d4
	DWORD lowSize, highSize;
Packit 1fb8d4
	UINT64 size;
Packit 1fb8d4
	char* hostname = NULL;
Packit 1fb8d4
	char* subject = NULL;
Packit 1fb8d4
	char* issuer = NULL;
Packit 1fb8d4
	char* fingerprint = NULL;
Packit 1fb8d4
	unsigned short port = 0;
Packit 1fb8d4
	DWORD read;
Packit 1fb8d4
	/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
Packit 1fb8d4
	PathCchConvertStyleA(certificate_store->file, strlen(certificate_store->file), PATH_STYLE_UNIX);
Packit Service 5a9772
	fp = CreateFileA(certificate_store->file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS,
Packit Service 5a9772
	                 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (fp == INVALID_HANDLE_VALUE)
Packit 1fb8d4
		return match;
Packit 1fb8d4
Packit 1fb8d4
	if ((lowSize = GetFileSize(fp, &highSize)) == INVALID_FILE_SIZE)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "GetFileSize(%s) returned %s [0x%08" PRIX32 "]",
Packit Service 5a9772
		         certificate_store->legacy_file, strerror(errno), GetLastError());
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit Service 5a9772
Packit 1fb8d4
	size = (UINT64)lowSize | ((UINT64)highSize << 32);
Packit 1fb8d4
Packit 1fb8d4
	if (size < 1)
Packit 1fb8d4
	{
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	mdata = (char*)malloc(size + 2);
Packit Service 5a9772
Packit 1fb8d4
	if (!mdata)
Packit 1fb8d4
	{
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	data = mdata;
Packit Service 5a9772
Packit 1fb8d4
	if (!ReadFile(fp, data, size, &read, NULL) || (read != size))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(data);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return match;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	CloseHandle(fp);
Packit 1fb8d4
	data[size] = '\n';
Packit 1fb8d4
	data[size + 1] = '\0';
Packit 1fb8d4
	pline = StrSep(&data, "\r\n");
Packit 1fb8d4
Packit 1fb8d4
	while (pline != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		length = strlen(pline);
Packit 1fb8d4
Packit 1fb8d4
		if (length > 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			if (certificate_line_is_comment(pline, length))
Packit 1fb8d4
			{
Packit 1fb8d4
			}
Packit Service 5a9772
			else if (!certificate_split_line(pline, &hostname, &port, &subject, &issuer,
Packit Service 5a9772
			                                 &fingerprint))
Packit Service 5a9772
				WLog_WARN(TAG, "Invalid %s entry %s!", certificate_known_hosts_file, pline);
Packit 1fb8d4
			else if (strcmp(pline, certificate_data->hostname) == 0)
Packit 1fb8d4
			{
Packit 1fb8d4
				int outLen;
Packit 1fb8d4
Packit 1fb8d4
				if (port == certificate_data->port)
Packit 1fb8d4
				{
Packit 1fb8d4
					found = TRUE;
Packit Service 5a9772
Packit 1fb8d4
					if (fingerprint)
Packit 1fb8d4
					{
Packit 1fb8d4
						match = (strcmp(certificate_data->fingerprint, fingerprint) == 0) ? 0 : -1;
Packit Service 5a9772
Packit 1fb8d4
						if (fprint)
Packit 1fb8d4
							*fprint = _strdup(fingerprint);
Packit 1fb8d4
					}
Packit Service 5a9772
Packit 1fb8d4
					if (subject && psubject)
Packit 1fb8d4
						crypto_base64_decode(subject, strlen(subject), (BYTE**)psubject, &outLen);
Packit Service 5a9772
Packit 1fb8d4
					if (issuer && pissuer)
Packit 1fb8d4
						crypto_base64_decode(issuer, strlen(issuer), (BYTE**)pissuer, &outLen);
Packit Service 5a9772
Packit 1fb8d4
					break;
Packit 1fb8d4
				}
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		pline = StrSep(&data, "\r\n");
Packit 1fb8d4
	}
Packit Service 5a9772
Packit 1fb8d4
	free(mdata);
Packit 1fb8d4
Packit 1fb8d4
	if ((match != 0) && !found)
Packit 1fb8d4
		match = certificate_data_match_legacy(certificate_store, certificate_data);
Packit 1fb8d4
Packit 1fb8d4
	return match;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL certificate_get_stored_data(rdpCertificateStore* certificate_store,
Packit Service 5a9772
                                 rdpCertificateData* certificate_data, char** subject,
Packit Service 5a9772
                                 char** issuer, char** fingerprint)
Packit 1fb8d4
{
Packit Service 5a9772
	int rc = certificate_data_match_raw(certificate_store, certificate_data, subject, issuer,
Packit Service 5a9772
	                                    fingerprint);
Packit 1fb8d4
Packit 1fb8d4
	if ((rc == 0) || (rc == -1))
Packit 1fb8d4
		return TRUE;
Packit Service 5a9772
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
int certificate_data_match(rdpCertificateStore* certificate_store,
Packit Service 5a9772
                           rdpCertificateData* certificate_data)
Packit 1fb8d4
{
Packit Service 5a9772
	return certificate_data_match_raw(certificate_store, certificate_data, NULL, NULL, NULL);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
Packit Service 5a9772
                              rdpCertificateData* certificate_data)
Packit 1fb8d4
{
Packit 1fb8d4
	HANDLE fp;
Packit 1fb8d4
	BOOL rc = FALSE;
Packit 1fb8d4
	size_t length;
Packit 1fb8d4
	char* data;
Packit 1fb8d4
	char* sdata;
Packit 1fb8d4
	char* pline;
Packit 1fb8d4
	UINT64 size;
Packit 1fb8d4
	DWORD read, written;
Packit 1fb8d4
	DWORD lowSize, highSize;
Packit 1fb8d4
	/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
Packit 1fb8d4
	PathCchConvertStyleA(certificate_store->file, strlen(certificate_store->file), PATH_STYLE_UNIX);
Packit Service 5a9772
	fp = CreateFileA(certificate_store->file, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
Packit Service 5a9772
	                 FILE_ATTRIBUTE_NORMAL, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (fp == INVALID_HANDLE_VALUE)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if ((lowSize = GetFileSize(fp, &highSize)) == INVALID_FILE_SIZE)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "GetFileSize(%s) returned %s [0x%08" PRIX32 "]",
Packit Service 5a9772
		         certificate_store->legacy_file, strerror(errno), GetLastError());
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit Service 5a9772
Packit 1fb8d4
	size = (UINT64)lowSize | ((UINT64)highSize << 32);
Packit 1fb8d4
Packit 1fb8d4
	if (size < 1)
Packit 1fb8d4
	{
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	data = (char*)malloc(size + 2);
Packit Service 5a9772
Packit 1fb8d4
	if (!data)
Packit 1fb8d4
	{
Packit Service 5a9772
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (!ReadFile(fp, data, size, &read, NULL) || (read != size))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(data);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (SetFilePointer(fp, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "SetFilePointer(%s) returned %s [0x%08" PRIX32 "]", certificate_store->file,
Packit Service 5a9772
		         strerror(errno), GetLastError());
Packit 1fb8d4
		free(data);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (!SetEndOfFile(fp))
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "SetEndOfFile(%s) returned %s [0x%08" PRIX32 "]", certificate_store->file,
Packit Service 5a9772
		         strerror(errno), GetLastError());
Packit 1fb8d4
		free(data);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* Write the file back out, with appropriate fingerprint substitutions */
Packit 1fb8d4
	data[size] = '\n';
Packit 1fb8d4
	data[size + 1] = '\0';
Packit 1fb8d4
	sdata = data;
Packit 1fb8d4
	pline = StrSep(&sdata, "\r\n");
Packit 1fb8d4
Packit 1fb8d4
	while (pline != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		length = strlen(pline);
Packit 1fb8d4
Packit 1fb8d4
		if (length > 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			UINT16 port = 0;
Packit 1fb8d4
			char* hostname = NULL;
Packit 1fb8d4
			char* fingerprint = NULL;
Packit 1fb8d4
			char* subject = NULL;
Packit 1fb8d4
			char* issuer = NULL;
Packit 1fb8d4
			char* tdata;
Packit 1fb8d4
Packit 1fb8d4
			if (certificate_line_is_comment(pline, length))
Packit 1fb8d4
			{
Packit 1fb8d4
			}
Packit Service 5a9772
			else if (!certificate_split_line(pline, &hostname, &port, &subject, &issuer,
Packit Service 5a9772
			                                 &fingerprint))
Packit Service 5a9772
				WLog_WARN(TAG, "Skipping invalid %s entry %s!", certificate_known_hosts_file,
Packit Service 5a9772
				          pline);
Packit 1fb8d4
			else
Packit 1fb8d4
			{
Packit Service 5a9772
				int res;
Packit Service 5a9772
Packit 1fb8d4
				/* If this is the replaced hostname, use the updated fingerprint. */
Packit 1fb8d4
				if ((strcmp(hostname, certificate_data->hostname) == 0) &&
Packit Service 5a9772
				    (port == certificate_data->port))
Packit 1fb8d4
				{
Packit 1fb8d4
					fingerprint = certificate_data->fingerprint;
Packit 1fb8d4
					rc = TRUE;
Packit 1fb8d4
				}
Packit 1fb8d4
Packit Service 5a9772
				res = _snprintf(NULL, 0, "%s %" PRIu16 " %s %s %s\n", hostname, port, fingerprint,
Packit Service 5a9772
				                subject, issuer);
Packit Service 5a9772
				if (res < 0)
Packit Service 5a9772
				{
Packit Service 5a9772
					free(data);
Packit Service 5a9772
					CloseHandle(fp);
Packit Service 5a9772
					return FALSE;
Packit Service 5a9772
				}
Packit Service 5a9772
				size = (size_t)res;
Packit Service 5a9772
Packit 1fb8d4
				tdata = malloc(size + 1);
Packit Service 5a9772
Packit 1fb8d4
				if (!tdata)
Packit 1fb8d4
				{
Packit Service 5a9772
					WLog_ERR(TAG, "malloc(%s) returned %s [0x%08X]", certificate_store->file,
Packit Service 5a9772
					         strerror(errno), errno);
Packit Service 5a9772
					free(data);
Packit Service 5a9772
					CloseHandle(fp);
Packit Service 5a9772
					return FALSE;
Packit Service 5a9772
				}
Packit Service 5a9772
Packit Service 5a9772
				res = _snprintf(tdata, size + 1, "%s %" PRIu16 " %s %s %s\n", hostname, port,
Packit Service 5a9772
				                fingerprint, subject, issuer);
Packit Service 5a9772
				if (res < 0)
Packit Service 5a9772
				{
Packit Service 5a9772
					free(tdata);
Packit 1fb8d4
					free(data);
Packit 1fb8d4
					CloseHandle(fp);
Packit 1fb8d4
					return FALSE;
Packit 1fb8d4
				}
Packit 1fb8d4
Packit Service 5a9772
				if ((size_t)res != size)
Packit 1fb8d4
				{
Packit Service 5a9772
					WLog_ERR(TAG, "_snprintf(%s) returned %s [0x%08X]", certificate_store->file,
Packit Service 5a9772
					         strerror(errno), errno);
Packit 1fb8d4
					free(tdata);
Packit 1fb8d4
					free(data);
Packit 1fb8d4
					CloseHandle(fp);
Packit 1fb8d4
					return FALSE;
Packit 1fb8d4
				}
Packit Service 5a9772
Packit 1fb8d4
				if (!WriteFile(fp, tdata, size, &written, NULL) || (written != size))
Packit 1fb8d4
				{
Packit Service 5a9772
					WLog_ERR(TAG, "WriteFile(%s) returned %s [0x%08X]", certificate_store->file,
Packit Service 5a9772
					         strerror(errno), errno);
Packit 1fb8d4
					free(tdata);
Packit 1fb8d4
					free(data);
Packit 1fb8d4
					CloseHandle(fp);
Packit 1fb8d4
					return FALSE;
Packit 1fb8d4
				}
Packit Service 5a9772
Packit 1fb8d4
				free(tdata);
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		pline = StrSep(&sdata, "\r\n");
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	CloseHandle(fp);
Packit 1fb8d4
	free(data);
Packit 1fb8d4
	return rc;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
BOOL certificate_split_line(char* line, char** host, UINT16* port, char** subject, char** issuer,
Packit Service 5a9772
                            char** fingerprint)
Packit 1fb8d4
{
Packit Service 5a9772
	char* cur;
Packit Service 5a9772
	size_t length = strlen(line);
Packit 1fb8d4
Packit Service 5a9772
	if (length <= 0)
Packit Service 5a9772
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	cur = StrSep(&line, " \t");
Packit 1fb8d4
Packit Service 5a9772
	if (!cur)
Packit Service 5a9772
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	*host = cur;
Packit Service 5a9772
	cur = StrSep(&line, " \t");
Packit 1fb8d4
Packit Service 5a9772
	if (!cur)
Packit Service 5a9772
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	if (sscanf(cur, "%hu", port) != 1)
Packit Service 5a9772
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	cur = StrSep(&line, " \t");
Packit 1fb8d4
Packit Service 5a9772
	if (!cur)
Packit Service 5a9772
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	*fingerprint = cur;
Packit Service 5a9772
	cur = StrSep(&line, " \t");
Packit 1fb8d4
Packit Service 5a9772
	if (!cur)
Packit Service 5a9772
		return FALSE;
Packit 1fb8d4
Packit Service 5a9772
	*subject = cur;
Packit Service 5a9772
	cur = StrSep(&line, " \t");
Packit Service 5a9772
Packit Service 5a9772
	if (!cur)
Packit Service 5a9772
		return FALSE;
Packit Service 5a9772
Packit Service 5a9772
	*issuer = cur;
Packit Service 5a9772
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
BOOL certificate_data_print(rdpCertificateStore* certificate_store,
Packit Service 5a9772
                            rdpCertificateData* certificate_data)
Packit 1fb8d4
{
Packit Service 5a9772
	int rc;
Packit 1fb8d4
	HANDLE fp;
Packit 1fb8d4
	char* tdata;
Packit Service 5a9772
	size_t size;
Packit 1fb8d4
	DWORD written;
Packit 1fb8d4
	/* reopen in append mode */
Packit 1fb8d4
	/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
Packit 1fb8d4
	PathCchConvertStyleA(certificate_store->file, strlen(certificate_store->file), PATH_STYLE_UNIX);
Packit Service 5a9772
	fp = CreateFileA(certificate_store->file, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
Packit Service 5a9772
	                 FILE_ATTRIBUTE_NORMAL, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (fp == INVALID_HANDLE_VALUE)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (SetFilePointer(fp, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "SetFilePointer(%s) returned %s [0x%08" PRIX32 "]", certificate_store->file,
Packit Service 5a9772
		         strerror(errno), GetLastError());
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	rc = _snprintf(NULL, 0, "%s %" PRIu16 " %s %s %s\n", certificate_data->hostname,
Packit Service 5a9772
	               certificate_data->port, certificate_data->fingerprint, certificate_data->subject,
Packit Service 5a9772
	               certificate_data->issuer);
Packit Service 5a9772
	if (rc < 0)
Packit Service 5a9772
		return FALSE;
Packit Service 5a9772
	size = (size_t)rc;
Packit Service 5a9772
Packit 1fb8d4
	tdata = malloc(size + 1);
Packit Service 5a9772
Packit 1fb8d4
	if (!tdata)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "malloc(%s) returned %s [0x%08X]", certificate_store->file, strerror(errno),
Packit Service 5a9772
		         errno);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit Service 5a9772
Packit Service 5a9772
	rc = _snprintf(tdata, size + 1, "%s %" PRIu16 " %s %s %s\n", certificate_data->hostname,
Packit Service 5a9772
	               certificate_data->port, certificate_data->fingerprint, certificate_data->subject,
Packit Service 5a9772
	               certificate_data->issuer);
Packit Service 5a9772
Packit Service 5a9772
	if ((rc < 0) || ((size_t)rc != size))
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "_snprintf(%s) returned %s [0x%08X]", certificate_store->file,
Packit Service 5a9772
		         strerror(errno), errno);
Packit 1fb8d4
		free(tdata);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit Service 5a9772
Packit 1fb8d4
	if (!WriteFile(fp, tdata, size, &written, NULL) || (written != size))
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "WriteFile(%s) returned %s [0x%08X]", certificate_store->file,
Packit Service 5a9772
		         strerror(errno), errno);
Packit 1fb8d4
		free(tdata);
Packit 1fb8d4
		CloseHandle(fp);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	free(tdata);
Packit 1fb8d4
	CloseHandle(fp);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
rdpCertificateData* certificate_data_new(const char* hostname, UINT16 port, const char* subject,
Packit Service 5a9772
                                         const char* issuer, const char* fingerprint)
Packit 1fb8d4
{
Packit 1fb8d4
	size_t i;
Packit 1fb8d4
	rdpCertificateData* certdata;
Packit 1fb8d4
Packit 1fb8d4
	if (!hostname)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (!fingerprint)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit Service 5a9772
	certdata = (rdpCertificateData*)calloc(1, sizeof(rdpCertificateData));
Packit Service 5a9772
Packit 1fb8d4
	if (!certdata)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	certdata->port = port;
Packit 1fb8d4
	certdata->hostname = _strdup(hostname);
Packit Service 5a9772
Packit 1fb8d4
	if (subject)
Packit Service 5a9772
		certdata->subject = crypto_base64_encode((const BYTE*)subject, strlen(subject));
Packit 1fb8d4
	else
Packit Service 5a9772
		certdata->subject = crypto_base64_encode((const BYTE*)"", 0);
Packit Service 5a9772
Packit 1fb8d4
	if (issuer)
Packit Service 5a9772
		certdata->issuer = crypto_base64_encode((const BYTE*)issuer, strlen(issuer));
Packit 1fb8d4
	else
Packit Service 5a9772
		certdata->issuer = crypto_base64_encode((const BYTE*)"", 0);
Packit Service 5a9772
Packit 1fb8d4
	certdata->fingerprint = _strdup(fingerprint);
Packit 1fb8d4
Packit Service 5a9772
	if (!certdata->hostname || !certdata->subject || !certdata->issuer || !certdata->fingerprint)
Packit 1fb8d4
		goto fail;
Packit 1fb8d4
Packit Service 5a9772
	for (i = 0; i < strlen(hostname); i++)
Packit 1fb8d4
		certdata->hostname[i] = tolower(certdata->hostname[i]);
Packit 1fb8d4
Packit 1fb8d4
	return certdata;
Packit 1fb8d4
fail:
Packit 1fb8d4
	free(certdata->hostname);
Packit 1fb8d4
	free(certdata->subject);
Packit 1fb8d4
	free(certdata->issuer);
Packit 1fb8d4
	free(certdata->fingerprint);
Packit 1fb8d4
	free(certdata);
Packit 1fb8d4
	return NULL;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void certificate_data_free(rdpCertificateData* certificate_data)
Packit 1fb8d4
{
Packit 1fb8d4
	if (certificate_data != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(certificate_data->hostname);
Packit 1fb8d4
		free(certificate_data->subject);
Packit 1fb8d4
		free(certificate_data->issuer);
Packit 1fb8d4
		free(certificate_data->fingerprint);
Packit 1fb8d4
		free(certificate_data);
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
rdpCertificateStore* certificate_store_new(rdpSettings* settings)
Packit 1fb8d4
{
Packit 1fb8d4
	rdpCertificateStore* certificate_store;
Packit Service 5a9772
	certificate_store = (rdpCertificateStore*)calloc(1, sizeof(rdpCertificateStore));
Packit 1fb8d4
Packit 1fb8d4
	if (!certificate_store)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	certificate_store->settings = settings;
Packit 1fb8d4
Packit 1fb8d4
	if (!certificate_store_init(certificate_store))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(certificate_store);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return certificate_store;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void certificate_store_free(rdpCertificateStore* certstore)
Packit 1fb8d4
{
Packit 1fb8d4
	if (certstore != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(certstore->path);
Packit 1fb8d4
		free(certstore->file);
Packit 1fb8d4
		free(certstore->legacy_file);
Packit 1fb8d4
		free(certstore);
Packit 1fb8d4
	}
Packit 1fb8d4
}