OpenDDS  Snapshot(2023/04/28-20:55)
Permissions.cpp
Go to the documentation of this file.
1 /*
2  * Distributed under the OpenDDS License.
3  * See: http://www.OpenDDS.org/license.html
4  */
5 
6 #include "Permissions.h"
7 
8 #include "XmlUtils.h"
9 
11 
13 
14 namespace OpenDDS {
15 namespace Security {
16 
18 
20 {
21  using XML::XStr;
22  using namespace XmlUtils;
23 
24  const std::string& xml = doc.content();
25  ParserPtr parser;
26  if (!get_parser(parser, doc.filename(), xml)) {
28  ACE_ERROR((LM_ERROR, "(%P|%t) ERROR: {access_error} Permissions::load: "
29  "get_parser failed\n"));
30  }
31  return -1;
32  }
33 
34  // Find the validity rules
35  const xercesc::DOMNodeList* const grantRules =
36  parser->getDocument()->getElementsByTagName(XStr(ACE_TEXT("grant")));
37 
38  for (XMLSize_t r = 0, r_len = grantRules->getLength(); r < r_len; ++r) {
39  Grant_rch grant = DCPS::make_rch<Grant>();
40  const xercesc::DOMNode* const grantRule = grantRules->item(r);
41 
42  // Pull out the grant name for this grant
43  xercesc::DOMNamedNodeMap* rattrs = grantRule->getAttributes();
44  grant->name = to_string(rattrs->item(0));
45 
46  // Pull out subject name, validity, and default
47  const xercesc::DOMNodeList* grantNodes = grantRule->getChildNodes();
48 
49  bool valid_subject = false, valid_default = false;
50  for (XMLSize_t gn = 0, gn_len = grantNodes->getLength(); gn < gn_len; ++gn) {
51 
52  const xercesc::DOMNode* grantNode = grantNodes->item(gn);
53 
54  const XStr g_tag = grantNode->getNodeName();
55 
56  if (g_tag == ACE_TEXT("subject_name")) {
57  valid_subject = grant->subject.parse(to_string(grantNode)) == 0;
58 
59  } else if (g_tag == ACE_TEXT("validity")) {
60  const xercesc::DOMNodeList* validityNodes = grantNode->getChildNodes();
61  for (XMLSize_t vn = 0, vn_len = validityNodes->getLength(); vn < vn_len; ++vn) {
62  const xercesc::DOMNode* validityNode = validityNodes->item(vn);
63  const XStr v_tag = validityNode->getNodeName();
64  if (v_tag == ACE_TEXT("not_before")) {
65  if (!parse_time(validityNode->getTextContent(), grant->validity.not_before)) {
67  ACE_ERROR((LM_ERROR, "(%P|%t) ERROR: {access_error} Permissions::load: "
68  "invalid datetime in not_before\n"));
69  }
70  return -1;
71  }
72  } else if (v_tag == ACE_TEXT("not_after")) {
73  if (!parse_time(validityNode->getTextContent(), grant->validity.not_after)) {
75  ACE_ERROR((LM_ERROR, "(%P|%t) ERROR: {access_error} Permissions::load: "
76  "invalid datetime in not_after\n"));
77  }
78  return -1;
79  }
80  }
81  }
82 
83  } else if (g_tag == ACE_TEXT("default")) {
84  const std::string def = to_string(grantNode);
85  valid_default = true;
86  if (def == "ALLOW") {
87  grant->default_permission = ALLOW;
88  } else if (def == "DENY") {
89  grant->default_permission = DENY;
90  } else {
92  ACE_ERROR((LM_ERROR, "(%P|%t) ERROR: {access_error} Permissions::load: "
93  "<default> must be ALLOW or DENY\n"));
94  }
95  return -1;
96  }
97  }
98  }
99 
100  if (!valid_default) {
102  ACE_ERROR((LM_ERROR, "(%P|%t) ERROR: {access_error} Permissions::load: "
103  "<default> is required\n"));
104  }
105  return -1;
106  }
107 
108  // Pull out allow/deny rules
109  const xercesc::DOMNodeList* adGrantNodes = grantRule->getChildNodes();
110 
111  for (XMLSize_t gn = 0, gn_len = adGrantNodes->getLength(); gn < gn_len; ++gn) {
112 
113  const xercesc::DOMNode* adGrantNode = adGrantNodes->item(gn);
114 
115  const XStr g_tag = adGrantNode->getNodeName();
116 
117  if (g_tag == ACE_TEXT("allow_rule") || g_tag == ACE_TEXT("deny_rule")) {
118  Rule rule;
119 
120  rule.ad_type = (g_tag == ACE_TEXT("allow_rule")) ? ALLOW : DENY;
121 
122  const xercesc::DOMNodeList* adNodeChildren = adGrantNode->getChildNodes();
123 
124  for (XMLSize_t anc = 0, anc_len = adNodeChildren->getLength(); anc < anc_len; ++anc) {
125  const xercesc::DOMNode* const adNodeChild = adNodeChildren->item(anc);
126  const XStr anc_tag = adNodeChild->getNodeName();
127  if (anc_tag == ACE_TEXT("domains")) {
128  if (!parse_domain_id_set(adNodeChild, rule.domains)) {
130  ACE_ERROR((LM_ERROR, "(%P|%t) ERROR: {access_error} Permissions::load: "
131  "failed to parse domain set\n"));
132  }
133  return -1;
134  }
135 
136  } else if (anc_tag == ACE_TEXT("publish") || anc_tag == ACE_TEXT("subscribe")) {
137  Action action;
138 
139  action.ps_type = (anc_tag == ACE_TEXT("publish")) ? PUBLISH : SUBSCRIBE;
140  const xercesc::DOMNodeList* topicListNodes = adNodeChild->getChildNodes();
141 
142  for (XMLSize_t tln = 0, tln_len = topicListNodes->getLength(); tln < tln_len; ++tln) {
143 
144  const xercesc::DOMNode* topicListNode = topicListNodes->item(tln);
145 
146  if (ACE_TEXT("topics") == XStr(topicListNode->getNodeName())) {
147  const xercesc::DOMNodeList* topicNodes = topicListNode->getChildNodes();
148 
149  for (XMLSize_t tn = 0, tn_len = topicNodes->getLength(); tn < tn_len; ++tn) {
150 
151  const xercesc::DOMNode* topicNode = topicNodes->item(tn);
152 
153  if (ACE_TEXT("topic") == XStr(topicNode->getNodeName())) {
154  action.topics.push_back(to_string(topicNode));
155  }
156  }
157 
158  } else if (ACE_TEXT("partitions") == XStr(topicListNode->getNodeName())) {
159  const xercesc::DOMNodeList* partitionNodes = topicListNode->getChildNodes();
160 
161  for (XMLSize_t pn = 0, pn_len = partitionNodes->getLength(); pn < pn_len; ++pn) {
162 
163  const xercesc::DOMNode* partitionNode = partitionNodes->item(pn);
164 
165  if (ACE_TEXT("partition") == XStr(partitionNode->getNodeName())) {
166  action.partitions.push_back(to_string(partitionNode));
167  }
168  }
169  }
170  }
171 
172  rule.actions.push_back(action);
173  }
174  }
175 
176  grant->rules.push_back(rule);
177  }
178  }
179 
180  if (!valid_subject) {
182  ACE_DEBUG((LM_WARNING, ACE_TEXT("(%P|%t) WARNING: {access_warn} Permissions::load: ")
183  ACE_TEXT("Unable to parse subject name, ignoring grant.\n")));
184  }
185  } else if (find_grant(grant->subject)) {
187  ACE_DEBUG((LM_WARNING, ACE_TEXT("(%P|%t) WARNING: {access_warn} Permissions::load: ")
188  ACE_TEXT("Ignoring grant with duplicate subject name.\n")));
189  }
190  } else {
191  grants_.push_back(grant);
192  }
193  } // grant_rules
194 
195  return 0;
196 }
197 
199 {
200  for (Grants::const_iterator it = grants_.begin(); it != grants_.end(); ++it) {
201  if (name == (*it)->subject) {
202  return true;
203  }
204  }
205  return false;
206 }
207 
209 {
210  for (Grants::const_iterator it = grants_.begin(); it != grants_.end(); ++it) {
211  if (name == (*it)->subject) {
212  return *it;
213  }
214  }
215  return Grant_rch();
216 }
217 
218 namespace {
219  typedef std::vector<std::string>::const_iterator vsiter_t;
220 }
221 
222 bool Permissions::Action::topic_matches(const char* topic) const
223 {
224  for (vsiter_t it = topics.begin(); it != topics.end(); ++it) {
225  if (AccessControlBuiltInImpl::pattern_match(topic, it->c_str())) {
226  return true;
227  }
228  }
229  return false;
230 }
231 
232 bool Permissions::Action::partitions_match(const DDS::StringSeq& entity_partitions, AllowDeny_t allow_or_deny) const
233 {
234  const unsigned int n_entity_names = entity_partitions.length();
235  if (partitions.empty()) {
236  if (allow_or_deny == DENY) {
237  // DDS-Security v1.1 9.4.1.3.2.3.2.4
238  // If there is no <partitions> section ... the deny action would
239  // apply independent of the partition associated with the DDS Endpoint
240  return true;
241  }
242  // DDS-Security v1.1 9.4.1.3.2.3.1.4
243  // If there is no <partitions> Section within an allow rule, then the default "empty string" partition is
244  // assumed. ... This means that the allow rule would only allow a DataWriter to publish on
245  // the "empty string" partition.
246  // DDS v1.4 2.2.3 "PARTITION"
247  // The zero-length sequence is treated as a special value equivalent to a sequence containing a single
248  // element consisting of the empty string.
249  return n_entity_names == 0 || (n_entity_names == 1 && entity_partitions[0].in()[0] == 0);
250  }
251 
252  for (unsigned int i = 0; i < n_entity_names; ++i) {
253  bool found = false;
254  for (vsiter_t perm_it = partitions.begin(); !found && perm_it != partitions.end(); ++perm_it) {
255  if (AccessControlBuiltInImpl::pattern_match(entity_partitions[i], perm_it->c_str())) {
256  found = true;
257  }
258  }
259  if (allow_or_deny == ALLOW && !found) {
260  // DDS-Security v1.1 9.4.1.3.2.3.1.4
261  // In order for an action to meet the allowed partitions condition that appears
262  // within an allow rule, the set of the Partitions associated with the DDS entity
263  // ... must be contained in the set of partitions defined by the allowed partitions
264  // condition section.
265  return false; // i'th QoS partition name is not matched by any <partition> in Permissions
266  }
267  if (allow_or_deny == DENY && found) {
268  // DDS-Security v1.1 9.4.1.3.2.3.2.4
269  // In order for an action to be denied it must meet the denied partitions condition.
270  // For this to happen one [or] more of the partition names associated with the DDS Entity
271  // ... must match one [of] the partitions ... listed in the partitions condition section.
272  return true; // i'th QoS partition name matches some <partition> in Permissions
273  }
274  }
275 
276  return allow_or_deny == ALLOW;
277 }
278 
279 }
280 }
281 
bool parse_domain_id_set(const xercesc::DOMNode *node, Security::DomainIdSet &domain_id_set)
Definition: XmlUtils.cpp:445
Grant_rch find_grant(const SSL::SubjectName &name) const
#define ACE_DEBUG(X)
#define ACE_ERROR(X)
std::vector< std::string > topics
Definition: Permissions.h:49
bool partitions_match(const DDS::StringSeq &entity_partitions, AllowDeny_t allow_or_deny) const
int load(const SSL::SignedDocument &doc)
Definition: Permissions.cpp:19
bool topic_matches(const char *topic) const
const std::string & filename() const
bool has_grant(const SSL::SubjectName &name) const
static bool pattern_match(const char *string, const char *pattern)
bool access_error
Permissions and Governance.
Definition: debug.h:132
const std::string & content() const
LM_WARNING
std::vector< std::string > partitions
Definition: Permissions.h:50
const char *const name
Definition: debug.cpp:60
ACE_TEXT("TCP_Factory")
DCPS::RcHandle< Grant > Grant_rch
Definition: Permissions.h:74
bool get_parser(ParserPtr &parser, const std::string &filename, const std::string &xml)
Definition: XmlUtils.cpp:71
#define OPENDDS_END_VERSIONED_NAMESPACE_DECL
bool parse_time(const XMLCh *in, time_t &value)
Definition: XmlUtils.cpp:245
const char * to_string(MessageId value)
LM_ERROR
The Internal API and Implementation of OpenDDS.
Definition: AddressCache.h:28
OpenDDS_Dcps_Export SecurityDebug security_debug
Definition: debug.cpp:32
sequence< string > StringSeq
Definition: DdsDcpsCore.idl:50