Back to home page

OSCL-LXR

 
 

    


0001 /* Key type used to cache DNS lookups made by the kernel
0002  *
0003  * See Documentation/networking/dns_resolver.rst
0004  *
0005  *   Copyright (c) 2007 Igor Mammedov
0006  *   Author(s): Igor Mammedov (niallain@gmail.com)
0007  *              Steve French (sfrench@us.ibm.com)
0008  *              Wang Lei (wang840925@gmail.com)
0009  *      David Howells (dhowells@redhat.com)
0010  *
0011  *   This library is free software; you can redistribute it and/or modify
0012  *   it under the terms of the GNU Lesser General Public License as published
0013  *   by the Free Software Foundation; either version 2.1 of the License, or
0014  *   (at your option) any later version.
0015  *
0016  *   This library is distributed in the hope that it will be useful,
0017  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0018  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
0019  *   the GNU Lesser General Public License for more details.
0020  *
0021  *   You should have received a copy of the GNU Lesser General Public License
0022  *   along with this library; if not, see <http://www.gnu.org/licenses/>.
0023  */
0024 #include <linux/module.h>
0025 #include <linux/moduleparam.h>
0026 #include <linux/slab.h>
0027 #include <linux/string.h>
0028 #include <linux/kernel.h>
0029 #include <linux/keyctl.h>
0030 #include <linux/err.h>
0031 #include <linux/seq_file.h>
0032 #include <linux/dns_resolver.h>
0033 #include <keys/dns_resolver-type.h>
0034 #include <keys/user-type.h>
0035 #include "internal.h"
0036 
0037 MODULE_DESCRIPTION("DNS Resolver");
0038 MODULE_AUTHOR("Wang Lei");
0039 MODULE_LICENSE("GPL");
0040 
0041 unsigned int dns_resolver_debug;
0042 module_param_named(debug, dns_resolver_debug, uint, 0644);
0043 MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
0044 
0045 const struct cred *dns_resolver_cache;
0046 
0047 #define DNS_ERRORNO_OPTION  "dnserror"
0048 
0049 /*
0050  * Preparse instantiation data for a dns_resolver key.
0051  *
0052  * For normal hostname lookups, the data must be a NUL-terminated string, with
0053  * the NUL char accounted in datalen.
0054  *
0055  * If the data contains a '#' characters, then we take the clause after each
0056  * one to be an option of the form 'key=value'.  The actual data of interest is
0057  * the string leading up to the first '#'.  For instance:
0058  *
0059  *        "ip1,ip2,...#foo=bar"
0060  *
0061  * For server list requests, the data must begin with a NUL char and be
0062  * followed by a byte indicating the version of the data format.  Version 1
0063  * looks something like (note this is packed):
0064  *
0065  *  u8      Non-string marker (ie. 0)
0066  *  u8  Content (DNS_PAYLOAD_IS_*)
0067  *  u8  Version (e.g. 1)
0068  *  u8  Source of server list
0069  *  u8  Lookup status of server list
0070  *  u8  Number of servers
0071  *  foreach-server {
0072  *      __le16  Name length
0073  *      __le16  Priority (as per SRV record, low first)
0074  *      __le16  Weight (as per SRV record, higher first)
0075  *      __le16  Port
0076  *      u8  Source of address list
0077  *      u8  Lookup status of address list
0078  *      u8  Protocol (DNS_SERVER_PROTOCOL_*)
0079  *      u8  Number of addresses
0080  *      char[]  Name (not NUL-terminated)
0081  *      foreach-address {
0082  *          u8      Family (DNS_ADDRESS_IS_*)
0083  *          union {
0084  *              u8[4]   ipv4_addr
0085  *              u8[16]  ipv6_addr
0086  *          }
0087  *      }
0088  *  }
0089  *
0090  */
0091 static int
0092 dns_resolver_preparse(struct key_preparsed_payload *prep)
0093 {
0094     const struct dns_payload_header *bin;
0095     struct user_key_payload *upayload;
0096     unsigned long derrno;
0097     int ret;
0098     int datalen = prep->datalen, result_len = 0;
0099     const char *data = prep->data, *end, *opt;
0100 
0101     if (datalen <= 1 || !data)
0102         return -EINVAL;
0103 
0104     if (data[0] == 0) {
0105         /* It may be a server list. */
0106         if (datalen <= sizeof(*bin))
0107             return -EINVAL;
0108 
0109         bin = (const struct dns_payload_header *)data;
0110         kenter("[%u,%u],%u", bin->content, bin->version, datalen);
0111         if (bin->content != DNS_PAYLOAD_IS_SERVER_LIST) {
0112             pr_warn_ratelimited(
0113                 "dns_resolver: Unsupported content type (%u)\n",
0114                 bin->content);
0115             return -EINVAL;
0116         }
0117 
0118         if (bin->version != 1) {
0119             pr_warn_ratelimited(
0120                 "dns_resolver: Unsupported server list version (%u)\n",
0121                 bin->version);
0122             return -EINVAL;
0123         }
0124 
0125         result_len = datalen;
0126         goto store_result;
0127     }
0128 
0129     kenter("'%*.*s',%u", datalen, datalen, data, datalen);
0130 
0131     if (!data || data[datalen - 1] != '\0')
0132         return -EINVAL;
0133     datalen--;
0134 
0135     /* deal with any options embedded in the data */
0136     end = data + datalen;
0137     opt = memchr(data, '#', datalen);
0138     if (!opt) {
0139         /* no options: the entire data is the result */
0140         kdebug("no options");
0141         result_len = datalen;
0142     } else {
0143         const char *next_opt;
0144 
0145         result_len = opt - data;
0146         opt++;
0147         kdebug("options: '%s'", opt);
0148         do {
0149             int opt_len, opt_nlen;
0150             const char *eq;
0151             char optval[128];
0152 
0153             next_opt = memchr(opt, '#', end - opt) ?: end;
0154             opt_len = next_opt - opt;
0155             if (opt_len <= 0 || opt_len > sizeof(optval)) {
0156                 pr_warn_ratelimited("Invalid option length (%d) for dns_resolver key\n",
0157                             opt_len);
0158                 return -EINVAL;
0159             }
0160 
0161             eq = memchr(opt, '=', opt_len);
0162             if (eq) {
0163                 opt_nlen = eq - opt;
0164                 eq++;
0165                 memcpy(optval, eq, next_opt - eq);
0166                 optval[next_opt - eq] = '\0';
0167             } else {
0168                 opt_nlen = opt_len;
0169                 optval[0] = '\0';
0170             }
0171 
0172             kdebug("option '%*.*s' val '%s'",
0173                    opt_nlen, opt_nlen, opt, optval);
0174 
0175             /* see if it's an error number representing a DNS error
0176              * that's to be recorded as the result in this key */
0177             if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 &&
0178                 memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) {
0179                 kdebug("dns error number option");
0180 
0181                 ret = kstrtoul(optval, 10, &derrno);
0182                 if (ret < 0)
0183                     goto bad_option_value;
0184 
0185                 if (derrno < 1 || derrno > 511)
0186                     goto bad_option_value;
0187 
0188                 kdebug("dns error no. = %lu", derrno);
0189                 prep->payload.data[dns_key_error] = ERR_PTR(-derrno);
0190                 continue;
0191             }
0192 
0193         bad_option_value:
0194             pr_warn_ratelimited("Option '%*.*s' to dns_resolver key: bad/missing value\n",
0195                         opt_nlen, opt_nlen, opt);
0196             return -EINVAL;
0197         } while (opt = next_opt + 1, opt < end);
0198     }
0199 
0200     /* don't cache the result if we're caching an error saying there's no
0201      * result */
0202     if (prep->payload.data[dns_key_error]) {
0203         kleave(" = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error]));
0204         return 0;
0205     }
0206 
0207 store_result:
0208     kdebug("store result");
0209     prep->quotalen = result_len;
0210 
0211     upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
0212     if (!upayload) {
0213         kleave(" = -ENOMEM");
0214         return -ENOMEM;
0215     }
0216 
0217     upayload->datalen = result_len;
0218     memcpy(upayload->data, data, result_len);
0219     upayload->data[result_len] = '\0';
0220 
0221     prep->payload.data[dns_key_data] = upayload;
0222     kleave(" = 0");
0223     return 0;
0224 }
0225 
0226 /*
0227  * Clean up the preparse data
0228  */
0229 static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
0230 {
0231     pr_devel("==>%s()\n", __func__);
0232 
0233     kfree(prep->payload.data[dns_key_data]);
0234 }
0235 
0236 /*
0237  * The description is of the form "[<type>:]<domain_name>"
0238  *
0239  * The domain name may be a simple name or an absolute domain name (which
0240  * should end with a period).  The domain name is case-independent.
0241  */
0242 static bool dns_resolver_cmp(const struct key *key,
0243                  const struct key_match_data *match_data)
0244 {
0245     int slen, dlen, ret = 0;
0246     const char *src = key->description, *dsp = match_data->raw_data;
0247 
0248     kenter("%s,%s", src, dsp);
0249 
0250     if (!src || !dsp)
0251         goto no_match;
0252 
0253     if (strcasecmp(src, dsp) == 0)
0254         goto matched;
0255 
0256     slen = strlen(src);
0257     dlen = strlen(dsp);
0258     if (slen <= 0 || dlen <= 0)
0259         goto no_match;
0260     if (src[slen - 1] == '.')
0261         slen--;
0262     if (dsp[dlen - 1] == '.')
0263         dlen--;
0264     if (slen != dlen || strncasecmp(src, dsp, slen) != 0)
0265         goto no_match;
0266 
0267 matched:
0268     ret = 1;
0269 no_match:
0270     kleave(" = %d", ret);
0271     return ret;
0272 }
0273 
0274 /*
0275  * Preparse the match criterion.
0276  */
0277 static int dns_resolver_match_preparse(struct key_match_data *match_data)
0278 {
0279     match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
0280     match_data->cmp = dns_resolver_cmp;
0281     return 0;
0282 }
0283 
0284 /*
0285  * Describe a DNS key
0286  */
0287 static void dns_resolver_describe(const struct key *key, struct seq_file *m)
0288 {
0289     seq_puts(m, key->description);
0290     if (key_is_positive(key)) {
0291         int err = PTR_ERR(key->payload.data[dns_key_error]);
0292 
0293         if (err)
0294             seq_printf(m, ": %d", err);
0295         else
0296             seq_printf(m, ": %u", key->datalen);
0297     }
0298 }
0299 
0300 /*
0301  * read the DNS data
0302  * - the key's semaphore is read-locked
0303  */
0304 static long dns_resolver_read(const struct key *key,
0305                   char *buffer, size_t buflen)
0306 {
0307     int err = PTR_ERR(key->payload.data[dns_key_error]);
0308 
0309     if (err)
0310         return err;
0311 
0312     return user_read(key, buffer, buflen);
0313 }
0314 
0315 struct key_type key_type_dns_resolver = {
0316     .name       = "dns_resolver",
0317     .flags      = KEY_TYPE_NET_DOMAIN,
0318     .preparse   = dns_resolver_preparse,
0319     .free_preparse  = dns_resolver_free_preparse,
0320     .instantiate    = generic_key_instantiate,
0321     .match_preparse = dns_resolver_match_preparse,
0322     .revoke     = user_revoke,
0323     .destroy    = user_destroy,
0324     .describe   = dns_resolver_describe,
0325     .read       = dns_resolver_read,
0326 };
0327 
0328 static int __init init_dns_resolver(void)
0329 {
0330     struct cred *cred;
0331     struct key *keyring;
0332     int ret;
0333 
0334     /* create an override credential set with a special thread keyring in
0335      * which DNS requests are cached
0336      *
0337      * this is used to prevent malicious redirections from being installed
0338      * with add_key().
0339      */
0340     cred = prepare_kernel_cred(NULL);
0341     if (!cred)
0342         return -ENOMEM;
0343 
0344     keyring = keyring_alloc(".dns_resolver",
0345                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
0346                 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
0347                 KEY_USR_VIEW | KEY_USR_READ,
0348                 KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
0349     if (IS_ERR(keyring)) {
0350         ret = PTR_ERR(keyring);
0351         goto failed_put_cred;
0352     }
0353 
0354     ret = register_key_type(&key_type_dns_resolver);
0355     if (ret < 0)
0356         goto failed_put_key;
0357 
0358     /* instruct request_key() to use this special keyring as a cache for
0359      * the results it looks up */
0360     set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
0361     cred->thread_keyring = keyring;
0362     cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
0363     dns_resolver_cache = cred;
0364 
0365     kdebug("DNS resolver keyring: %d\n", key_serial(keyring));
0366     return 0;
0367 
0368 failed_put_key:
0369     key_put(keyring);
0370 failed_put_cred:
0371     put_cred(cred);
0372     return ret;
0373 }
0374 
0375 static void __exit exit_dns_resolver(void)
0376 {
0377     key_revoke(dns_resolver_cache->thread_keyring);
0378     unregister_key_type(&key_type_dns_resolver);
0379     put_cred(dns_resolver_cache);
0380 }
0381 
0382 module_init(init_dns_resolver)
0383 module_exit(exit_dns_resolver)
0384 MODULE_LICENSE("GPL");