Commit d3b02f25b for imagemagick.org
commit d3b02f25be7d2e097c4e0af90fe2535954381063
Author: Cristy <urban-warrior@imagemagick.org>
Date: Sat May 16 08:51:09 2026 -0400
https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-6gxq-f64p-5w6f
diff --git a/MagickCore/distribute-cache-private.h b/MagickCore/distribute-cache-private.h
index dc525135d..4b602a450 100644
--- a/MagickCore/distribute-cache-private.h
+++ b/MagickCore/distribute-cache-private.h
@@ -30,7 +30,7 @@ typedef struct _DistributeCacheInfo
int
file;
- size_t
+ uint64_t
session_key;
char
diff --git a/MagickCore/distribute-cache.c b/MagickCore/distribute-cache.c
index a0cc426eb..d9a35893c 100644
--- a/MagickCore/distribute-cache.c
+++ b/MagickCore/distribute-cache.c
@@ -107,7 +107,7 @@
#define DPCHostname "127.0.0.1"
#define DPCPendingConnections 10
#define DPCPort 6668
-#define DPCSessionKeyLength 8
+#define DPCSessionKeyLength 16
#ifndef MSG_NOSIGNAL
# define MSG_NOSIGNAL 0
#endif
@@ -157,8 +157,8 @@ static inline MagickOffsetType dpc_read(SOCKET_TYPE magick_unused(file),
return(-1);
}
#else
-static inline MagickOffsetType dpc_read(SOCKET_TYPE file,const MagickSizeType length,
- unsigned char *magick_restrict message)
+static inline MagickOffsetType dpc_read(SOCKET_TYPE file,
+ const MagickSizeType length,unsigned char *magick_restrict message)
{
MagickOffsetType
i;
@@ -215,9 +215,122 @@ static int ConnectPixelCacheServer(const char *magick_unused(hostname),
return(MagickFalse);
}
#else
+static inline uint64_t ROTL(uint64_t x,int b)
+{
+ return (x << b) | (x >> (64-b));
+}
+
+static inline uint64_t U8TO64_LE(const uint8_t *p)
+{
+ return(((uint64_t) p[0] << 0) | ((uint64_t) p[1] << 8) |
+ ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24) |
+ ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) |
+ ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56));
+}
+
+static inline uint64_t SIPHash24(const uint8_t key[16],const uint8_t *message,
+ const size_t length)
+{
+ const uint8_t
+ *end = message+length-(length % 8);
+
+ size_t
+ i;
+
+ uint64_t
+ b = ((uint64_t) length) << 56,
+ k0 = U8TO64_LE(key),
+ k1 = U8TO64_LE(key+8),
+ m,
+ v0 = 0x736f6d6570736575ULL^k0,
+ v1 = 0x646f72616e646f6dULL^k1,
+ v2 = 0x6c7967656e657261ULL^k0,
+ v3 = 0x7465646279746573ULL^k1;
+
+ for ( ; message != end; message+=8)
+ {
+ m=U8TO64_LE(message);
+ v3^=m;
+ for (i=0; i < 2; i++)
+ {
+ v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
+ v2+=v3; v3=ROTL(v3,16); v3^=v2;
+ v0+=v3; v3=ROTL(v3,21); v3^=v0;
+ v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
+ }
+ v0^=m;
+ }
+ switch (length & 0x07)
+ {
+ case 7: b|=((uint64_t) message[6]) << 48; magick_fallthrough;
+ case 6: b|=((uint64_t) message[5]) << 40; magick_fallthrough;
+ case 5: b|=((uint64_t) message[4]) << 32; magick_fallthrough;
+ case 4: b|=((uint64_t) message[3]) << 24; magick_fallthrough;
+ case 3: b|=((uint64_t) message[2]) << 16; magick_fallthrough;
+ case 2: b|=((uint64_t) message[1]) << 8; magick_fallthrough;
+ case 1: b|=((uint64_t) message[0]); magick_fallthrough;
+ default: break;
+ }
+ v3^=b;
+ for (i=0; i < 2; i++)
+ {
+ v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
+ v2+=v3; v3=ROTL(v3,16); v3^=v2;
+ v0+=v3; v3=ROTL(v3,21); v3^=v0;
+ v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
+ }
+ v0^=b;
+ v2^=0xff;
+ for (i=0; i < 4; i++)
+ {
+ v0+=v1; v1=ROTL(v1,13); v1^=v0; v0=ROTL(v0,32);
+ v2+=v3; v3=ROTL(v3,16); v3^=v2;
+ v0+=v3; v3=ROTL(v3,21); v3^=v0;
+ v2+=v1; v1=ROTL(v1,17); v1^=v2; v2=ROTL(v2,32);
+ }
+ return(v0^v1^v2^v3);
+}
+
+static inline void DeriveSipKeyFromSecret(const char *shared_secret,
+ uint8_t key[16])
+{
+ size_t
+ i,
+ length;
+
+ uint64_t
+ k0 = 0x0706050403020100ULL,
+ k1 = 0x0f0e0d0c0b0a0908ULL;
+
+ length=strlen(shared_secret);
+ for (i=0; i < length; i++)
+ {
+ uint8_t
+ b = shared_secret[i];
+
+ k0^=b;
+ k0*=0x100000001b3ULL;
+ k1^=(uint64_t) b << ((i & 7)*8);
+ k1=(k1 << 5) | (k1 >> (64-5));
+ }
+ (void) memcpy(key,&k0,8);
+ (void) memcpy(key+8,&k1,8);
+}
+
+static inline uint64_t GenerateSessionKey(const char *shared_secret,
+ const unsigned char *nonce,size_t length)
+{
+ uint8_t
+ key[16];
+
+ DeriveSipKeyFromSecret(shared_secret,key);
+ return(SIPHash24(key,nonce,length));
+}
+
static int ConnectPixelCacheServer(const char *hostname,const int port,
- size_t *session_key,ExceptionInfo *exception)
+ uint64_t *session_key,ExceptionInfo *exception)
{
+#if defined(MAGICKCORE_HAVE_DISTRIBUTE_CACHE)
char
service[MagickPathExtent],
*shared_secret;
@@ -228,29 +341,29 @@ static int ConnectPixelCacheServer(const char *hostname,const int port,
SOCKET_TYPE
client_socket;
- StringInfo
- *nonce;
-
ssize_t
count;
struct addrinfo
- hint,
+ hints,
*result;
+ unsigned char
+ nonce[DPCSessionKeyLength];
+
/*
- Connect to distributed pixel cache and get session key.
+ Connect to distributed pixel cache server and get session key.
*/
*session_key=0;
-#if defined(MAGICKCORE_HAVE_WINSOCK2)
- InitializeWinsock2(MagickTrue);
+#if defined(MAGICKCORE_WINDOWS_SUPPORT)
+ NTInitializeWinsock(MagickTrue);
#endif
- (void) memset(&hint,0,sizeof(hint));
- hint.ai_family=AF_INET;
- hint.ai_socktype=SOCK_STREAM;
- hint.ai_flags=AI_PASSIVE;
+ (void) memset(&hints,0,sizeof(hints));
+ hints.ai_family=AF_INET;
+ hints.ai_socktype=SOCK_STREAM;
+ hints.ai_flags=AI_PASSIVE;
(void) FormatLocaleString(service,MagickPathExtent,"%d",port);
- status=getaddrinfo(hostname,service,&hint,&result);
+ status=getaddrinfo(hostname,service,&hints,&result);
if (status != 0)
{
(void) ThrowMagickException(exception,GetMagickModule(),CacheError,
@@ -275,8 +388,11 @@ static int ConnectPixelCacheServer(const char *hostname,const int port,
"DistributedPixelCache","'%s': %s",hostname,GetExceptionMessage(errno));
return(-1);
}
- count=recv(client_socket,(char *) session_key,sizeof(*session_key),0);
- if (count == -1)
+ /*
+ Receive server nonce.
+ */
+ count=recv(client_socket,(char *) nonce,sizeof(nonce),0);
+ if (count != (ssize_t) sizeof(nonce))
{
CLOSE_SOCKET(client_socket);
(void) ThrowMagickException(exception,GetMagickModule(),CacheError,
@@ -284,27 +400,36 @@ static int ConnectPixelCacheServer(const char *hostname,const int port,
return(-1);
}
/*
- Authenticate client session key to server session key.
+ Compute HMAC(shared_secret,nonce).
*/
shared_secret=GetPolicyValue("cache:shared-secret");
- if (shared_secret == (char *) NULL)
+ if (shared_secret == (char*) NULL)
{
CLOSE_SOCKET(client_socket);
(void) ThrowMagickException(exception,GetMagickModule(),CacheError,
"DistributedPixelCache","'%s': shared secret required",hostname);
return(-1);
}
- nonce=StringToStringInfo(shared_secret);
- if ((size_t) GetMagickSignature(nonce) != *session_key)
+ *session_key=GenerateSessionKey(shared_secret,nonce,sizeof(nonce));
+ shared_secret=DestroyString(shared_secret);
+ /*
+ Send HMAC back to client.
+ */
+ count=send(client_socket,(char *) session_key,sizeof(*session_key),
+ MSG_NOSIGNAL);
+ if (count != (ssize_t) sizeof(*session_key))
{
CLOSE_SOCKET(client_socket);
(void) ThrowMagickException(exception,GetMagickModule(),CacheError,
- "DistributedPixelCache","'%s' authentication failed",hostname);
+ "DistributedPixelCache","'%s': authentication failed",hostname);
return(-1);
}
- shared_secret=DestroyString(shared_secret);
- nonce=DestroyStringInfo(nonce);
- return((int) client_socket);
+ return(client_socket);
+#else
+ (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
+ "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
+ return(-1);
+#endif
}
#endif
@@ -372,7 +497,7 @@ MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
DistributeCacheInfo
*server_info;
- size_t
+ uint64_t
session_key;
/*
@@ -475,8 +600,8 @@ static inline MagickOffsetType dpc_send(SOCKET_TYPE magick_unused(file),
return(-1);
}
#else
-static inline MagickOffsetType dpc_send(SOCKET_TYPE file,const MagickSizeType length,
- const void *magick_restrict message)
+static inline MagickOffsetType dpc_send(SOCKET_TYPE file,
+ const MagickSizeType length,const void *magick_restrict message)
{
MagickOffsetType
i;
@@ -490,8 +615,8 @@ static inline MagickOffsetType dpc_send(SOCKET_TYPE file,const MagickSizeType le
count=0;
for (i=0; i < (MagickOffsetType) length; i+=count)
{
- count=(ssize_t) send(file,(char *) message+i,(LENGTH_TYPE)
- MagickMin(length-(MagickSizeType) i,(MagickSizeType) MagickMaxBufferExtent),
+ count=(ssize_t) send(file,(char *) message+i,(LENGTH_TYPE) MagickMin(
+ length-(MagickSizeType) i,(MagickSizeType) MagickMaxBufferExtent),
MSG_NOSIGNAL);
if (count <= 0)
{
@@ -514,7 +639,7 @@ MagickExport void DistributePixelCacheServer(const int magick_unused(port),
}
#else
static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *registry,
- const size_t session_key)
+ const uint64_t session_key)
{
MagickAddressType
key = (MagickAddressType) session_key;
@@ -526,7 +651,7 @@ static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *registry,
}
static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,
- SOCKET_TYPE file,const size_t session_key,ExceptionInfo *exception)
+ SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
{
Image
*image;
@@ -590,7 +715,7 @@ static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,
}
static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
- SOCKET_TYPE file,const size_t session_key,ExceptionInfo *exception)
+ SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
{
const Quantum
*p;
@@ -730,7 +855,7 @@ static inline MagickBooleanType ValidateDistributedPixelCache(
}
static MagickBooleanType WriteDistributeCacheMetacontent(
- SplayTreeInfo *registry,SOCKET_TYPE file,const size_t session_key,
+ SplayTreeInfo *registry,SOCKET_TYPE file,const uint64_t session_key,
ExceptionInfo *exception)
{
Image
@@ -797,7 +922,7 @@ static MagickBooleanType WriteDistributeCacheMetacontent(
}
static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
- SOCKET_TYPE file,const size_t session_key,ExceptionInfo *exception)
+ SOCKET_TYPE file,const uint64_t session_key,ExceptionInfo *exception)
{
Image
*image;
@@ -866,16 +991,15 @@ static HANDLER_RETURN_TYPE DistributePixelCacheClient(void *socket_arg)
ExceptionInfo
*exception;
-
+
MagickBooleanType
status = MagickFalse;
MagickOffsetType
count;
- size_t
- key,
- session_key;
+ RandomInfo
+ *random_info;
SOCKET_TYPE
client_socket,
@@ -885,32 +1009,66 @@ static HANDLER_RETURN_TYPE DistributePixelCacheClient(void *socket_arg)
*registry;
StringInfo
- *nonce;
+ *entropy;
+
+ uint64_t
+ key,
+ session_key;
unsigned char
- command;
+ command,
+ nonce[DPCSessionKeyLength];
/*
- Generate session key.
+ Load shared secret.
*/
client_socket=(*client_socket_ptr);
client_socket_ptr=(SOCKET_TYPE *) RelinquishMagickMemory(client_socket_ptr);
- shared_secret = GetPolicyValue("cache:shared-secret");
- if (shared_secret == (char *) NULL)
+ shared_secret=GetPolicyValue("cache:shared-secret");
+ if (shared_secret == NULL)
ThrowFatalException(CacheFatalError,"shared secret required");
- nonce=StringToStringInfo(shared_secret);
+ /*
+ Generate random nonce.
+ */
+ random_info=AcquireRandomInfo();
+ entropy=GetRandomKey(random_info,sizeof(nonce));
+ (void) memcpy(nonce,GetStringInfoDatum(entropy),sizeof(nonce));
+ entropy=DestroyStringInfo(entropy);
+ random_info=DestroyRandomInfo(random_info);
+ /*
+ Derive session key.
+ */
+ session_key=GenerateSessionKey(shared_secret,nonce,sizeof(nonce));
shared_secret=DestroyString(shared_secret);
- session_key=GetMagickSignature(nonce);
- nonce=DestroyStringInfo(nonce);
- exception=AcquireExceptionInfo();
/*
- Process client commands.
+ Send nonce to client.
+ */
+ count=dpc_send(client_socket,sizeof(nonce),nonce);
+ if (count != (MagickOffsetType) sizeof(nonce))
+ {
+ CLOSE_SOCKET(client_socket);
+ return(HANDLER_RETURN_VALUE);
+ }
+ /*
+ Receive client's HMAC.
*/
+ count=dpc_read(client_socket,sizeof(key),(unsigned char *) &key);
+ if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
+ {
+ CLOSE_SOCKET(client_socket);
+ return(HANDLER_RETURN_VALUE);
+ }
+ exception=AcquireExceptionInfo();
registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
(void *(*)(void *)) NULL,RelinquishImageRegistry);
- count=dpc_send(client_socket,sizeof(session_key),&session_key);
+ /*
+ Command loop.
+ */
for (status=MagickFalse; ; )
{
+ /*
+ Each command must echo the authenticated session key.
+ */
count=dpc_read(client_socket,1,(unsigned char *) &command);
if (count <= 0)
break;
@@ -923,7 +1081,7 @@ static HANDLER_RETURN_TYPE DistributePixelCacheClient(void *socket_arg)
{
status=OpenDistributeCache(registry,client_socket,session_key,
exception);
- count=dpc_send(client_socket,sizeof(status),&status);
+ dpc_send(client_socket,sizeof(status),&status);
break;
}
case 'r':
@@ -1012,7 +1170,7 @@ MagickExport void DistributePixelCacheServer(const int port,
#if defined(MAGICKCORE_HAVE_WINSOCK2)
InitializeWinsock2(MagickFalse);
#endif
- memset(&hint,0,sizeof(hint));
+ (void) memset(&hint,0,sizeof(hint));
hint.ai_family=AF_INET;
hint.ai_socktype=SOCK_STREAM;
hint.ai_flags=AI_PASSIVE;
@@ -1057,7 +1215,7 @@ MagickExport void DistributePixelCacheServer(const int port,
for ( ; ; )
{
SOCKET_TYPE
- *client_socket_ptr;
+ *client_socket_ptr;
socklen_t
length = (socklen_t) sizeof(address);
@@ -1074,7 +1232,7 @@ MagickExport void DistributePixelCacheServer(const int port,
continue;
}
#if defined(MAGICKCORE_THREAD_SUPPORT)
- status=pthread_create(&thread_id, &attributes,DistributePixelCacheClient,
+ status=pthread_create(&thread_id,&attributes,DistributePixelCacheClient,
(void *) client_socket_ptr);
if (status != 0)
{