aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/networking/dns_resolver.txt146
-rw-r--r--fs/cifs/Kconfig17
-rw-r--r--fs/cifs/cifsfs.c13
-rw-r--r--fs/cifs/dns_resolve.c229
-rw-r--r--fs/cifs/dns_resolve.h2
-rw-r--r--include/keys/dns_resolver-type.h23
-rw-r--r--include/linux/dns_resolver.h34
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile1
-rw-r--r--net/dns_resolver/Kconfig27
-rw-r--r--net/dns_resolver/Makefile7
-rw-r--r--net/dns_resolver/dns_key.c210
-rw-r--r--net/dns_resolver/dns_query.c159
-rw-r--r--net/dns_resolver/internal.h44
14 files changed, 708 insertions, 205 deletions
diff --git a/Documentation/networking/dns_resolver.txt b/Documentation/networking/dns_resolver.txt
new file mode 100644
index 00000000000..d8e0ce1d38c
--- /dev/null
+++ b/Documentation/networking/dns_resolver.txt
@@ -0,0 +1,146 @@
1 ===================
2 DNS Resolver Module
3 ===================
4
5Contents:
6
7 - Overview.
8 - Compilation.
9 - Setting up.
10 - Usage.
11 - Mechanism.
12 - Debugging.
13
14
15========
16OVERVIEW
17========
18
19The DNS resolver module provides a way for kernel services to make DNS queries
20by way of requesting a key of key type dns_resolver. These queries are
21upcalled to userspace through /sbin/request-key.
22
23These routines must be supported by userspace tools dns.upcall, cifs.upcall and
24request-key. It is under development and does not yet provide the full feature
25set. The features it does support include:
26
27 (*) Implements the dns_resolver key_type to contact userspace.
28
29It does not yet support the following AFS features:
30
31 (*) Dns query support for AFSDB resource record.
32
33This code is extracted from the CIFS filesystem.
34
35
36===========
37COMPILATION
38===========
39
40The module should be enabled by turning on the kernel configuration options:
41
42 CONFIG_DNS_RESOLVER - tristate "DNS Resolver support"
43
44
45==========
46SETTING UP
47==========
48
49To 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
51basic dname to IPv4/IPv6 address resolution, the following line should be
52added:
53
54 #OP TYPE DESC CO-INFO PROGRAM ARG1 ARG2 ARG3 ...
55 #====== ============ ======= ======= ==========================
56 create dns_resolver * * /usr/sbin/cifs.upcall %k
57
58To direct a query for query type 'foo', a line of the following should be added
59before the more general line given above as the first match is the one taken.
60
61 create dns_resolver foo:* * /usr/sbin/dns.foo %k
62
63
64
65=====
66USAGE
67=====
68
69To make use of this facility, one of the following functions that are
70implemented in the module can be called after doing:
71
72 #include <linux/dns_resolver.h>
73
74 (1) int dns_query(const char *type, const char *name, size_t namelen,
75 const char *options, char **_result, time_t *_expiry);
76
77 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
79 may then be cached. The key description is constructed as a string of the
80 form:
81
82 [<type>:]<name>
83
84 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
86 looked up. The default query type is a straight hostname to IP address
87 set lookup.
88
89 The name parameter is not required to be a NUL-terminated string, and its
90 length should be given by the namelen argument.
91
92 The options parameter may be NULL or it may be a set of options
93 appropriate to the query type.
94
95 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
97 IPv6 addresses. The caller must free the result.
98
99 The length of the result string is returned on success, and a -ve error
100 code is returned otherwise. -EKEYREJECTED will be returned if the DNS
101 lookup failed.
102
103 If _expiry is non-NULL, the expiry time (TTL) of the result will be
104 returned also.
105
106
107=========
108MECHANISM
109=========
110
111The dnsresolver module registers a key type called "dns_resolver". Keys of
112this type are used to transport and cache DNS lookup results from userspace.
113
114When dns_query() is invoked, it calls request_key() to search the local
115keyrings for a cached DNS result. If that fails to find one, it upcalls to
116userspace to get a new result.
117
118Upcalls to userspace are made through the request_key() upcall vector, and are
119directed by means of configuration lines in /etc/request-key.conf that tell
120/sbin/request-key what program to run to instantiate the key.
121
122The upcall handler program is responsible for querying the DNS, processing the
123result into a form suitable for passing to the keyctl_instantiate_key()
124routine. This then passes the data to dns_resolver_instantiate() which strips
125off and processes any options included in the data, and then attaches the
126remainder of the string to the key as its payload.
127
128The upcall handler program should set the expiry time on the key to that of the
129lowest TTL of all the records it has extracted a result from. This means that
130the key will be discarded and recreated when the data it holds has expired.
131
132dns_query() returns a copy of the value attached to the key, or an error if
133that is indicated instead.
134
135See <file:Documentation/keys-request-key.txt> for further information about
136request-key function.
137
138
139=========
140DEBUGGING
141=========
142
143Debugging messages can be turned on dynamically by writing a 1 into the
144following file:
145
146 /sys/module/dnsresolver/parameters/debug
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 5739fd7f88b..57f0aa9f141 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -71,14 +71,14 @@ config CIFS_WEAK_PW_HASH
71 If unsure, say N. 71 If unsure, say N.
72 72
73config CIFS_UPCALL 73config CIFS_UPCALL
74 bool "Kerberos/SPNEGO advanced session setup" 74 bool "Kerberos/SPNEGO advanced session setup"
75 depends on CIFS && KEYS 75 depends on CIFS && KEYS
76 help 76 select DNS_RESOLVER
77 Enables an upcall mechanism for CIFS which accesses 77 help
78 userspace helper utilities to provide SPNEGO packaged (RFC 4178) 78 Enables an upcall mechanism for CIFS which accesses userspace helper
79 Kerberos tickets which are needed to mount to certain secure servers 79 utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets
80 (for which more secure Kerberos authentication is required). If 80 which are needed to mount to certain secure servers (for which more
81 unsure, say N. 81 secure Kerberos authentication is required). If unsure, say N.
82 82
83config CIFS_XATTR 83config CIFS_XATTR
84 bool "CIFS extended attributes" 84 bool "CIFS extended attributes"
@@ -122,6 +122,7 @@ config CIFS_DEBUG2
122config CIFS_DFS_UPCALL 122config CIFS_DFS_UPCALL
123 bool "DFS feature support" 123 bool "DFS feature support"
124 depends on CIFS && KEYS 124 depends on CIFS && KEYS
125 select DNS_RESOLVER
125 help 126 help
126 Distributed File System (DFS) support is used to access shares 127 Distributed File System (DFS) support is used to access shares
127 transparently in an enterprise name space, even if the share 128 transparently in an enterprise name space, even if the share
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 8a2cf129e53..2a0c892959f 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -45,7 +45,6 @@
45#include "cifs_fs_sb.h" 45#include "cifs_fs_sb.h"
46#include <linux/mm.h> 46#include <linux/mm.h>
47#include <linux/key-type.h> 47#include <linux/key-type.h>
48#include "dns_resolve.h"
49#include "cifs_spnego.h" 48#include "cifs_spnego.h"
50#include "fscache.h" 49#include "fscache.h"
51#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ 50#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
@@ -934,22 +933,13 @@ init_cifs(void)
934 if (rc) 933 if (rc)
935 goto out_unregister_filesystem; 934 goto out_unregister_filesystem;
936#endif 935#endif
937#ifdef CONFIG_CIFS_DFS_UPCALL
938 rc = cifs_init_dns_resolver();
939 if (rc)
940 goto out_unregister_key_type;
941#endif
942 rc = slow_work_register_user(THIS_MODULE); 936 rc = slow_work_register_user(THIS_MODULE);
943 if (rc) 937 if (rc)
944 goto out_unregister_resolver_key; 938 goto out_unregister_key_type;
945 939
946 return 0; 940 return 0;
947 941
948 out_unregister_resolver_key:
949#ifdef CONFIG_CIFS_DFS_UPCALL
950 cifs_exit_dns_resolver();
951 out_unregister_key_type: 942 out_unregister_key_type:
952#endif
953#ifdef CONFIG_CIFS_UPCALL 943#ifdef CONFIG_CIFS_UPCALL
954 unregister_key_type(&cifs_spnego_key_type); 944 unregister_key_type(&cifs_spnego_key_type);
955 out_unregister_filesystem: 945 out_unregister_filesystem:
@@ -976,7 +966,6 @@ exit_cifs(void)
976 cifs_fscache_unregister(); 966 cifs_fscache_unregister();
977#ifdef CONFIG_CIFS_DFS_UPCALL 967#ifdef CONFIG_CIFS_DFS_UPCALL
978 cifs_dfs_release_automount_timer(); 968 cifs_dfs_release_automount_timer();
979 cifs_exit_dns_resolver();
980#endif 969#endif
981#ifdef CONFIG_CIFS_UPCALL 970#ifdef CONFIG_CIFS_UPCALL
982 unregister_key_type(&cifs_spnego_key_type); 971 unregister_key_type(&cifs_spnego_key_type);
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index aa967e7917f..0eb87026cad 100644
--- a/fs/cifs/dns_resolve.c
+++ b/fs/cifs/dns_resolve.c
@@ -4,6 +4,8 @@
4 * Copyright (c) 2007 Igor Mammedov 4 * Copyright (c) 2007 Igor Mammedov
5 * Author(s): Igor Mammedov (niallain@gmail.com) 5 * Author(s): Igor Mammedov (niallain@gmail.com)
6 * Steve French (sfrench@us.ibm.com) 6 * Steve French (sfrench@us.ibm.com)
7 * Wang Lei (wang840925@gmail.com)
8 * David Howells (dhowells@redhat.com)
7 * 9 *
8 * Contains the CIFS DFS upcall routines used for hostname to 10 * Contains the CIFS DFS upcall routines used for hostname to
9 * IP address translation. 11 * IP address translation.
@@ -24,212 +26,73 @@
24 */ 26 */
25 27
26#include <linux/slab.h> 28#include <linux/slab.h>
27#include <linux/keyctl.h> 29#include <linux/dns_resolver.h>
28#include <linux/key-type.h>
29#include <keys/user-type.h>
30#include "dns_resolve.h" 30#include "dns_resolve.h"
31#include "cifsglob.h" 31#include "cifsglob.h"
32#include "cifsproto.h" 32#include "cifsproto.h"
33#include "cifs_debug.h" 33#include "cifs_debug.h"
34 34
35static const struct cred *dns_resolver_cache; 35/**
36 36 * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
37/* Checks if supplied name is IP address 37 * @unc: UNC path specifying the server
38 * returns: 38 * @ip_addr: Where to return the IP address.
39 * 1 - name is IP 39 *
40 * 0 - name is not IP 40 * The IP address will be returned in string form, and the caller is
41 */ 41 * responsible for freeing it.
42static int 42 *
43is_ip(const char *name, int len) 43 * Returns length of result on success, -ve on error.
44{
45 struct sockaddr_storage ss;
46
47 return cifs_convert_address((struct sockaddr *)&ss, name, len);
48}
49
50static int
51dns_resolver_instantiate(struct key *key, const void *data,
52 size_t datalen)
53{
54 int rc = 0;
55 char *ip;
56
57 /* make sure this looks like an address */
58 if (!is_ip(data, datalen))
59 return -EINVAL;
60
61 ip = kmalloc(datalen + 1, GFP_KERNEL);
62 if (!ip)
63 return -ENOMEM;
64
65 memcpy(ip, data, datalen);
66 ip[datalen] = '\0';
67
68 key->type_data.x[0] = datalen;
69 key->payload.data = ip;
70
71 return rc;
72}
73
74static void
75dns_resolver_destroy(struct key *key)
76{
77 kfree(key->payload.data);
78}
79
80struct key_type key_type_dns_resolver = {
81 .name = "dns_resolver",
82 .def_datalen = sizeof(struct in_addr),
83 .describe = user_describe,
84 .instantiate = dns_resolver_instantiate,
85 .destroy = dns_resolver_destroy,
86 .match = user_match,
87};
88
89/* Resolves server name to ip address.
90 * input:
91 * unc - server UNC
92 * output:
93 * *ip_addr - pointer to server ip, caller responcible for freeing it.
94 * return the length of the returned string on success
95 */ 44 */
96int 45int
97dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) 46dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
98{ 47{
99 const struct cred *saved_cred; 48 struct sockaddr_storage ss;
100 int rc = -EAGAIN; 49 const char *hostname, *sep;
101 struct key *rkey = ERR_PTR(-EAGAIN);
102 char *name; 50 char *name;
103 char *data = NULL; 51 int len, rc;
104 int len;
105 52
106 if (!ip_addr || !unc) 53 if (!ip_addr || !unc)
107 return -EINVAL; 54 return -EINVAL;
108 55
109 /* search for server name delimiter */
110 len = strlen(unc); 56 len = strlen(unc);
111 if (len < 3) { 57 if (len < 3) {
112 cFYI(1, "%s: unc is too short: %s", __func__, unc); 58 cFYI(1, "%s: unc is too short: %s", __func__, unc);
113 return -EINVAL; 59 return -EINVAL;
114 } 60 }
115 len -= 2;
116 name = memchr(unc+2, '\\', len);
117 if (!name) {
118 cFYI(1, "%s: probably server name is whole unc: %s",
119 __func__, unc);
120 } else {
121 len = (name - unc) - 2/* leading // */;
122 }
123
124 name = kmalloc(len+1, GFP_KERNEL);
125 if (!name) {
126 rc = -ENOMEM;
127 return rc;
128 }
129 memcpy(name, unc+2, len);
130 name[len] = 0;
131
132 if (is_ip(name, len)) {
133 cFYI(1, "%s: it is IP, skipping dns upcall: %s",
134 __func__, name);
135 data = name;
136 goto skip_upcall;
137 }
138 61
139 saved_cred = override_creds(dns_resolver_cache); 62 /* Discount leading slashes for cifs */
140 rkey = request_key(&key_type_dns_resolver, name, ""); 63 len -= 2;
141 revert_creds(saved_cred); 64 hostname = unc + 2;
142 if (!IS_ERR(rkey)) {
143 if (!(rkey->perm & KEY_USR_VIEW)) {
144 down_read(&rkey->sem);
145 rkey->perm |= KEY_USR_VIEW;
146 up_read(&rkey->sem);
147 }
148 len = rkey->type_data.x[0];
149 data = rkey->payload.data;
150 } else {
151 cERROR(1, "%s: unable to resolve: %s", __func__, name);
152 goto out;
153 }
154
155skip_upcall:
156 if (data) {
157 *ip_addr = kmalloc(len + 1, GFP_KERNEL);
158 if (*ip_addr) {
159 memcpy(*ip_addr, data, len + 1);
160 if (!IS_ERR(rkey))
161 cFYI(1, "%s: resolved: %s to %s", __func__,
162 name,
163 *ip_addr
164 );
165 rc = len;
166 } else {
167 rc = -ENOMEM;
168 }
169 if (!IS_ERR(rkey))
170 key_put(rkey);
171 }
172 65
173out: 66 /* Search for server name delimiter */
174 kfree(name); 67 sep = memchr(hostname, '\\', len);
68 if (sep)
69 len = sep - unc;
70 else
71 cFYI(1, "%s: probably server name is whole unc: %s",
72 __func__, unc);
73
74 /* Try to interpret hostname as an IPv4 or IPv6 address */
75 rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len);
76 if (rc > 0)
77 goto name_is_IP_address;
78
79 /* Perform the upcall */
80 rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL);
81 if (rc < 0)
82 cERROR(1, "%s: unable to resolve: %*.*s",
83 __func__, len, len, hostname);
84 else
85 cFYI(1, "%s: resolved: %*.*s to %s",
86 __func__, len, len, hostname, *ip_addr);
175 return rc; 87 return rc;
176}
177 88
178int __init cifs_init_dns_resolver(void) 89name_is_IP_address:
179{ 90 name = kmalloc(len + 1, GFP_KERNEL);
180 struct cred *cred; 91 if (!name)
181 struct key *keyring;
182 int ret;
183
184 printk(KERN_NOTICE "Registering the %s key type\n",
185 key_type_dns_resolver.name);
186
187 /* create an override credential set with a special thread keyring in
188 * which DNS requests are cached
189 *
190 * this is used to prevent malicious redirections from being installed
191 * with add_key().
192 */
193 cred = prepare_kernel_cred(NULL);
194 if (!cred)
195 return -ENOMEM; 92 return -ENOMEM;
196 93 memcpy(name, hostname, len);
197 keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, 94 name[len] = 0;
198 (KEY_POS_ALL & ~KEY_POS_SETATTR) | 95 cFYI(1, "%s: unc is IP, skipping dns upcall: %s", __func__, name);
199 KEY_USR_VIEW | KEY_USR_READ, 96 *ip_addr = name;
200 KEY_ALLOC_NOT_IN_QUOTA);
201 if (IS_ERR(keyring)) {
202 ret = PTR_ERR(keyring);
203 goto failed_put_cred;
204 }
205
206 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
207 if (ret < 0)
208 goto failed_put_key;
209
210 ret = register_key_type(&key_type_dns_resolver);
211 if (ret < 0)
212 goto failed_put_key;
213
214 /* instruct request_key() to use this special keyring as a cache for
215 * the results it looks up */
216 cred->thread_keyring = keyring;
217 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
218 dns_resolver_cache = cred;
219 return 0; 97 return 0;
220
221failed_put_key:
222 key_put(keyring);
223failed_put_cred:
224 put_cred(cred);
225 return ret;
226}
227
228void cifs_exit_dns_resolver(void)
229{
230 key_revoke(dns_resolver_cache->thread_keyring);
231 unregister_key_type(&key_type_dns_resolver);
232 put_cred(dns_resolver_cache);
233 printk(KERN_NOTICE "Unregistered %s key type\n",
234 key_type_dns_resolver.name);
235} 98}
diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h
index 5d7f291df16..d3f5d27f4d0 100644
--- a/fs/cifs/dns_resolve.h
+++ b/fs/cifs/dns_resolve.h
@@ -24,8 +24,6 @@
24#define _DNS_RESOLVE_H 24#define _DNS_RESOLVE_H
25 25
26#ifdef __KERNEL__ 26#ifdef __KERNEL__
27extern int __init cifs_init_dns_resolver(void);
28extern void cifs_exit_dns_resolver(void);
29extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); 27extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
30#endif /* KERNEL */ 28#endif /* KERNEL */
31 29
diff --git a/include/keys/dns_resolver-type.h b/include/keys/dns_resolver-type.h
new file mode 100644
index 00000000000..9284a19393a
--- /dev/null
+++ b/include/keys/dns_resolver-type.h
@@ -0,0 +1,23 @@
1/* DNS resolver key type
2 *
3 * Copyright (C) 2010 Wang Lei. All Rights Reserved.
4 * Written by Wang Lei (wang840925@gmail.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#ifndef _KEYS_DNS_RESOLVER_TYPE_H
13#define _KEYS_DNS_RESOLVER_TYPE_H
14
15#include <linux/key-type.h>
16
17extern struct key_type key_type_dns_resolver;
18
19extern int request_dns_resolver_key(const char *description,
20 const char *callout_info,
21 char **data);
22
23#endif /* _KEYS_DNS_RESOLVER_TYPE_H */
diff --git a/include/linux/dns_resolver.h b/include/linux/dns_resolver.h
new file mode 100644
index 00000000000..cc92268af89
--- /dev/null
+++ b/include/linux/dns_resolver.h
@@ -0,0 +1,34 @@
1/*
2 * DNS Resolver upcall management for CIFS DFS and AFS
3 * Handles host name to IP address resolution and DNS query for AFSDB RR.
4 *
5 * Copyright (c) International Business Machines Corp., 2008
6 * Author(s): Steve French (sfrench@us.ibm.com)
7 * Wang Lei (wang840925@gmail.com)
8 *
9 * This library is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24#ifndef _LINUX_DNS_RESOLVER_H
25#define _LINUX_DNS_RESOLVER_H
26
27#ifdef __KERNEL__
28
29extern int dns_query(const char *type, const char *name, size_t namelen,
30 const char *options, char **_result, time_t *_expiry);
31
32#endif /* KERNEL */
33
34#endif /* _LINUX_DNS_RESOLVER_H */
diff --git a/net/Kconfig b/net/Kconfig
index e24fa0873f3..e330594d370 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -213,6 +213,7 @@ source "net/phonet/Kconfig"
213source "net/ieee802154/Kconfig" 213source "net/ieee802154/Kconfig"
214source "net/sched/Kconfig" 214source "net/sched/Kconfig"
215source "net/dcb/Kconfig" 215source "net/dcb/Kconfig"
216source "net/dns_resolver/Kconfig"
216 217
217config RPS 218config RPS
218 boolean 219 boolean
diff --git a/net/Makefile b/net/Makefile
index 41d420070a3..ea60fbce9b1 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -67,3 +67,4 @@ ifeq ($(CONFIG_NET),y)
67obj-$(CONFIG_SYSCTL) += sysctl_net.o 67obj-$(CONFIG_SYSCTL) += sysctl_net.o
68endif 68endif
69obj-$(CONFIG_WIMAX) += wimax/ 69obj-$(CONFIG_WIMAX) += wimax/
70obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/
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 @@
1#
2# Configuration for DNS Resolver
3#
4config DNS_RESOLVER
5 tristate "DNS Resolver support"
6 depends on NET && KEYS
7 help
8 Saying Y here will include support for the DNS Resolver key type
9 which can be used to make upcalls to perform DNS lookups in
10 userspace.
11
12 DNS Resolver is used to query DNS server for information. Examples
13 being resolving a UNC hostname element to an IP address for CIFS or
14 performing a DNS query for AFSDB records so that AFS can locate a
15 cell's volume location database servers.
16
17 DNS Resolver is used by the CIFS and AFS modules, and would support
18 samba4 later. DNS Resolver is supported by the userspace upcall
19 helper "/sbin/dns.resolver" via /etc/request-key.conf.
20
21 See <file:Documentation/networking/dns_resolver.txt> for further
22 information.
23
24 To compile this as a module, choose M here: the module will be called
25 dnsresolver.
26
27 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 @@
1#
2# Makefile for the Linux DNS Resolver.
3#
4
5obj-$(CONFIG_DNS_RESOLVER) += dns_resolver.o
6
7dns_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 @@
1/* Key type used to cache DNS lookups made by the kernel
2 *
3 * See Documentation/networking/dns_resolver.txt
4 *
5 * Copyright (c) 2007 Igor Mammedov
6 * Author(s): Igor Mammedov (niallain@gmail.com)
7 * Steve French (sfrench@us.ibm.com)
8 * Wang Lei (wang840925@gmail.com)
9 * David Howells (dhowells@redhat.com)
10 *
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
13 * by the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
19 * the GNU Lesser General Public License for more details.
20 *
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
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25#include <linux/module.h>
26#include <linux/moduleparam.h>
27#include <linux/slab.h>
28#include <linux/string.h>
29#include <linux/kernel.h>
30#include <linux/keyctl.h>
31#include <keys/dns_resolver-type.h>
32#include <keys/user-type.h>
33#include "internal.h"
34
35MODULE_DESCRIPTION("DNS Resolver");
36MODULE_AUTHOR("Wang Lei");
37MODULE_LICENSE("GPL");
38
39unsigned dns_resolver_debug;
40module_param_named(debug, dns_resolver_debug, uint, S_IWUSR | S_IRUGO);
41MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
42
43const struct cred *dns_resolver_cache;
44
45/*
46 * Instantiate a user defined key for dns_resolver.
47 *
48 * The data must be a NUL-terminated string, with the NUL char accounted in
49 * datalen.
50 *
51 * If the data contains a '#' characters, then we take the clause after each
52 * one to be an option of the form 'key=value'. The actual data of interest is
53 * the string leading up to the first '#'. For instance:
54 *
55 * "ip1,ip2,...#foo=bar"
56 */
57static int
58dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
59{
60 struct user_key_payload *upayload;
61 int ret;
62 size_t result_len = 0;
63 const char *data = _data, *opt;
64
65 kenter("%%%d,%s,'%s',%zu",
66 key->serial, key->description, data, datalen);
67
68 if (datalen <= 1 || !data || data[datalen - 1] != '\0')
69 return -EINVAL;
70 datalen--;
71
72 /* deal with any options embedded in the data */
73 opt = memchr(data, '#', datalen);
74 if (!opt) {
75 kdebug("no options currently supported");
76 return -EINVAL;
77 }
78
79 result_len = datalen;
80 ret = key_payload_reserve(key, result_len);
81 if (ret < 0)
82 return -EINVAL;
83
84 upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
85 if (!upayload) {
86 kleave(" = -ENOMEM");
87 return -ENOMEM;
88 }
89
90 upayload->datalen = result_len;
91 memcpy(upayload->data, data, result_len);
92 upayload->data[result_len] = '\0';
93 rcu_assign_pointer(key->payload.data, upayload);
94
95 kleave(" = 0");
96 return 0;
97}
98
99/*
100 * The description is of the form "[<type>:]<domain_name>"
101 *
102 * The domain name may be a simple name or an absolute domain name (which
103 * should end with a period). The domain name is case-independent.
104 */
105static int
106dns_resolver_match(const struct key *key, const void *description)
107{
108 int slen, dlen, ret = 0;
109 const char *src = key->description, *dsp = description;
110
111 kenter("%s,%s", src, dsp);
112
113 if (!src || !dsp)
114 goto no_match;
115
116 if (strcasecmp(src, dsp) == 0)
117 goto matched;
118
119 slen = strlen(src);
120 dlen = strlen(dsp);
121 if (slen <= 0 || dlen <= 0)
122 goto no_match;
123 if (src[slen - 1] == '.')
124 slen--;
125 if (dsp[dlen - 1] == '.')
126 dlen--;
127 if (slen != dlen || strncasecmp(src, dsp, slen) != 0)
128 goto no_match;
129
130matched:
131 ret = 1;
132no_match:
133 kleave(" = %d", ret);
134 return ret;
135}
136
137struct key_type key_type_dns_resolver = {
138 .name = "dns_resolver",
139 .instantiate = dns_resolver_instantiate,
140 .match = dns_resolver_match,
141 .revoke = user_revoke,
142 .destroy = user_destroy,
143 .describe = user_describe,
144 .read = user_read,
145};
146
147static int __init init_dns_resolver(void)
148{
149 struct cred *cred;
150 struct key *keyring;
151 int ret;
152
153 printk(KERN_NOTICE "Registering the %s key type\n",
154 key_type_dns_resolver.name);
155
156 /* create an override credential set with a special thread keyring in
157 * which DNS requests are cached
158 *
159 * this is used to prevent malicious redirections from being installed
160 * with add_key().
161 */
162 cred = prepare_kernel_cred(NULL);
163 if (!cred)
164 return -ENOMEM;
165
166 keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred,
167 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
168 KEY_USR_VIEW | KEY_USR_READ,
169 KEY_ALLOC_NOT_IN_QUOTA);
170 if (IS_ERR(keyring)) {
171 ret = PTR_ERR(keyring);
172 goto failed_put_cred;
173 }
174
175 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
176 if (ret < 0)
177 goto failed_put_key;
178
179 ret = register_key_type(&key_type_dns_resolver);
180 if (ret < 0)
181 goto failed_put_key;
182
183 /* instruct request_key() to use this special keyring as a cache for
184 * the results it looks up */
185 cred->thread_keyring = keyring;
186 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
187 dns_resolver_cache = cred;
188
189 kdebug("DNS resolver keyring: %d\n", key_serial(keyring));
190 return 0;
191
192failed_put_key:
193 key_put(keyring);
194failed_put_cred:
195 put_cred(cred);
196 return ret;
197}
198
199static void __exit exit_dns_resolver(void)
200{
201 key_revoke(dns_resolver_cache->thread_keyring);
202 unregister_key_type(&key_type_dns_resolver);
203 put_cred(dns_resolver_cache);
204 printk(KERN_NOTICE "Unregistered %s key type\n",
205 key_type_dns_resolver.name);
206}
207
208module_init(init_dns_resolver)
209module_exit(exit_dns_resolver)
210MODULE_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 @@
1/* Upcall routine, designed to work as a key type and working through
2 * /sbin/request-key to contact userspace when handling DNS queries.
3 *
4 * See Documentation/networking/dns_resolver.txt
5 *
6 * Copyright (c) 2007 Igor Mammedov
7 * Author(s): Igor Mammedov (niallain@gmail.com)
8 * Steve French (sfrench@us.ibm.com)
9 * Wang Lei (wang840925@gmail.com)
10 * David Howells (dhowells@redhat.com)
11 *
12 * The upcall wrapper used to make an arbitrary DNS query.
13 *
14 * This function requires the appropriate userspace tool dns.upcall to be
15 * installed and something like the following lines should be added to the
16 * /etc/request-key.conf file:
17 *
18 * create dns_resolver * * /sbin/dns.upcall %k
19 *
20 * For example to use this module to query AFSDB RR:
21 *
22 * create dns_resolver afsdb:* * /sbin/dns.afsdb %k
23 *
24 * This library is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU Lesser General Public License as published
26 * by the Free Software Foundation; either version 2.1 of the License, or
27 * (at your option) any later version.
28 *
29 * This library is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
32 * the GNU Lesser General Public License for more details.
33 *
34 * You should have received a copy of the GNU Lesser General Public License
35 * along with this library; if not, write to the Free Software
36 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 */
38
39#include <linux/module.h>
40#include <linux/slab.h>
41#include <linux/dns_resolver.h>
42#include <keys/dns_resolver-type.h>
43#include <keys/user-type.h>
44
45#include "internal.h"
46
47/*
48 * dns_query - Query the DNS
49 * @type: Query type (or NULL for straight host->IP lookup)
50 * @name: Name to look up
51 * @namelen: Length of name
52 * @options: Request options (or NULL if no options)
53 * @_result: Where to place the returned data.
54 * @_expiry: Where to store the result expiry time (or NULL)
55 *
56 * The data will be returned in the pointer at *result, and the caller is
57 * responsible for freeing it.
58 *
59 * The description should be of the form "[<query_type>:]<domain_name>", and
60 * the options need to be appropriate for the query type requested. If no
61 * query_type is given, then the query is a straight hostname to IP address
62 * lookup.
63 *
64 * The DNS resolution lookup is performed by upcalling to userspace by way of
65 * requesting a key of type dns_resolver.
66 *
67 * Returns the size of the result on success, -ve error code otherwise.
68 */
69int dns_query(const char *type, const char *name, size_t namelen,
70 const char *options, char **_result, time_t *_expiry)
71{
72 struct key *rkey;
73 struct user_key_payload *upayload;
74 const struct cred *saved_cred;
75 size_t typelen, desclen;
76 char *desc, *cp;
77 int ret, len;
78
79 kenter("%s,%*.*s,%zu,%s",
80 type, (int)namelen, (int)namelen, name, namelen, options);
81
82 if (!name || namelen == 0 || !_result)
83 return -EINVAL;
84
85 /* construct the query key description as "[<type>:]<name>" */
86 typelen = 0;
87 desclen = 0;
88 if (type) {
89 typelen = strlen(type);
90 if (typelen < 1)
91 return -EINVAL;
92 desclen += typelen + 1;
93 }
94
95 if (!namelen)
96 namelen = strlen(name);
97 if (namelen < 3)
98 return -EINVAL;
99 desclen += namelen + 1;
100
101 desc = kmalloc(desclen, GFP_KERNEL);
102 if (!desc)
103 return -ENOMEM;
104
105 cp = desc;
106 if (type) {
107 memcpy(cp, type, typelen);
108 cp += typelen;
109 *cp++ = ':';
110 }
111 memcpy(cp, name, namelen);
112 cp += namelen;
113 *cp = '\0';
114
115 if (!options)
116 options = "";
117 kdebug("call request_key(,%s,%s)", desc, options);
118
119 /* make the upcall, using special credentials to prevent the use of
120 * add_key() to preinstall malicious redirections
121 */
122 saved_cred = override_creds(dns_resolver_cache);
123 rkey = request_key(&key_type_dns_resolver, desc, options);
124 revert_creds(saved_cred);
125 kfree(desc);
126 if (IS_ERR(rkey)) {
127 ret = PTR_ERR(rkey);
128 goto out;
129 }
130
131 down_read(&rkey->sem);
132 rkey->perm |= KEY_USR_VIEW;
133
134 ret = key_validate(rkey);
135 if (ret < 0)
136 goto put;
137
138 upayload = rcu_dereference_protected(rkey->payload.data,
139 lockdep_is_held(&rkey->sem));
140 len = upayload->datalen;
141
142 ret = -ENOMEM;
143 *_result = kmalloc(len + 1, GFP_KERNEL);
144 if (!*_result)
145 goto put;
146
147 memcpy(*_result, upayload->data, len + 1);
148 if (_expiry)
149 *_expiry = rkey->expiry;
150
151 ret = len;
152put:
153 up_read(&rkey->sem);
154 key_put(rkey);
155out:
156 kleave(" = %d", ret);
157 return ret;
158}
159EXPORT_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 @@
1/*
2 * Copyright (c) 2010 Wang Lei
3 * Author(s): Wang Lei (wang840925@gmail.com). All Rights Reserved.
4 *
5 * Internal DNS Rsolver stuff
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/compiler.h>
23#include <linux/kernel.h>
24#include <linux/sched.h>
25
26/*
27 * dns_key.c
28 */
29extern const struct cred *dns_resolver_cache;
30
31/*
32 * debug tracing
33 */
34extern unsigned dns_resolver_debug;
35
36#define kdebug(FMT, ...) \
37do { \
38 if (unlikely(dns_resolver_debug)) \
39 printk(KERN_DEBUG "[%-6.6s] "FMT"\n", \
40 current->comm, ##__VA_ARGS__); \
41} while (0)
42
43#define kenter(FMT, ...) kdebug("==> %s("FMT")", __func__, ##__VA_ARGS__)
44#define kleave(FMT, ...) kdebug("<== %s()"FMT"", __func__, ##__VA_ARGS__)