diff options
Diffstat (limited to 'security')
51 files changed, 1078 insertions, 319 deletions
diff --git a/security/Kconfig b/security/Kconfig index e45237897..176758cdf 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -122,6 +122,7 @@ source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig source security/apparmor/Kconfig +source security/loadpin/Kconfig source security/yama/Kconfig source security/integrity/Kconfig diff --git a/security/Makefile b/security/Makefile index c9bfbc84f..f2d71cdb8 100644 --- a/security/Makefile +++ b/security/Makefile @@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_YAMA) += yama +subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin # always enable default capabilities obj-y += commoncap.o @@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT) += lsm_audit.o obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ obj-$(CONFIG_SECURITY_YAMA) += yama/ +obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ad4fa49ad..9068369f8 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) seq_printf(seq, "%.2x", profile->hash[i]); seq_puts(seq, "\n"); } + aa_put_profile(profile); return 0; } diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 913f377a0..d186674f9 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -275,7 +275,7 @@ static inline bool is_deleted(struct dentry *dentry) * * Returns: %0 else error if access denied or other error */ -int aa_path_perm(int op, struct aa_profile *profile, struct path *path, +int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, int flags, u32 request, struct path_cond *cond) { char *buffer = NULL; @@ -346,7 +346,7 @@ static inline bool xindex_is_subset(u32 link, u32 target) * Returns: %0 if allowed else error */ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, - struct path *new_dir, struct dentry *new_dentry) + const struct path *new_dir, struct dentry *new_dentry) { struct path link = { new_dir->mnt, new_dentry }; struct path target = { new_dir->mnt, old_dentry }; diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 2c922b86b..4803c97d1 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -171,11 +171,11 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, const char *name, struct path_cond *cond, struct file_perms *perms); -int aa_path_perm(int op, struct aa_profile *profile, struct path *path, +int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, int flags, u32 request, struct path_cond *cond); int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, - struct path *new_dir, struct dentry *new_dentry); + const struct path *new_dir, struct dentry *new_dentry); int aa_file_perm(int op, struct aa_profile *profile, struct file *file, u32 request); diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 286ac75dc..73560f258 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -26,7 +26,7 @@ enum path_flags { PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ }; -int aa_path_name(struct path *path, int flags, char **buffer, +int aa_path_name(const struct path *path, int flags, char **buffer, const char **name, const char **info); #endif /* __AA_PATH_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 5ee820111..7798e1608 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -149,7 +149,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns, * * Returns: %0 else error code if error or permission denied */ -static int common_perm(int op, struct path *path, u32 mask, +static int common_perm(int op, const struct path *path, u32 mask, struct path_cond *cond) { struct aa_profile *profile; @@ -172,7 +172,7 @@ static int common_perm(int op, struct path *path, u32 mask, * * Returns: %0 else error code if error or permission denied */ -static int common_perm_dir_dentry(int op, struct path *dir, +static int common_perm_dir_dentry(int op, const struct path *dir, struct dentry *dentry, u32 mask, struct path_cond *cond) { @@ -182,23 +182,22 @@ static int common_perm_dir_dentry(int op, struct path *dir, } /** - * common_perm_mnt_dentry - common permission wrapper when mnt, dentry + * common_perm_path - common permission wrapper when mnt, dentry * @op: operation being checked - * @mnt: mount point of dentry (NOT NULL) - * @dentry: dentry to check (NOT NULL) + * @path: location to check (NOT NULL) * @mask: requested permissions mask * * Returns: %0 else error code if error or permission denied */ -static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, - struct dentry *dentry, u32 mask) +static inline int common_perm_path(int op, const struct path *path, u32 mask) { - struct path path = { mnt, dentry }; - struct path_cond cond = { d_backing_inode(dentry)->i_uid, - d_backing_inode(dentry)->i_mode + struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, + d_backing_inode(path->dentry)->i_mode }; + if (!mediated_filesystem(path->dentry)) + return 0; - return common_perm(op, &path, mask, &cond); + return common_perm(op, path, mask, &cond); } /** @@ -210,13 +209,13 @@ static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, * * Returns: %0 else error code if error or permission denied */ -static int common_perm_rm(int op, struct path *dir, +static int common_perm_rm(int op, const struct path *dir, struct dentry *dentry, u32 mask) { struct inode *inode = d_backing_inode(dentry); struct path_cond cond = { }; - if (!inode || !dir->mnt || !mediated_filesystem(dentry)) + if (!inode || !mediated_filesystem(dentry)) return 0; cond.uid = inode->i_uid; @@ -235,61 +234,53 @@ static int common_perm_rm(int op, struct path *dir, * * Returns: %0 else error code if error or permission denied */ -static int common_perm_create(int op, struct path *dir, struct dentry *dentry, - u32 mask, umode_t mode) +static int common_perm_create(int op, const struct path *dir, + struct dentry *dentry, u32 mask, umode_t mode) { struct path_cond cond = { current_fsuid(), mode }; - if (!dir->mnt || !mediated_filesystem(dir->dentry)) + if (!mediated_filesystem(dir->dentry)) return 0; return common_perm_dir_dentry(op, dir, dentry, mask, &cond); } -static int apparmor_path_unlink(struct path *dir, struct dentry *dentry) +static int apparmor_path_unlink(const struct path *dir, struct dentry *dentry) { return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE); } -static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry, +static int apparmor_path_mkdir(const struct path *dir, struct dentry *dentry, umode_t mode) { return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE, S_IFDIR); } -static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry) +static int apparmor_path_rmdir(const struct path *dir, struct dentry *dentry) { return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE); } -static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, +static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev) { return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode); } -static int apparmor_path_truncate(struct path *path) +static int apparmor_path_truncate(const struct path *path) { - struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, - d_backing_inode(path->dentry)->i_mode - }; - - if (!path->mnt || !mediated_filesystem(path->dentry)) - return 0; - - return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE, - &cond); + return common_perm_path(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); } -static int apparmor_path_symlink(struct path *dir, struct dentry *dentry, +static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, const char *old_name) { return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE, S_IFLNK); } -static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, +static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) { struct aa_profile *profile; @@ -304,8 +295,8 @@ static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, return error; } -static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, - struct path *new_dir, struct dentry *new_dentry) +static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_dentry, + const struct path *new_dir, struct dentry *new_dentry) { struct aa_profile *profile; int error = 0; @@ -334,33 +325,19 @@ static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, return error; } -static int apparmor_path_chmod(struct path *path, umode_t mode) +static int apparmor_path_chmod(const struct path *path, umode_t mode) { - if (!mediated_filesystem(path->dentry)) - return 0; - - return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD); + return common_perm_path(OP_CHMOD, path, AA_MAY_CHMOD); } -static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid) +static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { - struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, - d_backing_inode(path->dentry)->i_mode - }; - - if (!mediated_filesystem(path->dentry)) - return 0; - - return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond); + return common_perm_path(OP_CHOWN, path, AA_MAY_CHOWN); } static int apparmor_inode_getattr(const struct path *path) { - if (!mediated_filesystem(path->dentry)) - return 0; - - return common_perm_mnt_dentry(OP_GETATTR, path->mnt, path->dentry, - AA_MAY_META_READ); + return common_perm_path(OP_GETATTR, path, AA_MAY_META_READ); } static int apparmor_file_open(struct file *file, const struct cred *cred) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 71e0e3a15..edddc0264 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -53,7 +53,7 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) * When no error the path name is returned in @name which points to * to a position in @buf */ -static int d_namespace_path(struct path *path, char *buf, int buflen, +static int d_namespace_path(const struct path *path, char *buf, int buflen, char **name, int flags) { char *res; @@ -158,7 +158,7 @@ out: * * Returns: %0 else error on failure */ -static int get_name_to_buffer(struct path *path, int flags, char *buffer, +static int get_name_to_buffer(const struct path *path, int flags, char *buffer, int size, char **name, const char **info) { int adjust = (flags & PATH_IS_DIR) ? 1 : 0; @@ -204,8 +204,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, * * Returns: %0 else error code if could retrieve name */ -int aa_path_name(struct path *path, int flags, char **buffer, const char **name, - const char **info) +int aa_path_name(const struct path *path, int flags, char **buffer, + const char **name, const char **info) { char *buf, *str = NULL; int size = 256; diff --git a/security/commoncap.c b/security/commoncap.c index 50a1a40f0..6423e53a5 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -111,7 +111,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, * Determine whether the current process may set the system clock and timezone * information, returning 0 if permission granted, -ve if denied. */ -int cap_settime(const struct timespec *ts, const struct timezone *tz) +int cap_settime(const struct timespec64 *ts, const struct timezone *tz) { if (!capable(CAP_SYS_TIME)) return -EPERM; @@ -313,7 +313,7 @@ int cap_inode_need_killpriv(struct dentry *dentry) if (!inode->i_op->getxattr) return 0; - error = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0); + error = inode->i_op->getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0); if (error <= 0) return 0; return 1; @@ -397,8 +397,8 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data if (!inode || !inode->i_op->getxattr) return -ENODATA; - size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps, - XATTR_CAPS_SZ); + size = inode->i_op->getxattr((struct dentry *)dentry, inode, + XATTR_NAME_CAPS, &caps, XATTR_CAPS_SZ); if (size == -ENODATA || size == -EOPNOTSUPP) /* no data, that's ok */ return -ENODATA; diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 979be65d2..da9565891 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS default n select ASYMMETRIC_KEY_TYPE select ASYMMETRIC_PUBLIC_KEY_SUBTYPE - select PUBLIC_KEY_ALGO_RSA select CRYPTO_RSA select X509_CERTIFICATE_PARSER help diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 8ef15118c..4304372b3 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -18,6 +18,8 @@ #include <linux/cred.h> #include <linux/key-type.h> #include <linux/digsig.h> +#include <crypto/public_key.h> +#include <keys/system_keyring.h> #include "integrity.h" @@ -40,6 +42,12 @@ static bool init_keyring __initdata = true; static bool init_keyring __initdata; #endif +#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY +#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted +#else +#define restrict_link_to_ima restrict_link_by_builtin_trusted +#endif + int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen) { @@ -83,10 +91,9 @@ int __init integrity_init_keyring(const unsigned int id) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), - KEY_ALLOC_NOT_IN_QUOTA, NULL); - if (!IS_ERR(keyring[id])) - set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags); - else { + KEY_ALLOC_NOT_IN_QUOTA, + restrict_link_to_ima, NULL); + if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", keyring_name[id], err); diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index e6ea9d4b1..b9e26288d 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -82,7 +82,7 @@ static int evm_find_protected_xattrs(struct dentry *dentry) return -EOPNOTSUPP; for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) { - error = inode->i_op->getxattr(dentry, *xattr, NULL, 0); + error = inode->i_op->getxattr(dentry, inode, *xattr, NULL, 0); if (error < 0) { if (error == -ENODATA) continue; @@ -299,8 +299,8 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, return 0; /* exception for pseudo filesystems */ - if (dentry->d_inode->i_sb->s_magic == TMPFS_MAGIC - || dentry->d_inode->i_sb->s_magic == SYSFS_MAGIC) + if (dentry->d_sb->s_magic == TMPFS_MAGIC + || dentry->d_sb->s_magic == SYSFS_MAGIC) return 0; integrity_audit_msg(AUDIT_INTEGRITY_METADATA, diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index e54a8a8da..5487827fa 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING -config IMA_MOK_KEYRING - bool "Create IMA machine owner keys (MOK) and blacklist keyrings" +config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY + bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)" + depends on SYSTEM_TRUSTED_KEYRING + depends on SECONDARY_TRUSTED_KEYRING + depends on INTEGRITY_ASYMMETRIC_KEYS + select INTEGRITY_TRUSTED_KEYRING + default n + help + Keys may be added to the IMA or IMA blacklist keyrings, if the + key is validly signed by a CA cert in the system built-in or + secondary trusted keyrings. + + Intermediate keys between those the kernel has compiled in and the + IMA keys to be added may be added to the system secondary keyring, + provided they are validly signed by a key already resident in the + built-in or secondary trusted keyrings. + +config IMA_BLACKLIST_KEYRING + bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)" depends on SYSTEM_TRUSTED_KEYRING depends on IMA_TRUSTED_KEYRING default n help - This option creates IMA MOK and blacklist keyrings. IMA MOK is an - intermediate keyring that sits between .system and .ima keyrings, - effectively forming a simple CA hierarchy. To successfully import a - key into .ima_mok it must be signed by a key which CA is in .system - keyring. On turn any key that needs to go in .ima keyring must be - signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty - at kernel boot. - - IMA blacklist keyring contains all revoked IMA keys. It is consulted - before any other keyring. If the search is successful the requested - operation is rejected and error is returned to the caller. + This option creates an IMA blacklist keyring, which contains all + revoked IMA keys. It is consulted before any other keyring. If + the search is successful the requested operation is rejected and + an error is returned to the caller. config IMA_LOAD_X509 bool "Load X509 certificate onto the '.ima' trusted keyring" diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index a8539f9e0..9aeaedad1 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -8,4 +8,4 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o -obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o +obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 5d0f61163..d3a939bf2 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -170,7 +170,7 @@ int ima_alloc_init_template(struct ima_event_data *event_data, int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename); void ima_free_template_entry(struct ima_template_entry *entry); -const char *ima_d_path(struct path *path, char **pathbuf); +const char *ima_d_path(const struct path *path, char **pathbuf); /* IMA policy related functions */ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 370e42dfc..5a2218fe8 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -313,7 +313,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint, iint->flags |= IMA_AUDITED; } -const char *ima_d_path(struct path *path, char **pathbuf) +const char *ima_d_path(const struct path *path, char **pathbuf) { char *pathname = NULL; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 6b4694aed..1bcbc12e0 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -275,6 +275,11 @@ out: xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { if (!ima_fix_xattr(dentry, iint)) status = INTEGRITY_PASS; + } else if ((inode->i_size == 0) && + (iint->flags & IMA_NEW_FILE) && + (xattr_value && + xattr_value->type == EVM_IMA_XATTR_DIGSIG)) { + status = INTEGRITY_PASS; } integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); @@ -328,7 +333,7 @@ void ima_inode_post_setattr(struct dentry *dentry) if (iint) { iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | - IMA_ACTION_FLAGS); + IMA_ACTION_RULE_FLAGS); if (must_appraise) iint->flags |= IMA_APPRAISE; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 391f41751..68b26c340 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -246,7 +246,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size, ima_audit_measurement(iint, pathname); out_digsig: - if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) + if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) && + !(iint->flags & IMA_NEW_FILE)) rc = -EACCES; kfree(xattr_value); out_free: @@ -316,6 +317,28 @@ int ima_file_check(struct file *file, int mask, int opened) EXPORT_SYMBOL_GPL(ima_file_check); /** + * ima_post_path_mknod - mark as a new inode + * @dentry: newly created dentry + * + * Mark files created via the mknodat syscall as new, so that the + * file data can be written later. + */ +void ima_post_path_mknod(struct dentry *dentry) +{ + struct integrity_iint_cache *iint; + struct inode *inode = dentry->d_inode; + int must_appraise; + + must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK); + if (!must_appraise) + return; + + iint = integrity_inode_get(inode); + if (iint) + iint->flags |= IMA_NEW_FILE; +} + +/** * ima_read_file - pre-measure/appraise hook decision based on policy * @file: pointer to the file to be measured/appraised/audit * @read_id: caller identifier diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 676885e43..74a279957 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -17,38 +17,29 @@ #include <linux/cred.h> #include <linux/err.h> #include <linux/init.h> -#include <keys/asymmetric-type.h> +#include <keys/system_keyring.h> -struct key *ima_mok_keyring; struct key *ima_blacklist_keyring; /* - * Allocate the IMA MOK and blacklist keyrings + * Allocate the IMA blacklist keyring */ __init int ima_mok_init(void) { - pr_notice("Allocating IMA MOK and blacklist keyrings.\n"); - - ima_mok_keyring = keyring_alloc(".ima_mok", - KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ | - KEY_USR_WRITE | KEY_USR_SEARCH, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + pr_notice("Allocating IMA blacklist keyring.\n"); ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + restrict_link_by_builtin_trusted, NULL); - if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) - panic("Can't allocate IMA MOK or blacklist keyrings."); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags); + if (IS_ERR(ima_blacklist_keyring)) + panic("Can't allocate IMA blacklist keyring."); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags); set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); return 0; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3cd0a5867..0f887a564 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -972,7 +972,7 @@ static void policy_func_show(struct seq_file *m, enum ima_hooks func) int ima_policy_show(struct seq_file *m, void *v) { struct ima_rule_entry *entry = v; - int i = 0; + int i; char tbuf[64] = {0,}; rcu_read_lock(); @@ -1012,17 +1012,7 @@ int ima_policy_show(struct seq_file *m, void *v) } if (entry->flags & IMA_FSUUID) { - seq_puts(m, "fsuuid="); - for (i = 0; i < ARRAY_SIZE(entry->fsuuid); ++i) { - switch (i) { - case 4: - case 6: - case 8: - case 10: - seq_puts(m, "-"); - } - seq_printf(m, "%x", entry->fsuuid[i]); - } + seq_printf(m, "fsuuid=%pU", entry->fsuuid); seq_puts(m, " "); } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index e08935cf3..90bc57d79 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -28,6 +28,7 @@ /* iint cache flags */ #define IMA_ACTION_FLAGS 0xff000000 +#define IMA_ACTION_RULE_FLAGS 0x06000000 #define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG_REQUIRED 0x02000000 #define IMA_PERMIT_DIRECTIO 0x04000000 diff --git a/security/keys/Kconfig b/security/keys/Kconfig index fe4d74e12..f826e8739 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -41,6 +41,10 @@ config BIG_KEYS bool "Large payload keys" depends on KEYS depends on TMPFS + select CRYPTO + select CRYPTO_AES + select CRYPTO_ECB + select CRYPTO_RNG help This option provides support for holding large keys within the kernel (for example Kerberos ticket caches). The data may be stored out to @@ -81,3 +85,14 @@ config ENCRYPTED_KEYS Userspace only ever sees/stores encrypted blobs. If you are unsure as to whether this is required, answer N. + +config KEY_DH_OPERATIONS + bool "Diffie-Hellman operations on retained keys" + depends on KEYS + select MPILIB + help + This option provides support for calculating Diffie-Hellman + public keys and shared secrets using values stored as keys + in the kernel. + + If you are unsure as to whether this is required, answer N. diff --git a/security/keys/Makefile b/security/keys/Makefile index dfb3a7bed..1fd4a16e6 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o +obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o # # Key types diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 5643fafcb..3d241f92e 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -14,8 +14,10 @@ #include <linux/file.h> #include <linux/shmem_fs.h> #include <linux/err.h> +#include <linux/scatterlist.h> #include <keys/user-type.h> #include <keys/big_key-type.h> +#include <crypto/rng.h> /* * Layout of key payload words. @@ -28,6 +30,14 @@ enum { }; /* + * Crypto operation with big_key data + */ +enum big_key_op { + BIG_KEY_ENC, + BIG_KEY_DEC, +}; + +/* * If the data is under this limit, there's no point creating a shm file to * hold it as the permanently resident metadata for the shmem fs will be at * least as large as the data. @@ -35,6 +45,11 @@ enum { #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) /* + * Key size for big_key data encryption + */ +#define ENC_KEY_SIZE 16 + +/* * big_key defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload */ @@ -50,12 +65,62 @@ struct key_type key_type_big_key = { }; /* + * Crypto names for big_key data encryption + */ +static const char big_key_rng_name[] = "stdrng"; +static const char big_key_alg_name[] = "ecb(aes)"; + +/* + * Crypto algorithms for big_key data encryption + */ +static struct crypto_rng *big_key_rng; +static struct crypto_blkcipher *big_key_blkcipher; + +/* + * Generate random key to encrypt big_key data + */ +static inline int big_key_gen_enckey(u8 *key) +{ + return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE); +} + +/* + * Encrypt/decrypt big_key data + */ +static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) +{ + int ret = -EINVAL; + struct scatterlist sgio; + struct blkcipher_desc desc; + + if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) { + ret = -EAGAIN; + goto error; + } + + desc.flags = 0; + desc.tfm = big_key_blkcipher; + + sg_init_one(&sgio, data, datalen); + + if (op == BIG_KEY_ENC) + ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen); + else + ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen); + +error: + return ret; +} + +/* * Preparse a big key */ int big_key_preparse(struct key_preparsed_payload *prep) { struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct file *file; + u8 *enckey; + u8 *data = NULL; ssize_t written; size_t datalen = prep->datalen; int ret; @@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep) /* Create a shmem file to store the data in. This will permit the data * to be swapped out if needed. * - * TODO: Encrypt the stored data with a temporary key. + * File content is stored encrypted with randomly generated key. */ - file = shmem_kernel_file_setup("", datalen, 0, 0); + size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher)); + + /* prepare aligned data to encrypt */ + data = kmalloc(enclen, GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(data, prep->data, datalen); + memset(data + datalen, 0x00, enclen - datalen); + + /* generate random key */ + enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); + if (!enckey) { + ret = -ENOMEM; + goto error; + } + + ret = big_key_gen_enckey(enckey); + if (ret) + goto err_enckey; + + /* encrypt aligned data */ + ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey); + if (ret) + goto err_enckey; + + /* save aligned data to file */ + file = shmem_kernel_file_setup("", enclen, 0, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto error; + goto err_enckey; } - written = kernel_write(file, prep->data, prep->datalen, 0); - if (written != datalen) { + written = kernel_write(file, data, enclen, 0); + if (written != enclen) { ret = written; if (written >= 0) ret = -ENOMEM; @@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep) /* Pin the mount and dentry to the key so that we can open it again * later */ + prep->payload.data[big_key_data] = enckey; *path = file->f_path; path_get(path); fput(file); + kfree(data); } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); + if (!data) return -ENOMEM; @@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep) err_fput: fput(file); +err_enckey: + kfree(enckey); error: + kfree(data); return ret; } @@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep) { if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&prep->payload.data[big_key_path]; + path_put(path); - } else { - kfree(prep->payload.data[big_key_data]); } + kfree(prep->payload.data[big_key_data]); } /* @@ -147,15 +245,15 @@ void big_key_destroy(struct key *key) { size_t datalen = (size_t)key->payload.data[big_key_len]; - if (datalen) { + if (datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&key->payload.data[big_key_path]; + path_put(path); path->mnt = NULL; path->dentry = NULL; - } else { - kfree(key->payload.data[big_key_data]); - key->payload.data[big_key_data] = NULL; } + kfree(key->payload.data[big_key_data]); + key->payload.data[big_key_data] = NULL; } /* @@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) if (datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&key->payload.data[big_key_path]; struct file *file; - loff_t pos; + u8 *data; + u8 *enckey = (u8 *)key->payload.data[big_key_data]; + size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher)); + + data = kmalloc(enclen, GFP_KERNEL); + if (!data) + return -ENOMEM; file = dentry_open(path, O_RDONLY, current_cred()); - if (IS_ERR(file)) - return PTR_ERR(file); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto error; + } - pos = 0; - ret = vfs_read(file, buffer, datalen, &pos); - fput(file); - if (ret >= 0 && ret != datalen) + /* read file to kernel and decrypt */ + ret = kernel_read(file, 0, data, enclen); + if (ret >= 0 && ret != enclen) { ret = -EIO; + goto err_fput; + } + + ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey); + if (ret) + goto err_fput; + + ret = datalen; + + /* copy decrypted data to user */ + if (copy_to_user(buffer, data, datalen) != 0) + ret = -EFAULT; + +err_fput: + fput(file); +error: + kfree(data); } else { ret = datalen; if (copy_to_user(buffer, key->payload.data[big_key_data], @@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) return ret; } +/* + * Register key type + */ static int __init big_key_init(void) { return register_key_type(&key_type_big_key); } + +/* + * Initialize big_key crypto and RNG algorithms + */ +static int __init big_key_crypto_init(void) +{ + int ret = -EINVAL; + + /* init RNG */ + big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0); + if (IS_ERR(big_key_rng)) { + big_key_rng = NULL; + return -EFAULT; + } + + /* seed RNG */ + ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng)); + if (ret) + goto error; + + /* init block cipher */ + big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0); + if (IS_ERR(big_key_blkcipher)) { + big_key_blkcipher = NULL; + ret = -EFAULT; + goto error; + } + + return 0; + +error: + crypto_free_rng(big_key_rng); + big_key_rng = NULL; + return ret; +} + device_initcall(big_key_init); +late_initcall(big_key_crypto_init); diff --git a/security/keys/compat.c b/security/keys/compat.c index 25430a3aa..36c80bf5b 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, case KEYCTL_GET_PERSISTENT: return keyctl_get_persistent(arg2, arg3); + case KEYCTL_DH_COMPUTE: + return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), + arg4, compat_ptr(arg5)); + default: return -EOPNOTSUPP; } diff --git a/security/keys/dh.c b/security/keys/dh.c new file mode 100644 index 000000000..531ed2ec1 --- /dev/null +++ b/security/keys/dh.c @@ -0,0 +1,166 @@ +/* Crypto operations using stored keys + * + * Copyright (c) 2016, Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/mpi.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <keys/user-type.h> +#include "internal.h" + +/* + * Public key or shared secret generation function [RFC2631 sec 2.1.1] + * + * ya = g^xa mod p; + * or + * ZZ = yb^xa mod p; + * + * where xa is the local private key, ya is the local public key, g is + * the generator, p is the prime, yb is the remote public key, and ZZ + * is the shared secret. + * + * Both are the same calculation, so g or yb are the "base" and ya or + * ZZ are the "result". + */ +static int do_dh(MPI result, MPI base, MPI xa, MPI p) +{ + return mpi_powm(result, base, xa, p); +} + +static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi) +{ + struct key *key; + key_ref_t key_ref; + long status; + ssize_t ret; + + key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ); + if (IS_ERR(key_ref)) { + ret = -ENOKEY; + goto error; + } + + key = key_ref_to_ptr(key_ref); + + ret = -EOPNOTSUPP; + if (key->type == &key_type_user) { + down_read(&key->sem); + status = key_validate(key); + if (status == 0) { + const struct user_key_payload *payload; + + payload = user_key_payload(key); + + if (maxlen == 0) { + *mpi = NULL; + ret = payload->datalen; + } else if (payload->datalen <= maxlen) { + *mpi = mpi_read_raw_data(payload->data, + payload->datalen); + if (*mpi) + ret = payload->datalen; + } else { + ret = -EINVAL; + } + } + up_read(&key->sem); + } + + key_put(key); +error: + return ret; +} + +long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + void __user *reserved) +{ + long ret; + MPI base, private, prime, result; + unsigned nbytes; + struct keyctl_dh_params pcopy; + uint8_t *kbuf; + ssize_t keylen; + size_t resultlen; + + if (!params || (!buffer && buflen)) { + ret = -EINVAL; + goto out; + } + if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) { + ret = -EFAULT; + goto out; + } + + if (reserved) { + ret = -EINVAL; + goto out; + } + + keylen = mpi_from_key(pcopy.prime, buflen, &prime); + if (keylen < 0 || !prime) { + /* buflen == 0 may be used to query the required buffer size, + * which is the prime key length. + */ + ret = keylen; + goto out; + } + + /* The result is never longer than the prime */ + resultlen = keylen; + + keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base); + if (keylen < 0 || !base) { + ret = keylen; + goto error1; + } + + keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private); + if (keylen < 0 || !private) { + ret = keylen; + goto error2; + } + + result = mpi_alloc(0); + if (!result) { + ret = -ENOMEM; + goto error3; + } + + kbuf = kmalloc(resultlen, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto error4; + } + + ret = do_dh(result, base, private, prime); + if (ret) + goto error5; + + ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL); + if (ret != 0) + goto error5; + + ret = nbytes; + if (copy_to_user(buffer, kbuf, nbytes) != 0) + ret = -EFAULT; + +error5: + kfree(kbuf); +error4: + mpi_free(result); +error3: + mpi_free(private); +error2: + mpi_free(base); +error1: + mpi_free(prime); +out: + return ret; +} diff --git a/security/keys/internal.h b/security/keys/internal.h index 5105c2c2d..a705a7d92 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -15,6 +15,7 @@ #include <linux/sched.h> #include <linux/key-type.h> #include <linux/task_work.h> +#include <linux/keyctl.h> struct iovec; @@ -257,6 +258,18 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) } #endif +#ifdef CONFIG_KEY_DH_OPERATIONS +extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, + size_t, void __user *); +#else +static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + void __user *reserved) +{ + return -EOPNOTSUPP; +} +#endif + /* * Debugging key validation */ diff --git a/security/keys/key.c b/security/keys/key.c index af7f6821d..346fbf201 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -201,6 +201,7 @@ serial_exists: * @cred: The credentials specifying UID namespace. * @perm: The permissions mask of the new key. * @flags: Flags specifying quota properties. + * @restrict_link: Optional link restriction method for new keyrings. * * Allocate a key of the specified type with the attributes given. The key is * returned in an uninstantiated state and the caller needs to instantiate the @@ -223,7 +224,10 @@ serial_exists: */ struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, - key_perm_t perm, unsigned long flags) + key_perm_t perm, unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + const union key_payload *)) { struct key_user *user = NULL; struct key *key; @@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->uid = uid; key->gid = gid; key->perm = perm; + key->restrict_link = restrict_link; if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; - if (flags & KEY_ALLOC_TRUSTED) - key->flags |= 1 << KEY_FLAG_TRUSTED; if (flags & KEY_ALLOC_BUILT_IN) key->flags |= 1 << KEY_FLAG_BUILTIN; @@ -496,6 +499,12 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { + if (keyring->restrict_link) { + ret = keyring->restrict_link(keyring, key->type, + &prep.payload); + if (ret < 0) + goto error; + } ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret < 0) goto error; @@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key, awaken = 0; ret = -EBUSY; - if (keyring) + if (keyring) { + if (keyring->restrict_link) + return -EPERM; + link_ret = __key_link_begin(keyring, &key->index_key, &edit); + } mutex_lock(&key_construction_mutex); @@ -793,6 +806,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; + int (*restrict_link)(struct key *, + const struct key_type *, + const union key_payload *) = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key_check(keyring); + key_ref = ERR_PTR(-EPERM); + if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION)) + restrict_link = keyring->restrict_link; + key_ref = ERR_PTR(-ENOTDIR); if (keyring->type != &key_type_keyring) goto error_put_type; @@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.data = payload; prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; - prep.trusted = flags & KEY_ALLOC_TRUSTED; prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); @@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - key_ref = ERR_PTR(-EPERM); - if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) - goto error_free_prep; - flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; + if (restrict_link) { + ret = restrict_link(keyring, index_key.type, &prep.payload); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_free_prep; + } + } ret = __key_link_begin(keyring, &index_key, &edit); if (ret < 0) { @@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* allocate a new key */ key = key_alloc(index_key.type, index_key.description, - cred->fsuid, cred->fsgid, cred, perm, flags); + cred->fsuid, cred->fsgid, cred, perm, flags, NULL); if (IS_ERR(key)) { key_ref = ERR_CAST(key); goto error_link_end; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ed73c6c1c..d580ad06b 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_GET_PERSISTENT: return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3); + case KEYCTL_DH_COMPUTE: + return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, + (char __user *) arg3, (size_t) arg4, + (void __user *) arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index f931ccfee..c91e4e0ce 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -491,13 +491,17 @@ static long keyring_read(const struct key *keyring, */ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, - unsigned long flags, struct key *dest) + unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + const union key_payload *), + struct key *dest) { struct key *keyring; int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, cred, perm, flags); + uid, gid, cred, perm, flags, restrict_link); if (!IS_ERR(keyring)) { ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); if (ret < 0) { @@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); +/** + * restrict_link_reject - Give -EPERM to restrict link + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @payload: The payload of the key intended to be added. + * + * Reject the addition of any links to a keyring. It can be overridden by + * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when + * adding a key to a keyring. + * + * This is meant to be passed as the restrict_link parameter to + * keyring_alloc(). + */ +int restrict_link_reject(struct key *keyring, + const struct key_type *type, + const union key_payload *payload) +{ + return -EPERM; +} + /* * By default, we keys found by getting an exact match on their descriptions. */ @@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring, up_write(&keyring->sem); } +/* + * Check addition of keys to restricted keyrings. + */ +static int __key_link_check_restriction(struct key *keyring, struct key *key) +{ + if (!keyring->restrict_link) + return 0; + return keyring->restrict_link(keyring, key->type, &key->payload); +} + /** * key_link - Link a key to a keyring * @keyring: The keyring to make the link in. @@ -1221,14 +1255,12 @@ int key_link(struct key *keyring, struct key *key) key_check(keyring); key_check(key); - if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) && - !test_bit(KEY_FLAG_TRUSTED, &key->flags)) - return -EPERM; - ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret == 0) { kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage)); - ret = __key_link_check_live_key(keyring, key); + ret = __key_link_check_restriction(keyring, key); + if (ret == 0) + ret = __key_link_check_live_key(keyring, key); if (ret == 0) __key_link(key, &edit); __key_link_end(keyring, &key->index_key, edit); diff --git a/security/keys/persistent.c b/security/keys/persistent.c index c9fae5ea8..2ef45b319 100644 --- a/security/keys/persistent.c +++ b/security/keys/persistent.c @@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns) current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(reg)) return PTR_ERR(reg); @@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, uid, INVALID_GID, current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, + KEY_ALLOC_NOT_IN_QUOTA, NULL, ns->persistent_keyring_register); if (IS_ERR(persistent)) return ERR_CAST(persistent); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index e6d501728..40a885239 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -76,7 +76,8 @@ int install_user_keyrings(void) if (IS_ERR(uid_keyring)) { uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(uid_keyring)) { ret = PTR_ERR(uid_keyring); goto error; @@ -92,7 +93,8 @@ int install_user_keyrings(void) session_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(session_keyring)) { ret = PTR_ERR(session_keyring); goto error_release; @@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_tid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_pid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - flags, NULL); + flags, NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); } else { @@ -785,7 +789,7 @@ long join_session_keyring(const char *name) keyring = keyring_alloc( name, old->uid, old->gid, old, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index c7a117c9a..a29e35547 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons, cred = get_current_cred(); keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL); put_cred(cred); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); @@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, key = key_alloc(ctx->index_key.type, ctx->index_key.description, ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, - perm, flags); + perm, flags, NULL); if (IS_ERR(key)) goto alloc_failed; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 4f0f112fe..9db8b4a82 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, authkey = key_alloc(&key_type_request_key_auth, desc, cred->fsuid, cred->fsgid, cred, KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | - KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA); + KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(authkey)) { ret = PTR_ERR(authkey); goto error_alloc; diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 8705d79b2..66b1840b4 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse); */ int user_update(struct key *key, struct key_preparsed_payload *prep) { - struct user_key_payload *upayload, *zap; - size_t datalen = prep->datalen; + struct user_key_payload *zap = NULL; int ret; - ret = -EINVAL; - if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - /* construct a replacement payload */ - ret = -ENOMEM; - upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); - if (!upayload) - goto error; - - upayload->datalen = datalen; - memcpy(upayload->data, prep->data, datalen); - /* check the quota and attach the new data */ - zap = upayload; - - ret = key_payload_reserve(key, datalen); - - if (ret == 0) { - /* attach the new data, displacing the old */ - if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) - zap = key->payload.data[0]; - else - zap = NULL; - rcu_assign_keypointer(key, upayload); - key->expiry = 0; - } + ret = key_payload_reserve(key, prep->datalen); + if (ret < 0) + return ret; + + /* attach the new data, displacing the old */ + key->expiry = prep->expiry; + if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + zap = rcu_dereference_key(key); + rcu_assign_keypointer(key, prep->payload.data[0]); + prep->payload.data[0] = NULL; if (zap) kfree_rcu(zap, rcu); - -error: return ret; } - EXPORT_SYMBOL_GPL(user_update); /* diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig new file mode 100644 index 000000000..dd01aa91e --- /dev/null +++ b/security/loadpin/Kconfig @@ -0,0 +1,19 @@ +config SECURITY_LOADPIN + bool "Pin load of kernel files (modules, fw, etc) to one filesystem" + depends on SECURITY && BLOCK + help + Any files read through the kernel file reading interface + (kernel modules, firmware, kexec images, security policy) + can be pinned to the first filesystem used for loading. When + enabled, any files that come from other filesystems will be + rejected. This is best used on systems without an initrd that + have a root filesystem backed by a read-only device such as + dm-verity or a CDROM. + +config SECURITY_LOADPIN_ENABLED + bool "Enforce LoadPin at boot" + depends on SECURITY_LOADPIN + help + If selected, LoadPin will enforce pinning at boot. If not + selected, it can be enabled at boot with the kernel parameter + "loadpin.enabled=1". diff --git a/security/loadpin/Makefile b/security/loadpin/Makefile new file mode 100644 index 000000000..c2d77f830 --- /dev/null +++ b/security/loadpin/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c new file mode 100644 index 000000000..89a46f10b --- /dev/null +++ b/security/loadpin/loadpin.c @@ -0,0 +1,190 @@ +/* + * Module and Firmware Pinning Security Module + * + * Copyright 2011-2016 Google Inc. + * + * Author: Kees Cook <keescook@chromium.org> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "LoadPin: " fmt + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/fs_struct.h> +#include <linux/lsm_hooks.h> +#include <linux/mount.h> +#include <linux/path.h> +#include <linux/sched.h> /* current */ +#include <linux/string_helpers.h> + +static void report_load(const char *origin, struct file *file, char *operation) +{ + char *cmdline, *pathname; + + pathname = kstrdup_quotable_file(file, GFP_KERNEL); + cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL); + + pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n", + origin, operation, + (pathname && pathname[0] != '<') ? "\"" : "", + pathname, + (pathname && pathname[0] != '<') ? "\"" : "", + task_pid_nr(current), + cmdline ? "\"" : "", cmdline, cmdline ? "\"" : ""); + + kfree(cmdline); + kfree(pathname); +} + +static int enabled = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENABLED); +static struct super_block *pinned_root; +static DEFINE_SPINLOCK(pinned_root_spinlock); + +#ifdef CONFIG_SYSCTL +static int zero; +static int one = 1; + +static struct ctl_path loadpin_sysctl_path[] = { + { .procname = "kernel", }, + { .procname = "loadpin", }, + { } +}; + +static struct ctl_table loadpin_sysctl_table[] = { + { + .procname = "enabled", + .data = &enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + { } +}; + +/* + * This must be called after early kernel init, since then the rootdev + * is available. + */ +static void check_pinning_enforcement(struct super_block *mnt_sb) +{ + bool ro = false; + + /* + * If load pinning is not enforced via a read-only block + * device, allow sysctl to change modes for testing. + */ + if (mnt_sb->s_bdev) { + ro = bdev_read_only(mnt_sb->s_bdev); + pr_info("dev(%u,%u): %s\n", + MAJOR(mnt_sb->s_bdev->bd_dev), + MINOR(mnt_sb->s_bdev->bd_dev), + ro ? "read-only" : "writable"); + } else + pr_info("mnt_sb lacks block device, treating as: writable\n"); + + if (!ro) { + if (!register_sysctl_paths(loadpin_sysctl_path, + loadpin_sysctl_table)) + pr_notice("sysctl registration failed!\n"); + else + pr_info("load pinning can be disabled.\n"); + } else + pr_info("load pinning engaged.\n"); +} +#else +static void check_pinning_enforcement(struct super_block *mnt_sb) +{ + pr_info("load pinning engaged.\n"); +} +#endif + +static void loadpin_sb_free_security(struct super_block *mnt_sb) +{ + /* + * When unmounting the filesystem we were using for load + * pinning, we acknowledge the superblock release, but make sure + * no other modules or firmware can be loaded. + */ + if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) { + pinned_root = ERR_PTR(-EIO); + pr_info("umount pinned fs: refusing further loads\n"); + } +} + +static int loadpin_read_file(struct file *file, enum kernel_read_file_id id) +{ + struct super_block *load_root; + const char *origin = kernel_read_file_id_str(id); + + /* This handles the older init_module API that has a NULL file. */ + if (!file) { + if (!enabled) { + report_load(origin, NULL, "old-api-pinning-ignored"); + return 0; + } + + report_load(origin, NULL, "old-api-denied"); + return -EPERM; + } + + load_root = file->f_path.mnt->mnt_sb; + + /* First loaded module/firmware defines the root for all others. */ + spin_lock(&pinned_root_spinlock); + /* + * pinned_root is only NULL at startup. Otherwise, it is either + * a valid reference, or an ERR_PTR. + */ + if (!pinned_root) { + pinned_root = load_root; + /* + * Unlock now since it's only pinned_root we care about. + * In the worst case, we will (correctly) report pinning + * failures before we have announced that pinning is + * enabled. This would be purely cosmetic. + */ + spin_unlock(&pinned_root_spinlock); + check_pinning_enforcement(pinned_root); + report_load(origin, file, "pinned"); + } else { + spin_unlock(&pinned_root_spinlock); + } + + if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) { + if (unlikely(!enabled)) { + report_load(origin, file, "pinning-ignored"); + return 0; + } + + report_load(origin, file, "denied"); + return -EPERM; + } + + return 0; +} + +static struct security_hook_list loadpin_hooks[] = { + LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security), + LSM_HOOK_INIT(kernel_read_file, loadpin_read_file), +}; + +void __init loadpin_add_hooks(void) +{ + pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis"); + security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks)); +} + +/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ +module_param(enabled, int, 0); +MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)"); diff --git a/security/security.c b/security/security.c index 593879b2a..da7fe2c28 100644 --- a/security/security.c +++ b/security/security.c @@ -60,6 +60,7 @@ int __init security_init(void) */ capability_add_hooks(); yama_add_hooks(); + loadpin_add_hooks(); /* * Load all the remaining security modules. @@ -208,7 +209,7 @@ int security_syslog(int type) return call_int_hook(syslog, 0, type); } -int security_settime(const struct timespec *ts, const struct timezone *tz) +int security_settime64(const struct timespec64 *ts, const struct timezone *tz) { return call_int_hook(settime, 0, ts, tz); } @@ -302,7 +303,7 @@ int security_sb_statfs(struct dentry *dentry) return call_int_hook(sb_statfs, 0, dentry); } -int security_sb_mount(const char *dev_name, struct path *path, +int security_sb_mount(const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data) { return call_int_hook(sb_mount, 0, dev_name, path, type, flags, data); @@ -313,7 +314,7 @@ int security_sb_umount(struct vfsmount *mnt, int flags) return call_int_hook(sb_umount, 0, mnt, flags); } -int security_sb_pivotroot(struct path *old_path, struct path *new_path) +int security_sb_pivotroot(const struct path *old_path, const struct path *new_path) { return call_int_hook(sb_pivotroot, 0, old_path, new_path); } @@ -410,7 +411,7 @@ int security_old_inode_init_security(struct inode *inode, struct inode *dir, EXPORT_SYMBOL(security_old_inode_init_security); #ifdef CONFIG_SECURITY_PATH -int security_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, +int security_path_mknod(const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev) { if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) @@ -419,7 +420,7 @@ int security_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, } EXPORT_SYMBOL(security_path_mknod); -int security_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) +int security_path_mkdir(const struct path *dir, struct dentry *dentry, umode_t mode) { if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) return 0; @@ -427,7 +428,7 @@ int security_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) } EXPORT_SYMBOL(security_path_mkdir); -int security_path_rmdir(struct path *dir, struct dentry *dentry) +int security_path_rmdir(const struct path *dir, struct dentry *dentry) { if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) return 0; @@ -435,7 +436,7 @@ int security_path_rmdir(struct path *dir, struct dentry *dentry) } EXPORT_SYMBOL_GPL(security_path_rmdir); -int security_path_unlink(struct path *dir, struct dentry *dentry) +int security_path_unlink(const struct path *dir, struct dentry *dentry) { if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) return 0; @@ -443,7 +444,7 @@ int security_path_unlink(struct path *dir, struct dentry *dentry) } EXPORT_SYMBOL(security_path_unlink); -int security_path_symlink(struct path *dir, struct dentry *dentry, +int security_path_symlink(const struct path *dir, struct dentry *dentry, const char *old_name) { if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) @@ -452,7 +453,7 @@ int security_path_symlink(struct path *dir, struct dentry *dentry, } EXPORT_SYMBOL_GPL(security_path_symlink); -int security_path_link(struct dentry *old_dentry, struct path *new_dir, +int security_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) { if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)))) @@ -461,8 +462,8 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir, } EXPORT_SYMBOL_GPL(security_path_link); -int security_path_rename(struct path *old_dir, struct dentry *old_dentry, - struct path *new_dir, struct dentry *new_dentry, +int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, + const struct path *new_dir, struct dentry *new_dentry, unsigned int flags) { if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) || @@ -481,7 +482,7 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, } EXPORT_SYMBOL(security_path_rename); -int security_path_truncate(struct path *path) +int security_path_truncate(const struct path *path) { if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) return 0; @@ -489,7 +490,7 @@ int security_path_truncate(struct path *path) } EXPORT_SYMBOL_GPL(security_path_truncate); -int security_path_chmod(struct path *path, umode_t mode) +int security_path_chmod(const struct path *path, umode_t mode) { if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) return 0; @@ -497,7 +498,7 @@ int security_path_chmod(struct path *path, umode_t mode) } EXPORT_SYMBOL_GPL(security_path_chmod); -int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) +int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) return 0; @@ -505,7 +506,7 @@ int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) } EXPORT_SYMBOL_GPL(security_path_chown); -int security_path_chroot(struct path *path) +int security_path_chroot(const struct path *path) { return call_int_hook(path_chroot, 0, path); } @@ -1858,7 +1859,6 @@ struct security_hook_heads security_hook_heads = { .tun_dev_attach = LIST_HEAD_INIT(security_hook_heads.tun_dev_attach), .tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open), - .skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by), #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM .xfrm_policy_alloc_security = diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 912deee3f..a86d537eb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode, might_sleep_if(may_sleep); - if (isec->initialized == LABEL_INVALID) { + if (ss_initialized && isec->initialized != LABEL_INITIALIZED) { if (!may_sleep) return -ECHILD; @@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode) return inode->i_security; } +static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry) +{ + struct inode *inode = d_backing_inode(dentry); + + return inode->i_security; +} + /* * Get the security label of a dentry's backing inode. */ @@ -506,7 +513,8 @@ static int sb_finish_set_opts(struct super_block *sb) rc = -EOPNOTSUPP; goto out; } - rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0); + rc = root_inode->i_op->getxattr(root, root_inode, + XATTR_NAME_SELINUX, NULL, 0); if (rc < 0 && rc != -ENODATA) { if (rc == -EOPNOTSUPP) printk(KERN_WARNING "SELinux: (dev %s, type " @@ -686,7 +694,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, struct superblock_security_struct *sbsec = sb->s_security; const char *name = sb->s_type->name; struct dentry *root = sbsec->sb->s_root; - struct inode_security_struct *root_isec = backing_inode_security(root); + struct inode_security_struct *root_isec; u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; u32 defcontext_sid = 0; char **mount_options = opts->mnt_opts; @@ -729,6 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, && (num_opts == 0)) goto out; + root_isec = backing_inode_security_novalidate(root); + /* * parse the mount options, check if they are valid sids. * also check if someone is trying to mount the same sb more @@ -1316,7 +1326,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry, u32 *sid) { int rc; - struct super_block *sb = dentry->d_inode->i_sb; + struct super_block *sb = dentry->d_sb; char *buffer, *path; buffer = (char *)__get_free_page(GFP_KERNEL); @@ -1412,13 +1422,13 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent goto out_unlock; } context[len] = '\0'; - rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, + rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); if (rc == -ERANGE) { kfree(context); /* Need a larger buffer. Query for the right size. */ - rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, + rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); if (rc < 0) { dput(dentry); @@ -1432,7 +1442,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent goto out_unlock; } context[len] = '\0'; - rc = inode->i_op->getxattr(dentry, + rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); } @@ -1622,7 +1632,7 @@ static int current_has_perm(const struct task_struct *tsk, /* Check whether a task is allowed to use a capability. */ static int cred_has_capability(const struct cred *cred, - int cap, int audit) + int cap, int audit, bool initns) { struct common_audit_data ad; struct av_decision avd; @@ -1636,10 +1646,10 @@ static int cred_has_capability(const struct cred *cred, switch (CAP_TO_INDEX(cap)) { case 0: - sclass = SECCLASS_CAPABILITY; + sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS; break; case 1: - sclass = SECCLASS_CAPABILITY2; + sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS; break; default: printk(KERN_ERR @@ -1781,7 +1791,6 @@ static int selinux_determine_inode_label(struct inode *dir, u32 *_new_isid) { const struct superblock_security_struct *sbsec = dir->i_sb->s_security; - const struct inode_security_struct *dsec = inode_security(dir); const struct task_security_struct *tsec = current_security(); if ((sbsec->flags & SE_SBINITIALIZED) && @@ -1791,6 +1800,7 @@ static int selinux_determine_inode_label(struct inode *dir, tsec->create_sid) { *_new_isid = tsec->create_sid; } else { + const struct inode_security_struct *dsec = inode_security(dir); return security_transition_sid(tsec->sid, dsec->sid, tclass, name, _new_isid); } @@ -2075,7 +2085,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, u32 sid = task_sid(to); struct file_security_struct *fsec = file->f_security; struct dentry *dentry = file->f_path.dentry; - struct inode_security_struct *isec = backing_inode_security(dentry); + struct inode_security_struct *isec; struct common_audit_data ad; int rc; @@ -2094,6 +2104,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; + isec = backing_inode_security(dentry); return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), &ad); } @@ -2142,7 +2153,7 @@ static int selinux_capset(struct cred *new, const struct cred *old, static int selinux_capable(const struct cred *cred, struct user_namespace *ns, int cap, int audit) { - return cred_has_capability(cred, cap, audit); + return cred_has_capability(cred, cap, audit, ns == &init_user_ns); } static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) @@ -2220,7 +2231,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) int rc, cap_sys_admin = 0; rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN, - SECURITY_CAP_NOAUDIT); + SECURITY_CAP_NOAUDIT, true); if (rc == 0) cap_sys_admin = 1; @@ -2229,6 +2240,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) /* binprm security operations */ +static u32 ptrace_parent_sid(struct task_struct *task) +{ + u32 sid = 0; + struct task_struct *tracer; + + rcu_read_lock(); + tracer = ptrace_parent(task); + if (tracer) + sid = task_sid(tracer); + rcu_read_unlock(); + + return sid; +} + static int check_nnp_nosuid(const struct linux_binprm *bprm, const struct task_security_struct *old_tsec, const struct task_security_struct *new_tsec) @@ -2350,18 +2375,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) * changes its SID has the appropriate permit */ if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { - struct task_struct *tracer; - struct task_security_struct *sec; - u32 ptsid = 0; - - rcu_read_lock(); - tracer = ptrace_parent(current); - if (likely(tracer != NULL)) { - sec = __task_cred(tracer)->security; - ptsid = sec->sid; - } - rcu_read_unlock(); - + u32 ptsid = ptrace_parent_sid(current); if (ptsid != 0) { rc = avc_has_perm(ptsid, new_tsec->sid, SECCLASS_PROCESS, @@ -2760,7 +2774,7 @@ static int selinux_sb_statfs(struct dentry *dentry) } static int selinux_mount(const char *dev_name, - struct path *path, + const struct path *path, const char *type, unsigned long flags, void *data) @@ -3045,7 +3059,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = backing_inode_security(dentry); + struct inode_security_struct *isec; struct superblock_security_struct *sbsec; struct common_audit_data ad; u32 newsid, sid = current_sid(); @@ -3064,6 +3078,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; + isec = backing_inode_security(dentry); rc = avc_has_perm(sid, isec->sid, isec->sclass, FILE__RELABELFROM, &ad); if (rc) @@ -3122,7 +3137,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = backing_inode_security(dentry); + struct inode_security_struct *isec; u32 newsid; int rc; @@ -3139,6 +3154,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, return; } + isec = backing_inode_security(dentry); isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = LABEL_INITIALIZED; @@ -3180,7 +3196,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void u32 size; int error; char *context = NULL; - struct inode_security_struct *isec = inode_security(inode); + struct inode_security_struct *isec; if (strcmp(name, XATTR_SELINUX_SUFFIX)) return -EOPNOTSUPP; @@ -3198,7 +3214,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void SECURITY_CAP_NOAUDIT); if (!error) error = cred_has_capability(current_cred(), CAP_MAC_ADMIN, - SECURITY_CAP_NOAUDIT); + SECURITY_CAP_NOAUDIT, true); + isec = inode_security(inode); if (!error) error = security_sid_to_context_force(isec->sid, &context, &size); @@ -3219,7 +3236,7 @@ out_nofree: static int selinux_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - struct inode_security_struct *isec = inode_security(inode); + struct inode_security_struct *isec = inode_security_novalidate(inode); u32 newsid; int rc; @@ -3308,7 +3325,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, struct common_audit_data ad; struct file_security_struct *fsec = file->f_security; struct inode *inode = file_inode(file); - struct inode_security_struct *isec = inode_security(inode); + struct inode_security_struct *isec; struct lsm_ioctlop_audit ioctl; u32 ssid = cred_sid(cred); int rc; @@ -3332,6 +3349,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, if (unlikely(IS_PRIVATE(inode))) return 0; + isec = inode_security(inode); rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, requested, driver, xperm, &ad); out: @@ -3373,7 +3391,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, case KDSKBENT: case KDSKBSENT: error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG, - SECURITY_CAP_AUDIT); + SECURITY_CAP_AUDIT, true); break; /* default case assumes that the command will go @@ -3462,8 +3480,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, vma->vm_end <= vma->vm_mm->brk) { rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP); } else if (!vma->vm_file && - vma->vm_start <= vma->vm_mm->start_stack && - vma->vm_end >= vma->vm_mm->start_stack) { + ((vma->vm_start <= vma->vm_mm->start_stack && + vma->vm_end >= vma->vm_mm->start_stack) || + vma_is_stack_for_task(vma, current))) { rc = current_has_perm(current, PROCESS__EXECSTACK); } else if (vma->vm_file && vma->anon_vma) { /* @@ -3719,6 +3738,52 @@ static int selinux_kernel_module_request(char *kmod_name) SYSTEM__MODULE_REQUEST, &ad); } +static int selinux_kernel_module_from_file(struct file *file) +{ + struct common_audit_data ad; + struct inode_security_struct *isec; + struct file_security_struct *fsec; + u32 sid = current_sid(); + int rc; + + /* init_module */ + if (file == NULL) + return avc_has_perm(sid, sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, NULL); + + /* finit_module */ + + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; + + fsec = file->f_security; + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); + if (rc) + return rc; + } + + isec = inode_security(file_inode(file)); + return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, &ad); +} + +static int selinux_kernel_read_file(struct file *file, + enum kernel_read_file_id id) +{ + int rc = 0; + + switch (id) { + case READING_MODULE: + rc = selinux_kernel_module_from_file(file); + break; + default: + break; + } + + return rc; +} + static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { return current_has_perm(p, PROCESS__SETPGID); @@ -4598,6 +4663,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * { u32 peer_secid = SECSID_NULL; u16 family; + struct inode_security_struct *isec; if (skb && skb->protocol == htons(ETH_P_IP)) family = PF_INET; @@ -4608,9 +4674,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * else goto out; - if (sock && family == PF_UNIX) - selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid); - else if (skb) + if (sock && family == PF_UNIX) { + isec = inode_security_novalidate(SOCK_INODE(sock)); + peer_secid = isec->sid; + } else if (skb) selinux_skb_peerlbl_sid(skb, family, &peer_secid); out: @@ -5675,7 +5742,6 @@ static int selinux_setprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_security_struct *tsec; - struct task_struct *tracer; struct cred *new; u32 sid = 0, ptsid; int error; @@ -5782,14 +5848,8 @@ static int selinux_setprocattr(struct task_struct *p, /* Check for ptracing, and update the task SID if ok. Otherwise, leave SID unchanged and fail. */ - ptsid = 0; - rcu_read_lock(); - tracer = ptrace_parent(p); - if (tracer) - ptsid = task_sid(tracer); - rcu_read_unlock(); - - if (tracer) { + ptsid = ptrace_parent_sid(p); + if (ptsid != 0) { error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); if (error) @@ -6020,6 +6080,7 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as), LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as), LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request), + LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file), LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), LSM_HOOK_INIT(task_getsid, selinux_task_getsid), diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index ef83c4b85..1f1f4b2f6 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -12,6 +12,18 @@ #define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \ "write", "associate", "unix_read", "unix_write" +#define COMMON_CAP_PERMS "chown", "dac_override", "dac_read_search", \ + "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \ + "linux_immutable", "net_bind_service", "net_broadcast", \ + "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \ + "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \ + "sys_boot", "sys_nice", "sys_resource", "sys_time", \ + "sys_tty_config", "mknod", "lease", "audit_write", \ + "audit_control", "setfcap" + +#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \ + "wake_alarm", "block_suspend", "audit_read" + /* * Note: The name for any socket class should be suffixed by "socket", * and doesn't contain more than one substr of "socket". @@ -32,16 +44,9 @@ struct security_class_mapping secclass_map[] = { "setsockcreate", NULL } }, { "system", { "ipc_info", "syslog_read", "syslog_mod", - "syslog_console", "module_request", NULL } }, + "syslog_console", "module_request", "module_load", NULL } }, { "capability", - { "chown", "dac_override", "dac_read_search", - "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", - "linux_immutable", "net_bind_service", "net_broadcast", - "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", - "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", - "sys_boot", "sys_nice", "sys_resource", "sys_time", - "sys_tty_config", "mknod", "lease", "audit_write", - "audit_control", "setfcap", NULL } }, + { COMMON_CAP_PERMS, NULL } }, { "filesystem", { "mount", "remount", "unmount", "getattr", "relabelfrom", "relabelto", "associate", "quotamod", @@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = { { "memprotect", { "mmap_zero", NULL } }, { "peer", { "recv", NULL } }, { "capability2", - { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", - "audit_read", NULL } }, + { COMMON_CAP2_PERMS, NULL } }, { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, "attach_queue", NULL } }, { "binder", { "impersonate", "call", "set_context_mgr", "transfer", NULL } }, + { "cap_userns", + { COMMON_CAP_PERMS, NULL } }, + { "cap2_userns", + { COMMON_CAP2_PERMS, NULL } }, { NULL } }; diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h index 67ce7a8d8..ff4fddca9 100644 --- a/security/selinux/include/conditional.h +++ b/security/selinux/include/conditional.h @@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values); int security_set_bools(int len, int *values); -int security_get_bool_value(int bool); +int security_get_bool_value(int index); #endif diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index a2ae05414..c21e13546 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -38,9 +38,8 @@ struct task_security_struct { }; enum label_initialized { - LABEL_MISSING, /* not initialized */ - LABEL_INITIALIZED, /* inizialized */ - LABEL_INVALID /* invalid */ + LABEL_INVALID, /* invalid or not initialized */ + LABEL_INITIALIZED /* initialized */ }; struct inode_security_struct { diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 8495b9368..2ca9cde93 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -76,6 +76,8 @@ static struct nlmsg_perm nlmsg_route_perms[] = { RTM_NEWNSID, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_DELNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, + { RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, }; static struct nlmsg_perm nlmsg_tcpdiag_perms[] = @@ -155,7 +157,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) switch (sclass) { case SECCLASS_NETLINK_ROUTE_SOCKET: /* RTM_MAX always point to RTM_SETxxxx, ie RTM_NEWxxx + 3 */ - BUILD_BUG_ON(RTM_MAX != (RTM_NEWNSID + 3)); + BUILD_BUG_ON(RTM_MAX != (RTM_NEWSTATS + 3)); err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms, sizeof(nlmsg_route_perms)); break; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ebda97333..89df64672 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2696,7 +2696,7 @@ out: return rc; } -int security_get_bool_value(int bool) +int security_get_bool_value(int index) { int rc; int len; @@ -2705,10 +2705,10 @@ int security_get_bool_value(int bool) rc = -EFAULT; len = policydb.p_bools.nprim; - if (bool >= len) + if (index >= len) goto out; - rc = policydb.bool_val_to_struct[bool]->state; + rc = policydb.bool_val_to_struct[index]->state; out: read_unlock(&policy_rwlock); return rc; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 11f79013a..6777295f4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -272,7 +272,7 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip, if (buffer == NULL) return ERR_PTR(-ENOMEM); - rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL); + rc = ip->i_op->getxattr(dp, ip, name, buffer, SMK_LONGLABEL); if (rc < 0) skp = ERR_PTR(rc); else if (rc == 0) @@ -1444,7 +1444,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) * XATTR_NAME_SMACKIPOUT */ if (strcmp(name, XATTR_NAME_SMACK) == 0) { - struct super_block *sbp = d_backing_inode(dentry)->i_sb; + struct super_block *sbp = dentry->d_sb; struct superblock_smack *sbsp = sbp->s_security; isp->smk_inode = sbsp->smk_default; @@ -3514,12 +3514,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ if (isp->smk_flags & SMK_INODE_CHANGED) { isp->smk_flags &= ~SMK_INODE_CHANGED; - rc = inode->i_op->setxattr(dp, + rc = inode->i_op->setxattr(dp, inode, XATTR_NAME_SMACKTRANSMUTE, TRANS_TRUE, TRANS_TRUE_SIZE, 0); } else { - rc = inode->i_op->getxattr(dp, + rc = inode->i_op->getxattr(dp, inode, XATTR_NAME_SMACKTRANSMUTE, trattr, TRANS_TRUE_SIZE); if (rc >= 0 && strncmp(trattr, TRANS_TRUE, diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f9c9fb1d5..361e7a284 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -957,7 +957,7 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name); const struct tomoyo_path_info *tomoyo_path_matches_group (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group); int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, - struct path *path, const int flag); + const struct path *path, const int flag); void tomoyo_close_control(struct tomoyo_io_buffer *head); int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); int tomoyo_execute_permission(struct tomoyo_request_info *r, @@ -968,15 +968,15 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, int tomoyo_init_request_info(struct tomoyo_request_info *r, struct tomoyo_domain_info *domain, const u8 index); -int tomoyo_mkdev_perm(const u8 operation, struct path *path, +int tomoyo_mkdev_perm(const u8 operation, const struct path *path, const unsigned int mode, unsigned int dev); -int tomoyo_mount_permission(const char *dev_name, struct path *path, +int tomoyo_mount_permission(const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data_page); int tomoyo_open_control(const u8 type, struct file *file); -int tomoyo_path2_perm(const u8 operation, struct path *path1, - struct path *path2); -int tomoyo_path_number_perm(const u8 operation, struct path *path, +int tomoyo_path2_perm(const u8 operation, const struct path *path1, + const struct path *path2); +int tomoyo_path_number_perm(const u8 operation, const struct path *path, unsigned long number); int tomoyo_path_perm(const u8 operation, const struct path *path, const char *target); diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 2367b100c..7041a5800 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -687,7 +687,7 @@ static int tomoyo_update_path_number_acl(const u8 perm, * * Returns 0 on success, negative value otherwise. */ -int tomoyo_path_number_perm(const u8 type, struct path *path, +int tomoyo_path_number_perm(const u8 type, const struct path *path, unsigned long number) { struct tomoyo_request_info r; @@ -733,7 +733,7 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, * Returns 0 on success, negative value otherwise. */ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, - struct path *path, const int flag) + const struct path *path, const int flag) { const u8 acc_mode = ACC_MODE(flag); int error = 0; @@ -838,7 +838,7 @@ int tomoyo_path_perm(const u8 operation, const struct path *path, const char *ta * * Returns 0 on success, negative value otherwise. */ -int tomoyo_mkdev_perm(const u8 operation, struct path *path, +int tomoyo_mkdev_perm(const u8 operation, const struct path *path, const unsigned int mode, unsigned int dev) { struct tomoyo_request_info r; @@ -882,8 +882,8 @@ int tomoyo_mkdev_perm(const u8 operation, struct path *path, * * Returns 0 on success, negative value otherwise. */ -int tomoyo_path2_perm(const u8 operation, struct path *path1, - struct path *path2) +int tomoyo_path2_perm(const u8 operation, const struct path *path1, + const struct path *path2) { int error = -ENOMEM; struct tomoyo_path_info buf1; diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 390c64601..14b53fb2a 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -73,7 +73,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, */ static int tomoyo_mount_acl(struct tomoyo_request_info *r, const char *dev_name, - struct path *dir, const char *type, + const struct path *dir, const char *type, unsigned long flags) { struct tomoyo_obj_info obj = { }; @@ -184,7 +184,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, * * Returns 0 on success, negative value otherwise. */ -int tomoyo_mount_permission(const char *dev_name, struct path *path, +int tomoyo_mount_permission(const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data_page) { diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index cbf3df422..75c998700 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -150,7 +150,7 @@ static int tomoyo_inode_getattr(const struct path *path) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_truncate(struct path *path) +static int tomoyo_path_truncate(const struct path *path) { return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL); } @@ -163,7 +163,7 @@ static int tomoyo_path_truncate(struct path *path) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry) +static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry) { struct path path = { parent->mnt, dentry }; return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL); @@ -178,7 +178,7 @@ static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, +static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry, umode_t mode) { struct path path = { parent->mnt, dentry }; @@ -194,7 +194,7 @@ static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) +static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry) { struct path path = { parent->mnt, dentry }; return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL); @@ -209,7 +209,7 @@ static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry, +static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry, const char *old_name) { struct path path = { parent->mnt, dentry }; @@ -226,7 +226,7 @@ static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry, * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, +static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry, umode_t mode, unsigned int dev) { struct path path = { parent->mnt, dentry }; @@ -265,7 +265,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, +static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) { struct path path1 = { new_dir->mnt, old_dentry }; @@ -283,9 +283,9 @@ static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_rename(struct path *old_parent, +static int tomoyo_path_rename(const struct path *old_parent, struct dentry *old_dentry, - struct path *new_parent, + const struct path *new_parent, struct dentry *new_dentry) { struct path path1 = { old_parent->mnt, old_dentry }; @@ -351,7 +351,7 @@ static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_chmod(struct path *path, umode_t mode) +static int tomoyo_path_chmod(const struct path *path, umode_t mode) { return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, path, mode & S_IALLUGO); @@ -366,7 +366,7 @@ static int tomoyo_path_chmod(struct path *path, umode_t mode) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_chown(struct path *path, kuid_t uid, kgid_t gid) +static int tomoyo_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { int error = 0; if (uid_valid(uid)) @@ -385,7 +385,7 @@ static int tomoyo_path_chown(struct path *path, kuid_t uid, kgid_t gid) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_path_chroot(struct path *path) +static int tomoyo_path_chroot(const struct path *path) { return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL); } @@ -401,7 +401,7 @@ static int tomoyo_path_chroot(struct path *path) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_sb_mount(const char *dev_name, struct path *path, +static int tomoyo_sb_mount(const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data) { return tomoyo_mount_permission(dev_name, path, type, flags, data); @@ -429,7 +429,7 @@ static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path) +static int tomoyo_sb_pivotroot(const struct path *old_path, const struct path *new_path) { return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path); } diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index cb6ed1081..0309f2111 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -18,6 +18,10 @@ #include <linux/prctl.h> #include <linux/ratelimit.h> #include <linux/workqueue.h> +#include <linux/string_helpers.h> +#include <linux/task_work.h> +#include <linux/sched.h> +#include <linux/spinlock.h> #define YAMA_SCOPE_DISABLED 0 #define YAMA_SCOPE_RELATIONAL 1 @@ -41,6 +45,73 @@ static DEFINE_SPINLOCK(ptracer_relations_lock); static void yama_relation_cleanup(struct work_struct *work); static DECLARE_WORK(yama_relation_work, yama_relation_cleanup); +struct access_report_info { + struct callback_head work; + const char *access; + struct task_struct *target; + struct task_struct *agent; +}; + +static void __report_access(struct callback_head *work) +{ + struct access_report_info *info = + container_of(work, struct access_report_info, work); + char *target_cmd, *agent_cmd; + + target_cmd = kstrdup_quotable_cmdline(info->target, GFP_KERNEL); + agent_cmd = kstrdup_quotable_cmdline(info->agent, GFP_KERNEL); + + pr_notice_ratelimited( + "ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n", + info->access, target_cmd, info->target->pid, agent_cmd, + info->agent->pid); + + kfree(agent_cmd); + kfree(target_cmd); + + put_task_struct(info->agent); + put_task_struct(info->target); + kfree(info); +} + +/* defers execution because cmdline access can sleep */ +static void report_access(const char *access, struct task_struct *target, + struct task_struct *agent) +{ + struct access_report_info *info; + char agent_comm[sizeof(agent->comm)]; + + assert_spin_locked(&target->alloc_lock); /* for target->comm */ + + if (current->flags & PF_KTHREAD) { + /* I don't think kthreads call task_work_run() before exiting. + * Imagine angry ranting about procfs here. + */ + pr_notice_ratelimited( + "ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n", + access, target->comm, target->pid, + get_task_comm(agent_comm, agent), agent->pid); + return; + } + + info = kmalloc(sizeof(*info), GFP_ATOMIC); + if (!info) + return; + init_task_work(&info->work, __report_access); + get_task_struct(target); + get_task_struct(agent); + info->access = access; + info->target = target; + info->agent = agent; + if (task_work_add(current, &info->work, true) == 0) + return; /* success */ + + WARN(1, "report_access called from exiting task"); + put_task_struct(target); + put_task_struct(agent); + kfree(info); +} + /** * yama_relation_cleanup - remove invalid entries from the relation list * @@ -307,11 +378,8 @@ static int yama_ptrace_access_check(struct task_struct *child, } } - if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) { - printk_ratelimited(KERN_NOTICE - "ptrace of pid %d was attempted by: %s (pid %d)\n", - child->pid, current->comm, current->pid); - } + if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) + report_access("attach", child, current); return rc; } @@ -338,9 +406,9 @@ int yama_ptrace_traceme(struct task_struct *parent) } if (rc) { - printk_ratelimited(KERN_NOTICE - "ptraceme of pid %d was attempted by: %s (pid %d)\n", - current->pid, parent->comm, parent->pid); + task_lock(current); + report_access("traceme", current, parent); + task_unlock(current); } return rc; |