dns_key.c (9961B)
1/* Key type used to cache DNS lookups made by the kernel 2 * 3 * See Documentation/networking/dns_resolver.rst 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 37MODULE_DESCRIPTION("DNS Resolver"); 38MODULE_AUTHOR("Wang Lei"); 39MODULE_LICENSE("GPL"); 40 41unsigned int dns_resolver_debug; 42module_param_named(debug, dns_resolver_debug, uint, 0644); 43MODULE_PARM_DESC(debug, "DNS Resolver debugging mask"); 44 45const 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 */ 91static int 92dns_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 int datalen = prep->datalen, result_len = 0; 99 const char *data = prep->data, *end, *opt; 100 101 if (datalen <= 1 || !data) 102 return -EINVAL; 103 104 if (data[0] == 0) { 105 /* It may be a server list. */ 106 if (datalen <= sizeof(*bin)) 107 return -EINVAL; 108 109 bin = (const struct dns_payload_header *)data; 110 kenter("[%u,%u],%u", bin->content, bin->version, datalen); 111 if (bin->content != DNS_PAYLOAD_IS_SERVER_LIST) { 112 pr_warn_ratelimited( 113 "dns_resolver: Unsupported content type (%u)\n", 114 bin->content); 115 return -EINVAL; 116 } 117 118 if (bin->version != 1) { 119 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 kenter("'%*.*s',%u", datalen, datalen, data, datalen); 130 131 if (!data || data[datalen - 1] != '\0') 132 return -EINVAL; 133 datalen--; 134 135 /* deal with any options embedded in the data */ 136 end = data + datalen; 137 opt = memchr(data, '#', datalen); 138 if (!opt) { 139 /* no options: the entire data is the result */ 140 kdebug("no options"); 141 result_len = datalen; 142 } else { 143 const char *next_opt; 144 145 result_len = opt - data; 146 opt++; 147 kdebug("options: '%s'", opt); 148 do { 149 int opt_len, opt_nlen; 150 const char *eq; 151 char optval[128]; 152 153 next_opt = memchr(opt, '#', end - opt) ?: end; 154 opt_len = next_opt - opt; 155 if (opt_len <= 0 || opt_len > sizeof(optval)) { 156 pr_warn_ratelimited("Invalid option length (%d) for dns_resolver key\n", 157 opt_len); 158 return -EINVAL; 159 } 160 161 eq = memchr(opt, '=', opt_len); 162 if (eq) { 163 opt_nlen = eq - opt; 164 eq++; 165 memcpy(optval, eq, next_opt - eq); 166 optval[next_opt - eq] = '\0'; 167 } else { 168 opt_nlen = opt_len; 169 optval[0] = '\0'; 170 } 171 172 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 if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 && 178 memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) { 179 kdebug("dns error number option"); 180 181 ret = kstrtoul(optval, 10, &derrno); 182 if (ret < 0) 183 goto bad_option_value; 184 185 if (derrno < 1 || derrno > 511) 186 goto bad_option_value; 187 188 kdebug("dns error no. = %lu", derrno); 189 prep->payload.data[dns_key_error] = ERR_PTR(-derrno); 190 continue; 191 } 192 193 bad_option_value: 194 pr_warn_ratelimited("Option '%*.*s' to dns_resolver key: bad/missing value\n", 195 opt_nlen, opt_nlen, opt); 196 return -EINVAL; 197 } 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 if (prep->payload.data[dns_key_error]) { 203 kleave(" = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error])); 204 return 0; 205 } 206 207store_result: 208 kdebug("store result"); 209 prep->quotalen = result_len; 210 211 upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL); 212 if (!upayload) { 213 kleave(" = -ENOMEM"); 214 return -ENOMEM; 215 } 216 217 upayload->datalen = result_len; 218 memcpy(upayload->data, data, result_len); 219 upayload->data[result_len] = '\0'; 220 221 prep->payload.data[dns_key_data] = upayload; 222 kleave(" = 0"); 223 return 0; 224} 225 226/* 227 * Clean up the preparse data 228 */ 229static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) 230{ 231 pr_devel("==>%s()\n", __func__); 232 233 kfree(prep->payload.data[dns_key_data]); 234} 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 */ 242static bool dns_resolver_cmp(const struct key *key, 243 const struct key_match_data *match_data) 244{ 245 int slen, dlen, ret = 0; 246 const char *src = key->description, *dsp = match_data->raw_data; 247 248 kenter("%s,%s", src, dsp); 249 250 if (!src || !dsp) 251 goto no_match; 252 253 if (strcasecmp(src, dsp) == 0) 254 goto matched; 255 256 slen = strlen(src); 257 dlen = strlen(dsp); 258 if (slen <= 0 || dlen <= 0) 259 goto no_match; 260 if (src[slen - 1] == '.') 261 slen--; 262 if (dsp[dlen - 1] == '.') 263 dlen--; 264 if (slen != dlen || strncasecmp(src, dsp, slen) != 0) 265 goto no_match; 266 267matched: 268 ret = 1; 269no_match: 270 kleave(" = %d", ret); 271 return ret; 272} 273 274/* 275 * Preparse the match criterion. 276 */ 277static int dns_resolver_match_preparse(struct key_match_data *match_data) 278{ 279 match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; 280 match_data->cmp = dns_resolver_cmp; 281 return 0; 282} 283 284/* 285 * Describe a DNS key 286 */ 287static void dns_resolver_describe(const struct key *key, struct seq_file *m) 288{ 289 seq_puts(m, key->description); 290 if (key_is_positive(key)) { 291 int err = PTR_ERR(key->payload.data[dns_key_error]); 292 293 if (err) 294 seq_printf(m, ": %d", err); 295 else 296 seq_printf(m, ": %u", key->datalen); 297 } 298} 299 300/* 301 * read the DNS data 302 * - the key's semaphore is read-locked 303 */ 304static long dns_resolver_read(const struct key *key, 305 char *buffer, size_t buflen) 306{ 307 int err = PTR_ERR(key->payload.data[dns_key_error]); 308 309 if (err) 310 return err; 311 312 return user_read(key, buffer, buflen); 313} 314 315struct 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 328static 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 cred = prepare_kernel_cred(NULL); 341 if (!cred) 342 return -ENOMEM; 343 344 keyring = keyring_alloc(".dns_resolver", 345 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 if (IS_ERR(keyring)) { 350 ret = PTR_ERR(keyring); 351 goto failed_put_cred; 352 } 353 354 ret = register_key_type(&key_type_dns_resolver); 355 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 set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); 361 cred->thread_keyring = keyring; 362 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; 363 dns_resolver_cache = cred; 364 365 kdebug("DNS resolver keyring: %d\n", key_serial(keyring)); 366 return 0; 367 368failed_put_key: 369 key_put(keyring); 370failed_put_cred: 371 put_cred(cred); 372 return ret; 373} 374 375static void __exit exit_dns_resolver(void) 376{ 377 key_revoke(dns_resolver_cache->thread_keyring); 378 unregister_key_type(&key_type_dns_resolver); 379 put_cred(dns_resolver_cache); 380} 381 382module_init(init_dns_resolver) 383module_exit(exit_dns_resolver) 384MODULE_LICENSE("GPL");