Skip to content

Commit 4ec4a38

Browse files
committed
GUACAMOLE-2185: Port numbers should be validated by guacd.
1 parent 44b1638 commit 4ec4a38

File tree

14 files changed

+274
-105
lines changed

14 files changed

+274
-105
lines changed

src/libguac/guacamole/string.h

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,24 @@
3030
#include <string.h>
3131

3232
/**
33-
* Convert the provided unsigned integer into a string, returning the number of
34-
* characters written into the destination string, or a negative value if an
35-
* error occurs.
33+
* Converts the given integer to a string safely. The resulting string will be
34+
* written into the provided buffer, ensuring that the buffer is not exceeded.
35+
* The conversion will fail if the buffer is too small to hold the result.
3636
*
3737
* @param dest
38-
* The destination string to copy the data into, which should already be
39-
* allocated and at a size that can handle the string representation of the
40-
* inteer.
38+
* The buffer to write the converted string into.
39+
*
40+
* @param dest_size
41+
* The size of the provided buffer, in bytes.
4142
*
4243
* @param integer
43-
* The unsigned integer to convert to a string.
44-
*
44+
* The integer to convert to a string.
45+
*
4546
* @return
46-
* The number of characters written into the dest string.
47+
* The number of characters written (excluding the null terminator), or
48+
* -1 if the string was truncated, or a negative value if an error occurs.
4749
*/
48-
int guac_itoa(char* restrict dest, unsigned int integer);
50+
int guac_itoa_safe(char* restrict dest, size_t dest_size, int integer);
4951

5052
/**
5153
* Copies a limited number of bytes from the given source string to the given

src/libguac/guacamole/user.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,98 @@ int guac_user_supports_webp(guac_user* user);
932932
char* guac_user_parse_args_string(guac_user* user, const char** arg_names,
933933
const char** argv, int index, const char* default_value);
934934

935+
/**
936+
* Automatically handles a single argument received from a joining user,
937+
* returning a newly-allocated string containing that value. The argument
938+
* is parsed as an integer and checked to ensure it is within the specified
939+
* bounds. If the argument provided by the user is blank, a newly-allocated
940+
* string containing the default value is returned. If the integer is out of
941+
* bounds, the default value is returned.
942+
*
943+
* @param user
944+
* The user joining the connection and providing the given arguments.
945+
*
946+
* @param arg_names
947+
* A NULL-terminated array of argument names, corresponding to the provided
948+
* array of argument values. This array must be exactly the same size as
949+
* the argument value array, with one additional entry for the NULL
950+
* terminator.
951+
*
952+
* @param argv
953+
* An array of all argument values, corresponding to the provided array of
954+
* argument names. This array must be exactly the same size as the argument
955+
* name array, with the exception of the NULL terminator.
956+
*
957+
* @param index
958+
* The index of the entry in both the arg_names and argv arrays which
959+
* corresponds to the argument being parsed.
960+
*
961+
* @param default_value
962+
* The value to return if the provided argument is blank or invalid. If this
963+
* value is not NULL, the returned value will be a newly-allocated string
964+
* containing this value.
965+
*
966+
* @param min
967+
* The minimum allowed value for the parsed integer. If the parsed integer is
968+
* less than this value, the default value will be returned.
969+
*
970+
* @param max
971+
* The maximum allowed value for the parsed integer. If the parsed integer is
972+
* greater than this value, the default value will be returned.
973+
*
974+
* @return
975+
* A newly-allocated string containing the provided argument value, parsed as
976+
* an integer and checked against the specified bounds. If the argument is blank,
977+
* or if the integer is out of bounds, the function returns a newly-allocated string
978+
* containing the default value. If the default value is NULL and the provided
979+
* argument is blank or invalid, NULL is returned.
980+
*/
981+
char* guac_user_parse_args_int_string_bound(guac_user* user, const char** arg_names,
982+
const char** argv, int index, const char* default_value, int min, int max);
983+
984+
/**
985+
* Automatically handles a single integer argument received from a joining
986+
* user, returning the integer value of that argument. The argument is checked
987+
* to ensure it is within the specified bounds. If the argument is blank,
988+
* invalid, or out of bounds, the default value is returned.
989+
*
990+
* @param user
991+
* The user joining the connection and providing the given arguments.
992+
*
993+
* @param arg_names
994+
* A NULL-terminated array of argument names, corresponding to the provided
995+
* array of argument values. This array must be exactly the same size as
996+
* the argument value array, with one additional entry for the NULL
997+
* terminator.
998+
*
999+
* @param argv
1000+
* An array of all argument values, corresponding to the provided array of
1001+
* argument names. This array must be exactly the same size as the argument
1002+
* name array, with the exception of the NULL terminator.
1003+
*
1004+
* @param index
1005+
* The index of the entry in both the arg_names and argv arrays which
1006+
* corresponds to the argument being parsed.
1007+
*
1008+
* @param default_value
1009+
* The value to return if the provided argument is blank, invalid, or out of bounds.
1010+
*
1011+
* @param min
1012+
* The minimum allowed value for the argument. If the parsed value is less
1013+
* than this, the default value will be returned.
1014+
*
1015+
* @param max
1016+
* The maximum allowed value for the argument. If the parsed value is greater
1017+
* than this, the default value will be returned.
1018+
*
1019+
* @return
1020+
* The integer value parsed from the provided argument value, if it is valid
1021+
* and within the specified bounds. If the argument is blank, invalid, or out of bounds,
1022+
* the default value is returned.
1023+
*/
1024+
int guac_user_parse_args_int_bound(guac_user* user, const char** arg_names,
1025+
const char** argv, int index, int default_value, int min, int max);
1026+
9351027
/**
9361028
* Automatically handles a single integer argument received from a joining
9371029
* user, returning the integer value of that argument. If the argument provided

src/libguac/guacamole/wol-constants.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,12 @@
5959
*/
6060
#define GUAC_WOL_PORT 9
6161

62+
/**
63+
* The maximum length (in characters) of the decimal string form of a TCP/UDP
64+
* port number, including the null terminator. The largest valid port number is
65+
* 65535 (5 digits), so the buffer must be at least 6 bytes long.
66+
*/
67+
#define GUAC_WOL_PORT_STRLEN 6
68+
6269
#endif /* GUAC_WOL_CONSTANTS_H */
6370

src/libguac/string.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,19 @@
4545
*/
4646
#define REMAINING(n, length) (((n) < (length)) ? 0 : ((n) - (length)))
4747

48-
int guac_itoa(char* restrict dest, unsigned int integer) {
48+
int guac_itoa_safe(char* restrict dest, size_t dest_size, int integer)
49+
{
50+
int str_size = snprintf(dest, dest_size, "%d", integer);
4951

50-
/* Determine size of string. */
51-
int str_size = snprintf(dest, 0, "%i", integer);
52-
53-
/* If an error occurs, just return that and skip the conversion. */
5452
if (str_size < 0)
5553
return str_size;
5654

57-
/* Do the conversion and return. */
58-
return snprintf(dest, (str_size + 1), "%i", integer);
55+
/* Return an error if the string was truncated. */
56+
if ((size_t)str_size >= dest_size)
57+
return -1;
5958

59+
/* Return the number of characters written (excluding the terminator). */
60+
return str_size;
6061
}
6162

6263
size_t guac_strlcpy(char* restrict dest, const char* restrict src, size_t n) {

src/libguac/user.c

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -366,12 +366,15 @@ char* guac_user_parse_args_string(guac_user* user, const char** arg_names,
366366
/* Pull parameter value from argv */
367367
const char* value = argv[index];
368368

369-
/* Use default value if blank */
370-
if (value[0] == 0) {
369+
/* Use default_value if value is NULL, or an empty string */
370+
if (value == NULL || value[0] == 0) {
371371

372372
/* NULL is a completely legal default value */
373-
if (default_value == NULL)
373+
if (default_value == NULL) {
374+
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. No default value provided.",
375+
arg_names[index]);
374376
return NULL;
377+
}
375378

376379
/* Log use of default */
377380
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
@@ -386,17 +389,74 @@ char* guac_user_parse_args_string(guac_user* user, const char** arg_names,
386389

387390
}
388391

389-
int guac_user_parse_args_int(guac_user* user, const char** arg_names,
390-
const char** argv, int index, int default_value) {
392+
char* guac_user_parse_args_int_string_bound(guac_user* user, const char** arg_names,
393+
const char** argv, int index, const char* default_value, int min, int max) {
394+
395+
char* parse_end;
396+
long parsed_value;
397+
398+
/* Pull parameter value from argv */
399+
const char* value = argv[index];
400+
401+
/* Use default_value if value is NULL, or an empty string */
402+
if (value == NULL || value[0] == 0) {
403+
404+
/* NULL is a completely legal default value */
405+
if (default_value == NULL) {
406+
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. No default value provided.",
407+
arg_names[index]);
408+
409+
return NULL;
410+
}
411+
412+
/* Log use of default */
413+
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
414+
"default value of \"%s\".", arg_names[index], default_value);
415+
416+
return guac_strdup(default_value);
417+
418+
}
419+
420+
/* Parse value using strtol, checking for errors */
421+
errno = 0;
422+
parsed_value = strtol(value, &parse_end, 10);
423+
424+
/* Check for parsing errors or invalid range */
425+
if (errno != 0 || *parse_end != '\0' || parsed_value < min || parsed_value > max) {
426+
427+
/* NULL is a completely legal default value */
428+
if (default_value == NULL) {
429+
guac_user_log(user, GUAC_LOG_WARNING, "Specified value \"%s\" for "
430+
"parameter \"%s\" is not valid. No default value provided",
431+
value, arg_names[index]);
432+
433+
return NULL;
434+
}
435+
436+
/* Log use of default */
437+
guac_user_log(user, GUAC_LOG_WARNING, "Specified value \"%s\" for "
438+
"parameter \"%s\" is not valid. Using default value "
439+
"of \"%s\".", value, arg_names[index], default_value);
440+
441+
return guac_strdup(default_value);
442+
443+
}
444+
445+
/* Otherwise use provided value */
446+
return guac_strdup(value);
447+
}
448+
449+
int guac_user_parse_args_int_bound(guac_user* user, const char** arg_names,
450+
const char** argv, int index, int default_value, int min, int max) {
391451

392452
char* parse_end;
393453
long parsed_value;
394454

395455
/* Pull parameter value from argv */
396456
const char* value = argv[index];
397457

398-
/* Use default value if blank */
399-
if (value[0] == 0) {
458+
/* Use default_value if value is NULL, or an empty string */
459+
if (value == NULL || value[0] == 0) {
400460

401461
/* Log use of default */
402462
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
@@ -410,24 +470,28 @@ int guac_user_parse_args_int(guac_user* user, const char** arg_names,
410470
errno = 0;
411471
parsed_value = strtol(value, &parse_end, 10);
412472

413-
/* Ensure parsed value is within the legal range of an int */
414-
if (parsed_value < INT_MIN || parsed_value > INT_MAX)
415-
errno = ERANGE;
416-
417-
/* Resort to default if input is invalid */
418-
if (errno != 0 || *parse_end != '\0') {
473+
/* Check for parsing errors or invalid range */
474+
if (errno != 0 || *parse_end != '\0' || parsed_value < min || parsed_value > max) {
419475

420476
/* Log use of default */
421477
guac_user_log(user, GUAC_LOG_WARNING, "Specified value \"%s\" for "
422-
"parameter \"%s\" is not a valid integer. Using default value "
478+
"parameter \"%s\" is not valid. Using default value "
423479
"of %i.", value, arg_names[index], default_value);
424480

425481
return default_value;
426482

427483
}
428484

429485
/* Parsed successfully */
430-
return parsed_value;
486+
return (int)parsed_value;
487+
488+
}
489+
490+
int guac_user_parse_args_int(guac_user* user, const char** arg_names,
491+
const char** argv, int index, int default_value) {
492+
493+
return guac_user_parse_args_int_bound(user, arg_names, argv, index,
494+
default_value, -INT_MAX, INT_MAX);
431495

432496
}
433497

@@ -437,8 +501,8 @@ int guac_user_parse_args_boolean(guac_user* user, const char** arg_names,
437501
/* Pull parameter value from argv */
438502
const char* value = argv[index];
439503

440-
/* Use default value if blank */
441-
if (value[0] == 0) {
504+
/* Use default_value if value is NULL, or an empty string */
505+
if (value == NULL || value[0] == 0) {
442506

443507
/* Log use of default */
444508
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "

src/protocols/kubernetes/settings.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,9 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
278278
IDX_HOSTNAME, "");
279279

280280
/* Read port */
281-
settings->port =
282-
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
283-
IDX_PORT, GUAC_KUBERNETES_DEFAULT_PORT);
281+
settings->port = (unsigned short)
282+
guac_user_parse_args_int_bound(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
283+
IDX_PORT, GUAC_KUBERNETES_DEFAULT_PORT, 0, USHRT_MAX);
284284

285285
/* Read Kubernetes namespace */
286286
settings->kubernetes_namespace =

src/protocols/rdp/rdp.c

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -737,13 +737,12 @@ void* guac_rdp_client_thread(void* data) {
737737
*/
738738
if (settings->wol_wait_time > 0) {
739739
guac_client_log(client, GUAC_LOG_DEBUG, "Sending Wake-on-LAN packet, "
740-
"and pausing for %d seconds.", settings->wol_wait_time);
740+
"and retrying connection check %d times every %d seconds.",
741+
GUAC_WOL_DEFAULT_CONNECT_RETRIES, settings->wol_wait_time);
741742

742-
/* char representation of a port should be, at most, 5 digits plus terminator. */
743-
char* str_port = guac_mem_alloc(6);
744-
if (guac_itoa(str_port, settings->port) < 1) {
745-
guac_client_log(client, GUAC_LOG_ERROR, "Failed to convert port to integer for WOL function.");
746-
guac_mem_free(str_port);
743+
char str_port[GUAC_WOL_PORT_STRLEN];
744+
if (guac_itoa_safe(str_port, sizeof(str_port), settings->port) < 1) {
745+
guac_client_log(client, GUAC_LOG_ERROR, "Failed to convert port to string for WOL function.");
747746
return NULL;
748747
}
749748

@@ -754,26 +753,27 @@ void* guac_rdp_client_thread(void* data) {
754753
settings->wol_wait_time,
755754
GUAC_WOL_DEFAULT_CONNECT_RETRIES,
756755
settings->hostname,
757-
(const char *) str_port,
758-
GUAC_WOL_DEFAULT_CONNECTION_TIMEOUT)) {
759-
guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet, or server failed to wake up.");
760-
guac_mem_free(str_port);
756+
str_port,
757+
settings->timeout)) {
758+
guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet, or connect to remote server.");
761759
return NULL;
762760
}
763-
764-
guac_mem_free(str_port);
765-
766761
}
767762

768-
/* Just send the packet and continue the connection, or return if failed. */
769-
else if(guac_wol_wake(settings->wol_mac_addr,
763+
/* Just send the packet, or return NULL if failed. */
764+
else {
765+
guac_client_log(client, GUAC_LOG_DEBUG, "Sending RDP Wake-on-LAN packet.");
766+
767+
if(guac_wol_wake(settings->wol_mac_addr,
770768
settings->wol_broadcast_addr,
771769
settings->wol_udp_port)) {
772-
guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet.");
773-
return NULL;
770+
guac_client_log(client, GUAC_LOG_ERROR, "Failed to send WOL packet.");
771+
return NULL;
772+
}
774773
}
774+
775775
}
776-
776+
777777
/* If audio enabled, choose an encoder */
778778
if (settings->audio_enabled) {
779779

0 commit comments

Comments
 (0)