Allow TLS pre-master secrets to be recorded

This allows for RDP sessions to be easily decrypted within Wireshark
This commit is contained in:
matt335672
2025-09-11 17:17:00 +01:00
parent 976a708599
commit 7f6899567b
7 changed files with 149 additions and 0 deletions
+24
View File
@@ -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 */
+1
View File
@@ -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);
+92
View File
@@ -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)
+8
View File
@@ -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
+13
View File
@@ -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).
+8
View File
@@ -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)
{
+3
View File
@@ -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