diff options
Diffstat (limited to 'net/dns_resolver/dns_key.c')
| -rw-r--r-- | net/dns_resolver/dns_key.c | 92 | 
1 files changed, 87 insertions, 5 deletions
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 400a04d5c9a..739435a6af3 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -29,6 +29,7 @@  #include <linux/kernel.h>  #include <linux/keyctl.h>  #include <linux/err.h> +#include <linux/seq_file.h>  #include <keys/dns_resolver-type.h>  #include <keys/user-type.h>  #include "internal.h" @@ -43,6 +44,8 @@ MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");  const struct cred *dns_resolver_cache; +#define	DNS_ERRORNO_OPTION	"dnserror" +  /*   * Instantiate a user defined key for dns_resolver.   * @@ -59,9 +62,10 @@ static int  dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)  {  	struct user_key_payload *upayload; +	unsigned long derrno;  	int ret;  	size_t result_len = 0; -	const char *data = _data, *opt; +	const char *data = _data, *end, *opt;  	kenter("%%%d,%s,'%s',%zu",  	       key->serial, key->description, data, datalen); @@ -71,13 +75,77 @@ dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)  	datalen--;  	/* deal with any options embedded in the data */ +	end = data + datalen;  	opt = memchr(data, '#', datalen);  	if (!opt) { -		kdebug("no options currently supported"); -		return -EINVAL; +		/* no options: the entire data is the result */ +		kdebug("no options"); +		result_len = datalen; +	} else { +		const char *next_opt; + +		result_len = opt - data; +		opt++; +		kdebug("options: '%s'", opt); +		do { +			const char *eq; +			int opt_len, opt_nlen, opt_vlen, tmp; + +			next_opt = memchr(opt, '#', end - opt) ?: end; +			opt_len = next_opt - opt; +			if (!opt_len) { +				printk(KERN_WARNING +				       "Empty option to dns_resolver key %d\n", +				       key->serial); +				return -EINVAL; +			} + +			eq = memchr(opt, '=', opt_len) ?: end; +			opt_nlen = eq - opt; +			eq++; +			opt_vlen = next_opt - eq; /* will be -1 if no value */ + +			tmp = opt_vlen >= 0 ? opt_vlen : 0; +			kdebug("option '%*.*s' val '%*.*s'", +			       opt_nlen, opt_nlen, opt, tmp, tmp, eq); + +			/* see if it's an error number representing a DNS error +			 * that's to be recorded as the result in this key */ +			if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 && +			    memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) { +				kdebug("dns error number option"); +				if (opt_vlen <= 0) +					goto bad_option_value; + +				ret = strict_strtoul(eq, 10, &derrno); +				if (ret < 0) +					goto bad_option_value; + +				if (derrno < 1 || derrno > 511) +					goto bad_option_value; + +				kdebug("dns error no. = %lu", derrno); +				key->type_data.x[0] = -derrno; +				continue; +			} + +		bad_option_value: +			printk(KERN_WARNING +			       "Option '%*.*s' to dns_resolver key %d:" +			       " bad/missing value\n", +			       opt_nlen, opt_nlen, opt, key->serial); +			return -EINVAL; +		} while (opt = next_opt + 1, opt < end); +	} + +	/* don't cache the result if we're caching an error saying there's no +	 * result */ +	if (key->type_data.x[0]) { +		kleave(" = 0 [h_error %ld]", key->type_data.x[0]); +		return 0;  	} -	result_len = datalen; +	kdebug("store result");  	ret = key_payload_reserve(key, result_len);  	if (ret < 0)  		return -EINVAL; @@ -135,13 +203,27 @@ no_match:  	return ret;  } +/* + * Describe a DNS key + */ +static void dns_resolver_describe(const struct key *key, struct seq_file *m) +{ +	int err = key->type_data.x[0]; + +	seq_puts(m, key->description); +	if (err) +		seq_printf(m, ": %d", err); +	else +		seq_printf(m, ": %u", key->datalen); +} +  struct key_type key_type_dns_resolver = {  	.name		= "dns_resolver",  	.instantiate	= dns_resolver_instantiate,  	.match		= dns_resolver_match,  	.revoke		= user_revoke,  	.destroy	= user_destroy, -	.describe	= user_describe, +	.describe	= dns_resolver_describe,  	.read		= user_read,  };  |