</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>
* 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) {
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;
bool convert_adouble;
bool wipe_intentionally_left_blank_rfork;
bool delete_empty_adfiles;
+ bool validate_afpinfo;
/*
* Additional options, all enabled by default,
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);
}
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;
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;
}
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;
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;
}
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) {