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 if (!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])) {
295 LogDebug("Error: attribute parsing failed.");
299 for (int i = 0; i < 9; i++) {
300 LogDebug("val " << i << " :" << val[i]);
304 if (find_error(val)) {
305 LogDebug("Input is not an URI " << *input);
306 for (int i = 0; i < 9; ++i) {
315 Attribute::~Attribute()
319 std::string * Attribute::uriScheme(const std::string *input) const
322 if (!parse(input, part)) {
325 return new string(part[1]);
329 Attribute::uriAuthority(const std::string *input) const
332 if (!parse(input, part)) {
335 return new string(part[3]);
339 Attribute::uriSchemeAuthority(const std::string *input) const
342 if (!parse(input, part)) {
346 if (part[0].size() == 0 || part[2].size() == 0) {
347 return new std::string();
349 return new string(part[0] + part[2]);
353 Attribute::uriHost(const std::string *input) const
356 if (!parse(input, part)) {
359 return getHost(&(part[3]));
363 Attribute::uriPath(const std::string *input) const
365 //TODO right now uriPath leaves leading '/' in uri, this slash is removed from the string
366 //it's not clear if leading '/' is a part of path component or only the separator
368 if (!parse(input, part)) {
372 std::string * temp = NULL;
374 if (part[4].at(0) == '/') {
375 temp = new string(part[4].substr(1, part[4].length() - 1));
377 temp = new string(part[4]);
383 bool Attribute::find_error(const std::string *tab) const
385 //We are checking tab[1] which contains scheme without ':' at the end
386 if (!checkScheme(&(tab[1]))) {
387 LogDebug("Check scheme failed, URI is invalid");
388 return true; //error found
390 if (!checkAuthority(&(tab[3]))) {
391 LogDebug("Check authority failed, URI is invalid");
392 return true; //error found
395 if (!checkPath(&(tab[4]))) {
396 LogDebug("Check path failed, URI is invalid");
397 return true; //error found
403 bool Attribute::checkScheme(const std::string *part) const
405 Assert(part != NULL && "Checking NULLable string. This should never happen");
409 //TODO change part->at to data=part->c_str()
410 //TODO can scheme be empty? In absolute URI no, in relative URI yes
412 //Empty string is a correct schema
414 } else if (alpha[(int) (part->at(0))] == 0) {
415 result = false; // First scheme character must be alpha
417 // rest must be alpha or digit or '+' or '-' or '.'
418 for (unsigned int i = 1; i < part->size(); ++i) {
419 int c = static_cast<int>(part->at(i));
420 if (!isSchemeAllowedCharacter(c)) {
429 bool Attribute::checkAuthority(const std::string *part) const
431 Assert(part != NULL && "Checking NULLable string. This should never happen");
433 //Server is a subset of reg_m_names so here we only check if authority matches reg_m_name
434 //Additional check if authority is a valid 'server' component is done in getHost
436 return true; //empty authority is valid uri
440 const char * data = part->c_str();
441 for (size_t i = 0; i < part->length(); ++i) {
442 int c = (int) data[i];
443 if (isUnreserved(c)) {
471 if (isEscaped(data + i)) {
472 i += 2; //rewind the two escaped characters
483 std::string * Attribute::getHost(const std::string *part) const
486 return new std::string("");
490 size_t userInfoPos = part->find("@");
491 if (userInfoPos != std::string::npos) {
492 std::string data = part->substr(0, userInfoPos);
493 if (!isUserInfoAllowedString(&data)) {
494 return new string(""); //the authority is not composed of 'server' part
499 //If we use host modifier then authority is composed of 'server' part so
500 //the port must contain only digits
501 size_t portPos = part->find(":");
502 if (portPos != std::string::npos) {
503 for (unsigned int i = portPos + 1; i < part->size(); ++i) {
504 if (!digit[(int) part->at(i)]) {
505 return new string(""); //the authority is not composed of 'server' part
508 host = part->substr(userInfoPos + 1, portPos - (userInfoPos + 1));
510 host = part->substr(userInfoPos + 1, part->length() - (userInfoPos + 1));
513 if (!isHostAllowedString(&host)) {
514 //Even if the string is not allowed for host this can still be a valid uri
515 return new string("");
518 return new std::string(host);
521 bool Attribute::checkPath(const std::string *part) const
525 const char * data = part->c_str();
527 for (unsigned int i = 0; i < part->size(); ++i) {
530 //If we found slash then the next character must be a part of segment
531 //It cannot be '/' so we have to check it immediately
534 if (!isSegmentAllowedCharacter(c)) {
538 } else if (c == ';') {
539 //Start param part of segment
540 i++; //Param can be empty so we don't have to check what's right after semicolon
542 } else if (c == '%') {
543 //We have to handle escaped characters differently than other segment allowed characters
544 //because we need an array
545 if (isEscaped(data + i)) {
552 if (!isSegmentAllowedCharacter(c)) {
562 bool Attribute::isSchemeAllowedCharacter(int c) const
567 } else if (c == '+') {
569 } else if (c == '-') {
571 } else if (c == '.') {
578 bool Attribute::isSegmentAllowedCharacter(int c) const
582 // LogDebug("Checking is segment allowed for char "<<(char)c);
584 if (isUnreserved(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
590 } else if (c == '$') { //do nothing, result = true
591 } else if (c == ',') { //do nothing, result = true
599 bool Attribute::isUserInfoAllowedString(const std::string * str) const
603 const char * data = str->c_str();
605 for (unsigned int i = 0; i < str->length(); ++i) {
607 if (isUnreserved(c)) {
609 } else if (c == '%') {
610 //isEsacped method checks if we don't cross array bounds, so we can
611 //safely give data[i] here
612 result = isEscaped((data + i));
613 if (result == false) {
616 i += 2; //rewind the next two characters sEsacped method checks if we don't cross array bounds, so we can safely rewind
617 } else if (c == ',') {
619 } else if (c == '$') {
621 } else if (c == '+') {
623 } else if (c == '=') {
625 } else if (c == '&') {
627 } else if (c == '@') {
629 } else if (c == ':') {
636 bool Attribute::isUnreserved(int c) const
638 return isAlphanum(c) || mark[c];
641 bool Attribute::isAlphanum(int c) const
643 return alpha[c] || digit[c];
646 bool Attribute::isHex(int c) const
652 } else if (c == 'A') {
654 } else if (c == 'B') {
656 } else if (c == 'C') {
658 } else if (c == 'D') {
660 } else if (c == 'E') {
662 } else if (c == 'F') {
664 } else if (c == 'a') {
666 } else if (c == 'b') {
668 } else if (c == 'c') {
670 } else if (c == 'd') {
672 } else if (c == 'e') {
674 } else if (c == 'f') {
681 bool Attribute::isEscaped(const char esc[3]) const
687 if ((esc[0] == 0) || (esc[1] == 0) || (esc[2] == 0)) {
688 //We get an array that seems to be out of bounds.
689 //To be on the safe side return here
690 LogDebug("HEX NULLS");
696 "Error: first character of escaped value must be a precent but is "
703 for (int i = 0; i < 3; i++) {
704 LogDebug("HEX " << esc[i]);
707 return isHex((int) esc[1]) && isHex((int) esc[2]);
710 bool Attribute::isHostAllowedString(const std::string * str) const
714 if (digit[(int) str->at(0)]) {
716 result = isIPv4AllowedString(str);
719 result = isHostNameAllowedString(str);
725 bool Attribute::isIPv4AllowedString(const std::string * str) const
727 LogDebug("Is hostIPv4 allowed String for " << *str);
729 const char * data = str->c_str();
731 int digitCounter = 0;
734 for (unsigned int i = 0; i < str->length(); ++i) {
735 if (data[i] == '.') {
738 } else if (digit[(int) data[i]]) {
740 if ((digitCounter > 3) || !digitCounter) {
749 if (dotCounter != 3) {
755 bool Attribute::isHostNameAllowedString(const std::string * str) const
757 LogDebug("Is hostname allowed String for " << *str);
759 int lastPosition = 0; //the position of last dot + 1
760 const char * data = str->c_str();
761 bool finalDot = false;
762 size_t end = str->length();
765 for (size_t i = 0; i < end; ++i) {
766 if (data[i] == '.') {
767 if (i == str->length() - 1) { //ending dot
768 //There can be a leading '.' int the hostm_name
772 //we found domain label
773 if (!isDomainLabelAllowedString(data + lastPosition, i -
778 lastPosition = i + 1; //Set position to position of last dot + 1
784 //we have to rewind one position to check the rightmost string
785 //but only in case we find final dot
788 //Compare only the rightmost string aaa.bbbb.rightmostString.
789 result = isTopLabelAllowedString(data + lastPosition, end - lastPosition);
794 LogInfo("Hostname is allowed");
796 LogInfo("Hostname is NOT allowed");
802 bool Attribute::isDomainLabelAllowedString(const char * data,
806 "Is domain allowed String for " << data << " taking first " <<
810 if (!isAlphanum((int) data[0]) || !isAlphanum((int) data[length - 1])) {
814 for (int i = 0; i < length; i++) {
815 if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
822 bool Attribute::isTopLabelAllowedString(const char * data,
825 if ((!alpha[(int) data[0]]) || (!isAlphanum((int) data[length - 1]))) {
829 for (int i = 1; i < length - 1; i++) {
830 if ((!isAlphanum(data[i])) && !(data[i] == '-')) {
837 void printAttributes(const AttributeSet& attrs)
840 LogWarning("Empty attribute set");
842 LogDebug("PRINT ATTRIBUTES:");
843 for (AttributeSet::const_iterator it = attrs.begin();
847 LogDebug("name: " << *(*it)->getName());
852 void printAttributes(const std::list<Attribute> & attrs)
855 LogWarning("Empty attribute set");
857 LogDebug("PRINT ATTRIBUTES:");
858 for (std::list<Attribute>::const_iterator it = attrs.begin();
867 //KW const char * matchResultToString(Attribute::MatchResult result){
869 //KW const char * ret = NULL;
873 //KW case Attribute::MRTrue:
876 //KW case Attribute::MRFalse:
879 //KW case Attribute::MRUndetermined:
880 //KW ret = "undetermined";
883 //KW ret = "Wrong match result";