freebsd-dev/crypto/openssh/dns.c

352 lines
9.0 KiB
C
Raw Normal View History

2015-08-26 09:27:05 +00:00
/* $OpenBSD: dns.c,v 1.35 2015/08/20 22:32:42 deraadt Exp $ */
2004-01-07 11:10:17 +00:00
/*
* Copyright (c) 2003 Wesley Griffin. All rights reserved.
* Copyright (c) 2003 Jakob Schlyter. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "includes.h"
2006-09-30 13:29:51 +00:00
#include <sys/types.h>
#include <sys/socket.h>
2004-01-07 11:10:17 +00:00
#include <netdb.h>
2006-09-30 13:29:51 +00:00
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
2015-01-05 16:09:55 +00:00
#include <stdarg.h>
#include <stdlib.h>
2004-01-07 11:10:17 +00:00
#include "xmalloc.h"
2015-07-02 13:15:34 +00:00
#include "sshkey.h"
#include "ssherr.h"
2004-01-07 11:10:17 +00:00
#include "dns.h"
#include "log.h"
2015-07-02 13:15:34 +00:00
#include "digest.h"
2004-01-07 11:10:17 +00:00
static const char *errset_text[] = {
"success", /* 0 ERRSET_SUCCESS */
"out of memory", /* 1 ERRSET_NOMEMORY */
"general failure", /* 2 ERRSET_FAIL */
"invalid parameter", /* 3 ERRSET_INVAL */
"name does not exist", /* 4 ERRSET_NONAME */
"data does not exist", /* 5 ERRSET_NODATA */
};
static const char *
2004-10-28 16:03:53 +00:00
dns_result_totext(unsigned int res)
2004-01-07 11:10:17 +00:00
{
2004-10-28 16:03:53 +00:00
switch (res) {
2004-01-07 11:10:17 +00:00
case ERRSET_SUCCESS:
return errset_text[ERRSET_SUCCESS];
case ERRSET_NOMEMORY:
return errset_text[ERRSET_NOMEMORY];
case ERRSET_FAIL:
return errset_text[ERRSET_FAIL];
case ERRSET_INVAL:
return errset_text[ERRSET_INVAL];
case ERRSET_NONAME:
return errset_text[ERRSET_NONAME];
case ERRSET_NODATA:
return errset_text[ERRSET_NODATA];
default:
return "unknown error";
}
}
/*
* Read SSHFP parameters from key buffer.
*/
static int
dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
2015-07-02 13:15:34 +00:00
u_char **digest, size_t *digest_len, struct sshkey *key)
2004-01-07 11:10:17 +00:00
{
2015-07-02 13:15:34 +00:00
int r, success = 0;
int fp_alg = -1;
2004-01-07 11:10:17 +00:00
switch (key->type) {
case KEY_RSA:
*algorithm = SSHFP_KEY_RSA;
2012-08-29 15:55:54 +00:00
if (!*digest_type)
*digest_type = SSHFP_HASH_SHA1;
2004-01-07 11:10:17 +00:00
break;
case KEY_DSA:
*algorithm = SSHFP_KEY_DSA;
2012-08-29 15:55:54 +00:00
if (!*digest_type)
*digest_type = SSHFP_HASH_SHA1;
break;
case KEY_ECDSA:
*algorithm = SSHFP_KEY_ECDSA;
if (!*digest_type)
*digest_type = SSHFP_HASH_SHA256;
2004-01-07 11:10:17 +00:00
break;
2015-01-05 16:09:55 +00:00
case KEY_ED25519:
*algorithm = SSHFP_KEY_ED25519;
if (!*digest_type)
*digest_type = SSHFP_HASH_SHA256;
break;
2004-01-07 11:10:17 +00:00
default:
2006-03-22 19:46:12 +00:00
*algorithm = SSHFP_KEY_RESERVED; /* 0 */
2012-08-29 15:55:54 +00:00
*digest_type = SSHFP_HASH_RESERVED; /* 0 */
}
switch (*digest_type) {
case SSHFP_HASH_SHA1:
2015-07-02 13:15:34 +00:00
fp_alg = SSH_DIGEST_SHA1;
2012-08-29 15:55:54 +00:00
break;
case SSHFP_HASH_SHA256:
2015-07-02 13:15:34 +00:00
fp_alg = SSH_DIGEST_SHA256;
2012-08-29 15:55:54 +00:00
break;
default:
*digest_type = SSHFP_HASH_RESERVED; /* 0 */
2004-01-07 11:10:17 +00:00
}
2012-08-29 15:55:54 +00:00
if (*algorithm && *digest_type) {
2015-07-02 13:15:34 +00:00
if ((r = sshkey_fingerprint_raw(key, fp_alg, digest,
digest_len)) != 0)
fatal("%s: sshkey_fingerprint_raw: %s", __func__,
ssh_err(r));
2004-01-07 11:10:17 +00:00
success = 1;
} else {
*digest = NULL;
*digest_len = 0;
success = 0;
}
return success;
}
/*
* Read SSHFP parameters from rdata buffer.
*/
static int
dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type,
2015-07-02 13:15:34 +00:00
u_char **digest, size_t *digest_len, u_char *rdata, int rdata_len)
2004-01-07 11:10:17 +00:00
{
int success = 0;
*algorithm = SSHFP_KEY_RESERVED;
*digest_type = SSHFP_HASH_RESERVED;
if (rdata_len >= 2) {
*algorithm = rdata[0];
*digest_type = rdata[1];
*digest_len = rdata_len - 2;
if (*digest_len > 0) {
2015-08-26 09:27:05 +00:00
*digest = xmalloc(*digest_len);
2004-01-07 11:10:17 +00:00
memcpy(*digest, rdata + 2, *digest_len);
} else {
2006-09-30 13:29:51 +00:00
*digest = (u_char *)xstrdup("");
2004-01-07 11:10:17 +00:00
}
success = 1;
}
return success;
}
2005-09-03 06:59:33 +00:00
/*
* Check if hostname is numerical.
* Returns -1 if hostname is numeric, 0 otherwise
*/
static int
is_numeric_hostname(const char *hostname)
{
struct addrinfo hints, *ai;
2008-07-23 09:33:08 +00:00
/*
* We shouldn't ever get a null host but if we do then log an error
* and return -1 which stops DNS key fingerprint processing.
*/
if (hostname == NULL) {
error("is_numeric_hostname called with NULL hostname");
return -1;
}
2005-09-03 06:59:33 +00:00
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_NUMERICHOST;
2008-07-23 09:33:08 +00:00
if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) {
2005-09-03 06:59:33 +00:00
freeaddrinfo(ai);
return -1;
}
return 0;
}
2004-01-07 11:10:17 +00:00
/*
* Verify the given hostname, address and host key using DNS.
2004-02-26 10:38:49 +00:00
* Returns 0 if lookup succeeds, -1 otherwise
2004-01-07 11:10:17 +00:00
*/
int
verify_host_key_dns(const char *hostname, struct sockaddr *address,
2015-07-02 13:15:34 +00:00
struct sshkey *hostkey, int *flags)
2004-01-07 11:10:17 +00:00
{
2005-09-03 06:59:33 +00:00
u_int counter;
2004-01-07 11:10:17 +00:00
int result;
struct rrsetinfo *fingerprints = NULL;
u_int8_t hostkey_algorithm;
2012-08-29 15:55:54 +00:00
u_int8_t hostkey_digest_type = SSHFP_HASH_RESERVED;
2004-01-07 11:10:17 +00:00
u_char *hostkey_digest;
2015-07-02 13:15:34 +00:00
size_t hostkey_digest_len;
2004-01-07 11:10:17 +00:00
u_int8_t dnskey_algorithm;
u_int8_t dnskey_digest_type;
u_char *dnskey_digest;
2015-07-02 13:15:34 +00:00
size_t dnskey_digest_len;
2004-01-07 11:10:17 +00:00
2004-02-26 10:38:49 +00:00
*flags = 0;
2004-01-07 11:10:17 +00:00
2006-03-22 19:46:12 +00:00
debug3("verify_host_key_dns");
2004-01-07 11:10:17 +00:00
if (hostkey == NULL)
fatal("No key to look up!");
2005-09-03 06:59:33 +00:00
if (is_numeric_hostname(hostname)) {
debug("skipped DNS lookup for numerical hostname");
return -1;
}
2004-01-07 11:10:17 +00:00
result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
DNS_RDATATYPE_SSHFP, 0, &fingerprints);
if (result) {
verbose("DNS lookup error: %s", dns_result_totext(result));
2004-02-26 10:38:49 +00:00
return -1;
2004-01-07 11:10:17 +00:00
}
2004-02-26 10:38:49 +00:00
if (fingerprints->rri_flags & RRSET_VALIDATED) {
*flags |= DNS_VERIFY_SECURE;
debug("found %d secure fingerprints in DNS",
fingerprints->rri_nrdatas);
} else {
debug("found %d insecure fingerprints in DNS",
fingerprints->rri_nrdatas);
2004-01-07 11:10:17 +00:00
}
2012-08-29 15:55:54 +00:00
/* Initialize default host key parameters */
2004-01-07 11:10:17 +00:00
if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type,
&hostkey_digest, &hostkey_digest_len, hostkey)) {
error("Error calculating host key fingerprint.");
freerrset(fingerprints);
2004-02-26 10:38:49 +00:00
return -1;
2004-01-07 11:10:17 +00:00
}
2004-02-26 10:38:49 +00:00
if (fingerprints->rri_nrdatas)
*flags |= DNS_VERIFY_FOUND;
for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) {
2004-01-07 11:10:17 +00:00
/*
* Extract the key from the answer. Ignore any badly
* formatted fingerprints.
*/
if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type,
&dnskey_digest, &dnskey_digest_len,
fingerprints->rri_rdatas[counter].rdi_data,
fingerprints->rri_rdatas[counter].rdi_length)) {
verbose("Error parsing fingerprint from DNS.");
continue;
}
2012-08-29 15:55:54 +00:00
if (hostkey_digest_type != dnskey_digest_type) {
hostkey_digest_type = dnskey_digest_type;
2013-09-18 17:27:38 +00:00
free(hostkey_digest);
2012-08-29 15:55:54 +00:00
/* Initialize host key parameters */
if (!dns_read_key(&hostkey_algorithm,
&hostkey_digest_type, &hostkey_digest,
&hostkey_digest_len, hostkey)) {
error("Error calculating key fingerprint.");
freerrset(fingerprints);
return -1;
}
}
2004-01-07 11:10:17 +00:00
/* Check if the current key is the same as the given key */
if (hostkey_algorithm == dnskey_algorithm &&
hostkey_digest_type == dnskey_digest_type) {
if (hostkey_digest_len == dnskey_digest_len &&
2012-08-29 15:55:54 +00:00
timingsafe_bcmp(hostkey_digest, dnskey_digest,
hostkey_digest_len) == 0)
2004-02-26 10:38:49 +00:00
*flags |= DNS_VERIFY_MATCH;
2004-01-07 11:10:17 +00:00
}
2013-09-18 17:27:38 +00:00
free(dnskey_digest);
2004-01-07 11:10:17 +00:00
}
2015-07-02 13:15:34 +00:00
free(hostkey_digest); /* from sshkey_fingerprint_raw() */
2004-01-07 11:10:17 +00:00
freerrset(fingerprints);
2004-02-26 10:38:49 +00:00
if (*flags & DNS_VERIFY_FOUND)
if (*flags & DNS_VERIFY_MATCH)
debug("matching host key fingerprint found in DNS");
else
debug("mismatching host key fingerprint found in DNS");
else
debug("no host key fingerprint found in DNS");
2004-01-07 11:10:17 +00:00
2004-02-26 10:38:49 +00:00
return 0;
2004-01-07 11:10:17 +00:00
}
/*
* Export the fingerprint of a key as a DNS resource record
*/
int
2015-07-02 13:15:34 +00:00
export_dns_rr(const char *hostname, struct sshkey *key, FILE *f, int generic)
2004-01-07 11:10:17 +00:00
{
u_int8_t rdata_pubkey_algorithm = 0;
2012-08-29 15:55:54 +00:00
u_int8_t rdata_digest_type = SSHFP_HASH_RESERVED;
u_int8_t dtype;
2004-01-07 11:10:17 +00:00
u_char *rdata_digest;
2015-07-02 13:15:34 +00:00
size_t i, rdata_digest_len;
2004-01-07 11:10:17 +00:00
int success = 0;
2012-08-29 15:55:54 +00:00
for (dtype = SSHFP_HASH_SHA1; dtype < SSHFP_HASH_MAX; dtype++) {
rdata_digest_type = dtype;
if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type,
&rdata_digest, &rdata_digest_len, key)) {
if (generic) {
2015-07-02 13:15:34 +00:00
fprintf(f, "%s IN TYPE%d \\# %zu %02x %02x ",
2012-08-29 15:55:54 +00:00
hostname, DNS_RDATATYPE_SSHFP,
2 + rdata_digest_len,
rdata_pubkey_algorithm, rdata_digest_type);
} else {
fprintf(f, "%s IN SSHFP %d %d ", hostname,
rdata_pubkey_algorithm, rdata_digest_type);
}
for (i = 0; i < rdata_digest_len; i++)
fprintf(f, "%02x", rdata_digest[i]);
fprintf(f, "\n");
2015-07-02 13:15:34 +00:00
free(rdata_digest); /* from sshkey_fingerprint_raw() */
2012-08-29 15:55:54 +00:00
success = 1;
}
}
2004-01-07 11:10:17 +00:00
2012-08-29 15:55:54 +00:00
/* No SSHFP record was generated at all */
if (success == 0) {
error("%s: unsupported algorithm and/or digest_type", __func__);
2004-01-07 11:10:17 +00:00
}
return success;
}