diff options
Diffstat (limited to 'security')
27 files changed, 816 insertions, 189 deletions
diff --git a/security/capability.c b/security/capability.c index ad0d4de69944..e76373de3129 100644 --- a/security/capability.c +++ b/security/capability.c @@ -879,7 +879,7 @@ static void cap_key_free(struct key *key) } static int cap_key_permission(key_ref_t key_ref, const struct cred *cred, - key_perm_t perm) + unsigned perm) { return 0; } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 9134dbf70d3e..d9d69e6930ed 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -182,7 +182,7 @@ static inline bool is_devcg_online(const struct dev_cgroup *devcg) static int devcgroup_online(struct cgroup_subsys_state *css) { struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); - struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css)); + struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css->parent); int ret = 0; mutex_lock(&devcgroup_mutex); @@ -455,7 +455,7 @@ static bool verify_new_ex(struct dev_cgroup *dev_cgroup, static int parent_has_perm(struct dev_cgroup *childcg, struct dev_exception_item *ex) { - struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); + struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent); if (!parent) return 1; @@ -476,7 +476,7 @@ static int parent_has_perm(struct dev_cgroup *childcg, static bool parent_allows_removal(struct dev_cgroup *childcg, struct dev_exception_item *ex) { - struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); + struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent); if (!parent) return true; @@ -587,13 +587,6 @@ static int propagate_exception(struct dev_cgroup *devcg_root, return rc; } -static inline bool has_children(struct dev_cgroup *devcgroup) -{ - struct cgroup *cgrp = devcgroup->css.cgroup; - - return !list_empty(&cgrp->children); -} - /* * Modify the exception list using allow/deny rules. * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD @@ -614,7 +607,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, char temp[12]; /* 11 + 1 characters needed for a u32 */ int count, rc = 0; struct dev_exception_item ex; - struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css)); + struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent); if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -626,7 +619,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, case 'a': switch (filetype) { case DEVCG_ALLOW: - if (has_children(devcgroup)) + if (css_has_online_children(&devcgroup->css)) return -EINVAL; if (!may_allow_all(parent)) @@ -642,7 +635,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, return rc; break; case DEVCG_DENY: - if (has_children(devcgroup)) + if (css_has_online_children(&devcgroup->css)) return -EINVAL; dev_exception_clean(devcgroup); @@ -767,27 +760,27 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, return rc; } -static int devcgroup_access_write(struct cgroup_subsys_state *css, - struct cftype *cft, char *buffer) +static ssize_t devcgroup_access_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { int retval; mutex_lock(&devcgroup_mutex); - retval = devcgroup_update_access(css_to_devcgroup(css), - cft->private, buffer); + retval = devcgroup_update_access(css_to_devcgroup(of_css(of)), + of_cft(of)->private, strstrip(buf)); mutex_unlock(&devcgroup_mutex); - return retval; + return retval ?: nbytes; } static struct cftype dev_cgroup_files[] = { { .name = "allow", - .write_string = devcgroup_access_write, + .write = devcgroup_access_write, .private = DEVCG_ALLOW, }, { .name = "deny", - .write_string = devcgroup_access_write, + .write = devcgroup_access_write, .private = DEVCG_DENY, }, { diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index d35b4915b00d..d606f3d12d6b 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -12,15 +12,41 @@ config EVM If you are unsure how to answer this question, answer N. -config EVM_HMAC_VERSION - int "EVM HMAC version" +if EVM + +menu "EVM options" + +config EVM_ATTR_FSUUID + bool "FSUUID (version 2)" + default y depends on EVM - default 2 help - This options adds EVM HMAC version support. - 1 - original version - 2 - add per filesystem unique identifier (UUID) (default) + Include filesystem UUID for HMAC calculation. + + Default value is 'selected', which is former version 2. + if 'not selected', it is former version 1 - WARNING: changing the HMAC calculation method or adding + WARNING: changing the HMAC calculation method or adding additional info to the calculation, requires existing EVM - labeled file systems to be relabeled. + labeled file systems to be relabeled. + +config EVM_EXTRA_SMACK_XATTRS + bool "Additional SMACK xattrs" + depends on EVM && SECURITY_SMACK + default n + help + Include additional SMACK xattrs for HMAC calculation. + + In addition to the original security xattrs (eg. security.selinux, + security.SMACK64, security.capability, and security.ima) included + in the HMAC calculation, enabling this option includes newly defined + Smack xattrs: security.SMACK64EXEC, security.SMACK64TRANSMUTE and + security.SMACK64MMAP. + + WARNING: changing the HMAC calculation method or adding + additional info to the calculation, requires existing EVM + labeled file systems to be relabeled. + +endmenu + +endif diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 37c88ddb3cfe..88bfe77efa1c 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -24,7 +24,10 @@ extern int evm_initialized; extern char *evm_hmac; extern char *evm_hash; -extern int evm_hmac_version; + +#define EVM_ATTR_FSUUID 0x0001 + +extern int evm_hmac_attrs; extern struct crypto_shash *hmac_tfm; extern struct crypto_shash *hash_tfm; diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 6b540f1822e0..5e9687f02e1b 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -112,7 +112,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid); hmac_misc.mode = inode->i_mode; crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); - if (evm_hmac_version > 1) + if (evm_hmac_attrs & EVM_ATTR_FSUUID) crypto_shash_update(desc, inode->i_sb->s_uuid, sizeof(inode->i_sb->s_uuid)); crypto_shash_final(desc, digest); diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 6e0bd933b6a9..3bcb80df4d01 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -32,7 +32,7 @@ static char *integrity_status_msg[] = { }; char *evm_hmac = "hmac(sha1)"; char *evm_hash = "sha1"; -int evm_hmac_version = CONFIG_EVM_HMAC_VERSION; +int evm_hmac_attrs; char *evm_config_xattrnames[] = { #ifdef CONFIG_SECURITY_SELINUX @@ -40,6 +40,11 @@ char *evm_config_xattrnames[] = { #endif #ifdef CONFIG_SECURITY_SMACK XATTR_NAME_SMACK, +#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS + XATTR_NAME_SMACKEXEC, + XATTR_NAME_SMACKTRANSMUTE, + XATTR_NAME_SMACKMMAP, +#endif #endif #ifdef CONFIG_IMA_APPRAISE XATTR_NAME_IMA, @@ -57,6 +62,14 @@ static int __init evm_set_fixmode(char *str) } __setup("evm=", evm_set_fixmode); +static void __init evm_init_config(void) +{ +#ifdef CONFIG_EVM_ATTR_FSUUID + evm_hmac_attrs |= EVM_ATTR_FSUUID; +#endif + pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs); +} + static int evm_find_protected_xattrs(struct dentry *dentry) { struct inode *inode = dentry->d_inode; @@ -287,12 +300,20 @@ out: * @xattr_value: pointer to the new extended attribute value * @xattr_value_len: pointer to the new extended attribute value length * - * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that - * the current value is valid. + * Before allowing the 'security.evm' protected xattr to be updated, + * verify the existing value is valid. As only the kernel should have + * access to the EVM encrypted key needed to calculate the HMAC, prevent + * userspace from writing HMAC value. Writing 'security.evm' requires + * requires CAP_SYS_ADMIN privileges. */ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { + const struct evm_ima_xattr_data *xattr_data = xattr_value; + + if ((strcmp(xattr_name, XATTR_NAME_EVM) == 0) + && (xattr_data->type == EVM_XATTR_HMAC)) + return -EPERM; return evm_protect_xattr(dentry, xattr_name, xattr_value, xattr_value_len); } @@ -432,6 +453,8 @@ static int __init init_evm(void) { int error; + evm_init_config(); + error = evm_init_secfs(); if (error < 0) { pr_info("Error registering secfs\n"); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index ba9e4d792dd5..d9cd5ce14d2b 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -199,6 +199,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, struct evm_ima_xattr_data **xattr_value, int *xattr_len) { + const char *audit_cause = "failed"; struct inode *inode = file_inode(file); const char *filename = file->f_dentry->d_name.name; int result = 0; @@ -213,6 +214,12 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, if (!(iint->flags & IMA_COLLECTED)) { u64 i_version = file_inode(file)->i_version; + if (file->f_flags & O_DIRECT) { + audit_cause = "failed(directio)"; + result = -EACCES; + goto out; + } + /* use default hash algorithm */ hash.hdr.algo = ima_hash_algo; @@ -233,9 +240,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, result = -ENOMEM; } } +out: if (result) integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, - filename, "collect_data", "failed", + filename, "collect_data", audit_cause, result, 0); return result; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 291bf0f3a46d..59ac90275070 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -214,7 +214,7 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, hash_start = 1; case IMA_XATTR_DIGEST: if (iint->flags & IMA_DIGSIG_REQUIRED) { - cause = "IMA signature required"; + cause = "IMA-signature-required"; status = INTEGRITY_FAIL; break; } @@ -341,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name, return 0; } -static void ima_reset_appraise_flags(struct inode *inode) +static void ima_reset_appraise_flags(struct inode *inode, int digsig) { struct integrity_iint_cache *iint; @@ -353,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode) return; iint->flags &= ~IMA_DONE_MASK; + if (digsig) + iint->flags |= IMA_DIGSIG; return; } int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { + const struct evm_ima_xattr_data *xvalue = xattr_value; int result; result = ima_protect_xattr(dentry, xattr_name, xattr_value, xattr_value_len); if (result == 1) { - ima_reset_appraise_flags(dentry->d_inode); + ima_reset_appraise_flags(dentry->d_inode, + (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0); result = 0; } return result; @@ -376,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) result = ima_protect_xattr(dentry, xattr_name, NULL, 0); if (result == 1) { - ima_reset_appraise_flags(dentry->d_inode); + ima_reset_appraise_flags(dentry->d_inode, 0); result = 0; } return result; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 1bde8e627766..0bd732843fe7 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -16,6 +16,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/ratelimit.h> #include <linux/file.h> #include <linux/crypto.h> #include <linux/scatterlist.h> @@ -25,7 +27,75 @@ #include <crypto/hash_info.h> #include "ima.h" +struct ahash_completion { + struct completion completion; + int err; +}; + +/* minimum file size for ahash use */ +static unsigned long ima_ahash_minsize; +module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644); +MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use"); + +/* default is 0 - 1 page. */ +static int ima_maxorder; +static unsigned int ima_bufsize = PAGE_SIZE; + +static int param_set_bufsize(const char *val, const struct kernel_param *kp) +{ + unsigned long long size; + int order; + + size = memparse(val, NULL); + order = get_order(size); + if (order >= MAX_ORDER) + return -EINVAL; + ima_maxorder = order; + ima_bufsize = PAGE_SIZE << order; + return 0; +} + +static struct kernel_param_ops param_ops_bufsize = { + .set = param_set_bufsize, + .get = param_get_uint, +}; +#define param_check_bufsize(name, p) __param_check(name, p, unsigned int) + +module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644); +MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); + static struct crypto_shash *ima_shash_tfm; +static struct crypto_ahash *ima_ahash_tfm; + +/** + * ima_kernel_read - read file content + * + * This is a function for reading file content instead of kernel_read(). + * It does not perform locking checks to ensure it cannot be blocked. + * It does not perform security checks because it is irrelevant for IMA. + * + */ +static int ima_kernel_read(struct file *file, loff_t offset, + char *addr, unsigned long count) +{ + mm_segment_t old_fs; + char __user *buf = addr; + ssize_t ret; + + if (!(file->f_mode & FMODE_READ)) + return -EBADF; + if (!file->f_op->read && !file->f_op->aio_read) + return -EINVAL; + + old_fs = get_fs(); + set_fs(get_ds()); + if (file->f_op->read) + ret = file->f_op->read(file, buf, count, &offset); + else + ret = do_sync_read(file, buf, count, &offset); + set_fs(old_fs); + return ret; +} int ima_init_crypto(void) { @@ -63,9 +133,246 @@ static void ima_free_tfm(struct crypto_shash *tfm) crypto_free_shash(tfm); } -/* - * Calculate the MD5/SHA1 file digest +/** + * ima_alloc_pages() - Allocate contiguous pages. + * @max_size: Maximum amount of memory to allocate. + * @allocated_size: Returned size of actual allocation. + * @last_warn: Should the min_size allocation warn or not. + * + * Tries to do opportunistic allocation for memory first trying to allocate + * max_size amount of memory and then splitting that until zero order is + * reached. Allocation is tried without generating allocation warnings unless + * last_warn is set. Last_warn set affects only last allocation of zero order. + * + * By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL) + * + * Return pointer to allocated memory, or NULL on failure. + */ +static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size, + int last_warn) +{ + void *ptr; + int order = ima_maxorder; + gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY; + + if (order) + order = min(get_order(max_size), order); + + for (; order; order--) { + ptr = (void *)__get_free_pages(gfp_mask, order); + if (ptr) { + *allocated_size = PAGE_SIZE << order; + return ptr; + } + } + + /* order is zero - one page */ + + gfp_mask = GFP_KERNEL; + + if (!last_warn) + gfp_mask |= __GFP_NOWARN; + + ptr = (void *)__get_free_pages(gfp_mask, 0); + if (ptr) { + *allocated_size = PAGE_SIZE; + return ptr; + } + + *allocated_size = 0; + return NULL; +} + +/** + * ima_free_pages() - Free pages allocated by ima_alloc_pages(). + * @ptr: Pointer to allocated pages. + * @size: Size of allocated buffer. */ +static void ima_free_pages(void *ptr, size_t size) +{ + if (!ptr) + return; + free_pages((unsigned long)ptr, get_order(size)); +} + +static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo) +{ + struct crypto_ahash *tfm = ima_ahash_tfm; + int rc; + + if ((algo != ima_hash_algo && algo < HASH_ALGO__LAST) || !tfm) { + tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0); + if (!IS_ERR(tfm)) { + if (algo == ima_hash_algo) + ima_ahash_tfm = tfm; + } else { + rc = PTR_ERR(tfm); + pr_err("Can not allocate %s (reason: %d)\n", + hash_algo_name[algo], rc); + } + } + return tfm; +} + +static void ima_free_atfm(struct crypto_ahash *tfm) +{ + if (tfm != ima_ahash_tfm) + crypto_free_ahash(tfm); +} + +static void ahash_complete(struct crypto_async_request *req, int err) +{ + struct ahash_completion *res = req->data; + + if (err == -EINPROGRESS) + return; + res->err = err; + complete(&res->completion); +} + +static int ahash_wait(int err, struct ahash_completion *res) +{ + switch (err) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + wait_for_completion(&res->completion); + reinit_completion(&res->completion); + err = res->err; + /* fall through */ + default: + pr_crit_ratelimited("ahash calculation failed: err: %d\n", err); + } + + return err; +} + +static int ima_calc_file_hash_atfm(struct file *file, + struct ima_digest_data *hash, + struct crypto_ahash *tfm) +{ + loff_t i_size, offset; + char *rbuf[2] = { NULL, }; + int rc, read = 0, rbuf_len, active = 0, ahash_rc = 0; + struct ahash_request *req; + struct scatterlist sg[1]; + struct ahash_completion res; + size_t rbuf_size[2]; + + hash->length = crypto_ahash_digestsize(tfm); + + req = ahash_request_alloc(tfm, GFP_KERNEL); + if (!req) + return -ENOMEM; + + init_completion(&res.completion); + ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + ahash_complete, &res); + + rc = ahash_wait(crypto_ahash_init(req), &res); + if (rc) + goto out1; + + i_size = i_size_read(file_inode(file)); + + if (i_size == 0) + goto out2; + + /* + * Try to allocate maximum size of memory. + * Fail if even a single page cannot be allocated. + */ + rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1); + if (!rbuf[0]) { + rc = -ENOMEM; + goto out1; + } + + /* Only allocate one buffer if that is enough. */ + if (i_size > rbuf_size[0]) { + /* + * Try to allocate secondary buffer. If that fails fallback to + * using single buffering. Use previous memory allocation size + * as baseline for possible allocation size. + */ + rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0], + &rbuf_size[1], 0); + } + + if (!(file->f_mode & FMODE_READ)) { + file->f_mode |= FMODE_READ; + read = 1; + } + + for (offset = 0; offset < i_size; offset += rbuf_len) { + if (!rbuf[1] && offset) { + /* Not using two buffers, and it is not the first + * read/request, wait for the completion of the + * previous ahash_update() request. + */ + rc = ahash_wait(ahash_rc, &res); + if (rc) + goto out3; + } + /* read buffer */ + rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]); + rc = ima_kernel_read(file, offset, rbuf[active], rbuf_len); + if (rc != rbuf_len) + goto out3; + + if (rbuf[1] && offset) { + /* Using two buffers, and it is not the first + * read/request, wait for the completion of the + * previous ahash_update() request. + */ + rc = ahash_wait(ahash_rc, &res); + if (rc) + goto out3; + } + + sg_init_one(&sg[0], rbuf[active], rbuf_len); + ahash_request_set_crypt(req, sg, NULL, rbuf_len); + + ahash_rc = crypto_ahash_update(req); + + if (rbuf[1]) + active = !active; /* swap buffers, if we use two */ + } + /* wait for the last update request to complete */ + rc = ahash_wait(ahash_rc, &res); +out3: + if (read) + file->f_mode &= ~FMODE_READ; + ima_free_pages(rbuf[0], rbuf_size[0]); + ima_free_pages(rbuf[1], rbuf_size[1]); +out2: + if (!rc) { + ahash_request_set_crypt(req, NULL, hash->digest, 0); + rc = ahash_wait(crypto_ahash_final(req), &res); + } +out1: + ahash_request_free(req); + return rc; +} + +static int ima_calc_file_ahash(struct file *file, struct ima_digest_data *hash) +{ + struct crypto_ahash *tfm; + int rc; + + tfm = ima_alloc_atfm(hash->algo); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = ima_calc_file_hash_atfm(file, hash, tfm); + + ima_free_atfm(tfm); + + return rc; +} + static int ima_calc_file_hash_tfm(struct file *file, struct ima_digest_data *hash, struct crypto_shash *tfm) @@ -104,7 +411,7 @@ static int ima_calc_file_hash_tfm(struct file *file, while (offset < i_size) { int rbuf_len; - rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE); + rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE); if (rbuf_len < 0) { rc = rbuf_len; break; @@ -126,7 +433,7 @@ out: return rc; } -int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) +static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash) { struct crypto_shash *tfm; int rc; @@ -143,6 +450,35 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) } /* + * ima_calc_file_hash - calculate file hash + * + * Asynchronous hash (ahash) allows using HW acceleration for calculating + * a hash. ahash performance varies for different data sizes on different + * crypto accelerators. shash performance might be better for smaller files. + * The 'ima.ahash_minsize' module parameter allows specifying the best + * minimum file size for using ahash on the system. + * + * If the ima.ahash_minsize parameter is not specified, this function uses + * shash for the hash calculation. If ahash fails, it falls back to using + * shash. + */ +int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) +{ + loff_t i_size; + int rc; + + i_size = i_size_read(file_inode(file)); + + if (ima_ahash_minsize && i_size >= ima_ahash_minsize) { + rc = ima_calc_file_ahash(file, hash); + if (!rc) + return 0; + } + + return ima_calc_file_shash(file, hash); +} + +/* * Calculate the hash of template data */ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 52ac6cf41f88..f474c608fa11 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -81,7 +81,6 @@ static void ima_rdwr_violation_check(struct file *file) { struct inode *inode = file_inode(file); fmode_t mode = file->f_mode; - int must_measure; bool send_tomtou = false, send_writers = false; char *pathbuf = NULL; const char *pathname; @@ -89,23 +88,20 @@ static void ima_rdwr_violation_check(struct file *file) if (!S_ISREG(inode->i_mode) || !ima_initialized) return; - mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */ - if (mode & FMODE_WRITE) { - if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) - send_tomtou = true; - goto out; + if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { + struct integrity_iint_cache *iint; + iint = integrity_iint_find(inode); + /* IMA_MEASURE is set from reader side */ + if (iint && (iint->flags & IMA_MEASURE)) + send_tomtou = true; + } + } else { + if ((atomic_read(&inode->i_writecount) > 0) && + ima_must_measure(inode, MAY_READ, FILE_CHECK)) + send_writers = true; } - must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK); - if (!must_measure) - goto out; - - if (atomic_read(&inode->i_writecount) > 0) - send_writers = true; -out: - mutex_unlock(&inode->i_mutex); - if (!send_tomtou && !send_writers) return; @@ -163,7 +159,7 @@ static int process_measurement(struct file *file, const char *filename, { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint; - struct ima_template_desc *template_desc = ima_template_desc_current(); + struct ima_template_desc *template_desc; char *pathbuf = NULL; const char *pathname = NULL; int rc = -ENOMEM, action, must_appraise, _func; @@ -207,6 +203,7 @@ static int process_measurement(struct file *file, const char *filename, goto out_digsig; } + template_desc = ima_template_desc_current(); if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { if (action & IMA_APPRAISE_SUBMASK) xattr_ptr = &xattr_value; @@ -214,8 +211,11 @@ static int process_measurement(struct file *file, const char *filename, xattr_ptr = &xattr_value; rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); - if (rc != 0) + if (rc != 0) { + if (file->f_flags & O_DIRECT) + rc = (iint->flags & IMA_PERMIT_DIRECTIO) ? 0 : -EACCES; goto out_digsig; + } pathname = filename ?: ima_d_path(&file->f_path, &pathbuf); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 93873a450ff7..cea84d8bd7be 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -332,7 +332,7 @@ void __init ima_init_policy(void) void ima_update_policy(void) { static const char op[] = "policy_update"; - const char *cause = "already exists"; + const char *cause = "already-exists"; int result = 1; int audit_info = 0; @@ -353,7 +353,7 @@ enum { Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner, - Opt_appraise_type, Opt_fsuuid + Opt_appraise_type, Opt_fsuuid, Opt_permit_directio }; static match_table_t policy_tokens = { @@ -375,6 +375,7 @@ static match_table_t policy_tokens = { {Opt_uid, "uid=%s"}, {Opt_fowner, "fowner=%s"}, {Opt_appraise_type, "appraise_type=%s"}, + {Opt_permit_directio, "permit_directio"}, {Opt_err, NULL} }; @@ -622,6 +623,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) else result = -EINVAL; break; + case Opt_permit_directio: + entry->flags |= IMA_PERMIT_DIRECTIO; + break; case Opt_err: ima_log_string(ab, "UNKNOWN", p); result = -EINVAL; @@ -655,7 +659,7 @@ ssize_t ima_parse_add_rule(char *rule) /* Prevent installed policy from changing */ if (ima_rules != &ima_default_rules) { integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, - NULL, op, "already exists", + NULL, op, "already-exists", -EACCES, audit_info); return -EACCES; } @@ -681,7 +685,7 @@ ssize_t ima_parse_add_rule(char *rule) if (result) { kfree(entry); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, - NULL, op, "invalid policy", result, + NULL, op, "invalid-policy", result, audit_info); return result; } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 2fb5e53e927f..33c0a70f6b15 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -30,6 +30,7 @@ #define IMA_ACTION_FLAGS 0xff000000 #define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG_REQUIRED 0x02000000 +#define IMA_PERMIT_DIRECTIO 0x04000000 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_APPRAISE_SUBMASK) diff --git a/security/keys/internal.h b/security/keys/internal.h index 80b2aac4f50c..5f20da01fd8d 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -176,20 +176,11 @@ extern int key_task_permission(const key_ref_t key_ref, /* * Check to see whether permission is granted to use a key in the desired way. */ -static inline int key_permission(const key_ref_t key_ref, key_perm_t perm) +static inline int key_permission(const key_ref_t key_ref, unsigned perm) { return key_task_permission(key_ref, current_cred(), perm); } -/* required permissions */ -#define KEY_VIEW 0x01 /* require permission to view attributes */ -#define KEY_READ 0x02 /* require permission to read content */ -#define KEY_WRITE 0x04 /* require permission to update / modify */ -#define KEY_SEARCH 0x08 /* require permission to search (keyring) or find (key) */ -#define KEY_LINK 0x10 /* require permission to link */ -#define KEY_SETATTR 0x20 /* require permission to change attributes */ -#define KEY_ALL 0x3f /* all the above permissions */ - /* * Authorisation record for request_key(). */ diff --git a/security/keys/key.c b/security/keys/key.c index 6e21c11e48bc..2048a110e7f1 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -714,7 +714,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref, int ret; /* need write permission on the key to update it */ - ret = key_permission(key_ref, KEY_WRITE); + ret = key_permission(key_ref, KEY_NEED_WRITE); if (ret < 0) goto error; @@ -838,7 +838,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* if we're going to allocate a new key, we're going to have * to modify the keyring */ - ret = key_permission(keyring_ref, KEY_WRITE); + ret = key_permission(keyring_ref, KEY_NEED_WRITE); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_link_end; @@ -928,7 +928,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) key_check(key); /* the key must be writable */ - ret = key_permission(key_ref, KEY_WRITE); + ret = key_permission(key_ref, KEY_NEED_WRITE); if (ret < 0) goto error; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cee72ce64222..cd5bd0cef25d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -111,7 +111,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, } /* find the target keyring (which must be writable) */ - keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); + keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error3; @@ -195,7 +195,7 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, dest_ref = NULL; if (destringid) { dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, - KEY_WRITE); + KEY_NEED_WRITE); if (IS_ERR(dest_ref)) { ret = PTR_ERR(dest_ref); goto error3; @@ -253,7 +253,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create) long ret; lflags = create ? KEY_LOOKUP_CREATE : 0; - key_ref = lookup_user_key(id, lflags, KEY_SEARCH); + key_ref = lookup_user_key(id, lflags, KEY_NEED_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; @@ -334,7 +334,7 @@ long keyctl_update_key(key_serial_t id, } /* find the target key (which must be writable) */ - key_ref = lookup_user_key(id, 0, KEY_WRITE); + key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; @@ -365,12 +365,12 @@ long keyctl_revoke_key(key_serial_t id) key_ref_t key_ref; long ret; - key_ref = lookup_user_key(id, 0, KEY_WRITE); + key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); if (ret != -EACCES) goto error; - key_ref = lookup_user_key(id, 0, KEY_SETATTR); + key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; @@ -401,7 +401,7 @@ long keyctl_invalidate_key(key_serial_t id) kenter("%d", id); - key_ref = lookup_user_key(id, 0, KEY_SEARCH); + key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; @@ -428,7 +428,7 @@ long keyctl_keyring_clear(key_serial_t ringid) key_ref_t keyring_ref; long ret; - keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); + keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); @@ -470,13 +470,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) key_ref_t keyring_ref, key_ref; long ret; - keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); + keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; } - key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_LINK); + key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; @@ -505,7 +505,7 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) key_ref_t keyring_ref, key_ref; long ret; - keyring_ref = lookup_user_key(ringid, 0, KEY_WRITE); + keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; @@ -548,7 +548,7 @@ long keyctl_describe_key(key_serial_t keyid, char *tmpbuf; long ret; - key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW); + key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW); if (IS_ERR(key_ref)) { /* viewing a key under construction is permitted if we have the * authorisation token handy */ @@ -639,7 +639,7 @@ long keyctl_keyring_search(key_serial_t ringid, } /* get the keyring at which to begin the search */ - keyring_ref = lookup_user_key(ringid, 0, KEY_SEARCH); + keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_SEARCH); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error2; @@ -649,7 +649,7 @@ long keyctl_keyring_search(key_serial_t ringid, dest_ref = NULL; if (destringid) { dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, - KEY_WRITE); + KEY_NEED_WRITE); if (IS_ERR(dest_ref)) { ret = PTR_ERR(dest_ref); goto error3; @@ -676,7 +676,7 @@ long keyctl_keyring_search(key_serial_t ringid, /* link the resulting key to the destination keyring if we can */ if (dest_ref) { - ret = key_permission(key_ref, KEY_LINK); + ret = key_permission(key_ref, KEY_NEED_LINK); if (ret < 0) goto error6; @@ -727,7 +727,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) key = key_ref_to_ptr(key_ref); /* see if we can read it directly */ - ret = key_permission(key_ref, KEY_READ); + ret = key_permission(key_ref, KEY_NEED_READ); if (ret == 0) goto can_read_key; if (ret != -EACCES) @@ -799,7 +799,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) goto error; key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, - KEY_SETATTR); + KEY_NEED_SETATTR); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; @@ -905,7 +905,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) goto error; key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, - KEY_SETATTR); + KEY_NEED_SETATTR); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; @@ -947,7 +947,7 @@ static long get_instantiation_keyring(key_serial_t ringid, /* if a specific keyring is nominated by ID, then use that */ if (ringid > 0) { - dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); + dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); if (IS_ERR(dkref)) return PTR_ERR(dkref); *_dest_keyring = key_ref_to_ptr(dkref); @@ -1315,7 +1315,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) long ret; key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, - KEY_SETATTR); + KEY_NEED_SETATTR); if (IS_ERR(key_ref)) { /* setting the timeout on a key under construction is permitted * if we have the authorisation token handy */ @@ -1418,7 +1418,7 @@ long keyctl_get_security(key_serial_t keyid, char *context; long ret; - key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW); + key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW); if (IS_ERR(key_ref)) { if (PTR_ERR(key_ref) != -EACCES) return PTR_ERR(key_ref); @@ -1482,7 +1482,7 @@ long keyctl_session_to_parent(void) struct cred *cred; int ret; - keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); + keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_LINK); if (IS_ERR(keyring_r)) return PTR_ERR(keyring_r); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 2fb2576dc644..9cf2575f0d97 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -541,7 +541,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) /* key must have search permissions */ if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && key_task_permission(make_key_ref(key, ctx->possessed), - ctx->cred, KEY_SEARCH) < 0) { + ctx->cred, KEY_NEED_SEARCH) < 0) { ctx->result = ERR_PTR(-EACCES); kleave(" = %d [!perm]", ctx->skipped_ret); goto skipped; @@ -721,7 +721,7 @@ ascend_to_node: /* Search a nested keyring */ if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && key_task_permission(make_key_ref(key, ctx->possessed), - ctx->cred, KEY_SEARCH) < 0) + ctx->cred, KEY_NEED_SEARCH) < 0) continue; /* stack the current position */ @@ -843,7 +843,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, return ERR_PTR(-ENOTDIR); if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM)) { - err = key_task_permission(keyring_ref, ctx->cred, KEY_SEARCH); + err = key_task_permission(keyring_ref, ctx->cred, KEY_NEED_SEARCH); if (err < 0) return ERR_PTR(err); } @@ -973,7 +973,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) if (!skip_perm_check && key_permission(make_key_ref(keyring, 0), - KEY_SEARCH) < 0) + KEY_NEED_SEARCH) < 0) continue; /* we've got a match but we might end up racing with diff --git a/security/keys/permission.c b/security/keys/permission.c index efcc0c855a0d..732cc0beffdf 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -28,7 +28,7 @@ * permissions bits or the LSM check. */ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, - key_perm_t perm) + unsigned perm) { struct key *key; key_perm_t kperm; @@ -68,7 +68,7 @@ use_these_perms: if (is_key_possessed(key_ref)) kperm |= key->perm >> 24; - kperm = kperm & perm & KEY_ALL; + kperm = kperm & perm & KEY_NEED_ALL; if (kperm != perm) return -EACCES; diff --git a/security/keys/persistent.c b/security/keys/persistent.c index 0ad3ee283781..c9fae5ea89fe 100644 --- a/security/keys/persistent.c +++ b/security/keys/persistent.c @@ -108,7 +108,7 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid, return PTR_ERR(persistent_ref); found: - ret = key_task_permission(persistent_ref, current_cred(), KEY_LINK); + ret = key_task_permission(persistent_ref, current_cred(), KEY_NEED_LINK); if (ret == 0) { persistent = key_ref_to_ptr(persistent_ref); ret = key_link(key_ref_to_ptr(dest_ref), persistent); @@ -151,7 +151,7 @@ long keyctl_get_persistent(uid_t _uid, key_serial_t destid) } /* There must be a destination keyring */ - dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_WRITE); + dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); if (IS_ERR(dest_ref)) return PTR_ERR(dest_ref); if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) { diff --git a/security/keys/proc.c b/security/keys/proc.c index 88e9a466940f..d3f6f2fd21db 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -218,7 +218,7 @@ static int proc_keys_show(struct seq_file *m, void *v) * - the caller holds a spinlock, and thus the RCU read lock, making our * access to __current_cred() safe */ - rc = key_task_permission(key_ref, ctx.cred, KEY_VIEW); + rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); if (rc < 0) return 0; diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index 8c0af08760c8..b68faa1a5cfd 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -15,7 +15,7 @@ static const int zero, one = 1, max = INT_MAX; -ctl_table key_sysctls[] = { +struct ctl_table key_sysctls[] = { { .procname = "maxkeys", .data = &key_quota_maxkeys, diff --git a/security/security.c b/security/security.c index 8b774f362a3d..31614e9e96e5 100644 --- a/security/security.c +++ b/security/security.c @@ -1425,7 +1425,7 @@ void security_key_free(struct key *key) } int security_key_permission(key_ref_t key_ref, - const struct cred *cred, key_perm_t perm) + const struct cred *cred, unsigned perm) { return security_ops->key_permission(key_ref, cred, perm); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 39bc8c94b969..7740f61588d6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4510,9 +4510,18 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; struct sk_security_struct *sksec = sk->sk_security; - if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || - sk->sk_family == PF_UNIX) + switch (sk->sk_family) { + case PF_INET: + case PF_INET6: + case PF_UNIX: isec->sid = sksec->sid; + break; + default: + /* by default there is no special labeling mechanism for the + * sksec label so inherit the label from the parent socket */ + BUG_ON(sksec->sid != SECINITSID_UNLABELED); + sksec->sid = isec->sid; + } sksec->sclass = isec->sclass; } @@ -5736,7 +5745,7 @@ static void selinux_key_free(struct key *k) static int selinux_key_permission(key_ref_t key_ref, const struct cred *cred, - key_perm_t perm) + unsigned perm) { struct key *key; struct key_security_struct *ksec; diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 14d04e63b1f0..be491a74c1ed 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -147,7 +147,7 @@ struct security_class_mapping secclass_map[] = { { "peer", { "recv", NULL } }, { "capability2", { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", - NULL } }, + "audit_read", NULL } }, { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, "attach_queue", NULL } }, diff --git a/security/smack/smack.h b/security/smack/smack.h index d072fd32212d..020307ef0972 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -80,8 +80,8 @@ struct superblock_smack { struct socket_smack { struct smack_known *smk_out; /* outbound label */ - char *smk_in; /* inbound label */ - char *smk_packet; /* TCP peer label */ + struct smack_known *smk_in; /* inbound label */ + struct smack_known *smk_packet; /* TCP peer label */ }; /* @@ -133,7 +133,7 @@ struct smk_port_label { struct list_head list; struct sock *smk_sock; /* socket initialized on */ unsigned short smk_port; /* the port number */ - char *smk_in; /* incoming label */ + struct smack_known *smk_in; /* inbound label */ struct smack_known *smk_out; /* outgoing label */ }; @@ -177,6 +177,14 @@ struct smk_port_label { #define SMACK_CIPSO_MAXCATNUM 184 /* 23 * 8 */ /* + * Ptrace rules + */ +#define SMACK_PTRACE_DEFAULT 0 +#define SMACK_PTRACE_EXACT 1 +#define SMACK_PTRACE_DRACONIAN 2 +#define SMACK_PTRACE_MAX SMACK_PTRACE_DRACONIAN + +/* * Flags for untraditional access modes. * It shouldn't be necessary to avoid conflicts with definitions * in fs.h, but do so anyway. @@ -225,6 +233,7 @@ struct inode_smack *new_inode_smack(char *); */ int smk_access_entry(char *, char *, struct list_head *); int smk_access(struct smack_known *, char *, int, struct smk_audit_info *); +int smk_tskacc(struct task_smack *, char *, u32, struct smk_audit_info *); int smk_curacc(char *, u32, struct smk_audit_info *); struct smack_known *smack_from_secid(const u32); char *smk_parse_smack(const char *string, int len); @@ -244,6 +253,7 @@ extern struct smack_known *smack_net_ambient; extern struct smack_known *smack_onlycap; extern struct smack_known *smack_syslog_label; extern const char *smack_cipso_option; +extern int smack_ptrace_rule; extern struct smack_known smack_known_floor; extern struct smack_known smack_known_hat; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 14293cd9b1e5..c062e9467b62 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -192,20 +192,21 @@ out_audit: } /** - * smk_curacc - determine if current has a specific access to an object + * smk_tskacc - determine if a task has a specific access to an object + * @tsp: a pointer to the subject task * @obj_label: a pointer to the object's Smack label * @mode: the access requested, in "MAY" format * @a : common audit data * - * This function checks the current subject label/object label pair + * This function checks the subject task's label/object label pair * in the access rule list and returns 0 if the access is permitted, - * non zero otherwise. It allows that current may have the capability + * non zero otherwise. It allows that the task may have the capability * to override the rules. */ -int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) +int smk_tskacc(struct task_smack *subject, char *obj_label, + u32 mode, struct smk_audit_info *a) { - struct task_smack *tsp = current_security(); - struct smack_known *skp = smk_of_task(tsp); + struct smack_known *skp = smk_of_task(subject); int may; int rc; @@ -219,7 +220,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) * it can further restrict access. */ may = smk_access_entry(skp->smk_known, obj_label, - &tsp->smk_rules); + &subject->smk_rules); if (may < 0) goto out_audit; if ((mode & may) == mode) @@ -241,6 +242,24 @@ out_audit: return rc; } +/** + * smk_curacc - determine if current has a specific access to an object + * @obj_label: a pointer to the object's Smack label + * @mode: the access requested, in "MAY" format + * @a : common audit data + * + * This function checks the current subject label/object label pair + * in the access rule list and returns 0 if the access is permitted, + * non zero otherwise. It allows that current may have the capability + * to override the rules. + */ +int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) +{ + struct task_smack *tsp = current_security(); + + return smk_tskacc(tsp, obj_label, mode, a); +} + #ifdef CONFIG_AUDIT /** * smack_str_from_perm : helper to transalate an int to a @@ -285,7 +304,10 @@ static void smack_log_callback(struct audit_buffer *ab, void *a) audit_log_untrustedstring(ab, sad->subject); audit_log_format(ab, " object="); audit_log_untrustedstring(ab, sad->object); - audit_log_format(ab, " requested=%s", sad->request); + if (sad->request[0] == '\0') + audit_log_format(ab, " labels_differ"); + else + audit_log_format(ab, " requested=%s", sad->request); } /** diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 14f52be78c75..f2c30801ce41 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -157,6 +157,74 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead, return rc; } +/** + * 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 + */ +static inline unsigned int smk_ptrace_mode(unsigned int mode) +{ + switch (mode) { + case PTRACE_MODE_READ: + return MAY_READ; + case PTRACE_MODE_ATTACH: + return MAY_READWRITE; + } + + return 0; +} + +/** + * smk_ptrace_rule_check - helper for ptrace access + * @tracer: tracer process + * @tracee_label: label of the process that's about to be traced, + * the pointer must originate from smack structures + * @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 + */ +static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, + unsigned int mode, const char *func) +{ + int rc; + struct smk_audit_info ad, *saip = NULL; + struct task_smack *tsp; + struct smack_known *skp; + + if ((mode & PTRACE_MODE_NOAUDIT) == 0) { + smk_ad_init(&ad, func, LSM_AUDIT_DATA_TASK); + smk_ad_setfield_u_tsk(&ad, tracer); + saip = &ad; + } + + tsp = task_security(tracer); + skp = smk_of_task(tsp); + + if ((mode & PTRACE_MODE_ATTACH) && + (smack_ptrace_rule == SMACK_PTRACE_EXACT || + smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { + if (skp->smk_known == tracee_label) + rc = 0; + else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) + rc = -EACCES; + else if (capable(CAP_SYS_PTRACE)) + rc = 0; + else + rc = -EACCES; + + if (saip) + smack_log(skp->smk_known, tracee_label, 0, rc, saip); + + return rc; + } + + /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ + rc = smk_tskacc(tsp, tracee_label, smk_ptrace_mode(mode), saip); + return rc; +} + /* * LSM hooks. * We he, that is fun! @@ -165,16 +233,15 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead, /** * smack_ptrace_access_check - Smack approval on PTRACE_ATTACH * @ctp: child task pointer - * @mode: ptrace attachment mode + * @mode: ptrace attachment mode (PTRACE_MODE_*) * * Returns 0 if access is OK, an error code otherwise * - * Do the capability checks, and require read and write. + * Do the capability checks. */ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { int rc; - struct smk_audit_info ad; struct smack_known *skp; rc = cap_ptrace_access_check(ctp, mode); @@ -182,10 +249,8 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) return rc; skp = smk_of_task(task_security(ctp)); - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); - smk_ad_setfield_u_tsk(&ad, ctp); - rc = smk_curacc(skp->smk_known, mode, &ad); + rc = smk_ptrace_rule_check(current, skp->smk_known, mode, __func__); return rc; } @@ -195,23 +260,21 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) * * Returns 0 if access is OK, an error code otherwise * - * Do the capability checks, and require read and write. + * Do the capability checks, and require PTRACE_MODE_ATTACH. */ static int smack_ptrace_traceme(struct task_struct *ptp) { int rc; - struct smk_audit_info ad; struct smack_known *skp; rc = cap_ptrace_traceme(ptp); if (rc != 0) return rc; - skp = smk_of_task(task_security(ptp)); - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); - smk_ad_setfield_u_tsk(&ad, ptp); + skp = smk_of_task(current_security()); - rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad); + rc = smk_ptrace_rule_check(ptp, skp->smk_known, + PTRACE_MODE_ATTACH, __func__); return rc; } @@ -413,9 +476,11 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) * Initialize the root inode. */ isp = inode->i_security; - if (inode->i_security == NULL) { - inode->i_security = new_inode_smack(sp->smk_root); - isp = inode->i_security; + if (isp == NULL) { + isp = new_inode_smack(sp->smk_root); + if (isp == NULL) + return -ENOMEM; + inode->i_security = isp; } else isp->smk_inode = sp->smk_root; @@ -453,7 +518,7 @@ static int smack_sb_statfs(struct dentry *dentry) * smack_bprm_set_creds - set creds for exec * @bprm: the exec information * - * Returns 0 if it gets a blob, -ENOMEM otherwise + * Returns 0 if it gets a blob, -EPERM if exec forbidden and -ENOMEM otherwise */ static int smack_bprm_set_creds(struct linux_binprm *bprm) { @@ -473,7 +538,22 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) return 0; - if (bprm->unsafe) + if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { + struct task_struct *tracer; + rc = 0; + + rcu_read_lock(); + tracer = ptrace_parent(current); + if (likely(tracer != NULL)) + rc = smk_ptrace_rule_check(tracer, + isp->smk_task->smk_known, + PTRACE_MODE_ATTACH, + __func__); + rcu_read_unlock(); + + if (rc != 0) + return rc; + } else if (bprm->unsafe) return -EPERM; bsp->smk_task = isp->smk_task; @@ -880,18 +960,20 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, return; } - skp = smk_import_entry(value, size); if (strcmp(name, XATTR_NAME_SMACK) == 0) { + skp = smk_import_entry(value, size); if (skp != NULL) isp->smk_inode = skp->smk_known; else isp->smk_inode = smack_known_invalid.smk_known; } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { + skp = smk_import_entry(value, size); if (skp != NULL) isp->smk_task = skp; else isp->smk_task = &smack_known_invalid; } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { + skp = smk_import_entry(value, size); if (skp != NULL) isp->smk_mmap = skp; else @@ -938,24 +1020,37 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 || - strcmp(name, XATTR_NAME_SMACKMMAP)) { + strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { if (!smack_privileged(CAP_MAC_ADMIN)) rc = -EPERM; } else rc = cap_inode_removexattr(dentry, name); + if (rc != 0) + return rc; + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - if (rc == 0) - rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); - if (rc == 0) { - isp = dentry->d_inode->i_security; + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + if (rc != 0) + return rc; + + isp = dentry->d_inode->i_security; + /* + * Don't do anything special for these. + * XATTR_NAME_SMACKIPIN + * XATTR_NAME_SMACKIPOUT + * XATTR_NAME_SMACKEXEC + */ + if (strcmp(name, XATTR_NAME_SMACK) == 0) isp->smk_task = NULL; + else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) isp->smk_mmap = NULL; - } + else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) + isp->smk_flags &= ~SMK_INODE_TRANSMUTE; - return rc; + return 0; } /** @@ -1000,7 +1095,7 @@ static int smack_inode_getsecurity(const struct inode *inode, ssp = sock->sk->sk_security; if (strcmp(name, XATTR_SMACK_IPIN) == 0) - isp = ssp->smk_in; + isp = ssp->smk_in->smk_known; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) isp = ssp->smk_out->smk_known; else @@ -1367,19 +1462,32 @@ static int smack_file_receive(struct file *file) /** * smack_file_open - Smack dentry open processing * @file: the object - * @cred: unused + * @cred: task credential * * 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 */ static int smack_file_open(struct file *file, const struct cred *cred) { + struct task_smack *tsp = cred->security; struct inode_smack *isp = file_inode(file)->i_security; + struct smk_audit_info ad; + int rc; - file->f_security = isp->smk_inode; + if (smack_privileged(CAP_MAC_OVERRIDE)) + return 0; - return 0; + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); + smk_ad_setfield_u_fs_path(&ad, file->f_path); + rc = smk_access(tsp->smk_task, isp->smk_inode, MAY_READ, &ad); + if (rc == 0) + file->f_security = isp->smk_inode; + + return rc; } /* @@ -1764,7 +1872,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) if (ssp == NULL) return -ENOMEM; - ssp->smk_in = skp->smk_known; + ssp->smk_in = skp; ssp->smk_out = skp; ssp->smk_packet = NULL; @@ -2004,7 +2112,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, if (act == SMK_RECEIVING) { skp = smack_net_ambient; - object = ssp->smk_in; + object = ssp->smk_in->smk_known; } else { skp = ssp->smk_out; object = smack_net_ambient->smk_known; @@ -2034,9 +2142,9 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, list_for_each_entry(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port) continue; - object = spp->smk_in; + object = spp->smk_in->smk_known; if (act == SMK_CONNECTING) - ssp->smk_packet = spp->smk_out->smk_known; + ssp->smk_packet = spp->smk_out; break; } @@ -2076,7 +2184,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, int rc = 0; if (value == NULL || size > SMK_LONGLABEL || size == 0) - return -EACCES; + return -EINVAL; skp = smk_import_entry(value, size); if (skp == NULL) @@ -2100,7 +2208,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, ssp = sock->sk->sk_security; if (strcmp(name, XATTR_SMACK_IPIN) == 0) - ssp->smk_in = skp->smk_known; + ssp->smk_in = skp; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) { ssp->smk_out = skp; if (sock->sk->sk_family == PF_INET) { @@ -2713,6 +2821,15 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * of the superblock. */ if (opt_dentry->d_parent == opt_dentry) { + if (sbp->s_magic == CGROUP_SUPER_MAGIC) { + /* + * The cgroup filesystem is never mounted, + * so there's no opportunity to set the mount + * options. + */ + sbsp->smk_root = smack_known_star.smk_known; + sbsp->smk_default = smack_known_star.smk_known; + } isp->smk_inode = sbsp->smk_root; isp->smk_flags |= SMK_INODE_INSTANT; goto unlockandout; @@ -2726,16 +2843,20 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ switch (sbp->s_magic) { case SMACK_MAGIC: + case PIPEFS_MAGIC: + case SOCKFS_MAGIC: + case CGROUP_SUPER_MAGIC: /* * Casey says that it's a little embarrassing * that the smack file system doesn't do * extended attributes. - */ - final = smack_known_star.smk_known; - break; - case PIPEFS_MAGIC: - /* + * * Casey says pipes are easy (?) + * + * Socket access is controlled by the socket + * structures associated with the task involved. + * + * Cgroupfs is special */ final = smack_known_star.smk_known; break; @@ -2747,13 +2868,6 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ final = ckp->smk_known; break; - case SOCKFS_MAGIC: - /* - * Socket access is controlled by the socket - * structures associated with the task involved. - */ - final = smack_known_star.smk_known; - break; case PROC_SUPER_MAGIC: /* * Casey says procfs appears not to care. @@ -2959,30 +3073,34 @@ static int smack_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk) { struct smack_known *skp; + struct smack_known *okp; struct socket_smack *ssp = sock->sk_security; struct socket_smack *osp = other->sk_security; struct socket_smack *nsp = newsk->sk_security; struct smk_audit_info ad; int rc = 0; - #ifdef CONFIG_AUDIT struct lsm_network_audit net; - - smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); - smk_ad_setfield_u_net_sk(&ad, other); #endif if (!smack_privileged(CAP_MAC_OVERRIDE)) { skp = ssp->smk_out; - rc = smk_access(skp, osp->smk_in, MAY_WRITE, &ad); + okp = osp->smk_out; +#ifdef CONFIG_AUDIT + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + smk_ad_setfield_u_net_sk(&ad, other); +#endif + rc = smk_access(skp, okp->smk_known, MAY_WRITE, &ad); + if (rc == 0) + rc = smk_access(okp, okp->smk_known, MAY_WRITE, NULL); } /* * Cross reference the peer labels for SO_PEERSEC. */ if (rc == 0) { - nsp->smk_packet = ssp->smk_out->smk_known; - ssp->smk_packet = osp->smk_out->smk_known; + nsp->smk_packet = ssp->smk_out; + ssp->smk_packet = osp->smk_out; } return rc; @@ -3014,7 +3132,7 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) return 0; skp = ssp->smk_out; - return smk_access(skp, osp->smk_in, MAY_WRITE, &ad); + return smk_access(skp, osp->smk_in->smk_known, MAY_WRITE, &ad); } /** @@ -3109,7 +3227,7 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, if (found) return skp; - if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known) + if (ssp != NULL && ssp->smk_in == &smack_known_star) return &smack_known_web; return &smack_known_star; } @@ -3228,7 +3346,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * This is the simplist possible security model * for networking. */ - rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); if (rc != 0) netlbl_skbuff_err(skb, rc, 0); break; @@ -3263,7 +3381,7 @@ static int smack_socket_getpeersec_stream(struct socket *sock, ssp = sock->sk->sk_security; if (ssp->smk_packet != NULL) { - rcp = ssp->smk_packet; + rcp = ssp->smk_packet->smk_known; slen = strlen(rcp) + 1; } @@ -3348,7 +3466,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent) return; ssp = sk->sk_security; - ssp->smk_in = skp->smk_known; + ssp->smk_in = skp; ssp->smk_out = skp; /* cssp->smk_packet is already set in smack_inet_csk_clone() */ } @@ -3408,7 +3526,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * 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_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); if (rc != 0) return rc; @@ -3452,7 +3570,7 @@ static void smack_inet_csk_clone(struct sock *sk, if (req->peer_secid != 0) { skp = smack_from_secid(req->peer_secid); - ssp->smk_packet = skp->smk_known; + ssp->smk_packet = skp; } else ssp->smk_packet = NULL; } @@ -3506,11 +3624,12 @@ static void smack_key_free(struct key *key) * an error code otherwise */ static int smack_key_permission(key_ref_t key_ref, - const struct cred *cred, key_perm_t perm) + const struct cred *cred, unsigned perm) { struct key *keyp; struct smk_audit_info ad; struct smack_known *tkp = smk_of_task(cred->security); + int request = 0; keyp = key_ref_to_ptr(key_ref); if (keyp == NULL) @@ -3531,7 +3650,11 @@ static int smack_key_permission(key_ref_t key_ref, ad.a.u.key_struct.key = keyp->serial; ad.a.u.key_struct.key_desc = keyp->description; #endif - return smk_access(tkp, keyp->security, MAY_READWRITE, &ad); + if (perm & KEY_NEED_READ) + request = MAY_READ; + if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR)) + request = MAY_WRITE; + return smk_access(tkp, keyp->security, request, &ad); } #endif /* CONFIG_KEYS */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 3198cfe1dcc6..32b248820840 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -53,6 +53,7 @@ enum smk_inos { SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */ SMK_SYSLOG = 20, /* change syslog label) */ + SMK_PTRACE = 21, /* set ptrace rule */ }; /* @@ -101,6 +102,15 @@ struct smack_known *smack_onlycap; struct smack_known *smack_syslog_label; /* + * Ptrace current rule + * SMACK_PTRACE_DEFAULT regular smack ptrace rules (/proc based) + * SMACK_PTRACE_EXACT labels must match, but can be overriden with + * CAP_SYS_PTRACE + * SMACK_PTRACE_DRACONIAN lables must match, CAP_SYS_PTRACE has no effect + */ +int smack_ptrace_rule = SMACK_PTRACE_DEFAULT; + +/* * Certain IP addresses may be designated as single label hosts. * Packets are sent there unlabeled, but only from tasks that * can write to the specified label. @@ -1183,7 +1193,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, data[count] = '\0'; - rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%d %s", + rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s", &host[0], &host[1], &host[2], &host[3], &m, smack); if (rc != 6) { rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s", @@ -2244,6 +2254,68 @@ static const struct file_operations smk_syslog_ops = { /** + * smk_read_ptrace - read() for /smack/ptrace + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_ptrace(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[32]; + ssize_t rc; + + if (*ppos != 0) + return 0; + + sprintf(temp, "%d\n", smack_ptrace_rule); + rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + return rc; +} + +/** + * smk_write_ptrace - write() for /smack/ptrace + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_ptrace(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[32]; + int i; + + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + if (*ppos != 0 || count >= sizeof(temp) || count == 0) + return -EINVAL; + + if (copy_from_user(temp, buf, count) != 0) + return -EFAULT; + + temp[count] = '\0'; + + if (sscanf(temp, "%d", &i) != 1) + return -EINVAL; + if (i < SMACK_PTRACE_DEFAULT || i > SMACK_PTRACE_MAX) + return -EINVAL; + smack_ptrace_rule = i; + + return count; +} + +static const struct file_operations smk_ptrace_ops = { + .write = smk_write_ptrace, + .read = smk_read_ptrace, + .llseek = default_llseek, +}; + +/** * smk_fill_super - fill the smackfs superblock * @sb: the empty superblock * @data: unused @@ -2296,6 +2368,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) "change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR}, [SMK_SYSLOG] = { "syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR}, + [SMK_PTRACE] = { + "ptrace", &smk_ptrace_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; |