Branch data Line data Source code
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, see <http://www.gnu.org/licenses/>. 23 : : */ 24 : : #include <linux/module.h> 25 : : #include <linux/moduleparam.h> 26 : : #include <linux/slab.h> 27 : : #include <linux/string.h> 28 : : #include <linux/kernel.h> 29 : : #include <linux/keyctl.h> 30 : : #include <linux/err.h> 31 : : #include <linux/seq_file.h> 32 : : #include <linux/dns_resolver.h> 33 : : #include <keys/dns_resolver-type.h> 34 : : #include <keys/user-type.h> 35 : : #include "internal.h" 36 : : 37 : : MODULE_DESCRIPTION("DNS Resolver"); 38 : : MODULE_AUTHOR("Wang Lei"); 39 : : MODULE_LICENSE("GPL"); 40 : : 41 : : unsigned int dns_resolver_debug; 42 : : module_param_named(debug, dns_resolver_debug, uint, 0644); 43 : : MODULE_PARM_DESC(debug, "DNS Resolver debugging mask"); 44 : : 45 : : const struct cred *dns_resolver_cache; 46 : : 47 : : #define DNS_ERRORNO_OPTION "dnserror" 48 : : 49 : : /* 50 : : * Preparse instantiation data for a dns_resolver key. 51 : : * 52 : : * For normal hostname lookups, the data must be a NUL-terminated string, with 53 : : * the NUL char accounted in datalen. 54 : : * 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 57 : : * the string leading up to the first '#'. For instance: 58 : : * 59 : : * "ip1,ip2,...#foo=bar" 60 : : * 61 : : * For server list requests, the data must begin with a NUL char and be 62 : : * followed by a byte indicating the version of the data format. Version 1 63 : : * looks something like (note this is packed): 64 : : * 65 : : * u8 Non-string marker (ie. 0) 66 : : * u8 Content (DNS_PAYLOAD_IS_*) 67 : : * u8 Version (e.g. 1) 68 : : * u8 Source of server list 69 : : * u8 Lookup status of server list 70 : : * u8 Number of servers 71 : : * foreach-server { 72 : : * __le16 Name length 73 : : * __le16 Priority (as per SRV record, low first) 74 : : * __le16 Weight (as per SRV record, higher first) 75 : : * __le16 Port 76 : : * u8 Source of address list 77 : : * u8 Lookup status of address list 78 : : * u8 Protocol (DNS_SERVER_PROTOCOL_*) 79 : : * u8 Number of addresses 80 : : * char[] Name (not NUL-terminated) 81 : : * foreach-address { 82 : : * u8 Family (DNS_ADDRESS_IS_*) 83 : : * union { 84 : : * u8[4] ipv4_addr 85 : : * u8[16] ipv6_addr 86 : : * } 87 : : * } 88 : : * } 89 : : * 90 : : */ 91 : : static int 92 : 0 : dns_resolver_preparse(struct key_preparsed_payload *prep) 93 : : { 94 : : const struct dns_payload_header *bin; 95 : : struct user_key_payload *upayload; 96 : : unsigned long derrno; 97 : : int ret; 98 : 0 : int datalen = prep->datalen, result_len = 0; 99 : 0 : const char *data = prep->data, *end, *opt; 100 : : 101 : 0 : if (datalen <= 1 || !data) 102 : : return -EINVAL; 103 : : 104 : 0 : if (data[0] == 0) { 105 : : /* It may be a server list. */ 106 : 0 : if (datalen <= sizeof(*bin)) 107 : : return -EINVAL; 108 : : 109 : : bin = (const struct dns_payload_header *)data; 110 : 0 : kenter("[%u,%u],%u", bin->content, bin->version, datalen); 111 : 0 : if (bin->content != DNS_PAYLOAD_IS_SERVER_LIST) { 112 : 0 : pr_warn_ratelimited( 113 : : "dns_resolver: Unsupported content type (%u)\n", 114 : : bin->content); 115 : : return -EINVAL; 116 : : } 117 : : 118 : 0 : if (bin->version != 1) { 119 : 0 : pr_warn_ratelimited( 120 : : "dns_resolver: Unsupported server list version (%u)\n", 121 : : bin->version); 122 : : return -EINVAL; 123 : : } 124 : : 125 : : result_len = datalen; 126 : : goto store_result; 127 : : } 128 : : 129 : 0 : kenter("'%*.*s',%u", datalen, datalen, data, datalen); 130 : : 131 : 0 : if (!data || data[datalen - 1] != '\0') 132 : : return -EINVAL; 133 : 0 : datalen--; 134 : : 135 : : /* deal with any options embedded in the data */ 136 : 0 : end = data + datalen; 137 : 0 : opt = memchr(data, '#', datalen); 138 : 0 : if (!opt) { 139 : : /* no options: the entire data is the result */ 140 : 0 : kdebug("no options"); 141 : : result_len = datalen; 142 : : } else { 143 : : const char *next_opt; 144 : : 145 : 0 : result_len = opt - data; 146 : 0 : opt++; 147 : 0 : kdebug("options: '%s'", opt); 148 : : do { 149 : : int opt_len, opt_nlen; 150 : : const char *eq; 151 : : char optval[128]; 152 : : 153 : 0 : next_opt = memchr(opt, '#', end - opt) ?: end; 154 : 0 : opt_len = next_opt - opt; 155 : 0 : if (opt_len <= 0 || opt_len > sizeof(optval)) { 156 : 0 : pr_warn_ratelimited("Invalid option length (%d) for dns_resolver key\n", 157 : : opt_len); 158 : 0 : return -EINVAL; 159 : : } 160 : : 161 : 0 : eq = memchr(opt, '=', opt_len); 162 : 0 : if (eq) { 163 : 0 : opt_nlen = eq - opt; 164 : 0 : eq++; 165 : 0 : memcpy(optval, eq, next_opt - eq); 166 : 0 : optval[next_opt - eq] = '\0'; 167 : : } else { 168 : : opt_nlen = opt_len; 169 : 0 : optval[0] = '\0'; 170 : : } 171 : : 172 : 0 : kdebug("option '%*.*s' val '%s'", 173 : : opt_nlen, opt_nlen, opt, optval); 174 : : 175 : : /* see if it's an error number representing a DNS error 176 : : * that's to be recorded as the result in this key */ 177 : 0 : if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 && 178 : 0 : memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) { 179 : 0 : kdebug("dns error number option"); 180 : : 181 : : ret = kstrtoul(optval, 10, &derrno); 182 : 0 : if (ret < 0) 183 : : goto bad_option_value; 184 : : 185 : 0 : if (derrno < 1 || derrno > 511) 186 : : goto bad_option_value; 187 : : 188 : 0 : kdebug("dns error no. = %lu", derrno); 189 : 0 : prep->payload.data[dns_key_error] = ERR_PTR(-derrno); 190 : 0 : continue; 191 : : } 192 : : 193 : : bad_option_value: 194 : 0 : pr_warn_ratelimited("Option '%*.*s' to dns_resolver key: bad/missing value\n", 195 : : opt_nlen, opt_nlen, opt); 196 : : return -EINVAL; 197 : 0 : } while (opt = next_opt + 1, opt < end); 198 : : } 199 : : 200 : : /* don't cache the result if we're caching an error saying there's no 201 : : * result */ 202 : 0 : if (prep->payload.data[dns_key_error]) { 203 : 0 : kleave(" = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error])); 204 : : return 0; 205 : : } 206 : : 207 : : store_result: 208 : 0 : kdebug("store result"); 209 : 0 : prep->quotalen = result_len; 210 : : 211 : 0 : upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL); 212 : 0 : if (!upayload) { 213 : 0 : kleave(" = -ENOMEM"); 214 : : return -ENOMEM; 215 : : } 216 : : 217 : 0 : upayload->datalen = result_len; 218 : 0 : memcpy(upayload->data, data, result_len); 219 : 0 : upayload->data[result_len] = '\0'; 220 : : 221 : 0 : prep->payload.data[dns_key_data] = upayload; 222 : 0 : kleave(" = 0"); 223 : : return 0; 224 : : } 225 : : 226 : : /* 227 : : * Clean up the preparse data 228 : : */ 229 : 0 : static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) 230 : : { 231 : : pr_devel("==>%s()\n", __func__); 232 : : 233 : 0 : kfree(prep->payload.data[dns_key_data]); 234 : 0 : } 235 : : 236 : : /* 237 : : * The description is of the form "[<type>:]<domain_name>" 238 : : * 239 : : * The domain name may be a simple name or an absolute domain name (which 240 : : * should end with a period). The domain name is case-independent. 241 : : */ 242 : 0 : static bool dns_resolver_cmp(const struct key *key, 243 : : const struct key_match_data *match_data) 244 : : { 245 : : int slen, dlen, ret = 0; 246 : 0 : const char *src = key->description, *dsp = match_data->raw_data; 247 : : 248 : 0 : kenter("%s,%s", src, dsp); 249 : : 250 : 0 : if (!src || !dsp) 251 : : goto no_match; 252 : : 253 : 0 : if (strcasecmp(src, dsp) == 0) 254 : : goto matched; 255 : : 256 : 0 : slen = strlen(src); 257 : 0 : dlen = strlen(dsp); 258 : 0 : if (slen <= 0 || dlen <= 0) 259 : : goto no_match; 260 : 0 : if (src[slen - 1] == '.') 261 : 0 : slen--; 262 : 0 : if (dsp[dlen - 1] == '.') 263 : 0 : dlen--; 264 : 0 : if (slen != dlen || strncasecmp(src, dsp, slen) != 0) 265 : : goto no_match; 266 : : 267 : : matched: 268 : : ret = 1; 269 : : no_match: 270 : 0 : kleave(" = %d", ret); 271 : 0 : return ret; 272 : : } 273 : : 274 : : /* 275 : : * Preparse the match criterion. 276 : : */ 277 : 0 : static int dns_resolver_match_preparse(struct key_match_data *match_data) 278 : : { 279 : 0 : match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; 280 : 0 : match_data->cmp = dns_resolver_cmp; 281 : 0 : return 0; 282 : : } 283 : : 284 : : /* 285 : : * Describe a DNS key 286 : : */ 287 : 0 : static void dns_resolver_describe(const struct key *key, struct seq_file *m) 288 : : { 289 : 0 : seq_puts(m, key->description); 290 : 0 : if (key_is_positive(key)) { 291 : 0 : int err = PTR_ERR(key->payload.data[dns_key_error]); 292 : : 293 : 0 : if (err) 294 : 0 : seq_printf(m, ": %d", err); 295 : : else 296 : 0 : seq_printf(m, ": %u", key->datalen); 297 : : } 298 : 0 : } 299 : : 300 : : /* 301 : : * read the DNS data 302 : : * - the key's semaphore is read-locked 303 : : */ 304 : 0 : static long dns_resolver_read(const struct key *key, 305 : : char *buffer, size_t buflen) 306 : : { 307 : 0 : int err = PTR_ERR(key->payload.data[dns_key_error]); 308 : : 309 : 0 : if (err) 310 : : return err; 311 : : 312 : 0 : return user_read(key, buffer, buflen); 313 : : } 314 : : 315 : : struct key_type key_type_dns_resolver = { 316 : : .name = "dns_resolver", 317 : : .flags = KEY_TYPE_NET_DOMAIN, 318 : : .preparse = dns_resolver_preparse, 319 : : .free_preparse = dns_resolver_free_preparse, 320 : : .instantiate = generic_key_instantiate, 321 : : .match_preparse = dns_resolver_match_preparse, 322 : : .revoke = user_revoke, 323 : : .destroy = user_destroy, 324 : : .describe = dns_resolver_describe, 325 : : .read = dns_resolver_read, 326 : : }; 327 : : 328 : 3 : static int __init init_dns_resolver(void) 329 : : { 330 : : struct cred *cred; 331 : : struct key *keyring; 332 : : int ret; 333 : : 334 : : /* create an override credential set with a special thread keyring in 335 : : * which DNS requests are cached 336 : : * 337 : : * this is used to prevent malicious redirections from being installed 338 : : * with add_key(). 339 : : */ 340 : 3 : cred = prepare_kernel_cred(NULL); 341 : 3 : if (!cred) 342 : : return -ENOMEM; 343 : : 344 : 3 : keyring = keyring_alloc(".dns_resolver", 345 : 3 : GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 346 : : (KEY_POS_ALL & ~KEY_POS_SETATTR) | 347 : : KEY_USR_VIEW | KEY_USR_READ, 348 : : KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); 349 : 3 : if (IS_ERR(keyring)) { 350 : : ret = PTR_ERR(keyring); 351 : 0 : goto failed_put_cred; 352 : : } 353 : : 354 : 3 : ret = register_key_type(&key_type_dns_resolver); 355 : 3 : if (ret < 0) 356 : : goto failed_put_key; 357 : : 358 : : /* instruct request_key() to use this special keyring as a cache for 359 : : * the results it looks up */ 360 : 3 : set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); 361 : 3 : cred->thread_keyring = keyring; 362 : 3 : cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; 363 : 3 : dns_resolver_cache = cred; 364 : : 365 : 3 : kdebug("DNS resolver keyring: %d\n", key_serial(keyring)); 366 : : return 0; 367 : : 368 : : failed_put_key: 369 : 0 : key_put(keyring); 370 : : failed_put_cred: 371 : 0 : put_cred(cred); 372 : 0 : return ret; 373 : : } 374 : : 375 : 0 : static void __exit exit_dns_resolver(void) 376 : : { 377 : 0 : key_revoke(dns_resolver_cache->thread_keyring); 378 : 0 : unregister_key_type(&key_type_dns_resolver); 379 : 0 : put_cred(dns_resolver_cache); 380 : 0 : } 381 : : 382 : : module_init(init_dns_resolver) 383 : : module_exit(exit_dns_resolver) 384 : : MODULE_LICENSE("GPL");