Commit 753ffb699 for imagemagick.org
commit 753ffb699934331b31028d4e271f2f6d6db85074
Author: Cristy <urban-warrior@imagemagick.org>
Date: Sun Feb 22 19:00:06 2026 -0500
https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-493f-jh8w-qhx3
diff --git a/MagickCore/blob.c b/MagickCore/blob.c
index 48fd4e66f..60a2382ff 100644
--- a/MagickCore/blob.c
+++ b/MagickCore/blob.c
@@ -1464,19 +1464,36 @@ MagickExport void *FileToBlob(const char *filename,const size_t extent,
file=fileno(stdin);
if (LocaleCompare(filename,"-") != 0)
{
+ int
+ flags = O_RDONLY | O_BINARY;
+
status=GetPathAttributes(filename,&attributes);
if ((status == MagickFalse) || (S_ISDIR(attributes.st_mode) != 0))
{
ThrowFileException(exception,BlobError,"UnableToReadBlob",filename);
return(NULL);
}
- file=open_utf8(filename,O_RDONLY | O_BINARY,0);
+#if defined(O_NOFOLLOW)
+ status=IsRightsAuthorized(SystemPolicyDomain,ReadPolicyRights,"follow");
+ if (status == MagickFalse)
+ flags|=O_NOFOLLOW;
+#endif
+ file=open_utf8(filename,flags,0);
}
if (file == -1)
{
ThrowFileException(exception,BlobError,"UnableToOpenFile",filename);
return(NULL);
}
+ status=IsRightsAuthorized(PathPolicyDomain,ReadPolicyRights,filename);
+ if (status == MagickFalse)
+ {
+ file=close_utf8(file)-1;
+ errno=EPERM;
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",filename);
+ return(NULL);
+ }
offset=(MagickOffsetType) lseek(file,0,SEEK_END);
count=0;
if ((file == fileno(stdin)) || (offset < 0) ||
@@ -1670,7 +1687,7 @@ MagickExport MagickBooleanType FileToImage(Image *image,const char *filename,
assert(filename != (const char *) NULL);
if (IsEventLogging() != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
- status=IsRightsAuthorized(PathPolicyDomain,WritePolicyRights,filename);
+ status=IsRightsAuthorized(PathPolicyDomain,ReadPolicyRights,filename);
if (status == MagickFalse)
{
errno=EPERM;
@@ -1680,12 +1697,31 @@ MagickExport MagickBooleanType FileToImage(Image *image,const char *filename,
}
file=fileno(stdin);
if (LocaleCompare(filename,"-") != 0)
- file=open_utf8(filename,O_RDONLY | O_BINARY,0);
+ {
+ int
+ flags = O_RDONLY | O_BINARY;
+
+#if defined(O_NOFOLLOW)
+ status=IsRightsAuthorized(SystemPolicyDomain,ReadPolicyRights,"follow");
+ if (status == MagickFalse)
+ flags|=O_NOFOLLOW;
+#endif
+ file=open_utf8(filename,flags,0);
+ }
if (file == -1)
{
ThrowFileException(exception,BlobError,"UnableToOpenBlob",filename);
return(MagickFalse);
}
+ status=IsRightsAuthorized(PathPolicyDomain,ReadPolicyRights,filename);
+ if (status == MagickFalse)
+ {
+ file=close_utf8(file);
+ errno=EPERM;
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",filename);
+ return(MagickFalse);
+ }
quantum=(size_t) MagickMaxBufferExtent;
if ((fstat(file,&file_stats) == 0) && (file_stats.st_size > 0))
quantum=(size_t) MagickMin(file_stats.st_size,MagickMaxBufferExtent);
@@ -3277,6 +3313,9 @@ MagickExport MagickBooleanType OpenBlob(const ImageInfo *image_info,
const char
*type;
+ int
+ flags = O_RDONLY;
+
MagickBooleanType
status;
@@ -3309,13 +3348,48 @@ MagickExport MagickBooleanType OpenBlob(const ImageInfo *image_info,
blob_info->mode=mode;
switch (mode)
{
- default: type="r"; break;
- case ReadBlobMode: type="r"; break;
- case ReadBinaryBlobMode: type="rb"; break;
- case WriteBlobMode: type="w"; break;
- case WriteBinaryBlobMode: type="w+b"; break;
- case AppendBlobMode: type="a"; break;
- case AppendBinaryBlobMode: type="a+b"; break;
+ case ReadBlobMode:
+ {
+ flags=O_RDONLY;
+ type="r";
+ break;
+ }
+ case ReadBinaryBlobMode:
+ {
+ flags=O_RDONLY | O_BINARY;
+ type="rb";
+ break;
+ }
+ case WriteBlobMode:
+ {
+ flags=O_WRONLY | O_CREAT | O_TRUNC;
+ type="w";
+ break;
+ }
+ case WriteBinaryBlobMode:
+ {
+ flags=O_RDWR | O_CREAT | O_TRUNC | O_BINARY;
+ type="w+b";
+ break;
+ }
+ case AppendBlobMode:
+ {
+ flags=O_WRONLY | O_CREAT | O_APPEND;
+ type="a";
+ break;
+ }
+ case AppendBinaryBlobMode:
+ {
+ flags=O_RDWR | O_CREAT | O_APPEND | O_BINARY;
+ type="a+b";
+ break;
+ }
+ default:
+ {
+ flags=O_RDONLY;
+ type="r";
+ break;
+ }
}
if (*type != 'r')
blob_info->synchronize=image_info->synchronize;
@@ -3460,7 +3534,18 @@ MagickExport MagickBooleanType OpenBlob(const ImageInfo *image_info,
else
if (*type == 'r')
{
- blob_info->file_info.file=(FILE *) fopen_utf8(filename,type);
+ int
+ file;
+
+ blob_info->file_info.file=(FILE *) NULL;
+#if defined(O_NOFOLLOW)
+ status=IsRightsAuthorized(SystemPolicyDomain,ReadPolicyRights,"follow");
+ if (status == MagickFalse)
+ flags|=O_NOFOLLOW;
+#endif
+ file=open_utf8(filename,flags,0);
+ if (file >= 0)
+ blob_info->file_info.file=fdopen(file,type);
if (blob_info->file_info.file != (FILE *) NULL)
{
size_t
@@ -3580,13 +3665,32 @@ MagickExport MagickBooleanType OpenBlob(const ImageInfo *image_info,
else
#endif
{
- blob_info->file_info.file=(FILE *) fopen_utf8(filename,type);
+ int
+ file;
+
+ blob_info->file_info.file=(FILE *) NULL;
+#if defined(O_NOFOLLOW)
+ status=IsRightsAuthorized(SystemPolicyDomain,WritePolicyRights,
+ "follow");
+ if (status == MagickFalse)
+ flags|=O_NOFOLLOW;
+#endif
+ file=open_utf8(filename,flags,0666);
+ if (file >= 0)
+ blob_info->file_info.file=fdopen(file,type);
if (blob_info->file_info.file != (FILE *) NULL)
{
blob_info->type=FileStream;
(void) SetStreamBuffering(image_info,blob_info);
}
}
+ if (IsRightsAuthorized(PathPolicyDomain,rights,filename) == MagickFalse)
+ {
+ errno=EPERM;
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",filename);
+ return(MagickFalse);
+ }
blob_info->status=0;
blob_info->error_number=0;
if (blob_info->type != UndefinedStream)
diff --git a/MagickCore/utility.c b/MagickCore/utility.c
index acb344149..9b367b3ff 100644
--- a/MagickCore/utility.c
+++ b/MagickCore/utility.c
@@ -180,8 +180,13 @@ MagickExport MagickBooleanType AcquireUniqueSymbolicLink(const char *source,
char
*passes;
+ /*
+ Does policy permit symbolic links?
+ */
+ status=IsRightsAuthorized(SystemPolicyDomain,ReadPolicyRights |
+ WritePolicyRights,"follow");
passes=GetPolicyValue("system:shred");
- if (passes != (char *) NULL)
+ if ((passes != (char *) NULL) || (status == MagickFalse))
passes=DestroyString(passes);
else
{
@@ -209,6 +214,9 @@ MagickExport MagickBooleanType AcquireUniqueSymbolicLink(const char *source,
}
}
#endif
+ /*
+ Copy file from source to destination.
+ */
destination_file=AcquireUniqueFileResource(destination);
if (destination_file == -1)
return(MagickFalse);
diff --git a/config/policy-secure.xml b/config/policy-secure.xml
index 768487c58..15fb85b3f 100644
--- a/config/policy-secure.xml
+++ b/config/policy-secure.xml
@@ -108,4 +108,6 @@
<!-- Set the maximum amount of memory in bytes that are permitted for
allocation requests. -->
<policy domain="system" name="max-memory-request" value="256MiB"/>
+ <!-- If the basename of path is a symbolic link, the open fails -->
+ <policy domain="system" name="symlink" rights="none" pattern="follow"/>
</policymap>