Сообщение Re: Детект прав админа у пользователя от 29.03.2024 17:06
Изменено 29.03.2024 17:10 VladD2
Re: Детект прав админа у пользователя
Здравствуйте, VladD2, Вы писали:
VD>В продукте есть функционал по обновлению стороннего софта. Для большинства обновлений требуются права администратора. В этом случае пользователю выдается стандартный виндовый диалог ввода учетки и пароля.
Если кому интересно, вот финальный, рабочий код:
VD>В продукте есть функционал по обновлению стороннего софта. Для большинства обновлений требуются права администратора. В этом случае пользователю выдается стандартный виндовый диалог ввода учетки и пароля.
Если кому интересно, вот финальный, рабочий код:
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
using System.Security;
using System.Net;
using System.Runtime.Versioning;
using System.Text;
using System.Windows;
using System.Diagnostics;
using System.DirectoryServices.ActiveDirectory;
using System.Windows.Interop;
namespace WpfCred;
internal class Credentials
{
const int CREDUI_MAX_USERNAME_LENGTH = 256 + 1 + 256;
const int CREDUI_MAX_DOMAIN_TARGET_LENGTH = 256 + 1 + 80;
const int CREDUI_MAX_PASSWORD_LENGTH = 512 / 2;
[Flags]
enum CredUiWinSecure : uint
{
/// <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>
Generic = 0x1,
/// <summary> The Save check box is displayed in the dialog box.</summary>
Checkbox = 0x2,
/// <summary>
/// Only credential providers that support the authentication package specified by the pulAuthPackage parameter should be enumerated.
/// This value cannot be combined with IN_CRED_ONLY.
/// </summary>
AuthpackageOnly = 0x10,
/// <summary>
/// Only the credentials specified by the pvInAuthBuffer parameter for the authentication package specified by the pulAuthPackage parameter should be enumerated.
/// If this flag is set, and the pvInAuthBuffer parameter is NULL, the function fails.
///
/// This value cannot be combined with AuthpackageOnly.
/// </summary>
InCredOnly = 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>
EnumerateAdmins = 0x100,
/// <summary>
/// Only the incoming credentials for the authentication package specified by the pulAuthPackage parameter should be enumerated.
/// </summary>
EnumerateCurrentUser = 0x200,
/// <summary>
/// The credential dialog box should be displayed on the secure desktop.This value cannot be combined with Generic
/// Windows Vista: This value is supported beginning with Windows Vista with SP1.
/// </summary>
SecurePrompt = 0x1000,
/// <summary>
/// The credential dialog box is invoked by the SspiPromptForCredentials function, and the client is prompted before a prior handshake.If SSPIPFC_NO_CHECKBOX is passed in the pvInAuthBuffer parameter, then the credential provider should not display the check box.
/// Windows Vista: This value is supported beginning with Windows Vista with SP1.
/// </summary>
PrePrompting = 0x2000,
/// <summary>
/// The credential provider will not pack the AAD authority name. This is only applied to Azure AD joined devices.
/// Windows 10, version 1607: This value is supported beginning with Windows 10, version 1607.
/// </summary>
NotPackAadAuthority = 0x40000,
/// <summary>
/// The credential provider should align the credential BLOB pointed to by the ppvOutAuthBuffer parameter to a 32-bit boundary, even if the provider is running on a 64-bit system.
/// </summary>
Pack32Wow = 0x10000000,
/// <summary>
/// Windows Hello credentials will be packed in a smart card auth buffer.This only applies to the face, fingerprint, and PIN credential providers.
/// Windows 10, version 1809: This value is supported beginning with Windows 10, version 1809.
/// </summary>
WindowsHello = 0x80000000
}
enum CRED_PACK
{
None = 0,
PROTECTED_CREDENTIALS = 0x1,
WOW_BUFFER = 0x2,
GENERIC_CREDENTIALS = 0x4,
ID_PROVIDER_CREDENTIALS = 0x8,
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint="RtlSecureZeroMemory")]
private static extern void SecureZeroMemory(IntPtr ptr, IntPtr cnt);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct CREDUI_INFO
{
public int cbSize;
public IntPtr hwndParent;
public string pszMessageText;
public string pszCaptionText;
public IntPtr hbmBanner;
}
//
[DllImport("credui.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool CredUnPackAuthenticationBuffer(CRED_PACK dwFlags,
IntPtr pAuthBuffer,
uint cbAuthBuffer,
StringBuilder pszUserName,
ref int pcchMaxUserName,
StringBuilder pszDomainName,
ref int pcchMaxDomainame,
StringBuilder pszPassword,
ref int pcchMaxPassword);
// Decode credentials
[DllImport("credui.dll", CharSet = CharSet.Auto)]
private static extern int CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere,
int authError,
ref uint authPackage,
IntPtr InAuthBuffer,
uint InAuthBufferSize,
out IntPtr refOutAuthBuffer,
out uint refOutAuthBufferSize,
ref bool fSave,
CredUiWinSecure flags);
[DllImport("ole32.dll")]
[SuppressUnmanagedCodeSecurity]
[ResourceExposure(ResourceScope.None)]
private static extern void CoTaskMemFree(IntPtr ptr);
private enum Logon32
{
Interactive = 2,
Network = 3,
Batch = 4,
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
Logon32 dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
private sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle() : base(true) { }
[DllImport("kernel32.dll")]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle() => CloseHandle(handle);
}
private enum IsLoginResult
{
IsAdmin,
PasswordIncorrect,
IsNotAdmin,
}
private static IsLoginResult IsLoginLocalAdministrator(string userName, string domainName, string password)
{
var parts = userName.Split('\\');
if (parts.Length == 2)
{
userName = parts[1];
if (string.IsNullOrEmpty(domainName))
domainName = parts[0];
}
SafeTokenHandle safeTokenHandle;
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, domainName, password,
Logon32.Network, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
if (!returnValue)
{
var passwordIncorrect = 1326;
var ret = Marshal.GetLastWin32Error();
if (ret == passwordIncorrect)
return IsLoginResult.PasswordIncorrect;
throw new System.ComponentModel.Win32Exception(ret);
}
using (safeTokenHandle)
{
// Use the token handle returned by LogonUser.
using WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
var windowsPrincipal = new WindowsPrincipal(newId);
bool isAdmin = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
return isAdmin ? IsLoginResult.IsAdmin : IsLoginResult.IsNotAdmin;
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception occurred. " + ex.Message);
}
return IsLoginResult.IsNotAdmin;
}
public static NetworkCredential? GetCredentials(Window parent, string caption, string message, NetworkCredential? defaultCreds = null)
{
var inCredBuffer = IntPtr.Zero;
var inCredSize = 0u;
var hWndParent = new WindowInteropHelper(parent).EnsureHandle();
var credui = new CREDUI_INFO
{
hwndParent = hWndParent,
pszCaptionText = caption,
pszMessageText = message,
};
credui.cbSize = Marshal.SizeOf(credui);
for (;;)
{
uint authPackage = 0;
var save = false;
var usernameBuf = new StringBuilder(CREDUI_MAX_USERNAME_LENGTH + 1);
var passwordBuf = new StringBuilder(CREDUI_MAX_PASSWORD_LENGTH + 1);
var domainBuf = new StringBuilder(CREDUI_MAX_DOMAIN_TARGET_LENGTH);
var userNameLen = CREDUI_MAX_USERNAME_LENGTH;
var domainLen = CREDUI_MAX_DOMAIN_TARGET_LENGTH;
var passwordLen = CREDUI_MAX_PASSWORD_LENGTH;
var result = CredUIPromptForWindowsCredentials(
ref credui,
0,
ref authPackage,
InAuthBuffer: inCredBuffer,
InAuthBufferSize: inCredSize,
out var outCredBuffer,
out var outCredSize,
ref save,
CredUiWinSecure.EnumerateAdmins /*| CredUiWinSecure.Checkbox*/ | CredUiWinSecure.WindowsHello
);
inCredBuffer = outCredBuffer;
inCredSize = outCredSize;
if (result != 0)
{
var res = Marshal.GetLastWin32Error();
return null;
}
var packAuthRes = CredUnPackAuthenticationBuffer(CRED_PACK.PROTECTED_CREDENTIALS, outCredBuffer, outCredSize,
usernameBuf, ref userNameLen,
domainBuf, ref domainLen,
passwordBuf, ref passwordLen);
if (!packAuthRes)
{
var res = Marshal.GetLastWin32Error();
if (outCredBuffer != IntPtr.Zero)
CoTaskMemFree(outCredBuffer);
return null;
}
var userName = usernameBuf.ToString();
var domain = domainBuf.ToString();
var password = passwordBuf.ToString();
var isLoginResult = IsLoginLocalAdministrator(userName, domain, password);
if (outCredBuffer != IntPtr.Zero)
CoTaskMemFree(outCredBuffer);
switch (isLoginResult)
{
case IsLoginResult.IsAdmin:
return new NetworkCredential
{
UserName = userName,
Domain = domain,
Password = password,
};
case IsLoginResult.PasswordIncorrect:
MessageBox.Show(parent, "Неверный пароль.", caption, MessageBoxButton.OK, MessageBoxImage.Error);
continue;
case IsLoginResult.IsNotAdmin:
default:
MessageBox.Show(parent, "Пользователь не является локальным администратором.", caption, MessageBoxButton.OK, MessageBoxImage.Error);
continue;
}
}
}
} // end Class
Re: Детект прав админа у пользователя
Здравствуйте, VladD2, Вы писали:
VD>В продукте есть функционал по обновлению стороннего софта. Для большинства обновлений требуются права администратора. В этом случае пользователю выдается стандартный виндовый диалог ввода учетки и пароля.
Если кому интересно, вот финальный, рабочий код:
VD>В продукте есть функционал по обновлению стороннего софта. Для большинства обновлений требуются права администратора. В этом случае пользователю выдается стандартный виндовый диалог ввода учетки и пароля.
Если кому интересно, вот финальный, рабочий код:
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
using System.Security;
using System.Net;
using System.Runtime.Versioning;
using System.Text;
using System.Windows;
using System.Diagnostics;
using System.Windows.Interop;
namespace WpfCred;
internal class Credentials
{
const int CREDUI_MAX_USERNAME_LENGTH = 256 + 1 + 256;
const int CREDUI_MAX_DOMAIN_TARGET_LENGTH = 256 + 1 + 80;
const int CREDUI_MAX_PASSWORD_LENGTH = 512 / 2;
[Flags]
private enum CredUiWinSecure : uint
{
/// <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>
Generic = 0x1,
/// <summary> The Save check box is displayed in the dialog box.</summary>
Checkbox = 0x2,
/// <summary>
/// Only credential providers that support the authentication package specified by the pulAuthPackage parameter should be enumerated.
/// This value cannot be combined with IN_CRED_ONLY.
/// </summary>
AuthpackageOnly = 0x10,
/// <summary>
/// Only the credentials specified by the pvInAuthBuffer parameter for the authentication package specified by the pulAuthPackage parameter should be enumerated.
/// If this flag is set, and the pvInAuthBuffer parameter is NULL, the function fails.
///
/// This value cannot be combined with AuthpackageOnly.
/// </summary>
InCredOnly = 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>
EnumerateAdmins = 0x100,
/// <summary>
/// Only the incoming credentials for the authentication package specified by the pulAuthPackage parameter should be enumerated.
/// </summary>
EnumerateCurrentUser = 0x200,
/// <summary>
/// The credential dialog box should be displayed on the secure desktop.This value cannot be combined with Generic
/// Windows Vista: This value is supported beginning with Windows Vista with SP1.
/// </summary>
SecurePrompt = 0x1000,
/// <summary>
/// The credential dialog box is invoked by the SspiPromptForCredentials function, and the client is prompted before a prior handshake.If SSPIPFC_NO_CHECKBOX is passed in the pvInAuthBuffer parameter, then the credential provider should not display the check box.
/// Windows Vista: This value is supported beginning with Windows Vista with SP1.
/// </summary>
PrePrompting = 0x2000,
/// <summary>
/// The credential provider will not pack the AAD authority name. This is only applied to Azure AD joined devices.
/// Windows 10, version 1607: This value is supported beginning with Windows 10, version 1607.
/// </summary>
NotPackAadAuthority = 0x40000,
/// <summary>
/// The credential provider should align the credential BLOB pointed to by the ppvOutAuthBuffer parameter to a 32-bit boundary, even if the provider is running on a 64-bit system.
/// </summary>
Pack32Wow = 0x10000000,
/// <summary>
/// Windows Hello credentials will be packed in a smart card auth buffer.This only applies to the face, fingerprint, and PIN credential providers.
/// Windows 10, version 1809: This value is supported beginning with Windows 10, version 1809.
/// </summary>
WindowsHello = 0x80000000
}
private enum CRED_PACK
{
None = 0,
PROTECTED_CREDENTIALS = 0x1,
WOW_BUFFER = 0x2,
GENERIC_CREDENTIALS = 0x4,
ID_PROVIDER_CREDENTIALS = 0x8,
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint="RtlSecureZeroMemory")]
private static extern void SecureZeroMemory(IntPtr ptr, IntPtr cnt);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct CREDUI_INFO
{
public int cbSize;
public IntPtr hwndParent;
public string pszMessageText;
public string pszCaptionText;
public IntPtr hbmBanner;
}
[DllImport("credui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CredUnPackAuthenticationBuffer(CRED_PACK dwFlags,
IntPtr pAuthBuffer,
uint cbAuthBuffer,
StringBuilder pszUserName,
ref int pcchMaxUserName,
StringBuilder pszDomainName,
ref int pcchMaxDomainame,
StringBuilder pszPassword,
ref int pcchMaxPassword);
// Decode credentials
[DllImport("credui.dll", CharSet = CharSet.Auto)]
private static extern int CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere,
int authError,
ref uint authPackage,
IntPtr InAuthBuffer,
uint InAuthBufferSize,
out IntPtr refOutAuthBuffer,
out uint refOutAuthBufferSize,
ref bool fSave,
CredUiWinSecure flags);
[DllImport("ole32.dll")]
[SuppressUnmanagedCodeSecurity]
[ResourceExposure(ResourceScope.None)]
private static extern void CoTaskMemFree(IntPtr ptr);
private enum Logon32
{
Interactive = 2,
Network = 3,
Batch = 4,
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
Logon32 dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
private sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle() : base(true) { }
[DllImport("kernel32.dll")]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle() => CloseHandle(handle);
}
private enum IsLoginResult
{
IsAdmin,
PasswordIncorrect,
IsNotAdmin,
}
private static IsLoginResult IsLoginLocalAdministrator(string userName, string domainName, string password)
{
var parts = userName.Split('\\');
if (parts.Length == 2)
{
userName = parts[1];
if (string.IsNullOrEmpty(domainName))
domainName = parts[0];
}
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, domainName, password,
Logon32.Network, LOGON32_PROVIDER_DEFAULT,
out SafeTokenHandle safeTokenHandle);
if (!returnValue)
{
var passwordIncorrect = 1326;
var ret = Marshal.GetLastWin32Error();
if (ret == passwordIncorrect)
return IsLoginResult.PasswordIncorrect;
throw new System.ComponentModel.Win32Exception(ret);
}
using (safeTokenHandle)
{
// Use the token handle returned by LogonUser.
using WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
var windowsPrincipal = new WindowsPrincipal(newId);
bool isAdmin = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
return isAdmin ? IsLoginResult.IsAdmin : IsLoginResult.IsNotAdmin;
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception occurred. " + ex.Message);
}
return IsLoginResult.IsNotAdmin;
}
public static NetworkCredential? GetCredentials(Window parent, string caption, string message, NetworkCredential? defaultCreds = null)
{
var inCredBuffer = IntPtr.Zero;
var inCredSize = 0u;
var hWndParent = new WindowInteropHelper(parent).EnsureHandle();
var credui = new CREDUI_INFO
{
hwndParent = hWndParent,
pszCaptionText = caption,
pszMessageText = message,
};
credui.cbSize = Marshal.SizeOf(credui);
for (;;)
{
uint authPackage = 0;
var save = false;
var usernameBuf = new StringBuilder(CREDUI_MAX_USERNAME_LENGTH + 1);
var passwordBuf = new StringBuilder(CREDUI_MAX_PASSWORD_LENGTH + 1);
var domainBuf = new StringBuilder(CREDUI_MAX_DOMAIN_TARGET_LENGTH);
var userNameLen = CREDUI_MAX_USERNAME_LENGTH;
var domainLen = CREDUI_MAX_DOMAIN_TARGET_LENGTH;
var passwordLen = CREDUI_MAX_PASSWORD_LENGTH;
var result = CredUIPromptForWindowsCredentials(
ref credui,
0,
ref authPackage,
InAuthBuffer: inCredBuffer,
InAuthBufferSize: inCredSize,
out var outCredBuffer,
out var outCredSize,
ref save,
CredUiWinSecure.EnumerateAdmins /*| CredUiWinSecure.Checkbox*/ | CredUiWinSecure.WindowsHello
);
inCredBuffer = outCredBuffer;
inCredSize = outCredSize;
if (result != 0)
{
var res = Marshal.GetLastWin32Error();
Debug.WriteLine(new System.ComponentModel.Win32Exception(res).Message);
return null;
}
var packAuthRes = CredUnPackAuthenticationBuffer(CRED_PACK.PROTECTED_CREDENTIALS, outCredBuffer, outCredSize,
usernameBuf, ref userNameLen,
domainBuf, ref domainLen,
passwordBuf, ref passwordLen);
if (!packAuthRes)
{
var res = Marshal.GetLastWin32Error();
Debug.WriteLine(new System.ComponentModel.Win32Exception(res).Message);
if (outCredBuffer != IntPtr.Zero)
CoTaskMemFree(outCredBuffer);
return null;
}
var userName = usernameBuf.ToString();
var domain = domainBuf.ToString();
var password = passwordBuf.ToString();
var isLoginResult = IsLoginLocalAdministrator(userName, domain, password);
if (outCredBuffer != IntPtr.Zero)
CoTaskMemFree(outCredBuffer);
switch (isLoginResult)
{
case IsLoginResult.IsAdmin:
return new NetworkCredential
{
UserName = userName,
Domain = domain,
Password = password,
};
case IsLoginResult.PasswordIncorrect:
MessageBox.Show(parent, "Неверный пароль.", caption, MessageBoxButton.OK, MessageBoxImage.Error);
continue;
case IsLoginResult.IsNotAdmin:
default:
MessageBox.Show(parent, "Пользователь не является локальным администратором.", caption, MessageBoxButton.OK, MessageBoxImage.Error);
continue;
}
}
}
} // end Class