Blame view

net/dns_resolver/dns_key.c 8.2 KB
1a4240f47   Wang Lei   DNS: Separate out...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /* 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
c057b190b   Jeff Kirsher   net/*: Fix FSF ad...
22
   *   along with this library; if not, see <http://www.gnu.org/licenses/>.
1a4240f47   Wang Lei   DNS: Separate out...
23
24
25
26
27
   */
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/slab.h>
  #include <linux/string.h>
c7a936b1d   Eric Biggers   KEYS: DNS: limit ...
28
  #include <linux/ratelimit.h>
1a4240f47   Wang Lei   DNS: Separate out...
29
30
  #include <linux/kernel.h>
  #include <linux/keyctl.h>
af352fe96   Stephen Rothwell   cifs: Include lin...
31
  #include <linux/err.h>
4a2d78926   Wang Lei   DNS: If the DNS s...
32
  #include <linux/seq_file.h>
1a4240f47   Wang Lei   DNS: Separate out...
33
34
35
36
37
38
39
  #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");
95c961747   Eric Dumazet   net: cleanup unsi...
40
  unsigned int dns_resolver_debug;
1a4240f47   Wang Lei   DNS: Separate out...
41
42
43
44
  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;
4a2d78926   Wang Lei   DNS: If the DNS s...
45
  #define	DNS_ERRORNO_OPTION	"dnserror"
1a4240f47   Wang Lei   DNS: Separate out...
46
  /*
d46d49421   David Howells   KEYS: DNS: Use ke...
47
   * Preparse instantiation data for a dns_resolver key.
1a4240f47   Wang Lei   DNS: Separate out...
48
49
50
51
52
53
54
55
56
57
58
   *
   * 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
d46d49421   David Howells   KEYS: DNS: Use ke...
59
  dns_resolver_preparse(struct key_preparsed_payload *prep)
1a4240f47   Wang Lei   DNS: Separate out...
60
61
  {
  	struct user_key_payload *upayload;
4a2d78926   Wang Lei   DNS: If the DNS s...
62
  	unsigned long derrno;
1a4240f47   Wang Lei   DNS: Separate out...
63
  	int ret;
d46d49421   David Howells   KEYS: DNS: Use ke...
64
  	int datalen = prep->datalen, result_len = 0;
cf7f601c0   David Howells   KEYS: Add payload...
65
  	const char *data = prep->data, *end, *opt;
1a4240f47   Wang Lei   DNS: Separate out...
66

d46d49421   David Howells   KEYS: DNS: Use ke...
67
  	kenter("'%*.*s',%u", datalen, datalen, data, datalen);
1a4240f47   Wang Lei   DNS: Separate out...
68
69
70
71
72
73
  
  	if (datalen <= 1 || !data || data[datalen - 1] != '\0')
  		return -EINVAL;
  	datalen--;
  
  	/* deal with any options embedded in the data */
4a2d78926   Wang Lei   DNS: If the DNS s...
74
  	end = data + datalen;
1a4240f47   Wang Lei   DNS: Separate out...
75
76
  	opt = memchr(data, '#', datalen);
  	if (!opt) {
4a2d78926   Wang Lei   DNS: If the DNS s...
77
78
79
80
81
82
83
84
85
86
  		/* 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 {
d9bb71d76   Eric Biggers   KEYS: DNS: fix pa...
87
  			int opt_len, opt_nlen;
4a2d78926   Wang Lei   DNS: If the DNS s...
88
  			const char *eq;
d9bb71d76   Eric Biggers   KEYS: DNS: fix pa...
89
  			char optval[128];
4a2d78926   Wang Lei   DNS: If the DNS s...
90
91
92
  
  			next_opt = memchr(opt, '#', end - opt) ?: end;
  			opt_len = next_opt - opt;
d9bb71d76   Eric Biggers   KEYS: DNS: fix pa...
93
  			if (opt_len <= 0 || opt_len > sizeof(optval)) {
c7a936b1d   Eric Biggers   KEYS: DNS: limit ...
94
95
96
  				pr_warn_ratelimited("Invalid option length (%d) for dns_resolver key
  ",
  						    opt_len);
4a2d78926   Wang Lei   DNS: If the DNS s...
97
98
  				return -EINVAL;
  			}
d9bb71d76   Eric Biggers   KEYS: DNS: fix pa...
99
100
101
102
103
104
105
106
107
108
  			eq = memchr(opt, '=', opt_len);
  			if (eq) {
  				opt_nlen = eq - opt;
  				eq++;
  				memcpy(optval, eq, next_opt - eq);
  				optval[next_opt - eq] = '\0';
  			} else {
  				opt_nlen = opt_len;
  				optval[0] = '\0';
  			}
4a2d78926   Wang Lei   DNS: If the DNS s...
109

d9bb71d76   Eric Biggers   KEYS: DNS: fix pa...
110
111
  			kdebug("option '%*.*s' val '%s'",
  			       opt_nlen, opt_nlen, opt, optval);
4a2d78926   Wang Lei   DNS: If the DNS s...
112
113
114
115
116
117
  
  			/* 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");
4a2d78926   Wang Lei   DNS: If the DNS s...
118

d9bb71d76   Eric Biggers   KEYS: DNS: fix pa...
119
  				ret = kstrtoul(optval, 10, &derrno);
4a2d78926   Wang Lei   DNS: If the DNS s...
120
121
122
123
124
125
126
  				if (ret < 0)
  					goto bad_option_value;
  
  				if (derrno < 1 || derrno > 511)
  					goto bad_option_value;
  
  				kdebug("dns error no. = %lu", derrno);
146aa8b14   David Howells   KEYS: Merge the t...
127
  				prep->payload.data[dns_key_error] = ERR_PTR(-derrno);
4a2d78926   Wang Lei   DNS: If the DNS s...
128
129
130
131
  				continue;
  			}
  
  		bad_option_value:
c7a936b1d   Eric Biggers   KEYS: DNS: limit ...
132
133
134
  			pr_warn_ratelimited("Option '%*.*s' to dns_resolver key: bad/missing value
  ",
  					    opt_nlen, opt_nlen, opt);
4a2d78926   Wang Lei   DNS: If the DNS s...
135
136
  			return -EINVAL;
  		} while (opt = next_opt + 1, opt < end);
1a4240f47   Wang Lei   DNS: Separate out...
137
  	}
4a2d78926   Wang Lei   DNS: If the DNS s...
138
139
  	/* don't cache the result if we're caching an error saying there's no
  	 * result */
146aa8b14   David Howells   KEYS: Merge the t...
140
141
  	if (prep->payload.data[dns_key_error]) {
  		kleave(" = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error]));
4a2d78926   Wang Lei   DNS: If the DNS s...
142
143
144
145
  		return 0;
  	}
  
  	kdebug("store result");
d46d49421   David Howells   KEYS: DNS: Use ke...
146
  	prep->quotalen = result_len;
1a4240f47   Wang Lei   DNS: Separate out...
147
148
149
150
151
152
153
154
155
156
  
  	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';
1a4240f47   Wang Lei   DNS: Separate out...
157

146aa8b14   David Howells   KEYS: Merge the t...
158
  	prep->payload.data[dns_key_data] = upayload;
1a4240f47   Wang Lei   DNS: Separate out...
159
160
161
162
163
  	kleave(" = 0");
  	return 0;
  }
  
  /*
d46d49421   David Howells   KEYS: DNS: Use ke...
164
165
166
167
168
169
   * Clean up the preparse data
   */
  static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
  {
  	pr_devel("==>%s()
  ", __func__);
146aa8b14   David Howells   KEYS: Merge the t...
170
  	kfree(prep->payload.data[dns_key_data]);
d46d49421   David Howells   KEYS: DNS: Use ke...
171
172
173
  }
  
  /*
1a4240f47   Wang Lei   DNS: Separate out...
174
175
176
177
178
   * 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.
   */
0c903ab64   David Howells   KEYS: Make the ke...
179
180
  static bool dns_resolver_cmp(const struct key *key,
  			     const struct key_match_data *match_data)
1a4240f47   Wang Lei   DNS: Separate out...
181
182
  {
  	int slen, dlen, ret = 0;
462919591   David Howells   KEYS: Preparse ma...
183
  	const char *src = key->description, *dsp = match_data->raw_data;
1a4240f47   Wang Lei   DNS: Separate out...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  
  	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;
  }
4a2d78926   Wang Lei   DNS: If the DNS s...
210
  /*
c06cfb08b   David Howells   KEYS: Remove key_...
211
212
213
214
215
216
217
218
219
220
   * Preparse the match criterion.
   */
  static int dns_resolver_match_preparse(struct key_match_data *match_data)
  {
  	match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
  	match_data->cmp = dns_resolver_cmp;
  	return 0;
  }
  
  /*
4a2d78926   Wang Lei   DNS: If the DNS s...
221
222
223
224
   * Describe a DNS key
   */
  static void dns_resolver_describe(const struct key *key, struct seq_file *m)
  {
4a2d78926   Wang Lei   DNS: If the DNS s...
225
  	seq_puts(m, key->description);
363b02dab   David Howells   KEYS: Fix race be...
226
  	if (key_is_positive(key)) {
146aa8b14   David Howells   KEYS: Merge the t...
227
  		int err = PTR_ERR(key->payload.data[dns_key_error]);
78b7280cc   David Howells   KEYS: Improve /pr...
228
229
230
231
232
  		if (err)
  			seq_printf(m, ": %d", err);
  		else
  			seq_printf(m, ": %u", key->datalen);
  	}
4a2d78926   Wang Lei   DNS: If the DNS s...
233
  }
1362fa078   David Howells   DNS: Fix a NULL p...
234
235
236
237
238
239
240
  /*
   * read the DNS data
   * - the key's semaphore is read-locked
   */
  static long dns_resolver_read(const struct key *key,
  			      char __user *buffer, size_t buflen)
  {
146aa8b14   David Howells   KEYS: Merge the t...
241
242
243
244
  	int err = PTR_ERR(key->payload.data[dns_key_error]);
  
  	if (err)
  		return err;
1362fa078   David Howells   DNS: Fix a NULL p...
245
246
247
  
  	return user_read(key, buffer, buflen);
  }
1a4240f47   Wang Lei   DNS: Separate out...
248
249
  struct key_type key_type_dns_resolver = {
  	.name		= "dns_resolver",
d46d49421   David Howells   KEYS: DNS: Use ke...
250
251
252
  	.preparse	= dns_resolver_preparse,
  	.free_preparse	= dns_resolver_free_preparse,
  	.instantiate	= generic_key_instantiate,
c06cfb08b   David Howells   KEYS: Remove key_...
253
  	.match_preparse	= dns_resolver_match_preparse,
1a4240f47   Wang Lei   DNS: Separate out...
254
255
  	.revoke		= user_revoke,
  	.destroy	= user_destroy,
4a2d78926   Wang Lei   DNS: If the DNS s...
256
  	.describe	= dns_resolver_describe,
1362fa078   David Howells   DNS: Fix a NULL p...
257
  	.read		= dns_resolver_read,
1a4240f47   Wang Lei   DNS: Separate out...
258
259
260
261
262
263
264
  };
  
  static int __init init_dns_resolver(void)
  {
  	struct cred *cred;
  	struct key *keyring;
  	int ret;
1a4240f47   Wang Lei   DNS: Separate out...
265
266
267
268
269
270
271
272
273
  	/* 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;
2a74dbb9a   Linus Torvalds   Merge branch 'for...
274
275
  	keyring = keyring_alloc(".dns_resolver",
  				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
f8aa23a55   David Howells   KEYS: Use keyring...
276
277
  				(KEY_POS_ALL & ~KEY_POS_SETATTR) |
  				KEY_USR_VIEW | KEY_USR_READ,
5ac7eace2   David Howells   KEYS: Add a facil...
278
  				KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
1a4240f47   Wang Lei   DNS: Separate out...
279
280
281
282
  	if (IS_ERR(keyring)) {
  		ret = PTR_ERR(keyring);
  		goto failed_put_cred;
  	}
1a4240f47   Wang Lei   DNS: Separate out...
283
284
285
286
287
288
  	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 */
700920eb5   David Howells   KEYS: Allow speci...
289
  	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
1a4240f47   Wang Lei   DNS: Separate out...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  	cred->thread_keyring = keyring;
  	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
  	dns_resolver_cache = cred;
  
  	kdebug("DNS resolver keyring: %d
  ", 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);
1a4240f47   Wang Lei   DNS: Separate out...
310
311
312
313
314
  }
  
  module_init(init_dns_resolver)
  module_exit(exit_dns_resolver)
  MODULE_LICENSE("GPL");
f8aa23a55   David Howells   KEYS: Use keyring...
315