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 400a04d..739435a 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, }; |