1 /* $OpenBSD: dns.c,v 1.9 2003/11/21 11:57:03 djm Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Wesley Griffin. All rights reserved. 5 * Copyright (c) 2003 Jakob Schlyter. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 29 #include "includes.h" 30 31 #include <openssl/bn.h> 32 #ifdef LWRES 33 #include <lwres/netdb.h> 34 #include <dns/result.h> 35 #else /* LWRES */ 36 #include <netdb.h> 37 #endif /* LWRES */ 38 39 #include "xmalloc.h" 40 #include "key.h" 41 #include "dns.h" 42 #include "log.h" 43 #include "uuencode.h" 44 45 extern char *__progname; 46 RCSID("$OpenBSD: dns.c,v 1.9 2003/11/21 11:57:03 djm Exp $"); 47 48 #ifndef LWRES 49 static const char *errset_text[] = { 50 "success", /* 0 ERRSET_SUCCESS */ 51 "out of memory", /* 1 ERRSET_NOMEMORY */ 52 "general failure", /* 2 ERRSET_FAIL */ 53 "invalid parameter", /* 3 ERRSET_INVAL */ 54 "name does not exist", /* 4 ERRSET_NONAME */ 55 "data does not exist", /* 5 ERRSET_NODATA */ 56 }; 57 58 static const char * 59 dns_result_totext(unsigned int error) 60 { 61 switch (error) { 62 case ERRSET_SUCCESS: 63 return errset_text[ERRSET_SUCCESS]; 64 case ERRSET_NOMEMORY: 65 return errset_text[ERRSET_NOMEMORY]; 66 case ERRSET_FAIL: 67 return errset_text[ERRSET_FAIL]; 68 case ERRSET_INVAL: 69 return errset_text[ERRSET_INVAL]; 70 case ERRSET_NONAME: 71 return errset_text[ERRSET_NONAME]; 72 case ERRSET_NODATA: 73 return errset_text[ERRSET_NODATA]; 74 default: 75 return "unknown error"; 76 } 77 } 78 #endif /* LWRES */ 79 80 81 /* 82 * Read SSHFP parameters from key buffer. 83 */ 84 static int 85 dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, 86 u_char **digest, u_int *digest_len, const Key *key) 87 { 88 int success = 0; 89 90 switch (key->type) { 91 case KEY_RSA: 92 *algorithm = SSHFP_KEY_RSA; 93 break; 94 case KEY_DSA: 95 *algorithm = SSHFP_KEY_DSA; 96 break; 97 default: 98 *algorithm = SSHFP_KEY_RESERVED; 99 } 100 101 if (*algorithm) { 102 *digest_type = SSHFP_HASH_SHA1; 103 *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); 104 success = 1; 105 } else { 106 *digest_type = SSHFP_HASH_RESERVED; 107 *digest = NULL; 108 *digest_len = 0; 109 success = 0; 110 } 111 112 return success; 113 } 114 115 /* 116 * Read SSHFP parameters from rdata buffer. 117 */ 118 static int 119 dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, 120 u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) 121 { 122 int success = 0; 123 124 *algorithm = SSHFP_KEY_RESERVED; 125 *digest_type = SSHFP_HASH_RESERVED; 126 127 if (rdata_len >= 2) { 128 *algorithm = rdata[0]; 129 *digest_type = rdata[1]; 130 *digest_len = rdata_len - 2; 131 132 if (*digest_len > 0) { 133 *digest = (u_char *) xmalloc(*digest_len); 134 memcpy(*digest, rdata + 2, *digest_len); 135 } else { 136 *digest = NULL; 137 } 138 139 success = 1; 140 } 141 142 return success; 143 } 144 145 146 /* 147 * Verify the given hostname, address and host key using DNS. 148 * Returns 0 if lookup succeeds, -1 otherwise 149 */ 150 int 151 verify_host_key_dns(const char *hostname, struct sockaddr *address, 152 const Key *hostkey, int *flags) 153 { 154 int counter; 155 int result; 156 struct rrsetinfo *fingerprints = NULL; 157 158 u_int8_t hostkey_algorithm; 159 u_int8_t hostkey_digest_type; 160 u_char *hostkey_digest; 161 u_int hostkey_digest_len; 162 163 u_int8_t dnskey_algorithm; 164 u_int8_t dnskey_digest_type; 165 u_char *dnskey_digest; 166 u_int dnskey_digest_len; 167 168 *flags = 0; 169 170 debug3("verify_hostkey_dns"); 171 if (hostkey == NULL) 172 fatal("No key to look up!"); 173 174 result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, 175 DNS_RDATATYPE_SSHFP, 0, &fingerprints); 176 if (result) { 177 verbose("DNS lookup error: %s", dns_result_totext(result)); 178 return -1; 179 } 180 181 if (fingerprints->rri_flags & RRSET_VALIDATED) { 182 *flags |= DNS_VERIFY_SECURE; 183 debug("found %d secure fingerprints in DNS", 184 fingerprints->rri_nrdatas); 185 } else { 186 debug("found %d insecure fingerprints in DNS", 187 fingerprints->rri_nrdatas); 188 } 189 190 /* Initialize host key parameters */ 191 if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, 192 &hostkey_digest, &hostkey_digest_len, hostkey)) { 193 error("Error calculating host key fingerprint."); 194 freerrset(fingerprints); 195 return -1; 196 } 197 198 if (fingerprints->rri_nrdatas) 199 *flags |= DNS_VERIFY_FOUND; 200 201 for (counter = 0 ; counter < fingerprints->rri_nrdatas ; counter++) { 202 /* 203 * Extract the key from the answer. Ignore any badly 204 * formatted fingerprints. 205 */ 206 if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, 207 &dnskey_digest, &dnskey_digest_len, 208 fingerprints->rri_rdatas[counter].rdi_data, 209 fingerprints->rri_rdatas[counter].rdi_length)) { 210 verbose("Error parsing fingerprint from DNS."); 211 continue; 212 } 213 214 /* Check if the current key is the same as the given key */ 215 if (hostkey_algorithm == dnskey_algorithm && 216 hostkey_digest_type == dnskey_digest_type) { 217 218 if (hostkey_digest_len == dnskey_digest_len && 219 memcmp(hostkey_digest, dnskey_digest, 220 hostkey_digest_len) == 0) { 221 222 *flags |= DNS_VERIFY_MATCH; 223 } 224 } 225 } 226 227 freerrset(fingerprints); 228 229 if (*flags & DNS_VERIFY_FOUND) 230 if (*flags & DNS_VERIFY_MATCH) 231 debug("matching host key fingerprint found in DNS"); 232 else 233 debug("mismatching host key fingerprint found in DNS"); 234 else 235 debug("no host key fingerprint found in DNS"); 236 237 return 0; 238 } 239 240 241 /* 242 * Export the fingerprint of a key as a DNS resource record 243 */ 244 int 245 export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) 246 { 247 u_int8_t rdata_pubkey_algorithm = 0; 248 u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; 249 u_char *rdata_digest; 250 u_int rdata_digest_len; 251 252 int i; 253 int success = 0; 254 255 if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, 256 &rdata_digest, &rdata_digest_len, key)) { 257 258 if (generic) 259 fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname, 260 DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, 261 rdata_pubkey_algorithm, rdata_digest_type); 262 else 263 fprintf(f, "%s IN SSHFP %d %d ", hostname, 264 rdata_pubkey_algorithm, rdata_digest_type); 265 266 for (i = 0; i < rdata_digest_len; i++) 267 fprintf(f, "%02x", rdata_digest[i]); 268 fprintf(f, "\n"); 269 success = 1; 270 } else { 271 error("dns_export_rr: unsupported algorithm"); 272 } 273 274 return success; 275 } 276