Wed, 31 Dec 2014 06:55:46 +0100
Added tag TORBROWSER_REPLICA for changeset 6474c204b198
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 4 | * http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 5 | |
michael@0 | 6 | /** |
michael@0 | 7 | * This file tests signature extraction using Windows Authenticode APIs of |
michael@0 | 8 | * downloaded files. |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 12 | //// Globals |
michael@0 | 13 | |
michael@0 | 14 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 15 | |
michael@0 | 16 | XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", |
michael@0 | 17 | "resource://gre/modules/FileUtils.jsm"); |
michael@0 | 18 | XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", |
michael@0 | 19 | "resource://gre/modules/NetUtil.jsm"); |
michael@0 | 20 | XPCOMUtils.defineLazyModuleGetter(this, "Promise", |
michael@0 | 21 | "resource://gre/modules/Promise.jsm"); |
michael@0 | 22 | XPCOMUtils.defineLazyModuleGetter(this, "Task", |
michael@0 | 23 | "resource://gre/modules/Task.jsm"); |
michael@0 | 24 | |
michael@0 | 25 | const BackgroundFileSaverOutputStream = Components.Constructor( |
michael@0 | 26 | "@mozilla.org/network/background-file-saver;1?mode=outputstream", |
michael@0 | 27 | "nsIBackgroundFileSaver"); |
michael@0 | 28 | |
michael@0 | 29 | const StringInputStream = Components.Constructor( |
michael@0 | 30 | "@mozilla.org/io/string-input-stream;1", |
michael@0 | 31 | "nsIStringInputStream", |
michael@0 | 32 | "setData"); |
michael@0 | 33 | |
michael@0 | 34 | const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt"; |
michael@0 | 35 | |
michael@0 | 36 | /** |
michael@0 | 37 | * Returns a reference to a temporary file. If the file is then created, it |
michael@0 | 38 | * will be removed when tests in this file finish. |
michael@0 | 39 | */ |
michael@0 | 40 | function getTempFile(aLeafName) { |
michael@0 | 41 | let file = FileUtils.getFile("TmpD", [aLeafName]); |
michael@0 | 42 | do_register_cleanup(function GTF_cleanup() { |
michael@0 | 43 | if (file.exists()) { |
michael@0 | 44 | file.remove(false); |
michael@0 | 45 | } |
michael@0 | 46 | }); |
michael@0 | 47 | return file; |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | /** |
michael@0 | 51 | * Waits for the given saver object to complete. |
michael@0 | 52 | * |
michael@0 | 53 | * @param aSaver |
michael@0 | 54 | * The saver, with the output stream or a stream listener implementation. |
michael@0 | 55 | * @param aOnTargetChangeFn |
michael@0 | 56 | * Optional callback invoked with the target file name when it changes. |
michael@0 | 57 | * |
michael@0 | 58 | * @return {Promise} |
michael@0 | 59 | * @resolves When onSaveComplete is called with a success code. |
michael@0 | 60 | * @rejects With an exception, if onSaveComplete is called with a failure code. |
michael@0 | 61 | */ |
michael@0 | 62 | function promiseSaverComplete(aSaver, aOnTargetChangeFn) { |
michael@0 | 63 | let deferred = Promise.defer(); |
michael@0 | 64 | aSaver.observer = { |
michael@0 | 65 | onTargetChange: function BFSO_onSaveComplete(aSaver, aTarget) |
michael@0 | 66 | { |
michael@0 | 67 | if (aOnTargetChangeFn) { |
michael@0 | 68 | aOnTargetChangeFn(aTarget); |
michael@0 | 69 | } |
michael@0 | 70 | }, |
michael@0 | 71 | onSaveComplete: function BFSO_onSaveComplete(aSaver, aStatus) |
michael@0 | 72 | { |
michael@0 | 73 | if (Components.isSuccessCode(aStatus)) { |
michael@0 | 74 | deferred.resolve(); |
michael@0 | 75 | } else { |
michael@0 | 76 | deferred.reject(new Components.Exception("Saver failed.", aStatus)); |
michael@0 | 77 | } |
michael@0 | 78 | }, |
michael@0 | 79 | }; |
michael@0 | 80 | return deferred.promise; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | /** |
michael@0 | 84 | * Feeds a string to a BackgroundFileSaverOutputStream. |
michael@0 | 85 | * |
michael@0 | 86 | * @param aSourceString |
michael@0 | 87 | * The source data to copy. |
michael@0 | 88 | * @param aSaverOutputStream |
michael@0 | 89 | * The BackgroundFileSaverOutputStream to feed. |
michael@0 | 90 | * @param aCloseWhenDone |
michael@0 | 91 | * If true, the output stream will be closed when the copy finishes. |
michael@0 | 92 | * |
michael@0 | 93 | * @return {Promise} |
michael@0 | 94 | * @resolves When the copy completes with a success code. |
michael@0 | 95 | * @rejects With an exception, if the copy fails. |
michael@0 | 96 | */ |
michael@0 | 97 | function promiseCopyToSaver(aSourceString, aSaverOutputStream, aCloseWhenDone) { |
michael@0 | 98 | let deferred = Promise.defer(); |
michael@0 | 99 | let inputStream = new StringInputStream(aSourceString, aSourceString.length); |
michael@0 | 100 | let copier = Cc["@mozilla.org/network/async-stream-copier;1"] |
michael@0 | 101 | .createInstance(Ci.nsIAsyncStreamCopier); |
michael@0 | 102 | copier.init(inputStream, aSaverOutputStream, null, false, true, 0x8000, true, |
michael@0 | 103 | aCloseWhenDone); |
michael@0 | 104 | copier.asyncCopy({ |
michael@0 | 105 | onStartRequest: function () { }, |
michael@0 | 106 | onStopRequest: function (aRequest, aContext, aStatusCode) |
michael@0 | 107 | { |
michael@0 | 108 | if (Components.isSuccessCode(aStatusCode)) { |
michael@0 | 109 | deferred.resolve(); |
michael@0 | 110 | } else { |
michael@0 | 111 | deferred.reject(new Components.Exception(aResult)); |
michael@0 | 112 | } |
michael@0 | 113 | }, |
michael@0 | 114 | }, null); |
michael@0 | 115 | return deferred.promise; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | let gStillRunning = true; |
michael@0 | 119 | |
michael@0 | 120 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 121 | //// Tests |
michael@0 | 122 | |
michael@0 | 123 | function run_test() |
michael@0 | 124 | { |
michael@0 | 125 | run_next_test(); |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | add_task(function test_setup() |
michael@0 | 129 | { |
michael@0 | 130 | // Wait 10 minutes, that is half of the external xpcshell timeout. |
michael@0 | 131 | do_timeout(10 * 60 * 1000, function() { |
michael@0 | 132 | if (gStillRunning) { |
michael@0 | 133 | do_throw("Test timed out."); |
michael@0 | 134 | } |
michael@0 | 135 | }) |
michael@0 | 136 | }); |
michael@0 | 137 | |
michael@0 | 138 | function readFileToString(aFilename) { |
michael@0 | 139 | let f = do_get_file(aFilename); |
michael@0 | 140 | let stream = Cc["@mozilla.org/network/file-input-stream;1"] |
michael@0 | 141 | .createInstance(Ci.nsIFileInputStream); |
michael@0 | 142 | stream.init(f, -1, 0, 0); |
michael@0 | 143 | let buf = NetUtil.readInputStreamToString(stream, stream.available()); |
michael@0 | 144 | return buf; |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | add_task(function test_signature() |
michael@0 | 148 | { |
michael@0 | 149 | // Check that we get a signature if the saver is finished on Windows. |
michael@0 | 150 | let destFile = getTempFile(TEST_FILE_NAME_1); |
michael@0 | 151 | |
michael@0 | 152 | let data = readFileToString("data/signed_win.exe"); |
michael@0 | 153 | let saver = new BackgroundFileSaverOutputStream(); |
michael@0 | 154 | let completionPromise = promiseSaverComplete(saver); |
michael@0 | 155 | |
michael@0 | 156 | try { |
michael@0 | 157 | let signatureInfo = saver.signatureInfo; |
michael@0 | 158 | do_throw("Can't get signature before saver is complete."); |
michael@0 | 159 | } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { } |
michael@0 | 160 | |
michael@0 | 161 | saver.enableSignatureInfo(); |
michael@0 | 162 | saver.setTarget(destFile, false); |
michael@0 | 163 | yield promiseCopyToSaver(data, saver, true); |
michael@0 | 164 | |
michael@0 | 165 | saver.finish(Cr.NS_OK); |
michael@0 | 166 | yield completionPromise; |
michael@0 | 167 | |
michael@0 | 168 | // There's only one nsIX509CertList in the signature array. |
michael@0 | 169 | do_check_eq(1, saver.signatureInfo.length); |
michael@0 | 170 | let certLists = saver.signatureInfo.enumerate(); |
michael@0 | 171 | do_check_true(certLists.hasMoreElements()); |
michael@0 | 172 | let certList = certLists.getNext().QueryInterface(Ci.nsIX509CertList); |
michael@0 | 173 | do_check_false(certLists.hasMoreElements()); |
michael@0 | 174 | |
michael@0 | 175 | // Check that it has 3 certs. |
michael@0 | 176 | let certs = certList.getEnumerator(); |
michael@0 | 177 | do_check_true(certs.hasMoreElements()); |
michael@0 | 178 | let signer = certs.getNext().QueryInterface(Ci.nsIX509Cert); |
michael@0 | 179 | do_check_true(certs.hasMoreElements()); |
michael@0 | 180 | let issuer = certs.getNext().QueryInterface(Ci.nsIX509Cert); |
michael@0 | 181 | do_check_true(certs.hasMoreElements()); |
michael@0 | 182 | let root = certs.getNext().QueryInterface(Ci.nsIX509Cert); |
michael@0 | 183 | do_check_false(certs.hasMoreElements()); |
michael@0 | 184 | |
michael@0 | 185 | // Check that the certs have expected strings attached. |
michael@0 | 186 | let organization = "Microsoft Corporation"; |
michael@0 | 187 | do_check_eq("Microsoft Corporation", signer.commonName); |
michael@0 | 188 | do_check_eq(organization, signer.organization); |
michael@0 | 189 | do_check_eq("Copyright (c) 2002 Microsoft Corp.", signer.organizationalUnit); |
michael@0 | 190 | |
michael@0 | 191 | do_check_eq("Microsoft Code Signing PCA", issuer.commonName); |
michael@0 | 192 | do_check_eq(organization, issuer.organization); |
michael@0 | 193 | do_check_eq("Copyright (c) 2000 Microsoft Corp.", issuer.organizationalUnit); |
michael@0 | 194 | |
michael@0 | 195 | do_check_eq("Microsoft Root Authority", root.commonName); |
michael@0 | 196 | do_check_false(root.organization); |
michael@0 | 197 | do_check_eq("Copyright (c) 1997 Microsoft Corp.", root.organizationalUnit); |
michael@0 | 198 | |
michael@0 | 199 | // Clean up. |
michael@0 | 200 | destFile.remove(false); |
michael@0 | 201 | }); |
michael@0 | 202 | |
michael@0 | 203 | add_task(function test_teardown() |
michael@0 | 204 | { |
michael@0 | 205 | gStillRunning = false; |
michael@0 | 206 | }); |