2010-08-04 18:16:33 +04:00
/* Upcall routine, designed to work as a key type and working through
* / sbin / request - key to contact userspace when handling DNS queries .
*
* See Documentation / networking / dns_resolver . txt
*
* Copyright ( c ) 2007 Igor Mammedov
* Author ( s ) : Igor Mammedov ( niallain @ gmail . com )
* Steve French ( sfrench @ us . ibm . com )
* Wang Lei ( wang840925 @ gmail . com )
* David Howells ( dhowells @ redhat . com )
*
* The upcall wrapper used to make an arbitrary DNS query .
*
* This function requires the appropriate userspace tool dns . upcall to be
* installed and something like the following lines should be added to the
* / etc / request - key . conf file :
*
* create dns_resolver * * / sbin / dns . upcall % k
*
* For example to use this module to query AFSDB RR :
*
* create dns_resolver afsdb : * * / sbin / dns . afsdb % k
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
2013-12-06 21:13:44 +04:00
* along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2010-08-04 18:16:33 +04:00
*/
# include <linux/module.h>
# include <linux/slab.h>
2017-02-02 19:54:15 +03:00
# include <linux/cred.h>
2010-08-04 18:16:33 +04:00
# include <linux/dns_resolver.h>
2010-08-06 06:13:47 +04:00
# include <linux/err.h>
2017-02-02 19:54:15 +03:00
2010-08-04 18:16:33 +04:00
# include <keys/dns_resolver-type.h>
# include <keys/user-type.h>
# include "internal.h"
2010-08-06 06:13:52 +04:00
/**
2010-08-04 18:16:33 +04:00
* dns_query - Query the DNS
* @ type : Query type ( or NULL for straight host - > IP lookup )
* @ name : Name to look up
* @ namelen : Length of name
* @ options : Request options ( or NULL if no options )
2018-02-06 09:26:30 +03:00
* @ _result : Where to place the returned data ( or NULL )
2010-08-04 18:16:33 +04:00
* @ _expiry : Where to store the result expiry time ( or NULL )
*
2018-02-06 09:26:30 +03:00
* The data will be returned in the pointer at * result , if provided , and the
* caller is responsible for freeing it .
2010-08-04 18:16:33 +04:00
*
* The description should be of the form " [<query_type>:]<domain_name> " , and
* the options need to be appropriate for the query type requested . If no
* query_type is given , then the query is a straight hostname to IP address
* lookup .
*
* The DNS resolution lookup is performed by upcalling to userspace by way of
* requesting a key of type dns_resolver .
*
* Returns the size of the result on success , - ve error code otherwise .
*/
int dns_query ( const char * type , const char * name , size_t namelen ,
2015-11-18 09:36:44 +03:00
const char * options , char * * _result , time64_t * _expiry )
2010-08-04 18:16:33 +04:00
{
struct key * rkey ;
2017-03-01 18:11:23 +03:00
struct user_key_payload * upayload ;
2010-08-04 18:16:33 +04:00
const struct cred * saved_cred ;
size_t typelen , desclen ;
char * desc , * cp ;
int ret , len ;
kenter ( " %s,%*.*s,%zu,%s " ,
type , ( int ) namelen , ( int ) namelen , name , namelen , options ) ;
2018-02-06 09:26:30 +03:00
if ( ! name | | namelen = = 0 )
2010-08-04 18:16:33 +04:00
return - EINVAL ;
/* construct the query key description as "[<type>:]<name>" */
typelen = 0 ;
desclen = 0 ;
if ( type ) {
typelen = strlen ( type ) ;
if ( typelen < 1 )
return - EINVAL ;
desclen + = typelen + 1 ;
}
if ( ! namelen )
2014-06-01 01:37:40 +04:00
namelen = strnlen ( name , 256 ) ;
if ( namelen < 3 | | namelen > 255 )
2010-08-04 18:16:33 +04:00
return - EINVAL ;
desclen + = namelen + 1 ;
desc = kmalloc ( desclen , GFP_KERNEL ) ;
if ( ! desc )
return - ENOMEM ;
cp = desc ;
if ( type ) {
memcpy ( cp , type , typelen ) ;
cp + = typelen ;
* cp + + = ' : ' ;
}
memcpy ( cp , name , namelen ) ;
cp + = namelen ;
* cp = ' \0 ' ;
if ( ! options )
options = " " ;
kdebug ( " call request_key(,%s,%s) " , desc , options ) ;
/* make the upcall, using special credentials to prevent the use of
* add_key ( ) to preinstall malicious redirections
*/
saved_cred = override_creds ( dns_resolver_cache ) ;
rkey = request_key ( & key_type_dns_resolver , desc , options ) ;
revert_creds ( saved_cred ) ;
kfree ( desc ) ;
if ( IS_ERR ( rkey ) ) {
ret = PTR_ERR ( rkey ) ;
goto out ;
}
down_read ( & rkey - > sem ) ;
2014-07-17 23:45:08 +04:00
set_bit ( KEY_FLAG_ROOT_CAN_INVAL , & rkey - > flags ) ;
2010-08-04 18:16:33 +04:00
rkey - > perm | = KEY_USR_VIEW ;
ret = key_validate ( rkey ) ;
if ( ret < 0 )
goto put ;
2010-08-11 12:37:58 +04:00
/* If the DNS server gave an error, return that to the caller */
2015-10-21 16:04:48 +03:00
ret = PTR_ERR ( rkey - > payload . data [ dns_key_error ] ) ;
2010-08-11 12:37:58 +04:00
if ( ret )
goto put ;
2017-03-01 18:11:23 +03:00
upayload = user_key_payload_locked ( rkey ) ;
2010-08-04 18:16:33 +04:00
len = upayload - > datalen ;
2018-02-06 09:26:30 +03:00
if ( _result ) {
ret = - ENOMEM ;
2018-10-04 16:27:55 +03:00
* _result = kmemdup_nul ( upayload - > data , len , GFP_KERNEL ) ;
2018-02-06 09:26:30 +03:00
if ( ! * _result )
goto put ;
}
2014-06-08 01:57:25 +04:00
2010-08-04 18:16:33 +04:00
if ( _expiry )
* _expiry = rkey - > expiry ;
ret = len ;
put :
up_read ( & rkey - > sem ) ;
key_put ( rkey ) ;
out :
kleave ( " = %d " , ret ) ;
return ret ;
}
EXPORT_SYMBOL ( dns_query ) ;