Changeset View
Changeset View
Standalone View
Standalone View
files/patch-vfs_freebsd
Property | Old Value | New Value |
---|---|---|
fbsd:nokeywords | null | yes \ No newline at end of property |
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
--- docs-xml/wscript_build.orig 2019-06-25 00:52:38 UTC | |||||
+++ docs-xml/wscript_build | |||||
@@ -79,6 +79,7 @@ vfs_module_manpages = ['vfs_acl_tdb', | |||||
'vfs_extd_audit', | |||||
'vfs_fake_perms', | |||||
'vfs_fileid', | |||||
+ 'vfs_freebsd', | |||||
'vfs_fruit', | |||||
'vfs_full_audit', | |||||
'vfs_glusterfs', | |||||
--- source3/modules/wscript_build.orig 2019-05-07 08:38:21 UTC | |||||
+++ source3/modules/wscript_build | |||||
@@ -613,3 +613,11 @@ bld.SAMBA3_MODULE('vfs_delay_inject', | |||||
init_function='', | |||||
internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_delay_inject'), | |||||
enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_delay_inject')) | |||||
+ | |||||
+bld.SAMBA3_MODULE('vfs_freebsd', | |||||
+ subsystem='vfs', | |||||
+ source='vfs_freebsd.c', | |||||
+ deps='samba-util', | |||||
+ init_function='', | |||||
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_freebsd'), | |||||
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_freebsd')) | |||||
bld.SAMBA_GENERATOR('nfs41acl-xdr-c', | |||||
--- source3/modules/vfs_freebsd.c.orig 2019-06-22 11:56:57 UTC | |||||
+++ source3/modules/vfs_freebsd.c | |||||
@@ -0,0 +1,800 @@ | |||||
+/* | |||||
+ * This module implements VFS calls specific to FreeBSD | |||||
+ * | |||||
+ * Copyright (C) Timur I. Bakeyev, 2018 | |||||
+ * | |||||
+ * This program is free software; you can redistribute it and/or modify | |||||
+ * it under the terms of the GNU General Public License as published by | |||||
+ * the Free Software Foundation; either version 3 of the License, or | |||||
+ * (at your option) any later version. | |||||
+ * | |||||
+ * This program is distributed in the hope that it will be useful, | |||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
+ * GNU General Public License for more details. | |||||
+ * | |||||
+ * You should have received a copy of the GNU General Public License | |||||
+ * along with this program; if not, see <http://www.gnu.org/licenses/>. | |||||
+ */ | |||||
+ | |||||
+#include "includes.h" | |||||
+ | |||||
+#include "lib/util/tevent_unix.h" | |||||
+#include "lib/util/tevent_ntstatus.h" | |||||
+#include "system/filesys.h" | |||||
+ | |||||
+#include <sys/sysctl.h> | |||||
+ | |||||
+static int vfs_freebsd_debug_level = DBGC_VFS; | |||||
+ | |||||
+#undef DBGC_CLASS | |||||
+#define DBGC_CLASS vfs_freebsd_debug_level | |||||
+ | |||||
+#ifndef EXTATTR_MAXNAMELEN | |||||
+#define EXTATTR_MAXNAMELEN UINT8_MAX | |||||
+#endif | |||||
+ | |||||
+#define EXTATTR_NAMESPACE(NS) EXTATTR_NAMESPACE_ ## NS, \ | |||||
+ EXTATTR_NAMESPACE_ ## NS ## _STRING ".", \ | |||||
+ .data.len = (sizeof(EXTATTR_NAMESPACE_ ## NS ## _STRING ".") - 1) | |||||
+ | |||||
+#define EXTATTR_EMPTY 0x00 | |||||
+#define EXTATTR_USER 0x01 | |||||
+#define EXTATTR_SYSTEM 0x02 | |||||
+#define EXTATTR_SECURITY 0x03 | |||||
+#define EXTATTR_TRUSTED 0x04 | |||||
+ | |||||
+enum extattr_mode { | |||||
+ FREEBSD_EXTATTR_SECURE, | |||||
+ FREEBSD_EXTATTR_COMPAT, | |||||
+ FREEBSD_EXTATTR_LEGACY | |||||
+}; | |||||
+ | |||||
+typedef struct { | |||||
+ int namespace; | |||||
+ char name[EXTATTR_MAXNAMELEN+1]; | |||||
+ union { | |||||
+ uint16_t len; | |||||
+ uint16_t flags; | |||||
+ } data; | |||||
+} extattr_attr; | |||||
+ | |||||
+typedef struct { | |||||
+ enum { | |||||
+ EXTATTR_FILE, EXTATTR_LINK, EXTATTR_FDES | |||||
+ } method; | |||||
+ union { | |||||
+ const char *path; | |||||
+ int filedes; | |||||
+ } param; | |||||
+} extattr_arg; | |||||
+ | |||||
+static const struct enum_list extattr_mode_param[] = { | |||||
+ { FREEBSD_EXTATTR_SECURE, "secure" }, /* */ | |||||
+ { FREEBSD_EXTATTR_COMPAT, "compat" }, /* */ | |||||
+ { FREEBSD_EXTATTR_LEGACY, "legacy" }, /* */ | |||||
+ { -1, NULL } | |||||
+}; | |||||
+ | |||||
+ | |||||
+/* */ | |||||
+struct freebsd_handle_data { | |||||
+ enum extattr_mode extattr_mode; | |||||
+}; | |||||
+ | |||||
+ | |||||
+/* XXX: This order doesn't match namespace ids order! */ | |||||
+static extattr_attr extattr[] = { | |||||
+ { EXTATTR_NAMESPACE(EMPTY) }, | |||||
+ { EXTATTR_NAMESPACE(SYSTEM) }, | |||||
+ { EXTATTR_NAMESPACE(USER) }, | |||||
+}; | |||||
+ | |||||
+ | |||||
+static bool freebsd_in_jail(void) { | |||||
+ int val = 0; | |||||
+ size_t val_len = sizeof(val); | |||||
+ | |||||
+ if((sysctlbyname("security.jail.jailed", &val, &val_len, NULL, 0) != -1) && val == 1) { | |||||
+ return true; | |||||
+ } | |||||
+ return false; | |||||
+} | |||||
+ | |||||
+static uint16_t freebsd_map_attrname(const char *name) | |||||
+{ | |||||
+ if(name == NULL || name[0] == '\0') { | |||||
+ return EXTATTR_EMPTY; | |||||
+ } | |||||
+ | |||||
+ switch(name[0]) { | |||||
+ case 'u': | |||||
+ if(strncmp(name, "user.", 5) == 0) | |||||
+ return EXTATTR_USER; | |||||
+ break; | |||||
+ case 't': | |||||
+ if(strncmp(name, "trusted.", 8) == 0) | |||||
+ return EXTATTR_TRUSTED; | |||||
+ break; | |||||
+ case 's': | |||||
+ /* name[1] could be any character, including '\0' */ | |||||
+ switch(name[1]) { | |||||
+ case 'e': | |||||
+ if(strncmp(name, "security.", 9) == 0) | |||||
+ return EXTATTR_SECURITY; | |||||
+ break; | |||||
+ case 'y': | |||||
+ if(strncmp(name, "system.", 7) == 0) | |||||
+ return EXTATTR_SYSTEM; | |||||
+ break; | |||||
+ } | |||||
+ break; | |||||
+ } | |||||
+ return EXTATTR_USER; | |||||
+} | |||||
+ | |||||
+/* security, system, trusted or user */ | |||||
+static extattr_attr* freebsd_map_xattr(enum extattr_mode extattr_mode, const char *name, extattr_attr *attr) | |||||
+{ | |||||
+ int attrnamespace = EXTATTR_NAMESPACE_EMPTY; | |||||
+ const char *p, *attrname = name; | |||||
+ | |||||
+ if(name == NULL || name[0] == '\0') { | |||||
+ return NULL; | |||||
+ } | |||||
+ | |||||
+ if(attr == NULL) { | |||||
+ return NULL; | |||||
+ } | |||||
+ | |||||
+ uint16_t flags = freebsd_map_attrname(name); | |||||
+ | |||||
+ switch(flags) { | |||||
+ case EXTATTR_SECURITY: | |||||
+ case EXTATTR_TRUSTED: | |||||
+ case EXTATTR_SYSTEM: | |||||
+ attrnamespace = (extattr_mode == FREEBSD_EXTATTR_SECURE) ? | |||||
+ EXTATTR_NAMESPACE_SYSTEM : | |||||
+ EXTATTR_NAMESPACE_USER; | |||||
+ break; | |||||
+ case EXTATTR_USER: | |||||
+ attrnamespace = EXTATTR_NAMESPACE_USER; | |||||
+ break; | |||||
+ default: | |||||
+ /* Default to "user" namespace if nothing else was specified */ | |||||
+ attrnamespace = EXTATTR_NAMESPACE_USER; | |||||
+ flags = EXTATTR_USER; | |||||
+ break; | |||||
+ } | |||||
+ | |||||
+ if (extattr_mode == FREEBSD_EXTATTR_LEGACY) { | |||||
+ switch(flags) { | |||||
+ case EXTATTR_SECURITY: | |||||
+ attrname = name + 9; | |||||
+ break; | |||||
+ case EXTATTR_TRUSTED: | |||||
+ attrname = name + 8; | |||||
+ break; | |||||
+ case EXTATTR_SYSTEM: | |||||
+ attrname = name + 7; | |||||
+ break; | |||||
+ case EXTATTR_USER: | |||||
+ attrname = name + 5; | |||||
+ break; | |||||
+ default: | |||||
+ attrname = ((p=strchr(name, '.')) != NULL) ? p + 1 : name; | |||||
+ break; | |||||
+ } | |||||
+ } | |||||
+ | |||||
+ attr->namespace = attrnamespace; | |||||
+ attr->data.flags = flags; | |||||
+ strlcpy(attr->name, attrname, EXTATTR_MAXNAMELEN + 1); | |||||
+ | |||||
+ return attr; | |||||
+} | |||||
+ | |||||
+static ssize_t extattr_size(extattr_arg arg, extattr_attr *attr) | |||||
+{ | |||||
+ ssize_t result; | |||||
+ | |||||
+ switch(arg.method) { | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ case EXTATTR_FILE: | |||||
+ result = extattr_get_file(arg.param.path, attr->namespace, attr->name, NULL, 0); | |||||
+ break; | |||||
+ case EXTATTR_LINK: | |||||
+ result = extattr_get_link(arg.param.path, attr->namespace, attr->name, NULL, 0); | |||||
+ break; | |||||
+ case EXTATTR_FDES: | |||||
+ result = extattr_get_fd(arg.param.filedes, attr->namespace, attr->name, NULL, 0); | |||||
+ break; | |||||
+#endif | |||||
+ default: | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if(result < 0) { | |||||
+ errno = EINVAL; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ return result; | |||||
+} | |||||
+ | |||||
+ | |||||
+/* | |||||
+ * The list of names is returned as an unordered array of NULL-terminated | |||||
+ * character strings (attribute names are separated by NULL characters), | |||||
+ * like this: | |||||
+ * user.name1\0system.name1\0user.name2\0 | |||||
+ * | |||||
+ * Filesystems like ext2, ext3 and XFS which implement POSIX ACLs using | |||||
+ * extended attributes, might return a list like this: | |||||
+ * system.posix_acl_access\0system.posix_acl_default\0 | |||||
+ */ | |||||
+/* | |||||
+ * The extattr_list_file() returns a list of attributes present in the | |||||
+ * requested namespace. Each list entry consists of a single byte containing | |||||
+ * the length of the attribute name, followed by the attribute name. The | |||||
+ * attribute name is not terminated by ASCII 0 (nul). | |||||
+*/ | |||||
+ | |||||
+static ssize_t freebsd_extattr_list(extattr_arg arg, enum extattr_mode extattr_mode, char *list, size_t size) | |||||
+{ | |||||
+ ssize_t list_size, total_size = 0; | |||||
+ char *p, *q, *list_end; | |||||
+ int len; | |||||
+ /* | |||||
+ Ignore all but user namespace when we are not root or in jail | |||||
+ See: https://bugzilla.samba.org/show_bug.cgi?id=10247 | |||||
+ */ | |||||
+ bool as_root = (geteuid() == 0); | |||||
+ | |||||
+ int ns = (extattr_mode == FREEBSD_EXTATTR_SECURE && as_root) ? 1 : 2; | |||||
+ | |||||
+ /* Iterate through extattr(2) namespaces */ | |||||
+ for(; ns < ARRAY_SIZE(extattr); ns++) { | |||||
+ switch(arg.method) { | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ case EXTATTR_FILE: | |||||
+ list_size = extattr_list_file(arg.param.path, extattr[ns].namespace, list, size); | |||||
+ break; | |||||
+ case EXTATTR_LINK: | |||||
+ list_size = extattr_list_link(arg.param.path, extattr[ns].namespace, list, size); | |||||
+ break; | |||||
+ case EXTATTR_FDES: | |||||
+ list_size = extattr_list_fd(arg.param.filedes, extattr[ns].namespace, list, size); | |||||
+ break; | |||||
+#endif | |||||
+ default: | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+ } | |||||
+ /* Some error happend. Errno should be set by the previous call */ | |||||
+ if(list_size < 0) | |||||
+ return -1; | |||||
+ /* No attributes in this namespace */ | |||||
+ if(list_size == 0) | |||||
+ continue; | |||||
+ /* | |||||
+ Call with an empty buffer may be used to calculate | |||||
+ necessary buffer size. | |||||
+ */ | |||||
+ if(list == NULL) { | |||||
+ /* | |||||
+ XXX: Unfortunately, we can't say, how many attributes were | |||||
+ returned, so here is the potential problem with the emulation. | |||||
+ */ | |||||
+ if(extattr_mode == FREEBSD_EXTATTR_LEGACY) { | |||||
+ /* | |||||
+ Take the worse case of one char attribute names - | |||||
+ two bytes per name plus one more for sanity. | |||||
+ */ | |||||
+ total_size += list_size + (list_size/2 + 1)*extattr[ns].data.len; | |||||
+ } | |||||
+ else { | |||||
+ total_size += list_size; | |||||
+ } | |||||
+ continue; | |||||
+ } | |||||
+ | |||||
+ if(extattr_mode == FREEBSD_EXTATTR_LEGACY) { | |||||
+ /* Count necessary offset to fit namespace prefixes */ | |||||
+ int extra_len = 0; | |||||
+ uint16_t flags; | |||||
+ list_end = list + list_size; | |||||
+ for(list_size = 0, p = q = list; p < list_end; p += len) { | |||||
+ len = p[0] + 1; | |||||
+ (void)strlcpy(q, p + 1, len); | |||||
+ flags = freebsd_map_attrname(q); | |||||
+ /* Skip secure attributes for non-root user */ | |||||
+ if(extattr_mode != FREEBSD_EXTATTR_SECURE && !as_root && flags > EXTATTR_USER) { | |||||
+ continue; | |||||
+ } | |||||
+ if(flags <= EXTATTR_USER) { | |||||
+ /* Don't count trailing '\0' */ | |||||
+ extra_len += extattr[ns].data.len; | |||||
+ } | |||||
+ list_size += len; | |||||
+ q += len; | |||||
+ } | |||||
+ total_size += list_size + extra_len; | |||||
+ /* Buffer is too small to fit the results */ | |||||
+ if(total_size > size) { | |||||
+ errno = ERANGE; | |||||
+ return -1; | |||||
+ } | |||||
+ /* Shift results backwards, so we can prepend prefixes */ | |||||
+ list_end = list + extra_len; | |||||
+ p = (char*)memmove(list_end, list, list_size); | |||||
+ /* | |||||
+ We enter the loop with `p` pointing to the shifted list and | |||||
+ `extra_len` having the total margin between `list` and `p` | |||||
+ */ | |||||
+ for(list_end += list_size; p < list_end; p += len) { | |||||
+ len = strlen(p) + 1; | |||||
+ flags = freebsd_map_attrname(p); | |||||
+ if(flags <= EXTATTR_USER) { | |||||
+ /* Add namespace prefix */ | |||||
+ (void)strncpy(list, extattr[ns].name, extattr[ns].data.len); | |||||
+ list += extattr[ns].data.len; | |||||
+ } | |||||
+ /* Append attribute name */ | |||||
+ (void)strlcpy(list, p, len); | |||||
+ list += len; | |||||
+ } | |||||
+ } | |||||
+ else { | |||||
+ /* Convert UCSD strings into nul-terminated strings */ | |||||
+ for(list_end = list + list_size; list < list_end; list += len) { | |||||
+ len = list[0] + 1; | |||||
+ (void)strlcpy(list, list + 1, len); | |||||
+ } | |||||
+ total_size += list_size; | |||||
+ } | |||||
+ } | |||||
+ return total_size; | |||||
+} | |||||
+ | |||||
+/* | |||||
+static ssize_t freebsd_getxattr_size(vfs_handle_struct *handle, | |||||
+ const struct smb_filename *smb_fname, | |||||
+ const char *name) | |||||
+{ | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_arg arg = { EXTATTR_FILE, smb_fname->base_name }; | |||||
+ extattr_attr attr; | |||||
+ | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ if(!freebsd_map_xattr(data->extattr_mode, name, &attr)) { | |||||
+ errno = EINVAL; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if(data->extattr_mode != FREEBSD_EXTATTR_SECURE && geteuid() != 0 && attr.data.flags > EXTATTR_USER) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ return extattr_size(arg, &attr); | |||||
+} | |||||
+*/ | |||||
+ | |||||
+/* VFS entries */ | |||||
+static ssize_t freebsd_getxattr(vfs_handle_struct *handle, | |||||
+ const struct smb_filename *smb_fname, | |||||
+ const char *name, | |||||
+ void *value, | |||||
+ size_t size) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_arg arg = { EXTATTR_FILE, .param.path = smb_fname->base_name }; | |||||
+ extattr_attr attr; | |||||
+ ssize_t res; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ if(!freebsd_map_xattr(data->extattr_mode, name, &attr)) { | |||||
+ errno = EINVAL; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ /* Filter out 'secure' entries */ | |||||
+ if(data->extattr_mode != FREEBSD_EXTATTR_SECURE && geteuid() != 0 && attr.data.flags > EXTATTR_USER) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ /* | |||||
+ * The BSD implementation has a nasty habit of silently truncating | |||||
+ * the returned value to the size of the buffer, so we have to check | |||||
+ * that the buffer is large enough to fit the returned value. | |||||
+ */ | |||||
+ if((res=extattr_size(arg, &attr)) < 0) { | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if (size == 0) { | |||||
+ return res; | |||||
+ } | |||||
+ else if (res > size) { | |||||
+ errno = ERANGE; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if((res=extattr_get_file(smb_fname->base_name, attr.namespace, attr.name, value, size)) >= 0) { | |||||
+ return res; | |||||
+ } | |||||
+ return -1; | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+ | |||||
+static ssize_t freebsd_fgetxattr(vfs_handle_struct *handle, | |||||
+ struct files_struct *fsp, const char *name, | |||||
+ void *value, size_t size) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_arg arg = { EXTATTR_FDES, .param.filedes = fsp->fh->fd }; | |||||
+ extattr_attr attr; | |||||
+ ssize_t res; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ if(!freebsd_map_xattr(data->extattr_mode, name, &attr)) { | |||||
+ errno = EINVAL; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ /* Filter out 'secure' entries */ | |||||
+ if(data->extattr_mode != FREEBSD_EXTATTR_SECURE && geteuid() != 0 && attr.data.flags > EXTATTR_USER) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ /* | |||||
+ * The BSD implementation has a nasty habit of silently truncating | |||||
+ * the returned value to the size of the buffer, so we have to check | |||||
+ * that the buffer is large enough to fit the returned value. | |||||
+ */ | |||||
+ if((res=extattr_size(arg, &attr)) < 0) { | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if (size == 0) { | |||||
+ return res; | |||||
+ } | |||||
+ else if (res > size) { | |||||
+ errno = ERANGE; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if((res=extattr_get_fd(fsp->fh->fd, attr.namespace, attr.name, value, size)) >= 0) { | |||||
+ return res; | |||||
+ } | |||||
+ return -1; | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+ | |||||
+static ssize_t freebsd_listxattr(vfs_handle_struct *handle, | |||||
+ const struct smb_filename *smb_fname, | |||||
+ char *list, | |||||
+ size_t size) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ extattr_arg arg = { EXTATTR_FILE, .param.path = smb_fname->base_name }; | |||||
+ | |||||
+ return freebsd_extattr_list(arg, data->extattr_mode, list, size); | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+ | |||||
+static ssize_t freebsd_flistxattr(vfs_handle_struct *handle, | |||||
+ struct files_struct *fsp, char *list, | |||||
+ size_t size) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_arg arg = { EXTATTR_FDES, .param.filedes = fsp->fh->fd }; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ return freebsd_extattr_list(arg, data->extattr_mode, list, size); | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+static int freebsd_removexattr(vfs_handle_struct *handle, | |||||
+ const struct smb_filename *smb_fname, | |||||
+ const char *name) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_attr attr; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ | |||||
+ /* Filter out 'secure' entries */ | |||||
+ if(data->extattr_mode != FREEBSD_EXTATTR_SECURE && geteuid() != 0 && attr.data.flags > EXTATTR_USER) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ return extattr_delete_file(smb_fname->base_name, attr.namespace, attr.name); | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+ | |||||
+static int freebsd_fremovexattr(vfs_handle_struct *handle, | |||||
+ struct files_struct *fsp, const char *name) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_attr attr; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ if(!freebsd_map_xattr(data->extattr_mode, name, &attr)) { | |||||
+ errno = EINVAL; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ /* Filter out 'secure' entries */ | |||||
+ if(data->extattr_mode != FREEBSD_EXTATTR_SECURE && geteuid() != 0 && attr.data.flags > EXTATTR_USER) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ return extattr_delete_fd(fsp->fh->fd, attr.namespace, attr.name); | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+ | |||||
+static int freebsd_setxattr(vfs_handle_struct *handle, | |||||
+ const struct smb_filename *smb_fname, | |||||
+ const char *name, | |||||
+ const void *value, | |||||
+ size_t size, | |||||
+ int flags) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_attr attr; | |||||
+ ssize_t res; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ if(!freebsd_map_xattr(data->extattr_mode, name, &attr)) { | |||||
+ errno = EINVAL; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ /* Filter out 'secure' entries */ | |||||
+ if(data->extattr_mode != FREEBSD_EXTATTR_SECURE && geteuid() != 0 && attr.data.flags > EXTATTR_USER) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if (flags) { | |||||
+ extattr_arg arg = { EXTATTR_FILE, .param.path = smb_fname->base_name }; | |||||
+ /* Check attribute existence */ | |||||
+ res = extattr_size(arg, &attr); | |||||
+ if (res < 0) { | |||||
+ /* REPLACE attribute, that doesn't exist */ | |||||
+ if ((flags & XATTR_REPLACE) && errno == ENOATTR) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ /* Ignore other errors */ | |||||
+ } | |||||
+ else { | |||||
+ /* CREATE attribute, that already exists */ | |||||
+ if (flags & XATTR_CREATE) { | |||||
+ errno = EEXIST; | |||||
+ return -1; | |||||
+ } | |||||
+ } | |||||
+ } | |||||
+ res = extattr_set_file(smb_fname->base_name, attr.namespace, attr.name, value, size); | |||||
+ | |||||
+ return (res >= 0) ? 0 : -1; | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+ | |||||
+static int freebsd_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp, | |||||
+ const char *name, const void *value, size_t size, | |||||
+ int flags) | |||||
+{ | |||||
+#if defined(HAVE_XATTR_EXTATTR) | |||||
+ struct freebsd_handle_data *data; | |||||
+ extattr_attr attr; | |||||
+ ssize_t res; | |||||
+ | |||||
+ SMB_VFS_HANDLE_GET_DATA(handle, data, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ if(!freebsd_map_xattr(data->extattr_mode, name, &attr)) { | |||||
+ errno = EINVAL; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ /* Filter out 'secure' entries */ | |||||
+ if(data->extattr_mode != FREEBSD_EXTATTR_SECURE && geteuid() != 0 && attr.data.flags > EXTATTR_USER) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if (flags) { | |||||
+ extattr_arg arg = { EXTATTR_FDES, .param.filedes = fsp->fh->fd }; | |||||
+ /* Check attribute existence */ | |||||
+ res = extattr_size(arg, &attr); | |||||
+ if (res < 0) { | |||||
+ /* REPLACE attribute, that doesn't exist */ | |||||
+ if ((flags & XATTR_REPLACE) && errno == ENOATTR) { | |||||
+ errno = ENOATTR; | |||||
+ return -1; | |||||
+ } | |||||
+ /* Ignore other errors */ | |||||
+ } | |||||
+ else { | |||||
+ /* CREATE attribute, that already exists */ | |||||
+ if (flags & XATTR_CREATE) { | |||||
+ errno = EEXIST; | |||||
+ return -1; | |||||
+ } | |||||
+ } | |||||
+ } | |||||
+ | |||||
+ res = extattr_set_fd(fsp->fh->fd, attr.namespace, attr.name, value, size); | |||||
+ | |||||
+ return (res >= 0) ? 0 : -1; | |||||
+#else | |||||
+ errno = ENOSYS; | |||||
+ return -1; | |||||
+#endif | |||||
+} | |||||
+ | |||||
+static int freebsd_connect(vfs_handle_struct *handle, const char *service, | |||||
+ const char *user) | |||||
+{ | |||||
+ struct freebsd_handle_data *data; | |||||
+ int enumval, saved_errno; | |||||
+ | |||||
+ int ret = SMB_VFS_NEXT_CONNECT(handle, service, user); | |||||
+ | |||||
+ if (ret < 0) { | |||||
+ return ret; | |||||
+ } | |||||
+ | |||||
+ data = talloc_zero(handle->conn, struct freebsd_handle_data); | |||||
+ if (!data) { | |||||
+ saved_errno = errno; | |||||
+ SMB_VFS_NEXT_DISCONNECT(handle); | |||||
+ DEBUG(0, ("talloc_zero() failed\n")); | |||||
+ errno = saved_errno; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ enumval = lp_parm_enum(SNUM(handle->conn), "freebsd", | |||||
+ "extattr mode", extattr_mode_param, FREEBSD_EXTATTR_LEGACY); | |||||
+ if (enumval == -1) { | |||||
+ saved_errno = errno; | |||||
+ SMB_VFS_NEXT_DISCONNECT(handle); | |||||
+ DBG_DEBUG("value for freebsd: 'extattr mode' is unknown\n"); | |||||
+ errno = saved_errno; | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ if(freebsd_in_jail()) { | |||||
+ enumval = FREEBSD_EXTATTR_COMPAT; | |||||
+ DBG_WARNING("running in jail, enforcing 'compat' mode\n"); | |||||
+ } | |||||
+ | |||||
+ data->extattr_mode = (enum extattr_mode)enumval; | |||||
+ | |||||
+ SMB_VFS_HANDLE_SET_DATA(handle, data, NULL, | |||||
+ struct freebsd_handle_data, | |||||
+ return -1); | |||||
+ | |||||
+ DBG_DEBUG("connect to service[%s] with '%s' extattr mode\n", | |||||
+ service, extattr_mode_param[data->extattr_mode].name); | |||||
+ | |||||
+ return 0; | |||||
+} | |||||
+ | |||||
+static void freebsd_disconnect(vfs_handle_struct *handle) | |||||
+{ | |||||
+ SMB_VFS_NEXT_DISCONNECT(handle); | |||||
+} | |||||
+ | |||||
+/* VFS operations structure */ | |||||
+ | |||||
+struct vfs_fn_pointers freebsd_fns = { | |||||
+ /* Disk operations */ | |||||
+ | |||||
+ .connect_fn = freebsd_connect, | |||||
+ .disconnect_fn = freebsd_disconnect, | |||||
+ | |||||
+ /* EA operations. */ | |||||
+ .getxattr_fn = freebsd_getxattr, | |||||
+ .fgetxattr_fn = freebsd_fgetxattr, | |||||
+ .listxattr_fn = freebsd_listxattr, | |||||
+ .flistxattr_fn = freebsd_flistxattr, | |||||
+ .removexattr_fn = freebsd_removexattr, | |||||
+ .fremovexattr_fn = freebsd_fremovexattr, | |||||
+ .setxattr_fn = freebsd_setxattr, | |||||
+ .fsetxattr_fn = freebsd_fsetxattr, | |||||
+}; | |||||
+ | |||||
+static_decl_vfs; | |||||
+NTSTATUS vfs_freebsd_init(TALLOC_CTX *ctx) | |||||
+{ | |||||
+ NTSTATUS ret; | |||||
+ | |||||
+ ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "freebsd", | |||||
+ &freebsd_fns); | |||||
+ | |||||
+ if (!NT_STATUS_IS_OK(ret)) { | |||||
+ return ret; | |||||
+ } | |||||
+ | |||||
+ vfs_freebsd_debug_level = debug_add_class("freebsd"); | |||||
+ if (vfs_freebsd_debug_level == -1) { | |||||
+ vfs_freebsd_debug_level = DBGC_VFS; | |||||
+ DEBUG(0, ("vfs_freebsd: Couldn't register custom debugging class!\n")); | |||||
+ } else { | |||||
+ DEBUG(10, ("vfs_freebsd: Debug class number of 'fileid': %d\n", vfs_freebsd_debug_level)); | |||||
+ } | |||||
+ | |||||
+ return ret; | |||||
+} | |||||
--- docs-xml/manpages/vfs_freebsd.8.xml.orig 2019-06-25 00:51:54 UTC | |||||
+++ docs-xml/manpages/vfs_freebsd.8.xml | |||||
@@ -0,0 +1,169 @@ | |||||
+<?xml version="1.0" encoding="iso-8859-1"?> | |||||
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> | |||||
+<refentry id="vfs_freebsd.8"> | |||||
+ | |||||
+<refmeta> | |||||
+ <refentrytitle>vfs_freebsd</refentrytitle> | |||||
+ <manvolnum>8</manvolnum> | |||||
+ <refmiscinfo class="source">Samba</refmiscinfo> | |||||
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo> | |||||
+ <refmiscinfo class="version">&doc.version;</refmiscinfo> | |||||
+</refmeta> | |||||
+ | |||||
+<refnamediv> | |||||
+ <refname>vfs_freebsd</refname> | |||||
+ <refpurpose>FreeBSD-specific VFS functions</refpurpose> | |||||
+</refnamediv> | |||||
+ | |||||
+<refsynopsisdiv> | |||||
+ <cmdsynopsis> | |||||
+ <command>vfs objects = freebsd</command> | |||||
+ </cmdsynopsis> | |||||
+</refsynopsisdiv> | |||||
+ | |||||
+<refsect1> | |||||
+ <title>DESCRIPTION</title> | |||||
+ | |||||
+ <para>This VFS module is part of the <citerefentry><refentrytitle>samba</refentrytitle> | |||||
+ <manvolnum>7</manvolnum></citerefentry> suite.</para> | |||||
+ | |||||
+ <para>The <command>vfs_freebsd</command> module implements some of the FreeBSD-specific VFS functions.</para> | |||||
+ | |||||
+ <para>This module is stackable.</para> | |||||
+</refsect1> | |||||
+ | |||||
+ | |||||
+<refsect1> | |||||
+ <title>OPTIONS</title> | |||||
+ | |||||
+ <variablelist> | |||||
+ | |||||
+ <varlistentry> | |||||
+ <term>freebsd:extattr mode=[legacy|compat|secure]</term> | |||||
+ <listitem> | |||||
+ <para>This parameter defines how the emulation of the Linux attr(5) extended attributes | |||||
+ is performed through the FreeBSD native extattr(9) system calls.</para> | |||||
+ | |||||
+ <para>Currently the <emphasis>security</emphasis>, <emphasis>system</emphasis>, | |||||
+ <emphasis>trusted</emphasis> and <emphasis>user</emphasis> extended attribute(xattr) | |||||
+ classes are defined in Linux. Contrary FreeBSD has only <emphasis>USER</emphasis> | |||||
+ and <emphasis>SYSTEM</emphasis> extended attribute(extattr) namespaces, so mapping | |||||
+ of one set into another isn't straightforward and can be done in different ways.</para> | |||||
+ | |||||
+ <para>Historically the Samba(7) built-in xattr mapping implementation simply converted | |||||
+ <emphasis>system</emphasis> and <emphasis>user</emphasis> xattr into corresponding | |||||
+ <emphasis>SYSTEM</emphasis> and <emphasis>USER</emphasis> extattr namespaces, dropping | |||||
+ the class prefix name with the separating dot and using attribute name only within the | |||||
+ mapped namespace. It also rejected any other xattr classes, like <emphasis>security</emphasis> | |||||
+ and <emphasis>trusted</emphasis> as invalid. Such behavior in particular broke AD | |||||
+ provisioning on UFS2 file systems as essential <emphasis>security.NTACL</emphasis> | |||||
+ xattr was rejected as invalid.</para> | |||||
+ | |||||
+ <para>This module tries to address this problem and provide secure, where it's possible, | |||||
+ way to map Linux xattr into FreeBSD's extattr.</para> | |||||
+ | |||||
+ <para>When <emphasis>mode</emphasis> is set to the <emphasis>legacy (default)</emphasis> | |||||
+ then modified version of built-in mapping is used, where <emphasis>system</emphasis> xattr | |||||
+ is mapped into SYSTEM namespace, while <emphasis>secure</emphasis>, <emphasis>trusted</emphasis> | |||||
+ and <emphasis>user</emphasis> xattr are all mapped into the USER namespace, dropping class | |||||
+ prefixes and mix them all together. This is the way how Samba FreeBSD ports were patched | |||||
+ up to the 4.9 version and that created multiple potential security issues. This mode is aimed for | |||||
+ the compatibility with the legacy installations only and should be avoided in new setups.</para> | |||||
+ | |||||
+ <para>The <emphasis>compat</emphasis> mode is mostly designed for the jailed environments, | |||||
+ where it's not possible to write extattrs into the secure SYSTEM namespace, so all four | |||||
+ classes are mapped into the USER namespace. To preserve information about origin of the | |||||
+ extended attribute it is stored together with the class preffix in the <emphasis>class.attribute</emphasis> | |||||
+ format.</para> | |||||
+ | |||||
+ <para>The <emphasis>secure</emphasis> mode is meant for storing extended attributes in a secure | |||||
+ manner, so that <emphasis>security</emphasis>, <emphasis>system</emphasis> and <emphasis>trusted</emphasis> | |||||
+ are stored in the SYSTEM namespace, which can be modified only by root. | |||||
+ </para> | |||||
+ </listitem> | |||||
+ </varlistentry> | |||||
+ | |||||
+ | |||||
+ </variablelist> | |||||
+</refsect1> | |||||
+ | |||||
+<refsect1> | |||||
+ <table frame="all" rowheader="firstcol"> | |||||
+ <title>Attributes mapping</title> | |||||
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'> | |||||
+ <thead> | |||||
+ <row> | |||||
+ <entry> </entry> | |||||
+ <entry>built-in</entry> | |||||
+ <entry>legacy</entry> | |||||
+ <entry>compat/jail</entry> | |||||
+ <entry>secure</entry> | |||||
+ </row> | |||||
+ </thead> | |||||
+ <tbody> | |||||
+ <row> | |||||
+ <entry>user</entry> | |||||
+ <entry>USER; attribute</entry> | |||||
+ <entry>USER; attribute</entry> | |||||
+ <entry>USER; user.attribute</entry> | |||||
+ <entry>USER; user.attribute</entry> | |||||
+ </row> | |||||
+ <row> | |||||
+ <entry>system</entry> | |||||
+ <entry>SYSTEM; attribute</entry> | |||||
+ <entry>SYSTEM; attribute</entry> | |||||
+ <entry>USER; system.attribute</entry> | |||||
+ <entry>SYSTEM; system.attribute</entry> | |||||
+ </row> | |||||
+ <row> | |||||
+ <entry>trusted</entry> | |||||
+ <entry>FAIL</entry> | |||||
+ <entry>USER; attribute</entry> | |||||
+ <entry>USER; trusted.attribute</entry> | |||||
+ <entry>SYSTEM; trusted.attribute</entry> | |||||
+ </row> | |||||
+ <row> | |||||
+ <entry>security</entry> | |||||
+ <entry>FAIL</entry> | |||||
+ <entry>USER; attribute</entry> | |||||
+ <entry>USER; security.attribute</entry> | |||||
+ <entry>SYSTEM; security.attribute</entry> | |||||
+ </row> | |||||
+ </tbody> | |||||
+ </tgroup> | |||||
+ </table> | |||||
+</refsect1> | |||||
+ | |||||
+<refsect1> | |||||
+ <title>EXAMPLES</title> | |||||
+ | |||||
+ <para>Use secure method of setting extended attributes on the share:</para> | |||||
+ | |||||
+<programlisting> | |||||
+ <smbconfsection name="[sysvol]"/> | |||||
+ <smbconfoption name="vfs objects">freebsd</smbconfoption> | |||||
+ <smbconfoption name="freebsd:extattr mode">secure</smbconfoption> | |||||
+</programlisting> | |||||
+ | |||||
+</refsect1> | |||||
+ | |||||
+<refsect1> | |||||
+ <title>VERSION</title> | |||||
+ | |||||
+ <para>This man page is part of version &doc.version; of the Samba suite. | |||||
+ </para> | |||||
+</refsect1> | |||||
+ | |||||
+<refsect1> | |||||
+ <title>AUTHOR</title> | |||||
+ | |||||
+ <para>The original Samba software and related utilities | |||||
+ were created by Andrew Tridgell. Samba is now developed | |||||
+ by the Samba Team as an Open Source project similar | |||||
+ to the way the Linux kernel is developed.</para> | |||||
+ | |||||
+ <para>This module was written by Timur I. Bakeyev</para> | |||||
+ | |||||
+</refsect1> | |||||
+ | |||||
+</refentry> |