| 1 | |
| 2 | |
| 3 | * /sbin/request-key to contact userspace when handling DNS queries.
|
| 4 | *
|
| 5 | * See Documentation/networking/dns_resolver.rst
|
| 6 | *
|
| 7 | * Copyright (c) 2007 Igor Mammedov
|
| 8 | * Author(s): Igor Mammedov ([email protected])
|
| 9 | * Steve French ([email protected])
|
| 10 | * Wang Lei ([email protected])
|
| 11 | * David Howells ([email protected])
|
| 12 | *
|
| 13 | * The upcall wrapper used to make an arbitrary DNS query.
|
| 14 | *
|
| 15 | * This function requires the appropriate userspace tool dns.upcall to be
|
| 16 | * installed and something like the following lines should be added to the
|
| 17 | * /etc/request-key.conf file:
|
| 18 | *
|
| 19 | * create dns_resolver * * /sbin/dns.upcall %k
|
| 20 | *
|
| 21 | * For example to use this module to query AFSDB RR:
|
| 22 | *
|
| 23 | * create dns_resolver afsdb:* * /sbin/dns.afsdb %k
|
| 24 |
|
| 25 |
|
| 26 | #include <linux/module.h>
|
| 27 | #include <linux/slab.h>
|
| 28 | #include <linux/cred.h>
|
| 29 | #include <linux/dns_resolver.h>
|
| 30 | #include <linux/err.h>
|
| 31 | #include <net/net_namespace.h>
|
| 32 |
|
| 33 | #include <keys/dns_resolver-type.h>
|
| 34 | #include <keys/user-type.h>
|
| 35 |
|
| 36 | #include "internal.h"
|
| 37 |
|
| 38 | |
| 39 | * dns_query - Query the DNS
|
| 40 | * @net: The network namespace to operate in.
|
| 41 | * @type: Query type (or NULL for straight host->IP lookup)
|
| 42 | * @name: Name to look up
|
| 43 | * @namelen: Length of name
|
| 44 | * @options: Request options (or NULL if no options)
|
| 45 | * @_result: Where to place the returned data (or NULL)
|
| 46 | * @_expiry: Where to store the result expiry time (or NULL)
|
| 47 | * @invalidate: Always invalidate the key after use
|
| 48 | *
|
| 49 | * The data will be returned in the pointer at *result, if provided, and the
|
| 50 | * caller is responsible for freeing it.
|
| 51 | *
|
| 52 | * The description should be of the form "[<query_type>:]<domain_name>", and
|
| 53 | * the options need to be appropriate for the query type requested. If no
|
| 54 | * query_type is given, then the query is a straight hostname to IP address
|
| 55 | * lookup.
|
| 56 | *
|
| 57 | * The DNS resolution lookup is performed by upcalling to userspace by way of
|
| 58 | * requesting a key of type dns_resolver.
|
| 59 | *
|
| 60 | * Returns the size of the result on success, -ve error code otherwise.
|
| 61 |
|
| 62 | int dns_query(struct net *net,
|
| 63 | const char *type, const char *name, size_t namelen,
|
| 64 | const char *options, char **_result, time64_t *_expiry,
|
| 65 | bool invalidate)
|
| 66 | {
|
| 67 | struct user_key_payload *upayload;
|
| 68 | struct key *rkey;
|
| 69 | int ret, len;
|
| 70 | char *desc;
|
| 71 |
|
| 72 | kenter("%s,%*.*s,%zu,%s",
|
| 73 | type, (int)namelen, (int)namelen, name, namelen, options);
|
| 74 |
|
| 75 | if (!name || namelen < 1 || namelen > 255)
|
| 76 | return -EINVAL;
|
| 77 | if (type && *type == '\0')
|
| 78 | return -EINVAL;
|
| 79 |
|
| 80 |
|
| 81 | if (type)
|
| 82 | desc = kasprintf(GFP_KERNEL, "%s:%.*s", type, (int)namelen, name);
|
| 83 | else
|
| 84 | desc = kmemdup_nul(name, namelen, GFP_KERNEL);
|
| 85 | if (!desc)
|
| 86 | return -ENOMEM;
|
| 87 |
|
| 88 | if (!options)
|
| 89 | options = "";
|
| 90 | kdebug("call request_key(,%s,%s)", desc, options);
|
| 91 |
|
| 92 | |
| 93 | * add_key() to preinstall malicious redirections
|
| 94 |
|
| 95 | scoped_with_creds(dns_resolver_cache)
|
| 96 | rkey = request_key_net(&key_type_dns_resolver, desc, net, options);
|
| 97 | kfree(desc);
|
| 98 | if (IS_ERR(rkey)) {
|
| 99 | ret = PTR_ERR(rkey);
|
| 100 | goto out;
|
| 101 | }
|
| 102 |
|
| 103 | down_read(&rkey->sem);
|
| 104 | set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
|
| 105 | rkey->perm |= KEY_USR_VIEW;
|
| 106 |
|
| 107 | ret = key_validate(rkey);
|
| 108 | if (ret < 0)
|
| 109 | goto put;
|
| 110 |
|
| 111 |
|
| 112 | ret = PTR_ERR(rkey->payload.data[dns_key_error]);
|
| 113 | if (ret)
|
| 114 | goto put;
|
| 115 |
|
| 116 | upayload = user_key_payload_locked(rkey);
|
| 117 | len = upayload->datalen;
|
| 118 |
|
| 119 | if (_result) {
|
| 120 | ret = -ENOMEM;
|
| 121 | *_result = kmemdup_nul(upayload->data, len, GFP_KERNEL);
|
| 122 | if (!*_result)
|
| 123 | goto put;
|
| 124 | }
|
| 125 |
|
| 126 | if (_expiry)
|
| 127 | *_expiry = rkey->expiry;
|
| 128 |
|
| 129 | ret = len;
|
| 130 | put:
|
| 131 | up_read(&rkey->sem);
|
| 132 | if (invalidate)
|
| 133 | key_invalidate(rkey);
|
| 134 | key_put(rkey);
|
| 135 | out:
|
| 136 | kleave(" = %d", ret);
|
| 137 | return ret;
|
| 138 | }
|
| 139 | EXPORT_SYMBOL(dns_query);
|