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/installer/tizen/signature_validator.h"
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"
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";
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];
35 vsnprintf(buffer, sizeof(buffer), msg, args);
37 LOG(ERROR) << "ERROR: " << buffer;
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];
46 vsnprintf(buffer, sizeof(buffer), msg, args);
48 LOG(WARNING) << "Warning: " << buffer;
53 SignatureFile(const std::string& file_name, int file_number)
54 : file_name_(file_name), file_number_(file_number) {
57 std::string file_name() const {
61 int file_number() const {
65 bool operator<(const SignatureFile &second) const {
66 return file_number_ < second.file_number();
70 std::string file_name_;
73 typedef std::set<SignatureFile> SignatureFileSet;
75 const SignatureFileSet& GetSignatureFiles(const base::FilePath& widget_path) {
76 SignatureFileSet signature_set;
77 std::string file_name;
79 base::FileEnumerator iter(widget_path, false, base::FileEnumerator::FILES,
80 FILE_PATH_LITERAL("*.xml"));
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));
88 if (re2::RE2::FullMatch(file_name, kDistributorSignatureRex, &number)) {
89 // Find distributor signature file.
90 signature_set.insert(SignatureFile(file_name, number));
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.";
105 xmlSchemaParserCtxtPtr ctx = xmlSchemaNewParserCtxt(kSignatureSchemaPath);
107 LOG(ERROR) << "Initing xml schema parser context failed.";
111 xmlSchemaPtr xschema = xmlSchemaParse(ctx);
112 if (xschema == NULL) {
113 LOG(ERROR) << "Parsing xml schema failed.";
117 xmlSchemaValidCtxtPtr vctx = xmlSchemaNewValidCtxt(xschema);
119 LOG(ERROR) << "Initing xml schema context failed.";
122 xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&LogErrorLibxml2,
123 (xmlSchemaValidityWarningFunc)&LogWarningLibxml2, NULL);
125 int ret = xmlSchemaValidateFile(vctx, widget_path.Append(
126 signature_file.file_name()).MaybeAsASCII().c_str(), 0);
129 LOG(ERROR) << "Validating " << signature_file.file_name()
130 << " schema failed.";
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();
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.";
151 const xwalk::application::SignatureData& signature_data) {
152 std::string role_uri = signature_data.role_uri();
154 if (role_uri.empty()) {
155 LOG(ERROR) << "URI attribute in Role tag couldn't be empty.";
159 if (role_uri != kTokenRoleAuthorURI && signature_data.isAuthorSignature()) {
160 LOG(ERROR) << "URI attribute in Role tag does not "
161 << "match with signature filename.";
165 if (role_uri != kTokenRoleDistributor &&
166 !signature_data.isAuthorSignature()) {
167 LOG(ERROR) << "URI attribute in Role tag does not "
168 << "match with signature filename.";
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.";
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);
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.
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.";
207 } // anonymous namespace
210 namespace application {
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.";
223 SignatureFileSet::reverse_iterator iter = signature_set.rbegin();
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.";
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()))
239 // Validate the profile property.
240 if (!CheckProfileURI(*data.get()))
243 // Validate the identifier property.
244 if (!CheckObjectID(*data.get()))
247 // Validate role property.
248 if (!CheckRoleURI(*data.get()))
251 // Perform reference validation and signature validation on signature
252 // TODO(XU): depends on root CA certificate installed on Tizen platform.
257 } // namespace application