ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
ROS 2 C++ Client Library with ROS Client Library
security.c
1 // Copyright 2018-2020 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 "rcl/security.h"
16 
17 #include <stdbool.h>
18 #include <stddef.h>
19 #include <string.h>
20 
21 #include "rcl/allocator.h"
22 #include "rcl/error_handling.h"
23 #include "rcl/types.h"
24 
25 #include "rcutils/allocator.h"
26 #include "rcutils/env.h"
27 #include "rcutils/filesystem.h"
28 #include "rcutils/logging_macros.h"
29 #include "rcutils/strdup.h"
30 
31 #include "rmw/security_options.h"
32 
35  const char * name,
36  const rcutils_allocator_t * allocator,
37  rmw_security_options_t * security_options)
38 {
39  bool use_security = false;
40  rcl_ret_t ret = rcl_security_enabled(&use_security);
41  if (RCL_RET_OK != ret) {
42  return ret;
43  }
44 
45  RCUTILS_LOG_DEBUG_NAMED(
46  ROS_PACKAGE_NAME, "Using security: %s", use_security ? "true" : "false");
47 
48  if (!use_security) {
49  security_options->enforce_security = RMW_SECURITY_ENFORCEMENT_PERMISSIVE;
50  return RCL_RET_OK;
51  }
52 
53  ret = rcl_get_enforcement_policy(&security_options->enforce_security);
54  if (RCL_RET_OK != ret) {
55  return ret;
56  }
57 
58  // File discovery magic here
59  char * secure_root = rcl_get_secure_root(name, allocator);
60  if (secure_root) {
61  RCUTILS_LOG_INFO_NAMED(ROS_PACKAGE_NAME, "Found security directory: %s", secure_root);
62  security_options->security_root_path = secure_root;
63  } else {
64  if (RMW_SECURITY_ENFORCEMENT_ENFORCE == security_options->enforce_security) {
65  return RCL_RET_ERROR;
66  }
67  }
68  return RCL_RET_OK;
69 }
70 
72 rcl_security_enabled(bool * use_security)
73 {
74  const char * ros_security_enable = NULL;
75  const char * get_env_error_str = NULL;
76 
77  RCL_CHECK_ARGUMENT_FOR_NULL(use_security, RCL_RET_INVALID_ARGUMENT);
78 
79  get_env_error_str = rcutils_get_env(ROS_SECURITY_ENABLE_VAR_NAME, &ros_security_enable);
80  if (NULL != get_env_error_str) {
81  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
82  "Error getting env var '" RCUTILS_STRINGIFY(ROS_SECURITY_ENABLE_VAR_NAME) "': %s\n",
83  get_env_error_str);
84  return RCL_RET_ERROR;
85  }
86 
87  *use_security = (0 == strcmp(ros_security_enable, "true"));
88  return RCL_RET_OK;
89 }
90 
92 rcl_get_enforcement_policy(rmw_security_enforcement_policy_t * policy)
93 {
94  const char * ros_enforce_security = NULL;
95  const char * get_env_error_str = NULL;
96 
97  RCL_CHECK_ARGUMENT_FOR_NULL(policy, RCL_RET_INVALID_ARGUMENT);
98 
99  get_env_error_str = rcutils_get_env(ROS_SECURITY_STRATEGY_VAR_NAME, &ros_enforce_security);
100  if (NULL != get_env_error_str) {
101  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
102  "Error getting env var '" RCUTILS_STRINGIFY(ROS_SECURITY_STRATEGY_VAR_NAME) "': %s\n",
103  get_env_error_str);
104  return RCL_RET_ERROR;
105  }
106 
107  *policy = (0 == strcmp(ros_enforce_security, "Enforce")) ?
108  RMW_SECURITY_ENFORCEMENT_ENFORCE : RMW_SECURITY_ENFORCEMENT_PERMISSIVE;
109  return RCL_RET_OK;
110 }
111 
112 char * exact_match_lookup(
113  const char * name,
114  const char * ros_secure_keystore_env,
115  const rcl_allocator_t * allocator)
116 {
117  // Perform an exact match for the enclave name in directory <root dir>.
118  char * secure_root = NULL;
119  char * enclaves_dir = NULL;
120  enclaves_dir = rcutils_join_path(ros_secure_keystore_env, "enclaves", *allocator);
121  // "/" case when root namespace is explicitly passed in
122  if (0 == strcmp(name, "/")) {
123  secure_root = enclaves_dir;
124  } else {
125  char * relative_path = NULL;
126  // Get native path, ignore the leading forward slash
127  // TODO(ros2team): remove the hard-coded length, use the length of the root namespace instead
128  relative_path = rcutils_to_native_path(name + 1, *allocator);
129  secure_root = rcutils_join_path(enclaves_dir, relative_path, *allocator);
130  allocator->deallocate(relative_path, allocator->state);
131  allocator->deallocate(enclaves_dir, allocator->state);
132  }
133  return secure_root;
134 }
135 
136 static const char *
137 dupenv(const char * name, const rcl_allocator_t * allocator, char ** value)
138 {
139  const char * buffer = NULL;
140  const char * error = rcutils_get_env(name, &buffer);
141  if (NULL != error) {
142  return error;
143  }
144  *value = NULL;
145  if (0 != strcmp("", buffer)) {
146  *value = rcutils_strdup(buffer, *allocator);
147  if (NULL == *value) {
148  return "string duplication failed";
149  }
150  }
151  return NULL;
152 }
153 
155  const char * name,
156  const rcl_allocator_t * allocator)
157 {
158  RCL_CHECK_ARGUMENT_FOR_NULL(name, NULL);
159  RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "allocator is invalid", return NULL);
160 
161  char * secure_root = NULL;
162  char * ros_secure_keystore_env = NULL;
163  char * ros_secure_enclave_override_env = NULL;
164 
165  // check keystore environment variable
166  const char * error =
167  dupenv(ROS_SECURITY_KEYSTORE_VAR_NAME, allocator, &ros_secure_keystore_env);
168  if (NULL != error) {
169  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
170  "failed to get %s: %s", ROS_SECURITY_KEYSTORE_VAR_NAME, error);
171  return NULL;
172  }
173 
174  if (NULL == ros_secure_keystore_env) {
175  return NULL; // environment variable was empty
176  }
177 
178  // check enclave override environment variable
179  error = dupenv(ROS_SECURITY_ENCLAVE_OVERRIDE, allocator, &ros_secure_enclave_override_env);
180  if (NULL != error) {
181  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
182  "failed to get %s: %s", ROS_SECURITY_ENCLAVE_OVERRIDE, error);
183  goto leave_rcl_get_secure_root;
184  }
185 
186  // given usable environment variables, overwrite with next lookup
187  if (NULL != ros_secure_enclave_override_env) {
188  secure_root = exact_match_lookup(
189  ros_secure_enclave_override_env,
190  ros_secure_keystore_env,
191  allocator);
192  } else {
193  secure_root = exact_match_lookup(
194  name,
195  ros_secure_keystore_env,
196  allocator);
197  }
198 
199  if (NULL == secure_root) {
200  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
201  "SECURITY ERROR: unable to find a folder matching the name '%s' in '%s'. ",
202  name, ros_secure_keystore_env);
203  goto leave_rcl_get_secure_root;
204  }
205 
206  if (!rcutils_is_directory(secure_root)) {
207  RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
208  "SECURITY ERROR: directory '%s' does not exist.", secure_root);
209  allocator->deallocate(secure_root, allocator->state);
210  secure_root = NULL;
211  }
212 
213 leave_rcl_get_secure_root:
214  allocator->deallocate(ros_secure_enclave_override_env, allocator->state);
215  allocator->deallocate(ros_secure_keystore_env, allocator->state);
216  return secure_root;
217 }
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
Definition: allocator.h:56
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
#define ROS_SECURITY_ENABLE_VAR_NAME
The name of the environment variable controlling whether security is enabled.
Definition: security.h:51
RCL_PUBLIC rcl_ret_t rcl_get_enforcement_policy(rmw_security_enforcement_policy_t *policy)
Get security enforcement policy from the environment.
Definition: security.c:92
RCL_PUBLIC char * rcl_get_secure_root(const char *name, const rcl_allocator_t *allocator)
Return the secure root given a enclave name.
Definition: security.c:154
#define ROS_SECURITY_KEYSTORE_VAR_NAME
The name of the environment variable containing the path to the keystore.
Definition: security.h:41
#define ROS_SECURITY_STRATEGY_VAR_NAME
The name of the environment variable containing the security strategy.
Definition: security.h:46
#define ROS_SECURITY_ENCLAVE_OVERRIDE
The name of the environment variable containing the security enclave override.
Definition: security.h:36
RCL_PUBLIC rcl_ret_t rcl_security_enabled(bool *use_security)
Check if security has to be used, according to the environment.
Definition: security.c:72
RCL_PUBLIC rcl_ret_t rcl_get_security_options_from_environment(const char *name, const rcutils_allocator_t *allocator, rmw_security_options_t *security_options)
Initialize security options from values in the environment variables and given names.
Definition: security.c:34
#define RCL_RET_OK
Success return code.
Definition: types.h:28
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:36
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:30
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:25