My code needs to determine how long a particular process has been running. But it continues to fail with an access denied error message on the Process.StartTime request. This is a process running with a User's credentials (ie, not a high-privilege process). There's clearly a security setting or a policy setting, or something that I need to twiddle with to fix this, as I can't believe the StartTime property is in the Framework just so that it can fail 100% of the time.
A Google search indicated that I could resolve this by adding the user whose credentials the querying code is running under to the "Performance Log Users" group. However, no such user group exists on this machine.
I've read something similar to what you said in the past, Lars. Unfortunately, I'm somewhat restricted with what I can do with the machine in question (in other words, I can't go creating user groups willy-nilly: it's a server, not just some random PC).
Thanks for the answers, Will and Lars. Unfortunately, they didn't solve my problem.
Ultimate solution to this is to use WMI:
using System.Management;
String queryString = "select CreationDate from Win32_Process where ProcessId='" + ProcessId + "'";
SelectQuery query = new SelectQuery(queryString);
ManagementScope scope = new System.Management.ManagementScope(#"\\.\root\CIMV2");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection processes = searcher.Get();
//... snip ... logic to figure out which of the processes in the collection is the right one goes here
DateTime startTime = ManagementDateTimeConverter.ToDateTime(processes[0]["CreationDate"].ToString());
TimeSpan uptime = DateTime.Now.Subtract(startTime);
Parts of this were scraped from Code Project:
http://www.codeproject.com/KB/system/win32processusingwmi.aspx
And "Hey, Scripting Guy!":
http://www.microsoft.com/technet/scriptcenter/resources/qanda/jul05/hey0720.mspx
Process of .Net 1.1 uses the Performance Counters to get the information. Either they are disabled or the user does not have administrative rights. Making sure the Performance Counters are enabled and the user is an administrator should make your code work.
Actually the "Performance Counter Users Group" should enough. The group doesn't exist by default. So you should create it yourself.
Process of .Net 2.0 is not depended on the Performance Counters.
See http://weblogs.asp.net/nunitaddin/archive/2004/11/21/267559.aspx
The underlying code needs to be able to call OpenProcess, for which you may require SeDebugPrivilege.
Is the process you're doing the StartTime request on running as a different user to your own process?
OK, sorry that didn't work... I am no expert on ASP.NET impersonation, I tend to use app pools which I don't think you can do on W2K Have you tried writing a tiny little test app which does the same query, and then running that as various users?
I am reluctant to post a chunk of MS framework code here, but you could use either Reflector or this: http://www.codeplex.com/NetMassDownloader to get the source code for the relevant bits of the framework so that you could try implementing various bits to see where it fails.
Can you get any other info about the process without getting Access Denied?
I can enumerate the process (ie, the GetProcessById function works), and we have other code that gets the EXE name and other bits of information.
I will give the test app a try. I'm also going to attempt to use WMI to get this information if I can't get the C# implementation working properly in short order (this is not critical functionality, so I can't spend days on it).
Related
I am using the following C# code to set a user's password via the DirectoryEntry class:
user.Invoke("SetPassword", password);
The code is contained in a DLL. Loading the DLL and using this code in a service running under a GMSA takes 60 seconds to return, although successfully. However loading the DLL and using this code from a command line executable works fine - even if I use psexec to run the command line under the GMSA.
There's a fair few similar problems reported that I've tried to eliminate by the above testing, e.g. accessing port 464, or the SetPassword function falling through different mechanisms.
Why does calling SetPassword on a DirectoryEntry takes 60 seconds to succeed? Alternatively, what debugging can I do to determine why this is happening?
In the Remarks section of the SetPassword documentation, it lists several mechanisms that it tries to set the password, because you need a secure connection to do it. I'm guessing that the first one didn't work, so it has to try 2 or maybe even all three.
The LDAP string used to create user could determine how many hoops SetPassword has to run through to find an acceptably secure method. Running from a different computer can also make a difference on which mechanisms would work, depending on firewall restrictions or if one of those computers is not joined to the same domain and the other is.
SetPassword isn't the only way to set a password. You can do it by setting the unicodePwd attribute directly, in the very specific way that the documentation describes. That looks like this in C#:
user.Properties["unicodePwd"].Value = Encoding.Unicode.GetBytes("\"NewPassword\"");
user.CommitChanges();
But you have to make sure that you're connecting in a secure way. That's not taken care for you like it is with SetPassword. You can either use LDAPS (LDAP over SSL), which requires that the server is using a trusted certificate (notice port 636):
var user = new DirectoryEntry("LDAP://example.com:636/CN=SomePerson,OU=Users,DC=example,DC=com");
Or, if the computer you are running this from is joined to the same domain or a trusted domain, then you can use Kerberos, which is done by passing AuthenticationTypes.Sealing in the constructor:
var user = new DirectoryEntry("LDAP://CN=SomePerson,OU=Users,DC=example,DC=com"
, null, null, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
This is a way of explicitly telling it how to connect securely, rather than letting SetPassword try different methods until it finds one. So that might save time.
I apologize for how open ended I'm sure this will end up. I'll try to break this up in manageable chunks.
I'm writing a program that deals with SCCM. This program is locked down by security groups.
If you are not a member of the 'Server Operators' group, you don't get in. Simple. I've been testing for awhile on my dev machine (a Dell that is on wireless) with no issues.
When I released a beta, I found that under certain circumstances the program will not pick up on the user's security group membership and therefore deny access.
I was able to reproduce the issue, it seems machines on wireless tend to have this issue. Though, it's more complicated than that.
-Freshly imaged machines seem to have this issue
-Not all wireless machines; my dev machine is wireless
-One desktop (no wireless) has this issue. (It's at a remote site, so I can't really pick that one apart) I think it's a fresh image as well. I did however test on another computer at the same site - worked fine.
-Connecting to ethernet seems to have an effect - 75% of the time it fixes the issue somehow - after a bit of a wait. (Works on both ethernet and wireless at that point)
I've been trying to get a breakpoint set on one of these machines so I can see what is going on. Problem is, by the time I get VS.net installed the problem solves itself. I know (very little) about remote debugging - currently looking into that. This scenario makes me wonder if it's update related (the image is fairly up to date, maybe a month or two out?)
I also wrote a small utility that tests the login procedure (using the same code) and it finds the security group every time. Wat.
Code for finding security group of a user:
(courtesy of Stack :) )
static bool IsUserMemberOf_(string user, string group)
{ // (I realize the user parameter is superfluous in this case)
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + TC.act_Domain, TC.act_AD_User, TC.enc_GetADPassword());
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(&(objectClass=user)(|(cn=" + TC.act_AD_User + ")(sAMAccountName=" + TC.act_AD_User + ")))";
SearchResult result = mySearcher.FindOne();
foreach (string GroupPath in result.Properties["memberOf"])
{
if (GroupPath.Contains(group))
{
return true;
}
}
}
catch (DirectoryServicesCOMException)
{
}
return false;
}
I've also tried another method (using Principal.IsMemberOf() ), which had the same result. I've also investigated DNS/Network problems. Not ruling it out, but doesn't seem to be a factor in the testing I've done.
I'm at a loss. If anyone has any thoughts, by all means please lay them on me.
Thanks
Yeah, it was the .net version. Our image starts with .net 4.0. Throwing 4.5.2 at it clears it right up. I would have expected an error at runtime, but I guess not.
... I'll show myself out
I am currently writing a C# service to basically make sure a group of exes are running and if they aren't then relaunch them.
The service starts up automatically so I run in to the problem that it attempts to launch the exe when no one is logged in. I have a solution that works for non-VM systems as shown below:
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem");
ManagementObjectCollection collection = searcher.Get();
string loggedIn = (string)collection.Cast<ManagementBaseObject>().First()["UserName"];
I then check this against null or whitespace and then just don't check the process list if no user is logged in.
However, while running on a VM this section of code returns null when a user is logged in. So I need a way of checking for a logged in user on both a standard machine and VM. I also need it to work for XP and windows 7, and both 32bit and 64bit. I am using .Net 4.
Official documentation to list sessions prescribes to use LsaLogOnSessions to enumerate them, refer to MSDN or to this CodeProject article for more examples.
That said if you just need to be sure there is at least one logged in user then you can simply check for default shell process. Default shell will always be loaded for each session:
bool loggedInUserExist = Process.GetProcessesByName("explorer").Any();
In this example I hard-coded value but you must read it from string value Shell in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon\, in case default shell has been overridden (or for Server Core installation when GUI isn't present).
If you don't need anything else then you're done. If you have to filter specific users then you have to get process' owner. It's not such easy in C# unless you use WMI to query Win32_Process, search for explorer in Name property and then call GetOwner() method of the ManagementObject (see this post here on SO). Note that you may use same query both to determine if there is any logged in user and who he is. As alternative you may P/Invoke for Windows API functions as described in this post (but in this case I'd go back to LsaLogOnSessions).
I am trying to write some c# code that writes a new Performance Counter Category if it doesn't exist, and then adds a specific counter. When I run this code from the Visual Studio dev server, everything works fine. When I deploy it to IIS and try it, I get permission errors. I am running Windows 7 and using IIS 7.5.
What I have done so far, some of which is being done out of desperation:
Created a new App Pool that I will run as a specific user
Created a new user and added him to the Process Monitor and Administrator groups
Set the new user as the identity of the new App Pool
Pointed my web service to that App Pool.
Went in to regedit and gave the user full control over HKLM/System/CurrentControlSet (apparently there should be a permissions folder, but I don't see it anywhere).
I know these steps have worked partially, because I am now able to check whether or not a category exists (ASPNET user couldn't even do that). I can check if a category exists, I just cant add a new one.
The error that I get is
Cannot create or delete the Performance Category 'C:\Windows\TEMP\tmp1AA8.tmp' because access is denied.
The code to add the performance counter category is looks like this:
if (!PerformanceCounterCategory.Exists("APIService"))
{
CounterCreationDataCollection counters = new CounterCreationDataCollection();
CounterCreationData counter = new CounterCreationData();
counter.CounterName = "# of operations executed";
counter.CounterHelp = "Operations executed";
counter.CounterType = PerformanceCounterType.RateOfCountsPerSecond32;
counters.Add(counter);
PerformanceCounterCategory.Create("APIService", "Api Counter", PerformanceCounterCategoryType.SingleInstance, counters); // This code blows up
}
I have searched high and low and cant find anyone with the same problem. I have even tried giving Everyone Full Control over c:\windows\temp. Any idea what I might be missing here?
I think I figured out the solution (or rather someone else did, but I can find a link to that forum this morning). Instead of setting the App Pool to run as an admin, I switched it to the Local System user. That seems to have resolved everything, as the user has sufficient rights to write performance metrics and everything else that needs to be done.
I'm trying to retrieve all the users that have ever logged into a pc and populate them in a combobox, but after searching, I'm not finding any good answers.
I was going to look at the:
DirectoryInfo(Environment.GetEnvironmentVariable("USERPROFILE")).Parent.GetDirectories();
But I feel that is way to unreliable.
Next I was going to look at the registry, but after reading, that list will not update if a user account name has ever been changed. I know there has to be a record of all the user profiles on a machine, because I have used Microsoft systernals tools to manage them. but I just cannot figure out how to do it programatically with c#.
Ok, well, i figured this out, actually with WMI after all, here is my code.
using System.Security.Principal;
using System.Management;
private void GetLocalUserAccounts()
{
SelectQuery query = new SelectQuery("Win32_UserProfile");
ManagementObjectsSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject sid in searcher.Get())
{
MessageBox.Show(new SecurityIdentifier(sid["SID"].ToString()).Translate(typeof(NTAccount)).ToString());
}
}
This also returns the system accounts IE: NT_Authority NT_System, but those can be filtered easily. Thanks for all the help.
I would suggest to take a look at WMI. It allows you to run sql-like queries on a machine to get loads of system informations.
Some inspiration in VBScript : http://social.technet.microsoft.com/Forums/en-US/ITCG/thread/2b99b836-ed8f-4146-89e4-947b79bf4862/
If the user is created on the local machine, it should have a user folder, as Joshua said.
Also, you could try running CMD's "net user" command and capturing the return value using something like this.
Edit: per the comments below, check this out and see if it helps: https://stackoverflow.com/a/8455654/1046689
You could iterate the %windir%\Users directory ommitting default folder names (public/admin etc) and parse out the usernames from the filenames