Allow prompting for credentials

pull/579/head
NextTurn 2018-11-28 00:00:00 +08:00 committed by Next Turn
parent 5efcc8aca5
commit afe25d1d82
5 changed files with 183 additions and 6 deletions

View File

@ -207,13 +207,24 @@ namespace winsw
allowServiceLogonRight = true;
}
}
else
{
if (descriptor.HasServiceAccount())
else if (descriptor.HasServiceAccount())
{
username = descriptor.ServiceAccountUserName;
password = descriptor.ServiceAccountPassword;
allowServiceLogonRight = descriptor.AllowServiceAcountLogonRight;
if (username is null || password is null)
{
switch (descriptor.ServiceAccountPrompt)
{
case "dialog":
PropmtForCredentialsDialog();
break;
case "console":
PromptForCredentialsConsole();
break;
}
}
}
@ -257,6 +268,116 @@ namespace winsw
{
EventLog.CreateEventSource(eventLogSource, "Application");
}
void PropmtForCredentialsDialog()
{
username ??= string.Empty;
password ??= string.Empty;
int inBufferSize = 0;
_ = CredentialApis.CredPackAuthenticationBuffer(
0,
username,
password,
IntPtr.Zero,
ref inBufferSize);
IntPtr inBuffer = Marshal.AllocCoTaskMem(inBufferSize);
try
{
if (!CredentialApis.CredPackAuthenticationBuffer(
0,
username,
password,
inBuffer,
ref inBufferSize))
{
Throw.Win32Exception("Failed to pack auth buffer.");
}
CredentialApis.CREDUI_INFO info = new CredentialApis.CREDUI_INFO
{
Size = Marshal.SizeOf(typeof(CredentialApis.CREDUI_INFO)),
CaptionText = "Windows Service Wrapper", // TODO
MessageText = "service account credentials", // TODO
};
uint authPackage = 0;
bool save = false;
int error = CredentialApis.CredUIPromptForWindowsCredentials(
info,
0,
ref authPackage,
inBuffer,
inBufferSize,
out IntPtr outBuffer,
out uint outBufferSize,
ref save,
CredentialApis.CREDUIWIN_GENERIC);
if (error != Errors.ERROR_SUCCESS)
{
throw new Win32Exception(error);
}
try
{
int userNameLength = 0;
int passwordLength = 0;
_ = CredentialApis.CredUnPackAuthenticationBuffer(
0,
outBuffer,
outBufferSize,
null,
ref userNameLength,
default,
default,
null,
ref passwordLength);
username = userNameLength == 0 ? null : new string('\0', userNameLength - 1);
password = passwordLength == 0 ? null : new string('\0', passwordLength - 1);
if (!CredentialApis.CredUnPackAuthenticationBuffer(
0,
outBuffer,
outBufferSize,
username,
ref userNameLength,
default,
default,
password,
ref passwordLength))
{
Throw.Win32Exception("Failed to unpack auth buffer.");
}
}
finally
{
Marshal.FreeCoTaskMem(outBuffer);
}
}
finally
{
Marshal.FreeCoTaskMem(inBuffer);
}
}
void PromptForCredentialsConsole()
{
if (username is null)
{
Console.Write("Username: ");
username = Console.ReadLine();
}
if (password is null)
{
Console.Write("Password: ");
password = ReadPassword();
}
Console.WriteLine();
}
}
void Uninstall()

View File

@ -0,0 +1,52 @@
using System;
using System.Runtime.InteropServices;
namespace winsw.Native
{
internal static class CredentialApis
{
internal const uint CREDUIWIN_GENERIC = 0x00000001;
[DllImport(Libraries.CredUI, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CredPackAuthenticationBufferW")]
internal static extern bool CredPackAuthenticationBuffer(
uint flags,
string userName,
string password,
IntPtr packedCredentials,
ref int packedCredentialsSize);
[DllImport(Libraries.CredUI, SetLastError = false, CharSet = CharSet.Unicode, EntryPoint = "CredUIPromptForWindowsCredentialsW")]
internal static extern int CredUIPromptForWindowsCredentials(
in CREDUI_INFO uiInfo,
uint authError,
ref uint authPackage,
IntPtr inAuthBuffer,
int inAuthBufferSize,
out IntPtr outAuthBuffer,
out uint outAuthBufferSize,
ref bool save,
uint flags);
[DllImport(Libraries.CredUI, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CredUnPackAuthenticationBufferW")]
internal static extern bool CredUnPackAuthenticationBuffer(
uint flags,
IntPtr authBuffer,
uint authBufferSize,
string? userName,
ref int maxUserName,
string? domainName,
IntPtr maxDomainName,
string? password,
ref int maxPassword);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct CREDUI_INFO
{
internal int Size;
internal IntPtr ParentWindow;
internal string MessageText;
internal string CaptionText;
internal IntPtr BannerBitmap;
}
}
}

View File

@ -2,6 +2,7 @@
{
internal static class Errors
{
internal const int ERROR_SUCCESS = 0;
internal const int ERROR_ACCESS_DENIED = 5;
internal const int ERROR_INVALID_HANDLE = 6;
internal const int ERROR_INVALID_PARAMETER = 7;

View File

@ -3,6 +3,7 @@
internal static class Libraries
{
internal const string Advapi32 = "advapi32.dll";
internal const string CredUI = "credui.dll";
internal const string Kernel32 = "kernel32.dll";
internal const string NtDll = "ntdll.dll";
}

View File

@ -643,6 +643,8 @@ namespace winsw
return null;
}
public string? ServiceAccountPrompt => GetServiceAccountPart("prompt")?.ToLowerInvariant();
protected string? AllowServiceLogon => GetServiceAccountPart("allowservicelogon");
public string? ServiceAccountPassword => GetServiceAccountPart("password");
@ -651,7 +653,7 @@ namespace winsw
public bool HasServiceAccount()
{
return !string.IsNullOrEmpty(ServiceAccountUserName);
return this.dom.SelectSingleNode("//serviceaccount") != null;
}
public bool AllowServiceAcountLogonRight