Commit 1362fa078dae16776cd439791c6605b224ea6171

Authored by David Howells
Committed by James Morris
1 parent dd9c1549ed

DNS: Fix a NULL pointer deref when trying to read an error key [CVE-2011-1076]

When a DNS resolver key is instantiated with an error indication, attempts to
read that key will result in an oops because user_read() is expecting there to
be a payload - and there isn't one [CVE-2011-1076].

Give the DNS resolver key its own read handler that returns the error cached in
key->type_data.x[0] as an error rather than crashing.

Also make the kenter() at the beginning of dns_resolver_instantiate() limit the
amount of data it prints, since the data is not necessarily NUL-terminated.

The buggy code was added in:

	commit 4a2d789267e00b5a1175ecd2ddefcc78b83fbf09
	Author: Wang Lei <wang840925@gmail.com>
	Date:   Wed Aug 11 09:37:58 2010 +0100
	Subject: DNS: If the DNS server returns an error, allow that to be cached [ver #2]

This can trivially be reproduced by any user with the following program
compiled with -lkeyutils:

	#include <stdlib.h>
	#include <keyutils.h>
	#include <err.h>
	static char payload[] = "#dnserror=6";
	int main()
	{
		key_serial_t key;
		key = add_key("dns_resolver", "a", payload, sizeof(payload),
			      KEY_SPEC_SESSION_KEYRING);
		if (key == -1)
			err(1, "add_key");
		if (keyctl_read(key, NULL, 0) == -1)
			err(1, "read_key");
		return 0;
	}

What should happen is that keyctl_read() reports error 6 (ENXIO) to the user:

	dns-break: read_key: No such device or address

but instead the kernel oopses.

This cannot be reproduced with the 'keyutils add' or 'keyutils padd' commands
as both of those cut the data down below the NUL termination that must be
included in the data.  Without this dns_resolver_instantiate() will return
-EINVAL and the key will not be instantiated such that it can be read.

The oops looks like:

BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
IP: [<ffffffff811b99f7>] user_read+0x4f/0x8f
PGD 3bdf8067 PUD 385b9067 PMD 0
Oops: 0000 [#1] SMP
last sysfs file: /sys/devices/pci0000:00/0000:00:19.0/irq
CPU 0
Modules linked in:

Pid: 2150, comm: dns-break Not tainted 2.6.38-rc7-cachefs+ #468                  /DG965RY
RIP: 0010:[<ffffffff811b99f7>]  [<ffffffff811b99f7>] user_read+0x4f/0x8f
RSP: 0018:ffff88003bf47f08  EFLAGS: 00010246
RAX: 0000000000000001 RBX: ffff88003b5ea378 RCX: ffffffff81972368
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88003b5ea378
RBP: ffff88003bf47f28 R08: ffff88003be56620 R09: 0000000000000000
R10: 0000000000000395 R11: 0000000000000002 R12: 0000000000000000
R13: 0000000000000000 R14: 0000000000000000 R15: ffffffffffffffa1
FS:  00007feab5751700(0000) GS:ffff88003e000000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000010 CR3: 000000003de40000 CR4: 00000000000006f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process dns-break (pid: 2150, threadinfo ffff88003bf46000, task ffff88003be56090)
Stack:
 ffff88003b5ea378 ffff88003b5ea3a0 0000000000000000 0000000000000000
 ffff88003bf47f68 ffffffff811b708e ffff88003c442bc8 0000000000000000
 00000000004005a0 00007fffba368060 0000000000000000 0000000000000000
Call Trace:
 [<ffffffff811b708e>] keyctl_read_key+0xac/0xcf
 [<ffffffff811b7c07>] sys_keyctl+0x75/0xb6
 [<ffffffff81001f7b>] system_call_fastpath+0x16/0x1b
Code: 75 1f 48 83 7b 28 00 75 18 c6 05 58 2b fb 00 01 be bb 00 00 00 48 c7 c7 76 1c 75 81 e8 13 c2 e9 ff 4c 8b b3 e0 00 00 00 4d 85 ed <41> 0f b7 5e 10 74 2d 4d 85 e4 74 28 e8 98 79 ee ff 49 39 dd 48
RIP  [<ffffffff811b99f7>] user_read+0x4f/0x8f
 RSP <ffff88003bf47f08>
CR2: 0000000000000010

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Jeff Layton <jlayton@redhat.com>
cc: Wang Lei <wang840925@gmail.com>
Signed-off-by: James Morris <jmorris@namei.org>

Showing 2 changed files with 25 additions and 4 deletions Inline Diff

Documentation/networking/dns_resolver.txt
1 =================== 1 ===================
2 DNS Resolver Module 2 DNS Resolver Module
3 =================== 3 ===================
4 4
5 Contents: 5 Contents:
6 6
7 - Overview. 7 - Overview.
8 - Compilation. 8 - Compilation.
9 - Setting up. 9 - Setting up.
10 - Usage. 10 - Usage.
11 - Mechanism. 11 - Mechanism.
12 - Debugging. 12 - Debugging.
13 13
14 14
15 ======== 15 ========
16 OVERVIEW 16 OVERVIEW
17 ======== 17 ========
18 18
19 The DNS resolver module provides a way for kernel services to make DNS queries 19 The DNS resolver module provides a way for kernel services to make DNS queries
20 by way of requesting a key of key type dns_resolver. These queries are 20 by way of requesting a key of key type dns_resolver. These queries are
21 upcalled to userspace through /sbin/request-key. 21 upcalled to userspace through /sbin/request-key.
22 22
23 These routines must be supported by userspace tools dns.upcall, cifs.upcall and 23 These routines must be supported by userspace tools dns.upcall, cifs.upcall and
24 request-key. It is under development and does not yet provide the full feature 24 request-key. It is under development and does not yet provide the full feature
25 set. The features it does support include: 25 set. The features it does support include:
26 26
27 (*) Implements the dns_resolver key_type to contact userspace. 27 (*) Implements the dns_resolver key_type to contact userspace.
28 28
29 It does not yet support the following AFS features: 29 It does not yet support the following AFS features:
30 30
31 (*) Dns query support for AFSDB resource record. 31 (*) Dns query support for AFSDB resource record.
32 32
33 This code is extracted from the CIFS filesystem. 33 This code is extracted from the CIFS filesystem.
34 34
35 35
36 =========== 36 ===========
37 COMPILATION 37 COMPILATION
38 =========== 38 ===========
39 39
40 The module should be enabled by turning on the kernel configuration options: 40 The module should be enabled by turning on the kernel configuration options:
41 41
42 CONFIG_DNS_RESOLVER - tristate "DNS Resolver support" 42 CONFIG_DNS_RESOLVER - tristate "DNS Resolver support"
43 43
44 44
45 ========== 45 ==========
46 SETTING UP 46 SETTING UP
47 ========== 47 ==========
48 48
49 To set up this facility, the /etc/request-key.conf file must be altered so that 49 To set up this facility, the /etc/request-key.conf file must be altered so that
50 /sbin/request-key can appropriately direct the upcalls. For example, to handle 50 /sbin/request-key can appropriately direct the upcalls. For example, to handle
51 basic dname to IPv4/IPv6 address resolution, the following line should be 51 basic dname to IPv4/IPv6 address resolution, the following line should be
52 added: 52 added:
53 53
54 #OP TYPE DESC CO-INFO PROGRAM ARG1 ARG2 ARG3 ... 54 #OP TYPE DESC CO-INFO PROGRAM ARG1 ARG2 ARG3 ...
55 #====== ============ ======= ======= ========================== 55 #====== ============ ======= ======= ==========================
56 create dns_resolver * * /usr/sbin/cifs.upcall %k 56 create dns_resolver * * /usr/sbin/cifs.upcall %k
57 57
58 To direct a query for query type 'foo', a line of the following should be added 58 To direct a query for query type 'foo', a line of the following should be added
59 before the more general line given above as the first match is the one taken. 59 before the more general line given above as the first match is the one taken.
60 60
61 create dns_resolver foo:* * /usr/sbin/dns.foo %k 61 create dns_resolver foo:* * /usr/sbin/dns.foo %k
62 62
63 63
64
65 ===== 64 =====
66 USAGE 65 USAGE
67 ===== 66 =====
68 67
69 To make use of this facility, one of the following functions that are 68 To make use of this facility, one of the following functions that are
70 implemented in the module can be called after doing: 69 implemented in the module can be called after doing:
71 70
72 #include <linux/dns_resolver.h> 71 #include <linux/dns_resolver.h>
73 72
74 (1) int dns_query(const char *type, const char *name, size_t namelen, 73 (1) int dns_query(const char *type, const char *name, size_t namelen,
75 const char *options, char **_result, time_t *_expiry); 74 const char *options, char **_result, time_t *_expiry);
76 75
77 This is the basic access function. It looks for a cached DNS query and if 76 This is the basic access function. It looks for a cached DNS query and if
78 it doesn't find it, it upcalls to userspace to make a new DNS query, which 77 it doesn't find it, it upcalls to userspace to make a new DNS query, which
79 may then be cached. The key description is constructed as a string of the 78 may then be cached. The key description is constructed as a string of the
80 form: 79 form:
81 80
82 [<type>:]<name> 81 [<type>:]<name>
83 82
84 where <type> optionally specifies the particular upcall program to invoke, 83 where <type> optionally specifies the particular upcall program to invoke,
85 and thus the type of query to do, and <name> specifies the string to be 84 and thus the type of query to do, and <name> specifies the string to be
86 looked up. The default query type is a straight hostname to IP address 85 looked up. The default query type is a straight hostname to IP address
87 set lookup. 86 set lookup.
88 87
89 The name parameter is not required to be a NUL-terminated string, and its 88 The name parameter is not required to be a NUL-terminated string, and its
90 length should be given by the namelen argument. 89 length should be given by the namelen argument.
91 90
92 The options parameter may be NULL or it may be a set of options 91 The options parameter may be NULL or it may be a set of options
93 appropriate to the query type. 92 appropriate to the query type.
94 93
95 The return value is a string appropriate to the query type. For instance, 94 The return value is a string appropriate to the query type. For instance,
96 for the default query type it is just a list of comma-separated IPv4 and 95 for the default query type it is just a list of comma-separated IPv4 and
97 IPv6 addresses. The caller must free the result. 96 IPv6 addresses. The caller must free the result.
98 97
99 The length of the result string is returned on success, and a negative 98 The length of the result string is returned on success, and a negative
100 error code is returned otherwise. -EKEYREJECTED will be returned if the 99 error code is returned otherwise. -EKEYREJECTED will be returned if the
101 DNS lookup failed. 100 DNS lookup failed.
102 101
103 If _expiry is non-NULL, the expiry time (TTL) of the result will be 102 If _expiry is non-NULL, the expiry time (TTL) of the result will be
104 returned also. 103 returned also.
104
105
106 ===============================
107 READING DNS KEYS FROM USERSPACE
108 ===============================
109
110 Keys of dns_resolver type can be read from userspace using keyctl_read() or
111 "keyctl read/print/pipe".
105 112
106 113
107 ========= 114 =========
108 MECHANISM 115 MECHANISM
109 ========= 116 =========
110 117
111 The dnsresolver module registers a key type called "dns_resolver". Keys of 118 The dnsresolver module registers a key type called "dns_resolver". Keys of
112 this type are used to transport and cache DNS lookup results from userspace. 119 this type are used to transport and cache DNS lookup results from userspace.
113 120
114 When dns_query() is invoked, it calls request_key() to search the local 121 When dns_query() is invoked, it calls request_key() to search the local
115 keyrings for a cached DNS result. If that fails to find one, it upcalls to 122 keyrings for a cached DNS result. If that fails to find one, it upcalls to
116 userspace to get a new result. 123 userspace to get a new result.
117 124
118 Upcalls to userspace are made through the request_key() upcall vector, and are 125 Upcalls to userspace are made through the request_key() upcall vector, and are
119 directed by means of configuration lines in /etc/request-key.conf that tell 126 directed by means of configuration lines in /etc/request-key.conf that tell
120 /sbin/request-key what program to run to instantiate the key. 127 /sbin/request-key what program to run to instantiate the key.
121 128
122 The upcall handler program is responsible for querying the DNS, processing the 129 The upcall handler program is responsible for querying the DNS, processing the
123 result into a form suitable for passing to the keyctl_instantiate_key() 130 result into a form suitable for passing to the keyctl_instantiate_key()
124 routine. This then passes the data to dns_resolver_instantiate() which strips 131 routine. This then passes the data to dns_resolver_instantiate() which strips
125 off and processes any options included in the data, and then attaches the 132 off and processes any options included in the data, and then attaches the
126 remainder of the string to the key as its payload. 133 remainder of the string to the key as its payload.
127 134
128 The upcall handler program should set the expiry time on the key to that of the 135 The upcall handler program should set the expiry time on the key to that of the
129 lowest TTL of all the records it has extracted a result from. This means that 136 lowest TTL of all the records it has extracted a result from. This means that
130 the key will be discarded and recreated when the data it holds has expired. 137 the key will be discarded and recreated when the data it holds has expired.
131 138
132 dns_query() returns a copy of the value attached to the key, or an error if 139 dns_query() returns a copy of the value attached to the key, or an error if
133 that is indicated instead. 140 that is indicated instead.
134 141
135 See <file:Documentation/keys-request-key.txt> for further information about 142 See <file:Documentation/keys-request-key.txt> for further information about
136 request-key function. 143 request-key function.
137 144
138 145
139 ========= 146 =========
140 DEBUGGING 147 DEBUGGING
141 ========= 148 =========
142 149
143 Debugging messages can be turned on dynamically by writing a 1 into the 150 Debugging messages can be turned on dynamically by writing a 1 into the
144 following file: 151 following file:
145 152
146 /sys/module/dnsresolver/parameters/debug 153 /sys/module/dnsresolver/parameters/debug
net/dns_resolver/dns_key.c
1 /* Key type used to cache DNS lookups made by the kernel 1 /* Key type used to cache DNS lookups made by the kernel
2 * 2 *
3 * See Documentation/networking/dns_resolver.txt 3 * See Documentation/networking/dns_resolver.txt
4 * 4 *
5 * Copyright (c) 2007 Igor Mammedov 5 * Copyright (c) 2007 Igor Mammedov
6 * Author(s): Igor Mammedov (niallain@gmail.com) 6 * Author(s): Igor Mammedov (niallain@gmail.com)
7 * Steve French (sfrench@us.ibm.com) 7 * Steve French (sfrench@us.ibm.com)
8 * Wang Lei (wang840925@gmail.com) 8 * Wang Lei (wang840925@gmail.com)
9 * David Howells (dhowells@redhat.com) 9 * David Howells (dhowells@redhat.com)
10 * 10 *
11 * This library is free software; you can redistribute it and/or modify 11 * This library is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published 12 * it under the terms of the GNU Lesser General Public License as published
13 * by the Free Software Foundation; either version 2.1 of the License, or 13 * by the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version. 14 * (at your option) any later version.
15 * 15 *
16 * This library is distributed in the hope that it will be useful, 16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
19 * the GNU Lesser General Public License for more details. 19 * the GNU Lesser General Public License for more details.
20 * 20 *
21 * You should have received a copy of the GNU Lesser General Public License 21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this library; if not, write to the Free Software 22 * along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */ 24 */
25 #include <linux/module.h> 25 #include <linux/module.h>
26 #include <linux/moduleparam.h> 26 #include <linux/moduleparam.h>
27 #include <linux/slab.h> 27 #include <linux/slab.h>
28 #include <linux/string.h> 28 #include <linux/string.h>
29 #include <linux/kernel.h> 29 #include <linux/kernel.h>
30 #include <linux/keyctl.h> 30 #include <linux/keyctl.h>
31 #include <linux/err.h> 31 #include <linux/err.h>
32 #include <linux/seq_file.h> 32 #include <linux/seq_file.h>
33 #include <keys/dns_resolver-type.h> 33 #include <keys/dns_resolver-type.h>
34 #include <keys/user-type.h> 34 #include <keys/user-type.h>
35 #include "internal.h" 35 #include "internal.h"
36 36
37 MODULE_DESCRIPTION("DNS Resolver"); 37 MODULE_DESCRIPTION("DNS Resolver");
38 MODULE_AUTHOR("Wang Lei"); 38 MODULE_AUTHOR("Wang Lei");
39 MODULE_LICENSE("GPL"); 39 MODULE_LICENSE("GPL");
40 40
41 unsigned dns_resolver_debug; 41 unsigned dns_resolver_debug;
42 module_param_named(debug, dns_resolver_debug, uint, S_IWUSR | S_IRUGO); 42 module_param_named(debug, dns_resolver_debug, uint, S_IWUSR | S_IRUGO);
43 MODULE_PARM_DESC(debug, "DNS Resolver debugging mask"); 43 MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
44 44
45 const struct cred *dns_resolver_cache; 45 const struct cred *dns_resolver_cache;
46 46
47 #define DNS_ERRORNO_OPTION "dnserror" 47 #define DNS_ERRORNO_OPTION "dnserror"
48 48
49 /* 49 /*
50 * Instantiate a user defined key for dns_resolver. 50 * Instantiate a user defined key for dns_resolver.
51 * 51 *
52 * The data must be a NUL-terminated string, with the NUL char accounted in 52 * The data must be a NUL-terminated string, with the NUL char accounted in
53 * datalen. 53 * datalen.
54 * 54 *
55 * If the data contains a '#' characters, then we take the clause after each 55 * If the data contains a '#' characters, then we take the clause after each
56 * one to be an option of the form 'key=value'. The actual data of interest is 56 * one to be an option of the form 'key=value'. The actual data of interest is
57 * the string leading up to the first '#'. For instance: 57 * the string leading up to the first '#'. For instance:
58 * 58 *
59 * "ip1,ip2,...#foo=bar" 59 * "ip1,ip2,...#foo=bar"
60 */ 60 */
61 static int 61 static int
62 dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) 62 dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
63 { 63 {
64 struct user_key_payload *upayload; 64 struct user_key_payload *upayload;
65 unsigned long derrno; 65 unsigned long derrno;
66 int ret; 66 int ret;
67 size_t result_len = 0; 67 size_t result_len = 0;
68 const char *data = _data, *end, *opt; 68 const char *data = _data, *end, *opt;
69 69
70 kenter("%%%d,%s,'%s',%zu", 70 kenter("%%%d,%s,'%*.*s',%zu",
71 key->serial, key->description, data, datalen); 71 key->serial, key->description,
72 (int)datalen, (int)datalen, data, datalen);
72 73
73 if (datalen <= 1 || !data || data[datalen - 1] != '\0') 74 if (datalen <= 1 || !data || data[datalen - 1] != '\0')
74 return -EINVAL; 75 return -EINVAL;
75 datalen--; 76 datalen--;
76 77
77 /* deal with any options embedded in the data */ 78 /* deal with any options embedded in the data */
78 end = data + datalen; 79 end = data + datalen;
79 opt = memchr(data, '#', datalen); 80 opt = memchr(data, '#', datalen);
80 if (!opt) { 81 if (!opt) {
81 /* no options: the entire data is the result */ 82 /* no options: the entire data is the result */
82 kdebug("no options"); 83 kdebug("no options");
83 result_len = datalen; 84 result_len = datalen;
84 } else { 85 } else {
85 const char *next_opt; 86 const char *next_opt;
86 87
87 result_len = opt - data; 88 result_len = opt - data;
88 opt++; 89 opt++;
89 kdebug("options: '%s'", opt); 90 kdebug("options: '%s'", opt);
90 do { 91 do {
91 const char *eq; 92 const char *eq;
92 int opt_len, opt_nlen, opt_vlen, tmp; 93 int opt_len, opt_nlen, opt_vlen, tmp;
93 94
94 next_opt = memchr(opt, '#', end - opt) ?: end; 95 next_opt = memchr(opt, '#', end - opt) ?: end;
95 opt_len = next_opt - opt; 96 opt_len = next_opt - opt;
96 if (!opt_len) { 97 if (!opt_len) {
97 printk(KERN_WARNING 98 printk(KERN_WARNING
98 "Empty option to dns_resolver key %d\n", 99 "Empty option to dns_resolver key %d\n",
99 key->serial); 100 key->serial);
100 return -EINVAL; 101 return -EINVAL;
101 } 102 }
102 103
103 eq = memchr(opt, '=', opt_len) ?: end; 104 eq = memchr(opt, '=', opt_len) ?: end;
104 opt_nlen = eq - opt; 105 opt_nlen = eq - opt;
105 eq++; 106 eq++;
106 opt_vlen = next_opt - eq; /* will be -1 if no value */ 107 opt_vlen = next_opt - eq; /* will be -1 if no value */
107 108
108 tmp = opt_vlen >= 0 ? opt_vlen : 0; 109 tmp = opt_vlen >= 0 ? opt_vlen : 0;
109 kdebug("option '%*.*s' val '%*.*s'", 110 kdebug("option '%*.*s' val '%*.*s'",
110 opt_nlen, opt_nlen, opt, tmp, tmp, eq); 111 opt_nlen, opt_nlen, opt, tmp, tmp, eq);
111 112
112 /* see if it's an error number representing a DNS error 113 /* see if it's an error number representing a DNS error
113 * that's to be recorded as the result in this key */ 114 * that's to be recorded as the result in this key */
114 if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 && 115 if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 &&
115 memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) { 116 memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) {
116 kdebug("dns error number option"); 117 kdebug("dns error number option");
117 if (opt_vlen <= 0) 118 if (opt_vlen <= 0)
118 goto bad_option_value; 119 goto bad_option_value;
119 120
120 ret = strict_strtoul(eq, 10, &derrno); 121 ret = strict_strtoul(eq, 10, &derrno);
121 if (ret < 0) 122 if (ret < 0)
122 goto bad_option_value; 123 goto bad_option_value;
123 124
124 if (derrno < 1 || derrno > 511) 125 if (derrno < 1 || derrno > 511)
125 goto bad_option_value; 126 goto bad_option_value;
126 127
127 kdebug("dns error no. = %lu", derrno); 128 kdebug("dns error no. = %lu", derrno);
128 key->type_data.x[0] = -derrno; 129 key->type_data.x[0] = -derrno;
129 continue; 130 continue;
130 } 131 }
131 132
132 bad_option_value: 133 bad_option_value:
133 printk(KERN_WARNING 134 printk(KERN_WARNING
134 "Option '%*.*s' to dns_resolver key %d:" 135 "Option '%*.*s' to dns_resolver key %d:"
135 " bad/missing value\n", 136 " bad/missing value\n",
136 opt_nlen, opt_nlen, opt, key->serial); 137 opt_nlen, opt_nlen, opt, key->serial);
137 return -EINVAL; 138 return -EINVAL;
138 } while (opt = next_opt + 1, opt < end); 139 } while (opt = next_opt + 1, opt < end);
139 } 140 }
140 141
141 /* don't cache the result if we're caching an error saying there's no 142 /* don't cache the result if we're caching an error saying there's no
142 * result */ 143 * result */
143 if (key->type_data.x[0]) { 144 if (key->type_data.x[0]) {
144 kleave(" = 0 [h_error %ld]", key->type_data.x[0]); 145 kleave(" = 0 [h_error %ld]", key->type_data.x[0]);
145 return 0; 146 return 0;
146 } 147 }
147 148
148 kdebug("store result"); 149 kdebug("store result");
149 ret = key_payload_reserve(key, result_len); 150 ret = key_payload_reserve(key, result_len);
150 if (ret < 0) 151 if (ret < 0)
151 return -EINVAL; 152 return -EINVAL;
152 153
153 upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL); 154 upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
154 if (!upayload) { 155 if (!upayload) {
155 kleave(" = -ENOMEM"); 156 kleave(" = -ENOMEM");
156 return -ENOMEM; 157 return -ENOMEM;
157 } 158 }
158 159
159 upayload->datalen = result_len; 160 upayload->datalen = result_len;
160 memcpy(upayload->data, data, result_len); 161 memcpy(upayload->data, data, result_len);
161 upayload->data[result_len] = '\0'; 162 upayload->data[result_len] = '\0';
162 rcu_assign_pointer(key->payload.data, upayload); 163 rcu_assign_pointer(key->payload.data, upayload);
163 164
164 kleave(" = 0"); 165 kleave(" = 0");
165 return 0; 166 return 0;
166 } 167 }
167 168
168 /* 169 /*
169 * The description is of the form "[<type>:]<domain_name>" 170 * The description is of the form "[<type>:]<domain_name>"
170 * 171 *
171 * The domain name may be a simple name or an absolute domain name (which 172 * The domain name may be a simple name or an absolute domain name (which
172 * should end with a period). The domain name is case-independent. 173 * should end with a period). The domain name is case-independent.
173 */ 174 */
174 static int 175 static int
175 dns_resolver_match(const struct key *key, const void *description) 176 dns_resolver_match(const struct key *key, const void *description)
176 { 177 {
177 int slen, dlen, ret = 0; 178 int slen, dlen, ret = 0;
178 const char *src = key->description, *dsp = description; 179 const char *src = key->description, *dsp = description;
179 180
180 kenter("%s,%s", src, dsp); 181 kenter("%s,%s", src, dsp);
181 182
182 if (!src || !dsp) 183 if (!src || !dsp)
183 goto no_match; 184 goto no_match;
184 185
185 if (strcasecmp(src, dsp) == 0) 186 if (strcasecmp(src, dsp) == 0)
186 goto matched; 187 goto matched;
187 188
188 slen = strlen(src); 189 slen = strlen(src);
189 dlen = strlen(dsp); 190 dlen = strlen(dsp);
190 if (slen <= 0 || dlen <= 0) 191 if (slen <= 0 || dlen <= 0)
191 goto no_match; 192 goto no_match;
192 if (src[slen - 1] == '.') 193 if (src[slen - 1] == '.')
193 slen--; 194 slen--;
194 if (dsp[dlen - 1] == '.') 195 if (dsp[dlen - 1] == '.')
195 dlen--; 196 dlen--;
196 if (slen != dlen || strncasecmp(src, dsp, slen) != 0) 197 if (slen != dlen || strncasecmp(src, dsp, slen) != 0)
197 goto no_match; 198 goto no_match;
198 199
199 matched: 200 matched:
200 ret = 1; 201 ret = 1;
201 no_match: 202 no_match:
202 kleave(" = %d", ret); 203 kleave(" = %d", ret);
203 return ret; 204 return ret;
204 } 205 }
205 206
206 /* 207 /*
207 * Describe a DNS key 208 * Describe a DNS key
208 */ 209 */
209 static void dns_resolver_describe(const struct key *key, struct seq_file *m) 210 static void dns_resolver_describe(const struct key *key, struct seq_file *m)
210 { 211 {
211 int err = key->type_data.x[0]; 212 int err = key->type_data.x[0];
212 213
213 seq_puts(m, key->description); 214 seq_puts(m, key->description);
214 if (err) 215 if (err)
215 seq_printf(m, ": %d", err); 216 seq_printf(m, ": %d", err);
216 else 217 else
217 seq_printf(m, ": %u", key->datalen); 218 seq_printf(m, ": %u", key->datalen);
218 } 219 }
219 220
221 /*
222 * read the DNS data
223 * - the key's semaphore is read-locked
224 */
225 static long dns_resolver_read(const struct key *key,
226 char __user *buffer, size_t buflen)
227 {
228 if (key->type_data.x[0])
229 return key->type_data.x[0];
230
231 return user_read(key, buffer, buflen);
232 }
233
220 struct key_type key_type_dns_resolver = { 234 struct key_type key_type_dns_resolver = {
221 .name = "dns_resolver", 235 .name = "dns_resolver",
222 .instantiate = dns_resolver_instantiate, 236 .instantiate = dns_resolver_instantiate,
223 .match = dns_resolver_match, 237 .match = dns_resolver_match,
224 .revoke = user_revoke, 238 .revoke = user_revoke,
225 .destroy = user_destroy, 239 .destroy = user_destroy,
226 .describe = dns_resolver_describe, 240 .describe = dns_resolver_describe,
227 .read = user_read, 241 .read = dns_resolver_read,
228 }; 242 };
229 243
230 static int __init init_dns_resolver(void) 244 static int __init init_dns_resolver(void)
231 { 245 {
232 struct cred *cred; 246 struct cred *cred;
233 struct key *keyring; 247 struct key *keyring;
234 int ret; 248 int ret;
235 249
236 printk(KERN_NOTICE "Registering the %s key type\n", 250 printk(KERN_NOTICE "Registering the %s key type\n",
237 key_type_dns_resolver.name); 251 key_type_dns_resolver.name);
238 252
239 /* create an override credential set with a special thread keyring in 253 /* create an override credential set with a special thread keyring in
240 * which DNS requests are cached 254 * which DNS requests are cached
241 * 255 *
242 * this is used to prevent malicious redirections from being installed 256 * this is used to prevent malicious redirections from being installed
243 * with add_key(). 257 * with add_key().
244 */ 258 */
245 cred = prepare_kernel_cred(NULL); 259 cred = prepare_kernel_cred(NULL);
246 if (!cred) 260 if (!cred)
247 return -ENOMEM; 261 return -ENOMEM;
248 262
249 keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, 263 keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred,
250 (KEY_POS_ALL & ~KEY_POS_SETATTR) | 264 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
251 KEY_USR_VIEW | KEY_USR_READ, 265 KEY_USR_VIEW | KEY_USR_READ,
252 KEY_ALLOC_NOT_IN_QUOTA); 266 KEY_ALLOC_NOT_IN_QUOTA);
253 if (IS_ERR(keyring)) { 267 if (IS_ERR(keyring)) {
254 ret = PTR_ERR(keyring); 268 ret = PTR_ERR(keyring);
255 goto failed_put_cred; 269 goto failed_put_cred;
256 } 270 }
257 271
258 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); 272 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
259 if (ret < 0) 273 if (ret < 0)
260 goto failed_put_key; 274 goto failed_put_key;
261 275
262 ret = register_key_type(&key_type_dns_resolver); 276 ret = register_key_type(&key_type_dns_resolver);
263 if (ret < 0) 277 if (ret < 0)
264 goto failed_put_key; 278 goto failed_put_key;
265 279
266 /* instruct request_key() to use this special keyring as a cache for 280 /* instruct request_key() to use this special keyring as a cache for
267 * the results it looks up */ 281 * the results it looks up */
268 cred->thread_keyring = keyring; 282 cred->thread_keyring = keyring;
269 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; 283 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
270 dns_resolver_cache = cred; 284 dns_resolver_cache = cred;
271 285
272 kdebug("DNS resolver keyring: %d\n", key_serial(keyring)); 286 kdebug("DNS resolver keyring: %d\n", key_serial(keyring));
273 return 0; 287 return 0;
274 288
275 failed_put_key: 289 failed_put_key:
276 key_put(keyring); 290 key_put(keyring);
277 failed_put_cred: 291 failed_put_cred:
278 put_cred(cred); 292 put_cred(cred);
279 return ret; 293 return ret;
280 } 294 }
281 295
282 static void __exit exit_dns_resolver(void) 296 static void __exit exit_dns_resolver(void)
283 { 297 {
284 key_revoke(dns_resolver_cache->thread_keyring); 298 key_revoke(dns_resolver_cache->thread_keyring);
285 unregister_key_type(&key_type_dns_resolver); 299 unregister_key_type(&key_type_dns_resolver);
286 put_cred(dns_resolver_cache); 300 put_cred(dns_resolver_cache);
287 printk(KERN_NOTICE "Unregistered %s key type\n", 301 printk(KERN_NOTICE "Unregistered %s key type\n",
288 key_type_dns_resolver.name); 302 key_type_dns_resolver.name);
289 } 303 }
290 304
291 module_init(init_dns_resolver) 305 module_init(init_dns_resolver)
292 module_exit(exit_dns_resolver) 306 module_exit(exit_dns_resolver)
293 MODULE_LICENSE("GPL"); 307 MODULE_LICENSE("GPL");
294 308