Commit 1a4240f4764ac78adbf4b0ebb49b3bd8c72ffa11

Authored by Wang Lei
Committed by Steve French
1 parent ba5dadbf4e

DNS: Separate out CIFS DNS Resolver code

Separate out the DNS resolver key type from the CIFS filesystem into its own
module so that it can be made available for general use, including the AFS
filesystem module.

This facility makes it possible for the kernel to upcall to userspace to have
it issue DNS requests, package up the replies and present them to the kernel
in a useful form.  The kernel is then able to cache the DNS replies as keys
can be retained in keyrings.

Resolver keys are of type "dns_resolver" and have a case-insensitive
description that is of the form "[<type>:]<domain_name>".  The optional <type>
indicates the particular DNS lookup and packaging that's required.  The
<domain_name> is the query to be made.

If <type> isn't given, a basic hostname to IP address lookup is made, and the
result is stored in the key in the form of a printable string consisting of a
comma-separated list of IPv4 and IPv6 addresses.

This key type is supported by userspace helpers driven from /sbin/request-key
and configured through /etc/request-key.conf.  The cifs.upcall utility is
invoked for UNC path server name to IP address resolution.

The CIFS functionality is encapsulated by the dns_resolve_unc_to_ip() function,
which is used to resolve a UNC path to an IP address for CIFS filesystem.  This
part remains in the CIFS module for now.

See the added Documentation/networking/dns_resolver.txt for more information.

Signed-off-by: Wang Lei <wang840925@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>

Showing 14 changed files with 708 additions and 205 deletions Side-by-side Diff

Documentation/networking/dns_resolver.txt
  1 + ===================
  2 + DNS Resolver Module
  3 + ===================
  4 +
  5 +Contents:
  6 +
  7 + - Overview.
  8 + - Compilation.
  9 + - Setting up.
  10 + - Usage.
  11 + - Mechanism.
  12 + - Debugging.
  13 +
  14 +
  15 +========
  16 +OVERVIEW
  17 +========
  18 +
  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
  21 +upcalled to userspace through /sbin/request-key.
  22 +
  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
  25 +set. The features it does support include:
  26 +
  27 + (*) Implements the dns_resolver key_type to contact userspace.
  28 +
  29 +It does not yet support the following AFS features:
  30 +
  31 + (*) Dns query support for AFSDB resource record.
  32 +
  33 +This code is extracted from the CIFS filesystem.
  34 +
  35 +
  36 +===========
  37 +COMPILATION
  38 +===========
  39 +
  40 +The module should be enabled by turning on the kernel configuration options:
  41 +
  42 + CONFIG_DNS_RESOLVER - tristate "DNS Resolver support"
  43 +
  44 +
  45 +==========
  46 +SETTING UP
  47 +==========
  48 +
  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
  51 +basic dname to IPv4/IPv6 address resolution, the following line should be
  52 +added:
  53 +
  54 + #OP TYPE DESC CO-INFO PROGRAM ARG1 ARG2 ARG3 ...
  55 + #====== ============ ======= ======= ==========================
  56 + create dns_resolver * * /usr/sbin/cifs.upcall %k
  57 +
  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.
  60 +
  61 + create dns_resolver foo:* * /usr/sbin/dns.foo %k
  62 +
  63 +
  64 +
  65 +=====
  66 +USAGE
  67 +=====
  68 +
  69 +To make use of this facility, one of the following functions that are
  70 +implemented 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 +=========
  108 +MECHANISM
  109 +=========
  110 +
  111 +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.
  113 +
  114 +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
  116 +userspace to get a new result.
  117 +
  118 +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
  120 +/sbin/request-key what program to run to instantiate the key.
  121 +
  122 +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()
  124 +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
  126 +remainder of the string to the key as its payload.
  127 +
  128 +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
  130 +the key will be discarded and recreated when the data it holds has expired.
  131 +
  132 +dns_query() returns a copy of the value attached to the key, or an error if
  133 +that is indicated instead.
  134 +
  135 +See <file:Documentation/keys-request-key.txt> for further information about
  136 +request-key function.
  137 +
  138 +
  139 +=========
  140 +DEBUGGING
  141 +=========
  142 +
  143 +Debugging messages can be turned on dynamically by writing a 1 into the
  144 +following file:
  145 +
  146 + /sys/module/dnsresolver/parameters/debug
... ... @@ -71,14 +71,14 @@
71 71 If unsure, say N.
72 72  
73 73 config CIFS_UPCALL
74   - bool "Kerberos/SPNEGO advanced session setup"
75   - depends on CIFS && KEYS
76   - help
77   - Enables an upcall mechanism for CIFS which accesses
78   - userspace helper utilities to provide SPNEGO packaged (RFC 4178)
79   - Kerberos tickets which are needed to mount to certain secure servers
80   - (for which more secure Kerberos authentication is required). If
81   - unsure, say N.
  74 + bool "Kerberos/SPNEGO advanced session setup"
  75 + depends on CIFS && KEYS
  76 + select DNS_RESOLVER
  77 + help
  78 + Enables an upcall mechanism for CIFS which accesses userspace helper
  79 + utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets
  80 + which are needed to mount to certain secure servers (for which more
  81 + secure Kerberos authentication is required). If unsure, say N.
82 82  
83 83 config CIFS_XATTR
84 84 bool "CIFS extended attributes"
... ... @@ -122,6 +122,7 @@
122 122 config CIFS_DFS_UPCALL
123 123 bool "DFS feature support"
124 124 depends on CIFS && KEYS
  125 + select DNS_RESOLVER
125 126 help
126 127 Distributed File System (DFS) support is used to access shares
127 128 transparently in an enterprise name space, even if the share
... ... @@ -45,7 +45,6 @@
45 45 #include "cifs_fs_sb.h"
46 46 #include <linux/mm.h>
47 47 #include <linux/key-type.h>
48   -#include "dns_resolve.h"
49 48 #include "cifs_spnego.h"
50 49 #include "fscache.h"
51 50 #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
52 51  
53 52  
54 53  
... ... @@ -934,22 +933,13 @@
934 933 if (rc)
935 934 goto out_unregister_filesystem;
936 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 936 rc = slow_work_register_user(THIS_MODULE);
943 937 if (rc)
944   - goto out_unregister_resolver_key;
  938 + goto out_unregister_key_type;
945 939  
946 940 return 0;
947 941  
948   - out_unregister_resolver_key:
949   -#ifdef CONFIG_CIFS_DFS_UPCALL
950   - cifs_exit_dns_resolver();
951 942 out_unregister_key_type:
952   -#endif
953 943 #ifdef CONFIG_CIFS_UPCALL
954 944 unregister_key_type(&cifs_spnego_key_type);
955 945 out_unregister_filesystem:
... ... @@ -976,7 +966,6 @@
976 966 cifs_fscache_unregister();
977 967 #ifdef CONFIG_CIFS_DFS_UPCALL
978 968 cifs_dfs_release_automount_timer();
979   - cifs_exit_dns_resolver();
980 969 #endif
981 970 #ifdef CONFIG_CIFS_UPCALL
982 971 unregister_key_type(&cifs_spnego_key_type);
fs/cifs/dns_resolve.c
... ... @@ -4,6 +4,8 @@
4 4 * Copyright (c) 2007 Igor Mammedov
5 5 * Author(s): Igor Mammedov (niallain@gmail.com)
6 6 * Steve French (sfrench@us.ibm.com)
  7 + * Wang Lei (wang840925@gmail.com)
  8 + * David Howells (dhowells@redhat.com)
7 9 *
8 10 * Contains the CIFS DFS upcall routines used for hostname to
9 11 * IP address translation.
10 12  
11 13  
12 14  
13 15  
14 16  
15 17  
16 18  
17 19  
18 20  
19 21  
20 22  
21 23  
22 24  
23 25  
... ... @@ -24,213 +26,74 @@
24 26 */
25 27  
26 28 #include <linux/slab.h>
27   -#include <linux/keyctl.h>
28   -#include <linux/key-type.h>
29   -#include <keys/user-type.h>
  29 +#include <linux/dns_resolver.h>
30 30 #include "dns_resolve.h"
31 31 #include "cifsglob.h"
32 32 #include "cifsproto.h"
33 33 #include "cifs_debug.h"
34 34  
35   -static const struct cred *dns_resolver_cache;
36   -
37   -/* Checks if supplied name is IP address
38   - * returns:
39   - * 1 - name is IP
40   - * 0 - name is not IP
  35 +/**
  36 + * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
  37 + * @unc: UNC path specifying the server
  38 + * @ip_addr: Where to return the IP address.
  39 + *
  40 + * The IP address will be returned in string form, and the caller is
  41 + * responsible for freeing it.
  42 + *
  43 + * Returns length of result on success, -ve on error.
41 44 */
42   -static int
43   -is_ip(const char *name, int len)
44   -{
45   - struct sockaddr_storage ss;
46   -
47   - return cifs_convert_address((struct sockaddr *)&ss, name, len);
48   -}
49   -
50   -static int
51   -dns_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   -
74   -static void
75   -dns_resolver_destroy(struct key *key)
76   -{
77   - kfree(key->payload.data);
78   -}
79   -
80   -struct 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   - */
96 45 int
97 46 dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
98 47 {
99   - const struct cred *saved_cred;
100   - int rc = -EAGAIN;
101   - struct key *rkey = ERR_PTR(-EAGAIN);
  48 + struct sockaddr_storage ss;
  49 + const char *hostname, *sep;
102 50 char *name;
103   - char *data = NULL;
104   - int len;
  51 + int len, rc;
105 52  
106 53 if (!ip_addr || !unc)
107 54 return -EINVAL;
108 55  
109   - /* search for server name delimiter */
110 56 len = strlen(unc);
111 57 if (len < 3) {
112 58 cFYI(1, "%s: unc is too short: %s", __func__, unc);
113 59 return -EINVAL;
114 60 }
  61 +
  62 + /* Discount leading slashes for cifs */
115 63 len -= 2;
116   - name = memchr(unc+2, '\\', len);
117   - if (!name) {
  64 + hostname = unc + 2;
  65 +
  66 + /* Search for server name delimiter */
  67 + sep = memchr(hostname, '\\', len);
  68 + if (sep)
  69 + len = sep - unc;
  70 + else
118 71 cFYI(1, "%s: probably server name is whole unc: %s",
119   - __func__, unc);
120   - } else {
121   - len = (name - unc) - 2/* leading // */;
122   - }
  72 + __func__, unc);
123 73  
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;
  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;
131 78  
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   -
139   - saved_cred = override_creds(dns_resolver_cache);
140   - rkey = request_key(&key_type_dns_resolver, name, "");
141   - revert_creds(saved_cred);
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   -
155   -skip_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   -
173   -out:
174   - kfree(name);
  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 87 return rc;
176   -}
177 88  
178   -int __init cifs_init_dns_resolver(void)
179   -{
180   - struct cred *cred;
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)
  89 +name_is_IP_address:
  90 + name = kmalloc(len + 1, GFP_KERNEL);
  91 + if (!name)
195 92 return -ENOMEM;
196   -
197   - keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred,
198   - (KEY_POS_ALL & ~KEY_POS_SETATTR) |
199   - KEY_USR_VIEW | KEY_USR_READ,
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;
  93 + memcpy(name, hostname, len);
  94 + name[len] = 0;
  95 + cFYI(1, "%s: unc is IP, skipping dns upcall: %s", __func__, name);
  96 + *ip_addr = name;
219 97 return 0;
220   -
221   -failed_put_key:
222   - key_put(keyring);
223   -failed_put_cred:
224   - put_cred(cred);
225   - return ret;
226   -}
227   -
228   -void 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 }
fs/cifs/dns_resolve.h
... ... @@ -24,8 +24,6 @@
24 24 #define _DNS_RESOLVE_H
25 25  
26 26 #ifdef __KERNEL__
27   -extern int __init cifs_init_dns_resolver(void);
28   -extern void cifs_exit_dns_resolver(void);
29 27 extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
30 28 #endif /* KERNEL */
31 29  
include/keys/dns_resolver-type.h
  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 +
  17 +extern struct key_type key_type_dns_resolver;
  18 +
  19 +extern int request_dns_resolver_key(const char *description,
  20 + const char *callout_info,
  21 + char **data);
  22 +
  23 +#endif /* _KEYS_DNS_RESOLVER_TYPE_H */
include/linux/dns_resolver.h
  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 +
  29 +extern 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 */
... ... @@ -213,6 +213,7 @@
213 213 source "net/ieee802154/Kconfig"
214 214 source "net/sched/Kconfig"
215 215 source "net/dcb/Kconfig"
  216 +source "net/dns_resolver/Kconfig"
216 217  
217 218 config RPS
218 219 boolean
... ... @@ -67,4 +67,5 @@
67 67 obj-$(CONFIG_SYSCTL) += sysctl_net.o
68 68 endif
69 69 obj-$(CONFIG_WIMAX) += wimax/
  70 +obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/
net/dns_resolver/Kconfig
  1 +#
  2 +# Configuration for DNS Resolver
  3 +#
  4 +config 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.
net/dns_resolver/Makefile
  1 +#
  2 +# Makefile for the Linux DNS Resolver.
  3 +#
  4 +
  5 +obj-$(CONFIG_DNS_RESOLVER) += dns_resolver.o
  6 +
  7 +dns_resolver-objs := dns_key.o dns_query.o
net/dns_resolver/dns_key.c
  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 +
  35 +MODULE_DESCRIPTION("DNS Resolver");
  36 +MODULE_AUTHOR("Wang Lei");
  37 +MODULE_LICENSE("GPL");
  38 +
  39 +unsigned dns_resolver_debug;
  40 +module_param_named(debug, dns_resolver_debug, uint, S_IWUSR | S_IRUGO);
  41 +MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
  42 +
  43 +const 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 + */
  57 +static int
  58 +dns_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 + */
  105 +static int
  106 +dns_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 +
  130 +matched:
  131 + ret = 1;
  132 +no_match:
  133 + kleave(" = %d", ret);
  134 + return ret;
  135 +}
  136 +
  137 +struct 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 +
  147 +static 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 +
  192 +failed_put_key:
  193 + key_put(keyring);
  194 +failed_put_cred:
  195 + put_cred(cred);
  196 + return ret;
  197 +}
  198 +
  199 +static 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 +
  208 +module_init(init_dns_resolver)
  209 +module_exit(exit_dns_resolver)
  210 +MODULE_LICENSE("GPL");
net/dns_resolver/dns_query.c
  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 + */
  69 +int 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;
  152 +put:
  153 + up_read(&rkey->sem);
  154 + key_put(rkey);
  155 +out:
  156 + kleave(" = %d", ret);
  157 + return ret;
  158 +}
  159 +EXPORT_SYMBOL(dns_query);
net/dns_resolver/internal.h
  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 + */
  29 +extern const struct cred *dns_resolver_cache;
  30 +
  31 +/*
  32 + * debug tracing
  33 + */
  34 +extern unsigned dns_resolver_debug;
  35 +
  36 +#define kdebug(FMT, ...) \
  37 +do { \
  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__)