Tizen 2.1 base
[framework/security/security-server.git] / ace / engine / parser.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include <memory>
17 #include <functional>
18 #include <string.h>
19 #include <stdarg.h>
20 #include <dpl/log/log.h>
21
22 #include <ace/parser.h>
23 #include <string.h>
24
25 namespace {
26
27 class ParserWarningLogger
28 {
29   public:
30     void operator()(const std::string& logMsg)
31     {
32         LogWarning(logMsg);
33     }
34 };
35
36 class ParserErrorLogger
37 {
38   public:
39     void operator()(const std::string& logMsg)
40     {
41         LogError(logMsg);
42     }
43 };
44
45 template <class Logger>
46 void xmlLogFunction(void* /*ctx*/, const char *msg, ...)
47 {
48     const int BUFFER_SIZE = 1024;
49     char buffer[BUFFER_SIZE];
50     buffer[BUFFER_SIZE - 1] = '\0';
51     Logger l;
52
53     va_list va;
54     va_start(va, msg);
55     vsnprintf(buffer, BUFFER_SIZE - 1, msg, va);
56     va_end(va);
57
58     std::string logmsg(buffer);
59     l(logmsg);
60 }
61
62 }
63
64 const char *Parser::TOKEN_PARAM = "param:";
65
66 Parser::Parser() :
67     ruleId(0),
68     reader(NULL),
69     root(NULL),
70     currentRoot(NULL),
71     currentSubject(NULL),
72     currentCondition(NULL),
73     currentAttribute(NULL),
74     currentText(NULL),
75     processingSignature(false),
76     canonicalizeOnce(false)
77 {
78     processingSignature = true;
79     canonicalizeOnce = true;
80 }
81
82 Parser::~Parser()
83 {
84     /* parse function destroys reader */
85     //  free(this->xmlFilename);
86 }
87
88 TreeNode* Parser::parse(const std::string& filename, const std::string& schema)
89 {
90     if(root != NULL) {
91         root->releaseResources();
92         root = NULL;
93     }
94
95     LogDebug("Parser: opening file " << filename);
96
97     xmlDocPtr xmlDocument = xmlParseFile(filename.c_str());
98     if (!xmlDocument) {
99         LogError("Couldn't parse file " << filename);
100         return root;
101     }
102
103     std::unique_ptr <xmlDoc, std::function<void(xmlDoc*)> >
104         doc(xmlDocument, xmlFreeDoc);
105
106     xmlSchemaParserCtxtPtr xmlSchemaParserContext =
107         xmlSchemaNewParserCtxt(schema.c_str());
108
109     if (!xmlSchemaParserContext) {
110         LogError("Couldn't load xml schema: " << schema);
111         return root;
112     }
113
114     std::unique_ptr <
115                      xmlSchemaParserCtxt,
116                      std::function<void(xmlSchemaParserCtxt*)> >
117                      schemaContext(
118                                    xmlSchemaParserContext,
119                                    xmlSchemaFreeParserCtxt);
120
121     LogDebug("Setting callbacks");
122
123     xmlSchemaSetParserErrors(
124         schemaContext.get(),
125         static_cast<xmlValidityErrorFunc>
126             (&xmlLogFunction<ParserErrorLogger>),
127         static_cast<xmlValidityWarningFunc>
128             (&xmlLogFunction<ParserWarningLogger>),
129         NULL);
130
131     xmlSchemaPtr xmlSchema = xmlSchemaParse(schemaContext.get());
132
133     if (!xmlSchema) {
134         LogError("Couldn't parse xml schema: " << xmlSchema);
135         return root;
136     }
137
138     xmlSchemaValidCtxtPtr xmlValidContext = xmlSchemaNewValidCtxt(xmlSchema);
139
140     if (!xmlValidContext) {
141         LogError("Couldn't create validation context!");
142         return root;
143     }
144
145     std::unique_ptr <
146                      xmlSchemaValidCtxt,
147                      std::function<void(xmlSchemaValidCtxt*)> >
148                      schemaValidContext(
149                                         xmlValidContext,
150                                         xmlSchemaFreeValidCtxt);
151
152     xmlSchemaSetValidErrors(
153         schemaValidContext.get(),
154         static_cast<xmlValidityErrorFunc>
155             (&xmlLogFunction<ParserErrorLogger>),
156         static_cast<xmlValidityWarningFunc>
157             (&xmlLogFunction<ParserWarningLogger>),
158         NULL);
159
160     xmlSchemaSetValidOptions(
161                              schemaValidContext.get(),
162                              XML_SCHEMA_VAL_VC_I_CREATE);
163
164     bool result =
165         (xmlSchemaValidateDoc(
166                               schemaValidContext.get(),
167                               xmlDocument) == 0 ? true : false);
168
169     if (!result) {
170         LogError("Couldn't validate policy file: " << filename <<
171                  " against xml schema: " << schema);
172
173         return root;
174     }
175
176     LogInfo("Policy file: " << filename << " validated!");
177
178     xmlTextReaderPtr xmlReader = xmlReaderWalker(xmlDocument);
179
180     //[CR] consider using ASSERT/DASSERT
181     if (NULL == xmlReader) {
182         LogError("Error, xml reader cannot be created. Probably xml file is missing (opening file " << filename << ")");
183         return root;
184     }
185
186     std::unique_ptr <xmlTextReader, std::function<void(xmlTextReader*)> >
187          reader(xmlReader, xmlFreeTextReader);
188
189     int ret;
190     ret = xmlTextReaderRead(reader.get());
191     while (ret == 1) {
192         std::unique_ptr<xmlChar, std::function<void(xmlChar*)> >
193             name(xmlTextReaderName(reader.get()), xmlFree);
194
195         if (!strcmp("policy-set", (const char *)name.get())) {
196             processingSignature = false;
197         } else if (!strcmp("SignedInfo",
198                            (const char *)name.get()) && canonicalizeOnce) {
199             #if 0 //TODO I think we don't need canonicalization in ACE only in PM,
200             //we have to  verify it tough
201             extractNodeToFile(reader, "output.xml");
202             //TODO we should be able to handle more than one canonicalization algorithm
203             canonicalize("output.xml", "canon.xml", Canonicalization::C14N);
204             canonicalizeOnce = false;
205             #endif
206         }
207         //Do not process signature of xml file
208         if(!processingSignature) {
209             processNode(reader.get());
210         }
211         ret = xmlTextReaderRead(reader.get());
212     }
213
214     if (ret != 0) {
215         LogError("Error while parsing XML file");
216         if (root) {
217             root->releaseResources();
218             root = NULL;
219         }
220     }
221
222     return root;
223 }
224
225 void Parser::processNode(xmlTextReaderPtr reader)
226 {
227     //TODO this is interesting, xmlTextReaderNodeType returns int but I am pretty sure
228     //those integers coresponds to xmlReaderTypes
229     xmlReaderTypes type =
230         static_cast<xmlReaderTypes>(xmlTextReaderNodeType(reader));
231
232     switch (type) {
233     //Start element
234     case XML_READER_TYPE_ELEMENT:
235         startNodeHandler(reader);
236         break;
237     //End element
238     case XML_READER_TYPE_END_ELEMENT:
239         endNodeHandler(reader);
240         break;
241     //Text element
242     case XML_READER_TYPE_TEXT:
243         textNodeHandler(reader);
244         break;
245     default:
246         //Do not handle other xml tags
247         break;
248     }
249 }
250
251 void Parser::startNodeHandler(xmlTextReaderPtr reader)
252 {
253     xmlChar *name = xmlTextReaderName(reader);
254
255     switch (*name) {
256     case 'p':     //policy and policy-set
257         if (*(name + 6) == 0) {
258             handlePolicy(reader, TreeNode::Policy);
259         } else {
260             handlePolicy(reader, TreeNode::PolicySet);
261         }
262         break;
263     case 'r':     //rule and resource-match
264         if (*(name + 1) == 'u') {
265             handleRule(reader);
266         } else if (*(name + 9) == 'm') {
267             handleMatch(reader, Attribute::Type::Resource);
268         } else {
269             handleAttr(reader);
270         }
271         break;
272     case 's':     //subject and subject-match
273         if (*(name + 7) == 0) {
274             handleSubject();
275         } else if (*(name + 8) == 'm') { //subject match
276             handleSubjectMatch(reader);
277         } else {  //subject attr
278             handleAttr(reader);
279         }
280         break;
281     case 'c':    //condition
282         handleCondition(reader);
283         break;
284     case 'e':    //environment-match
285         if (*(name + 12) == 'm') {
286             handleMatch(reader, Attribute::Type::Environment);
287         } else {  //env-attr
288             handleAttr(reader);
289         }
290         break;
291     }
292     xmlFree(name);
293 }
294
295 void Parser::endNodeHandler(xmlTextReaderPtr reader)
296 {
297     xmlChar *name = xmlTextReaderName(reader);
298
299     switch (*name) {
300     case 'p':     //policy and policy-set
301         //Restore old root
302         currentRoot = currentRoot->getParent();
303         break;
304     case 'r':     //Rule and resource match
305         if (*(name + 1) == 'u') { //Rule
306             currentRoot = currentRoot->getParent();
307         } else {  //Resource-match
308             consumeCurrentText();     //consume text if any available
309             consumeCurrentAttribute();     //consume attribute
310         }
311         break;
312     case 's':     //subject and subject-match
313         if (*(name + 7) == 0) { //handle subject
314             consumeCurrentSubject();
315         } else if (*(name + 8) == 'm') { //handle subject match
316             consumeCurrentText();
317             consumeSubjectMatch();
318         }
319         //Subject-match end doesn't require handling
320         break;
321     case 'c':    //condition
322         consumeCurrentCondition();
323         break;
324     case 'e':    //environment-match
325         consumeCurrentText();     //consume text if any available
326         consumeCurrentAttribute();     //consume attribute
327         break;
328     }
329     xmlFree(name);
330 }
331
332 void Parser::textNodeHandler(xmlTextReaderPtr reader)
333 {
334     delete currentText;
335     xmlChar * text = xmlTextReaderValue(reader);
336     Assert(text != NULL && "Parser couldn't parse PCDATA");
337
338     currentText = new std::string(reinterpret_cast<const char * >(text));
339     trim(currentText);
340     xmlFree(text);
341 }
342
343 void Parser::handlePolicy(xmlTextReaderPtr reader,
344         TreeNode::TypeID type)
345 {
346     Policy::CombineAlgorithm algorithm;
347
348     //Get first attribute
349     xmlChar * combAlg = xmlTextReaderGetAttribute(reader, BAD_CAST("combine"));
350
351     Assert(combAlg != NULL && "Parser error while getting attributes");
352     algorithm = convertToCombineAlgorithm(combAlg);
353
354     //Create TreeNode element
355     Policy * policy = NULL;
356     if (type == TreeNode::Policy) {
357         policy = new Policy();
358     } else {
359         policy = new PolicySet();
360     }
361     policy->setCombineAlgorithm(algorithm);
362     TreeNode * node = new TreeNode(currentRoot, type, policy);
363     //Add new tree node to current's root children set
364     if (currentRoot != NULL) {
365         currentRoot->addChild(node);
366     }
367
368     //Switch the current root to the new node
369     if (!xmlTextReaderIsEmptyElement(reader)) {
370         //Current root switching is necessary only if tag is not empty
371         currentRoot = node;
372     }
373     if (root == NULL) {
374         root = currentRoot;
375     }
376
377     if (NULL == currentRoot) {
378         node->releaseResources();
379     }
380
381     xmlFree(combAlg);
382 }
383
384 void Parser::handleRule(xmlTextReaderPtr reader)
385 {
386     ExtendedEffect effect(Inapplicable);
387
388     //[CR] create macros for attribute names
389     xmlChar * eff = xmlTextReaderGetAttribute(reader, BAD_CAST("effect")); //get the rule attribute
390
391     Assert(eff != NULL && "Parser error while getting attributes");
392     effect = convertToEffect(eff);
393
394     Rule * rule = NULL;
395     rule = new Rule();
396     rule->setEffect(effect);
397
398     TreeNode * node = new TreeNode(currentRoot, TreeNode::Rule, rule);
399     //Add new tree node to current's root children set
400     if (currentRoot != NULL) { //
401         currentRoot->addChild(node);
402     }
403
404     if (!xmlTextReaderIsEmptyElement(reader)) {
405         currentRoot = node;
406     }
407
408     if (NULL == currentRoot) {
409         node->releaseResources();
410     }
411
412     xmlFree(eff);
413 }
414
415 void Parser::handleSubject()
416 {
417     currentSubject = new Subject();
418     //TODO what about empty subject tag
419 }
420
421 void Parser::handleCondition(xmlTextReaderPtr reader)
422 {
423     Condition::CombineType combineType = Condition::AND;
424
425     xmlChar * combine = xmlTextReaderGetAttribute(reader, BAD_CAST("combine")); //get the rule attribute
426
427     Assert(combine != NULL && "Parser error while getting attributes");
428
429     combineType = *combine == 'a' ? Condition::AND : Condition::OR;
430
431     Condition * condition = new Condition();
432     condition->setCombineType(combineType);
433     condition->setParent(currentCondition);
434
435     currentCondition = condition;
436     //TODO what about empty condition tag?
437 }
438
439 //Subject match is handled differently than resource or environment match
440 //Because it cannot have any children tags and can only include PCDATA
441 void Parser::handleSubjectMatch(xmlTextReaderPtr reader)
442 {
443     //processing Subject
444     int attributes = xmlTextReaderAttributeCount(reader);
445
446     xmlChar * func = NULL;
447     xmlChar * value = NULL;
448     xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
449
450     if (attributes == 2) {
451         //match attribute ommited, text value will be used
452         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
453     } else if (attributes == 3) {
454         value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
455         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
456     } else {
457         Assert(false && "Wrong XML file format");
458     }
459
460     // creating temporiary object is not good idea
461     // but we have no choice untill Attribute have constructor taking std::string*
462     std::string temp(reinterpret_cast<const char *>(attrName));
463     Attribute * attr = new Attribute(&temp, convertToMatchFunction(
464                                          func), Attribute::Type::Subject);
465     if (value != NULL) { //add value of the attribute if possible
466         //[CR] consider create Attribute::addValue(char *) function
467         std::string temp(reinterpret_cast<const char *>(value));
468         attr->addValue(&temp);
469     }
470     currentAttribute = attr;
471
472     if (xmlTextReaderIsEmptyElement(reader)) {
473         Assert(value != NULL && "XML file format is wrong");
474         //Attribute value is required to obtain the match value easier
475         consumeSubjectMatch(value);
476     }
477
478     if (attributes == 2 || attributes == 3) {
479         xmlFree(func);
480     }
481     xmlFree(value);
482     xmlFree(attrName);
483 }
484
485 void Parser::handleMatch(xmlTextReaderPtr reader,
486         Attribute::Type type)
487 {
488     int attributes = xmlTextReaderAttributeCount(reader);
489
490     xmlChar * func = NULL;
491     xmlChar * value = NULL;
492     xmlChar * attrName = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
493
494     if (attributes == 2) {
495         //match attribute ommited, text value will be used
496         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
497         //the content may be resource-attr or PCDATA
498     } else if (attributes == 3) {
499         value = xmlTextReaderGetAttribute(reader, BAD_CAST("match"));
500         func = xmlTextReaderGetAttribute(reader, BAD_CAST("func"));
501     } else {
502         Assert(false && "Wrong XML file format");
503     }
504
505     // FunctionParam type is sybtype of Resource.
506     // FunctionParam is used to storage attriburess of call functions.
507     if (0 ==
508         xmlStrncmp(attrName, BAD_CAST(TOKEN_PARAM),
509                    xmlStrlen(BAD_CAST(TOKEN_PARAM))) && type ==
510         Attribute::Type::Resource) {
511         type = Attribute::Type::FunctionParam;
512     }
513
514     std::string temp(reinterpret_cast<const char*>(attrName));
515     Attribute * attr = new Attribute(&temp, convertToMatchFunction(func), type);
516     currentAttribute = attr;
517
518     if (xmlTextReaderIsEmptyElement(reader)) {
519         Assert(value != NULL && "XML is currupted");
520         std::string tempVal(reinterpret_cast<const char*>(value));
521         currentAttribute->addValue(&tempVal);
522         consumeCurrentAttribute();
523     }
524
525     if (attributes == 2 || attributes == 3) {
526         xmlFree(func);
527     }
528     xmlFree(value);
529     xmlFree(attrName);
530 }
531
532 Policy::CombineAlgorithm Parser::convertToCombineAlgorithm(xmlChar* algorithm)
533 {
534     switch (*algorithm) {
535     case 'f':
536         if (*(algorithm + 6) == 'a') { //first applicable
537             return Policy::FirstApplicable;
538         }
539         return Policy::FirstTargetMatching;
540     case 'd':
541         return Policy::DenyOverride;
542     case 'p':
543         return Policy::PermitOverride;
544     default:
545         Assert(false && "Wrong combine algorithm name");
546         return Policy::DenyOverride;
547     }
548 }
549
550 ExtendedEffect Parser::convertToEffect(xmlChar *effect)
551 {
552     switch (*effect) {
553     case 'd':     //deny
554         return Deny;
555         break;
556     case 'p':
557         //permit, prompt-blanket, prompt-session, prompt-oneshot
558         if (*(effect + 1) == 'e') {
559             return ExtendedEffect(Permit, ruleId++);
560         }
561         switch (*(effect + 7)) {
562         case 'b':
563             return ExtendedEffect(PromptBlanket, ruleId++);
564         case 's':
565             return ExtendedEffect(PromptSession, ruleId++);
566         case 'o':
567             return ExtendedEffect(PromptOneShot, ruleId++);
568         default:
569             Assert(false && "Effect is Error");
570             return ExtendedEffect();
571         }
572         break;
573     default:
574         Assert(false && "Effect is Error");
575         return ExtendedEffect();
576     }
577     //return ExtendedEffect(Inapplicable); //unreachable statement
578 }
579
580 Attribute::Match Parser::convertToMatchFunction(xmlChar * func)
581 {
582     if (func == NULL) {
583         LogError("[ERROR] match function value is NULL");
584         return Attribute::Match::Error;
585     }
586
587     if (*func == 'g') {
588         return Attribute::Match::Glob;
589     } else if (*func == 'e') {
590         return Attribute::Match::Equal;
591     } else if (*func == 'r') {
592         return Attribute::Match::Regexp;
593     } else {
594         LogError("[ERROR] match function value is NULL");
595         return Attribute::Match::Error;
596     }
597 }
598
599 void Parser::handleAttr(xmlTextReaderPtr reader)
600 {
601     xmlChar * attrValue = xmlTextReaderGetAttribute(reader, BAD_CAST("attr")); //get the first attribute
602     Assert(attrValue != NULL && "Error while obtaining attribute");
603
604     std::string temp(reinterpret_cast<const char*>(attrValue));
605     currentAttribute->addValue(&temp);
606
607     xmlFree(attrValue);
608 }
609
610 void Parser::consumeCurrentText()
611 {
612     Assert(currentText != NULL);
613     currentAttribute->addValue(currentText);
614     delete currentText;
615
616     currentText = NULL;
617 }
618
619 void Parser::consumeCurrentAttribute()
620 {
621     Assert(currentAttribute != NULL);
622
623     currentCondition->addAttribute(*currentAttribute);
624     delete currentAttribute;
625
626     currentAttribute = NULL;
627 }
628
629 void Parser::consumeCurrentSubject()
630 {
631     Policy * policy = dynamic_cast<Policy *>(currentRoot->getElement());
632     Assert(policy != NULL);
633     policy->addSubject(currentSubject);
634     //TODO maybe keep subjects not subject pointers in Policies and consume subjects here
635     currentSubject = NULL;
636 }
637
638 void Parser::consumeCurrentCondition()
639 {
640     Condition * temp = NULL;
641     if (currentCondition != NULL) {
642         if (currentCondition->getParent() != NULL) { //Condition is a child of another condition
643             currentCondition->getParent()->addCondition(*currentCondition);
644         } else { //Condition parent is a Rule
645             Rule * rule = dynamic_cast<Rule *>(currentRoot->getElement());
646             Assert(rule != NULL);
647             rule->setCondition(*currentCondition);
648         }
649         temp = currentCondition->getParent();
650         delete currentCondition;
651     }
652     currentCondition = temp;  //switch current condition ( it may be switched to NULL if condition's parent was rule
653 }
654
655 void Parser::consumeSubjectMatch(xmlChar * value)
656 {
657     Assert(
658         currentAttribute != NULL &&
659         "consuming subject match without attribute set");
660
661     if (currentSubject != NULL) {
662         currentSubject->addNewAttribute(*currentAttribute);
663         //[CR] matching/modyfing functions transform uri.host to uri ( etc. ) so strncmp is not needed, string equality will do
664         if (!strncmp(currentAttribute->getName()->c_str(), "uri",
665                      3) ||
666             !strncmp(currentAttribute->getName()->c_str(), "id", 2)) {
667             if (value != NULL) {
668                 currentSubject->setSubjectId(reinterpret_cast<const char *>(
669                                                  value));
670             } else if (currentAttribute->getValue()->size()) {
671                 currentSubject->setSubjectId(
672                     currentAttribute->getValue()->front());
673             } else {
674                 Assert(false);
675             }
676         }
677     } else if (currentCondition != NULL) {
678         currentCondition->addAttribute(*currentAttribute);
679     }
680
681     delete currentAttribute;
682     currentAttribute = NULL;
683 }
684
685 void Parser::trim(std::string * str)
686 {
687     std::string::size_type pos = str->find_last_not_of(whitespaces);
688     if (pos != std::string::npos) {
689         str->erase(pos + 1);
690         pos = str->find_first_not_of(whitespaces);
691         if (pos != std::string::npos) {
692             str->erase(0, pos);
693         }
694     } else {
695         str->erase(str->begin(), str->end());
696         LogInfo("Warning, empty string as attribute value");
697     }
698 }
699
700 // KW void Parser::canonicalize(const char * input, const char * output, CanonicalizationAlgorithm canonicalizationAlgorithm){
701 // KW
702 // KW     xmlDocPtr       doc =  xmlParseFile(input);
703 // KW     //xmlDocDump(stdout, doc);
704 // KW
705 // KW     if(doc == NULL)
706 // KW     {
707 // KW         LogError("Canonicalization error, cannot parser xml file");
708 // KW     }
709 // KW
710 // KW
711 // KW     int mode = -1;
712 // KW     if(canonicalizationAlgorithm == C14N)
713 // KW     {
714 // KW         mode = 0;
715 // KW     }
716 // KW     else if(canonicalizationAlgorithm == C14NEXCLUSIVE)
717 // KW     {
718 // KW         mode = 1;
719 // KW     }
720 // KW
721 // KW
722 // KW     xmlC14NDocSave(doc, NULL, mode, NULL, 0, output, 0);
723 // KW
724 // KW     xmlFreeDoc(doc);
725 // KW
726 // KW }
727
728 // KW int Parser::extractNodeToFile(xmlTextReaderPtr reader, const char * filename){
729 // KW
730 // KW        xmlNodePtr node = xmlTextReaderExpand(reader);
731 // KW        xmlBufferPtr buff = xmlBufferCreate();
732 // KW        xmlNodeDump(buff, node->doc, node, 0, 0);
733 // KW        FILE * file = fopen(filename, "w");
734 // KW        if(file == NULL){
735 // KW            LogError("Error while trying to open file "<<filename);
736 // KW            return -1;
737 // KW        }
738 // KW        int ret = xmlBufferDump(file, buff);
739 // KW        fclose(file);
740 // KW        xmlBufferFree(buff);
741 // KW        return ret;
742 // KW
743 // KW }
744