I am trying to determinate if my program was launched by an Administrator with admin rights or by a normal user with Administrator rights using the following code:
private void Form1_Load(object sender, EventArgs e)
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
bool isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (isAdmin)
{
ini();
MessageBox.Show("The user is an admin with admin rights");
}
else
{
MessageBox.Show("Error The user is a normal user with admin rights", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Application.Exit();
}
}
But when I test this code using a normal user account with admin rights, it fails, telling me The user is an admin with admin rights.
I am using .Net 3.5 and the code needs to be compatible with Windows Vista and up.
Can anybody give me some idea to solve this, please? Thank you!!!
To clarify a little bit more, another way to see this problem is this: How can I determinate if my program (running with admin rights by the admin) is running inside the standard user account context.
mmm you need to know is if the user connected to that computer has Admin Rights but an user that is part of a domain control ? (active Directory?) not the BuiltIn ones ? please Clarify., take a look to this post https://social.msdn.microsoft.com/Forums/vstudio/en-US/c2166023-fcfc-4c3c-ba8f-200977971c5d/how-to-check-if-given-user-have-admin-rights-in-c
There are more variables to account for, for instance Operating System. Which version of Microsoft Window's are you utilizing? Remember, the User Access Control (UAC) was introduced in a later version, which can impact the code. Another massive impactor will be a Domain Policy which is regulated through Active Directory.
To avoid those constraints, I would do the following:
private static bool IsElevated()
{
var user = WindowsIdentity.GetCurrent();
var role = WindowsPrincipal(user);
if(Environment.OSVersion.Platform == PlatformID.Win32NT || Environment.OSVersion.Version.Major > 6)
if(user != null)
if(role.IsInRole(WindowsBuiltInRole.Administrator))
return true;
return false;
}
This was a quick recreation, but you'll want to test the code to ensure it correctly queries the Operating System and User.
I did solved it!!!
Explanation
I realized that what I needed was the session ID (the enviroment) to get somehow the Logged in Username
(owner of the context).
Getting the session Id was really easy:
int session = System.Diagnostics.Process.GetCurrentProcess().SessionId;
By this time, my goal was getting the username linked to this session, then I found this
Get Windows user name from SessionID
Now, with the username I thought it was really easy to check if this username belongs
to an Administrator group. I was wrong. The easiest way that I found requeried to use wmi and pinvoke.
The main problem was, in order to make the queries, I must know the name of the administrators group in the current OS, that is language-dependent.
The solution was this:
http://www.pinvoke.net/default.aspx/advapi32.lookupaccountsid
I tweaked the code, put all pieces together and that was it.
Summarizing, I found the sessionid, found the username linked to session, found the OS Administrators group name, and did some Wmi queries to check if the user is an Administrator.
This solution works fine for me, but I must say that runs slow.
If anybody can improve this code, or if it is useful to anybody else, I left it here.
Just remember to add a reference to System.Management; I used Visual Studio C# Express 2010. The project was a Windows Forms Application.
Happy coding!!!
//Make sure to add a reference to System.Management;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management;
using System.Security.Principal;
using System.Collections;
using System.Runtime.InteropServices;
namespace RealUSer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Getting the session id
int session = System.Diagnostics.Process.GetCurrentProcess().SessionId;
//Getting the username related to the session id
string user = GetUsernameBySessionId(session, false);
try
{
//Cheching if the user belongs to the local admin group using wmi
if (CheckAdminRights(user))
{
MessageBox.Show("The logged in User "+user+" is an Admin");
}
else
{
MessageBox.Show("The logged in User " + user + " is not an Admin");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// This method checks if the context user, belongs to the local administrators group
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public bool CheckAdminRights(string username)
{
bool result = false;
List<string> admins = new List<string>();
//SelectQuery query = new SelectQuery("Select AccountType from Win32_UserAccount where Name=\"" + username + "\"");
SelectQuery query = new SelectQuery("SELECT * FROM win32_group WHERE Name=\"" + getAdministratorGroupName() + "\"");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection OC = searcher.Get();
IEnumerator enumerator = OC.GetEnumerator();
enumerator.MoveNext();
ManagementObject O = (ManagementObject)enumerator.Current;
ManagementObjectCollection adminusers = O.GetRelated("Win32_UserAccount");
foreach (ManagementObject envVar in adminusers)
{
admins.Add(envVar["Name"].ToString());
//Console.WriteLine("Username : {0}", envVar["Name"]);
//foreach (PropertyData pd in envVar.Properties)
// Console.WriteLine(string.Format(" {0,20}: {1}", pd.Name, pd.Value));
}
if (admins.Contains(username))
result = true;
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///This code will find the administrators group name, independent of the OS language using the LookupAccountSid function with the BUILTIN\Administrators sid
#region
const int NO_ERROR = 0;
const int ERROR_INSUFFICIENT_BUFFER = 122;
enum SID_NAME_USE
{
SidTypeUser = 1,
SidTypeGroup,
SidTypeDomain,
SidTypeAlias,
SidTypeWellKnownGroup,
SidTypeDeletedAccount,
SidTypeInvalid,
SidTypeUnknown,
SidTypeComputer
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool LookupAccountSid(
string lpSystemName,
[MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
StringBuilder lpName,
ref uint cchName,
StringBuilder ReferencedDomainName,
ref uint cchReferencedDomainName,
out SID_NAME_USE peUse);
public string getAdministratorGroupName()
{
String result = "";
StringBuilder name = new StringBuilder();
uint cchName = (uint)name.Capacity;
StringBuilder referencedDomainName = new StringBuilder();
uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;
SID_NAME_USE sidUse;
// Sid for BUILTIN\Administrators
byte[] Sid = new byte[] { 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2 };
int err = NO_ERROR;
if (!LookupAccountSid(null, Sid, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse))
{
err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (err == ERROR_INSUFFICIENT_BUFFER)
{
name.EnsureCapacity((int)cchName);
referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);
err = NO_ERROR;
if (!LookupAccountSid(null, Sid, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse))
err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
}
}
if (err == 0)
{
result = name.ToString();
}
return result;
}
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void Form1_Load(object sender, EventArgs e)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///This code will retrieve user name given the session id
#region
[DllImport("Wtsapi32.dll")]
private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pointer);
public static string GetUsernameBySessionId(int sessionId, bool prependDomain)
{
IntPtr buffer;
int strLen;
string username = "SYSTEM";
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTS_INFO_CLASS.WTSUserName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
if (prependDomain)
{
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTS_INFO_CLASS.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
WTSFreeMemory(buffer);
}
}
}
return username;
}
public enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType
}
#endregion
}
}
Related
I need to check if a user is an administrator on the machine where the application is running. Basically, the user will provide the username, password and domain, possibly from another machine. These are transmitted over WCF and at this stage I need to verify whether the provided username, password and domain have admin rights on that machine. This means that there is no need for WMI since everything is happening locally (the user simply sent the username, password, domain as strings over WCF)
Users can be on a domain and hence linked to Active Directory but can can also be a local user, which means that I cannot reply to look it up from Active Directory.
I managed to impersonate the user and can verify that this user is an administrator in the local group. I tested this using the following command:
net localgroup administrators
I am now creating a WindowsPrincipal with the current impersonated user. However, I am getting false when checking if this user is an admin. Here is the important code:
// obtains user token
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
// creates duplicate token handle
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
bool bImpersonated = LogonUser(sUsername, sDomain, sPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);
bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle);
WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
if (wp.IsInRole(WindowsBuiltInRole.Administrator))
{
//is admin
}
else
{
//is not an admin (I am still getting this when user is an admin)
}
bImpersonated returns true (so Impersonation worked)
bRetVal also returns true (so token is valid)
The Impersonation code is from here (except for the Admin check)
Any help would be greatly appreciated.
I use a check by token :
private static Boolean IsAdministratorByToken(WindowsIdentity identity)
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
// Check if this user has the Administrator role. If they do, return immediately.
// If UAC is on, and the process is not elevated, then this will actually return false.
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
{
return true;
}
// If we're not running in Vista onwards, we don't have to worry about checking for UAC.
if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6)
{
// Operating system does not support UAC; skipping elevation check.
return false;
}
int tokenInfLength = Marshal.SizeOf(typeof(int));
IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInfLength);
try
{
IntPtr token = identity.Token;
Boolean result = NativeMethods.GetTokenInformation(token, NativeMethods.TokenInformationClass.TokenElevationType, tokenInformation, tokenInfLength, out tokenInfLength);
if (!result)
{
Exception exception = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
throw new InvalidOperationException("Couldn't get token information", exception);
}
NativeMethods.TokenElevationType elevationType = (NativeMethods.TokenElevationType)Marshal.ReadInt32(tokenInformation);
switch (elevationType)
{
case NativeMethods.TokenElevationType.TokenElevationTypeDefault:
// TokenElevationTypeDefault - User is not using a split token, so they cannot elevate.
return false;
case NativeMethods.TokenElevationType.TokenElevationTypeFull:
// TokenElevationTypeFull - User has a split token, and the process is running elevated. Assuming they're an administrator.
return true;
case NativeMethods.TokenElevationType.TokenElevationTypeLimited:
// TokenElevationTypeLimited - User has a split token, but the process is not running elevated. Assuming they're an administrator.
return true;
default:
// Unknown token elevation type.
return false;
}
}
finally
{
if (tokenInformation != IntPtr.Zero)
{
Marshal.FreeHGlobal(tokenInformation);
}
}
}
this is from this blog.
You can also check by using PrincipalContext, but this solution doesn't work if Server Service is not running.
Code from Xaruth is wrong about TokenElevationTypeLimited, in that case, you should return false and also for build in administrator you will get TokenElevationTypeDefault, so in that case, check if the user is admin instead of just returning false.
Here is code from this post:
https://candritzky.wordpress.com/2012/08/28/uac-elevation-and-the-default-administrator-account/
/// <summary>
/// Base on code found here:
/// http://stackoverflow.com/questions/1220213/c-detect-if-running-with-elevated-privileges
/// </summary>
public static class UacHelper
{
private const string uacRegistryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System";
private const string uacRegistryValue = "EnableLUA";
private const uint STANDARD_RIGHTS_READ = 0x00020000;
private const uint TOKEN_QUERY = 0x0008;
private const uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation, uint TokenInformationLength,
out uint ReturnLength);
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
public enum TOKEN_ELEVATION_TYPE
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
}
private static bool? _isUacEnabled;
public static bool IsUacEnabled
{
get
{
if (_isUacEnabled == null)
{
var uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
if (uacKey == null)
{
_isUacEnabled = false;
}
else
{
var enableLua = uacKey.GetValue(uacRegistryValue);
_isUacEnabled = enableLua.Equals(1);
}
}
return _isUacEnabled.Value;
}
}
private static bool? _isAdministrator;
public static bool IsAdministrator
{
get
{
if (_isAdministrator == null)
{
var identity = WindowsIdentity.GetCurrent();
Debug.Assert(identity != null);
var principal = new WindowsPrincipal(identity);
_isAdministrator = principal.IsInRole(WindowsBuiltInRole.Administrator);
}
return _isAdministrator.Value;
}
}
private static bool? _isProcessElevated;
public static bool IsProcessElevated
{
get
{
if (_isProcessElevated == null)
{
if (IsUacEnabled)
{
var process = Process.GetCurrentProcess();
IntPtr tokenHandle;
if (!OpenProcessToken(process.Handle, TOKEN_READ, out tokenHandle))
{
throw new ApplicationException("Could not get process token. Win32 Error Code: " +
Marshal.GetLastWin32Error());
}
var elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;
var elevationResultSize = Marshal.SizeOf((int) elevationResult);
uint returnedSize;
var elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
var success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType,
elevationTypePtr, (uint) elevationResultSize, out returnedSize);
if (!success)
{
Marshal.FreeHGlobal(elevationTypePtr);
throw new ApplicationException("Unable to determine the current elevation.");
}
elevationResult = (TOKEN_ELEVATION_TYPE) Marshal.ReadInt32(elevationTypePtr);
Marshal.FreeHGlobal(elevationTypePtr);
// Special test for TokenElevationTypeDefault.
// If the current user is the default Administrator, then the
// process is also assumed to run elevated. This is assumed
// because by default the default Administrator (which is disabled by default)
// gets all access rights even without showing a UAC prompt.
switch (elevationResult)
{
case TOKEN_ELEVATION_TYPE.TokenElevationTypeFull:
_isProcessElevated = true;
break;
case TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited:
_isProcessElevated = false;
break;
default:
// Will come here if either
// 1. We are running as the default Administrator.
// 2. We were started using "Run as administrator" from a non-admin
// account and logged on as the default Administrator account from
// the list of available Administrator accounts.
//
// Note: By default the default Administrator account always behaves
// as if UAC was turned off.
//
// This can be controlled through the Local Security Policy editor
// (secpol.msc) using the
// "User Account Control: Use Admin Approval Mode for the built-in Administrator account"
// option of the Security Settings\Local Policies\Security Options branch.
_isProcessElevated = IsAdministrator;
break;
}
}
else
{
_isProcessElevated = IsAdministrator;
}
}
return _isProcessElevated.Value;
}
}
}
I have a windows service which run a WPF application all the time for all the logged in users which works fine, now in the WPF application i can not get a current username as Environment.UserName; returns 'SYSTEM' which is understandable. so what i thought was to find session id of current process which could be retrieved by Process.GetCurrentProcess().SessionId and then get the list of all users logged in to the machine and looping through it to find the session id match with process session id and later his username.
but i don't how to get the list of all logged in users or i would appreciate if someone can help me with alternative.
I ran into a similar problem while building a Windows Service. Just like you, I had the Session ID and needed to get the corresponding username. Syed's answer above did not work on my machine (Windows 10) as Microsoft seems to have removed the quser executable. After several unsuccessful solution hereon SO, I ran into this particular answer and it inspired my solution:
Here's my code (all of them residing inside a class; in my case, the class inheriting ServiceBase)
[DllImport("Wtsapi32.dll")]
private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WtsInfoClass wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pointer);
private enum WtsInfoClass
{
WTSUserName = 5,
WTSDomainName = 7,
}
private static string GetUsername(int sessionId, bool prependDomain = true)
{
IntPtr buffer;
int strLen;
string username = "SYSTEM";
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSUserName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
if (prependDomain)
{
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
WTSFreeMemory(buffer);
}
}
}
return username;
}
I solve it by executing powershell command "quser" in my WPF application which returns all the logged in users then I am iterating to find session id in which the application is running with user session id and then retrieving his name. below is the function which fetch the username by passing his session id
private string GetUserName(int SessionId)
{
try
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript("Quser");
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
foreach (string User in stringBuilder.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Skip(1))
{
string[] UserAttributes = User.Split(new string[]{" "},StringSplitOptions.RemoveEmptyEntries);
if (int.Parse(UserAttributes[2].Trim()) == SessionId)
{
return UserAttributes[0].Replace(">", string.Empty).Trim();
}
}
return stringBuilder.ToString();
}
catch (Exception ex)
{
}
return string.Empty;
}
the function can be called by
string CurrentUser = GetUserName(Process.GetCurrentProcess().SessionId);
You can try this Code spinet.
Whenever a user logged onto windows, the Username attribute will contain the username of the user. In the case when there are no users in the windows system, there will be no instances of the Win32_ComputerSystem class.
ManagementScope ms = new ManagementScope("\\\\.\\root\\cimv2");
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_ComputerSystem");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ms, query);
foreach(ManagementObject mo in searcher.Get())
{
Console.WriteLine(mo["UserName"].ToString());
}
Found this solution on MSDN forums:
using System.Security.Principal;
.
.
.
WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
String username = wp.Identity.Name;
Now this is easily tested by creating a new Console Application, pasting the above code, and writing the username string to Console. Seems to work fine, but for services it's apparently a more complex situation. Since all services are run in a container that runs under the SYSTEM user, that's what they return. For more information see the below link, esp. all Harry Zhu's answers.
http://social.msdn.microsoft.com/Forums/vstudio/en-US/3be119b0-88b4-442e-9613-6856cbb27adb/how-can-i-get-current-username-in-windows-service?forum=csharpgeneral
It would seem it's not possible to achieve what you are trying to get, as services are totally seperate from users' session.
i made a little search and find this code that should work for you:
it will get the username whom is running the process...
reference: How do I determine the owner of a process in C#?
public string GetProcessOwner(string processName)
{
string query = "Select * from Win32_Process Where Name = \"" + processName + "\"";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection processList = searcher.Get();
foreach (ManagementObject obj in processList)
{
string[] argList = new string[] { string.Empty, string.Empty };
int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnVal == 0)
{
// return DOMAIN\user
string owner = argList[1] + "\\" + argList[0];
return owner;
}
}
return "NO OWNER";
}
You can try this Code:
string username = "SYSTEM";
var explorer = Process.GetProcessesByName("explorer").FirstOrDefault();
if (explorer != null)
{
username = GetUsername(explorer.SessionId);
}
Implementation of method GetUsername here: https://stackoverflow.com/a/35810391/10412686
I'm an expert programmer, so therefore, I don't have a clue as to WTH I'm doing :)
On a serious note; no, I'm not expert by any means. I do have a problem though, and don't know how to fix it. The good thing is, I (think I) know what the problem is, and I'm hoping someone here can help.
Here's the synopsis of the problem. I am creating a form in C# that will do some server and database administration task for me. I have a button that when clicked is supposed to return the service status of "x" service on "y" server. The status is printed on the screen to a textbox.
Here's my code:
private void button2_Click(object sender, EventArgs e)
{
string fs = "Service X Status = ";
string mr = "Service A Status = ";
string qp = "Service B Status = ";
string sp = "Spooler Service Status = ";
ServiceController fssc = new ServiceController("xService", "yServer");
ServiceController mrsc = new ServiceController("aService", "yServer");
ServiceController qpsc = new ServiceController("bService", "yServer");
ServiceController spsc = new ServiceController("Spooler", "yServer");
try
{
txtGtwySts.AppendText(sp + spsc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(fs + fssc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(mr + mrsc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(qp + qpsc.Status.ToString());
}
catch (Exception crap)
{
string msg = "";
int i;
for (i = 0; i < crap.Message.Count(); i++)
{
msg += "Error # " + i + " Message: " + crap.Message + "\n";
}
MessageBox.Show(msg);
MessageBox.Show(i.ToString());
}
}
I get exceptions, basically saying: Cannot Open "Service" on "Server." Since this is a remote server, I'm assuming this is a credential/security problem. I do NOT, however, have any problems with the Spooler service.
My question is...How can I pass userID and password to this server so that it will authenticate or runas so I can check the status of these services, it that is the problem. If someone doesnt think its the problem, then please inform me where I've went wrong :)
Finally figured it out...
Created a new class, and is shown below:
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Security.Permissions;
public class ImpersonateUser
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
private static IntPtr tokenHandle = new IntPtr(0);
private static WindowsImpersonationContext impersonatedUser;
// If you incorporate this code into a DLL, be sure to demand that it
// runs with FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Impersonate(string domainName, string userName, string password)
{
//try
{
// Use the unmanaged LogonUser function to get the user token for
// the specified user, domain, and password.
const int LOGON32_PROVIDER_DEFAULT = 0;
// Passing this parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
tokenHandle = IntPtr.Zero;
// ---- Step - 1
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle - new security token
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// ---- Step - 2
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
// ---- Step - 3
{
impersonatedUser = newId.Impersonate();
}
}
}
// Stops impersonation
public void Undo()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
}
and the original code that I posted is wrapped by:
ImpersonateUser iu = new ImpersonateUser();
iu.Impersonate("[domain]","[username]","[password]");
// code you want to execute as impersonated user.....
iu.Undo();
ASP.NET: Impersonate against a domain on VMWare
This question is what I am asking, but the answer does not provide details on how the _token is derived. It seems to only use WindowsIdentity.GetCurrent().Token so there's no impersonation happening.
Can I impersonate a user on a different Active Directory domain in .NET?
This next question has conflicting answers, with the accepted one bearing a comment "I'm beginning to suspect that my problem lies elsewhere." Not helpful.
LogonUser works only for my domain
This next question seems to imply it is not possible, but it deals with 2 domains so I am not sure if it is relevant.
My real question is:
Is it possible? And if so,
How? or Where did I go wrong?
What I have tried so far is, using the code from http://msdn.microsoft.com/en-us/library/chf6fbt4%28v=VS.80%29.aspx
bool returnValue = LogonUser(user, domain, password,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
// after this point, returnValue = false
The Win32 error is
Logon failure: unknown user name or bad password
Very few posts suggest using LOGON_TYPE_NEW_CREDENTIALS instead of LOGON_TYPE_NETWORK or LOGON_TYPE_INTERACTIVE. I had an impersonation issue with one machine connected to a domain and one not, and this fixed it.
The last code snippet in this post suggests that impersonating across a forest does work, but it doesn't specifically say anything about trust being set up. So this may be worth trying:
const int LOGON_TYPE_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_WINNT50 = 3;
bool returnValue = LogonUser(user, domain, password,
LOGON_TYPE_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,
ref tokenHandle);
MSDN says that LOGON_TYPE_NEW_CREDENTIALS only works when using LOGON32_PROVIDER_WINNT50.
this works for me, full working example (I wish more people would do this):
//logon impersonation
using System.Runtime.InteropServices; // DllImport
using System.Security.Principal; // WindowsImpersonationContext
using System.Security.Permissions; // PermissionSetAttribute
...
class Program {
// obtains user token
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public void DoWorkUnderImpersonation() {
//elevate privileges before doing file copy to handle domain security
WindowsImpersonationContext impersonationContext = null;
IntPtr userHandle = IntPtr.Zero;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
string domain = ConfigurationManager.AppSettings["ImpersonationDomain"];
string user = ConfigurationManager.AppSettings["ImpersonationUser"];
string password = ConfigurationManager.AppSettings["ImpersonationPassword"];
try {
Console.WriteLine("windows identify before impersonation: " + WindowsIdentity.GetCurrent().Name);
// if domain name was blank, assume local machine
if (domain == "")
domain = System.Environment.MachineName;
// Call LogonUser to get a token for the user
bool loggedOn = LogonUser(user,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref userHandle);
if (!loggedOn) {
Console.WriteLine("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
return;
}
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
Console.WriteLine("Main() windows identify after impersonation: " + WindowsIdentity.GetCurrent().Name);
//run the program with elevated privileges (like file copying from a domain server)
DoWork();
} catch (Exception ex) {
Console.WriteLine("Exception impersonating user: " + ex.Message);
} finally {
// Clean up
if (impersonationContext != null) {
impersonationContext.Undo();
}
if (userHandle != IntPtr.Zero) {
CloseHandle(userHandle);
}
}
}
private void DoWork() {
//everything in here has elevated privileges
//example access files on a network share through e$
string[] files = System.IO.Directory.GetFiles(#"\\domainserver\e$\images", "*.jpg");
}
}
I was having the same problem. Don't know if you've solved this or not, but what I was really trying to do was access a network share with AD credentials. WNetAddConnection2() is what you need to use in that case.
I have been successfull at impersonating users in another domain, but only with a trust set up between the 2 domains.
var token = IntPtr.Zero;
var result = LogonUser(userID, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
if (result)
{
return WindowsIdentity.Impersonate(token);
}
It's better to use a SecureString:
var password = new SecureString();
var phPassword phPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
IntPtr phUserToken;
LogonUser(username, domain, phPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out phUserToken);
And:
Marshal.ZeroFreeGlobalAllocUnicode(phPassword);
password.Dispose();
Function definition:
private static extern bool LogonUser(
string pszUserName,
string pszDomain,
IntPtr pszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);
Invalid login/password could be also related to issues in your DNS server - that's what happened to me and cost me good 5 hours of my life. See if you can specify ip address instead on domain name.
The problem I encountered was when my workstation was on one domain, but I needed to authenticate to a server on a different domain:
ERROR: "Exception impersonating user, error code: 1326"
SOLUTION: Added LOGON32_LOGON_NEW_CREDENTIALS as a fallback to Impersonate/LogonUser()
Impersonation.cs
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace TestDBAccess
{
public class Impersonation : IDisposable
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String Username, String Domain, String Password, int LogonType, int LogonProvider, out IntPtr Token);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public const int LOGON32_PROVIDER_DEFAULT = 0;
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_LOGON_NETWORK = 3;
public const int LOGON32_LOGON_BATCH = 4;
public const int LOGON32_LOGON_SERVICE = 5;
public const int LOGON32_LOGON_UNLOCK = 7;
public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
private WindowsImpersonationContext impersonationContext = null;
private IntPtr userHandle = IntPtr.Zero;
public Impersonation(string user, string domain, string password)
{
// Extract domain/username from user string
string[] principal = user.Split('\\');
if (principal.Length == 2)
{
domain = principal[0];
user = principal[1];
}
if (string.IsNullOrEmpty(domain))
domain = GetDefaultDomain();
// Call LogonUser to get a token for the user
bool loggedOn =
LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, out userHandle);
if (!loggedOn)
{
int ierr = Marshal.GetLastWin32Error();
if (ierr == 1326)
{
loggedOn =
LogonUser(user, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT, out userHandle);
}
if (!loggedOn)
throw new Exception("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
}
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
}
public static string GetDefaultDomain ()
{
return System.Environment.UserDomainName;
}
public void Dispose()
{
// Clean up
if (impersonationContext != null)
impersonationContext.Undo();
if (userHandle != IntPtr.Zero)
CloseHandle(userHandle);
}
}
}
ExampleClient.cs
Impersonation Impersonation = null;
try
{
Impersonation = new Impersonation(username, null, password);
LogMsg("Attempting to connect to (" + dbInstance.instance + ")...");
using (SqlConnection connection = new SqlConnection(connString))
{
connection.Open();
string sql = edtTestSql.Text;
LogMsg("Attempting to query (" + sql + ")...");
using (SqlCommand command = new SqlCommand(sql, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
LogMsg("next row: " + DumpRow(reader));
}
}
}
}
catch (Exception ex)
{
LogMsg(ex.Message);
}
finally
{
if (Impersonation != null)
Impersonation.Dispose();
}
I want to know, if a WindowsAccount is Password protected.
For security reasons, you cannot get the Password, that’s clear, but there must be a way, to find out, if a Password is set.
public bool IsAccountPasswordProteced(String userName)
{
String entryString = "WinNT://" + Environment.MachineName + ",Computer";
DirectoryEntry dirEntry = new DirectoryEntry(entryString);
DirectoryEntry user = dirEntry.Children.Find(userName, "User");
// First try was to check the ADS_UF_PASSWD_NOTREQD flag.
// If this flag is set, the account has no password,
// but if not, both is possible.
int userFlags = (int)user.Properties["UserFlags"].Value;
return (userFlags & (int)ActiveDs.ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) == 0;
// Second try was to check the PasswordAge.
int pwAge = (int)user.Properties["PasswordAge"].Value;
return pwAge > 0;
}
If there is no better way, I will use the LogonUser-function, but that’s not the way I was looking for. But it’s better than nothing.
If I use this from a local account (not via network, because of the LogonType) and for an enabled account, it should work.
I used this two links:
Calling LogonUser
Detect empty Passwords
public bool IsAccountPasswordProtected(String userName)
{
String entryString = "WinNT://" + Environment.MachineName + ",Computer";
DirectoryEntry dirEntry = new DirectoryEntry(entryString);
DirectoryEntry user = dirEntry.Children.Find(userName, "User");
////EDIT: this flag can also be set, if the account has a password
//int userFlags = (int)user.Properties["UserFlags"].Value;
//if ((userFlags & (int)ActiveDs.ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) != 0)
// return false;
IntPtr token;
bool result = LogonUser(
user.Name, Environment.UserDomainName,
"",
LogonTypes.Interactive,
LogonProviders.Default,
out token);
if (result)
{
CloseHandle(token);
return false;
}
else
{
int err = Marshal.GetLastWin32Error();
if (err == 1327) // ERROR_ACCOUNT_RESTRICTION
return false;
//if(err == 1331) // ERROR_ACCOUNT_DISABLED
return true;
}
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonTypes logonType,
LogonProviders logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
enum LogonTypes : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
enum LogonProviders : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
If you can get the UPN name or user token for the given account (one of the properties of the user object should tell you), then you should just be able to use the WindowsIdentity class something like this:
using System.Security.Principal;
// ...
var identity = new WindowsIdentity("foo-UPN");
var requiresPassword = identity.AuthenticationType != string.Empty;