cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dns_key.c (9961B)


      1/* Key type used to cache DNS lookups made by the kernel
      2 *
      3 * See Documentation/networking/dns_resolver.rst
      4 *
      5 *   Copyright (c) 2007 Igor Mammedov
      6 *   Author(s): Igor Mammedov (niallain@gmail.com)
      7 *              Steve French (sfrench@us.ibm.com)
      8 *              Wang Lei (wang840925@gmail.com)
      9 *		David Howells (dhowells@redhat.com)
     10 *
     11 *   This library is free software; you can redistribute it and/or modify
     12 *   it under the terms of the GNU Lesser General Public License as published
     13 *   by the Free Software Foundation; either version 2.1 of the License, or
     14 *   (at your option) any later version.
     15 *
     16 *   This library is distributed in the hope that it will be useful,
     17 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     19 *   the GNU Lesser General Public License for more details.
     20 *
     21 *   You should have received a copy of the GNU Lesser General Public License
     22 *   along with this library; if not, see <http://www.gnu.org/licenses/>.
     23 */
     24#include <linux/module.h>
     25#include <linux/moduleparam.h>
     26#include <linux/slab.h>
     27#include <linux/string.h>
     28#include <linux/kernel.h>
     29#include <linux/keyctl.h>
     30#include <linux/err.h>
     31#include <linux/seq_file.h>
     32#include <linux/dns_resolver.h>
     33#include <keys/dns_resolver-type.h>
     34#include <keys/user-type.h>
     35#include "internal.h"
     36
     37MODULE_DESCRIPTION("DNS Resolver");
     38MODULE_AUTHOR("Wang Lei");
     39MODULE_LICENSE("GPL");
     40
     41unsigned int dns_resolver_debug;
     42module_param_named(debug, dns_resolver_debug, uint, 0644);
     43MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
     44
     45const struct cred *dns_resolver_cache;
     46
     47#define	DNS_ERRORNO_OPTION	"dnserror"
     48
     49/*
     50 * Preparse instantiation data for a dns_resolver key.
     51 *
     52 * For normal hostname lookups, the data must be a NUL-terminated string, with
     53 * the NUL char accounted in datalen.
     54 *
     55 * If the data contains a '#' characters, then we take the clause after each
     56 * one to be an option of the form 'key=value'.  The actual data of interest is
     57 * the string leading up to the first '#'.  For instance:
     58 *
     59 *        "ip1,ip2,...#foo=bar"
     60 *
     61 * For server list requests, the data must begin with a NUL char and be
     62 * followed by a byte indicating the version of the data format.  Version 1
     63 * looks something like (note this is packed):
     64 *
     65 *	u8      Non-string marker (ie. 0)
     66 *	u8	Content (DNS_PAYLOAD_IS_*)
     67 *	u8	Version (e.g. 1)
     68 *	u8	Source of server list
     69 *	u8	Lookup status of server list
     70 *	u8	Number of servers
     71 *	foreach-server {
     72 *		__le16	Name length
     73 *		__le16	Priority (as per SRV record, low first)
     74 *		__le16	Weight (as per SRV record, higher first)
     75 *		__le16	Port
     76 *		u8	Source of address list
     77 *		u8	Lookup status of address list
     78 *		u8	Protocol (DNS_SERVER_PROTOCOL_*)
     79 *		u8	Number of addresses
     80 *		char[]	Name (not NUL-terminated)
     81 *		foreach-address {
     82 *			u8		Family (DNS_ADDRESS_IS_*)
     83 *			union {
     84 *				u8[4]	ipv4_addr
     85 *				u8[16]	ipv6_addr
     86 *			}
     87 *		}
     88 *	}
     89 *
     90 */
     91static int
     92dns_resolver_preparse(struct key_preparsed_payload *prep)
     93{
     94	const struct dns_payload_header *bin;
     95	struct user_key_payload *upayload;
     96	unsigned long derrno;
     97	int ret;
     98	int datalen = prep->datalen, result_len = 0;
     99	const char *data = prep->data, *end, *opt;
    100
    101	if (datalen <= 1 || !data)
    102		return -EINVAL;
    103
    104	if (data[0] == 0) {
    105		/* It may be a server list. */
    106		if (datalen <= sizeof(*bin))
    107			return -EINVAL;
    108
    109		bin = (const struct dns_payload_header *)data;
    110		kenter("[%u,%u],%u", bin->content, bin->version, datalen);
    111		if (bin->content != DNS_PAYLOAD_IS_SERVER_LIST) {
    112			pr_warn_ratelimited(
    113				"dns_resolver: Unsupported content type (%u)\n",
    114				bin->content);
    115			return -EINVAL;
    116		}
    117
    118		if (bin->version != 1) {
    119			pr_warn_ratelimited(
    120				"dns_resolver: Unsupported server list version (%u)\n",
    121				bin->version);
    122			return -EINVAL;
    123		}
    124
    125		result_len = datalen;
    126		goto store_result;
    127	}
    128
    129	kenter("'%*.*s',%u", datalen, datalen, data, datalen);
    130
    131	if (!data || data[datalen - 1] != '\0')
    132		return -EINVAL;
    133	datalen--;
    134
    135	/* deal with any options embedded in the data */
    136	end = data + datalen;
    137	opt = memchr(data, '#', datalen);
    138	if (!opt) {
    139		/* no options: the entire data is the result */
    140		kdebug("no options");
    141		result_len = datalen;
    142	} else {
    143		const char *next_opt;
    144
    145		result_len = opt - data;
    146		opt++;
    147		kdebug("options: '%s'", opt);
    148		do {
    149			int opt_len, opt_nlen;
    150			const char *eq;
    151			char optval[128];
    152
    153			next_opt = memchr(opt, '#', end - opt) ?: end;
    154			opt_len = next_opt - opt;
    155			if (opt_len <= 0 || opt_len > sizeof(optval)) {
    156				pr_warn_ratelimited("Invalid option length (%d) for dns_resolver key\n",
    157						    opt_len);
    158				return -EINVAL;
    159			}
    160
    161			eq = memchr(opt, '=', opt_len);
    162			if (eq) {
    163				opt_nlen = eq - opt;
    164				eq++;
    165				memcpy(optval, eq, next_opt - eq);
    166				optval[next_opt - eq] = '\0';
    167			} else {
    168				opt_nlen = opt_len;
    169				optval[0] = '\0';
    170			}
    171
    172			kdebug("option '%*.*s' val '%s'",
    173			       opt_nlen, opt_nlen, opt, optval);
    174
    175			/* see if it's an error number representing a DNS error
    176			 * that's to be recorded as the result in this key */
    177			if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 &&
    178			    memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) {
    179				kdebug("dns error number option");
    180
    181				ret = kstrtoul(optval, 10, &derrno);
    182				if (ret < 0)
    183					goto bad_option_value;
    184
    185				if (derrno < 1 || derrno > 511)
    186					goto bad_option_value;
    187
    188				kdebug("dns error no. = %lu", derrno);
    189				prep->payload.data[dns_key_error] = ERR_PTR(-derrno);
    190				continue;
    191			}
    192
    193		bad_option_value:
    194			pr_warn_ratelimited("Option '%*.*s' to dns_resolver key: bad/missing value\n",
    195					    opt_nlen, opt_nlen, opt);
    196			return -EINVAL;
    197		} while (opt = next_opt + 1, opt < end);
    198	}
    199
    200	/* don't cache the result if we're caching an error saying there's no
    201	 * result */
    202	if (prep->payload.data[dns_key_error]) {
    203		kleave(" = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error]));
    204		return 0;
    205	}
    206
    207store_result:
    208	kdebug("store result");
    209	prep->quotalen = result_len;
    210
    211	upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
    212	if (!upayload) {
    213		kleave(" = -ENOMEM");
    214		return -ENOMEM;
    215	}
    216
    217	upayload->datalen = result_len;
    218	memcpy(upayload->data, data, result_len);
    219	upayload->data[result_len] = '\0';
    220
    221	prep->payload.data[dns_key_data] = upayload;
    222	kleave(" = 0");
    223	return 0;
    224}
    225
    226/*
    227 * Clean up the preparse data
    228 */
    229static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
    230{
    231	pr_devel("==>%s()\n", __func__);
    232
    233	kfree(prep->payload.data[dns_key_data]);
    234}
    235
    236/*
    237 * The description is of the form "[<type>:]<domain_name>"
    238 *
    239 * The domain name may be a simple name or an absolute domain name (which
    240 * should end with a period).  The domain name is case-independent.
    241 */
    242static bool dns_resolver_cmp(const struct key *key,
    243			     const struct key_match_data *match_data)
    244{
    245	int slen, dlen, ret = 0;
    246	const char *src = key->description, *dsp = match_data->raw_data;
    247
    248	kenter("%s,%s", src, dsp);
    249
    250	if (!src || !dsp)
    251		goto no_match;
    252
    253	if (strcasecmp(src, dsp) == 0)
    254		goto matched;
    255
    256	slen = strlen(src);
    257	dlen = strlen(dsp);
    258	if (slen <= 0 || dlen <= 0)
    259		goto no_match;
    260	if (src[slen - 1] == '.')
    261		slen--;
    262	if (dsp[dlen - 1] == '.')
    263		dlen--;
    264	if (slen != dlen || strncasecmp(src, dsp, slen) != 0)
    265		goto no_match;
    266
    267matched:
    268	ret = 1;
    269no_match:
    270	kleave(" = %d", ret);
    271	return ret;
    272}
    273
    274/*
    275 * Preparse the match criterion.
    276 */
    277static int dns_resolver_match_preparse(struct key_match_data *match_data)
    278{
    279	match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
    280	match_data->cmp = dns_resolver_cmp;
    281	return 0;
    282}
    283
    284/*
    285 * Describe a DNS key
    286 */
    287static void dns_resolver_describe(const struct key *key, struct seq_file *m)
    288{
    289	seq_puts(m, key->description);
    290	if (key_is_positive(key)) {
    291		int err = PTR_ERR(key->payload.data[dns_key_error]);
    292
    293		if (err)
    294			seq_printf(m, ": %d", err);
    295		else
    296			seq_printf(m, ": %u", key->datalen);
    297	}
    298}
    299
    300/*
    301 * read the DNS data
    302 * - the key's semaphore is read-locked
    303 */
    304static long dns_resolver_read(const struct key *key,
    305			      char *buffer, size_t buflen)
    306{
    307	int err = PTR_ERR(key->payload.data[dns_key_error]);
    308
    309	if (err)
    310		return err;
    311
    312	return user_read(key, buffer, buflen);
    313}
    314
    315struct key_type key_type_dns_resolver = {
    316	.name		= "dns_resolver",
    317	.flags		= KEY_TYPE_NET_DOMAIN,
    318	.preparse	= dns_resolver_preparse,
    319	.free_preparse	= dns_resolver_free_preparse,
    320	.instantiate	= generic_key_instantiate,
    321	.match_preparse	= dns_resolver_match_preparse,
    322	.revoke		= user_revoke,
    323	.destroy	= user_destroy,
    324	.describe	= dns_resolver_describe,
    325	.read		= dns_resolver_read,
    326};
    327
    328static int __init init_dns_resolver(void)
    329{
    330	struct cred *cred;
    331	struct key *keyring;
    332	int ret;
    333
    334	/* create an override credential set with a special thread keyring in
    335	 * which DNS requests are cached
    336	 *
    337	 * this is used to prevent malicious redirections from being installed
    338	 * with add_key().
    339	 */
    340	cred = prepare_kernel_cred(NULL);
    341	if (!cred)
    342		return -ENOMEM;
    343
    344	keyring = keyring_alloc(".dns_resolver",
    345				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
    346				(KEY_POS_ALL & ~KEY_POS_SETATTR) |
    347				KEY_USR_VIEW | KEY_USR_READ,
    348				KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
    349	if (IS_ERR(keyring)) {
    350		ret = PTR_ERR(keyring);
    351		goto failed_put_cred;
    352	}
    353
    354	ret = register_key_type(&key_type_dns_resolver);
    355	if (ret < 0)
    356		goto failed_put_key;
    357
    358	/* instruct request_key() to use this special keyring as a cache for
    359	 * the results it looks up */
    360	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
    361	cred->thread_keyring = keyring;
    362	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
    363	dns_resolver_cache = cred;
    364
    365	kdebug("DNS resolver keyring: %d\n", key_serial(keyring));
    366	return 0;
    367
    368failed_put_key:
    369	key_put(keyring);
    370failed_put_cred:
    371	put_cred(cred);
    372	return ret;
    373}
    374
    375static void __exit exit_dns_resolver(void)
    376{
    377	key_revoke(dns_resolver_cache->thread_keyring);
    378	unregister_key_type(&key_type_dns_resolver);
    379	put_cred(dns_resolver_cache);
    380}
    381
    382module_init(init_dns_resolver)
    383module_exit(exit_dns_resolver)
    384MODULE_LICENSE("GPL");