# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # include ../Makefile.lib SUBDIRS = \ libfksmbsrv \ libmlsvc \ libsmb \ libsmbns \ libsmbrp include ./Makefile.subdirs libsmbns: libsmb libmlsvc: libsmb libsmbns libfksmbsrv: libsmb # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Common Makefile definitions for smbsrv. # # We reset the Makefile.lib macros ROOTLIBDIR to refer to usr/lib/smbsrv. # Hammerhead: 64-bit only - ROOTLIBDIR64 also at usr/lib/smbsrv (flattened) # We also install the userland library header files under /usr/include/smbsrv. ROOTSMBHDRDIR= $(ROOTHDRDIR)/smbsrv ROOTSMBHDRS= $(HDRS:%=$(ROOTSMBHDRDIR)/%) ROOTLIBDIR = $(ROOT)/usr/lib/smbsrv # Hammerhead: 64-bit only - flatten smbsrv library path ROOTLIBDIR64 = $(ROOT)/usr/lib/smbsrv SRCDIR= ../common NDLDIR= $(ROOT)/usr/include/smbsrv/ndl LIBS= $(DYNLIB) CSTD = $(CSTD_GNU99) CPPFLAGS += -I$(SRCDIR) -I. LDLIBS32 += -L$(ROOT)/usr/lib/smbsrv # Hammerhead: 64-bit only - flatten smbsrv library search path LDLIBS64 += -L$(ROOT)/usr/lib/smbsrv CLEANFILES += $(OBJECTS:%_ndr.o=%_ndr.c) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Toplevel Makefile included by each subdirectory. Responsible for the 'check' # and 'install_h' targets, as well as descending into the architecture directory # to actually build the library. # include ../../Makefile.lib include ../Makefile.lib # Hammerhead: amd64-only SUBDIRS= $(MACH64) HDRDIR= common all : TARGET = all clean : TARGET = clean clobber : TARGET = clobber install : TARGET = install .KEEP_STATE: all clean clobber: $(SUBDIRS) install: install_h $(SUBDIRS) check: $(CHECKHDRS) install_h: $(ROOTSMBHDRS) $(SUBDIRS): FRC @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET) FRC: $(ROOTSMBHDRDIR)/%: common/% $(INS.file) include ../../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # .KEEP_STATE: all : TARGET = all check : TARGET = check clean : TARGET = clean clobber : TARGET = clobber install : TARGET = install install_h : TARGET = install_h all check clean clobber install install_h: $(SUBDIRS) $(filter-out .WAIT,$(SUBDIRS)): FRC @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # # # Common targets for smbsrv Makefiles # %_ndr.c: $(NDLDIR)/%.ndl $(NDRGEN) -Y $(ANSI_CPP) $(CPPFLAGS) $< pics/%.o: $(SRC)/common/smbsrv/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) .KEEP_STATE: all: $(LIBS) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2017 Nexenta Systems, Inc. All rights reserved. # include ../Makefile.smbsrv CLOBBERFILES += common/fksmb_dt.h all: install_h install_h: common/fksmb_dt.h common/fksmb_dt.h : common/fksmb_dt.d $(DTRACE) -xnolibs -h -o $@ -s common/fksmb_dt.d # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2014-2021 Tintri by DDN, Inc. All rights reserved. # Copyright (c) 2018, Joyent, Inc. # Copyright 2020-2024 RackTop Systems, Inc. # LIBRARY = libfksmbsrv.a VERS = .1 OBJS_LOCAL = \ fksmb_cred.o \ fksmb_encrypt_pkcs.o \ fksmb_fem.o \ fksmb_idmap.o \ fksmb_init.o \ fksmb_kdoor.o \ fksmb_preauth_pkcs.o \ fksmb_sign_pkcs.o \ fake_ksocket.o \ fake_lookup.o \ fake_nblk.o \ fake_stream.o \ fake_vfs.o \ fake_vnode.o \ fake_vop.o \ fake_xattr.o \ reparse.o \ vncache.o # See also: $SRC/uts/common/Makefile.files # NB: Intentionally ommitted, compared w/ the above: # smb_cred, smb_fem, smb_idmap, smb_init, smb_kdoor # OBJS_FS_SMBSRV = \ smb_acl.o \ smb_alloc.o \ smb_authenticate.o \ smb_close.o \ smb_cmn_oplock.o \ smb_cmn_rename.o \ smb_cmn_setfile.o \ smb_common_open.o \ smb_common_transact.o \ smb_create.o \ smb_delete.o \ smb_dfs.o \ smb_directory.o \ smb_dispatch.o \ smb_echo.o \ smb_errno.o \ smb_find.o \ smb_flush.o \ smb_fsinfo.o \ smb_fsops.o \ smb_kshare.o \ smb_kutil.o \ smb_lock.o \ smb_lock_byte_range.o \ smb_locking_andx.o \ smb_logoff_andx.o \ smb_mangle_name.o \ smb_mbuf_marshaling.o \ smb_mbuf_util.o \ smb_negotiate.o \ smb_net.o \ smb_node.o \ smb_notify.o \ smb_nt_cancel.o \ smb_nt_create_andx.o \ smb_nt_transact_create.o \ smb_nt_transact_ioctl.o \ smb_nt_transact_notify_change.o \ smb_nt_transact_quota.o \ smb_nt_transact_security.o \ smb_odir.o \ smb_ofile.o \ smb_open_andx.o \ smb_opipe.o \ smb_oplock.o \ smb_pathname.o \ smb_print.o \ smb_process_exit.o \ smb_query_fileinfo.o \ smb_quota.o \ smb_read.o \ smb_rename.o \ smb_sd.o \ smb_seek.o \ smb_server.o \ smb_session.o \ smb_session_setup_andx.o \ smb_set_fileinfo.o \ smb_signing.o \ smb_srv_oplock.o \ smb_thread.o \ smb_tree.o \ smb_trans2_create_directory.o \ smb_trans2_dfs.o \ smb_trans2_find.o \ smb_tree_connect.o \ smb_unlock_byte_range.o \ smb_user.o \ smb_vops.o \ smb_vss.o \ smb_write.o \ \ smb2_aapl.o \ smb2_dispatch.o \ smb2_durable.o \ smb2_cancel.o \ smb2_change_notify.o \ smb2_close.o \ smb2_create.o \ smb2_echo.o \ smb2_flush.o \ smb2_fsctl_copychunk.o \ smb2_fsctl_fs.o \ smb2_fsctl_odx.o \ smb2_fsctl_sparse.o \ smb2_ioctl.o \ smb2_lease.o \ smb2_lock.o \ smb2_logoff.o \ smb2_negotiate.o \ smb2_ofile.o \ smb2_oplock.o \ smb2_qinfo_file.o \ smb2_qinfo_fs.o \ smb2_qinfo_sec.o \ smb2_qinfo_quota.o \ smb2_query_dir.o \ smb2_query_info.o \ smb2_read.o \ smb2_session_setup.o \ smb2_set_info.o \ smb2_setinfo_file.o \ smb2_setinfo_fs.o \ smb2_setinfo_quota.o \ smb2_setinfo_sec.o \ smb2_signing.o \ smb2_tree_connect.o \ smb2_tree_disconn.o \ smb2_write.o \ \ smb3_kdf.o \ smb3_encrypt.o # Can't just link with -lsmb because of user vs kernel API # i.e. can't call free with mem from kmem_alloc, which is # what happens if we just link with -lsmb OBJS_CMN_SMBSRV = \ smb_cfg_util.o \ smb_inet.o \ smb_match.o \ smb_msgbuf.o \ smb_native.o \ smb_netbios_util.o \ smb_oem.o \ smb_sid.o \ smb_string.o \ smb_token.o \ smb_token_xdr.o \ smb_utf8.o \ smb_xdr.o OBJS_MISC = \ acl_common.o \ avl.o \ pathname.o \ refstr.o \ smb_status2winerr.o \ xattr_common.o # This one can't be in OBJECTS, as it has to depend on # all of those for the COMPILE.d rule (which processes # all those objects collecting probe instances). DTRACE_OBJS = fksmb_dt.o OBJECTS = \ $(OBJS_LOCAL) \ $(OBJS_FS_SMBSRV) \ $(OBJS_CMN_SMBSRV) \ $(OBJS_MISC) include ../../../Makefile.lib include ../../Makefile.lib # Force SOURCEDEBUG CSOURCEDEBUGFLAGS = $(CCGDEBUG) CCSOURCEDEBUGFLAGS = $(CCGDEBUG) STRIP_STABS = : # Note: need our sys includes _before_ ENVCPPFLAGS, proto etc. # Also, like Makefile.uts, reset CPPFLAGS CPPFLAGS.first += -I../../../libfakekernel/common CPPFLAGS.first += -I../common CPPFLAGS = $(CPPFLAGS.first) INCS += -I$(SRC)/uts/common INCS += -I$(SRC)/common/smbsrv INCS += -I$(SRC)/common LDLIBS += $(MACH_LDLIBS) LDLIBS += -lfakekernel -lidmap -lcmdutils LDLIBS += -lnvpair -lnsl -lpkcs11 -lreparse -lc # Hammerhead: DTrace DOF section (.SUNW_dof) has relocations resolved by the # kernel at runtime. GNU ld needs -z notext to allow these in shared objects. # Override ZTEXT (used in DYNFLAGS for shared library linking). ZTEXT = -Wl,-znotext CPPFLAGS += $(INCS) -D_REENTRANT -D_FAKE_KERNEL CPPFLAGS += -D_FILE_OFFSET_BITS=64 # Always want DEBUG here CPPFLAGS += -DDEBUG CERRWARN += -Wno-switch # not linted SMATCH=off SRCS= $(OBJS_LOCAL:%.o=$(SRCDIR)/%.c) \ $(OBJS_FS_SMBSRV:%.o=$(SRC)/uts/common/fs/smbsrv/%.c) \ $(OBJS_CMN_SMBSRV:%.o=$(SRC)/common/smbsrv/%.c) all: pics/%.o: $(SRC)/uts/common/fs/smbsrv/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/acl_common.o: $(SRC)/common/acl/acl_common.c $(COMPILE.c) -o $@ $(SRC)/common/acl/acl_common.c $(POST_PROCESS_O) # build avl instead of using -lavl so we get debugging pics/avl.o: $(SRC)/common/avl/avl.c $(COMPILE.c) -o $@ $(SRC)/common/avl/avl.c $(POST_PROCESS_O) pics/pathname.o: $(SRC)/uts/common/fs/pathname.c $(COMPILE.c) -o $@ $(SRC)/uts/common/fs/pathname.c $(POST_PROCESS_O) pics/refstr.o: $(SRC)/uts/common/os/refstr.c $(COMPILE.c) -o $@ $(SRC)/uts/common/os/refstr.c $(POST_PROCESS_O) pics/smb_status2winerr.o: $(SRC)/common/smbclnt/smb_status2winerr.c $(COMPILE.c) -o $@ $(SRC)/common/smbclnt/smb_status2winerr.c $(POST_PROCESS_O) pics/xattr_common.o: $(SRC)/common/xattr/xattr_common.c $(COMPILE.c) -o $@ $(SRC)/common/xattr/xattr_common.c $(POST_PROCESS_O) # Makefile.targ has rule for $(SRC)/common/smbsrv/%.c .KEEP_STATE: include ../../Makefile.targ include ../../../Makefile.targ EXTPICS= $(DTRACE_OBJS:%=pics/%) CLEANFILES += $(EXTPICS) $(OBJS) $(PICS) : ../common/fksmb_dt.h pics/fksmb_dt.o: ../common/fksmb_dt.d $(PICS) $(COMPILE.d) -C -s ../common/fksmb_dt.d -o $@ $(PICS) $(POST_PROCESS_O) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # Hammerhead: 64-bit only - flatten smbsrv library paths MACH_LDLIBS += -L$(ROOT)/usr/lib/smbsrv include ../Makefile.com include ../../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ /* * Shim to implement ksocket_sendmblk on top of ksocket_sendmsg */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int ksocket_sendmblk(ksocket_t ks, struct nmsghdr *msg, int flags, mblk_t **mpp, cred_t *cr) { struct msghdr tmsg; mblk_t *m; iovec_t *iov = NULL; size_t iov_sz = 0; size_t tlen, sent; int i, nseg; int rc; /* * Setup the IOV. First, count the number of IOV segments * and get the total length. */ nseg = 0; tlen = 0; m = *mpp; while (m != NULL) { nseg++; tlen += MBLKL(m); m = m->b_cont; } ASSERT(tlen > 0); if (tlen == 0) { rc = 0; goto out; } iov_sz = nseg * sizeof (iovec_t); iov = kmem_alloc(iov_sz, KM_SLEEP); /* * Build the iov list */ i = 0; m = *mpp; while (m != NULL) { iov[i].iov_base = (void *) m->b_rptr; iov[i++].iov_len = MBLKL(m); m = m->b_cont; } ASSERT3S(i, ==, nseg); bzero(&tmsg, sizeof (tmsg)); tmsg.msg_iov = iov; tmsg.msg_iovlen = nseg; while (tlen > 0) { sent = 0; rc = ksocket_sendmsg(ks, &tmsg, 0, &sent, CRED()); if (rc != 0) break; tlen -= sent; } out: if (iov != NULL) kmem_free(iov, iov_sz); if (*mpp != NULL) { freemsg(*mpp); *mpp = NULL; } return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. * Copyright 2022 RackTop Systems, Inc. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int lookupname( const char *fnamep, enum uio_seg seg, int followlink, vnode_t **dirvpp, vnode_t **compvpp) { return (lookupnameatcred(fnamep, seg, followlink, dirvpp, compvpp, NULL, CRED())); } /* * Lookup the user file name, * Handle allocation and freeing of pathname buffer, return error. */ int lookupnameatcred( const char *fnamep, /* user pathname */ enum uio_seg seg, /* addr space that name is in */ int followlink, /* follow sym links */ vnode_t **dirvpp, /* ret for ptr to parent dir vnode */ vnode_t **compvpp, /* ret for ptr to component vnode */ vnode_t *startvp, /* start path search from vp */ cred_t *cr) /* credential */ { char namebuf[TYPICALMAXPATHLEN]; struct pathname lookpn; int error; error = pn_get_buf(fnamep, seg, &lookpn, namebuf, sizeof (namebuf)); if (error == 0) { error = lookuppnatcred(&lookpn, NULL, followlink, dirvpp, compvpp, startvp, cr); } if (error == ENAMETOOLONG) { /* * This thread used a pathname > TYPICALMAXPATHLEN bytes long. */ if ((error = pn_get(fnamep, seg, &lookpn)) != 0) return (error); error = lookuppnatcred(&lookpn, NULL, followlink, dirvpp, compvpp, startvp, cr); pn_free(&lookpn); } return (error); } /* * Lookup the user file name from a given vp, using a specific credential. */ int lookuppnatcred( struct pathname *pnp, /* pathname to lookup */ struct pathname *rpnp, /* if non-NULL, return resolved path */ int followlink, /* (don't) follow sym links */ vnode_t **dirvpp, /* ptr for parent vnode */ vnode_t **compvpp, /* ptr for entry vnode */ vnode_t *startvp, /* start search from this vp */ cred_t *cr) /* user credential */ { vnode_t *vp; /* current directory vp */ vnode_t *rootvp; if (pnp->pn_pathlen == 0) return (ENOENT); /* Simplified for fake_... */ vp = rootvp = rootdir; /* * Skip over leading slashes */ if (pnp->pn_path[0] == '/') { do { pnp->pn_path++; pnp->pn_pathlen--; } while (pnp->pn_path[0] == '/'); } return (lookuppnvp(pnp, rpnp, followlink, dirvpp, compvpp, rootvp, vp, cr)); } /* * Starting at current directory, translate pathname pnp to end. * Leave pathname of final component in pnp, return the vnode * for the final component in *compvpp, and return the vnode * for the parent of the final component in dirvpp. * * This is the central routine in pathname translation and handles * multiple components in pathnames, separating them at /'s. It also * implements mounted file systems and processes symbolic links. * * vp is the vnode where the directory search should start. * * Reference counts: vp must be held prior to calling this function. rootvp * should only be held if rootvp != rootdir. */ int lookuppnvp( struct pathname *pnp, /* pathname to lookup */ struct pathname *rpnp, /* if non-NULL, return resolved path */ int flags, /* follow symlinks */ vnode_t **dirvpp, /* ptr for parent vnode */ vnode_t **compvpp, /* ptr for entry vnode */ vnode_t *rootvp, /* rootvp */ vnode_t *vp, /* directory to start search at */ cred_t *cr) /* user's credential */ { vnode_t *cvp; /* current component vp */ vnode_t *tvp; /* addressable temp ptr */ char component[MAXNAMELEN]; /* buffer for component (incl null) */ int error; int nlink; int lookup_flags; struct pathname presrvd; /* case preserved name */ struct pathname *pp = NULL; vnode_t *startvp; int must_be_directory = 0; boolean_t retry_with_kcred; nlink = 0; cvp = NULL; if (rpnp) rpnp->pn_pathlen = 0; lookup_flags = dirvpp ? LOOKUP_DIR : 0; if (flags & FIGNORECASE) { lookup_flags |= FIGNORECASE; pn_alloc(&presrvd); pp = &presrvd; } /* * Eliminate any trailing slashes in the pathname. * If there are any, we must follow all symlinks. * Also, we must guarantee that the last component is a directory. */ if (pn_fixslash(pnp)) { flags |= FOLLOW; must_be_directory = 1; } startvp = vp; next: retry_with_kcred = B_FALSE; /* * Make sure we have a directory. */ if (vp->v_type != VDIR) { error = ENOTDIR; goto bad; } if (rpnp && VN_CMP(vp, rootvp)) (void) pn_set(rpnp, "/"); /* * Process the next component of the pathname. */ if ((error = pn_getcomponent(pnp, component)) != 0) { goto bad; } /* * Handle "..": two special cases. * 1. If we're at the root directory (e.g. after chroot or * zone_enter) then change ".." to "." so we can't get * out of this subtree. * 2. If this vnode is the root of a mounted file system, * then replace it with the vnode that was mounted on * so that we take the ".." in the other file system. */ if (component[0] == '.' && component[1] == '.' && component[2] == 0) { checkforroot: if (VN_CMP(vp, rootvp)) { component[1] = '\0'; } else if (vp->v_flag & VROOT) { vfs_t *vfsp; cvp = vp; /* * While we deal with the vfs pointer from the vnode * the filesystem could have been forcefully unmounted * and the vnode's v_vfsp could have been invalidated * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it * with vfs_rlock_wait/vfs_unlock. * It is safe to use the v_vfsp even it is freed by * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock * do not dereference v_vfsp. It is just used as a * magic cookie. * One more corner case here is the memory getting * reused for another vfs structure. In this case * lookuppnvp's vfs_rlock_wait will succeed, domount's * vfs_lock will fail and domount will bail out with an * error (EBUSY). */ vfsp = cvp->v_vfsp; /* * This lock is used to synchronize * mounts/unmounts and lookups. * Threads doing mounts/unmounts hold the * writers version vfs_lock_wait(). */ vfs_rlock_wait(vfsp); /* * If this vnode is on a file system that * has been forcibly unmounted, * we can't proceed. Cancel this operation * and return EIO. * * vfs_vnodecovered is NULL if unmounted. * Currently, nfs uses VFS_UNMOUNTED to * check if it's a forced-umount. Keep the * same checking here as well even though it * may not be needed. */ if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { vfs_unlock(vfsp); VN_RELE(cvp); if (pp) pn_free(pp); return (EIO); } VN_HOLD(vp); vfs_unlock(vfsp); VN_RELE(cvp); cvp = NULL; /* * Crossing mount points. For eg: We are doing * a lookup of ".." for file systems root vnode * mounted here, and VOP_LOOKUP() (with covered vnode) * will be on underlying file systems mount point * vnode. Set retry_with_kcred flag as we might end * up doing VOP_LOOKUP() with kcred if required. */ retry_with_kcred = B_TRUE; goto checkforroot; } } /* * Perform a lookup in the current directory. */ error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags, rootvp, cr, NULL, NULL, pp); /* * Retry with kcred - If crossing mount points & error is EACCES. * * If we are crossing mount points here and doing ".." lookup, * VOP_LOOKUP() might fail if the underlying file systems * mount point has no execute permission. In cases like these, * we retry VOP_LOOKUP() by giving as much privilage as possible * by passing kcred credentials. * * In case of hierarchical file systems, passing kcred still may * or may not work. * For eg: UFS FS --> Mount NFS FS --> Again mount UFS on some * directory inside NFS FS. */ if ((error == EACCES) && retry_with_kcred) error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags, rootvp, zone_kcred(), NULL, NULL, pp); cvp = tvp; if (error) { cvp = NULL; /* * On error, return hard error if * (a) we're not at the end of the pathname yet, or * (b) the caller didn't want the parent directory, or * (c) we failed for some reason other than a missing entry. */ if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT) goto bad; pn_setlast(pnp); /* * We inform the caller that the desired entry must be * a directory by adding a '/' to the component name. */ if (must_be_directory && (error = pn_addslash(pnp)) != 0) goto bad; *dirvpp = vp; if (compvpp != NULL) *compvpp = NULL; if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (0); } /* * Traverse mount points. */ if (vn_mountedvfs(cvp) != NULL) { tvp = cvp; if ((error = traverse(&tvp)) != 0) { /* * It is required to assign cvp here, because * traverse() will return a held vnode which * may different than the vnode that was passed * in (even in the error case). If traverse() * changes the vnode it releases the original, * and holds the new one. */ cvp = tvp; goto bad; } cvp = tvp; } /* * If we hit a symbolic link and there is more path to be * translated or this operation does not wish to apply * to a link, then place the contents of the link at the * front of the remaining pathname. */ if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) { struct pathname linkpath; if (++nlink > MAXSYMLINKS) { error = ELOOP; goto bad; } pn_alloc(&linkpath); if ((error = pn_getsymlink(cvp, &linkpath, cr)) != 0) { pn_free(&linkpath); goto bad; } if (pn_pathleft(&linkpath) == 0) (void) pn_set(&linkpath, "."); error = pn_insert(pnp, &linkpath, strlen(component)); pn_free(&linkpath); if (error) goto bad; VN_RELE(cvp); cvp = NULL; if (pnp->pn_pathlen == 0) { error = ENOENT; goto bad; } if (pnp->pn_path[0] == '/') { do { pnp->pn_path++; pnp->pn_pathlen--; } while (pnp->pn_path[0] == '/'); VN_RELE(vp); vp = rootvp; VN_HOLD(vp); } if (pn_fixslash(pnp)) { flags |= FOLLOW; must_be_directory = 1; } goto next; } /* * If rpnp is non-NULL, remember the resolved path name therein. * Do not include "." components. Collapse occurrences of * "previous/..", so long as "previous" is not itself "..". * Exhausting rpnp results in error ENAMETOOLONG. */ if (rpnp && strcmp(component, ".") != 0) { size_t len; if (strcmp(component, "..") == 0 && rpnp->pn_pathlen != 0 && !((rpnp->pn_pathlen > 2 && strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) || (rpnp->pn_pathlen == 2 && strncmp(rpnp->pn_path, "..", 2) == 0))) { while (rpnp->pn_pathlen && rpnp->pn_path[rpnp->pn_pathlen-1] != '/') rpnp->pn_pathlen--; if (rpnp->pn_pathlen > 1) rpnp->pn_pathlen--; rpnp->pn_path[rpnp->pn_pathlen] = '\0'; } else { if (rpnp->pn_pathlen != 0 && rpnp->pn_path[rpnp->pn_pathlen-1] != '/') rpnp->pn_path[rpnp->pn_pathlen++] = '/'; if (flags & FIGNORECASE) { /* * Return the case-preserved name * within the resolved path. */ error = copystr(pp->pn_buf, rpnp->pn_path + rpnp->pn_pathlen, rpnp->pn_bufsize - rpnp->pn_pathlen, &len); } else { error = copystr(component, rpnp->pn_path + rpnp->pn_pathlen, rpnp->pn_bufsize - rpnp->pn_pathlen, &len); } if (error) /* copystr() returns ENAMETOOLONG */ goto bad; rpnp->pn_pathlen += (len - 1); ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen); } } /* * If no more components, return last directory (if wanted) and * last component (if wanted). */ if (pn_pathleft(pnp) == 0) { /* * If there was a trailing slash in the pathname, * make sure the last component is a directory. */ if (must_be_directory && cvp->v_type != VDIR) { error = ENOTDIR; goto bad; } if (dirvpp != NULL) { /* * Check that we have the real parent and not * an alias of the last component. */ if (vn_compare(vp, cvp)) { pn_setlast(pnp); VN_RELE(vp); VN_RELE(cvp); if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (EINVAL); } *dirvpp = vp; } else VN_RELE(vp); if (pnp->pn_path == pnp->pn_buf) (void) pn_set(pnp, "."); else pn_setlast(pnp); if (rpnp) { if (VN_CMP(cvp, rootvp)) (void) pn_set(rpnp, "/"); else if (rpnp->pn_pathlen == 0) (void) pn_set(rpnp, "."); } if (compvpp != NULL) *compvpp = cvp; else VN_RELE(cvp); if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (0); } /* * Skip over slashes from end of last component. */ while (pnp->pn_path[0] == '/') { pnp->pn_path++; pnp->pn_pathlen--; } /* * Searched through another level of directory: * release previous directory handle and save new (result * of lookup) as current directory. */ VN_RELE(vp); vp = cvp; cvp = NULL; goto next; bad: /* * Error. Release vnodes and return. */ if (cvp) VN_RELE(cvp); /* * If the error was ESTALE and the current directory to look in * was the root for this lookup, the root for a mounted file * system, or the starting directory for lookups, then * return ENOENT instead of ESTALE. In this case, no recovery * is possible by the higher level. If ESTALE was returned for * some intermediate directory along the path, then recovery * is potentially possible and retrying from the higher level * will either correct the situation by purging stale cache * entries or eventually get back to the point where no recovery * is possible. */ if (error == ESTALE && (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp)) error = ENOENT; VN_RELE(vp); if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (error); } /* * Traverse a mount point. Routine accepts a vnode pointer as a reference * parameter and performs the indirection, releasing the original vnode. */ int traverse(vnode_t **cvpp) { int error = 0; vnode_t *cvp; vnode_t *tvp; vfs_t *vfsp; cvp = *cvpp; /* * If this vnode is mounted on, then we transparently indirect * to the vnode which is the root of the mounted file system. * Before we do this we must check that an unmount is not in * progress on this vnode. */ for (;;) { /* * Used to try to read lock the vnode here. */ /* * Reached the end of the mount chain? */ vfsp = vn_mountedvfs(cvp); if (vfsp == NULL) { break; } /* * The read lock must be held across the call to VFS_ROOT() to * prevent a concurrent unmount from destroying the vfs. */ error = VFS_ROOT(vfsp, &tvp); if (error) break; VN_RELE(cvp); cvp = tvp; } *cvpp = cvp; return (error); } /* * Get the vnode path, relative to the passed rootvp. * Our vncache always fills in v_path, so this is easy. */ /* ARGSUSED */ int vnodetopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr) { int len, rvp_len = 0; const char *p = vp->v_path; if (vrootp) rvp_len = strlen(vrootp->v_path); len = strlen(p); if (rvp_len < len) p += rvp_len; else p = "/"; (void) strlcpy(buf, p, buflen); return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Utility routines and top-level conflict detection code for NBMAND * locks. */ #include #include #include #include #include #include #include #include /* * Enter the critical region for synchronizing I/O requests with lock/share * requests. "mode" specifies whether the caller intends to update * lock/share state (as opposed to just query it). */ void nbl_start_crit(vnode_t *vp, krw_t mode) { rw_enter(&vp->v_nbllock, mode); } /* * Leave the critical region. */ void nbl_end_crit(vnode_t *vp) { rw_exit(&vp->v_nbllock); } /* * Return non-zero if some thread is in the critical region. * Note that this is appropriate for use in ASSERT()s only. */ int nbl_in_crit(vnode_t *vp) { return (RW_LOCK_HELD(&vp->v_nbllock)); } /* * Returns non-zero if we need to look further for an NBMAND lock or * share conflict. */ /* ARGSUSED */ int nbl_need_check(vnode_t *vp) { /* * Currently we only check if NBMAND locks/shares are allowed on * the filesystem. An option for the future would be to have a * flag on the vnode, though the locking for that can get tricky. */ /* (vp->v_vfsp) && (vp->v_vfsp->vfs_flag & VFS_NBMAND)) */ return (0); } /* * Top-level conflict detection routine. The arguments describe the * operation that is being attempted. If the operation conflicts with an * existing lock or share reservation, a non-zero value is returned. If * the operation is allowed, zero is returned. Note that there is an * implicit argument, which is the process ID of the requester. * * svmand indicates that the file has System V mandatory locking enabled, * so we should look at all record locks, not just NBMAND record locks. * (This is to avoid a deadlock between a process making an I/O request and * a process trying to release a lock. Instead of letting the first * process block in the filesystem code, we flag a conflict here.) */ int nbl_conflict(vnode_t *vp, nbl_op_t op, /* attempted operation */ u_offset_t offset, /* ignore if not I/O */ ssize_t length, /* ignore if not I/O */ int svmand, /* System V mandatory locking */ caller_context_t *ct) /* caller context */ { ASSERT(nbl_in_crit(vp)); ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_RENAME || op == NBL_REMOVE || op == NBL_READWRITE); if (nbl_share_conflict(vp, op, ct)) { return (1); } /* * If this is not an I/O request, there's no need to check against * the locks on the file. */ if (op == NBL_REMOVE || op == NBL_RENAME) return (0); return (nbl_lock_conflict(vp, op, offset, length, svmand, ct)); } /* * Determine if the given file has mode bits for System V mandatory locks. * If there was an error, the errno value is returned. Otherwise, zero is * returned and *svp is set appropriately (non-zero for mandatory locks, * zero for no mandatory locks). */ int nbl_svmand(vnode_t *vp, cred_t *cr, int *svp) { struct vattr va; int error; va.va_mask = AT_MODE; error = VOP_GETATTR(vp, &va, 0, cr, NULL); if (error != 0) return (error); *svp = MANDLOCK(vp, va.va_mode); return (0); } /* * The kernel handles this for us when we actually try I/O. */ /* ARGSUSED */ int nbl_share_conflict(vnode_t *vp, nbl_op_t op, caller_context_t *ct) { return (0); } /* * The kernel handles this for us when we actually try I/O. */ /* ARGSUSED */ int nbl_lock_conflict(vnode_t *vp, nbl_op_t op, u_offset_t offset, ssize_t length, int svmand, caller_context_t *ct) { return (0); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ /* * Some minimal streams mblk_t management functions needed by * ksocket_sendmblk: esballoca(), freemsg(), ... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void lastfree(mblk_t *mp, dblk_t *db) { frtn_t *frp = db->db_frtnp; ASSERT(db->db_mblk == mp); ASSERT(mp->b_datap == db); ASSERT(frp != NULL); frp->free_func(frp->free_arg); kmem_free(mp, sizeof (*mp)); kmem_free(db, sizeof (*db)); } mblk_t * esballoca(unsigned char *base, size_t size, uint_t pri, frtn_t *frp) { dblk_t *db; mblk_t *mp; db = kmem_zalloc(sizeof (*db), KM_SLEEP); mp = kmem_zalloc(sizeof (*mp), KM_SLEEP); mp->b_datap = db; db->db_mblk = mp; db->db_base = base; db->db_lim = base + size; db->db_free = db->db_lastfree = lastfree; db->db_frtnp = frp; /* * streams.c uses these weird macro: * DBLK_RTFU_WORD(dbp) = db_rtfu * where db_rtfu = DBLK_RTFU(1, M_DATA, 0, 0) * Probably only care about db_ref */ db->db_ref = 1; mp->b_next = mp->b_prev = mp->b_cont = NULL; mp->b_rptr = mp->b_wptr = base; mp->b_queue = NULL; return (mp); } /* * Same as esballoca() but sleeps waiting for memory. * (in here, both sleep) */ mblk_t * esballoca_wait(unsigned char *base, size_t size, uint_t pri, frtn_t *frp) { return (esballoca(base, size, pri, frp)); } void freemsg(mblk_t *mp) { mblk_t *mp_cont; dblk_t *db; while (mp) { db = mp->b_datap; mp_cont = mp->b_cont; ASSERT(db->db_ref > 0); ASSERT(mp->b_next == NULL && mp->b_prev == NULL); db->db_free(mp, db); mp = mp_cont; } } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2016 Nexenta Systems, Inc. All rights reserved. * Copyright 2024 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vncache.h" #define VFTBITS(feature) ((feature) & 0xFFFFFFFFLL) static uint64_t vfs_features = VFSFT_XVATTR; vnode_t *rootdir = NULL; /* pointer to root inode vnode. */ static struct vfs fake_rootvfs; static struct vfsops fake_vfsops; struct vfs *rootvfs = NULL; static struct vfssw fake_vfssw = { .vsw_name = "fake" /* see smb_tree.c:smb_mtype[] */ }; int fksmbsrv_vfs_init(void) { struct stat st; int err, fd; vnode_t *vp; char *name = "/"; (void) vncache_init(); if (rootvfs == NULL) { rootvfs = &fake_rootvfs; rootvfs->vfs_mntpt = refstr_alloc(name); rootvfs->vfs_fsid.val[0] = 1; rootvfs->vfs_op = &fake_vfsops; } if (rootdir == NULL) { if (lstat(name, &st) == -1) return (errno); fd = open(name, O_RDONLY, 0); if (fd < 0) { return (errno); } if (fstat(fd, &st) == -1) { err = errno; (void) close(fd); return (err); } vp = vncache_enter(&st, NULL, name, fd); /* give this hold to rootdir */ rootdir = vp; /* VFS stuff in global zone struct. */ zone0.zone_rootvp = rootdir; zone0.zone_rootpath = "/"; } return (0); } void fksmbsrv_vfs_fini(void) { if (rootdir != NULL) { VN_RELE(rootdir); rootdir = NULL; } vncache_fini(); } /* * Query a vfs for a feature. * Returns 1 if feature is present, 0 if not */ /* ARGSUSED */ int vfs_has_feature(vfs_t *vfsp, vfs_feature_t feature) { int ret = 0; if (vfs_features & VFTBITS(feature)) ret = 1; return (ret); } /* ARGSUSED */ struct vfs * getvfs(fsid_t *fsid) { return (rootvfs); } vfsops_t * vfs_getops(vfs_t *vfsp) { return (vfsp->vfs_op); } /* ARGSUSED */ struct vfssw * vfs_getvfsswbyvfsops(vfsops_t *vfsops) { if (vfsops == &fake_vfsops) return (&fake_vfssw); return (NULL); } /* ARGSUSED */ void vfs_unrefvfssw(struct vfssw *vswp) { } /* ARGSUSED */ int fsop_root(vfs_t *vfsp, vnode_t **vpp) { vnode_t *vp; if ((vp = rootdir) == NULL) return (ENXIO); VN_HOLD(vp); *vpp = vp; return (0); } /* ARGSUSED */ int fsop_statfs(vfs_t *vfsp, statvfs64_t *sp) { vnode_t *vp; int fd; int rc; if ((vp = rootdir) == NULL) return (ENXIO); fd = vncache_getfd(vp); rc = fstatvfs64(fd, sp); if (rc == -1) { rc = errno; } return (rc); } refstr_t * vfs_getmntpoint(const struct vfs *vfsp) { refstr_t *mntpt; mntpt = vfsp->vfs_mntpt; refstr_hold(mntpt); return (mntpt); } /* ARGSUSED */ void vfs_hold(vfs_t *vfsp) { } /* ARGSUSED */ void vfs_rele(vfs_t *vfsp) { } /* ARGSUSED */ int vfs_lock(vfs_t *vfsp) { return (0); } /* ARGSUSED */ int vfs_rlock(vfs_t *vfsp) { return (0); } /* ARGSUSED */ void vfs_lock_wait(vfs_t *vfsp) { } /* ARGSUSED */ void vfs_rlock_wait(vfs_t *vfsp) { } /* ARGSUSED */ void vfs_unlock(vfs_t *vfsp) { } static u_longlong_t fs_caller_id; u_longlong_t fs_new_caller_id(void) { return (++fs_caller_id); } static sysid_t lm_sysid; sysid_t lm_alloc_sysidt(void) { return (++lm_sysid); } /* ARGSUSED */ void lm_free_sysidt(sysid_t id) { } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. * Copyright 2019-2024 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Are vp1 and vp2 the same vnode? */ int vn_compare(vnode_t *vp1, vnode_t *vp2) { vnode_t *realvp; if (vp1 != NULL && VOP_REALVP(vp1, &realvp, NULL) == 0) vp1 = realvp; if (vp2 != NULL && VOP_REALVP(vp2, &realvp, NULL) == 0) vp2 = realvp; return (VN_CMP(vp1, vp2)); } vnodeops_t * vn_getops(vnode_t *vp) { return (vp->v_op); } int vn_ismntpt(vnode_t *vp __unused) { return (0); } vfs_t * vn_mountedvfs(vnode_t *vp __unused) { return (NULL); } void xva_init(xvattr_t *xvap) { bzero(xvap, sizeof (xvattr_t)); xvap->xva_mapsize = XVA_MAPSIZE; xvap->xva_magic = XVA_MAGIC; xvap->xva_vattr.va_mask = AT_XVATTR; xvap->xva_rtnattrmapp = &(xvap->xva_rtnattrmap)[0]; } /* * If AT_XVATTR is set, returns a pointer to the embedded xoptattr_t * structure. Otherwise, returns NULL. */ xoptattr_t * xva_getxoptattr(xvattr_t *xvap) { xoptattr_t *xoap = NULL; if (xvap->xva_vattr.va_mask & AT_XVATTR) xoap = &xvap->xva_xoptattrs; return (xoap); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. * Copyright 2024 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vncache.h" #define O_RWMASK (O_WRONLY | O_RDWR) /* == 3 */ int fop_shrlock_enable = 0; int stat_to_vattr(const struct stat *, vattr_t *); int fop__getxvattr(vnode_t *, xvattr_t *); int fop__setxvattr(vnode_t *, xvattr_t *); static void fake_inactive_xattrdir(vnode_t *); typedef struct fake_xuio { off_t map_foff; // file offset at start of mapping char *map_addr; // mapped address size_t map_len; // length of mapping iovec_t iovec[2]; } fake_xuio_t; int fake_xuio_blksz = 4096; /* ARGSUSED */ int fop_open( vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct) { if ((*vpp)->v_type == VREG) { if (mode & FREAD) atomic_add_32(&((*vpp)->v_rdcnt), 1); if (mode & FWRITE) atomic_add_32(&((*vpp)->v_wrcnt), 1); } /* call to ->vop_open was here */ return (0); } /* ARGSUSED */ int fop_close( vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, caller_context_t *ct) { /* call to ->vop_close was here */ /* * Check passed in count to handle possible dups. Vnode counts are only * kept on regular files */ if ((vp->v_type == VREG) && (count == 1)) { if (flag & FREAD) { ASSERT(vp->v_rdcnt > 0); atomic_add_32(&(vp->v_rdcnt), -1); } if (flag & FWRITE) { ASSERT(vp->v_wrcnt > 0); atomic_add_32(&(vp->v_wrcnt), -1); } } return (0); } /* ARGSUSED */ int fop_read( vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct) { struct stat st; struct iovec *iov; ssize_t resid; size_t cnt; int n; int fd = vncache_getfd(vp); /* * If that caller asks for read beyond end of file, * that causes the pread call to block. (Ugh!) * Get the file size and return what we can. */ (void) fstat(fd, &st); resid = uio->uio_resid; if ((uio->uio_loffset + resid) > st.st_size) resid = st.st_size - uio->uio_loffset; if (resid == 0) return (0); /* * Simulating zero-copy support with mmap. See: * fop_reqzcbuf(), fop_retzcbuf() */ if ((uio->uio_extflg == UIO_XUIO) && (((xuio_t *)uio)->xu_type == UIOTYPE_ZEROCOPY)) { xuio_t *xuio = (xuio_t *)uio; int poff; fake_xuio_t *priv = XUIO_XUZC_PRIV(xuio); /* * Sanity check mapped range overlaps this I/O: * uio_offset >= mapped base * uio_resid <= (mapped length - page offset) */ if (uio->uio_loffset < priv->map_foff) return (EINVAL); poff = uio->uio_loffset - priv->map_foff; if ((uio->uio_resid + poff) > priv->map_len) return (EINVAL); /* * Setup the uio with our loaned buffers, * and update offset, resid. */ uio->uio_iovcnt = 1; uio->uio_iov = &priv->iovec[0]; iov = uio->uio_iov; iov->iov_base = priv->map_addr + poff; iov->iov_len = priv->map_len - poff; uio->uio_loffset += iov->iov_len; uio->uio_resid -= iov->iov_len; return (0); } while (resid > 0) { ASSERT(uio->uio_iovcnt > 0); iov = uio->uio_iov; if (iov->iov_len == 0) { uio->uio_iov++; uio->uio_iovcnt--; continue; } cnt = iov->iov_len; if (cnt > resid) cnt = resid; n = pread(fd, iov->iov_base, cnt, uio->uio_loffset); if (n < 0) return (errno); iov->iov_base += n; iov->iov_len -= n; uio->uio_resid -= n; uio->uio_loffset += n; resid -= n; } return (0); } /* ARGSUSED */ int fop_write( vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct) { struct iovec *iov; size_t cnt; int n; int fd = vncache_getfd(vp); while (uio->uio_resid > 0) { ASSERT(uio->uio_iovcnt > 0); iov = uio->uio_iov; if (iov->iov_len == 0) { uio->uio_iov++; uio->uio_iovcnt--; continue; } cnt = iov->iov_len; if (cnt > uio->uio_resid) cnt = uio->uio_resid; n = pwrite(fd, iov->iov_base, iov->iov_len, uio->uio_loffset); if (n < 0) return (errno); iov->iov_base += n; iov->iov_len -= n; uio->uio_resid -= n; uio->uio_loffset += n; } if (ioflag == FSYNC) { (void) fsync(fd); } return (0); } /* ARGSUSED */ int fop_ioctl( vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp, caller_context_t *ct) { off64_t off; int rv, whence; int fd = vncache_getfd(vp); switch (cmd) { case _FIO_SEEK_DATA: case _FIO_SEEK_HOLE: whence = (cmd == _FIO_SEEK_DATA) ? SEEK_DATA : SEEK_HOLE; bcopy((void *)arg, &off, sizeof (off)); off = lseek(fd, off, whence); if (off == (off64_t)-1) { rv = errno; } else { bcopy(&off, (void *)arg, sizeof (off)); rv = 0; } break; default: rv = ENOTTY; break; } return (rv); } /* ARGSUSED */ int fop_setfl( vnode_t *vp, int oflags, int nflags, cred_t *cr, caller_context_t *ct) { /* allow any flags? See fs_setfl */ return (0); } /* ARGSUSED */ int fop_getattr( vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, caller_context_t *ct) { struct stat st; int error; int fd = vncache_getfd(vp); if (fstat(fd, &st) == -1) return (errno); error = stat_to_vattr(&st, vap); if (vap->va_mask & AT_XVATTR) (void) fop__getxvattr(vp, (xvattr_t *)vap); return (error); } /* ARGSUSED */ int fop_setattr( vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, caller_context_t *ct) { timespec_t times[2]; int err; int fd = vncache_getfd(vp); if (vap->va_mask & AT_SIZE) { if (ftruncate(fd, vap->va_size) == -1) { err = errno; if (err == EBADF) err = EACCES; return (err); } } /* AT_MODE or anything else? */ if (vap->va_mask & AT_XVATTR) (void) fop__setxvattr(vp, (xvattr_t *)vap); if (vap->va_mask & (AT_ATIME | AT_MTIME)) { if (vap->va_mask & AT_ATIME) { times[0] = vap->va_atime; } else { times[0].tv_sec = 0; times[0].tv_nsec = UTIME_OMIT; } if (vap->va_mask & AT_MTIME) { times[1] = vap->va_mtime; } else { times[1].tv_sec = 0; times[1].tv_nsec = UTIME_OMIT; } (void) futimens(fd, times); } return (0); } /* ARGSUSED */ int fop_access( vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) { return (0); } /* * Conceptually like xattr_dir_lookup() * * Once we've looked up the XATTRDIR for some vp, we keep it in * v_xattrdir until this vp goes inactive. See: vncache_inactive() */ static int fake_lookup_xattrdir( vnode_t *dvp, vnode_t **vpp) { int len, fd; int omode = O_RDWR | O_NOFOLLOW; vnode_t *vp; int dfd = vncache_getfd(dvp); *vpp = NULL; if (dvp->v_type != VDIR && dvp->v_type != VREG) return (EINVAL); /* * If we're already in sysattr space, don't allow creation * of another level of sysattrs. */ if (dvp->v_flag & V_SYSATTR) return (EINVAL); /* * We may already have the XATTR dir. */ mutex_enter(&dvp->v_lock); if (dvp->v_xattrdir != NULL) { *vpp = dvp->v_xattrdir; VN_HOLD(*vpp); mutex_exit(&dvp->v_lock); return (0); } mutex_exit(&dvp->v_lock); /* * Need to "create" the XATTR dir vnode. */ omode = O_RDONLY|O_XATTR; fd = openat(dfd, ".", omode); if (fd < 0) return (errno); /* * Normally vn_alloc() is called by vncache_enter(), but * we don't enter the special xattr dir into the cache. * These are only found via the parent's v_xattrdir field. */ vp = vn_alloc(KM_SLEEP); vncache_setfd(vp, fd); vp->v_flag = V_XATTRDIR|V_SYSATTR; vp->v_type = VDIR; vp->v_vfsp = dvp->v_vfsp; /* Set v_path to parent path + "/@" (like NFS) */ len = strlen(dvp->v_path) + 3; vp->v_path = kmem_alloc(len, KM_SLEEP); (void) snprintf(vp->v_path, len, "%s/@", dvp->v_path); mutex_enter(&dvp->v_lock); if (dvp->v_xattrdir == NULL) { dvp->v_xattrdir = vp; vp = NULL; } *vpp = dvp->v_xattrdir; VN_HOLD(*vpp); mutex_exit(&dvp->v_lock); if (vp != NULL) { /* Lost race filling in v_xattrdir */ fake_inactive_xattrdir(vp); } return (0); } /* ARGSUSED */ int fop_lookup( vnode_t *dvp, char *name, vnode_t **vpp, pathname_t *pnp, int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, int *deflags, /* Returned per-dirent flags */ pathname_t *ppnp) /* Returned case-preserved name in directory */ { int err, fd; int omode = O_RDWR | O_NOFOLLOW; vnode_t *vp; struct stat st; int dfd = vncache_getfd(dvp); if (flags & LOOKUP_XATTR) return (fake_lookup_xattrdir(dvp, vpp)); /* * If lookup is for "", just return dvp. */ if (name[0] == '\0') { VN_HOLD(dvp); *vpp = dvp; return (0); } if (fstatat(dfd, name, &st, AT_SYMLINK_NOFOLLOW) == -1) return (errno); vp = vncache_lookup(&st); if (vp != NULL) { /* lookup gave us a hold */ *vpp = vp; return (0); } if (S_ISDIR(st.st_mode)) omode = O_RDONLY | O_NOFOLLOW; again: err = 0; fd = openat(dfd, name, omode, 0); if (fd < 0) err = errno; DTRACE_PROBE3(openat, int, dfd, char *, name, int, err); if (err != 0) { if ((omode & O_RWMASK) == O_RDWR) { omode &= ~O_RWMASK; omode |= O_RDONLY; goto again; } return (err); } if (fstat(fd, &st) == -1) { (void) close(fd); return (errno); } vp = vncache_enter(&st, dvp, name, fd); *vpp = vp; return (0); } /* ARGSUSED */ int fop_create( vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flags, caller_context_t *ct, vsecattr_t *vsecp) /* ACL to set during create */ { struct stat st; vnode_t *vp; int err, fd, omode; int dfd = vncache_getfd(dvp); /* * If creating "", just return dvp. */ if (name[0] == '\0') { VN_HOLD(dvp); *vpp = dvp; return (0); } err = fstatat(dfd, name, &st, AT_SYMLINK_NOFOLLOW); if (err != 0) err = errno; vp = NULL; if (err == 0) { /* The file already exists. */ if (excl == EXCL) return (EEXIST); vp = vncache_lookup(&st); /* vp gained a hold */ } if (vp == NULL) { /* * Open it. (may or may not exist) */ omode = O_RDWR | O_CREAT | O_NOFOLLOW; if (excl == EXCL) omode |= O_EXCL; open_again: err = 0; fd = openat(dfd, name, omode, mode); if (fd < 0) err = errno; DTRACE_PROBE3(openat, int, dfd, char *, name, int, err); if (err != 0) { if ((omode & O_RWMASK) == O_RDWR) { omode &= ~O_RWMASK; omode |= O_RDONLY; goto open_again; } return (err); } (void) fstat(fd, &st); vp = vncache_enter(&st, dvp, name, fd); /* vp has its initial hold */ } /* Should have the vp now. */ if (vp == NULL) return (EFAULT); if (vp->v_type == VDIR && vap->va_type != VDIR) { vn_rele(vp); return (EISDIR); } if (vp->v_type != VDIR && vap->va_type == VDIR) { vn_rele(vp); return (ENOTDIR); } /* * Might need to set attributes. */ (void) fop_setattr(vp, vap, 0, cr, ct); *vpp = vp; return (0); } /* ARGSUSED */ int fop_remove( vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct, int flags) { int dfd = vncache_getfd(dvp); if (unlinkat(dfd, name, 0)) return (errno); return (0); } /* ARGSUSED */ int fop_link( vnode_t *to_dvp, vnode_t *fr_vp, char *to_name, cred_t *cr, caller_context_t *ct, int flags) { int to_dfd = vncache_getfd(to_dvp); int err; /* * Would prefer to specify "from" as the combination: * (fr_vp, NULL) but linkat does not permit it. */ err = linkat(AT_FDCWD, fr_vp->v_path, to_dfd, to_name, AT_SYMLINK_FOLLOW); if (err == -1) err = errno; return (err); } /* ARGSUSED */ int fop_rename( vnode_t *from_dvp, char *from_name, vnode_t *to_dvp, char *to_name, cred_t *cr, caller_context_t *ct, int flags) { struct stat st; vnode_t *vp; int err; int from_dfd = vncache_getfd(from_dvp); int to_dfd = vncache_getfd(to_dvp); if (fstatat(from_dfd, from_name, &st, AT_SYMLINK_NOFOLLOW) == -1) return (errno); vp = vncache_lookup(&st); if (vp == NULL) return (ENOENT); err = renameat(from_dfd, from_name, to_dfd, to_name); if (err == -1) err = errno; else vncache_renamed(vp, to_dvp, to_name); vn_rele(vp); return (err); } /* ARGSUSED */ int fop_mkdir( vnode_t *dvp, char *name, vattr_t *vap, vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp) /* ACL to set during create */ { struct stat st; int err, fd; int dfd = vncache_getfd(dvp); mode_t mode = vap->va_mode & 0777; if (mkdirat(dfd, name, mode) == -1) return (errno); err = 0; fd = openat(dfd, name, O_RDONLY); if (fd < 0) err = errno; DTRACE_PROBE3(openat, int, dfd, char *, name, int, err); if (err != 0) return (err); if (fstat(fd, &st) == -1) { err = errno; (void) close(fd); return (err); } *vpp = vncache_enter(&st, dvp, name, fd); /* * Might need to set attributes. */ (void) fop_setattr(*vpp, vap, 0, cr, ct); return (0); } /* ARGSUSED */ int fop_rmdir( vnode_t *dvp, char *name, vnode_t *cdir, cred_t *cr, caller_context_t *ct, int flags) { int dfd = vncache_getfd(dvp); if (unlinkat(dfd, name, AT_REMOVEDIR) == -1) return (errno); return (0); } /* ARGSUSED */ int fop_readdir( vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct, int flags) { struct iovec *iov; int cnt; int error = 0; int fd = vncache_getfd(vp); if (eofp) { *eofp = 0; } error = lseek(fd, uiop->uio_loffset, SEEK_SET); if (error == -1) return (errno); ASSERT(uiop->uio_iovcnt > 0); iov = uiop->uio_iov; if (iov->iov_len < sizeof (struct dirent)) return (EINVAL); /* LINTED E_BAD_PTR_CAST_ALIGN */ cnt = getdents(fd, (struct dirent *)(uiop->uio_iov->iov_base), uiop->uio_resid); if (cnt == -1) return (errno); if (cnt == 0) { if (eofp) { *eofp = 1; } return (ENOENT); } iov->iov_base += cnt; iov->iov_len -= cnt; uiop->uio_resid -= cnt; uiop->uio_loffset = lseek(fd, 0LL, SEEK_CUR); return (0); } /* ARGSUSED */ int fop_symlink( vnode_t *dvp, char *linkname, vattr_t *vap, char *target, cred_t *cr, caller_context_t *ct, int flags) { return (ENOSYS); } /* ARGSUSED */ int fop_readlink( vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_fsync( vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { int fd = vncache_getfd(vp); if (fsync(fd) == -1) return (errno); return (0); } /* ARGSUSED */ void fop_inactive( vnode_t *vp, cred_t *cr, caller_context_t *ct) { if (vp->v_flag & V_XATTRDIR) { fake_inactive_xattrdir(vp); } else { vncache_inactive(vp); } } /* * The special xattr directories are not in the vncache AVL, but * hang off the parent's v_xattrdir field. When vn_rele finds * an xattr dir at v_count == 1 it calls here via fop_inactive(). */ static void fake_inactive_xattrdir(vnode_t *vp) { mutex_enter(&vp->v_lock); if (vp->v_count > 1) { /* new ref. via v_xattrdir */ mutex_exit(&vp->v_lock); return; } mutex_exit(&vp->v_lock); vn_free(vp); } /* ARGSUSED */ int fop_fid( vnode_t *vp, fid_t *fidp, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_rwlock( vnode_t *vp, int write_lock, caller_context_t *ct) { /* See: fs_rwlock */ return (-1); } /* ARGSUSED */ void fop_rwunlock( vnode_t *vp, int write_lock, caller_context_t *ct) { /* See: fs_rwunlock */ } /* ARGSUSED */ int fop_seek( vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_cmp( vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) { /* See fs_cmp */ return (vncache_cmp(vp1, vp2)); } /* ARGSUSED */ int fop_frlock( vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset, struct flk_callback *flk_cbp, cred_t *cr, caller_context_t *ct) { #if defined(_LP64) offset_t maxoffset = INT64_MAX; #elif defined(_ILP32) /* * Sadly, the fcntl API enforces 32-bit offsets, * even though we have _FILE_OFFSET_BITS=64 */ offset_t maxoffset = INT32_MAX; #else #error "unsupported env." #endif int fd = vncache_getfd(vp); /* See fs_frlock */ switch (cmd) { case F_GETLK: case F_SETLK_NBMAND: case F_SETLK: case F_SETLKW: break; default: return (EINVAL); } /* We only get SEEK_SET ranges here. */ if (bfp->l_whence != 0) return (EINVAL); /* * One limitation of using fcntl(2) F_SETLK etc is that * the real kernel limits the offsets we can use. * (Maybe the fcntl API should loosen that up?) * See syscall/fcntl.c:flock_check() * * Here in libfksmbsrv we can just ignore such locks, * or ignore the part that extends beyond maxoffset. * The SMB layer still keeps track of such locks for * conflict detection, so not reflecting such locks * into the real FS layer is OK. Note: this may * modify the pased bfp->l_len. */ if (bfp->l_start < 0 || bfp->l_start > maxoffset) return (0); if (bfp->l_len < 0 || bfp->l_len > maxoffset) return (0); if (bfp->l_len > (maxoffset - bfp->l_start + 1)) bfp->l_len = (maxoffset - bfp->l_start + 1); if (fcntl(fd, cmd, bfp) == -1) return (errno); return (0); } /* ARGSUSED */ int fop_space( vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset, cred_t *cr, caller_context_t *ct) { int fd = vncache_getfd(vp); /* See fs_frlock */ switch (cmd) { case F_ALLOCSP: case F_FREESP: break; default: return (EINVAL); } if (fcntl(fd, cmd, bfp) == -1) return (errno); return (0); } /* ARGSUSED */ int fop_realvp( vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_getpage( vnode_t *vp, offset_t off, size_t len, uint_t *protp, struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr, enum seg_rw rw, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_putpage( vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_map( vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_addmap( vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_delmap( vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_poll( vnode_t *vp, short events, int anyyet, short *reventsp, struct pollhead **phpp, caller_context_t *ct) { *reventsp = 0; if (events & POLLIN) *reventsp |= POLLIN; if (events & POLLRDNORM) *reventsp |= POLLRDNORM; if (events & POLLRDBAND) *reventsp |= POLLRDBAND; if (events & POLLOUT) *reventsp |= POLLOUT; if (events & POLLWRBAND) *reventsp |= POLLWRBAND; *phpp = NULL; /* or fake_pollhead? */ return (0); } /* ARGSUSED */ int fop_dump( vnode_t *vp, caddr_t addr, offset_t lbdn, offset_t dblks, caller_context_t *ct) { return (ENOSYS); } /* * See fs_pathconf */ /* ARGSUSED */ int fop_pathconf( vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, caller_context_t *ct) { register ulong_t val; register int error = 0; switch (cmd) { case _PC_LINK_MAX: val = MAXLINK; break; case _PC_MAX_CANON: val = MAX_CANON; break; case _PC_MAX_INPUT: val = MAX_INPUT; break; case _PC_NAME_MAX: val = MAXNAMELEN; break; case _PC_PATH_MAX: case _PC_SYMLINK_MAX: val = MAXPATHLEN; break; case _PC_PIPE_BUF: val = PIPE_BUF; break; case _PC_NO_TRUNC: val = (ulong_t)-1; break; case _PC_VDISABLE: val = _POSIX_VDISABLE; break; case _PC_CHOWN_RESTRICTED: val = 1; /* chown restricted enabled */ break; case _PC_FILESIZEBITS: val = (ulong_t)-1; /* large file support */ break; case _PC_ACL_ENABLED: val = _ACL_ACE_ENABLED; break; case _PC_CASE_BEHAVIOR: val = _CASE_SENSITIVE; break; case _PC_SATTR_ENABLED: case _PC_SATTR_EXISTS: val = 0; break; case _PC_ACCESS_FILTERING: val = 0; break; default: error = EINVAL; break; } if (error == 0) *valp = val; return (error); } /* ARGSUSED */ int fop_pageio( vnode_t *vp, struct page *pp, u_offset_t io_off, size_t io_len, int flags, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_dumpctl( vnode_t *vp, int action, offset_t *blkp, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ void fop_dispose( vnode_t *vp, struct page *pp, int flag, int dn, cred_t *cr, caller_context_t *ct) { } /* ARGSUSED */ int fop_setsecattr( vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr, caller_context_t *ct) { return (0); } /* * Fake up just enough of this so we can test get/set SDs. */ /* ARGSUSED */ int fop_getsecattr( vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, caller_context_t *ct) { vsecattr->vsa_aclcnt = 0; vsecattr->vsa_aclentsz = 0; vsecattr->vsa_aclentp = NULL; vsecattr->vsa_dfaclcnt = 0; /* Default ACLs are not fabricated */ vsecattr->vsa_dfaclentp = NULL; if (vsecattr->vsa_mask & (VSA_ACLCNT | VSA_ACL)) { aclent_t *aclentp; size_t aclsize; aclsize = sizeof (aclent_t); vsecattr->vsa_aclcnt = 1; vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP); aclentp = vsecattr->vsa_aclentp; aclentp->a_type = OTHER_OBJ; aclentp->a_perm = 0777; aclentp->a_id = (gid_t)-1; aclentp++; } else if (vsecattr->vsa_mask & (VSA_ACECNT | VSA_ACE)) { ace_t *acl; acl = kmem_alloc(sizeof (ace_t), KM_SLEEP); acl->a_who = (uint32_t)-1; acl->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; acl->a_flags = ACE_EVERYONE; acl->a_access_mask = ACE_MODIFY_PERMS; vsecattr->vsa_aclentp = (void *)acl; vsecattr->vsa_aclcnt = 1; vsecattr->vsa_aclentsz = sizeof (ace_t); } return (0); } /* ARGSUSED */ int fop_shrlock( vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr, caller_context_t *ct) { int fd = vncache_getfd(vp); switch (cmd) { case F_SHARE: case F_SHARE_NBMAND: case F_UNSHARE: break; default: return (EINVAL); } if (!fop_shrlock_enable) return (0); if (fcntl(fd, cmd, shr) == -1) return (errno); return (0); } /* ARGSUSED */ int fop_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *fnm, caller_context_t *ct) { return (ENOSYS); } /* ARGSUSED */ int fop_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr, caller_context_t *ct) { fake_xuio_t *priv; uio_t *uio = &xuio->xu_uio; int blksz = fake_xuio_blksz; off_t foff, moff; size_t flen, mlen; int poff; char *ma; struct stat st; int fd = vncache_getfd(vp); if (xuio->xu_type != UIOTYPE_ZEROCOPY) return (EINVAL); foff = uio->uio_loffset; flen = uio->uio_resid; if (fstat(fd, &st) == -1) return (errno); if (foff >= st.st_size) return (EINVAL); if ((foff + flen) > st.st_size) flen = st.st_size - foff; switch (ioflag) { case UIO_READ: if (flen < blksz/2) return (EINVAL); break; case UIO_WRITE: default: return (EINVAL); } /* * See if we can map the file for read. * Round down start offset for mmap. */ poff = P2PHASE((int)foff, blksz); moff = foff - poff; mlen = flen + poff; ma = mmap(NULL, mlen, PROT_READ, MAP_SHARED, fd, moff); if (ma == MAP_FAILED) { /* Can't use loaned buffers. */ return (EINVAL); } priv = kmem_zalloc(sizeof (*priv), KM_SLEEP); priv->map_foff = foff; priv->map_addr = ma; priv->map_len = mlen; XUIO_XUZC_PRIV(xuio) = priv; XUIO_XUZC_RW(xuio) = ioflag; uio->uio_extflg = UIO_XUIO; return (0); } /* ARGSUSED */ int fop_retzcbuf(vnode_t *vp, xuio_t *xuio, cred_t *cr, caller_context_t *ct) { fake_xuio_t *priv = XUIO_XUZC_PRIV(xuio); int ioflag = XUIO_XUZC_RW(xuio); ASSERT(xuio->xu_type == UIOTYPE_ZEROCOPY); ASSERT(ioflag == UIO_READ); munmap(priv->map_addr, priv->map_len); kmem_free(priv, sizeof (fake_xuio_t)); XUIO_XUZC_PRIV(xuio) = NULL; return (0); } /* * *************************************************************** * other VOP support */ /* * Convert stat(2) formats to vnode types and vice versa. (Knows about * numerical order of S_IFMT and vnode types.) */ enum vtype iftovt_tab[] = { VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VNON }; ushort_t vttoif_tab[] = { 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFIFO, S_IFDOOR, 0, S_IFSOCK, S_IFPORT, 0 }; /* * stat_to_vattr() * * Convert from a stat structure to an vattr structure * Note: only set fields according to va_mask */ int stat_to_vattr(const struct stat *st, vattr_t *vap) { if (vap->va_mask & AT_TYPE) vap->va_type = IFTOVT(st->st_mode); if (vap->va_mask & AT_MODE) vap->va_mode = st->st_mode; if (vap->va_mask & AT_UID) vap->va_uid = st->st_uid; if (vap->va_mask & AT_GID) vap->va_gid = st->st_gid; if (vap->va_mask & AT_FSID) vap->va_fsid = st->st_dev; if (vap->va_mask & AT_NODEID) vap->va_nodeid = st->st_ino; if (vap->va_mask & AT_NLINK) vap->va_nlink = st->st_nlink; if (vap->va_mask & AT_SIZE) vap->va_size = (u_offset_t)st->st_size; if (vap->va_mask & AT_ATIME) { vap->va_atime.tv_sec = st->st_atim.tv_sec; vap->va_atime.tv_nsec = st->st_atim.tv_nsec; } if (vap->va_mask & AT_MTIME) { vap->va_mtime.tv_sec = st->st_mtim.tv_sec; vap->va_mtime.tv_nsec = st->st_mtim.tv_nsec; } if (vap->va_mask & AT_CTIME) { vap->va_ctime.tv_sec = st->st_ctim.tv_sec; vap->va_ctime.tv_nsec = st->st_ctim.tv_nsec; } if (vap->va_mask & AT_RDEV) vap->va_rdev = st->st_rdev; if (vap->va_mask & AT_BLKSIZE) vap->va_blksize = (uint_t)st->st_blksize; if (vap->va_mask & AT_NBLOCKS) vap->va_nblocks = (u_longlong_t)st->st_blocks; if (vap->va_mask & AT_SEQ) vap->va_seq = 0; return (0); } /* ARGSUSED */ void flk_init_callback(flk_callback_t *flk_cb, callb_cpr_t *(*cb_fcn)(flk_cb_when_t, void *), void *cbdata) { } /* See: VN_HOLD / VN_RELE */ void vn_rele(vnode_t *vp) { VERIFY3U(vp->v_count, !=, 0); mutex_enter(&vp->v_lock); if (vp->v_count == 1) { mutex_exit(&vp->v_lock); fop_inactive(vp, NULL, NULL); } else { vp->v_count--; mutex_exit(&vp->v_lock); } } int vn_has_other_opens( vnode_t *vp, v_mode_t mode) { switch (mode) { case V_WRITE: if (vp->v_wrcnt > 1) return (V_TRUE); break; case V_RDORWR: if ((vp->v_rdcnt > 1) || (vp->v_wrcnt > 1)) return (V_TRUE); break; case V_RDANDWR: if ((vp->v_rdcnt > 1) && (vp->v_wrcnt > 1)) return (V_TRUE); break; case V_READ: if (vp->v_rdcnt > 1) return (V_TRUE); break; } return (V_FALSE); } /* * vn_is_opened() checks whether a particular file is opened and * whether the open is for read and/or write. * * Vnode counts are only kept on regular files (v_type=VREG). */ int vn_is_opened( vnode_t *vp, v_mode_t mode) { ASSERT(vp != NULL); switch (mode) { case V_WRITE: if (vp->v_wrcnt) return (V_TRUE); break; case V_RDANDWR: if (vp->v_rdcnt && vp->v_wrcnt) return (V_TRUE); break; case V_RDORWR: if (vp->v_rdcnt || vp->v_wrcnt) return (V_TRUE); break; case V_READ: if (vp->v_rdcnt) return (V_TRUE); break; } return (V_FALSE); } /* * vn_is_mapped() checks whether a particular file is mapped and whether * the file is mapped read and/or write. */ /* ARGSUSED */ int vn_is_mapped( vnode_t *vp, v_mode_t mode) { return (V_FALSE); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2024 RackTop Systems, Inc. */ #include #include #include #include #include #include "vncache.h" static uint64_t zero_times[2]; static int getxva_parse_nvl(xvattr_t *xvap, xoptattr_t *xoap, nvlist_t *nvl); /* * See similar code to parse the nvlist in: * uts/common/fs/xattr.c : xattr_file_write() */ int fop__getxvattr(vnode_t *vp, xvattr_t *xvap) { nvlist_t *nvl = NULL; xoptattr_t *xoap = NULL; int fd = vncache_getfd(vp); int error; if ((xoap = xva_getxoptattr(xvap)) == NULL) { return (EINVAL); } error = fgetattr(fd, XATTR_VIEW_READWRITE, &nvl); if (error == 0) { error = getxva_parse_nvl(xvap, xoap, nvl); nvlist_free(nvl); nvl = NULL; } /* * Also get the readonly attrs, but don't fail. */ if (fgetattr(fd, XATTR_VIEW_READONLY, &nvl) == 0) { (void) getxva_parse_nvl(xvap, xoap, nvl); nvlist_free(nvl); } return (error); } static int getxva_parse_nvl(xvattr_t *xvap, xoptattr_t *xoap, nvlist_t *nvl) { nvpair_t *pair = NULL; int error; while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { data_type_t type; f_attr_t attr; boolean_t value = B_FALSE; uint64_t *times = zero_times; uint_t nelems = 2; /* * Validate the name and type of each attribute. * Log any unknown names and continue. This will * help if additional attributes are added later. */ type = nvpair_type(pair); attr = name_to_attr(nvpair_name(pair)); if (attr == F_ATTR_INVAL) continue; /* * Verify nvlist type matches required type and view is OK */ if (type != attr_to_data_type(attr) || (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) continue; /* * For OWNERSID/GROUPSID, just skip. */ if ((attr == F_OWNERSID || attr == F_GROUPSID)) continue; /* * Retrieve data from nvpair */ switch (type) { case DATA_TYPE_BOOLEAN_VALUE: if (nvpair_value_boolean_value(pair, &value)) { error = EINVAL; goto out; } break; case DATA_TYPE_UINT64_ARRAY: if (nvpair_value_uint64_array(pair, ×, &nelems)) { error = EINVAL; goto out; } if (nelems < 2) continue; break; case DATA_TYPE_NVLIST: continue; case DATA_TYPE_UINT8_ARRAY: continue; default: error = EINVAL; goto out; } switch (attr) { /* * If we have several similar optional attributes to * process then we should do it all together here so that * xoap and the requested bitmap can be set in one place. */ case F_READONLY: XVA_SET_RTN(xvap, XAT_READONLY); xoap->xoa_readonly = value; break; case F_HIDDEN: XVA_SET_RTN(xvap, XAT_HIDDEN); xoap->xoa_hidden = value; break; case F_SYSTEM: XVA_SET_RTN(xvap, XAT_SYSTEM); xoap->xoa_system = value; break; case F_ARCHIVE: XVA_SET_RTN(xvap, XAT_ARCHIVE); xoap->xoa_archive = value; break; case F_CRTIME: XVA_SET_RTN(xvap, XAT_CREATETIME); xoap->xoa_createtime.tv_sec = times[0]; xoap->xoa_createtime.tv_nsec = times[1]; break; case F_REPARSE: XVA_SET_RTN(xvap, XAT_REPARSE); xoap->xoa_reparse = value; break; case F_OFFLINE: XVA_SET_RTN(xvap, XAT_OFFLINE); xoap->xoa_offline = value; break; case F_SPARSE: XVA_SET_RTN(xvap, XAT_SPARSE); xoap->xoa_sparse = value; break; default: break; } } error = 0; out: return (error); } /* * See similar code to build the nvlist in: * uts/common/fs/xattr.c : xattr_fill_nvlist() */ int fop__setxvattr(vnode_t *vp, xvattr_t *xvap) { uint64_t times[2]; nvlist_t *nvl; xoptattr_t *xoap; /* Pointer to optional attributes */ int fd = vncache_getfd(vp); int error; if ((xoap = xva_getxoptattr(xvap)) == NULL) return (EINVAL); if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) return (ENOMEM); if (XVA_ISSET_REQ(xvap, XAT_READONLY)) { VERIFY(nvlist_add_boolean_value(nvl, attr_to_name(F_READONLY), xoap->xoa_readonly) == 0); } if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) { VERIFY(nvlist_add_boolean_value(nvl, attr_to_name(F_HIDDEN), xoap->xoa_hidden) == 0); } if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) { VERIFY(nvlist_add_boolean_value(nvl, attr_to_name(F_SYSTEM), xoap->xoa_system) == 0); } if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) { VERIFY(nvlist_add_boolean_value(nvl, attr_to_name(F_ARCHIVE), xoap->xoa_archive) == 0); } if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) { times[0] = xoap->xoa_createtime.tv_sec; times[1] = xoap->xoa_createtime.tv_nsec; VERIFY(nvlist_add_uint64_array(nvl, attr_to_name(F_CRTIME), times, 2) == 0); } if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) { VERIFY(nvlist_add_boolean_value(nvl, attr_to_name(F_REPARSE), xoap->xoa_reparse) == 0); } if (XVA_ISSET_REQ(xvap, XAT_OFFLINE)) { VERIFY(nvlist_add_boolean_value(nvl, attr_to_name(F_OFFLINE), xoap->xoa_offline) == 0); } if (XVA_ISSET_REQ(xvap, XAT_SPARSE)) { VERIFY(nvlist_add_boolean_value(nvl, attr_to_name(F_SPARSE), xoap->xoa_sparse) == 0); } error = fsetattr(fd, XATTR_VIEW_READWRITE, nvl); nvlist_free(nvl); return (error); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include /* * Kind of a hack here. In this user-space test implementation, * we don't bother with real credential. Everything here uses * the ordinary credentials of the process running this. */ /* * This library does not implement real credentials. All contexts * use an opaque cred_t object, and all activity happens in the * context of the user who runs the program. */ cred_t * smb_cred_create(smb_token_t *token) { _NOTE(ARGUNUSED(token)) cred_t *cr; cr = CRED(); return (cr); } cred_t * smb_kcred_create(void) { cred_t *cr; cr = CRED(); return (cr); } void smb_user_setcred(smb_user_t *user, cred_t *cr, uint32_t privileges) { user->u_cred = cr; user->u_privcred = NULL; user->u_privileges = privileges; } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2017-2021 Tintri by DDN, Inc. All rights reserved. */ /* * See: DTRACE_PROBE... in ./sys/sdt.h */ provider fksmb { /* generic probes */ probe probe0(char *probename); probe probe1(char *probename, unsigned long arg1); probe probe2(char *probename, unsigned long arg1, unsigned long arg2); probe probe3(char *probename, unsigned long arg1, unsigned long arg2, unsigned long arg3); /* smb provider probes */ probe smb_start(char *probename, unsigned long arg1); probe smb_done(char *probename, unsigned long arg1); /* smb2 provider probes */ probe smb2_start(char *probename, unsigned long arg1); probe smb2_done(char *probename, unsigned long arg1); probe set__error(int err); }; #pragma D attributes Evolving/Evolving/ISA provider fksmb provider #pragma D attributes Private/Private/Unknown provider fksmb module #pragma D attributes Private/Private/Unknown provider fksmb function #pragma D attributes Evolving/Evolving/ISA provider fksmb name #pragma D attributes Evolving/Evolving/ISA provider fksmb args /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2018-2021 Tintri by DDN, Inc. All rights reserved. * Copyright 2021 RackTop Systems, Inc. */ /* * Helper functions for SMB3 encryption using PKCS#11 * * There are two implementations of these functions: * This one (for user space) and another for kernel. * See: uts/common/fs/smbsrv/smb3_encrypt_kcf.c * * Contrary to what one might assume from the file name, * there should be NO SMB implementation knowledge here * beyond a few carefully selected things (smb_kcrypt.h). */ #include #include #include #include #include #include #include /* * Common function to see if a mech is available. */ static int find_mech(CK_MECHANISM_TYPE id) { CK_SESSION_HANDLE hdl; CK_RV rv; rv = SUNW_C_GetMechSession(id, &hdl); if (rv != CKR_OK) { return (-1); } (void) C_CloseSession(hdl); return (0); } /* * SMB3 encryption helpers: * (getmech, init, update, final) */ int smb3_aes_ccm_getmech(smb_crypto_mech_t *mech) { if (find_mech(CKM_AES_CCM) != 0) { cmn_err(CE_NOTE, "PKCS#11: no mech AES_CCM"); return (-1); } mech->mechanism = CKM_AES_CCM; return (0); } int smb3_aes_gcm_getmech(smb_crypto_mech_t *mech) { if (find_mech(CKM_AES_GCM) != 0) { cmn_err(CE_NOTE, "PKCS#11: no mech CKM_AES_GCM"); return (-1); } mech->mechanism = CKM_AES_GCM; return (0); } void smb3_crypto_init_ccm_param(smb_enc_ctx_t *ctx, uint8_t *nonce, size_t noncesize, uint8_t *auth, size_t authsize, size_t datasize) { ASSERT3U(noncesize, >=, SMB3_AES_CCM_NONCE_SIZE); /* CK_CCM_PARAMS */ ctx->param.ccm.ulDataLen = datasize; ctx->param.ccm.pNonce = nonce; ctx->param.ccm.ulNonceLen = SMB3_AES_CCM_NONCE_SIZE; ctx->param.ccm.pAAD = auth; ctx->param.ccm.ulAADLen = authsize; ctx->param.ccm.ulMACLen = SMB2_SIG_SIZE; ctx->mech.pParameter = (caddr_t)&ctx->param.ccm; ctx->mech.ulParameterLen = sizeof (ctx->param.ccm); } void smb3_crypto_init_gcm_param(smb_enc_ctx_t *ctx, uint8_t *nonce, size_t noncesize, uint8_t *auth, size_t authsize) { ASSERT3U(noncesize, >=, SMB3_AES_GCM_NONCE_SIZE); /* CK_GCM_PARAMS */ ctx->param.gcm.pIv = nonce; ctx->param.gcm.ulIvLen = SMB3_AES_GCM_NONCE_SIZE; ctx->param.gcm.pAAD = auth; /* auth data */ ctx->param.gcm.ulAADLen = authsize; /* auth data len */ ctx->param.gcm.ulTagBits = SMB2_SIG_SIZE << 3; /* bytes to bits */ ctx->mech.pParameter = (caddr_t)&ctx->param.gcm; ctx->mech.ulParameterLen = sizeof (ctx->param.gcm); } /* * Start the KCF encrypt session, load the key * If this returns zero, the caller should call * smb3_enc_ctx_done to cleanup the context, * even if there are intervening errors. */ int smb3_encrypt_init(smb_enc_ctx_t *ctxp, uint8_t *key, size_t keylen) { CK_OBJECT_HANDLE hkey = 0; CK_MECHANISM *mech = &ctxp->mech; CK_RV rv; rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx); if (rv != CKR_OK) return (-1); rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism, key, keylen, &hkey); if (rv != CKR_OK) return (-1); rv = C_EncryptInit(ctxp->ctx, mech, hkey); if (rv != CKR_OK) { cmn_err(CE_WARN, "C_EncryptInit failed: 0x%lx", rv); } (void) C_DestroyObject(ctxp->ctx, hkey); return (rv == CKR_OK ? 0 : -1); } /* * Start the KCF decrypt session, load the key * If this returns zero, the caller should call * smb3_enc_ctx_done to cleanup the context, * even if there are intervening errors. */ int smb3_decrypt_init(smb_enc_ctx_t *ctxp, uint8_t *key, size_t keylen) { CK_OBJECT_HANDLE hkey = 0; CK_MECHANISM *mech = &ctxp->mech; CK_RV rv; rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx); if (rv != CKR_OK) return (-1); rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism, key, keylen, &hkey); if (rv != CKR_OK) return (-1); rv = C_DecryptInit(ctxp->ctx, mech, hkey); if (rv != CKR_OK) { cmn_err(CE_WARN, "C_DecryptInit failed: 0x%lx", rv); } (void) C_DestroyObject(ctxp->ctx, hkey); return (rv == CKR_OK ? 0 : -1); } /* * Encrypt a whole message with scatter/gather (UIO) * * While the PKCS#11 implementation internally has the ability to * handle scatter/gather, it currently presents no interface for it. * As this library is used primarily for debugging, performance in * here is not a big concern, so we'll get around the limitation of * libpkcs11 by copying to/from a contiguous working buffer. */ int smb3_encrypt_uio(smb_enc_ctx_t *ctxp, uio_t *in, uio_t *out) { uint8_t *buf = NULL; size_t inlen, outlen; ulong_t tlen; int err, rc = -1; CK_RV rv; if (in->uio_resid <= 0) return (-1); inlen = in->uio_resid; outlen = inlen + 16; buf = malloc(outlen); if (buf == NULL) return (-1); /* Copy from uio segs to buf */ err = uiomove(buf, inlen, UIO_WRITE, in); if (err != 0) goto out; /* Encrypt in-place in our work buffer. */ tlen = outlen; rv = C_Encrypt(ctxp->ctx, buf, inlen, buf, &tlen); if (rv != CKR_OK) { cmn_err(CE_WARN, "C_Encrypt failed: 0x%lx", rv); goto out; } if (tlen != outlen) { cmn_err(CE_WARN, "smb3_encrypt_uio outlen %d vs %d", (int)tlen, (int)outlen); goto out; } /* Copy from buf to uio segs */ err = uiomove(buf, outlen, UIO_READ, out); if (err != 0) goto out; rc = 0; out: free(buf); return (rc); } int smb3_decrypt_uio(smb_enc_ctx_t *ctxp, uio_t *in, uio_t *out) { uint8_t *buf = NULL; size_t inlen, outlen; ulong_t tlen; int err, rc = -1; CK_RV rv; if (in->uio_resid <= 16) return (-1); inlen = in->uio_resid; outlen = inlen - 16; buf = malloc(inlen); if (buf == NULL) return (-1); /* Copy from uio segs to buf */ err = uiomove(buf, inlen, UIO_WRITE, in); if (err != 0) goto out; /* Decrypt in-place in our work buffer. */ tlen = outlen; rv = C_Decrypt(ctxp->ctx, buf, inlen, buf, &tlen); if (rv != CKR_OK) { cmn_err(CE_WARN, "C_Decrypt failed: 0x%lx", rv); goto out; } if (tlen != outlen) { cmn_err(CE_WARN, "smb3_decrypt_uio outlen %d vs %d", (int)tlen, (int)outlen); goto out; } /* Copy from buf to uio segs */ err = uiomove(buf, outlen, UIO_READ, out); if (err != 0) goto out; rc = 0; out: free(buf); return (rc); } void smb3_enc_ctx_done(smb_enc_ctx_t *ctxp) { if (ctxp->ctx != 0) { (void) C_CloseSession(ctxp->ctx); ctxp->ctx = 0; } } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #include #include #include /* * smb_fem_init */ int smb_fem_init(void) { return (0); } /* * smb_fem_fini */ void smb_fem_fini(void) { } /* ARGSUSED */ int smb_fem_fcn_install(smb_node_t *node) { return (0); } /* ARGSUSED */ int smb_fem_fcn_uninstall(smb_node_t *node) { return (0); } /* ARGSUSED */ int smb_fem_oplock_install(smb_node_t *node) { return (0); } /* ARGSUSED */ void smb_fem_oplock_uninstall(smb_node_t *node) { } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2018 Nexenta Systems, Inc. All rights reserved. * Copyright 2023 RackTop Systems, Inc. */ /* * SMB server interface to idmap * (smb_idmap_get..., smb_idmap_batch_...) * * There are three implementations of this interface. * This is the "fake kernel" version of these routines. See also: * $SRC/lib/smbsrv/libsmb/common/smb_idmap.c * $SRC/uts/common/fs/smbsrv/smb_idmap.c * * There are enough differences (relative to the code size) * that it's more trouble than it's worth to merge them. * * This one differs from the others in that it: * calls idmap interfaces (libidmap) * uses kmem_... interfaces (libfakekernel) * uses cmn_err instead of syslog, etc. * The code in this variant looks a lot like the one in libsmb. */ #include #include #include #include static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib); /* * Report an idmap error. */ void smb_idmap_check(const char *s, idmap_stat stat) { if (stat != IDMAP_SUCCESS) { if (s == NULL) s = "smb_idmap_check"; cmn_err(CE_NOTE, "%s: %d", s, (int)stat); } } /* * smb_idmap_getsid * * Tries to get a mapping for the given uid/gid * Allocates ->sim_domsid */ idmap_stat smb_idmap_getsid(uid_t id, int idtype, smb_sid_t **sid) { smb_idmap_batch_t sib; idmap_stat stat; stat = smb_idmap_batch_create(&sib, 1, SMB_IDMAP_ID2SID); if (stat != IDMAP_SUCCESS) return (stat); stat = smb_idmap_batch_getsid(sib.sib_idmaph, &sib.sib_maps[0], id, idtype); if (stat != IDMAP_SUCCESS) { smb_idmap_batch_destroy(&sib); return (stat); } /* Leave error reporting to the caller. */ stat = smb_idmap_batch_getmappings(&sib, NULL); if (stat != IDMAP_SUCCESS) { smb_idmap_batch_destroy(&sib); return (stat); } *sid = smb_sid_dup(sib.sib_maps[0].sim_sid); smb_idmap_batch_destroy(&sib); return (IDMAP_SUCCESS); } /* * smb_idmap_getid * * Tries to get a mapping for the given SID */ idmap_stat smb_idmap_getid(smb_sid_t *sid, uid_t *id, int *id_type) { smb_idmap_batch_t sib; smb_idmap_t *sim; idmap_stat stat; stat = smb_idmap_batch_create(&sib, 1, SMB_IDMAP_SID2ID); if (stat != IDMAP_SUCCESS) return (stat); sim = &sib.sib_maps[0]; sim->sim_id = id; stat = smb_idmap_batch_getid(sib.sib_idmaph, sim, sid, *id_type); if (stat != IDMAP_SUCCESS) { smb_idmap_batch_destroy(&sib); return (stat); } /* Leave error reporting to the caller. */ stat = smb_idmap_batch_getmappings(&sib, NULL); if (stat != IDMAP_SUCCESS) { smb_idmap_batch_destroy(&sib); return (stat); } *id_type = sim->sim_idtype; smb_idmap_batch_destroy(&sib); return (IDMAP_SUCCESS); } /* * smb_idmap_batch_create * * Creates and initializes the context for batch ID mapping. */ idmap_stat smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags) { idmap_stat stat; ASSERT(sib != NULL); bzero(sib, sizeof (smb_idmap_batch_t)); stat = idmap_get_create(&sib->sib_idmaph); if (stat != IDMAP_SUCCESS) { smb_idmap_check("idmap_get_create", stat); return (stat); } sib->sib_flags = flags; sib->sib_nmap = nmap; sib->sib_size = nmap * sizeof (smb_idmap_t); sib->sib_maps = kmem_zalloc(sib->sib_size, KM_SLEEP); return (IDMAP_SUCCESS); } /* * smb_idmap_batch_destroy * * Frees the batch ID mapping context. */ void smb_idmap_batch_destroy(smb_idmap_batch_t *sib) { char *domsid; int i; ASSERT(sib != NULL); ASSERT(sib->sib_maps != NULL); if (sib->sib_idmaph) { idmap_get_destroy(sib->sib_idmaph); sib->sib_idmaph = NULL; } if (sib->sib_flags & SMB_IDMAP_ID2SID) { /* * SIDs are allocated only when mapping * UID/GID to SIDs */ for (i = 0; i < sib->sib_nmap; i++) { smb_sid_free(sib->sib_maps[i].sim_sid); /* from strdup() in libidmap */ free(sib->sib_maps[i].sim_domsid); } } else if (sib->sib_flags & SMB_IDMAP_SID2ID) { /* * SID prefixes are allocated only when mapping * SIDs to UID/GID */ for (i = 0; i < sib->sib_nmap; i++) { domsid = sib->sib_maps[i].sim_domsid; if (domsid) smb_mem_free(domsid); } } if (sib->sib_size && sib->sib_maps) { kmem_free(sib->sib_maps, sib->sib_size); sib->sib_maps = NULL; } } /* * smb_idmap_batch_getid * * Queue a request to map the given SID to a UID or GID. * * sim->sim_id should point to variable that's supposed to * hold the returned UID/GID. This needs to be setup by caller * of this function. * If requested ID type is known, it's passed as 'idtype', * if it's unknown it'll be returned in sim->sim_idtype. */ idmap_stat smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, smb_sid_t *sid, int idtype) { char sidstr[SMB_SID_STRSZ]; idmap_stat stat; int flag = 0; ASSERT(idmaph != NULL); ASSERT(sim != NULL); ASSERT(sid != NULL); smb_sid_tostr(sid, sidstr); if (smb_sid_splitstr(sidstr, &sim->sim_rid) != 0) return (IDMAP_ERR_SID); /* Note: Free sim_domsid in smb_idmap_batch_destroy */ sim->sim_domsid = smb_mem_strdup(sidstr); sim->sim_idtype = idtype; switch (idtype) { case SMB_IDMAP_USER: stat = idmap_get_uidbysid(idmaph, sim->sim_domsid, sim->sim_rid, flag, sim->sim_id, &sim->sim_stat); smb_idmap_check("idmap_get_uidbysid", stat); break; case SMB_IDMAP_GROUP: stat = idmap_get_gidbysid(idmaph, sim->sim_domsid, sim->sim_rid, flag, sim->sim_id, &sim->sim_stat); smb_idmap_check("idmap_get_gidbysid", stat); break; case SMB_IDMAP_UNKNOWN: stat = idmap_get_pidbysid(idmaph, sim->sim_domsid, sim->sim_rid, flag, sim->sim_id, &sim->sim_idtype, &sim->sim_stat); smb_idmap_check("idmap_get_pidbysid", stat); break; default: stat = IDMAP_ERR_ARG; break; } return (stat); } /* * smb_idmap_batch_getsid * * Queue a request to map the given UID/GID to a SID. * * sim->sim_domsid and sim->sim_rid will contain the mapping * result upon successful process of the batched request. * Stash the type for error reporting (caller saves the ID). * * NB: sim_domsid allocated by strdup, here or in libidmap */ idmap_stat smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, uid_t id, int idtype) { idmap_stat stat; int flag = 0; if (!idmaph || !sim) return (IDMAP_ERR_ARG); sim->sim_idtype = idtype; switch (idtype) { case SMB_IDMAP_USER: stat = idmap_get_sidbyuid(idmaph, id, flag, &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat); smb_idmap_check("idmap_get_sidbyuid", stat); break; case SMB_IDMAP_GROUP: stat = idmap_get_sidbygid(idmaph, id, flag, &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat); smb_idmap_check("idmap_get_sidbygid", stat); break; case SMB_IDMAP_OWNERAT: /* Current Owner S-1-5-32-766 */ sim->sim_domsid = strdup(NT_BUILTIN_DOMAIN_SIDSTR); sim->sim_rid = SECURITY_CURRENT_OWNER_RID; sim->sim_stat = IDMAP_SUCCESS; stat = IDMAP_SUCCESS; break; case SMB_IDMAP_GROUPAT: /* Current Group S-1-5-32-767 */ sim->sim_domsid = strdup(NT_BUILTIN_DOMAIN_SIDSTR); sim->sim_rid = SECURITY_CURRENT_GROUP_RID; sim->sim_stat = IDMAP_SUCCESS; stat = IDMAP_SUCCESS; break; case SMB_IDMAP_EVERYONE: /* Everyone S-1-1-0 */ sim->sim_domsid = strdup(NT_WORLD_AUTH_SIDSTR); sim->sim_rid = 0; sim->sim_stat = IDMAP_SUCCESS; stat = IDMAP_SUCCESS; break; default: ASSERT(0); return (IDMAP_ERR_ARG); } return (stat); } /* * smb_idmap_batch_getmappings * * trigger ID mapping service to get the mappings for queued * requests. * * Checks the result of all the queued requests. */ idmap_stat smb_idmap_batch_getmappings(smb_idmap_batch_t *sib, smb_idmap_batch_errcb_t errcb) { idmap_stat stat = IDMAP_SUCCESS; smb_idmap_t *sim; int i; if ((stat = idmap_get_mappings(sib->sib_idmaph)) != IDMAP_SUCCESS) { smb_idmap_check("idmap_get_mappings", stat); return (stat); } /* * Check the status for all the queued requests */ for (i = 0, sim = sib->sib_maps; i < sib->sib_nmap; i++, sim++) { if (sim->sim_stat != IDMAP_SUCCESS) { sib->sib_nerr++; if (errcb != NULL) errcb(sib, sim); if ((sib->sib_flags & SMB_IDMAP_SKIP_ERRS) == 0) { return (sim->sim_stat); } } } if (smb_idmap_batch_binsid(sib) != 0) stat = IDMAP_ERR_OTHER; return (stat); } /* * smb_idmap_batch_binsid * * Convert sidrids to binary sids * * Returns 0 if successful and non-zero upon failure. */ static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib) { smb_sid_t *sid; smb_idmap_t *sim; int i; if (sib->sib_flags & SMB_IDMAP_SID2ID) /* This operation is not required */ return (0); sim = sib->sib_maps; for (i = 0; i < sib->sib_nmap; sim++, i++) { ASSERT(sim->sim_domsid != NULL); if (sim->sim_domsid == NULL) return (-1); sid = smb_sid_fromstr(sim->sim_domsid); if (sid == NULL) return (-1); sim->sim_sid = smb_sid_splice(sid, sim->sim_rid); smb_sid_free(sid); } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. * Copyright 2015-2024 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include /* * ***************************************************************************** * ****************************** Global Variables ***************************** * ***************************************************************************** * * These variables can only be changed through the /etc/system file. */ /* * Maximum buffer size for NT: configurable based on the client environment. * IR104720 Experiments with Windows 2000 indicate that we achieve better * SmbWriteX performance with a buffer size of 64KB instead of the 37KB used * with Windows NT4.0. Previous experiments with NT4.0 resulted in directory * listing problems so this buffer size is configurable based on the end-user * environment. When in doubt use 37KB. */ int smb_maxbufsize = SMB_NT_MAXBUF; int smb_flush_required = 1; int smb_dirsymlink_enable = 1; int smb_sign_debug = 0; uint_t smb_audit_flags = #ifdef DEBUG SMB_AUDIT_NODE; #else 0; #endif /* * We don't normally have nbmand support in the test share * used by fksmbd, but we'd still like the locking code * to be testable. Intereactions with NFS etc. are not a * concern in fksmbd, so allow it to use advisory locks. * * Should fix the fksmbd test share so it supports nbmand, * and then set this to zero like the real server. */ int smb_allow_advisory_locks = 1; /* See smb_vops.c */ /* * Maximum number of simultaneous authentication, share mapping, pipe open * requests to be processed. */ int smb_ssetup_threshold = SMB_AUTHSVC_MAXTHREAD; int smb_tcon_threshold = 1024; int smb_opipe_threshold = 1024; int smb_logoff_threshold = 1024; /* * Number of milliseconds that a request will be stalled if it comes in after * the maximum number of inflight operations are being proccessed. */ int smb_ssetup_timeout = (30 * 1000); int smb_tcon_timeout = (30 * 1000); int smb_opipe_timeout = (30 * 1000); int smb_logoff_timeout = (600 * 1000); /* * Thread priorities used in smbsrv -- actually unused here. * See $UTS/common/fs/smbsrv/smb_init.c */ int smbsrv_base_pri = MINCLSYSPRI; int smbsrv_listen_pri = MINCLSYSPRI; int smbsrv_receive_pri = MINCLSYSPRI; int smbsrv_worker_pri = MINCLSYSPRI; int smbsrv_notify_pri = MINCLSYSPRI; int smbsrv_timer_pri = MINCLSYSPRI; /* * These are the (open,close,ioctl) entry points into this * (fake) "driver". They are declared in smb_ioctl.h */ static int g_init_done = 0; int fksmbsrv_vfs_init(void); void fksmbsrv_vfs_fini(void); int fksmbsrv_drv_open(void) { int rc; if (g_init_done == 0) { if ((rc = fksmbsrv_vfs_init()) != 0) { cmn_err(CE_WARN, "fksmbsrv_vfs_init, rc=%d", rc); return (rc); } if ((rc = smb_server_g_init()) != 0) { cmn_err(CE_WARN, "smb_server_g_init, rc=%d", rc); return (rc); } g_init_done = 1; } rc = smb_server_create(0); return (rc); } int fksmbsrv_drv_close(void) { smb_server_t *sv; int rc; rc = smb_server_lookup(&sv); if (rc == 0) rc = smb_server_delete(sv); if (g_init_done != 0) { smb_server_g_fini(); fksmbsrv_vfs_fini(); g_init_done = 0; } return (rc); } /* * This is the primary entry point into this library, called by * fksmbd (user-level debug version of smbsrv). * * The code here is a reduced version of: smb_drv_ioctl * from $UTS/common/fs/smbsrv/smb_init.c */ int fksmbsrv_drv_ioctl(int cmd, void *varg) { smb_server_t *sv; smb_ioc_t *ioc = varg; int rc = 0; /* Unlike smb_drv_ioctl(), no copyin/copyout here. */ rc = smb_server_lookup(&sv); if (rc != 0) return (rc); /* No access control checks. */ switch (cmd) { case SMB_IOC_CONFIG: rc = smb_server_configure(sv, &ioc->ioc_cfg); break; case SMB_IOC_START: rc = smb_server_start(sv, &ioc->ioc_start); break; case SMB_IOC_STOP: rc = smb_server_stop(sv); break; case SMB_IOC_EVENT: rc = smb_server_notify_event(sv, &ioc->ioc_event); break; case SMB_IOC_GMTOFF: rc = smb_server_set_gmtoff(sv, &ioc->ioc_gmt); break; case SMB_IOC_SHARE: rc = smb_kshare_export_list(sv, &ioc->ioc_share); break; case SMB_IOC_UNSHARE: rc = smb_kshare_unexport_list(sv, &ioc->ioc_share); break; case SMB_IOC_SHAREINFO: rc = smb_kshare_info(sv, &ioc->ioc_shareinfo); break; case SMB_IOC_SHAREACCESS: rc = smb_kshare_access(sv, &ioc->ioc_shareaccess); break; case SMB_IOC_NUMOPEN: rc = smb_server_numopen(sv, &ioc->ioc_opennum); break; case SMB_IOC_SVCENUM: rc = smb_server_enum(sv, &ioc->ioc_svcenum); break; case SMB_IOC_SESSION_CLOSE: rc = smb_server_session_close(sv, &ioc->ioc_session); break; case SMB_IOC_FILE_CLOSE: rc = smb_server_file_close(sv, &ioc->ioc_fileid); break; case SMB_IOC_SPOOLDOC: rc = smb_server_spooldoc(sv, &ioc->ioc_spooldoc); break; default: rc = SET_ERROR(ENOTTY); break; } smb_server_release(sv); return (rc); } /* * This function intentionally does nothing. It's used only to * force libfksmbsrv to load when fksmbd starts so one can set * breakpoints etc. without debugger "force load" tricks. */ void fksmbsrv_drv_load(void) { } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * "Upcall" glue for the fake (user-mode) smbsrv module. */ #include #include #include #include #include #include #include #include static int smb_kdoor_encode(smb_doorarg_t *); static int smb_kdoor_decode(smb_doorarg_t *); static void smb_kdoor_sethdr(smb_doorarg_t *, uint32_t); static boolean_t smb_kdoor_chkhdr(smb_doorarg_t *, smb_doorhdr_t *); static void smb_kdoor_free(door_arg_t *); void smb_kdoor_init(smb_server_t *sv) { sv->sv_kdoor_id = -1; mutex_init(&sv->sv_kdoor_mutex, NULL, MUTEX_DEFAULT, NULL); cv_init(&sv->sv_kdoor_cv, NULL, CV_DEFAULT, NULL); } void smb_kdoor_fini(smb_server_t *sv) { smb_kdoor_close(sv); cv_destroy(&sv->sv_kdoor_cv); mutex_destroy(&sv->sv_kdoor_mutex); } /* * In the "fake kernen", our "upcalls" don't use the * real door, but just call via a function pointer. * This is where we setup that pointer, which is * fksmbd_door_dispatch() */ void fksmb_kdoor_open(smb_server_t *sv, void *varg) { sv->sv_kdoor_hd = varg; } void smb_kdoor_close(smb_server_t *sv) { sv->sv_kdoor_hd = NULL; sv->sv_kdoor_id = -1; } /* ARGSUSED */ int smb_kdoor_upcall(smb_server_t *sv, uint32_t cmd, void *req_data, xdrproc_t req_xdr, void *rsp_data, xdrproc_t rsp_xdr) { smb_doorarg_t da; fksmb_kdoor_disp_func_t *func; int rc; bzero(&da, sizeof (smb_doorarg_t)); da.da_opcode = cmd; da.da_opname = smb_doorhdr_opname(cmd); da.da_req_xdr = req_xdr; da.da_rsp_xdr = rsp_xdr; da.da_req_data = req_data; da.da_rsp_data = rsp_data; if ((req_data == NULL && req_xdr != NULL) || (rsp_data == NULL && rsp_xdr != NULL)) { cmn_err(CE_WARN, "smb_kdoor_upcall[%s]: invalid param", da.da_opname); return (-1); } /* NB: no ASYNC, nor event stuff */ func = (fksmb_kdoor_disp_func_t *)(sv->sv_kdoor_hd); if (func == NULL) return (EFAULT); if ((rc = smb_kdoor_encode(&da)) != 0) goto out; /* * The "upcall" (just call via function pointer) * i.e. see: fksmbd_door_dispatch() */ if ((rc = (*func)(&da)) != 0) goto out; rc = smb_kdoor_decode(&da); out: smb_kdoor_free(&da.da_arg); return (rc); } /* no smb_kdoor_send, smb_kdoor_receive */ /* no smb_kdoor_upcall_private */ static int smb_kdoor_encode(smb_doorarg_t *da) { XDR xdrs; char *buf; uint32_t len; len = xdr_sizeof(smb_doorhdr_xdr, &da->da_hdr); if (da->da_req_xdr != NULL) len += xdr_sizeof(da->da_req_xdr, da->da_req_data); smb_kdoor_sethdr(da, len); buf = kmem_zalloc(len, KM_SLEEP); xdrmem_create(&xdrs, buf, len, XDR_ENCODE); if (!smb_doorhdr_xdr(&xdrs, &da->da_hdr)) { cmn_err(CE_WARN, "smb_kdoor_encode[%s]: header encode failed", da->da_opname); kmem_free(buf, len); xdr_destroy(&xdrs); return (-1); } if (da->da_req_xdr != NULL) { if (!da->da_req_xdr(&xdrs, da->da_req_data)) { cmn_err(CE_WARN, "smb_kdoor_encode[%s]: encode failed", da->da_opname); kmem_free(buf, len); xdr_destroy(&xdrs); return (-1); } } da->da_arg.data_ptr = buf; da->da_arg.data_size = len; da->da_arg.desc_ptr = NULL; da->da_arg.desc_num = 0; da->da_arg.rbuf = buf; da->da_arg.rsize = len; xdr_destroy(&xdrs); return (0); } /* * Decode the response in rbuf and rsize. */ static int smb_kdoor_decode(smb_doorarg_t *da) { XDR xdrs; smb_doorhdr_t hdr; char *rbuf = da->da_arg.rbuf; uint32_t rsize = da->da_arg.rsize; if (rbuf == NULL || rsize == 0) { cmn_err(CE_WARN, "smb_kdoor_decode[%s]: invalid param", da->da_opname); return (-1); } xdrmem_create(&xdrs, rbuf, rsize, XDR_DECODE); if (!smb_doorhdr_xdr(&xdrs, &hdr)) { cmn_err(CE_WARN, "smb_kdoor_decode[%s]: header decode failed", da->da_opname); xdr_destroy(&xdrs); return (-1); } if (!smb_kdoor_chkhdr(da, &hdr)) { xdr_destroy(&xdrs); return (-1); } if (hdr.dh_datalen != 0 && da->da_rsp_xdr != NULL) { if (!da->da_rsp_xdr(&xdrs, da->da_rsp_data)) { cmn_err(CE_WARN, "smb_kdoor_decode[%s]: decode failed", da->da_opname); xdr_destroy(&xdrs); return (-1); } } xdr_destroy(&xdrs); return (0); } static void smb_kdoor_sethdr(smb_doorarg_t *da, uint32_t datalen) { smb_doorhdr_t *hdr = &da->da_hdr; bzero(hdr, sizeof (smb_doorhdr_t)); hdr->dh_magic = SMB_DOOR_HDR_MAGIC; hdr->dh_flags = da->da_flags | SMB_DF_FAKE_KERNEL; hdr->dh_op = da->da_opcode; /* hdr->dh_txid = 0 (not used) */ hdr->dh_datalen = datalen; hdr->dh_door_rc = SMB_DOP_NOT_CALLED; } static boolean_t smb_kdoor_chkhdr(smb_doorarg_t *da, smb_doorhdr_t *hdr) { if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) || (hdr->dh_op != da->da_hdr.dh_op) || (hdr->dh_txid != da->da_hdr.dh_txid)) { cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: invalid header", da->da_opname); return (B_FALSE); } switch (hdr->dh_door_rc) { case SMB_DOP_SUCCESS: break; /* SMB_DOP_EMPTYBUF is a "normal" error (silent). */ case SMB_DOP_EMPTYBUF: return (B_FALSE); default: cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: call failed: %u", da->da_opname, hdr->dh_door_rc); return (B_FALSE); } return (B_TRUE); } /* * Free both the argument and result door buffers regardless of the status * of the up-call. The doorfs allocates a new buffer if the result buffer * passed by the client is too small. */ static void smb_kdoor_free(door_arg_t *arg) { if (arg->rbuf != NULL && arg->rbuf != arg->data_ptr) kmem_free(arg->rbuf, arg->rsize); if (arg->data_ptr != NULL) kmem_free(arg->data_ptr, arg->data_size); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2021 RackTop Systems, Inc. */ #include #include #include #include #include /* * SMB 3.1.1 Preauth Integrity */ static int getmech_sha512(smb_crypto_mech_t *mech) { ulong_t mid = CKM_SHA512; CK_SESSION_HANDLE hdl; CK_RV rv; rv = SUNW_C_GetMechSession(mid, &hdl); if (rv != CKR_OK) { cmn_err(CE_NOTE, "PKCS#11: no mech 0x%x", (unsigned int)mid); return (-1); } (void) C_CloseSession(hdl); mech->mechanism = mid; mech->pParameter = NULL; mech->ulParameterLen = 0; return (0); } /* * (called from smb2_negotiate_common) */ void smb31_preauth_init_mech(smb_session_t *s) { smb_crypto_mech_t *mech; int rc; ASSERT3S(s->dialect, >=, SMB_VERS_3_11); if (s->preauth_mech != NULL) return; mech = kmem_zalloc(sizeof (*mech), KM_SLEEP); rc = getmech_sha512(mech); if (rc != 0) { kmem_free(mech, sizeof (*mech)); return; } s->preauth_mech = mech; } void smb31_preauth_fini(smb_session_t *s) { smb_crypto_mech_t *mech; if ((mech = s->preauth_mech) != NULL) { kmem_free(mech, sizeof (*mech)); s->preauth_mech = NULL; } } /* * Start the KCF session, load the key */ int smb_sha512_init(smb_sign_ctx_t *ctxp, smb_crypto_mech_t *mech) { CK_RV rv; rv = SUNW_C_GetMechSession(mech->mechanism, ctxp); if (rv != CKR_OK) return (-1); rv = C_DigestInit(*ctxp, mech); return (rv == CKR_OK ? 0 : -1); } /* * Digest one segment */ int smb_sha512_update(smb_sign_ctx_t ctx, void *buf, size_t len) { CK_RV rv; rv = C_DigestUpdate(ctx, buf, len); if (rv != CKR_OK) (void) C_CloseSession(ctx); return (rv == CKR_OK ? 0 : -1); } /* * Get the final digest. */ int smb_sha512_final(smb_sign_ctx_t ctx, uint8_t *digest) { CK_ULONG len = SHA512_DIGEST_LENGTH; CK_RV rv; rv = C_DigestFinal(ctx, digest, &len); (void) C_CloseSession(ctx); return (rv == CKR_OK ? 0 : -1); } int smb31_preauth_sha512_calc(smb_request_t *sr, struct mbuf_chain *mbc, uint8_t *in_hashval, uint8_t *out_hashval) { smb_session_t *s = sr->session; smb_sign_ctx_t ctx = 0; struct mbuf *mbuf = mbc->chain; int rc; ASSERT3U(s->smb31_preauth_hashid, !=, 0); if (s->preauth_mech == NULL) return (-1); if ((rc = smb_sha512_init(&ctx, s->preauth_mech)) != 0) return (rc); /* Digest current hashval */ rc = smb_sha512_update(ctx, in_hashval, SHA512_DIGEST_LENGTH); if (rc != 0) return (rc); while (mbuf != NULL) { rc = smb_sha512_update(ctx, mbuf->m_data, mbuf->m_len); if (rc != 0) return (rc); mbuf = mbuf->m_next; } rc = smb_sha512_final(ctx, out_hashval); return (rc); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Nexenta Systems, Inc. All rights reserved. * Copyright 2022-2023 RackTop Systems, Inc. */ /* * Helper functions for SMB signing using PKCS#11 * * There are two implementations of these functions: * This one (for user space) and another for kernel. * See: uts/common/fs/smbsrv/smb_sign_kcf.c */ #include #include #include #include #include /* * Common function to see if a mech is available. */ static int find_mech(smb_crypto_mech_t *mech, ulong_t mid) { CK_SESSION_HANDLE hdl; CK_RV rv; rv = SUNW_C_GetMechSession(mid, &hdl); if (rv != CKR_OK) { cmn_err(CE_NOTE, "PKCS#11: no mech 0x%x", (unsigned int)mid); return (-1); } (void) C_CloseSession(hdl); mech->mechanism = mid; mech->pParameter = NULL; mech->ulParameterLen = 0; return (0); } /* * SMB1 signing helpers: * (getmech, init, update, final) */ /* * Find out if we have this mech. */ int smb_md5_getmech(smb_crypto_mech_t *mech) { return (find_mech(mech, CKM_MD5)); } /* * Start PKCS#11 session. */ int smb_md5_init(smb_sign_ctx_t *ctxp, smb_crypto_mech_t *mech) { CK_RV rv; rv = SUNW_C_GetMechSession(mech->mechanism, ctxp); if (rv != CKR_OK) return (-1); rv = C_DigestInit(*ctxp, mech); return (rv == CKR_OK ? 0 : -1); } /* * Digest one segment */ int smb_md5_update(smb_sign_ctx_t ctx, void *buf, size_t len) { CK_RV rv; rv = C_DigestUpdate(ctx, buf, len); if (rv != CKR_OK) (void) C_CloseSession(ctx); return (rv == CKR_OK ? 0 : -1); } /* * Get the final digest. */ int smb_md5_final(smb_sign_ctx_t ctx, uint8_t *digest16) { CK_ULONG len = MD5_DIGEST_LENGTH; CK_RV rv; rv = C_DigestFinal(ctx, digest16, &len); (void) C_CloseSession(ctx); return (rv == CKR_OK ? 0 : -1); } /* * SMB2 signing helpers: * (getmech, init, update, final) */ /* * Find out if we have this mech. */ int smb2_hmac_getmech(smb_crypto_mech_t *mech) { return (find_mech(mech, CKM_SHA256_HMAC_GENERAL)); } int smb3_cmac_getmech(smb_crypto_mech_t *mech) { return (find_mech(mech, CKM_AES_CMAC)); } int smb3_gmac_getmech(smb_crypto_mech_t *mech) { return (find_mech(mech, CKM_AES_GMAC)); } /* * Note, the SMB2 signature is the first 16 bytes of the digest, * even in the case of SHA256 HMAC (32-byte digest). * * CMAC has no parameter. */ void smb2_sign_init_hmac_param(smb_crypto_mech_t *mech, smb_crypto_param_t *param, ulong_t hmac_len) { param->hmac = hmac_len; mech->pParameter = (caddr_t)¶m->hmac; mech->ulParameterLen = sizeof (param->hmac); } /* * GMAC parameters are just the initialization vector: CK_BYTE IV[12] * * Most other smb2_sign_init_... functions use a data structure, and * those that do have a union arm in smb_crypto_param_t parem->X. * However, GMAC is unusual in that it uses no such struct. * Just to be helpful with debugging, store the IV pointer in the * param->gmac arm of that union (just a pointer), though only * the mech->pParameter pointer is actually used. */ void smb3_sign_init_gmac_param(smb_crypto_mech_t *mech, smb_crypto_param_t *param, uint8_t *iv) { param->gmac = iv; /* see above */ mech->pParameter = (caddr_t)iv; mech->ulParameterLen = SMB3_AES_GMAC_NONCE_SIZE; } int smb2_mac_raw(smb_crypto_mech_t *mech, uint8_t *key, size_t key_len, uint8_t *data, size_t data_len, uint8_t *mac, size_t mac_len) { CK_SESSION_HANDLE hssn = 0; CK_OBJECT_HANDLE hkey = 0; CK_ULONG ck_maclen = mac_len; CK_RV rv; int rc = 0; rv = SUNW_C_GetMechSession(mech->mechanism, &hssn); if (rv != CKR_OK) return (-1); rv = SUNW_C_KeyToObject(hssn, mech->mechanism, key, key_len, &hkey); if (rv != CKR_OK) { rc = -2; goto out; } rv = C_SignInit(hssn, mech, hkey); if (rv != CKR_OK) { rc = -3; goto out; } rv = C_Sign(hssn, data, data_len, mac, &ck_maclen); if (rv != CKR_OK) rc = -4; else if (ck_maclen != mac_len) rc = -5; else rc = 0; out: if (hkey != 0) (void) C_DestroyObject(hssn, hkey); if (hssn != 0) (void) C_CloseSession(hssn); mech->pParameter = NULL; mech->ulParameterLen = 0; return (rc); } /* * Digest a whole message with scatter/gather (UIO) * * Returns zero on success, else non-zero. The error return values * are arbitrary, and different from each other just to make it easy * to see which error path was taken when looking at dtrace returns. */ int smb2_mac_uio(smb_crypto_mech_t *mech, uint8_t *key, size_t key_len, uio_t *in_uio, uint8_t *digest16) { CK_SESSION_HANDLE hssn = 0; CK_OBJECT_HANDLE hkey = 0; CK_ULONG mac_len = SMB2_SIG_SIZE; CK_ULONG ck_maclen = mac_len; CK_RV rv; int rc = 0; if (in_uio->uio_resid <= 0) return (EIO); if (in_uio->uio_segflg != UIO_USERSPACE && in_uio->uio_segflg != UIO_SYSSPACE) { return (EINVAL); } rv = SUNW_C_GetMechSession(mech->mechanism, &hssn); if (rv != CKR_OK) return (-1); rv = SUNW_C_KeyToObject(hssn, mech->mechanism, key, key_len, &hkey); if (rv != CKR_OK) { rc = -2; goto out; } rv = C_SignInit(hssn, mech, hkey); if (rv != CKR_OK) { rc = -3; goto out; } /* Like uiomove() */ while (in_uio->uio_resid > 0) { struct iovec *iov = in_uio->uio_iov; CK_ULONG data_len = MIN(in_uio->uio_resid, iov->iov_len); if (data_len == 0) { in_uio->uio_iov++; in_uio->uio_iovcnt--; continue; } rv = C_SignUpdate(hssn, (CK_BYTE_PTR)iov->iov_base, data_len); if (rv != CKR_OK) { rc = -4; goto out; } iov->iov_base += data_len; iov->iov_len -= data_len; in_uio->uio_resid -= data_len; in_uio->uio_loffset += data_len; } rv = C_SignFinal(hssn, digest16, &ck_maclen); if (rv != CKR_OK) rc = -5; else if (ck_maclen != mac_len) rc = -6; else rc = 0; out: if (hkey != 0) (void) C_DestroyObject(hssn, hkey); if (hssn != 0) (void) C_CloseSession(hssn); return (rc); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2013 Nexenta Systems, Inc. All rights reserved. # # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate { global: fksmbsrv_drv_close; fksmbsrv_drv_ioctl; fksmbsrv_drv_load; fksmbsrv_drv_open; local: *; }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include /* * support functions for reparse point * copied from uts/common/fs/fs_subr.c */ /* * reparse_vnode_parse * * Read the symlink data of a reparse point specified by the vnode * and return the reparse data as name-value pair in the nvlist. */ int reparse_vnode_parse(vnode_t *vp, nvlist_t *nvl) { int err; char *lkdata; struct uio uio; struct iovec iov; if (vp == NULL || nvl == NULL) return (EINVAL); lkdata = kmem_alloc(MAXREPARSELEN, KM_SLEEP); /* * Set up io vector to read sym link data */ iov.iov_base = lkdata; iov.iov_len = MAXREPARSELEN; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_segflg = UIO_SYSSPACE; uio.uio_extflg = UIO_COPY_CACHED; uio.uio_loffset = (offset_t)0; uio.uio_resid = MAXREPARSELEN; if ((err = VOP_READLINK(vp, &uio, zone_kcred(), NULL)) == 0) { *(lkdata + MAXREPARSELEN - uio.uio_resid) = '\0'; err = reparse_parse(lkdata, nvl); } kmem_free(lkdata, MAXREPARSELEN); /* done with lkdata */ return (err); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Windows to Solaris Identity Mapping kernel API * This header defines an API to map Windows SIDs to * Solaris UID and GIDs and versa visa. */ #ifndef _SYS_KIDMAP_H #define _SYS_KIDMAP_H #include #include #include #ifdef __cplusplus extern "C" { #endif /* * The ifdef's for these two accomodate duplicate definitions in * lib/libidmap/common/idmap.h (the real one). In this code we * simulate a kernel environment in user space using the real * idmap library, so need to be able to use both headers. */ /* Return status */ #ifndef _IDMAP_STAT_TYPE #define _IDMAP_STAT_TYPE typedef int32_t idmap_stat; #endif /* _IDMAP_STAT_TYPE */ /* Opaque get handle */ #ifndef _IDMAP_GET_HANDLE_T #define _IDMAP_GET_HANDLE_T typedef struct idmap_get_handle idmap_get_handle_t; #endif /* _IDMAP_GET_HANDLE_T */ /* * In all the routines a Windows SID is handled as a * string SID prefix plus a RID. For example * * S-1-5-5-12-34-568 will be passed as SID prefix * S-1-5-5-12-34 and RID 568 * * Certain routines returns pointers to a SID prefix string. * These strings are stored internally and should not be modified * or freed. */ /* * The following routines are simple get ID mapping routines. */ idmap_stat kidmap_getuidbysid(zone_t *zone, const char *sid_prefix, uint32_t rid, uid_t *uid); idmap_stat kidmap_getgidbysid(zone_t *zone, const char *sid_prefix, uint32_t rid, gid_t *gid); idmap_stat kidmap_getpidbysid(zone_t *zone, const char *sid_prefix, uint32_t rid, uid_t *pid, int *is_user); idmap_stat kidmap_getsidbyuid(zone_t *zone, uid_t uid, const char **sid_prefix, uint32_t *rid); idmap_stat kidmap_getsidbygid(zone_t *zone, gid_t gid, const char **sid_prefix, uint32_t *rid); /* * The following routines provide a batch interface for mapping IDs. */ /* * Create a batch "get mapping" handle for batch mappings. */ idmap_get_handle_t * kidmap_get_create(zone_t *zone); /* * These routines queue the request to the "get mapping" handle */ idmap_stat kidmap_batch_getuidbysid(idmap_get_handle_t *get_handle, const char *sid_prefix, uint32_t rid, uid_t *uid, idmap_stat *stat); idmap_stat kidmap_batch_getgidbysid(idmap_get_handle_t *get_handle, const char *sid_prefix, uint32_t rid, gid_t *gid, idmap_stat *stat); idmap_stat kidmap_batch_getpidbysid(idmap_get_handle_t *get_handle, const char *sid_prefix, uint32_t rid, uid_t *pid, int *is_user, idmap_stat *stat); idmap_stat kidmap_batch_getsidbyuid(idmap_get_handle_t *get_handle, uid_t uid, const char **sid_prefix, uint32_t *rid, idmap_stat *stat); idmap_stat kidmap_batch_getsidbygid(idmap_get_handle_t *get_handle, gid_t gid, const char **sid_prefix, uint32_t *rid, idmap_stat *stat); /* * Process the queued "get mapping" requests. The results (i.e. * status and identity) will be available in the data areas * provided by individual requests. */ idmap_stat kidmap_get_mappings(idmap_get_handle_t *get_handle); /* * Destroy the "get mapping" handle */ void kidmap_get_destroy(idmap_get_handle_t *get_handle); #ifdef _KERNEL /* * Functions that do the hard part of door registration/unregistration * for the idmap_reg()/idmap_unreg() syscalls */ int idmap_reg_dh(zone_t *zone, door_handle_t dh); int idmap_unreg_dh(zone_t *zone, door_handle_t dh); /* * Function needed by allocids() to ensure only the daemon that owns * the door gets ephemeral IDS */ door_handle_t idmap_get_door(zone_t *zone); /* * Function used by system call allocids() to purge the * ID mapping cache */ void idmap_purge_cache(zone_t *zone); #endif /* _KERNEL */ #ifdef __cplusplus } #endif #endif /* _SYS_KIDMAP_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2017-2021 Tintri by DDN, Inc. All rights reserved. */ #ifndef _SYS_SDT_H #define _SYS_SDT_H #include #include #ifdef __cplusplus extern "C" { #endif #ifdef _KERNEL #error "libfksmbsrv/common/sys/sdt.h in kernel?" #endif /* * DTrace SDT probes have different signatures in userland than they do in * the kernel. This file is strictly for libfksmbsrv, where we compile the * smbsrv kernel code for user space. In "fksmbd", we can use the probes * defined for the (real, in-kernel) "smb" and "smb2" SDT provider by * mapping them onto the USDT proviver defined in ../fksmb_dt.d * * An example of how to use these probes can be found in: * $SRC/cmd/smbsrv/fksmbd/Watch-fksmb.d */ /* * Map "smb" provider probes. */ #define DTRACE_SMB_START(name, type1, arg1) \ FKSMB_SMB_START(#name, (unsigned long)arg1) #define DTRACE_SMB_DONE(name, type1, arg1) \ FKSMB_SMB_DONE(#name, (unsigned long)arg1) /* * Map "smb2" provider probes. */ #define DTRACE_SMB2_START(name, type1, arg1) \ FKSMB_SMB2_START(#name, (unsigned long)arg1) #define DTRACE_SMB2_DONE(name, type1, arg1) \ FKSMB_SMB2_DONE(#name, (unsigned long)arg1) /* * These are for the other (specialized) dtrace SDT probes sprinkled * through the smbsrv code. These are less often used. */ #define DTRACE_PROBE(name, type1, arg1) \ FKSMB_PROBE0(#name) #define DTRACE_PROBE1(name, type1, arg1) \ FKSMB_PROBE1(#name, (unsigned long)arg1) #define DTRACE_PROBE2(name, type1, arg1, type2, arg2) \ FKSMB_PROBE2(#name, (unsigned long)arg1, (unsigned long)arg2) #define DTRACE_PROBE3(name, type1, arg1, type2, arg2, type3, arg3) \ FKSMB_PROBE3(#name, (unsigned long)arg1, (unsigned long)arg2, \ (unsigned long)arg3) /* * We use the comma operator so that this macro can be used without much * additional code. For example, "return (EINVAL);" becomes * "return (SET_ERROR(EINVAL));". Note that the argument will be evaluated * twice, so it should not have side effects (e.g. something like: * "return (SET_ERROR(log_error(EINVAL, info)));" would log the error twice). */ #define SET_ERROR(err) (FKSMB_SET_ERROR(err), err) #ifdef __cplusplus } #endif #endif /* _SYS_SDT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SYS_SUNDDI_H #define _SYS_SUNDDI_H /* * Sun Specific DDI definitions (fakekernel version) * The real sunddi.h has become a "kitchen sink" full of * includes we don't want, and lots of places include it. * Rather than fight that battle now, provide this one * with just the str*, mem*, and kiconv* functions. * Some day, re-factor: sunddi.h, systm.h */ #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #if defined(_KERNEL) || defined(_FAKE_KERNEL) extern char *ddi_strdup(const char *str, int flag); extern char *strdup(const char *str); extern void strfree(char *str); extern size_t strlen(const char *) __PURE; extern size_t strnlen(const char *, size_t) __PURE; extern char *strcpy(char *, const char *); extern char *strncpy(char *, const char *, size_t); /* Need to be consistent with C++ definition for strchr() */ #if __cplusplus >= 199711L extern const char *strchr(const char *, int); #else extern char *strchr(const char *, int); #endif /* __cplusplus >= 199711L */ #define DDI_STRSAME(s1, s2) ((*(s1) == *(s2)) && (strcmp((s1), (s2)) == 0)) extern int strcmp(const char *, const char *) __PURE; extern int strncmp(const char *, const char *, size_t) __PURE; extern char *strncat(char *, const char *, size_t); extern size_t strlcat(char *, const char *, size_t); extern size_t strlcpy(char *, const char *, size_t); extern size_t strspn(const char *, const char *); extern size_t strcspn(const char *, const char *); extern int bcmp(const void *, const void *, size_t) __PURE; extern int stoi(char **); extern void numtos(ulong_t, char *); extern void bcopy(const void *, void *, size_t); extern void bzero(void *, size_t); extern void *memcpy(void *, const void *, size_t); extern void *memset(void *, int, size_t); extern void *memmove(void *, const void *, size_t); extern int memcmp(const void *, const void *, size_t) __PURE; /* Need to be consistent with C++ definition for memchr() */ #if __cplusplus >= 199711L extern const void *memchr(const void *, int, size_t); #else extern void *memchr(const void *, int, size_t); #endif /* __cplusplus >= 199711L */ extern int ddi_strtol(const char *, char **, int, long *); extern int ddi_strtoul(const char *, char **, int, unsigned long *); extern int ddi_strtoll(const char *, char **, int, longlong_t *); extern int ddi_strtoull(const char *, char **, int, u_longlong_t *); #endif /* _KERNEL || _FAKE_KERNEL */ #ifdef __cplusplus } #endif #endif /* _SYS_SUNDDI_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2024 RackTop Systems, Inc. */ /* * The SMB server supports its local file system operations using * kernel-style VOP_... calls. This layer simulates creating and * finding vnodes for "libfksmbsrv". * * The vnodes manged here are always paired with a private struct * (see fakefs_node_t) to hold the details we need to find them * in our cache and the file descriptor used in simulations. * * The actual VOP_... and VFS_... call simulations are in other * files, generall named after the original kernel ones. * (eg. fake_vfs.c) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vncache.h" #define VTOF(vp) ((struct fakefs_node *)(vp)->v_data) #define FTOV(fnp) ((fnp)->fn_vnode) /* Private to the fake vnode impl. */ typedef struct fakefs_node { avl_node_t fn_avl_node; vnode_t *fn_vnode; dev_t fn_st_dev; ino_t fn_st_ino; int fn_fd; int fn_mode; } fakefs_node_t; typedef struct fnode_vnode { struct fakefs_node fn; struct vnode vn; } fnode_vnode_t; /* * You can dump this AVL tree with mdb, i.e. * fncache_avl ::walk avl |::print fakefs_node_t * fncache_avl ::walk avl |::print fnode_vnode_t fn vn.v_path */ avl_tree_t fncache_avl; kmutex_t fncache_lock; /* * Fake node / vnode cache. */ kmem_cache_t *fn_cache; /* ARGSUSED */ static int fn_cache_constructor(void *buf, void *cdrarg, int kmflags) { fnode_vnode_t *fvp = buf; bzero(fvp, sizeof (*fvp)); fvp->fn.fn_vnode = &fvp->vn; fvp->fn.fn_fd = -1; fvp->vn.v_data = &fvp->fn; mutex_init(&fvp->vn.v_lock, NULL, MUTEX_DEFAULT, NULL); return (0); } /* ARGSUSED */ static void fn_cache_destructor(void *buf, void *cdrarg) { fnode_vnode_t *fvp = buf; mutex_destroy(&fvp->vn.v_lock); } /* * Used by file systems when fs-specific nodes (e.g., ufs inodes) are * cached by the file system and vnodes remain associated. */ void vn_recycle(vnode_t *vp) { fakefs_node_t *fnp = VTOF(vp); ASSERT(fnp->fn_fd == -1); vp->v_rdcnt = 0; vp->v_wrcnt = 0; if (vp->v_path) { strfree(vp->v_path); vp->v_path = NULL; } } /* * Used to reset the vnode fields including those that are directly accessible * as well as those which require an accessor function. * * Does not initialize: * synchronization objects: v_lock, v_vsd_lock, v_nbllock, v_cv * v_data (since FS-nodes and vnodes point to each other and should * be updated simultaneously) * v_op (in case someone needs to make a VOP call on this object) */ void vn_reinit(vnode_t *vp) { vp->v_count = 1; vp->v_vfsp = NULL; vp->v_stream = NULL; vp->v_flag = 0; vp->v_type = VNON; vp->v_rdev = NODEV; vn_recycle(vp); } vnode_t * vn_alloc(int kmflag) { fnode_vnode_t *fvp; vnode_t *vp = NULL; fvp = kmem_cache_alloc(fn_cache, kmflag); if (fvp != NULL) { vp = &fvp->vn; vn_reinit(vp); } return (vp); } void vn_free(vnode_t *vp) { fakefs_node_t *fnp = VTOF(vp); /* * Some file systems call vn_free() with v_count of zero, * some with v_count of 1. In any case, the value should * never be anything else. */ ASSERT((vp->v_count == 0) || (vp->v_count == 1)); if (vp->v_path != NULL) { strfree(vp->v_path); vp->v_path = NULL; } ASSERT(fnp->fn_fd > 2); (void) close(fnp->fn_fd); fnp->fn_fd = -1; /* * Make sure fnp points to the beginning of fnode_vnode_t, * which is what we must pass to kmem_cache_free. */ CTASSERT(offsetof(fnode_vnode_t, fn) == 0); kmem_cache_free(fn_cache, fnp); } static int fncache_cmp(const void *v1, const void *v2) { const fakefs_node_t *np1 = v1; const fakefs_node_t *np2 = v2; /* The args are really fnode_vnode_t */ CTASSERT(offsetof(fnode_vnode_t, fn) == 0); if (np1->fn_st_dev < np2->fn_st_dev) return (-1); if (np1->fn_st_dev > np2->fn_st_dev) return (+1); if (np1->fn_st_ino < np2->fn_st_ino) return (-1); if (np1->fn_st_ino > np2->fn_st_ino) return (+1); return (0); } int vncache_cmp(const vnode_t *vp1, const vnode_t *vp2) { fakefs_node_t *np1 = VTOF(vp1); fakefs_node_t *np2 = VTOF(vp2); return (fncache_cmp(np1, np2)); } vnode_t * vncache_lookup(struct stat *st) { fakefs_node_t tmp_fn; fakefs_node_t *fnp; vnode_t *vp = NULL; tmp_fn.fn_st_dev = st->st_dev; tmp_fn.fn_st_ino = st->st_ino; mutex_enter(&fncache_lock); fnp = avl_find(&fncache_avl, &tmp_fn, NULL); if (fnp != NULL) { vp = FTOV(fnp); VN_HOLD(vp); } mutex_exit(&fncache_lock); return (vp); } vnode_t * vncache_enter(struct stat *st, vnode_t *dvp, char *name, int fd) { vnode_t *old_vp; vnode_t *new_vp; fakefs_node_t *old_fnp; fakefs_node_t *new_fnp; vfs_t *vfs; char *vpath; avl_index_t where; int len; ASSERT(fd > 2); /* * Fill in v_path * Note: fsop_root() calls with dvp=NULL */ len = strlen(name) + 1; if (dvp == NULL) { vpath = kmem_alloc(len, KM_SLEEP); (void) strlcpy(vpath, name, len); vfs = rootvfs; } else { /* add to length for parent path + "/" */ len += (strlen(dvp->v_path) + 1); vpath = kmem_alloc(len, KM_SLEEP); (void) snprintf(vpath, len, "%s/%s", dvp->v_path, name); vfs = dvp->v_vfsp; } /* Note: (vp : fnp) linkage setup in constructor */ new_vp = vn_alloc(KM_SLEEP); new_vp->v_path = vpath; new_vp->v_vfsp = vfs; new_vp->v_type = IFTOVT(st->st_mode); new_fnp = VTOF(new_vp); new_fnp->fn_fd = fd; new_fnp->fn_st_dev = st->st_dev; new_fnp->fn_st_ino = st->st_ino; old_vp = NULL; mutex_enter(&fncache_lock); old_fnp = avl_find(&fncache_avl, new_fnp, &where); if (old_fnp != NULL) { DTRACE_PROBE1(found, fakefs_node_t *, old_fnp); old_vp = FTOV(old_fnp); VN_HOLD(old_vp); } else { DTRACE_PROBE1(insert, fakefs_node_t *, new_fnp); avl_insert(&fncache_avl, new_fnp, where); } mutex_exit(&fncache_lock); /* If we lost the race, free new_vp */ if (old_vp != NULL) { vn_free(new_vp); return (old_vp); } return (new_vp); } /* * Called after a successful rename to update v_path */ void vncache_renamed(vnode_t *vp, vnode_t *to_dvp, char *to_name) { char *vpath; char *ovpath; int len; len = strlen(to_name) + 1; /* add to length for parent path + "/" */ len += (strlen(to_dvp->v_path) + 1); vpath = kmem_alloc(len, KM_SLEEP); (void) snprintf(vpath, len, "%s/%s", to_dvp->v_path, to_name); mutex_enter(&fncache_lock); ovpath = vp->v_path; vp->v_path = vpath; mutex_exit(&fncache_lock); strfree(ovpath); } /* * Last reference to this vnode is (possibly) going away. * This is normally called by vn_rele() when v_count==1. * Note that due to lock order concerns, we have to take * the fncache_lock (for the avl tree) and then recheck * v_count, which might have gained a ref during the time * we did not hold vp->v_lock. */ void vncache_inactive(vnode_t *vp) { fakefs_node_t *fnp = VTOF(vp); vnode_t *xvp; uint_t count; mutex_enter(&fncache_lock); mutex_enter(&vp->v_lock); if ((count = vp->v_count) <= 1) { /* This is (still) the last ref. */ DTRACE_PROBE1(remove, fakefs_node_t *, fnp); avl_remove(&fncache_avl, fnp); } mutex_exit(&vp->v_lock); mutex_exit(&fncache_lock); if (count > 1) return; /* * See fake_lookup_xattrdir() */ xvp = vp->v_xattrdir; vp->v_xattrdir = NULL; vn_free(vp); if (xvp != NULL) { ASSERT((xvp->v_flag & V_XATTRDIR) != 0); VN_RELE(xvp); } } int vncache_getfd(vnode_t *vp) { fakefs_node_t *fnp = VTOF(vp); ASSERT(fnp->fn_fd > 2); return (fnp->fn_fd); } /* * See fake_lookup_xattrdir() * Special case vnode creation. */ void vncache_setfd(vnode_t *vp, int fd) { fakefs_node_t *fnp = VTOF(vp); ASSERT(fnp->fn_fd == -1); ASSERT(fd > 2); fnp->fn_fd = fd; } int vncache_init(void) { fn_cache = kmem_cache_create("fn_cache", sizeof (fnode_vnode_t), VNODE_ALIGN, fn_cache_constructor, fn_cache_destructor, NULL, NULL, NULL, 0); avl_create(&fncache_avl, fncache_cmp, sizeof (fnode_vnode_t), offsetof(fnode_vnode_t, fn.fn_avl_node)); mutex_init(&fncache_lock, NULL, MUTEX_DEFAULT, NULL); return (0); } void vncache_fini(void) { mutex_destroy(&fncache_lock); avl_destroy(&fncache_avl); kmem_cache_destroy(fn_cache); } /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2024 RackTop Systems, Inc. */ #ifndef _VNCACHE_H #define _VNCACHE_H /* * Declarations for thigns in vncache.c */ #ifdef __cplusplus extern "C" { #endif struct stat; vnode_t *vncache_lookup(struct stat *); vnode_t *vncache_enter(struct stat *, vnode_t *, char *, int); void vncache_renamed(vnode_t *, vnode_t *, char *); void vncache_inactive(vnode_t *); int vncache_cmp(const vnode_t *, const vnode_t *); int vncache_getfd(vnode_t *); void vncache_setfd(vnode_t *, int); int vncache_init(void); void vncache_fini(void); #ifdef __cplusplus } #endif #endif /* _VNCACHE_H */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # HDRS= libmlsvc.h include ../Makefile.smbsrv # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2020 Tintri by DDN, Inc. All rights reserved. # # Copyright (c) 2018, Joyent, Inc. LIBRARY = libmlsvc.a VERS = .1 OBJS_COMMON = \ dfs.o \ dssetup_clnt.o \ dssetup_svc.o \ eventlog_svc.o \ eventlog_log.o \ lsalib.o \ lsar_clnt.o \ lsar_svc.o \ mlsvc_client.o \ mlsvc_domain.o \ mlsvc_init.o \ mlsvc_netr.o \ mlsvc_util.o \ msgsvc_svc.o \ netdfs.o \ netr_auth.o \ netr_logon.o \ netr_ssp.o \ samlib.o \ samr_clnt.o \ samr_svc.o \ smb_autohome.o \ smb_logon.o \ smb_share.o \ smb_quota.o \ smbrdr_glue.o \ spoolss_svc.o \ srvsvc_clnt.o \ srvsvc_sd.o \ srvsvc_svc.o \ svcctl_scm.o \ svcctl_svc.o \ winreg_svc.o \ wkssvc_svc.o # Automatically generated from .ndl files NDLLIST = \ dssetup \ eventlog \ lsarpc \ msgsvc \ netdfs \ netlogon \ samrpc \ spoolss \ srvsvc \ svcctl \ winreg OBJECTS= $(OBJS_COMMON) $(NDLLIST:%=%_ndr.o) include ../../../Makefile.lib include ../../Makefile.lib INCS += -I$(SRC)/common/smbsrv INCS += -I$(SRC)/uts/common/smbsrv/ndl LDLIBS += $(MACH_LDLIBS) # Hammerhead: Add -ldl for dlopen/dlsym/dlclose LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lnsl -lpkcs11 -lmd \ -lscf -lcmdutils -lsec -lavl -lnvpair -luutil -luuid -lgen -lzfs \ -lresolv -lc -ldl CPPFLAGS += $(INCS) -D_REENTRANT CPPFLAGS += -Dsyslog=smb_syslog # Hammerhead: converted from dmake conditional ifneq ($(ENABLE_SMB_PRINTING),$(POUND_SIGN)) CPPFLAGS += -DHAVE_CUPS endif CERRWARN += -Wno-unused-function CERRWARN += $(CNOWARN_UNINIT) # not linted SMATCH=off SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) include ../../Makefile.targ include ../../../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Hammerhead: 64-bit only - flatten smbsrv library paths MACH_LDLIBS += -L$(ROOT)/usr/lib/smbsrv include ../Makefile.com include ../../../Makefile.lib.64 DYNFLAGS += -R/usr/lib/smbsrv # Hammerhead: GNU ld needs rpath-link for mech_krb5.so.1 (transitive dep via libsmbns) DYNFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/gss install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * default timeout (TTL) values (in second) for root and link */ #define DFS_ROOT_TIMEOUT 300 #define DFS_LINK_TIMEOUT 1800 /* * DFS link data format in reparse point * * ver:state:prop:timeout:guid:ntarget:cmntlen:comment * [[:tserver:tshare:tstate:pclass:prank]...] */ #define DFS_LINK_V1 1 #define DFS_LINK_HDR_NFIELDS 7 /* # fields in header section */ #define DFS_LINK_TRGT_NFIELDS 5 /* # fields for each target */ #define DFS_ROOT_XATTR "SUNWdfs.rootinfo" #define DFS_INFO_ALL 0 static void *dfs_intr_hdl = NULL; static struct { int (*dfsops_remote_count)(uint32_t *); } dfs_intr_ops; /* * Namespace cache * * Caches links' UNC and filesystem path where the key is the UNC path. */ static smb_cache_t dfs_nscache; static char dfs_nbname[NETBIOS_NAME_SZ]; /* * The name of cached namespace. This will be the only * exported namespace until hosting multiple namespaces * is supported */ static char dfs_cached_ns[MAXNAMELEN]; static mutex_t dfs_nsmtx; /* * Lock for accessing root information (extended attribute) */ static rwlock_t dfs_root_rwl; extern uint32_t srvsvc_shr_setdfsroot(smb_share_t *, boolean_t); /* * Namespace functions */ static boolean_t dfs_namespace_findlink(const char *, char *, char *, size_t); static void *dfs_namespace_cache(void *); static boolean_t dfs_namespace_iscached(const char *); /* * Root functions */ static int dfs_root_add(const char *, dfs_info_t *); static uint32_t dfs_root_remove(const char *); static uint32_t dfs_root_encode(dfs_info_t *, char **, size_t *); static uint32_t dfs_root_decode(dfs_info_t *, char *, size_t, uint32_t); static uint32_t dfs_root_isvalidstate(uint32_t); static int dfs_root_xopen(const char *, int); static void dfs_root_xclose(int); static uint32_t dfs_root_xwrite(int, dfs_info_t *); static uint32_t dfs_root_xread(int, dfs_info_t *, uint32_t); /* * Link functions */ static uint32_t dfs_link_encode(dfs_info_t *, char *, size_t); static uint32_t dfs_link_decode(dfs_info_t *, char *, uint32_t); static uint32_t dfs_link_commit(const char *, dfs_info_t *); static boolean_t dfs_link_isvalidstate(uint32_t); /* * Target functions */ static void dfs_target_init(dfs_target_t *, const char *, const char *, uint32_t); static int dfs_target_find(dfs_target_t *, uint32_t, const char *, const char *); static boolean_t dfs_target_isvalidstate(uint32_t); /* * Cache functions */ static uint32_t dfs_cache_add_byunc(const char *, const char *, uint32_t); static void dfs_cache_populate(const char *, const char *); static int dfs_cache_cmp(const void *, const void *); static void dfs_cache_flush(const char *); static uint32_t dfs_cache_nscount(void); /* * Utility functions */ static boolean_t dfs_path_isdir(const char *); static uint32_t dfs_modinfo(uint32_t, dfs_info_t *, dfs_info_t *, uint32_t); /* * DFS module initializationr: * * - creates the namespace cache * - gets system's NetBIOS name */ void dfs_init(void) { smb_cache_create(&dfs_nscache, 0, dfs_cache_cmp, free, bcopy, sizeof (dfs_nscnode_t)); if (smb_getnetbiosname(dfs_nbname, sizeof (dfs_nbname)) != 0) { syslog(LOG_ERR, "dfs: can't get machine name"); return; } bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops)); if ((dfs_intr_hdl = smb_dlopen()) == NULL) return; if ((dfs_intr_ops.dfsops_remote_count = (int (*)())dlsym(dfs_intr_hdl, "smb_dfs_remote_count")) == NULL) { smb_dlclose(dfs_intr_hdl); dfs_intr_hdl = NULL; bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops)); } } /* * DFS module cleanup: * * - destroys the namespace cache */ void dfs_fini(void) { smb_dlclose(dfs_intr_hdl); smb_cache_destroy(&dfs_nscache); } /* * To successfully handle some of link/root requests, some * file system operations need to be performed. These operations * should take place on behalf of the connected user (typically * Administrator) and to do so we need to have an infrastructure * in place so that smbd can act as a client and sends request to * the kernel. Right now, we lack this infrastructure, so we make * a compromise by temporarily enabling some privileges for smbd * to be able to fulfill various link/root requests. */ void dfs_setpriv(priv_op_t op) { (void) priv_set(op, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, PRIV_FILE_DAC_EXECUTE, PRIV_FILE_DAC_SEARCH, NULL); } /* * ======================== * Namespace API (public) * ======================== */ /* * Launches a thread to cache the specified namespace */ void dfs_namespace_load(const char *name) { pthread_t thr; pthread_attr_t tattr; char *rootshr; int rc; if ((rootshr = strdup(name)) == NULL) { syslog(LOG_ERR, "dfs: failed to load %s namespace (no memory)", name); return; } (void) pthread_attr_init(&tattr); (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&thr, &tattr, dfs_namespace_cache, rootshr); (void) pthread_attr_destroy(&tattr); if (rc != 0) syslog(LOG_ERR, "dfs: fail to loading %s namespace (%d)", name, rc); } /* * Flushes the cache when a DFS root share is removed */ void /*ARGSUSED*/ dfs_namespace_unload(const char *name) { dfs_cache_flush(name); } /* * Returns the file system path for the given share if it * is a DFS root share. * If 'path' is NULL, this function only indicates whether * or not the given share represents a DFS namespace */ uint32_t dfs_namespace_path(const char *name, char *path, size_t pathsz) { smb_share_t si; if (smb_shr_get((char *)name, &si) != NERR_Success) return (ERROR_NOT_FOUND); if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0) return (ERROR_NOT_FOUND); if (!dfs_namespace_iscached(name)) return (ERROR_NOT_FOUND); if (path != NULL) (void) strlcpy(path, si.shr_path, pathsz); return (ERROR_SUCCESS); } /* * Returns the number of DFS root shares i.e. the number * of standalone namespaces. */ uint32_t dfs_namespace_count(void) { uint32_t nroot = 0; int rc; if (dfs_intr_ops.dfsops_remote_count != NULL && (rc = dfs_intr_ops.dfsops_remote_count(&nroot)) != 0) { /* * If this call fails, let's assume there's at least one root * namespace already configured. The interposer library cannot * confirm or deny the presence of a namespace, so let's take * the safe approach and assume one exists. */ nroot = 1; syslog(LOG_WARNING, "dfs: dfsops_remote_count() failed: %d, " "assuming one namespace exists", rc); } nroot += dfs_cache_nscount(); return (nroot); } /* * Creates a DFS root with the given name and comment. * * This function does not create the root share, it * should already exist. */ uint32_t dfs_namespace_add(const char *rootshr, const char *cmnt) { dfs_info_t info; dfs_target_t t; smb_share_t si; uuid_t uuid; uint32_t status; if (*rootshr == '\\') { /* Windows has a special case here! */ return (ERROR_BAD_PATHNAME); } if (smb_shr_get((char *)rootshr, &si) != NERR_Success) return (NERR_NetNameNotFound); (void) mutex_lock(&dfs_nsmtx); if (smb_strcasecmp(dfs_cached_ns, rootshr, 0) == 0) { /* This DFS root is already exported */ (void) mutex_unlock(&dfs_nsmtx); return (ERROR_FILE_EXISTS); } if (*dfs_cached_ns != '\0') { syslog(LOG_WARNING, "dfs: trying to add %s namespace." " Only one standalone namespace is supported." " A namespace is already exported for %s", rootshr, dfs_cached_ns); (void) mutex_unlock(&dfs_nsmtx); return (ERROR_NOT_SUPPORTED); } bzero(&info, sizeof (info)); if (cmnt) (void) strlcpy(info.i_comment, cmnt, sizeof (info.i_comment)); info.i_state = DFS_VOLUME_STATE_OK | DFS_VOLUME_FLAVOR_STANDALONE; info.i_timeout = DFS_ROOT_TIMEOUT; info.i_propflags = 0; uuid_generate_random(uuid); uuid_unparse(uuid, info.i_guid); dfs_target_init(&t, dfs_nbname, rootshr, DFS_STORAGE_STATE_ONLINE); info.i_ntargets = 1; info.i_targets = &t; if ((status = dfs_root_add(si.shr_path, &info)) != ERROR_SUCCESS) { (void) mutex_unlock(&dfs_nsmtx); return (status); } status = srvsvc_shr_setdfsroot(&si, B_TRUE); if (status == ERROR_SUCCESS) { (void) dfs_cache_add_byname(rootshr, NULL, DFS_OBJECT_ROOT); (void) strlcpy(dfs_cached_ns, rootshr, sizeof (dfs_cached_ns)); (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 1); } (void) mutex_unlock(&dfs_nsmtx); return (status); } /* * Removes the namespace and all the links in it. */ uint32_t dfs_namespace_remove(const char *name) { smb_cache_cursor_t cursor; dfs_nscnode_t nscnode; smb_share_t si; uint32_t status; if (smb_shr_get((char *)name, &si) != NERR_Success) return (ERROR_NOT_FOUND); if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0) return (ERROR_NOT_FOUND); if ((status = dfs_root_remove(si.shr_path)) != ERROR_SUCCESS) return (status); status = srvsvc_shr_setdfsroot(&si, B_FALSE); if (status != ERROR_SUCCESS) syslog(LOG_WARNING, "dfs: failed to disable root share %s (%d)", name, status); if (!dfs_namespace_iscached(name)) return (ERROR_SUCCESS); smb_cache_iterinit(&dfs_nscache, &cursor); while (smb_cache_iterate(&dfs_nscache, &cursor, &nscnode)) { if (nscnode.nsc_type == DFS_OBJECT_ROOT) continue; status = dfs_link_remove(nscnode.nsc_fspath, NULL, NULL); if (status != ERROR_SUCCESS) syslog(LOG_WARNING, "dfs: failed to remove %s (%d)", nscnode.nsc_fspath, status); } dfs_cache_flush(name); /* TODO: remove empty dirs */ return (ERROR_SUCCESS); } /* * Determines the DFS namespace flavor. */ uint32_t dfs_namespace_getflavor(const char *name) { char rootdir[DFS_PATH_MAX]; dfs_info_t info; if (dfs_namespace_path(name, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS) return (0); /* get flavor info from state info (info level 2) */ if (dfs_root_getinfo(rootdir, &info, 2) != ERROR_SUCCESS) return (0); return (info.i_state & DFS_VOLUME_FLAVORS); } /* * ================== * Root API (public) * ================== */ /* * Retrieves the information of the root specified by its path. * * Info level (1) only needs the UNC path which is not stored, * it is constructed so the function will return without * accessing the backend storage. */ uint32_t dfs_root_getinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl) { uint32_t status = ERROR_INTERNAL_ERROR; int xfd; bzero(info, sizeof (dfs_info_t)); info->i_type = DFS_OBJECT_ROOT; if (infolvl == 1) return (ERROR_SUCCESS); (void) rw_rdlock(&dfs_root_rwl); if ((xfd = dfs_root_xopen(rootdir, O_RDONLY)) > 0) { status = dfs_root_xread(xfd, info, infolvl); dfs_root_xclose(xfd); } (void) rw_unlock(&dfs_root_rwl); return (status); } /* * Sets the provided information for the specified root or root target. * Root is specified by 'rootdir' and the target is specified by * (t_server, t_share) pair. Only information items needed for given * information level (infolvl) is valid in the passed DFS info structure * 'info'. */ uint32_t dfs_root_setinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl) { dfs_info_t curinfo; uint32_t status = ERROR_SUCCESS; int xfd; (void) rw_wrlock(&dfs_root_rwl); if ((xfd = dfs_root_xopen(rootdir, O_RDWR)) < 0) { (void) rw_unlock(&dfs_root_rwl); return (ERROR_INTERNAL_ERROR); } status = dfs_root_xread(xfd, &curinfo, DFS_INFO_ALL); if (status != ERROR_SUCCESS) { dfs_root_xclose(xfd); (void) rw_unlock(&dfs_root_rwl); return (status); } status = dfs_modinfo(DFS_OBJECT_ROOT, &curinfo, info, infolvl); if (status == ERROR_SUCCESS) status = dfs_root_xwrite(xfd, &curinfo); dfs_root_xclose(xfd); (void) rw_unlock(&dfs_root_rwl); dfs_info_free(&curinfo); return (status); } /* * ================== * Link API (public) * ================== */ /* * Gets the status of the given path as a link */ uint32_t dfs_link_stat(const char *path, uint32_t *stat) { if (smb_reparse_stat(path, stat) != 0) return (ERROR_INTERNAL_ERROR); switch (*stat) { case SMB_REPARSE_NOTFOUND: *stat = DFS_STAT_NOTFOUND; break; case SMB_REPARSE_NOTREPARSE: *stat = DFS_STAT_NOTLINK; break; case SMB_REPARSE_ISREPARSE: *stat = DFS_STAT_ISREPARSE; if (smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, NULL) == 0) *stat = DFS_STAT_ISDFS; break; default: *stat = DFS_STAT_UNKNOWN; break; } return (ERROR_SUCCESS); } /* * Creates a new DFS link or adds a new target to an existing link */ uint32_t dfs_link_add(const char *path, const char *server, const char *share, const char *cmnt, uint32_t flags, boolean_t *newlink) { dfs_info_t info; dfs_target_t *t; int ntargets; uint32_t status; uint32_t stat; *newlink = B_FALSE; if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS) return (status); switch (stat) { case DFS_STAT_NOTFOUND: case DFS_STAT_ISREPARSE: /* Create a new DFS link */ status = dfs_link_getinfo(NULL, &info, DFS_INFO_ALL); if (status != ERROR_SUCCESS) return (status); (void) strlcpy(info.i_comment, (cmnt) ? cmnt : "", sizeof (info.i_comment)); *newlink = B_TRUE; break; case DFS_STAT_ISDFS: /* Add a target to an existing link */ if (flags & DFS_ADD_VOLUME) return (ERROR_FILE_EXISTS); status = dfs_link_getinfo(path, &info, DFS_INFO_ALL); if (status != ERROR_SUCCESS) return (status); break; case DFS_STAT_NOTLINK: /* specified path points to a non-reparse object */ return (ERROR_FILE_EXISTS); default: return (ERROR_INTERNAL_ERROR); } /* checks to see if the target already exists */ ntargets = info.i_ntargets; if (dfs_target_find(info.i_targets, ntargets, server, share) != -1) { dfs_info_free(&info); return (ERROR_FILE_EXISTS); } /* add the new target */ t = realloc(info.i_targets, (ntargets + 1) * sizeof (dfs_target_t)); if (t == NULL) { dfs_info_free(&info); return (ERROR_NOT_ENOUGH_MEMORY); } info.i_targets = t; dfs_target_init(&info.i_targets[ntargets], server, share, DFS_STORAGE_STATE_ONLINE); info.i_ntargets++; status = dfs_link_commit(path, &info); dfs_info_free(&info); return (status); } /* * Removes a link or a link target from a DFS namespace. A link can be * removed regardless of the number of targets associated with it. * * 'server' and 'share' parameters specify a target, so if they are NULL * it means the link should be removed, otherwise the specified target * is removed if found. */ uint32_t dfs_link_remove(const char *path, const char *server, const char *share) { dfs_info_t info; uint32_t status, stat; int rc, idx; if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS) return (status); if (stat != DFS_STAT_ISDFS) return (ERROR_NOT_FOUND); if (server == NULL && share == NULL) { /* remove the link */ if (smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE) != 0) return (ERROR_INTERNAL_ERROR); return (ERROR_SUCCESS); } /* remove the specified target in the link */ status = dfs_link_getinfo(path, &info, DFS_INFO_ALL); if (status != ERROR_SUCCESS) return (status); /* checks to see if the target exists */ idx = dfs_target_find(info.i_targets, info.i_ntargets, server, share); if (idx != -1) { bcopy(&info.i_targets[idx + 1], &info.i_targets[idx], (info.i_ntargets - idx - 1) * sizeof (dfs_target_t)); info.i_ntargets--; } else { dfs_info_free(&info); return (ERROR_FILE_NOT_FOUND); } if (info.i_ntargets == 0) { /* if last target, then remove the link */ rc = smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE); status = (rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR; } else { status = dfs_link_commit(path, &info); } dfs_info_free(&info); return (status); } /* * Sets the provided information for the specified link or link target. * Link is specified by 'path' and the target is specified by * (t_server, t_share) pair. Only information items needed for given * information level (infolvl) is valid in the passed DFS info structure * 'info'. */ uint32_t dfs_link_setinfo(const char *path, dfs_info_t *info, uint32_t infolvl) { dfs_info_t curinfo; uint32_t status; status = dfs_link_getinfo(path, &curinfo, DFS_INFO_ALL); if (status != ERROR_SUCCESS) return (status); status = dfs_modinfo(DFS_OBJECT_LINK, &curinfo, info, infolvl); if (status == ERROR_SUCCESS) status = dfs_link_commit(path, &curinfo); dfs_info_free(&curinfo); return (status); } /* * Gets the DFS link info. * * If path is NULL, it just does some initialization. * * Info level (1) only needs the UNC path which is not * stored, it is constructed so the function will return * without accessing the backend storage. */ uint32_t dfs_link_getinfo(const char *path, dfs_info_t *info, uint32_t infolvl) { char *link_data; uint32_t status; uuid_t uuid; int rc; bzero(info, sizeof (dfs_info_t)); info->i_type = DFS_OBJECT_LINK; if (path == NULL) { info->i_state = DFS_VOLUME_STATE_OK; info->i_timeout = DFS_LINK_TIMEOUT; info->i_propflags = 0; uuid_generate_random(uuid); uuid_unparse(uuid, info->i_guid); return (ERROR_SUCCESS); } if (infolvl == 1) return (ERROR_SUCCESS); rc = smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, &link_data); if (rc != 0) return (ERROR_INTERNAL_ERROR); status = dfs_link_decode(info, link_data, infolvl); free(link_data); return (status); } /* * =================== * Cache API (public) * =================== */ /* * Adds an entry with given DFS name (root sharename) and relative path * to the share (relpath) and the specified entry type (i.e. root/link) * to the namespace cache. */ uint32_t dfs_cache_add_byname(const char *name, const char *relpath, uint32_t type) { char uncpath[DFS_PATH_MAX]; char fspath[DFS_PATH_MAX]; smb_share_t si; if (smb_shr_get((char *)name, &si) != NERR_Success) return (ERROR_NOT_FOUND); if (type == DFS_OBJECT_ROOT) { (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s", dfs_nbname, name); return (dfs_cache_add_byunc(uncpath, si.shr_path, type)); } /* add link entry */ (void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", si.shr_path, relpath); (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s\\%s", dfs_nbname, name, relpath); /* relpath may contain '/' */ (void) strsubst(uncpath, '/', '\\'); return (dfs_cache_add_byunc(uncpath, fspath, type)); } /* * Removes the namespace cache entry for the given link * in the namespace ('name') with 'relpath' */ void dfs_cache_remove(const char *name, const char *relpath) { dfs_nscnode_t dn; (void) snprintf(dn.nsc_uncpath, sizeof (dn.nsc_uncpath), "\\\\%s\\%s\\%s", dfs_nbname, name, relpath); /* relpath may contain '/' */ (void) strsubst(dn.nsc_uncpath, '/', '\\'); smb_cache_remove(&dfs_nscache, &dn); } /* * Get the DFS data for the specified cache entry */ uint32_t dfs_cache_getinfo(dfs_nscnode_t *dn, dfs_info_t *info, uint32_t infolvl) { uint32_t status; if (dn->nsc_type == DFS_OBJECT_LINK) status = dfs_link_getinfo(dn->nsc_fspath, info, infolvl); else status = dfs_root_getinfo(dn->nsc_fspath, info, infolvl); (void) strlcpy(info->i_uncpath, dn->nsc_uncpath, sizeof (info->i_uncpath)); if (status == ERROR_SUCCESS) dfs_info_trace("dfs_cache_getinfo", info); return (status); } /* * Returns the number of cache entries i.e. the number of * root(s) and link(s) */ uint32_t dfs_cache_num(void) { return (smb_cache_num(&dfs_nscache)); } void dfs_cache_iterinit(smb_cache_cursor_t *cursor) { smb_cache_iterinit(&dfs_nscache, cursor); } boolean_t dfs_cache_iterate(smb_cache_cursor_t *cursor, dfs_nscnode_t *dn) { return (smb_cache_iterate(&dfs_nscache, cursor, dn)); } /* * ================== * Misc API (public) * ================== */ /* * This is the function that is called by smbd door server to * fullfil a GetReferrals request from smbsrv kernel module * * 'reftype' specifies the requested referral type. If it is * DFS_REFERRAL_ROOT then dfs_path should point to a namespace * root. If it is DFS_REFERRAL_LINK then dfs_path should CONTAIN * a link, in which case this function will find the link and * returns its target information. */ uint32_t dfs_get_referrals(const char *dfs_path, dfs_reftype_t reftype, dfs_info_t *referrals) { dfs_path_t path; smb_unc_t *unc; char linkpath[DFS_PATH_MAX]; uint32_t status; status = dfs_path_parse(&path, dfs_path, DFS_OBJECT_ANY); if (status != ERROR_SUCCESS) return (status); dfs_setpriv(PRIV_ON); referrals->i_type = path.p_type; switch (reftype) { case DFS_REFERRAL_ROOT: if (path.p_type != DFS_OBJECT_ROOT) { status = ERROR_INVALID_PARAMETER; break; } status = dfs_root_getinfo((const char *)path.p_fspath, referrals, DFS_INFO_ALL); (void) strlcpy(referrals->i_uncpath, dfs_path, DFS_PATH_MAX); break; case DFS_REFERRAL_LINK: if (path.p_type != DFS_OBJECT_LINK) { status = ERROR_INVALID_PARAMETER; break; } unc = &path.p_unc; if (!dfs_namespace_findlink(unc->unc_share, unc->unc_path, linkpath, DFS_PATH_MAX)) { status = ERROR_NOT_FOUND; break; } status = dfs_link_getinfo(linkpath, referrals, DFS_INFO_ALL); (void) snprintf(referrals->i_uncpath, DFS_PATH_MAX, "/%s/%s/%s", unc->unc_server, unc->unc_share, unc->unc_path); break; default: status = ERROR_INVALID_PARAMETER; break; } dfs_setpriv(PRIV_OFF); dfs_path_free(&path); return (status); } /* * Takes a DFS path in UNC format (dfs_path) and parse it into a dfs_path_t * structure. * * dfs_path_free() MUST be called to free the allocated memory in this * function. * * Returns: * * ERROR_INVALID_PARAMETER path is not a valid UNC or not valid for the * specified object type * ERROR_NOT_ENOUGH_MEMORY not enough memory to peform the parse * ERROR_NOT_FOUND namespace specified does not exist */ uint32_t dfs_path_parse(dfs_path_t *path, const char *dfs_path, uint32_t path_type) { char rootdir[DFS_PATH_MAX]; smb_unc_t *unc; uint32_t status = ERROR_SUCCESS; int rc; bzero(path, sizeof (dfs_path_t)); unc = &path->p_unc; rc = smb_unc_init(dfs_path, unc); switch (rc) { case EINVAL: return (ERROR_INVALID_PARAMETER); case ENOMEM: return (ERROR_NOT_ENOUGH_MEMORY); default: break; } if (dfs_namespace_path(unc->unc_share, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS) { smb_unc_free(unc); return (ERROR_NOT_FOUND); } if (path_type == DFS_OBJECT_ANY) path->p_type = (unc->unc_path != NULL) ? DFS_OBJECT_LINK : DFS_OBJECT_ROOT; else path->p_type = path_type; switch (path->p_type) { case DFS_OBJECT_LINK: if ((unc->unc_path == NULL) || (*unc->unc_path == '\0')) status = ERROR_NOT_FOUND; else (void) snprintf(path->p_fspath, sizeof (path->p_fspath), "%s/%s", rootdir, unc->unc_path); break; case DFS_OBJECT_ROOT: if (unc->unc_path == NULL) (void) strlcpy(path->p_fspath, rootdir, sizeof (path->p_fspath)); else status = ERROR_INVALID_PARAMETER; break; default: status = ERROR_INVALID_PARAMETER; } if (status != ERROR_SUCCESS) smb_unc_free(unc); return (status); } /* * Frees the allocated memory for p_unc field of the passed path */ void dfs_path_free(dfs_path_t *path) { if (path != NULL) smb_unc_free(&path->p_unc); } /* * Free the allocated memory for targets in the given info * structure */ void dfs_info_free(dfs_info_t *info) { if (info) free(info->i_targets); } /* * Trace the given DFS info structure */ void dfs_info_trace(const char *msg, dfs_info_t *info) { dfs_target_t *t; int i; smb_tracef("%s", msg); if (info == NULL) return; smb_tracef("UNC\t%s", info->i_uncpath); smb_tracef("comment\t%s", info->i_comment); smb_tracef("GUID\t%s", info->i_guid); smb_tracef("state\t%X", info->i_state); smb_tracef("timeout\t%d", info->i_timeout); smb_tracef("props\t%X", info->i_propflags); smb_tracef("# targets\t%X", info->i_ntargets); if (info->i_targets == NULL) return; for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) { smb_tracef("[%d] \\\\%s\\%s", i, t->t_server, t->t_share); smb_tracef("[%d] state\t%X", i, t->t_state); smb_tracef("[%d] priority\t%d:%d", i, t->t_priority.p_class, t->t_priority.p_rank); } } /* * Search the path specified by 'relpath' to see if it contains * a DFS link starting from the last component. If a link is found * the full path is returned in 'linkpath' */ static boolean_t dfs_namespace_findlink(const char *name, char *relpath, char *linkpath, size_t bufsz) { char rootdir[DFS_PATH_MAX]; uint32_t stat; char *p; if (dfs_namespace_path(name, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS) return (B_FALSE); (void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath); for (;;) { if (dfs_link_stat(linkpath, &stat) != ERROR_SUCCESS) return (B_FALSE); if (stat == DFS_STAT_ISDFS) return (B_TRUE); if ((p = strrchr(relpath, '/')) == NULL) return (B_FALSE); *p = '\0'; (void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath); } /*NOTREACHED*/ return (B_FALSE); } /* * Caches the specified namespace */ static void * dfs_namespace_cache(void *arg) { char *share = arg; char uncpath[DFS_PATH_MAX]; smb_share_t si; if (smb_shr_get(share, &si) != NERR_Success) { free(share); return (NULL); } /* * This check should be removed when multiple standalone * namespaces are supported. */ (void) mutex_lock(&dfs_nsmtx); if (*dfs_cached_ns != '\0') { syslog(LOG_WARNING, "dfs: trying to load %s namespace." " Only one standalone namespace is supported." " A namespace is already exported for %s", share, dfs_cached_ns); (void) mutex_unlock(&dfs_nsmtx); free(share); return (NULL); } (void) strlcpy(dfs_cached_ns, share, sizeof (dfs_cached_ns)); (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 1); (void) mutex_unlock(&dfs_nsmtx); (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s", dfs_nbname, share); (void) dfs_cache_add_byunc(uncpath, si.shr_path, DFS_OBJECT_ROOT); dfs_cache_populate(uncpath, si.shr_path); free(share); return (NULL); } /* * Checks whether the given name matches the name of * the cached namespace. */ static boolean_t dfs_namespace_iscached(const char *name) { boolean_t iscached; (void) mutex_lock(&dfs_nsmtx); iscached = (smb_strcasecmp(name, dfs_cached_ns, 0) == 0); (void) mutex_unlock(&dfs_nsmtx); return (iscached); } static int dfs_root_add(const char *rootdir, dfs_info_t *info) { uint32_t status = ERROR_INTERNAL_ERROR; int xfd; (void) rw_wrlock(&dfs_root_rwl); if ((xfd = dfs_root_xopen(rootdir, O_CREAT | O_TRUNC | O_RDWR)) > 0) { status = dfs_root_xwrite(xfd, info); dfs_root_xclose(xfd); } (void) rw_unlock(&dfs_root_rwl); return (status); } /* * Deletes the specified root information */ static uint32_t dfs_root_remove(const char *rootdir) { int attrdirfd; int err = 0; (void) rw_wrlock(&dfs_root_rwl); if ((attrdirfd = attropen(rootdir, ".", O_RDONLY)) > 0) { if (unlinkat(attrdirfd, DFS_ROOT_XATTR, 0) == -1) { if (errno != ENOENT) err = errno; } (void) close(attrdirfd); } else { err = errno; } (void) rw_unlock(&dfs_root_rwl); if (err != 0) { syslog(LOG_DEBUG, "dfs: failed to remove root info %s (%d)", rootdir, err); return (ERROR_INTERNAL_ERROR); } return (ERROR_SUCCESS); } /* * Opens DFS root directory's extended attribute with the given mode. */ static int dfs_root_xopen(const char *rootdir, int oflag) { int dfd; int xfd = -1; int err = 0; if ((dfd = open(rootdir, O_RDONLY)) > 0) { xfd = openat(dfd, DFS_ROOT_XATTR, oflag | O_XATTR, 0600); if (xfd == -1) err = errno; (void) close(dfd); } else { err = errno; } if (err != 0) { syslog(LOG_DEBUG, "dfs: failed to open root directory %s (%d)", rootdir, err); } return (xfd); } /* * Closes given extended attribute file descriptor */ static void dfs_root_xclose(int xfd) { (void) close(xfd); } /* * Writes the given DFS data in the DFS root directory's * extended attribute specified with xfd file descriptor. */ static uint32_t dfs_root_xwrite(int xfd, dfs_info_t *info) { size_t nbytes; char *buf = NULL; size_t buflen; uint32_t status; if ((status = dfs_root_encode(info, &buf, &buflen)) != ERROR_SUCCESS) return (status); (void) lseek(xfd, 0, SEEK_SET); nbytes = write(xfd, buf, buflen); free(buf); return ((nbytes == buflen) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR); } /* * Reads DFS root information from its directory extended attribute * and parse it into given dfs_info_t structure */ static uint32_t dfs_root_xread(int xfd, dfs_info_t *info, uint32_t infolvl) { struct stat statbuf; uint32_t status; char *buf; if (fstat(xfd, &statbuf) != 0) return (ERROR_INTERNAL_ERROR); if ((buf = malloc(statbuf.st_size)) == NULL) return (ERROR_NOT_ENOUGH_MEMORY); if (read(xfd, buf, statbuf.st_size) == statbuf.st_size) status = dfs_root_decode(info, buf, statbuf.st_size, infolvl); else status = ERROR_INTERNAL_ERROR; free(buf); return (status); } /* * Encodes (packs) DFS information in 'info' into a flat * buffer in a name-value format. This function allocates a * buffer with appropriate size to contain all the information * so the caller MUST free the allocated memory by calling free(). */ static uint32_t dfs_root_encode(dfs_info_t *info, char **buf, size_t *bufsz) { dfs_target_t *t; nvlist_t *nvl; int rc; if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) return (ERROR_NOT_ENOUGH_MEMORY); rc = nvlist_add_string(nvl, "comment", info->i_comment); rc |= nvlist_add_string(nvl, "guid", info->i_guid); rc |= nvlist_add_uint32(nvl, "state", info->i_state); rc |= nvlist_add_uint32(nvl, "timeout", info->i_timeout); rc |= nvlist_add_uint32(nvl, "propflags", info->i_propflags); t = info->i_targets; rc |= nvlist_add_string(nvl, "t_server", t->t_server); rc |= nvlist_add_string(nvl, "t_share", t->t_share); rc |= nvlist_add_uint32(nvl, "t_state", t->t_state); rc |= nvlist_add_uint32(nvl, "t_priority_class", t->t_priority.p_class); rc |= nvlist_add_uint16(nvl, "t_priority_rank", t->t_priority.p_rank); if (rc == 0) rc = nvlist_pack(nvl, buf, bufsz, NV_ENCODE_NATIVE, 0); nvlist_free(nvl); return ((rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR); } /* * Decodes (unpack) provided buffer which contains a list of name-value * pairs into given dfs_info_t structure */ static uint32_t dfs_root_decode(dfs_info_t *info, char *buf, size_t bufsz, uint32_t infolvl) { nvlist_t *nvl; char *cmnt, *guid; char *t_server, *t_share; uint32_t t_state; uint32_t t_priority_class; uint16_t t_priority_rank; boolean_t decode_priority = B_FALSE; int rc; if (nvlist_unpack(buf, bufsz, &nvl, 0) != 0) return (ERROR_INTERNAL_ERROR); rc = nvlist_lookup_string(nvl, "comment", &cmnt); rc |= nvlist_lookup_string(nvl, "guid", &guid); rc |= nvlist_lookup_uint32(nvl, "state", &info->i_state); rc |= nvlist_lookup_uint32(nvl, "timeout", &info->i_timeout); rc |= nvlist_lookup_uint32(nvl, "propflags", &info->i_propflags); if (rc != 0) { nvlist_free(nvl); return (ERROR_INTERNAL_ERROR); } (void) strlcpy(info->i_comment, (cmnt) ? cmnt : "", sizeof (info->i_comment)); (void) strlcpy(info->i_guid, (guid) ? guid : "", sizeof (info->i_guid)); info->i_targets = NULL; info->i_ntargets = 1; switch (infolvl) { case DFS_INFO_ALL: case 3: case 4: /* need target information */ break; case 6: case 9: /* need target and priority information */ decode_priority = B_TRUE; break; default: nvlist_free(nvl); return (ERROR_SUCCESS); } info->i_targets = malloc(sizeof (dfs_target_t)); if (info->i_targets == NULL) { nvlist_free(nvl); return (ERROR_NOT_ENOUGH_MEMORY); } rc = nvlist_lookup_string(nvl, "t_server", &t_server); rc |= nvlist_lookup_string(nvl, "t_share", &t_share); rc |= nvlist_lookup_uint32(nvl, "t_state", &t_state); if (rc != 0) { nvlist_free(nvl); free(info->i_targets); return (ERROR_INTERNAL_ERROR); } dfs_target_init(info->i_targets, t_server, t_share, t_state); if (decode_priority) { rc = nvlist_lookup_uint32(nvl, "t_priority_class", &t_priority_class); if (rc == 0) rc = nvlist_lookup_uint16(nvl, "t_priority_rank", &t_priority_rank); if (rc != 0 && rc != ENOENT) { nvlist_free(nvl); free(info->i_targets); return (ERROR_INTERNAL_ERROR); } else if (rc == 0) { info->i_targets->t_priority.p_class = t_priority_class; info->i_targets->t_priority.p_rank = t_priority_rank; } } nvlist_free(nvl); return (ERROR_SUCCESS); } /* * Determines if the passed state is valid for a DFS root * * This is based on test results against Win2003 and in some cases * does not match [MS-DFSNM] spec. */ static uint32_t dfs_root_isvalidstate(uint32_t state) { switch (state) { case DFS_VOLUME_STATE_OK: case DFS_VOLUME_STATE_RESYNCHRONIZE: return (ERROR_SUCCESS); case DFS_VOLUME_STATE_INCONSISTENT: case DFS_VOLUME_STATE_FORCE_SYNC: return (ERROR_INVALID_PARAMETER); case DFS_VOLUME_STATE_OFFLINE: case DFS_VOLUME_STATE_ONLINE: case DFS_VOLUME_STATE_STANDBY: return (ERROR_NOT_SUPPORTED); default: break; } return (ERROR_INVALID_PARAMETER); } /* * Decodes the link info from given string buffer (buf) into * dfs_info_t structure. */ static uint32_t dfs_link_decode(dfs_info_t *info, char *buf, uint32_t infolvl) { char *lfield[DFS_LINK_HDR_NFIELDS]; dfs_target_t *t; uint32_t linkver; uint32_t cmntlen; uint32_t cpylen; int i, j; /* * Header format * ver:state:prop:timeout:guid:ntarget:cmntlen:comment: */ for (i = 0; i < DFS_LINK_HDR_NFIELDS; i++) { if ((lfield[i] = strsep((char **)&buf, ":")) == NULL) return (ERROR_INVALID_DATA); } i = 0; linkver = strtoul(lfield[i++], NULL, 10); if (linkver != DFS_LINK_V1) return (ERROR_INVALID_DATA); info->i_state = strtoul(lfield[i++], NULL, 10); info->i_propflags = strtoul(lfield[i++], NULL, 10); info->i_timeout = strtoul(lfield[i++], NULL, 10); (void) strlcpy(info->i_guid, lfield[i++], sizeof (info->i_guid)); info->i_ntargets = strtoul(lfield[i++], NULL, 10); info->i_targets = NULL; cpylen = cmntlen = strtoul(lfield[i++], NULL, 10); if (cmntlen > sizeof (info->i_comment)) cpylen = sizeof (info->i_comment); else if (cmntlen != 0) cpylen = cmntlen + 1; (void) strlcpy(info->i_comment, buf, cpylen); buf += (cmntlen + 1); switch (infolvl) { case DFS_INFO_ALL: case 3: case 4: case 6: case 9: /* need target information */ break; default: return (ERROR_SUCCESS); } info->i_targets = calloc(info->i_ntargets, sizeof (dfs_target_t)); if (info->i_targets == NULL) return (ERROR_NOT_ENOUGH_MEMORY); /* * Format for each target * server:share:state:class:rank */ for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) { for (j = 0; j < DFS_LINK_TRGT_NFIELDS; j++) { if ((lfield[j] = strsep((char **)&buf, ":")) == NULL) { dfs_info_free(info); return (ERROR_INVALID_DATA); } } (void) strlcpy(t->t_server, lfield[0], sizeof (t->t_server)); (void) strlcpy(t->t_share, lfield[1], sizeof (t->t_share)); t->t_state = strtoul(lfield[2], NULL, 10); t->t_priority.p_class = strtoul(lfield[3], NULL, 10); t->t_priority.p_rank = strtoul(lfield[4], NULL, 10); } return (ERROR_SUCCESS); } /* * Encodes given link information (info) */ static uint32_t dfs_link_encode(dfs_info_t *info, char *buf, size_t bufsz) { char linkdata[MAXREPARSELEN]; dfs_target_t *t; int i, sz; /* * Header format * ver:state:prop:timeout:guid:ntarget:cmntlen:comment */ sz = snprintf(buf, bufsz, "%u:%u:%u:%u:%s:%u:%u:%s", DFS_LINK_V1, info->i_state, info->i_propflags, info->i_timeout, info->i_guid, info->i_ntargets, strlen(info->i_comment), info->i_comment); if (sz > bufsz) { syslog(LOG_WARNING, "dfs: link data is too large"); dfs_info_trace("DFS link encode", info); return (ERROR_INTERNAL_ERROR); } /* * Format for each target * :server:share:state:class:rank */ bufsz -= sz; for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) { if (strchr(t->t_server, ':') || strchr(t->t_share, ':')) return (ERROR_INVALID_NAME); sz = snprintf(linkdata, MAXREPARSELEN, ":%s:%s:%u:%u:%u", t->t_server, t->t_share, t->t_state, t->t_priority.p_class, t->t_priority.p_rank); if (sz > bufsz) { syslog(LOG_WARNING, "dfs: link data is too large"); dfs_info_trace("DFS link encode", info); return (ERROR_INTERNAL_ERROR); } (void) strcat(buf, linkdata); bufsz -= sz; } return (ERROR_SUCCESS); } /* * Stores given information for the specified link */ static uint32_t dfs_link_commit(const char *path, dfs_info_t *info) { char linkdata[MAXREPARSELEN]; uint32_t status; int rc; status = dfs_link_encode(info, linkdata, MAXREPARSELEN); if (status == ERROR_SUCCESS) { rc = smb_reparse_svcadd(path, DFS_REPARSE_SVCTYPE, linkdata); if (rc != 0) status = ERROR_INTERNAL_ERROR; } return (status); } /* * Determines if the passed state is valid for a link */ static boolean_t dfs_link_isvalidstate(uint32_t state) { return (state == DFS_VOLUME_STATE_OK || state == DFS_VOLUME_STATE_OFFLINE || state == DFS_VOLUME_STATE_ONLINE); } /* * Initializes the given target structure (t) with provided information. */ static void dfs_target_init(dfs_target_t *t, const char *srv, const char *share, uint32_t state) { (void) strlcpy(t->t_server, (srv) ? srv : "", sizeof (t->t_server)); (void) strlcpy(t->t_share, (share) ? share : "", sizeof (t->t_share)); t->t_state = state; t->t_priority.p_class = DfsSiteCostNormalPriorityClass; t->t_priority.p_rank = 0; } /* * Lookup the specified target (server, share) in the given * target list (targets). If there is a match its index is * returned, otherwise -1 will be returned. */ static int dfs_target_find(dfs_target_t *targets, uint32_t ntargets, const char *server, const char *share) { dfs_target_t *t; int i; for (i = 0, t = targets; i < ntargets; i++, t++) { if ((smb_strcasecmp(t->t_server, server, 0) == 0) && (smb_strcasecmp(t->t_share, share, 0) == 0)) return (i); } return (-1); } /* * Determines if the passed state is valid for a link/root target */ static boolean_t dfs_target_isvalidstate(uint32_t state) { return (state == DFS_STORAGE_STATE_ONLINE || state == DFS_STORAGE_STATE_OFFLINE); } /* * Cache compare function, the key is UNC path */ static int dfs_cache_cmp(const void *p1, const void *p2) { smb_cache_node_t *cn1 = (smb_cache_node_t *)p1; smb_cache_node_t *cn2 = (smb_cache_node_t *)p2; dfs_nscnode_t *dn1 = cn1->cn_data; dfs_nscnode_t *dn2 = cn2->cn_data; int rc; rc = smb_strcasecmp(dn1->nsc_uncpath, dn2->nsc_uncpath, 0); if (rc < 0) return (-1); if (rc > 0) return (1); return (0); } /* * Adds an entry with given UNC and filesystem path and the specified * entry type (i.e. root/link) to the namespace cache. */ static uint32_t dfs_cache_add_byunc(const char *uncpath, const char *fspath, uint32_t type) { dfs_nscnode_t *dn; uint32_t status = ERROR_SUCCESS; if ((dn = malloc(sizeof (dfs_nscnode_t))) == NULL) return (ERROR_NOT_ENOUGH_MEMORY); (void) strlcpy(dn->nsc_uncpath, uncpath, sizeof (dn->nsc_uncpath)); (void) strlcpy(dn->nsc_fspath, fspath, sizeof (dn->nsc_fspath)); dn->nsc_type = type; if (smb_cache_add(&dfs_nscache, dn, SMB_CACHE_ADD) != 0) { free(dn); status = ERROR_INTERNAL_ERROR; } return (status); } /* * starting from DFS root directory, scans the tree for DFS links * and adds them to the cache. */ static void dfs_cache_populate(const char *unc_prefix, const char *dir) { char fspath[DFS_PATH_MAX]; char uncpath[DFS_PATH_MAX]; char *fname; int nentries, i; struct dirent **entry_list; uint32_t stat; nentries = scandir(dir, &entry_list, NULL, NULL); if (nentries == -1) return; for (i = 0; i < nentries; i++) { fname = entry_list[i]->d_name; if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { free(entry_list[i]); continue; } (void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", dir, fname); (void) snprintf(uncpath, DFS_PATH_MAX, "%s\\%s", unc_prefix, fname); if (dfs_path_isdir(fspath)) { dfs_cache_populate(uncpath, fspath); } else if (dfs_link_stat(fspath, &stat) == ERROR_SUCCESS) { if (stat == DFS_STAT_ISDFS) (void) dfs_cache_add_byunc(uncpath, fspath, DFS_OBJECT_LINK); } free(entry_list[i]); } for (; i < nentries; i++) free(entry_list[i]); free(entry_list); } /* * If this namespace hasn't been cached then return * without flushing the cache; otherwise clear the * name and flush the cache. */ static void dfs_cache_flush(const char *name) { (void) mutex_lock(&dfs_nsmtx); if (smb_strcasecmp(name, dfs_cached_ns, 0) != 0) { (void) mutex_unlock(&dfs_nsmtx); return; } *dfs_cached_ns = '\0'; (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 0); (void) mutex_unlock(&dfs_nsmtx); smb_cache_flush(&dfs_nscache); } /* * Returns the number of cached namespaces */ static uint32_t dfs_cache_nscount(void) { uint32_t nscount; (void) mutex_lock(&dfs_nsmtx); nscount = (*dfs_cached_ns == '\0') ? 0 : 1; (void) mutex_unlock(&dfs_nsmtx); return (nscount); } /* * Determines whether the given path is a directory. */ static boolean_t dfs_path_isdir(const char *path) { struct stat statbuf; if (lstat(path, &statbuf) != 0) return (B_FALSE); return ((statbuf.st_mode & S_IFMT) == S_IFDIR); } /* * Validates the given state based on the object type (root/link), info * level, and whether it is the object's state or its target's state */ static uint32_t dfs_isvalidstate(uint32_t state, uint32_t type, boolean_t target, uint32_t infolvl) { uint32_t status = ERROR_SUCCESS; switch (infolvl) { case 101: if (type == DFS_OBJECT_ROOT) { if (!target) return (dfs_root_isvalidstate(state)); if (!dfs_target_isvalidstate(state)) status = ERROR_INVALID_PARAMETER; else if (state == DFS_STORAGE_STATE_OFFLINE) status = ERROR_NOT_SUPPORTED; } else { if (!target) { if (!dfs_link_isvalidstate(state)) status = ERROR_INVALID_PARAMETER; } else { if (!dfs_target_isvalidstate(state)) status = ERROR_INVALID_PARAMETER; } } break; case 105: if (state == 0) return (ERROR_SUCCESS); if (type == DFS_OBJECT_ROOT) { switch (state) { case DFS_VOLUME_STATE_OK: case DFS_VOLUME_STATE_OFFLINE: case DFS_VOLUME_STATE_ONLINE: case DFS_VOLUME_STATE_RESYNCHRONIZE: case DFS_VOLUME_STATE_STANDBY: status = ERROR_NOT_SUPPORTED; break; default: status = ERROR_INVALID_PARAMETER; } } else { switch (state) { case DFS_VOLUME_STATE_OK: case DFS_VOLUME_STATE_OFFLINE: case DFS_VOLUME_STATE_ONLINE: break; case DFS_VOLUME_STATE_RESYNCHRONIZE: case DFS_VOLUME_STATE_STANDBY: status = ERROR_NOT_SUPPORTED; break; default: status = ERROR_INVALID_PARAMETER; } } break; default: status = ERROR_INVALID_LEVEL; } return (status); } /* * Validates the given property flag mask based on the object * type (root/link) and namespace flavor. */ static uint32_t dfs_isvalidpropflagmask(uint32_t propflag_mask, uint32_t type, uint32_t flavor) { uint32_t flgs_not_supported; flgs_not_supported = DFS_PROPERTY_FLAG_ROOT_SCALABILITY | DFS_PROPERTY_FLAG_CLUSTER_ENABLED | DFS_PROPERTY_FLAG_ABDE; if (flavor == DFS_VOLUME_FLAVOR_STANDALONE) { if (type == DFS_OBJECT_LINK) flgs_not_supported |= DFS_PROPERTY_FLAG_SITE_COSTING; if (propflag_mask & flgs_not_supported) return (ERROR_NOT_SUPPORTED); } return (ERROR_SUCCESS); } /* * Based on the specified information level (infolvl) copy parts of the * information provided through newinfo into the existing information * (info) for the given object. */ static uint32_t dfs_modinfo(uint32_t type, dfs_info_t *info, dfs_info_t *newinfo, uint32_t infolvl) { boolean_t target_op = B_FALSE; uint32_t status = ERROR_SUCCESS; uint32_t state; int target_idx; if (newinfo->i_targets != NULL) { target_idx = dfs_target_find(info->i_targets, info->i_ntargets, newinfo->i_targets->t_server, newinfo->i_targets->t_share); if (target_idx == -1) return (ERROR_FILE_NOT_FOUND); target_op = B_TRUE; } switch (infolvl) { case 100: (void) strlcpy(info->i_comment, newinfo->i_comment, sizeof (newinfo->i_comment)); break; case 101: state = (target_op) ? newinfo->i_targets->t_state : newinfo->i_state; status = dfs_isvalidstate(state, type, target_op, 101); if (status != ERROR_SUCCESS) return (status); if (!target_op) { /* * states specified by this mask should not be stored */ if (state & DFS_VOLUME_STATES_SRV_OPS) return (ERROR_SUCCESS); info->i_state = state; } else { info->i_targets[target_idx].t_state = state; } break; case 102: info->i_timeout = newinfo->i_timeout; break; case 103: info->i_propflags = newinfo->i_propflags; break; case 104: info->i_targets[target_idx].t_priority = newinfo->i_targets->t_priority; break; case 105: status = dfs_isvalidstate(newinfo->i_state, type, B_FALSE, 105); if (status != ERROR_SUCCESS) return (status); status = dfs_isvalidpropflagmask(newinfo->i_propflag_mask, type, newinfo->i_flavor); if (status != ERROR_SUCCESS) return (status); (void) strlcpy(info->i_comment, newinfo->i_comment, sizeof (newinfo->i_comment)); if (newinfo->i_state != 0) info->i_state = newinfo->i_state; info->i_timeout = newinfo->i_timeout; info->i_propflags = newinfo->i_propflags; break; default: status = ERROR_INVALID_LEVEL; } return (status); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _DFS_H #define _DFS_H #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Status returned by dfs_link_stat() */ #define DFS_STAT_UNKNOWN 0 #define DFS_STAT_NOTFOUND 1 #define DFS_STAT_NOTLINK 2 #define DFS_STAT_ISREPARSE 3 #define DFS_STAT_ISDFS 4 typedef struct dfs_path { char p_fspath[DFS_PATH_MAX]; smb_unc_t p_unc; uint32_t p_type; } dfs_path_t; typedef struct dfs_nscnode { char nsc_uncpath[DFS_PATH_MAX]; char nsc_fspath[DFS_PATH_MAX]; uint32_t nsc_type; } dfs_nscnode_t; uint32_t dfs_namespace_count(void); uint32_t dfs_namespace_path(const char *, char *, size_t); uint32_t dfs_namespace_add(const char *, const char *); uint32_t dfs_namespace_remove(const char *); void dfs_namespace_load(const char *); void dfs_namespace_unload(const char *); uint32_t dfs_namespace_getflavor(const char *); uint32_t dfs_root_getinfo(const char *, dfs_info_t *, uint32_t); uint32_t dfs_root_setinfo(const char *, dfs_info_t *, uint32_t); uint32_t dfs_link_add(const char *, const char *, const char *, const char *, uint32_t, boolean_t *); uint32_t dfs_link_remove(const char *, const char *, const char *); uint32_t dfs_link_stat(const char *, uint32_t *); uint32_t dfs_link_getinfo(const char *, dfs_info_t *, uint32_t); uint32_t dfs_link_setinfo(const char *, dfs_info_t *, uint32_t); uint32_t dfs_path_parse(dfs_path_t *, const char *, uint32_t); void dfs_path_free(dfs_path_t *); void dfs_cache_iterinit(smb_cache_cursor_t *); boolean_t dfs_cache_iterate(smb_cache_cursor_t *, dfs_nscnode_t *); uint32_t dfs_cache_add_byname(const char *, const char *, uint32_t); void dfs_cache_remove(const char *, const char *); uint32_t dfs_cache_getinfo(dfs_nscnode_t *, dfs_info_t *, uint32_t); uint32_t dfs_cache_num(void); void dfs_init(void); void dfs_fini(void); void dfs_setpriv(priv_op_t); void dfs_info_trace(const char *, dfs_info_t *); #ifdef __cplusplus } #endif #endif /* _DFS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2023 RackTop Systems, Inc. */ /* * Client side for the DSSETUP RPC service. */ #include #include #include #include #include #include int dssetup_get_domain_info(ds_primary_domain_info_t *ds_info) { dssetup_DsRoleGetPrimaryDomainInfo_t arg; char user[SMB_USERNAME_MAXLEN]; struct dssetup_DsRolePrimaryDomInfo1 *info; smb_domainex_t di; mlsvc_handle_t handle; int opnum; int rc; if (!smb_domain_getinfo(&di)) return (-1); smb_ipc_get_user(user, sizeof (user)); if (ndr_rpc_bind(&handle, di.d_dci.dc_name, di.d_primary.di_nbname, user, "DSSETUP") != 0) return (-1); opnum = DSSETUP_OPNUM_DsRoleGetPrimaryDomainInfo; bzero(&arg, sizeof (dssetup_DsRoleGetPrimaryDomainInfo_t)); arg.level = DS_ROLE_BASIC_INFORMATION; rc = ndr_rpc_call(&handle, opnum, &arg); if ((rc != 0) || (arg.status != 0) || arg.info == NULL) { ndr_rpc_unbind(&handle); return (-1); } info = &arg.info->ru.info1; if (info->nt_domain == NULL || info->dns_domain == NULL || info->forest == NULL) { ndr_rpc_unbind(&handle); return (-1); } bcopy(info, ds_info, sizeof (ds_primary_domain_info_t)); ds_info->nt_domain = (uint8_t *)strdup((char *)info->nt_domain); ds_info->dns_domain = (uint8_t *)strdup((char *)info->dns_domain); ds_info->forest = (uint8_t *)strdup((char *)info->forest); ndr_rpc_unbind(&handle); return (0); } /* * Check whether our connection to the DC is working. */ int dssetup_check_service(void) { ds_primary_domain_info_t ds_info; int rc; bzero(&ds_info, sizeof (ds_info)); if ((rc = dssetup_get_domain_info(&ds_info)) == 0) { free(ds_info.nt_domain); free(ds_info.dns_domain); free(ds_info.forest); } return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Active Directory Setup RPC interface used by Windows 2000. */ #include #include #include #include #include #include #include #include #include #include int dssetup_get_domain_info(ds_primary_domain_info_t *); static int dssetup_DsRoleGetPrimaryDomainInfo(void *, ndr_xa_t *); static uint32_t dssetup_member_server(ds_primary_domain_info_t *, ndr_xa_t *); static uint32_t dssetup_standalone_server(ds_primary_domain_info_t *, ndr_xa_t *); static ndr_stub_table_t dssetup_stub_table[] = { { dssetup_DsRoleGetPrimaryDomainInfo, DSSETUP_OPNUM_DsRoleGetPrimaryDomainInfo }, {0} }; static ndr_service_t dssetup_service = { "DSSETUP", /* name */ "Active Directory Setup", /* desc */ "\\lsarpc", /* endpoint */ PIPE_LSASS, /* sec_addr_port */ "3919286a-b10c-11d0-9ba8-00c04fd92ef5", 0, /* abstract */ NDR_TRANSFER_SYNTAX_UUID, 2, /* transfer */ 0, /* no bind_instance_size */ 0, /* no bind_req() */ 0, /* no unbind_and_close() */ 0, /* use generic_call_stub() */ &TYPEINFO(dssetup_interface), /* interface ti */ dssetup_stub_table /* stub_table */ }; static ds_primary_domain_info_t ds_info; static mutex_t ds_info_mtx; /* * dssetup_initialize * * This function registers the DSSETUP interface with the RPC runtime * library. It must be called in order to use either the client side * or the server side functions. */ void dssetup_initialize(void) { dssetup_clear_domain_info(); (void) ndr_svc_register(&dssetup_service); } void dssetup_clear_domain_info(void) { (void) mutex_lock(&ds_info_mtx); free(ds_info.nt_domain); free(ds_info.dns_domain); free(ds_info.forest); bzero(&ds_info, sizeof (ds_primary_domain_info_t)); (void) mutex_unlock(&ds_info_mtx); } /* * Request for machine role and primary domain information. */ static int dssetup_DsRoleGetPrimaryDomainInfo(void *arg, ndr_xa_t *mxa) { dssetup_DsRoleGetPrimaryDomainInfo_t *param = arg; dssetup_GetPrimaryDomainInfo_t *info; ds_primary_domain_info_t *info1; uint32_t status; int security_mode; info = NDR_MALLOC(mxa, sizeof (dssetup_GetPrimaryDomainInfo_t)); if (info == NULL) { status = NT_STATUS_NO_MEMORY; } else if (param->level != DS_ROLE_BASIC_INFORMATION) { status = NT_STATUS_INVALID_LEVEL; } else { info->switch_value = param->level; info1 = &info->ru.info1; security_mode = smb_config_get_secmode(); if (security_mode == SMB_SECMODE_DOMAIN) status = dssetup_member_server(info1, mxa); else status = dssetup_standalone_server(info1, mxa); } if (status != NT_STATUS_SUCCESS) { bzero(param, sizeof (dssetup_DsRoleGetPrimaryDomainInfo_t)); param->status = NT_SC_ERROR(status); } else { param->info = info; param->status = NT_STATUS_SUCCESS; } return (NDR_DRC_OK); } /* * When the machine role is domain member: * nt_domain must contain the NetBIOS domain name * dns_domain must contain the DNS domain name (cannot be NULL) * forest must contain the forest name (cannot be NULL) * * If DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT is set in flags, the domain_guid * must contain the domain UUID. Otherwise domain_guid is ignored. */ static uint32_t dssetup_member_server(ds_primary_domain_info_t *info, ndr_xa_t *mxa) { char dns_domain[MAXHOSTNAMELEN]; char nt_domain[MAXHOSTNAMELEN]; (void) mutex_lock(&ds_info_mtx); if ((ds_info.flags & DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT) == 0) { /* * If we don't have the domain GUID, try to get it from a * domain controller. Otherwise, use local configuration. */ free(ds_info.nt_domain); free(ds_info.dns_domain); free(ds_info.forest); (void) dssetup_get_domain_info(&ds_info); } if (ds_info.flags & DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT) { info->flags = DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT; info->nt_domain = NDR_STRDUP(mxa, (char *)ds_info.nt_domain); info->dns_domain = NDR_STRDUP(mxa, (char *)ds_info.dns_domain); info->forest = NDR_STRDUP(mxa, (char *)ds_info.forest); bcopy(&ds_info.domain_guid, &info->domain_guid, sizeof (ndr_uuid_t)); } else { if (smb_getdomainname(nt_domain, MAXHOSTNAMELEN) != 0) { (void) mutex_unlock(&ds_info_mtx); return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); } if (smb_getfqdomainname(dns_domain, MAXHOSTNAMELEN) != 0) { (void) mutex_unlock(&ds_info_mtx); return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); } (void) smb_strlwr(dns_domain); info->flags = 0; info->nt_domain = NDR_STRDUP(mxa, nt_domain); info->dns_domain = NDR_STRDUP(mxa, dns_domain); info->forest = NDR_STRDUP(mxa, dns_domain); bzero(&info->domain_guid, sizeof (ndr_uuid_t)); } (void) mutex_unlock(&ds_info_mtx); if (info->nt_domain == NULL || info->dns_domain == NULL || info->forest == NULL) return (NT_STATUS_NO_MEMORY); info->role = DS_ROLE_MEMBER_SERVER; return (NT_STATUS_SUCCESS); } /* * When the machine role is standalone: * nt_domain must contain the NetBIOS workgroup name * dns_domain must be NULL * forest must be NULL * * We don't maintain a domain GUID. When DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT * is not set in flags, domain_guid is ignored. */ static uint32_t dssetup_standalone_server(ds_primary_domain_info_t *info, ndr_xa_t *mxa) { char nt_domain[MAXHOSTNAMELEN]; if (smb_getdomainname(nt_domain, MAXHOSTNAMELEN) != 0) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); info->nt_domain = NDR_STRDUP(mxa, nt_domain); if (info->nt_domain == NULL) return (NT_STATUS_NO_MEMORY); info->role = DS_ROLE_STANDALONE_SERVER; info->flags = 0; info->dns_domain = NULL; info->forest = NULL; bzero(&info->domain_guid, sizeof (ndr_uuid_t)); return (NT_STATUS_SUCCESS); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct logr_eventlog { const char *el_name; const char *el_path; } logr_eventlog_t; logr_eventlog_t logr_eventlog[] = { { "System", "/var/adm/messages" }, { "smbd", "/var/smb/smbd_log.txt" }, { "smbrdr", "/var/smb/smbrdr_log.txt" } }; typedef enum { LOGR_MONTH = 0, LOGR_DAY, LOGR_TIME, LOGR_HOST, LOGR_SOURCE, LOGR_IDTAG, LOGR_ID, LOGR_PRI_FAC, LOGR_NARG } logr_syslog_tokens_t; /* * Event code translation struct for use in processing config file */ typedef struct logr_priority { char *p_name; int p_value; } logr_priority_t; static logr_priority_t logr_pri_names[] = { "panic", LOG_EMERG, "emerg", LOG_EMERG, "alert", LOG_ALERT, "crit", LOG_CRIT, "err", LOG_ERR, "error", LOG_ERR, "warn", LOG_WARNING, "warning", LOG_WARNING, "notice", LOG_NOTICE, "info", LOG_INFO, "debug", LOG_DEBUG }; typedef struct logr_syslog_node { list_node_t ln_node; char ln_logline[LOGR_MAXENTRYLEN]; } logr_syslog_node_t; static void *logr_interposer_hdl = NULL; static struct { boolean_t (*logr_op_supported)(char *); int (*logr_op_snapshot)(logr_context_t *); } logr_interposer_ops; /* * Set the syslog timestamp. * * This is a private helper for logr_syslog_parse_entry(), which * must ensure that the appropriate argv entries are non-null. */ static void logr_syslog_set_timestamp(char **argv, logr_entry_t *le) { char *month = argv[LOGR_MONTH]; char *day = argv[LOGR_DAY]; char *time = argv[LOGR_TIME]; struct timeval now; struct tm tm, cur_tm; char buf[32]; bzero(&tm, sizeof (tm)); (void) snprintf(buf, 32, "%s %s %s", month, day, time); if (strptime(buf, "%b" "%d" "%H:%M:%S", &tm) == NULL) { le->le_timestamp.tv_sec = 0; return; } (void) gettimeofday(&now, NULL); (void) localtime_r(&now.tv_sec, &cur_tm); tm.tm_isdst = cur_tm.tm_isdst; tm.tm_year = cur_tm.tm_year; if (tm.tm_mon > cur_tm.tm_mon) tm.tm_year--; le->le_timestamp.tv_sec = mktime(&tm); } /* * Set the syslog priority. * * This is a private helper for logr_syslog_parse_entry(), which * must ensure that the appropriate argv entries are non-null. */ static void logr_syslog_set_priority(char **argv, logr_entry_t *le) { logr_priority_t *entry; char *token; int sz = sizeof (logr_pri_names) / sizeof (logr_pri_names[0]); int i; le->le_pri = LOG_INFO; if ((token = argv[LOGR_PRI_FAC]) == NULL) return; for (i = 0; i < sz; i++) { entry = &logr_pri_names[i]; if (strstr(token, entry->p_name) != NULL) { le->le_pri = entry->p_value; break; } } } /* * Parse a syslog entry into a log_entry_t structure. A typical syslog * entry has one of the following formats: * *