View Issue Details

IDProjectCategoryView StatusLast Update
0005365JEDI API & WSC LibraryWindows Security Code Library (JWSCL)public2010-10-14 18:16
ReporterdangiAssigned ToChristianWimmer 
PrioritynormalSeveritymajorReproducibilitysometimes
Status resolvedResolutionfixed 
Summary0005365: Buffer overflow in TJwCredentialsPrompt.ShowModal()
DescriptionTo reproduce:
- change MaxUserNameLength and/or MaxPasswordLength
- set UserName and/or Password properties to values which exceed these limits
- call TJwCredentialsPrompt.ShowModal() --> access violation

Apply the patch to fix this
TagsNo tags attached.

Activities

2010-10-14 10:19

 

20101014_JwsclCredentials_buffer_overflow_patch.txt (1,103 bytes)
Index: JwsclCredentials.pas
===================================================================
--- JwsclCredentials.pas	(revision 1006)
+++ JwsclCredentials.pas	(working copy)
@@ -415,17 +415,19 @@
   pUser := TJwPChar(LocalAlloc(LMEM_ZEROINIT, fMaxUserNameLength *
     sizeof(TJwChar)));
   {$IFDEF UNICODE}
-      CopyMemory(pUser,TJwPChar(fUserName),Length(fUserName)*sizeof(TJwChar));
+  CopyMemory(pUser, TJwPChar(fUserName),
+    Min(fMaxUserNameLength-1, Length(fUserName)) * sizeof(TJwChar));
   {$ELSE}
-  StrCopy(pUser, TJwPChar(fUserName + #0));
+  StrLCopy(pUser, TJwPChar(fUserName), fMaxUserNameLength-1);
   {$ENDIF}
 
   pPass := TJwPChar(LocalAlloc(LMEM_ZEROINIT, fMaxPasswordLength *
     sizeof(TJwChar)));
   {$IFDEF UNICODE}
-      CopyMemory(pPass,TJwPChar(fPassword),Length(fPassword)*sizeof(TJwChar));
+  CopyMemory(pPass, TJwPChar(fPassword),
+    Min(fMaxPasswordLength-1, Length(fPassword)) * sizeof(TJwChar));
   {$ELSE}
-  StrCopy(pPass, TJwPChar(fPassword + #0));
+  StrLCopy(pPass, TJwPChar(fPassword), fMaxPasswordLength-1);
   {$ENDIF}
 
 

ChristianWimmer

2010-10-14 13:20

administrator   ~0017891

Thank you for your report.

I wonder if these MaxUserNameLength and MaxPasswordLength are necessary. Do you need them explicitly?

To the MSDN doc of CredUICmdLinePromptForCredentials the strings are truncated to the maximum given size.
However, on my Win7, the function returns 87 (Invalid Parameter) if the user inputs a username or password that is longer than the given max sizes.

ChristianWimmer

2010-10-14 14:16

administrator   ~0017894

Since people want to enter any username and password I would suggest to ignore the max TJwCredentialsPrompt properties.

dangi

2010-10-14 14:23

reporter   ~0017897

I agree to remove MaxUserNameLength and MaxPasswordLength properties.

As the command line option of ShowModal() does not work (see comment in code), I suggest to remove it also, because nobody is using it.

ChristianWimmer

2010-10-14 16:18

administrator   ~0017899

I fixed the issue by ignoring max properties and assume always maximum possible values.
Furthermore, I fixed the call ShowModal(True) (reading credentials on command line) which works now.
For more information see ShowModal doc in code.

The code is fixed in trunk (SVN revision >= 1025) and branch 0.9.3 (SVN revision >= 1026)

dangi

2010-10-14 17:30

reporter   ~0017900

Last edited: 2010-10-14 17:36

Changes now raises an exception when the user click on Cancel (Windows 7 32 bits EN).
I made a patch for branch 0.9.3 SVN rev # 1026 to handle this correctly and always zero and free login and password memory.

2010-10-14 17:30

 

20101014_JwsclCredentials_ERROR_CANCELLED_patch.txt (9,104 bytes)
Index: JwsclCredentials.pas
===================================================================
--- JwsclCredentials.pas	(revision 1026)
+++ JwsclCredentials.pas	(working copy)
@@ -420,123 +420,134 @@
       RsUNCredentialsEmptyServerName, 'ShowModal',
       ClassName, RsUNCredentials, 0, False, []);
 
-  GetMem(pUser, CRED_MAX_USERNAME_LENGTH * sizeof(TJwChar));
+  lResult := NO_ERROR;
 
-  hRes := {$IFDEF UNICODE}StringCchCopyW{$ELSE}StringCchCopyA{$ENDIF}
-    (pUser, CRED_MAX_USERNAME_LENGTH, TJwPChar(fUserName));
+  pUser := nil;
+  pPass := nil;
+  try
+    GetMem(pUser, CRED_MAX_USERNAME_LENGTH * sizeof(TJwChar));
 
-  if FAILED(hRes) then
-  begin
-    raise EJwsclInvalidParameterException.CreateFmtEx(
-      'Property %0:s must be between %1:d and %2:d.', 'ShowModal',
-      ClassName, RsUNCredentials, 0, hRes, ['UserName', 0, CRED_MAX_USERNAME_LENGTH]);
-  end;
+    hRes := {$IFDEF UNICODE}StringCchCopyW{$ELSE}StringCchCopyA{$ENDIF}
+      (pUser, CRED_MAX_USERNAME_LENGTH, TJwPChar(fUserName));
 
-  GetMem(pPass, CREDUI_MAX_PASSWORD_LENGTH * sizeof(TJwChar));
+    if FAILED(hRes) then
+    begin
+      raise EJwsclInvalidParameterException.CreateFmtEx(
+        'Property %0:s must be between %1:d and %2:d.', 'ShowModal',
+        ClassName, RsUNCredentials, 0, hRes, ['UserName', 0, CRED_MAX_USERNAME_LENGTH]);
+    end;
 
-  hRes := {$IFDEF UNICODE}StringCchCopyW{$ELSE}StringCchCopyA{$ENDIF}
-    (pPass, CREDUI_MAX_PASSWORD_LENGTH, TJwPChar(fPassword));
-  if FAILED(hRes) then
-  begin
-    raise EJwsclInvalidParameterException.CreateFmtEx(
-      'Property %0:s must be between %1:d and %2:d.', 'ShowModal',
-      ClassName, RsUNCredentials, 0, hRes, ['Password', 0, CREDUI_MAX_PASSWORD_LENGTH]);
-  end;
+    GetMem(pPass, CREDUI_MAX_PASSWORD_LENGTH * sizeof(TJwChar));
 
-  FillChar(info, sizeof(info), 0);
-  info.cbSize := sizeof(info);
-  info.hwndParent := fParentWindow;
-  info.pszMessageText := TJwPChar(fMessageText);
-  info.pszCaptionText := TJwPChar(fCaption);
+    hRes := {$IFDEF UNICODE}StringCchCopyW{$ELSE}StringCchCopyA{$ENDIF}
+      (pPass, CREDUI_MAX_PASSWORD_LENGTH, TJwPChar(fPassword));
+    if FAILED(hRes) then
+    begin
+      raise EJwsclInvalidParameterException.CreateFmtEx(
+        'Property %0:s must be between %1:d and %2:d.', 'ShowModal',
+        ClassName, RsUNCredentials, 0, hRes, ['Password', 0, CREDUI_MAX_PASSWORD_LENGTH]);
+    end;
 
-  if Assigned(fBanner) then
-    info.hbmBanner := fBanner.Handle;
+    FillChar(info, sizeof(info), 0);
+    info.cbSize := sizeof(info);
+    info.hwndParent := fParentWindow;
+    info.pszMessageText := TJwPChar(fMessageText);
+    info.pszCaptionText := TJwPChar(fCaption);
 
-  aBool := fSaveCheck;
+    if Assigned(fBanner) then
+      info.hbmBanner := fBanner.Handle;
 
-  if (CommandLine) then
-  begin
-    tempFlag := fFlags;
-    //CredUICmdLinePromptForCredentials needs cfFlagsRequireSmartCard or cfFlagsExcludeCertificates
-    if not (cfFlagsRequireSmartCard in Flags)  then
-      Include(tempFlag, cfFlagsExcludeCertificates)
-    else
-      Exclude(tempFlag, cfFlagsExcludeCertificates);
+    aBool := fSaveCheck;
 
-    lResult :=
+    if (CommandLine) then
+    begin
+      tempFlag := fFlags;
+      //CredUICmdLinePromptForCredentials needs cfFlagsRequireSmartCard or cfFlagsExcludeCertificates
+      if not (cfFlagsRequireSmartCard in Flags)  then
+        Include(tempFlag, cfFlagsExcludeCertificates)
+      else
+        Exclude(tempFlag, cfFlagsExcludeCertificates);
+
+      lResult :=
 {$IFDEF UNICODE}
-      CredUICmdLinePromptForCredentialsW
+        CredUICmdLinePromptForCredentialsW
 {$ELSE}
-      CredUICmdLinePromptForCredentialsA
+        CredUICmdLinePromptForCredentialsA
 {$ENDIF}
-          (TJwPChar(fServerName),      //PCTSTR pszTargetName,
+            (TJwPChar(fServerName),      //PCTSTR pszTargetName,
+            nil,     //PCtxtHandle Reserved,
+            fAuthError,     //DWORD dwAuthError,
+            pUser,     //PCTSTR pszUserName,
+            CRED_MAX_USERNAME_LENGTH,     //ULONG ulUserNameMaxChars,
+            pPass,     //PCTSTR pszPassword,
+            CREDUI_MAX_PASSWORD_LENGTH,     //ULONG ulPasswordMaxChars,
+            @aBool,     //PBOOL pfSave,
+            TJwEnumMap.ConvertToCredentialFlag(tempFlag)     //DWORD dwFlags
+            );
+    end
+    else
+    begin
+      lResult :=
+{$IFDEF UNICODE}
+        CredUIPromptForCredentialsW
+{$ELSE}
+        CredUIPromptForCredentialsA
+{$ENDIF}
+          (@info,      //PCREDUI_INFO pUiInfo,
+          TJwPChar(fServerName),      //PCTSTR pszTargetName,
           nil,     //PCtxtHandle Reserved,
           fAuthError,     //DWORD dwAuthError,
           pUser,     //PCTSTR pszUserName,
           CRED_MAX_USERNAME_LENGTH,     //ULONG ulUserNameMaxChars,
           pPass,     //PCTSTR pszPassword,
           CREDUI_MAX_PASSWORD_LENGTH,     //ULONG ulPasswordMaxChars,
-          @aBool,     //PBOOL pfSave,
-          TJwEnumMap.ConvertToCredentialFlag(tempFlag)     //DWORD dwFlags
+          aBool,     //PBOOL pfSave,
+          TJwEnumMap.ConvertToCredentialFlag(fFlags)      //DWORD dwFlags
           );
-  end
-  else
-  begin
-    lResult :=
-{$IFDEF UNICODE}
-      CredUIPromptForCredentialsW
-{$ELSE}
-      CredUIPromptForCredentialsA
-{$ENDIF}
-        (@info,      //PCREDUI_INFO pUiInfo,
-        TJwPChar(fServerName),      //PCTSTR pszTargetName,
-        nil,     //PCtxtHandle Reserved,
-        fAuthError,     //DWORD dwAuthError,
-        pUser,     //PCTSTR pszUserName,
-        CRED_MAX_USERNAME_LENGTH,     //ULONG ulUserNameMaxChars,
-        pPass,     //PCTSTR pszPassword,
-        CREDUI_MAX_PASSWORD_LENGTH,     //ULONG ulPasswordMaxChars,
-        aBool,     //PBOOL pfSave,
-        TJwEnumMap.ConvertToCredentialFlag(fFlags)      //DWORD dwFlags
-        );
 
-  end;
+    end;
 
-  if (lResult <> NO_ERROR) then
-  begin
-    FreeMem(pUser);
-    FreeMem(pPass);
-    SetLastError(lResult);
+    if (lResult <> NO_ERROR) then
+    begin
+      SetLastError(lResult);
 
-    case lResult of
-      ERROR_INVALID_FLAGS:
-        raise EJwsclInvalidFlagsException.CreateFmtEx(
-          RsUNCredentialsInvalidPropertyFlags, 'ShowModal', ClassName, RsUNCredentials,
-          0, true, []);
-      ERROR_INVALID_PARAMETER:
-        raise EJwsclInvalidParameterException.CreateFmtEx(
-          RsUNCredentialsInvalidParametersCUIPFC, 'ShowModal',
-          ClassName, RsUNCredentials, 0, true, []);
-      ERROR_NO_SUCH_LOGON_SESSION: raise EJwsclNoSuchLogonSession.CreateFmtEx(
-          RsUNCredentialsInvalidLogonSession, 'ShowModal', ClassName, RsUNCredentials,
-          0, true, []);
+      case lResult of
+        ERROR_CANCELLED: ; // User chose Cancel. The user name and password have not changed.
+        ERROR_INVALID_FLAGS:
+          raise EJwsclInvalidFlagsException.CreateFmtEx(
+            RsUNCredentialsInvalidPropertyFlags, 'ShowModal', ClassName, RsUNCredentials,
+            0, true, []);
+        ERROR_INVALID_PARAMETER:
+          raise EJwsclInvalidParameterException.CreateFmtEx(
+            RsUNCredentialsInvalidParametersCUIPFC, 'ShowModal',
+            ClassName, RsUNCredentials, 0, true, []);
+        ERROR_NO_SUCH_LOGON_SESSION: raise EJwsclNoSuchLogonSession.CreateFmtEx(
+            RsUNCredentialsInvalidLogonSession, 'ShowModal', ClassName, RsUNCredentials,
+            0, true, []);
+      else
+        raise EJwsclWinCallFailedException.CreateFmtWinCall(
+            RsWinCallFailed, 'ShowModal', ClassName, RsUNCredentials,
+            0, true, 'CredUIPromptForCredentials',['CredUIPromptForCredentials']);
+      end;
+    end
     else
-      raise EJwsclWinCallFailedException.CreateFmtWinCall(
-          RsWinCallFailed, 'ShowModal', ClassName, RsUNCredentials,
-          0, true, 'CredUIPromptForCredentials',['CredUIPromptForCredentials']);
+    if (lResult = NO_ERROR) then
+    begin
+      fUserName := TJwString(pUser);
+      fPassword := TJwString(pPass);
     end;
-  end
-  else
-  if (lResult = NO_ERROR) then
-  begin
-    fUserName := TJwString(pUser);
-    fPassword := TJwString(pPass);
+  finally
+    if Assigned(pUser) then
+    begin
+      ZeroMemory(pUser, CRED_MAX_USERNAME_LENGTH * SizeOf(TJwChar));
+      FreeMem(pUser);        
+    end;
 
-    ZeroMemory(pUser, CRED_MAX_USERNAME_LENGTH * SizeOf(TJwChar));
-    ZeroMemory(pPass, CREDUI_MAX_PASSWORD_LENGTH * SizeOf(TJwChar));
-
-    FreeMem(pUser);
-    FreeMem(pPass);
+    if Assigned(pPass) then
+    begin
+      ZeroMemory(pPass, CREDUI_MAX_PASSWORD_LENGTH * SizeOf(TJwChar));
+      FreeMem(pPass);
+    end;    
   end;
 
   fSaveCheck := aBool;
@@ -550,7 +561,7 @@
 
     if (
   {$IFDEF UNICODE}
-     CredUIConfirmCredentialsW
+      CredUIConfirmCredentialsW
   {$ELSE}
       CredUIConfirmCredentialsA
   {$ENDIF}

ChristianWimmer

2010-10-14 17:56

administrator   ~0017902

I resolved the issue in 0.9.3 (rev 1027)

The username and password are wrapped into JWSCL auto pointers to free and clear them automatically.

The problem with cancel was that the command prompt version returns ERROR_CANCELLED if no smartcard was not found.

dangi

2010-10-14 18:09

reporter   ~0017903

Thank you.
It work properly for me now using 0.9.3 (rev 1027).

Issue History

Date Modified Username Field Change
2010-10-14 10:19 dangi New Issue
2010-10-14 10:19 dangi Status new => assigned
2010-10-14 10:19 dangi Assigned To => ChristianWimmer
2010-10-14 10:19 dangi File Added: 20101014_JwsclCredentials_buffer_overflow_patch.txt
2010-10-14 13:20 ChristianWimmer Note Added: 0017891
2010-10-14 13:20 ChristianWimmer Status assigned => acknowledged
2010-10-14 14:16 ChristianWimmer Note Added: 0017894
2010-10-14 14:23 dangi Note Added: 0017897
2010-10-14 16:18 ChristianWimmer Note Added: 0017899
2010-10-14 16:18 ChristianWimmer Status acknowledged => resolved
2010-10-14 16:18 ChristianWimmer Resolution open => fixed
2010-10-14 17:30 dangi Note Added: 0017900
2010-10-14 17:30 dangi Status resolved => feedback
2010-10-14 17:30 dangi Resolution fixed => reopened
2010-10-14 17:30 dangi File Added: 20101014_JwsclCredentials_ERROR_CANCELLED_patch.txt
2010-10-14 17:30 dangi Note Edited: 0017900
2010-10-14 17:36 ChristianWimmer Note Edited: 0017900
2010-10-14 17:42 ChristianWimmer Status feedback => confirmed
2010-10-14 17:56 ChristianWimmer Note Added: 0017902
2010-10-14 17:56 ChristianWimmer Status confirmed => feedback
2010-10-14 18:09 dangi Note Added: 0017903
2010-10-14 18:16 ChristianWimmer Status feedback => resolved
2010-10-14 18:16 ChristianWimmer Resolution reopened => fixed