diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/commoncap.c | 10 | ||||
-rw-r--r-- | security/integrity/evm/evm_crypto.c | 3 | ||||
-rw-r--r-- | security/keys/big_key.c | 11 | ||||
-rw-r--r-- | security/keys/dh.c | 35 | ||||
-rw-r--r-- | security/keys/proc.c | 34 | ||||
-rw-r--r-- | security/security.c | 6 | ||||
-rw-r--r-- | security/selinux/hooks.c | 90 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 18 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 2 | ||||
-rw-r--r-- | security/smack/smack_lsm.c | 22 |
10 files changed, 136 insertions, 95 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index 48620c93d697..f4c33abd9959 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -449,6 +449,8 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, magic |= VFS_CAP_FLAGS_EFFECTIVE; memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32); cap->magic_etc = cpu_to_le32(magic); + } else { + size = -ENOMEM; } } kfree(tmpbuf); @@ -917,6 +919,8 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) int cap_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { + struct user_namespace *user_ns = dentry->d_sb->s_user_ns; + /* Ignore non-security xattrs */ if (strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) != 0) @@ -929,7 +933,7 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name, if (strcmp(name, XATTR_NAME_CAPS) == 0) return 0; - if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; return 0; } @@ -947,6 +951,8 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name, */ int cap_inode_removexattr(struct dentry *dentry, const char *name) { + struct user_namespace *user_ns = dentry->d_sb->s_user_ns; + /* Ignore non-security xattrs */ if (strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) != 0) @@ -962,7 +968,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name) return 0; } - if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; return 0; } diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index a46fba322340..facf9cdd577d 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -200,7 +200,8 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, int size; bool ima_present = false; - if (!(inode->i_opflags & IOP_XATTR)) + if (!(inode->i_opflags & IOP_XATTR) || + inode->i_sb->s_user_ns != &init_user_ns) return -EOPNOTSUPP; desc = init_desc(type); diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 933623784ccd..2806e70d7f8f 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -22,6 +22,7 @@ #include <keys/user-type.h> #include <keys/big_key-type.h> #include <crypto/aead.h> +#include <crypto/gcm.h> struct big_key_buf { unsigned int nr_pages; @@ -85,6 +86,7 @@ struct key_type key_type_big_key = { * Crypto names for big_key data authenticated encryption */ static const char big_key_alg_name[] = "gcm(aes)"; +#define BIG_KEY_IV_SIZE GCM_AES_IV_SIZE /* * Crypto algorithms for big_key data authenticated encryption @@ -109,7 +111,7 @@ static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t dat * an .update function, so there's no chance we'll wind up reusing the * key to encrypt updated data. Simply put: one key, one encryption. */ - u8 zero_nonce[crypto_aead_ivsize(big_key_aead)]; + u8 zero_nonce[BIG_KEY_IV_SIZE]; aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL); if (!aead_req) @@ -425,6 +427,13 @@ static int __init big_key_init(void) pr_err("Can't alloc crypto: %d\n", ret); return ret; } + + if (unlikely(crypto_aead_ivsize(big_key_aead) != BIG_KEY_IV_SIZE)) { + WARN(1, "big key algorithm changed?"); + ret = -EINVAL; + goto free_aead; + } + ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE); if (ret < 0) { pr_err("Can't set crypto auth tag len: %d\n", ret); diff --git a/security/keys/dh.c b/security/keys/dh.c index d1ea9f325f94..f7403821db7f 100644 --- a/security/keys/dh.c +++ b/security/keys/dh.c @@ -162,8 +162,8 @@ static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, goto err; if (zlen && h) { - u8 tmpbuffer[h]; - size_t chunk = min_t(size_t, zlen, h); + u8 tmpbuffer[32]; + size_t chunk = min_t(size_t, zlen, sizeof(tmpbuffer)); memset(tmpbuffer, 0, chunk); do { @@ -173,7 +173,7 @@ static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, goto err; zlen -= chunk; - chunk = min_t(size_t, zlen, h); + chunk = min_t(size_t, zlen, sizeof(tmpbuffer)); } while (zlen); } @@ -183,24 +183,13 @@ static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, goto err; } - if (dlen < h) { - u8 tmpbuffer[h]; - - err = crypto_shash_final(desc, tmpbuffer); - if (err) - goto err; - memcpy(dst, tmpbuffer, dlen); - memzero_explicit(tmpbuffer, h); - return 0; - } else { - err = crypto_shash_final(desc, dst); - if (err) - goto err; + err = crypto_shash_final(desc, dst); + if (err) + goto err; - dlen -= h; - dst += h; - counter = cpu_to_be32(be32_to_cpu(counter) + 1); - } + dlen -= h; + dst += h; + counter = cpu_to_be32(be32_to_cpu(counter) + 1); } return 0; @@ -216,14 +205,16 @@ static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, { uint8_t *outbuf = NULL; int ret; + size_t outbuf_len = round_up(buflen, + crypto_shash_digestsize(sdesc->shash.tfm)); - outbuf = kmalloc(buflen, GFP_KERNEL); + outbuf = kmalloc(outbuf_len, GFP_KERNEL); if (!outbuf) { ret = -ENOMEM; goto err; } - ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen, lzero); + ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, outbuf_len, lzero); if (ret) goto err; diff --git a/security/keys/proc.c b/security/keys/proc.c index fbc4af5c6c9f..5af2934965d8 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -18,7 +18,6 @@ #include <asm/errno.h> #include "internal.h" -static int proc_keys_open(struct inode *inode, struct file *file); static void *proc_keys_start(struct seq_file *p, loff_t *_pos); static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); static void proc_keys_stop(struct seq_file *p, void *v); @@ -31,14 +30,6 @@ static const struct seq_operations proc_keys_ops = { .show = proc_keys_show, }; -static const struct file_operations proc_keys_fops = { - .open = proc_keys_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static int proc_key_users_open(struct inode *inode, struct file *file); static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); static void proc_key_users_stop(struct seq_file *p, void *v); @@ -51,13 +42,6 @@ static const struct seq_operations proc_key_users_ops = { .show = proc_key_users_show, }; -static const struct file_operations proc_key_users_fops = { - .open = proc_key_users_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - /* * Declare the /proc files. */ @@ -65,11 +49,11 @@ static int __init key_proc_init(void) { struct proc_dir_entry *p; - p = proc_create("keys", 0, NULL, &proc_keys_fops); + p = proc_create_seq("keys", 0, NULL, &proc_keys_ops); if (!p) panic("Cannot create /proc/keys\n"); - p = proc_create("key-users", 0, NULL, &proc_key_users_fops); + p = proc_create_seq("key-users", 0, NULL, &proc_key_users_ops); if (!p) panic("Cannot create /proc/key-users\n"); @@ -96,11 +80,6 @@ static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n) return n; } -static int proc_keys_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &proc_keys_ops); -} - static struct key *find_ge_key(struct seq_file *p, key_serial_t id) { struct user_namespace *user_ns = seq_user_ns(p); @@ -293,15 +272,6 @@ static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_ return __key_user_next(user_ns, n); } -/* - * Implement "/proc/key-users" to provides a list of the key users and their - * quotas. - */ -static int proc_key_users_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &proc_key_users_ops); -} - static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) __acquires(key_user_lock) { diff --git a/security/security.c b/security/security.c index 7bc2fde023a7..68f46d849abe 100644 --- a/security/security.c +++ b/security/security.c @@ -1358,6 +1358,12 @@ int security_socket_post_create(struct socket *sock, int family, protocol, kern); } +int security_socket_socketpair(struct socket *socka, struct socket *sockb) +{ + return call_int_hook(socket_socketpair, 0, socka, sockb); +} +EXPORT_SYMBOL(security_socket_socketpair); + int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { return call_int_hook(socket_bind, 0, sock, address, addrlen); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 713c1648014f..25905d09500e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -274,11 +274,10 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent * Try reloading inode security labels that have been marked as invalid. The * @may_sleep parameter indicates when sleeping and thus reloading labels is * allowed; when set to false, returns -ECHILD when the label is - * invalid. The @opt_dentry parameter should be set to a dentry of the inode; - * when no dentry is available, set it to NULL instead. + * invalid. The @dentry parameter should be set to a dentry of the inode. */ static int __inode_security_revalidate(struct inode *inode, - struct dentry *opt_dentry, + struct dentry *dentry, bool may_sleep) { struct inode_security_struct *isec = inode->i_security; @@ -295,7 +294,7 @@ static int __inode_security_revalidate(struct inode *inode, * @opt_dentry is NULL and no dentry for this inode can be * found; in that case, continue using the old label. */ - inode_doinit_with_dentry(inode, opt_dentry); + inode_doinit_with_dentry(inode, dentry); } return 0; } @@ -1568,8 +1567,15 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent /* Called from d_instantiate or d_splice_alias. */ dentry = dget(opt_dentry); } else { - /* Called from selinux_complete_init, try to find a dentry. */ + /* + * Called from selinux_complete_init, try to find a dentry. + * Some filesystems really want a connected one, so try + * that first. We could split SECURITY_FS_USE_XATTR in + * two, depending upon that... + */ dentry = d_find_alias(inode); + if (!dentry) + dentry = d_find_any_alias(inode); } if (!dentry) { /* @@ -1674,14 +1680,19 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { /* We must have a dentry to determine the label on * procfs inodes */ - if (opt_dentry) + if (opt_dentry) { /* Called from d_instantiate or * d_splice_alias. */ dentry = dget(opt_dentry); - else + } else { /* Called from selinux_complete_init, try to - * find a dentry. */ + * find a dentry. Some filesystems really want + * a connected one, so try that first. + */ dentry = d_find_alias(inode); + if (!dentry) + dentry = d_find_any_alias(inode); + } /* * This can be hit on boot when a file is accessed * before the policy is loaded. When we load policy we @@ -4570,6 +4581,18 @@ static int selinux_socket_post_create(struct socket *sock, int family, return err; } +static int selinux_socket_socketpair(struct socket *socka, + struct socket *sockb) +{ + struct sk_security_struct *sksec_a = socka->sk->sk_security; + struct sk_security_struct *sksec_b = sockb->sk->sk_security; + + sksec_a->peer_sid = sksec_b->sid; + sksec_b->peer_sid = sksec_a->sid; + + return 0; +} + /* Range of port numbers used to automatically bind. Need to determine whether we should perform a name_bind permission check between the socket and the port number. */ @@ -4577,6 +4600,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { struct sock *sk = sock->sk; + struct sk_security_struct *sksec = sk->sk_security; u16 family; int err; @@ -4588,11 +4612,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { char *addrp; - struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; + u16 family_sa = address->sa_family; unsigned short snum; u32 sid, node_perm; @@ -4602,11 +4626,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in * need to check address->sa_family as it is possible to have * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. */ - switch (address->sa_family) { + switch (family_sa) { + case AF_UNSPEC: case AF_INET: if (addrlen < sizeof(struct sockaddr_in)) return -EINVAL; addr4 = (struct sockaddr_in *)address; + if (family_sa == AF_UNSPEC) { + /* see __inet_bind(), we only want to allow + * AF_UNSPEC if the address is INADDR_ANY + */ + if (addr4->sin_addr.s_addr != htonl(INADDR_ANY)) + goto err_af; + family_sa = AF_INET; + } snum = ntohs(addr4->sin_port); addrp = (char *)&addr4->sin_addr.s_addr; break; @@ -4618,15 +4651,14 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in addrp = (char *)&addr6->sin6_addr.s6_addr; break; default: - /* Note that SCTP services expect -EINVAL, whereas - * others expect -EAFNOSUPPORT. - */ - if (sksec->sclass == SECCLASS_SCTP_SOCKET) - return -EINVAL; - else - return -EAFNOSUPPORT; + goto err_af; } + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sport = htons(snum); + ad.u.net->family = family_sa; + if (snum) { int low, high; @@ -4638,10 +4670,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in snum, &sid); if (err) goto out; - ad.type = LSM_AUDIT_DATA_NET; - ad.u.net = &net; - ad.u.net->sport = htons(snum); - ad.u.net->family = family; err = avc_has_perm(&selinux_state, sksec->sid, sid, sksec->sclass, @@ -4673,16 +4701,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in break; } - err = sel_netnode_sid(addrp, family, &sid); + err = sel_netnode_sid(addrp, family_sa, &sid); if (err) goto out; - ad.type = LSM_AUDIT_DATA_NET; - ad.u.net = &net; - ad.u.net->sport = htons(snum); - ad.u.net->family = family; - - if (address->sa_family == AF_INET) + if (family_sa == AF_INET) ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; else ad.u.net->v6info.saddr = addr6->sin6_addr; @@ -4695,6 +4718,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in } out: return err; +err_af: + /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + return -EINVAL; + return -EAFNOSUPPORT; } /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) @@ -4772,7 +4800,7 @@ static int selinux_socket_connect_helper(struct socket *sock, ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; ad.u.net->dport = htons(snum); - ad.u.net->family = sk->sk_family; + ad.u.net->family = address->sa_family; err = avc_has_perm(&selinux_state, sksec->sid, sid, sksec->sclass, perm, &ad); if (err) @@ -5273,6 +5301,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, while (walk_size < addrlen) { addr = addr_buf; switch (addr->sa_family) { + case AF_UNSPEC: case AF_INET: len = sizeof(struct sockaddr_in); break; @@ -5280,7 +5309,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, len = sizeof(struct sockaddr_in6); break; default: - return -EAFNOSUPPORT; + return -EINVAL; } err = -EINVAL; @@ -7002,6 +7031,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(socket_create, selinux_socket_create), LSM_HOOK_INIT(socket_post_create, selinux_socket_post_create), + LSM_HOOK_INIT(socket_socketpair, selinux_socket_socketpair), LSM_HOOK_INIT(socket_bind, selinux_socket_bind), LSM_HOOK_INIT(socket_connect, selinux_socket_connect), LSM_HOOK_INIT(socket_listen, selinux_socket_listen), diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 35fd77737c59..f3d374d2ca04 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -168,10 +168,12 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, if (length) goto out; audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, - "enforcing=%d old_enforcing=%d auid=%u ses=%u", + "enforcing=%d old_enforcing=%d auid=%u ses=%u" + " enabled=%d old-enabled=%d lsm=selinux res=1", new_value, old_value, from_kuid(&init_user_ns, audit_get_loginuid(current)), - audit_get_sessionid(current)); + audit_get_sessionid(current), + selinux_enabled, selinux_enabled); enforcing_set(state, new_value); if (new_value) avc_ss_reset(state->avc, 0); @@ -279,6 +281,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, char *page; ssize_t length; int new_value; + int enforcing; if (count >= PAGE_SIZE) return -ENOMEM; @@ -296,13 +299,16 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, goto out; if (new_value) { + enforcing = enforcing_enabled(fsi->state); length = selinux_disable(fsi->state); if (length) goto out; audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, - "selinux=0 auid=%u ses=%u", + "enforcing=%d old_enforcing=%d auid=%u ses=%u" + " enabled=%d old-enabled=%d lsm=selinux res=1", + enforcing, enforcing, from_kuid(&init_user_ns, audit_get_loginuid(current)), - audit_get_sessionid(current)); + audit_get_sessionid(current), 0, 1); } length = count; @@ -453,7 +459,7 @@ out: return ret; } -static int sel_mmap_policy_fault(struct vm_fault *vmf) +static vm_fault_t sel_mmap_policy_fault(struct vm_fault *vmf) { struct policy_load_memory *plm = vmf->vma->vm_file->private_data; unsigned long offset; @@ -576,7 +582,7 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, out1: audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, - "policy loaded auid=%u ses=%u", + "auid=%u ses=%u lsm=selinux res=1", from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); out: diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 0fd8ad955a38..a2d44824121c 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1494,7 +1494,7 @@ static int security_context_to_sid_core(struct selinux_state *state, scontext_len, &context, def_sid); if (rc == -EINVAL && force) { context.str = str; - context.len = scontext_len; + context.len = strlen(str) + 1; str = NULL; } else if (rc) goto out_unlock; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0b414836bebd..dcb976f98df2 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2842,6 +2842,27 @@ static int smack_socket_post_create(struct socket *sock, int family, return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); } +/** + * smack_socket_socketpair - create socket pair + * @socka: one socket + * @sockb: another socket + * + * Cross reference the peer labels for SO_PEERSEC + * + * Returns 0 on success, and error code otherwise + */ +static int smack_socket_socketpair(struct socket *socka, + struct socket *sockb) +{ + struct socket_smack *asp = socka->sk->sk_security; + struct socket_smack *bsp = sockb->sk->sk_security; + + asp->smk_packet = bsp->smk_out; + bsp->smk_packet = asp->smk_out; + + return 0; +} + #ifdef SMACK_IPV6_PORT_LABELING /** * smack_socket_bind - record port binding information. @@ -4724,6 +4745,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(unix_may_send, smack_unix_may_send), LSM_HOOK_INIT(socket_post_create, smack_socket_post_create), + LSM_HOOK_INIT(socket_socketpair, smack_socket_socketpair), #ifdef SMACK_IPV6_PORT_LABELING LSM_HOOK_INIT(socket_bind, smack_socket_bind), #endif |