| 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 |  | 
|---|
| 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 | dns_resolver_preparse(struct key_preparsed_payload *prep) | 
|---|
| 93 | { | 
|---|
| 94 | struct user_key_payload *upayload; | 
|---|
| 95 | unsigned long derrno; | 
|---|
| 96 | int ret; | 
|---|
| 97 | int datalen = prep->datalen, result_len = 0; | 
|---|
| 98 | const char *data = prep->data, *end, *opt; | 
|---|
| 99 |  | 
|---|
| 100 | if (datalen <= 1 || !data) | 
|---|
| 101 | return -EINVAL; | 
|---|
| 102 |  | 
|---|
| 103 | if (data[0] == 0) { | 
|---|
| 104 | const struct dns_server_list_v1_header *v1; | 
|---|
| 105 |  | 
|---|
| 106 | /* It may be a server list. */ | 
|---|
| 107 | if (datalen < sizeof(*v1)) | 
|---|
| 108 | return -EINVAL; | 
|---|
| 109 |  | 
|---|
| 110 | v1 = (const struct dns_server_list_v1_header *)data; | 
|---|
| 111 | kenter( "[%u,%u],%u", v1->hdr.content, v1->hdr.version, datalen); | 
|---|
| 112 | if (v1->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST) { | 
|---|
| 113 | pr_warn_ratelimited( | 
|---|
| 114 | "dns_resolver: Unsupported content type (%u)\n", | 
|---|
| 115 | v1->hdr.content); | 
|---|
| 116 | return -EINVAL; | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | if (v1->hdr.version != 1) { | 
|---|
| 120 | pr_warn_ratelimited( | 
|---|
| 121 | "dns_resolver: Unsupported server list version (%u)\n", | 
|---|
| 122 | v1->hdr.version); | 
|---|
| 123 | return -EINVAL; | 
|---|
| 124 | } | 
|---|
| 125 |  | 
|---|
| 126 | if ((v1->status != DNS_LOOKUP_GOOD && | 
|---|
| 127 | v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) { | 
|---|
| 128 | if (prep->expiry == TIME64_MAX) | 
|---|
| 129 | prep->expiry = ktime_get_real_seconds() + 1; | 
|---|
| 130 | } | 
|---|
| 131 |  | 
|---|
| 132 | result_len = datalen; | 
|---|
| 133 | goto store_result; | 
|---|
| 134 | } | 
|---|
| 135 |  | 
|---|
| 136 | kenter( "'%*.*s',%u", datalen, datalen, data, datalen); | 
|---|
| 137 |  | 
|---|
| 138 | if (!data || data[datalen - 1] != '\0') | 
|---|
| 139 | return -EINVAL; | 
|---|
| 140 | datalen--; | 
|---|
| 141 |  | 
|---|
| 142 | /* deal with any options embedded in the data */ | 
|---|
| 143 | end = data + datalen; | 
|---|
| 144 | opt = memchr(data, '#', datalen); | 
|---|
| 145 | if (!opt) { | 
|---|
| 146 | /* no options: the entire data is the result */ | 
|---|
| 147 | kdebug( "no options"); | 
|---|
| 148 | result_len = datalen; | 
|---|
| 149 | } else { | 
|---|
| 150 | const char *next_opt; | 
|---|
| 151 |  | 
|---|
| 152 | result_len = opt - data; | 
|---|
| 153 | opt++; | 
|---|
| 154 | kdebug( "options: '%s'", opt); | 
|---|
| 155 | do { | 
|---|
| 156 | int opt_len, opt_nlen; | 
|---|
| 157 | const char *eq; | 
|---|
| 158 | char optval[128]; | 
|---|
| 159 |  | 
|---|
| 160 | next_opt = memchr(opt, '#', end - opt) ?: end; | 
|---|
| 161 | opt_len = next_opt - opt; | 
|---|
| 162 | if (opt_len <= 0 || opt_len > sizeof(optval)) { | 
|---|
| 163 | pr_warn_ratelimited( "Invalid option length (%d) for dns_resolver key\n", | 
|---|
| 164 | opt_len); | 
|---|
| 165 | return -EINVAL; | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | eq = memchr(opt, '=', opt_len); | 
|---|
| 169 | if (eq) { | 
|---|
| 170 | opt_nlen = eq - opt; | 
|---|
| 171 | eq++; | 
|---|
| 172 | memcpy(to: optval, from: eq, len: next_opt - eq); | 
|---|
| 173 | optval[next_opt - eq] = '\0'; | 
|---|
| 174 | } else { | 
|---|
| 175 | opt_nlen = opt_len; | 
|---|
| 176 | optval[0] = '\0'; | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | kdebug( "option '%*.*s' val '%s'", | 
|---|
| 180 | opt_nlen, opt_nlen, opt, optval); | 
|---|
| 181 |  | 
|---|
| 182 | /* see if it's an error number representing a DNS error | 
|---|
| 183 | * that's to be recorded as the result in this key */ | 
|---|
| 184 | if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 && | 
|---|
| 185 | memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) { | 
|---|
| 186 | kdebug( "dns error number option"); | 
|---|
| 187 |  | 
|---|
| 188 | ret = kstrtoul(s: optval, base: 10, res: &derrno); | 
|---|
| 189 | if (ret < 0) | 
|---|
| 190 | goto bad_option_value; | 
|---|
| 191 |  | 
|---|
| 192 | if (derrno < 1 || derrno > 511) | 
|---|
| 193 | goto bad_option_value; | 
|---|
| 194 |  | 
|---|
| 195 | kdebug( "dns error no. = %lu", derrno); | 
|---|
| 196 | prep->payload.data[dns_key_error] = ERR_PTR(error: -derrno); | 
|---|
| 197 | continue; | 
|---|
| 198 | } | 
|---|
| 199 |  | 
|---|
| 200 | bad_option_value: | 
|---|
| 201 | pr_warn_ratelimited( "Option '%*.*s' to dns_resolver key: bad/missing value\n", | 
|---|
| 202 | opt_nlen, opt_nlen, opt); | 
|---|
| 203 | return -EINVAL; | 
|---|
| 204 | } while (opt = next_opt + 1, opt < end); | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | /* don't cache the result if we're caching an error saying there's no | 
|---|
| 208 | * result */ | 
|---|
| 209 | if (prep->payload.data[dns_key_error]) { | 
|---|
| 210 | kleave( " = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error])); | 
|---|
| 211 | return 0; | 
|---|
| 212 | } | 
|---|
| 213 |  | 
|---|
| 214 | store_result: | 
|---|
| 215 | kdebug( "store result"); | 
|---|
| 216 | prep->quotalen = result_len; | 
|---|
| 217 |  | 
|---|
| 218 | upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL); | 
|---|
| 219 | if (!upayload) { | 
|---|
| 220 | kleave( " = -ENOMEM"); | 
|---|
| 221 | return -ENOMEM; | 
|---|
| 222 | } | 
|---|
| 223 |  | 
|---|
| 224 | upayload->datalen = result_len; | 
|---|
| 225 | memcpy(to: upayload->data, from: data, len: result_len); | 
|---|
| 226 | upayload->data[result_len] = '\0'; | 
|---|
| 227 |  | 
|---|
| 228 | prep->payload.data[dns_key_data] = upayload; | 
|---|
| 229 | kleave( " = 0"); | 
|---|
| 230 | return 0; | 
|---|
| 231 | } | 
|---|
| 232 |  | 
|---|
| 233 | /* | 
|---|
| 234 | * Clean up the preparse data | 
|---|
| 235 | */ | 
|---|
| 236 | static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) | 
|---|
| 237 | { | 
|---|
| 238 | pr_devel( "==>%s()\n", __func__); | 
|---|
| 239 |  | 
|---|
| 240 | kfree(objp: prep->payload.data[dns_key_data]); | 
|---|
| 241 | } | 
|---|
| 242 |  | 
|---|
| 243 | /* | 
|---|
| 244 | * The description is of the form "[<type>:]<domain_name>" | 
|---|
| 245 | * | 
|---|
| 246 | * The domain name may be a simple name or an absolute domain name (which | 
|---|
| 247 | * should end with a period).  The domain name is case-independent. | 
|---|
| 248 | */ | 
|---|
| 249 | static bool dns_resolver_cmp(const struct key *key, | 
|---|
| 250 | const struct key_match_data *match_data) | 
|---|
| 251 | { | 
|---|
| 252 | int slen, dlen, ret = 0; | 
|---|
| 253 | const char *src = key->description, *dsp = match_data->raw_data; | 
|---|
| 254 |  | 
|---|
| 255 | kenter( "%s,%s", src, dsp); | 
|---|
| 256 |  | 
|---|
| 257 | if (!src || !dsp) | 
|---|
| 258 | goto no_match; | 
|---|
| 259 |  | 
|---|
| 260 | if (strcasecmp(s1: src, s2: dsp) == 0) | 
|---|
| 261 | goto matched; | 
|---|
| 262 |  | 
|---|
| 263 | slen = strlen(src); | 
|---|
| 264 | dlen = strlen(dsp); | 
|---|
| 265 | if (slen <= 0 || dlen <= 0) | 
|---|
| 266 | goto no_match; | 
|---|
| 267 | if (src[slen - 1] == '.') | 
|---|
| 268 | slen--; | 
|---|
| 269 | if (dsp[dlen - 1] == '.') | 
|---|
| 270 | dlen--; | 
|---|
| 271 | if (slen != dlen || strncasecmp(s1: src, s2: dsp, n: slen) != 0) | 
|---|
| 272 | goto no_match; | 
|---|
| 273 |  | 
|---|
| 274 | matched: | 
|---|
| 275 | ret = 1; | 
|---|
| 276 | no_match: | 
|---|
| 277 | kleave( " = %d", ret); | 
|---|
| 278 | return ret; | 
|---|
| 279 | } | 
|---|
| 280 |  | 
|---|
| 281 | /* | 
|---|
| 282 | * Preparse the match criterion. | 
|---|
| 283 | */ | 
|---|
| 284 | static int dns_resolver_match_preparse(struct key_match_data *match_data) | 
|---|
| 285 | { | 
|---|
| 286 | match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; | 
|---|
| 287 | match_data->cmp = dns_resolver_cmp; | 
|---|
| 288 | return 0; | 
|---|
| 289 | } | 
|---|
| 290 |  | 
|---|
| 291 | /* | 
|---|
| 292 | * Describe a DNS key | 
|---|
| 293 | */ | 
|---|
| 294 | static void dns_resolver_describe(const struct key *key, struct seq_file *m) | 
|---|
| 295 | { | 
|---|
| 296 | seq_puts(m, s: key->description); | 
|---|
| 297 | if (key_is_positive(key)) { | 
|---|
| 298 | int err = PTR_ERR(ptr: key->payload.data[dns_key_error]); | 
|---|
| 299 |  | 
|---|
| 300 | if (err) | 
|---|
| 301 | seq_printf(m, fmt: ": %d", err); | 
|---|
| 302 | else | 
|---|
| 303 | seq_printf(m, fmt: ": %u", key->datalen); | 
|---|
| 304 | } | 
|---|
| 305 | } | 
|---|
| 306 |  | 
|---|
| 307 | /* | 
|---|
| 308 | * read the DNS data | 
|---|
| 309 | * - the key's semaphore is read-locked | 
|---|
| 310 | */ | 
|---|
| 311 | static long dns_resolver_read(const struct key *key, | 
|---|
| 312 | char *buffer, size_t buflen) | 
|---|
| 313 | { | 
|---|
| 314 | int err = PTR_ERR(ptr: key->payload.data[dns_key_error]); | 
|---|
| 315 |  | 
|---|
| 316 | if (err) | 
|---|
| 317 | return err; | 
|---|
| 318 |  | 
|---|
| 319 | return user_read(key, buffer, buflen); | 
|---|
| 320 | } | 
|---|
| 321 |  | 
|---|
| 322 | struct key_type key_type_dns_resolver = { | 
|---|
| 323 | .name		= "dns_resolver", | 
|---|
| 324 | .flags		= KEY_TYPE_NET_DOMAIN | KEY_TYPE_INSTANT_REAP, | 
|---|
| 325 | .preparse	= dns_resolver_preparse, | 
|---|
| 326 | .free_preparse	= dns_resolver_free_preparse, | 
|---|
| 327 | .instantiate	= generic_key_instantiate, | 
|---|
| 328 | .match_preparse	= dns_resolver_match_preparse, | 
|---|
| 329 | .revoke		= user_revoke, | 
|---|
| 330 | .destroy	= user_destroy, | 
|---|
| 331 | .describe	= dns_resolver_describe, | 
|---|
| 332 | .read		= dns_resolver_read, | 
|---|
| 333 | }; | 
|---|
| 334 |  | 
|---|
| 335 | static int __init init_dns_resolver(void) | 
|---|
| 336 | { | 
|---|
| 337 | struct cred *cred; | 
|---|
| 338 | struct key *keyring; | 
|---|
| 339 | int ret; | 
|---|
| 340 |  | 
|---|
| 341 | /* create an override credential set with a special thread keyring in | 
|---|
| 342 | * which DNS requests are cached | 
|---|
| 343 | * | 
|---|
| 344 | * this is used to prevent malicious redirections from being installed | 
|---|
| 345 | * with add_key(). | 
|---|
| 346 | */ | 
|---|
| 347 | cred = prepare_kernel_cred(&init_task); | 
|---|
| 348 | if (!cred) | 
|---|
| 349 | return -ENOMEM; | 
|---|
| 350 |  | 
|---|
| 351 | keyring = keyring_alloc(description: ".dns_resolver", | 
|---|
| 352 | GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, | 
|---|
| 353 | perm: (KEY_POS_ALL & ~KEY_POS_SETATTR) | | 
|---|
| 354 | KEY_USR_VIEW | KEY_USR_READ, | 
|---|
| 355 | KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); | 
|---|
| 356 | if (IS_ERR(keyring)) { | 
|---|
| 357 | ret = PTR_ERR(keyring); | 
|---|
| 358 | goto failed_put_cred; | 
|---|
| 359 | } | 
|---|
| 360 |  | 
|---|
| 361 | ret = register_key_type(&key_type_dns_resolver); | 
|---|
| 362 | if (ret < 0) | 
|---|
| 363 | goto failed_put_key; | 
|---|
| 364 |  | 
|---|
| 365 | /* instruct request_key() to use this special keyring as a cache for | 
|---|
| 366 | * the results it looks up */ | 
|---|
| 367 | set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); | 
|---|
| 368 | cred->thread_keyring = keyring; | 
|---|
| 369 | cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; | 
|---|
| 370 | dns_resolver_cache = cred; | 
|---|
| 371 |  | 
|---|
| 372 | kdebug( "DNS resolver keyring: %d\n", key_serial(keyring)); | 
|---|
| 373 | return 0; | 
|---|
| 374 |  | 
|---|
| 375 | failed_put_key: | 
|---|
| 376 | key_put(keyring); | 
|---|
| 377 | failed_put_cred: | 
|---|
| 378 | put_cred(cred); | 
|---|
| 379 | return ret; | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | static void __exit exit_dns_resolver(void) | 
|---|
| 383 | { | 
|---|
| 384 | key_revoke(key: dns_resolver_cache->thread_keyring); | 
|---|
| 385 | unregister_key_type(ktype: &key_type_dns_resolver); | 
|---|
| 386 | put_cred(cred: dns_resolver_cache); | 
|---|
| 387 | } | 
|---|
| 388 |  | 
|---|
| 389 | module_init(init_dns_resolver) | 
|---|
| 390 | module_exit(exit_dns_resolver) | 
|---|
| 391 | MODULE_LICENSE( "GPL"); | 
|---|
| 392 |  | 
|---|