浏览代码

Add and use LoadTrustedLibrary function which protects the library from changes between the trust check and the load. Also fixes a typo and improves ISCmplr/isscint error messages on trust fail.

Martijn Laan 5 月之前
父节点
当前提交
84ccece758

+ 2 - 2
Components/ScintEdit.pas

@@ -858,8 +858,8 @@ end;
 
 procedure TScintEdit.CreateWnd;
 begin
-  if IsscintLibary = 0 then
-    Error('CreateWnd: IsscintLibary is 0');
+  if IsscintLibrary = 0 then
+    Error('CreateWnd: IsscintLibrary is 0');
   inherited;
   FDirectPtr := Pointer(SendMessage(Handle, SCI_GETDIRECTPOINTER, 0, 0));
   if FDirectPtr = nil then

+ 3 - 3
Components/ScintInt.pas

@@ -1357,7 +1357,8 @@ const
   IsscintDLL = 'isscint.dll';
 
 var
-  IsscintLibary: HMODULE;
+  IsscintLibrary: HMODULE;
+  IsscintLibraryTrustFail: Boolean;
 
 implementation
 
@@ -1366,6 +1367,5 @@ uses
 
 initialization
   var FileName := AddBackslash(PathExtractPath(ParamStr(0))) + IsscintDLL;
-  if TrustedFileExists(FileName) then
-    IsscintLibary := LoadLibrary(PChar(FileName));
+  IsscintLibrary := LoadTrustedLibrary(PChar(FileName), IsscintLibraryTrustFail);
 end.

+ 36 - 4
Components/TrustFunc.pas

@@ -13,17 +13,21 @@ unit TrustFunc;
 
 interface
 
-function TrustedFileExists(const FileName: String): Boolean;
+function TrustedFileExists(const FileName: String; const CheckExists: Boolean = True): Boolean;
+function LoadTrustedLibrary(const FileName: String; out TrustFail: Boolean; const TrustAllOnDebug: Boolean = False): HMODULE;
 
 implementation
 
 uses
   Winapi.Windows, System.SysUtils {$IFNDEF TRUSTALL}, System.Classes, ECDSA, SHA256, ISSigFunc {$ENDIF};
 
-function TrustedFileExists(const FileName: String): Boolean;
+function TrustedFileExists(const FileName: String; const CheckExists: Boolean): Boolean;
 begin
-  var Attr := GetFileAttributes(PChar(FileName));
-  Result := (Attr <> INVALID_FILE_ATTRIBUTES) and (Attr and faDirectory = 0);
+  if CheckExists then begin
+    var Attr := GetFileAttributes(PChar(FileName));
+    Result := (Attr <> INVALID_FILE_ATTRIBUTES) and (Attr and faDirectory = 0);
+  end else
+    Result := True;
 {$IFNDEF TRUSTALL}
   if Result then begin
     try
@@ -79,4 +83,32 @@ begin
 {$ENDIF}
 end;
 
+function LoadTrustedLibrary(const FileName: String; out TrustFail: Boolean; const TrustAllOnDebug: Boolean): HMODULE;
+begin
+  TrustFail := False;
+{$IFDEF DEBUG}
+  if TrustAllOnDebug then begin
+    Result := SafeLoadLibrary(PChar(FileName), SEM_NOOPENFILEERRORBOX);
+    Exit;
+  end;
+{$ENDIF}
+  try
+    { First open a temporary regular handle to the library to protect it from changes
+      between the trust check and the load }
+    const F = TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
+    try
+      if TrustedFileExists(FileName, False) then
+        Result := SafeLoadLibrary(PChar(FileName), SEM_NOOPENFILEERRORBOX)
+      else begin
+        TrustFail := True;
+        Result := 0;
+      end;
+    finally
+      F.Free;
+    end;
+  except
+    Result := 0;
+  end;
+end;
+
 end.

+ 6 - 3
Projects/Compil32.dpr

@@ -13,6 +13,7 @@ uses
   SafeDLLPath in '..\Components\SafeDLLPath.pas',
   Windows,
   SysUtils,
+  StrUtils,
   Forms,
   PathFunc in '..\Components\PathFunc.pas',
   TrustFunc in '..\Components\TrustFunc.pas',
@@ -207,13 +208,15 @@ end;
 begin
   {$IFNDEF STATICCOMPILER}
   if ISCmplrLibrary = 0 then begin
-    MessageBox(0, PChar(Format('Could not load %s.', [ISCmplrDLL])), nil, MB_OK or MB_ICONSTOP);
+    MessageBox(0, PChar(Format('Could not load %s%s.',
+      [ISCmplrDLL, IfThen(ISCmplrLibraryTrustFail, ' (not trusted)', '')])), nil, MB_OK or MB_ICONSTOP);
     Halt(3);
   end;
   {$ENDIF}
 
-  if IsscintLibary = 0 then begin
-    MessageBox(0, PChar(Format('Could not load %s.' {$IFDEF DEBUG} + #13#10#13#10'Did you run Projects\Bin\synch-isfiles.bat as instructed in README.md?' {$ENDIF} , [IsscintDLL])), nil, MB_OK or MB_ICONSTOP);
+  if IsscintLibrary = 0 then begin
+    MessageBox(0, PChar(Format('Could not load %s%s.' {$IFDEF DEBUG} + #13#10#13#10'Did you run Projects\Bin\synch-isfiles.bat as instructed in README.md?' {$ENDIF} ,
+      [IsscintDLL, IfThen(IsscintLibraryTrustFail, ' (not trusted)', '')])), nil, MB_OK or MB_ICONSTOP);
     Halt(4);
   end;
 

+ 2 - 1
Projects/ISCC.dpr

@@ -20,6 +20,7 @@ uses
   SafeDLLPath in '..\Components\SafeDLLPath.pas',
   Windows,
   SysUtils,
+  StrUtils,
   Classes,
   {$IFDEF STATICCOMPILER} Compiler.Compile, {$ENDIF}
   PathFunc in '..\Components\PathFunc.pas',
@@ -566,7 +567,7 @@ begin
 
   {$IFNDEF STATICCOMPILER}
   if ISCmplrLibrary = 0 then begin
-    WriteStdErr(Format('Could not load %s.', [ISCmplrDLL]), True);
+    WriteStdErr(Format('Could not load %s%s.', [ISCmplrDLL, IfThen(ISCmplrLibraryTrustFail, ' (not trusted)', '')]), True);
     Halt(1);
   end;
   Ver := ISDllGetVersion;

+ 33 - 33
Projects/Src/Compiler.SetupCompiler.pas

@@ -512,15 +512,15 @@ begin
 {$IFNDEF STATICPREPROC}
   var Filename := CompilerDir + 'ISPP.dll';
   if NewFileExists(Filename) then begin
-    if {$IFNDEF DEBUG} TrustedFileExists(Filename) {$ELSE} True {$ENDIF} then begin
-      var M := SafeLoadLibrary(Filename, SEM_NOOPENFILEERRORBOX);
-      if M = 0 then
-        AbortCompileFmt('Failed to load ISPP.dll (%d)', [GetLastError]);
-      PreprocessScriptProc := GetProcAddress(M, 'ISPreprocessScriptW');
-      if not Assigned(PreprocessScriptProc) then
-        AbortCompile('Failed to get address of functions in ISPP.dll');
-    end else
-      AbortCompile('Failed to load ISPP.dll (not trusted)');
+    var TrustFail: Boolean;
+    var M := LoadTrustedLibrary(Filename, TrustFail, True);
+    if TrustFail then
+      AbortCompile('Failed to load ISPP.dll (not trusted)')
+    else if M = 0 then
+      AbortCompileFmt('Failed to load ISPP.dll (%d)', [GetLastError]);
+    PreprocessScriptProc := GetProcAddress(M, 'ISPreprocessScriptW');
+    if not Assigned(PreprocessScriptProc) then
+      AbortCompile('Failed to get address of functions in ISPP.dll');
   end; { else ISPP unavailable; fall back to built-in preprocessor }
 {$ELSE}
   PreprocessScriptProc := ISPreprocessScript;
@@ -533,14 +533,14 @@ begin
   if ZipInitialized then
     Exit;
   var Filename := CompilerDir + 'iszlib.dll';
-  if TrustedFileExists(Filename) then begin
-    var M := SafeLoadLibrary(Filename, SEM_NOOPENFILEERRORBOX);
-    if M = 0 then
-      AbortCompileFmt('Failed to load iszlib.dll (%d)', [GetLastError]);
-    if not ZlibInitCompressFunctions(M) then
-      AbortCompile('Failed to get address of functions in iszlib.dll');
-  end else
-    AbortCompile('Failed to load iszlib.dll (not trusted)');
+  var TrustFail: Boolean;
+  var M := LoadTrustedLibrary(Filename, TrustFail);
+  if TrustFail then
+    AbortCompile('Failed to load iszlib.dll (not trusted)')
+  else if M = 0 then
+    AbortCompileFmt('Failed to load iszlib.dll (%d)', [GetLastError]);
+  if not ZlibInitCompressFunctions(M) then
+    AbortCompile('Failed to get address of functions in iszlib.dll');
   ZipInitialized := True;
 end;
 
@@ -549,14 +549,14 @@ begin
   if BzipInitialized then
     Exit;
   var Filename := CompilerDir + 'isbzip.dll';
-  if TrustedFileExists(Filename) then begin
-    var M := SafeLoadLibrary(Filename, SEM_NOOPENFILEERRORBOX);
-    if M = 0 then
-      AbortCompileFmt('Failed to load isbzip.dll (%d)', [GetLastError]);
-    if not BZInitCompressFunctions(M) then
-      AbortCompile('Failed to get address of functions in isbzip.dll');
-  end else
-    AbortCompile('Failed to load isbzip.dll (not trusted)');
+  var TrustFail: Boolean;
+  var M := LoadTrustedLibrary(Filename, TrustFail);
+  if TrustFail then
+    AbortCompile('Failed to load isbzip.dll (not trusted)')
+  else if M = 0 then
+    AbortCompileFmt('Failed to load isbzip.dll (%d)', [GetLastError]);
+  if not BZInitCompressFunctions(M) then
+    AbortCompile('Failed to get address of functions in isbzip.dll');
   BzipInitialized := True;
 end;
 
@@ -565,14 +565,14 @@ begin
   if LZMAInitialized then
     Exit;
   var Filename := CompilerDir + 'islzma.dll';
-  if TrustedFileExists(Filename) then begin
-    var M := SafeLoadLibrary(Filename, SEM_NOOPENFILEERRORBOX);
-    if M = 0 then
-      AbortCompileFmt('Failed to load islzma.dll (%d)', [GetLastError]);
-    if not LZMAInitCompressFunctions(M) then
-      AbortCompile('Failed to get address of functions in islzma.dll');
-  end else
-    AbortCompile('Failed to load islzma.dll (not trusted)');
+  var TrustFail: Boolean;
+  var M := LoadTrustedLibrary(Filename, TrustFail);
+  if TrustFail then
+    AbortCompile('Failed to load islzma.dll (not trusted)')
+  else if M = 0 then
+    AbortCompileFmt('Failed to load islzma.dll (%d)', [GetLastError]);
+  if not LZMAInitCompressFunctions(M) then
+    AbortCompile('Failed to get address of functions in islzma.dll');
   LZMAInitialized := True;
 end;
 

+ 11 - 12
Projects/Src/Shared.CompilerInt.pas

@@ -19,6 +19,7 @@ const
 
 var
   ISCmplrLibrary: HMODULE;
+  ISCmplrLibraryTrustFail: Boolean;
 
 { The ISDllCompileScript function begins compilation of a script. See the above
   description of the TCompileScriptParams record. Return value is one of the
@@ -34,21 +35,19 @@ implementation
 uses
   Windows,
   SysUtils,
-  PathFunc {$IFNDEF DEBUG}, TrustFunc{$ENDIF};
+  PathFunc, TrustFunc;
 
 initialization
   var FileName := AddBackslash(PathExtractPath(ParamStr(0))) + ISCmplrDLL;
-  if {$IFNDEF DEBUG} TrustedFileExists(FileName) {$ELSE} True {$ENDIF} then begin
-    ISCmplrLibrary := SafeLoadLibrary(PChar(FileName), SEM_NOOPENFILEERRORBOX);
-    if ISCmplrLibrary <> 0 then begin
-      ISDllCompileScript := GetProcAddress(ISCmplrLibrary, 'ISDllCompileScriptW');
-      ISDllGetVersion := GetProcAddress(ISCmplrLibrary, 'ISDllGetVersion');
-      if not Assigned(ISDllCompileScript) or not Assigned(ISDllGetVersion) then begin
-        FreeLibrary(ISCmplrLibrary);
-        ISCmplrLibrary := 0;
-        ISDllCompileScript := nil;
-        ISDllGetVersion := nil;
-      end;
+  ISCmplrLibrary := LoadTrustedLibrary(FileName, ISCmplrLibraryTrustFail, True);
+  if ISCmplrLibrary <> 0 then begin
+    ISDllCompileScript := GetProcAddress(ISCmplrLibrary, 'ISDllCompileScriptW');
+    ISDllGetVersion := GetProcAddress(ISCmplrLibrary, 'ISDllGetVersion');
+    if not Assigned(ISDllCompileScript) or not Assigned(ISDllGetVersion) then begin
+      FreeLibrary(ISCmplrLibrary);
+      ISCmplrLibrary := 0;
+      ISDllCompileScript := nil;
+      ISDllGetVersion := nil;
     end;
   end;
 end.