Commit 28f65867d for imagemagick.org
commit 28f65867d313fd5da01cc890addc9094b03d12f6
Author: Cristy <urban-warrior@imagemagick.org>
Date: Fri Oct 30 14:04:03 2026 -0400
https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-5v9f-r478-x7vx
diff --git a/MagickCore/distribute-cache.c b/MagickCore/distribute-cache.c
index db68c531b..547e4d2e6 100644
--- a/MagickCore/distribute-cache.c
+++ b/MagickCore/distribute-cache.c
@@ -80,6 +80,7 @@
#if defined(MAGICKCORE_DPC_SUPPORT)
#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
@@ -112,6 +113,21 @@
# define MSG_NOSIGNAL 0
#endif
+/*
+ Typedef declarations.
+*/
+typedef struct
+{
+ uint32_t
+ state[8];
+
+ uint64_t
+ count;
+
+ unsigned char
+ buffer[64];
+} SHA256Hash;
+
/*
Static declarations.
*/
@@ -124,11 +140,14 @@ static WSADATA
#endif
#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
+static char
+ *dpc_shared_secret = (char *) NULL;
+
static SemaphoreInfo
*dpc_semaphore = (SemaphoreInfo *) NULL;
-static char
- *dpc_shared_secret = (char *) NULL;
+static int
+ ComputeHMACSHA256Mac(const void *,size_t,const void *,size_t,unsigned char *,size_t);
static size_t
dpc_clients = 0,
@@ -169,6 +188,18 @@ static inline MagickOffsetType dpc_receive(SOCKET_TYPE magick_unused(file),
return(-1);
}
#else
+static void SetSocketNoDelay(SOCKET_TYPE file)
+{
+ int
+ one = 1;
+
+ /*
+ Disable Nagle.
+ */
+ (void) setsockopt(file,IPPROTO_TCP,TCP_NODELAY,(const char *) &one,
+ (socklen_t) sizeof(one));
+}
+
static void SetSocketReceiveTimeout(SOCKET_TYPE file,double seconds)
{
/*
@@ -275,6 +306,53 @@ static inline MagickOffsetType dpc_receive_deadline(SOCKET_TYPE file,
}
return(offset);
}
+
+static inline int mac_compare(const unsigned char *a,const unsigned char *b,
+ const size_t length)
+{
+ size_t
+ i;
+
+ volatile unsigned char
+ difference = 0;
+
+ /*
+ Compare in time independent of where (or whether) the buffers differ.
+ */
+ for (i=0; i < length; i++)
+ difference|=(unsigned char) (a[i] ^ b[i]);
+ return(difference == 0 ? 0 : -1);
+}
+
+static inline MagickOffsetType dpc_receive_mac(SOCKET_TYPE file,
+ const MagickSizeType length,unsigned char *magick_restrict message,
+ uint64_t session_key)
+{
+ MagickOffsetType
+ count;
+
+ unsigned char
+ expected_mac[32],
+ received_mac[32];
+
+ /*
+ Read back the MAC from the wire and the message.
+ */
+ if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
+ return(-1);
+ count=dpc_receive(file,sizeof(received_mac),received_mac);
+ if (count != (MagickOffsetType) sizeof(received_mac))
+ return(-1);
+ count=dpc_receive(file,length,message);
+ if (count != (MagickOffsetType) length)
+ return(count);
+ if (ComputeHMACSHA256Mac(&session_key,sizeof(session_key),message,(size_t) length,expected_mac,sizeof(expected_mac)) != 0)
+ return(-1); /* HMAC computation failed */
+ if (mac_compare(received_mac,expected_mac,sizeof(expected_mac)) != 0)
+ return(-1); /* HMAC verification failed */
+ return(count);
+}
+
#endif
#if defined(MAGICKCORE_HAVE_WINSOCK2)
@@ -488,6 +566,7 @@ static int ConnectPixelCacheServer(const char *hostname,const int port,
message=DestroyString(message);
return(-1);
}
+ SetSocketNoDelay(client_socket);
/*
Receive server nonce.
*/
@@ -636,6 +715,244 @@ MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
% %
% %
% %
++ C o m p u t e H M A C S H A 2 5 6 M a c %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ComputeHMACSHA256Mac() opens a pixel cache on a remote server.
+%
+% The format of the ComputeHMACSHA256Mac method is:
+%
+% int ComputeHMACSHA256Mac(const void *key,size_t key_length,
+% const void *message,size_t message_length,unsigned char *mac,
+% size_t mac_length)
+%
+% A description of each parameter follows:
+%
+% o key: the HMAC key.
+%
+% o key_length: HMAC key length in bytes.
+%
+% o message: the message.
+%
+% o message_length: the message length in bytes.
+%
+% o mac: the mac is computed and returned in this buffer.
+%
+% o mac_length: the mac length in bytes; mac must be at least this length.
+%
+*/
+
+static void InitializeSHA256Hash(SHA256Hash *sha256_hash)
+{
+ sha256_hash->count=0;
+ sha256_hash->state[0]=0x6a09e667;
+ sha256_hash->state[1]=0xbb67ae85;
+ sha256_hash->state[2]=0x3c6ef372;
+ sha256_hash->state[3]=0xa54ff53a;
+ sha256_hash->state[4]=0x510e527f;
+ sha256_hash->state[5]=0x9b05688c;
+ sha256_hash->state[6]=0x1f83d9ab;
+ sha256_hash->state[7]=0x5be0cd19;
+}
+
+static void TransformSHA256Hash(SHA256Hash *sha256_hash,const unsigned char data[])
+{
+#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
+#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
+#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
+#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
+
+ static const uint32_t K[64] =
+ {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
+ 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
+ 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+
+ uint32_t
+ a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i,
+ j,
+ m[64];
+
+ j=0;
+ for (i=0; i < 16; i++, j+=4)
+ m[i]=((uint32_t) data[j] << 24) | ((uint32_t) data[j+1] << 16) |
+ ((uint32_t) data[j+2] << 8) | ((uint32_t) data[j+3]);
+ for ( ; i < 64; i++)
+ m[i]=SIG1(m[i-2])+m[i-7]+SIG0(m[i-15])+m[i-16];
+ a=sha256_hash->state[0];
+ b=sha256_hash->state[1];
+ c=sha256_hash->state[2];
+ d=sha256_hash->state[3];
+ e=sha256_hash->state[4];
+ f=sha256_hash->state[5];
+ g=sha256_hash->state[6];
+ h=sha256_hash->state[7];
+ for (i=0; i < 64; i++)
+ {
+ uint32_t
+ alpha = h+EP1(e)+CH(e,f,g)+K[i]+m[i],
+ beta = EP0(a)+MAJ(a,b,c);
+
+ h=g;
+ g=f;
+ f=e;
+ e=d+alpha;
+ d=c;
+ c=b;
+ b=a;
+ a=alpha+beta;
+ }
+ sha256_hash->state[0]+=a;
+ sha256_hash->state[1]+=b;
+ sha256_hash->state[2]+=c;
+ sha256_hash->state[3]+=d;
+ sha256_hash->state[4]+=e;
+ sha256_hash->state[5]+=f;
+ sha256_hash->state[6]+=g;
+ sha256_hash->state[7]+=h;
+}
+
+static void UpdateSHA256Hash(SHA256Hash *sha256_hash,const unsigned char *data,
+ const size_t data_length)
+{
+ size_t
+ i,
+ index = (size_t) ((sha256_hash->count/8) % 64);
+
+ sha256_hash->count+=(uint64_t) data_length*8;
+ for (i=0; i < data_length; i++)
+ {
+ sha256_hash->buffer[index++]=data[i];
+ if (index == 64)
+ {
+ TransformSHA256Hash(sha256_hash,sha256_hash->buffer);
+ index=0;
+ }
+ }
+}
+
+static void FinalizeSHA256Hash(SHA256Hash *sha256_hash,unsigned char hash[32])
+{
+ size_t
+ i,
+ index = (size_t) ((sha256_hash->count/8) % 64),
+ pad_length = (index < 56) ? (56-index) : (120-index);
+
+ uint64_t
+ bit_count = sha256_hash->count; /* message length before padding */
+
+ unsigned char
+ pad[64];
+
+ pad[0]=0x80;
+ (void) memset(pad+1,0,pad_length-1);
+ UpdateSHA256Hash(sha256_hash,pad,pad_length);
+ for (i=0; i < 8; i++)
+ pad[i]=(unsigned char) (bit_count >> ((7-i)*8));
+ UpdateSHA256Hash(sha256_hash, pad, 8);
+ for (i=0; i < 32; i++)
+ hash[i]=(unsigned char) (sha256_hash->state[i/4] >> ((3-(i % 4))*8));
+}
+
+static void ComputeSHA256Hash(const unsigned char *data,size_t length,
+ unsigned char hash[32])
+{
+ SHA256Hash
+ sha256_hash;
+
+ /*
+ Hash a buffer in one shot.
+ */
+ InitializeSHA256Hash(&sha256_hash);
+ UpdateSHA256Hash(&sha256_hash,data,length);
+ FinalizeSHA256Hash(&sha256_hash,hash);
+}
+
+static int ComputeHMACSHA256Mac(const void *key,size_t key_length,const void *message,
+ size_t message_length,unsigned char *mac,size_t mac_length)
+{
+ SHA256Hash
+ sha256_hash;
+
+ size_t
+ i;
+
+ unsigned char
+ full_hash[32],
+ inner_hash[32],
+ inner_pad[64],
+ k_prime[64]={0},
+ outer_pad[64];
+
+ /*
+ If key is longer than 64 bytes, hash it down first.
+ */
+ if ((mac == (unsigned char *) NULL) || (mac_length == 0))
+ return(-1);
+ if (((key == (const void *) NULL) && (key_length != 0)) ||
+ ((message == (const void *) NULL) && (message_length != 0)))
+ return(-1);
+ if (key_length > 64)
+ ComputeSHA256Hash(key,key_length,k_prime);
+ else
+ (void) memcpy(k_prime,key,key_length);
+ /*
+ Prepare inner pad and outer pad masks.
+ */
+ for (i=0; i < 64; i++)
+ {
+ inner_pad[i]=k_prime[i] ^ 0x36;
+ outer_pad[i]=k_prime[i] ^ 0x5c;
+ }
+ /*
+ Inner hash: SHA256(inner pad || message)
+ */
+ InitializeSHA256Hash(&sha256_hash);
+ UpdateSHA256Hash(&sha256_hash,inner_pad,64);
+ UpdateSHA256Hash(&sha256_hash,message,message_length);
+ FinalizeSHA256Hash(&sha256_hash,inner_hash);
+ /*
+ Outer hash: SHA256(outer pad || inner hash)
+ */
+ InitializeSHA256Hash(&sha256_hash);
+ UpdateSHA256Hash(&sha256_hash,outer_pad,64);
+ UpdateSHA256Hash(&sha256_hash,inner_hash,32);
+ FinalizeSHA256Hash(&sha256_hash,full_hash);
+ /*
+ Truncate or copy to destination buffer based on requested mac length.
+ */
+ (void) memcpy(mac,full_hash,(mac_length < 32) ? mac_length : 32);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+ D e s t r o y D i s t r i b u t e C a c h e I n f o %
% %
% %
@@ -735,6 +1052,33 @@ static inline MagickOffsetType dpc_send(SOCKET_TYPE file,
}
#endif
+static inline MagickOffsetType dpc_send_mac(SOCKET_TYPE file,
+ const MagickSizeType length,const void *magick_restrict message,
+ uint64_t session_key)
+{
+ MagickOffsetType
+ status;
+
+ unsigned char
+ mac[32];
+
+ /*
+ Compute HMAC-SHA-256 MAC of the message using the session key.
+ */
+ if (length > (MagickSizeType) MAGICK_SSIZE_MAX)
+ return(-1);
+ if (ComputeHMACSHA256Mac(&session_key,sizeof(session_key),message,
+ (size_t) length,mac,sizeof(mac)) != 0)
+ return(-1); /* HMAC computation failed */
+ /*
+ Push the MAC onto the wire then the actual message.
+ */
+ status=dpc_send(file,sizeof(mac),mac);
+ if (status != (MagickOffsetType) sizeof(mac))
+ return(-1);
+ return(dpc_send(file,length,message));
+}
+
#if !defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
MagickExport void DistributePixelCacheServer(const int magick_unused(port),
ExceptionInfo *magick_unused(exception))
@@ -909,7 +1253,7 @@ static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
if (p == (const Quantum *) NULL)
return(MagickFalse);
metacontent=(const unsigned char *) GetVirtualMetacontent(image);
- count=dpc_send(file,length,metacontent);
+ count=dpc_send_mac(file,length,metacontent,session_key);
if (count != (MagickOffsetType) length)
return(MagickFalse);
return(MagickTrue);
@@ -972,7 +1316,7 @@ static MagickBooleanType ReadDistributeCachePixels(SplayTreeInfo *registry,
exception);
if (p == (const Quantum *) NULL)
return(MagickFalse);
- count=dpc_send(file,length,p);
+ count=dpc_send_mac(file,length,p,session_key);
if (count != (MagickOffsetType) length)
return(MagickFalse);
return(MagickTrue);
@@ -1043,7 +1387,7 @@ static MagickBooleanType WriteDistributeCacheMetacontent(
if (q == (Quantum *) NULL)
return(MagickFalse);
metacontent=(unsigned char *) GetAuthenticMetacontent(image);
- count=dpc_receive(file,length,metacontent);
+ count=dpc_receive_mac(file,length,metacontent,session_key);
if (count != (MagickOffsetType) length)
return(MagickFalse);
return(SyncAuthenticPixels(image,exception));
@@ -1106,7 +1450,7 @@ static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
exception);
if (q == (Quantum *) NULL)
return(MagickFalse);
- count=dpc_receive(file,length,(unsigned char *) q);
+ count=dpc_receive_mac(file,length,(unsigned char *) q,session_key);
if (count != (MagickOffsetType) length)
return(MagickFalse);
return(SyncAuthenticPixels(image,exception));
@@ -1467,6 +1811,7 @@ MagickExport void DistributePixelCacheServer(const int port,
continue;
}
SetSocketReceiveTimeout(*client_socket_ptr,5.0);
+ SetSocketNoDelay(*client_socket_ptr);
if (status == -1)
{
CLOSE_SOCKET(*client_socket_ptr);
@@ -1780,7 +2125,7 @@ MagickPrivate MagickOffsetType ReadDistributePixelCacheMetacontent(
count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
if (count != (MagickOffsetType) (p-message))
return(-1);
- return(dpc_receive(server_info->file,length,metacontent));
+ return(dpc_receive_mac(server_info->file,length,metacontent,server_info->session_key));
}
/*
@@ -1853,7 +2198,7 @@ MagickPrivate MagickOffsetType ReadDistributePixelCachePixels(
count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
if (count != (MagickOffsetType) (p-message))
return(-1);
- return(dpc_receive(server_info->file,length,pixels));
+ return(dpc_receive_mac(server_info->file,length,pixels,server_info->session_key));
}
/*
@@ -1982,7 +2327,7 @@ MagickPrivate MagickOffsetType WriteDistributePixelCacheMetacontent(
count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
if (count != (MagickOffsetType) (p-message))
return(-1);
- return(dpc_send(server_info->file,length,metacontent));
+ return(dpc_send_mac(server_info->file,length,metacontent,server_info->session_key));
}
/*
@@ -2056,5 +2401,5 @@ MagickPrivate MagickOffsetType WriteDistributePixelCachePixels(
count=dpc_send(server_info->file,(MagickSizeType) (p-message),message);
if (count != (MagickOffsetType) (p-message))
return(-1);
- return(dpc_send(server_info->file,length,pixels));
+ return(dpc_send_mac(server_info->file,length,pixels,server_info->session_key));
}