Skip to content

Commit b4a18f1

Browse files
jamoralppablogs9
authored andcommitted
Release micro-ROS Foxy (#8)
* micro-ROS changes over dashing Feature/add security directory (#1) * Added security directory * Updated security directory Feature/avoid filesystem and allocation (#2) * Included RCUTILS_NO_FILESYSTEM and RCUTILS_AVOID_DYNAMIC_ALLOCATION * Added no filesystem options * Default allocators write access * Avoid dynamic allocation and no filesytem on error handling * Typo * New flags for filesystem and avoid dynamic * Error handling template * New allocator approach Add test_security_directory test from rcl. (#3) Merge pull request #4 from micro-ROS/feature/zephyr_fixes Feature/zephyr fixes CMake refactor (#5) Update approach (#6) * Update approach * Remove target_compile_definitions and refactor flags install * Added RCUTILS_NO_FILESYSTEM on new functions * Added RCUTILS_NO_FILESYSTEM on new functions Co-authored-by: Pablo Garrido <[email protected]>
1 parent 96c0312 commit b4a18f1

File tree

4 files changed

+498
-0
lines changed

4 files changed

+498
-0
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ set(rcutils_sources
7373
src/time.c
7474
${time_impl_c}
7575
src/uint8_array.c
76+
src/security_directory.c
7677
)
7778

7879
set_source_files_properties(
@@ -152,6 +153,9 @@ if(BUILD_TESTING)
152153
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
153154
endif()
154155

156+
set(test_resources_dir_name "test/resources")
157+
add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/${test_resources_dir_name}")
158+
155159
find_package(ament_cmake_gmock REQUIRED)
156160
find_package(ament_cmake_gtest REQUIRED)
157161
find_package(ament_cmake_pytest REQUIRED)

src/security_directory.c

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
// Copyright 2018 Open Source Robotics Foundation, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "rcutils/security_directory.h"
16+
17+
#include "rcutils/error_handling.h"
18+
#include "rcutils/filesystem.h"
19+
#include "rcutils/get_env.h"
20+
#include "rcutils/format_string.h"
21+
22+
#ifdef __clang__
23+
# pragma clang diagnostic push
24+
# pragma clang diagnostic ignored "-Wembedded-directive"
25+
#endif
26+
27+
#ifndef RCUTILS_NO_FILESYSTEM
28+
#include "tinydir/tinydir.h"
29+
#endif
30+
31+
#ifdef __clang__
32+
# pragma clang diagnostic pop
33+
#endif
34+
35+
/**
36+
* A security lookup function takes in the node's name, namespace, a security root directory and an allocator;
37+
* It returns the relevant information required to load the security credentials,
38+
* which is currently a path to a directory on the filesystem containing DDS Security permission files.
39+
*/
40+
typedef char * (* security_lookup_fn_t) (
41+
const char * node_name,
42+
const char * node_namespace,
43+
const char * ros_secure_root_env,
44+
const rcutils_allocator_t * allocator
45+
);
46+
47+
char * exact_match_lookup(
48+
const char * node_name,
49+
const char * node_namespace,
50+
const char * ros_secure_root_env,
51+
const rcutils_allocator_t * allocator
52+
);
53+
54+
char * prefix_match_lookup(
55+
const char * node_name,
56+
const char * node_namespace,
57+
const char * ros_secure_root_env,
58+
const rcutils_allocator_t * allocator
59+
);
60+
61+
security_lookup_fn_t g_security_lookup_fns[] = {
62+
NULL,
63+
exact_match_lookup,
64+
prefix_match_lookup,
65+
};
66+
67+
typedef enum ros_security_lookup_type_e
68+
{
69+
ROS_SECURITY_LOOKUP_NODE_OVERRIDE = 0,
70+
ROS_SECURITY_LOOKUP_MATCH_EXACT = 1,
71+
ROS_SECURITY_LOOKUP_MATCH_PREFIX = 2,
72+
} ros_security_lookup_type_t;
73+
74+
char * g_security_lookup_type_strings[] = {
75+
"NODE_OVERRIDE",
76+
"MATCH_EXACT",
77+
"MATCH_PREFIX"
78+
};
79+
80+
/// Return the directory whose name most closely matches node_name (longest-prefix match),
81+
/// scanning under base_dir.
82+
/**
83+
* By using a prefix match, a node named e.g. "my_node_123" will be able to load and use the
84+
* directory "my_node" if no better match exists.
85+
* \param[in] base_dir
86+
* \param[in] node_name
87+
* \param[out] matched_name must be a valid memory address allocated with at least
88+
* _TINYDIR_FILENAME_MAX characters.
89+
* \return true if a match was found
90+
*/
91+
static bool get_best_matching_directory(
92+
const char * base_dir,
93+
const char * node_name,
94+
char * matched_name)
95+
{
96+
#ifdef RCUTILS_NO_FILESYSTEM
97+
RCUTILS_SET_ERROR_MSG("not available filesystem");
98+
return false;
99+
#else
100+
size_t max_match_length = 0;
101+
tinydir_dir dir;
102+
if (NULL == base_dir || NULL == node_name || NULL == matched_name) {
103+
return false;
104+
}
105+
if (-1 == tinydir_open(&dir, base_dir)) {
106+
return false;
107+
}
108+
while (dir.has_next) {
109+
tinydir_file file;
110+
if (-1 == tinydir_readfile(&dir, &file)) {
111+
goto cleanup;
112+
}
113+
if (file.is_dir) {
114+
size_t matched_name_length = strnlen(file.name, sizeof(file.name) - 1);
115+
if (0 ==
116+
strncmp(file.name, node_name,
117+
matched_name_length) && matched_name_length > max_match_length)
118+
{
119+
max_match_length = matched_name_length;
120+
memcpy(matched_name, file.name, max_match_length);
121+
}
122+
}
123+
if (-1 == tinydir_next(&dir)) {
124+
goto cleanup;
125+
}
126+
}
127+
cleanup:
128+
tinydir_close(&dir);
129+
return max_match_length > 0;
130+
#endif // _RCUTILS_NO_FILESYSTEM
131+
}
132+
133+
char * exact_match_lookup(
134+
const char * node_name,
135+
const char * node_namespace,
136+
const char * ros_secure_root_env,
137+
const rcutils_allocator_t * allocator)
138+
{
139+
// Perform an exact match for the node's name in directory <root dir>/<namespace>.
140+
char * node_secure_root = NULL;
141+
// "/" case when root namespace is explicitly passed in
142+
if (1 == strlen(node_namespace)) {
143+
node_secure_root = rcutils_join_path(ros_secure_root_env, node_name, *allocator);
144+
} else {
145+
char * node_fqn = NULL;
146+
char * node_root_path = NULL;
147+
// Combine node namespace with node name
148+
// TODO(ros2team): remove the hard-coded value of the root namespace
149+
node_fqn = rcutils_format_string(*allocator, "%s%s%s", node_namespace, "/", node_name);
150+
// Get native path, ignore the leading forward slash
151+
// TODO(ros2team): remove the hard-coded length, use the length of the root namespace instead
152+
node_root_path = rcutils_to_native_path(node_fqn + 1, *allocator);
153+
node_secure_root = rcutils_join_path(ros_secure_root_env, node_root_path, *allocator);
154+
allocator->deallocate(node_fqn, allocator->state);
155+
allocator->deallocate(node_root_path, allocator->state);
156+
}
157+
return node_secure_root;
158+
}
159+
160+
char * prefix_match_lookup(
161+
const char * node_name,
162+
const char * node_namespace,
163+
const char * ros_secure_root_env,
164+
const rcutils_allocator_t * allocator)
165+
{
166+
#ifdef RCUTILS_NO_FILESYSTEM
167+
RCUTILS_SET_ERROR_MSG("not available filesystem");
168+
return false;
169+
#else
170+
// Perform longest prefix match for the node's name in directory <root dir>/<namespace>.
171+
char * node_secure_root = NULL;
172+
char matched_dir[_TINYDIR_FILENAME_MAX] = {0};
173+
char * base_lookup_dir = NULL;
174+
if (strlen(node_namespace) == 1) {
175+
base_lookup_dir = (char *) ros_secure_root_env;
176+
} else {
177+
// TODO(ros2team): remove the hard-coded length, use the length of the root namespace instead.
178+
base_lookup_dir = rcutils_join_path(ros_secure_root_env, node_namespace + 1, *allocator);
179+
}
180+
if (get_best_matching_directory(base_lookup_dir, node_name, matched_dir)) {
181+
node_secure_root = rcutils_join_path(base_lookup_dir, matched_dir, *allocator);
182+
}
183+
if (base_lookup_dir != ros_secure_root_env && NULL != base_lookup_dir) {
184+
allocator->deallocate(base_lookup_dir, allocator->state);
185+
}
186+
return node_secure_root;
187+
#endif // _RCUTILS_NO_FILESYSTEM
188+
}
189+
190+
char * rcutils_get_secure_root(
191+
const char * node_name,
192+
const char * node_namespace,
193+
const rcutils_allocator_t * allocator)
194+
{
195+
bool ros_secure_node_override = true;
196+
197+
// find out if either of the configuration environment variables are set
198+
const char * env_buf = NULL;
199+
if (NULL == node_name) {
200+
return NULL;
201+
}
202+
if (rcutils_get_env(ROS_SECURITY_NODE_DIRECTORY_VAR_NAME, &env_buf)) {
203+
return NULL;
204+
}
205+
if (!env_buf) {
206+
return NULL;
207+
}
208+
size_t ros_secure_root_size = strlen(env_buf);
209+
if (!ros_secure_root_size) {
210+
// check root directory if node directory environment variable is empty
211+
if (rcutils_get_env(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME, &env_buf)) {
212+
return NULL;
213+
}
214+
if (!env_buf) {
215+
return NULL;
216+
}
217+
ros_secure_root_size = strlen(env_buf);
218+
if (!ros_secure_root_size) {
219+
return NULL; // environment variable was empty
220+
} else {
221+
ros_secure_node_override = false;
222+
}
223+
}
224+
225+
// found a usable environment variable, copy into our memory before overwriting with next lookup
226+
char * ros_secure_root_env =
227+
(char *)allocator->allocate(ros_secure_root_size + 1, allocator->state);
228+
memcpy(ros_secure_root_env, env_buf, ros_secure_root_size + 1);
229+
// TODO(ros2team): This make an assumption on the value and length of the root namespace.
230+
// This should likely come from another (rcl/rmw?) function for reuse.
231+
// If the namespace is the root namespace ("/"), the secure root is just the node name.
232+
233+
char * lookup_strategy = NULL;
234+
char * node_secure_root = NULL;
235+
if (ros_secure_node_override) {
236+
node_secure_root = (char *)allocator->allocate(ros_secure_root_size + 1, allocator->state);
237+
memcpy(node_secure_root, ros_secure_root_env, ros_secure_root_size + 1);
238+
lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_NODE_OVERRIDE];
239+
240+
} else {
241+
// Check which lookup method to use and invoke the relevant function.
242+
const char * ros_security_lookup_type = NULL;
243+
if (rcutils_get_env(ROS_SECURITY_LOOKUP_TYPE_VAR_NAME, &ros_security_lookup_type)) {
244+
allocator->deallocate(ros_secure_root_env, allocator->state);
245+
return NULL;
246+
}
247+
if (0 == strcmp(ros_security_lookup_type,
248+
g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_PREFIX]))
249+
{
250+
node_secure_root = g_security_lookup_fns[ROS_SECURITY_LOOKUP_MATCH_PREFIX]
251+
(node_name, node_namespace, ros_secure_root_env, allocator);
252+
lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_PREFIX];
253+
} else { /* Default is MATCH_EXACT */
254+
node_secure_root = g_security_lookup_fns[ROS_SECURITY_LOOKUP_MATCH_EXACT]
255+
(node_name, node_namespace, ros_secure_root_env, allocator);
256+
lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_EXACT];
257+
}
258+
}
259+
260+
if (NULL == node_secure_root || !rcutils_is_directory(node_secure_root)) {
261+
// Check node_secure_root is not NULL before checking directory
262+
if (NULL == node_secure_root) {
263+
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
264+
"SECURITY ERROR: unable to find a folder matching the node name in %s%s."
265+
"Lookup strategy: %s",
266+
ros_secure_root_env, node_namespace, lookup_strategy);
267+
} else {
268+
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
269+
"SECURITY ERROR: directory %s does not exist. Lookup strategy: %s",
270+
node_secure_root, lookup_strategy);
271+
}
272+
allocator->deallocate(ros_secure_root_env, allocator->state);
273+
allocator->deallocate(node_secure_root, allocator->state);
274+
return NULL;
275+
}
276+
allocator->deallocate(ros_secure_root_env, allocator->state);
277+
return node_secure_root;
278+
}

test/resources/test_security_directory/dummy_node/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)