Skip to content

Commit 64d2079

Browse files
committed
GUACAMOLE-2281: Add auth-challenge and auth-response instructions to libguac.
Adds two new protocol instructions for authentication exchanges: - auth-challenge: opens a stream carrying the body of a challenge for a pending authentication exchange. The wire form is "auth-challenge,<stream_idx>,<mimetype>,<challenge_id>". The mimetype identifies the auth flavor; the challenge_id is an opaque identifier the peer references in its matching auth-response. - auth-response: opens a stream carrying the response body for a previously-issued auth-challenge identified by the same challenge_id. Same wire shape as auth-challenge. Each instruction's body rides as blobs on the announced stream, terminated by end. No paired pipe, no single-slot per-user buffering; per-stream blob and end handlers do the work. Also adds a destructor callback to guac_stream, called from guac_user_free_stream / guac_client_free_stream and from guac_user_free / guac_client_free for streams still open at disconnect, so consumers can attach per-stream state that needs cleanup if the peer drops mid-stream. The first consumer is WebAuthn passthrough (companion PR on guacamole-client) using application/x-webauthn-create+json and application/x-webauthn-get+json mimetypes.
1 parent e775052 commit 64d2079

13 files changed

Lines changed: 773 additions & 4 deletions

File tree

src/libguac/client.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,25 @@ guac_stream* guac_client_alloc_stream(guac_client* client) {
139139
allocd_stream->ack_handler = NULL;
140140
allocd_stream->blob_handler = NULL;
141141
allocd_stream->end_handler = NULL;
142+
allocd_stream->free_handler = NULL;
142143

143144
return allocd_stream;
144145

145146
}
146147

147148
void guac_client_free_stream(guac_client* client, guac_stream* stream) {
148149

150+
/* Run the registered free handler for the stream's data, if any */
151+
if (stream->free_handler != NULL)
152+
stream->free_handler(stream->data);
153+
154+
/* Clean up any remaining dangling pointers before permitting reuse */
155+
stream->data = NULL;
156+
stream->ack_handler = NULL;
157+
stream->blob_handler = NULL;
158+
stream->end_handler = NULL;
159+
stream->free_handler = NULL;
160+
149161
/* Mark stream as closed */
150162
int freed_index = stream->index;
151163
stream->index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
@@ -289,6 +301,7 @@ guac_client* guac_client_alloc(void) {
289301

290302
for (i=0; i<GUAC_CLIENT_MAX_STREAMS; i++) {
291303
client->__output_streams[i].index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
304+
client->__output_streams[i].free_handler = NULL;
292305
}
293306

294307
/* Init locks */
@@ -344,6 +357,15 @@ void guac_client_free(guac_client* client) {
344357
guac_pool_free(client->__buffer_pool);
345358
guac_pool_free(client->__layer_pool);
346359

360+
/* Run any registered free handlers for streams still open at
361+
* disconnect so their data pointers do not leak */
362+
for (int i = 0; i < GUAC_CLIENT_MAX_STREAMS; i++) {
363+
guac_stream* stream = &client->__output_streams[i];
364+
if (stream->index != GUAC_CLIENT_CLOSED_STREAM_INDEX
365+
&& stream->free_handler != NULL)
366+
stream->free_handler(stream->data);
367+
}
368+
347369
/* Free streams */
348370
guac_mem_free(client->__output_streams);
349371

src/libguac/guacamole/protocol.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,38 @@ int guac_protocol_send_clipboard(guac_socket* socket, const guac_stream* stream,
11191119
*/
11201120
int guac_protocol_send_name(guac_socket* socket, const char* name);
11211121

1122+
/**
1123+
* Sends an auth-challenge instruction over the given guac_socket
1124+
* connection, announcing a stream carrying the challenge body for a
1125+
* pending authentication exchange. The peer responds via an
1126+
* auth-response instruction carrying the same challenge_id.
1127+
*
1128+
* The body is shipped on the given stream as blobs, terminated by an
1129+
* end, after this function returns. Callers are responsible for
1130+
* allocating and freeing the stream.
1131+
*
1132+
* @param socket
1133+
* The guac_socket connection to use.
1134+
*
1135+
* @param stream
1136+
* The stream along which the challenge body will be sent.
1137+
*
1138+
* @param mimetype
1139+
* The mimetype of the data that will be sent along the given stream.
1140+
* The mimetype identifies the auth flavor (e.g. WebAuthn create vs.
1141+
* get).
1142+
*
1143+
* @param challenge_id
1144+
* Opaque identifier carried by the auth-challenge. The peer's
1145+
* matching auth-response will reference this identifier.
1146+
*
1147+
* @return
1148+
* Zero on success, non-zero on error.
1149+
*/
1150+
int guac_protocol_send_auth_challenge(guac_socket* socket,
1151+
const guac_stream* stream, const char* mimetype,
1152+
const char* challenge_id);
1153+
11221154
/**
11231155
* Decodes the given base64-encoded string in-place. The base64 string must
11241156
* be NULL-terminated.

src/libguac/guacamole/stream.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,34 @@ struct guac_stream {
105105
*/
106106
guac_user_end_handler* end_handler;
107107

108+
/**
109+
* Optional cleanup callback for whatever the stream's data pointer
110+
* holds. Called from guac_user_free_stream when the stream is freed,
111+
* and from guac_user_free for any streams still open when the user
112+
* disconnects. NULL if no cleanup is needed.
113+
*
114+
* Example:
115+
* @code
116+
* static void my_data_free(void* data) {
117+
* my_data* d = (my_data*) data;
118+
* free(d->buffer);
119+
* free(d);
120+
* }
121+
*
122+
* int my_pipe_handler(guac_user* user, guac_stream* stream,
123+
* char* mimetype, char* name) {
124+
* my_data* d = malloc(sizeof(*d));
125+
* d->buffer = NULL;
126+
* stream->data = d;
127+
* stream->blob_handler = my_blob_handler;
128+
* stream->end_handler = my_end_handler;
129+
* stream->free_handler = my_data_free;
130+
* return 0;
131+
* }
132+
* @endcode
133+
*/
134+
guac_stream_free_handler* free_handler;
135+
108136
};
109137

110138
#endif

src/libguac/guacamole/user-fntypes.h

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,18 @@ typedef int guac_user_ack_handler(guac_user* user, guac_stream* stream,
380380
*/
381381
typedef int guac_user_end_handler(guac_user* user, guac_stream* stream);
382382

383+
/**
384+
* Optional cleanup callback installed on a guac_stream for whatever the
385+
* stream's data pointer holds. Called from guac_user_free_stream and
386+
* guac_client_free_stream when the stream is freed, and from
387+
* guac_user_free and guac_client_free for any streams still open when
388+
* the user or client is disposed.
389+
*
390+
* @param data
391+
* The stream's data pointer at the time the stream is being freed.
392+
*/
393+
typedef void guac_stream_free_handler(void* data);
394+
383395
/**
384396
* Handler for Guacamole join events. A join event is fired by the
385397
* guac_client whenever a guac_user joins the connection. There is no
@@ -500,7 +512,7 @@ typedef int guac_user_put_handler(guac_user* user, guac_object* object,
500512
guac_stream* stream, char* mimetype, char* name);
501513

502514
/**
503-
* Handler for Guacamole USB connect events, invoked when a "usbconnect"
515+
* Handler for Guacamole USB connect events, invoked when a "usbconnect"
504516
* instruction has been received from a user. This indicates that the user
505517
* has connected a USB device via WebUSB and it is available for redirection.
506518
*
@@ -540,10 +552,36 @@ typedef int guac_user_put_handler(guac_user* user, guac_object* object,
540552
* an error occurred.
541553
*/
542554
typedef int guac_user_usbconnect_handler(guac_user* user, const char* device_id,
543-
int vendor_id, int product_id, const char* device_name,
555+
int vendor_id, int product_id, const char* device_name,
544556
const char* serial_number, int device_class, int device_subclass,
545557
int device_protocol, const char* interface_data);
546558

559+
/**
560+
* Handler for Guacamole "auth-response" events, invoked when an
561+
* "auth-response" instruction has been received from a user. An
562+
* auth-response announces a stream carrying the response body for a
563+
* previously-issued auth-challenge identified by the same challenge_id.
564+
*
565+
* @param user
566+
* The user that sent the auth-response.
567+
*
568+
* @param stream
569+
* The stream along which the response body will be received.
570+
*
571+
* @param mimetype
572+
* The mimetype of the data that will be received along the given
573+
* stream.
574+
*
575+
* @param challenge_id
576+
* The challenge identifier of the originating auth-challenge being
577+
* responded to.
578+
*
579+
* @return
580+
* Zero on success, non-zero on error.
581+
*/
582+
typedef int guac_user_auth_response_handler(guac_user* user,
583+
guac_stream* stream, char* mimetype, char* challenge_id);
584+
547585
/**
548586
* Handler for Guacamole USB data events, invoked when a "usbdata" instruction
549587
* has been received from a user. This carries data from a client-side USB

src/libguac/guacamole/user.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,24 @@ struct guac_user {
598598
*/
599599
guac_user_usbdisconnect_handler* usbdisconnect_handler;
600600

601+
/**
602+
* Handler for "auth-response" events sent by the Guacamole web-client,
603+
* carrying the response body for a previously-issued auth-challenge
604+
* identified by the same challenge_id. The handler installs the blob
605+
* and end handlers on the stream so the body can be consumed.
606+
*
607+
* Example:
608+
* @code
609+
* int auth_response_handler(guac_user* user, guac_stream* stream,
610+
* char* mimetype, char* challenge_id);
611+
*
612+
* int guac_user_init(guac_user* user, int argc, char** argv) {
613+
* user->auth_response_handler = auth_response_handler;
614+
* }
615+
* @endcode
616+
*/
617+
guac_user_auth_response_handler* auth_response_handler;
618+
601619
};
602620

603621
/**

src/libguac/protocol.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,7 @@ int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
13011301
int ret_val;
13021302

13031303
guac_socket_instruction_begin(socket);
1304-
ret_val =
1304+
ret_val =
13051305
guac_socket_write_string(socket, "5.video,")
13061306
|| __guac_socket_write_length_int(socket, stream->index)
13071307
|| guac_socket_write_string(socket, ",")
@@ -1315,6 +1315,27 @@ int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
13151315

13161316
}
13171317

1318+
int guac_protocol_send_auth_challenge(guac_socket* socket,
1319+
const guac_stream* stream, const char* mimetype,
1320+
const char* challenge_id) {
1321+
1322+
int ret_val;
1323+
1324+
guac_socket_instruction_begin(socket);
1325+
ret_val =
1326+
guac_socket_write_string(socket, "14.auth-challenge,")
1327+
|| __guac_socket_write_length_int(socket, stream->index)
1328+
|| guac_socket_write_string(socket, ",")
1329+
|| __guac_socket_write_length_string(socket, mimetype)
1330+
|| guac_socket_write_string(socket, ",")
1331+
|| __guac_socket_write_length_string(socket, challenge_id)
1332+
|| guac_socket_write_string(socket, ";");
1333+
guac_socket_instruction_end(socket);
1334+
1335+
return ret_val;
1336+
1337+
}
1338+
13181339
/**
13191340
* Returns the value of a single base64 character.
13201341
*/

src/libguac/tests/Makefile.am

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ noinst_HEADERS = \
3939
test_libguac_SOURCES = \
4040
client/buffer_pool.c \
4141
client/layer_pool.c \
42+
client/stream_free_handler.c \
4243
fifo/fifo.c \
4344
file/openat.c \
4445
flag/flag.c \
@@ -59,6 +60,7 @@ test_libguac_SOURCES = \
5960
pool/next_free.c \
6061
protocol/base64_decode.c \
6162
protocol/guac_protocol_version.c \
63+
protocol/auth_send.c \
6264
rect/align.c \
6365
rect/constrain.c \
6466
rect/extend.c \
@@ -74,7 +76,8 @@ test_libguac_SOURCES = \
7476
unicode/charsize.c \
7577
unicode/read.c \
7678
unicode/strlen.c \
77-
unicode/write.c
79+
unicode/write.c \
80+
user/auth_dispatch.c
7881

7982
test_libguac_CFLAGS = \
8083
-Werror -Wall -pedantic \

0 commit comments

Comments
 (0)