mirror of https://github.com/winsw/winsw
Save credentials
parent
576cf6be55
commit
61fedfc0e0
|
@ -75,6 +75,7 @@ namespace WinSW.Native
|
||||||
}
|
}
|
||||||
else if (key == '\r')
|
else if (key == '\r')
|
||||||
{
|
{
|
||||||
|
Write(consoleOutput, Environment.NewLine);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (key == '\b')
|
else if (key == '\b')
|
||||||
|
|
|
@ -7,7 +7,15 @@ namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class CredentialApis
|
internal static class CredentialApis
|
||||||
{
|
{
|
||||||
|
internal const uint CRED_PERSIST_LOCAL_MACHINE = 2;
|
||||||
|
|
||||||
|
internal const uint CRED_TYPE_GENERIC = 1;
|
||||||
|
|
||||||
internal const uint CREDUIWIN_GENERIC = 0x00000001;
|
internal const uint CREDUIWIN_GENERIC = 0x00000001;
|
||||||
|
internal const uint CREDUIWIN_CHECKBOX = 0x00000002;
|
||||||
|
|
||||||
|
[DllImport(Libraries.Advapi32, SetLastError = false)]
|
||||||
|
internal static extern unsafe void CredFree(CREDENTIALW* buffer);
|
||||||
|
|
||||||
[DllImport(Libraries.CredUI, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CredPackAuthenticationBufferW")]
|
[DllImport(Libraries.CredUI, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CredPackAuthenticationBufferW")]
|
||||||
internal static extern bool CredPackAuthenticationBuffer(
|
internal static extern bool CredPackAuthenticationBuffer(
|
||||||
|
@ -17,6 +25,9 @@ namespace WinSW.Native
|
||||||
IntPtr packedCredentials,
|
IntPtr packedCredentials,
|
||||||
ref int packedCredentialsSize);
|
ref int packedCredentialsSize);
|
||||||
|
|
||||||
|
[DllImport(Libraries.Advapi32, SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
|
internal static extern unsafe bool CredReadW(string targetName, uint type, uint flags, out CREDENTIALW* credential);
|
||||||
|
|
||||||
[DllImport(Libraries.CredUI, SetLastError = false, CharSet = CharSet.Unicode, EntryPoint = "CredUIPromptForWindowsCredentialsW")]
|
[DllImport(Libraries.CredUI, SetLastError = false, CharSet = CharSet.Unicode, EntryPoint = "CredUIPromptForWindowsCredentialsW")]
|
||||||
internal static extern int CredUIPromptForWindowsCredentials(
|
internal static extern int CredUIPromptForWindowsCredentials(
|
||||||
in CREDUI_INFO uiInfo,
|
in CREDUI_INFO uiInfo,
|
||||||
|
@ -41,6 +52,26 @@ namespace WinSW.Native
|
||||||
string? password,
|
string? password,
|
||||||
ref int maxPassword);
|
ref int maxPassword);
|
||||||
|
|
||||||
|
[DllImport(Libraries.Advapi32, SetLastError = true)]
|
||||||
|
internal static extern bool CredWriteW(in CREDENTIALW credential, uint flags);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal unsafe struct CREDENTIALW
|
||||||
|
{
|
||||||
|
internal uint Flags;
|
||||||
|
internal uint Type;
|
||||||
|
internal char* TargetName;
|
||||||
|
internal char* Comment;
|
||||||
|
internal FileTime LastWritten;
|
||||||
|
internal int CredentialBlobSize;
|
||||||
|
internal IntPtr CredentialBlob;
|
||||||
|
internal uint Persist;
|
||||||
|
internal uint AttributeCount;
|
||||||
|
internal uint Attributes;
|
||||||
|
internal char* TargetAlias;
|
||||||
|
internal char* UserName;
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
internal struct CREDUI_INFO
|
internal struct CREDUI_INFO
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,52 @@ namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class Credentials
|
internal static class Credentials
|
||||||
{
|
{
|
||||||
|
internal static unsafe bool Load(string targetName, out string? userName, out string? password)
|
||||||
|
{
|
||||||
|
if (!CredReadW(targetName, CRED_TYPE_GENERIC, 0, out var credential))
|
||||||
|
{
|
||||||
|
userName = null;
|
||||||
|
password = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
userName = Marshal.PtrToStringUni((IntPtr)credential->UserName);
|
||||||
|
password = Marshal.PtrToStringUni(credential->CredentialBlob, credential->CredentialBlobSize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CredFree(credential);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static unsafe void Save(string targetName, string? userName, string? password)
|
||||||
|
{
|
||||||
|
#pragma warning disable SA1519 // Braces should not be omitted from multi-line child statement
|
||||||
|
fixed (char* targetNamePtr = targetName)
|
||||||
|
fixed (char* userNamePtr = userName)
|
||||||
|
fixed (char* passwordPtr = password)
|
||||||
|
{
|
||||||
|
var credential = new CREDENTIALW
|
||||||
|
{
|
||||||
|
Type = CRED_TYPE_GENERIC,
|
||||||
|
TargetName = targetNamePtr,
|
||||||
|
CredentialBlobSize = password?.Length * sizeof(char) ?? 0,
|
||||||
|
CredentialBlob = (IntPtr)passwordPtr,
|
||||||
|
Persist = CRED_PERSIST_LOCAL_MACHINE,
|
||||||
|
UserName = userNamePtr,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!CredWriteW(credential, 0))
|
||||||
|
{
|
||||||
|
Throw.Command.Win32Exception("Failed to save credential.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore SA1519 // Braces should not be omitted from multi-line child statement
|
||||||
|
}
|
||||||
|
|
||||||
internal static void PromptForCredentialsConsole(ref string? userName, ref string? password)
|
internal static void PromptForCredentialsConsole(ref string? userName, ref string? password)
|
||||||
{
|
{
|
||||||
using var consoleOutput = ConsoleEx.OpenConsoleOutput();
|
using var consoleOutput = ConsoleEx.OpenConsoleOutput();
|
||||||
|
@ -24,7 +70,7 @@ namespace WinSW.Native
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void PromptForCredentialsDialog(ref string? userName, ref string? password, string caption, string message)
|
internal static void PromptForCredentialsDialog(ref string? userName, ref string? password, string caption, string message, ref bool save)
|
||||||
{
|
{
|
||||||
userName ??= string.Empty;
|
userName ??= string.Empty;
|
||||||
password ??= string.Empty;
|
password ??= string.Empty;
|
||||||
|
@ -52,12 +98,11 @@ namespace WinSW.Native
|
||||||
|
|
||||||
var info = new CREDUI_INFO
|
var info = new CREDUI_INFO
|
||||||
{
|
{
|
||||||
Size = Marshal.SizeOf(typeof(CREDUI_INFO)),
|
Size = Marshal.SizeOf<CREDUI_INFO>(),
|
||||||
CaptionText = caption,
|
CaptionText = caption,
|
||||||
MessageText = message,
|
MessageText = message,
|
||||||
};
|
};
|
||||||
uint authPackage = 0;
|
uint authPackage = 0;
|
||||||
bool save = false;
|
|
||||||
int error = CredUIPromptForWindowsCredentials(
|
int error = CredUIPromptForWindowsCredentials(
|
||||||
info,
|
info,
|
||||||
0,
|
0,
|
||||||
|
@ -67,10 +112,15 @@ namespace WinSW.Native
|
||||||
out var outBuffer,
|
out var outBuffer,
|
||||||
out uint outBufferSize,
|
out uint outBufferSize,
|
||||||
ref save,
|
ref save,
|
||||||
CREDUIWIN_GENERIC);
|
CREDUIWIN_GENERIC | CREDUIWIN_CHECKBOX);
|
||||||
|
|
||||||
if (error != Errors.ERROR_SUCCESS)
|
if (error != Errors.ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
if (error == Errors.ERROR_CANCELLED)
|
||||||
|
{
|
||||||
|
Throw.Command.Win32Exception(error);
|
||||||
|
}
|
||||||
|
|
||||||
throw new Win32Exception(error);
|
throw new Win32Exception(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WinSW.Native
|
||||||
|
{
|
||||||
|
internal struct FileTime
|
||||||
|
{
|
||||||
|
internal int LowDateTime;
|
||||||
|
internal int HighDateTime;
|
||||||
|
|
||||||
|
public DateTime ToDateTime() => DateTime.FromFileTime(((long)this.HighDateTime << 32) + this.LowDateTime);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,14 +18,6 @@ namespace WinSW.Native
|
||||||
int* maxValueNameLength,
|
int* maxValueNameLength,
|
||||||
int* maxValueLength,
|
int* maxValueLength,
|
||||||
int* securityDescriptorLength,
|
int* securityDescriptorLength,
|
||||||
out FILETIME lastWriteTime);
|
out FileTime lastWriteTime);
|
||||||
|
|
||||||
internal struct FILETIME
|
|
||||||
{
|
|
||||||
internal int LowDateTime;
|
|
||||||
internal int HighDateTime;
|
|
||||||
|
|
||||||
public long ToTicks() => ((long)this.HighDateTime << 32) + this.LowDateTime;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace WinSW.Util
|
||||||
Throw.Command.Win32Exception(error, "Failed to query registry key.");
|
Throw.Command.Win32Exception(error, "Failed to query registry key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return DateTime.FromFileTime(lastWriteTime.ToTicks());
|
return lastWriteTime.ToDateTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -434,6 +434,7 @@ namespace WinSW
|
||||||
Throw.Command.Win32Exception(Errors.ERROR_SERVICE_EXISTS, "Failed to install the service.");
|
Throw.Command.Win32Exception(Errors.ERROR_SERVICE_EXISTS, "Failed to install the service.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool saveCredential = false;
|
||||||
if (config.HasServiceAccount())
|
if (config.HasServiceAccount())
|
||||||
{
|
{
|
||||||
username = config.ServiceAccountUserName ?? username;
|
username = config.ServiceAccountUserName ?? username;
|
||||||
|
@ -444,11 +445,16 @@ namespace WinSW
|
||||||
switch (config.ServiceAccountPrompt)
|
switch (config.ServiceAccountPrompt)
|
||||||
{
|
{
|
||||||
case "dialog":
|
case "dialog":
|
||||||
Credentials.PromptForCredentialsDialog(
|
if (!Credentials.Load($"WinSW:{config.Name}", out username, out password))
|
||||||
ref username,
|
{
|
||||||
ref password,
|
Credentials.PromptForCredentialsDialog(
|
||||||
"Windows Service Wrapper",
|
ref username,
|
||||||
"Enter the service account credentials");
|
ref password,
|
||||||
|
"Windows Service Wrapper",
|
||||||
|
"Enter the service account credentials",
|
||||||
|
ref saveCredential);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "console":
|
case "console":
|
||||||
|
@ -472,6 +478,11 @@ namespace WinSW
|
||||||
username,
|
username,
|
||||||
password);
|
password);
|
||||||
|
|
||||||
|
if (saveCredential)
|
||||||
|
{
|
||||||
|
Credentials.Save($"WinSW:{config.Name}", username, password);
|
||||||
|
}
|
||||||
|
|
||||||
string description = config.Description;
|
string description = config.Description;
|
||||||
if (description.Length != 0)
|
if (description.Length != 0)
|
||||||
{
|
{
|
||||||
|
@ -508,7 +519,7 @@ namespace WinSW
|
||||||
EventLog.CreateEventSource(eventLogSource, "Application");
|
EventLog.CreateEventSource(eventLogSource, "Application");
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info($"Service '{config.Format()}' was installed successfully.");
|
Log.Info($"Service '{config.Format()}' was installed successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Uninstall(string? pathToConfig, bool noElevate)
|
void Uninstall(string? pathToConfig, bool noElevate)
|
||||||
|
|
Loading…
Reference in New Issue