Skip to content

Commit 922afc5

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

File tree

15 files changed

+321
-91
lines changed

15 files changed

+321
-91
lines changed

src/libguac/guacamole/string.h

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,39 @@
2626
* @file string.h
2727
*/
2828

29+
#include <limits.h>
2930
#include <stddef.h>
3031
#include <string.h>
3132

33+
/**
34+
* String buffer size needed to represent 'int', and 'unsigned short'in decimal
35+
* format. This includes the NULL terminator.
36+
*
37+
* Integer size can vary by platform, but short size is consistent across
38+
* all modern platforms.
39+
*/
40+
#if (__SIZEOF_INT__ == 2) /* INT_MIN: '-32768' */
41+
#define GUAC_INT_STRING_BUFSIZE 7
42+
#elif (__SIZEOF_INT__ == 4) /* INT_MIN: '-2147483648' */
43+
#define GUAC_INT_STRING_BUFSIZE 12
44+
#elif (__SIZEOF_INT__ == 8) /* INT_MIN: '-9223372036854775808' */
45+
#define GUAC_INT_STRING_BUFSIZE 21
46+
#else
47+
#error Unsupported unsigned int size
48+
#endif
49+
50+
#define GUAC_USHORT_STRING_BUFSIZE 6 /* '65535' */
51+
52+
/**
53+
* Convenience macros for the minimum and maximum values for int and and
54+
* unsigned short data types.
55+
*/
56+
#define GUAC_ITOA_INT_MIN INT_MIN
57+
#define GUAC_ITOA_INT_MAX INT_MAX
58+
59+
#define GUAC_ITOA_USHORT_MAX USHRT_MAX
60+
#define GUAC_ITOA_USHORT_MIN 0
61+
3262
/**
3363
* Convert the provided unsigned integer into a string, returning the number of
3464
* characters written into the destination string, or a negative value if an
@@ -37,15 +67,35 @@
3767
* @param dest
3868
* The destination string to copy the data into, which should already be
3969
* allocated and at a size that can handle the string representation of the
40-
* inteer.
70+
* integer (at least GUAC_INT_STRING_BUFSIZE bytes).
4171
*
4272
* @param integer
43-
* The unsigned integer to convert to a string.
73+
* The integer to convert to a string.
4474
*
4575
* @return
4676
* The number of characters written into the dest string.
4777
*/
48-
int guac_itoa(char* restrict dest, unsigned int integer);
78+
int guac_itoa(char* restrict dest, int integer);
79+
80+
/**
81+
* Converts the given integer to a string safely. The resulting string will be
82+
* written into the provided buffer, ensuring that the buffer is not exceeded.
83+
* The conversion will fail if the buffer is too small to hold the result.
84+
*
85+
* @param dest
86+
* The buffer to write the converted string into.
87+
*
88+
* @param dest_size
89+
* The size of the provided buffer, in bytes.
90+
*
91+
* @param integer
92+
* The integer to convert to a string.
93+
*
94+
* @return
95+
* The number of characters written (excluding the null terminator), or
96+
* -1 if the string was truncated, or a negative value if an error occurs.
97+
*/
98+
int guac_itoa_safe(char* restrict dest, size_t dest_size, int integer);
4999

50100
/**
51101
* 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_bounded(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_bounded(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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#ifndef GUAC_WOL_CONSTANTS_H
2121
#define GUAC_WOL_CONSTANTS_H
2222

23+
#include "guacamole/string.h"
24+
2325
/**
2426
* Header file that provides constants and defaults related to libguac
2527
* Wake-on-LAN support.

src/libguac/string.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,21 @@ int guac_itoa(char* restrict dest, unsigned int integer) {
5959

6060
}
6161

62+
int guac_itoa_safe(char* restrict dest, size_t dest_size, int integer)
63+
{
64+
int str_size = snprintf(dest, dest_size, "%d", integer);
65+
66+
if (str_size < 0)
67+
return str_size;
68+
69+
/* Return an error if the string was truncated. */
70+
if ((size_t)str_size >= dest_size)
71+
return -1;
72+
73+
/* Return the number of characters written (excluding the terminator). */
74+
return str_size;
75+
}
76+
6277
size_t guac_strlcpy(char* restrict dest, const char* restrict src, size_t n) {
6378

6479
#ifdef HAVE_STRLCPY

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_bounded(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_bounded(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_bounded(user, arg_names, argv, index,
494+
default_value, GUAC_ITOA_INT_MIN, GUAC_ITOA_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_bounded(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
283+
IDX_PORT, GUAC_KUBERNETES_DEFAULT_PORT, GUAC_ITOA_USHORT_MIN, GUAC_ITOA_USHORT_MAX);
284284

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

src/protocols/kubernetes/settings.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ typedef struct guac_kubernetes_settings {
6161
/**
6262
* The port of the Kubernetes server to connect to.
6363
*/
64-
int port;
64+
unsigned short port;
6565

6666
/**
6767
* The name of the Kubernetes namespace of the pod containing the container

0 commit comments

Comments
 (0)