I have a Problem, which is... i start a programm with right click -> run as administrator.
Which means the programm is running in an administrative context.
WindowsIdentity.GetCurrent().Name;
if i try to get the user name that way i will get the user that started the programm as admin.. for example "administrator", but what i need is the name of the current logged in user which is for example: bob
Can anybody help me out? :)
You could try using WMI (System.Management.dll) to get the owner of the explorer.exe process.
string GetExplorerUser()
{
var query = new ObjectQuery(
"SELECT * FROM Win32_Process WHERE Name = 'explorer.exe'");
var explorerProcesses = new ManagementObjectSearcher(query).Get();
foreach (ManagementObject mo in explorerProcesses)
{
string[] ownerInfo = new string[2];
mo.InvokeMethod("GetOwner", (object[])ownerInfo);
return String.Concat(ownerInfo[1], #"\", ownerInfo[0]);
}
return string.Empty;
}
This relies on the fact that the explorer process is single instance an so you don't end up with the possibility of having several explorer processes running with different user credentials.
You will probably need to use win32 API for that. Read about Window Station and Desktop functions here: http://msdn.microsoft.com/en-us/library/ms687107%28v=vs.85%29.aspx
Also see this question:
Get the logged in Windows user name associated with a desktop
Maybe you could start as normal user, save user name, then programmatically request elevation :
Windows 7 and Vista UAC - Programmatically requesting elevation in C#
All .NET libraries will give you the user from the current context ('Administrator' in your case).
If you are trying to secure your code, you might consider reading about: Security in the .NET framework
1) Cassia should be able to give you a list of currently logged in users including RDC.
foreach (ITerminalServicesSession sess in new TerminalServicesManager().GetSessions())
{
// sess.SessionId
// sess.UserName
}
2) WMI (SO answer)
Select * from Win32_LogonSession
3) PInvoke to WTSEnumerateSessions
4) Enumerate all instances of "explorer.exe" and get the owner using PInvoke (OpenProcessHandle).
Process[] processes = Process.GetProcessesByName("explorer");
This is a bit hacky. WMI can also be used for this.
It might be a good idea to set winmgmt as a dependency for your service if you decided to go with solution that uses WMI.
Related
I'm coding a windows service and in one point I need to know if there is an active interactive session.
I tried using OnSessionChange() and keep in a variable the last SessionChangeReason. When I reach to the mentioned point I compare if it's equal to SessionChangeReason.SessionLogOn. This works with the inconvenient that the service has a delayed start so if the user logs on before the service starts running this information is lost.
I have also seen the System.Environment.Interactive property but as I understand this refers to the process of the current service which is not interactive, so it wouldn't give me the information I need (I might misunderstood this, though).
Is there a way to get this info 'on demand' without having to keep register of the SessionChangeReason?
Edit: Maybe I wasn't clear about this point. Aside from knowing that there is an interactive session I also need to know that it isn't locked.
P/Invoke WTSEnumerateSessions to see if there are additional sessions and what their connected states are. You obviously have to ignore session 0 on Vista+.
You should only do this when your service is started, the session change notification should be used to detect further changes.
Finally I've resigned to knowing specifically that there is a session and isn't locked so we'll work with whether there is an active session or not.
If only knowing there is an active session works for you and you don't want to use pInvoke you can either:
a) Search for the explorer process
Process[] ps = Process.GetProcessesByName("explorer");
bool explorerActive = (ps.Length > 0);
b) Use the following WMI query to get the UserName of the active session:
using System.Management;
ConnectionOptions oConn = new ConnectionOptions();
ManagementScope oMs = new ManagementScope("\\\\localhost", oConn);
ObjectQuery oQuery = new ObjectQuery("select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection)
{
if (oReturn["UserName"] == null)
{
// No active session
Console.Write("UserName: null");
}
else
{
// Active session
Console.Write("UserName: " + oReturn["UserName"].ToString());
}
}
If you want to use pInvoke see Anders' answer.
The action I need help about, is to execute a EXE file on own servers disk from a intranet-webpage, which IIS are on same server-installation. The webpage use a business layer to execute a ProcessStart together with given parameters.
When I perform the execution from web, the taskmanager show me that the application are starting up with the IIS AppPool of webpage as user. Few seconds later it's killed. In my database logs, I can see;
The Microsoft Jet database engine cannot open the file '\\computer\pathfile.ext'. It is already opened exclusively by another user, or you need permission to view its data.
That's correct. The EXE tool are, in turn, loading files from other computers. This is a special behavior which are well studied and well working while using the tool from desktop.
My goal/question,
I want this web-function-call behave with desktop rights. Is it possible at all?
The IIS AppPool have a regular setup with account ApplicationPoolIdentity. I appeared to be "lucky unwise", without knowledge about how much IIS 7.5 and Windows Server 2008 R2 raised the security model since <=IIS6.
I tried to change the app-pool user to NetworkService, Administrator.
I tried to set the application with app-pool as exec/read right
I even tried to let webapp to run a batch-file with a call to application inside..
Then I was begin to change the ProcessStart-behavior. And here, I
don't know much of what to do. I tried to add VERB runas. Force a
password prompt is not a solution here. I tried to simulate a
username/password. No luck there. I also tried to add runas /user:
blabla as parameters with ProcessStart, after used /savecred in a
desktop command window once. No luck there.
Maybe this should work but I just don't understand the correct setup of properties. I add the ProcessStart code snippet below, also added some commented code to let you see what I tried.
public string RunProcess(ApplicationType type, int param)
{
currentSelection = GetApplicationType(type);
ProcessStartInfo info = new ProcessStartInfo(currentSelection.Path);
info.CreateNoWindow = false;
info.UseShellExecute = true;
//info.UseShellExecute = false;
//info.ErrorDialog = false;
//info.UserName = "dummyUsEr";
//info.Password = this.SecurePwd("DummyPWd");
info.WindowStyle = ProcessWindowStyle.Normal;
info.Arguments = string.Format(" {0}", param.ToString());
using (Process exec = Process.Start(info))
{
try
{
exec.WaitForExit();
}
catch
{
}
}
return output;
}
EDIT
Just to be clear, and perhaps help some another guy/girl browsing to this question, I attach the snippet of Password-generation,
protected System.Security.SecureString SecurePwd(string pwd)
{
SecureString securePwd = new SecureString();
foreach (char ch in pwd.ToCharArray())
securePwd.AppendChar(ch);
return securePwd;
}
I see that you've tried putting in a specific username and password for the process start impersonation, but you say that the process accesses files on another computer and I don't see any mention of specifying a domain name which presumably you would need to access remote files?
So like this:
info.Domain = "domainname";
info.UserName = "dummyUsEr";
info.Password = "DummyPWd";
Also, what does this.SecurePwd() do and have you tried it with just the straight password string that you're passing into it?
In my current C# code I'm able to lock a Windows user session programmatically (same as Windows + L).
Since the app would still be running, is there any way to unlock the session from that C# program. User credentials are known. The app is running on Windows 7.
You'll need a custom windows credential provider to log in for you. Also, you'll need to save the user's credentials somewhere to log in. There are some samples in Windows SDK 7 https://www.microsoft.com/en-us/download/details.aspx?id=8279
There's a bunch of projects to get you started under Samples\security\credentialproviders.
To unlock the screen:
set the username / password in CSampleCredential::Initialize
set autologin to true in CSampleCredential::SetSelected
search the hardware provider sample for WM_TOGGLE_CONNECTED_STATUS message to see how to trigger the login
build some way to communicate with your app to trigger the unlock (local tcp server for example)
It's a pain in the ass, but it works.
Here is some hackery to do that: http://www.codeproject.com/Articles/16197/Remotely-Unlock-a-Windows-Workstation
Didn't test it myself though.
Not for .NET part, but you could also make your own custom Logon UI and inject some mechanism there. It can easily become security problem though.
var path = new ManagementPath();
path.NamespacePath = "\\ROOT\\CIMV2\\Security\\MicrosoftVolumeEncryption"; path.ClassName = "Win32_EncryptableVolume";
var scope = new ManagementScope(path, new ConnectionOptions() { Impersonation = ImpersonationLevel.Impersonate });
var management = new ManagementClass(scope, path, new ObjectGetOptions());
foreach (ManagementObject vol in management.GetInstances())
{
Console.WriteLine("----" + vol["DriveLetter"]);
switch ((uint)vol["ProtectionStatus"])
{
case 0:
Console.WriteLine("not protected by bitlocker");
break;
case 1:
Console.WriteLine("unlocked");
break;
case 2:
Console.WriteLine("locked");
break;
}
if ((uint)vol["ProtectionStatus"] == 2)
{
Console.WriteLine("unlock this driver ...");
vol.InvokeMethod("UnlockWithPassphrase", new object[] { "here your pwd" });
Console.WriteLine("unlock done.");
}
}
Note: this only works if you run Visual Studio as an administrator.
No, there is no way to do this, by design. What's your scenario and why do you need to lock/unlock the workstation?
Of course you can't unlock it. Unlocking a session requires the user physically be there to enter their account credentials. Allowing software to do this, even with saved credentials, would be a security issue for many of the other situations where workstation locking is used.
How do i get the current logged on user name in windows 7 (i.e the user who is physically logged on to the console in which the program that i am launching is running).
For example if i am logged on as "MainUser" and run my console application (that will display the logged on user name) as "SubUser", then the program only returns "SubUser" as the currently logged on user.
I used the following 2 techniques to get the user name. Both are not getting me the thing that i want.
System.Environment.GetEnvironmentVariable("USERNAME")
System.Security.Principal.WindowsIdentity.GetCurrent().User;
Note that however, this VBScript code returns the logged on user name irrespective of the user account from which this script is run:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set compsys_arr = objWMIService.ExecQuery _
("Select * from Win32_ComputerSystem")
For Each sys in compsys_arr
Wscript.Echo "username: " & sys.UserName
Next
Any way it is possible in C#?
I think just converting the WMI calls to c# works just fine for me.
ConnectionOptions oConn = new ConnectionOptions();
System.Management.ManagementScope oMs = new System.Management.ManagementScope("\\\\localhost", oConn);
System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection) {
Console.WriteLine(oReturn["UserName"].ToString().ToLower());
}
I think you'd have to go down a P/Invoke route. You need to find out which WindowStation your process is running within, and then determine the owner of that WindowStation. I don't think that there's a .NET api for determining these things.
Win32 APIs that you'd need to look at, are probably GetProcessWindowStation and GetUserObjectSecurity to find the owner.
Altough I don't understand if you want to get the user name, who is logged on the system or the user name under which the console is running - maybe you could try using System.Environment.UserName - MSDN claims that it shows the logged on user name.
You want the user name of your session. You can find out your session ID by calling ProcessIdToSessionId. Then use WTSQuerySessionInformation to find out the user name.
I have a windows service which runs under system account and executes some programs from time to time (yeah,yeah, I know that's a bad practice, but that's not my decision). I need to set the "interact with desktop" check, to see the gui of that executed programs, after the service is installed. I've tried several ways, putting the code below in AfterInstall or OnCommited event handlers of my service installer:
ConnectionOptions coOptions = new ConnectionOptions();
coOptions.Impersonation = ImpersonationLevel.Impersonate;
ManagementScope mgmtScope = new System.Management.ManagementScope(#"root\CIMV2", coOptions);
mgmtScope.Connect();
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + ServiceMonitorInstaller.ServiceName + "'");
ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
InParam["DesktopInteract"] = true;
ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", InParam, null);
or
RegistryKey ckey = Registry.LocalMachine.OpenSubKey(
#"SYSTEM\CurrentControlSet\Services\WindowsService1", true);
if(ckey != null)
{
if(ckey.GetValue("Type") != null)
{
ckey.SetValue("Type", ((int)ckey.GetValue("Type") | 256));
}
}
both of these methods "work". They set the check, but after I start the service it launches the exe - and gui isn't shown! So, if I stop the service, recheck and start it again - bingo! everything starts and is shown. The second way to achieve the result is to reboot - after it the gui is also shown.
So the question is: Is there a correct way to set "interact with desktop" check, so it'll start working without rechecks and reboots?
OS: Windows XP (haven't tried Vista and 7 yet...)
private static void SetInterActWithDeskTop()
{
var service = new System.Management.ManagementObject(
String.Format("WIN32_Service.Name='{0}'", "YourServiceName"));
try
{
var paramList = new object[11];
paramList[5] = true;
service.InvokeMethod("Change", paramList);
}
finally
{
service.Dispose();
}
}
And finally after searching the internet for a week - I've found a great working solution:
http://asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html
Find the desktop to launch into. This
may seem facetious but it isn't as
simple as it seems. With Terminal
Services and Fast User Switching there
can be multiple interactive users
logged on to the computer at the same
time. If you want the user that is
currently sitting at the physical
console then you're in luck, the
Terminal Services API call
WTSGetActiveConsoleSessionId will get
you the session ID you need. If your
needs are more complex (i.e. you need
to interact with a specific user on a
TS server or you need the name of the
window station in a non-interactive
session) you'll need to enumerate the
Terminal Server sessions with
WTSEnumerateSessions and check the
session for the information you need
with WTSGetSessionInformation.
Now you know what session you need to
interact with and you have its ID.
This is the key to the whole process,
using WTSQueryUserToken and the
session ID you can now retrieve the
token of the user logged on to the
target session. This completely
mitigates the security problem of the
'interact with the desktop' setting,
the launched process will not be
running with the LOCAL SYSTEM
credentials but with the same
credentials as the user that is
already logged on to that session! No
privilege elevation.
Using CreateProcessAsUser and the
token we have retrieved we can launch
the process in the normal way and it
will run in the target session with
the target user's credentials. There
are a couple of caveats, both
lpCurrentDirectory and lpEnvironment
must point to valid values - the
normal default resolution methods for
these parameters don't work for
cross-session launching. You can use
CreateEnvironmentBlock to create a
default environment block for the
target user.
There is source code of the working project attached.
Same as Heisa but with WMI. (code is Powershell, but can be easily ported to C#)
if ($svc = gwmi win32_service|?{$_.name -eq $svcname})
{
try {
$null = $svc.change($svc.displayname,$svc.pathname,16,1,`
"Manual",$false,$svc.startname,$null,$null,$null,$null)
write-host "Change made"
catch { throw "Error: $_" }
} else
{ throw "Service $svcname not installed" }
See MSDN: Service Change() method for param description.