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