Nils Guillermin
Nils Guillermin

Reputation: 1987

Username and password switched when using Windows Credential Manager

I got this Delphi code from SO that seemed pretty straightforward. I added some types that were missing and substituted some string handling functions that were missing, and got this.

unit WinCred;

interface

uses
  Windows, SysUtils;

type
  CREDENTIAL_ATTRIBUTE = packed record
    Keyword: LPTSTR;
    Flags: DWORD;
    ValueSize: DWORD;
    Value: LPBYTE;
  end;
  PCREDENTIAL_ATTRIBUTE = ^CREDENTIAL_ATTRIBUTE;

  CREDENTIALW = packed record
    Flags: DWORD;
    Type_: DWORD;
    TargetName: LPTSTR;
    Comment: LPTSTR;
    LastWritten: FILETIME;
    CredentialBlobSize: DWORD;
    CredentialBlob: LPBYTE;
    Persist: DWORD;
    AttributeCount: DWORD;
    Attributes: PCREDENTIAL_ATTRIBUTE;
    TargetAlias: LPTSTR;
    UserName: LPTSTR;
  end;
  PCREDENTIALW = ^CREDENTIALW;

function CredWriteW(Credential: PCREDENTIALW; Flags: DWORD): Boolean; stdcall; external 'Advapi32.dll';

function CredWriteGenericCredentials(const Target, Username, Password: UnicodeString): Boolean;

implementation

function CredWriteGenericCredentials(const Target, Username, Password: UnicodeString): Boolean;
var
    Credentials: CREDENTIALW;
begin
    ZeroMemory(@Credentials, SizeOf(Credentials));
    Credentials.TargetName := PWideChar(Target); //cannot be longer than CRED_MAX_GENERIC_TARGET_NAME_LENGTH (32767) characters. Recommended format "Company_Target"
    Credentials.Type_ := CRED_TYPE_GENERIC;
    Credentials.UserName := PWideChar(Username);
    Credentials.Persist := CRED_PERSIST_LOCAL_MACHINE;
    Credentials.CredentialBlob := PByte(Password);
    Credentials.CredentialBlobSize := 2*(Length(Password)); //By convention no trailing null. Cannot be longer than CRED_MAX_CREDENTIAL_BLOB_SIZE (512) bytes
    Result := CredWriteW(@Credentials, 0);
end;

end.

However, when I test this function

[Test]
[TestCase('user', 'pass')]
procedure TestInsertCred(Username, password: string);

...

procedure TWinCredTests.TestInsertCred(Username, password: string);
begin
  Assert.IsTrue(CredWriteGenericCredentials('WinCred_Test', Username, password));
end;

The password shows up in the username.

enter image description here

This is baffling me. Shouldn't the .Username field take the username? And the password is written to the "credentialBlob"? I've checked all my function signatures to make sure I haven't mistakenly switched things around.

Upvotes: 2

Views: 972

Answers (1)

Ondrej Kelle
Ondrej Kelle

Reputation: 37221

Assuming you're using DUnitX, the first parameter to the TestCase attribute is the test case name, followed by parameters. So you're defining a test case named 'user', passing it 'pass' as UserName and nothing as Password. Try this instead:

[TestCase('TestInsertCred', 'user,pass')]

Upvotes: 4

Related Questions