s3:loadparm: Ensure to truncate FS Volume Label at multibyte boundary
authorShyamsunder Rathi <shyam.rathi@nutanix.com>
Mon, 13 May 2019 06:06:17 +0000 (23:06 -0700)
committerAndreas Schneider <asn@cryptomilk.org>
Thu, 16 May 2019 17:55:18 +0000 (17:55 +0000)
For FS_VOLUME_INFO/FS_INFO operation, a maximum of 32 characters are
sent back. However, since Samba chops off any share name with >32
bytes at 32, it is possible that a multi-byte share name can get chopped
off between a full character. This causes the string decoding for unicode
failure which sends back NT_STATUS_ILLEGAL_CHARACTER (EILSEQ) to the client
applications.

On Windows, Notepad doesn't like it, and refuses to open a file in this
case and fails with the following error:

  Invalid character. For multibyte character sets, only the leading byte is
  included without the trailing byte. For Unicode character sets, include
  the characters 0xFFFF and 0xFFFE.

Proposed fix:
- Find the last starting point of a multibyte codepoint if the character
  at 32nd byte is a subsequent byte of a MB codepoint.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13947

Signed-off-by: Shyamsunder Rathi <shyam.rathi@nutanix.com>
Reviewed-by: Hemanth Thummala <hemanth.thummala@nutanix.com>
Reviewed-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
source3/param/loadparm.c

index 5af1621..5104a34 100644 (file)
@@ -4244,15 +4244,47 @@ const char *volume_label(TALLOC_CTX *ctx, int snum)
 {
        char *ret;
        const char *label = lp_volume(ctx, snum);
+       size_t end = 32;
+
        if (!*label) {
                label = lp_servicename(ctx, snum);
        }
 
-       /* This returns a 33 byte guarenteed null terminated string. */
-       ret = talloc_strndup(ctx, label, 32);
+       /*
+        * Volume label can be a max of 32 bytes. Make sure to truncate
+        * it at a codepoint boundary if it's longer than 32 and contains
+        * multibyte characters. Windows insists on a volume label being
+        * a valid mb sequence, and errors out if not.
+        */
+       if (strlen(label) > 32) {
+               /*
+                * A MB char can be a max of 5 bytes, thus
+                * we should have a valid mb character at a
+                * minimum position of (32-5) = 27.
+                */
+               while (end >= 27) {
+                       /*
+                        * Check if a codepoint starting from next byte
+                        * is valid. If yes, then the current byte is the
+                        * end of a MB or ascii sequence and the label can
+                        * be safely truncated here. If not, keep going
+                        * backwards till a valid codepoint is found.
+                        */
+                       size_t len = 0;
+                       const char *s = &label[end];
+                       codepoint_t c = next_codepoint(s, &len);
+                       if (c != INVALID_CODEPOINT) {
+                               break;
+                       }
+                       end--;
+               }
+       }
+
+       /* This returns a max of 33 byte guarenteed null terminated string. */
+       ret = talloc_strndup(ctx, label, end);
        if (!ret) {
                return "";
-       }               
+       }
        return ret;
 }