security/manager/ssl/tests/unit/test_signed_apps.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 "use strict";
michael@0 2 /* To regenerate the certificates and apps for this test:
michael@0 3
michael@0 4 cd security/manager/ssl/tests/unit/test_signed_apps
michael@0 5 PATH=$NSS/bin:$NSS/lib:$PATH ./generate.sh
michael@0 6 cd ../../../../../..
michael@0 7 make -C $OBJDIR/security/manager/ssl/tests
michael@0 8
michael@0 9 $NSS is the path to NSS binaries and libraries built for the host platform.
michael@0 10 If you get error messages about "CertUtil" on Windows, then it means that
michael@0 11 the Windows CertUtil.exe is ahead of the NSS certutil.exe in $PATH.
michael@0 12
michael@0 13 Check in the generated files. These steps are not done as part of the build
michael@0 14 because we do not want to add a build-time dependency on the OpenSSL or NSS
michael@0 15 tools or libraries built for the host platform.
michael@0 16 */
michael@0 17
michael@0 18 // XXX from prio.h
michael@0 19 const PR_RDWR = 0x04;
michael@0 20 const PR_CREATE_FILE = 0x08;
michael@0 21 const PR_TRUNCATE = 0x20;
michael@0 22
michael@0 23 do_get_profile(); // must be called before getting nsIX509CertDB
michael@0 24 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB);
michael@0 25
michael@0 26 // Creates a new app package based in the inFilePath package, with a set of
michael@0 27 // modifications (including possibly deletions) applied to the existing entries,
michael@0 28 // and/or a set of new entries to be included.
michael@0 29 function tamper(inFilePath, outFilePath, modifications, newEntries) {
michael@0 30 var writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
michael@0 31 writer.open(outFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
michael@0 32 try {
michael@0 33 var reader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
michael@0 34 reader.open(inFilePath);
michael@0 35 try {
michael@0 36 var entries = reader.findEntries("");
michael@0 37 while (entries.hasMore()) {
michael@0 38 var entryName = entries.getNext();
michael@0 39 var inEntry = reader.getEntry(entryName);
michael@0 40 var entryInput = reader.getInputStream(entryName);
michael@0 41 try {
michael@0 42 var f = modifications[entryName];
michael@0 43 var outEntry, outEntryInput;
michael@0 44 if (f) {
michael@0 45 [outEntry, outEntryInput] = f(inEntry, entryInput);
michael@0 46 delete modifications[entryName];
michael@0 47 } else {
michael@0 48 [outEntry, outEntryInput] = [inEntry, entryInput];
michael@0 49 }
michael@0 50 // if f does not want the input entry to be copied to the output entry
michael@0 51 // at all (i.e. it wants it to be deleted), it will return null.
michael@0 52 if (outEntryInput) {
michael@0 53 try {
michael@0 54 writer.addEntryStream(entryName,
michael@0 55 outEntry.lastModifiedTime,
michael@0 56 outEntry.compression,
michael@0 57 outEntryInput,
michael@0 58 false);
michael@0 59 } finally {
michael@0 60 if (entryInput != outEntryInput)
michael@0 61 outEntryInput.close();
michael@0 62 }
michael@0 63 }
michael@0 64 } finally {
michael@0 65 entryInput.close();
michael@0 66 }
michael@0 67 }
michael@0 68 } finally {
michael@0 69 reader.close();
michael@0 70 }
michael@0 71
michael@0 72 // Any leftover modification means that we were expecting to modify an entry
michael@0 73 // in the input file that wasn't there.
michael@0 74 for(var name in modifications) {
michael@0 75 if (modifications.hasOwnProperty(name)) {
michael@0 76 throw "input file was missing expected entries: " + name;
michael@0 77 }
michael@0 78 }
michael@0 79
michael@0 80 // Now, append any new entries to the end
michael@0 81 newEntries.forEach(function(newEntry) {
michael@0 82 var sis = Cc["@mozilla.org/io/string-input-stream;1"]
michael@0 83 .createInstance(Ci.nsIStringInputStream);
michael@0 84 try {
michael@0 85 sis.setData(newEntry.content, newEntry.content.length);
michael@0 86 writer.addEntryStream(newEntry.name,
michael@0 87 new Date(),
michael@0 88 Ci.nsIZipWriter.COMPRESSION_BEST,
michael@0 89 sis,
michael@0 90 false);
michael@0 91 } finally {
michael@0 92 sis.close();
michael@0 93 }
michael@0 94 });
michael@0 95 } finally {
michael@0 96 writer.close();
michael@0 97 }
michael@0 98 }
michael@0 99
michael@0 100 function removeEntry(entry, entryInput) { return [null, null]; }
michael@0 101
michael@0 102 function truncateEntry(entry, entryInput) {
michael@0 103 if (entryInput.available() == 0)
michael@0 104 throw "Truncating already-zero length entry will result in identical entry.";
michael@0 105
michael@0 106 var content = Cc["@mozilla.org/io/string-input-stream;1"]
michael@0 107 .createInstance(Ci.nsIStringInputStream);
michael@0 108 content.data = "";
michael@0 109
michael@0 110 return [entry, content]
michael@0 111 }
michael@0 112
michael@0 113 function run_test() {
michael@0 114 run_next_test();
michael@0 115 }
michael@0 116
michael@0 117 function check_open_result(name, expectedRv) {
michael@0 118 return function openSignedAppFileCallback(rv, aZipReader, aSignerCert) {
michael@0 119 do_print("openSignedAppFileCallback called for " + name);
michael@0 120 do_check_eq(rv, expectedRv);
michael@0 121 do_check_eq(aZipReader != null, Components.isSuccessCode(expectedRv));
michael@0 122 do_check_eq(aSignerCert != null, Components.isSuccessCode(expectedRv));
michael@0 123 run_next_test();
michael@0 124 };
michael@0 125 }
michael@0 126
michael@0 127 function original_app_path(test_name) {
michael@0 128 return do_get_file("test_signed_apps/" + test_name + ".zip", false);
michael@0 129 }
michael@0 130
michael@0 131 function tampered_app_path(test_name) {
michael@0 132 return FileUtils.getFile("TmpD", ["test_signed_app-" + test_name + ".zip"]);
michael@0 133 }
michael@0 134
michael@0 135 add_test(function () {
michael@0 136 certdb.openSignedAppFileAsync(
michael@0 137 Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
michael@0 138 check_open_result("valid", Cr.NS_OK));
michael@0 139 });
michael@0 140
michael@0 141 add_test(function () {
michael@0 142 certdb.openSignedAppFileAsync(
michael@0 143 Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned_app_1"),
michael@0 144 check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
michael@0 145 });
michael@0 146
michael@0 147 add_test(function () {
michael@0 148 certdb.openSignedAppFileAsync(
michael@0 149 Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer_app_1"),
michael@0 150 check_open_result("unknown_issuer",
michael@0 151 getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)));
michael@0 152 });
michael@0 153
michael@0 154 // Sanity check to ensure a no-op tampering gives a valid result
michael@0 155 add_test(function () {
michael@0 156 var tampered = tampered_app_path("identity_tampering");
michael@0 157 tamper(original_app_path("valid_app_1"), tampered, { }, []);
michael@0 158 certdb.openSignedAppFileAsync(
michael@0 159 Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
michael@0 160 check_open_result("identity_tampering", Cr.NS_OK));
michael@0 161 });
michael@0 162
michael@0 163 add_test(function () {
michael@0 164 var tampered = tampered_app_path("missing_rsa");
michael@0 165 tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.RSA" : removeEntry }, []);
michael@0 166 certdb.openSignedAppFileAsync(
michael@0 167 Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
michael@0 168 check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
michael@0 169 });
michael@0 170
michael@0 171 add_test(function () {
michael@0 172 var tampered = tampered_app_path("missing_sf");
michael@0 173 tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.SF" : removeEntry }, []);
michael@0 174 certdb.openSignedAppFileAsync(
michael@0 175 Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
michael@0 176 check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
michael@0 177 });
michael@0 178
michael@0 179 add_test(function () {
michael@0 180 var tampered = tampered_app_path("missing_manifest_mf");
michael@0 181 tamper(original_app_path("valid_app_1"), tampered, { "META-INF/MANIFEST.MF" : removeEntry }, []);
michael@0 182 certdb.openSignedAppFileAsync(
michael@0 183 Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
michael@0 184 check_open_result("missing_manifest_mf",
michael@0 185 Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
michael@0 186 });
michael@0 187
michael@0 188 add_test(function () {
michael@0 189 var tampered = tampered_app_path("missing_entry");
michael@0 190 tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp" : removeEntry }, []);
michael@0 191 certdb.openSignedAppFileAsync(
michael@0 192 Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
michael@0 193 check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
michael@0 194 });
michael@0 195
michael@0 196 add_test(function () {
michael@0 197 var tampered = tampered_app_path("truncated_entry");
michael@0 198 tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp" : truncateEntry }, []);
michael@0 199 certdb.openSignedAppFileAsync(
michael@0 200 Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
michael@0 201 check_open_result("truncated_entry", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
michael@0 202 });
michael@0 203
michael@0 204 add_test(function () {
michael@0 205 var tampered = tampered_app_path("unsigned_entry");
michael@0 206 tamper(original_app_path("valid_app_1"), tampered, {},
michael@0 207 [ { "name": "unsigned.txt", "content": "unsigned content!" } ]);
michael@0 208 certdb.openSignedAppFileAsync(
michael@0 209 Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
michael@0 210 check_open_result("unsigned_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
michael@0 211 });
michael@0 212
michael@0 213 add_test(function () {
michael@0 214 var tampered = tampered_app_path("unsigned_metainf_entry");
michael@0 215 tamper(original_app_path("valid_app_1"), tampered, {},
michael@0 216 [ { name: "META-INF/unsigned.txt", content: "unsigned content!" } ]);
michael@0 217 certdb.openSignedAppFileAsync(
michael@0 218 Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
michael@0 219 check_open_result("unsigned_metainf_entry",
michael@0 220 Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
michael@0 221 });
michael@0 222
michael@0 223 // TODO: tampered MF, tampered SF
michael@0 224 // TODO: too-large MF, too-large RSA, too-large SF
michael@0 225 // TODO: MF and SF that end immediately after the last main header
michael@0 226 // (no CR nor LF)
michael@0 227 // TODO: broken headers to exercise the parser

mercurial