如何使用管理员权限运行C#应用程序?

6
我写了一个C#应用程序,当用户被锁定时(Active Directory),该应用程序将解锁用户。该应用程序在特定组织单位中搜索用户,并将被锁定的用户列在ComboBox中。然后,您从ComboBox中选择该用户并选择解锁。
如果您以管理员身份登录,则它可以完美地工作。如果您是普通用户,则不行。
我想使用管理员凭据运行我的应用程序,但也要尽可能安全地在普通用户下运行。
我读到可能需要编写一个Windows服务,但我不清楚如何编程以安装、运行为服务和在管理员凭据下运行我的应用程序。

你是否可能正在使用Vista/W7/Server2008,并且以管理员身份登录,但应用程序在没有提升的凭据的情况下运行? - Nate
这是所有的XP专业工作站,AD是Win2k3。 - Ryan Summey
7个回答

8
这个应用的目标似乎是错误的。你基本上是在试图创建一种允许非管理员用户解锁帐户的方式……这是一个正常用户无法使用的功能,有其合理性。

2
我同意通常这是一个坏主意;然而,他可能允许某个高级用户(支持代表?)解锁其他用户帐户而不授予完全的管理员权限。 - Nate
那个特定的权限不能委派给适当的组吗? - Rob

2
这是我用于在运行在Windows 2000上的ASP.NET 2.0网站上执行模拟的类。
示例用法:
if (iu.impersonateValidUser("AdminUserName", "DomainName", "AdminPassword"))
{            
    // Do Something Under Other Users Security Context
    iu.undoImpersonation();
}

就是这样... 下面是完整的类。

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();
    }
}

由于某种原因,我的程序只能解锁最近为测试而创建的用户。如果我将其中一个已存在的用户锁定,然后运行我的应用程序,它会在组合框中列出它们,我点击它们,然后点击解锁按钮,它不起作用。在调试和观察代码执行过程后,我发现rstest找到了我创建的帐户并将信息发送回结果,但其他用户没有结果保持为空,可能是什么问题? 谢谢您的时间 - Ryan Summey
账户的位置是否相同,您可能需要更改LDAP路径以指向正确的位置。默认情况下,Windows用户会在名为“Users”的CN中创建。我们通常将用户存储在名为“地理位置”的OU中。如果路径语句没有指向正确的位置,用户帐户将无法找到。 - Zachary

2
“你不必使用Windows服务来以其他人的身份执行某些操作。你可以使用模拟登录来以另一个用户的身份进行实际切换。这里有一个例子,它使用了Windows DLL “advapi32.dll”来进行登录。
从页面底部获取示例代码。我不想在此处复制他的代码。”

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

“但是需要注意的是,使用模拟用户时,执行模拟的计算机需要与被模拟的用户在同一个域中。”

1

看起来您想要模拟管理员用户。这里有一篇文章和演示。它似乎是使用 .Net 1 编写的,但应该可以帮助您入门。此外,请查看WindowsIdentity类。


1
问题在于它非常古老且不是最新的。到目前为止,冒充管理员用户意味着需要传递凭据,这并不理想,因为它们需要保护。 - IbrarMumtaz

0

我在我的内部网站上有一个非常类似的小部件,因此位于不同时区的IT部门成员可以处理密码重置,同时在西海岸的域管理员不可用时执行帐户解锁。这是一个相当简单的任务,以下是我如何完成它的摘录...

        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();
        }

我非常同意,如果使用不当,这可能会导致安全问题。我们记录每一次更改并通过电子邮件发送给域管理员(以便他们了解情况),并自动生成密码。对于我们的小型IT部门来说,这是一个巨大的帮助,因为管理员不再需要在凌晨4点起床重置密码。


这正是我所说的。我制作了一个程序,将安装在工厂经理的计算机上,但他只是一个普通用户,我们做同样的事情。我还为我的程序添加了更新功能,因此我只需重新发布应用程序到目录中,当工厂主管打开我的应用程序时,它会搜索更新,然后进行更新,从而更改存储的管理员凭据。 - Ryan Summey
当我尝试使用你的示例并添加了正确的引用时,我遇到了以下错误...错误1:找不到类型或命名空间名称“ImpersonateUser”(是否缺少using指令或程序集引用?)我正在使用dotnet 3.5,可能是这个原因吗? - Ryan Summey
几件事情...
  1. 我相当确定启动模拟(ASPNET或NETWORK SERVICES帐户)的用户需要设置“作为操作系统的一部分”特权。已经有一段时间了,所以请再次确认...
  2. 我应该告诉你,我上面提到的模拟用户引用是另一个类的一部分,在那里我封装了模拟处理...以便重复使用...查看这个Code Project文章,了解类似的内容:http://www.codeproject.com/KB/cs/zetaimpersonator.aspx
如果你没有弄清楚,我可以在周一发布完整的工作源代码...祝你好运!
- Zachary
如果您能够发布完整的可工作代码,那就太好了。感谢您的时间! - Ryan Summey

0

这段代码将允许您调用另一个可执行文件并以管理员身份运行它。

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
}

这不完全是你要找的,但它是一个可能的解决方案。


只要它能验证我的应用程序并赋予管理员权限以解锁用户,那就可以了。让我更好地阐述一下。首先非常感谢每个人! 我将其限制为仅搜索特定OU,并且在该OU中有一个通用帐户,大约有10个人用于登录工厂周围的5个控制台,他们有时会出错并锁定该通用帐户,因此安全性真的不是我所担心的,我已经覆盖了大部分基础知识,并且存储在我的应用程序中的该帐户将被密切监视并且凭据会经常更改。 - Ryan Summey
只是想避免因这个简单的任务而在凌晨3点接到电话。 - Ryan Summey

0

你不能轻易地使用Windows服务,因为Windows服务不能有GUI。唯一的方法是安装该服务,然后创建一个使用IPC与服务通信的GUI应用程序。但这样做可能会打开一个潜在的漏洞。

如果你在运行Vista操作系统,最好的选择是编辑清单文件并添加requireAdministrator。


编辑:

听起来我的第一个建议可能是你想要的... 要做到这一点,基本过程如下:

  • 将您的应用程序作为Windows服务。 MSDN上有一个此过程的演练
  • 使您的服务响应某种形式的IPC。 您可以使用套接字、管道或任何其他形式的通信。 服务将“侦听”请求以解除用户阻止,然后执行此操作。
  • 在机器上安装服务。 这将使其作为管理员运行,并始终处于开启状态。
  • 制作第二个应用程序作为客户端。 使用相同的IPC技术与服务器通信。 这将向服务发送请求以解除客户端的阻止。

然后,您可以将客户端作为普通用户运行(因为它只需要与服务交谈,不会执行需要权限的任何操作)。


@Reed:所谓的唯一方法可能就是他想要做的(无论他是否意识到)。通过右键单击也可以轻松地要求管理员权限,但在这种情况下,普通用户将无法运行应用程序。 - Brian
我几年前不得不这样做,以使用某个只能适用于Power Users及以上权限的COM端口API。它不太美观,但是有效。 - crashmstr
实际上,微软认为这种技术是编写面向非管理员用户但需要某些需要管理员权限的功能的应用程序的“正确”方法。这是编写Vista应用程序的建议方法,以便它们不需要特权升级。 - Brian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接