Keyrings fixes 2013-12-10
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQIVAwUAUqdgihOxKuMESys7AQIishAAjGG3LnEp12fd7oay5//4SEg31ybPqGj7 Xotk9mblBW7cWPbLqk7xyIhxpIj2zM14XatEV2TfBNZV0Lcrzr1M5s87ZRUX0ui+ FHbtfn/M3Or8AvX7W1HZgG2Se7M0Ba6Y2RRXNsEdgqk6KMQuHhPEZ2FZ5vCx6BAD vXzxWIKVNeqMXR7z6xkyqmpztnVQs+ZgzE9+c96QKyhBdtBy4spDmfgJS90m0pjN HhgONpdfgosknj8yu43rWIQvd3UUO5BVntCeic94Fbgh77ZAEi0dD7ifLz/ebcja pfJfPXxYzkCfIrSdAzQF8iYnQ+5rRomvGsMcvtq6mBooah/YmEkmKpKJDTp47wyN IEUeJaxM2Qp2jcUSjEd7lY9o1AK4sj+90cKeVRUd5kzZP1iolkQHR+dGiAwqbn6w MJBn9fCamvhpsZDhl2G/DICprFDvErd9zAlNubivggJmITnPXNWx+K1RDL4fO4qc zLHTGHkxYyACM/7oJfwbH/NyZ1yu53OlE3R2h6TA6ISc7nAed7qzGAZyrSV92Quc pItGaQ/zNa0sbe+nufCx9FOWu8sA3x7qnazLxhVtlPde9nlxcpGo5cagqcyc4hyp /IGK2JoGkgHvehECY8miJlsu7UqmThIhmy6o4T4X6ErX1M9ifR92WGeXkAi/2mh/ WciVpPvTrqM= =/AdA -----END PGP SIGNATURE----- Merge tag 'keys-devel-20131210' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs Pull misc keyrings fixes from David Howells: "These break down into five sets: - A patch to error handling in the big_key type for huge payloads. If the payload is larger than the "low limit" and the backing store allocation fails, then big_key_instantiate() doesn't clear the payload pointers in the key, assuming them to have been previously cleared - but only one of them is. Unfortunately, the garbage collector still calls big_key_destroy() when sees one of the pointers with a weird value in it (and not NULL) which it then tries to clean up. - Three patches to fix the keyring type: * A patch to fix the hash function to correctly divide keyrings off from keys in the topology of the tree inside the associative array. This is only a problem if searching through nested keyrings - and only if the hash function incorrectly puts the a keyring outside of the 0 branch of the root node. * A patch to fix keyrings' use of the associative array. The __key_link_begin() function initially passes a NULL key pointer to assoc_array_insert() on the basis that it's holding a place in the tree whilst it does more allocation and stuff. This is only a problem when a node contains 16 keys that match at that level and we want to add an also matching 17th. This should easily be manufactured with a keyring full of keyrings (without chucking any other sort of key into the mix) - except for (a) above which makes it on average adding the 65th keyring. * A patch to fix searching down through nested keyrings, where any keyring in the set has more than 16 keyrings and none of the first keyrings we look through has a match (before the tree iteration needs to step to a more distal node). Test in keyutils test suite: http://git.kernel.org/cgit/linux/kernel/git/dhowells/keyutils.git/commit/?id=8b4ae963ed92523aea18dfbb8cab3f4979e13bd1 - A patch to fix the big_key type's use of a shmem file as its backing store causing audit messages and LSM check failures. This is done by setting S_PRIVATE on the file to avoid LSM checks on the file (access to the shmem file goes through the keyctl() interface and so is gated by the LSM that way). This isn't normally a problem if a key is used by the context that generated it - and it's currently only used by libkrb5. Test in keyutils test suite: http://git.kernel.org/cgit/linux/kernel/git/dhowells/keyutils.git/commit/?id=d9a53cbab42c293962f2f78f7190253fc73bd32e - A patch to add a generated file to .gitignore. - A patch to fix the alignment of the system certificate data such that it it works on s390. As I understand it, on the S390 arch, symbols must be 2-byte aligned because loading the address discards the least-significant bit" * tag 'keys-devel-20131210' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: KEYS: correct alignment of system_certificate_list content in assembly file Ignore generated file kernel/x509_certificate_list security: shmem: implement kernel private shmem inodes KEYS: Fix searching of nested keyrings KEYS: Fix multiple key add into associative array KEYS: Fix the keyring hash function KEYS: Pre-clear struct key on allocation
This commit is contained in:
commit
5dec682c7f
|
@ -164,10 +164,10 @@ This points to a number of methods, all of which need to be provided:
|
||||||
|
|
||||||
(4) Diff the index keys of two objects.
|
(4) Diff the index keys of two objects.
|
||||||
|
|
||||||
int (*diff_objects)(const void *a, const void *b);
|
int (*diff_objects)(const void *object, const void *index_key);
|
||||||
|
|
||||||
Return the bit position at which the index keys of two objects differ or
|
Return the bit position at which the index key of the specified object
|
||||||
-1 if they are the same.
|
differs from the given index key or -1 if they are the same.
|
||||||
|
|
||||||
|
|
||||||
(5) Free an object.
|
(5) Free an object.
|
||||||
|
|
|
@ -41,10 +41,10 @@ struct assoc_array_ops {
|
||||||
/* Is this the object we're looking for? */
|
/* Is this the object we're looking for? */
|
||||||
bool (*compare_object)(const void *object, const void *index_key);
|
bool (*compare_object)(const void *object, const void *index_key);
|
||||||
|
|
||||||
/* How different are two objects, to a bit position in their keys? (or
|
/* How different is an object from an index key, to a bit position in
|
||||||
* -1 if they're the same)
|
* their keys? (or -1 if they're the same)
|
||||||
*/
|
*/
|
||||||
int (*diff_objects)(const void *a, const void *b);
|
int (*diff_objects)(const void *object, const void *index_key);
|
||||||
|
|
||||||
/* Method to free an object. */
|
/* Method to free an object. */
|
||||||
void (*free_object)(void *object);
|
void (*free_object)(void *object);
|
||||||
|
|
|
@ -47,6 +47,8 @@ extern int shmem_init(void);
|
||||||
extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
|
extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
|
||||||
extern struct file *shmem_file_setup(const char *name,
|
extern struct file *shmem_file_setup(const char *name,
|
||||||
loff_t size, unsigned long flags);
|
loff_t size, unsigned long flags);
|
||||||
|
extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
|
||||||
|
unsigned long flags);
|
||||||
extern int shmem_zero_setup(struct vm_area_struct *);
|
extern int shmem_zero_setup(struct vm_area_struct *);
|
||||||
extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
|
extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
|
||||||
extern void shmem_unlock_mapping(struct address_space *mapping);
|
extern void shmem_unlock_mapping(struct address_space *mapping);
|
||||||
|
|
|
@ -5,3 +5,4 @@ config_data.h
|
||||||
config_data.gz
|
config_data.gz
|
||||||
timeconst.h
|
timeconst.h
|
||||||
hz.bc
|
hz.bc
|
||||||
|
x509_certificate_list
|
||||||
|
|
|
@ -3,8 +3,18 @@
|
||||||
|
|
||||||
__INITRODATA
|
__INITRODATA
|
||||||
|
|
||||||
|
.align 8
|
||||||
.globl VMLINUX_SYMBOL(system_certificate_list)
|
.globl VMLINUX_SYMBOL(system_certificate_list)
|
||||||
VMLINUX_SYMBOL(system_certificate_list):
|
VMLINUX_SYMBOL(system_certificate_list):
|
||||||
|
__cert_list_start:
|
||||||
.incbin "kernel/x509_certificate_list"
|
.incbin "kernel/x509_certificate_list"
|
||||||
.globl VMLINUX_SYMBOL(system_certificate_list_end)
|
__cert_list_end:
|
||||||
VMLINUX_SYMBOL(system_certificate_list_end):
|
|
||||||
|
.align 8
|
||||||
|
.globl VMLINUX_SYMBOL(system_certificate_list_size)
|
||||||
|
VMLINUX_SYMBOL(system_certificate_list_size):
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
.quad __cert_list_end - __cert_list_start
|
||||||
|
#else
|
||||||
|
.long __cert_list_end - __cert_list_start
|
||||||
|
#endif
|
||||||
|
|
|
@ -22,7 +22,7 @@ struct key *system_trusted_keyring;
|
||||||
EXPORT_SYMBOL_GPL(system_trusted_keyring);
|
EXPORT_SYMBOL_GPL(system_trusted_keyring);
|
||||||
|
|
||||||
extern __initconst const u8 system_certificate_list[];
|
extern __initconst const u8 system_certificate_list[];
|
||||||
extern __initconst const u8 system_certificate_list_end[];
|
extern __initconst const unsigned long system_certificate_list_size;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load the compiled-in keys
|
* Load the compiled-in keys
|
||||||
|
@ -60,8 +60,8 @@ static __init int load_system_certificate_list(void)
|
||||||
|
|
||||||
pr_notice("Loading compiled-in X.509 certificates\n");
|
pr_notice("Loading compiled-in X.509 certificates\n");
|
||||||
|
|
||||||
end = system_certificate_list_end;
|
|
||||||
p = system_certificate_list;
|
p = system_certificate_list;
|
||||||
|
end = p + system_certificate_list_size;
|
||||||
while (p < end) {
|
while (p < end) {
|
||||||
/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
|
/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
|
||||||
* than 256 bytes in size.
|
* than 256 bytes in size.
|
||||||
|
|
|
@ -759,8 +759,8 @@ all_leaves_cluster_together:
|
||||||
pr_devel("all leaves cluster together\n");
|
pr_devel("all leaves cluster together\n");
|
||||||
diff = INT_MAX;
|
diff = INT_MAX;
|
||||||
for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
|
for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
|
||||||
int x = ops->diff_objects(assoc_array_ptr_to_leaf(edit->leaf),
|
int x = ops->diff_objects(assoc_array_ptr_to_leaf(node->slots[i]),
|
||||||
assoc_array_ptr_to_leaf(node->slots[i]));
|
index_key);
|
||||||
if (x < diff) {
|
if (x < diff) {
|
||||||
BUG_ON(x < 0);
|
BUG_ON(x < 0);
|
||||||
diff = x;
|
diff = x;
|
||||||
|
|
36
mm/shmem.c
36
mm/shmem.c
|
@ -2918,13 +2918,8 @@ static struct dentry_operations anon_ops = {
|
||||||
.d_dname = simple_dname
|
.d_dname = simple_dname
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
static struct file *__shmem_file_setup(const char *name, loff_t size,
|
||||||
* shmem_file_setup - get an unlinked file living in tmpfs
|
unsigned long flags, unsigned int i_flags)
|
||||||
* @name: name for dentry (to be seen in /proc/<pid>/maps
|
|
||||||
* @size: size to be set for the file
|
|
||||||
* @flags: VM_NORESERVE suppresses pre-accounting of the entire object size
|
|
||||||
*/
|
|
||||||
struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags)
|
|
||||||
{
|
{
|
||||||
struct file *res;
|
struct file *res;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
@ -2957,6 +2952,7 @@ struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags
|
||||||
if (!inode)
|
if (!inode)
|
||||||
goto put_dentry;
|
goto put_dentry;
|
||||||
|
|
||||||
|
inode->i_flags |= i_flags;
|
||||||
d_instantiate(path.dentry, inode);
|
d_instantiate(path.dentry, inode);
|
||||||
inode->i_size = size;
|
inode->i_size = size;
|
||||||
clear_nlink(inode); /* It is unlinked */
|
clear_nlink(inode); /* It is unlinked */
|
||||||
|
@ -2977,6 +2973,32 @@ put_memory:
|
||||||
shmem_unacct_size(flags, size);
|
shmem_unacct_size(flags, size);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shmem_kernel_file_setup - get an unlinked file living in tmpfs which must be
|
||||||
|
* kernel internal. There will be NO LSM permission checks against the
|
||||||
|
* underlying inode. So users of this interface must do LSM checks at a
|
||||||
|
* higher layer. The one user is the big_key implementation. LSM checks
|
||||||
|
* are provided at the key level rather than the inode level.
|
||||||
|
* @name: name for dentry (to be seen in /proc/<pid>/maps
|
||||||
|
* @size: size to be set for the file
|
||||||
|
* @flags: VM_NORESERVE suppresses pre-accounting of the entire object size
|
||||||
|
*/
|
||||||
|
struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags)
|
||||||
|
{
|
||||||
|
return __shmem_file_setup(name, size, flags, S_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shmem_file_setup - get an unlinked file living in tmpfs
|
||||||
|
* @name: name for dentry (to be seen in /proc/<pid>/maps
|
||||||
|
* @size: size to be set for the file
|
||||||
|
* @flags: VM_NORESERVE suppresses pre-accounting of the entire object size
|
||||||
|
*/
|
||||||
|
struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags)
|
||||||
|
{
|
||||||
|
return __shmem_file_setup(name, size, flags, 0);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(shmem_file_setup);
|
EXPORT_SYMBOL_GPL(shmem_file_setup);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -70,7 +70,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||||
*
|
*
|
||||||
* TODO: Encrypt the stored data with a temporary key.
|
* TODO: Encrypt the stored data with a temporary key.
|
||||||
*/
|
*/
|
||||||
file = shmem_file_setup("", datalen, 0);
|
file = shmem_kernel_file_setup("", datalen, 0);
|
||||||
if (IS_ERR(file)) {
|
if (IS_ERR(file)) {
|
||||||
ret = PTR_ERR(file);
|
ret = PTR_ERR(file);
|
||||||
goto err_quota;
|
goto err_quota;
|
||||||
|
|
|
@ -272,7 +272,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allocate and initialise the key and its description */
|
/* allocate and initialise the key and its description */
|
||||||
key = kmem_cache_alloc(key_jar, GFP_KERNEL);
|
key = kmem_cache_zalloc(key_jar, GFP_KERNEL);
|
||||||
if (!key)
|
if (!key)
|
||||||
goto no_memory_2;
|
goto no_memory_2;
|
||||||
|
|
||||||
|
@ -293,18 +293,12 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||||
key->uid = uid;
|
key->uid = uid;
|
||||||
key->gid = gid;
|
key->gid = gid;
|
||||||
key->perm = perm;
|
key->perm = perm;
|
||||||
key->flags = 0;
|
|
||||||
key->expiry = 0;
|
|
||||||
key->payload.data = NULL;
|
|
||||||
key->security = NULL;
|
|
||||||
|
|
||||||
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
|
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
|
||||||
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
|
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
|
||||||
if (flags & KEY_ALLOC_TRUSTED)
|
if (flags & KEY_ALLOC_TRUSTED)
|
||||||
key->flags |= 1 << KEY_FLAG_TRUSTED;
|
key->flags |= 1 << KEY_FLAG_TRUSTED;
|
||||||
|
|
||||||
memset(&key->type_data, 0, sizeof(key->type_data));
|
|
||||||
|
|
||||||
#ifdef KEY_DEBUGGING
|
#ifdef KEY_DEBUGGING
|
||||||
key->magic = KEY_DEBUG_MAGIC;
|
key->magic = KEY_DEBUG_MAGIC;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -160,7 +160,7 @@ static u64 mult_64x32_and_fold(u64 x, u32 y)
|
||||||
static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)
|
static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)
|
||||||
{
|
{
|
||||||
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
|
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
|
||||||
const unsigned long level_mask = ASSOC_ARRAY_LEVEL_STEP_MASK;
|
const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK;
|
||||||
const char *description = index_key->description;
|
const char *description = index_key->description;
|
||||||
unsigned long hash, type;
|
unsigned long hash, type;
|
||||||
u32 piece;
|
u32 piece;
|
||||||
|
@ -194,10 +194,10 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde
|
||||||
* ordinary keys by making sure the lowest level segment in the hash is
|
* ordinary keys by making sure the lowest level segment in the hash is
|
||||||
* zero for keyrings and non-zero otherwise.
|
* zero for keyrings and non-zero otherwise.
|
||||||
*/
|
*/
|
||||||
if (index_key->type != &key_type_keyring && (hash & level_mask) == 0)
|
if (index_key->type != &key_type_keyring && (hash & fan_mask) == 0)
|
||||||
return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1;
|
return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1;
|
||||||
if (index_key->type == &key_type_keyring && (hash & level_mask) != 0)
|
if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0)
|
||||||
return (hash + (hash << level_shift)) & ~level_mask;
|
return (hash + (hash << level_shift)) & ~fan_mask;
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,12 +279,11 @@ static bool keyring_compare_object(const void *object, const void *data)
|
||||||
* Compare the index keys of a pair of objects and determine the bit position
|
* Compare the index keys of a pair of objects and determine the bit position
|
||||||
* at which they differ - if they differ.
|
* at which they differ - if they differ.
|
||||||
*/
|
*/
|
||||||
static int keyring_diff_objects(const void *_a, const void *_b)
|
static int keyring_diff_objects(const void *object, const void *data)
|
||||||
{
|
{
|
||||||
const struct key *key_a = keyring_ptr_to_key(_a);
|
const struct key *key_a = keyring_ptr_to_key(object);
|
||||||
const struct key *key_b = keyring_ptr_to_key(_b);
|
|
||||||
const struct keyring_index_key *a = &key_a->index_key;
|
const struct keyring_index_key *a = &key_a->index_key;
|
||||||
const struct keyring_index_key *b = &key_b->index_key;
|
const struct keyring_index_key *b = data;
|
||||||
unsigned long seg_a, seg_b;
|
unsigned long seg_a, seg_b;
|
||||||
int level, i;
|
int level, i;
|
||||||
|
|
||||||
|
@ -691,8 +690,8 @@ descend_to_node:
|
||||||
smp_read_barrier_depends();
|
smp_read_barrier_depends();
|
||||||
ptr = ACCESS_ONCE(shortcut->next_node);
|
ptr = ACCESS_ONCE(shortcut->next_node);
|
||||||
BUG_ON(!assoc_array_ptr_is_node(ptr));
|
BUG_ON(!assoc_array_ptr_is_node(ptr));
|
||||||
node = assoc_array_ptr_to_node(ptr);
|
|
||||||
}
|
}
|
||||||
|
node = assoc_array_ptr_to_node(ptr);
|
||||||
|
|
||||||
begin_node:
|
begin_node:
|
||||||
kdebug("begin_node");
|
kdebug("begin_node");
|
||||||
|
|
Loading…
Reference in New Issue