diff options
Diffstat (limited to 'fs/cifs/dns_resolve.c')
| -rw-r--r-- | fs/cifs/dns_resolve.c | 231 |
1 files changed, 46 insertions, 185 deletions
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 3ad7f4300c45..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,214 +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 | ||
| 35 | static 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. |
| 42 | static int | 42 | * |
| 43 | is_ip(char *name) | 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); | ||
| 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 | ip = kmalloc(datalen + 1, GFP_KERNEL); | ||
| 58 | if (!ip) | ||
| 59 | return -ENOMEM; | ||
| 60 | |||
| 61 | memcpy(ip, data, datalen); | ||
| 62 | ip[datalen] = '\0'; | ||
| 63 | |||
| 64 | /* make sure this looks like an address */ | ||
| 65 | if (!is_ip(ip)) { | ||
| 66 | kfree(ip); | ||
| 67 | return -EINVAL; | ||
| 68 | } | ||
| 69 | |||
| 70 | key->type_data.x[0] = datalen; | ||
| 71 | key->payload.data = ip; | ||
| 72 | |||
| 73 | return rc; | ||
| 74 | } | ||
| 75 | |||
| 76 | static void | ||
| 77 | dns_resolver_destroy(struct key *key) | ||
| 78 | { | ||
| 79 | kfree(key->payload.data); | ||
| 80 | } | ||
| 81 | |||
| 82 | struct key_type key_type_dns_resolver = { | ||
| 83 | .name = "dns_resolver", | ||
| 84 | .def_datalen = sizeof(struct in_addr), | ||
| 85 | .describe = user_describe, | ||
| 86 | .instantiate = dns_resolver_instantiate, | ||
| 87 | .destroy = dns_resolver_destroy, | ||
| 88 | .match = user_match, | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* Resolves server name to ip address. | ||
| 92 | * input: | ||
| 93 | * unc - server UNC | ||
| 94 | * output: | ||
| 95 | * *ip_addr - pointer to server ip, caller responcible for freeing it. | ||
| 96 | * return 0 on success | ||
| 97 | */ | 44 | */ |
| 98 | int | 45 | int |
| 99 | dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) | 46 | dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) |
| 100 | { | 47 | { |
| 101 | const struct cred *saved_cred; | 48 | struct sockaddr_storage ss; |
| 102 | int rc = -EAGAIN; | 49 | const char *hostname, *sep; |
| 103 | struct key *rkey = ERR_PTR(-EAGAIN); | ||
| 104 | char *name; | 50 | char *name; |
| 105 | char *data = NULL; | 51 | int len, rc; |
| 106 | int len; | ||
| 107 | 52 | ||
| 108 | if (!ip_addr || !unc) | 53 | if (!ip_addr || !unc) |
| 109 | return -EINVAL; | 54 | return -EINVAL; |
| 110 | 55 | ||
| 111 | /* search for server name delimiter */ | ||
| 112 | len = strlen(unc); | 56 | len = strlen(unc); |
| 113 | if (len < 3) { | 57 | if (len < 3) { |
| 114 | cFYI(1, "%s: unc is too short: %s", __func__, unc); | 58 | cFYI(1, "%s: unc is too short: %s", __func__, unc); |
| 115 | return -EINVAL; | 59 | return -EINVAL; |
| 116 | } | 60 | } |
| 117 | len -= 2; | ||
| 118 | name = memchr(unc+2, '\\', len); | ||
| 119 | if (!name) { | ||
| 120 | cFYI(1, "%s: probably server name is whole unc: %s", | ||
| 121 | __func__, unc); | ||
| 122 | } else { | ||
| 123 | len = (name - unc) - 2/* leading // */; | ||
| 124 | } | ||
| 125 | |||
| 126 | name = kmalloc(len+1, GFP_KERNEL); | ||
| 127 | if (!name) { | ||
| 128 | rc = -ENOMEM; | ||
| 129 | return rc; | ||
| 130 | } | ||
| 131 | memcpy(name, unc+2, len); | ||
| 132 | name[len] = 0; | ||
| 133 | |||
| 134 | if (is_ip(name)) { | ||
| 135 | cFYI(1, "%s: it is IP, skipping dns upcall: %s", | ||
| 136 | __func__, name); | ||
| 137 | data = name; | ||
| 138 | goto skip_upcall; | ||
| 139 | } | ||
| 140 | 61 | ||
| 141 | saved_cred = override_creds(dns_resolver_cache); | 62 | /* Discount leading slashes for cifs */ |
| 142 | rkey = request_key(&key_type_dns_resolver, name, ""); | 63 | len -= 2; |
| 143 | revert_creds(saved_cred); | 64 | hostname = unc + 2; |
| 144 | if (!IS_ERR(rkey)) { | ||
| 145 | if (!(rkey->perm & KEY_USR_VIEW)) { | ||
| 146 | down_read(&rkey->sem); | ||
| 147 | rkey->perm |= KEY_USR_VIEW; | ||
| 148 | up_read(&rkey->sem); | ||
| 149 | } | ||
| 150 | len = rkey->type_data.x[0]; | ||
| 151 | data = rkey->payload.data; | ||
| 152 | } else { | ||
| 153 | cERROR(1, "%s: unable to resolve: %s", __func__, name); | ||
| 154 | goto out; | ||
| 155 | } | ||
| 156 | |||
| 157 | skip_upcall: | ||
| 158 | if (data) { | ||
| 159 | *ip_addr = kmalloc(len + 1, GFP_KERNEL); | ||
| 160 | if (*ip_addr) { | ||
| 161 | memcpy(*ip_addr, data, len + 1); | ||
| 162 | if (!IS_ERR(rkey)) | ||
| 163 | cFYI(1, "%s: resolved: %s to %s", __func__, | ||
| 164 | name, | ||
| 165 | *ip_addr | ||
| 166 | ); | ||
| 167 | rc = 0; | ||
| 168 | } else { | ||
| 169 | rc = -ENOMEM; | ||
| 170 | } | ||
| 171 | if (!IS_ERR(rkey)) | ||
| 172 | key_put(rkey); | ||
| 173 | } | ||
| 174 | 65 | ||
| 175 | out: | 66 | /* Search for server name delimiter */ |
| 176 | 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); | ||
| 177 | return rc; | 87 | return rc; |
| 178 | } | ||
| 179 | 88 | ||
| 180 | int __init cifs_init_dns_resolver(void) | 89 | name_is_IP_address: |
| 181 | { | 90 | name = kmalloc(len + 1, GFP_KERNEL); |
| 182 | struct cred *cred; | 91 | if (!name) |
| 183 | struct key *keyring; | ||
| 184 | int ret; | ||
| 185 | |||
| 186 | printk(KERN_NOTICE "Registering the %s key type\n", | ||
| 187 | key_type_dns_resolver.name); | ||
| 188 | |||
| 189 | /* create an override credential set with a special thread keyring in | ||
| 190 | * which DNS requests are cached | ||
| 191 | * | ||
| 192 | * this is used to prevent malicious redirections from being installed | ||
| 193 | * with add_key(). | ||
| 194 | */ | ||
| 195 | cred = prepare_kernel_cred(NULL); | ||
| 196 | if (!cred) | ||
| 197 | return -ENOMEM; | 92 | return -ENOMEM; |
| 198 | 93 | memcpy(name, hostname, len); | |
| 199 | keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, | 94 | name[len] = 0; |
| 200 | (KEY_POS_ALL & ~KEY_POS_SETATTR) | | 95 | cFYI(1, "%s: unc is IP, skipping dns upcall: %s", __func__, name); |
| 201 | KEY_USR_VIEW | KEY_USR_READ, | 96 | *ip_addr = name; |
| 202 | KEY_ALLOC_NOT_IN_QUOTA); | ||
| 203 | if (IS_ERR(keyring)) { | ||
| 204 | ret = PTR_ERR(keyring); | ||
| 205 | goto failed_put_cred; | ||
| 206 | } | ||
| 207 | |||
| 208 | ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); | ||
| 209 | if (ret < 0) | ||
| 210 | goto failed_put_key; | ||
| 211 | |||
| 212 | ret = register_key_type(&key_type_dns_resolver); | ||
| 213 | if (ret < 0) | ||
| 214 | goto failed_put_key; | ||
| 215 | |||
| 216 | /* instruct request_key() to use this special keyring as a cache for | ||
| 217 | * the results it looks up */ | ||
| 218 | cred->thread_keyring = keyring; | ||
| 219 | cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; | ||
| 220 | dns_resolver_cache = cred; | ||
| 221 | return 0; | 97 | return 0; |
| 222 | |||
| 223 | failed_put_key: | ||
| 224 | key_put(keyring); | ||
| 225 | failed_put_cred: | ||
| 226 | put_cred(cred); | ||
| 227 | return ret; | ||
| 228 | } | ||
| 229 | |||
| 230 | void cifs_exit_dns_resolver(void) | ||
| 231 | { | ||
| 232 | key_revoke(dns_resolver_cache->thread_keyring); | ||
| 233 | unregister_key_type(&key_type_dns_resolver); | ||
| 234 | put_cred(dns_resolver_cache); | ||
| 235 | printk(KERN_NOTICE "Unregistered %s key type\n", | ||
| 236 | key_type_dns_resolver.name); | ||
| 237 | } | 98 | } |
