Re: Changing domain user password
- From: "Joe Kaplan \(MVP - ADSI\)" <joseph.e.kaplan@xxxxxxxxxxxxxxxxxxxxxxxx>
- Date: Thu, 22 Dec 2005 11:56:57 -0600
Ok. In this case, you really want to be using the ChangePassword method,
not SetPassword. SetPassword is for administrative resets. ChangePassword
is generally only performed by a user on their own account and requires the
current and new password.
ChangePassword can be a little tricky to get working on Win2K, especially if
your domain controller does not have an SSL certificate installed. They
don't by default, so someone would have needed to do this.
You will have trouble using Ryan's Impersonation class on Win2K server
though as an account must have the "act as part of the operating system"
privilege to call the LogonUser function he is using, and only SYSTEM has
that by default. You really should not be running your processModel as
SYSTEM. It is a very bad security practice! The problem you may be having
right now might relate to this.
As such, you probably want to consider doing regular impersonation combined
with Kerberos delegation. Kerberos delegation is an AD feature that allows
you to specify specific accounts that may log on to another service on the
network using the credentials the user originally supplied to them. In this
case, the web server would be logging in to AD on behalf of the user who
authenticated with the web site.
You would configure the app to use integrated auth in IIS (no anonymous),
configure ASP.NET for Windows auth and <identity impersonate="true"/> and
configure the machine account for the web server for Kerberos Delegation in
AD.
The code for doing the change would be similar, except that you would not do
programmatic impersonation and would invoke "ChangePassword", passing in
both the old and the new password.
You also want to be using SSL to protect this website since you are dealing
in plain text credentials. You don't want this traffic getting sniffed on
the wire!
Regarding the .NET Framework version, I definitely think you should be using
the 1.1 Framework minimum and possibly the 2.0 one. It is released now and
there is no good reason not to use it here. It has some nice bug fixes and
new features for System.DirectoryServices. The new features you don't need,
but the fixes are nice.
Your options get considerably better if you can upgrade your web server to
2003 SP1. Some of the limitations and difficulties go away and 2003 is
overall a much better web server platform in general.
HTH,
Joe K.
"Harry Devine" <hdevine@xxxxxxxxxxxxxxxx> wrote in message
news:e30ZkVxBGHA.516@xxxxxxxxxxxxxxxxxxxxxxx
>I wanted to allow a user to change their password to something else, so
>maybe I've been confusing myself here. Our server is Win2K, and I was
>changing the processModel section due to some info I found on the 'Net. I
>can put it back to it's original form. Would you recommend upgrading to
>the .NET framework 2.0 as well?
>
> Basically, I'm looking for an example of allowing a user to hit the site,
> enter their current password, and then enter a new one, using ASP.NET and
> System.Directory services.
>
> Sorry for the confusion, and thanks again for any help.
> Harry
>
> "Joe Kaplan (MVP - ADSI)" <joseph.e.kaplan@xxxxxxxxxxxxxxxxxxxxxxxx> wrote
> in message news:%23znXXqwBGHA.1144@xxxxxxxxxxxxxxxxxxxxxxx
>>I thought you were doing password changes, not resets. There are actually
>>important differences on how that is approached.
>>
>> It sounds like this is running on Win2K or XP, right? That's why you are
>> editing the processModel section.
>>
>> It also looks like you are using the 1.0 version of the .NET Framework.
>> Any reason for that? 1.1 or 2.0 would be preferred. The 1.0.3300.0
>> version for System.DirectoryServices points to the 1.0 framework.
>>
>> If you have changed the process model account, there is no reason to also
>> impersonate the same account in code. The process account will be
>> running as the admin user. However, this is generally not considered a
>> good practice, especially if other apps may be running on the same server
>> as they will all run under this administrative account and have more
>> privileges than they should.
>>
>> Can you also show the exact line where this is crashing? If they page
>> displays, it seems like there should be some indication of what the
>> problem was on the postback.
>>
>> Joe K.
>>
>> "Harry Devine" <hdevine@xxxxxxxxxxxxxxxx> wrote in message
>> news:eZ8u$cvBGHA.3604@xxxxxxxxxxxxxxxxxxxxxxx
>>> I'm reasonably sure ASP.NET is working (the .aspx page that I'm putting
>>> below comes up), but this is my first try at programming anything in it,
>>> so take that for what it's worth. I'm including the code for the
>>> reset.aspx file and web.config. The only change that I made to
>>> machine.config was to change the username and password under the
>>> processModel section to be my domain admin account (I found that
>>> suggestion somewhere on the Web while researching the error).
>>>
>>> Thanks for any help,
>>> Harry
>>>
>>> reset.aspx:
>>> <%@ Assembly Name="System.DirectoryServices, Version=1.0.3300.0,
>>> Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"%>
>>> <%@ Assembly Name="Dunnry.Security" %>
>>>
>>> <%@ Import Namespace="System.DirectoryServices" %>
>>> <%@ Import Namespace="Dunnry.Security" %>
>>>
>>> <HTML>
>>> <script language="C#" runat="server">
>>>
>>> void Page_Load(Object Src, EventArgs E ) {
>>> if(!Page.IsPostBack)
>>> {
>>>
>>> }
>>> }
>>>
>>> private void ResetPassword(object sender, EventArgs e)
>>> {
>>> //for impersonation
>>> string username = "AdminUser";
>>> string password = "adminpwd";
>>> string domain = "domain";
>>>
>>> Impersonate i = new
>>> Impersonate(LogonProvider.LOGON32_PROVIDER_WINNT50);
>>> i.ImpersonateUser(username, domain, password);
>>>
>>> string ldapPath = LDAP://dc=mydomain,dc=com;
>>> DirectoryEntry de = new DirectoryEntry(ldapPath);
>>> de.AuthenticationType = AuthenticationTypes.Secure;
>>> string qry =
>>> String.Format("(&(objectClass=user)(objectCategory=person)(sAMAccountName={0}))",
>>> txtUsername.Text);
>>>
>>> DirectorySearcher ds = new DirectorySearcher(de,qry);
>>> SearchResult sr = ds.FindOne();
>>>
>>> if(sr==null)
>>> {
>>> lblMessage.Text = "User not found";
>>> return;
>>> }
>>>
>>> try
>>> {
>>> DirectoryEntry user = sr.GetDirectoryEntry();
>>> user.AuthenticationType = AuthenticationTypes.Secure;
>>> user.Invoke("SetPassword", new object[]{txtPassword.Text});
>>> lblMessage.Text = "Success <br>";
>>> }
>>> catch(Exception ex)
>>> {
>>> //throw ex;
>>> lblMessage.Text = "Failure: " + ex.Message;
>>> if(ex.InnerException != null)
>>> lblMessage.Text += "<br>" + ex.InnerException.Message;
>>> }
>>> finally
>>> {
>>> de.Close();
>>> i.UndoImpersonation();
>>> }
>>> }
>>> </script>
>>>
>>> <body>
>>> <form runat="server">
>>> UserName: <asp:textbox id="txtUsername" runat="server"/><br>
>>> New Password: <asp:textbox id="txtPassword" runat="server"/><br>
>>> <asp:button id="btnReset" runat="server" Text="Reset"
>>> OnClick="ResetPassword" /><br>
>>> <asp:label id="lblMessage" runat="server"/><br><br>
>>> I am running as: <%=Context.User.Identity.Name %><br>
>>> My process is running as:
>>> <%=System.Security.Principal.WindowsIdentity.GetCurrent().Name %>
>>> </form>
>>> </body>
>>> </HTML>
>>>
>>>
>>> web.config:
>>>
>>> <?xml version="1.0" encoding="utf-8" ?>
>>> <configuration>
>>> <system.web>
>>> <compilation debug="true"/>
>>> <authentication mode="Windows" />
>>> <!--<authorization>
>>> <deny users="?" />
>>> </authorization>-->
>>> </system.web>
>>>
>>> <!-- Secure the .aspx page using web.config
>>> <location path="reset.aspx">
>>> <system.web>
>>> <authorization>
>>> <allow roles="DOMAIN\AdminUser" />
>>> <deny users="*" />
>>> </authorization>
>>> </system.web>
>>> </location> -->
>>> </configuration>
>>>
>>> processModel section of machine.config:
>>> <processModel
>>> enable="true"
>>> timeout="Infinite"
>>> idleTimeout="Infinite"
>>> shutdownTimeout="0:00:05"
>>> requestLimit="Infinite"
>>> requestQueueLimit="5000"
>>> restartQueueLimit="10"
>>> memoryLimit="60"
>>> webGarden="false"
>>> cpuMask="0xffffffff"
>>> userName="domain\adminuser"
>>> password="adminpwd"
>>> logLevel="Errors"
>>> clientConnectedCheck="0:00:05"
>>> comAuthenticationLevel="Connect"
>>> comImpersonationLevel="Impersonate"
>>> responseDeadlockInterval="00:03:00"
>>> maxWorkerThreads="20"
>>> maxIoThreads="20"
>>> />
>>>
>>>
>>> "Joe Kaplan (MVP - ADSI)" <joseph.e.kaplan@xxxxxxxxxxxxxxxxxxxxxxxx>
>>> wrote in message news:e%235SZFnBGHA.1864@xxxxxxxxxxxxxxxxxxxxxxx
>>>> The crux of doing this in a web page is simply to create a
>>>> DirectoryEntry object that is bound to the user's object in AD and
>>>> invoking the ChangePassword ADSI method. Impersonation may or may not
>>>> be needed as you need to prompt for the old password anyway, so it
>>>> doesn't really hurt to simply use those credentials in your
>>>> DirectoryEntry constructor.
>>>>
>>>> The error you are getting sounds like it is unrelated to an
>>>> DirectoryServices programming stuff though. Are you sure ASP.NET is
>>>> working in general?
>>>>
>>>> Note also that Ryan and I have book coming out that covers this stuff
>>>> in detail, but it won't be available for a few more months now.
>>>>
>>>> Posting an example of the code you are using would be a great start.
>>>>
>>>> Joe K.
>>>>
>>>> "Harry Devine" <hdevine@xxxxxxxxxxxxxxxx> wrote in message
>>>> news:%23FysvCmBGHA.1008@xxxxxxxxxxxxxxxxxxxxxxx
>>>>> I've been searching around for an answer to this question, but haven't
>>>>> gotten too far. I'm fairly new to ASP.NET, so I'm not sure how to
>>>>> setup machine.config and web.config properly.
>>>>>
>>>>> What I want to be able to do is allow a domain user to change their
>>>>> password in the AD via a webpage. We have several users with domain
>>>>> accounts, but they do not actually login to our domain as they are
>>>>> spread out all over the country. I have a VBS script that notifies
>>>>> them when their password is due to expire, starting 10 days out.
>>>>>
>>>>> Since these users are not local to where my domain controller resides,
>>>>> they have to call me or email me to have their password reset. I
>>>>> found an example written by Ryan Dunn using an Impersonate function
>>>>> that he wrote (www.dunnry.com), but I keep getting an error stating:
>>>>> "Parser Error Message: The XML file
>>>>> c:\winnt\microsoft.net\framework\v1.1.4322\Config\machine.config could
>>>>> not be loaded. Either a required impersonation level was not provided,
>>>>> or the provided impersonation level is invalid. "
>>>>>
>>>>> This seems like, to me, a fundamental type of function to do, but info
>>>>> on how to do it is all over the place. Does anyone have any good
>>>>> ideas or steps on how to accomplish this?
>>>>>
>>>>> Thanks for any help,
>>>>> Harry
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>
.
- Follow-Ups:
- Re: Changing domain user password
- From: Harry Devine
- Re: Changing domain user password
- References:
- Changing domain user password
- From: Harry Devine
- Re: Changing domain user password
- From: Joe Kaplan \(MVP - ADSI\)
- Re: Changing domain user password
- From: Harry Devine
- Re: Changing domain user password
- From: Joe Kaplan \(MVP - ADSI\)
- Re: Changing domain user password
- From: Harry Devine
- Changing domain user password
- Prev by Date: Re: Unable to update the password
- Next by Date: Re: Changing domain user password
- Previous by thread: Re: Changing domain user password
- Next by thread: Re: Changing domain user password
- Index(es):