GUACAMOLE-637: Add unit tests for RDP filesystem path normalization.
diff --git a/configure.ac b/configure.ac
index 1323123..505f696 100644
--- a/configure.ac
+++ b/configure.ac
@@ -151,6 +151,10 @@
AC_SUBST([COMMON_SSH_LTLIB], '$(top_builddir)/src/common-ssh/libguac_common_ssh.la')
AC_SUBST([COMMON_SSH_INCLUDE], '-I$(top_srcdir)/src/common-ssh')
+# RDP support
+AC_SUBST([LIBGUAC_CLIENT_RDP_LTLIB], '$(top_builddir)/src/protocols/rdp/libguac-client-rdp.la')
+AC_SUBST([LIBGUAC_CLIENT_RDP_INCLUDE], '-I$(top_srcdir)/src/protocols/rdp')
+
# Terminal emulator
AC_SUBST([TERMINAL_LTLIB], '$(top_builddir)/src/terminal/libguac_terminal.la')
AC_SUBST([TERMINAL_INCLUDE], '-I$(top_srcdir)/src/terminal $(PANGO_CFLAGS) $(PANGOCAIRO_CFLAGS) $(COMMON_INCLUDE)')
@@ -1331,6 +1335,7 @@
src/pulse/Makefile
src/protocols/kubernetes/Makefile
src/protocols/rdp/Makefile
+ src/protocols/rdp/tests/Makefile
src/protocols/ssh/Makefile
src/protocols/telnet/Makefile
src/protocols/vnc/Makefile])
diff --git a/src/protocols/rdp/.gitignore b/src/protocols/rdp/.gitignore
new file mode 100644
index 0000000..9f87ecb
--- /dev/null
+++ b/src/protocols/rdp/.gitignore
@@ -0,0 +1,8 @@
+
+# Auto-generated test runner and binary
+_generated_runner.c
+test_rdp
+
+# Autogenerated sources
+_generated_keymaps.c
+
diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am
index 05a7a3a..cbb6c4f 100644
--- a/src/protocols/rdp/Makefile.am
+++ b/src/protocols/rdp/Makefile.am
@@ -27,6 +27,7 @@
ACLOCAL_AMFLAGS = -I m4
lib_LTLIBRARIES = libguac-client-rdp.la
+SUBDIRS = . tests
nodist_libguac_client_rdp_la_SOURCES = \
_generated_keymaps.c
diff --git a/src/protocols/rdp/tests/Makefile.am b/src/protocols/rdp/tests/Makefile.am
new file mode 100644
index 0000000..a803b63
--- /dev/null
+++ b/src/protocols/rdp/tests/Makefile.am
@@ -0,0 +1,64 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+# NOTE: Parts of this file (Makefile.am) are automatically transcluded verbatim
+# into Makefile.in. Though the build system (GNU Autotools) automatically adds
+# its own license boilerplate to the generated Makefile.in, that boilerplate
+# does not apply to the transcluded portions of Makefile.am which are licensed
+# to you by the ASF under the Apache License, Version 2.0, as described above.
+#
+
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+
+#
+# Unit tests for RDP support
+#
+
+check_PROGRAMS = test_rdp
+TESTS = $(check_PROGRAMS)
+
+test_rdp_SOURCES = \
+ fs/normalize_path.c
+
+test_rdp_CFLAGS = \
+ -Werror -Wall -pedantic \
+ @LIBGUAC_CLIENT_RDP_INCLUDE@
+
+test_rdp_LDADD = \
+ @CUNIT_LIBS@ \
+ @LIBGUAC_CLIENT_RDP_LTLIB@
+
+#
+# Autogenerate test runner
+#
+
+GEN_RUNNER = $(top_srcdir)/util/generate-test-runner.pl
+CLEANFILES = _generated_runner.c
+
+_generated_runner.c: $(test_rdp_SOURCES)
+ $(AM_V_GEN) $(GEN_RUNNER) $(test_rdp_SOURCES) > $@
+
+nodist_test_rdp_SOURCES = \
+ _generated_runner.c
+
+# Use automake's TAP test driver for running any tests
+LOG_DRIVER = \
+ env AM_TAP_AWK='$(AWK)' \
+ $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
+
diff --git a/src/protocols/rdp/tests/fs/normalize_path.c b/src/protocols/rdp/tests/fs/normalize_path.c
new file mode 100644
index 0000000..0d8f17f
--- /dev/null
+++ b/src/protocols/rdp/tests/fs/normalize_path.c
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "rdp_fs.h"
+
+#include <CUnit/CUnit.h>
+#include <stdlib.h>
+
+/**
+ * Test which verifies absolute Windows-style paths are correctly normalized to
+ * absolute paths with Windows separators and no relative components.
+ */
+void test_fs__normalize_absolute_windows() {
+
+ char normalized[GUAC_RDP_FS_MAX_PATH];
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo\\bar\\baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\bar\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo\\bar\\..\\baz\\", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo\\bar\\..\\..\\baz\\a\\..\\b", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\baz\\b", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo\\.\\bar\\baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\bar\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo\\bar\\..\\..\\..\\..\\..\\..\\baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\baz", sizeof(normalized));
+
+}
+
+/**
+ * Test which verifies absolute UNIX-style paths are correctly normalized to
+ * absolute paths with Windows separators and no relative components.
+ */
+void test_fs__normalize_absolute_unix() {
+
+ char normalized[GUAC_RDP_FS_MAX_PATH];
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("/", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("/foo/bar/baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\bar\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("/foo/bar/../baz/", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("/foo/bar/../../baz/a/../b", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\baz\\b", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("/foo/./bar/baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\bar\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("/foo/bar/../../../../../../baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\baz", sizeof(normalized));
+
+}
+
+/**
+ * Test which verifies absolute paths consisting of mixed Windows and UNIX path
+ * separators are correctly normalized to absolute paths with Windows
+ * separators and no relative components.
+ */
+void test_fs__normalize_absolute_mixed() {
+
+ char normalized[GUAC_RDP_FS_MAX_PATH];
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo/bar\\baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\bar\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("/foo\\bar/..\\baz/", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo/bar\\../../baz\\a\\..\\b", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\baz\\b", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo\\.\\bar/baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\foo\\bar\\baz", sizeof(normalized));
+
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path("\\foo/bar\\../..\\..\\..\\../..\\baz", normalized), 0)
+ CU_ASSERT_NSTRING_EQUAL(normalized, "\\baz", sizeof(normalized));
+
+}
+
+/**
+ * Test which verifies relative Windows-style paths are always rejected.
+ */
+void test_fs__normalize_relative_windows() {
+
+ char normalized[GUAC_RDP_FS_MAX_PATH];
+
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path(".", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("..", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("foo", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path(".\\foo", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("..\\foo", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("foo\\bar\\baz", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path(".\\foo\\bar\\baz", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("..\\foo\\bar\\baz", normalized), 0)
+
+}
+
+/**
+ * Test which verifies relative UNIX-style paths are always rejected.
+ */
+void test_fs__normalize_relative_unix() {
+
+ char normalized[GUAC_RDP_FS_MAX_PATH];
+
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path(".", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("..", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("foo", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("./foo", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("../foo", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("foo/bar/baz", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("./foo/bar/baz", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("../foo/bar/baz", normalized), 0)
+
+}
+
+/**
+ * Test which verifies relative paths consisting of mixed Windows and UNIX path
+ * separators are always rejected.
+ */
+void test_fs__normalize_relative_mixed() {
+
+ char normalized[GUAC_RDP_FS_MAX_PATH];
+
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("foo\\bar/baz", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path(".\\foo/bar/baz", normalized), 0)
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path("../foo\\bar\\baz", normalized), 0)
+
+}
+
+/**
+ * Generates a dynamically-allocated path having the given number of bytes, not
+ * counting the null-terminator. The path will contain only Windows-style path
+ * separators. The returned path must eventually be freed with a call to
+ * free().
+ *
+ * @param length
+ * The number of bytes to include in the generated path, not counting the
+ * null-terminator.
+ *
+ * @return
+ * A dynamically-allocated path containing the given number of bytes, not
+ * counting the null-terminator. This path must eventually be freed with a
+ * call to free().
+ */
+static char* generate_path(int length) {
+
+ int i;
+ char* input = malloc(length + 1);
+
+ /* Fill path with \x\x\x\x\x\x\x\x\x\x\... */
+ for (i = 0; i < length; i++) {
+ input[i] = (i % 2 == 0) ? '\\' : 'x';
+ }
+
+ /* Add null terminator */
+ input[length] = '\0';
+
+ return input;
+
+}
+
+/**
+ * Test which verifies that paths exceeding the maximum path length are
+ * rejected.
+ */
+void test_fs__normalize_long() {
+
+ char* input;
+ char normalized[GUAC_RDP_FS_MAX_PATH];
+
+ /* Exceeds maximum length by a factor of 2 */
+ input = generate_path(GUAC_RDP_FS_MAX_PATH*2);
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path(input, normalized), 0);
+ free(input);
+
+ /* Exceeds maximum length by one byte */
+ input = generate_path(GUAC_RDP_FS_MAX_PATH);
+ CU_ASSERT_NOT_EQUAL(guac_rdp_fs_normalize_path(input, normalized), 0);
+ free(input);
+
+ /* Exactly maximum length */
+ input = generate_path(GUAC_RDP_FS_MAX_PATH - 1);
+ CU_ASSERT_EQUAL(guac_rdp_fs_normalize_path(input, normalized), 0);
+ free(input);
+
+}
+