summaryrefslogtreecommitdiff
path: root/qpid/cpp/src/qpid/acl/AclReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/src/qpid/acl/AclReader.cpp')
-rw-r--r--qpid/cpp/src/qpid/acl/AclReader.cpp827
1 files changed, 827 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/acl/AclReader.cpp b/qpid/cpp/src/qpid/acl/AclReader.cpp
new file mode 100644
index 0000000000..e8223c3570
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclReader.cpp
@@ -0,0 +1,827 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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 "qpid/acl/AclReader.h"
+#include "qpid/acl/AclData.h"
+
+#include <cctype>
+#include <cstring>
+#include <fstream>
+#include <sstream>
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+#include <boost/lexical_cast.hpp>
+#include <algorithm>
+
+#include <iomanip> // degug
+#include <iostream> // debug
+
+#define ACL_FORMAT_ERR_LOG_PREFIX "ACL format error: " << fileName << ":" << lineNumber << ": "
+
+namespace qpid {
+namespace acl {
+
+ AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups) : res(r), actionAll(true), objStatus(NONE) {
+ processName(n, groups);
+ }
+ AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a) : res(r), actionAll(false), action(a), objStatus(NONE) {
+ processName(n, groups);
+ }
+
+ void AclReader::aclRule::setObjectType(const ObjectType o) {
+ objStatus = VALUE;
+ object = o;
+ }
+
+ void AclReader::aclRule::setObjectTypeAll() {
+ objStatus = ALL;
+ }
+
+ bool AclReader::aclRule::addProperty(const SpecProperty p, const std::string v) {
+ return props.insert(propNvPair(p, v)).second;
+ }
+
+ // Debug aid
+ std::string AclReader::aclRule::toString() {
+ std::ostringstream oss;
+ oss << AclHelper::getAclResultStr(res) << " [";
+ for (nsCitr itr = names.begin(); itr != names.end(); itr++) {
+ if (itr != names.begin()) oss << ", ";
+ oss << *itr;
+ }
+ oss << "]";
+ if (actionAll) {
+ oss << " *";
+ } else {
+ oss << " " << AclHelper::getActionStr(action);
+ }
+ if (objStatus == ALL) {
+ oss << " *";
+ } else if (objStatus == VALUE) {
+ oss << " " << AclHelper::getObjectTypeStr(object);
+ }
+ for (pmCitr i=props.begin(); i!=props.end(); i++) {
+ oss << " " << AclHelper::getPropertyStr(i->first) << "=" << i->second;
+ }
+ return oss.str();
+ }
+
+ void AclReader::loadDecisionData(boost::shared_ptr<AclData> d) {
+ d->clear();
+ QPID_LOG(debug, "ACL: Load Rules");
+ bool foundmode = false;
+ bool foundConnectionMode = false;
+
+ rlCitr i = rules.end();
+ for (int cnt = rules.size(); cnt; cnt--) {
+ i--;
+ QPID_LOG(debug, "ACL: Processing " << std::setfill(' ') << std::setw(2)
+ << cnt << " " << (*i)->toString());
+
+ if (!(*i)->actionAll && (*i)->objStatus == aclRule::VALUE &&
+ !validator.validateAllowedProperties(
+ (*i)->action, (*i)->object, (*i)->props, false)) {
+ // specific object/action has bad property
+ // this rule gets ignored
+ continue;
+ } else {
+ // action=all or object=none/all means the rule gets propagated
+ // possibly to many places.
+ // Invalid rule combinations are not propagated.
+ }
+
+ if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1
+ && (*((*i)->names.begin())).compare(AclData::ACL_KEYWORD_WILDCARD) == 0) {
+ d->decisionMode = (*i)->res;
+ QPID_LOG(debug, "ACL: FoundMode "
+ << AclHelper::getAclResultStr(d->decisionMode));
+ foundmode = true;
+ } else if ((*i)->action == acl::ACT_CREATE && (*i)->object == acl::OBJ_CONNECTION) {
+ // Intercept CREATE CONNECTION rules process them into separate lists to
+ // be consumed in the connection approval code path.
+ propMap::const_iterator pName = (*i)->props.find(SPECPROP_NAME);
+ if (pName != (*i)->props.end()) {
+ throw Exception(QPID_MSG("ACL: CREATE CONNECTION rule " << cnt << " must not have a 'name' property"));
+ }
+ propMap::const_iterator pHost = (*i)->props.find(SPECPROP_HOST);
+ if (pHost == (*i)->props.end()) {
+ throw Exception(QPID_MSG("ACL: CREATE CONNECTION rule " << cnt << " has no 'host' property"));
+ }
+ // create the connection rule
+ bool allUsers = (*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0;
+ bool allHosts = pHost->second.compare(AclData::ACL_KEYWORD_ALL) == 0;
+ AclBWHostRule bwRule((*i)->res, (allHosts ? "" : pHost->second));
+
+ // apply the rule globally or to user list
+ if (allUsers) {
+ if (allHosts) {
+ // allow one specification of allUsers,allHosts
+ if (foundConnectionMode) {
+ throw Exception(QPID_MSG("ACL: only one CREATE CONNECTION rule for user=all and host=all allowed"));
+ }
+ foundConnectionMode = true;
+ d->connectionDecisionMode = (*i)->res;
+ QPID_LOG(trace, "ACL: Found connection mode: " << AclHelper::getAclResultStr( (*i)->res ));
+ } else {
+ // Rules for allUsers but not allHosts go into the global list
+ globalHostRules->insert( globalHostRules->begin(), bwRule );
+ }
+ } else {
+ // other rules go into binned rule sets for each user
+ for (nsCitr itr = (*i)->names.begin();
+ itr != (*i)->names.end();
+ itr++) {
+ (*userHostRules)[(*itr)].insert( (*userHostRules)[(*itr)].begin(), bwRule);
+ }
+ }
+ } else {
+ AclData::Rule rule(cnt, (*i)->res, (*i)->props);
+ // Record which properties have the user substitution string
+ for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) {
+ if ((pItr->second.find(AclData::ACL_KEYWORD_USER_SUBST, 0) != std::string::npos) ||
+ (pItr->second.find(AclData::ACL_KEYWORD_DOMAIN_SUBST, 0) != std::string::npos) ||
+ (pItr->second.find(AclData::ACL_KEYWORD_USERDOMAIN_SUBST, 0) != std::string::npos)) {
+ rule.ruleHasUserSub[pItr->first] = true;
+ }
+ }
+
+ // Find possible routingkey property and cache its pattern
+ for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) {
+ if (acl::SPECPROP_ROUTINGKEY == pItr->first)
+ {
+ rule.pubRoutingKeyInRule = true;
+ rule.pubRoutingKey = (std::string)pItr->second;
+ rule.addTopicTest(rule.pubRoutingKey);
+ }
+ }
+
+ // Action -> Object -> map<user -> set<Rule> >
+ std::ostringstream actionstr;
+ for (int acnt = ((*i)->actionAll ? 0 : (*i)->action);
+ acnt < acl::ACTIONSIZE;
+ (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) {
+
+ if (acnt == acl::ACT_PUBLISH)
+ {
+ d->transferAcl = true; // we have transfer ACL
+ // For Publish the only object should be Exchange
+ // and the only property should be routingkey.
+ // Go through the rule properties and find the name and the key.
+ // If found then place them specially for the lookup engine.
+ for (pmCitr pItr=(*i)->props.begin(); pItr!=(*i)->props.end(); pItr++) {
+ if (acl::SPECPROP_NAME == pItr->first)
+ {
+ rule.pubExchNameInRule = true;
+ rule.pubExchName = pItr->second;
+ rule.pubExchNameMatchesBlank = rule.pubExchName.compare(AclData::ACL_KEYWORD_DEFAULT_EXCHANGE) == 0;
+ }
+ }
+ }
+ actionstr << AclHelper::getActionStr((Action) acnt) << ",";
+
+ //find the Action, create if not exist
+ if (d->actionList[acnt] == NULL) {
+ d->actionList[acnt] =
+ new AclData::aclAction[qpid::acl::OBJECTSIZE];
+ for (int j = 0; j < qpid::acl::OBJECTSIZE; j++)
+ d->actionList[acnt][j] = NULL;
+ }
+
+ for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0
+ : (*i)->object);
+ ocnt < acl::OBJECTSIZE;
+ (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) {
+
+ //find the Object, create if not exist
+ if (d->actionList[acnt][ocnt] == NULL)
+ d->actionList[acnt][ocnt] =
+ new AclData::actionObject;
+
+ // add users and Rule to object set
+ bool allNames = false;
+ // check to see if names.begin is '*'
+ if ((*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0)
+ allNames = true;
+
+ for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin());
+ itr != (allNames ? names.end() : (*i)->names.end());
+ itr++) {
+ if (validator.validateAllowedProperties(acl::Action(acnt),
+ acl::ObjectType(ocnt),
+ (*i)->props,
+ false)) {
+ AclData::actObjItr itrRule =
+ d->actionList[acnt][ocnt]->find(*itr);
+
+ if (itrRule == d->actionList[acnt][ocnt]->end()) {
+ AclData::ruleSet rSet;
+ rSet.push_back(rule);
+ d->actionList[acnt][ocnt]->insert
+ (make_pair(std::string(*itr), rSet));
+ } else {
+ itrRule->second.push_back(rule);
+ }
+ } else {
+ // Skip propagating this rule as it will never match.
+ }
+ }
+ }
+ }
+
+ std::ostringstream objstr;
+ for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object);
+ ocnt < acl::OBJECTSIZE;
+ (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) {
+ objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ",";
+ }
+
+ bool allNames = ((*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0);
+ std::ostringstream userstr;
+ for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin());
+ itr != (allNames ? names.end() : (*i)->names.end());
+ itr++) {
+ userstr << *itr << ",";
+ }
+
+ QPID_LOG(debug, "ACL: Adding actions {" <<
+ actionstr.str().substr(0,actionstr.str().length()-1)
+ << "} to objects {" <<
+ objstr.str().substr(0,objstr.str().length()-1)
+ << "} with props " <<
+ AclHelper::propertyMapToString(&rule.props)
+ << " for users {" <<
+ userstr.str().substr(0,userstr.str().length()-1)
+ << "}");
+ }
+ }
+
+ // connection quota
+ d->setConnQuotaRuleSettings(connQuota);
+ // queue quota
+ d->setQueueQuotaRuleSettings(queueQuota);
+ // global B/W connection rules
+ d->setConnGlobalRules(globalHostRules);
+ // user B/W connection rules
+ d->setConnUserRules(userHostRules);
+ }
+
+
+ void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) {
+ if (name.compare(AclData::ACL_KEYWORD_ALL) == 0) {
+ names.insert(AclData::ACL_KEYWORD_WILDCARD);
+ } else {
+ gmCitr itr = groups.find(name);
+ if (itr == groups.end()) {
+ names.insert(name);
+ } else {
+ names.insert(itr->second->begin(), itr->second->end());
+ }
+ }
+ }
+
+ AclReader::AclReader(uint16_t theCliMaxConnPerUser, uint16_t theCliMaxQueuesPerUser) :
+ lineNumber(0), contFlag(false),
+ cliMaxConnPerUser (theCliMaxConnPerUser),
+ connQuotaRulesExist(false),
+ connQuota(new AclData::quotaRuleSet),
+ cliMaxQueuesPerUser (theCliMaxQueuesPerUser),
+ queueQuotaRulesExist(false),
+ queueQuota(new AclData::quotaRuleSet),
+ globalHostRules(new AclData::bwHostRuleSet),
+ userHostRules(new AclData::bwHostUserRuleMap) {
+ names.insert(AclData::ACL_KEYWORD_WILDCARD);
+ }
+
+ AclReader::~AclReader() {}
+
+ std::string AclReader::getError() {
+ return errorStream.str();
+ }
+
+ int AclReader::read(const std::string& fn, boost::shared_ptr<AclData> d) {
+ fileName = fn;
+ lineNumber = 0;
+ char buff[1024];
+ std::ifstream ifs(fn.c_str(), std::ios_base::in);
+ if (!ifs.good()) {
+ errorStream << "Unable to open ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F");
+ return -1;
+ }
+ // Propagate nonzero per-user max connection setting from CLI
+ if (cliMaxConnPerUser > 0) {
+ connQuotaRulesExist = true;
+ (*connQuota)[AclData::ACL_KEYWORD_ALL] = cliMaxConnPerUser;
+ }
+ // Propagate nonzero per-user max queue setting from CLI
+ if (cliMaxQueuesPerUser > 0) {
+ queueQuotaRulesExist = true;
+ (*queueQuota)[AclData::ACL_KEYWORD_ALL] = cliMaxQueuesPerUser;
+ }
+ // Loop to process the Acl file
+ try {
+ bool err = false;
+ while (ifs.good()) {
+ ifs.getline(buff, 1024);
+ lineNumber++;
+ if (std::strlen(buff) > 0 && buff[0] != '#') // Ignore blank lines and comments
+ err |= !processLine(buff);
+ }
+ if (!ifs.eof())
+ {
+ errorStream << "Unable to read ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F");
+ ifs.close();
+ return -2;
+ }
+ ifs.close();
+ if (err) return -3;
+ QPID_LOG(notice, "ACL: Read file \"" << fn << "\"");
+ } catch (const std::exception& e) {
+ errorStream << "Unable to read ACL file \"" << fn << "\": " << e.what();
+ ifs.close();
+ return -4;
+ } catch (...) {
+ errorStream << "Unable to read ACL file \"" << fn << "\": Unknown exception";
+ ifs.close();
+ return -5;
+ }
+ printNames();
+ printRules();
+ printQuotas(AclData::ACL_KEYWORD_QUOTA_CONNECTIONS, connQuota);
+ printQuotas(AclData::ACL_KEYWORD_QUOTA_QUEUES, queueQuota);
+ try {
+ loadDecisionData(d);
+ } catch (const std::exception& e) {
+ errorStream << "Error loading decision data : " << e.what();
+ return -6;
+ }
+ printGlobalConnectRules();
+ printUserConnectRules();
+ validator.tracePropertyDefs();
+ d->printDecisionRules( printNamesFieldWidth() );
+
+ return 0;
+ }
+
+ bool AclReader::processLine(char* line) {
+ bool ret = false;
+ std::vector<std::string> toks;
+
+ // Check for continuation
+ char* contCharPtr = std::strrchr(line, AclData::ACL_SYMBOL_LINE_CONTINUATION);
+ bool cont = contCharPtr != 0;
+ if (cont) *contCharPtr = 0;
+
+ int numToks = tokenize(line, toks);
+
+ if (cont && numToks == 0){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line \"" << lineNumber << "\" contains an illegal extension.";
+ return false;
+ }
+
+ if (numToks && (toks[0].compare(AclData::ACL_KEYWORD_GROUP) == 0 || contFlag)) {
+ ret = processGroupLine(toks, cont);
+ } else if (numToks && toks[0].compare(AclData::ACL_KEYWORD_ACL) == 0) {
+ ret = processAclLine(toks);
+ } else if (numToks && toks[0].compare(AclData::ACL_KEYWORD_QUOTA) == 0) {
+ ret = processQuotaLine(toks);
+ } else {
+ // Check for whitespace only line, ignore these
+ bool ws = true;
+ for (unsigned i=0; i<std::strlen(line) && ws; i++) {
+ if (!std::isspace(line[i])) ws = false;
+ }
+ if (ws) {
+ ret = true;
+ } else {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Non-continuation line must start with \""
+ << AclData::ACL_KEYWORD_GROUP << "\", \""
+ << AclData::ACL_KEYWORD_ACL << "\". or \""
+ << AclData::ACL_KEYWORD_QUOTA << "\".";
+ ret = false;
+ }
+ }
+ contFlag = cont;
+ return ret;
+ }
+
+ int AclReader::tokenize(char* line, std::vector<std::string>& toks) {
+ const char* tokChars = " \t\n\f\v\r";
+ int cnt = 0;
+ char* cp = std::strtok(line, tokChars);
+ while (cp != 0) {
+ toks.push_back(std::string(cp));
+ cnt++;
+ cp = std::strtok(0, tokChars);
+ }
+ return cnt;
+ }
+
+
+ // Process 'quota' rule lines
+ // Return true if the line is successfully processed without errors
+ bool AclReader::processQuotaLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+ const unsigned minimumSize = 3;
+ if (toksSize < minimumSize) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for quota definition.";
+ return false;
+ }
+
+ if (toks[1].compare(AclData::ACL_KEYWORD_QUOTA_CONNECTIONS) == 0) {
+ if (processQuotaLine(toks, AclData::ACL_KEYWORD_QUOTA_CONNECTIONS, AclData::getConnectMaxSpec(), connQuota)) {
+ // We have processed a connection quota rule
+ connQuotaRulesExist = true;
+ return true;
+ }
+ } else if (toks[1].compare(AclData::ACL_KEYWORD_QUOTA_QUEUES) == 0) {
+ if (processQuotaLine(toks, AclData::ACL_KEYWORD_QUOTA_QUEUES, AclData::getConnectMaxSpec(), queueQuota)) {
+ // We have processed a queue quota rule
+ queueQuotaRulesExist = true;
+ return true;
+ }
+ } else {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Quota type \"" << toks[1] << "\" unrecognized.";
+ return false;
+ }
+ return false;
+ }
+
+
+ // Process quota rule lines
+ // Return true if the line is successfully processed without errors
+ bool AclReader::processQuotaLine(tokList& toks, const std::string theNoun, uint16_t maxSpec, aclQuotaRuleSet theRules) {
+ const unsigned toksSize = toks.size();
+
+ uint16_t nEntities(0);
+ try {
+ nEntities = boost::lexical_cast<uint16_t>(toks[2]);
+ } catch(const boost::bad_lexical_cast&) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", " << theNoun << " quota value \"" << toks[2]
+ << "\" cannot be converted to a 16-bit unsigned integer.";
+ return false;
+ }
+
+ // limit check the setting
+ if (nEntities > maxSpec)
+ {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", " << theNoun << " quota value \"" << toks[2]
+ << "\" exceeds maximum configuration setting of "
+ << maxSpec;
+ return false;
+ }
+
+ // Apply the ount to all names in rule
+ for (unsigned idx = 3; idx < toksSize; idx++) {
+ if (groups.find(toks[idx]) == groups.end()) {
+ // This is the name of an individual, not a group
+ (*theRules)[toks[idx]] = nEntities;
+ } else {
+ if (!processQuotaGroup(toks[idx], nEntities, theRules))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ // Process quota group expansion
+ // Return true if the quota is applied to all members of the group
+ bool AclReader::processQuotaGroup(const std::string& theGroup, uint16_t theQuota, aclQuotaRuleSet theRules) {
+ gmCitr citr = groups.find(theGroup);
+
+ if (citr == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Failed to expand group \"" << theGroup << "\".";
+ return false;
+ }
+
+ for (nsCitr gni=citr->second->begin(); gni!=citr->second->end(); gni++) {
+ if (groups.find(*gni) == groups.end()) {
+ (*theRules)[*gni] = theQuota;
+ } else {
+ if (!processQuotaGroup(*gni, theQuota, theRules))
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ void AclReader::printQuotas(const std::string theNoun, aclQuotaRuleSet theRules) const {
+ QPID_LOG(debug, "ACL: " << theNoun << " quota: " << (*theRules).size() << " rules found:");
+ int cnt = 1;
+ for (AclData::quotaRuleSetItr itr=(*theRules).begin();
+ itr != (*theRules).end();
+ ++itr,++cnt) {
+ QPID_LOG(debug, "ACL: quota " << cnt << " : " << (*itr).second
+ << " " << theNoun << " for " << (*itr).first)
+ }
+ }
+
+
+ // Return true if the line is successfully processed without errors
+ // If cont is true, then groupName must be set to the continuation group name
+ bool AclReader::processGroupLine(tokList& toks, const bool cont) {
+ const unsigned toksSize = toks.size();
+
+ if (contFlag) {
+ gmCitr citr = groups.find(groupName);
+ for (unsigned i = 0; i < toksSize; i++) {
+ if (isValidGroupName(toks[i])) {
+ if (toks[i] == groupName) {
+ QPID_LOG(debug, "ACL: Line: " << lineNumber
+ << ", Ignoring recursive sub-group \"" << toks[i] << "\".");
+ continue;
+ } else if (groups.find(toks[i]) == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Sub-group \"" << toks[i] << "\" not defined yet.";
+ return false;
+ }
+ } else if (!isValidUserName(toks[i])) return false;
+ addName(toks[i], citr->second);
+ }
+ } else {
+ const unsigned minimumSize = (cont ? 2 : 3);
+ if (toksSize < minimumSize) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for group definition.";
+ return false;
+ }
+ if (!isValidGroupName(toks[1])) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Group name \"" << toks[1] << "\" contains illegal characters.";
+ return false;
+ }
+ gmCitr citr = addGroup(toks[1]);
+ if (citr == groups.end()) return false;
+ for (unsigned i = 2; i < toksSize; i++) {
+ if (isValidGroupName(toks[i])) {
+ if (toks[i] == groupName) {
+ QPID_LOG(debug, "ACL: Line: " << lineNumber
+ << ", Ignoring recursive sub-group \"" << toks[i] << "\".");
+ continue;
+ } else if (groups.find(toks[i]) == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Sub-group \"" << toks[i] << "\" not defined yet.";
+ return false;
+ }
+ } else if (!isValidUserName(toks[i])) return false;
+ addName(toks[i], citr->second);
+ }
+ }
+ return true;
+ }
+
+ // Return true if sucessfully added group
+ AclReader::gmCitr AclReader::addGroup(const std::string& newGroupName) {
+ gmCitr citr = groups.find(newGroupName);
+ if (citr != groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Duplicate group name \"" << newGroupName << "\".";
+ return groups.end();
+ }
+ groupPair p(newGroupName, nameSetPtr(new nameSet));
+ gmRes res = groups.insert(p);
+ assert(res.second);
+ groupName = newGroupName;
+ return res.first;
+ }
+
+ void AclReader::addName(const std::string& name, nameSetPtr groupNameSet) {
+ gmCitr citr = groups.find(name);
+ if (citr != groups.end()) {
+ // This is a previously defined group: add all the names in that group to this group
+ groupNameSet->insert(citr->second->begin(), citr->second->end());
+ } else {
+ // Not a known group name
+ groupNameSet->insert(name);
+ addName(name);
+ }
+ }
+
+ void AclReader::addName(const std::string& name) {
+ names.insert(name);
+ }
+
+ /**
+ * Emit debug logs exposing the name lists
+ */
+ void AclReader::printNames() const {
+ QPID_LOG(debug, "ACL: Group list: " << groups.size() << " groups found:" );
+ std::string tmp("ACL: ");
+ for (gmCitr i=groups.begin(); i!= groups.end(); i++) {
+ tmp += " \"";
+ tmp += i->first;
+ tmp += "\":";
+ for (nsCitr j=i->second->begin(); j!=i->second->end(); j++) {
+ tmp += " ";
+ tmp += *j;
+ }
+ QPID_LOG(debug, tmp);
+ tmp = "ACL: ";
+ }
+ QPID_LOG(debug, "ACL: name list: " << names.size() << " names found:" );
+ tmp = "ACL: ";
+ for (nsCitr k=names.begin(); k!=names.end(); k++) {
+ tmp += " ";
+ tmp += *k;
+ }
+ QPID_LOG(debug, tmp);
+ }
+
+ /**
+ * compute the width of longest user name
+ */
+ int AclReader::printNamesFieldWidth() const {
+ std::string::size_type max = 0;
+ for (nsCitr k=names.begin(); k!=names.end(); k++) {
+ max = std::max(max, (*k).length());
+ }
+ return max;
+ }
+
+ bool AclReader::processAclLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+ if (toksSize < 4) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for acl definition.";
+ return false;
+ }
+
+ AclResult res;
+ try {
+ res = AclHelper::getAclResult(toks[1]);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown ACL permission \"" << toks[1] << "\".";
+ return false;
+ }
+
+ bool actionAllFlag = toks[3].compare(AclData::ACL_KEYWORD_ALL) == 0;
+ bool userAllFlag = toks[2].compare(AclData::ACL_KEYWORD_ALL) == 0;
+ Action action;
+ if (actionAllFlag) {
+
+ if (userAllFlag && toksSize > 4) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Tokens found after action \"all\".";
+ return false;
+ }
+ action = ACT_CONSUME; // dummy; compiler must initialize action for this code path
+ } else {
+ try {
+ action = AclHelper::getAction(toks[3]);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown action \"" << toks[3] << "\".";
+ return false;
+ }
+ }
+
+ // Create rule obj; then add object (if any) and properties (if any)
+ aclRulePtr rule;
+ if (actionAllFlag) {
+ rule.reset(new aclRule(res, toks[2], groups));
+ } else {
+ rule.reset(new aclRule(res, toks[2], groups, action));
+ }
+
+ if (toksSize >= 5) { // object name-value pair
+ if (toks[4].compare(AclData::ACL_KEYWORD_ALL) == 0) {
+ rule->setObjectTypeAll();
+ } else {
+ try {
+ rule->setObjectType(AclHelper::getObjectType(toks[4]));
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown object \"" << toks[4] << "\".";
+ return false;
+ }
+ }
+ }
+
+ if (toksSize >= 6) { // property name-value pair(s)
+ for (unsigned i=5; i<toksSize; i++) {
+ nvPair propNvp = splitNameValuePair(toks[i]);
+ if (propNvp.second.size() == 0) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ <<", Badly formed property name-value pair \""
+ << propNvp.first << "\". (Must be name=value)";
+ return false;
+ }
+ SpecProperty prop;
+ try {
+ prop = AclHelper::getSpecProperty(propNvp.first);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown property \"" << propNvp.first << "\".";
+ return false;
+ }
+ rule->addProperty(prop, propNvp.second);
+ }
+ }
+ // Check if name (toks[2]) is group; if not, add as name of individual
+ if (toks[2].compare(AclData::ACL_KEYWORD_ALL) != 0) {
+ if (groups.find(toks[2]) == groups.end()) {
+ addName(toks[2]);
+ }
+ }
+
+ rules.push_back(rule);
+
+ return true;
+ }
+
+ // Debug aid
+ void AclReader::printRules() const {
+ QPID_LOG(debug, "ACL: Rule list: " << rules.size() << " ACL rules found:");
+ int cnt = 1;
+ for (rlCitr i=rules.begin(); i<rules.end(); i++,cnt++) {
+ QPID_LOG(debug, "ACL: " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString());
+ if (!(*i)->actionAll && (*i)->objStatus == aclRule::VALUE) {
+ (void)validator.validateAllowedProperties((*i)->action, (*i)->object, (*i)->props, true);
+ }
+ }
+ }
+
+ void AclReader::printConnectionRules(const std::string name, const AclData::bwHostRuleSet& rules) const {
+ QPID_LOG(debug, "ACL: " << name << " Connection Rule list : " << rules.size() << " rules found :");
+ int cnt = 1;
+ for (AclData::bwHostRuleSetItr i=rules.begin(); i<rules.end(); i++,cnt++) {
+ QPID_LOG(debug, "ACL: " << std::setfill(' ') << std::setw(2) << cnt << " " << i->toString());
+ }
+ }
+
+ void AclReader::printGlobalConnectRules() const {
+ printConnectionRules("global", *globalHostRules);
+ }
+
+ void AclReader::printUserConnectRules() const {
+ QPID_LOG(debug, "ACL: User Connection Rule lists : " << userHostRules->size() << " user lists found :");
+ int cnt = 1;
+ for (AclData::bwHostUserRuleMapItr i=userHostRules->begin(); i!=userHostRules->end(); i++,cnt++) {
+ printConnectionRules(std::string((*i).first), (*i).second);
+ }
+ }
+
+ // Static function
+ // Return true if the name is well-formed (ie contains legal characters)
+ bool AclReader::isValidGroupName(const std::string& name) {
+ for (unsigned i=0; i<name.size(); i++) {
+ const char ch = name.at(i);
+ if (!std::isalnum(ch) && ch != '-' && ch != '_') return false;
+ }
+ return true;
+ }
+
+ // Static function
+ // Split name-value pair around '=' char of the form "name=value"
+ AclReader::nvPair AclReader::splitNameValuePair(const std::string& nvpString) {
+ std::size_t pos = nvpString.find("=");
+ if (pos == std::string::npos || pos == nvpString.size() - 1) {
+ return nvPair(nvpString, "");
+ }
+ return nvPair(nvpString.substr(0, pos), nvpString.substr(pos+1));
+ }
+
+ // Returns true if a username has the name@realm format
+ bool AclReader::isValidUserName(const std::string& name){
+ size_t pos = name.find('@');
+ if ( pos == std::string::npos || pos == name.length() -1){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Username '" << name << "' must contain a realm";
+ return false;
+ }
+ for (unsigned i=0; i<name.size(); i++) {
+ const char ch = name.at(i);
+ if (!std::isalnum(ch) && ch != '-' && ch != '_' && ch != '@' && ch != '.' && ch != '/'){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Username \"" << name << "\" contains illegal characters.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+}} // namespace qpid::acl