Skip to content

Instantly share code, notes, and snippets.

@sean-m
Created March 1, 2019 19:33
Show Gist options
  • Save sean-m/1c6abf00f170ca2339f63fa6024a2415 to your computer and use it in GitHub Desktop.
Save sean-m/1c6abf00f170ca2339f63fa6024a2415 to your computer and use it in GitHub Desktop.
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