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 <ace/Attribute.h>
24 const bool Attribute::alpha[256] = {
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
30 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
31 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
32 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
34 const bool Attribute::digit[256] = {
35 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0
41 const bool Attribute::mark[256] = {
42 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0,
45 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1,
48 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0
52 bool Attribute::searchAndCut(const char *str)
55 size_t pos = m_name.rfind(str);
56 if (pos == std::string::npos) {
59 if ((strlen(str) + pos) == m_name.size()) {
60 m_name.erase(pos, std::string::npos);
66 Attribute::Attribute(const std::string *name,
67 const Match matchFunc,
69 matchFunction(matchFunc)
73 m_undetermindState = false;
74 if (matchFunction != Match::Equal
75 && matchFunction != Match::Glob
76 && matchFunction != Match::Regexp)
78 //LogDebug("MID: " << matchFunction);
79 Assert(0 && "Match function problem");
82 if (searchAndCut(".scheme")) {
83 modifierFunction = Modifier::Scheme;
84 } else if (searchAndCut(".authority")) {
85 modifierFunction = Modifier::Authority;
86 } else if (searchAndCut(".scheme-authority")) {
87 modifierFunction = Modifier::SchemeAuthority;
88 } else if (searchAndCut(".host")) {
89 modifierFunction = Modifier::Host;
90 } else if (searchAndCut(".path")) {
91 modifierFunction = Modifier::Path;
93 modifierFunction = Modifier::Non;
97 static Attribute::MatchResult equal_comparator(const std::string *first,
98 const std::string *second)
100 if((*first) == (*second)) {
101 return Attribute::MatchResult::MRTrue;
103 return Attribute::MatchResult::MRFalse;
106 static Attribute::MatchResult glob_comparator(const std::string *first,
107 const std::string *second)
109 // order is important
110 if (!fnmatch(first->c_str(), second->c_str(), 0)) {
111 return Attribute::MatchResult::MRTrue;
113 return Attribute::MatchResult::MRFalse;
116 static Attribute::MatchResult regexp_comparator(const std::string *first,
117 const std::string *second)
119 // order is important
120 pcrecpp::RE re(first->c_str());
121 if (re.FullMatch(second->c_str())) {
122 return Attribute::MatchResult::MRTrue;
124 return Attribute::MatchResult::MRFalse;
127 Attribute::MatchResult Attribute::lists_comparator(
128 const std::list<std::string> *first,
129 const std::list<std::string> *second,
130 Attribute::MatchResult (*comparator)(const std::string *,
131 const std::string *)) const
133 //NOTE: BONDI defines all availabe matching function as: if some string from first input bag
134 //matches some input string from second input bag, so it's required to find only one matching string
135 MatchResult result = MatchResult::MRFalse;
137 for (std::list<std::string>::const_iterator second_iter = second->begin();
138 (second_iter != second->end()) && (result != MatchResult::MRTrue);
141 std::string *modified_value = applyModifierFunction(&(*second_iter));
142 //Value was not an URI, it will be removed from the string bag (ignored)
143 if (modified_value == NULL) {
147 for (std::list<std::string>::const_iterator first_iter = first->begin();
148 first_iter != first->end();
151 if ((*comparator)(&(*first_iter), modified_value) == MatchResult::MRTrue) {
152 result = MatchResult::MRTrue;
153 break; //Only one match is enough
156 if (modified_value) {
157 delete modified_value;
158 modified_value = NULL;
162 if (result == MatchResult::MRTrue) {
163 LogDebug("Returning TRUE");
164 } else if (result == MatchResult::MRFalse) {
165 LogDebug("Returning FALSE");
166 } else if (result == MatchResult::MRUndetermined) {
167 LogDebug("Returning UNDETERMINED");
172 std::string * Attribute::applyModifierFunction(const std::string * val) const
174 std::string * result = NULL;
175 switch (modifierFunction) {
176 case Modifier::Scheme:
177 result = uriScheme(val);
179 case Modifier::Authority:
180 result = uriAuthority(val);
182 case Modifier::SchemeAuthority:
183 result = uriSchemeAuthority(val);
186 result = uriHost(val);
189 result = uriPath(val);
192 result = new std::string(*val);
199 * this - attribute obtained from xmlPolicy tree
200 * attribute - attribute obtained from PIP
202 Attribute::MatchResult Attribute::matchAttributes(
203 const BaseAttribute *attribute) const
205 std::string tempNam = *(attribute->getName());
209 if (!(attribute->getValue()->empty())) {
210 tempVal = attribute->getValue()->front();
213 if (!(this->value.empty())) {
214 myVal = this->value.front();
217 LogDebug("Comparing attribute: " << this->m_name << "(" <<
218 myVal << ") with: " << tempNam <<
219 "(" << tempVal << ")");
222 (this->m_name == *(attribute->getName())) &&
223 "Two completely different attributes are being compared!");
225 (this->m_typeId == attribute->getType()) &&
226 "Two completely different attributes are being compared!");
228 if (attribute->isUndetermind()) {
229 LogDebug("Attribute match undetermined");
230 return MatchResult::MRUndetermined;
233 //Regardles the algorithm used, if we have empty
234 //bag the result is always false
235 if (this->isValueEmpty() || attribute->isValueEmpty()) {
236 if (this->isValueEmpty()) {
237 LogDebug("empty bag in condition comparing");
239 if (attribute->isValueEmpty()) {
240 LogDebug("empty bag in attribute comparing");
242 return MatchResult::MRFalse;
245 if (this->matchFunction == Match::Equal) {
246 return lists_comparator(&(this->value),
247 attribute->getValue(),
249 } else if (this->matchFunction == Match::Glob) {
250 return lists_comparator(&(this->value),
251 attribute->getValue(),
253 } else if (this->matchFunction == Match::Regexp) {
254 return lists_comparator(&(this->value),
255 attribute->getValue(),
257 } //[CR] Change to Assert
258 Assert(false && " ** Critical :: no match function selected!");
259 return MatchResult::MRFalse; // to remove compilator warning
262 void Attribute::addValue(const std::string *val)
264 this->getValue()->push_back(*val);
267 std::ostream & operator<<(std::ostream & out,
268 const Attribute & attr)
270 out << "attr: m_name: " << *(attr.getName())
271 << " type: " << Attribute::typeToString(attr.getType())
273 if (attr.m_undetermindState) {
274 out << "Undetermined";
275 } else if (attr.getValue()->empty()) {
276 out << "Empty string bag";
278 FOREACH (it, *attr.getValue()) {
286 Attribute::parse(const std::string *input,
287 std::string *val) const
289 static const char *pattern =
290 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?";
291 pcrecpp::RE re(pattern);
292 re.FullMatch(input->c_str(), &val[0], &val[1],
293 &val[2], &val[3], &val[4],
294 &val[5], &val[6], &val[7], &val[8]);
297 for (int i = 0; i < 9; i++) {
298 LogDebug("val " << i << " :" << val[i]);
302 if (find_error(val)) {
303 LogDebug("Input is not an URI " << *input);
304 for (int i = 0; i < 9; ++i) {
313 Attribute::~Attribute()
317 std::string * Attribute::uriScheme(const std::string *input) const
320 if (!parse(input, part)) {
323 return new string(part[1]);
327 Attribute::uriAuthority(const std::string *input) const
330 if (!parse(input, part)) {
333 return new string(part[3]);
337 Attribute::uriSchemeAuthority(const std::string *input) const
340 if (!parse(input, part)) {
344 if (part[0].size() == 0 || part[2].size() == 0) {
345 return new std::string();
347 return new string(part[0] + part[2]);
351 Attribute::uriHost(const std::string *input) const
354 if (!parse(input, part)) {
357 return getHost(&(part[3]));
361 Attribute::uriPath(const std::string *input) const
363 //TODO right now uriPath leaves leading '/' in uri, this slash is removed from the string
364 //it's not clear if leading '/' is a part of path component or only the separator
366 if (!parse(input, part)) {
370 std::string * temp = NULL;
372 if (part[4].at(0) == '/') {
373 temp = new string(part[4].substr(1, part[4].length() - 1));
375 temp = new string(part[4]);
381 bool Attribute::find_error(const std::string *tab) const
383 //We are checking tab[1] which contains scheme without ':' at the end
384 if (!checkScheme(&(tab[1]))) {
385 LogDebug("Check scheme failed, URI is invalid");
386 return true; //error found
388 if (!checkAuthority(&(tab[3]))) {
389 LogDebug("Check authority failed, URI is invalid");
390 return true; //error found
393 if (!checkPath(&(tab[4]))) {
394 LogDebug("Check path failed, URI is invalid");
395 return true; //error found
401 bool Attribute::checkScheme(const std::string *part) const
403 Assert(part != NULL && "Checking NULLable string. This should never happen");
407 //TODO change part->at to data=part->c_str()
408 //TODO can scheme be empty? In absolute URI no, in relative URI yes
410 //Empty string is a correct schema
412 } else if (alpha[(int) (part->at(0))] == 0) {
413 result = false; // First scheme character must be alpha
415 // rest must be alpha or digit or '+' or '-' or '.'
416 for (unsigned int i = 1; i < part->size(); ++i) {
417 int c = static_cast<int>(part->at(i));
418 if (!isSchemeAllowedCharacter(c)) {
427 bool Attribute::checkAuthority(const std::string *part) const
429 Assert(part != NULL && "Checking NULLable string. This should never happen");
431 //Server is a subset of reg_m_names so here we only check if authority matches reg_m_name
432 //Additional check if authority is a valid 'server' component is done in getHost
434 return true; //empty authority is valid uri
438 const char * data = part->c_str();
439 for (size_t i = 0; i < part->length(); ++i) {
440 int c = (int) data[i];
441 if (isUnreserved(c)) {
469 if (isEscaped(data + i)) {
470 i += 2; //rewind the two escaped characters
481 std::string * Attribute::getHost(const std::string *part) const
484 return new std::string("");
488 size_t userInfoPos = part->find("@");
489 if (userInfoPos != std::string::npos) {
490 std::string data = part->substr(0, userInfoPos);
491 if (!isUserInfoAllowedString(&data)) {
492 return new string(""); //the authority is not composed of 'server' part
497 //If we use host modifier then authority is composed of 'server' part so
498 //the port must contain only digits
499 size_t portPos = part->find(":");
500 if (portPos != std::string::npos) {
501 for (unsigned int i = portPos + 1; i < part->size(); ++i) {
502 if (!digit[(int) part->at(i)]) {
503 return new string(""); //the authority is not composed of 'server' part
506 host = part->substr(userInfoPos + 1, portPos - (userInfoPos + 1));
508 host = part->substr(userInfoPos + 1, part->length() - (userInfoPos + 1));
511 if (!isHostAllowedString(&host)) {
512 //Even if the string is not allowed for host this can still be a valid uri
513 return new string("");
516 return new std::string(host);
519 bool Attribute::checkPath(const std::string *part) const
523 const char * data = part->c_str();
525 for (unsigned int i = 0; i < part->size(); ++i) {
528 //If we found slash then the next character must be a part of segment
529 //It cannot be '/' so we have to check it immediately
532 if (!isSegmentAllowedCharacter(c)) {
536 } else if (c == ';') {
537 //Start param part of segment
538 i++; //Param can be empty so we don't have to check what's right after semicolon
540 } else if (c == '%') {
541 //We have to handle escaped characters differently than other segment allowed characters
542 //because we need an array
543 if (isEscaped(data + i)) {
550 if (!isSegmentAllowedCharacter(c)) {
560 bool Attribute::isSchemeAllowedCharacter(int c) const
565 } else if (c == '+') {
567 } else if (c == '-') {
569 } else if (c == '.') {
576 bool Attribute::isSegmentAllowedCharacter(int c) const
580 // LogDebug("Checking is segment allowed for char "<<(char)c);
582 if (isUnreserved(c)) { //do nothing, result = true
583 } else if (c == ':') { //do nothing, result = true
584 } else if (c == '@') { //do nothing, result = true
585 } else if (c == '&') { //do nothing, result = true
586 } else if (c == '=') { //do nothing, result = true
587 } else if (c == '+') { //do nothing, result = true
588 } else if (c == '$') { //do nothing, result = true
589 } else if (c == ',') { //do nothing, result = true
597 bool Attribute::isUserInfoAllowedString(const std::string * str) const
601 const char * data = str->c_str();
603 for (unsigned int i = 0; i < str->length(); ++i) {
605 if (isUnreserved(c)) {
607 } else if (c == '%') {
608 //isEsacped method checks if we don't cross array bounds, so we can
609 //safely give data[i] here
610 result = isEscaped((data + i));
611 if (result == false) {
614 i += 2; //rewind the next two characters sEsacped method checks if we don't cross array bounds, so we can safely rewind
615 } else if (c == ',') {
617 } else if (c == '$') {
619 } else if (c == '+') {
621 } else if (c == '=') {
623 } else if (c == '&') {
625 } else if (c == '@') {
627 } else if (c == ':') {
634 bool Attribute::isUnreserved(int c) const
636 return isAlphanum(c) || mark[c];
639 bool Attribute::isAlphanum(int c) const
641 return alpha[c] || digit[c];
644 bool Attribute::isHex(int c) const
650 } else if (c == 'A') {
652 } else if (c == 'B') {
654 } else if (c == 'C') {
656 } else if (c == 'D') {
658 } else if (c == 'E') {
660 } else if (c == 'F') {
662 } else if (c == 'a') {
664 } else if (c == 'b') {
666 } else if (c == 'c') {
668 } else if (c == 'd') {
670 } else if (c == 'e') {
672 } else if (c == 'f') {
679 bool Attribute::isEscaped(const char esc[3]) const
685 if ((esc[0] == 0) || (esc[1] == 0) || (esc[2] == 0)) {
686 //We get an array that seems to be out of bounds.
687 //To be on the safe side return here
688 LogDebug("HEX NULLS");
694 "Error: first character of escaped value must be a precent but is "
701 for (int i = 0; i < 3; i++) {
702 LogDebug("HEX " << esc[i]);
705 return isHex((int) esc[1]) && isHex((int) esc[2]);
708 bool Attribute::isHostAllowedString(const std::string * str) const
712 if (digit[(int) str->at(0)]) {
714 result = isIPv4AllowedString(str);
717 result = isHostNameAllowedString(str);
723 bool Attribute::isIPv4AllowedString(const std::string * str) const
725 LogDebug("Is hostIPv4 allowed String for " << *str);
727 const char * data = str->c_str();
729 int digitCounter = 0;
732 for (unsigned int i = 0; i < str->length(); ++i) {
733 if (data[i] == '.') {
736 } else if (digit[(int) data[i]]) {
738 if ((digitCounter > 3) || !digitCounter) {
747 if (dotCounter != 3) {
753 bool Attribute::isHostNameAllowedString(const std::string * str) const
755 LogDebug("Is hostname allowed String for " << *str);
757 int lastPosition = 0; //the position of last dot + 1
758 const char * data = str->c_str();
759 bool finalDot = false;
760 size_t end = str->length();
763 for (size_t i = 0; i < end; ++i) {
764 if (data[i] == '.') {
765 if (i == str->length() - 1) { //ending dot
766 //There can be a leading '.' int the hostm_name
770 //we found domain label
771 if (!isDomainLabelAllowedString(data + lastPosition, i -
776 lastPosition = i + 1; //Set position to position of last dot + 1
782 //we have to rewind one position to check the rightmost string
783 //but only in case we find final dot
786 //Compare only the rightmost string aaa.bbbb.rightmostString.
787 result = isTopLabelAllowedString(data + lastPosition, end - lastPosition);
792 LogInfo("Hostname is allowed");
794 LogInfo("Hostname is NOT allowed");
800 bool Attribute::isDomainLabelAllowedString(const char * data,
804 "Is domain allowed String for " << data << " taking first " <<
808 if (!isAlphanum((int) data[0]) || !isAlphanum((int) data[length - 1])) {
812 for (int i = 0; i < length; i++) {
813 if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
820 bool Attribute::isTopLabelAllowedString(const char * data,
823 if ((!alpha[(int) data[0]]) || (!isAlphanum((int) data[length - 1]))) {
827 for (int i = 1; i < length - 1; i++) {
828 if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
835 void printAttributes(const AttributeSet& attrs)
838 LogWarning("Empty attribute set");
840 LogDebug("PRINT ATTRIBUTES:");
841 for (AttributeSet::const_iterator it = attrs.begin();
845 LogDebug("name: " << *(*it)->getName());
850 void printAttributes(const std::list<Attribute> & attrs)
853 LogWarning("Empty attribute set");
855 LogDebug("PRINT ATTRIBUTES:");
856 for (std::list<Attribute>::const_iterator it = attrs.begin();
865 //KW const char * matchResultToString(Attribute::MatchResult result){
867 //KW const char * ret = NULL;
871 //KW case Attribute::MRTrue:
874 //KW case Attribute::MRFalse:
877 //KW case Attribute::MRUndetermined:
878 //KW ret = "undetermined";
881 //KW ret = "Wrong match result";