Skip to content

Commit 799329a

Browse files
authored
Merge pull request #336 from gerardog/fix/2.4.2-issues
Release v2.4.3
2 parents 032691a + 3b68538 commit 799329a

17 files changed

+218
-87
lines changed

src/gsudo.Wrappers.Tests/Invoke-gsudo.Tests.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Describe "PS Invoke-Gsudo (PSv$($PSVersionTable.PSVersion.Major))" {
4343
}
4444

4545
It "It throws with .Net Exceptions" {
46-
{ Invoke-gsudo { [int]::Parse('foo') } } | Should -throw "*Input string was not*"
46+
{ Invoke-gsudo { [int]::Parse('foo') } } | Should -throw "*nput string*"
4747
}
4848

4949
It "It throws when ErrorAction = Stop" {

src/gsudo.Wrappers/gsudo

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
# For better experience (fix credentials cache) in git-bash/MinGw create this wrapper can be added as function in .bashrc:
99
# gsudo() { WSLENV=WSL_DISTRO_NAME:USER:$WSLENV MSYS_NO_PATHCONV=1 gsudo.exe "$@"; }
1010

11-
WSLENV=WSL_DISTRO_NAME:USER:$WSLENV MSYS_NO_PATHCONV=1 "$( dirname -- "$0")/gsudo.exe" "$@"
11+
thisdir="$(dirname "$(readlink "$0")")"
12+
WSLENV=WSL_DISTRO_NAME:USER:$WSLENV MSYS_NO_PATHCONV=1 "${thisdir}/gsudo.exe" "$@"

src/gsudo/AppSettings/Settings.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class Settings
8282

8383
public static RegistrySetting<string> ExceptionList { get; } =
8484
new RegistrySetting<string>(nameof(ExceptionList),
85-
defaultValue: "notepad.exe;powershell.exe;whoami.exe;",
85+
defaultValue: "notepad.exe;powershell.exe;whoami.exe;vim.exe;nano.exe;",
8686
deserializer: (string s)=>s,
8787
scope: RegistrySettingScope.GlobalOnly);
8888

@@ -127,8 +127,14 @@ internal static TimeSpan TimeSpanParseWithInfinite(string value)
127127
{
128128
if (value.In("-1", "Infinite"))
129129
return TimeSpan.MaxValue;
130-
else
131-
return TimeSpan.Parse(value, CultureInfo.InvariantCulture);
130+
131+
var timeSpan = TimeSpan.Parse(value, CultureInfo.InvariantCulture);
132+
133+
// Cap at 24 days.
134+
if (timeSpan.TotalDays > 24)
135+
return TimeSpan.MaxValue;
136+
137+
return timeSpan;
132138
}
133139

134140
internal static string TimeSpanWithInfiniteToString(TimeSpan value)

src/gsudo/Commands/RunCommand.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -231,15 +231,18 @@ private void AdjustUacIsolationRequest(ElevationRequest elevationRequest, bool i
231231
}
232232
}
233233

234-
internal static bool IsRunningAsDesiredUser()
234+
internal static bool IsRunningAsDesiredUser(bool allowHigherIntegrity = false)
235235
{
236236
if (InputArguments.TrustedInstaller && !WindowsIdentity.GetCurrent().Claims.Any(c => c.Value == Constants.TI_SID))
237237
return false;
238238

239239
if (InputArguments.RunAsSystem && !WindowsIdentity.GetCurrent().IsSystem)
240-
return false;
240+
return false;
241241

242-
if ((int)InputArguments.GetIntegrityLevel() != SecurityHelper.GetCurrentIntegrityLevel())
242+
if ((int)InputArguments.GetIntegrityLevel() != SecurityHelper.GetCurrentIntegrityLevel() && !allowHigherIntegrity)
243+
return false;
244+
245+
if ((int)InputArguments.GetIntegrityLevel() > SecurityHelper.GetCurrentIntegrityLevel())
243246
return false;
244247

245248
if (InputArguments.UserName != null && InputArguments.UserName != WindowsIdentity.GetCurrent().Name)

src/gsudo/Commands/ServiceCommand.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class ServiceCommand : ICommand, IDisposable
2828

2929
void EnableTimer()
3030
{
31+
if (CacheDuration > TimeSpan.FromDays(24)) CacheDuration = TimeSpan.FromDays(24);
32+
3133
if (CacheDuration != TimeSpan.MaxValue)
3234
ShutdownTimer.Change((int)CacheDuration.TotalMilliseconds, Timeout.Infinite);
3335
}
@@ -55,7 +57,7 @@ public async Task<int> Execute()
5557
|| (InputArguments.RunAsSystem && !System.Security.Principal.WindowsIdentity.GetCurrent().IsSystem)
5658
|| (InputArguments.UserName != null && !SecurityHelper.IsAdministrator() && SecurityHelper.IsMemberOfLocalAdmins())
5759
)*/
58-
if (!RunCommand.IsRunningAsDesiredUser())
60+
if (!RunCommand.IsRunningAsDesiredUser(allowHigherIntegrity: true))
5961
{
6062
Logger.Instance.Log("This service is not running with desired credentials. Starting a new service instance.", LogLevel.Info);
6163
#if DEBUG

src/gsudo/Commands/StatusCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ private static void PrintToConsole(Dictionary<string, object> result)
152152

153153
foreach (string s in result["CacheSessions"] as string[])
154154
{
155-
Console.WriteLine($" {s},");
155+
Console.WriteLine($" {s}");
156156
}
157157

158158
if ((bool)result["IsRedirected"])

src/gsudo/Helpers/ProcessFactory.cs

+38-8
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public static Process StartRedirected(string fileName, string arguments, string
6767

6868
public static Process StartAttached(string filename, string arguments)
6969
{
70-
Logger.Instance.Log($"Process Start: {filename} {arguments}", LogLevel.Debug );
70+
Logger.Instance.Log($"Process Start: {filename} {arguments}", LogLevel.Debug);
7171
var process = new Process();
7272
process.StartInfo = new ProcessStartInfo(filename)
7373
{
@@ -128,7 +128,7 @@ public static Process StartWithCredentials(string filename, string arguments, st
128128
CreateNoWindow = !InputArguments.Debug,
129129
});
130130
}
131-
catch(Win32Exception ex)
131+
catch (Win32Exception ex)
132132
{
133133
if (ex.NativeErrorCode == 1326)
134134
throw new ApplicationException("The user name or password is incorrect.");
@@ -332,7 +332,7 @@ private static SafeProcessHandle CreateProcessWithToken(IntPtr newToken, string
332332
return new SafeProcessHandle(processInformation.hProcess, true);
333333
}
334334

335-
internal static SafeProcessHandle CreateProcessAsUserWithFlags(string lpApplicationName, string args, ProcessApi.CreateProcessFlags dwCreationFlags, out PROCESS_INFORMATION pInfo)
335+
internal static void CreateProcessForTokenReplacement(string lpApplicationName, string args, ProcessApi.CreateProcessFlags dwCreationFlags, out SafeProcessHandle processHandle, out SafeHandle threadHandle, out int processId)
336336
{
337337
var sInfoEx = new ProcessApi.STARTUPINFOEX();
338338
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
@@ -342,7 +342,9 @@ internal static SafeProcessHandle CreateProcessAsUserWithFlags(string lpApplicat
342342
pSec.nLength = Marshal.SizeOf(pSec);
343343
tSec.nLength = Marshal.SizeOf(tSec);
344344

345-
// Set more restrictive Security Descriptor
345+
// Set a more restrictive Security Descriptor:
346+
// - This code runs at medium integrity, so we dont have permissions to change the SDACL to High integrity level.
347+
// - We will do that in TokenSwitcher.ReplaceProcessToken.
346348
string sddl = "D:(D;;GAFAWD;;;S-1-1-0)"; // Deny Generic-All, File-All, and Write-Dac to everyone.
347349

348350
IntPtr sd_ptr = new IntPtr();
@@ -354,15 +356,43 @@ internal static SafeProcessHandle CreateProcessAsUserWithFlags(string lpApplicat
354356

355357
var command = $"{lpApplicationName} {args}";
356358

357-
Logger.Instance.Log($"{nameof(CreateProcessAsUserWithFlags)}: {lpApplicationName} {args}", LogLevel.Debug);
359+
PROCESS_INFORMATION pInfo;
360+
Logger.Instance.Log($"Creating target process: {lpApplicationName} {args}", LogLevel.Debug);
358361
if (!ProcessApi.CreateProcess(null, command, ref pSec, ref tSec, false, dwCreationFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo))
359362
{
360363
throw new Win32Exception((int)ConsoleApi.GetLastError());
361364
}
362365

363-
return new SafeProcessHandle(pInfo.hProcess, true);
364-
}
366+
var currentProcessHandle = ProcessApi.GetCurrentProcess();
367+
368+
if (!DuplicateHandle(
369+
currentProcessHandle, // Source process handle is the current process
370+
pInfo.hProcess, // The handle to duplicate
371+
currentProcessHandle, // Target process handle is also the current process
372+
out var restrictedProcessHandle, // The duplicated handle with desired access rights
373+
0x1000 | 0x00100000 | 0x0001, // Desired access: PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE
374+
false, // The handle is not inheritable
375+
1)) // dwOptions: auto close pInfo.hProcess.
376+
{
377+
throw new Win32Exception(Marshal.GetLastWin32Error());
378+
}
365379

380+
if (!DuplicateHandle(
381+
currentProcessHandle, // Source process handle is the current process
382+
pInfo.hThread, // The thread handle to duplicate
383+
currentProcessHandle, // Target process handle is also the current process
384+
out var restrictedThreadHandle, // The duplicated handle with desired access rights
385+
0x0002, // Desired access: THREAD_SUSPEND_RESUME
386+
false, // The handle is not inheritable
387+
1)) // dwOptions: auto close pInfo.hThread.
388+
{
389+
throw new Win32Exception(Marshal.GetLastWin32Error());
390+
}
391+
392+
processHandle = new SafeProcessHandle(restrictedProcessHandle, true);
393+
threadHandle = new Native.SafeThreadHandle(restrictedThreadHandle);
394+
395+
processId = pInfo.dwProcessId;
396+
}
366397
}
367398
}
368-

src/gsudo/Helpers/ServiceHelper.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ public static async Task<ServiceLocation> FindAnyServiceFast()
6969
private static ServiceLocation FindServiceByIntegrity(int? clientPid, string user)
7070
{
7171
var anyIntegrity = InputArguments.UserName != null;
72-
var tryHighIntegrity = !InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value >= IntegrityLevel.High;
73-
var tryLowIntegrity = !InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value < IntegrityLevel.High;
72+
var tryHighIntegrity = !InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value > IntegrityLevel.Medium;
73+
var tryLowIntegrity = !InputArguments.IntegrityLevel.HasValue || InputArguments.IntegrityLevel.Value <= IntegrityLevel.Medium;
7474

7575
var targetUserSid = InputArguments.RunAsSystem ? "S-1-5-18" : InputArguments.UserSid;
7676

src/gsudo/Tokens/NativeMethods.cs src/gsudo/Native/NativeMethods.cs

+38-18
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
using gsudo.Native;
2-
using System;
1+
using System;
32
using System.Runtime.ConstrainedExecution;
43
using System.Runtime.InteropServices;
54
using System.Security.Principal;
65
using static gsudo.Native.TokensApi;
76

8-
namespace gsudo.Tokens
7+
namespace gsudo.Native
98
{
109
internal static partial class NativeMethods
1110
{
@@ -21,19 +20,19 @@ internal static partial class NativeMethods
2120
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
2221
internal const int ERROR_NOT_ALL_ASSIGNED = 0x00000514;
2322

24-
internal const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
25-
internal const UInt32 STANDARD_RIGHTS_READ = 0x00020000;
26-
internal const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;
27-
internal const UInt32 TOKEN_DUPLICATE = 0x0002;
28-
internal const UInt32 TOKEN_IMPERSONATE = 0x0004;
29-
internal const UInt32 TOKEN_QUERY = 0x0008;
30-
internal const UInt32 TOKEN_QUERY_SOURCE = 0x0010;
31-
internal const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
32-
internal const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;
33-
internal const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;
34-
internal const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
35-
internal const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
36-
internal const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
23+
internal const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
24+
internal const uint STANDARD_RIGHTS_READ = 0x00020000;
25+
internal const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
26+
internal const uint TOKEN_DUPLICATE = 0x0002;
27+
internal const uint TOKEN_IMPERSONATE = 0x0004;
28+
internal const uint TOKEN_QUERY = 0x0008;
29+
internal const uint TOKEN_QUERY_SOURCE = 0x0010;
30+
internal const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
31+
internal const uint TOKEN_ADJUST_GROUPS = 0x0040;
32+
internal const uint TOKEN_ADJUST_DEFAULT = 0x0080;
33+
internal const uint TOKEN_ADJUST_SESSIONID = 0x0100;
34+
internal const uint TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
35+
internal const uint TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
3736
TOKEN_ASSIGN_PRIMARY |
3837
TOKEN_DUPLICATE |
3938
TOKEN_IMPERSONATE |
@@ -42,7 +41,7 @@ internal static partial class NativeMethods
4241
TOKEN_ADJUST_PRIVILEGES |
4342
TOKEN_ADJUST_GROUPS |
4443
TOKEN_ADJUST_DEFAULT |
45-
TOKEN_ADJUST_SESSIONID);
44+
TOKEN_ADJUST_SESSIONID;
4645

4746
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
4847
internal static extern IntPtr GetCurrentProcess();
@@ -65,7 +64,13 @@ internal static extern bool OpenProcessToken(IntPtr processHandle,
6564

6665
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
6766
[return: MarshalAs(UnmanagedType.Bool)]
68-
internal static extern Boolean CloseHandle(IntPtr hObject);
67+
internal static extern bool CloseHandle(IntPtr hObject);
68+
69+
[DllImport("advapi32.dll", SetLastError = true)]
70+
internal static extern bool GetKernelObjectSecurity(IntPtr Handle, uint securityInformation, IntPtr pSecurityDescriptor, uint nLength, out uint lpnLengthNeeded);
71+
72+
[DllImport("advapi32.dll", SetLastError = true)]
73+
internal static extern bool SetKernelObjectSecurity(IntPtr Handle, uint securityInformation, IntPtr pSecurityDescriptor);
6974

7075
[StructLayout(LayoutKind.Sequential)]
7176
public struct LUID
@@ -100,5 +105,20 @@ public struct TOKEN_PRIVILEGES
100105

101106
public LUID_AND_ATTRIBUTES[] Privileges { get => privileges; set => privileges = value; }
102107
}
108+
103109
}
110+
111+
[Flags]
112+
internal enum SECURITY_INFORMATION : uint
113+
{
114+
OWNER_SECURITY_INFORMATION = 0x00000001,
115+
GROUP_SECURITY_INFORMATION = 0x00000002,
116+
DACL_SECURITY_INFORMATION = 0x00000004,
117+
SACL_SECURITY_INFORMATION = 0x00000008,
118+
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
119+
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
120+
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
121+
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
122+
}
123+
104124
}

src/gsudo/Native/ProcessApi.cs

+19-3
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,10 @@ public enum CreateProcessFlags : uint
120120
internal static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
121121

122122
[DllImport("kernel32.dll", SetLastError = true)]
123-
internal static extern bool CloseHandle(IntPtr hObject);
123+
internal static extern bool CloseHandle(IntPtr hObject);
124+
125+
[DllImport("kernel32.dll", SetLastError = true)]
126+
public static extern IntPtr LocalFree(IntPtr hMem);
124127

125128
[DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
126129
internal static extern bool GetExitCodeProcess(Microsoft.Win32.SafeHandles.SafeProcessHandle processHandle, out int exitCode);
@@ -190,7 +193,9 @@ protected override bool ReleaseHandle()
190193
#region Query Process Info
191194
public const UInt32 PROCESS_QUERY_INFORMATION = 0x0400;
192195
public const UInt32 PROCESS_SET_INFORMATION = 0x0200;
193-
196+
public const UInt32 READ_CONTROL = 0x00020000;
197+
public const UInt32 WRITE_DAC = 0x40000;
198+
194199
[DllImport("kernel32.dll", SetLastError = true)]
195200
internal static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Boolean bInheritHandle, UInt32 dwProcessId);
196201

@@ -225,13 +230,24 @@ internal static extern bool CheckRemoteDebuggerPresent(
225230
internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
226231

227232
[DllImport("kernel32.dll", SetLastError = true)]
228-
internal static extern uint ResumeThread(IntPtr hThread);
233+
internal static extern int ResumeThread(IntPtr hThread);
229234

230235
[DllImport("kernel32.dll", SetLastError = true)]
231236
[return: MarshalAs(UnmanagedType.Bool)]
232237
internal static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
233238

234239
[DllImport("kernel32.dll", SetLastError = true)]
235240
internal static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
241+
242+
[DllImport("kernel32.dll", SetLastError = true)]
243+
[return: MarshalAs(UnmanagedType.Bool)]
244+
internal static extern bool DuplicateHandle(
245+
IntPtr hSourceProcessHandle,
246+
IntPtr hSourceHandle,
247+
IntPtr hTargetProcessHandle,
248+
out IntPtr lpTargetHandle,
249+
uint dwDesiredAccess,
250+
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
251+
uint dwOptions);
236252
}
237253
}

src/gsudo/Native/SafeTokenHandle.cs

+15
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,19 @@ protected override bool ReleaseHandle()
2121
return Native.ProcessApi.CloseHandle(base.handle);
2222
}
2323
}
24+
25+
internal sealed class SafeThreadHandle : SafeHandleZeroOrMinusOneIsInvalid
26+
{
27+
internal SafeThreadHandle(IntPtr handle)
28+
: base(true)
29+
{
30+
base.SetHandle(handle);
31+
}
32+
33+
override protected bool ReleaseHandle()
34+
{
35+
return Native.ProcessApi.CloseHandle(handle);
36+
}
37+
38+
}
2439
}

src/gsudo/Native/TokensApi.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,19 @@ public struct LUID
414414
#endregion
415415

416416
[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
417-
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, uint StringSDRevision, out IntPtr SecurityDescriptor, out UIntPtr SecurityDescriptorSize);
418-
417+
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(
418+
string StringSecurityDescriptor,
419+
uint StringSDRevision,
420+
out IntPtr SecurityDescriptor,
421+
out UIntPtr SecurityDescriptorSize);
422+
423+
// Additional imports for DACL manipulation
424+
[DllImport("advapi32.dll", SetLastError = true)]
425+
internal static extern bool ConvertSecurityDescriptorToStringSecurityDescriptor(
426+
IntPtr SecurityDescriptor,
427+
uint StringSDRevision,
428+
SECURITY_INFORMATION SecurityInformation,
429+
out IntPtr StringSecurityDescriptor,
430+
out uint StringSecurityDescriptorLen);
419431
}
420432
}

0 commit comments

Comments
 (0)