diff options
Diffstat (limited to 'net/dns_resolver')
| -rw-r--r-- | net/dns_resolver/Kconfig | 27 | ||||
| -rw-r--r-- | net/dns_resolver/Makefile | 7 | ||||
| -rw-r--r-- | net/dns_resolver/dns_key.c | 210 | ||||
| -rw-r--r-- | net/dns_resolver/dns_query.c | 159 | ||||
| -rw-r--r-- | net/dns_resolver/internal.h | 44 | 
5 files changed, 447 insertions, 0 deletions
diff --git a/net/dns_resolver/Kconfig b/net/dns_resolver/Kconfig new file mode 100644 index 00000000000..2ec47cb5d0d --- /dev/null +++ b/net/dns_resolver/Kconfig @@ -0,0 +1,27 @@ +# +# Configuration for DNS Resolver +# +config DNS_RESOLVER +	tristate "DNS Resolver support" +	depends on NET && KEYS +	help +	  Saying Y here will include support for the DNS Resolver key type +	  which can be used to make upcalls to perform DNS lookups in +	  userspace. + +	  DNS Resolver is used to query DNS server for information.  Examples +	  being resolving a UNC hostname element to an IP address for CIFS or +	  performing a DNS query for AFSDB records so that AFS can locate a +	  cell's volume location database servers. + +	  DNS Resolver is used by the CIFS and AFS modules, and would support +	  samba4 later.  DNS Resolver is supported by the userspace upcall +	  helper "/sbin/dns.resolver" via /etc/request-key.conf. + +	  See <file:Documentation/networking/dns_resolver.txt> for further +	  information. + +	  To compile this as a module, choose M here: the module will be called +	  dnsresolver. + +	  If unsure, say N. diff --git a/net/dns_resolver/Makefile b/net/dns_resolver/Makefile new file mode 100644 index 00000000000..c0ef4e71dc4 --- /dev/null +++ b/net/dns_resolver/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux DNS Resolver. +# + +obj-$(CONFIG_DNS_RESOLVER) += dns_resolver.o + +dns_resolver-objs :=  dns_key.o dns_query.o diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c new file mode 100644 index 00000000000..1b1b411adcf --- /dev/null +++ b/net/dns_resolver/dns_key.c @@ -0,0 +1,210 @@ +/* Key type used to cache DNS lookups made by the kernel + * + * See Documentation/networking/dns_resolver.txt + * + *   Copyright (c) 2007 Igor Mammedov + *   Author(s): Igor Mammedov (niallain@gmail.com) + *              Steve French (sfrench@us.ibm.com) + *              Wang Lei (wang840925@gmail.com) + *		David Howells (dhowells@redhat.com) + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/keyctl.h> +#include <keys/dns_resolver-type.h> +#include <keys/user-type.h> +#include "internal.h" + +MODULE_DESCRIPTION("DNS Resolver"); +MODULE_AUTHOR("Wang Lei"); +MODULE_LICENSE("GPL"); + +unsigned dns_resolver_debug; +module_param_named(debug, dns_resolver_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "DNS Resolver debugging mask"); + +const struct cred *dns_resolver_cache; + +/* + * Instantiate a user defined key for dns_resolver. + * + * The data must be a NUL-terminated string, with the NUL char accounted in + * datalen. + * + * If the data contains a '#' characters, then we take the clause after each + * one to be an option of the form 'key=value'.  The actual data of interest is + * the string leading up to the first '#'.  For instance: + * + *        "ip1,ip2,...#foo=bar" + */ +static int +dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) +{ +	struct user_key_payload *upayload; +	int ret; +	size_t result_len = 0; +	const char *data = _data, *opt; + +	kenter("%%%d,%s,'%s',%zu", +	       key->serial, key->description, data, datalen); + +	if (datalen <= 1 || !data || data[datalen - 1] != '\0') +		return -EINVAL; +	datalen--; + +	/* deal with any options embedded in the data */ +	opt = memchr(data, '#', datalen); +	if (!opt) { +		kdebug("no options currently supported"); +		return -EINVAL; +	} + +	result_len = datalen; +	ret = key_payload_reserve(key, result_len); +	if (ret < 0) +		return -EINVAL; + +	upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL); +	if (!upayload) { +		kleave(" = -ENOMEM"); +		return -ENOMEM; +	} + +	upayload->datalen = result_len; +	memcpy(upayload->data, data, result_len); +	upayload->data[result_len] = '\0'; +	rcu_assign_pointer(key->payload.data, upayload); + +	kleave(" = 0"); +	return 0; +} + +/* + * The description is of the form "[<type>:]<domain_name>" + * + * The domain name may be a simple name or an absolute domain name (which + * should end with a period).  The domain name is case-independent. + */ +static int +dns_resolver_match(const struct key *key, const void *description) +{ +	int slen, dlen, ret = 0; +	const char *src = key->description, *dsp = description; + +	kenter("%s,%s", src, dsp); + +	if (!src || !dsp) +		goto no_match; + +	if (strcasecmp(src, dsp) == 0) +		goto matched; + +	slen = strlen(src); +	dlen = strlen(dsp); +	if (slen <= 0 || dlen <= 0) +		goto no_match; +	if (src[slen - 1] == '.') +		slen--; +	if (dsp[dlen - 1] == '.') +		dlen--; +	if (slen != dlen || strncasecmp(src, dsp, slen) != 0) +		goto no_match; + +matched: +	ret = 1; +no_match: +	kleave(" = %d", ret); +	return ret; +} + +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, +	.read		= user_read, +}; + +static int __init init_dns_resolver(void) +{ +	struct cred *cred; +	struct key *keyring; +	int ret; + +	printk(KERN_NOTICE "Registering the %s key type\n", +	       key_type_dns_resolver.name); + +	/* create an override credential set with a special thread keyring in +	 * which DNS requests are cached +	 * +	 * this is used to prevent malicious redirections from being installed +	 * with add_key(). +	 */ +	cred = prepare_kernel_cred(NULL); +	if (!cred) +		return -ENOMEM; + +	keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, +			    (KEY_POS_ALL & ~KEY_POS_SETATTR) | +			    KEY_USR_VIEW | KEY_USR_READ, +			    KEY_ALLOC_NOT_IN_QUOTA); +	if (IS_ERR(keyring)) { +		ret = PTR_ERR(keyring); +		goto failed_put_cred; +	} + +	ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); +	if (ret < 0) +		goto failed_put_key; + +	ret = register_key_type(&key_type_dns_resolver); +	if (ret < 0) +		goto failed_put_key; + +	/* instruct request_key() to use this special keyring as a cache for +	 * the results it looks up */ +	cred->thread_keyring = keyring; +	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; +	dns_resolver_cache = cred; + +	kdebug("DNS resolver keyring: %d\n", key_serial(keyring)); +	return 0; + +failed_put_key: +	key_put(keyring); +failed_put_cred: +	put_cred(cred); +	return ret; +} + +static void __exit exit_dns_resolver(void) +{ +	key_revoke(dns_resolver_cache->thread_keyring); +	unregister_key_type(&key_type_dns_resolver); +	put_cred(dns_resolver_cache); +	printk(KERN_NOTICE "Unregistered %s key type\n", +	       key_type_dns_resolver.name); +} + +module_init(init_dns_resolver) +module_exit(exit_dns_resolver) +MODULE_LICENSE("GPL"); diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c new file mode 100644 index 00000000000..6c0cf31ea00 --- /dev/null +++ b/net/dns_resolver/dns_query.c @@ -0,0 +1,159 @@ +/* Upcall routine, designed to work as a key type and working through + * /sbin/request-key to contact userspace when handling DNS queries. + * + * See Documentation/networking/dns_resolver.txt + * + *   Copyright (c) 2007 Igor Mammedov + *   Author(s): Igor Mammedov (niallain@gmail.com) + *              Steve French (sfrench@us.ibm.com) + *              Wang Lei (wang840925@gmail.com) + *		David Howells (dhowells@redhat.com) + * + *   The upcall wrapper used to make an arbitrary DNS query. + * + *   This function requires the appropriate userspace tool dns.upcall to be + *   installed and something like the following lines should be added to the + *   /etc/request-key.conf file: + * + *	create dns_resolver * * /sbin/dns.upcall %k + * + *   For example to use this module to query AFSDB RR: + * + *	create dns_resolver afsdb:* * /sbin/dns.afsdb %k + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/dns_resolver.h> +#include <keys/dns_resolver-type.h> +#include <keys/user-type.h> + +#include "internal.h" + +/* + * dns_query - Query the DNS + * @type: Query type (or NULL for straight host->IP lookup) + * @name: Name to look up + * @namelen: Length of name + * @options: Request options (or NULL if no options) + * @_result: Where to place the returned data. + * @_expiry: Where to store the result expiry time (or NULL) + * + * The data will be returned in the pointer at *result, and the caller is + * responsible for freeing it. + * + * The description should be of the form "[<query_type>:]<domain_name>", and + * the options need to be appropriate for the query type requested.  If no + * query_type is given, then the query is a straight hostname to IP address + * lookup. + * + * The DNS resolution lookup is performed by upcalling to userspace by way of + * requesting a key of type dns_resolver. + * + * Returns the size of the result on success, -ve error code otherwise. + */ +int dns_query(const char *type, const char *name, size_t namelen, +	      const char *options, char **_result, time_t *_expiry) +{ +	struct key *rkey; +	struct user_key_payload *upayload; +	const struct cred *saved_cred; +	size_t typelen, desclen; +	char *desc, *cp; +	int ret, len; + +	kenter("%s,%*.*s,%zu,%s", +	       type, (int)namelen, (int)namelen, name, namelen, options); + +	if (!name || namelen == 0 || !_result) +		return -EINVAL; + +	/* construct the query key description as "[<type>:]<name>" */ +	typelen = 0; +	desclen = 0; +	if (type) { +		typelen = strlen(type); +		if (typelen < 1) +			return -EINVAL; +		desclen += typelen + 1; +	} + +	if (!namelen) +		namelen = strlen(name); +	if (namelen < 3) +		return -EINVAL; +	desclen += namelen + 1; + +	desc = kmalloc(desclen, GFP_KERNEL); +	if (!desc) +		return -ENOMEM; + +	cp = desc; +	if (type) { +		memcpy(cp, type, typelen); +		cp += typelen; +		*cp++ = ':'; +	} +	memcpy(cp, name, namelen); +	cp += namelen; +	*cp = '\0'; + +	if (!options) +		options = ""; +	kdebug("call request_key(,%s,%s)", desc, options); + +	/* make the upcall, using special credentials to prevent the use of +	 * add_key() to preinstall malicious redirections +	 */ +	saved_cred = override_creds(dns_resolver_cache); +	rkey = request_key(&key_type_dns_resolver, desc, options); +	revert_creds(saved_cred); +	kfree(desc); +	if (IS_ERR(rkey)) { +		ret = PTR_ERR(rkey); +		goto out; +	} + +	down_read(&rkey->sem); +	rkey->perm |= KEY_USR_VIEW; + +	ret = key_validate(rkey); +	if (ret < 0) +		goto put; + +	upayload = rcu_dereference_protected(rkey->payload.data, +					     lockdep_is_held(&rkey->sem)); +	len = upayload->datalen; + +	ret = -ENOMEM; +	*_result = kmalloc(len + 1, GFP_KERNEL); +	if (!*_result) +		goto put; + +	memcpy(*_result, upayload->data, len + 1); +	if (_expiry) +		*_expiry = rkey->expiry; + +	ret = len; +put: +	up_read(&rkey->sem); +	key_put(rkey); +out: +	kleave(" = %d", ret); +	return ret; +} +EXPORT_SYMBOL(dns_query); diff --git a/net/dns_resolver/internal.h b/net/dns_resolver/internal.h new file mode 100644 index 00000000000..189ca9e9b78 --- /dev/null +++ b/net/dns_resolver/internal.h @@ -0,0 +1,44 @@ +/* + *   Copyright (c) 2010 Wang Lei + *   Author(s): Wang Lei (wang840925@gmail.com). All Rights Reserved. + * + *   Internal DNS Rsolver stuff + * + *   This library is free software; you can redistribute it and/or modify + *   it under the terms of the GNU Lesser General Public License as published + *   by the Free Software Foundation; either version 2.1 of the License, or + *   (at your option) any later version. + * + *   This library is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See + *   the GNU Lesser General Public License for more details. + * + *   You should have received a copy of the GNU Lesser General Public License + *   along with this library; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/sched.h> + +/* + * dns_key.c + */ +extern const struct cred *dns_resolver_cache; + +/* + * debug tracing + */ +extern unsigned dns_resolver_debug; + +#define	kdebug(FMT, ...)				\ +do {							\ +	if (unlikely(dns_resolver_debug))		\ +		printk(KERN_DEBUG "[%-6.6s] "FMT"\n",	\ +		       current->comm, ##__VA_ARGS__);	\ +} while (0) + +#define kenter(FMT, ...) kdebug("==> %s("FMT")", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) kdebug("<== %s()"FMT"", __func__, ##__VA_ARGS__)  |