Upstream version 9.38.198.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
19 namespace {
20
21 const int kXMLLogSize = 1024;
22 const char kAuthorSignatureName[] = "author-signature.xml";
23 const char kDistributorSignatureRex[] = "^signature([1-9][0-9]*)\\.xml";
24 const char kTokenRoleAuthorURI[] =
25   "http://www.w3.org/ns/widgets-digsig#role-author";
26 const char kTokenRoleDistributor[] =
27   "http://www.w3.org/ns/widgets-digsig#role-distributor";
28 const char kTokenProfileURI[] =
29   "http://www.w3.org/ns/widgets-digsig#profile";
30 const char kSignatureSchemaPath[] = "/usr/share/xwalk/signature_schema.xsd";
31
32 //  A wrapper of LOG(ERROR) function, which  is used as parameter of function
33 //  xmlSchemaSetValidErrors
34 void LogErrorLibxml2(void *, const char *msg, ...) {
35   char buffer[kXMLLogSize];
36   va_list args;
37   va_start(args, msg);
38   vsnprintf(buffer, sizeof(buffer), msg, args);
39   va_end(args);
40   LOG(ERROR) << "ERROR: " << buffer;
41 }
42
43 //  A wrapper of LOG(WARNING) function, which  is used as parameter of function
44 //  xmlSchemaSetValidErrors
45 void LogWarningLibxml2(void *, const char *msg, ...) {
46   char buffer[kXMLLogSize];
47   va_list args;
48   va_start(args, msg);
49   vsnprintf(buffer, sizeof(buffer), msg, args);
50   va_end(args);
51   LOG(WARNING) << "Warning: " << buffer;
52 }
53
54 class SignatureFile {
55  public:
56   SignatureFile(const std::string& file_name, int file_number)
57     : file_name_(file_name), file_number_(file_number) {
58   }
59
60   std::string file_name() const {
61     return file_name_;
62   }
63
64   int file_number() const {
65     return file_number_;
66   }
67
68   bool operator<(const SignatureFile &second) const {
69     return file_number_ < second.file_number();
70   }
71
72  private:
73   std::string file_name_;
74   int file_number_;
75 };
76 typedef std::set<SignatureFile> SignatureFileSet;
77
78 const SignatureFileSet GetSignatureFiles(const base::FilePath& widget_path) {
79   SignatureFileSet signature_set;
80   std::string file_name;
81   int number;
82   base::FileEnumerator iter(widget_path, false, base::FileEnumerator::FILES,
83       FILE_PATH_LITERAL("*.xml"));
84
85   for (base::FilePath name = iter.Next(); !name.empty(); name = iter.Next()) {
86     file_name = name.BaseName().MaybeAsASCII();
87     if (file_name.compare(kAuthorSignatureName) == 0) {
88       // Find author signature file.
89       signature_set.insert(SignatureFile(file_name, -1));
90     }
91     if (re2::RE2::FullMatch(file_name, kDistributorSignatureRex, &number)) {
92       // Find distributor signature file.
93       signature_set.insert(SignatureFile(file_name, number));
94     }
95   }
96   return signature_set;
97 }
98
99 bool XMLSchemaValidate(
100     const SignatureFile& signature_file, const base::FilePath& widget_path) {
101   xmlDocPtr schema_doc = xmlReadFile(
102       kSignatureSchemaPath, NULL, XML_PARSE_NONET|XML_PARSE_NOENT);
103   if (NULL == schema_doc) {
104     LOG(ERROR) << "Reading schema file failed.";
105     return false;
106   }
107
108   xmlSchemaParserCtxtPtr ctx = xmlSchemaNewParserCtxt(kSignatureSchemaPath);
109   if (ctx == NULL) {
110     LOG(ERROR) << "Initing xml schema parser context failed.";
111     return false;
112   }
113
114   xmlSchemaPtr xschema = xmlSchemaParse(ctx);
115   if (xschema == NULL) {
116     LOG(ERROR) << "Parsing xml schema failed.";
117     return false;
118   }
119
120   xmlSchemaValidCtxtPtr vctx = xmlSchemaNewValidCtxt(xschema);
121   if (vctx == NULL) {
122     LOG(ERROR) << "Initing xml schema context failed.";
123     return false;
124   }
125   xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&LogErrorLibxml2,
126       (xmlSchemaValidityWarningFunc)&LogWarningLibxml2, NULL);
127
128   int ret = xmlSchemaValidateFile(vctx, widget_path.Append(
129         signature_file.file_name()).MaybeAsASCII().c_str(), 0);
130
131   if (ret != 0) {
132     LOG(ERROR) << "Validating " << signature_file.file_name()
133                << " schema failed.";
134     return false;
135   }
136   return true;
137 }
138
139 bool CheckObjectID(
140     const xwalk::application::SignatureData& signature_data) {
141   std::string object_id = signature_data.object_id();
142   std::set<std::string> reference_set = signature_data.reference_set();
143
144   std::set<std::string>::const_iterator result =
145     reference_set.find(std::string("#") + object_id);
146   if (result == reference_set.end()) {
147     LOG(ERROR) << "No reference to object.";
148     return false;
149   }
150   return true;
151 }
152
153 bool CheckRoleURI(
154     const xwalk::application::SignatureData& signature_data) {
155   std::string role_uri = signature_data.role_uri();
156
157   if (role_uri.empty()) {
158     LOG(ERROR) << "URI attribute in Role tag couldn't be empty.";
159     return false;
160   }
161
162   if (role_uri != kTokenRoleAuthorURI && signature_data.isAuthorSignature()) {
163     LOG(ERROR) << "URI attribute in Role tag does not "
164                << "match with signature filename.";
165     return false;
166   }
167
168   if (role_uri != kTokenRoleDistributor &&
169       !signature_data.isAuthorSignature()) {
170     LOG(ERROR) << "URI attribute in Role tag does not "
171                << "match with signature filename.";
172     return false;
173   }
174
175   return true;
176 }
177
178 bool CheckProfileURI(const xwalk::application::SignatureData& signature_data) {
179   if (kTokenProfileURI != signature_data.profile_uri()) {
180     LOG(ERROR) << "Profile tag contains unsupported value in URI attribute.";
181     return false;
182   }
183   return true;
184 }
185
186 bool CheckReference(
187     const xwalk::application::SignatureData& signature_data) {
188   base::FilePath widget_path = signature_data.GetExtractedWidgetPath();
189   int prefix_length = widget_path.value().length();
190   std::string file_name;
191   std::set<std::string> reference_set = signature_data.reference_set();
192   base::FileEnumerator iter(widget_path, true, base::FileEnumerator::FILES);
193
194   for (base::FilePath name = iter.Next(); !name.empty(); name = iter.Next()) {
195     file_name = name.value().substr(prefix_length);
196     if (file_name.compare(kAuthorSignatureName) == 0 ||
197         re2::RE2::FullMatch(file_name, kDistributorSignatureRex)) {
198       // Skip signtature file.
199       continue;
200     }
201     std::set<std::string>::iterator ref_iter = reference_set.find(file_name);
202     if (ref_iter == reference_set.end()) {
203       LOG(ERROR) << file_name << "is not in signature ds:Reference.";
204       return false;
205     }
206   }
207   return true;
208 }
209
210 }  // anonymous namespace
211
212 namespace xwalk {
213 namespace application {
214 // static
215 SignatureValidator::Status SignatureValidator::Check(
216     const base::FilePath& widget_path) {
217   LOG(INFO) << "Verifying widget signature file.";
218   // Process every signature files (author and distributor) according to
219   // http://www.w3.org/TR/widgets-digsig/#signature-verification.
220   const SignatureFileSet& signature_set = GetSignatureFiles(widget_path);
221   if (signature_set.empty()) {
222     LOG(INFO) << "No signed signature in the package.";
223     return UNTRUSTED;
224   }
225
226   SignatureFileSet::reverse_iterator iter = signature_set.rbegin();
227   bool ret = false;
228   for (; iter != signature_set.rend(); ++iter) {
229     LOG(INFO) << "Checking signature with id=" << iter->file_number();
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     // TODO(XU): depends on root CA certificate installed on Tizen platform.
256   }
257   return VALID;
258 }
259
260 }  // namespace application
261 }  // namespace xwalk