Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / xwalk / application / common / tizen / signature_validator.cc
1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "xwalk/application/common/tizen/signature_validator.h"
6
7 #include <set>
8 #include <string>
9 #include "base/files/file_enumerator.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "libxml/tree.h"
13 #include "libxml/parser.h"
14 #include "libxml/xmlschemas.h"
15 #include "third_party/re2/re2/re2.h"
16 #include "xwalk/application/common/tizen/signature_data.h"
17 #include "xwalk/application/common/tizen/signature_parser.h"
18 #include "xwalk/application/common/tizen/signature_xmlsec_adaptor.h"
19
20 namespace {
21
22 const int kXMLLogSize = 1024;
23 const char kAuthorSignatureName[] = "author-signature.xml";
24 const char kDistributorSignatureRex[] = "^signature([1-9][0-9]*)\\.xml";
25 const char kTokenRoleAuthorURI[] =
26   "http://www.w3.org/ns/widgets-digsig#role-author";
27 const char kTokenRoleDistributor[] =
28   "http://www.w3.org/ns/widgets-digsig#role-distributor";
29 const char kTokenProfileURI[] =
30   "http://www.w3.org/ns/widgets-digsig#profile";
31 const char kSignatureSchemaPath[] = "/usr/share/xwalk/signature_schema.xsd";
32
33 //  A wrapper of LOG(ERROR) function, which  is used as parameter of function
34 //  xmlSchemaSetValidErrors
35 void LogErrorLibxml2(void *, const char *msg, ...) {
36   char buffer[kXMLLogSize];
37   va_list args;
38   va_start(args, msg);
39   vsnprintf(buffer, sizeof(buffer), msg, args);
40   va_end(args);
41   LOG(ERROR) << "ERROR: " << buffer;
42 }
43
44 //  A wrapper of LOG(WARNING) function, which  is used as parameter of function
45 //  xmlSchemaSetValidErrors
46 void LogWarningLibxml2(void *, const char *msg, ...) {
47   char buffer[kXMLLogSize];
48   va_list args;
49   va_start(args, msg);
50   vsnprintf(buffer, sizeof(buffer), msg, args);
51   va_end(args);
52   LOG(WARNING) << "Warning: " << buffer;
53 }
54
55 class SignatureFile {
56  public:
57   SignatureFile(const std::string& file_name, int file_number)
58     : file_name_(file_name), file_number_(file_number) {
59   }
60
61   std::string file_name() const {
62     return file_name_;
63   }
64
65   int file_number() const {
66     return file_number_;
67   }
68
69   bool operator<(const SignatureFile &second) const {
70     return file_number_ < second.file_number();
71   }
72
73  private:
74   std::string file_name_;
75   int file_number_;
76 };
77 typedef std::set<SignatureFile> SignatureFileSet;
78
79 const SignatureFileSet GetSignatureFiles(const base::FilePath& widget_path) {
80   SignatureFileSet signature_set;
81   std::string file_name;
82   int number;
83   base::FileEnumerator iter(widget_path, false, base::FileEnumerator::FILES,
84       FILE_PATH_LITERAL("*.xml"));
85
86   for (base::FilePath name = iter.Next(); !name.empty(); name = iter.Next()) {
87     file_name = name.BaseName().MaybeAsASCII();
88     if (file_name.compare(kAuthorSignatureName) == 0) {
89       // Find author signature file.
90       signature_set.insert(SignatureFile(file_name, -1));
91     }
92     if (re2::RE2::FullMatch(file_name, kDistributorSignatureRex, &number)) {
93       // Find distributor signature file.
94       signature_set.insert(SignatureFile(file_name, number));
95     }
96   }
97   return signature_set;
98 }
99
100 bool XMLSchemaValidate(
101     const SignatureFile& signature_file, const base::FilePath& widget_path) {
102   xmlDocPtr schema_doc = xmlReadFile(
103       kSignatureSchemaPath, NULL, XML_PARSE_NONET|XML_PARSE_NOENT);
104   if (NULL == schema_doc) {
105     LOG(ERROR) << "Reading schema file failed.";
106     return false;
107   }
108
109   xmlSchemaParserCtxtPtr ctx = xmlSchemaNewParserCtxt(kSignatureSchemaPath);
110   if (ctx == NULL) {
111     LOG(ERROR) << "Initing xml schema parser context failed.";
112     return false;
113   }
114
115   xmlSchemaPtr xschema = xmlSchemaParse(ctx);
116   if (xschema == NULL) {
117     LOG(ERROR) << "Parsing xml schema failed.";
118     return false;
119   }
120
121   xmlSchemaValidCtxtPtr vctx = xmlSchemaNewValidCtxt(xschema);
122   if (vctx == NULL) {
123     LOG(ERROR) << "Initing xml schema context failed.";
124     return false;
125   }
126   xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&LogErrorLibxml2,
127       (xmlSchemaValidityWarningFunc)&LogWarningLibxml2, NULL);
128
129   int ret = xmlSchemaValidateFile(vctx, widget_path.Append(
130         signature_file.file_name()).MaybeAsASCII().c_str(), 0);
131
132   if (ret != 0) {
133     LOG(ERROR) << "Validating " << signature_file.file_name()
134                << " schema failed.";
135     return false;
136   }
137   return true;
138 }
139
140 bool CheckObjectID(
141     const xwalk::application::SignatureData& signature_data) {
142   std::string object_id = signature_data.object_id();
143   std::set<std::string> reference_set = signature_data.reference_set();
144
145   std::set<std::string>::const_iterator result =
146     reference_set.find(std::string("#") + object_id);
147   if (result == reference_set.end()) {
148     LOG(ERROR) << "No reference to object.";
149     return false;
150   }
151   return true;
152 }
153
154 bool CheckRoleURI(
155     const xwalk::application::SignatureData& signature_data) {
156   std::string role_uri = signature_data.role_uri();
157
158   if (role_uri.empty()) {
159     LOG(ERROR) << "URI attribute in Role tag couldn't be empty.";
160     return false;
161   }
162
163   if (role_uri != kTokenRoleAuthorURI && signature_data.isAuthorSignature()) {
164     LOG(ERROR) << "URI attribute in Role tag does not "
165                << "match with signature filename.";
166     return false;
167   }
168
169   if (role_uri != kTokenRoleDistributor &&
170       !signature_data.isAuthorSignature()) {
171     LOG(ERROR) << "URI attribute in Role tag does not "
172                << "match with signature filename.";
173     return false;
174   }
175
176   return true;
177 }
178
179 bool CheckProfileURI(const xwalk::application::SignatureData& signature_data) {
180   if (kTokenProfileURI != signature_data.profile_uri()) {
181     LOG(ERROR) << "Profile tag contains unsupported value in URI attribute.";
182     return false;
183   }
184   return true;
185 }
186
187 bool CheckReference(
188     const xwalk::application::SignatureData& signature_data) {
189   base::FilePath widget_path = signature_data.GetExtractedWidgetPath();
190   int prefix_length = widget_path.value().length();
191   std::string file_name;
192   std::set<std::string> reference_set = signature_data.reference_set();
193   base::FileEnumerator iter(widget_path, true, base::FileEnumerator::FILES);
194
195   for (base::FilePath name = iter.Next(); !name.empty(); name = iter.Next()) {
196     file_name = name.value().substr(prefix_length);
197     if (file_name.compare(kAuthorSignatureName) == 0 ||
198         re2::RE2::FullMatch(file_name, kDistributorSignatureRex)) {
199       // Skip signtature file.
200       continue;
201     }
202     std::set<std::string>::iterator ref_iter = reference_set.find(file_name);
203     if (ref_iter == reference_set.end()) {
204       LOG(ERROR) << file_name << "is not in signature ds:Reference.";
205       return false;
206     }
207   }
208   return true;
209 }
210
211 }  // anonymous namespace
212
213 namespace xwalk {
214 namespace application {
215 // static
216 SignatureValidator::Status SignatureValidator::Check(
217     const base::FilePath& widget_path) {
218   LOG(INFO) << "Verifying widget signature file.";
219   // Process every signature files (author and distributor) according to
220   // http://www.w3.org/TR/widgets-digsig/#signature-verification.
221   const SignatureFileSet& signature_set = GetSignatureFiles(widget_path);
222   if (signature_set.empty()) {
223     LOG(INFO) << "No signed signature in the package.";
224     return UNTRUSTED;
225   }
226
227   SignatureFileSet::reverse_iterator iter = signature_set.rbegin();
228   bool ret = false;
229   for (; iter != signature_set.rend(); ++iter) {
230     // Verify whether signature xml is a valid [XMLDSIG] document.
231     if (!XMLSchemaValidate(*iter, widget_path)) {
232       LOG(ERROR) << "Validating " << iter->file_name() << "schema failed.";
233       return INVALID;
234     }
235
236     scoped_ptr<SignatureData> data = SignatureParser::CreateSignatureData(
237         widget_path.Append(iter->file_name()), iter->file_number());
238     // Check whether each file in the widget can be found from ds:Reference.
239     if (!CheckReference(*data.get()))
240       return INVALID;
241
242     // Validate the profile property.
243     if (!CheckProfileURI(*data.get()))
244       return INVALID;
245
246     // Validate the identifier property.
247     if (!CheckObjectID(*data.get()))
248       return INVALID;
249
250     // Validate role property.
251     if (!CheckRoleURI(*data.get()))
252       return INVALID;
253
254     // Perform reference validation and signature validation on signature
255     if (!SignatureXmlSecAdaptor::ValidateFile(*data.get(), widget_path))
256       return INVALID;
257   }
258   return VALID;
259 }
260
261 }  // namespace application
262 }  // namespace xwalk