Created
March 1, 2019 19:33
-
-
Save sean-m/1c6abf00f170ca2339f63fa6024a2415 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Net; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
namespace SMM.Helper | |
{ | |
public static class CredentialsUI | |
{ | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
private struct CREDUI_INFO | |
{ | |
public int cbSize; | |
public IntPtr hwndParent; | |
public string pszMessageText; | |
public string pszCaptionText; | |
public IntPtr hbmBanner; | |
} | |
[Flags] | |
private enum PromptForWindowsCredentialsFlags | |
{ | |
/// <summary> | |
/// The caller is requesting that the credential provider return the user name and password in plain text. | |
/// This value cannot be combined with SECURE_PROMPT. | |
/// </summary> | |
CREDUIWIN_GENERIC = 0x1, | |
/// <summary> | |
/// The Save check box is displayed in the dialog box. | |
/// </summary> | |
CREDUIWIN_CHECKBOX = 0x2, | |
/// <summary> | |
/// Only credential providers that support the authentication package specified by the authPackage parameter should be enumerated. | |
/// This value cannot be combined with CREDUIWIN_IN_CRED_ONLY. | |
/// </summary> | |
CREDUIWIN_AUTHPACKAGE_ONLY = 0x10, | |
/// <summary> | |
/// Only the credentials specified by the InAuthBuffer parameter for the authentication package specified by the authPackage parameter should be enumerated. | |
/// If this flag is set, and the InAuthBuffer parameter is NULL, the function fails. | |
/// This value cannot be combined with CREDUIWIN_AUTHPACKAGE_ONLY. | |
/// </summary> | |
CREDUIWIN_IN_CRED_ONLY = 0x20, | |
/// <summary> | |
/// Credential providers should enumerate only administrators. This value is intended for User Account Control (UAC) purposes only. We recommend that external callers not set this flag. | |
/// </summary> | |
CREDUIWIN_ENUMERATE_ADMINS = 0x100, | |
/// <summary> | |
/// Only the incoming credentials for the authentication package specified by the authPackage parameter should be enumerated. | |
/// </summary> | |
CREDUIWIN_ENUMERATE_CURRENT_USER = 0x200, | |
/// <summary> | |
/// The credential dialog box should be displayed on the secure desktop. This value cannot be combined with CREDUIWIN_GENERIC. | |
/// Windows Vista: This value is not supported until Windows Vista with SP1. | |
/// </summary> | |
CREDUIWIN_SECURE_PROMPT = 0x1000, | |
/// <summary> | |
/// The credential provider should align the credential BLOB pointed to by the refOutAuthBuffer parameter to a 32-bit boundary, even if the provider is running on a 64-bit system. | |
/// </summary> | |
CREDUIWIN_PACK_32_WOW = 0x10000000, | |
} | |
[DllImport("ole32.dll")] | |
private static extern void CoTaskMemFree(IntPtr ptr); | |
[DllImport("credui.dll", CharSet = CharSet.Unicode, SetLastError = true)] | |
private static extern Boolean CredPackAuthenticationBuffer( | |
int dwFlags, | |
string pszUserName, | |
string pszPassword, | |
IntPtr pPackedCredentials, | |
ref int pcbPackedCredentials); | |
[DllImport("credui.dll", CharSet = CharSet.Unicode)] | |
private static extern uint CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere, | |
int authError, | |
ref uint authPackage, | |
IntPtr InAuthBuffer, | |
uint InAuthBufferSize, | |
out IntPtr refOutAuthBuffer, | |
out uint refOutAuthBufferSize, | |
ref bool fSave, | |
PromptForWindowsCredentialsFlags flags); | |
[DllImport("credui.dll", CharSet = CharSet.Auto)] | |
private static extern bool CredUnPackAuthenticationBuffer(int dwFlags, | |
IntPtr pAuthBuffer, uint cbAuthBuffer, | |
StringBuilder pszUserName, | |
ref int pcchMaxUserName, | |
StringBuilder pszDomainName, | |
ref int pcchMaxDomainame, | |
StringBuilder pszPassword, | |
ref int pcchMaxPassword); | |
public static NetworkCredential PromptForCreds(string PromptCaption = "Credentials needed", string PromptMessage = "Please enter your username and password") | |
{ | |
bool save = false; | |
int errorcode = 0; | |
uint dialogReturn; | |
uint authPackage = 0; | |
IntPtr outCredBuffer = IntPtr.Zero; | |
uint outCredSize; | |
try | |
{ | |
CREDUI_INFO credui = new CREDUI_INFO(); | |
credui.cbSize = Marshal.SizeOf(credui); | |
credui.pszCaptionText = PromptCaption; | |
credui.pszMessageText = PromptMessage; | |
IntPtr hwnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle; | |
if (hwnd != IntPtr.Zero) credui.hwndParent = hwnd; | |
int maxUserName = 256; | |
int maxDomain = 100; | |
int maxPassword = 127; // Windows 10 max | |
var usernameBuf = new StringBuilder(maxUserName); | |
var domainBuf = new StringBuilder(maxDomain); | |
var passwordBuf = new StringBuilder(maxPassword); | |
while (true) //Show the dialog again and again, until Cancel is clicked or the entered credentials are correct. | |
{ | |
//Show the dialog | |
dialogReturn = CredUIPromptForWindowsCredentials(ref credui, | |
errorcode, | |
ref authPackage, | |
(IntPtr)0, //You can force that a specific username is shown in the dialog. Create it with 'CredPackAuthenticationBuffer()'. Then, the buffer goes here... | |
0, //...and the size goes here. You also have to add CREDUIWIN_IN_CRED_ONLY to the flags (last argument). | |
out outCredBuffer, | |
out outCredSize, | |
ref save, | |
0); //Use the PromptForWindowsCredentialsFlags-Enum here. You can use multiple flags if you seperate them with | . | |
if (dialogReturn != 0) break; //Break, if Cancel was clicked or an error occurred | |
/*Unpack your credentials (outCredBuffer, outCredSize) with 'CredUnPackAuthenticationBuffer()' | |
For example, it returns a bool 'credentialsEnteredCorrect':*/ | |
NetworkCredential netCredential; | |
if (CredUnPackAuthenticationBuffer(0, outCredBuffer, outCredSize, usernameBuf, ref maxUserName, domainBuf, ref maxDomain, passwordBuf, ref maxPassword)) | |
{ | |
//clear the memory allocated by CredUIPromptForWindowsCredentials | |
netCredential = new NetworkCredential() | |
{ | |
UserName = usernameBuf.ToString(), | |
Password = passwordBuf.ToString(), | |
Domain = domainBuf.ToString() | |
}; | |
return netCredential; | |
} | |
} | |
} | |
catch (Exception e) | |
{ | |
throw e; // Caller needs to handle this, only here so we can release the buffer in finally | |
} | |
finally | |
{ | |
if (outCredBuffer != IntPtr.Zero) CoTaskMemFree(outCredBuffer); | |
} | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment