2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include <dpl/foreach.h>
21 #include <dpl/log/log.h>
22 #include <dpl/ace/Attribute.h>
23 #include <dpl/ace/Serializer.h>
25 const bool Attribute::alpha[256] = {
26 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
31 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
32 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
33 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
35 const bool Attribute::digit[256] = {
36 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
38 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0
42 const bool Attribute::mark[256] = {
43 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0,
46 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
49 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0
53 bool Attribute::searchAndCut(const char *str)
56 size_t pos = m_name.rfind(str);
57 if (pos == std::string::npos) {
60 if ((strlen(str) + pos) == m_name.size()) {
61 m_name.erase(pos, std::string::npos);
67 Attribute::Attribute(const std::string *name,
68 const Match matchFunc,
70 matchFunction(matchFunc)
74 m_undetermindState = false;
75 if (matchFunction != Match::Equal
76 && matchFunction != Match::Glob
77 && matchFunction != Match::Regexp)
79 //LogDebug("MID: " << matchFunction);
80 Assert(0 && "Match function problem");
83 if (searchAndCut(".scheme")) {
84 modifierFunction = Modifier::Scheme;
85 } else if (searchAndCut(".authority")) {
86 modifierFunction = Modifier::Authority;
87 } else if (searchAndCut(".scheme-authority")) {
88 modifierFunction = Modifier::SchemeAuthority;
89 } else if (searchAndCut(".host")) {
90 modifierFunction = Modifier::Host;
91 } else if (searchAndCut(".path")) {
92 modifierFunction = Modifier::Path;
94 modifierFunction = Modifier::Non;
98 static Attribute::MatchResult equal_comparator(const std::string *first,
99 const std::string *second)
101 if((*first) == (*second)) {
102 return Attribute::MatchResult::MRTrue;
104 return Attribute::MatchResult::MRFalse;
107 static Attribute::MatchResult glob_comparator(const std::string *first,
108 const std::string *second)
110 // order is important
111 if (!fnmatch(first->c_str(), second->c_str(), 0)) {
112 return Attribute::MatchResult::MRTrue;
114 return Attribute::MatchResult::MRFalse;
117 static Attribute::MatchResult regexp_comparator(const std::string *first,
118 const std::string *second)
120 // order is important
121 pcrecpp::RE re(first->c_str());
122 if (re.FullMatch(second->c_str())) {
123 return Attribute::MatchResult::MRTrue;
125 return Attribute::MatchResult::MRFalse;
128 Attribute::MatchResult Attribute::lists_comparator(
129 const std::list<std::string> *first,
130 const std::list<std::string> *second,
131 Attribute::MatchResult (*comparator)(const std::string *,
132 const std::string *)) const
134 //NOTE: BONDI defines all availabe matching function as: if some string from first input bag
135 //matches some input string from second input bag, so it's required to find only one matching string
136 MatchResult result = MatchResult::MRFalse;
138 for (std::list<std::string>::const_iterator second_iter = second->begin();
139 (second_iter != second->end()) && (result != MatchResult::MRTrue);
142 std::string *modified_value = applyModifierFunction(&(*second_iter));
143 //Value was not an URI, it will be removed from the string bag (ignored)
144 if (modified_value == NULL) {
148 for (std::list<std::string>::const_iterator first_iter = first->begin();
149 first_iter != first->end();
152 if ((*comparator)(&(*first_iter), modified_value) == MatchResult::MRTrue) {
153 result = MatchResult::MRTrue;
154 break; //Only one match is enough
157 if (modified_value) {
158 delete modified_value;
159 modified_value = NULL;
163 if (result == MatchResult::MRTrue) {
164 LogDebug("Returning TRUE");
165 } else if (result == MatchResult::MRFalse) {
166 LogDebug("Returning FALSE");
167 } else if (result == MatchResult::MRUndetermined) {
168 LogDebug("Returning UNDETERMINED");
173 std::string * Attribute::applyModifierFunction(const std::string * val) const
175 std::string * result = NULL;
176 switch (modifierFunction) {
177 case Modifier::Scheme:
178 result = uriScheme(val);
180 case Modifier::Authority:
181 result = uriAuthority(val);
183 case Modifier::SchemeAuthority:
184 result = uriSchemeAuthority(val);
187 result = uriHost(val);
190 result = uriPath(val);
193 result = new std::string(*val);
200 * this - attribute obtained from xmlPolicy tree
201 * attribute - attribute obtained from PIP
203 Attribute::MatchResult Attribute::matchAttributes(
204 const BaseAttribute *attribute) const
206 std::string tempNam = *(attribute->getName());
210 if (!(attribute->getValue()->empty())) {
211 tempVal = attribute->getValue()->front();
214 if (!(this->value.empty())) {
215 myVal = this->value.front();
218 LogDebug("Comparing attribute: " << this->m_name << "(" <<
219 myVal << ") with: " << tempNam <<
220 "(" << tempVal << ")");
223 (this->m_name == *(attribute->getName())) &&
224 "Two completely different attributes are being compared!");
226 (this->m_typeId == attribute->getType()) &&
227 "Two completely different attributes are being compared!");
229 if (attribute->isUndetermind()) {
230 LogDebug("Attribute match undetermined");
231 return MatchResult::MRUndetermined;
234 //Regardles the algorithm used, if we have empty
235 //bag the result is always false
236 if (this->isValueEmpty() || attribute->isValueEmpty()) {
237 if (this->isValueEmpty()) {
238 LogDebug("empty bag in condition comparing");
240 if (attribute->isValueEmpty()) {
241 LogDebug("empty bag in attribute comparing");
243 return MatchResult::MRFalse;
246 if (this->matchFunction == Match::Equal) {
247 return lists_comparator(&(this->value),
248 attribute->getValue(),
250 } else if (this->matchFunction == Match::Glob) {
251 return lists_comparator(&(this->value),
252 attribute->getValue(),
254 } else if (this->matchFunction == Match::Regexp) {
255 return lists_comparator(&(this->value),
256 attribute->getValue(),
258 } //[CR] Change to Assert
259 Assert(false && " ** Critical :: no match function selected!");
260 return MatchResult::MRFalse; // to remove compilator warning
263 void Attribute::addValue(const std::string *val)
265 this->getValue()->push_back(*val);
268 std::ostream & operator<<(std::ostream & out,
269 const Attribute & attr)
271 out << "attr: m_name: " << *(attr.getName())
272 << " type: " << Attribute::typeToString(attr.getType())
274 if (attr.m_undetermindState) {
275 out << "Undetermined";
276 } else if (attr.getValue()->empty()) {
277 out << "Empty string bag";
279 FOREACH (it, *attr.getValue()) {
287 Attribute::parse(const std::string *input,
288 std::string *val) const
290 static const char *pattern =
291 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?";
292 pcrecpp::RE re(pattern);
293 re.FullMatch(input->c_str(), &val[0], &val[1],
294 &val[2], &val[3], &val[4],
295 &val[5], &val[6], &val[7], &val[8]);
298 for (int i = 0; i < 9; i++) {
299 LogDebug("val " << i << " :" << val[i]);
303 if (find_error(val)) {
304 LogDebug("Input is not an URI " << *input);
305 for (int i = 0; i < 9; ++i) {
314 Attribute::Attribute(std::istream& is)
316 Serializer* serializer = Serializer::getInstance();
318 m_name = serializer->deserializeString(is);
319 m_typeId = serializer->deserializeType(is);
321 setValue(serializer->deserializeListStrings(is));
323 matchFunction = serializer->deserializeMatch(is);
324 modifierFunction = serializer->deserializeModifier(is);
327 Attribute::~Attribute()
331 bool Attribute::serialize(std::ostream& os) const
333 Serializer* serializer = Serializer::getInstance();
335 serializer->serializeString(os, m_name);
336 serializer->serializeType(os, m_typeId);
337 serializer->serializeListStrings(os, value);
339 serializer->serializeMatch(os, matchFunction);
340 serializer->serializeModifier(os, modifierFunction);
344 std::string * Attribute::uriScheme(const std::string *input) const
347 if (!parse(input, part)) {
350 return new string(part[1]);
354 Attribute::uriAuthority(const std::string *input) const
357 if (!parse(input, part)) {
360 return new string(part[3]);
364 Attribute::uriSchemeAuthority(const std::string *input) const
367 if (!parse(input, part)) {
371 if (part[0].size() == 0 || part[2].size() == 0) {
372 return new std::string();
374 return new string(part[0] + part[2]);
378 Attribute::uriHost(const std::string *input) const
381 if (!parse(input, part)) {
384 return getHost(&(part[3]));
388 Attribute::uriPath(const std::string *input) const
390 //TODO right now uriPath leaves leading '/' in uri, this slash is removed from the string
391 //it's not clear if leading '/' is a part of path component or only the separator
393 if (!parse(input, part)) {
397 std::string * temp = NULL;
399 if (part[4].at(0) == '/') {
400 temp = new string(part[4].substr(1, part[4].length() - 1));
402 temp = new string(part[4]);
408 bool Attribute::find_error(const std::string *tab) const
410 //We are checking tab[1] which contains scheme without ':' at the end
411 if (!checkScheme(&(tab[1]))) {
412 LogDebug("Check scheme failed, URI is invalid");
413 return true; //error found
415 if (!checkAuthority(&(tab[3]))) {
416 LogDebug("Check authority failed, URI is invalid");
417 return true; //error found
420 if (!checkPath(&(tab[4]))) {
421 LogDebug("Check path failed, URI is invalid");
422 return true; //error found
428 bool Attribute::checkScheme(const std::string *part) const
430 Assert(part != NULL && "Checking NULLable string. This should never happen");
434 //TODO change part->at to data=part->c_str()
435 //TODO can scheme be empty? In absolute URI no, in relative URI yes
437 //Empty string is a correct schema
439 } else if (alpha[(int) (part->at(0))] == 0) {
440 result = false; // First scheme character must be alpha
442 // rest must be alpha or digit or '+' or '-' or '.'
443 for (unsigned int i = 1; i < part->size(); ++i) {
444 int c = static_cast<int>(part->at(i));
445 if (!isSchemeAllowedCharacter(c)) {
454 bool Attribute::checkAuthority(const std::string *part) const
456 Assert(part != NULL && "Checking NULLable string. This should never happen");
458 //Server is a subset of reg_m_names so here we only check if authority matches reg_m_name
459 //Additional check if authority is a valid 'server' component is done in getHost
461 return true; //empty authority is valid uri
465 const char * data = part->c_str();
466 for (size_t i = 0; i < part->length(); ++i) {
467 int c = (int) data[i];
468 if (isUnreserved(c)) {
496 if (isEscaped(data + i)) {
497 i += 2; //rewind the two escaped characters
508 std::string * Attribute::getHost(const std::string *part) const
511 return new std::string("");
515 size_t userInfoPos = part->find("@");
516 if (userInfoPos != std::string::npos) {
517 std::string data = part->substr(0, userInfoPos);
518 if (!isUserInfoAllowedString(&data)) {
519 return new string(""); //the authority is not composed of 'server' part
524 //If we use host modifier then authority is composed of 'server' part so
525 //the port must contain only digits
526 size_t portPos = part->find(":");
527 if (portPos != std::string::npos) {
528 for (unsigned int i = portPos + 1; i < part->size(); ++i) {
529 if (!digit[(int) part->at(i)]) {
530 return new string(""); //the authority is not composed of 'server' part
533 host = part->substr(userInfoPos + 1, portPos - (userInfoPos + 1));
535 host = part->substr(userInfoPos + 1, part->length() - (userInfoPos + 1));
538 if (!isHostAllowedString(&host)) {
539 //Even if the string is not allowed for host this can still be a valid uri
540 return new string("");
543 return new std::string(host);
546 bool Attribute::checkPath(const std::string *part) const
550 const char * data = part->c_str();
552 for (unsigned int i = 0; i < part->size(); ++i) {
555 //If we found slash then the next character must be a part of segment
556 //It cannot be '/' so we have to check it immediately
559 if (!isSegmentAllowedCharacter(c)) {
563 } else if (c == ';') {
564 //Start param part of segment
565 i++; //Param can be empty so we don't have to check what's right after semicolon
567 } else if (c == '%') {
568 //We have to handle escaped characters differently than other segment allowed characters
569 //because we need an array
570 if (isEscaped(data + i)) {
577 if (!isSegmentAllowedCharacter(c)) {
587 bool Attribute::isSchemeAllowedCharacter(int c) const
592 } else if (c == '+') {
594 } else if (c == '-') {
596 } else if (c == '.') {
603 bool Attribute::isSegmentAllowedCharacter(int c) const
607 // LogDebug("Checking is segment allowed for char "<<(char)c);
609 if (isUnreserved(c)) { //do nothing, result = true
610 } else if (c == ':') { //do nothing, result = true
611 } else if (c == '@') { //do nothing, result = true
612 } else if (c == '&') { //do nothing, result = true
613 } else if (c == '=') { //do nothing, result = true
614 } else if (c == '+') { //do nothing, result = true
615 } else if (c == '$') { //do nothing, result = true
616 } else if (c == ',') { //do nothing, result = true
624 bool Attribute::isUserInfoAllowedString(const std::string * str) const
628 const char * data = str->c_str();
630 for (unsigned int i = 0; i < str->length(); ++i) {
632 if (isUnreserved(c)) {
634 } else if (c == '%') {
635 //isEsacped method checks if we don't cross array bounds, so we can
636 //safely give data[i] here
637 result = isEscaped((data + i));
638 if (result == false) {
641 i += 2; //rewind the next two characters sEsacped method checks if we don't cross array bounds, so we can safely rewind
642 } else if (c == ',') {
644 } else if (c == '$') {
646 } else if (c == '+') {
648 } else if (c == '=') {
650 } else if (c == '&') {
652 } else if (c == '@') {
654 } else if (c == ':') {
661 bool Attribute::isUnreserved(int c) const
663 return isAlphanum(c) || mark[c];
666 bool Attribute::isAlphanum(int c) const
668 return alpha[c] || digit[c];
671 bool Attribute::isHex(int c) const
677 } else if (c == 'A') {
679 } else if (c == 'B') {
681 } else if (c == 'C') {
683 } else if (c == 'D') {
685 } else if (c == 'E') {
687 } else if (c == 'F') {
689 } else if (c == 'a') {
691 } else if (c == 'b') {
693 } else if (c == 'c') {
695 } else if (c == 'd') {
697 } else if (c == 'e') {
699 } else if (c == 'f') {
706 bool Attribute::isEscaped(const char esc[3]) const
712 if ((esc[0] == 0) || (esc[1] == 0) || (esc[2] == 0)) {
713 //We get an array that seems to be out of bounds.
714 //To be on the safe side return here
715 LogDebug("HEX NULLS");
721 "Error: first character of escaped value must be a precent but is "
728 for (int i = 0; i < 3; i++) {
729 LogDebug("HEX " << esc[i]);
732 return isHex((int) esc[1]) && isHex((int) esc[2]);
735 bool Attribute::isHostAllowedString(const std::string * str) const
739 if (digit[(int) str->at(0)]) {
741 result = isIPv4AllowedString(str);
744 result = isHostNameAllowedString(str);
750 bool Attribute::isIPv4AllowedString(const std::string * str) const
752 LogDebug("Is hostIPv4 allowed String for " << *str);
754 const char * data = str->c_str();
756 int digitCounter = 0;
759 for (unsigned int i = 0; i < str->length(); ++i) {
760 if (data[i] == '.') {
763 } else if (digit[(int) data[i]]) {
765 if ((digitCounter > 3) || !digitCounter) {
774 if (dotCounter != 3) {
780 bool Attribute::isHostNameAllowedString(const std::string * str) const
782 LogDebug("Is hostname allowed String for " << *str);
784 int lastPosition = 0; //the position of last dot + 1
785 const char * data = str->c_str();
786 bool finalDot = false;
787 size_t end = str->length();
790 for (size_t i = 0; i < end; ++i) {
791 if (data[i] == '.') {
792 if (i == str->length() - 1) { //ending dot
793 //There can be a leading '.' int the hostm_name
797 //we found domain label
798 if (!isDomainLabelAllowedString(data + lastPosition, i -
803 lastPosition = i + 1; //Set position to position of last dot + 1
809 //we have to rewind one position to check the rightmost string
810 //but only in case we find final dot
813 //Compare only the rightmost string aaa.bbbb.rightmostString.
814 result = isTopLabelAllowedString(data + lastPosition, end - lastPosition);
819 LogInfo("Hostname is allowed");
821 LogInfo("Hostname is NOT allowed");
827 bool Attribute::isDomainLabelAllowedString(const char * data,
831 "Is domain allowed String for " << data << " taking first " <<
835 if (!isAlphanum((int) data[0]) || !isAlphanum((int) data[length - 1])) {
839 for (int i = 0; i < length; i++) {
840 if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
847 bool Attribute::isTopLabelAllowedString(const char * data,
850 if ((!alpha[(int) data[0]]) || (!isAlphanum((int) data[length - 1]))) {
854 for (int i = 1; i < length - 1; i++) {
855 if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
862 void printAttributes(const AttributeSet& attrs)
865 LogWarning("Empty attribute set");
867 LogDebug("PRINT ATTRIBUTES:");
868 for (AttributeSet::const_iterator it = attrs.begin();
872 LogDebug("name: " << *(*it)->getName());
877 void printAttributes(const std::list<Attribute> & attrs)
880 LogWarning("Empty attribute set");
882 LogDebug("PRINT ATTRIBUTES:");
883 for (std::list<Attribute>::const_iterator it = attrs.begin();
892 //KW const char * matchResultToString(Attribute::MatchResult result){
894 //KW const char * ret = NULL;
898 //KW case Attribute::MRTrue:
901 //KW case Attribute::MRFalse:
904 //KW case Attribute::MRUndetermined:
905 //KW ret = "undetermined";
908 //KW ret = "Wrong match result";