1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
|
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
namespace WixToolset
{
using System.IO;
using WixToolset.Data;
/// <summary>
/// Converts a wixout representation of an MSM database into a ComponentGroup the form of WiX source.
/// </summary>
public sealed class Inscriber : IMessageHandler
{
// private TempFileCollection tempFiles;
private TableDefinitionCollection tableDefinitions;
public Inscriber()
{
this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions());
}
/// <summary>
/// Gets or sets the temp files collection.
/// </summary>
/// <value>The temp files collection.</value>
// public TempFileCollection TempFiles
// {
// get { return this.tempFiles; }
// set { this.tempFiles = value; }
// }
/// <summary>
/// Gets or sets the path to the temp files location.
/// </summary>
/// <value>The path to the temp files location.</value>
public string TempFilesLocation
{
get
{
// if (null == this.tempFiles)
// {
// return null;
// }
// else
// {
// return this.tempFiles.BasePath;
// }
return Path.GetTempPath();
}
// set
// {
// this.DeleteTempFiles();
// if (null == value)
// {
// this.tempFiles = new TempFileCollection();
// }
// else
// {
// this.tempFiles = new TempFileCollection(value);
// }
// // ensure the base path exists
// Directory.CreateDirectory(this.tempFiles.BasePath);
// }
}
/// <summary>
/// Extracts engine from attached container and updates engine with detached container signatures.
/// </summary>
/// <param name="bundleFile">Bundle with attached container.</param>
/// <param name="outputFile">Bundle engine only.</param>
/// <returns>True if bundle was updated.</returns>
public bool InscribeBundleEngine(string bundleFile, string outputFile)
{
//string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_unsigned.exe");
//using (BurnReader reader = BurnReader.Open(bundleFile))
//using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
//{
// reader.Stream.Seek(0, SeekOrigin.Begin);
// byte[] buffer = new byte[4 * 1024];
// int total = 0;
// int read = 0;
// do
// {
// read = Math.Min(buffer.Length, (int)reader.EngineSize - total);
// read = reader.Stream.Read(buffer, 0, read);
// writer.Write(buffer, 0, read);
// total += read;
// } while (total < reader.EngineSize && 0 < read);
// if (total != reader.EngineSize)
// {
// throw new InvalidOperationException("Failed to copy engine out of bundle.");
// }
// // TODO: update writer with detached container signatures.
//}
//Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
//if (File.Exists(outputFile))
//{
// File.Delete(outputFile);
//}
//File.Move(tempFile, outputFile);
//WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
return true;
}
/// <summary>
/// Updates engine with attached container information and adds attached container again.
/// </summary>
/// <param name="bundleFile">Bundle with attached container.</param>
/// <param name="signedEngineFile">Signed bundle engine.</param>
/// <param name="outputFile">Signed engine with attached container.</param>
/// <returns>True if bundle was updated.</returns>
public bool InscribeBundle(string bundleFile, string signedEngineFile, string outputFile)
{
//bool inscribed = false;
//string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_signed.exe");
//using (BurnReader reader = BurnReader.Open(bundleFile))
//{
// File.Copy(signedEngineFile, tempFile, true);
// // If there was an attached container on the original (unsigned) bundle, put it back.
// if (reader.AttachedContainerSize > 0)
// {
// reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
// using (BurnWriter writer = BurnWriter.Open(tempFile))
// {
// writer.RememberThenResetSignature();
// writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
// inscribed = true;
// }
// }
//}
//Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
//if (File.Exists(outputFile))
//{
// File.Delete(outputFile);
//}
//File.Move(tempFile, outputFile);
//WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
//return inscribed;
return false;
}
/// <summary>
/// Updates database with signatures from external cabinets.
/// </summary>
/// <param name="databaseFile">Path to MSI database.</param>
/// <param name="outputFile">Ouput for updated MSI database.</param>
/// <param name="tidy">Clean up files.</param>
/// <returns>True if database is updated.</returns>
public bool InscribeDatabase(string databaseFile, string outputFile, bool tidy)
{
//// Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered
//bool foundUnsignedExternals = false;
//bool shouldCommit = false;
//FileAttributes attributes = File.GetAttributes(databaseFile);
//if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
//{
// this.OnMessage(WixErrors.ReadOnlyOutputFile(databaseFile));
// return shouldCommit;
//}
//using (Database database = new Database(databaseFile, OpenDatabase.Transact))
//{
// // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
// int codepage = 1252;
// // list of certificates for this database (hash/identifier)
// Dictionary<string, string> certificates = new Dictionary<string, string>();
// // Reset the in-memory tables for this new database
// Table digitalSignatureTable = new Table(null, this.tableDefinitions["MsiDigitalSignature"]);
// Table digitalCertificateTable = new Table(null, this.tableDefinitions["MsiDigitalCertificate"]);
// // If any digital signature records exist that are not of the media type, preserve them
// if (database.TableExists("MsiDigitalSignature"))
// {
// using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
// {
// while (true)
// {
// using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
// {
// if (null == digitalSignatureRecord)
// {
// break;
// }
// Row digitalSignatureRow = null;
// digitalSignatureRow = digitalSignatureTable.CreateRow(null);
// string table = digitalSignatureRecord.GetString(0);
// string signObject = digitalSignatureRecord.GetString(1);
// digitalSignatureRow[0] = table;
// digitalSignatureRow[1] = signObject;
// digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
// if (false == digitalSignatureRecord.IsNull(3))
// {
// // Export to a file, because the MSI API's require us to provide a file path on disk
// string hashPath = Path.Combine(this.TempFilesLocation, "MsiDigitalSignature");
// string hashFileName = string.Concat(table, ".", signObject, ".bin");
// Directory.CreateDirectory(hashPath);
// hashPath = Path.Combine(hashPath, hashFileName);
// using (FileStream fs = File.Create(hashPath))
// {
// int bytesRead;
// byte[] buffer = new byte[1024 * 4];
// while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
// {
// fs.Write(buffer, 0, bytesRead);
// }
// }
// digitalSignatureRow[3] = hashFileName;
// }
// }
// }
// }
// }
// // If any digital certificates exist, extract and preserve them
// if (database.TableExists("MsiDigitalCertificate"))
// {
// using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
// {
// while (true)
// {
// using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
// {
// if (null == digitalCertificateRecord)
// {
// break;
// }
// string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
// // Export to a file, because the MSI API's require us to provide a file path on disk
// string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
// Directory.CreateDirectory(certPath);
// certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
// using (FileStream fs = File.Create(certPath))
// {
// int bytesRead;
// byte[] buffer = new byte[1024 * 4];
// while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
// {
// fs.Write(buffer, 0, bytesRead);
// }
// }
// // Add it to our "add to MsiDigitalCertificate" table dictionary
// Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
// digitalCertificateRow[0] = certificateId;
// // Now set the file path on disk where this binary stream will be picked up at import time
// digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
// // Load the cert to get it's thumbprint
// X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
// X509Certificate2 cert2 = new X509Certificate2(cert);
// certificates.Add(cert2.Thumbprint, certificateId);
// }
// }
// }
// }
// using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
// {
// while (true)
// {
// using (Record mediaRecord = mediaView.Fetch())
// {
// if (null == mediaRecord)
// {
// break;
// }
// X509Certificate2 cert2 = null;
// Row digitalSignatureRow = null;
// string cabName = mediaRecord.GetString(4); // get the name of the cab
// // If there is no cabinet or it's an internal cab, skip it.
// if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
// {
// continue;
// }
// string cabId = mediaRecord.GetString(1); // get the ID of the cab
// string cabPath = Path.Combine(Path.GetDirectoryName(databaseFile), cabName);
// // If the cabs aren't there, throw an error but continue to catch the other errors
// if (!File.Exists(cabPath))
// {
// this.OnMessage(WixErrors.WixFileNotFound(cabPath));
// continue;
// }
// try
// {
// // Get the certificate from the cab
// X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
// cert2 = new X509Certificate2(signedFileCert);
// }
// catch (System.Security.Cryptography.CryptographicException e)
// {
// uint HResult = unchecked((uint)Marshal.GetHRForException(e));
// // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
// if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
// {
// foundUnsignedExternals = true;
// continue;
// }
// // todo: exactly which HRESULT corresponds to this issue?
// // If it's one of these exact platforms, warn the user that it may be due to their OS.
// if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
// (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
// {
// this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
// }
// else // otherwise, generic error
// {
// this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
// }
// }
// // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
// if (!certificates.ContainsKey(cert2.Thumbprint))
// {
// // generate a stable identifier
// string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
// // Add it to our "add to MsiDigitalCertificate" table dictionary
// Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
// digitalCertificateRow[0] = certificateGeneratedId;
// // Export to a file, because the MSI API's require us to provide a file path on disk
// string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
// Directory.CreateDirectory(certPath);
// certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
// File.Delete(certPath);
// using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
// {
// writer.Write(cert2.RawData);
// writer.Close();
// }
// // Now set the file path on disk where this binary stream will be picked up at import time
// digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
// certificates.Add(cert2.Thumbprint, certificateGeneratedId);
// }
// digitalSignatureRow = digitalSignatureTable.CreateRow(null);
// digitalSignatureRow[0] = "Media";
// digitalSignatureRow[1] = cabId;
// digitalSignatureRow[2] = certificates[cert2.Thumbprint];
// }
// }
// }
// if (digitalCertificateTable.Rows.Count > 0)
// {
// database.ImportTable(codepage, digitalCertificateTable, this.TempFilesLocation, true);
// shouldCommit = true;
// }
// if (digitalSignatureTable.Rows.Count > 0)
// {
// database.ImportTable(codepage, digitalSignatureTable, this.TempFilesLocation, true);
// shouldCommit = true;
// }
// // TODO: if we created the table(s), then we should add the _Validation records for them.
// certificates = null;
// // If we did find external cabs but none of them were signed, give a warning
// if (foundUnsignedExternals)
// {
// this.OnMessage(WixWarnings.ExternalCabsAreNotSigned(databaseFile));
// }
// if (shouldCommit)
// {
// database.Commit();
// }
//}
//return shouldCommit;
return false;
}
/// <summary>
/// Cleans up the temp files used by the Inscriber.
/// </summary>
/// <returns>True if all files were deleted, false otherwise.</returns>
public bool DeleteTempFiles()
{
#if REDO_IN_NETCORE
if (null == this.tempFiles)
{
return true; // no work to do
}
else
{
bool deleted = Common.DeleteTempFiles(this.TempFilesLocation, this);
if (deleted)
{
((IDisposable)this.tempFiles).Dispose();
this.tempFiles = null; // temp files have been deleted, no need to remember this now
}
return deleted;
}
#endif
return true;
}
public void OnMessage(MessageEventArgs e)
{
Messaging.Instance.OnMessage(e);
}
}
}
|