mirror of https://github.com/winsw/winsw
Save credentials
parent
576cf6be55
commit
61fedfc0e0
|
@ -75,6 +75,7 @@ namespace WinSW.Native
|
|||
}
|
||||
else if (key == '\r')
|
||||
{
|
||||
Write(consoleOutput, Environment.NewLine);
|
||||
break;
|
||||
}
|
||||
else if (key == '\b')
|
||||
|
|
|
@ -7,7 +7,15 @@ namespace WinSW.Native
|
|||
{
|
||||
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_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")]
|
||||
internal static extern bool CredPackAuthenticationBuffer(
|
||||
|
@ -17,6 +25,9 @@ namespace WinSW.Native
|
|||
IntPtr packedCredentials,
|
||||
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")]
|
||||
internal static extern int CredUIPromptForWindowsCredentials(
|
||||
in CREDUI_INFO uiInfo,
|
||||
|
@ -41,6 +52,26 @@ namespace WinSW.Native
|
|||
string? password,
|
||||
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)]
|
||||
internal struct CREDUI_INFO
|
||||
{
|
||||
|
|
|
@ -7,6 +7,52 @@ namespace WinSW.Native
|
|||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
password ??= string.Empty;
|
||||
|
@ -52,12 +98,11 @@ namespace WinSW.Native
|
|||
|
||||
var info = new CREDUI_INFO
|
||||
{
|
||||
Size = Marshal.SizeOf(typeof(CREDUI_INFO)),
|
||||
Size = Marshal.SizeOf<CREDUI_INFO>(),
|
||||
CaptionText = caption,
|
||||
MessageText = message,
|
||||
};
|
||||
uint authPackage = 0;
|
||||
bool save = false;
|
||||
int error = CredUIPromptForWindowsCredentials(
|
||||
info,
|
||||
0,
|
||||
|
@ -67,10 +112,15 @@ namespace WinSW.Native
|
|||
out var outBuffer,
|
||||
out uint outBufferSize,
|
||||
ref save,
|
||||
CREDUIWIN_GENERIC);
|
||||
CREDUIWIN_GENERIC | CREDUIWIN_CHECKBOX);
|
||||
|
||||
if (error != Errors.ERROR_SUCCESS)
|
||||
{
|
||||
if (error == Errors.ERROR_CANCELLED)
|
||||
{
|
||||
Throw.Command.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* maxValueLength,
|
||||
int* securityDescriptorLength,
|
||||
out FILETIME lastWriteTime);
|
||||
|
||||
internal struct FILETIME
|
||||
{
|
||||
internal int LowDateTime;
|
||||
internal int HighDateTime;
|
||||
|
||||
public long ToTicks() => ((long)this.HighDateTime << 32) + this.LowDateTime;
|
||||
}
|
||||
out FileTime lastWriteTime);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace WinSW.Util
|
|||
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.");
|
||||
}
|
||||
|
||||
bool saveCredential = false;
|
||||
if (config.HasServiceAccount())
|
||||
{
|
||||
username = config.ServiceAccountUserName ?? username;
|
||||
|
@ -444,11 +445,16 @@ namespace WinSW
|
|||
switch (config.ServiceAccountPrompt)
|
||||
{
|
||||
case "dialog":
|
||||
if (!Credentials.Load($"WinSW:{config.Name}", out username, out password))
|
||||
{
|
||||
Credentials.PromptForCredentialsDialog(
|
||||
ref username,
|
||||
ref password,
|
||||
"Windows Service Wrapper",
|
||||
"Enter the service account credentials");
|
||||
"Enter the service account credentials",
|
||||
ref saveCredential);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "console":
|
||||
|
@ -472,6 +478,11 @@ namespace WinSW
|
|||
username,
|
||||
password);
|
||||
|
||||
if (saveCredential)
|
||||
{
|
||||
Credentials.Save($"WinSW:{config.Name}", username, password);
|
||||
}
|
||||
|
||||
string description = config.Description;
|
||||
if (description.Length != 0)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue