Skip to content

Commit 7bc94de

Browse files
author
Stephen Halter
committed
Use Path.GetTempFileName() for 600 Unix file mode
1 parent d9591c9 commit 7bc94de

File tree

11 files changed

+246
-0
lines changed

11 files changed

+246
-0
lines changed

src/DataProtection/DataProtection/src/Repositories/FileSystemXmlRepository.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Linq;
8+
using System.Runtime.InteropServices;
89
using System.Xml.Linq;
910
using Microsoft.AspNetCore.DataProtection.Internal;
1011
using Microsoft.Extensions.Logging;
@@ -131,9 +132,17 @@ private void StoreElementCore(XElement element, string filename)
131132
// crashes mid-write, we won't end up with a corrupt .xml file.
132133

133134
Directory.Create(); // won't throw if the directory already exists
135+
134136
var tempFilename = Path.Combine(Directory.FullName, Guid.NewGuid().ToString() + ".tmp");
135137
var finalFilename = Path.Combine(Directory.FullName, filename + ".xml");
136138

139+
// Create a temp file with the correct Unix file mode before moving it to the expected finalFilename.
140+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
141+
{
142+
var tempTempFilename = Path.GetTempFileName();
143+
File.Move(tempTempFilename, tempFilename);
144+
}
145+
137146
try
138147
{
139148
using (var tempFileStream = File.OpenWrite(tempFilename))

src/DataProtection/DataProtection/test/Repositories/FileSystemXmlRepositoryTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Diagnostics;
56
using System.IO;
67
using System.Linq;
78
using System.Runtime.InteropServices;
@@ -158,6 +159,39 @@ public void Logs_DockerEphemeralFolders()
158159
});
159160
}
160161

162+
[ConditionalFact]
163+
[OSSkipCondition(OperatingSystems.Windows, SkipReason = "UnixFileMode is not supported on Windows.")]
164+
public void StoreElement_CreatesFileWithUserOnlyUnixFileMode()
165+
{
166+
WithUniqueTempDirectory(dirInfo =>
167+
{
168+
// Arrange
169+
var element = XElement.Parse("<element1 />");
170+
var repository = new FileSystemXmlRepository(dirInfo, NullLoggerFactory.Instance);
171+
172+
// Act
173+
repository.StoreElement(element, "friendly-name");
174+
175+
// Assert
176+
var fileInfo = Assert.Single(dirInfo.GetFiles());
177+
178+
//Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, fileInfo.UnixFileMode);
179+
var processStartInfo = new ProcessStartInfo
180+
{
181+
FileName = "ls",
182+
Arguments = $"-l {fileInfo.FullName}",
183+
RedirectStandardOutput = true,
184+
UseShellExecute = false
185+
};
186+
var process = Process.Start(processStartInfo);
187+
188+
Assert.NotNull(process);
189+
var output = process!.StandardOutput.ReadToEnd();
190+
process.WaitForExit();
191+
Assert.StartsWith("-rw-------", output);
192+
});
193+
}
194+
161195
/// <summary>
162196
/// Runs a test and cleans up the temp directory afterward.
163197
/// </summary>

src/Http/WebUtilities/src/FileBufferingReadStream.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Diagnostics;
77
using System.Diagnostics.CodeAnalysis;
88
using System.IO;
9+
using System.Runtime.InteropServices;
910
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Microsoft.AspNetCore.Internal;
@@ -258,6 +259,14 @@ private Stream CreateTempFile()
258259
}
259260

260261
_tempFileName = Path.Combine(_tempFileDirectory, "ASPNETCORE_" + Guid.NewGuid().ToString() + ".tmp");
262+
263+
// Create a temp file with the correct Unix file mode before moving it to the assigned _tempFileName in the _tempFileDirectory.
264+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
265+
{
266+
var tempTempFileName = Path.GetTempFileName();
267+
File.Move(tempTempFileName, _tempFileName);
268+
}
269+
261270
return new FileStream(_tempFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete, 1024 * 16,
262271
FileOptions.Asynchronous | FileOptions.DeleteOnClose | FileOptions.SequentialScan);
263272
}

src/Http/WebUtilities/src/FileBufferingWriteStream.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics.CodeAnalysis;
88
using System.IO;
99
using System.IO.Pipelines;
10+
using System.Runtime.InteropServices;
1011
using System.Threading;
1112
using System.Threading.Tasks;
1213
using Microsoft.AspNetCore.Internal;
@@ -271,6 +272,14 @@ private void EnsureFileStream()
271272
{
272273
var tempFileDirectory = _tempFileDirectoryAccessor();
273274
var tempFileName = Path.Combine(tempFileDirectory, "ASPNETCORE_" + Guid.NewGuid() + ".tmp");
275+
276+
// Create a temp file with the correct Unix file mode before moving it to the assigned tempFileName in the _tempFileDirectory.
277+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
278+
{
279+
var tempTempFileName = Path.GetTempFileName();
280+
File.Move(tempTempFileName, tempFileName);
281+
}
282+
274283
FileStream = new FileStream(
275284
tempFileName,
276285
FileMode.Create,

src/Http/WebUtilities/test/FileBufferingReadStreamTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
using System;
55
using System.Buffers;
6+
using System.Diagnostics;
67
using System.IO;
78
using System.Linq;
89
using System.Threading.Tasks;
10+
using Microsoft.AspNetCore.Testing;
911
using Moq;
1012
using Xunit;
1113

@@ -598,6 +600,47 @@ public async Task PartialReadAsyncThenSeekReplaysBuffer()
598600
Assert.Equal(data.AsMemory(0, read2).ToArray(), buffer2.AsMemory(0, read2).ToArray());
599601
}
600602

603+
[ConditionalFact]
604+
[OSSkipCondition(OperatingSystems.Windows, SkipReason = "UnixFileMode is not supported on Windows.")]
605+
public void Read_BufferingContentToDisk_CreatesFileWithUserOnlyUnixFileMode()
606+
{
607+
var inner = MakeStream(1024 * 2);
608+
string tempFileName;
609+
using (var stream = new FileBufferingReadStream(inner, 1024, null, GetCurrentDirectory()))
610+
{
611+
var bytes = new byte[1024 * 2];
612+
var read0 = stream.Read(bytes, 0, bytes.Length);
613+
Assert.Equal(bytes.Length, read0);
614+
Assert.Equal(read0, stream.Length);
615+
Assert.Equal(read0, stream.Position);
616+
Assert.False(stream.InMemory);
617+
Assert.NotNull(stream.TempFileName);
618+
619+
var read1 = stream.Read(bytes, 0, bytes.Length);
620+
Assert.Equal(0, read1);
621+
622+
tempFileName = stream.TempFileName!;
623+
Assert.True(File.Exists(tempFileName));
624+
625+
//Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, File.GetUnixFileMode(tempFileName));
626+
var processStartInfo = new ProcessStartInfo
627+
{
628+
FileName = "ls",
629+
Arguments = $"-l {tempFileName}",
630+
RedirectStandardOutput = true,
631+
UseShellExecute = false
632+
};
633+
var process = Process.Start(processStartInfo);
634+
635+
Assert.NotNull(process);
636+
var output = process!.StandardOutput.ReadToEnd();
637+
process.WaitForExit();
638+
Assert.StartsWith("-rw-------", output);
639+
}
640+
641+
Assert.False(File.Exists(tempFileName));
642+
}
643+
601644
private static string GetCurrentDirectory()
602645
{
603646
return AppContext.BaseDirectory;

src/Http/WebUtilities/test/FileBufferingWriteStreamTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
using System.Text;
99
using System.Threading.Tasks;
1010
using Microsoft.Extensions.Configuration;
11+
using Microsoft.AspNetCore.Testing;
1112
using Xunit;
13+
using System.Diagnostics;
1214

1315
namespace Microsoft.AspNetCore.WebUtilities
1416
{
@@ -371,6 +373,37 @@ public async Task DrainBufferAsync_WithContentInDisk_CopiesContentFromMemoryStre
371373
Assert.Equal(0, bufferingStream.Length);
372374
}
373375

376+
[ConditionalFact]
377+
[OSSkipCondition(OperatingSystems.Windows, SkipReason = "UnixFileMode is not supported on Windows.")]
378+
public void Write_BufferingContentToDisk_CreatesFileWithUserOnlyUnixFileMode()
379+
{
380+
// Arrange
381+
var input = new byte[] { 1, 2, 3, };
382+
using var bufferingStream = new FileBufferingWriteStream(memoryThreshold: 2, tempFileDirectoryAccessor: () => TempDirectory);
383+
bufferingStream.Write(input, 0, 2);
384+
385+
// Act
386+
bufferingStream.Write(input, 2, 1);
387+
388+
// Assert
389+
Assert.NotNull(bufferingStream.FileStream);
390+
391+
//Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, File.GetUnixFileMode(bufferingStream.FileStream.SafeFileHandle));
392+
var processStartInfo = new ProcessStartInfo
393+
{
394+
FileName = "ls",
395+
Arguments = $"-l {bufferingStream.FileStream!.Name}",
396+
RedirectStandardOutput = true,
397+
UseShellExecute = false
398+
};
399+
var process = Process.Start(processStartInfo);
400+
401+
Assert.NotNull(process);
402+
var output = process!.StandardOutput.ReadToEnd();
403+
process.WaitForExit();
404+
Assert.StartsWith("-rw-------", output);
405+
}
406+
374407
public void Dispose()
375408
{
376409
try

src/Shared/CertificateGeneration/CertificateManager.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics.Tracing;
88
using System.IO;
99
using System.Linq;
10+
using System.Runtime.InteropServices;
1011
using System.Security.Cryptography;
1112
using System.Security.Cryptography.X509Certificates;
1213
using System.Text;
@@ -539,6 +540,14 @@ internal void ExportCertificate(X509Certificate2 certificate, string path, bool
539540
try
540541
{
541542
Log.WriteCertificateToDisk(path);
543+
544+
// Create a temp file with the correct Unix file mode before moving it to the expected path.
545+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
546+
{
547+
var tempFilename = Path.GetTempFileName();
548+
File.Move(tempFilename, path, overwrite: true);
549+
}
550+
542551
File.WriteAllBytes(path, bytes);
543552
}
544553
catch (Exception ex) when (Log.IsEnabled())
@@ -559,6 +568,14 @@ internal void ExportCertificate(X509Certificate2 certificate, string path, bool
559568
{
560569
var keyPath = Path.ChangeExtension(path, ".key");
561570
Log.WritePemKeyToDisk(keyPath);
571+
572+
// Create a temp file with the correct Unix file mode before moving it to the expected path.
573+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
574+
{
575+
var tempFilename = Path.GetTempFileName();
576+
File.Move(tempFilename, keyPath, overwrite: true);
577+
}
578+
562579
File.WriteAllBytes(keyPath, pemEnvelope);
563580
}
564581
catch (Exception ex) when (Log.IsEnabled())

src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Diagnostics;
56
using System.IO;
67
using System.Linq;
78
using System.Runtime.InteropServices;
@@ -419,6 +420,51 @@ public void ListCertificates_AlwaysReturnsTheCertificate_WithHighestVersion()
419420
e.Oid.Value == "1.3.6.1.4.1.311.84.1.1" &&
420421
e.RawData[0] == 1);
421422
}
423+
424+
[ConditionalFact]
425+
[OSSkipCondition(OperatingSystems.Windows, SkipReason = "UnixFileMode is not supported on Windows.")]
426+
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "https://github.com/dotnet/aspnetcore/issues/6720")]
427+
public void EnsureCreateHttpsCertificate_CreatesFilesWithUserOnlyUnixFileMode()
428+
{
429+
_fixture.CleanupCertificates();
430+
431+
const string CertificateName = nameof(EnsureCreateHttpsCertificate_CreatesFilesWithUserOnlyUnixFileMode) + ".pem";
432+
const string KeyName = nameof(EnsureCreateHttpsCertificate_CreatesFilesWithUserOnlyUnixFileMode) + ".key";
433+
434+
var certificatePassword = Guid.NewGuid().ToString();
435+
var now = DateTimeOffset.UtcNow;
436+
now = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0, now.Offset);
437+
438+
var result = _manager
439+
.EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), CertificateName, trust: false, includePrivateKey: true, password: certificatePassword, keyExportFormat: CertificateKeyExportFormat.Pem, isInteractive: false);
440+
441+
Assert.Equal(EnsureCertificateResult.Succeeded, result);
442+
443+
Assert.True(File.Exists(CertificateName));
444+
//Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, File.GetUnixFileMode(CertificateName));
445+
AssertFileMode(CertificateName, "-rw-------");
446+
447+
Assert.True(File.Exists(KeyName));
448+
//Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, File.GetUnixFileMode(KeyName));
449+
AssertFileMode(KeyName, "-rw-------");
450+
}
451+
452+
private static void AssertFileMode(string path, string fileMode)
453+
{
454+
var processStartInfo = new ProcessStartInfo
455+
{
456+
FileName = "ls",
457+
Arguments = $"-l {path}",
458+
RedirectStandardOutput = true,
459+
UseShellExecute = false
460+
};
461+
var process = Process.Start(processStartInfo);
462+
463+
Assert.NotNull(process);
464+
var output = process!.StandardOutput.ReadToEnd();
465+
process.WaitForExit();
466+
Assert.StartsWith(fileMode, output);
467+
}
422468
}
423469

424470
public class CertFixture : IDisposable

src/Tools/dotnet-user-secrets/src/Internal/SecretsStore.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Linq;
8+
using System.Runtime.InteropServices;
89
using System.Text;
910
using Microsoft.Extensions.Configuration;
1011
using Microsoft.Extensions.Configuration.UserSecrets;
@@ -46,6 +47,9 @@ public string this[string key]
4647

4748
public int Count => _secrets.Count;
4849

50+
// For testing.
51+
internal string SecretsFilePath => _secretsFilePath;
52+
4953
public bool ContainsKey(string key) => _secrets.ContainsKey(key);
5054

5155
public IEnumerable<KeyValuePair<string, string>> AsEnumerable() => _secrets;
@@ -75,6 +79,13 @@ public virtual void Save()
7579
}
7680
}
7781

82+
// Create a temp file with the correct Unix file mode before moving it to the expected _filePath.
83+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
84+
{
85+
var tempFilename = Path.GetTempFileName();
86+
File.Move(tempFilename, _secretsFilePath, overwrite: true);
87+
}
88+
7889
File.WriteAllText(_secretsFilePath, contents.ToString(), Encoding.UTF8);
7990
}
8091

src/Tools/dotnet-user-secrets/src/Program.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public Program(IConsole console, string workingDirectory)
2929
_workingDirectory = workingDirectory;
3030
}
3131

32+
// For testing.
33+
internal string SecretsFilePath { get; private set; }
34+
3235
public bool TryRun(string[] args, out int returnCode)
3336
{
3437
try
@@ -91,6 +94,10 @@ internal int RunInternal(params string[] args)
9194
var store = new SecretsStore(userSecretsId, reporter);
9295
var context = new Internal.CommandContext(store, reporter, _console);
9396
options.Command.Execute(context);
97+
98+
// For testing.
99+
SecretsFilePath = store.SecretsFilePath;
100+
94101
return 0;
95102
}
96103

0 commit comments

Comments
 (0)