5

I am having a problem that occurred in a specific circumstances in my Delphi 7 application.

I have ADOConnection that goes to my MS SQL server with some username and pass - SQL authentication. The problem is that MS SQL login was created with "User must change pass at next login" flag that makes ADO Connection impossible to connect with error message "18488 - Login failed for user '%.*ls'. Reason: The password of the account must be changed."

Normally, in MS SQL Management Studio a change password prompt is shown, and user is able to enter new password. The question is what should I do to force password change on this user in my application? I am able to catch error number and prompt for login change, but what then? There is no flag in a connection string that I could use to change pass/reset to new (like Old Password and New Password). What should I do then?

Can anyone help?

Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Surrogate
  • 331
  • 3
  • 11

2 Answers2

3

When you trap the 18488 error, You need to show your own "change password" dialog, and use connection string properties "Old Password"/"Password" via SQL Native Client as your provider (i.e. Provider=SQLNCLI10 or SQLNCLI.1).

Here is a small code I used to test this:

procedure TForm1.Button1Click(Sender: TObject);
begin
  // 12345 is the "old password"
  ADOConnection1.ConnectionString := 'Provider=SQLOLEDB.1;Password=12345;User ID=test;Initial Catalog=test;Data Source=127.0.0.1;Persist Security Info=True;';
  try
    ADOConnection1.Open;
  except
    if Assigned(ADOConnection1.Errors) and (ADOConnection1.Errors.Count > 0) and
      (ADOConnection1.Errors.Item[0].NativeError = 18488) then
    begin
      // show your "change password" dialog... new password is 67890
      ADOConnection1.ConnectionString := 'Provider=SQLNCLI10.1;Old Password=12345;Password=67890;User ID=test;Initial Catalog=test;Data Source=127.0.0.1;Persist Security Info=True;';
      ADOConnection1.Open; // this will login and change the password

      // OPTIONAL (unless you use SQLNCLI10.1 anyway)
      // you may close the connection and re-open with your original provider and new password
      ADOConnection1.Close;
      ADOConnection1.ConnectionString := 'Provider=SQLOLEDB.1;Password=67890;User ID=test;Initial Catalog=test;Data Source=127.0.0.1;Persist Security Info=True;';
      ADOConnection1.Open;
    end
    else
      raise;          
  end;
  ShowMessage('Login OK');
end;

My answer is based on these readings:

This is the official way of changing passwords from the client side when you enforce password expiration and use "User must change password at next login" option on the SQL server.


If installing the SQL Server Native Client on the users machine is an issue, I can think of few more options:

  1. Create a web-service (on your servers) that will be responsible for changing the user's password provided old/new passwords and returning status back to your client.
  2. Connect as "super user" (such as sa), and alter the user's/login password. meaning you will need to hold that username/password on the client machine (bad idea IMHO from security point of view but might be workable). - not tested
  3. do NOT create SQL logins with "User must change password at next login". which is my favorite solution.
kobik
  • 21,001
  • 4
  • 61
  • 121
  • I know, that could help but I have to use Microsoft OLE DB Provider for SQL Server instead :( – Surrogate Aug 04 '14 at 14:54
  • You only use `Provider=SQLNCLI10` when/if you change the password. then fall back to your original provider. this is in theory of curse. sorry, don't have time to check right now. I leave that to you :) – kobik Aug 04 '14 at 15:05
  • It is not that easy, i am afraid. I would have to install SQL Server Native Client on each computer that runs my application, and that is problematic – Surrogate Aug 04 '14 at 15:21
  • @Surrogate How do you update your application? Build a setup and include the msi-package for the native client. – Sir Rufo Aug 04 '14 at 20:42
  • my app is being run from the remote resource. building a setup and installing provider on a machine where i have put app executable will not solve the issue as client machines will not know what sql native client is. I will use a walkaround and "ask some other login" to alter my login pass. – Surrogate Aug 05 '14 at 09:33
  • 1
    I think I gave you the right/official answer. I don't see other sane options other than using the official way to change the passwords from the client machines, without any major security rinks such as "ask some other login" to alter my login pass" - if you mean something like connection via `sa` account from client to alter the password, is a very bad idea IMHO. let us know your solution when it's done. – kobik Aug 05 '14 at 13:32
  • I think a web service solution will take too much effort and creating db logins it is not up to me. I know it is a bad idea to save high privileged login credentials in client app, but i don't have to do this. This is client-server architecture where server is connected to database with different credentials than client apps. Clients are connected to server via socket and can ask to change the pass for them. This way client application does not know server's database credentials. I is not something I like 100% but I believe this it the best solution in my situation. Thanks again – Surrogate Aug 21 '14 at 10:22
0

If you are happy that you can trap this particular message, you can then show your own form, explaining that the SQL login password must be changed and ask the user for a new password. Then use the ALTER LOGIN command to change the password.

For safety, I would use the OLD_PASSWORD option to ensure that this user knew the old password and therefore has the facility to set the new one.

If you do not want the user to set it, then set it yourself.

Note that your user must have the ALTER ANY LOGIN permission set in order to do this.

J__
  • 3,777
  • 1
  • 23
  • 31
  • 3
    But if you get that error, you have no connection and thus you cannot execute a query, don't you ? – TLama Aug 04 '14 at 14:33
  • That's a good point. Perhaps you could use Windows Authentication or the 'sa' details to connect first. Or while the password is still valid, change the policy using the CHECK_EXPIRATION = OFF option. – J__ Aug 04 '14 at 14:35
  • 2
    The [`official source`](http://msdn.microsoft.com/en-us/library/ms131024.aspx) says that on SQL Server below 2005 only admins can change expired passwords. Above versions can do it e.g. through the login dialog, so one option might be to enable the login dialog when you detect that error. Then there is also an option by setting ADO connection properties. – TLama Aug 04 '14 at 14:38