1ab3c3587SDavid Howells /* Large capacity key type 2ab3c3587SDavid Howells * 3ab3c3587SDavid Howells * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. 4ab3c3587SDavid Howells * Written by David Howells (dhowells@redhat.com) 5ab3c3587SDavid Howells * 6ab3c3587SDavid Howells * This program is free software; you can redistribute it and/or 7ab3c3587SDavid Howells * modify it under the terms of the GNU General Public Licence 8ab3c3587SDavid Howells * as published by the Free Software Foundation; either version 9ab3c3587SDavid Howells * 2 of the Licence, or (at your option) any later version. 10ab3c3587SDavid Howells */ 11ab3c3587SDavid Howells 12ab3c3587SDavid Howells #include <linux/module.h> 13ab3c3587SDavid Howells #include <linux/init.h> 14ab3c3587SDavid Howells #include <linux/seq_file.h> 15ab3c3587SDavid Howells #include <linux/file.h> 16ab3c3587SDavid Howells #include <linux/shmem_fs.h> 17ab3c3587SDavid Howells #include <linux/err.h> 18ab3c3587SDavid Howells #include <keys/user-type.h> 19ab3c3587SDavid Howells #include <keys/big_key-type.h> 20ab3c3587SDavid Howells 21ab3c3587SDavid Howells MODULE_LICENSE("GPL"); 22ab3c3587SDavid Howells 23ab3c3587SDavid Howells /* 24ab3c3587SDavid Howells * If the data is under this limit, there's no point creating a shm file to 25ab3c3587SDavid Howells * hold it as the permanently resident metadata for the shmem fs will be at 26ab3c3587SDavid Howells * least as large as the data. 27ab3c3587SDavid Howells */ 28ab3c3587SDavid Howells #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) 29ab3c3587SDavid Howells 30ab3c3587SDavid Howells /* 31ab3c3587SDavid Howells * big_key defined keys take an arbitrary string as the description and an 32ab3c3587SDavid Howells * arbitrary blob of data as the payload 33ab3c3587SDavid Howells */ 34ab3c3587SDavid Howells struct key_type key_type_big_key = { 35ab3c3587SDavid Howells .name = "big_key", 36ab3c3587SDavid Howells .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, 37*002edaf7SDavid Howells .preparse = big_key_preparse, 38*002edaf7SDavid Howells .free_preparse = big_key_free_preparse, 39*002edaf7SDavid Howells .instantiate = generic_key_instantiate, 40ab3c3587SDavid Howells .match = user_match, 41ab3c3587SDavid Howells .revoke = big_key_revoke, 42ab3c3587SDavid Howells .destroy = big_key_destroy, 43ab3c3587SDavid Howells .describe = big_key_describe, 44ab3c3587SDavid Howells .read = big_key_read, 45ab3c3587SDavid Howells }; 46ab3c3587SDavid Howells 47ab3c3587SDavid Howells /* 48*002edaf7SDavid Howells * Preparse a big key 49ab3c3587SDavid Howells */ 50*002edaf7SDavid Howells int big_key_preparse(struct key_preparsed_payload *prep) 51ab3c3587SDavid Howells { 52*002edaf7SDavid Howells struct path *path = (struct path *)&prep->payload; 53ab3c3587SDavid Howells struct file *file; 54ab3c3587SDavid Howells ssize_t written; 55ab3c3587SDavid Howells size_t datalen = prep->datalen; 56ab3c3587SDavid Howells int ret; 57ab3c3587SDavid Howells 58ab3c3587SDavid Howells ret = -EINVAL; 59ab3c3587SDavid Howells if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) 60ab3c3587SDavid Howells goto error; 61ab3c3587SDavid Howells 62ab3c3587SDavid Howells /* Set an arbitrary quota */ 63*002edaf7SDavid Howells prep->quotalen = 16; 64ab3c3587SDavid Howells 65*002edaf7SDavid Howells prep->type_data[1] = (void *)(unsigned long)datalen; 66ab3c3587SDavid Howells 67ab3c3587SDavid Howells if (datalen > BIG_KEY_FILE_THRESHOLD) { 68ab3c3587SDavid Howells /* Create a shmem file to store the data in. This will permit the data 69ab3c3587SDavid Howells * to be swapped out if needed. 70ab3c3587SDavid Howells * 71ab3c3587SDavid Howells * TODO: Encrypt the stored data with a temporary key. 72ab3c3587SDavid Howells */ 73c7277090SEric Paris file = shmem_kernel_file_setup("", datalen, 0); 74d2b86970SWei Yongjun if (IS_ERR(file)) { 75d2b86970SWei Yongjun ret = PTR_ERR(file); 76*002edaf7SDavid Howells goto error; 77d2b86970SWei Yongjun } 78ab3c3587SDavid Howells 79ab3c3587SDavid Howells written = kernel_write(file, prep->data, prep->datalen, 0); 80ab3c3587SDavid Howells if (written != datalen) { 8197826c82SDavid Howells ret = written; 82ab3c3587SDavid Howells if (written >= 0) 83ab3c3587SDavid Howells ret = -ENOMEM; 84ab3c3587SDavid Howells goto err_fput; 85ab3c3587SDavid Howells } 86ab3c3587SDavid Howells 87ab3c3587SDavid Howells /* Pin the mount and dentry to the key so that we can open it again 88ab3c3587SDavid Howells * later 89ab3c3587SDavid Howells */ 90ab3c3587SDavid Howells *path = file->f_path; 91ab3c3587SDavid Howells path_get(path); 92ab3c3587SDavid Howells fput(file); 93ab3c3587SDavid Howells } else { 94ab3c3587SDavid Howells /* Just store the data in a buffer */ 95ab3c3587SDavid Howells void *data = kmalloc(datalen, GFP_KERNEL); 96*002edaf7SDavid Howells if (!data) 97*002edaf7SDavid Howells return -ENOMEM; 98ab3c3587SDavid Howells 99*002edaf7SDavid Howells prep->payload[0] = memcpy(data, prep->data, prep->datalen); 100ab3c3587SDavid Howells } 101ab3c3587SDavid Howells return 0; 102ab3c3587SDavid Howells 103ab3c3587SDavid Howells err_fput: 104ab3c3587SDavid Howells fput(file); 105ab3c3587SDavid Howells error: 106ab3c3587SDavid Howells return ret; 107ab3c3587SDavid Howells } 108ab3c3587SDavid Howells 109ab3c3587SDavid Howells /* 110*002edaf7SDavid Howells * Clear preparsement. 111*002edaf7SDavid Howells */ 112*002edaf7SDavid Howells void big_key_free_preparse(struct key_preparsed_payload *prep) 113*002edaf7SDavid Howells { 114*002edaf7SDavid Howells if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { 115*002edaf7SDavid Howells struct path *path = (struct path *)&prep->payload; 116*002edaf7SDavid Howells path_put(path); 117*002edaf7SDavid Howells } else { 118*002edaf7SDavid Howells kfree(prep->payload[0]); 119*002edaf7SDavid Howells } 120*002edaf7SDavid Howells } 121*002edaf7SDavid Howells 122*002edaf7SDavid Howells /* 123ab3c3587SDavid Howells * dispose of the links from a revoked keyring 124ab3c3587SDavid Howells * - called with the key sem write-locked 125ab3c3587SDavid Howells */ 126ab3c3587SDavid Howells void big_key_revoke(struct key *key) 127ab3c3587SDavid Howells { 128ab3c3587SDavid Howells struct path *path = (struct path *)&key->payload.data2; 129ab3c3587SDavid Howells 130ab3c3587SDavid Howells /* clear the quota */ 131ab3c3587SDavid Howells key_payload_reserve(key, 0); 132ab3c3587SDavid Howells if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) 133ab3c3587SDavid Howells vfs_truncate(path, 0); 134ab3c3587SDavid Howells } 135ab3c3587SDavid Howells 136ab3c3587SDavid Howells /* 137ab3c3587SDavid Howells * dispose of the data dangling from the corpse of a big_key key 138ab3c3587SDavid Howells */ 139ab3c3587SDavid Howells void big_key_destroy(struct key *key) 140ab3c3587SDavid Howells { 141ab3c3587SDavid Howells if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) { 142ab3c3587SDavid Howells struct path *path = (struct path *)&key->payload.data2; 143ab3c3587SDavid Howells path_put(path); 144ab3c3587SDavid Howells path->mnt = NULL; 145ab3c3587SDavid Howells path->dentry = NULL; 146ab3c3587SDavid Howells } else { 147ab3c3587SDavid Howells kfree(key->payload.data); 148ab3c3587SDavid Howells key->payload.data = NULL; 149ab3c3587SDavid Howells } 150ab3c3587SDavid Howells } 151ab3c3587SDavid Howells 152ab3c3587SDavid Howells /* 153ab3c3587SDavid Howells * describe the big_key key 154ab3c3587SDavid Howells */ 155ab3c3587SDavid Howells void big_key_describe(const struct key *key, struct seq_file *m) 156ab3c3587SDavid Howells { 157ab3c3587SDavid Howells unsigned long datalen = key->type_data.x[1]; 158ab3c3587SDavid Howells 159ab3c3587SDavid Howells seq_puts(m, key->description); 160ab3c3587SDavid Howells 161ab3c3587SDavid Howells if (key_is_instantiated(key)) 162ab3c3587SDavid Howells seq_printf(m, ": %lu [%s]", 163ab3c3587SDavid Howells datalen, 164ab3c3587SDavid Howells datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); 165ab3c3587SDavid Howells } 166ab3c3587SDavid Howells 167ab3c3587SDavid Howells /* 168ab3c3587SDavid Howells * read the key data 169ab3c3587SDavid Howells * - the key's semaphore is read-locked 170ab3c3587SDavid Howells */ 171ab3c3587SDavid Howells long big_key_read(const struct key *key, char __user *buffer, size_t buflen) 172ab3c3587SDavid Howells { 173ab3c3587SDavid Howells unsigned long datalen = key->type_data.x[1]; 174ab3c3587SDavid Howells long ret; 175ab3c3587SDavid Howells 176ab3c3587SDavid Howells if (!buffer || buflen < datalen) 177ab3c3587SDavid Howells return datalen; 178ab3c3587SDavid Howells 179ab3c3587SDavid Howells if (datalen > BIG_KEY_FILE_THRESHOLD) { 180ab3c3587SDavid Howells struct path *path = (struct path *)&key->payload.data2; 181ab3c3587SDavid Howells struct file *file; 182ab3c3587SDavid Howells loff_t pos; 183ab3c3587SDavid Howells 184ab3c3587SDavid Howells file = dentry_open(path, O_RDONLY, current_cred()); 185ab3c3587SDavid Howells if (IS_ERR(file)) 186ab3c3587SDavid Howells return PTR_ERR(file); 187ab3c3587SDavid Howells 188ab3c3587SDavid Howells pos = 0; 189ab3c3587SDavid Howells ret = vfs_read(file, buffer, datalen, &pos); 190ab3c3587SDavid Howells fput(file); 191ab3c3587SDavid Howells if (ret >= 0 && ret != datalen) 192ab3c3587SDavid Howells ret = -EIO; 193ab3c3587SDavid Howells } else { 194ab3c3587SDavid Howells ret = datalen; 195ab3c3587SDavid Howells if (copy_to_user(buffer, key->payload.data, datalen) != 0) 196ab3c3587SDavid Howells ret = -EFAULT; 197ab3c3587SDavid Howells } 198ab3c3587SDavid Howells 199ab3c3587SDavid Howells return ret; 200ab3c3587SDavid Howells } 201ab3c3587SDavid Howells 202ab3c3587SDavid Howells /* 203ab3c3587SDavid Howells * Module stuff 204ab3c3587SDavid Howells */ 205ab3c3587SDavid Howells static int __init big_key_init(void) 206ab3c3587SDavid Howells { 207ab3c3587SDavid Howells return register_key_type(&key_type_big_key); 208ab3c3587SDavid Howells } 209ab3c3587SDavid Howells 210ab3c3587SDavid Howells static void __exit big_key_cleanup(void) 211ab3c3587SDavid Howells { 212ab3c3587SDavid Howells unregister_key_type(&key_type_big_key); 213ab3c3587SDavid Howells } 214ab3c3587SDavid Howells 215ab3c3587SDavid Howells module_init(big_key_init); 216ab3c3587SDavid Howells module_exit(big_key_cleanup); 217