How to delete registry key for specific user? - c#

I have two users: SuperUser and MyUser. How can I delete some registry key for MyUser when I am logged in as SuperUser.
In my C# application I want to delete key by path: MyUser\Software\Microsoft\Windows\CurrentVersion\Run\MyApp.
Note I am runnnig my C# application when I am logged as SuperUser.
It seems that Microsoft.Win32.Registry class doesn't have a possibility to do that.

The profile may or may not be loaded. In order to ensure that it is loaded, you'll need to logon the user. Then you can load the profile and make changes. You'll need to pinvoke LogonUser() and LoadUserProfile().
// log on user
UserToken usertoken = null;
LogonUser(user, domain, pass, LogonType.LOGON32_LOGON_NETWORK,
LogonProvider.LOGON32_PROVIDER_DEFAULT, out usertoken));
// load the profile
PROFILEINFO p = new PROFILEINFO();
p.dwFlags = 1;
p.lpUserName = user;
p.dwSize = Marshal.SizeOf(p);
LoadUserProfile(usertoken, ref profile);
// get a handle to the registry
Microsoft.Win32.SafeHandles.SafeRegistryHandle saferh =
new Microsoft.Win32.SafeHandles.SafeRegistryHandle(profile.hProfile, false);
Microsoft.Win32.RegistryKey rk =
Microsoft.Win32.RegistryKey.FromHandle(m_RegHandle, Microsoft.Win32.RegistryView.Default);

Related

Unable to fetch user properties detail from active directory using c#

I am trying to connect to Active Directory using service account credentials that have full access to connect to Active Directory, but unable to load property details of users.
This happens when I am logged in using 'miminstall' account which does not have access to fetch user details from AD, but in my app I have passed credentials of account that has access in AD.
When I run Visual Studio with different user (adma) that has full connection access to Active directory, I am able to connect and fetch user details without any issue.
I don't know why it is happening even though adma account credentials are passed in the code.
public string getADattributes(string DN, string operation)
{
string path = "LDAP://xyz.com";
DirectoryEntry directoryEntry = new DirectoryEntry(path, "xyz\\adma", "abc", AuthenticationTypes.Secure);
using (directoryEntry)
{
DirectorySearcher objDSearcher = new DirectorySearcher();
objDSearcher.Filter = "(distinguishedName=" + DN + ")";//search user in AD using DN
objDSearcher.PropertiesToLoad.Add("whenCreated");
objDSearcher.PropertiesToLoad.Add("whenChanged");
objDSearcher.PropertiesToLoad.Add("EmployeeID");
objDSearcher.SearchScope = SearchScope.Subtree;
SearchResult result = objDSearcher.FindOne();
if (result != null)//if count!=0 that means user exist in ad
{
string createdDate = "";
string modifiedDate = "";
string employeeID = "";
if (result.Properties["whenCreated"].Count >0)
{
//able to come inside if statement when running visual studio using adma account but not when runnning with login account i.e., miminstall
createdDate = result.Properties["whenCreated"][0].ToString();
}
if(result.Properties["whenChanged"].Count>0)
{
modifiedDate = result.Properties["whenChanged"][0].ToString();
}
if(result.Properties["EmployeeID"].Count > 0)
{
employeeID = result.Properties["EmployeeID"][0].ToString();
}
}
return null;
}
}
Unless this is a one time task, one would typically create a task in task scheduler or a webapp under IIS.
If this is a console application, add a new Task under Task Scheduler, set the action to run your app (give it path to your app's exe), and set the task user as 'adma'
If it's part of a webapp, create a new application pool in IIS. Then right click the newly created app pool, go to Advanced Settings > Identity and provide the credentials for 'adma'. Then assign this application pool to your webapp.
If this is not going to be a scheduled task or a webapp, and occasional on-demand run, I believe adding Impersonation would be your best option. See this SO

Check user is locked in LDAP?

I need to verify if the user account in the LDAP is locked
I am using below code
const int ADS_UF_LOCKOUT = 0x00000010;
DirectoryEntry entry = new DirectoryEntry (_path, domainAndUsername, pwd);
if (((int)entry.Properties["useraccountcontrol"].Value & ADS_UF_LOCKOUT) == 1)
{
return true;
}
But if the user account is locked , I am receiving , "Login Failed: Bad username /password"
Please Help.
If you want to determine if a user account is locked, then you can't use the user account information that you're checking for to determine this fact - because the user account is locked, you will be denied access.
You will not be told that the reason for being unable to log on is due to the account being locked, that would be considered excessive information disclosure.
If you want to determine if the reason for not being permitted to log on is due to the account being locked then you will need an already logged on account that can check the account lock state instead of trying from the failed connection.
You can use attribute lockout time for it too: it is 0 if user is not locked. (Connect to AD using administrator credentials).
DirectoryEntry _de = new DirectoryEntry (_path, domainAdmininstratorName, pwd); // get user as directory entry object
object largeInteger = _de.Properties["lockoutTime"].Value; // it's a large integer so we need to get it's value by a little bit complex way
long highPart =
(Int32)
largeInteger.GetType()
.InvokeMember("HighPart", BindingFlags.GetProperty, null, largeInteger, null);
long lowPart =
(Int32)
largeInteger.GetType()
.InvokeMember("LowPart", BindingFlags.GetProperty, null, largeInteger, null);
long result = (long) ((uint) lowPart + (((long) highPart) << 32));
if (result == 0)
{
// account is not locked
}
else
{
// account is locked
}

C# Nested impersonation - impersonate a user whilst already impersonating another

I have a fairly odd requirement to be able to impersonate a user, when I'm already impersonating another, using C#.
I'm writing an app to allow the management of Active Directory users. This app will provide the ability for anyone in the company to view and maintain certain details about themselves (some of which will not actually be saved to Active Directory, but some of which will), for managers to be able to view and maintain details about their team, and for HR to be able to view and maintain details about anyone.
For obvious reasons I don't want to develop or test this against the live domain. We have recently ported all users over to this domain from another domain, which means I can actually test against the old domain without affecting anything. However, to enable me to do this I have to impersonate my old account on the old domain, which I do on loading the application.
Although for me everything will work fine as I'm setup as a domain admin, going forward obviously not all users will be domain admins, and won't be able to write to AD under their own account, and therefore we have another domain admin user setup specifically for this application, whenever data needs to be saved to AD that user is impersonated. This was working great before when I was testing against an Active Directory I'd setup on a virtual machine because I was logging onto the local domain, however that didn't allow me to step through the code in Visual Studio so debugging was slow, and hence I've stopped using that virtual machine and am using this old domain. Now I'm already impersonating another user (i.e. my old domain account), when it then tries to impersonate the domain admin user it fails with an "System.Security.SecurityException: Access is denied." exception. The line this fails on is just writing out some debugging information using "WindowsIdentity.GetCurrent().Name".
If I change my code so I'm actually logging in using the new domain admin rather than my old account, the first time it goes through it logs in successfully (so the credentials are correct), however when it then goes through and tries to do the same again to write to AD it fails with the above exception. Therefore I think it must be a problem with trying to do a nested impersonate.
Is it possible to do a nested impersonate?
Below is the code I'm using:
private static WindowsImpersonationContext ImpersonateUser(out string result, string sUsername,
string sDomain, string sPassword)
{
// initialize tokens
var pExistingTokenHandle = new IntPtr(0);
var pDuplicateTokenHandle = new IntPtr(0);
// if domain name was blank, assume local machine
if (sDomain == "")
{
sDomain = Environment.MachineName;
}
try
{
result = null;
const int logon32ProviderDefault = 0;
// create token
const int logon32LogonInteractive = 2;
// get handle to token
var bImpersonated = LogonUser(sUsername, sDomain, sPassword,
logon32LogonInteractive,
logon32ProviderDefault,
ref pExistingTokenHandle);
// did impersonation fail?
if (!bImpersonated)
{
var nErrorCode = Marshal.GetLastWin32Error();
result = "LogonUser() failed with error code: " + nErrorCode + "\r\n";
}
// Get identity before impersonation
result += string.Format("Before impersonation: {0}\r\n", WindowsIdentity.GetCurrent().Name);
var bRetVal = DuplicateToken(pExistingTokenHandle, (int)SecurityImpersonationLevel.SecurityImpersonation,
ref pDuplicateTokenHandle);
// did DuplicateToken fail?
if (bRetVal)
{
// create new identity using new primary token
var newId = new WindowsIdentity(pDuplicateTokenHandle);
var impersonatedUser = newId.Impersonate();
// check the identity after impersonation
result += "After impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";
return impersonatedUser;
}
else
{
var nErrorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // close existing handle
result += "DuplicateToken() failed with error code: " + nErrorCode + "\r\n";
return null;
}
}
finally
{
// close handle(s)
if (pExistingTokenHandle != IntPtr.Zero)
{
CloseHandle(pExistingTokenHandle);
}
if (pDuplicateTokenHandle != IntPtr.Zero)
{
CloseHandle(pDuplicateTokenHandle);
}
}
}
When this is called for the nested impersonation which fails, "bImpersonated" is actually "true", as is bRetVal, which suggests its worked, however when it gets to "WindowsIdentity.GetCurrent().Name" it fails with the exception above.
I hope this makes sense, and would appreciate any assistance.

Remove a 'Deny' rule (permission) from the 'UserChoice' key in the Registry via C#

I am working on File Associations. I have identified that there is a key called UserChoice in:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\[ext].
I have been able to read from and write to the UserChoice key provided that I create it and that it has not already been created by Windows. However, if the UserChoice key has already been created by Windows, then I need to run as Administrator to get access to the key. My ultimate goal is to delete the UserChoice key.
I have noted that Windows places a Deny rule on the UserChoice key which is preventing me from deleting that key. If I can succeed in removing that rule, I believe that I'll be able to delete the UserChoice key. Here is the code that I have tried:
public static void ShowSecurity(RegistryKey regKeyRoot, string user) {
RegistrySecurity security = regKeyRoot.GetAccessControl(AccessControlSections.All);
foreach (RegistryAccessRule ar in
security.GetAccessRules(true, true, typeof(NTAccount))) {
if (ar.IdentityReference.Value.Contains(User) &&
ar.AccessControlType.ToString().ToLower() == "deny") {
security.RemoveAccessRuleSpecific(ar);
regKeyRoot.SetAccessControl(security);
}
}
}
When Windows creates the UserChoice key it adds a security rule for the current user of Type Deny; permission: Special. This rule is not inherited and applies to the UserChoice key only.
With some messing about and running as Administrator I am able to access that RegistryAccessRule. However even running as Administrator, I cannot remove this rule. I have read somewhere in my research that there is not a programmatic way to do it. I can remove this rule via RegEdit. I can also remove the UserChoice key using File Types Manager from NirSoft. So I assume there is some way to do this.
Summary: Is there a way that I can remove the Deny rule so that I can delete the UserChoice key?
Your code example and the revisions suggested in the answer by #ali lead me to a solution for overcoming the security setting that Windows places on the UserChoice key which enabled me to delete that key.
My solution presumes that the UserChoice key is present in the HKEY_CURRENT_USER (HKCU) hive. If that is the case, the user owns the UserChoice key and therefore has the necessary privileges to change the security settings on that key and ultimately delete it. (This means that the user does not need to be a member of the Administrators group.)
The extensionKey parameter of this method is the parent key of the UserChoice key.
static void DeleteUserChoiceKey(RegistryKey extensionKey)
{
const string userChoiceKeyName = "UserChoice";
using (RegistryKey userChoiceKey =
extensionKey.OpenSubKey(userChoiceKeyName,
RegistryKeyPermissionCheck.ReadWriteSubTree,
RegistryRights.ChangePermissions))
{
if (userChoiceKey == null) { return; }
string userName = WindowsIdentity.GetCurrent().Name;
RegistrySecurity security = userChoiceKey.GetAccessControl();
AuthorizationRuleCollection accRules =
security.GetAccessRules(true, true, typeof(NTAccount));
foreach (RegistryAccessRule ar in accRules)
{
if (ar.IdentityReference.Value == userName &&
ar.AccessControlType == AccessControlType.Deny)
{
security.RemoveAccessRuleSpecific(ar); // remove the 'Deny' permission
}
}
userChoiceKey.SetAccessControl(security); // restore all original permissions
// *except* for the 'Deny' permission
}
extensionKey.DeleteSubKeyTree(userChoiceKeyName, true);
}
A quick thought. Does it work if you take ownership og the regKey, before changing the rules on it
public static void ShowSecurity(RegistryKey regKeyRoot, string user)
{
regKeyRoot.OpenSubKey("", RegistryKeyPermissionCheck.ReadWriteSubTree,
RegistryRights.ChangePermissions);
RegistrySecurity security = regKeyRoot.GetAccessControl(AccessControlSections.All);
security.SetGroup( new NTAccount("Administrators") );
security.SetOwner( new NTAccount("ali") ); //Your account name
security.SetAccessRuleProtection(true, false);
regKeyRoot.SetAccessControl(security);
//---------
foreach (RegistryAccessRule ar in security.GetAccessRules(true, true, typeof(NTAccount)))
{
if (ar.IdentityReference.Value.Contains(User) && ar.AccessControlType == AccessControlType.Deny )
security.RemoveAccessRuleSpecific(ar);
}
regKeyRoot.SetAccessControl(security);
}

What is the best way to get the current user's SID?

Prerequisite Detail
Working in .NET 2.0.
The code is in a common library that could be called from ASP.Net, Windows Forms, or a Console application.
Running in a Windows Domain on a corporate network.
The Question
What is the best way to get the current user's SID? I'm not talking about the identity that is executing the application, but the user who is accessing the interface. In background applications and desktop based applications this should be the identity actually executing the application, but in ASP.Net (without impersionation) this should be the HttpContext.Current.User SID.
The Current Method
This is what I've got right now. It just seems... wrong. It's nasty. Is there a better way to do this, or some built in class that does it for you?
public static SecurityIdentifier SID
{
get
{
WindowsIdentity identity = null;
if (HttpContext.Current == null)
{
identity = WindowsIdentity.GetCurrent();
}
else
{
identity = HttpContext.Current.User.Identity as WindowsIdentity;
}
return identity.User;
}
}
I don't think there is a better way at getting at this info--you've got to get at the WindowsPrincipal somehow and .NET's rather clean API abstracts that behind the User object. I'd just leave this nice and wrapped up in a method and call it a day.
Well, ok, there is one thing you should do (unless your web users are always going to be WindowsIdentity), which would be to check for null identity and handle it according to whatever rules you have.
Without the use of third-party libraries This code will give correct results, if the user changed his user name.
String SID = "";
string currentUserName = System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString();
RegistryKey regDir = Registry.LocalMachine;
using (RegistryKey regKey = regDir.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData", true))
{
if (regKey != null)
{
string[] valueNames = regKey.GetSubKeyNames();
for (int i = 0; i < valueNames.Length; i++)
{
using (RegistryKey key = regKey.OpenSubKey(valueNames[i], true))
{
string[] names = key.GetValueNames();
for (int e = 0; e < names.Length; e++)
{
if (names[e] == "LoggedOnSAMUser")
{
if (key.GetValue(names[e]).ToString() == currentUserName)
SID = key.GetValue("LoggedOnUserSID").ToString();
}
}
}
}
}
}MessageBox.Show(SID);
The WindowsIdentity class is the "built in class that does it for you". You've got as simple a solution as you're going to get really, as long as you have a valid WindowsIdentity to work with.
Alternatively, if you have the username of the user in question and you want to get the SID directly from AD, you can build your own library to use the DirectoryServices namespace and retrieve the DirectoryEntry for your user (this is a fairly complicated process as DirectoryServices is tricky). You can even use LDAP to get it if you have a need.
//the current user
WindowsIdentity.GetCurrent().Owner
Returns the security identifier (SID) in SDDL format
you know them as S-1-5-9.
WindowsIdentity.GetCurrent().Owner.ToString()
Returns the account domain security identifier (SID)
If the SID does not represent a Windows account SID, this
property returns null
WindowsIdentity.GetCurrent().Owner.AccountDomainSid
You could use this as:
_pipeSecurity = new PipeSecurity();
var sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, WindowsIdentity.GetCurrent().Owner);
var audid = new PipeAuditRule(sid, PipeAccessRights.FullControl, AuditFlags.Failure | AuditFlags.Success);
var access = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, AccessControlType.Allow);
_pipeSecurity.AddAuditRule(audid);
_pipeSecurity.AddAccessRule(access);

Categories

Resources