diff options
Diffstat (limited to 'usr/src/lib/libsmbfs')
-rw-r--r-- | usr/src/lib/libsmbfs/Makefile.com | 5 | ||||
-rw-r--r-- | usr/src/lib/libsmbfs/netsmb/smbfs_acl.h | 92 | ||||
-rw-r--r-- | usr/src/lib/libsmbfs/netsmb/smbfs_isec.h | 113 | ||||
-rw-r--r-- | usr/src/lib/libsmbfs/smb/acl_api.c | 306 | ||||
-rw-r--r-- | usr/src/lib/libsmbfs/smb/acl_conv.c | 932 | ||||
-rw-r--r-- | usr/src/lib/libsmbfs/smb/acl_print.c | 121 | ||||
-rw-r--r-- | usr/src/lib/libsmbfs/smb/mapfile-vers | 8 | ||||
-rw-r--r-- | usr/src/lib/libsmbfs/smb/mbuf.c | 11 |
8 files changed, 1585 insertions, 3 deletions
diff --git a/usr/src/lib/libsmbfs/Makefile.com b/usr/src/lib/libsmbfs/Makefile.com index 76e2eac287..9ffc1634ef 100644 --- a/usr/src/lib/libsmbfs/Makefile.com +++ b/usr/src/lib/libsmbfs/Makefile.com @@ -32,6 +32,9 @@ VERS= .1 # leaving out: kiconv.o OBJECTS=\ + acl_api.o \ + acl_conv.o \ + acl_print.o \ charsets.o \ cfopt.o \ ctx.o \ @@ -66,7 +69,7 @@ $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) C99MODE= $(C99_ENABLE) -LDLIBS += -lsocket -lnsl -lc -lkrb5 +LDLIBS += -lsocket -lnsl -lc -lkrb5 -lsec -lidmap # normal warnings... CFLAGS += $(CCVERBOSE) diff --git a/usr/src/lib/libsmbfs/netsmb/smbfs_acl.h b/usr/src/lib/libsmbfs/netsmb/smbfs_acl.h new file mode 100644 index 0000000000..dc8972e5cb --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/smbfs_acl.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#ifndef _NETSMB_SMBFS_ACL_H +#define _NETSMB_SMBFS_ACL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Get/set ACL via contracted interface in libsmbfs. + * The ACL is in the form used by libsec (type=ACE_T) + * but we need to carry the uid/gid info here too. + */ + +#include <sys/acl.h> + +/* + * Get a ZFS-style acl from an FD opened in smbfs. + * Intentionally similar to: facl_get(3SEC) + * + * Allocates an acl_t via libsec. Free with: acl_free(3SEC) + * Get owner/group IDs too if ID pointers != NULL + */ +int smbfs_acl_get(int fd, acl_t **, uid_t *, gid_t *); + +/* + * Set a ZFS-style acl onto an FD opened in smbfs. + * Intentionally similar to: facl_set(3SEC) + * + * The acl_t must be of type ACE_T (from libsec). + * Set owner/group IDs too if ID values != -1 + */ +int smbfs_acl_set(int fd, acl_t *, uid_t, gid_t); + + +/* + * Slightly lower-level functions, allowing access to + * the raw Windows Security Descriptor (SD) + */ +typedef struct i_ntsd i_ntsd_t; + +/* + * Get an "internal form" SD from the FD (opened in smbfs). + * Allocates a hierarchy in isdp. Caller must free it via + * smbfs_acl_free_isd() + */ +int smbfs_acl_getsd(int fd, uint32_t, i_ntsd_t **); + +/* + * Set an "internal form" SD onto the FD (opened in smbfs). + */ +int smbfs_acl_setsd(int fd, uint32_t, i_ntsd_t *); + +/* + * Convert an internal SD to a ZFS-style ACL. + * Get uid/gid too if pointers != NULL. + */ +int smbfs_acl_sd2zfs(i_ntsd_t *, acl_t *, uid_t *, gid_t *); + +/* + * Convert an internal SD to a ZFS-style ACL. + * Include owner/group too if uid/gid != -1. + */ +int smbfs_acl_zfs2sd(acl_t *, uid_t, gid_t, i_ntsd_t **); + +void smbfs_acl_free_sd(i_ntsd_t *); +void smbfs_acl_print_sd(FILE *, i_ntsd_t *); + +#endif /* _NETSMB_SMBFS_ACL_H */ diff --git a/usr/src/lib/libsmbfs/netsmb/smbfs_isec.h b/usr/src/lib/libsmbfs/netsmb/smbfs_isec.h new file mode 100644 index 0000000000..f6b3555345 --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/smbfs_isec.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#ifndef _SMBFS_ISEC_H +#define _SMBFS_ISEC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Internal Security Descriptor (SD) + */ + +#include <netsmb/smbfs_acl.h> + +/* + * Internal form of an NT SID + * Same as on the wire, but possibly byte-swapped. + */ +typedef struct i_ntsid { + uint8_t sid_revision; + uint8_t sid_subauthcount; + uint8_t sid_authority[6]; + uint32_t sid_subauthvec[1]; /* actually len=subauthcount */ +} i_ntsid_t; +#define I_SID_SIZE(sacnt) (8 + 4 * (sacnt)) + +/* + * Internal form of an NT ACE + */ +typedef struct i_ntace { + uint8_t ace_type; + uint8_t ace_flags; + uint32_t ace_rights; /* generic, standard, specific, etc */ + i_ntsid_t *ace_sid; +} i_ntace_t; + +/* + * Internal form of an NT ACL (see sacl/dacl below) + */ +typedef struct i_ntacl { + uint8_t acl_revision; /* 0x02 observed with W2K */ + uint16_t acl_acecount; + i_ntace_t *acl_acevec[1]; /* actually, len=acecount */ +} i_ntacl_t; + +/* + * Internal form of an NT Security Descriptor (SD) + */ +struct i_ntsd { + uint8_t sd_revision; /* 0x01 observed between W2K */ + uint16_t sd_flags; + i_ntsid_t *sd_owner; + i_ntsid_t *sd_group; + i_ntacl_t *sd_sacl; + i_ntacl_t *sd_dacl; +}; + + +/* + * Import a raw SD (mb chain) into "internal" form. + * (like "absolute" form per. NT docs) + * Returns allocated data in sdp + */ +int mb_get_ntsd(mbdata_t *mbp, i_ntsd_t **sdp); + +/* + * Export an "internal" SD into an raw SD (mb chain). + * (a.k.a "self-relative" form per. NT docs) + * Returns allocated mbchain in mbp. + */ +int mb_put_ntsd(mbdata_t *mbp, i_ntsd_t *sd); + + +/* + * Get an SD via ioctl on FD (with "selector" bits), + * stroing the raw Windows SD in the mb chain mbp. + */ +int smbfs_acl_iocget(int fd, uint32_t selector, mbdata_t *mbp); + +/* + * Set an SD via ioctl on FD (with "selector" bits), + * with a raw Windows SD from the chain mbp. + */ +int smbfs_acl_iocset(int fd, uint32_t selector, mbdata_t *mbp); + + +int smbfs_sid2str(i_ntsid_t *sid, + char *obuf, size_t olen, uint32_t *ridp); + +#endif /* _SMBFS_ISEC_H */ diff --git a/usr/src/lib/libsmbfs/smb/acl_api.c b/usr/src/lib/libsmbfs/smb/acl_api.c new file mode 100644 index 0000000000..2dc024e086 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/acl_api.c @@ -0,0 +1,306 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * ACL API for smbfs + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/cred.h> +#include <sys/cmn_err.h> +#include <sys/kmem.h> +#include <sys/sunddi.h> +#include <sys/acl.h> +#include <sys/vnode.h> +#include <sys/vfs.h> +#include <sys/byteorder.h> + +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> + +#include <umem.h> +#include <idmap.h> + +#include <sys/fs/smbfs_ioctl.h> + +#include <netsmb/smb_lib.h> +#include <netsmb/smbfs_acl.h> +#include <netsmb/smbfs_isec.h> + +/* Sanity check SD sizes */ +#define MAX_RAW_SD_SIZE 32768 + +/* XXX: acl_common.h */ +acl_t *acl_alloc(enum acl_type); +void acl_free(acl_t *); + + +/* + * Get/set a Windows security descriptor (SD) + * using the (private) smbfs ioctl mechanism. + * Note: Get allocates mbp->mb_top + */ + +/* ARGSUSED */ +int +smbfs_acl_iocget(int fd, uint32_t selector, mbdata_t *mbp) +{ + ioc_sdbuf_t iocb; + struct mbuf *m; + int error; + + error = mb_init(mbp, MAX_RAW_SD_SIZE); + if (error) + return (error); + + m = mbp->mb_top; + iocb.addr = mtod(m, uintptr_t); + iocb.alloc = m->m_maxlen; + iocb.used = 0; + iocb.selector = selector; + + /* + * This does the OTW Get. + */ + if (ioctl(fd, SMBFSIO_GETSD, &iocb) < 0) { + error = errno; + goto errout; + } + + m->m_len = iocb.used; + return (0); + +errout: + mb_done(mbp); + return (error); +} + +/* ARGSUSED */ +int +smbfs_acl_iocset(int fd, uint32_t selector, mbdata_t *mbp) +{ + ioc_sdbuf_t iocb; + struct mbuf *m; + int error; + + /* Make the data contiguous. */ + error = m_lineup(mbp->mb_top, &m); + if (error) + return (error); + + if (mbp->mb_top != m) + mb_initm(mbp, m); + + iocb.addr = mtod(m, uintptr_t); + iocb.alloc = m->m_maxlen; + iocb.used = m->m_len; + iocb.selector = selector; + + /* + * This does the OTW Set. + */ + if (ioctl(fd, SMBFSIO_SETSD, &iocb) < 0) + error = errno; + + return (error); +} + +/* + * Get an NT SD from the open file via ioctl. + */ +int +smbfs_acl_getsd(int fd, uint32_t selector, i_ntsd_t **sdp) +{ + mbdata_t *mbp, mb_store; + int error; + + mbp = &mb_store; + bzero(mbp, sizeof (*mbp)); + + /* + * Get the raw Windows SD via ioctl. + * Returns allocated mbchain in mbp. + */ + error = smbfs_acl_iocget(fd, selector, mbp); + if (error == 0) { + /* + * Import the raw SD into "internal" form. + * (like "absolute" form per. NT docs) + * Returns allocated data in sdp + */ + error = mb_get_ntsd(mbp, sdp); + } + + mb_done(mbp); + return (error); +} + +/* + * Set an NT SD onto the open file via ioctl. + */ +int +smbfs_acl_setsd(int fd, uint32_t selector, i_ntsd_t *sd) +{ + mbdata_t *mbp, mb_store; + int error; + + mbp = &mb_store; + mb_init(mbp, M_MINSIZE); + + /* + * Export the "internal" SD into an mb chain. + * (a.k.a "self-relative" form per. NT docs) + * Returns allocated mbchain in mbp. + */ + error = mb_put_ntsd(mbp, sd); + if (error == 0) { + /* + * Set the raw Windows SD via ioctl. + */ + error = smbfs_acl_iocset(fd, selector, mbp); + } + + mb_done(mbp); + + return (error); +} + + + +/* + * Convenience function to Get security using a + * ZFS-style ACL (libsec acl, type=ACE_T) + * Intentionally similar to: facl_get(3SEC) + */ +int +smbfs_acl_get(int fd, acl_t **aclp, uid_t *uidp, gid_t *gidp) +{ + i_ntsd_t *sd = NULL; + acl_t *acl = NULL; + uint32_t selector; + int error; + + /* + * Which parts of the SD are being requested? + * XXX: Should we request the SACL too? If so, + * might that cause this access to be denied? + * Or maybe: if we get access denied, try the + * open/fetch again without the SACL bit. + */ + selector = 0; + if (aclp) + selector |= DACL_SECURITY_INFORMATION; + if (uidp) + selector |= OWNER_SECURITY_INFORMATION; + if (gidp) + selector |= GROUP_SECURITY_INFORMATION; + + if (selector == 0) + return (0); + + /* + * Get the Windows SD via ioctl, in + * "internal" (absolute) form. + */ + error = smbfs_acl_getsd(fd, selector, &sd); + if (error) + return (error); + /* Note: sd now holds allocated data. */ + + /* + * Convert the internal SD to a ZFS ACL. + * Get uid/gid too if pointers != NULL. + */ + if (aclp) { + acl = acl_alloc(ACE_T); + if (acl == NULL) { + error = ENOMEM; + goto out; + } + } + error = smbfs_acl_sd2zfs(sd, acl, uidp, gidp); + if (error) + goto out; + + /* Success! */ + if (aclp) { + *aclp = acl; + acl = NULL; + } + +out: + if (acl) + acl_free(acl); + smbfs_acl_free_sd(sd); + return (error); +} + +/* + * Convenience function to Set security using a + * ZFS-style ACL (libsec acl, type=ACE_T) + * Intentionally similar to: facl_set(3SEC) + */ +int +smbfs_acl_set(int fd, acl_t *acl, uid_t uid, gid_t gid) +{ + i_ntsd_t *sd = NULL; + uint32_t selector; + int error; + + /* + * Which parts of the SD are being modified? + * XXX: Ditto comments above re. SACL. + */ + selector = 0; + if (acl) + selector |= DACL_SECURITY_INFORMATION; + if (uid != (uid_t)-1) + selector |= OWNER_SECURITY_INFORMATION; + if (gid != (gid_t)-1) + selector |= GROUP_SECURITY_INFORMATION; + if (selector == 0) + return (0); + + if (acl && acl->acl_type != ACE_T) + return (EINVAL); + + /* + * Convert the ZFS ACL to an internal SD. + * Returns allocated data in sd + */ + error = smbfs_acl_zfs2sd(acl, uid, gid, &sd); + if (error == 0) + error = smbfs_acl_setsd(fd, selector, sd); + + smbfs_acl_free_sd(sd); + + return (error); +} diff --git a/usr/src/lib/libsmbfs/smb/acl_conv.c b/usr/src/lib/libsmbfs/smb/acl_conv.c new file mode 100644 index 0000000000..2073bf47f5 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/acl_conv.c @@ -0,0 +1,932 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * ACL support for smbfs + * + * May want to move some of this to usr/src/common + * and compile with the smbfs kmod too, once we + * implement VOP_GETSECATTR, VOP_SETSECATTR. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/cred.h> +#include <sys/cmn_err.h> +#include <sys/kmem.h> +#include <sys/sunddi.h> +#include <sys/acl.h> +#include <sys/vnode.h> +#include <sys/vfs.h> +#include <sys/byteorder.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +#include <umem.h> +#include <idmap.h> + +#include <sys/fs/smbfs_ioctl.h> + +#include <netsmb/smb_lib.h> +#include <netsmb/smbfs_acl.h> +#include <netsmb/smbfs_isec.h> + +#ifdef _KERNEL +#define MALLOC(size) kmem_alloc(size, KM_SLEEP) +#define FREESZ(p, sz) kmem_free(p, sz) +#else /* _KERNEL */ +#define MALLOC(size) malloc(size) +#ifndef lint +#define FREESZ(p, sz) free(p) +#else /* lint */ +/* ARGSUSED */ +static void +FREESZ(void *p, size_t sz) +{ + free(p); +} +#endif /* lint */ +#endif /* _KERNEL */ + + +#define ERRCHK(expr) if ((error = expr) != 0) goto errout + +/* + * Security IDentifier (SID) + */ +static void +ifree_sid(i_ntsid_t *sid) +{ + size_t sz; + + if (sid == NULL) + return; + + sz = I_SID_SIZE(sid->sid_subauthcount); + FREESZ(sid, sz); +} + +static int +mb_get_sid(mbdata_t *mbp, i_ntsid_t **sidp) +{ + i_ntsid_t *sid = NULL; + uint8_t revision, subauthcount; + uint32_t *subauthp; + size_t sidsz; + int error, i; + + if ((error = mb_get_uint8(mbp, &revision)) != 0) + return (error); + if ((error = mb_get_uint8(mbp, &subauthcount)) != 0) + return (error); + + sidsz = I_SID_SIZE(subauthcount); + + if ((sid = MALLOC(sidsz)) == NULL) + return (ENOMEM); + + bzero(sid, sidsz); + sid->sid_revision = revision; + sid->sid_subauthcount = subauthcount; + ERRCHK(mb_get_mem(mbp, (char *)sid->sid_authority, 6)); + + subauthp = &sid->sid_subauthvec[0]; + for (i = 0; i < subauthcount; i++) { + ERRCHK(mb_get_uint32le(mbp, subauthp)); + subauthp++; + } + + /* Success! */ + *sidp = sid; + return (0); + +errout: + ifree_sid(sid); + return (error); +} + +static int +mb_put_sid(mbdata_t *mbp, i_ntsid_t *sid) +{ + uint32_t *subauthp; + int error, i; + + if (sid == NULL) + return (EINVAL); + + ERRCHK(mb_put_uint8(mbp, sid->sid_revision)); + ERRCHK(mb_put_uint8(mbp, sid->sid_subauthcount)); + ERRCHK(mb_put_mem(mbp, (char *)sid->sid_authority, 6)); + + subauthp = &sid->sid_subauthvec[0]; + for (i = 0; i < sid->sid_subauthcount; i++) { + ERRCHK(mb_put_uint32le(mbp, *subauthp)); + subauthp++; + } + + /* Success! */ + return (0); + +errout: + return (error); +} + + +/* + * Access Control Entry (ACE) + */ +static void +ifree_ace(i_ntace_t *ace) +{ + size_t sz; + + if (ace == NULL) + return; + + ifree_sid(ace->ace_sid); + FREESZ(ace, sizeof (*ace)); +} + +static int +mb_get_ace(mbdata_t *mbp, i_ntace_t **acep) +{ + i_ntace_t *ace = NULL; + uint16_t ace_len; + int error; + + if ((ace = MALLOC(sizeof (*ace))) == NULL) + return (ENOMEM); + bzero(ace, sizeof (*ace)); + + ERRCHK(mb_get_uint8(mbp, &ace->ace_type)); + ERRCHK(mb_get_uint8(mbp, &ace->ace_flags)); + ERRCHK(mb_get_uint16le(mbp, &ace_len)); + ERRCHK(mb_get_uint32le(mbp, &ace->ace_rights)); + + ERRCHK(mb_get_sid(mbp, &ace->ace_sid)); + /* XXX: consume any ace_len not used? */ + + /* Success! */ + *acep = ace; + return (0); + +errout: + ifree_ace(ace); + return (error); +} + +static int +mb_put_ace(mbdata_t *mbp, i_ntace_t *ace) +{ + int cnt0, error; + char *ace_len_p; + uint16_t ace_len; + + if (ace == NULL) + return (EINVAL); + + cnt0 = mbp->mb_count; + + ERRCHK(mb_put_uint8(mbp, ace->ace_type)); + ERRCHK(mb_put_uint8(mbp, ace->ace_flags)); + ERRCHK(mb_fit(mbp, 2, &ace_len_p)); + ERRCHK(mb_put_uint32le(mbp, ace->ace_rights)); + + ERRCHK(mb_put_sid(mbp, ace->ace_sid)); + + ace_len = mbp->mb_count - cnt0; + /* LINTED */ + setwle(ace_len_p, 0, ace_len); + + /* Success! */ + return (0); + +errout: + return (error); +} + + +/* + * Access Control List (ACL) + */ + +/* Not an OTW structure, so size can be at our convenience. */ +#define I_ACL_SIZE(cnt) (sizeof (i_ntacl_t) + (cnt) * sizeof (void *)) + +static void +ifree_acl(i_ntacl_t *acl) +{ + i_ntace_t **acep; + size_t sz; + int i; + + if (acl == NULL) + return; + + acep = &acl->acl_acevec[0]; + for (i = 0; i < acl->acl_acecount; i++) { + ifree_ace(*acep); + acep++; + } + sz = I_ACL_SIZE(acl->acl_acecount); + FREESZ(acl, sz); +} + +static int +mb_get_acl(mbdata_t *mbp, i_ntacl_t **aclp) +{ + i_ntacl_t *acl = NULL; + i_ntace_t **acep; + uint8_t revision; + uint16_t acl_len, acecount; + uint32_t *subauthp; + size_t aclsz; + int i, error; + + if ((error = mb_get_uint8(mbp, &revision)) != 0) + return (error); + if ((error = mb_get_uint8(mbp, NULL)) != 0) + return (error); + if ((error = mb_get_uint16le(mbp, &acl_len)) != 0) + return (error); + if ((error = mb_get_uint16le(mbp, &acecount)) != 0) + return (error); + if ((error = mb_get_uint16(mbp, NULL)) != 0) + return (error); + + aclsz = I_ACL_SIZE(acecount); + if ((acl = MALLOC(aclsz)) == NULL) + return (ENOMEM); + bzero(acl, aclsz); + acl->acl_revision = revision; + acl->acl_acecount = acecount; + + acep = &acl->acl_acevec[0]; + for (i = 0; i < acl->acl_acecount; i++) { + ERRCHK(mb_get_ace(mbp, acep)); + acep++; + } + /* XXX: consume any acl_len not used? */ + + /* Success! */ + *aclp = acl; + return (0); + +errout: + ifree_acl(acl); + return (error); +} + +static int +mb_put_acl(mbdata_t *mbp, i_ntacl_t *acl) +{ + i_ntace_t **acep; + uint8_t revision; + char *acl_len_p; + uint16_t acl_len; + uint32_t *subauthp; + size_t aclsz; + int i, cnt0, error; + + cnt0 = mbp->mb_count; + + ERRCHK(mb_put_uint8(mbp, acl->acl_revision)); + ERRCHK(mb_put_uint8(mbp, 0)); /* pad1 */ + ERRCHK(mb_fit(mbp, 2, &acl_len_p)); + ERRCHK(mb_put_uint16le(mbp, acl->acl_acecount)); + ERRCHK(mb_put_uint16le(mbp, 0)); /* pad2 */ + + acep = &acl->acl_acevec[0]; + for (i = 0; i < acl->acl_acecount; i++) { + ERRCHK(mb_put_ace(mbp, *acep)); + acep++; + } + + /* Fill in acl_len_p */ + acl_len = mbp->mb_count - cnt0; + /* LINTED */ + setwle(acl_len_p, 0, acl_len); + + /* Success! */ + return (0); + +errout: + return (error); +} + + +/* + * Security Descriptor + */ +void +smbfs_acl_free_sd(i_ntsd_t *sd) +{ + + if (sd == NULL) + return; + + ifree_sid(sd->sd_owner); + ifree_sid(sd->sd_group); + ifree_acl(sd->sd_sacl); + ifree_acl(sd->sd_dacl); + + FREESZ(sd, sizeof (*sd)); +} + +/* + * Import a raw SD (mb chain) into "internal" form. + * (like "absolute" form per. NT docs) + * Returns allocated data in sdp + * + * Note: does NOT consume all the mbp data, so the + * caller has to take care of that if necessary. + */ +int +mb_get_ntsd(mbdata_t *mbp, i_ntsd_t **sdp) +{ + i_ntsd_t *sd = NULL; + mbdata_t top_mb, tmp_mb; + uint32_t owneroff, groupoff, sacloff, dacloff; + int error; + + if ((sd = MALLOC(sizeof (*sd))) == NULL) + return (ENOMEM); + bzero(sd, sizeof (*sd)); + + /* + * Offsets below are relative to this point, + * so save the mbp state for use below. + */ + top_mb = *mbp; + + ERRCHK(mb_get_uint8(mbp, &sd->sd_revision)); + ERRCHK(mb_get_uint8(mbp, NULL)); + ERRCHK(mb_get_uint16le(mbp, &sd->sd_flags)); + ERRCHK(mb_get_uint32le(mbp, &owneroff)); + ERRCHK(mb_get_uint32le(mbp, &groupoff)); + ERRCHK(mb_get_uint32le(mbp, &sacloff)); + ERRCHK(mb_get_uint32le(mbp, &dacloff)); + + /* + * For each section make a temporary copy of the + * top_mb state, advance to the given offset, and + * pass that to the lower mb_get_xxx functions. + * These could be marshalled in any order, but + * are normally found in the order shown here. + */ + if (sacloff) { + tmp_mb = top_mb; + mb_get_mem(&tmp_mb, NULL, sacloff); + ERRCHK(mb_get_acl(&tmp_mb, &sd->sd_sacl)); + } + if (dacloff) { + tmp_mb = top_mb; + mb_get_mem(&tmp_mb, NULL, dacloff); + ERRCHK(mb_get_acl(&tmp_mb, &sd->sd_dacl)); + } + if (owneroff) { + tmp_mb = top_mb; + mb_get_mem(&tmp_mb, NULL, owneroff); + ERRCHK(mb_get_sid(&tmp_mb, &sd->sd_owner)); + } + if (groupoff) { + tmp_mb = top_mb; + mb_get_mem(&tmp_mb, NULL, groupoff); + ERRCHK(mb_get_sid(&tmp_mb, &sd->sd_group)); + } + + /* Success! */ + *sdp = sd; + return (0); + +errout: + smbfs_acl_free_sd(sd); + return (error); +} + +/* + * Export an "internal" SD into an raw SD (mb chain). + * (a.k.a "self-relative" form per. NT docs) + * Returns allocated mbchain in mbp. + */ +int +mb_put_ntsd(mbdata_t *mbp, i_ntsd_t *sd) +{ + char *owneroffp, *groupoffp, *sacloffp, *dacloffp; + uint32_t owneroff, groupoff, sacloff, dacloff; + int cnt0, error; + + cnt0 = mbp->mb_count; + owneroff = groupoff = sacloff = dacloff = 0; + + ERRCHK(mb_put_uint8(mbp, sd->sd_revision)); + ERRCHK(mb_put_uint8(mbp, 0)); /* pad1 */ + ERRCHK(mb_put_uint16le(mbp, sd->sd_flags)); + ERRCHK(mb_fit(mbp, 4, &owneroffp)); + ERRCHK(mb_fit(mbp, 4, &groupoffp)); + ERRCHK(mb_fit(mbp, 4, &sacloffp)); + ERRCHK(mb_fit(mbp, 4, &dacloffp)); + + /* + * These could be marshalled in any order, but + * are normally found in the order shown here. + */ + if (sd->sd_sacl) { + sacloff = mbp->mb_count - cnt0; + ERRCHK(mb_put_acl(mbp, sd->sd_sacl)); + } + if (sd->sd_dacl) { + dacloff = mbp->mb_count - cnt0; + ERRCHK(mb_put_acl(mbp, sd->sd_dacl)); + } + if (sd->sd_owner) { + owneroff = mbp->mb_count - cnt0; + ERRCHK(mb_put_sid(mbp, sd->sd_owner)); + } + if (sd->sd_group) { + groupoff = mbp->mb_count - cnt0; + ERRCHK(mb_put_sid(mbp, sd->sd_group)); + } + + /* Fill in the offsets */ + /* LINTED */ + setdle(owneroffp, 0, owneroff); + /* LINTED */ + setdle(groupoffp, 0, groupoff); + /* LINTED */ + setdle(sacloffp, 0, sacloff); + /* LINTED */ + setdle(dacloffp, 0, dacloff); + + /* Success! */ + return (0); + +errout: + return (error); +} + + +/* + * Helper functions for conversion between ZFS-style ACLs + * and Windows Security Descriptors. + */ + + +/* + * Convert an NT SID to a string. Optionally return the + * last sub-authority (or "relative ID" -- RID) in *ridp + * and truncate the output string after the domain part. + * If ridp==NULL, the output string is the whole SID, + * including both the domain and RID. + * + * Return length written, or -1 on error. + */ +int +smbfs_sid2str(i_ntsid_t *sid, + char *obuf, size_t osz, uint32_t *ridp) +{ + char *s = obuf; + uint64_t auth = 0; + uint_t i, n; + uint32_t subs, *ip; + + n = snprintf(s, osz, "S-%u", sid->sid_revision); + if (n > osz) + return (-1); + s += n; osz -= n; + + for (i = 0; i < 6; i++) + auth = (auth << 8) | sid->sid_authority[i]; + n = snprintf(s, osz, "-%llu", auth); + if (n > osz) + return (-1); + s += n; osz -= n; + + subs = sid->sid_subauthcount; + if (subs < 1 || subs > 15) + return (-1); + if (ridp) + subs--; + + ip = &sid->sid_subauthvec[0]; + for (; subs; subs--, ip++) { + n = snprintf(s, osz, "-%u", *ip); + if (n > osz) + return (-1); + s += n; osz -= n; + } + if (ridp) + *ridp = *ip; + + return (s - obuf); +} + +/* + * Our interface to the idmap service. + */ + +#ifdef _KERNEL +#define I_GetPidBySid kidmap_batch_getpidbysid +#define I_GetMappings kidmap_get_mappings +#else /* _KERNEL */ +#define I_GetPidBySid idmap_get_pidbysid +#define I_GetMappings idmap_get_mappings +#endif /* _KERNEL */ + +struct mapinfo { + uid_t mi_uid; /* or gid */ + int mi_isuser; + idmap_stat mi_status; +}; + +/* + * A special value for mi_isuser (above) to indicate + * that the SID is the well-known "Everyone" (S-1-1-0). + * The idmap library only uses -1, 0, 1, so this value + * is arbitrary but must not overlap w/ idmap values. + * XXX: Could use a way for idmap to tell us when + * it recognizes this well-known SID. + */ +#define IS_WKSID_EVERYONE 11 + +/* + * Build an idmap request. Cleanup is + * handled by the caller (error or not) + */ +static int +mkrq_idmap_sid2ux( + idmap_get_handle_t *idmap_gh, + i_ntsid_t *sid, + struct mapinfo *mip) +{ + char sid_prefix[256]; + uint32_t rid; + idmap_stat idms; + + if (smbfs_sid2str(sid, sid_prefix, sizeof (sid_prefix), &rid) < 0) + return (EINVAL); + + /* + * Give the "Everyone" group special treatment. + */ + if (strcmp(sid_prefix, "S-1-1") == 0 && rid == 0) { + /* This is "Everyone" */ + mip->mi_uid = (uid_t)-1; + mip->mi_isuser = IS_WKSID_EVERYONE; + mip->mi_status = 0; + return (0); + } + + idms = I_GetPidBySid(idmap_gh, sid_prefix, rid, 0, + &mip->mi_uid, &mip->mi_isuser, &mip->mi_status); + if (idms != IDMAP_SUCCESS) + return (EINVAL); + + return (0); +} + +static void +ntace2zace(ace_t *zacep, i_ntace_t *ntace, struct mapinfo *mip) +{ + uint32_t zamask; + uint16_t zflags, ntflags; + uint8_t zatype = ntace->ace_type; + + /* + * Translate NT ACE flags to ZFS ACE flags. + * The low four bits are the same, but not + * others: INHERITED_ACE_FLAG, etc. + */ + ntflags = ntace->ace_flags; + zflags = 0; + + if (ntflags & OBJECT_INHERIT_ACE_FLAG) + zflags |= ACE_FILE_INHERIT_ACE; + if (ntflags & CONTAINER_INHERIT_ACE_FLAG) + zflags |= ACE_DIRECTORY_INHERIT_ACE; + if (ntflags & NO_PROPAGATE_INHERIT_ACE_FLAG) + zflags |= ACE_NO_PROPAGATE_INHERIT_ACE; + if (ntflags & INHERIT_ONLY_ACE_FLAG) + zflags |= ACE_INHERIT_ONLY_ACE; + if (ntflags & INHERITED_ACE_FLAG) + zflags |= ACE_INHERITED_ACE; + + if (ntflags & SUCCESSFUL_ACCESS_ACE_FLAG) + zflags |= ACE_SUCCESSFUL_ACCESS_ACE_FLAG; + if (ntflags & FAILED_ACCESS_ACE_FLAG) + zflags |= ACE_FAILED_ACCESS_ACE_FLAG; + + /* + * Add the "ID type" flags to the ZFS ace flags. + * Would be nice if the idmap header defined some + * manifest constants for these "isuser" values. + */ + switch (mip->mi_isuser) { + case IS_WKSID_EVERYONE: + zflags |= ACE_EVERYONE; + break; + case 0: /* it's a GID */ + zflags |= ACE_IDENTIFIER_GROUP; + break; + default: + case 1: /* it's a UID */ + break; + } + + /* + * The access mask bits are the same, but + * mask off any bits we don't expect. + * Should not see any GENERIC_xxx flags, + * as those are only valid in requested + * access masks, not ACLs. But if we do, + * get those, silently clear them here. + */ + zamask = ntace->ace_rights & ACE_ALL_PERMS; + + /* + * Verify that it's a known ACE type. + * Only handle the types that appear in + * V2, V3, V4 ACLs for now. Avoid failing + * the whole conversion if we get unknown + * ace types, but convert them to something + * that will have no effect on access. + */ + if (zatype > SYSTEM_ALARM_OBJECT_ACE_TYPE) { + zatype = ACCESS_ALLOWED_ACE_TYPE; + zamask = 0; /* harmless */ + } + + /* + * Fill in the ZFS-style ACE + */ + zacep->a_who = mip->mi_uid; /* from ace_sid */ + zacep->a_access_mask = zamask; + zacep->a_flags = zflags; + zacep->a_type = zatype; +} + +/* + * Convert an internal SD to a ZFS-style ACL. + * Note optional args: vsa/acl, uidp, gidp. + */ +int +smbfs_acl_sd2zfs( + i_ntsd_t *sd, +#ifdef _KERNEL + vsecattr_t *acl_info, +#else /* _KERNEL */ + acl_t *acl_info, +#endif /* _KERNEL */ + uid_t *uidp, gid_t *gidp) +{ + struct mapinfo *mip, *mapinfo = NULL; + int error, i, mapcnt, zacecnt, zacl_size; + ace_t *zacep; + i_ntacl_t *ntacl; + i_ntace_t **ntacep; +#ifndef _KERNEL + idmap_handle_t *idmap_h = NULL; +#endif /* _KERNEL */ + idmap_get_handle_t *idmap_gh = NULL; + idmap_stat idms; + + /* + * sanity checks + */ +#ifndef _KERNEL + if (acl_info) { + if (acl_info->acl_type != ACE_T || + acl_info->acl_aclp != NULL || + acl_info->acl_entry_size != sizeof (ace_t)) + return (EINVAL); + } +#endif /* _KERNEL */ + + /* + * First, get all the SID mappings. + * How many? + */ + mapcnt = 0; + if (sd->sd_owner) + mapcnt++; + if (sd->sd_group) + mapcnt++; + if (sd->sd_sacl) + mapcnt += sd->sd_sacl->acl_acecount; + if (sd->sd_dacl) + mapcnt += sd->sd_dacl->acl_acecount; + if (mapcnt == 0) + return (EINVAL); + + mapinfo = MALLOC(mapcnt * sizeof (*mapinfo)); + if (mapinfo == NULL) { + error = ENOMEM; + goto errout; + } + bzero(mapinfo, mapcnt * sizeof (*mapinfo)); + + + /* + * Build our request to the idmap deamon. + */ +#ifdef _KERNEL + idmap_gh = kidmap_get_create(curproc->p_zone); +#else /* _KERNEL */ + idms = idmap_init(&idmap_h); + if (idms != IDMAP_SUCCESS) { + error = ENOTACTIVE; + goto errout; + } + idms = idmap_get_create(idmap_h, &idmap_gh); + if (idms != IDMAP_SUCCESS) { + error = ENOTACTIVE; + goto errout; + } +#endif /* _KERNEL */ + + mip = mapinfo; + if (sd->sd_owner) { + error = mkrq_idmap_sid2ux( + idmap_gh, sd->sd_owner, mip); + if (error) + goto errout; + mip++; + } + if (sd->sd_group) { + error = mkrq_idmap_sid2ux( + idmap_gh, sd->sd_group, mip); + if (error) + goto errout; + mip++; + } + if (sd->sd_sacl) { + ntacl = sd->sd_sacl; + ntacep = &ntacl->acl_acevec[0]; + for (i = 0; i < ntacl->acl_acecount; i++) { + error = mkrq_idmap_sid2ux( + idmap_gh, (*ntacep)->ace_sid, mip); + if (error) + goto errout; + ntacep++; + mip++; + } + } + if (sd->sd_dacl) { + ntacl = sd->sd_dacl; + ntacep = &ntacl->acl_acevec[0]; + for (i = 0; i < ntacl->acl_acecount; i++) { + error = mkrq_idmap_sid2ux( + idmap_gh, (*ntacep)->ace_sid, mip); + if (error) + goto errout; + ntacep++; + mip++; + } + } + + idms = I_GetMappings(idmap_gh); + if (idms != IDMAP_SUCCESS) { +#ifdef DEBUG + printf("idmap_get_mappings: rc=%d\n", rc); +#endif + /* creative error choice */ + error = EIDRM; + goto errout; + } + + /* + * With any luck, we now have Unix user/group IDs + * for every Windows SID in the security descriptor. + * The remaining work is just format conversion. + */ + mip = mapinfo; + if (sd->sd_owner) { + if (uidp) { + if (mip->mi_isuser == 1) + *uidp = mip->mi_uid; + else + *uidp = (uid_t)-1; + } + mip++; + } else { + if (uidp) + *uidp = (uid_t)-1; + } + if (sd->sd_group) { + if (gidp) { + if (mip->mi_isuser == 0) + *gidp = (gid_t)mip->mi_uid; + else + *gidp = (gid_t)-1; + } + mip++; + } else { + if (gidp) + *gidp = (gid_t)-1; + } + + if (acl_info == NULL) { + /* Caller only wanted uid/gid */ + goto ok_out; + } + + /* + * Build the ZFS-style ACL + */ + zacecnt = 0; + if (sd->sd_sacl) + zacecnt += sd->sd_sacl->acl_acecount; + if (sd->sd_dacl) + zacecnt += sd->sd_dacl->acl_acecount; + zacl_size = zacecnt * sizeof (ace_t); + zacep = MALLOC(zacl_size); +#ifdef _KERNEL + acl_info->vsa_aclentp = zacep; + acl_info->vsa_aclentsz = zacl_size; +#else /* _KERNEL */ + if (zacep == NULL) { + error = ENOMEM; + goto errout; + } + acl_info->acl_cnt = zacecnt; + acl_info->acl_aclp = zacep; +#endif /* _KERNEL */ + + if (sd->sd_sacl) { + ntacl = sd->sd_sacl; + ntacep = &ntacl->acl_acevec[0]; + for (i = 0; i < ntacl->acl_acecount; i++) { + ntace2zace(zacep, *ntacep, mip); + zacep++; + ntacep++; + mip++; + } + } + if (sd->sd_dacl) { + ntacl = sd->sd_dacl; + ntacep = &ntacl->acl_acevec[0]; + for (i = 0; i < ntacl->acl_acecount; i++) { + ntace2zace(zacep, *ntacep, mip); + zacep++; + ntacep++; + mip++; + } + } + +ok_out: + error = 0; + +errout: + if (mapinfo) + FREESZ(mapinfo, mapcnt * sizeof (*mapinfo)); + + return (error); +} + + +/* + * Convert an internal SD to a ZFS-style ACL. + * Include owner/group too if uid/gid != -1. + * Note optional arg: vsa/acl + */ +int smbfs_acl_zfs2sd( +#ifdef _KERNEL + vsecattr_t *vsa, +#else /* _KERNEL */ + acl_t *acl, +#endif /* _KERNEL */ + uid_t uid, gid_t gid, + i_ntsd_t **sdp) +{ + /* XXX - todo */ + return (ENOSYS); +} diff --git a/usr/src/lib/libsmbfs/smb/acl_print.c b/usr/src/lib/libsmbfs/smb/acl_print.c new file mode 100644 index 0000000000..9a1d51e320 --- /dev/null +++ b/usr/src/lib/libsmbfs/smb/acl_print.c @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Print an NT Security Descriptor (SD) and its sub-components. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/cred.h> +#include <sys/cmn_err.h> +#include <sys/kmem.h> +#include <sys/sunddi.h> +#include <sys/acl.h> +#include <sys/vnode.h> +#include <sys/vfs.h> +#include <sys/byteorder.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +#include <umem.h> +#include <idmap.h> + +#include <sys/fs/smbfs_ioctl.h> + +#include <netsmb/smb_lib.h> +#include <netsmb/smbfs_acl.h> +#include <netsmb/smbfs_isec.h> + +static void +fprint_sid(FILE *fp, i_ntsid_t *sid) +{ + static char sidbuf[256]; + + if (sid == NULL) { + fprintf(fp, "(null)\n"); + return; + } + + if (smbfs_sid2str(sid, sidbuf, sizeof (sidbuf), NULL) < 0) + fprintf(fp, "(error)\n"); + else + fprintf(fp, "%s\n", sidbuf); +} + +static void +fprint_ntace(FILE *fp, i_ntace_t *ace) +{ + if (ace == NULL) { + fprintf(fp, " (null)\n"); + return; + } + + /* ACEs are always printed in a list, so indent by 2. */ + fprintf(fp, " ace_type=%d ace_flags=0x%x ace_rights=0x%x\n", + ace->ace_type, ace->ace_flags, ace->ace_rights); + /* Show the SID as a "continuation" line. */ + fprintf(fp, " ace_sid: "); + fprint_sid(fp, ace->ace_sid); +} + +static void +fprint_ntacl(FILE *fp, i_ntacl_t *acl) +{ + int i; + + if (acl == NULL) { + fprintf(fp, "(null)\n"); + return; + } + + fprintf(fp, "acl_rev=%d acl_acecount=%d\n", + acl->acl_revision, acl->acl_acecount); + for (i = 0; i < acl->acl_acecount; i++) + fprint_ntace(fp, acl->acl_acevec[i]); +} + +void +smbfs_acl_print_sd(FILE *fp, i_ntsd_t *sd) +{ + + fprintf(fp, "sd_rev=%d, flags=0x%x\n", + sd->sd_revision, sd->sd_flags); + fprintf(fp, "owner: "); + fprint_sid(fp, sd->sd_owner); + fprintf(fp, "group: "); + fprint_sid(fp, sd->sd_group); + fprintf(fp, "sacl: "); + fprint_ntacl(fp, sd->sd_sacl); + fprintf(fp, "dacl: "); + fprint_ntacl(fp, sd->sd_dacl); +} diff --git a/usr/src/lib/libsmbfs/smb/mapfile-vers b/usr/src/lib/libsmbfs/smb/mapfile-vers index 1dadbbe825..f8f38ba6b0 100644 --- a/usr/src/lib/libsmbfs/smb/mapfile-vers +++ b/usr/src/lib/libsmbfs/smb/mapfile-vers @@ -60,6 +60,14 @@ SUNWprivate_1.0 { smb_read; smb_write; smb_verbose; + smbfs_acl_free_sd; + smbfs_acl_get; + smbfs_acl_getsd; + smbfs_acl_print_sd; + smbfs_acl_sd2zfs; + smbfs_acl_set; + smbfs_acl_setsd; + smbfs_acl_zfs2sd; smbfs_default_dom_usr; smbfs_keychain_add; smbfs_keychain_chk; diff --git a/usr/src/lib/libsmbfs/smb/mbuf.c b/usr/src/lib/libsmbfs/smb/mbuf.c index f03c4fedc3..6f08ce1def 100644 --- a/usr/src/lib/libsmbfs/smb/mbuf.c +++ b/usr/src/lib/libsmbfs/smb/mbuf.c @@ -42,6 +42,7 @@ #include <string.h> #include <strings.h> #include <libintl.h> +#include <assert.h> #include <netsmb/smb.h> #include <netsmb/smb_lib.h> @@ -58,6 +59,8 @@ m_get(size_t len, struct mbuf **mpp) { struct mbuf *m; + assert(len < 0x100000); /* sanity */ + len = M_ALIGN(len); if (len < M_MINSIZE) len = M_MINSIZE; @@ -163,10 +166,13 @@ int m_getm(struct mbuf *top, size_t len, struct mbuf **mpp) { struct mbuf *m, *mp; - int error; + int error, ts; for (mp = top; ; mp = mp->m_next) { - len -= M_TRAILINGSPACE(mp); + ts = M_TRAILINGSPACE(mp); + if (len <= ts) + goto out; + len -= ts; if (mp->m_next == NULL) break; @@ -176,6 +182,7 @@ m_getm(struct mbuf *top, size_t len, struct mbuf **mpp) return (error); mp->m_next = m; } +out: *mpp = top; return (0); } |