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.
5 #include "xwalk/application/common/tizen/signature_validator.h"
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"
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";
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];
39 vsnprintf(buffer, sizeof(buffer), msg, args);
41 LOG(ERROR) << "ERROR: " << buffer;
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];
50 vsnprintf(buffer, sizeof(buffer), msg, args);
52 LOG(WARNING) << "Warning: " << buffer;
57 SignatureFile(const std::string& file_name, int file_number)
58 : file_name_(file_name), file_number_(file_number) {
61 std::string file_name() const {
65 int file_number() const {
69 bool operator<(const SignatureFile &second) const {
70 return file_number_ < second.file_number();
74 std::string file_name_;
77 typedef std::set<SignatureFile> SignatureFileSet;
79 const SignatureFileSet GetSignatureFiles(const base::FilePath& widget_path) {
80 SignatureFileSet signature_set;
81 std::string file_name;
83 base::FileEnumerator iter(widget_path, false, base::FileEnumerator::FILES,
84 FILE_PATH_LITERAL("*.xml"));
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));
92 if (re2::RE2::FullMatch(file_name, kDistributorSignatureRex, &number)) {
93 // Find distributor signature file.
94 signature_set.insert(SignatureFile(file_name, number));
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.";
109 xmlSchemaParserCtxtPtr ctx = xmlSchemaNewParserCtxt(kSignatureSchemaPath);
111 LOG(ERROR) << "Initing xml schema parser context failed.";
115 xmlSchemaPtr xschema = xmlSchemaParse(ctx);
116 if (xschema == NULL) {
117 LOG(ERROR) << "Parsing xml schema failed.";
121 xmlSchemaValidCtxtPtr vctx = xmlSchemaNewValidCtxt(xschema);
123 LOG(ERROR) << "Initing xml schema context failed.";
126 xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&LogErrorLibxml2,
127 (xmlSchemaValidityWarningFunc)&LogWarningLibxml2, NULL);
129 int ret = xmlSchemaValidateFile(vctx, widget_path.Append(
130 signature_file.file_name()).MaybeAsASCII().c_str(), 0);
133 LOG(ERROR) << "Validating " << signature_file.file_name()
134 << " schema failed.";
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();
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.";
155 const xwalk::application::SignatureData& signature_data) {
156 std::string role_uri = signature_data.role_uri();
158 if (role_uri.empty()) {
159 LOG(ERROR) << "URI attribute in Role tag couldn't be empty.";
163 if (role_uri != kTokenRoleAuthorURI && signature_data.isAuthorSignature()) {
164 LOG(ERROR) << "URI attribute in Role tag does not "
165 << "match with signature filename.";
169 if (role_uri != kTokenRoleDistributor &&
170 !signature_data.isAuthorSignature()) {
171 LOG(ERROR) << "URI attribute in Role tag does not "
172 << "match with signature filename.";
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.";
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);
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.
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.";
211 } // anonymous namespace
214 namespace application {
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.";
227 SignatureFileSet::reverse_iterator iter = signature_set.rbegin();
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.";
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()))
242 // Validate the profile property.
243 if (!CheckProfileURI(*data.get()))
246 // Validate the identifier property.
247 if (!CheckObjectID(*data.get()))
250 // Validate role property.
251 if (!CheckRoleURI(*data.get()))
254 // Perform reference validation and signature validation on signature
255 if (!SignatureXmlSecAdaptor::ValidateFile(*data.get(), widget_path))
261 } // namespace application