/* * Smack uses multiple xattrs. * SMACK64 - for access control, * SMACK64TRANSMUTE - label initialization, * Not saved on files - SMACK64IPIN and SMACK64IPOUT, * Must be set explicitly - SMACK64EXEC and SMACK64MMAP
*/ #define SMACK_INODE_INIT_XATTRS 2
staticint match_opt_prefix(char *s, int l, char **arg)
{ int i;
for (i = 0; i < ARRAY_SIZE(smk_mount_opts); i++) {
size_t len = smk_mount_opts[i].len; if (len > l || memcmp(s, smk_mount_opts[i].name, len)) continue; if (len == l || s[len] != '=') continue;
*arg = s + len + 1; return smk_mount_opts[i].opt;
} return Opt_error;
}
/** * smk_fetch - Fetch the smack label from a file. * @name: type of the label (attribute) * @ip: a pointer to the inode * @dp: a pointer to the dentry * * Returns a pointer to the master list entry for the Smack label, * NULL if there was no label to fetch, or an error code.
*/ staticstruct smack_known *smk_fetch(constchar *name, struct inode *ip, struct dentry *dp)
{ int rc; char *buffer; struct smack_known *skp = NULL;
if (!(ip->i_opflags & IOP_XATTR)) return ERR_PTR(-EOPNOTSUPP);
buffer = kzalloc(SMK_LONGLABEL, GFP_NOFS); if (buffer == NULL) return ERR_PTR(-ENOMEM);
/** * init_inode_smack - initialize an inode security blob * @inode: inode to extract the info from * @skp: a pointer to the Smack label entry to use in the blob *
*/ staticvoid init_inode_smack(struct inode *inode, struct smack_known *skp)
{ struct inode_smack *isp = smack_inode(inode);
isp->smk_inode = skp;
isp->smk_flags = 0;
}
/** * init_task_smack - initialize a task security blob * @tsp: blob to initialize * @task: a pointer to the Smack label for the running task * @forked: a pointer to the Smack label for the forked task *
*/ staticvoid init_task_smack(struct task_smack *tsp, struct smack_known *task, struct smack_known *forked)
{
tsp->smk_task = task;
tsp->smk_forked = forked;
INIT_LIST_HEAD(&tsp->smk_rules);
INIT_LIST_HEAD(&tsp->smk_relabel);
mutex_init(&tsp->smk_rules_lock);
}
/** * smk_copy_rules - copy a rule set * @nhead: new rules header pointer * @ohead: old rules header pointer * @gfp: type of the memory for the allocation * * Returns 0 on success, -ENOMEM on error
*/ staticint smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
gfp_t gfp)
{ struct smack_rule *nrp; struct smack_rule *orp; int rc = 0;
/** * smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_* * @mode: input mode in form of PTRACE_MODE_* * * Returns a converted MAY_* mode usable by smack rules
*/ staticinlineunsignedint smk_ptrace_mode(unsignedint mode)
{ if (mode & PTRACE_MODE_ATTACH) return MAY_READWRITE; if (mode & PTRACE_MODE_READ) return MAY_READ;
return 0;
}
/** * smk_ptrace_rule_check - helper for ptrace access * @tracer: tracer process * @tracee_known: label entry of the process that's about to be traced * @mode: ptrace attachment mode (PTRACE_MODE_*) * @func: name of the function that called us, used for audit * * Returns 0 on access granted, -error on error
*/ staticint smk_ptrace_rule_check(struct task_struct *tracer, struct smack_known *tracee_known, unsignedint mode, constchar *func)
{ int rc; struct smk_audit_info ad, *saip = NULL; struct task_smack *tsp; struct smack_known *tracer_known; conststruct cred *tracercred;
if (sbsp->smk_default) {
ctx->fsdefault = kstrdup(sbsp->smk_default->smk_known, GFP_KERNEL); if (!ctx->fsdefault) return -ENOMEM;
}
if (sbsp->smk_floor) {
ctx->fsfloor = kstrdup(sbsp->smk_floor->smk_known, GFP_KERNEL); if (!ctx->fsfloor) return -ENOMEM;
}
if (sbsp->smk_hat) {
ctx->fshat = kstrdup(sbsp->smk_hat->smk_known, GFP_KERNEL); if (!ctx->fshat) return -ENOMEM;
}
if (isp->smk_flags & SMK_INODE_TRANSMUTE) { if (sbsp->smk_root) {
ctx->fstransmute = kstrdup(sbsp->smk_root->smk_known, GFP_KERNEL); if (!ctx->fstransmute) return -ENOMEM;
}
} return 0;
}
/** * smack_fs_context_dup - Duplicate the security data on fs_context duplication * @fc: The new filesystem context. * @src_fc: The source filesystem context being duplicated. * * Returns 0 on success or -ENOMEM on error.
*/ staticint smack_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
{ struct smack_mnt_opts *dst, *src = src_fc->security;
if (!src) return 0;
fc->security = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL); if (!fc->security) return -ENOMEM;
/** * smack_fs_context_parse_param - Parse a single mount parameter * @fc: The new filesystem context being constructed. * @param: The parameter. * * Returns 0 on success, -ENOPARAM to pass the parameter on or anything else on * error.
*/ staticint smack_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param)
{ struct fs_parse_result result; int opt, rc;
while (1) { char *next = strchr(from, ','); int token, len, rc; char *arg = NULL;
if (next)
len = next - from; else
len = strlen(from);
token = match_opt_prefix(from, len, &arg); if (token != Opt_error) {
arg = kmemdup_nul(arg, from + len - arg, GFP_KERNEL);
rc = smack_add_opt(token, arg, mnt_opts);
kfree(arg); if (unlikely(rc)) { if (*mnt_opts)
smack_free_mnt_opts(*mnt_opts);
*mnt_opts = NULL; return rc;
}
} else { if (!first) { // copy with preceding comma
from--;
len++;
} if (to != from)
memmove(to, from, len);
to += len;
first = false;
} if (!from[len]) break;
from += len + 1;
}
*to = '\0'; return 0;
}
/** * smack_set_mnt_opts - set Smack specific mount options * @sb: the file system superblock * @mnt_opts: Smack mount options * @kern_flags: mount option from kernel space or user space * @set_kern_flags: where to store converted mount opts * * Returns 0 on success, an error code on failure * * Allow filesystems with binary mount data to explicitly set Smack mount * labels.
*/ staticint smack_set_mnt_opts(struct super_block *sb, void *mnt_opts, unsignedlong kern_flags, unsignedlong *set_kern_flags)
{ struct dentry *root = sb->s_root; struct inode *inode = d_backing_inode(root); struct superblock_smack *sp = smack_superblock(sb); struct inode_smack *isp; struct smack_known *skp; struct smack_mnt_opts *opts = mnt_opts; bool transmute = false;
if (sp->smk_flags & SMK_SB_INITIALIZED) return 0;
if (!smack_privileged(CAP_MAC_ADMIN)) { /* * Unprivileged mounts don't get to specify Smack values.
*/ if (opts) return -EPERM; /* * Unprivileged mounts get root and default from the caller.
*/
skp = smk_of_current();
sp->smk_root = skp;
sp->smk_default = skp; /* * For a handful of fs types with no user-controlled * backing store it's okay to trust security labels * in the filesystem. The rest are untrusted.
*/ if (sb->s_user_ns != &init_user_ns &&
sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
sb->s_magic != RAMFS_MAGIC) {
transmute = true;
sp->smk_flags |= SMK_SB_UNTRUSTED;
}
}
sp->smk_flags |= SMK_SB_INITIALIZED;
if (opts) { if (opts->fsdefault) {
skp = smk_import_entry(opts->fsdefault, 0); if (IS_ERR(skp)) return PTR_ERR(skp);
sp->smk_default = skp;
} if (opts->fsfloor) {
skp = smk_import_entry(opts->fsfloor, 0); if (IS_ERR(skp)) return PTR_ERR(skp);
sp->smk_floor = skp;
} if (opts->fshat) {
skp = smk_import_entry(opts->fshat, 0); if (IS_ERR(skp)) return PTR_ERR(skp);
sp->smk_hat = skp;
} if (opts->fsroot) {
skp = smk_import_entry(opts->fsroot, 0); if (IS_ERR(skp)) return PTR_ERR(skp);
sp->smk_root = skp;
} if (opts->fstransmute) {
skp = smk_import_entry(opts->fstransmute, 0); if (IS_ERR(skp)) return PTR_ERR(skp);
sp->smk_root = skp;
transmute = true;
}
}
/* * Initialize the root inode.
*/
init_inode_smack(inode, sp->smk_root);
if (transmute) {
isp = smack_inode(inode);
isp->smk_flags |= SMK_INODE_TRANSMUTE;
}
return 0;
}
/** * smack_sb_statfs - Smack check on statfs * @dentry: identifies the file system in question * * Returns 0 if current can read the floor of the filesystem, * and error code otherwise
*/ staticint smack_sb_statfs(struct dentry *dentry)
{ struct superblock_smack *sbp = smack_superblock(dentry->d_sb); int rc; struct smk_audit_info ad;
/* Decide if this is a secure exec. */ if (bsp->smk_task != bsp->smk_forked)
bprm->secureexec = 1;
return 0;
}
/* * Inode hooks
*/
/** * smack_inode_alloc_security - allocate an inode blob * @inode: the inode in need of a blob * * Returns 0
*/ staticint smack_inode_alloc_security(struct inode *inode)
{ struct smack_known *skp = smk_of_current();
init_inode_smack(inode, skp); return 0;
}
/** * smack_inode_init_security - copy out the smack from an inode * @inode: the newly created inode * @dir: containing directory object * @qstr: unused * @xattrs: where to put the attributes * @xattr_count: current number of LSM-provided xattrs (updated) * * Returns 0 if it all works out, -ENOMEM if there's no memory
*/ staticint smack_inode_init_security(struct inode *inode, struct inode *dir, conststruct qstr *qstr, struct xattr *xattrs, int *xattr_count)
{ struct task_smack *tsp = smack_cred(current_cred()); struct inode_smack *issp = smack_inode(inode); struct smack_known *skp = smk_of_task(tsp); struct smack_known *isp = smk_of_inode(inode); struct smack_known *dsp = smk_of_inode(dir); struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count); int may;
/* * If equal, transmuting already occurred in * smack_dentry_create_files_as(). No need to check again.
*/ if (tsp->smk_task != tsp->smk_transmuted) {
rcu_read_lock();
may = smk_access_entry(skp->smk_known, dsp->smk_known,
&skp->smk_rules);
rcu_read_unlock();
}
/* * In addition to having smk_task equal to smk_transmuted, * if the access rule allows transmutation and the directory * requests transmutation then by all means transmute. * Mark the inode as changed.
*/ if ((tsp->smk_task == tsp->smk_transmuted) ||
(may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
smk_inode_transmutable(dir))) { struct xattr *xattr_transmute;
/* * The caller of smack_dentry_create_files_as() * should have overridden the current cred, so the * inode label was already set correctly in * smack_inode_alloc_security().
*/ if (tsp->smk_task != tsp->smk_transmuted)
isp = issp->smk_inode = dsp;
issp->smk_flags |= SMK_INODE_TRANSMUTE;
xattr_transmute = lsm_get_xattr_slot(xattrs,
xattr_count); if (xattr_transmute) {
xattr_transmute->value = kmemdup(TRANS_TRUE,
TRANS_TRUE_SIZE,
GFP_NOFS); if (!xattr_transmute->value) return -ENOMEM;
/* * You need write access to the thing you're removing
*/
rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad);
rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); if (rc == 0) { /* * You also need write access to the containing directory
*/
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE);
smk_ad_setfield_u_fs_inode(&ad, dir);
rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad);
rc = smk_bu_inode(dir, MAY_WRITE, rc);
}
return rc;
}
/** * smack_inode_rename - Smack check on rename * @old_inode: unused * @old_dentry: the old object * @new_inode: unused * @new_dentry: the new object * * Read and write access is required on both the old and * new directories. * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry)
{ int rc; struct smack_known *isp; struct smk_audit_info ad;
/** * smack_inode_permission - Smack version of permission() * @inode: the inode in question * @mask: the access requested * * This is the important Smack hook. * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_permission(struct inode *inode, int mask)
{ struct superblock_smack *sbsp = smack_superblock(inode->i_sb); struct smk_audit_info ad; int no_block = mask & MAY_NOT_BLOCK; int rc;
mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); /* * No permission to check. Existence test. Yup, it's there.
*/ if (mask == 0) return 0;
if (sbsp->smk_flags & SMK_SB_UNTRUSTED) { if (smk_of_inode(inode) != sbsp->smk_root) return -EACCES;
}
/* May be droppable after audit */ if (no_block) return -ECHILD;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE);
smk_ad_setfield_u_fs_inode(&ad, inode);
rc = smk_curacc(smk_of_inode(inode), mask, &ad);
rc = smk_bu_inode(inode, mask, rc); return rc;
}
/** * smack_inode_setattr - Smack check for setting attributes * @idmap: idmap of the mount * @dentry: the object * @iattr: for the force flag * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *iattr)
{ struct smk_audit_info ad; int rc;
/* * Need to allow for clearing the setuid bit.
*/ if (iattr->ia_valid & ATTR_FORCE) return 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
/** * smack_inode_getattr - Smack check for getting attributes * @path: path to extract the info from * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_getattr(conststruct path *path)
{ struct smk_audit_info ad; struct inode *inode = d_backing_inode(path->dentry); int rc;
/** * smack_inode_xattr_skipcap - Skip the xattr capability checks? * @name: name of the xattr * * Returns 1 to indicate that Smack "owns" the access control rights to xattrs * named @name; the LSM layer should avoid enforcing any traditional * capability based access controls on this xattr. Returns 0 to indicate that * Smack does not "own" the access control rights to xattrs named @name and is * deferring to the LSM layer for further access controls, including capability * based controls.
*/ staticint smack_inode_xattr_skipcap(constchar *name)
{ if (strncmp(name, XATTR_SMACK_SUFFIX, strlen(XATTR_SMACK_SUFFIX))) return 0;
/** * smack_inode_setxattr - Smack check for setting xattrs * @idmap: idmap of the mount * @dentry: the object * @name: name of the attribute * @value: value of the attribute * @size: size of the value * @flags: unused * * This protects the Smack attribute explicitly. * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, constchar *name, constvoid *value, size_t size, int flags)
{ struct smk_audit_info ad; struct smack_known *skp; int check_priv = 0; int check_import = 0; int check_star = 0; int rc = 0;
/** * smack_inode_removexattr - Smack check on removexattr * @idmap: idmap of the mount * @dentry: the object * @name: name of the attribute * * Removing the Smack attribute requires CAP_MAC_ADMIN * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, constchar *name)
{ struct inode_smack *isp; struct smk_audit_info ad; int rc = 0;
/** * smack_inode_set_acl - Smack check for setting posix acls * @idmap: idmap of the mnt this request came from * @dentry: the object * @acl_name: name of the posix acl * @kacl: the posix acls * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, constchar *acl_name, struct posix_acl *kacl)
{ struct smk_audit_info ad; int rc;
/** * smack_inode_get_acl - Smack check for getting posix acls * @idmap: idmap of the mnt this request came from * @dentry: the object * @acl_name: name of the posix acl * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, constchar *acl_name)
{ struct smk_audit_info ad; int rc;
/** * smack_inode_remove_acl - Smack check for getting posix acls * @idmap: idmap of the mnt this request came from * @dentry: the object * @acl_name: name of the posix acl * * Returns 0 if access is permitted, an error code otherwise
*/ staticint smack_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, constchar *acl_name)
{ struct smk_audit_info ad; int rc;
if (alloc) {
*buffer = kstrdup(label, GFP_KERNEL); if (*buffer == NULL) return -ENOMEM;
}
return label_len;
}
/** * smack_inode_listsecurity - list the Smack attributes * @inode: the object * @buffer: where they go * @buffer_size: size of buffer
*/ staticint smack_inode_listsecurity(struct inode *inode, char *buffer,
size_t buffer_size)
{ int len = sizeof(XATTR_NAME_SMACK);
if (buffer != NULL && len <= buffer_size)
memcpy(buffer, XATTR_NAME_SMACK, len);
return len;
}
/** * smack_inode_getlsmprop - Extract inode's security id * @inode: inode to extract the info from * @prop: where result will be saved
*/ staticvoid smack_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop)
{
prop->smack.skp = smk_of_inode(inode);
}
/* * File Hooks
*/
/* * There is no smack_file_permission hook * * Should access checks be done on each read or write? * UNICOS and SELinux say yes. * Trusted Solaris, Trusted Irix, and just about everyone else says no. * * I'll say no for now. Smack does not do the frequent * label changing that SELinux does.
*/
/** * smack_file_alloc_security - assign a file security blob * @file: the object * * The security blob for a file is a pointer to the master * label list, so no allocation is done. * * f_security is the owner security information. It * isn't used on file access checks, it's for send_sigio. * * Returns 0
*/ staticint smack_file_alloc_security(struct file *file)
{ struct smack_known **blob = smack_file(file);
*blob = smk_of_current(); return 0;
}
/** * smack_file_ioctl - Smack check on ioctls * @file: the object * @cmd: what to do * @arg: unused * * Relies heavily on the correct use of the ioctl command conventions. * * Returns 0 if allowed, error code otherwise
*/ staticint smack_file_ioctl(struct file *file, unsignedint cmd, unsignedlong arg)
{ int rc = 0; struct smk_audit_info ad; struct inode *inode = file_inode(file);
/** * smack_file_fcntl - Smack check on fcntl * @file: the object * @cmd: what action to check * @arg: unused * * Generally these operations are harmless. * File locking operations present an obvious mechanism * for passing information, so they require write access. * * Returns 0 if current has access, error code otherwise
*/ staticint smack_file_fcntl(struct file *file, unsignedint cmd, unsignedlong arg)
{ struct smk_audit_info ad; int rc = 0; struct inode *inode = file_inode(file);
if (unlikely(IS_PRIVATE(inode))) return 0;
switch (cmd) { case F_GETLK: break; case F_SETLK: case F_SETLKW:
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path);
rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad);
rc = smk_bu_file(file, MAY_LOCK, rc); break; case F_SETOWN: case F_SETSIG:
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path);
rc = smk_curacc(smk_of_inode(inode), MAY_WRITE, &ad);
rc = smk_bu_file(file, MAY_WRITE, rc); break; default: break;
}
return rc;
}
/** * smack_mmap_file - Check permissions for a mmap operation. * @file: contains the file structure for file to map (may be NULL). * @reqprot: contains the protection requested by the application. * @prot: contains the protection that will be applied by the kernel. * @flags: contains the operational flags. * * The @file may be NULL, e.g. if mapping anonymous memory. * * Return 0 if permission is granted.
*/ staticint smack_mmap_file(struct file *file, unsignedlong reqprot, unsignedlong prot, unsignedlong flags)
{ struct smack_known *skp; struct smack_known *mkp; struct smack_rule *srp; struct task_smack *tsp; struct smack_known *okp; struct inode_smack *isp; struct superblock_smack *sbsp; int may; int mmay; int tmay; int rc;
if (file == NULL) return 0;
if (unlikely(IS_PRIVATE(file_inode(file)))) return 0;
rcu_read_lock(); /* * For each Smack rule associated with the subject * label verify that the SMACK64MMAP also has access * to that rule's object label.
*/
list_for_each_entry_rcu(srp, &skp->smk_rules, list) {
okp = srp->smk_object; /* * Matching labels always allows access.
*/ if (mkp->smk_known == okp->smk_known) continue; /* * If there is a matching local rule take * that into account as well.
*/
may = smk_access_entry(srp->smk_subject->smk_known,
okp->smk_known,
&tsp->smk_rules); if (may == -ENOENT)
may = srp->smk_access; else
may &= srp->smk_access; /* * If may is zero the SMACK64MMAP subject can't * possibly have less access.
*/ if (may == 0) continue;
/* * Fetch the global list entry. * If there isn't one a SMACK64MMAP subject * can't have as much access as current.
*/
mmay = smk_access_entry(mkp->smk_known, okp->smk_known,
&mkp->smk_rules); if (mmay == -ENOENT) {
rc = -EACCES; break;
} /* * If there is a local entry it modifies the * potential access, too.
*/
tmay = smk_access_entry(mkp->smk_known, okp->smk_known,
&tsp->smk_rules); if (tmay != -ENOENT)
mmay &= tmay;
/* * If there is any access available to current that is * not available to a SMACK64MMAP subject * deny access.
*/ if ((may | mmay) != mmay) {
rc = -EACCES; break;
}
}
rcu_read_unlock();
return rc;
}
/** * smack_file_set_fowner - set the file security blob value * @file: object in question *
*/ staticvoid smack_file_set_fowner(struct file *file)
{ struct smack_known **blob = smack_file(file);
*blob = smk_of_current();
}
/** * smack_file_send_sigiotask - Smack on sigio * @tsk: The target task * @fown: the object the signal come from * @signum: unused * * Allow a privileged task to get signals even if it shouldn't * * Returns 0 if a subject with the object's smack could * write to the task, an error code otherwise.
*/ staticint smack_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum)
{ struct smack_known **blob; struct smack_known *skp; struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred)); conststruct cred *tcred; struct file *file; int rc; struct smk_audit_info ad;
/* * struct fown_struct is never outside the context of a struct file
*/
file = fown->file;
/* we don't log here as rc can be overridden */
blob = smack_file(file);
skp = *blob;
rc = smk_access(skp, tkp, MAY_DELIVER, NULL);
rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, rc);
if (inode->i_sb->s_magic == SOCKFS_MAGIC) {
sock = SOCKET_I(inode);
ssp = smack_sock(sock->sk);
tsp = smack_cred(current_cred()); /* * If the receiving process can't write to the * passed socket or if the passed socket can't * write to the receiving process don't accept * the passed socket.
*/
rc = smk_access(tsp->smk_task, ssp->smk_out, MAY_WRITE, &ad);
rc = smk_bu_file(file, may, rc); if (rc < 0) return rc;
rc = smk_access(ssp->smk_in, tsp->smk_task, MAY_WRITE, &ad);
rc = smk_bu_file(file, may, rc); return rc;
} /* * This code relies on bitmasks.
*/ if (file->f_mode & FMODE_READ)
may = MAY_READ; if (file->f_mode & FMODE_WRITE)
may |= MAY_WRITE;
rc = smk_curacc(smk_of_inode(inode), may, &ad);
rc = smk_bu_file(file, may, rc); return rc;
}
/** * smack_file_open - Smack dentry open processing * @file: the object * * Set the security blob in the file structure. * Allow the open only if the task has read access. There are * many read operations (e.g. fstat) that you can do with an * fd even if you have the file open write-only. * * Returns 0 if current has access, error code otherwise
*/ staticint smack_file_open(struct file *file)
{ struct task_smack *tsp = smack_cred(file->f_cred); struct inode *inode = file_inode(file); struct smk_audit_info ad; int rc;
/** * smack_cred_alloc_blank - "allocate" blank task-level security credentials * @cred: the new credentials * @gfp: the atomicity of any memory allocations * * Prepare a blank set of credentials for modification. This must allocate all * the memory the LSM module might require such that cred_transfer() can * complete without error.
*/ staticint smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
init_task_smack(smack_cred(cred), NULL, NULL); return 0;
}
list_for_each_safe(l, n, &tsp->smk_rules) {
rp = list_entry(l, struct smack_rule, list);
list_del(&rp->list);
kmem_cache_free(smack_rule_cache, rp);
}
}
/** * smack_cred_prepare - prepare new set of credentials for modification * @new: the new credentials * @old: the original credentials * @gfp: the atomicity of any memory allocations * * Prepare a new set of credentials for modification.
*/ staticint smack_cred_prepare(struct cred *new, conststruct cred *old,
gfp_t gfp)
{ struct task_smack *old_tsp = smack_cred(old); struct task_smack *new_tsp = smack_cred(new); int rc;
/** * smack_cred_transfer - Transfer the old credentials to the new credentials * @new: the new credentials * @old: the original credentials * * Fill in a set of blank credentials from another set of credentials.
*/ staticvoid smack_cred_transfer(struct cred *new, conststruct cred *old)
{ struct task_smack *old_tsp = smack_cred(old); struct task_smack *new_tsp = smack_cred(new);
/** * smack_cred_getsecid - get the secid corresponding to a creds structure * @cred: the object creds * @secid: where to put the result * * Sets the secid to contain a u32 version of the smack label.
*/ staticvoid smack_cred_getsecid(conststruct cred *cred, u32 *secid)
{ struct smack_known *skp;
/** * smack_cred_getlsmprop - get the Smack label for a creds structure * @cred: the object creds * @prop: where to put the data * * Sets the Smack part of the ref
*/ staticvoid smack_cred_getlsmprop(conststruct cred *cred, struct lsm_prop *prop)
{
rcu_read_lock();
prop->smack.skp = smk_of_task(smack_cred(cred));
rcu_read_unlock();
}
/** * smack_kernel_act_as - Set the subjective context in a set of credentials * @new: points to the set of credentials to be modified. * @secid: specifies the security ID to be set * * Set the security data for a kernel service.
*/ staticint smack_kernel_act_as(struct cred *new, u32 secid)
{ struct task_smack *new_tsp = smack_cred(new);
/** * smack_kernel_create_files_as - Set the file creation label in a set of creds * @new: points to the set of credentials to be modified * @inode: points to the inode to use as a reference * * Set the file creation context in a set of credentials to the same * as the objective context of the specified inode
*/ staticint smack_kernel_create_files_as(struct cred *new, struct inode *inode)
{ struct inode_smack *isp = smack_inode(inode); struct task_smack *tsp = smack_cred(new);
/** * smk_curacc_on_task - helper to log task related access * @p: the task object * @access: the access requested * @caller: name of the calling function for audit * * Return 0 if access is permitted
*/ staticint smk_curacc_on_task(struct task_struct *p, int access, constchar *caller)
{ struct smk_audit_info ad; struct smack_known *skp = smk_of_task_struct_obj(p); int rc;
/** * smack_task_setpgid - Smack check on setting pgid * @p: the task object * @pgid: unused * * Return 0 if write access is permitted
*/ staticint smack_task_setpgid(struct task_struct *p, pid_t pgid)
{ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/** * smack_task_getpgid - Smack access check for getpgid * @p: the object task * * Returns 0 if current can read the object task, error code otherwise
*/ staticint smack_task_getpgid(struct task_struct *p)
{ return smk_curacc_on_task(p, MAY_READ, __func__);
}
/** * smack_task_getsid - Smack access check for getsid * @p: the object task * * Returns 0 if current can read the object task, error code otherwise
*/ staticint smack_task_getsid(struct task_struct *p)
{ return smk_curacc_on_task(p, MAY_READ, __func__);
}
/** * smack_current_getlsmprop_subj - get the subjective secid of the current task * @prop: where to put the result * * Sets the secid to contain a u32 version of the task's subjective smack label.
*/ staticvoid smack_current_getlsmprop_subj(struct lsm_prop *prop)
{
prop->smack.skp = smk_of_current();
}
/** * smack_task_getlsmprop_obj - get the objective data of the task * @p: the task * @prop: where to put the result * * Sets the secid to contain a u32 version of the task's objective smack label.
*/ staticvoid smack_task_getlsmprop_obj(struct task_struct *p, struct lsm_prop *prop)
{
prop->smack.skp = smk_of_task_struct_obj(p);
}
/** * smack_task_setnice - Smack check on setting nice * @p: the task object * @nice: unused * * Return 0 if write access is permitted
*/ staticint smack_task_setnice(struct task_struct *p, int nice)
{ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/** * smack_task_setioprio - Smack check on setting ioprio * @p: the task object * @ioprio: unused * * Return 0 if write access is permitted
*/ staticint smack_task_setioprio(struct task_struct *p, int ioprio)
{ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/** * smack_task_getioprio - Smack check on reading ioprio * @p: the task object * * Return 0 if read access is permitted
*/ staticint smack_task_getioprio(struct task_struct *p)
{ return smk_curacc_on_task(p, MAY_READ, __func__);
}
/** * smack_task_setscheduler - Smack check on setting scheduler * @p: the task object * * Return 0 if read access is permitted
*/ staticint smack_task_setscheduler(struct task_struct *p)
{ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/** * smack_task_getscheduler - Smack check on reading scheduler * @p: the task object * * Return 0 if read access is permitted
*/ staticint smack_task_getscheduler(struct task_struct *p)
{ return smk_curacc_on_task(p, MAY_READ, __func__);
}
/** * smack_task_movememory - Smack check on moving memory * @p: the task object * * Return 0 if write access is permitted
*/ staticint smack_task_movememory(struct task_struct *p)
{ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/** * smack_task_kill - Smack check on signal delivery * @p: the task object * @info: unused * @sig: unused * @cred: identifies the cred to use in lieu of current's * * Return 0 if write access is permitted *
*/ staticint smack_task_kill(struct task_struct *p, struct kernel_siginfo *info, int sig, conststruct cred *cred)
{ struct smk_audit_info ad; struct smack_known *skp; struct smack_known *tkp = smk_of_task_struct_obj(p); int rc;
if (!sig) return 0; /* null signal; existence test */
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, p); /* * Sending a signal requires that the sender * can write the receiver.
*/ if (cred == NULL) {
rc = smk_curacc(tkp, MAY_DELIVER, &ad);
rc = smk_bu_task(p, MAY_DELIVER, rc); return rc;
} /* * If the cred isn't NULL we're dealing with some USB IO * specific behavior. This is not clean. For one thing * we can't take privilege into account.
*/
skp = smk_of_task(smack_cred(cred));
rc = smk_access(skp, tkp, MAY_DELIVER, &ad);
rc = smk_bu_note("USB signal", skp, tkp, MAY_DELIVER, rc); return rc;
}
/** * smack_task_to_inode - copy task smack into the inode blob * @p: task to copy from * @inode: inode to copy to * * Sets the smack pointer in the inode security blob
*/ staticvoid smack_task_to_inode(struct task_struct *p, struct inode *inode)
{ struct inode_smack *isp = smack_inode(inode); struct smack_known *skp = smk_of_task_struct_obj(p);
/** * smack_sk_clone_security - Copy security context * @sk: the old socket * @newsk: the new socket * * Copy the security context of the old socket pointer to the cloned
*/ staticvoid smack_sk_clone_security(conststruct sock *sk, struct sock *newsk)
{ struct socket_smack *ssp_old = smack_sock(sk); struct socket_smack *ssp_new = smack_sock(newsk);
*ssp_new = *ssp_old;
}
/** * smack_ipv4host_label - check host based restrictions * @sip: the object end * * looks for host based access restrictions * * This version will only be appropriate for really small sets of single label * hosts. The caller is responsible for ensuring that the RCU read lock is * taken before calling this function. * * Returns the label of the far end or NULL if it's not special.
*/ staticstruct smack_known *smack_ipv4host_label(struct sockaddr_in *sip)
{ struct smk_net4addr *snp; struct in_addr *siap = &sip->sin_addr;
if (siap->s_addr == 0) return NULL;
list_for_each_entry_rcu(snp, &smk_net4addr_list, list) /* * we break after finding the first match because * the list is sorted from longest to shortest mask * so we have found the most specific match
*/ if (snp->smk_host.s_addr ==
(siap->s_addr & snp->smk_mask.s_addr)) return snp->smk_label;
return NULL;
}
#if IS_ENABLED(CONFIG_IPV6) /* * smk_ipv6_localhost - Check for local ipv6 host address * @sip: the address * * Returns boolean true if this is the localhost address
*/ staticbool smk_ipv6_localhost(struct sockaddr_in6 *sip)
{
__be16 *be16p = (__be16 *)&sip->sin6_addr;
__be32 *be32p = (__be32 *)&sip->sin6_addr;
/** * smack_ipv6host_label - check host based restrictions * @sip: the object end * * looks for host based access restrictions * * This version will only be appropriate for really small sets of single label * hosts. The caller is responsible for ensuring that the RCU read lock is * taken before calling this function. * * Returns the label of the far end or NULL if it's not special.
*/ staticstruct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
{ struct smk_net6addr *snp; struct in6_addr *sap = &sip->sin6_addr; int i; int found = 0;
/* * It's local. Don't look for a host label.
*/ if (smk_ipv6_localhost(sip)) return NULL;
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) { /* * If the label is NULL the entry has * been renounced. Ignore it.
*/ if (snp->smk_label == NULL) continue; /* * we break after finding the first match because * the list is sorted from longest to shortest mask * so we have found the most specific match
*/ for (found = 1, i = 0; i < 8; i++) { if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
snp->smk_host.s6_addr16[i]) {
found = 0; break;
}
} if (found) return snp->smk_label;
}
return NULL;
} #endif/* CONFIG_IPV6 */
/** * smack_netlbl_add - Set the secattr on a socket * @sk: the socket * * Attach the outbound smack value (smk_out) to the socket. * * Returns 0 on success or an error code
*/ staticint smack_netlbl_add(struct sock *sk)
{ struct socket_smack *ssp = smack_sock(sk); struct smack_known *skp = ssp->smk_out; int rc;
/** * smack_netlbl_delete - Remove the secattr from a socket * @sk: the socket * * Remove the outbound smack value from a socket
*/ staticvoid smack_netlbl_delete(struct sock *sk)
{ struct socket_smack *ssp = smack_sock(sk);
/* * Take the label off the socket if one is set.
*/ if (ssp->smk_state != SMK_NETLBL_LABELED) return;
/** * smk_ipv4_check - Perform IPv4 host access checks * @sk: the socket * @sap: the destination address * * Set the correct secattr for the given socket based on the destination * address and perform any outbound access checks needed. * * Returns 0 on success or an error code. *
*/ staticint smk_ipv4_check(struct sock *sk, struct sockaddr_in *sap)
{ struct smack_known *skp; int rc = 0; struct smack_known *hkp; struct socket_smack *ssp = smack_sock(sk); struct smk_audit_info ad;
#ifdef SMACK_IPV6_PORT_LABELING /** * smk_ipv6_port_label - Smack port access table management * @sock: socket * @address: address * * Create or update the port list entry
*/ staticvoid smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
{ struct sock *sk = sock->sk; struct sockaddr_in6 *addr6; struct socket_smack *ssp = smack_sock(sock->sk); struct smk_port_label *spp; unsignedshort port = 0;
if (address == NULL) { /* * This operation is changing the Smack information * on the bound socket. Take the changes to the port * as well.
*/
rcu_read_lock();
list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { if (sk != spp->smk_sock) continue;
spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out;
rcu_read_unlock(); return;
} /* * A NULL address is only used for updating existing * bound entries. If there isn't one, it's OK.
*/
rcu_read_unlock(); return;
}
addr6 = (struct sockaddr_in6 *)address;
port = ntohs(addr6->sin6_port); /* * This is a special case that is safely ignored.
*/ if (port == 0) return;
/* * Look for an existing port list entry. * This is an indication that a port is getting reused.
*/
rcu_read_lock();
list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port || spp->smk_sock_type != sock->type) continue; if (spp->smk_can_reuse != 1) {
rcu_read_unlock(); return;
}
spp->smk_port = port;
spp->smk_sock = sk;
spp->smk_in = ssp->smk_in;
spp->smk_out = ssp->smk_out;
spp->smk_can_reuse = 0;
rcu_read_unlock(); return;
}
rcu_read_unlock(); /* * A new port entry is required.
*/
spp = kzalloc(sizeof(*spp), GFP_KERNEL); if (spp == NULL) return;
/* * The other end is a single label host.
*/ if (skp != NULL && object != NULL) return smk_ipv6_check(skp, object, address, act); if (skp == NULL)
skp = smack_net_ambient; if (object == NULL)
object = smack_net_ambient;
/* * It's remote, so port lookup does no good.
*/ if (!smk_ipv6_localhost(address)) return smk_ipv6_check(skp, object, address, act);
/* * It's local so the send check has to have passed.
*/ if (act == SMK_RECEIVING) return 0;
port = ntohs(address->sin6_port);
rcu_read_lock();
list_for_each_entry_rcu(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port || spp->smk_sock_type != sk->sk_type) continue;
object = spp->smk_in; if (act == SMK_CONNECTING)
ssp->smk_packet = spp->smk_out; break;
}
rcu_read_unlock();
skp = smk_import_entry(value, size); if (IS_ERR(skp)) return PTR_ERR(skp);
if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
nsp->smk_inode = skp;
nsp->smk_flags |= SMK_INODE_INSTANT; return 0;
} /* * The rest of the Smack xattrs are only on sockets.
*/ if (inode->i_sb->s_magic != SOCKFS_MAGIC) return -EOPNOTSUPP;
#ifdef SMACK_IPV6_PORT_LABELING if (sock->sk->sk_family == PF_INET6)
smk_ipv6_port_label(sock, NULL); #endif
return 0;
}
/** * smack_socket_post_create - finish socket setup * @sock: the socket * @family: protocol family * @type: unused * @protocol: unused * @kern: unused * * Sets the netlabel information on the socket * * Returns 0 on success, and error code otherwise
*/ staticint smack_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern)
{ struct socket_smack *ssp;
if (sock->sk == NULL) return 0;
/* * Sockets created by kernel threads receive web label.
*/ if (unlikely(current->flags & PF_KTHREAD)) {
ssp = smack_sock(sock->sk);
ssp->smk_in = &smack_known_web;
ssp->smk_out = &smack_known_web;
}
if (family != PF_INET) return 0; /* * Set the outbound netlbl.
*/ return smack_netlbl_add(sock->sk);
}
#ifdef SMACK_IPV6_PORT_LABELING /** * smack_socket_bind - record port binding information. * @sock: the socket * @address: the port address * @addrlen: size of the address * * Records the label bound to a port. * * Returns 0 on success, and error code otherwise
*/ staticint smack_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{ if (sock->sk != NULL && sock->sk->sk_family == PF_INET6) { if (addrlen < SIN6_LEN_RFC2133 ||
address->sa_family != AF_INET6) return -EINVAL;
smk_ipv6_port_label(sock, address);
} return 0;
} #endif/* SMACK_IPV6_PORT_LABELING */
/** * smack_socket_connect - connect access check * @sock: the socket * @sap: the other end * @addrlen: size of sap * * Verifies that a connection may be possible * * Returns 0 on success, and error code otherwise
*/ staticint smack_socket_connect(struct socket *sock, struct sockaddr *sap, int addrlen)
{ int rc = 0;
if (sock->sk == NULL) return 0; if (sock->sk->sk_family != PF_INET &&
(!IS_ENABLED(CONFIG_IPV6) || sock->sk->sk_family != PF_INET6)) return 0; if (addrlen < offsetofend(struct sockaddr, sa_family)) return 0;
/** * smack_flags_to_may - convert S_ to MAY_ values * @flags: the S_ value * * Returns the equivalent MAY_ value
*/ staticint smack_flags_to_may(int flags)
{ int may = 0;
if (flags & S_IRUGO)
may |= MAY_READ; if (flags & S_IWUGO)
may |= MAY_WRITE; if (flags & S_IXUGO)
may |= MAY_EXEC;
return may;
}
/** * smack_msg_msg_alloc_security - Set the security blob for msg_msg * @msg: the object * * Returns 0
*/ staticint smack_msg_msg_alloc_security(struct msg_msg *msg)
{ struct smack_known **blob = smack_msg_msg(msg);
*blob = smk_of_current(); return 0;
}
/** * smack_of_ipc - the smack pointer for the ipc * @isp: the object * * Returns a pointer to the smack value
*/ staticstruct smack_known *smack_of_ipc(struct kern_ipc_perm *isp)
{ struct smack_known **blob = smack_ipc(isp);
return *blob;
}
/** * smack_ipc_alloc_security - Set the security blob for ipc * @isp: the object * * Returns 0
*/ staticint smack_ipc_alloc_security(struct kern_ipc_perm *isp)
{ struct smack_known **blob = smack_ipc(isp);
*blob = smk_of_current(); return 0;
}
/** * smk_curacc_shm : check if current has access on shm * @isp : the object * @access : access requested * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smk_curacc_shm(struct kern_ipc_perm *isp, int access)
{ struct smack_known *ssp = smack_of_ipc(isp); struct smk_audit_info ad; int rc;
/** * smack_shm_associate - Smack access check for shm * @isp: the object * @shmflg: access requested * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_shm_associate(struct kern_ipc_perm *isp, int shmflg)
{ int may;
may = smack_flags_to_may(shmflg); return smk_curacc_shm(isp, may);
}
/** * smack_shm_shmctl - Smack access check for shm * @isp: the object * @cmd: what it wants to do * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_shm_shmctl(struct kern_ipc_perm *isp, int cmd)
{ int may;
switch (cmd) { case IPC_STAT: case SHM_STAT: case SHM_STAT_ANY:
may = MAY_READ; break; case IPC_SET: case SHM_LOCK: case SHM_UNLOCK: case IPC_RMID:
may = MAY_READWRITE; break; case IPC_INFO: case SHM_INFO: /* * System level information.
*/ return 0; default: return -EINVAL;
} return smk_curacc_shm(isp, may);
}
/** * smack_shm_shmat - Smack access for shmat * @isp: the object * @shmaddr: unused * @shmflg: access requested * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_shm_shmat(struct kern_ipc_perm *isp, char __user *shmaddr, int shmflg)
{ int may;
may = smack_flags_to_may(shmflg); return smk_curacc_shm(isp, may);
}
/** * smk_curacc_sem : check if current has access on sem * @isp : the object * @access : access requested * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smk_curacc_sem(struct kern_ipc_perm *isp, int access)
{ struct smack_known *ssp = smack_of_ipc(isp); struct smk_audit_info ad; int rc;
/** * smack_sem_associate - Smack access check for sem * @isp: the object * @semflg: access requested * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_sem_associate(struct kern_ipc_perm *isp, int semflg)
{ int may;
may = smack_flags_to_may(semflg); return smk_curacc_sem(isp, may);
}
/** * smack_sem_semctl - Smack access check for sem * @isp: the object * @cmd: what it wants to do * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_sem_semctl(struct kern_ipc_perm *isp, int cmd)
{ int may;
switch (cmd) { case GETPID: case GETNCNT: case GETZCNT: case GETVAL: case GETALL: case IPC_STAT: case SEM_STAT: case SEM_STAT_ANY:
may = MAY_READ; break; case SETVAL: case SETALL: case IPC_RMID: case IPC_SET:
may = MAY_READWRITE; break; case IPC_INFO: case SEM_INFO: /* * System level information
*/ return 0; default: return -EINVAL;
}
return smk_curacc_sem(isp, may);
}
/** * smack_sem_semop - Smack checks of semaphore operations * @isp: the object * @sops: unused * @nsops: unused * @alter: unused * * Treated as read and write in all cases. * * Returns 0 if access is allowed, error code otherwise
*/ staticint smack_sem_semop(struct kern_ipc_perm *isp, struct sembuf *sops, unsigned nsops, int alter)
{ return smk_curacc_sem(isp, MAY_READWRITE);
}
/** * smk_curacc_msq : helper to check if current has access on msq * @isp : the msq * @access : access requested * * return 0 if current has access, error otherwise
*/ staticint smk_curacc_msq(struct kern_ipc_perm *isp, int access)
{ struct smack_known *msp = smack_of_ipc(isp); struct smk_audit_info ad; int rc;
/** * smack_msg_queue_associate - Smack access check for msg_queue * @isp: the object * @msqflg: access requested * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_msg_queue_associate(struct kern_ipc_perm *isp, int msqflg)
{ int may;
may = smack_flags_to_may(msqflg); return smk_curacc_msq(isp, may);
}
/** * smack_msg_queue_msgctl - Smack access check for msg_queue * @isp: the object * @cmd: what it wants to do * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_msg_queue_msgctl(struct kern_ipc_perm *isp, int cmd)
{ int may;
switch (cmd) { case IPC_STAT: case MSG_STAT: case MSG_STAT_ANY:
may = MAY_READ; break; case IPC_SET: case IPC_RMID:
may = MAY_READWRITE; break; case IPC_INFO: case MSG_INFO: /* * System level information
*/ return 0; default: return -EINVAL;
}
return smk_curacc_msq(isp, may);
}
/** * smack_msg_queue_msgsnd - Smack access check for msg_queue * @isp: the object * @msg: unused * @msqflg: access requested * * Returns 0 if current has the requested access, error code otherwise
*/ staticint smack_msg_queue_msgsnd(struct kern_ipc_perm *isp, struct msg_msg *msg, int msqflg)
{ int may;
may = smack_flags_to_may(msqflg); return smk_curacc_msq(isp, may);
}
/** * smack_msg_queue_msgrcv - Smack access check for msg_queue * @isp: the object * @msg: unused * @target: unused * @type: unused * @mode: unused * * Returns 0 if current has read and write access, error code otherwise
*/ staticint smack_msg_queue_msgrcv(struct kern_ipc_perm *isp, struct msg_msg *msg, struct task_struct *target, long type, int mode)
{ return smk_curacc_msq(isp, MAY_READWRITE);
}
/** * smack_ipc_permission - Smack access for ipc_permission() * @ipp: the object permissions * @flag: access requested * * Returns 0 if current has read and write access, error code otherwise
*/ staticint smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
{ struct smack_known **blob = smack_ipc(ipp); struct smack_known *iskp = *blob; int may = smack_flags_to_may(flag); struct smk_audit_info ad; int rc;
/** * smack_ipc_getlsmprop - Extract smack security data * @ipp: the object permissions * @prop: where result will be saved
*/ staticvoid smack_ipc_getlsmprop(struct kern_ipc_perm *ipp, struct lsm_prop *prop)
{ struct smack_known **iskpp = smack_ipc(ipp);
prop->smack.skp = *iskpp;
}
/** * smack_d_instantiate - Make sure the blob is correct on an inode * @opt_dentry: dentry where inode will be attached * @inode: the object * * Set the inode's security blob if it hasn't been done already.
*/ staticvoid smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
{ struct super_block *sbp; struct superblock_smack *sbsp; struct inode_smack *isp; struct smack_known *skp; struct smack_known *ckp = smk_of_current(); struct smack_known *final; char trattr[TRANS_TRUE_SIZE]; int transflag = 0; int rc; struct dentry *dp;
if (inode == NULL) return;
isp = smack_inode(inode);
/* * If the inode is already instantiated * take the quick way out
*/ if (isp->smk_flags & SMK_INODE_INSTANT) return;
sbp = inode->i_sb;
sbsp = smack_superblock(sbp); /* * We're going to use the superblock default label * if there's no label on the file.
*/
final = sbsp->smk_default;
/* * If this is the root inode the superblock * may be in the process of initialization. * If that is the case use the root value out * of the superblock.
*/ if (opt_dentry->d_parent == opt_dentry) { switch (sbp->s_magic) { case CGROUP_SUPER_MAGIC: case CGROUP2_SUPER_MAGIC: /* * The cgroup filesystem is never mounted, * so there's no opportunity to set the mount * options.
*/
sbsp->smk_root = &smack_known_star;
sbsp->smk_default = &smack_known_star;
isp->smk_inode = sbsp->smk_root; break; case TMPFS_MAGIC: /* * What about shmem/tmpfs anonymous files with dentry * obtained from d_alloc_pseudo()?
*/
isp->smk_inode = smk_of_current(); break; case PIPEFS_MAGIC:
isp->smk_inode = smk_of_current(); break; case SOCKFS_MAGIC: /* * Socket access is controlled by the socket * structures associated with the task involved.
*/
isp->smk_inode = &smack_known_star; break; default:
isp->smk_inode = sbsp->smk_root; break;
}
isp->smk_flags |= SMK_INODE_INSTANT; return;
}
/* * This is pretty hackish. * Casey says that we shouldn't have to do * file system specific code, but it does help * with keeping it simple.
*/ switch (sbp->s_magic) { case SMACK_MAGIC: case CGROUP_SUPER_MAGIC: case CGROUP2_SUPER_MAGIC: /* * Casey says that it's a little embarrassing * that the smack file system doesn't do * extended attributes. * * Cgroupfs is special
*/
final = &smack_known_star; break; case DEVPTS_SUPER_MAGIC: /* * devpts seems content with the label of the task. * Programs that change smack have to treat the * pty with respect.
*/
final = ckp; break; case PROC_SUPER_MAGIC: /* * Casey says procfs appears not to care. * The superblock default suffices.
*/ break; case TMPFS_MAGIC: /* * Device labels should come from the filesystem, * but watch out, because they're volitile, * getting recreated on every reboot.
*/
final = &smack_known_star; /* * If a smack value has been set we want to use it, * but since tmpfs isn't giving us the opportunity * to set mount options simulate setting the * superblock default.
*/
fallthrough; default: /* * This isn't an understood special case. * Get the value from the xattr.
*/
/* * UNIX domain sockets use lower level socket data.
*/ if (S_ISSOCK(inode->i_mode)) {
final = &smack_known_star; break;
} /* * No xattr support means, alas, no SMACK label. * Use the aforeapplied default. * It would be curious if the label of the task * does not match that assigned.
*/ if (!(inode->i_opflags & IOP_XATTR)) break; /* * Get the dentry for xattr.
*/
dp = dget(opt_dentry);
skp = smk_fetch(XATTR_NAME_SMACK, inode, dp); if (!IS_ERR_OR_NULL(skp))
final = skp;
/* * Transmuting directory
*/ if (S_ISDIR(inode->i_mode)) { /* * If this is a new directory and the label was * transmuted when the inode was initialized * set the transmute attribute on the directory * and mark the inode. * * If there is a transmute attribute on the * directory mark the inode.
*/
rc = __vfs_getxattr(dp, inode,
XATTR_NAME_SMACKTRANSMUTE, trattr,
TRANS_TRUE_SIZE); if (rc >= 0 && strncmp(trattr, TRANS_TRUE,
TRANS_TRUE_SIZE) != 0)
rc = -EINVAL; if (rc >= 0)
transflag = SMK_INODE_TRANSMUTE;
} /* * Don't let the exec or mmap label be "*" or "@".
*/
skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); if (IS_ERR(skp) || skp == &smack_known_star ||
skp == &smack_known_web)
skp = NULL;
isp->smk_task = skp;
/** * smack_getselfattr - Smack current process attribute * @attr: which attribute to fetch * @ctx: buffer to receive the result * @size: available size in, actual size out * @flags: unused * * Fill the passed user space @ctx with the details of the requested * attribute. * * Returns the number of attributes on success, an error code otherwise. * There will only ever be one attribute.
*/ staticint smack_getselfattr(unsignedint attr, struct lsm_ctx __user *ctx,
u32 *size, u32 flags)
{ int rc; struct smack_known *skp;
/** * smack_getprocattr - Smack process attribute access * @p: the object task * @name: the name of the attribute in /proc/.../attr * @value: where to put the result * * Places a copy of the task Smack into value * * Returns the length of the smack label or an error code
*/ staticint smack_getprocattr(struct task_struct *p, constchar *name, char **value)
{ struct smack_known *skp = smk_of_task_struct_obj(p); char *cp; int slen;
if (strcmp(name, "current") != 0) return -EINVAL;
cp = kstrdup(skp->smk_known, GFP_KERNEL); if (cp == NULL) return -ENOMEM;
slen = strlen(cp);
*value = cp; return slen;
}
/** * do_setattr - Smack process attribute setting * @attr: the ID of the attribute * @value: the value to set * @size: the size of the value * * Sets the Smack value of the task. Only setting self * is permitted and only with privilege * * Returns the length of the smack label or an error code
*/ staticint do_setattr(u64 attr, void *value, size_t size)
{ struct task_smack *tsp = smack_cred(current_cred()); struct cred *new; struct smack_known *skp; struct smack_known_list_elem *sklep; int rc;
if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) return -EPERM;
skp = smk_import_entry(value, size); if (IS_ERR(skp)) return PTR_ERR(skp);
/* * No process is ever allowed the web ("@") label * and the star ("*") label.
*/ if (skp == &smack_known_web || skp == &smack_known_star) return -EINVAL;
if (!smack_privileged(CAP_MAC_ADMIN)) {
rc = -EPERM;
list_for_each_entry(sklep, &tsp->smk_relabel, list) if (sklep->smk_label == skp) {
rc = 0; break;
} if (rc) return rc;
}
new = prepare_creds(); if (new == NULL) return -ENOMEM;
tsp = smack_cred(new);
tsp->smk_task = skp; /* * process can change its label only once
*/
smk_destroy_label_list(&tsp->smk_relabel);
commit_creds(new); return size;
}
/** * smack_setselfattr - Set a Smack process attribute * @attr: which attribute to set * @ctx: buffer containing the data * @size: size of @ctx * @flags: unused * * Fill the passed user space @ctx with the details of the requested * attribute. * * Returns 0 on success, an error code otherwise.
*/ staticint smack_setselfattr(unsignedint attr, struct lsm_ctx *ctx,
u32 size, u32 flags)
{ int rc;
/** * smack_setprocattr - Smack process attribute setting * @name: the name of the attribute in /proc/.../attr * @value: the value to set * @size: the size of the value * * Sets the Smack value of the task. Only setting self * is permitted and only with privilege * * Returns the length of the smack label or an error code
*/ staticint smack_setprocattr(constchar *name, void *value, size_t size)
{ int attr = lsm_name_to_attr(name);
/** * smack_unix_may_send - Smack access on UDS * @sock: one socket * @other: the other socket * * Return 0 if a subject with the smack of sock could access * an object with the smack of other, otherwise an error code
*/ staticint smack_unix_may_send(struct socket *sock, struct socket *other)
{ struct socket_smack *ssp = smack_sock(sock->sk); struct socket_smack *osp = smack_sock(other->sk); struct smk_audit_info ad; int rc;
/** * smack_socket_sendmsg - Smack check based on destination host * @sock: the socket * @msg: the message * @size: the size of the message * * Return 0 if the current subject can write to the destination host. * For IPv4 this is only a question if the destination is a single label host. * For IPv6 this is a check against the label of the port.
*/ staticint smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{ struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; #if IS_ENABLED(CONFIG_IPV6) struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name; #endif #ifdef SMACK_IPV6_SECMARK_LABELING struct socket_smack *ssp = smack_sock(sock->sk); struct smack_known *rsp; #endif int rc = 0;
/* * Perfectly reasonable for this to be NULL
*/ if (sip == NULL) return 0;
/** * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack * @sap: netlabel secattr * @ssp: socket security information * * Returns a pointer to a Smack label entry found on the label list.
*/ staticstruct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, struct socket_smack *ssp)
{ struct smack_known *skp; int found = 0; int acat; int kcat;
/* * Netlabel found it in the cache.
*/ if ((sap->flags & NETLBL_SECATTR_CACHE) != 0) return (struct smack_known *)sap->cache->data;
if ((sap->flags & NETLBL_SECATTR_SECID) != 0) /* * Looks like a fallback, which gives us a secid.
*/ return smack_from_secid(sap->attr.secid);
if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { /* * Looks like a CIPSO packet. * If there are flags but no level netlabel isn't * behaving the way we expect it to. * * Look it up in the label table * Without guidance regarding the smack value * for the packet fall back on the network * ambient value.
*/
rcu_read_lock();
list_for_each_entry_rcu(skp, &smack_known_list, list) { if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) continue; /* * Compare the catsets. Use the netlbl APIs.
*/ if ((sap->flags & NETLBL_SECATTR_MLS_CAT) == 0) { if ((skp->smk_netlabel.flags &
NETLBL_SECATTR_MLS_CAT) == 0)
found = 1; break;
} for (acat = -1, kcat = -1; acat == kcat; ) {
acat = netlbl_catmap_walk(sap->attr.mls.cat,
acat + 1);
kcat = netlbl_catmap_walk(
skp->smk_netlabel.attr.mls.cat,
kcat + 1); if (acat < 0 || kcat < 0) break;
} if (acat == kcat) {
found = 1; break;
}
}
rcu_read_unlock();
if (found) return skp;
if (ssp != NULL && ssp->smk_in == &smack_known_star) return &smack_known_web; return &smack_known_star;
} /* * Without guidance regarding the smack value * for the packet fall back on the network * ambient value.
*/ return smack_net_ambient;
}
proto = nexthdr; switch (proto) { case IPPROTO_TCP:
th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); if (th != NULL)
sip->sin6_port = th->source; break; case IPPROTO_UDP: case IPPROTO_UDPLITE:
uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); if (uh != NULL)
sip->sin6_port = uh->source; break;
} return proto;
} #endif/* CONFIG_IPV6 */
/** * smack_from_skb - Smack data from the secmark in an skb * @skb: packet * * Returns smack_known of the secmark or NULL if that won't work.
*/ #ifdef CONFIG_NETWORK_SECMARK staticstruct smack_known *smack_from_skb(struct sk_buff *skb)
{ if (skb == NULL || skb->secmark == 0) return NULL;
/** * smack_from_netlbl - Smack data from the IP options in an skb * @sk: socket data came in on * @family: address family * @skb: packet * * Find the Smack label in the IP options. If it hasn't been * added to the netlabel cache, add it here. * * Returns smack_known of the IP options or NULL if that won't work.
*/ staticstruct smack_known *smack_from_netlbl(conststruct sock *sk, u16 family, struct sk_buff *skb)
{ struct netlbl_lsm_secattr secattr; struct socket_smack *ssp = NULL; struct smack_known *skp = NULL;
netlbl_secattr_init(&secattr);
if (sk)
ssp = smack_sock(sk);
if (netlbl_skbuff_getattr(skb, family, &secattr) == 0) {
skp = smack_from_secattr(&secattr, ssp); if (secattr.flags & NETLBL_SECATTR_CACHEABLE)
netlbl_cache_add(skb, family, &skp->smk_netlabel);
}
netlbl_secattr_destroy(&secattr);
return skp;
}
/** * smack_socket_sock_rcv_skb - Smack packet delivery access check * @sk: socket * @skb: packet * * Returns 0 if the packet should be delivered, an error code otherwise
*/ staticint smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{ struct socket_smack *ssp = smack_sock(sk); struct smack_known *skp = NULL; int rc = 0; struct smk_audit_info ad;
u16 family = sk->sk_family; #ifdef CONFIG_AUDIT struct lsm_network_audit net; #endif #if IS_ENABLED(CONFIG_IPV6) struct sockaddr_in6 sadd; int proto;
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET; #endif/* CONFIG_IPV6 */
switch (family) { case PF_INET: /* * If there is a secmark use it rather than the CIPSO label. * If there is no secmark fall back to CIPSO. * The secmark is assumed to reflect policy better.
*/
skp = smack_from_skb(skb); if (skp == NULL) {
skp = smack_from_netlbl(sk, family, skb); if (skp == NULL)
skp = smack_net_ambient;
}
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = family;
ad.a.u.net->netif = skb->skb_iif;
ipv4_skb_to_auditdata(skb, &ad.a, NULL); #endif /* * Receiving a packet requires that the other end * be able to write here. Read access is not required. * This is the simplest possible security model * for networking.
*/
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in,
MAY_WRITE, rc); if (rc != 0)
netlbl_skbuff_err(skb, family, rc, 0); break; #if IS_ENABLED(CONFIG_IPV6) case PF_INET6:
proto = smk_skb_to_addr_ipv6(skb, &sadd); if (proto != IPPROTO_UDP && proto != IPPROTO_UDPLITE &&
proto != IPPROTO_TCP) break; #ifdef SMACK_IPV6_SECMARK_LABELING
skp = smack_from_skb(skb); if (skp == NULL) { if (smk_ipv6_localhost(&sadd)) break;
skp = smack_ipv6host_label(&sadd); if (skp == NULL)
skp = smack_net_ambient;
} #ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = family;
ad.a.u.net->netif = skb->skb_iif;
ipv6_skb_to_auditdata(skb, &ad.a, NULL); #endif/* CONFIG_AUDIT */
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in,
MAY_WRITE, rc); #endif/* SMACK_IPV6_SECMARK_LABELING */ #ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING); #endif/* SMACK_IPV6_PORT_LABELING */ if (rc != 0)
icmpv6_send(skb, ICMPV6_DEST_UNREACH,
ICMPV6_ADM_PROHIBITED, 0); break; #endif/* CONFIG_IPV6 */
}
return rc;
}
/** * smack_socket_getpeersec_stream - pull in packet label * @sock: the socket * @optval: user's destination * @optlen: size thereof * @len: max thereof * * returns zero on success, an error code otherwise
*/ staticint smack_socket_getpeersec_stream(struct socket *sock,
sockptr_t optval, sockptr_t optlen, unsignedint len)
{ struct socket_smack *ssp; char *rcp = "";
u32 slen = 1; int rc = 0;
if (copy_to_sockptr(optval, rcp, slen))
rc = -EFAULT;
out_len: if (copy_to_sockptr(optlen, &slen, sizeof(slen)))
rc = -EFAULT; return rc;
}
/** * smack_socket_getpeersec_dgram - pull in packet label * @sock: the peer socket * @skb: packet data * @secid: pointer to where to put the secid of the packet * * Sets the netlabel socket state on sk from parent
*/ staticint smack_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
{ struct socket_smack *ssp = NULL; struct smack_known *skp; struct sock *sk = NULL; int family = PF_UNSPEC;
u32 s = 0; /* 0 is the invalid secid */
if (skb != NULL) { if (skb->protocol == htons(ETH_P_IP))
family = PF_INET; #if IS_ENABLED(CONFIG_IPV6) elseif (skb->protocol == htons(ETH_P_IPV6))
family = PF_INET6; #endif/* CONFIG_IPV6 */
} if (family == PF_UNSPEC && sock != NULL)
family = sock->sk->sk_family;
switch (family) { case PF_UNIX:
ssp = smack_sock(sock->sk);
s = ssp->smk_out->smk_secid; break; case PF_INET:
skp = smack_from_skb(skb); if (skp) {
s = skp->smk_secid; break;
} /* * Translate what netlabel gave us.
*/ if (sock != NULL)
sk = sock->sk;
skp = smack_from_netlbl(sk, family, skb); if (skp != NULL)
s = skp->smk_secid; break; case PF_INET6: #ifdef SMACK_IPV6_SECMARK_LABELING
skp = smack_from_skb(skb); if (skp)
s = skp->smk_secid; #endif break;
}
*secid = s; if (s == 0) return -EINVAL; return 0;
}
/** * smack_inet_conn_request - Smack access check on connect * @sk: socket involved * @skb: packet * @req: unused * * Returns 0 if a task with the packet label could write to * the socket, otherwise an error code
*/ staticint smack_inet_conn_request(conststruct sock *sk, struct sk_buff *skb, struct request_sock *req)
{
u16 family = sk->sk_family; struct smack_known *skp; struct socket_smack *ssp = smack_sock(sk); struct sockaddr_in addr; struct iphdr *hdr; struct smack_known *hskp; int rc; struct smk_audit_info ad; #ifdef CONFIG_AUDIT struct lsm_network_audit net; #endif
#if IS_ENABLED(CONFIG_IPV6) if (family == PF_INET6) { /* * Handle mapped IPv4 packets arriving * via IPv6 sockets. Don't set up netlabel * processing on IPv6.
*/ if (skb->protocol == htons(ETH_P_IP))
family = PF_INET; else return 0;
} #endif/* CONFIG_IPV6 */
/* * If there is a secmark use it rather than the CIPSO label. * If there is no secmark fall back to CIPSO. * The secmark is assumed to reflect policy better.
*/
skp = smack_from_skb(skb); if (skp == NULL) {
skp = smack_from_netlbl(sk, family, skb); if (skp == NULL)
skp = &smack_known_huh;
}
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = family;
ad.a.u.net->netif = skb->skb_iif;
ipv4_skb_to_auditdata(skb, &ad.a, NULL); #endif /* * Receiving a packet requires that the other end be able to write * here. Read access is not required.
*/
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
rc = smk_bu_note("IPv4 connect", skp, ssp->smk_in, MAY_WRITE, rc); if (rc != 0) return rc;
/* * Save the peer's label in the request_sock so we can later setup * smk_packet in the child socket so that SO_PEERCRED can report it.
*/
req->peer_secid = skp->smk_secid;
/* * We need to decide if we want to label the incoming connection here * if we do we only need to label the request_sock and the stack will * propagate the wire-label to the sock when it is created.
*/
hdr = ip_hdr(skb);
addr.sin_addr.s_addr = hdr->saddr;
rcu_read_lock();
hskp = smack_ipv4host_label(&addr);
rcu_read_unlock();
if (hskp == NULL)
rc = netlbl_req_setattr(req, &ssp->smk_out->smk_netlabel); else
netlbl_req_delattr(req);
return rc;
}
/** * smack_inet_csk_clone - Copy the connection information to the new socket * @sk: the new socket * @req: the connection's request_sock * * Transfer the connection's peer label to the newly created socket.
*/ staticvoid smack_inet_csk_clone(struct sock *sk, conststruct request_sock *req)
{ struct socket_smack *ssp = smack_sock(sk); struct smack_known *skp;
/* * Key management security hooks * * Casey has not tested key support very heavily. * The permission check is most likely too restrictive. * If you care about keys please have a look.
*/ #ifdef CONFIG_KEYS
/** * smack_key_alloc - Set the key security blob * @key: object * @cred: the credentials to use * @flags: unused * * No allocation required * * Returns 0
*/ staticint smack_key_alloc(struct key *key, conststruct cred *cred, unsignedlong flags)
{ struct smack_known **blob = smack_key(key); struct smack_known *skp = smk_of_task(smack_cred(cred));
*blob = skp; return 0;
}
/** * smack_key_permission - Smack access on a key * @key_ref: gets to the object * @cred: the credentials to use * @need_perm: requested key permission * * Return 0 if the task has read and write to the object, * an error code otherwise
*/ staticint smack_key_permission(key_ref_t key_ref, conststruct cred *cred, enum key_need_perm need_perm)
{ struct smack_known **blob; struct smack_known *skp; struct key *keyp; struct smk_audit_info ad; struct smack_known *tkp = smk_of_task(smack_cred(cred)); int request = 0; int rc;
/* * Validate requested permissions
*/ switch (need_perm) { case KEY_NEED_READ: case KEY_NEED_SEARCH: case KEY_NEED_VIEW:
request |= MAY_READ; break; case KEY_NEED_WRITE: case KEY_NEED_LINK: case KEY_NEED_SETATTR:
request |= MAY_WRITE; break; case KEY_NEED_UNSPECIFIED: case KEY_NEED_UNLINK: case KEY_SYSADMIN_OVERRIDE: case KEY_AUTHTOKEN_OVERRIDE: case KEY_DEFER_PERM_CHECK: return 0; default: return -EINVAL;
}
keyp = key_ref_to_ptr(key_ref); if (keyp == NULL) return -EINVAL; /* * If the key hasn't been initialized give it access so that * it may do so.
*/
blob = smack_key(keyp);
skp = *blob; if (skp == NULL) return 0; /* * This should not occur
*/ if (tkp == NULL) return -EACCES;
/* * smack_key_getsecurity - Smack label tagging the key * @key points to the key to be queried * @_buffer points to a pointer that should be set to point to the * resulting string (if no label or an error occurs). * Return the length of the string (including terminating NUL) or -ve if * an error. * May also return 0 (and a NULL buffer pointer) if there is no label.
*/ staticint smack_key_getsecurity(struct key *key, char **_buffer)
{ struct smack_known **blob = smack_key(key); struct smack_known *skp = *blob;
size_t length; char *copy;
#ifdef CONFIG_KEY_NOTIFICATIONS /** * smack_watch_key - Smack access to watch a key for notifications. * @key: The key to be watched * * Return 0 if the @watch->cred has permission to read from the key object and * an error otherwise.
*/ staticint smack_watch_key(struct key *key)
{ struct smk_audit_info ad; struct smack_known *tkp = smk_of_current(); struct smack_known **blob = smack_key(key); int rc;
/* * This should not occur
*/ if (tkp == NULL) return -EACCES;
if (smack_privileged_cred(CAP_MAC_OVERRIDE, current_cred())) return 0;
#ifdef CONFIG_WATCH_QUEUE /** * smack_post_notification - Smack access to post a notification to a queue * @w_cred: The credentials of the watcher. * @cred: The credentials of the event source (may be NULL). * @n: The notification message to be posted.
*/ staticint smack_post_notification(conststruct cred *w_cred, conststruct cred *cred, struct watch_notification *n)
{ struct smk_audit_info ad; struct smack_known *subj, *obj; int rc;
/* Always let maintenance notifications through. */ if (n->type == WATCH_TYPE_META) return 0;
if (!cred) return 0;
subj = smk_of_task(smack_cred(cred));
obj = smk_of_task(smack_cred(w_cred));
/* * Smack Audit hooks * * Audit requires a unique representation of each Smack specific * rule. This unique representation is used to distinguish the * object to be audited from remaining kernel objects and also * works as a glue between the audit hooks. * * Since repository entries are added but never deleted, we'll use * the smack_known label address related to the given audit rule as * the needed unique representation. This also better fits the smack * model where nearly everything is a label.
*/ #ifdef CONFIG_AUDIT
/** * smack_audit_rule_init - Initialize a smack audit rule * @field: audit rule fields given from user-space (audit.h) * @op: required testing operator (=, !=, >, <, ...) * @rulestr: smack label to be audited * @vrule: pointer to save our own audit rule representation * @gfp: type of the memory for the allocation * * Prepare to audit cases where (@field @op @rulestr) is true. * The label to be audited is created if necessary.
*/ staticint smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule,
gfp_t gfp)
{ struct smack_known *skp; char **rule = (char **)vrule;
*rule = NULL;
if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) return -EINVAL;
if (op != Audit_equal && op != Audit_not_equal) return -EINVAL;
skp = smk_import_entry(rulestr, 0); if (IS_ERR(skp)) return PTR_ERR(skp);
*rule = skp->smk_known;
return 0;
}
/** * smack_audit_rule_known - Distinguish Smack audit rules * @krule: rule of interest, in Audit kernel representation format * * This is used to filter Smack rules from remaining Audit ones. * If it's proved that this rule belongs to us, the * audit_rule_match hook will be called to do the final judgement.
*/ staticint smack_audit_rule_known(struct audit_krule *krule)
{ struct audit_field *f; int i;
for (i = 0; i < krule->field_count; i++) {
f = &krule->fields[i];
/** * smack_audit_rule_match - Audit given object ? * @prop: security id for identifying the object to test * @field: audit rule flags given from user-space * @op: required testing operator * @vrule: smack internal rule presentation * * The core Audit hook. It's used to take the decision of * whether to audit or not to audit a given object.
*/ staticint smack_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule)
{ struct smack_known *skp = prop->smack.skp; char *rule = vrule;
if (unlikely(!rule)) {
WARN_ONCE(1, "Smack: missing rule\n"); return -ENOENT;
}
if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) return 0;
/* * No need to do string comparisons. If a match occurs, * both pointers will point to the same smack_known * label.
*/ if (op == Audit_equal) return (rule == skp->smk_known); if (op == Audit_not_equal) return (rule != skp->smk_known);
return 0;
}
/* * There is no need for a smack_audit_rule_free hook. * No memory was allocated.
*/
#endif/* CONFIG_AUDIT */
/** * smack_ismaclabel - check if xattr @name references a smack MAC label * @name: Full xattr name to check.
*/ staticint smack_ismaclabel(constchar *name)
{ return (strcmp(name, XATTR_SMACK_SUFFIX) == 0);
}
/** * smack_to_secctx - fill a lsm_context * @skp: Smack label * @cp: destination * * Fill the passed @cp and return the length of the string
*/ staticint smack_to_secctx(struct smack_known *skp, struct lsm_context *cp)
{ int len = strlen(skp->smk_known);
/* * There used to be a smack_release_secctx hook * that did nothing back when hooks were in a vector. * Now that there's a list such a hook adds cost.
*/
if (new_creds == NULL) {
new_creds = prepare_creds(); if (new_creds == NULL) return -ENOMEM;
}
tsp = smack_cred(new_creds);
/* * Get label from overlay inode and set it in create_sid
*/
isp = smack_inode(d_inode(dentry));
skp = isp->smk_inode;
tsp->smk_task = skp;
*new = new_creds; return 0;
}
staticint smack_inode_copy_up_xattr(struct dentry *src, constchar *name)
{ /* * Return -ECANCELED if this is the smack access Smack attribute.
*/ if (!strcmp(name, XATTR_NAME_SMACK)) return -ECANCELED;
/* * Use the process credential unless all of * the transmuting criteria are met
*/
ntsp->smk_task = otsp->smk_task;
/* * the attribute of the containing directory
*/
isp = smack_inode(d_inode(dentry->d_parent));
if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
rcu_read_lock();
may = smk_access_entry(otsp->smk_task->smk_known,
isp->smk_inode->smk_known,
&otsp->smk_task->smk_rules);
rcu_read_unlock();
/* * If the directory is transmuting and the rule * providing access is transmuting use the containing * directory label instead of the process label.
*/ if (may > 0 && (may & MAY_TRANSMUTE)) {
ntsp->smk_task = isp->smk_inode;
ntsp->smk_transmuted = ntsp->smk_task;
}
} return 0;
}
#ifdef CONFIG_IO_URING /** * smack_uring_override_creds - Is io_uring cred override allowed? * @new: the target creds * * Check to see if the current task is allowed to override it's credentials * to service an io_uring operation.
*/ staticint smack_uring_override_creds(conststruct cred *new)
{ struct task_smack *tsp = smack_cred(current_cred()); struct task_smack *nsp = smack_cred(new);
/* * Allow the degenerate case where the new Smack value is * the same as the current Smack value.
*/ if (tsp->smk_task == nsp->smk_task) return 0;
if (smack_privileged_cred(CAP_MAC_OVERRIDE, current_cred())) return 0;
return -EPERM;
}
/** * smack_uring_sqpoll - check if a io_uring polling thread can be created * * Check to see if the current task is allowed to create a new io_uring * kernel polling thread.
*/ staticint smack_uring_sqpoll(void)
{ if (smack_privileged_cred(CAP_MAC_ADMIN, current_cred())) return 0;
return -EPERM;
}
/** * smack_uring_cmd - check on file operations for io_uring * @ioucmd: the command in question * * Make a best guess about whether a io_uring "command" should * be allowed. Use the same logic used for determining if the * file could be opened for read in the absence of better criteria.
*/ staticint smack_uring_cmd(struct io_uring_cmd *ioucmd)
{ struct file *file = ioucmd->file; struct smk_audit_info ad; struct task_smack *tsp; struct inode *inode; int rc;
/* initialize the smack_known_list */
init_smack_known_list();
return 0;
}
/* * Smack requires early initialization in order to label * all processes and objects when they are created.
*/
DEFINE_LSM(smack) = {
.name = "smack",
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
.blobs = &smack_blob_sizes,
.init = smack_init,
};
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.75 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.