Skip to content

Commit e775052

Browse files
authored
GUACAMOLE-1969: Merge report Clipboard History in recordings.
2 parents b706199 + 4410b76 commit e775052

22 files changed

Lines changed: 375 additions & 14 deletions

File tree

src/libguac/guacamole/recording.h

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
*/
4949
#define GUAC_COMMON_RECORDING_MAX_NAME_LENGTH 2048
5050

51+
/**
52+
* The block size to use when sending clipboard data in a recording.
53+
*/
54+
#define GUAC_RECORDING_CLIPBOARD_BLOCK_SIZE 4096
55+
5156
/**
5257
* An in-progress session recording, attached to a guac_client instance such
5358
* that output Guacamole instructions may be dynamically intercepted and
@@ -61,6 +66,12 @@ typedef struct guac_recording {
6166
*/
6267
guac_socket* socket;
6368

69+
/**
70+
* The stream used for recording clipboard data. This stream is allocated
71+
* once during recording creation and reused for all clipboard events.
72+
*/
73+
guac_stream* clipboard_stream;
74+
6475
/**
6576
* Non-zero if output which is broadcast to each connected client
6677
* (graphics, streams, etc.) should be included in the session recording,
@@ -95,6 +106,15 @@ typedef struct guac_recording {
95106
*/
96107
int include_keys;
97108

109+
/**
110+
* Non-zero if clipboard data should be included in the session
111+
* recording, zero otherwise. Including clipboard data within the recording may
112+
* be necessary in certain auditing contexts, but should only be done with
113+
* caution. Clipboard can easily contain sensitive information, such as
114+
* passwords, credit card numbers, etc.
115+
*/
116+
int include_clipboard;
117+
98118
} guac_recording;
99119

100120
/**
@@ -152,6 +172,13 @@ typedef struct guac_recording {
152172
* Non-zero if writing to an existing file should be allowed, or zero
153173
* otherwise.
154174
*
175+
* @param include_clipboard
176+
* Non-zero if clipboard data should be included in the session
177+
* recording, zero otherwise. Including clipboard data within the recording may
178+
* be necessary in certain auditing contexts, but should only be done with
179+
* caution. Clipboard can easily contain sensitive information, such as
180+
* passwords, credit card numbers, etc.
181+
*
155182
* @return
156183
* A new guac_recording structure representing the in-progress
157184
* recording if the recording file has been successfully created and a
@@ -160,7 +187,7 @@ typedef struct guac_recording {
160187
guac_recording* guac_recording_create(guac_client* client,
161188
const char* path, const char* name, int create_path,
162189
int include_output, int include_mouse, int include_touch,
163-
int include_keys, int allow_write_existing);
190+
int include_keys, int allow_write_existing, int include_clipboard);
164191

165192
/**
166193
* Frees the resources associated with the given in-progress recording. Note
@@ -256,5 +283,69 @@ void guac_recording_report_touch(guac_recording* recording,
256283
void guac_recording_report_key(guac_recording* recording,
257284
int keysym, int pressed);
258285

259-
#endif
286+
/**
287+
* Reports a clipboard instruction within the recording.
288+
* The full structure consists of clipboard instruction, one or more
289+
* blob instructions and end instruction.
290+
*
291+
* @param recording
292+
* The guac_recording associated with the clipboard instruction.
293+
*
294+
* @param stream
295+
* The guac_stream allocated for the clipboard instruction.
296+
*
297+
* @param mimetype
298+
* The clipboard data mimetype
299+
*/
300+
void guac_recording_report_clipboard_begin(guac_recording* recording,
301+
guac_stream* stream, char* mimetype);
302+
303+
/**
304+
* Report a clipboard blob within the recording.
305+
*
306+
* @param recording
307+
* The guac_recording associated with the clipboard instruction.
308+
*
309+
* @param stream
310+
* The guac_stream associated with the clipboard instruction.
311+
*
312+
* @param data
313+
* The clipboard blob data.
314+
*
315+
* @param length
316+
* Length of the blob data.
317+
*/
318+
void guac_recording_report_clipboard_blob(guac_recording* recording,
319+
guac_stream* stream, void* data, int length);
320+
321+
/**
322+
* Report a clipboard end instruction within the recording.
323+
*
324+
* @param recording
325+
* The guac_recording associated with the clipboard instruction.
326+
*
327+
* @param stream
328+
* The guac_stream associated with the clipboard instruction.
329+
*/
330+
void guac_recording_report_clipboard_end(guac_recording* recording,
331+
guac_stream* stream);
260332

333+
/**
334+
* Reports clipboard data within the recording.
335+
*
336+
* @param recording
337+
* The guac_recording to write clipboard data to.
338+
*
339+
* @param mimetype
340+
* The mimetype of the clipboard data (e.g., "text/plain").
341+
*
342+
* @param data
343+
* The clipboard data buffer.
344+
*
345+
* @param length
346+
* The length of the clipboard data in bytes.
347+
*/
348+
void guac_recording_report_clipboard(guac_recording* recording,
349+
const char* mimetype, const char* data, int length);
350+
351+
#endif

src/libguac/recording.c

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
guac_recording* guac_recording_create(guac_client* client,
4343
const char* path, const char* name, int create_path,
4444
int include_output, int include_mouse, int include_touch,
45-
int include_keys, int allow_write_existing) {
45+
int include_keys, int allow_write_existing, int include_clipboard) {
4646

4747
char filename[GUAC_COMMON_RECORDING_MAX_NAME_LENGTH];
4848

@@ -76,6 +76,12 @@ guac_recording* guac_recording_create(guac_client* client,
7676
recording->include_mouse = include_mouse;
7777
recording->include_touch = include_touch;
7878
recording->include_keys = include_keys;
79+
recording->include_clipboard = include_clipboard;
80+
81+
if (include_clipboard)
82+
recording->clipboard_stream = guac_client_alloc_stream(client);
83+
else
84+
recording->clipboard_stream = NULL;
7985

8086
/* Replace client socket with wrapped recording socket only if including
8187
* output within the recording */
@@ -133,3 +139,52 @@ void guac_recording_report_key(guac_recording* recording,
133139

134140
}
135141

142+
void guac_recording_report_clipboard_begin(guac_recording* recording, guac_stream* stream, char* mimetype) {
143+
/* Report clipboard only if recording should contain it */
144+
if (recording->include_clipboard)
145+
guac_protocol_send_clipboard(recording->socket, stream, mimetype);
146+
}
147+
148+
void guac_recording_report_clipboard_blob(guac_recording* recording, guac_stream* stream, void* data, int length) {
149+
/* Report clipboard only if recording should contain it */
150+
if (recording->include_clipboard)
151+
guac_protocol_send_blob(recording->socket, stream, data, length);
152+
}
153+
154+
void guac_recording_report_clipboard_end(guac_recording* recording, guac_stream* stream) {
155+
/* Report clipboard only if recording should contain it */
156+
if (recording->include_clipboard)
157+
guac_protocol_send_end(recording->socket, stream);
158+
}
159+
160+
void guac_recording_report_clipboard(guac_recording* recording,
161+
const char* mimetype, const char* data, int length) {
162+
163+
/* Report clipboard only if recording should contain clipboard changes */
164+
if (!recording->include_clipboard || !recording->clipboard_stream)
165+
return;
166+
167+
guac_socket* socket = recording->socket;
168+
guac_stream* stream = recording->clipboard_stream;
169+
170+
const char* current = data;
171+
int remaining = length;
172+
173+
guac_protocol_send_clipboard(socket, stream, mimetype);
174+
175+
while (remaining > 0) {
176+
177+
int block_size = GUAC_RECORDING_CLIPBOARD_BLOCK_SIZE;
178+
if (remaining < block_size)
179+
block_size = remaining;
180+
181+
guac_protocol_send_blob(socket, stream, current, block_size);
182+
183+
remaining -= block_size;
184+
current += block_size;
185+
186+
}
187+
188+
guac_protocol_send_end(socket, stream);
189+
190+
}

src/protocols/kubernetes/clipboard.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ int guac_kubernetes_clipboard_handler(guac_user* user, guac_stream* stream,
3939
stream->blob_handler = guac_kubernetes_clipboard_blob_handler;
4040
stream->end_handler = guac_kubernetes_clipboard_end_handler;
4141

42+
/* Report clipboard within recording */
43+
if (kubernetes_client->recording != NULL)
44+
guac_recording_report_clipboard_begin(kubernetes_client->recording, stream, mimetype);
45+
4246
return 0;
4347
}
4448

@@ -49,6 +53,10 @@ int guac_kubernetes_clipboard_blob_handler(guac_user* user,
4953
guac_kubernetes_client* kubernetes_client =
5054
(guac_kubernetes_client*) client->data;
5155

56+
/* Report clipboard blob within recording */
57+
if (kubernetes_client->recording != NULL)
58+
guac_recording_report_clipboard_blob(kubernetes_client->recording, stream, data, length);
59+
5260
/* Append new data */
5361
guac_terminal_clipboard_append(kubernetes_client->term, data, length);
5462

@@ -58,8 +66,15 @@ int guac_kubernetes_clipboard_blob_handler(guac_user* user,
5866
int guac_kubernetes_clipboard_end_handler(guac_user* user,
5967
guac_stream* stream) {
6068

69+
guac_client* client = user->client;
70+
guac_kubernetes_client* kubernetes_client =
71+
(guac_kubernetes_client*) client->data;
72+
73+
/* Report clipboard blob within recording */
74+
if (kubernetes_client->recording != NULL)
75+
guac_recording_report_clipboard_end(kubernetes_client->recording, stream);
76+
6177
/* Nothing to do - clipboard is implemented within client */
6278

6379
return 0;
6480
}
65-

src/protocols/kubernetes/kubernetes.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ void* guac_kubernetes_client_thread(void* data) {
238238
!settings->recording_exclude_mouse,
239239
0, /* Touch events not supported */
240240
settings->recording_include_keys,
241-
settings->recording_write_existing);
241+
settings->recording_write_existing,
242+
settings->recording_include_clipboard);
242243
}
243244

244245
/* Create terminal options with required parameters */

src/protocols/kubernetes/settings.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const char* GUAC_KUBERNETES_CLIENT_ARGS[] = {
5252
"recording-exclude-output",
5353
"recording-exclude-mouse",
5454
"recording-include-keys",
55+
"recording-include-clipboard",
5556
"create-recording-path",
5657
"recording-write-existing",
5758
"read-only",
@@ -215,6 +216,16 @@ enum KUBERNETES_ARGS_IDX {
215216
*/
216217
IDX_RECORDING_INCLUDE_KEYS,
217218

219+
/**
220+
* Whether clipboard data should be included in the session recording.
221+
* Clipboard data is NOT included by default within the recording,
222+
* as doing so has privacy and security implications. Including clipboard data
223+
* may be necessary in certain auditing contexts, but should only be done
224+
* with caution. Clipboard data can easily contain sensitive information, such
225+
* as passwords, credit card numbers, etc.
226+
*/
227+
IDX_RECORDING_INCLUDE_CLIPBOARD,
228+
218229
/**
219230
* Whether the specified screen recording path should automatically be
220231
* created if it does not yet exist.
@@ -417,6 +428,11 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
417428
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
418429
IDX_RECORDING_INCLUDE_KEYS, false);
419430

431+
/* Parse clipboard inclusion flag */
432+
settings->recording_include_clipboard =
433+
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
434+
IDX_RECORDING_INCLUDE_CLIPBOARD, false);
435+
420436
/* Parse path creation flag */
421437
settings->create_recording_path =
422438
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,

src/protocols/kubernetes/settings.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,16 @@ typedef struct guac_kubernetes_settings {
245245
*/
246246
bool recording_include_keys;
247247

248+
/**
249+
* Whether clipboard data should be included in the session recording.
250+
* Clipboard data is NOT included by default within the recording,
251+
* as doing so has privacy and security implications. Including clipboard data
252+
* may be necessary in certain auditing contexts, but should only be done
253+
* with caution. Clipboard data can easily contain sensitive information, such
254+
* as passwords, credit card numbers, etc.
255+
*/
256+
bool recording_include_clipboard;
257+
248258
/**
249259
* Whether existing files should be appended to when creating a new recording.
250260
* Disabled by default.

src/protocols/rdp/channels/cliprdr.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,13 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
521521
guac_common_clipboard_reset(clipboard->clipboard, "text/plain");
522522
guac_common_clipboard_append(clipboard->clipboard, received_data, length);
523523
guac_common_clipboard_send(clipboard->clipboard, client);
524+
525+
/* Record clipboard if recording is active */
526+
if (rdp_client->recording != NULL)
527+
guac_recording_report_clipboard(rdp_client->recording,
528+
clipboard->clipboard->mimetype,
529+
clipboard->clipboard->buffer,
530+
clipboard->clipboard->length);
524531
}
525532

526533
guac_mem_free(received_data);
@@ -695,6 +702,12 @@ int guac_rdp_clipboard_handler(guac_user* user, guac_stream* stream,
695702
/* Clear any current contents, assigning the mimetype the data which will
696703
* be received */
697704
guac_common_clipboard_reset(clipboard->clipboard, mimetype);
705+
706+
/* Report clipboard within recording */
707+
if (rdp_client->recording != NULL)
708+
guac_recording_report_clipboard_begin(rdp_client->recording, stream,
709+
mimetype);
710+
698711
return 0;
699712

700713
}
@@ -711,6 +724,10 @@ int guac_rdp_clipboard_blob_handler(guac_user* user, guac_stream* stream,
711724
if (clipboard == NULL)
712725
return 0;
713726

727+
/* Report clipboard blob within recording */
728+
if (rdp_client->recording != NULL)
729+
guac_recording_report_clipboard_blob(rdp_client->recording, stream, data, length);
730+
714731
/* Append received data to current clipboard contents */
715732
guac_common_clipboard_append(clipboard->clipboard, (char*) data, length);
716733
return 0;
@@ -728,6 +745,10 @@ int guac_rdp_clipboard_end_handler(guac_user* user, guac_stream* stream) {
728745
if (clipboard == NULL)
729746
return 0;
730747

748+
/* Report clipboard stream end within recording */
749+
if (rdp_client->recording != NULL)
750+
guac_recording_report_clipboard_end(rdp_client->recording, stream);
751+
731752
/* Terminate clipboard data with NULL */
732753
guac_common_clipboard_append(clipboard->clipboard, "", 1);
733754

src/protocols/rdp/rdp.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,8 @@ void* guac_rdp_client_thread(void* data) {
938938
!settings->recording_exclude_mouse,
939939
!settings->recording_exclude_touch,
940940
settings->recording_include_keys,
941-
settings->recording_write_existing);
941+
settings->recording_write_existing,
942+
settings->recording_include_clipboard);
942943
}
943944

944945
/* Continue handling connections until error or client disconnect */

0 commit comments

Comments
 (0)