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