From b75b9a573df51f262d0c0e1f5b2aeaadba4d67d1 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt 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 --- 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;