views:

5052

answers:

7

I wrote a c# application that unlocks users when they are locked out of their account (Active Directory). The application searches for users in a specific OU and will list the locked out users in a combobox. Then you selected that user from the combobox and choose unlock.

If you are logged in as an admin it works perfect. If you are a normal user no.

I want to run my application with admin creds but under a normal user also as secure as possible.

i was reading that maybe program a windows service but im unclear on how to program my application to install, run as a service, and under admin creds.

any advice would be great

Thank you for your time!

-Ryan Summey

+8  A: 

The goal behind this application strikes me as wrong. You are basically trying to create a means of allowing non-admin users to unlock accounts...which is, for good reason, a feature not available to normal users.

Brian
I agree typically that is a bad idea; however, he may be allowing a power user (support rep?) to unlock other user accounts w/out granting full administrative rights.
Nate Bross
Can't that specific permission be delegated to the appropriate group?
Rob
A: 

You cannot use a Windows Service (easily), since a Windows Service cannot have a GUI. The only way to do this as a service would be to install the service, and then make a GUI app that used IPC to communicate the request to the service. This would open up a potential loophole, though.

If you're running on Vista, a good option would be to edit the manifest file and add requireAdministrator.


Edit:

It sounds like my first suggestion may be what you want... To do this, the basic process is:

  • Make your application a windows service. There is a walkthrough of this process on MSDN.
  • Make your service respond to some form of IPC. You could use sockets, pipes, or any other form of communication. The service would "listen" for a request to unblock a user, then perform this.
  • Install the service on the machine. This will make it run as an administrator, and just be always on.
  • Make a second application to act as a client. Use the same IPC technology to communicate with the server. This would send the request to unblock the client to the service.

You could then run the client as a normal user (since it just needs to talk to the service, it does not do anything that requires permissions).

Reed Copsey
@Reed: The so-called only way to do it is what he probably (whether he realizes it or not) wants to do. Requiring admin privledges could just as easily be done via right-click, but normal users won't be able to run the application in that case.
Brian
Yeah - I do agree with you, though, that this is in general just a bad idea...
Reed Copsey
I *had* to do this several years back to use a certain COM port API that would only work for Power Users and up. Not pretty, but it works.
crashmstr
Actually, MS considers this technique to be the *correct* way to write applications that are intended for non-administrative users but have some functionality that requires administrative access. This is the suggested approach for writing Vista applications so that they don't require privledge escalation.
Brian
+2  A: 

You don't have to use a windows service to do something as someone else. You can use impersonation to login as another user to do the actual switch. Here’s an example I found that uses the windows dll "advapi32.dll" to login.

Grab the sample code off the bottom of the page. I didn’t want to just copy his code here.

http://csharptuning.blogspot.com/2007/06/impersonation-in-c.html

One cavet with Impersonation though is that the computer doing the impersonation needs to be on the same domain as the user that your impersonating.

dilbert789
+1  A: 

Looks like you want to impersonate the admin user. Here's an article and demo. Looks to be written in .Net 1 but should get you started. Also check out the WindowsIdentity class.

Jeremy
problem with this is that its very old and not up to date. Impersonating an admin user so far means credentials need passing which is not ideal as they need protecting.
IbrarMumtaz
A: 

I have a very similar widget on my intranet site, so members of the IT department located in different time zones can handle password resets that also performs a account unlock when the domain admins on the west coast are not available. This is a pretty simple tasks and here is an except of how I did this...

  using System.DirectoryServices;

  // Impersonate the Admin to Reset the Password / Unlock Account //
  // Change variables below.
  ImpersonateUser iu = new ImpersonateUser();
        if (iu.impersonateValidUser("AdminUserName", "DomainName", "AdminPassword"))
        {
            resetPassword("AdminUserName", "AdminPassword", UserToReset, "NewPassword");
            iu.undoImpersonation();
        }

  // Perform the Reset / Unlock //
  public void resetPassword(string username, string password, string acct, string newpassword)
  {
            string Path = // LDAP Connection String
            string Username = username;
            string Password = password;
            string Domain = "DomainName\\"; // Change to your domain name

            DirectoryEntry de = new DirectoryEntry(Path, Domain + Username, Password, AuthenticationTypes.Secure);

            DirectorySearcher ds = new DirectorySearcher(de);

            ds.Filter = "(&(objectClass=user)(|(sAMAccountName=" + acct + ")))";

            ds.PropertiesToLoad.Add("displayName");
            ds.PropertiesToLoad.Add("sAMAccountName");
            ds.PropertiesToLoad.Add("DistinguishedName");
            ds.PropertiesToLoad.Add("CN");

            SearchResult result = ds.FindOne();

            string dn = result.Properties["DistinguishedName"][0].ToString();

            DirectoryEntry uEntry = new DirectoryEntry("LDAP://" + dn, username, password);

            uEntry.Invoke("SetPassword", new object[] { newpassword });
            uEntry.Properties["LockOutTime"].Value = 0;
            uEntry.CommitChanges();
            uEntry.Close();
  }

I strongly agree that this can lead to security issues if incorrectly used, we have every change logged and emailed to the domain admins (so their in the loop) and we auto generate the passwords. This has been a huge help for our small IT department, since admins no longer have to wake up at 4 AM to reset a password.

Zachary
This is exactly exactly what im talking about i made a prog that will be installed on a plant managers machine but he is a normal user ofcorse and we do the same thing i also added up dates to my program so all i have to do is re publish my application to the directory and when the plant lead opens my application it searches for updates then it updates thus changing the stored admin creds.
Im getting this error when trying to use your example and i have added the correct references...Error 1 The type or namespace name 'ImpersonateUser' could not be found (are you missing a using directive or an assembly reference?) im using dotnet 3.5 maybe that?
A few things...1. I'm pretty sure the user that initiates the impersonation (ASPNET or NETWORK SERVICES account) needs to have the "Act as part of operating system" privilege set. It's been awhile, so double check this...2. I should have told you that my impersonate users reference above is part of another class where I wrap up the impersation processing... for reuse... Check out this Code Project article of something similar: http://www.codeproject.com/KB/cs/zetaimpersonator.aspxIf you don't figure it out, I can post full working source Monday... Good Luck!
Zachary
Yes that would be great if you could please post full working code.Thank you for your time!
A: 

This code will allow you to call another executable and run it is an administrator.

try
{
   path = path_to_your_executable;

   ProcessStartInfo myProcess = new ProcessStartInfo(path);
   myProcess.Domain = domain;
   myProcess.UserName = username;
   myProcess.Password = password;
   myProcess.UseShellExecute = false;

   Process.Start(myProcess);
}
catch (Exception myException)
{
   // error handling
}

Not exactly what you're looking for but it is a possible solution.

rodey
This may do the trick as long as it will authenticate my application and gives it admin rights to unlcok the user thats all that maters.let me lay this out better. first off THANK YOU SO MUCH TO EVERY ONE!I limited this to search only a specific OU and with in that ou is the generic account that like 10 people use to to log into 5 consoles around the plant they sometimes screw up and it locks that generic account out so security im really not worried bout i have most of the bases covered and that account stored in my app will be monitored very close and creds changed alot.
lol just trying to avoid those 3 am calls for this simple task
A: 

Here is the class I use to perform impersonation on a ASP.NET 2.0 website, running on Windows 2000.

Example Usage:

if (iu.impersonateValidUser("AdminUserName", "DomainName", "AdminPassword"))
{            
    // Do Something Under Other Users Security Context
    iu.undoImpersonation();
}

That's it... Complete class below.

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;


public class ImpersonateUser
{
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    WindowsImpersonationContext impersonationContext;

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

    public bool impersonateValidUser(String userName, String domain, String password)
    {
        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        return false;
    }

    public void undoImpersonation()
    {
        impersonationContext.Undo();
    }
}
Zachary
Awesome thank you!
For some reason my program will only unlock a user that i recently created for testing. The users that have been in there if i lock one of them out and then run my app it will list them in the combobox and i click on them and then click the unlock button it dosnt work after debuging and watching it go through the code i foudn that rstest the account i created gets found and send the info back to result but the other users no result stays null what could be the issue?Thank you for your time
Is the location of the accounts the same, you might need to change your LDAP PATH to point to the correct location. By default, users in Windows get created in a CN called "Users". We normally store our users in a OU called "Geographic Locations". If the path statement doesn't point to the correct location, the user accounts will not be found.
Zachary