|
4 | 4 | using System;
|
5 | 5 | using System.CommandLine.Parsing;
|
6 | 6 | using System.IO;
|
| 7 | +using System.Linq; |
| 8 | +using Microsoft.DotNet.Cli.Utils; |
| 9 | +using Microsoft.DotNet.Tools.Common; |
| 10 | +using NuGet.Common; |
| 11 | +using NuGet.Configuration; |
7 | 12 |
|
8 | 13 | namespace Microsoft.DotNet.Cli
|
9 | 14 | {
|
@@ -32,12 +37,100 @@ public static void OverrideEnvironmentVariableToTmp(ParseResult parseResult)
|
32 | 37 | {
|
33 | 38 | if (!OperatingSystem.IsWindows() && IsRunningUnderSudo() && IsRunningWorkloadCommand(parseResult))
|
34 | 39 | {
|
| 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 | + |
35 | 52 | Directory.CreateDirectory(SudoHomeDirectory);
|
| 53 | + |
| 54 | + var homeBeforeOverride = Path.Combine(Environment.GetEnvironmentVariable("HOME")); |
36 | 55 | Environment.SetEnvironmentVariable("HOME", SudoHomeDirectory);
|
| 56 | + |
| 57 | + CopyUserNuGetConfigToOverriddenHome(homeBeforeOverride); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + /// <summary> |
| 62 | + /// To make NuGet honor the user's NuGet config file. |
| 63 | + /// Copying instead of using the file directoy to avoid existing file being set higher permission |
| 64 | + /// Try to delete the existing NuGet config file in "/tmp/dotnet_sudo_home/" |
| 65 | + /// to avoid different user's NuGet config getting mixed. |
| 66 | + /// </summary> |
| 67 | + private static void CopyUserNuGetConfigToOverriddenHome(string homeBeforeOverride) |
| 68 | + { |
| 69 | + // https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Common/PathUtil/NuGetEnvironment.cs#L139 |
| 70 | + // home is cache in NuGet we cannot directly use the call |
| 71 | + var userSettingsDir = Path.Combine(homeBeforeOverride, ".nuget", "NuGet"); |
| 72 | + |
| 73 | + string userNuGetConfig = Settings.OrderedSettingsFileNames |
| 74 | + .Select(fileName => Path.Combine(userSettingsDir, fileName)) |
| 75 | + .FirstOrDefault(f => File.Exists(f)); |
| 76 | + |
| 77 | + var overridenSettingsDir = NuGetEnvironment.GetFolderPath(NuGetFolderPath.UserSettingsDirectory); |
| 78 | + var overridenNugetConfig = Path.Combine(overridenSettingsDir, Settings.DefaultSettingsFileName); |
| 79 | + |
| 80 | + if (File.Exists(overridenNugetConfig)) |
| 81 | + { |
| 82 | + try |
| 83 | + { |
| 84 | + FileAccessRetrier.RetryOnIOException( |
| 85 | + () => File.Delete(overridenNugetConfig)); |
| 86 | + } |
| 87 | + catch |
| 88 | + { |
| 89 | + // best effort to remove |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + if (userNuGetConfig != default) |
| 94 | + { |
| 95 | + try |
| 96 | + { |
| 97 | + FileAccessRetrier.RetryOnIOException( |
| 98 | + () => File.Copy(userNuGetConfig, overridenNugetConfig, overwrite: true)); |
| 99 | + } |
| 100 | + catch |
| 101 | + { |
| 102 | + // best effort to copy |
| 103 | + } |
37 | 104 | }
|
38 | 105 | }
|
39 | 106 |
|
40 | 107 | private static bool IsRunningWorkloadCommand(ParseResult parseResult) =>
|
41 | 108 | 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) && GroupCannotWrite(fileStat) && |
| 118 | + OtherUserCannotWrite(fileStat); |
| 119 | + } |
| 120 | + |
| 121 | + private static bool OtherUserCannotWrite(StatInterop.FileStatus fileStat) |
| 122 | + { |
| 123 | + return (fileStat.Mode & (int) StatInterop.Permissions.S_IWOTH) == 0; |
| 124 | + } |
| 125 | + |
| 126 | + private static bool GroupCannotWrite(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 | + } |
42 | 135 | }
|
43 | 136 | }
|
0 commit comments