|
@@ -29,6 +29,7 @@
|
|
#include <linux/kernel.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/keyctl.h>
|
|
#include <linux/keyctl.h>
|
|
#include <linux/err.h>
|
|
#include <linux/err.h>
|
|
|
|
+#include <linux/seq_file.h>
|
|
#include <keys/dns_resolver-type.h>
|
|
#include <keys/dns_resolver-type.h>
|
|
#include <keys/user-type.h>
|
|
#include <keys/user-type.h>
|
|
#include "internal.h"
|
|
#include "internal.h"
|
|
@@ -43,6 +44,8 @@ MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
|
|
|
|
|
|
const struct cred *dns_resolver_cache;
|
|
const struct cred *dns_resolver_cache;
|
|
|
|
|
|
|
|
+#define DNS_ERRORNO_OPTION "dnserror"
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Instantiate a user defined key for dns_resolver.
|
|
* 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)
|
|
dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
|
|
{
|
|
{
|
|
struct user_key_payload *upayload;
|
|
struct user_key_payload *upayload;
|
|
|
|
+ unsigned long derrno;
|
|
int ret;
|
|
int ret;
|
|
size_t result_len = 0;
|
|
size_t result_len = 0;
|
|
- const char *data = _data, *opt;
|
|
|
|
|
|
+ const char *data = _data, *end, *opt;
|
|
|
|
|
|
kenter("%%%d,%s,'%s',%zu",
|
|
kenter("%%%d,%s,'%s',%zu",
|
|
key->serial, key->description, data, datalen);
|
|
key->serial, key->description, data, datalen);
|
|
@@ -71,13 +75,77 @@ dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
|
|
datalen--;
|
|
datalen--;
|
|
|
|
|
|
/* deal with any options embedded in the data */
|
|
/* deal with any options embedded in the data */
|
|
|
|
+ end = data + datalen;
|
|
opt = memchr(data, '#', datalen);
|
|
opt = memchr(data, '#', datalen);
|
|
if (!opt) {
|
|
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);
|
|
ret = key_payload_reserve(key, result_len);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -135,13 +203,27 @@ no_match:
|
|
return ret;
|
|
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 = {
|
|
struct key_type key_type_dns_resolver = {
|
|
.name = "dns_resolver",
|
|
.name = "dns_resolver",
|
|
.instantiate = dns_resolver_instantiate,
|
|
.instantiate = dns_resolver_instantiate,
|
|
.match = dns_resolver_match,
|
|
.match = dns_resolver_match,
|
|
.revoke = user_revoke,
|
|
.revoke = user_revoke,
|
|
.destroy = user_destroy,
|
|
.destroy = user_destroy,
|
|
- .describe = user_describe,
|
|
|
|
|
|
+ .describe = dns_resolver_describe,
|
|
.read = user_read,
|
|
.read = user_read,
|
|
};
|
|
};
|
|
|
|
|