Allow TLS pre-master secrets to be recorded
This allows for RDP sessions to be easily decrypted within Wireshark
This commit is contained in:
@@ -2311,6 +2311,30 @@ g_file_seek(int fd, int offset)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* move file pointer to end of file, plus an offset */
|
||||
int
|
||||
g_file_seek_end(int fd, int offset)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
int rv;
|
||||
|
||||
rv = (int)SetFilePointer((HANDLE)fd, offset, 0, FILE_END);
|
||||
|
||||
if (rv == (int)INVALID_SET_FILE_POINTER)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rv;
|
||||
}
|
||||
|
||||
#else
|
||||
return (int)lseek(fd, offset, SEEK_END);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* do a write lock on a file */
|
||||
/* return boolean */
|
||||
|
||||
@@ -242,6 +242,7 @@ int g_file_is_open(int fd);
|
||||
int g_file_read(int fd, char *ptr, int len);
|
||||
int g_file_write(int fd, const char *ptr, int len);
|
||||
int g_file_seek(int fd, int offset);
|
||||
int g_file_seek_end(int fd, int offset);
|
||||
int g_file_lock(int fd, int start, int len);
|
||||
int
|
||||
g_file_map(int fd, int aread, int awrite, size_t length, void **addr);
|
||||
|
||||
@@ -53,6 +53,11 @@ static EVP_CIPHER *g_cipher_des_ede3_cbc; /* DES3 CBC cipher */
|
||||
static EVP_MAC *g_mac_hmac; /* HMAC MAC */
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
|
||||
#define HAS_KEYLOG_CALLBACK /* SSL_CTX_set_keylog_callback() is available */
|
||||
static char *g_keylog_filename = NULL;
|
||||
#endif
|
||||
|
||||
/* definition of ssl_tls */
|
||||
struct ssl_tls
|
||||
{
|
||||
@@ -173,13 +178,96 @@ ssl_finish(void)
|
||||
/* De-allocate any allocated globals
|
||||
* For OpenSSL 3, these can all safely be passed a NULL pointer */
|
||||
EVP_MD_free(g_md_md5);
|
||||
g_md_md5 = NULL;
|
||||
EVP_MD_free(g_md_sha1);
|
||||
g_md_sha1 = NULL;
|
||||
EVP_CIPHER_free(g_cipher_des_ede3_cbc);
|
||||
g_cipher_des_ede3_cbc = NULL;
|
||||
EVP_MAC_free(g_mac_hmac);
|
||||
g_mac_hmac = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_KEYLOG_CALLBACK
|
||||
free(g_keylog_filename);
|
||||
g_keylog_filename = NULL;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int
|
||||
ssl_set_pre_master_secret_logfile(const char *filename)
|
||||
{
|
||||
int rv = 0;
|
||||
#ifdef HAS_KEYLOG_CALLBACK
|
||||
int fd = -1;
|
||||
|
||||
/* Remove any existing setting */
|
||||
free(g_keylog_filename);
|
||||
g_keylog_filename = NULL;
|
||||
|
||||
if (filename == NULL || filename[0] == '\0')
|
||||
{
|
||||
/* all done */
|
||||
}
|
||||
else if (filename[0] != '/')
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "TLS pre-master log file must start with '/'");
|
||||
}
|
||||
else if ((fd = g_file_open_rw(filename)) < 0)
|
||||
{
|
||||
/* Can't open file for writing. We'll log a single error here
|
||||
* rather than lots of errors in the callback */
|
||||
LOG(LOG_LEVEL_ERROR, "Can't write TLS pre-master secrets to %s [%s]",
|
||||
filename, g_get_strerror());
|
||||
}
|
||||
else if (g_file_close(fd) != 0)
|
||||
{
|
||||
/* Ignore this one */
|
||||
}
|
||||
else if ((g_keylog_filename = g_strdup(filename)) == NULL)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "Out of memory setting TLS pre-master log");
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = 1;
|
||||
}
|
||||
#else
|
||||
LOG(LOG_LEVEL_WARNING,
|
||||
"This system is unable to log TLS pre-master secrets");
|
||||
#endif
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Log the pre-master secret for debugging purposes */
|
||||
#ifdef HAS_KEYLOG_CALLBACK
|
||||
static void
|
||||
log_pre_master_secret(const SSL *ssl, const char *line)
|
||||
{
|
||||
if (g_keylog_filename != NULL && g_keylog_filename[0] == '/')
|
||||
{
|
||||
int fd = g_file_open_rw(g_keylog_filename);
|
||||
if (fd < 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "Can't write pre-master secret to %s [ %s]",
|
||||
g_keylog_filename, g_get_strerror());
|
||||
}
|
||||
else
|
||||
{
|
||||
g_chmod_hex(g_keylog_filename, 0x640); // Must be group readable
|
||||
g_file_seek_end(fd, 0);
|
||||
g_file_write(fd, line, strlen(line));
|
||||
g_file_write(fd, "\n", 1);
|
||||
(void)g_file_close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HAS_KEYLOG_CALLBACK
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* rc4 stuff
|
||||
*
|
||||
* For OpenSSL 3.0, the rc4 encryption algorithm is only provided by the
|
||||
@@ -1167,6 +1255,10 @@ ssl_tls_accept(struct ssl_tls *self, long ssl_protocols,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_KEYLOG_CALLBACK
|
||||
SSL_CTX_set_keylog_callback(self->ctx, log_pre_master_secret);
|
||||
#endif // HAS_KEYLOG_CALLBACK
|
||||
|
||||
self->ssl = SSL_new(self->ctx);
|
||||
|
||||
if (self->ssl == NULL)
|
||||
|
||||
@@ -30,6 +30,14 @@ int
|
||||
ssl_init(void);
|
||||
int
|
||||
ssl_finish(void);
|
||||
/**
|
||||
* Sets a log file for recording TLS pre-master secrets
|
||||
*
|
||||
* @param filename Filename to log secrets in
|
||||
* @return != 0 if the log was successfully set
|
||||
*/
|
||||
int
|
||||
ssl_set_pre_master_secret_logfile(const char *filename);
|
||||
void *
|
||||
ssl_rc4_info_create(void);
|
||||
void
|
||||
|
||||
@@ -73,6 +73,19 @@ If not specified, defaults to \fB@sysconfdir@/@sysconfsubdir@/cert.pem\fP, \fB@s
|
||||
|
||||
This parameter is effective only if \fBsecurity_layer\fP is set to \fBtls\fP or \fBnegotiate\fP.
|
||||
|
||||
.TP
|
||||
\fBtls_pms_log_file\fR=\fI<path-to-log-file>\fR
|
||||
Logs TLS pre-master secrets to the specified file. This allows packet capture
|
||||
tools (e.g. Wireshark) to decrypt captured PDUs.
|
||||
|
||||
The file must be writeable by xrdp and readable by the packet capture tool.
|
||||
A good way to achive this is to create a temporary directory with
|
||||
permissions 2750 owned by the user running xrdp, and in the group used by
|
||||
the packet sniffer.
|
||||
|
||||
SETTING THIS OPTION IS A SECURITY RISK. ONLY SET THIS OPTION FOR DEBUGGING
|
||||
COMMUNICATIONS BETWEEN XRDP AND A CLIENT.
|
||||
|
||||
.TP
|
||||
\fBchannel_code\fP=\fI[true|false]\fP
|
||||
If set to \fB0\fR, \fBfalse\fR or \fBno\fR this option disables all channels \fBxrdp\fR(8).
|
||||
|
||||
@@ -277,6 +277,14 @@ xrdp_rdp_read_config(const char *xrdp_ini, struct xrdp_client_info *client_info)
|
||||
client_info->key_file, g_get_strerror());
|
||||
}
|
||||
}
|
||||
else if (g_strcasecmp(item, "tls_pms_log_file") == 0)
|
||||
{
|
||||
if (ssl_set_pre_master_secret_logfile(value))
|
||||
{
|
||||
LOG(LOG_LEVEL_WARNING, "TLS pre-master secrets will be logged. "
|
||||
"This is a security risk.");
|
||||
}
|
||||
}
|
||||
else if (g_strcasecmp(item, "domain_user_separator") == 0
|
||||
&& g_strlen(value) > 0)
|
||||
{
|
||||
|
||||
@@ -66,6 +66,9 @@ crypt_level=high
|
||||
certificate=
|
||||
key_file=
|
||||
|
||||
; [Debug] Log file for TLS pre-master secrets - see xrdp.ini(5)
|
||||
#tls_pms_log_file=/tmp/xrdp-pms/premaster.log
|
||||
|
||||
; set SSL protocols
|
||||
; can be comma separated list of 'SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'
|
||||
ssl_protocols=TLSv1.2, TLSv1.3
|
||||
|
||||
Reference in New Issue
Block a user