Annotation of src/usr.bin/ssh/dns.c, Revision 1.13
1.13 ! stevesk 1: /* $OpenBSD: dns.c,v 1.12 2005/06/17 02:44:32 djm Exp $ */
1.1 jakob 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:
1.13 ! stevesk 44: RCSID("$OpenBSD: dns.c,v 1.12 2005/06/17 02:44:32 djm Exp $");
1.1 jakob 45:
46: #ifndef LWRES
47: static const char *errset_text[] = {
48: "success", /* 0 ERRSET_SUCCESS */
49: "out of memory", /* 1 ERRSET_NOMEMORY */
50: "general failure", /* 2 ERRSET_FAIL */
51: "invalid parameter", /* 3 ERRSET_INVAL */
52: "name does not exist", /* 4 ERRSET_NONAME */
53: "data does not exist", /* 5 ERRSET_NODATA */
54: };
55:
56: static const char *
1.10 avsm 57: dns_result_totext(unsigned int res)
1.1 jakob 58: {
1.10 avsm 59: switch (res) {
1.1 jakob 60: case ERRSET_SUCCESS:
61: return errset_text[ERRSET_SUCCESS];
62: case ERRSET_NOMEMORY:
63: return errset_text[ERRSET_NOMEMORY];
64: case ERRSET_FAIL:
65: return errset_text[ERRSET_FAIL];
66: case ERRSET_INVAL:
67: return errset_text[ERRSET_INVAL];
68: case ERRSET_NONAME:
69: return errset_text[ERRSET_NONAME];
70: case ERRSET_NODATA:
71: return errset_text[ERRSET_NODATA];
72: default:
73: return "unknown error";
74: }
75: }
76: #endif /* LWRES */
77:
78:
79: /*
80: * Read SSHFP parameters from key buffer.
81: */
82: static int
83: dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
1.8 jakob 84: u_char **digest, u_int *digest_len, const Key *key)
1.1 jakob 85: {
86: int success = 0;
87:
88: switch (key->type) {
89: case KEY_RSA:
1.3 jakob 90: *algorithm = SSHFP_KEY_RSA;
1.1 jakob 91: break;
92: case KEY_DSA:
1.3 jakob 93: *algorithm = SSHFP_KEY_DSA;
1.1 jakob 94: break;
95: default:
1.3 jakob 96: *algorithm = SSHFP_KEY_RESERVED;
1.1 jakob 97: }
98:
99: if (*algorithm) {
1.3 jakob 100: *digest_type = SSHFP_HASH_SHA1;
1.1 jakob 101: *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len);
102: success = 1;
103: } else {
1.3 jakob 104: *digest_type = SSHFP_HASH_RESERVED;
1.1 jakob 105: *digest = NULL;
106: *digest_len = 0;
107: success = 0;
108: }
109:
110: return success;
111: }
112:
113: /*
114: * Read SSHFP parameters from rdata buffer.
115: */
116: static int
117: dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type,
118: u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len)
119: {
120: int success = 0;
121:
1.3 jakob 122: *algorithm = SSHFP_KEY_RESERVED;
123: *digest_type = SSHFP_HASH_RESERVED;
1.1 jakob 124:
125: if (rdata_len >= 2) {
126: *algorithm = rdata[0];
127: *digest_type = rdata[1];
128: *digest_len = rdata_len - 2;
129:
130: if (*digest_len > 0) {
131: *digest = (u_char *) xmalloc(*digest_len);
132: memcpy(*digest, rdata + 2, *digest_len);
133: } else {
134: *digest = NULL;
135: }
136:
137: success = 1;
138: }
139:
140: return success;
141: }
142:
1.11 jakob 143: /*
144: * Check if hostname is numerical.
145: * Returns -1 if hostname is numeric, 0 otherwise
146: */
147: static int
148: is_numeric_hostname(const char *hostname)
149: {
150: struct addrinfo hints, *ai;
151:
152: memset(&hints, 0, sizeof(hints));
153: hints.ai_socktype = SOCK_DGRAM;
154: hints.ai_flags = AI_NUMERICHOST;
155:
156: if (getaddrinfo(hostname, "0", &hints, &ai) == 0) {
157: freeaddrinfo(ai);
158: return -1;
159: }
160:
161: return 0;
162: }
1.1 jakob 163:
164: /*
165: * Verify the given hostname, address and host key using DNS.
1.9 djm 166: * Returns 0 if lookup succeeds, -1 otherwise
1.1 jakob 167: */
168: int
169: verify_host_key_dns(const char *hostname, struct sockaddr *address,
1.8 jakob 170: const Key *hostkey, int *flags)
1.1 jakob 171: {
1.12 djm 172: u_int counter;
1.1 jakob 173: int result;
1.4 jakob 174: struct rrsetinfo *fingerprints = NULL;
1.1 jakob 175:
176: u_int8_t hostkey_algorithm;
177: u_int8_t hostkey_digest_type;
178: u_char *hostkey_digest;
179: u_int hostkey_digest_len;
180:
181: u_int8_t dnskey_algorithm;
182: u_int8_t dnskey_digest_type;
183: u_char *dnskey_digest;
184: u_int dnskey_digest_len;
185:
1.8 jakob 186: *flags = 0;
1.1 jakob 187:
188: debug3("verify_hostkey_dns");
189: if (hostkey == NULL)
190: fatal("No key to look up!");
1.11 jakob 191:
192: if (is_numeric_hostname(hostname)) {
193: debug("skipped DNS lookup for numerical hostname");
194: return -1;
195: }
1.1 jakob 196:
197: result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
1.4 jakob 198: DNS_RDATATYPE_SSHFP, 0, &fingerprints);
1.1 jakob 199: if (result) {
200: verbose("DNS lookup error: %s", dns_result_totext(result));
1.8 jakob 201: return -1;
1.1 jakob 202: }
203:
1.8 jakob 204: if (fingerprints->rri_flags & RRSET_VALIDATED) {
205: *flags |= DNS_VERIFY_SECURE;
206: debug("found %d secure fingerprints in DNS",
207: fingerprints->rri_nrdatas);
208: } else {
209: debug("found %d insecure fingerprints in DNS",
210: fingerprints->rri_nrdatas);
1.1 jakob 211: }
212:
213: /* Initialize host key parameters */
214: if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type,
215: &hostkey_digest, &hostkey_digest_len, hostkey)) {
216: error("Error calculating host key fingerprint.");
1.5 jakob 217: freerrset(fingerprints);
1.8 jakob 218: return -1;
1.1 jakob 219: }
220:
1.8 jakob 221: if (fingerprints->rri_nrdatas)
222: *flags |= DNS_VERIFY_FOUND;
223:
1.13 ! stevesk 224: for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) {
1.1 jakob 225: /*
226: * Extract the key from the answer. Ignore any badly
1.4 jakob 227: * formatted fingerprints.
1.1 jakob 228: */
229: if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type,
230: &dnskey_digest, &dnskey_digest_len,
1.4 jakob 231: fingerprints->rri_rdatas[counter].rdi_data,
232: fingerprints->rri_rdatas[counter].rdi_length)) {
1.1 jakob 233: verbose("Error parsing fingerprint from DNS.");
234: continue;
235: }
236:
237: /* Check if the current key is the same as the given key */
238: if (hostkey_algorithm == dnskey_algorithm &&
239: hostkey_digest_type == dnskey_digest_type) {
240:
241: if (hostkey_digest_len == dnskey_digest_len &&
242: memcmp(hostkey_digest, dnskey_digest,
243: hostkey_digest_len) == 0) {
244:
1.8 jakob 245: *flags |= DNS_VERIFY_MATCH;
1.1 jakob 246: }
247: }
248: }
249:
1.4 jakob 250: freerrset(fingerprints);
1.1 jakob 251:
1.8 jakob 252: if (*flags & DNS_VERIFY_FOUND)
253: if (*flags & DNS_VERIFY_MATCH)
254: debug("matching host key fingerprint found in DNS");
255: else
256: debug("mismatching host key fingerprint found in DNS");
257: else
258: debug("no host key fingerprint found in DNS");
1.1 jakob 259:
1.8 jakob 260: return 0;
1.1 jakob 261: }
262:
263:
264: /*
265: * Export the fingerprint of a key as a DNS resource record
266: */
267: int
1.8 jakob 268: export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic)
1.1 jakob 269: {
270: u_int8_t rdata_pubkey_algorithm = 0;
1.3 jakob 271: u_int8_t rdata_digest_type = SSHFP_HASH_SHA1;
1.1 jakob 272: u_char *rdata_digest;
273: u_int rdata_digest_len;
274:
1.12 djm 275: u_int i;
1.1 jakob 276: int success = 0;
277:
278: if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type,
279: &rdata_digest, &rdata_digest_len, key)) {
280:
281: if (generic)
282: fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname,
283: DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len,
284: rdata_pubkey_algorithm, rdata_digest_type);
285: else
286: fprintf(f, "%s IN SSHFP %d %d ", hostname,
287: rdata_pubkey_algorithm, rdata_digest_type);
288:
289: for (i = 0; i < rdata_digest_len; i++)
290: fprintf(f, "%02x", rdata_digest[i]);
291: fprintf(f, "\n");
292: success = 1;
293: } else {
294: error("dns_export_rr: unsupported algorithm");
295: }
296:
297: return success;
298: }