Modify extended attribute protection model to authorize based on

attribute namespace and DAC protection on file:
	- Attribute names beginning with '$' are in the system namespace
	- The attribute name "$" is reserved
	- System namespace attributes may only be read/set by suser()
	  or by kernel (cred == NULL)
	- Other attribute names are in the application namespace
	- The attribute name "" is reserved
	- Application namespace attributes are protected in the manner
	  of the target file permission

o Kernel changes
	- Add ufs_extattr_valid_attrname() to check whether the requested
	  attribute "set" or "enable" is appropriate (i.e., non-reserved)
	- Modify ufs_extattr_credcheck() to accept target file vnode, not
	  to take inode uid
	- Modify ufs_extattr_credcheck() to check namespace, then enforce
	  either kernel/suser for system namespace, or vaccess() for
	  application namespace
o EA backing file format changes
	- Remove permission fields from extended attribute backing file
	  header
	- Bump extended attribute backing file header version to 3
o Update extattrctl.c and extattrctl.8
	- Remove now deprecated -r and -w arguments to initattr, as
	  permissions are now implicit
	- (unrelated) fix error reporting and unlinking during failed
	  initattr to remove duplicate/inaccurate error messages, and to
	  only unlink if the failure wasn't in the backing file open()

Obtained from:	TrustedBSD Project
This commit is contained in:
Robert Watson 2000-09-02 20:31:26 +00:00
parent 344c9c9981
commit bbf0607700
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=65377
4 changed files with 78 additions and 119 deletions

View file

@ -33,7 +33,7 @@
#define _UFS_UFS_EXTATTR_H_
#define UFS_EXTATTR_MAGIC 0x00b5d5ec
#define UFS_EXTATTR_VERSION 0x00000002
#define UFS_EXTATTR_VERSION 0x00000003
#define UFS_EXTATTR_FSROOTSUBDIR ".attribute"
#define UFS_EXTATTR_MAXEXTATTRNAME 65 /* including null */
@ -55,8 +55,6 @@ struct ufs_extattr_fileheader {
u_int uef_magic; /* magic number for sanity checking */
u_int uef_version; /* version of attribute file */
u_int uef_size; /* size of attributes, w/o header */
u_int uef_read_perm; /* permissions to read attribute */
u_int uef_write_perm; /* permissions to write attribute */
};
struct ufs_extattr_header {

View file

@ -49,8 +49,10 @@
static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
static int ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele,
u_int32_t fowner, struct ucred *cred, struct proc *p, int access);
static int ufs_extattr_valid_attrname(const char *attrname);
static int ufs_extattr_credcheck(struct vnode *vp,
struct ufs_extattr_list_entry *uele, struct ucred *cred, struct proc *p,
int access);
static int ufs_extattr_enable(struct ufsmount *ump, const char *attrname,
struct vnode *backing_vnode, struct proc *p);
static int ufs_extattr_disable(struct ufsmount *ump, const char *attrname,
@ -83,6 +85,28 @@ ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p)
lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, p);
}
/*
* Determine whether the name passed is a valid name for an actual
* attribute.
*
* Invalid currently consists of:
* NULL pointer for attrname
* zero-length attrname (used to retrieve application attr list)
* attrname consisting of "$" (used to treive system attr list)
*/
static int
ufs_extattr_valid_attrname(const char *attrname)
{
if (attrname == NULL)
return (0);
if (strlen(attrname) == 0)
return (0);
if (strlen(attrname) == 1 && attrname[0] == '$')
return (0);
return (1);
}
/*
* Locate an attribute given a name and mountpoint.
* Must be holding uepm lock for the mount point.
@ -199,6 +223,8 @@ ufs_extattr_enable(struct ufsmount *ump, const char *attrname,
struct uio auio;
int error = 0;
if (!ufs_extattr_valid_attrname(attrname))
return (EINVAL);
if (backing_vnode->v_type != VREG)
return (EINVAL);
@ -280,6 +306,9 @@ ufs_extattr_disable(struct ufsmount *ump, const char *attrname, struct proc *p)
struct ufs_extattr_list_entry *uele;
int error = 0;
if (!ufs_extattr_valid_attrname(attrname))
return (EINVAL);
uele = ufs_extattr_find_attr(ump, attrname);
if (!uele)
return (ENOENT);
@ -363,41 +392,27 @@ ufs_extattrctl(struct mount *mp, int cmd, const char *attrname,
* permissions.
*/
static int
ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, u_int32_t fowner,
struct ucred *cred, struct proc *p, int access)
ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele,
struct ucred *cred, struct proc *p, int access)
{
u_int uef_perm;
int system_namespace;
switch(access) {
case IREAD:
uef_perm = uele->uele_fileheader.uef_read_perm;
break;
case IWRITE:
uef_perm = uele->uele_fileheader.uef_write_perm;
break;
default:
return (EACCES);
}
system_namespace = (strlen(uele->uele_attrname) >= 1 &&
uele->uele_attrname[0] == '$');
/* Kernel sponsoring request does so without passing a cred */
if (!cred)
/*
* Kernel-invoked always succeeds
*/
if (cred == NULL)
return (0);
/* XXX there might eventually be a capability check here */
/* If it's set to root-only, check for suser(p) */
if (uef_perm == UFS_EXTATTR_PERM_ROOT && !suser(p))
return (0);
/* Allow the owner if appropriate */
if (uef_perm == UFS_EXTATTR_PERM_OWNER && cred->cr_uid == fowner)
return (0);
/* Allow anyone if appropriate */
if (uef_perm == UFS_EXTATTR_PERM_ANYONE)
return (0);
return (EACCES);
/*
* XXX What capability should apply here?
*/
if (system_namespace)
return (suser_xxx(cred, p, PRISON_ROOT));
else
return (VOP_ACCESS(vp, access, cred, p));
}
/*
@ -451,12 +466,16 @@ ufs_extattr_get(struct vnode *vp, const char *name, struct uio *uio,
if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
return (EOPNOTSUPP);
if (strlen(name) == 0 || (strlen(name) == 1 && name[0] == '$')) {
/* XXX retrieve attribute lists */
return (EINVAL);
}
attribute = ufs_extattr_find_attr(ump, name);
if (!attribute)
return (ENOENT);
if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p,
IREAD)))
if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IREAD)))
return (error);
/*
@ -613,16 +632,16 @@ ufs_extattr_set(struct vnode *vp, const char *name, struct uio *uio,
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
return (EOPNOTSUPP);
if (!ufs_extattr_valid_attrname(name))
return (EINVAL);
attribute = ufs_extattr_find_attr(ump, name);
if (!attribute)
return (ENOENT);
if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred,
p, IWRITE)))
if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
return (error);
/*
@ -718,16 +737,16 @@ ufs_extattr_rm(struct vnode *vp, const char *name, struct ucred *cred,
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
return (EOPNOTSUPP);
if (!ufs_extattr_valid_attrname(name))
return (EINVAL);
attribute = ufs_extattr_find_attr(ump, name);
if (!attribute)
return (ENOENT);
if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p,
IWRITE)))
if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
return (error);
/*

View file

@ -41,8 +41,6 @@
.Nm extattrctl
.Cm initattr
.Op Fl p Ar path
.Op Fl r Ar kroa
.Op Fl w Ar kroa
.Ar attrsize
.Ar attrfile
.Nm extattrctl
@ -63,7 +61,7 @@ as well as initialization of attribute backing files, and enabling and
disabling of specific extended attributes on a file system.
.Pp
The first argument on the command line indicates the operation to be
performend. Operation must be one of the following:
performed. Operation must be one of the following:
.Bl -tag -width indent
.It Cm start Ar path
Start extended attribute support on the file system named using
@ -77,8 +75,6 @@ Extended attribute support must previously have been started.
.It Xo
.Cm initattr
.Op Fl p Ar path
.Op Fl r Ar kroa
.Op Fl w Ar kroa
.Ar attrsize attrfile
.Xc
Create and initialize a file to use as an attribute backing file.
@ -95,25 +91,6 @@ This has the advantage of guaranteeing that space will be available
for attributes when they are written, preventing low disk space conditions
from denying attribute service.
.Pp
The
.Fl r
and
.Fl w
options can be used to set the read and write permissions on the named
attribute, respectively.
There are four levels possible for both read and write:
.Dq k
limits reading or writing to the kernel,
.Dq r
limits activities to root,
.Dq o
limits activities to root and the owner of the file having the attribute
read or written, and
.Dq q
allows any user to perform the attribute operation.
The default is to limit activities to the root user, or
.Dq r .
.Pp
This file should not exist before running
.Cm initattr.
.It Cm enable Ar path Ar attrname Ar attrfile
@ -145,9 +122,8 @@ Start extended attributes on the root file system.
.Dl extattrctl initattr 17 /.attribute/md5
.Pp
Create an attribute backing file in /.attribute/md5, and set the maximum
size of each attribute to 17 bytes. Sparse files are used for storing the
attributes, and the default permissions limiting access to the root user
are implied.
size of each attribute to 17 bytes, with a sparse file used for storing
the attributes.
.Pp
.Dl extattrctl enable / md5 /.attribute/md5
.Pp

View file

@ -54,41 +54,12 @@ usage(void)
"usage:\n"
" extattrctl start [path]\n"
" extattrctl stop [path]\n"
" extattrctl initattr [-p path] [-r [kroa]] [-w [kroa]] "
"[attrsize] [attrfile]\n"
" extattrctl initattr [-p path] [attrsize] [attrfile]\n"
" extattrctl enable [path] [attrname] [attrfile]\n"
" extattrctl disable [path] [attrname]\n");
exit(-1);
}
/*
* Return a level, or -1
*/
int
extattr_level_from_string(char *string)
{
if (strlen(string) != 1)
return (-1);
switch(string[0]) {
case 'k':
case 'K':
return (UFS_EXTATTR_PERM_KERNEL);
case 'r':
case 'R':
return (UFS_EXTATTR_PERM_ROOT);
case 'o':
case 'O':
return (UFS_EXTATTR_PERM_OWNER);
case 'a':
case 'A':
return (UFS_EXTATTR_PERM_ANYONE);
default:
return (-1);
}
}
long
num_inodes_by_path(char *path)
{
@ -111,8 +82,6 @@ initattr(int argc, char *argv[])
char *fs_path = NULL;
char *zero_buf = NULL;
long loop, num_inodes;
int initattr_rlevel = UFS_EXTATTR_PERM_ROOT;
int initattr_wlevel = UFS_EXTATTR_PERM_ROOT;
int ch, i, error;
optind = 0;
@ -121,16 +90,6 @@ initattr(int argc, char *argv[])
case 'p':
fs_path = strdup(optarg);
break;
case 'r':
initattr_rlevel = extattr_level_from_string(optarg);
if (initattr_rlevel == -1)
usage();
break;
case 'w':
initattr_wlevel = extattr_level_from_string(optarg);
if (initattr_wlevel == -1)
usage();
break;
case '?':
default:
usage();
@ -146,8 +105,6 @@ initattr(int argc, char *argv[])
if ((i = open(argv[1], O_CREAT | O_EXCL | O_WRONLY, 0600)) != -1) {
uef.uef_magic = UFS_EXTATTR_MAGIC;
uef.uef_version = UFS_EXTATTR_VERSION;
uef.uef_write_perm = initattr_wlevel;
uef.uef_read_perm = initattr_rlevel;
uef.uef_size = atoi(argv[0]);
if (write(i, &uef, sizeof(uef)) == -1)
error = -1;
@ -170,7 +127,12 @@ initattr(int argc, char *argv[])
}
}
}
if (i == -1 || error == -1) {
if (i == -1) {
/* unable to open file */
perror(argv[1]);
return (-1);
}
if (error == -1) {
perror(argv[1]);
unlink(argv[1]);
return (-1);
@ -191,29 +153,33 @@ main(int argc, char *argv[])
if (argc != 3)
usage();
error = extattrctl(argv[2], UFS_EXTATTR_CMD_START, 0, 0);
if (error)
perror("extattrctl start");
} else if (!strcmp(argv[1], "stop")) {
if (argc != 3)
usage();
error = extattrctl(argv[2], UFS_EXTATTR_CMD_STOP, 0, 0);
if (error)
perror("extattrctl stop");
} else if (!strcmp(argv[1], "enable")) {
if (argc != 5)
usage();
error = extattrctl(argv[2], UFS_EXTATTR_CMD_ENABLE, argv[3],
argv[4]);
if (error)
perror("extattrctl enable");
} else if (!strcmp(argv[1], "disable")) {
if (argc != 4)
usage();
error = extattrctl(argv[2], UFS_EXTATTR_CMD_DISABLE, argv[3],
NULL);
if (error)
perror("extattrctl disable");
} else if (!strcmp(argv[1], "initattr")) {
argc -= 2;
argv += 2;
error = initattr(argc, argv);
} else
usage();
if (error)
perror(argv[1]);
return(error);
}