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"
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";
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];
38 vsnprintf(buffer, sizeof(buffer), msg, args);
40 LOG(ERROR) << "ERROR: " << buffer;
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];
49 vsnprintf(buffer, sizeof(buffer), msg, args);
51 LOG(WARNING) << "Warning: " << buffer;
56 SignatureFile(const std::string& file_name, int file_number)
57 : file_name_(file_name), file_number_(file_number) {
60 std::string file_name() const {
64 int file_number() const {
68 bool operator<(const SignatureFile &second) const {
69 return file_number_ < second.file_number();
73 std::string file_name_;
76 typedef std::set<SignatureFile> SignatureFileSet;
78 const SignatureFileSet GetSignatureFiles(const base::FilePath& widget_path) {
79 SignatureFileSet signature_set;
80 std::string file_name;
82 base::FileEnumerator iter(widget_path, false, base::FileEnumerator::FILES,
83 FILE_PATH_LITERAL("*.xml"));
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));
91 if (re2::RE2::FullMatch(file_name, kDistributorSignatureRex, &number)) {
92 // Find distributor signature file.
93 signature_set.insert(SignatureFile(file_name, number));
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.";
108 xmlSchemaParserCtxtPtr ctx = xmlSchemaNewParserCtxt(kSignatureSchemaPath);
110 LOG(ERROR) << "Initing xml schema parser context failed.";
114 xmlSchemaPtr xschema = xmlSchemaParse(ctx);
115 if (xschema == NULL) {
116 LOG(ERROR) << "Parsing xml schema failed.";
120 xmlSchemaValidCtxtPtr vctx = xmlSchemaNewValidCtxt(xschema);
122 LOG(ERROR) << "Initing xml schema context failed.";
125 xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&LogErrorLibxml2,
126 (xmlSchemaValidityWarningFunc)&LogWarningLibxml2, NULL);
128 int ret = xmlSchemaValidateFile(vctx, widget_path.Append(
129 signature_file.file_name()).MaybeAsASCII().c_str(), 0);
132 LOG(ERROR) << "Validating " << signature_file.file_name()
133 << " schema failed.";
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();
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.";
154 const xwalk::application::SignatureData& signature_data) {
155 std::string role_uri = signature_data.role_uri();
157 if (role_uri.empty()) {
158 LOG(ERROR) << "URI attribute in Role tag couldn't be empty.";
162 if (role_uri != kTokenRoleAuthorURI && signature_data.isAuthorSignature()) {
163 LOG(ERROR) << "URI attribute in Role tag does not "
164 << "match with signature filename.";
168 if (role_uri != kTokenRoleDistributor &&
169 !signature_data.isAuthorSignature()) {
170 LOG(ERROR) << "URI attribute in Role tag does not "
171 << "match with signature filename.";
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.";
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);
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.
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.";
210 } // anonymous namespace
213 namespace application {
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.";
226 SignatureFileSet::reverse_iterator iter = signature_set.rbegin();
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.";
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 // TODO(XU): depends on root CA certificate installed on Tizen platform.
260 } // namespace application