xref: /openbmc/linux/net/dns_resolver/dns_key.c (revision 1188f7f111c61394ec56beb8e30322305a8220b6)
11a4240f4SWang Lei /* Key type used to cache DNS lookups made by the kernel
21a4240f4SWang Lei  *
39dfe1361SMauro Carvalho Chehab  * See Documentation/networking/dns_resolver.rst
41a4240f4SWang Lei  *
51a4240f4SWang Lei  *   Copyright (c) 2007 Igor Mammedov
61a4240f4SWang Lei  *   Author(s): Igor Mammedov (niallain@gmail.com)
71a4240f4SWang Lei  *              Steve French (sfrench@us.ibm.com)
81a4240f4SWang Lei  *              Wang Lei (wang840925@gmail.com)
91a4240f4SWang Lei  *		David Howells (dhowells@redhat.com)
101a4240f4SWang Lei  *
111a4240f4SWang Lei  *   This library is free software; you can redistribute it and/or modify
121a4240f4SWang Lei  *   it under the terms of the GNU Lesser General Public License as published
131a4240f4SWang Lei  *   by the Free Software Foundation; either version 2.1 of the License, or
141a4240f4SWang Lei  *   (at your option) any later version.
151a4240f4SWang Lei  *
161a4240f4SWang Lei  *   This library is distributed in the hope that it will be useful,
171a4240f4SWang Lei  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
181a4240f4SWang Lei  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
191a4240f4SWang Lei  *   the GNU Lesser General Public License for more details.
201a4240f4SWang Lei  *
211a4240f4SWang Lei  *   You should have received a copy of the GNU Lesser General Public License
22c057b190SJeff Kirsher  *   along with this library; if not, see <http://www.gnu.org/licenses/>.
231a4240f4SWang Lei  */
241a4240f4SWang Lei #include <linux/module.h>
251a4240f4SWang Lei #include <linux/moduleparam.h>
261a4240f4SWang Lei #include <linux/slab.h>
271a4240f4SWang Lei #include <linux/string.h>
281a4240f4SWang Lei #include <linux/kernel.h>
291a4240f4SWang Lei #include <linux/keyctl.h>
30af352fe9SStephen Rothwell #include <linux/err.h>
314a2d7892SWang Lei #include <linux/seq_file.h>
32bbb4c432SDavid Howells #include <linux/dns_resolver.h>
331a4240f4SWang Lei #include <keys/dns_resolver-type.h>
341a4240f4SWang Lei #include <keys/user-type.h>
351a4240f4SWang Lei #include "internal.h"
361a4240f4SWang Lei 
371a4240f4SWang Lei MODULE_DESCRIPTION("DNS Resolver");
381a4240f4SWang Lei MODULE_AUTHOR("Wang Lei");
391a4240f4SWang Lei MODULE_LICENSE("GPL");
401a4240f4SWang Lei 
4195c96174SEric Dumazet unsigned int dns_resolver_debug;
42d6444062SJoe Perches module_param_named(debug, dns_resolver_debug, uint, 0644);
431a4240f4SWang Lei MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
441a4240f4SWang Lei 
451a4240f4SWang Lei const struct cred *dns_resolver_cache;
461a4240f4SWang Lei 
474a2d7892SWang Lei #define	DNS_ERRORNO_OPTION	"dnserror"
484a2d7892SWang Lei 
491a4240f4SWang Lei /*
50d46d4942SDavid Howells  * Preparse instantiation data for a dns_resolver key.
511a4240f4SWang Lei  *
52bbb4c432SDavid Howells  * For normal hostname lookups, the data must be a NUL-terminated string, with
53bbb4c432SDavid Howells  * the NUL char accounted in datalen.
541a4240f4SWang Lei  *
551a4240f4SWang Lei  * If the data contains a '#' characters, then we take the clause after each
561a4240f4SWang Lei  * one to be an option of the form 'key=value'.  The actual data of interest is
571a4240f4SWang Lei  * the string leading up to the first '#'.  For instance:
581a4240f4SWang Lei  *
591a4240f4SWang Lei  *        "ip1,ip2,...#foo=bar"
60bbb4c432SDavid Howells  *
61bbb4c432SDavid Howells  * For server list requests, the data must begin with a NUL char and be
62bbb4c432SDavid Howells  * followed by a byte indicating the version of the data format.  Version 1
63bbb4c432SDavid Howells  * looks something like (note this is packed):
64bbb4c432SDavid Howells  *
65bbb4c432SDavid Howells  *	u8      Non-string marker (ie. 0)
66bbb4c432SDavid Howells  *	u8	Content (DNS_PAYLOAD_IS_*)
67bbb4c432SDavid Howells  *	u8	Version (e.g. 1)
68bbb4c432SDavid Howells  *	u8	Source of server list
69bbb4c432SDavid Howells  *	u8	Lookup status of server list
70bbb4c432SDavid Howells  *	u8	Number of servers
71bbb4c432SDavid Howells  *	foreach-server {
72bbb4c432SDavid Howells  *		__le16	Name length
73bbb4c432SDavid Howells  *		__le16	Priority (as per SRV record, low first)
74bbb4c432SDavid Howells  *		__le16	Weight (as per SRV record, higher first)
75bbb4c432SDavid Howells  *		__le16	Port
76bbb4c432SDavid Howells  *		u8	Source of address list
77bbb4c432SDavid Howells  *		u8	Lookup status of address list
78bbb4c432SDavid Howells  *		u8	Protocol (DNS_SERVER_PROTOCOL_*)
79bbb4c432SDavid Howells  *		u8	Number of addresses
80bbb4c432SDavid Howells  *		char[]	Name (not NUL-terminated)
81bbb4c432SDavid Howells  *		foreach-address {
82bbb4c432SDavid Howells  *			u8		Family (DNS_ADDRESS_IS_*)
83bbb4c432SDavid Howells  *			union {
84bbb4c432SDavid Howells  *				u8[4]	ipv4_addr
85bbb4c432SDavid Howells  *				u8[16]	ipv6_addr
86bbb4c432SDavid Howells  *			}
87bbb4c432SDavid Howells  *		}
88bbb4c432SDavid Howells  *	}
89bbb4c432SDavid Howells  *
901a4240f4SWang Lei  */
911a4240f4SWang Lei static int
dns_resolver_preparse(struct key_preparsed_payload * prep)92d46d4942SDavid Howells dns_resolver_preparse(struct key_preparsed_payload *prep)
931a4240f4SWang Lei {
941a4240f4SWang Lei 	struct user_key_payload *upayload;
954a2d7892SWang Lei 	unsigned long derrno;
961a4240f4SWang Lei 	int ret;
97d46d4942SDavid Howells 	int datalen = prep->datalen, result_len = 0;
98cf7f601cSDavid Howells 	const char *data = prep->data, *end, *opt;
991a4240f4SWang Lei 
100bbb4c432SDavid Howells 	if (datalen <= 1 || !data)
101bbb4c432SDavid Howells 		return -EINVAL;
102bbb4c432SDavid Howells 
103bbb4c432SDavid Howells 	if (data[0] == 0) {
104da893651SEdward Adam Davis 		const struct dns_server_list_v1_header *v1;
105da893651SEdward Adam Davis 
106bbb4c432SDavid Howells 		/* It may be a server list. */
107*06173edfSDavid Howells 		if (datalen < sizeof(*v1))
108bbb4c432SDavid Howells 			return -EINVAL;
109bbb4c432SDavid Howells 
110da893651SEdward Adam Davis 		v1 = (const struct dns_server_list_v1_header *)data;
111da893651SEdward Adam Davis 		kenter("[%u,%u],%u", v1->hdr.content, v1->hdr.version, datalen);
112da893651SEdward Adam Davis 		if (v1->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST) {
113bbb4c432SDavid Howells 			pr_warn_ratelimited(
114bbb4c432SDavid Howells 				"dns_resolver: Unsupported content type (%u)\n",
115da893651SEdward Adam Davis 				v1->hdr.content);
116bbb4c432SDavid Howells 			return -EINVAL;
117bbb4c432SDavid Howells 		}
118bbb4c432SDavid Howells 
119da893651SEdward Adam Davis 		if (v1->hdr.version != 1) {
120bbb4c432SDavid Howells 			pr_warn_ratelimited(
121bbb4c432SDavid Howells 				"dns_resolver: Unsupported server list version (%u)\n",
122da893651SEdward Adam Davis 				v1->hdr.version);
123bbb4c432SDavid Howells 			return -EINVAL;
124bbb4c432SDavid Howells 		}
125bbb4c432SDavid Howells 
126afc360e8SDavid Howells 		if ((v1->status != DNS_LOOKUP_GOOD &&
127afc360e8SDavid Howells 		     v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) {
128afc360e8SDavid Howells 			if (prep->expiry == TIME64_MAX)
129afc360e8SDavid Howells 				prep->expiry = ktime_get_real_seconds() + 1;
130afc360e8SDavid Howells 		}
131afc360e8SDavid Howells 
132bbb4c432SDavid Howells 		result_len = datalen;
133bbb4c432SDavid Howells 		goto store_result;
134bbb4c432SDavid Howells 	}
135bbb4c432SDavid Howells 
136d46d4942SDavid Howells 	kenter("'%*.*s',%u", datalen, datalen, data, datalen);
1371a4240f4SWang Lei 
138bbb4c432SDavid Howells 	if (!data || data[datalen - 1] != '\0')
1391a4240f4SWang Lei 		return -EINVAL;
1401a4240f4SWang Lei 	datalen--;
1411a4240f4SWang Lei 
1421a4240f4SWang Lei 	/* deal with any options embedded in the data */
1434a2d7892SWang Lei 	end = data + datalen;
1441a4240f4SWang Lei 	opt = memchr(data, '#', datalen);
1451a4240f4SWang Lei 	if (!opt) {
1464a2d7892SWang Lei 		/* no options: the entire data is the result */
1474a2d7892SWang Lei 		kdebug("no options");
1484a2d7892SWang Lei 		result_len = datalen;
1494a2d7892SWang Lei 	} else {
1504a2d7892SWang Lei 		const char *next_opt;
1514a2d7892SWang Lei 
1524a2d7892SWang Lei 		result_len = opt - data;
1534a2d7892SWang Lei 		opt++;
1544a2d7892SWang Lei 		kdebug("options: '%s'", opt);
1554a2d7892SWang Lei 		do {
156c604cb76SEric Biggers 			int opt_len, opt_nlen;
1574a2d7892SWang Lei 			const char *eq;
158c604cb76SEric Biggers 			char optval[128];
1594a2d7892SWang Lei 
1604a2d7892SWang Lei 			next_opt = memchr(opt, '#', end - opt) ?: end;
1614a2d7892SWang Lei 			opt_len = next_opt - opt;
162c604cb76SEric Biggers 			if (opt_len <= 0 || opt_len > sizeof(optval)) {
1639c438d7aSEric Biggers 				pr_warn_ratelimited("Invalid option length (%d) for dns_resolver key\n",
1649c438d7aSEric Biggers 						    opt_len);
1651a4240f4SWang Lei 				return -EINVAL;
1661a4240f4SWang Lei 			}
1671a4240f4SWang Lei 
168c604cb76SEric Biggers 			eq = memchr(opt, '=', opt_len);
169c604cb76SEric Biggers 			if (eq) {
1704a2d7892SWang Lei 				opt_nlen = eq - opt;
1714a2d7892SWang Lei 				eq++;
172c604cb76SEric Biggers 				memcpy(optval, eq, next_opt - eq);
173c604cb76SEric Biggers 				optval[next_opt - eq] = '\0';
174c604cb76SEric Biggers 			} else {
175c604cb76SEric Biggers 				opt_nlen = opt_len;
176c604cb76SEric Biggers 				optval[0] = '\0';
177c604cb76SEric Biggers 			}
1784a2d7892SWang Lei 
179c604cb76SEric Biggers 			kdebug("option '%*.*s' val '%s'",
180c604cb76SEric Biggers 			       opt_nlen, opt_nlen, opt, optval);
1814a2d7892SWang Lei 
1824a2d7892SWang Lei 			/* see if it's an error number representing a DNS error
1834a2d7892SWang Lei 			 * that's to be recorded as the result in this key */
1844a2d7892SWang Lei 			if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 &&
1854a2d7892SWang Lei 			    memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) {
1864a2d7892SWang Lei 				kdebug("dns error number option");
1874a2d7892SWang Lei 
188c604cb76SEric Biggers 				ret = kstrtoul(optval, 10, &derrno);
1894a2d7892SWang Lei 				if (ret < 0)
1904a2d7892SWang Lei 					goto bad_option_value;
1914a2d7892SWang Lei 
1924a2d7892SWang Lei 				if (derrno < 1 || derrno > 511)
1934a2d7892SWang Lei 					goto bad_option_value;
1944a2d7892SWang Lei 
1954a2d7892SWang Lei 				kdebug("dns error no. = %lu", derrno);
196146aa8b1SDavid Howells 				prep->payload.data[dns_key_error] = ERR_PTR(-derrno);
1974a2d7892SWang Lei 				continue;
1984a2d7892SWang Lei 			}
1994a2d7892SWang Lei 
2004a2d7892SWang Lei 		bad_option_value:
2019c438d7aSEric Biggers 			pr_warn_ratelimited("Option '%*.*s' to dns_resolver key: bad/missing value\n",
202d46d4942SDavid Howells 					    opt_nlen, opt_nlen, opt);
2034a2d7892SWang Lei 			return -EINVAL;
2044a2d7892SWang Lei 		} while (opt = next_opt + 1, opt < end);
2054a2d7892SWang Lei 	}
2064a2d7892SWang Lei 
2074a2d7892SWang Lei 	/* don't cache the result if we're caching an error saying there's no
2084a2d7892SWang Lei 	 * result */
209146aa8b1SDavid Howells 	if (prep->payload.data[dns_key_error]) {
210146aa8b1SDavid Howells 		kleave(" = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error]));
2114a2d7892SWang Lei 		return 0;
2124a2d7892SWang Lei 	}
2134a2d7892SWang Lei 
214bbb4c432SDavid Howells store_result:
2154a2d7892SWang Lei 	kdebug("store result");
216d46d4942SDavid Howells 	prep->quotalen = result_len;
2171a4240f4SWang Lei 
2181a4240f4SWang Lei 	upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
2191a4240f4SWang Lei 	if (!upayload) {
2201a4240f4SWang Lei 		kleave(" = -ENOMEM");
2211a4240f4SWang Lei 		return -ENOMEM;
2221a4240f4SWang Lei 	}
2231a4240f4SWang Lei 
2241a4240f4SWang Lei 	upayload->datalen = result_len;
2251a4240f4SWang Lei 	memcpy(upayload->data, data, result_len);
2261a4240f4SWang Lei 	upayload->data[result_len] = '\0';
2271a4240f4SWang Lei 
228146aa8b1SDavid Howells 	prep->payload.data[dns_key_data] = upayload;
2291a4240f4SWang Lei 	kleave(" = 0");
2301a4240f4SWang Lei 	return 0;
2311a4240f4SWang Lei }
2321a4240f4SWang Lei 
2331a4240f4SWang Lei /*
234d46d4942SDavid Howells  * Clean up the preparse data
235d46d4942SDavid Howells  */
dns_resolver_free_preparse(struct key_preparsed_payload * prep)236d46d4942SDavid Howells static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
237d46d4942SDavid Howells {
238d46d4942SDavid Howells 	pr_devel("==>%s()\n", __func__);
239d46d4942SDavid Howells 
240146aa8b1SDavid Howells 	kfree(prep->payload.data[dns_key_data]);
241d46d4942SDavid Howells }
242d46d4942SDavid Howells 
243d46d4942SDavid Howells /*
2441a4240f4SWang Lei  * The description is of the form "[<type>:]<domain_name>"
2451a4240f4SWang Lei  *
2461a4240f4SWang Lei  * The domain name may be a simple name or an absolute domain name (which
2471a4240f4SWang Lei  * should end with a period).  The domain name is case-independent.
2481a4240f4SWang Lei  */
dns_resolver_cmp(const struct key * key,const struct key_match_data * match_data)2490c903ab6SDavid Howells static bool dns_resolver_cmp(const struct key *key,
25046291959SDavid Howells 			     const struct key_match_data *match_data)
2511a4240f4SWang Lei {
2521a4240f4SWang Lei 	int slen, dlen, ret = 0;
25346291959SDavid Howells 	const char *src = key->description, *dsp = match_data->raw_data;
2541a4240f4SWang Lei 
2551a4240f4SWang Lei 	kenter("%s,%s", src, dsp);
2561a4240f4SWang Lei 
2571a4240f4SWang Lei 	if (!src || !dsp)
2581a4240f4SWang Lei 		goto no_match;
2591a4240f4SWang Lei 
2601a4240f4SWang Lei 	if (strcasecmp(src, dsp) == 0)
2611a4240f4SWang Lei 		goto matched;
2621a4240f4SWang Lei 
2631a4240f4SWang Lei 	slen = strlen(src);
2641a4240f4SWang Lei 	dlen = strlen(dsp);
2651a4240f4SWang Lei 	if (slen <= 0 || dlen <= 0)
2661a4240f4SWang Lei 		goto no_match;
2671a4240f4SWang Lei 	if (src[slen - 1] == '.')
2681a4240f4SWang Lei 		slen--;
2691a4240f4SWang Lei 	if (dsp[dlen - 1] == '.')
2701a4240f4SWang Lei 		dlen--;
2711a4240f4SWang Lei 	if (slen != dlen || strncasecmp(src, dsp, slen) != 0)
2721a4240f4SWang Lei 		goto no_match;
2731a4240f4SWang Lei 
2741a4240f4SWang Lei matched:
2751a4240f4SWang Lei 	ret = 1;
2761a4240f4SWang Lei no_match:
2771a4240f4SWang Lei 	kleave(" = %d", ret);
2781a4240f4SWang Lei 	return ret;
2791a4240f4SWang Lei }
2801a4240f4SWang Lei 
2814a2d7892SWang Lei /*
282c06cfb08SDavid Howells  * Preparse the match criterion.
283c06cfb08SDavid Howells  */
dns_resolver_match_preparse(struct key_match_data * match_data)284c06cfb08SDavid Howells static int dns_resolver_match_preparse(struct key_match_data *match_data)
285c06cfb08SDavid Howells {
286c06cfb08SDavid Howells 	match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
287c06cfb08SDavid Howells 	match_data->cmp = dns_resolver_cmp;
288c06cfb08SDavid Howells 	return 0;
289c06cfb08SDavid Howells }
290c06cfb08SDavid Howells 
291c06cfb08SDavid Howells /*
2924a2d7892SWang Lei  * Describe a DNS key
2934a2d7892SWang Lei  */
dns_resolver_describe(const struct key * key,struct seq_file * m)2944a2d7892SWang Lei static void dns_resolver_describe(const struct key *key, struct seq_file *m)
2954a2d7892SWang Lei {
2964a2d7892SWang Lei 	seq_puts(m, key->description);
297363b02daSDavid Howells 	if (key_is_positive(key)) {
298146aa8b1SDavid Howells 		int err = PTR_ERR(key->payload.data[dns_key_error]);
299146aa8b1SDavid Howells 
3004a2d7892SWang Lei 		if (err)
3014a2d7892SWang Lei 			seq_printf(m, ": %d", err);
3024a2d7892SWang Lei 		else
3034a2d7892SWang Lei 			seq_printf(m, ": %u", key->datalen);
3044a2d7892SWang Lei 	}
30578b7280cSDavid Howells }
3064a2d7892SWang Lei 
3071362fa07SDavid Howells /*
3081362fa07SDavid Howells  * read the DNS data
3091362fa07SDavid Howells  * - the key's semaphore is read-locked
3101362fa07SDavid Howells  */
dns_resolver_read(const struct key * key,char * buffer,size_t buflen)3111362fa07SDavid Howells static long dns_resolver_read(const struct key *key,
312d3ec10aaSWaiman Long 			      char *buffer, size_t buflen)
3131362fa07SDavid Howells {
314146aa8b1SDavid Howells 	int err = PTR_ERR(key->payload.data[dns_key_error]);
315146aa8b1SDavid Howells 
316146aa8b1SDavid Howells 	if (err)
317146aa8b1SDavid Howells 		return err;
3181362fa07SDavid Howells 
3191362fa07SDavid Howells 	return user_read(key, buffer, buflen);
3201362fa07SDavid Howells }
3211362fa07SDavid Howells 
3221a4240f4SWang Lei struct key_type key_type_dns_resolver = {
3231a4240f4SWang Lei 	.name		= "dns_resolver",
324afc360e8SDavid Howells 	.flags		= KEY_TYPE_NET_DOMAIN | KEY_TYPE_INSTANT_REAP,
325d46d4942SDavid Howells 	.preparse	= dns_resolver_preparse,
326d46d4942SDavid Howells 	.free_preparse	= dns_resolver_free_preparse,
327d46d4942SDavid Howells 	.instantiate	= generic_key_instantiate,
328c06cfb08SDavid Howells 	.match_preparse	= dns_resolver_match_preparse,
3291a4240f4SWang Lei 	.revoke		= user_revoke,
3301a4240f4SWang Lei 	.destroy	= user_destroy,
3314a2d7892SWang Lei 	.describe	= dns_resolver_describe,
3321362fa07SDavid Howells 	.read		= dns_resolver_read,
3331a4240f4SWang Lei };
3341a4240f4SWang Lei 
init_dns_resolver(void)3351a4240f4SWang Lei static int __init init_dns_resolver(void)
3361a4240f4SWang Lei {
3371a4240f4SWang Lei 	struct cred *cred;
3381a4240f4SWang Lei 	struct key *keyring;
3391a4240f4SWang Lei 	int ret;
3401a4240f4SWang Lei 
3411a4240f4SWang Lei 	/* create an override credential set with a special thread keyring in
3421a4240f4SWang Lei 	 * which DNS requests are cached
3431a4240f4SWang Lei 	 *
3441a4240f4SWang Lei 	 * this is used to prevent malicious redirections from being installed
3451a4240f4SWang Lei 	 * with add_key().
3461a4240f4SWang Lei 	 */
3475a17f040SKees Cook 	cred = prepare_kernel_cred(&init_task);
3481a4240f4SWang Lei 	if (!cred)
3491a4240f4SWang Lei 		return -ENOMEM;
3501a4240f4SWang Lei 
3512a74dbb9SLinus Torvalds 	keyring = keyring_alloc(".dns_resolver",
352c6089735SEric W. Biederman 				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
353028db3e2SLinus Torvalds 				(KEY_POS_ALL & ~KEY_POS_SETATTR) |
354028db3e2SLinus Torvalds 				KEY_USR_VIEW | KEY_USR_READ,
3555ac7eaceSDavid Howells 				KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
3561a4240f4SWang Lei 	if (IS_ERR(keyring)) {
3571a4240f4SWang Lei 		ret = PTR_ERR(keyring);
3581a4240f4SWang Lei 		goto failed_put_cred;
3591a4240f4SWang Lei 	}
3601a4240f4SWang Lei 
3611a4240f4SWang Lei 	ret = register_key_type(&key_type_dns_resolver);
3621a4240f4SWang Lei 	if (ret < 0)
3631a4240f4SWang Lei 		goto failed_put_key;
3641a4240f4SWang Lei 
3651a4240f4SWang Lei 	/* instruct request_key() to use this special keyring as a cache for
3661a4240f4SWang Lei 	 * the results it looks up */
367700920ebSDavid Howells 	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
3681a4240f4SWang Lei 	cred->thread_keyring = keyring;
3691a4240f4SWang Lei 	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
3701a4240f4SWang Lei 	dns_resolver_cache = cred;
3711a4240f4SWang Lei 
3721a4240f4SWang Lei 	kdebug("DNS resolver keyring: %d\n", key_serial(keyring));
3731a4240f4SWang Lei 	return 0;
3741a4240f4SWang Lei 
3751a4240f4SWang Lei failed_put_key:
3761a4240f4SWang Lei 	key_put(keyring);
3771a4240f4SWang Lei failed_put_cred:
3781a4240f4SWang Lei 	put_cred(cred);
3791a4240f4SWang Lei 	return ret;
3801a4240f4SWang Lei }
3811a4240f4SWang Lei 
exit_dns_resolver(void)3821a4240f4SWang Lei static void __exit exit_dns_resolver(void)
3831a4240f4SWang Lei {
3841a4240f4SWang Lei 	key_revoke(dns_resolver_cache->thread_keyring);
3851a4240f4SWang Lei 	unregister_key_type(&key_type_dns_resolver);
3861a4240f4SWang Lei 	put_cred(dns_resolver_cache);
3871a4240f4SWang Lei }
3881a4240f4SWang Lei 
3891a4240f4SWang Lei module_init(init_dns_resolver)
3901a4240f4SWang Lei module_exit(exit_dns_resolver)
3911a4240f4SWang Lei MODULE_LICENSE("GPL");
392