Skip to content

Commit e4cce51

Browse files
author
William Li
committed
Ensure the owner is root
1 parent d38e558 commit e4cce51

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

src/Cli/dotnet/StatInterop.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
using Microsoft.Win32.SafeHandles;
7+
8+
namespace Microsoft.DotNet.Cli
9+
{
10+
// https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Stat.cs
11+
12+
internal static class StatInterop
13+
{
14+
// Even though csc will by default use a sequential layout, a CS0649 warning as error
15+
// is produced for un-assigned fields when no StructLayout is specified.
16+
//
17+
// Explicitly saying Sequential disables that warning/error for consumers which only
18+
// use Stat in debug builds.
19+
[StructLayout(LayoutKind.Sequential)]
20+
internal struct FileStatus
21+
{
22+
internal FileStatusFlags Flags;
23+
internal int Mode;
24+
internal uint Uid;
25+
internal uint Gid;
26+
internal long Size;
27+
internal long ATime;
28+
internal long ATimeNsec;
29+
internal long MTime;
30+
internal long MTimeNsec;
31+
internal long CTime;
32+
internal long CTimeNsec;
33+
internal long BirthTime;
34+
internal long BirthTimeNsec;
35+
internal long Dev;
36+
internal long Ino;
37+
internal uint UserFlags;
38+
}
39+
40+
[Flags]
41+
internal enum Permissions
42+
{
43+
Mask = S_IRWXU | S_IRWXG | S_IRWXO,
44+
45+
S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR,
46+
S_IRUSR = 0x100,
47+
S_IWUSR = 0x80,
48+
S_IXUSR = 0x40,
49+
50+
S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP,
51+
S_IRGRP = 0x20,
52+
S_IWGRP = 0x10,
53+
S_IXGRP = 0x8,
54+
55+
S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH,
56+
S_IROTH = 0x4,
57+
S_IWOTH = 0x2,
58+
S_IXOTH = 0x1,
59+
60+
S_IXUGO = S_IXUSR | S_IXGRP | S_IXOTH,
61+
}
62+
63+
[Flags]
64+
internal enum FileStatusFlags
65+
{
66+
None = 0,
67+
HasBirthTime = 1,
68+
}
69+
70+
[DllImport("libSystem.Native", EntryPoint = "SystemNative_LStat", SetLastError = true)]
71+
internal static extern int LStat(string path, out FileStatus output);
72+
}
73+
}

src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IO;
77
using System.Linq;
88
using Microsoft.DotNet.Cli.Utils;
9+
using Microsoft.DotNet.Tools.Common;
910
using NuGet.Common;
1011
using NuGet.Configuration;
1112

@@ -36,6 +37,18 @@ public static void OverrideEnvironmentVariableToTmp(ParseResult parseResult)
3637
{
3738
if (!OperatingSystem.IsWindows() && IsRunningUnderSudo() && IsRunningWorkloadCommand(parseResult))
3839
{
40+
if (!TempHomeIsOnlyRootWritable(SudoHomeDirectory))
41+
{
42+
try
43+
{
44+
Directory.Delete(SudoHomeDirectory, recursive: true);
45+
}
46+
catch (DirectoryNotFoundException)
47+
{
48+
// Avoid read after write race condition
49+
}
50+
}
51+
3952
Directory.CreateDirectory(SudoHomeDirectory);
4053

4154
var homeBeforeOverride = Path.Combine(Environment.GetEnvironmentVariable("HOME"));
@@ -93,5 +106,31 @@ private static void CopyUserNuGetConfigToOverriddenHome(string homeBeforeOverrid
93106

94107
private static bool IsRunningWorkloadCommand(ParseResult parseResult) =>
95108
parseResult.RootSubCommandResult() == (WorkloadCommandParser.GetCommand().Name);
109+
110+
private static bool TempHomeIsOnlyRootWritable(string path)
111+
{
112+
if (StatInterop.LStat(path, out StatInterop.FileStatus fileStat) != 0)
113+
{
114+
return false;
115+
}
116+
117+
return IsOwnedByRoot(fileStat) && IsGroupWritable(fileStat) &&
118+
IsOtherUserWritable(fileStat);
119+
}
120+
121+
private static bool IsOtherUserWritable(StatInterop.FileStatus fileStat)
122+
{
123+
return (fileStat.Mode & (int) StatInterop.Permissions.S_IWOTH) == 0;
124+
}
125+
126+
private static bool IsGroupWritable(StatInterop.FileStatus fileStat)
127+
{
128+
return (fileStat.Mode & (int) StatInterop.Permissions.S_IWGRP) == 0;
129+
}
130+
131+
private static bool IsOwnedByRoot(StatInterop.FileStatus fileStat)
132+
{
133+
return fileStat.Uid == 0;
134+
}
96135
}
97136
}

0 commit comments

Comments
 (0)