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