aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/dns_resolve.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/dns_resolve.c')
-rw-r--r--fs/cifs/dns_resolve.c229
1 files changed, 46 insertions, 183 deletions
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index aa967e7917f8..0eb87026cad3 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}