[nginx] Mail: xtext encoding (RFC 3461) in XCLIENT LOGIN.

noreply at nginx.com noreply at nginx.com
Fri Sep 26 13:05:03 UTC 2025


details:   https://github.com/nginx/nginx/commit/6f81314a070201afc4e25b975b1f915698cff634
branches:  master
commit:    6f81314a070201afc4e25b975b1f915698cff634
user:      Sergey Kandaurov <pluknet at nginx.com>
date:      Thu, 11 Sep 2025 18:23:10 +0400
description:
Mail: xtext encoding (RFC 3461) in XCLIENT LOGIN.

The XCLIENT command uses xtext encoding for attribute values,
as specified in https://www.postfix.org/XCLIENT_README.html.

Reported by Igor Morgenstern of Aisle Research.

---
 src/core/ngx_string.c            | 32 +++++++++++++++++++++++++++++---
 src/core/ngx_string.h            |  1 +
 src/mail/ngx_mail_proxy_module.c | 14 +++++++++++++-
 3 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
index f8f738472..10fe764c3 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -1494,8 +1494,9 @@ ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
 uintptr_t
 ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
 {
-    ngx_uint_t      n;
+    u_char          prefix;
     uint32_t       *escape;
+    ngx_uint_t      n;
     static u_char   hex[] = "0123456789ABCDEF";
 
     /*
@@ -1633,11 +1634,36 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
 
                     /* mail_auth is the same as memcached */
 
+                    /* " ", "+", "=", not allowed */
+
+    static uint32_t   mail_xtext[] = {
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+
+                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
+        0x20000801, /* 0010 0000 0000 0000  0000 1000 0000 0001 */
+
+                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+
+                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
+        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
+
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+    };
+
     static uint32_t  *map[] =
-        { uri, args, uri_component, html, refresh, memcached, memcached };
+        { uri, args, uri_component, html, refresh, memcached, memcached,
+          mail_xtext };
+
+    static u_char  map_char[] =
+        { '%', '%', '%', '%', '%', '%', '%', '+' };
 
 
     escape = map[type];
+    prefix = map_char[type];
 
     if (dst == NULL) {
 
@@ -1658,7 +1684,7 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
 
     while (size) {
         if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
-            *dst++ = '%';
+            *dst++ = prefix;
             *dst++ = hex[*src >> 4];
             *dst++ = hex[*src & 0xf];
             src++;
diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h
index 713eb42a7..183a20521 100644
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -203,6 +203,7 @@ u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
 #define NGX_ESCAPE_REFRESH        4
 #define NGX_ESCAPE_MEMCACHED      5
 #define NGX_ESCAPE_MAIL_AUTH      6
+#define NGX_ESCAPE_MAIL_XTEXT     7
 
 #define NGX_UNESCAPE_URI       1
 #define NGX_UNESCAPE_REDIRECT  2
diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c
index 1c6d0372e..84a7f61a4 100644
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -531,6 +531,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
     ngx_int_t                  rc;
     ngx_str_t                  line, auth, encoded;
     ngx_buf_t                 *b;
+    uintptr_t                  n;
     ngx_connection_t          *c;
     ngx_mail_session_t        *s;
     ngx_mail_proxy_conf_t     *pcf;
@@ -627,6 +628,10 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
                           CRLF) - 1
                    + s->connection->addr_text.len + s->login.len + s->host.len;
 
+        n = ngx_escape_uri(NULL, s->login.data, s->login.len,
+                           NGX_ESCAPE_MAIL_XTEXT);
+        line.len += n * 2;
+
 #if (NGX_HAVE_INET6)
         if (s->connection->sockaddr->sa_family == AF_INET6) {
             line.len += sizeof("IPV6:") - 1;
@@ -654,7 +659,14 @@ ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
 
         if (s->login.len && !pcf->smtp_auth) {
             p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
-            p = ngx_copy(p, s->login.data, s->login.len);
+
+            if (n == 0) {
+                p = ngx_copy(p, s->login.data, s->login.len);
+
+            } else {
+                p = (u_char *) ngx_escape_uri(p, s->login.data, s->login.len,
+                                              NGX_ESCAPE_MAIL_XTEXT);
+            }
         }
 
         p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);


More information about the nginx-devel mailing list