From b75b9a573df51f262d0c0e1f5b2aeaadba4d67d1 Mon Sep 17 00:00:00 2001
From: Daniel Markstedt <daniel@mindani.net>
Date: Mon, 4 May 2026 20:47:21 +0200
Subject: [PATCH] CVE-2026-44049: libatalk: reserve charset terminator space in
 conversion

Reported-by: @00redbeer
Signed-off-by: Daniel Markstedt <daniel@mindani.net>
---
 etc/afpd/desktop.c         | 11 +++++------
 etc/afpd/mangle.c          | 13 +++++++------
 libatalk/unicode/charcnv.c | 12 ++++++++++--
 3 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/etc/afpd/desktop.c b/etc/afpd/desktop.c
index ee4516f2b..33986c170 100644
--- a/etc/afpd/desktop.c
+++ b/etc/afpd/desktop.c
@@ -848,7 +848,6 @@ char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
     static char  upath[MAXPATHLEN + 2];
     char	*m, *u;
     size_t       inplen;
-    size_t       outlen;
     uint16_t	 flags;
 
     if (*mpath == '\0') {
@@ -867,11 +866,11 @@ char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
     m = mpath;
     u = upath;
     inplen = strlen(m);
-    outlen = MAXPATHLEN;
 
-    if ((size_t) -1 == (outlen = convert_charset(utf8 ? CH_UTF8_MAC :
-                                 vol->v_maccharset, vol->v_volcharset, vol->v_maccharset, m, inplen, u, outlen,
-                                 &flags))) {
+    if ((size_t) -1 == convert_charset(utf8 ? CH_UTF8_MAC :
+                                       vol->v_maccharset, vol->v_volcharset, vol->v_maccharset, m, inplen, u,
+                                       sizeof(upath),
+                                       &flags)) {
         LOG(log_error, logtype_afpd, "conversion from %s to %s for %s failed.",
             (utf8) ? "UTF8-MAC" : vol->v_maccodepage, vol->v_volcodepage, mpath);
         return NULL;
@@ -899,7 +898,7 @@ char *utompath(const struct vol *vol, char *upath, cnid_t id, int utf8)
     /* convert charsets */
     if ((size_t) -1 == (outlen = convert_charset(vol->v_volcharset,
                                  utf8 ? CH_UTF8_MAC : vol->v_maccharset, vol->v_maccharset, u, outlen, mpath,
-                                 MAXPATHLEN, &flags))) {
+                                 sizeof(mpath), &flags))) {
         LOG(log_error, logtype_afpd, "Conversion from %s to %s for %s (%u) failed.",
             vol->v_volcodepage, vol->v_maccodepage, u, ntohl(id));
         goto utompath_error;
diff --git a/etc/afpd/mangle.c b/etc/afpd/mangle.c
index d34a95621..72e6a289c 100644
--- a/etc/afpd/mangle.c
+++ b/etc/afpd/mangle.c
@@ -36,7 +36,7 @@ static size_t mangle_extension(const struct vol *vol, const char *uname,
         uint16_t flags = CONV_FORCE | CONV_UNESCAPEHEX;
         size_t len = convert_charset(vol->v_volcharset, charset,
                                      vol->v_maccharset, p, strlen(p),
-                                     extension, MAX_EXT_LENGTH, &flags);
+                                     extension, MAX_EXT_LENGTH + 2, &flags);
 
         if (len != (size_t) -1) {
             return len;
@@ -83,7 +83,7 @@ static char *demangle_checks(const struct vol *vol, char *uname,
     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
 
     if ((size_t) -1 == (len = convert_charset(vol->v_volcharset, vol->v_maccharset,
-                              0, uname, strlen(uname), buffer, MAXPATHLEN, &flags))) {
+                              0, uname, strlen(uname), buffer, sizeof(buffer), &flags))) {
         return mfilename;
     }
 
@@ -106,7 +106,7 @@ static char *demangle_checks(const struct vol *vol, char *uname,
         if (len) {
             /* convert the buffer to UTF8_MAC ... */
             if ((size_t) -1 == (len = convert_charset(vol->v_maccharset, CH_UTF8_MAC, 0,
-                                      buffer, len, buffer, MAXPATHLEN, &flags))) {
+                                      buffer, len, buffer, sizeof(buffer), &flags))) {
                 return mfilename;
             }
 
@@ -130,7 +130,7 @@ static char *demangle_checks(const struct vol *vol, char *uname,
             /* characters have to match ... again a possible race FIXME			  */
 
             if ((size_t) -1 == (len = convert_charset(vol->v_volcharset, CH_UTF8_MAC, 0,
-                                      uname, strlen(uname), buffer, MAXPATHLEN, &flags))) {
+                                      uname, strlen(uname), buffer, sizeof(buffer), &flags))) {
                 return mfilename;
             }
 
@@ -267,7 +267,7 @@ mangle(const struct vol *vol, char *filename, size_t filenamelen, char *uname,
 {
     char *m = NULL;
     /* way > maxlen */
-    static char mfilename[MAXPATHLEN];
+    static char mfilename[MAXPATHLEN + 2];
     char mangle_suffix[MANGLE_LENGTH + 1];
     /* for convert_charset dest_len parameter +2 */
     char ext[MAX_EXT_LENGTH + 2];
@@ -295,10 +295,11 @@ mangle(const struct vol *vol, char *filename, size_t filenamelen, char *uname,
 
     if (filenamelen + k + ext_len > maxlen) {
         uint16_t opt = CONV_FORCE | CONV_UNESCAPEHEX;
+        size_t prefix_len = maxlen - k - ext_len;
         size_t n = convert_charset(vol->v_volcharset,
                                    (flags & 2) ? CH_UTF8_MAC : vol->v_maccharset,
                                    vol->v_maccharset, uname, strlen(uname),
-                                   m, maxlen - k - ext_len, &opt);
+                                   m, prefix_len + 2, &opt);
         m[n != (size_t) -1 ? n : 0] = 0;
     } else {
         strlcpy(m, filename, filenamelen + 1);
diff --git a/libatalk/unicode/charcnv.c b/libatalk/unicode/charcnv.c
index e6d0cd410..791ae3b2b 100644
--- a/libatalk/unicode/charcnv.c
+++ b/libatalk/unicode/charcnv.c
@@ -956,13 +956,14 @@ static size_t push_charset_flags(charset_t to_set, charset_t cap_set, char *src,
 
 /*!
  * @bug the size is a mess we really need a malloc/free logic
- * @note dest size must be dest_len +2
+ * @note dest_len must include space for the two-byte terminator
  */
 size_t convert_charset(charset_t from_set, charset_t to_set,
                        charset_t cap_charset, const char *src, size_t src_len, char *dest,
                        size_t dest_len, uint16_t *flags)
 {
     size_t i_len, o_len;
+    size_t dest_capacity;
     ucs2_t *u;
     ucs2_t buffer[MAXPATHLEN + 2];
     ucs2_t buffer2[MAXPATHLEN + 2];
@@ -1021,9 +1022,16 @@ size_t convert_charset(charset_t from_set, charset_t to_set,
         strlower_w(u);
     }
 
+    if (dest_len < 2) {
+        errno = E2BIG;
+        return (size_t) -1;
+    }
+
+    dest_capacity = dest_len - 2;
+
     /* Convert UCS2 to to_set */
     if ((size_t)(-1) == (o_len = push_charset_flags(to_set, cap_charset, (char *)u,
-                                 i_len, dest, dest_len, flags))) {
+                                 i_len, dest, dest_capacity, flags))) {
         LOG(log_error, logtype_default,
             "Conversion failed (CH_UCS2 to %s):%s", charset_name(to_set), strerror(errno));
         return (size_t) -1;
