Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F159739060
D56166.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
37 KB
Referenced Files
None
Subscribers
None
D56166.diff
View Options
diff --git a/lib/libugidfw/ugidfw.c b/lib/libugidfw/ugidfw.c
--- a/lib/libugidfw/ugidfw.c
+++ b/lib/libugidfw/ugidfw.c
@@ -342,6 +342,21 @@
left -= len;
cur += len;
}
+ if (!notdone && (rule->mbr_object.mbo_neg & MBO_GLOB_DEFINED)) {
+ len = snprintf(cur, left, "! ");
+ if (len < 0 || len > left)
+ goto truncated;
+ left -= len;
+ cur += len;
+ }
+ if (rule->mbr_object.mbo_flags & MBO_GLOB_DEFINED) {
+ len = snprintf(cur, left, "glob '%s' ",
+ rule->mbr_object.mbo_glob);
+ if (len < 0 || len > left)
+ goto truncated;
+ left -= len;
+ cur += len;
+ }
if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) {
len = snprintf(cur, left, "! ");
if (len < 0 || len > left)
@@ -797,6 +812,14 @@
return (0);
}
+static int
+bsde_parse_glob(char *spec, char *glob, size_t buflen, char *errstr)
+{
+ strncpy(glob, spec, buflen);
+
+ return (0);
+}
+
static int
bsde_parse_object(int argc, char *argv[],
struct mac_bsdextended_object *object, size_t buflen, char *errstr)
@@ -807,6 +830,7 @@
uid_t uid_min, uid_max;
gid_t gid_min, gid_max;
struct fsid fsid;
+ char glob[MBO_GLOB_MAX];
current = 0;
flags = 0;
@@ -875,6 +899,20 @@
nextnot = 0;
}
current += 2;
+ } else if (strcmp(argv[current], "glob") == 0) {
+ if (current + 2 > argc) {
+ snprintf(errstr, buflen, "glob short");
+ return (-1);
+ }
+ if (bsde_parse_glob(argv[current+1], glob,
+ MBO_GLOB_MAX, errstr) < 0)
+ return (-1);
+ flags |= MBO_GLOB_DEFINED;
+ if (nextnot) {
+ neg ^= MBO_GLOB_DEFINED;
+ nextnot = 0;
+ }
+ current += 2;
} else if (strcmp(argv[current], "suid") == 0) {
flags |= MBO_SUID;
if (nextnot) {
@@ -951,6 +989,8 @@
}
if (flags & MBO_FSID_DEFINED)
object->mbo_fsid = fsid;
+ if (flags & MBO_GLOB_DEFINED)
+ strncpy(object->mbo_glob, glob, MBO_GLOB_MAX);
if (flags & MBO_TYPE_DEFINED)
object->mbo_type = type;
diff --git a/sys/fs/tarfs/tarfs_vfsops.c b/sys/fs/tarfs/tarfs_vfsops.c
--- a/sys/fs/tarfs/tarfs_vfsops.c
+++ b/sys/fs/tarfs/tarfs_vfsops.c
@@ -991,7 +991,7 @@
/* vp is now held and locked */
/* Open the source tarball */
- error = vn_open_vnode(vp, flags, td->td_ucred, td, NULL);
+ error = vn_open_vnode(vp, flags, td->td_ucred, td, NULL, NULL);
if (error != 0) {
TARFS_DPF(FS, "%s: failed to open %s: %d\n", __func__,
from, error);
diff --git a/sys/fs/unionfs/union.h b/sys/fs/unionfs/union.h
--- a/sys/fs/unionfs/union.h
+++ b/sys/fs/unionfs/union.h
@@ -139,7 +139,8 @@
struct unionfs_node_status **);
void unionfs_tryrem_node_status(struct unionfs_node *,
struct unionfs_node_status *);
-int unionfs_check_rmdir(struct vnode *, struct ucred *, struct thread *);
+int unionfs_check_rmdir(struct vnode *, struct thread *,
+ struct componentname *cnp);
int unionfs_copyfile(struct vnode *, int, struct ucred *, struct thread *);
int unionfs_copylink(struct vnode *, struct ucred *, struct thread *);
void unionfs_create_uppervattr_core(struct unionfs_mount *, struct vattr *,
diff --git a/sys/fs/unionfs/union_subr.c b/sys/fs/unionfs/union_subr.c
--- a/sys/fs/unionfs/union_subr.c
+++ b/sys/fs/unionfs/union_subr.c
@@ -1701,7 +1701,8 @@
* locked.
*/
int
-unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td)
+unionfs_check_rmdir(struct vnode *vp, struct thread *td,
+ struct componentname *cnp)
{
struct vnode *uvp;
struct vnode *lvp;
@@ -1745,20 +1746,21 @@
ASSERT_VOP_LOCKED(lvp, __func__);
ASSERT_VOP_ELOCKED(uvp, __func__);
- if ((error = VOP_GETATTR(uvp, &va, cred)) != 0)
+ if ((error = VOP_GETATTR(uvp, &va, cnp->cn_cred)) != 0)
return (error);
if (va.va_flags & OPAQUE)
return (0);
#ifdef MAC
- if ((error = mac_vnode_check_open(cred, lvp, VEXEC | VREAD)) != 0)
+ if ((error = mac_vnode_check_open(cnp->cn_cred, lvp, VEXEC | VREAD,
+ cnp)) != 0)
return (error);
#endif
- if ((error = VOP_ACCESS(lvp, VEXEC | VREAD, cred, td)) != 0)
+ if ((error = VOP_ACCESS(lvp, VEXEC | VREAD, cnp->cn_cred, td)) != 0)
return (error);
- if ((error = VOP_OPEN(lvp, FREAD, cred, td, NULL)) != 0)
+ if ((error = VOP_OPEN(lvp, FREAD, cnp->cn_cred, td, NULL)) != 0)
return (error);
- if ((error = VOP_GETATTR(lvp, &va, cred)) != 0)
+ if ((error = VOP_GETATTR(lvp, &va, cnp->cn_cred)) != 0)
return (error);
dirbuflen = max(DEV_BSIZE, GENERIC_MAXDIRSIZ);
@@ -1812,7 +1814,7 @@
cn.cn_nameiop = LOOKUP;
cn.cn_flags = LOCKPARENT | LOCKLEAF | RDONLY | ISLASTCN;
cn.cn_lkflags = LK_EXCLUSIVE;
- cn.cn_cred = cred;
+ cn.cn_cred = cnp->cn_cred;
error = VOP_LOOKUP(uvp, &tvp, &cn);
if (tvp != NULL)
@@ -1826,7 +1828,7 @@
error = 0;
}
- VOP_CLOSE(lvp, FREAD, cred, td);
+ VOP_CLOSE(lvp, FREAD, cnp->cn_cred, td);
free(dirbuf, M_TEMP);
return (error);
}
diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c
--- a/sys/fs/unionfs/union_vnops.c
+++ b/sys/fs/unionfs/union_vnops.c
@@ -1754,7 +1754,7 @@
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
return (ERELOOKUP);
}
- error = unionfs_check_rmdir(ap->a_vp, cnp->cn_cred, td);
+ error = unionfs_check_rmdir(ap->a_vp, td, cnp);
/*
* It's possible for a direct operation on the lower FS
* to make the lower directory non-empty after we drop
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -2919,7 +2919,7 @@
goto bad;
}
#ifdef MAC
- error = mac_vnode_check_open(td->td_ucred, vp, VWRITE | VREAD);
+ error = mac_vnode_check_open(td->td_ucred, vp, VWRITE | VREAD, NULL);
if (error)
goto bad;
#endif
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -98,13 +98,13 @@
static int setutimes(struct thread *td, struct vnode *,
const struct timespec *, int, int);
static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred,
- struct thread *td);
+ struct thread *td, struct componentname *cnp);
static int kern_fhlinkat(struct thread *td, int fd, const char *path,
enum uio_seg pathseg, fhandle_t *fhp);
static int kern_readlink_vp(struct vnode *vp, char *buf, enum uio_seg bufseg,
size_t count, struct thread *td);
static int kern_linkat_vp(struct thread *td, struct vnode *vp, int fd,
- const char *path, enum uio_seg segflag);
+ const char *path, enum uio_seg segflag, const char *spath);
uint64_t
at2cnpflags(u_int at_flags, u_int mask)
@@ -1715,18 +1715,23 @@
return (error);
}
}
- error = kern_linkat_vp(td, nd.ni_vp, fd2, path2, segflag);
+ error = kern_linkat_vp(td, nd.ni_vp, fd2, path2, segflag,
+ path1);
} while (error == EAGAIN || error == ERELOOKUP);
return (error);
}
static int
kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path,
- enum uio_seg segflag)
+ enum uio_seg segflag, const char *spath)
{
struct nameidata nd;
struct mount *mp;
int error;
+#ifdef MAC
+ struct componentname cnp;
+ char kspath[PATH_MAX];
+#endif
if (vp->v_type == VDIR) {
vrele(vp);
@@ -1762,9 +1767,16 @@
} else if (vn_lock(vp, LK_EXCLUSIVE) == 0) {
error = can_hardlink(vp, td->td_ucred);
#ifdef MAC
- if (error == 0)
- error = mac_vnode_check_link(td->td_ucred,
- nd.ni_dvp, vp, &nd.ni_cnd);
+ if (error == 0 && spath != NULL) {
+ error = copyinstr(spath, kspath, PATH_MAX, NULL);
+ if (error == 0) {
+ cnp.cn_pnbuf = kspath;
+ cnp.cn_nameptr = cnp.cn_pnbuf;
+ cnp.cn_namelen = strlen(cnp.cn_pnbuf);
+ error = mac_vnode_check_link(td->td_ucred,
+ nd.ni_dvp, vp, &nd.ni_cnd, &cnp);
+ }
+ }
#endif
if (error != 0) {
vput(vp);
@@ -2174,7 +2186,7 @@
*/
static int
vn_access(struct vnode *vp, int user_flags, struct ucred *cred,
- struct thread *td)
+ struct thread *td, struct componentname *cnp)
{
accmode_t accmode;
int error;
@@ -2191,7 +2203,7 @@
if (user_flags & X_OK)
accmode |= VEXEC;
#ifdef MAC
- error = mac_vnode_check_access(cred, vp, accmode);
+ error = mac_vnode_check_access(cred, vp, accmode, cnp);
if (error != 0)
return (error);
#endif
@@ -2270,7 +2282,7 @@
goto out;
vp = nd.ni_vp;
- error = vn_access(vp, amode, usecred, td);
+ error = vn_access(vp, amode, usecred, td, &nd.ni_cnd);
NDFREE_PNBUF(&nd);
vput(vp);
out:
@@ -4677,7 +4689,7 @@
if (error != 0)
return (error);
VOP_UNLOCK(vp);
- error = kern_linkat_vp(td, vp, fd, path, pathseg);
+ error = kern_linkat_vp(td, vp, fd, path, pathseg, NULL);
} while (error == EAGAIN || error == ERELOOKUP);
return (error);
}
@@ -4792,7 +4804,7 @@
#ifdef INVARIANTS
td->td_dupfd = -1;
#endif
- error = vn_open_vnode(vp, flags, td->td_ucred, td, fp);
+ error = vn_open_vnode(vp, flags, td->td_ucred, td, fp, NULL);
if (error != 0) {
KASSERT(fp->f_ops == &badfileops,
("VOP_OPEN in fhopen() set f_ops"));
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -375,7 +375,7 @@
goto bad;
}
}
- error = vn_open_vnode(vp, fmode, cred, curthread, fp);
+ error = vn_open_vnode(vp, fmode, cred, curthread, fp, ndp);
if (first_open) {
VI_LOCK(vp);
vp->v_iflag &= ~VI_FOPENING;
@@ -433,7 +433,7 @@
*/
int
vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred,
- struct thread *td, struct file *fp)
+ struct thread *td, struct file *fp, struct nameidata *nd)
{
accmode_t accmode;
int error;
@@ -471,7 +471,8 @@
#ifdef MAC
if ((fmode & O_VERIFY) != 0)
accmode |= VVERIFY;
- error = mac_vnode_check_open(cred, vp, accmode);
+ error = mac_vnode_check_open(cred, vp, accmode,
+ nd ? &nd->ni_cnd : NULL);
if (error != 0)
return (error);
diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h
--- a/sys/security/mac/mac_framework.h
+++ b/sys/security/mac/mac_framework.h
@@ -515,17 +515,17 @@
int mac_vnode_associate_extattr(struct mount *mp, struct vnode *vp);
void mac_vnode_associate_singlelabel(struct mount *mp, struct vnode *vp);
int mac_vnode_check_access_impl(struct ucred *cred, struct vnode *dvp,
- accmode_t accmode);
+ accmode_t accmode, struct componentname *cnp);
extern bool mac_vnode_check_access_fp_flag;
#define mac_vnode_check_access_enabled() __predict_false(mac_vnode_check_access_fp_flag)
static inline int
mac_vnode_check_access(struct ucred *cred, struct vnode *dvp,
- accmode_t accmode)
+ accmode_t accmode, struct componentname *cnp)
{
mac_vnode_assert_locked(dvp, "mac_vnode_check_access");
if (mac_vnode_check_access_enabled())
- return (mac_vnode_check_access_impl(cred, dvp, accmode));
+ return (mac_vnode_check_access_impl(cred, dvp, accmode, cnp));
return (0);
}
int mac_vnode_check_chdir(struct ucred *cred, struct vnode *dvp);
@@ -543,7 +543,8 @@
int mac_vnode_check_getextattr(struct ucred *cred, struct vnode *vp,
int attrnamespace, const char *name);
int mac_vnode_check_link(struct ucred *cred, struct vnode *dvp,
- struct vnode *vp, struct componentname *cnp);
+ struct vnode *vp, struct componentname *cnp,
+ struct componentname *scnp);
int mac_vnode_check_listextattr(struct ucred *cred, struct vnode *vp,
int attrnamespace);
@@ -586,7 +587,7 @@
}
int mac_vnode_check_open_impl(struct ucred *cred, struct vnode *vp,
- accmode_t accmode);
+ accmode_t accmode, struct componentname *cnp);
#ifdef MAC
extern bool mac_vnode_check_open_fp_flag;
#else
@@ -595,12 +596,12 @@
#define mac_vnode_check_open_enabled() __predict_false(mac_vnode_check_open_fp_flag)
static inline int
mac_vnode_check_open(struct ucred *cred, struct vnode *vp,
- accmode_t accmode)
+ accmode_t accmode, struct componentname *cnp)
{
mac_vnode_assert_locked(vp, "mac_vnode_check_open");
if (mac_vnode_check_open_enabled())
- return (mac_vnode_check_open_impl(cred, vp, accmode));
+ return (mac_vnode_check_open_impl(cred, vp, accmode, cnp));
return (0);
}
diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h
--- a/sys/security/mac/mac_policy.h
+++ b/sys/security/mac/mac_policy.h
@@ -609,7 +609,7 @@
struct label *vplabel);
typedef int (*mpo_vnode_check_access_t)(struct ucred *cred,
struct vnode *vp, struct label *vplabel,
- accmode_t accmode);
+ accmode_t accmode, struct componentname *cnp);
typedef int (*mpo_vnode_check_chdir_t)(struct ucred *cred,
struct vnode *dvp, struct label *dvplabel);
typedef int (*mpo_vnode_check_chroot_t)(struct ucred *cred,
@@ -635,7 +635,7 @@
typedef int (*mpo_vnode_check_link_t)(struct ucred *cred,
struct vnode *dvp, struct label *dvplabel,
struct vnode *vp, struct label *vplabel,
- struct componentname *cnp);
+ struct componentname *cnp, struct componentname *scnp);
typedef int (*mpo_vnode_check_listextattr_t)(struct ucred *cred,
struct vnode *vp, struct label *vplabel,
int attrnamespace);
@@ -651,7 +651,7 @@
struct vnode *vp, struct label *vplabel, int prot);
typedef int (*mpo_vnode_check_open_t)(struct ucred *cred,
struct vnode *vp, struct label *vplabel,
- accmode_t accmode);
+ accmode_t accmode, struct componentname *cnp);
typedef int (*mpo_vnode_check_poll_t)(struct ucred *active_cred,
struct ucred *file_cred, struct vnode *vp,
struct label *vplabel);
diff --git a/sys/security/mac/mac_vfs.c b/sys/security/mac/mac_vfs.c
--- a/sys/security/mac/mac_vfs.c
+++ b/sys/security/mac/mac_vfs.c
@@ -366,18 +366,19 @@
return (result);
}
-MAC_CHECK_PROBE_DEFINE3(vnode_check_access, "struct ucred *",
- "struct vnode *", "accmode_t");
+MAC_CHECK_PROBE_DEFINE4(vnode_check_access, "struct ucred *",
+ "struct vnode *", "accmode_t", "struct componentname *");
int
-mac_vnode_check_access_impl(struct ucred *cred, struct vnode *vp, accmode_t accmode)
+mac_vnode_check_access_impl(struct ucred *cred, struct vnode *vp,
+ accmode_t accmode, struct componentname *cnp)
{
int error;
ASSERT_VOP_LOCKED(vp, "mac_vnode_check_access");
- MAC_POLICY_CHECK(vnode_check_access, cred, vp, vp->v_label, accmode);
- MAC_CHECK_PROBE3(vnode_check_access, error, cred, vp, accmode);
+ MAC_POLICY_CHECK(vnode_check_access, cred, vp, vp->v_label, accmode, cnp);
+ MAC_CHECK_PROBE4(vnode_check_access, error, cred, vp, accmode, cnp);
return (error);
}
@@ -521,12 +522,12 @@
return (error);
}
-MAC_CHECK_PROBE_DEFINE4(vnode_check_link, "struct ucred *", "struct vnode *",
- "struct vnode *", "struct componentname *");
+MAC_CHECK_PROBE_DEFINE5(vnode_check_link, "struct ucred *", "struct vnode *",
+ "struct vnode *", "struct componentname *", "struct componentname *");
int
mac_vnode_check_link(struct ucred *cred, struct vnode *dvp,
- struct vnode *vp, struct componentname *cnp)
+ struct vnode *vp, struct componentname *cnp, struct componentname *scnp)
{
int error;
@@ -534,8 +535,8 @@
ASSERT_VOP_LOCKED(vp, "mac_vnode_check_link");
MAC_POLICY_CHECK(vnode_check_link, cred, dvp, dvp->v_label, vp,
- vp->v_label, cnp);
- MAC_CHECK_PROBE4(vnode_check_link, error, cred, dvp, vp, cnp);
+ vp->v_label, cnp, scnp);
+ MAC_CHECK_PROBE5(vnode_check_link, error, cred, dvp, vp, cnp, scnp);
return (error);
}
@@ -625,18 +626,19 @@
return (error);
}
-MAC_CHECK_PROBE_DEFINE3(vnode_check_open, "struct ucred *", "struct vnode *",
- "accmode_t");
+MAC_CHECK_PROBE_DEFINE4(vnode_check_open, "struct ucred *", "struct vnode *",
+ "accmode_t", "struct componentname *");
int
-mac_vnode_check_open_impl(struct ucred *cred, struct vnode *vp, accmode_t accmode)
+mac_vnode_check_open_impl(struct ucred *cred, struct vnode *vp,
+ accmode_t accmode, struct componentname *cnp)
{
int error;
ASSERT_VOP_LOCKED(vp, "mac_vnode_check_open");
- MAC_POLICY_CHECK(vnode_check_open, cred, vp, vp->v_label, accmode);
- MAC_CHECK_PROBE3(vnode_check_open, error, cred, vp, accmode);
+ MAC_POLICY_CHECK(vnode_check_open, cred, vp, vp->v_label, accmode, cnp);
+ MAC_CHECK_PROBE4(vnode_check_open, error, cred, vp, accmode, cnp);
return (error);
}
diff --git a/sys/security/mac_biba/mac_biba.c b/sys/security/mac_biba/mac_biba.c
--- a/sys/security/mac_biba/mac_biba.c
+++ b/sys/security/mac_biba/mac_biba.c
@@ -62,6 +62,7 @@
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/file.h>
+#include <sys/namei.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/pipe.h>
@@ -3031,7 +3032,7 @@
static int
biba_vnode_check_link(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct vnode *vp, struct label *vplabel,
- struct componentname *cnp)
+ struct componentname *cnp, struct componentname *scnp)
{
struct mac_biba *subj, *obj;
@@ -3118,7 +3119,7 @@
static int
biba_vnode_check_open(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
struct mac_biba *subj, *obj;
diff --git a/sys/security/mac_bsdextended/mac_bsdextended.h b/sys/security/mac_bsdextended/mac_bsdextended.h
--- a/sys/security/mac_bsdextended/mac_bsdextended.h
+++ b/sys/security/mac_bsdextended/mac_bsdextended.h
@@ -51,6 +51,7 @@
#define MBI_APPEND 040000
#define MBI_ALLPERM (MBI_EXEC | MBI_WRITE | MBI_READ | MBI_ADMIN | \
MBI_STAT | MBI_APPEND)
+#define MBI_LINK 0100000
#define MBS_UID_DEFINED 0x00000001 /* uid field should be matched */
#define MBS_GID_DEFINED 0x00000002 /* gid field should be matched */
@@ -68,18 +69,19 @@
int mbs_prison;
};
-#define MBO_UID_DEFINED 0x00000001 /* uid field should be matched */
-#define MBO_GID_DEFINED 0x00000002 /* gid field should be matched */
+#define MBO_UID_DEFINED 0x00000001 /* uid field should be matched */
+#define MBO_GID_DEFINED 0x00000002 /* gid field should be matched */
#define MBO_FSID_DEFINED 0x00000004 /* fsid field should be matched */
-#define MBO_SUID 0x00000008 /* object must be suid */
-#define MBO_SGID 0x00000010 /* object must be sgid */
-#define MBO_UID_SUBJECT 0x00000020 /* uid must match subject */
-#define MBO_GID_SUBJECT 0x00000040 /* gid must match subject */
+#define MBO_SUID 0x00000008 /* object must be suid */
+#define MBO_SGID 0x00000010 /* object must be sgid */
+#define MBO_UID_SUBJECT 0x00000020 /* uid must match subject */
+#define MBO_GID_SUBJECT 0x00000040 /* gid must match subject */
#define MBO_TYPE_DEFINED 0x00000080 /* object type should be matched */
+#define MBO_GLOB_DEFINED 0x00000100 /* glob field should be matched */
#define MBO_ALL_FLAGS (MBO_UID_DEFINED | MBO_GID_DEFINED | MBO_FSID_DEFINED | \
MBO_SUID | MBO_SGID | MBO_UID_SUBJECT | MBO_GID_SUBJECT | \
- MBO_TYPE_DEFINED)
+ MBO_TYPE_DEFINED | MBO_GLOB_DEFINED)
#define MBO_TYPE_REG 0x00000001
#define MBO_TYPE_DIR 0x00000002
@@ -92,6 +94,8 @@
#define MBO_ALL_TYPE (MBO_TYPE_REG | MBO_TYPE_DIR | MBO_TYPE_BLK | \
MBO_TYPE_CHR | MBO_TYPE_LNK | MBO_TYPE_SOCK | MBO_TYPE_FIFO)
+#define MBO_GLOB_MAX 256
+
struct mac_bsdextended_object {
int mbo_flags;
int mbo_neg;
@@ -100,6 +104,7 @@
gid_t mbo_gid_min;
gid_t mbo_gid_max;
fsid_t mbo_fsid;
+ char mbo_glob[MBO_GLOB_MAX];
int mbo_type;
};
diff --git a/sys/security/mac_bsdextended/mac_bsdextended.c b/sys/security/mac_bsdextended/mac_bsdextended.c
--- a/sys/security/mac_bsdextended/mac_bsdextended.c
+++ b/sys/security/mac_bsdextended/mac_bsdextended.c
@@ -61,6 +61,7 @@
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/stat.h>
+#include <sys/namei.h>
#include <security/mac/mac_policy.h>
#include <security/mac_bsdextended/mac_bsdextended.h>
@@ -221,9 +222,29 @@
mtx_destroy(&ugidfw_mtx);
}
+static int
+ugidfw_globmatch(struct componentname *cnp, char *glob, int neg)
+{
+ int match;
+ char *matchthis;
+
+ if (cnp == NULL)
+ return (0);
+ matchthis = strrchr(cnp->cn_nameptr, '/');
+ if (matchthis == NULL)
+ matchthis = cnp->cn_nameptr;
+ else
+ matchthis++;
+ match = fnmatch(glob, matchthis, 0) != FNM_NOMATCH;
+ if (neg & MBO_GLOB_DEFINED)
+ match = !match;
+ return (match);
+}
+
static int
ugidfw_rulecheck(struct mac_bsdextended_rule *rule,
- struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode)
+ struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode,
+ struct componentname *cnp)
{
int mac_granted, match, priv_granted;
int i;
@@ -308,6 +329,18 @@
return (0);
}
+ if (rule->mbr_object.mbo_flags & MBO_GLOB_DEFINED) {
+ if (cnp != NULL) {
+ match = ugidfw_globmatch(cnp,
+ rule->mbr_object.mbo_glob,
+ rule->mbr_object.mbo_neg);
+ if (!match)
+ return (0);
+ } else {
+ return (0);
+ }
+ }
+
if (rule->mbr_object.mbo_flags & MBO_SUID) {
match = (vap->va_mode & S_ISUID);
if (rule->mbr_object.mbo_neg & MBO_SUID)
@@ -421,9 +454,9 @@
int
ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap,
- int acc_mode)
+ int acc_mode, struct componentname *cnp)
{
- int error, i;
+ int error, i, match;
/*
* Since we do not separately handle append, map append to write.
@@ -436,8 +469,27 @@
for (i = 0; i < rule_slots; i++) {
if (rules[i] == NULL)
continue;
+ if (acc_mode & MBI_LINK && cnp != NULL &&
+ rules[i]->mbr_object.mbo_flags & MBO_GLOB_DEFINED) {
+ /*
+ * Forbid hardlinks when glob restriction is in
+ * effect for the source file.
+ */
+ match = ugidfw_globmatch(cnp,
+ rules[i]->mbr_object.mbo_glob,
+ rules[i]->mbr_object.mbo_neg);
+ if (match) {
+ if (ugidfw_logging)
+ log(LOG_AUTHPRIV,
+ "mac_bsdextended: hardlink "
+ "prevented");
+ if (!ugidfw_firstmatch_enabled)
+ mtx_unlock(&ugidfw_mtx);
+ return (EACCES);
+ }
+ }
error = ugidfw_rulecheck(rules[i], cred,
- vp, vap, acc_mode);
+ vp, vap, acc_mode, cnp);
if (error == EJUSTRETURN)
break;
if (error) {
@@ -450,7 +502,8 @@
}
int
-ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode)
+ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode,
+ struct componentname *cnp)
{
int error;
struct vattr vap;
@@ -460,7 +513,7 @@
error = VOP_GETATTR(vp, &vap, cred);
if (error)
return (error);
- return (ugidfw_check(cred, vp, &vap, acc_mode));
+ return (ugidfw_check(cred, vp, &vap, acc_mode, cnp));
}
int
diff --git a/sys/security/mac_bsdextended/ugidfw_internal.h b/sys/security/mac_bsdextended/ugidfw_internal.h
--- a/sys/security/mac_bsdextended/ugidfw_internal.h
+++ b/sys/security/mac_bsdextended/ugidfw_internal.h
@@ -34,8 +34,9 @@
*/
int ugidfw_accmode2mbi(accmode_t accmode);
int ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap,
- int acc_mode);
-int ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode);
+ int acc_mode, struct componentname *cnp);
+int ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode,
+ struct componentname *cnp);
/*
* System access control checks.
@@ -51,7 +52,8 @@
* Vnode access control checks.
*/
int ugidfw_vnode_check_access(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode);
+ struct label *vplabel, accmode_t accmode,
+ struct componentname *cnp);
int ugidfw_vnode_check_chdir(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel);
int ugidfw_vnode_check_chroot(struct ucred *cred, struct vnode *dvp,
@@ -73,13 +75,14 @@
struct label *vplabel, int attrnamespace, const char *name);
int ugidfw_vnode_check_link(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct vnode *vp, struct label *label,
- struct componentname *cnp);
+ struct componentname *cnp, struct componentname *scnp);
int ugidfw_vnode_check_listextattr(struct ucred *cred, struct vnode *vp,
struct label *vplabel, int attrnamespace);
int ugidfw_vnode_check_lookup(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct componentname *cnp);
int ugidfw_vnode_check_open(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode);
+ struct label *vplabel, accmode_t accmode,
+ struct componentname *cnp);
int ugidfw_vnode_check_readdir(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel);
int ugidfw_vnode_check_readdlink(struct ucred *cred, struct vnode *vp,
diff --git a/sys/security/mac_bsdextended/ugidfw_system.c b/sys/security/mac_bsdextended/ugidfw_system.c
--- a/sys/security/mac_bsdextended/ugidfw_system.c
+++ b/sys/security/mac_bsdextended/ugidfw_system.c
@@ -64,7 +64,7 @@
{
if (vp != NULL)
- return (ugidfw_check_vp(cred, vp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL));
else
return (0);
}
@@ -75,7 +75,7 @@
{
if (vp != NULL)
- return (ugidfw_check_vp(cred, vp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL));
else
return (0);
}
@@ -85,5 +85,5 @@
struct label *vplabel)
{
- return (ugidfw_check_vp(cred, vp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL));
}
diff --git a/sys/security/mac_bsdextended/ugidfw_vnode.c b/sys/security/mac_bsdextended/ugidfw_vnode.c
--- a/sys/security/mac_bsdextended/ugidfw_vnode.c
+++ b/sys/security/mac_bsdextended/ugidfw_vnode.c
@@ -60,10 +60,10 @@
int
ugidfw_vnode_check_access(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
- return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode)));
+ return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode), cnp));
}
int
@@ -71,7 +71,7 @@
struct label *dvplabel)
{
- return (ugidfw_check_vp(cred, dvp, MBI_EXEC));
+ return (ugidfw_check_vp(cred, dvp, MBI_EXEC, NULL));
}
int
@@ -79,7 +79,7 @@
struct label *dvplabel)
{
- return (ugidfw_check_vp(cred, dvp, MBI_EXEC));
+ return (ugidfw_check_vp(cred, dvp, MBI_EXEC, NULL));
}
int
@@ -87,7 +87,7 @@
struct label *dvplabel, struct componentname *cnp, struct vattr *vap)
{
- return (ugidfw_check_vp(cred, dvp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, dvp, MBI_WRITE, cnp));
}
int
@@ -95,7 +95,7 @@
struct label *vplabel, acl_type_t type)
{
- return (ugidfw_check_vp(cred, vp, MBI_ADMIN));
+ return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL));
}
int
@@ -103,7 +103,7 @@
struct label *vplabel, int attrnamespace, const char *name)
{
- return (ugidfw_check_vp(cred, vp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL));
}
int
@@ -112,7 +112,7 @@
struct label *execlabel)
{
- return (ugidfw_check_vp(cred, vp, MBI_READ|MBI_EXEC));
+ return (ugidfw_check_vp(cred, vp, MBI_READ|MBI_EXEC, NULL));
}
int
@@ -120,7 +120,7 @@
struct label *vplabel, acl_type_t type)
{
- return (ugidfw_check_vp(cred, vp, MBI_STAT));
+ return (ugidfw_check_vp(cred, vp, MBI_STAT, NULL));
}
int
@@ -128,20 +128,28 @@
struct label *vplabel, int attrnamespace, const char *name)
{
- return (ugidfw_check_vp(cred, vp, MBI_READ));
+ return (ugidfw_check_vp(cred, vp, MBI_READ, NULL));
}
int
ugidfw_vnode_check_link(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct vnode *vp, struct label *label,
- struct componentname *cnp)
+ struct componentname *cnp, struct componentname *scnp)
{
int error;
- error = ugidfw_check_vp(cred, dvp, MBI_WRITE);
+ /*
+ * This is to allow ugidfw_check_vp to prevent hardlinks
+ * when filename globbing is in effect.
+ */
+ error = ugidfw_check_vp(cred, dvp, MBI_LINK, scnp);
if (error)
return (error);
- error = ugidfw_check_vp(cred, vp, MBI_WRITE);
+
+ error = ugidfw_check_vp(cred, dvp, MBI_WRITE, cnp);
+ if (error)
+ return (error);
+ error = ugidfw_check_vp(cred, vp, MBI_WRITE, cnp);
if (error)
return (error);
return (0);
@@ -152,7 +160,7 @@
struct label *vplabel, int attrnamespace)
{
- return (ugidfw_check_vp(cred, vp, MBI_READ));
+ return (ugidfw_check_vp(cred, vp, MBI_READ, NULL));
}
int
@@ -160,23 +168,23 @@
struct label *dvplabel, struct componentname *cnp)
{
- return (ugidfw_check_vp(cred, dvp, MBI_EXEC));
+ return (ugidfw_check_vp(cred, dvp, MBI_EXEC, cnp));
}
int
ugidfw_vnode_check_open(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
- return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode)));
+ return (ugidfw_check_vp(cred, vp, ugidfw_accmode2mbi(accmode), cnp));
}
int
ugidfw_vnode_check_readdir(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel)
{
-
- return (ugidfw_check_vp(cred, dvp, MBI_READ));
+
+ return (ugidfw_check_vp(cred, dvp, MBI_READ, NULL));
}
int
@@ -184,7 +192,7 @@
struct label *vplabel)
{
- return (ugidfw_check_vp(cred, vp, MBI_READ));
+ return (ugidfw_check_vp(cred, vp, MBI_READ, NULL));
}
int
@@ -194,10 +202,10 @@
{
int error;
- error = ugidfw_check_vp(cred, dvp, MBI_WRITE);
+ error = ugidfw_check_vp(cred, dvp, MBI_WRITE, cnp);
if (error)
return (error);
- return (ugidfw_check_vp(cred, vp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, vp, MBI_WRITE, cnp));
}
int
@@ -207,11 +215,11 @@
{
int error;
- error = ugidfw_check_vp(cred, dvp, MBI_WRITE);
+ error = ugidfw_check_vp(cred, dvp, MBI_WRITE, cnp);
if (error)
return (error);
if (vp != NULL)
- error = ugidfw_check_vp(cred, vp, MBI_WRITE);
+ error = ugidfw_check_vp(cred, vp, MBI_WRITE, cnp);
return (error);
}
@@ -220,7 +228,7 @@
struct label *vplabel)
{
- return (ugidfw_check_vp(cred, vp, MBI_ADMIN));
+ return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL));
}
int
@@ -228,7 +236,7 @@
struct label *vplabel, acl_type_t type, struct acl *acl)
{
- return (ugidfw_check_vp(cred, vp, MBI_ADMIN));
+ return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL));
}
int
@@ -236,7 +244,7 @@
struct label *vplabel, int attrnamespace, const char *name)
{
- return (ugidfw_check_vp(cred, vp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, vp, MBI_WRITE, NULL));
}
int
@@ -244,7 +252,7 @@
struct label *vplabel, u_long flags)
{
- return (ugidfw_check_vp(cred, vp, MBI_ADMIN));
+ return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL));
}
int
@@ -252,7 +260,7 @@
struct label *vplabel, mode_t mode)
{
- return (ugidfw_check_vp(cred, vp, MBI_ADMIN));
+ return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL));
}
int
@@ -260,7 +268,7 @@
struct label *vplabel, uid_t uid, gid_t gid)
{
- return (ugidfw_check_vp(cred, vp, MBI_ADMIN));
+ return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL));
}
int
@@ -268,15 +276,16 @@
struct label *vplabel, struct timespec atime, struct timespec utime)
{
- return (ugidfw_check_vp(cred, vp, MBI_ADMIN));
+ return (ugidfw_check_vp(cred, vp, MBI_ADMIN, NULL));
}
int
ugidfw_vnode_check_stat(struct ucred *active_cred,
- struct ucred *file_cred, struct vnode *vp, struct label *vplabel)
+ struct ucred *file_cred, struct vnode *vp, struct label *vplabel /*,
+ struct componentname *cnp */)
{
- return (ugidfw_check_vp(active_cred, vp, MBI_STAT));
+ return (ugidfw_check_vp(active_cred, vp, MBI_STAT, NULL));
}
int
@@ -286,8 +295,8 @@
{
int error;
- error = ugidfw_check_vp(cred, dvp, MBI_WRITE);
+ error = ugidfw_check_vp(cred, dvp, MBI_WRITE, cnp);
if (error)
return (error);
- return (ugidfw_check_vp(cred, vp, MBI_WRITE));
+ return (ugidfw_check_vp(cred, vp, MBI_WRITE, cnp));
}
diff --git a/sys/security/mac_lomac/mac_lomac.c b/sys/security/mac_lomac/mac_lomac.c
--- a/sys/security/mac_lomac/mac_lomac.c
+++ b/sys/security/mac_lomac/mac_lomac.c
@@ -2374,7 +2374,7 @@
static int
lomac_vnode_check_link(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct vnode *vp, struct label *vplabel,
- struct componentname *cnp)
+ struct componentname *cnp, struct componentname *scnp)
{
struct mac_lomac *subj, *obj;
@@ -2445,7 +2445,7 @@
static int
lomac_vnode_check_open(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
struct mac_lomac *subj, *obj;
diff --git a/sys/security/mac_mls/mac_mls.c b/sys/security/mac_mls/mac_mls.c
--- a/sys/security/mac_mls/mac_mls.c
+++ b/sys/security/mac_mls/mac_mls.c
@@ -2660,7 +2660,7 @@
static int
mls_vnode_check_link(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct vnode *vp, struct label *vplabel,
- struct componentname *cnp)
+ struct componentname *cnp, struct componentname *scnp)
{
struct mac_mls *subj, *obj;
@@ -2747,7 +2747,7 @@
static int
mls_vnode_check_open(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
struct mac_mls *subj, *obj;
diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c
--- a/sys/security/mac_stub/mac_stub.c
+++ b/sys/security/mac_stub/mac_stub.c
@@ -1453,7 +1453,7 @@
static int
stub_vnode_check_access(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
return (0);
@@ -1527,7 +1527,7 @@
static int
stub_vnode_check_link(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct vnode *vp, struct label *vplabel,
- struct componentname *cnp)
+ struct componentname *cnp, struct componentname *scnp)
{
return (0);
@@ -1574,7 +1574,7 @@
static int
stub_vnode_check_open(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
return (0);
diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c
--- a/sys/security/mac_test/mac_test.c
+++ b/sys/security/mac_test/mac_test.c
@@ -2668,7 +2668,7 @@
COUNTER_DECL(vnode_check_access);
static int
test_vnode_check_access(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
LABEL_CHECK(cred->cr_label, MAGIC_CRED);
@@ -2788,7 +2788,7 @@
static int
test_vnode_check_link(struct ucred *cred, struct vnode *dvp,
struct label *dvplabel, struct vnode *vp, struct label *vplabel,
- struct componentname *cnp)
+ struct componentname *cnp, struct componentname *scnp)
{
LABEL_CHECK(cred->cr_label, MAGIC_CRED);
@@ -2841,7 +2841,7 @@
COUNTER_DECL(vnode_check_open);
static int
test_vnode_check_open(struct ucred *cred, struct vnode *vp,
- struct label *vplabel, accmode_t accmode)
+ struct label *vplabel, accmode_t accmode, struct componentname *cnp)
{
LABEL_CHECK(cred->cr_label, MAGIC_CRED);
diff --git a/sys/sys/param.h b/sys/sys/param.h
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -74,7 +74,7 @@
* cannot include sys/param.h and should only be updated here.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 1600014
+#define __FreeBSD_version 1600015
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -791,7 +791,7 @@
int vn_open_cred(struct nameidata *ndp, int *flagp, int cmode,
u_int vn_open_flags, struct ucred *cred, struct file *fp);
int vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred,
- struct thread *td, struct file *fp);
+ struct thread *td, struct file *fp, struct nameidata *nd);
void vn_pages_remove(struct vnode *vp, vm_pindex_t start, vm_pindex_t end);
void vn_pages_remove_valid(struct vnode *vp, vm_pindex_t start,
vm_pindex_t end);
diff --git a/tests/sys/mac/bsdextended/matches_test.sh b/tests/sys/mac/bsdextended/matches_test.sh
--- a/tests/sys/mac/bsdextended/matches_test.sh
+++ b/tests/sys/mac/bsdextended/matches_test.sh
@@ -379,6 +379,38 @@
cleanup
}
+atf_test_case object_glob cleanup
+object_glob_head()
+{
+ atf_set "require.user" "root"
+}
+object_glob_body()
+{
+ setup
+
+ # access still working when glob not matched
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object glob 'nomatch' mode arx
+ atf_check -s exit:0 su -fm $uidinrange -c "$command1"
+
+ # permission denied when glob matches
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object glob 'test-*' mode arx
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+
+ # does hardlink prevention work?
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "ln $file1 mnt/meh"
+ # and do other handlinks still work?
+ atf_check -s exit:0 \
+ su -fm $uidinrange -c "touch mnt/file2a"
+ atf_check -s exit:0 \
+ su -fm $uidinrange -c "ln mnt/file2a mnt/file2b"
+}
+object_glob_cleanup()
+{
+ cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case no_rules
@@ -393,4 +425,5 @@
atf_add_test_case object_uid_matches_subject
atf_add_test_case object_gid_matches_subject
atf_add_test_case object_type
+ atf_add_test_case object_glob
}
diff --git a/usr.sbin/ugidfw/ugidfw.8 b/usr.sbin/ugidfw/ugidfw.8
--- a/usr.sbin/ugidfw/ugidfw.8
+++ b/usr.sbin/ugidfw/ugidfw.8
@@ -67,6 +67,10 @@
.Oc
.Oo
.Op Cm \&!
+.Cm glob Ad pattern
+.Oc
+.Oo
+.Op Cm \&!
.Cm suid
.Oc
.Oo
@@ -122,6 +126,10 @@
.Oc
.Oo
.Op Cm \&!
+.Cm glob Ad pattern
+.Oc
+.Oo
+.Op Cm \&!
.Cm suid
.Oc
.Oo
@@ -241,6 +249,10 @@
.Oc
.Oo
.Op Cm \&!
+.Cm glob Ad pattern
+.Oc
+.Oo
+.Op Cm \&!
.Cm suid
.Oc
.Oo
@@ -279,6 +291,14 @@
if the filesystem is unmounted and remounted,
then the rule may need to be reapplied to ensure the correct filesystem
id is used.
+The object can be required to match a filename glob pattern using
+.Cm glob .
+.Cm Pattern
+is evaluated against the last pathname component.
+When
+.Cm glob
+is in effect the system also prevents hardlinking to files matching the
+.Cm pattern .
The object can be required to have the
.Cm suid
or
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jun 18, 5:28 PM (3 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34054586
Default Alt Text
D56166.diff (37 KB)
Attached To
Mode
D56166: Add filename glob matching to mac_bsdextended
Attached
Detach File
Event Timeline
Log In to Comment