vfs_fruit: add option fruit:validate_afpinfo = yes|no (default: yes)
authorRalph Boehme <slow@samba.org>
Tue, 4 Jul 2023 15:46:40 +0000 (17:46 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 24 Oct 2023 21:31:38 +0000 (21:31 +0000)
Allows disabling validation of AfpInfo stream data. It seems in data migration
scenarios from other SMB servers to Samba with fruit, somehow such invalid
streams are present on the source SMB server and can't be copied to Samba.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
docs-xml/manpages/vfs_fruit.8.xml
source3/lib/adouble.c
source3/lib/adouble.h
source3/modules/vfs_fruit.c

index 7ae17c65976324d5e9e772bdfc6f602b952bd290..61051f90873bcbaafc50520366cdbf66984774ca 100644 (file)
            </listitem>
          </varlistentry>
 
+         <varlistentry>
+           <term>fruit:validate_afpinfo = yes | no</term>
+           <listitem>
+             <para>Apple clients use the AFP_AfpInfo stream to store structured
+             file metadata. As part of the marshaled data stored in the stream
+             the first eight bytes contain some header information. Apple's SMB
+             server as well as Samba will validate this header bytes processing
+             a client write request on this stream, and, if the validation
+             fails, fail the write. While this validation is generally correct,
+             in some data migration scenarios clients may try to migrate data
+             from 3rd-party SMB servers to Samba servers where the header
+             information is broken for whatever reason. To allow migration and
+             header fix-up in these scenarios, the validation can be temporarily
+             disabled by setting this option to <emphasis>no</emphasis>.</para>
+             <para>The default is <emphasis>yes</emphasis>.</para>
+           </listitem>
+         </varlistentry>
+
        </variablelist>
 </refsect1>
 
index f0fb3c3be27b376c75f28db28733824cb5891f64..ad1e5059cf6e2b39d649191c4769f8777c882e87 100644 (file)
@@ -2827,7 +2827,7 @@ ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
  * Buffer size must be at least AFP_INFO_SIZE
  * Returns allocated AfpInfo struct
  **/
-AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
+AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data, bool validate)
 {
        AfpInfo *ai = talloc_zero(ctx, AfpInfo);
        if (ai == NULL) {
@@ -2840,10 +2840,16 @@ AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
        memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
               sizeof(ai->afpi_FinderInfo));
 
-       if (ai->afpi_Signature != AFP_Signature
-           || ai->afpi_Version != AFP_Version) {
-               DEBUG(1, ("Bad AfpInfo signature or version\n"));
-               TALLOC_FREE(ai);
+       if (validate) {
+               if (ai->afpi_Signature != AFP_Signature
+                   || ai->afpi_Version != AFP_Version)
+               {
+                       DEBUG(1, ("Bad AfpInfo signature or version\n"));
+                       TALLOC_FREE(ai);
+               }
+       } else {
+               ai->afpi_Signature = AFP_Signature;
+               ai->afpi_Version = AFP_Version;
        }
 
        return ai;
index de44f3f5fdc7cce56812393b639b2ab709e628b7..d2c729305f9c28b4a600352db6433c502635aa73 100644 (file)
@@ -187,6 +187,6 @@ int adouble_path(TALLOC_CTX *ctx,
 
 AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
 ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
-AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
+AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data, bool validate);
 
 #endif
index 60bfecd8d35e2741309bd7b99f31c6dd1532122a..b510b04aea68c8d7be36a0de2085e3b465508355 100644 (file)
@@ -134,6 +134,7 @@ struct fruit_config_data {
        bool convert_adouble;
        bool wipe_intentionally_left_blank_rfork;
        bool delete_empty_adfiles;
+       bool validate_afpinfo;
 
        /*
         * Additional options, all enabled by default,
@@ -377,6 +378,10 @@ static int init_fruit_config(vfs_handle_struct *handle)
                SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
                "delete_empty_adfiles", false);
 
+       config->validate_afpinfo = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+               "validate_afpinfo", true);
+
        SMB_VFS_HANDLE_SET_DATA(handle, config,
                                NULL, struct fruit_config_data,
                                return -1);
@@ -2637,10 +2642,12 @@ static ssize_t fruit_pread_recv(struct tevent_req *req,
 }
 
 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
-                                       files_struct *fsp, const void *data,
+                                       files_struct *fsp, const void *indata,
                                        size_t n, off_t offset)
 {
        struct fio *fio = fruit_get_complete_fio(handle, fsp);
+       const void *data = indata;
+       char afpinfo_buf[AFP_INFO_SIZE];
        AfpInfo *ai = NULL;
        size_t nwritten;
        int ret;
@@ -2681,7 +2688,7 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
                fio->fake_fd = false;
        }
 
-       ai = afpinfo_unpack(talloc_tos(), data);
+       ai = afpinfo_unpack(talloc_tos(), data, fio->config->validate_afpinfo);
        if (ai == NULL) {
                return -1;
        }
@@ -2713,6 +2720,21 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
                return n;
        }
 
+       if (!fio->config->validate_afpinfo) {
+               /*
+                * Ensure the buffer contains a valid header, so marshall
+                * the data from the afpinfo struck back into a buffer
+                * and write that instead of the possibly malformed data
+                * we got from the client.
+                */
+               nwritten = afpinfo_pack(ai, afpinfo_buf);
+               if (nwritten != AFP_INFO_SIZE) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               data = afpinfo_buf;
+       }
+
        nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
        if (nwritten != n) {
                return -1;
@@ -2725,13 +2747,17 @@ static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
                                          files_struct *fsp, const void *data,
                                          size_t n, off_t offset)
 {
+       struct fruit_config_data *config = NULL;
        struct adouble *ad = NULL;
        AfpInfo *ai = NULL;
        char *p = NULL;
        int ret;
        bool ok;
 
-       ai = afpinfo_unpack(talloc_tos(), data);
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       ai = afpinfo_unpack(talloc_tos(), data, config->validate_afpinfo);
        if (ai == NULL) {
                return -1;
        }
@@ -2811,10 +2837,12 @@ static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
                return -1;
        }
 
-       cmp = memcmp(data, "AFP", 3);
-       if (cmp != 0) {
-               errno = EINVAL;
-               return -1;
+       if (fio->config->validate_afpinfo) {
+               cmp = memcmp(data, "AFP", 3);
+               if (cmp != 0) {
+                       errno = EINVAL;
+                       return -1;
+               }
        }
 
        if (n <= AFP_OFF_FinderInfo) {