diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ace91dd..91affcde 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.5)
project(rcutils)
+option(RCUTILS_NO_THREAD_SUPPORT "Disable thread support." OFF)
+option(RCUTILS_NO_FILESYSTEM "Disable filesystem usage." OFF)
+option(RCUTILS_AVOID_DYNAMIC_ALLOCATION "Disable dynamic allocations." OFF)
+
# Default to C11
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 11)
@@ -15,6 +19,7 @@ include(CheckLibraryExists)
find_package(ament_cmake_python REQUIRED)
find_package(ament_cmake_ros REQUIRED)
+find_package(tinydir_vendor REQUIRED)
ament_python_install_package(${PROJECT_NAME})
@@ -53,7 +58,9 @@ set(rcutils_sources
src/time.c
${time_impl_c}
src/uint8_array.c
+ src/security_directory.c
)
+
set_source_files_properties(
${rcutils_sources}
PROPERTIES language "C")
@@ -91,6 +98,7 @@ add_custom_command(OUTPUT include/rcutils/logging_macros.h
)
list(APPEND rcutils_sources
include/rcutils/logging_macros.h)
+include_directories("${CMAKE_CURRENT_BINARY_DIR}/include")
add_library(
${PROJECT_NAME}
@@ -104,6 +112,11 @@ target_include_directories(${PROJECT_NAME} PUBLIC
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME} PRIVATE "RCUTILS_BUILDING_DLL")
+configure_file(
+ "${PROJECT_SOURCE_DIR}/include/rcutils/configuration_flags.h.in"
+ "${PROJECT_BINARY_DIR}/include/rcutils/configuration_flags.h"
+)
+
target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS})
# Needed if pthread is used for thread local storage.
@@ -116,11 +129,18 @@ install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
+ament_target_dependencies(${PROJECT_NAME}
+ "tinydir_vendor"
+)
+
if(BUILD_TESTING)
if(NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
endif()
+ set(test_resources_dir_name "test/resources")
+ add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/${test_resources_dir_name}")
+
find_package(ament_cmake_gmock REQUIRED)
find_package(ament_cmake_gtest REQUIRED)
find_package(ament_cmake_pytest REQUIRED)
@@ -428,6 +448,16 @@ if(BUILD_TESTING)
endif()
+ rcutils_custom_add_gtest(test_security_directory
+ test/test_security_directory.cpp
+ )
+ if(TARGET test_security_directory)
+ target_link_libraries(test_security_directory ${PROJECT_NAME} osrf_testing_tools_cpp::memory_tools)
+ endif()
+
+ install(DIRECTORY ${test_resources_dir_name}
+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
+ )
endif()
ament_export_dependencies(ament_cmake)
diff --git a/include/rcutils/allocator.h b/include/rcutils/allocator.h
index e84706cb..7089adf7 100644
--- a/include/rcutils/allocator.h
+++ b/include/rcutils/allocator.h
@@ -83,6 +83,21 @@ RCUTILS_WARN_UNUSED
rcutils_allocator_t
rcutils_get_zero_initialized_allocator(void);
+/// Set rcutils default allocators.
+/**
+ *
+ * Attribute | Adherence
+ * ------------------ | -------------
+ * Allocates Memory | No
+ * Thread-Safe | Yes
+ * Uses Atomics | No
+ * Lock-Free | Yes
+ */
+RCUTILS_PUBLIC
+RCUTILS_WARN_UNUSED
+bool
+rcutils_set_default_allocator(rcutils_allocator_t * allocator);
+
/// Return a properly initialized rcutils_allocator_t with default values.
/**
* This defaults to:
diff --git a/include/rcutils/configuration_flags.h.in b/include/rcutils/configuration_flags.h.in
new file mode 100644
index 00000000..346c6a33
--- /dev/null
+++ b/include/rcutils/configuration_flags.h.in
@@ -0,0 +1,18 @@
+
+#ifndef RCUTILS__CONFIGURATION_FLAGS_H_
+#define RCUTILS__CONFIGURATION_FLAGS_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#cmakedefine RCUTILS_NO_FILESYSTEM
+#cmakedefine RCUTILS_AVOID_DYNAMIC_ALLOCATION
+#cmakedefine RCUTILS_NO_THREAD_SUPPORT
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RCUTILS__CONFIGURATION_FLAGS_H_
\ No newline at end of file
diff --git a/include/rcutils/error_handling.h b/include/rcutils/error_handling.h
index 9f19b834..51f93770 100644
--- a/include/rcutils/error_handling.h
+++ b/include/rcutils/error_handling.h
@@ -38,15 +38,18 @@ extern "C"
#include "rcutils/snprintf.h"
#include "rcutils/types/rcutils_ret.h"
#include "rcutils/visibility_control.h"
+#include "rcutils/configuration_flags.h"
-#ifdef __STDC_LIB_EXT1__
+#if defined(__STDC_LIB_EXT1__) && !defined(RCUTILS_NO_FILESYSTEM)
// Limit the buffer size in the `fwrite` call to give an upper bound to buffer overrun in the case
// of non-null terminated `msg`.
#define RCUTILS_SAFE_FWRITE_TO_STDERR(msg) \
do {fwrite(msg, sizeof(char), strnlen_s(msg, 4096), stderr);} while (0)
-#else
+#elif !defined(RCUTILS_NO_FILESYSTEM)
#define RCUTILS_SAFE_FWRITE_TO_STDERR(msg) \
do {fwrite(msg, sizeof(char), strlen(msg), stderr);} while (0)
+#else
+ #define RCUTILS_SAFE_FWRITE_TO_STDERR(msg)
#endif
// fixed constraints
@@ -199,8 +202,12 @@ rcutils_set_error_state(const char * error_string, const char * file, size_t lin
*
* \param[in] msg The error message to be set.
*/
+#ifdef RCUTILS_AVOID_DYNAMIC_ALLOCATION
+ #define RCUTILS_SET_ERROR_MSG(msg)
+#else
#define RCUTILS_SET_ERROR_MSG(msg) \
do {rcutils_set_error_state(msg, __FILE__, __LINE__);} while (0)
+#endif // RCUTILS_AVOID_DYNAMIC_ALLOCATION
/// Set the error message using a format string and format arguments.
/**
@@ -211,6 +218,9 @@ rcutils_set_error_state(const char * error_string, const char * file, size_t lin
* \param[in] format_string The string to be used as the format of the error message.
* \param[in] ... Arguments for the format string.
*/
+#ifdef RCUTILS_AVOID_DYNAMIC_ALLOCATION
+ #define RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(format_string, ...)
+#else
#define RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(format_string, ...) \
do { \
char output_msg[RCUTILS_ERROR_MESSAGE_MAX_LENGTH]; \
@@ -221,6 +231,8 @@ rcutils_set_error_state(const char * error_string, const char * file, size_t lin
RCUTILS_SET_ERROR_MSG(output_msg); \
} \
} while (0)
+#endif // RCUTILS_AVOID_DYNAMIC_ALLOCATION
+
/// Return `true` if the error is set, otherwise `false`.
RCUTILS_PUBLIC
diff --git a/include/rcutils/macros.h b/include/rcutils/macros.h
index 27736a58..0c8c467e 100644
--- a/include/rcutils/macros.h
+++ b/include/rcutils/macros.h
@@ -20,6 +20,8 @@ extern "C"
{
#endif
+#include "rcutils/configuration_flags.h"
+
#ifndef _WIN32
#define RCUTILS_WARN_UNUSED __attribute__((warn_unused_result))
#else
@@ -28,7 +30,9 @@ extern "C"
// Note: this block was migrated from rmw/macros.h
// This block either sets RCUTILS_THREAD_LOCAL or RCUTILS_THREAD_LOCAL_PTHREAD.
-#if defined _WIN32 || defined __CYGWIN__
+#if defined(RCUTILS_NO_THREAD_SUPPORT)
+ #define RCUTILS_THREAD_LOCAL
+#elif defined _WIN32 || defined __CYGWIN__
// Windows or Cygwin
#define RCUTILS_THREAD_LOCAL __declspec(thread)
#elif defined __APPLE__
diff --git a/include/rcutils/security_directory.h b/include/rcutils/security_directory.h
new file mode 100644
index 00000000..9d2c6806
--- /dev/null
+++ b/include/rcutils/security_directory.h
@@ -0,0 +1,67 @@
+// Copyright 2018 Open Source Robotics Foundation, Inc.
+//
+// Licensed 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.
+
+#ifndef RCUTILS__SECURITY_DIRECTORY_H_
+#define RCUTILS__SECURITY_DIRECTORY_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "rcutils/allocator.h"
+#include "rcutils/visibility_control.h"
+
+#ifndef ROS_SECURITY_NODE_DIRECTORY_VAR_NAME
+ #define ROS_SECURITY_NODE_DIRECTORY_VAR_NAME "ROS_SECURITY_NODE_DIRECTORY"
+#endif
+
+#ifndef ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME
+ #define ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "ROS_SECURITY_ROOT_DIRECTORY"
+#endif
+
+#ifndef ROS_SECURITY_LOOKUP_TYPE_VAR_NAME
+ #define ROS_SECURITY_LOOKUP_TYPE_VAR_NAME "ROS_SECURITY_LOOKUP_TYPE"
+#endif
+
+/// Return the secure root directory associated with a node given its validated name and namespace.
+/**
+ * E.g. for a node named "c" in namespace "/a/b", the secure root path will be
+ * "a/b/c", where the delimiter "/" is native for target file system (e.g. "\\" for _WIN32).
+ * If no exact match is found for the node name, a best match would be used instead
+ * (by performing longest-prefix matching).
+ *
+ * However, this expansion can be overridden by setting the secure node directory environment
+ * variable, allowing users to explicitly specify the exact secure root directory to be utilized.
+ * Such an override is useful for where the FQN of a node is non-deterministic before runtime,
+ * or when testing and using additional tools that may not otherwise be easily provisioned.
+ *
+ * \param[in] node_name validated node name (a single token)
+ * \param[in] node_namespace validated, absolute namespace (starting with "/")
+ * \param[in] allocator the allocator to use for allocation
+ * \returns machine specific (absolute) node secure root path or NULL on failure
+ * returned pointer must be deallocated by the caller of this function
+ */
+RCUTILS_PUBLIC
+char * rcutils_get_secure_root(
+ const char * node_name,
+ const char * node_namespace,
+ const rcutils_allocator_t * allocator
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RCUTILS__SECURITY_DIRECTORY_H_
diff --git a/package.xml b/package.xml
index fea8335e..f1906c0d 100644
--- a/package.xml
+++ b/package.xml
@@ -10,6 +10,9 @@
ament_cmake_ros
python3-empy
+ tinydir_vendor
+ tinydir_vendor
+
ament_cmake_gmock
ament_cmake_gtest
ament_cmake_pytest
diff --git a/src/allocator.c b/src/allocator.c
index 92ef82d0..ab1972c7 100644
--- a/src/allocator.c
+++ b/src/allocator.c
@@ -69,16 +69,31 @@ rcutils_get_zero_initialized_allocator(void)
return zero_allocator;
}
-rcutils_allocator_t
-rcutils_get_default_allocator()
-{
- static rcutils_allocator_t default_allocator = {
+static rcutils_allocator_t default_allocator = {
.allocate = __default_allocate,
.deallocate = __default_deallocate,
.reallocate = __default_reallocate,
.zero_allocate = __default_zero_allocate,
.state = NULL,
};
+
+bool
+rcutils_set_default_allocator(rcutils_allocator_t * allocator){
+ if (rcutils_allocator_is_valid(allocator))
+ {
+ default_allocator.allocate = allocator->allocate;
+ default_allocator.deallocate = allocator->deallocate;
+ default_allocator.reallocate = allocator->reallocate;
+ default_allocator.zero_allocate = allocator->zero_allocate;
+ default_allocator.state = NULL;
+ return true;
+ }
+ return false;
+}
+
+rcutils_allocator_t
+rcutils_get_default_allocator()
+{
return default_allocator;
}
diff --git a/src/filesystem.c b/src/filesystem.c
index 923bfd81..f89ebbd2 100644
--- a/src/filesystem.c
+++ b/src/filesystem.c
@@ -22,9 +22,13 @@ extern "C"
#include
#include
#include
+#ifndef RCUTILS_NO_FILESYSTEM
#include
+#endif
#ifndef _WIN32
+#ifndef RCUTILS_NO_FILESYSTEM
#include
+#endif
#include
#else
#include
@@ -33,6 +37,7 @@ extern "C"
#include "rcutils/format_string.h"
#include "rcutils/repl_str.h"
+#include "rcutils/error_handling.h"
#ifdef _WIN32
# define RCUTILS_PATH_DELIMITER "\\"
@@ -43,6 +48,10 @@ extern "C"
bool
rcutils_get_cwd(char * buffer, size_t max_length)
{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
if (NULL == buffer || max_length == 0) {
return false;
}
@@ -56,11 +65,16 @@ rcutils_get_cwd(char * buffer, size_t max_length)
}
#endif // _WIN32
return true;
+#endif // _RCUTILS_NO_FILESYSTEM
}
bool
rcutils_is_directory(const char * abs_path)
{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
struct stat buf;
if (stat(abs_path, &buf) < 0) {
return false;
@@ -70,11 +84,16 @@ rcutils_is_directory(const char * abs_path)
#else
return S_ISDIR(buf.st_mode);
#endif // _WIN32
+#endif // _RCUTILS_NO_FILESYSTEM
}
bool
rcutils_is_file(const char * abs_path)
{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
struct stat buf;
if (stat(abs_path, &buf) < 0) {
return false;
@@ -84,21 +103,31 @@ rcutils_is_file(const char * abs_path)
#else
return S_ISREG(buf.st_mode);
#endif // _WIN32
+#endif // _RCUTILS_NO_FILESYSTEM
}
bool
rcutils_exists(const char * abs_path)
{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
struct stat buf;
if (stat(abs_path, &buf) < 0) {
return false;
}
return true;
+#endif // _RCUTILS_NO_FILESYSTEM
}
bool
rcutils_is_readable(const char * abs_path)
{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
struct stat buf;
if (stat(abs_path, &buf) < 0) {
return false;
@@ -111,11 +140,16 @@ rcutils_is_readable(const char * abs_path)
return false;
}
return true;
+#endif // _RCUTILS_NO_FILESYSTEM
}
bool
rcutils_is_writable(const char * abs_path)
{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
struct stat buf;
if (stat(abs_path, &buf) < 0) {
return false;
@@ -128,11 +162,16 @@ rcutils_is_writable(const char * abs_path)
return false;
}
return true;
+#endif // _RCUTILS_NO_FILESYSTEM
}
bool
rcutils_is_readable_and_writable(const char * abs_path)
{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
struct stat buf;
if (stat(abs_path, &buf) < 0) {
return false;
@@ -147,6 +186,7 @@ rcutils_is_readable_and_writable(const char * abs_path)
return false;
}
return true;
+#endif // _RCUTILS_NO_FILESYSTEM
}
char *
@@ -215,6 +255,12 @@ rcutils_mkdir(const char * abs_path)
size_t
rcutils_calculate_directory_size(const char * directory_path, rcutils_allocator_t allocator)
{
+
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return 0;
+#else
+
size_t dir_size = 0;
if (!rcutils_is_directory(directory_path)) {
@@ -260,11 +306,18 @@ rcutils_calculate_directory_size(const char * directory_path, rcutils_allocator_
closedir(dir);
return dir_size;
#endif
+
+#endif // _RCUTILS_NO_FILESYSTEM
}
size_t
rcutils_get_file_size(const char * file_path)
{
+
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return 0;
+#else
if (!rcutils_is_file(file_path)) {
fprintf(stderr, "Path is not a file: %s\n", file_path);
return 0;
@@ -273,6 +326,7 @@ rcutils_get_file_size(const char * file_path)
struct stat stat_buffer;
int rc = stat(file_path, &stat_buffer);
return rc == 0 ? (size_t)(stat_buffer.st_size) : 0;
+#endif // _RCUTILS_NO_FILESYSTEM
}
#ifdef __cplusplus
diff --git a/src/process.c b/src/process.c
index 832c3d1a..288fa2a5 100644
--- a/src/process.c
+++ b/src/process.c
@@ -50,7 +50,13 @@ char * rcutils_get_executable_name(rcutils_allocator_t allocator)
#if defined __APPLE__ || defined __FreeBSD__ || (defined __ANDROID__ && __ANDROID_API__ >= 21)
const char * appname = getprogname();
#elif defined __GNUC__
- const char * appname = program_invocation_name;
+ #if defined __linux__ || defined __linux || defined __gnu_linux__ || defined linux
+ const char * appname = program_invocation_name;
+ #else
+ // Some embedded OS compile with __GNUC__ but are not quite conformant with GNU-specific extensions.
+ // They may fake to have a GLIBC in their custom C library implementation.
+ const char * appname = "";
+ #endif
#elif defined _WIN32 || defined __CYGWIN__
char appname[MAX_PATH];
int32_t size = GetModuleFileNameA(NULL, appname, MAX_PATH);
diff --git a/src/security_directory.c b/src/security_directory.c
new file mode 100644
index 00000000..4bcba2d7
--- /dev/null
+++ b/src/security_directory.c
@@ -0,0 +1,278 @@
+// Copyright 2018 Open Source Robotics Foundation, Inc.
+//
+// Licensed 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 "rcutils/security_directory.h"
+
+#include "rcutils/error_handling.h"
+#include "rcutils/filesystem.h"
+#include "rcutils/get_env.h"
+#include "rcutils/format_string.h"
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wembedded-directive"
+#endif
+
+#ifndef RCUTILS_NO_FILESYSTEM
+#include "tinydir/tinydir.h"
+#endif
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+/**
+ * A security lookup function takes in the node's name, namespace, a security root directory and an allocator;
+ * It returns the relevant information required to load the security credentials,
+ * which is currently a path to a directory on the filesystem containing DDS Security permission files.
+ */
+typedef char * (* security_lookup_fn_t) (
+ const char * node_name,
+ const char * node_namespace,
+ const char * ros_secure_root_env,
+ const rcutils_allocator_t * allocator
+);
+
+char * exact_match_lookup(
+ const char * node_name,
+ const char * node_namespace,
+ const char * ros_secure_root_env,
+ const rcutils_allocator_t * allocator
+);
+
+char * prefix_match_lookup(
+ const char * node_name,
+ const char * node_namespace,
+ const char * ros_secure_root_env,
+ const rcutils_allocator_t * allocator
+);
+
+security_lookup_fn_t g_security_lookup_fns[] = {
+ NULL,
+ exact_match_lookup,
+ prefix_match_lookup,
+};
+
+typedef enum ros_security_lookup_type_e
+{
+ ROS_SECURITY_LOOKUP_NODE_OVERRIDE = 0,
+ ROS_SECURITY_LOOKUP_MATCH_EXACT = 1,
+ ROS_SECURITY_LOOKUP_MATCH_PREFIX = 2,
+} ros_security_lookup_type_t;
+
+char * g_security_lookup_type_strings[] = {
+ "NODE_OVERRIDE",
+ "MATCH_EXACT",
+ "MATCH_PREFIX"
+};
+
+/// Return the directory whose name most closely matches node_name (longest-prefix match),
+/// scanning under base_dir.
+/**
+ * By using a prefix match, a node named e.g. "my_node_123" will be able to load and use the
+ * directory "my_node" if no better match exists.
+ * \param[in] base_dir
+ * \param[in] node_name
+ * \param[out] matched_name must be a valid memory address allocated with at least
+ * _TINYDIR_FILENAME_MAX characters.
+ * \return true if a match was found
+ */
+static bool get_best_matching_directory(
+ const char * base_dir,
+ const char * node_name,
+ char * matched_name)
+{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
+size_t max_match_length = 0;
+ tinydir_dir dir;
+ if (NULL == base_dir || NULL == node_name || NULL == matched_name) {
+ return false;
+ }
+ if (-1 == tinydir_open(&dir, base_dir)) {
+ return false;
+ }
+ while (dir.has_next) {
+ tinydir_file file;
+ if (-1 == tinydir_readfile(&dir, &file)) {
+ goto cleanup;
+ }
+ if (file.is_dir) {
+ size_t matched_name_length = strnlen(file.name, sizeof(file.name) - 1);
+ if (0 ==
+ strncmp(file.name, node_name,
+ matched_name_length) && matched_name_length > max_match_length)
+ {
+ max_match_length = matched_name_length;
+ memcpy(matched_name, file.name, max_match_length);
+ }
+ }
+ if (-1 == tinydir_next(&dir)) {
+ goto cleanup;
+ }
+ }
+cleanup:
+ tinydir_close(&dir);
+ return max_match_length > 0;
+#endif // _RCUTILS_NO_FILESYSTEM
+}
+
+char * exact_match_lookup(
+ const char * node_name,
+ const char * node_namespace,
+ const char * ros_secure_root_env,
+ const rcutils_allocator_t * allocator)
+{
+ // Perform an exact match for the node's name in directory /.
+ char * node_secure_root = NULL;
+ // "/" case when root namespace is explicitly passed in
+ if (1 == strlen(node_namespace)) {
+ node_secure_root = rcutils_join_path(ros_secure_root_env, node_name, *allocator);
+ } else {
+ char * node_fqn = NULL;
+ char * node_root_path = NULL;
+ // Combine node namespace with node name
+ // TODO(ros2team): remove the hard-coded value of the root namespace
+ node_fqn = rcutils_format_string(*allocator, "%s%s%s", node_namespace, "/", node_name);
+ // Get native path, ignore the leading forward slash
+ // TODO(ros2team): remove the hard-coded length, use the length of the root namespace instead
+ node_root_path = rcutils_to_native_path(node_fqn + 1, *allocator);
+ node_secure_root = rcutils_join_path(ros_secure_root_env, node_root_path, *allocator);
+ allocator->deallocate(node_fqn, allocator->state);
+ allocator->deallocate(node_root_path, allocator->state);
+ }
+ return node_secure_root;
+}
+
+char * prefix_match_lookup(
+ const char * node_name,
+ const char * node_namespace,
+ const char * ros_secure_root_env,
+ const rcutils_allocator_t * allocator)
+{
+#ifdef RCUTILS_NO_FILESYSTEM
+ RCUTILS_SET_ERROR_MSG("not available filesystem");
+ return false;
+#else
+ // Perform longest prefix match for the node's name in directory /.
+ char * node_secure_root = NULL;
+ char matched_dir[_TINYDIR_FILENAME_MAX] = {0};
+ char * base_lookup_dir = NULL;
+ if (strlen(node_namespace) == 1) {
+ base_lookup_dir = (char *) ros_secure_root_env;
+ } else {
+ // TODO(ros2team): remove the hard-coded length, use the length of the root namespace instead.
+ base_lookup_dir = rcutils_join_path(ros_secure_root_env, node_namespace + 1, *allocator);
+ }
+ if (get_best_matching_directory(base_lookup_dir, node_name, matched_dir)) {
+ node_secure_root = rcutils_join_path(base_lookup_dir, matched_dir, *allocator);
+ }
+ if (base_lookup_dir != ros_secure_root_env && NULL != base_lookup_dir) {
+ allocator->deallocate(base_lookup_dir, allocator->state);
+ }
+ return node_secure_root;
+#endif // _RCUTILS_NO_FILESYSTEM
+}
+
+char * rcutils_get_secure_root(
+ const char * node_name,
+ const char * node_namespace,
+ const rcutils_allocator_t * allocator)
+{
+ bool ros_secure_node_override = true;
+
+ // find out if either of the configuration environment variables are set
+ const char * env_buf = NULL;
+ if (NULL == node_name) {
+ return NULL;
+ }
+ if (rcutils_get_env(ROS_SECURITY_NODE_DIRECTORY_VAR_NAME, &env_buf)) {
+ return NULL;
+ }
+ if (!env_buf) {
+ return NULL;
+ }
+ size_t ros_secure_root_size = strlen(env_buf);
+ if (!ros_secure_root_size) {
+ // check root directory if node directory environment variable is empty
+ if (rcutils_get_env(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME, &env_buf)) {
+ return NULL;
+ }
+ if (!env_buf) {
+ return NULL;
+ }
+ ros_secure_root_size = strlen(env_buf);
+ if (!ros_secure_root_size) {
+ return NULL; // environment variable was empty
+ } else {
+ ros_secure_node_override = false;
+ }
+ }
+
+ // found a usable environment variable, copy into our memory before overwriting with next lookup
+ char * ros_secure_root_env =
+ (char *)allocator->allocate(ros_secure_root_size + 1, allocator->state);
+ memcpy(ros_secure_root_env, env_buf, ros_secure_root_size + 1);
+ // TODO(ros2team): This make an assumption on the value and length of the root namespace.
+ // This should likely come from another (rcl/rmw?) function for reuse.
+ // If the namespace is the root namespace ("/"), the secure root is just the node name.
+
+ char * lookup_strategy = NULL;
+ char * node_secure_root = NULL;
+ if (ros_secure_node_override) {
+ node_secure_root = (char *)allocator->allocate(ros_secure_root_size + 1, allocator->state);
+ memcpy(node_secure_root, ros_secure_root_env, ros_secure_root_size + 1);
+ lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_NODE_OVERRIDE];
+
+ } else {
+ // Check which lookup method to use and invoke the relevant function.
+ const char * ros_security_lookup_type = NULL;
+ if (rcutils_get_env(ROS_SECURITY_LOOKUP_TYPE_VAR_NAME, &ros_security_lookup_type)) {
+ allocator->deallocate(ros_secure_root_env, allocator->state);
+ return NULL;
+ }
+ if (0 == strcmp(ros_security_lookup_type,
+ g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_PREFIX]))
+ {
+ node_secure_root = g_security_lookup_fns[ROS_SECURITY_LOOKUP_MATCH_PREFIX]
+ (node_name, node_namespace, ros_secure_root_env, allocator);
+ lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_PREFIX];
+ } else { /* Default is MATCH_EXACT */
+ node_secure_root = g_security_lookup_fns[ROS_SECURITY_LOOKUP_MATCH_EXACT]
+ (node_name, node_namespace, ros_secure_root_env, allocator);
+ lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_EXACT];
+ }
+ }
+
+ if (NULL == node_secure_root || !rcutils_is_directory(node_secure_root)) {
+ // Check node_secure_root is not NULL before checking directory
+ if (NULL == node_secure_root) {
+ RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
+ "SECURITY ERROR: unable to find a folder matching the node name in %s%s."
+ "Lookup strategy: %s",
+ ros_secure_root_env, node_namespace, lookup_strategy);
+ } else {
+ RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
+ "SECURITY ERROR: directory %s does not exist. Lookup strategy: %s",
+ node_secure_root, lookup_strategy);
+ }
+ allocator->deallocate(ros_secure_root_env, allocator->state);
+ allocator->deallocate(node_secure_root, allocator->state);
+ return NULL;
+ }
+ allocator->deallocate(ros_secure_root_env, allocator->state);
+ return node_secure_root;
+}
diff --git a/src/shared_library.c b/src/shared_library.c
index a0ce9a7e..bd3b906a 100644
--- a/src/shared_library.c
+++ b/src/shared_library.c
@@ -19,6 +19,10 @@ extern "C"
#include
#include
+#include "rcutils/configuration_flags.h"
+
+#ifndef RCUTILS_NO_FILESYSTEM
+
#ifndef _WIN32
#include
#else
@@ -26,6 +30,8 @@ extern "C"
C_ASSERT(sizeof(void *) == sizeof(HINSTANCE));
#endif // _WIN32
+#endif //RCUTILS_NO_FILESYSTEM
+
#include "rcutils/error_handling.h"
#include "rcutils/shared_library.h"
#include "rcutils/strdup.h"
@@ -45,7 +51,10 @@ rcutils_load_shared_library(
rcutils_shared_library_t * lib,
const char * library_path,
rcutils_allocator_t allocator)
-{
+{
+
+#ifndef RCUTILS_NO_FILESYSTEM
+
RCUTILS_CHECK_ARGUMENT_FOR_NULL(lib, RCUTILS_RET_INVALID_ARGUMENT);
RCUTILS_CHECK_ARGUMENT_FOR_NULL(library_path, RCUTILS_RET_INVALID_ARGUMENT);
RCUTILS_CHECK_ALLOCATOR(&allocator, return RCUTILS_RET_INVALID_ARGUMENT);
@@ -76,6 +85,11 @@ rcutils_load_shared_library(
return RCUTILS_RET_ERROR;
}
return RCUTILS_RET_OK;
+
+#else
+ return RCUTILS_RET_ERROR;
+#endif //RCUTILS_NO_FILESYSTEM
+
}
void *
diff --git a/src/time_unix.c b/src/time_unix.c
index 028fb453..ea52711d 100644
--- a/src/time_unix.c
+++ b/src/time_unix.c
@@ -39,7 +39,7 @@ extern "C"
// This id an appropriate check for clock_gettime() according to:
// http://man7.org/linux/man-pages/man2/clock_gettime.2.html
# if !defined(_POSIX_TIMERS) || !_POSIX_TIMERS
-# error no monotonic clock function available
+# warning no monotonic clock function available
# endif // !defined(_POSIX_TIMERS) || !_POSIX_TIMERS
#endif // !defined(__MACH__)
diff --git a/test/resources/test_security_directory/dummy_node/.gitkeep b/test/resources/test_security_directory/dummy_node/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/test/test_security_directory.cpp b/test/test_security_directory.cpp
new file mode 100644
index 00000000..bf1b7f2b
--- /dev/null
+++ b/test/test_security_directory.cpp
@@ -0,0 +1,216 @@
+// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+//
+// Licensed 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
+
+#include
+#include
+#include "rcutils/security_directory.h"
+#include "rcutils/filesystem.h"
+#include "osrf_testing_tools_cpp/scope_exit.hpp"
+#include "rcutils/error_handling.h"
+
+#define ROOT_NAMESPACE "/"
+#define TEST_SECURITY_DIRECTORY_RESOURCES_DIR_NAME "test_security_directory"
+#define TEST_NODE_NAME "dummy_node"
+#define TEST_NODE_NAMESPACE ROOT_NAMESPACE TEST_SECURITY_DIRECTORY_RESOURCES_DIR_NAME
+
+char g_envstring[512] = {0};
+
+static int putenv_wrapper(const char * env_var)
+{
+#ifdef _WIN32
+ return _putenv(env_var);
+#else
+ return putenv(reinterpret_cast(const_cast(env_var)));
+#endif
+}
+
+static int unsetenv_wrapper(const char * var_name)
+{
+#ifdef _WIN32
+ // On windows, putenv("VAR=") deletes VAR from environment
+ std::string var(var_name);
+ var += "=";
+ return _putenv(var.c_str());
+#else
+ return unsetenv(var_name);
+#endif
+}
+
+class TestGetSecureRoot : public ::testing::Test
+{
+protected:
+ void SetUp() final
+ {
+ // Reset rcutils error global state in case a previously
+ // running test has failed.
+ rcutils_reset_error();
+
+ // Always make sure the variable we set is unset at the beginning of a test
+ unsetenv_wrapper(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME);
+ unsetenv_wrapper(ROS_SECURITY_NODE_DIRECTORY_VAR_NAME);
+ unsetenv_wrapper(ROS_SECURITY_LOOKUP_TYPE_VAR_NAME);
+ allocator = rcutils_get_default_allocator();
+ root_path = nullptr;
+ secure_root = nullptr;
+ base_lookup_dir_fqn = nullptr;
+ }
+
+ void TearDown() final
+ {
+ OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({
+ allocator.deallocate(root_path, allocator.state);
+ });
+ OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({
+ allocator.deallocate(secure_root, allocator.state);
+ });
+ OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({
+ allocator.deallocate(base_lookup_dir_fqn, allocator.state);
+ });
+ }
+
+ void set_base_lookup_dir_fqn(const char * resource_dir, const char * resource_dir_name)
+ {
+ base_lookup_dir_fqn = rcutils_join_path(resource_dir,
+ resource_dir_name, allocator);
+ std::string putenv_input = ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "=";
+ putenv_input += base_lookup_dir_fqn;
+ memcpy(g_envstring, putenv_input.c_str(),
+ std::min(putenv_input.length(), sizeof(g_envstring) - 1));
+ putenv_wrapper(g_envstring);
+ }
+
+ rcutils_allocator_t allocator;
+ char * root_path;
+ char * secure_root;
+ char * base_lookup_dir_fqn;
+};
+
+TEST_F(TestGetSecureRoot, failureScenarios) {
+ ASSERT_EQ(rcutils_get_secure_root(TEST_NODE_NAME, TEST_NODE_NAMESPACE, &allocator),
+ (char *) NULL);
+
+ putenv_wrapper(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+
+ /* Security directory is set, but there's no matching directory */
+ /// Wrong namespace
+ ASSERT_EQ(rcutils_get_secure_root(TEST_NODE_NAME, "/some_other_namespace", &allocator),
+ (char *) NULL);
+ /// Wrong node name
+ ASSERT_EQ(rcutils_get_secure_root("not_" TEST_NODE_NAME, TEST_NODE_NAMESPACE, &allocator),
+ (char *) NULL);
+}
+
+TEST_F(TestGetSecureRoot, successScenarios_local_exactMatch) {
+ putenv_wrapper(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+
+ /* --------------------------
+ * Namespace : Custom (local)
+ * Match type : Exact
+ * --------------------------
+ * Root: ${CMAKE_BINARY_DIR}/tests/resources
+ * Namespace: /test_security_directory
+ * Node: dummy_node
+ */
+ secure_root = rcutils_get_secure_root(TEST_NODE_NAME, TEST_NODE_NAMESPACE, &allocator);
+ std::string secure_root_str(secure_root);
+ ASSERT_STREQ(TEST_NODE_NAME,
+ secure_root_str.substr(secure_root_str.size() - (sizeof(TEST_NODE_NAME) - 1)).c_str());
+}
+
+TEST_F(TestGetSecureRoot, successScenarios_local_prefixMatch) {
+ putenv_wrapper(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+ secure_root = rcutils_get_secure_root(TEST_NODE_NAME, TEST_NODE_NAMESPACE, &allocator);
+
+ /* --------------------------
+ * Namespace : Custom (local)
+ * Match type : Prefix
+ * --------------------------
+ * Root: ${CMAKE_BINARY_DIR}/tests/resources
+ * Namespace: /test_security_directory
+ * Node: dummy_node_and_some_suffix_added */
+ root_path = rcutils_get_secure_root(TEST_NODE_NAME "_and_some_suffix_added",
+ TEST_NODE_NAMESPACE, &allocator);
+ ASSERT_STRNE(root_path, secure_root);
+ putenv_wrapper(ROS_SECURITY_LOOKUP_TYPE_VAR_NAME "=MATCH_PREFIX");
+ root_path = rcutils_get_secure_root(TEST_NODE_NAME "_and_some_suffix_added",
+ TEST_NODE_NAMESPACE, &allocator);
+ ASSERT_STREQ(root_path, secure_root);
+}
+
+TEST_F(TestGetSecureRoot, successScenarios_root_exactMatch) {
+ putenv_wrapper(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+ putenv_wrapper(ROS_SECURITY_LOOKUP_TYPE_VAR_NAME "=MATCH_PREFIX");
+ secure_root = rcutils_get_secure_root(TEST_NODE_NAME, TEST_NODE_NAMESPACE, &allocator);
+
+ /* Include the namespace as part of the root security directory and test root namespace */
+ set_base_lookup_dir_fqn(TEST_RESOURCES_DIRECTORY, TEST_SECURITY_DIRECTORY_RESOURCES_DIR_NAME);
+ /* --------------------------
+ * Namespace : Root
+ * Match type : Exact
+ * --------------------------
+ * Root: ${CMAKE_BINARY_DIR}/tests/resources/test_security_directory
+ * Namespace: /
+ * Node: dummy_node */
+ root_path = rcutils_get_secure_root(TEST_NODE_NAME, ROOT_NAMESPACE, &allocator);
+ ASSERT_STREQ(root_path, secure_root);
+}
+
+TEST_F(TestGetSecureRoot, successScenarios_root_prefixMatch) {
+ putenv_wrapper(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+ putenv_wrapper(ROS_SECURITY_LOOKUP_TYPE_VAR_NAME "=MATCH_PREFIX");
+ secure_root = rcutils_get_secure_root(TEST_NODE_NAME, TEST_NODE_NAMESPACE, &allocator);
+
+ /* Include the namespace as part of the root security directory and test root namespace */
+ set_base_lookup_dir_fqn(TEST_RESOURCES_DIRECTORY, TEST_SECURITY_DIRECTORY_RESOURCES_DIR_NAME);
+ /* --------------------------
+ * Namespace : Root
+ * Match type : Prefix
+ * --------------------------
+ * Root dir: ${CMAKE_BINARY_DIR}/tests/resources/test_security_directory
+ * Namespace: /
+ * Node: dummy_node_and_some_suffix_added */
+ root_path = rcutils_get_secure_root(TEST_NODE_NAME "_and_some_suffix_added",
+ ROOT_NAMESPACE, &allocator);
+ ASSERT_STREQ(root_path, secure_root);
+}
+
+TEST_F(TestGetSecureRoot, nodeSecurityDirectoryOverride_validDirectory) {
+ /* Specify a valid directory */
+ putenv_wrapper(ROS_SECURITY_NODE_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+ root_path = rcutils_get_secure_root("name shouldn't matter",
+ "namespace shouldn't matter", &allocator);
+ ASSERT_STREQ(root_path, TEST_RESOURCES_DIRECTORY);
+}
+
+TEST_F(TestGetSecureRoot,
+ nodeSecurityDirectoryOverride_validDirectory_overrideRootDirectoryAttempt) {
+ /* Setting root dir has no effect */
+ putenv_wrapper(ROS_SECURITY_NODE_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+ root_path = rcutils_get_secure_root("name shouldn't matter",
+ "namespace shouldn't matter", &allocator);
+ putenv_wrapper(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME "=" TEST_RESOURCES_DIRECTORY);
+ ASSERT_STREQ(root_path, TEST_RESOURCES_DIRECTORY);
+}
+
+TEST_F(TestGetSecureRoot, nodeSecurityDirectoryOverride_invalidDirectory) {
+ /* The override provided should exist. Providing correct node/namespace/root dir won't help
+ * if the node override is invalid. */
+ putenv_wrapper(
+ ROS_SECURITY_NODE_DIRECTORY_VAR_NAME
+ "=TheresN_oWayThi_sDirectory_Exists_hence_this_would_fail");
+ ASSERT_EQ(rcutils_get_secure_root(TEST_NODE_NAME, TEST_NODE_NAMESPACE, &allocator),
+ (char *) NULL);
+}