Annotation of src/usr.bin/ssh/dns.c, Revision 1.6
1.6 ! jakob 1: /* $OpenBSD: dns.c,v 1.5 2003/05/15 02:27:15 jakob 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: #ifdef DNS
32: #include <openssl/bn.h>
33: #ifdef LWRES
34: #include <lwres/netdb.h>
35: #include <dns/result.h>
36: #else /* LWRES */
37: #include <netdb.h>
38: #endif /* LWRES */
39:
40: #include "xmalloc.h"
41: #include "key.h"
42: #include "dns.h"
43: #include "log.h"
44: #include "uuencode.h"
45:
46: extern char *__progname;
1.6 ! jakob 47: RCSID("$OpenBSD: dns.c,v 1.5 2003/05/15 02:27:15 jakob Exp $");
1.1 jakob 48:
49: #ifndef LWRES
50: static const char *errset_text[] = {
51: "success", /* 0 ERRSET_SUCCESS */
52: "out of memory", /* 1 ERRSET_NOMEMORY */
53: "general failure", /* 2 ERRSET_FAIL */
54: "invalid parameter", /* 3 ERRSET_INVAL */
55: "name does not exist", /* 4 ERRSET_NONAME */
56: "data does not exist", /* 5 ERRSET_NODATA */
57: };
58:
59: static const char *
60: dns_result_totext(unsigned int error)
61: {
62: switch (error) {
63: case ERRSET_SUCCESS:
64: return errset_text[ERRSET_SUCCESS];
65: case ERRSET_NOMEMORY:
66: return errset_text[ERRSET_NOMEMORY];
67: case ERRSET_FAIL:
68: return errset_text[ERRSET_FAIL];
69: case ERRSET_INVAL:
70: return errset_text[ERRSET_INVAL];
71: case ERRSET_NONAME:
72: return errset_text[ERRSET_NONAME];
73: case ERRSET_NODATA:
74: return errset_text[ERRSET_NODATA];
75: default:
76: return "unknown error";
77: }
78: }
79: #endif /* LWRES */
80:
81:
82: /*
83: * Read SSHFP parameters from key buffer.
84: */
85: static int
86: dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
87: u_char **digest, u_int *digest_len, Key *key)
88: {
89: int success = 0;
90:
91: switch (key->type) {
92: case KEY_RSA:
1.3 jakob 93: *algorithm = SSHFP_KEY_RSA;
1.1 jakob 94: break;
95: case KEY_DSA:
1.3 jakob 96: *algorithm = SSHFP_KEY_DSA;
1.1 jakob 97: break;
98: default:
1.3 jakob 99: *algorithm = SSHFP_KEY_RESERVED;
1.1 jakob 100: }
101:
102: if (*algorithm) {
1.3 jakob 103: *digest_type = SSHFP_HASH_SHA1;
1.1 jakob 104: *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len);
105: success = 1;
106: } else {
1.3 jakob 107: *digest_type = SSHFP_HASH_RESERVED;
1.1 jakob 108: *digest = NULL;
109: *digest_len = 0;
110: success = 0;
111: }
112:
113: return success;
114: }
115:
116: /*
117: * Read SSHFP parameters from rdata buffer.
118: */
119: static int
120: dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type,
121: u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len)
122: {
123: int success = 0;
124:
1.3 jakob 125: *algorithm = SSHFP_KEY_RESERVED;
126: *digest_type = SSHFP_HASH_RESERVED;
1.1 jakob 127:
128: if (rdata_len >= 2) {
129: *algorithm = rdata[0];
130: *digest_type = rdata[1];
131: *digest_len = rdata_len - 2;
132:
133: if (*digest_len > 0) {
134: *digest = (u_char *) xmalloc(*digest_len);
135: memcpy(*digest, rdata + 2, *digest_len);
136: } else {
137: *digest = NULL;
138: }
139:
140: success = 1;
141: }
142:
143: return success;
144: }
145:
146:
147: /*
148: * Verify the given hostname, address and host key using DNS.
149: * Returns 0 if key verifies or -1 if key does NOT verify
150: */
151: int
152: verify_host_key_dns(const char *hostname, struct sockaddr *address,
153: Key *hostkey)
154: {
155: int counter;
156: int result;
1.4 jakob 157: struct rrsetinfo *fingerprints = NULL;
1.1 jakob 158: int failures = 0;
159:
160: u_int8_t hostkey_algorithm;
161: u_int8_t hostkey_digest_type;
162: u_char *hostkey_digest;
163: u_int hostkey_digest_len;
164:
165: u_int8_t dnskey_algorithm;
166: u_int8_t dnskey_digest_type;
167: u_char *dnskey_digest;
168: u_int dnskey_digest_len;
169:
170:
171: debug3("verify_hostkey_dns");
172: if (hostkey == NULL)
173: fatal("No key to look up!");
174:
175: result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
1.4 jakob 176: DNS_RDATATYPE_SSHFP, 0, &fingerprints);
1.1 jakob 177: if (result) {
178: verbose("DNS lookup error: %s", dns_result_totext(result));
179: return DNS_VERIFY_ERROR;
180: }
181:
182: #ifdef DNSSEC
183: /* Only accept validated answers */
1.4 jakob 184: if (!fingerprints->rri_flags & RRSET_VALIDATED) {
1.1 jakob 185: error("Ignored unvalidated fingerprint from DNS.");
1.5 jakob 186: freerrset(fingerprints);
1.1 jakob 187: return DNS_VERIFY_ERROR;
188: }
189: #endif
190:
1.4 jakob 191: debug("found %d fingerprints in DNS", fingerprints->rri_nrdatas);
1.1 jakob 192:
193: /* Initialize host key parameters */
194: if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type,
195: &hostkey_digest, &hostkey_digest_len, hostkey)) {
196: error("Error calculating host key fingerprint.");
1.5 jakob 197: freerrset(fingerprints);
1.1 jakob 198: return DNS_VERIFY_ERROR;
199: }
200:
1.4 jakob 201: for (counter = 0 ; counter < fingerprints->rri_nrdatas ; counter++) {
1.1 jakob 202: /*
203: * Extract the key from the answer. Ignore any badly
1.4 jakob 204: * formatted fingerprints.
1.1 jakob 205: */
206: if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type,
207: &dnskey_digest, &dnskey_digest_len,
1.4 jakob 208: fingerprints->rri_rdatas[counter].rdi_data,
209: fingerprints->rri_rdatas[counter].rdi_length)) {
1.1 jakob 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: /* Matching algoritm and digest. */
1.4 jakob 223: freerrset(fingerprints);
1.1 jakob 224: debug("matching host key fingerprint found in DNS");
225: return DNS_VERIFY_OK;
226: } else {
227: /* Correct algorithm but bad digest */
228: debug("verify_hostkey_dns: failed");
229: failures++;
230: }
231: }
232: }
233:
1.4 jakob 234: freerrset(fingerprints);
1.1 jakob 235:
236: if (failures) {
237: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
238: error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @");
239: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
240: error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
241: error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
242: error("It is also possible that the %s host key has just been changed.",
243: key_type(hostkey));
244: error("Please contact your system administrator.");
245: return DNS_VERIFY_FAILED;
246: }
247:
248: debug("fingerprints found in DNS, but none of them matched");
249:
250: return DNS_VERIFY_ERROR;
251: }
252:
253:
254: /*
255: * Export the fingerprint of a key as a DNS resource record
256: */
257: int
258: export_dns_rr(const char *hostname, Key *key, FILE *f, int generic)
259: {
260: u_int8_t rdata_pubkey_algorithm = 0;
1.3 jakob 261: u_int8_t rdata_digest_type = SSHFP_HASH_SHA1;
1.1 jakob 262: u_char *rdata_digest;
263: u_int rdata_digest_len;
264:
265: int i;
266: int success = 0;
267:
268: if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type,
269: &rdata_digest, &rdata_digest_len, key)) {
270:
271: if (generic)
272: fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname,
273: DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len,
274: rdata_pubkey_algorithm, rdata_digest_type);
275: else
276: fprintf(f, "%s IN SSHFP %d %d ", hostname,
277: rdata_pubkey_algorithm, rdata_digest_type);
278:
279: for (i = 0; i < rdata_digest_len; i++)
280: fprintf(f, "%02x", rdata_digest[i]);
281: fprintf(f, "\n");
282: success = 1;
283: } else {
284: error("dns_export_rr: unsupported algorithm");
285: }
286:
287: return success;
288: }
289:
290: #endif /* DNS */