2012-02-17 09:58:30 +04:00
|
|
|
/**
|
2012-02-21 09:56:55 +04:00
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
2012-02-17 09:58:30 +04:00
|
|
|
* Certificate Handling
|
|
|
|
*
|
|
|
|
* Copyright 2011 Jiten Pathy
|
2012-02-21 09:56:55 +04:00
|
|
|
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
2012-02-17 09:58:30 +04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-08-15 01:09:01 +04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2012-02-17 09:58:30 +04:00
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2012-10-09 07:42:01 +04:00
|
|
|
#include <winpr/crt.h>
|
2013-03-22 23:52:43 +04:00
|
|
|
#include <winpr/file.h>
|
|
|
|
#include <winpr/path.h>
|
2012-10-09 07:42:01 +04:00
|
|
|
|
2012-02-17 09:58:30 +04:00
|
|
|
#include <openssl/pem.h>
|
|
|
|
#include <openssl/rsa.h>
|
|
|
|
|
|
|
|
static const char certificate_store_dir[] = "certs";
|
2013-06-07 00:45:19 +04:00
|
|
|
static const char certificate_server_dir[] = "server";
|
2012-02-17 09:58:30 +04:00
|
|
|
static const char certificate_known_hosts_file[] = "known_hosts";
|
|
|
|
|
2014-09-12 16:36:29 +04:00
|
|
|
#include <freerdp/log.h>
|
2012-02-17 09:58:30 +04:00
|
|
|
#include <freerdp/crypto/certificate.h>
|
|
|
|
|
2014-09-12 16:36:29 +04:00
|
|
|
#define TAG FREERDP_TAG("crypto")
|
|
|
|
|
2015-05-05 20:45:34 +03:00
|
|
|
BOOL certificate_store_init(rdpCertificateStore* certificate_store)
|
2012-02-17 09:58:30 +04:00
|
|
|
{
|
2015-05-05 20:45:34 +03:00
|
|
|
char* server_path = NULL;
|
2012-02-17 09:58:30 +04:00
|
|
|
rdpSettings* settings;
|
|
|
|
|
|
|
|
settings = certificate_store->settings;
|
|
|
|
|
2013-03-28 04:10:18 +04:00
|
|
|
if (!PathFileExistsA(settings->ConfigPath))
|
|
|
|
{
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!CreateDirectoryA(settings->ConfigPath, 0))
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "error creating directory '%s'", settings->ConfigPath);
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-09-12 16:36:29 +04:00
|
|
|
WLog_INFO(TAG, "creating directory %s", settings->ConfigPath);
|
2013-03-28 04:10:18 +04:00
|
|
|
}
|
|
|
|
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!(certificate_store->path = GetCombinedPath(settings->ConfigPath, (char*) certificate_store_dir)))
|
|
|
|
goto fail;
|
2014-07-18 05:15:22 +04:00
|
|
|
|
2013-03-28 04:10:18 +04:00
|
|
|
if (!PathFileExistsA(certificate_store->path))
|
2012-02-17 09:58:30 +04:00
|
|
|
{
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!CreateDirectoryA(certificate_store->path, 0))
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "error creating directory [%s]", certificate_store->path);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
WLog_INFO(TAG, "creating directory [%s]", certificate_store->path);
|
2012-02-17 09:58:30 +04:00
|
|
|
}
|
|
|
|
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!(server_path = GetCombinedPath(settings->ConfigPath, (char*) certificate_server_dir)))
|
|
|
|
goto fail;
|
2014-07-18 05:15:22 +04:00
|
|
|
|
2013-06-07 00:45:19 +04:00
|
|
|
if (!PathFileExistsA(server_path))
|
|
|
|
{
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!CreateDirectoryA(server_path, 0))
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "error creating directory [%s]", server_path);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
WLog_INFO(TAG, "created directory [%s]", server_path);
|
2013-06-07 00:45:19 +04:00
|
|
|
}
|
|
|
|
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!(certificate_store->file = GetCombinedPath(settings->ConfigPath, (char*) certificate_known_hosts_file)))
|
|
|
|
goto fail;
|
2014-07-18 05:15:22 +04:00
|
|
|
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!PathFileExistsA(certificate_store->file))
|
2012-02-17 09:58:30 +04:00
|
|
|
certificate_store->fp = fopen((char*) certificate_store->file, "w+");
|
|
|
|
else
|
|
|
|
certificate_store->fp = fopen((char*) certificate_store->file, "r+");
|
2015-05-05 20:45:34 +03:00
|
|
|
|
|
|
|
if (!certificate_store->fp)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "error opening [%s]", certificate_store->file);
|
|
|
|
goto fail;
|
2012-02-17 09:58:30 +04:00
|
|
|
}
|
2014-07-18 05:15:22 +04:00
|
|
|
|
2015-05-05 20:45:34 +03:00
|
|
|
free(server_path);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
WLog_ERR(TAG, "certificate store initialization failed");
|
|
|
|
free(server_path);
|
|
|
|
free(certificate_store->path);
|
|
|
|
free(certificate_store->file);
|
|
|
|
certificate_store->path = NULL;
|
|
|
|
certificate_store->file = NULL;
|
|
|
|
return FALSE;
|
2012-02-17 09:58:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int certificate_data_match(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
|
|
|
|
{
|
|
|
|
FILE* fp;
|
|
|
|
int length;
|
|
|
|
char* data;
|
|
|
|
char* pline;
|
|
|
|
int match = 1;
|
|
|
|
long int size;
|
|
|
|
|
|
|
|
fp = certificate_store->fp;
|
|
|
|
|
|
|
|
if (!fp)
|
|
|
|
return match;
|
|
|
|
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
|
|
size = ftell(fp);
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
|
|
|
|
if (size < 1)
|
|
|
|
return match;
|
|
|
|
|
2012-10-09 07:21:26 +04:00
|
|
|
data = (char*) malloc(size + 2);
|
2012-02-17 09:58:30 +04:00
|
|
|
|
|
|
|
if (fread(data, size, 1, fp) != 1)
|
|
|
|
{
|
2012-10-09 07:21:26 +04:00
|
|
|
free(data);
|
2012-02-17 09:58:30 +04:00
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[size] = '\n';
|
|
|
|
data[size + 1] = '\0';
|
|
|
|
pline = strtok(data, "\n");
|
|
|
|
|
|
|
|
while (pline != NULL)
|
|
|
|
{
|
|
|
|
length = strlen(pline);
|
|
|
|
|
|
|
|
if (length > 0)
|
|
|
|
{
|
|
|
|
length = strcspn(pline, " \t");
|
|
|
|
pline[length] = '\0';
|
|
|
|
|
|
|
|
if (strcmp(pline, certificate_data->hostname) == 0)
|
|
|
|
{
|
|
|
|
pline = &pline[length + 1];
|
|
|
|
|
|
|
|
if (strcmp(pline, certificate_data->fingerprint) == 0)
|
|
|
|
match = 0;
|
|
|
|
else
|
|
|
|
match = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pline = strtok(NULL, "\n");
|
|
|
|
}
|
2012-10-09 07:21:26 +04:00
|
|
|
free(data);
|
2012-02-17 09:58:30 +04:00
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
2012-06-29 05:05:10 +04:00
|
|
|
void certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
|
|
|
|
{
|
|
|
|
FILE* fp;
|
|
|
|
int length;
|
|
|
|
char* data;
|
|
|
|
char* pline;
|
|
|
|
long int size;
|
|
|
|
|
|
|
|
fp = certificate_store->fp;
|
|
|
|
|
|
|
|
if (!fp)
|
|
|
|
return;
|
|
|
|
|
2012-11-13 20:06:33 +04:00
|
|
|
/* Read the current contents of the file. */
|
2012-06-29 05:05:10 +04:00
|
|
|
fseek(fp, 0, SEEK_END);
|
|
|
|
size = ftell(fp);
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
|
|
|
|
if (size < 1)
|
|
|
|
return;
|
|
|
|
|
2012-10-09 07:21:26 +04:00
|
|
|
data = (char*) malloc(size + 2);
|
2012-06-29 05:05:10 +04:00
|
|
|
|
|
|
|
if (fread(data, size, 1, fp) != 1)
|
|
|
|
{
|
2012-10-09 07:21:26 +04:00
|
|
|
free(data);
|
2012-06-29 05:05:10 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-13 20:06:33 +04:00
|
|
|
/* Write the file back out, with appropriate fingerprint substitutions */
|
2012-06-29 05:05:10 +04:00
|
|
|
fp = fopen(certificate_store->file, "w+");
|
|
|
|
data[size] = '\n';
|
|
|
|
data[size + 1] = '\0';
|
|
|
|
pline = strtok(data, "\n"); // xxx: use strsep
|
|
|
|
|
|
|
|
while (pline != NULL)
|
|
|
|
{
|
|
|
|
length = strlen(pline);
|
|
|
|
|
|
|
|
if (length > 0)
|
|
|
|
{
|
|
|
|
char* hostname = pline, *fingerprint;
|
|
|
|
|
|
|
|
length = strcspn(pline, " \t");
|
|
|
|
hostname[length] = '\0';
|
|
|
|
|
|
|
|
/* If this is the replaced hostname, use the updated fingerprint. */
|
|
|
|
if (strcmp(hostname, certificate_data->hostname) == 0)
|
|
|
|
fingerprint = certificate_data->fingerprint;
|
|
|
|
else
|
|
|
|
fingerprint = &hostname[length + 1];
|
|
|
|
|
|
|
|
fprintf(fp, "%s %s\n", hostname, fingerprint);
|
|
|
|
}
|
|
|
|
|
|
|
|
pline = strtok(NULL, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
2012-10-09 07:21:26 +04:00
|
|
|
free(data);
|
2012-06-29 05:05:10 +04:00
|
|
|
}
|
|
|
|
|
2012-02-17 09:58:30 +04:00
|
|
|
void certificate_data_print(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
|
|
|
|
{
|
|
|
|
FILE* fp;
|
|
|
|
|
|
|
|
/* reopen in append mode */
|
|
|
|
fp = fopen(certificate_store->file, "a");
|
|
|
|
|
|
|
|
if (!fp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fprintf(fp, "%s %s\n", certificate_data->hostname, certificate_data->fingerprint);
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
rdpCertificateData* certificate_data_new(char* hostname, char* fingerprint)
|
|
|
|
{
|
|
|
|
rdpCertificateData* certdata;
|
|
|
|
|
2014-03-26 02:13:08 +04:00
|
|
|
certdata = (rdpCertificateData *)calloc(1, sizeof(rdpCertificateData));
|
|
|
|
if (!certdata)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
certdata->hostname = _strdup(hostname);
|
|
|
|
if (!certdata->hostname)
|
|
|
|
goto out_free;
|
|
|
|
certdata->fingerprint = _strdup(fingerprint);
|
|
|
|
if (!certdata->fingerprint)
|
|
|
|
goto out_free_hostname;
|
2012-02-17 09:58:30 +04:00
|
|
|
return certdata;
|
2014-03-26 02:13:08 +04:00
|
|
|
|
|
|
|
out_free_hostname:
|
|
|
|
free(certdata->hostname);
|
|
|
|
out_free:
|
|
|
|
free(certdata);
|
|
|
|
return NULL;
|
2012-02-17 09:58:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void certificate_data_free(rdpCertificateData* certificate_data)
|
|
|
|
{
|
|
|
|
if (certificate_data != NULL)
|
|
|
|
{
|
2012-10-09 07:21:26 +04:00
|
|
|
free(certificate_data->hostname);
|
|
|
|
free(certificate_data->fingerprint);
|
|
|
|
free(certificate_data);
|
2012-02-17 09:58:30 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rdpCertificateStore* certificate_store_new(rdpSettings* settings)
|
|
|
|
{
|
|
|
|
rdpCertificateStore* certificate_store;
|
|
|
|
|
2014-07-18 05:15:22 +04:00
|
|
|
certificate_store = (rdpCertificateStore*) calloc(1, sizeof(rdpCertificateStore));
|
2012-02-17 09:58:30 +04:00
|
|
|
|
2014-04-09 18:07:06 +04:00
|
|
|
if (!certificate_store)
|
|
|
|
return NULL;
|
2012-11-22 04:22:41 +04:00
|
|
|
|
2014-04-09 18:07:06 +04:00
|
|
|
certificate_store->settings = settings;
|
2014-07-18 05:15:22 +04:00
|
|
|
|
2015-05-05 20:45:34 +03:00
|
|
|
if (!certificate_store_init(certificate_store))
|
|
|
|
{
|
|
|
|
free(certificate_store);
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-02-17 09:58:30 +04:00
|
|
|
|
|
|
|
return certificate_store;
|
|
|
|
}
|
|
|
|
|
|
|
|
void certificate_store_free(rdpCertificateStore* certstore)
|
|
|
|
{
|
|
|
|
if (certstore != NULL)
|
|
|
|
{
|
|
|
|
if (certstore->fp != NULL)
|
|
|
|
fclose(certstore->fp);
|
|
|
|
|
2012-10-09 07:21:26 +04:00
|
|
|
free(certstore->path);
|
|
|
|
free(certstore->file);
|
|
|
|
free(certstore);
|
2012-02-17 09:58:30 +04:00
|
|
|
}
|
|
|
|
}
|