Commit 4a9dc1075 for imagemagick.org
commit 4a9dc1075dcad3ab0579e1b37dbe854c882699a5
Author: Dirk Lemstra <dirk@lemstra.org>
Date: Tue Feb 3 21:09:59 2026 +0100
Prevent path traversal of paths that are blocked in the security policy (GHSA-8jvj-p28h-9gm7)
diff --git a/MagickCore/module.c b/MagickCore/module.c
index ca5ed0fe9..480dc53bb 100644
--- a/MagickCore/module.c
+++ b/MagickCore/module.c
@@ -595,15 +595,16 @@ static MagickBooleanType GetMagickModulePath(const char *filename,
(void) ConcatenateMagickString(path,DirectorySeparator,
MagickPathExtent);
(void) ConcatenateMagickString(path,filename,MagickPathExtent);
-#if defined(MAGICKCORE_HAVE_REALPATH)
{
char
- resolved_path[PATH_MAX+1];
+ *real_path = realpath_utf8(path);
- if (realpath(path,resolved_path) != (char *) NULL)
- (void) CopyMagickString(path,resolved_path,MagickPathExtent);
+ if (real_path != (char *) NULL)
+ {
+ (void) CopyMagickString(path,real_path,MagickPathExtent);
+ real_path=DestroyString(real_path);
+ }
}
-#endif
if (IsPathAccessible(path) != MagickFalse)
{
module_path=DestroyString(module_path);
diff --git a/MagickCore/policy.c b/MagickCore/policy.c
index 9ef1d6e67..2d96f5dce 100644
--- a/MagickCore/policy.c
+++ b/MagickCore/policy.c
@@ -640,6 +640,9 @@ static MagickBooleanType IsPolicyCacheInstantiated(ExceptionInfo *exception)
MagickExport MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
const PolicyRights rights,const char *pattern)
{
+ char
+ *real_pattern = (char *) NULL;
+
const PolicyInfo
*policy_info;
@@ -647,7 +650,8 @@ MagickExport MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
*exception;
MagickBooleanType
- authorized;
+ authorized,
+ match;
ElementInfo
*p;
@@ -671,22 +675,33 @@ MagickExport MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
*policy;
policy=(const PolicyInfo *) p->value;
- if ((policy->domain == domain) &&
- (GlobExpression(pattern,policy->pattern,MagickFalse) != MagickFalse))
+ if (policy->domain == domain)
{
- if ((rights & ReadPolicyRights) != 0)
- authorized=(policy->rights & ReadPolicyRights) != 0 ? MagickTrue :
- MagickFalse;
- if ((rights & WritePolicyRights) != 0)
- authorized=(policy->rights & WritePolicyRights) != 0 ? MagickTrue :
- MagickFalse;
- if ((rights & ExecutePolicyRights) != 0)
- authorized=(policy->rights & ExecutePolicyRights) != 0 ? MagickTrue :
- MagickFalse;
+ if ((policy->domain == PathPolicyDomain) &&
+ (real_pattern == (const char *) NULL))
+ real_pattern=realpath_utf8(pattern);
+ if (real_pattern != (char*) NULL)
+ match=GlobExpression(real_pattern,policy->pattern,MagickFalse);
+ else
+ match=GlobExpression(pattern,policy->pattern,MagickFalse);
+ if (match != MagickFalse)
+ {
+ if ((rights & ReadPolicyRights) != 0)
+ authorized=(policy->rights & ReadPolicyRights) != 0 ? MagickTrue :
+ MagickFalse;
+ if ((rights & WritePolicyRights) != 0)
+ authorized=(policy->rights & WritePolicyRights) != 0 ?
+ MagickTrue : MagickFalse;
+ if ((rights & ExecutePolicyRights) != 0)
+ authorized=(policy->rights & ExecutePolicyRights) != 0 ?
+ MagickTrue : MagickFalse;
+ }
}
p=p->next;
}
UnlockSemaphoreInfo(policy_semaphore);
+ if (real_pattern != (char *) NULL)
+ real_pattern=DestroyString(real_pattern);
return(authorized);
}
diff --git a/MagickCore/token.c b/MagickCore/token.c
index 4de86135b..84259aea2 100644
--- a/MagickCore/token.c
+++ b/MagickCore/token.c
@@ -518,6 +518,7 @@ MagickExport MagickBooleanType GlobExpression(
target=DestroyString(target);
break;
}
+#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
case '\\':
{
pattern+=GetUTFOctets(pattern);
@@ -525,6 +526,7 @@ MagickExport MagickBooleanType GlobExpression(
break;
magick_fallthrough;
}
+#endif
default:
{
if (case_insensitive != MagickFalse)
diff --git a/MagickCore/utility-private.h b/MagickCore/utility-private.h
index 444c706ca..d8b68e0e1 100644
--- a/MagickCore/utility-private.h
+++ b/MagickCore/utility-private.h
@@ -267,6 +267,121 @@ static inline FILE *popen_utf8(const char *command,const char *type)
#endif
}
+static inline char *realpath_utf8(const char *path)
+{
+#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
+#if defined(MAGICKCORE_HAVE_REALPATH)
+ return(realpath(path,(char *) NULL));
+#else
+ return(AcquireString(path));
+#endif
+#else
+ char
+ *real_path;
+
+ DWORD
+ final_path_length,
+ full_path_length;
+
+ HANDLE
+ file_handle;
+
+ int
+ length,
+ utf8_length;
+
+ wchar_t
+ *clean_path,
+ *full_path,
+ *wide_path;
+
+ /*
+ Convert UTF-8 to UTF-16.
+ */
+ if (path == (const char *) NULL)
+ return((char *) NULL);
+ length=MultiByteToWideChar(CP_UTF8,0,path,-1,NULL,0);
+ if (length <= 0)
+ return((char *) NULL);
+ wide_path=(wchar_t *) AcquireQuantumMemory(length,sizeof(wchar_t));
+ if (wide_path == (wchar_t *) NULL)
+ return((char *) NULL);
+ MultiByteToWideChar(CP_UTF8,0,path,-1,wide_path,length);
+ /*
+ Normalize syntactically.
+ */
+ full_path_length=GetFullPathNameW(wide_path,0,NULL,NULL);
+ if (full_path_length == 0)
+ {
+ wide_path=(wchar_t *) RelinquishMagickMemory(wide_path);
+ return((char *) NULL);
+ }
+ full_path=(wchar_t *) AcquireQuantumMemory(full_path_length,sizeof(wchar_t));
+ if (full_path == (wchar_t *) NULL)
+ {
+ wide_path=(wchar_t *) RelinquishMagickMemory(wide_path);
+ return((char *) NULL);
+ }
+ GetFullPathNameW(wide_path,full_path_length,full_path,NULL);
+ wide_path=(wchar_t *) RelinquishMagickMemory(wide_path);
+ /*
+ Open the file/directory to resolve symlinks.
+ */
+ file_handle=CreateFileW(full_path,GENERIC_READ,FILE_SHARE_READ |
+ FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ if (file_handle != INVALID_HANDLE_VALUE)
+ {
+ /*
+ Resolve final canonical path.
+ */
+ final_path_length=GetFinalPathNameByHandleW(file_handle,NULL,0,
+ FILE_NAME_NORMALIZED);
+ if (final_path_length == 0)
+ {
+ CloseHandle(file_handle);
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ return((char *) NULL);
+ }
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ full_path=(wchar_t *) AcquireQuantumMemory(final_path_length,
+ sizeof(wchar_t));
+ if (full_path == (wchar_t *) NULL)
+ {
+ CloseHandle(file_handle);
+ return((char *) NULL);
+ }
+ GetFinalPathNameByHandleW(file_handle,full_path,final_path_length,
+ FILE_NAME_NORMALIZED);
+ CloseHandle(file_handle);
+ }
+ /*
+ Remove \\?\ prefix for POSIX-like behavior.
+ */
+ clean_path=full_path;
+ if (wcsncmp(full_path,L"\\\\?\\",4) == 0)
+ clean_path=full_path+4;
+ /*
+ Convert UTF-16 to UTF-8.
+ */
+ utf8_length=WideCharToMultiByte(CP_UTF8,0,clean_path,-1,NULL,0,NULL,NULL);
+ if (utf8_length <= 0)
+ {
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ return NULL;
+ }
+ real_path=(char *) AcquireQuantumMemory(utf8_length,sizeof(char));
+ if (real_path == (char *) NULL)
+ {
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ return NULL;
+ }
+ WideCharToMultiByte(CP_UTF8,0,clean_path,-1,real_path,utf8_length,NULL,NULL);
+ full_path=(wchar_t *) RelinquishMagickMemory(full_path);
+ return(real_path);
+#endif
+}
+
static inline int remove_utf8(const char *path)
{
#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
diff --git a/MagickCore/utility.c b/MagickCore/utility.c
index 5b8f117c3..4e78d2891 100644
--- a/MagickCore/utility.c
+++ b/MagickCore/utility.c
@@ -1042,16 +1042,23 @@ MagickPrivate MagickBooleanType GetExecutionPath(char *path,const size_t extent)
#if defined(MAGICKCORE_HAVE__NSGETEXECUTABLEPATH)
{
char
- executable_path[PATH_MAX << 1],
- execution_path[PATH_MAX+1];
+ executable_path[PATH_MAX << 1];
uint32_t
length;
length=sizeof(executable_path);
- if ((_NSGetExecutablePath(executable_path,&length) == 0) &&
- (realpath(executable_path,execution_path) != (char *) NULL))
- (void) CopyMagickString(path,execution_path,extent);
+ if (_NSGetExecutablePath(executable_path,&length) == 0)
+ {
+ char
+ *real_path = realpath_utf8(executable_path);
+
+ if (real_path != (char *) NULL)
+ {
+ (void) CopyMagickString(path,real_path,extent);
+ real_path=DestroyString(real_path);
+ }
+ }
}
#endif
#if defined(MAGICKCORE_HAVE_GETEXECNAME)
@@ -1097,10 +1104,13 @@ MagickPrivate MagickBooleanType GetExecutionPath(char *path,const size_t extent)
if (count != -1)
{
char
- execution_path[PATH_MAX+1];
+ *real_path = realpath_utf8(program_name);
- if (realpath(program_name,execution_path) != (char *) NULL)
- (void) CopyMagickString(path,execution_path,extent);
+ if (real_path != (char *) NULL)
+ {
+ (void) CopyMagickString(path,real_path,extent);
+ real_path=DestroyString(real_path);
+ }
}
if (program_name != program_invocation_name)
program_name=(char *) RelinquishMagickMemory(program_name);
@@ -1882,7 +1892,7 @@ MagickPrivate MagickBooleanType ShredFile(const char *path)
{
char
*property;
-
+
passes=0;
property=GetEnvironmentValue("MAGICK_SHRED_PASSES");
if (property != (char *) NULL)