GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ssh/lib/../dns.c Lines: 0 141 0.0 %
Date: 2017-11-13 Branches: 0 79 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: dns.c,v 1.37 2017/09/14 04:32:21 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
#include <sys/types.h>
29
#include <sys/socket.h>
30
31
#include <netdb.h>
32
#include <stdio.h>
33
#include <string.h>
34
#include <stdarg.h>
35
#include <stdlib.h>
36
37
#include "xmalloc.h"
38
#include "sshkey.h"
39
#include "ssherr.h"
40
#include "dns.h"
41
#include "log.h"
42
#include "digest.h"
43
44
static const char *errset_text[] = {
45
	"success",		/* 0 ERRSET_SUCCESS */
46
	"out of memory",	/* 1 ERRSET_NOMEMORY */
47
	"general failure",	/* 2 ERRSET_FAIL */
48
	"invalid parameter",	/* 3 ERRSET_INVAL */
49
	"name does not exist",	/* 4 ERRSET_NONAME */
50
	"data does not exist",	/* 5 ERRSET_NODATA */
51
};
52
53
static const char *
54
dns_result_totext(unsigned int res)
55
{
56
	switch (res) {
57
	case ERRSET_SUCCESS:
58
		return errset_text[ERRSET_SUCCESS];
59
	case ERRSET_NOMEMORY:
60
		return errset_text[ERRSET_NOMEMORY];
61
	case ERRSET_FAIL:
62
		return errset_text[ERRSET_FAIL];
63
	case ERRSET_INVAL:
64
		return errset_text[ERRSET_INVAL];
65
	case ERRSET_NONAME:
66
		return errset_text[ERRSET_NONAME];
67
	case ERRSET_NODATA:
68
		return errset_text[ERRSET_NODATA];
69
	default:
70
		return "unknown error";
71
	}
72
}
73
74
/*
75
 * Read SSHFP parameters from key buffer.
76
 */
77
static int
78
dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
79
    u_char **digest, size_t *digest_len, struct sshkey *key)
80
{
81
	int r, success = 0;
82
	int fp_alg = -1;
83
84
	switch (key->type) {
85
	case KEY_RSA:
86
		*algorithm = SSHFP_KEY_RSA;
87
		if (!*digest_type)
88
			*digest_type = SSHFP_HASH_SHA1;
89
		break;
90
	case KEY_DSA:
91
		*algorithm = SSHFP_KEY_DSA;
92
		if (!*digest_type)
93
			*digest_type = SSHFP_HASH_SHA1;
94
		break;
95
	case KEY_ECDSA:
96
		*algorithm = SSHFP_KEY_ECDSA;
97
		if (!*digest_type)
98
			*digest_type = SSHFP_HASH_SHA256;
99
		break;
100
	case KEY_ED25519:
101
		*algorithm = SSHFP_KEY_ED25519;
102
		if (!*digest_type)
103
			*digest_type = SSHFP_HASH_SHA256;
104
		break;
105
	default:
106
		*algorithm = SSHFP_KEY_RESERVED; /* 0 */
107
		*digest_type = SSHFP_HASH_RESERVED; /* 0 */
108
	}
109
110
	switch (*digest_type) {
111
	case SSHFP_HASH_SHA1:
112
		fp_alg = SSH_DIGEST_SHA1;
113
		break;
114
	case SSHFP_HASH_SHA256:
115
		fp_alg = SSH_DIGEST_SHA256;
116
		break;
117
	default:
118
		*digest_type = SSHFP_HASH_RESERVED; /* 0 */
119
	}
120
121
	if (*algorithm && *digest_type) {
122
		if ((r = sshkey_fingerprint_raw(key, fp_alg, digest,
123
		    digest_len)) != 0)
124
			fatal("%s: sshkey_fingerprint_raw: %s", __func__,
125
			   ssh_err(r));
126
		success = 1;
127
	} else {
128
		*digest = NULL;
129
		*digest_len = 0;
130
		success = 0;
131
	}
132
133
	return success;
134
}
135
136
/*
137
 * Read SSHFP parameters from rdata buffer.
138
 */
139
static int
140
dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type,
141
    u_char **digest, size_t *digest_len, u_char *rdata, int rdata_len)
142
{
143
	int success = 0;
144
145
	*algorithm = SSHFP_KEY_RESERVED;
146
	*digest_type = SSHFP_HASH_RESERVED;
147
148
	if (rdata_len >= 2) {
149
		*algorithm = rdata[0];
150
		*digest_type = rdata[1];
151
		*digest_len = rdata_len - 2;
152
153
		if (*digest_len > 0) {
154
			*digest = xmalloc(*digest_len);
155
			memcpy(*digest, rdata + 2, *digest_len);
156
		} else {
157
			*digest = (u_char *)xstrdup("");
158
		}
159
160
		success = 1;
161
	}
162
163
	return success;
164
}
165
166
/*
167
 * Check if hostname is numerical.
168
 * Returns -1 if hostname is numeric, 0 otherwise
169
 */
170
static int
171
is_numeric_hostname(const char *hostname)
172
{
173
	struct addrinfo hints, *ai;
174
175
	/*
176
	 * We shouldn't ever get a null host but if we do then log an error
177
	 * and return -1 which stops DNS key fingerprint processing.
178
	 */
179
	if (hostname == NULL) {
180
		error("is_numeric_hostname called with NULL hostname");
181
		return -1;
182
	}
183
184
	memset(&hints, 0, sizeof(hints));
185
	hints.ai_socktype = SOCK_DGRAM;
186
	hints.ai_flags = AI_NUMERICHOST;
187
188
	if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) {
189
		freeaddrinfo(ai);
190
		return -1;
191
	}
192
193
	return 0;
194
}
195
196
/*
197
 * Verify the given hostname, address and host key using DNS.
198
 * Returns 0 if lookup succeeds, -1 otherwise
199
 */
200
int
201
verify_host_key_dns(const char *hostname, struct sockaddr *address,
202
    struct sshkey *hostkey, int *flags)
203
{
204
	u_int counter;
205
	int result;
206
	struct rrsetinfo *fingerprints = NULL;
207
208
	u_int8_t hostkey_algorithm;
209
	u_int8_t hostkey_digest_type = SSHFP_HASH_RESERVED;
210
	u_char *hostkey_digest;
211
	size_t hostkey_digest_len;
212
213
	u_int8_t dnskey_algorithm;
214
	u_int8_t dnskey_digest_type;
215
	u_char *dnskey_digest;
216
	size_t dnskey_digest_len;
217
218
	*flags = 0;
219
220
	debug3("verify_host_key_dns");
221
	if (hostkey == NULL)
222
		fatal("No key to look up!");
223
224
	if (is_numeric_hostname(hostname)) {
225
		debug("skipped DNS lookup for numerical hostname");
226
		return -1;
227
	}
228
229
	result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
230
	    DNS_RDATATYPE_SSHFP, 0, &fingerprints);
231
	if (result) {
232
		verbose("DNS lookup error: %s", dns_result_totext(result));
233
		return -1;
234
	}
235
236
	if (fingerprints->rri_flags & RRSET_VALIDATED) {
237
		*flags |= DNS_VERIFY_SECURE;
238
		debug("found %d secure fingerprints in DNS",
239
		    fingerprints->rri_nrdatas);
240
	} else {
241
		debug("found %d insecure fingerprints in DNS",
242
		    fingerprints->rri_nrdatas);
243
	}
244
245
	/* Initialize default host key parameters */
246
	if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type,
247
	    &hostkey_digest, &hostkey_digest_len, hostkey)) {
248
		error("Error calculating host key fingerprint.");
249
		freerrset(fingerprints);
250
		return -1;
251
	}
252
253
	if (fingerprints->rri_nrdatas)
254
		*flags |= DNS_VERIFY_FOUND;
255
256
	for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) {
257
		/*
258
		 * Extract the key from the answer. Ignore any badly
259
		 * formatted fingerprints.
260
		 */
261
		if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type,
262
		    &dnskey_digest, &dnskey_digest_len,
263
		    fingerprints->rri_rdatas[counter].rdi_data,
264
		    fingerprints->rri_rdatas[counter].rdi_length)) {
265
			verbose("Error parsing fingerprint from DNS.");
266
			continue;
267
		}
268
269
		if (hostkey_digest_type != dnskey_digest_type) {
270
			hostkey_digest_type = dnskey_digest_type;
271
			free(hostkey_digest);
272
273
			/* Initialize host key parameters */
274
			if (!dns_read_key(&hostkey_algorithm,
275
			    &hostkey_digest_type, &hostkey_digest,
276
			    &hostkey_digest_len, hostkey)) {
277
				error("Error calculating key fingerprint.");
278
				freerrset(fingerprints);
279
				return -1;
280
			}
281
		}
282
283
		/* Check if the current key is the same as the given key */
284
		if (hostkey_algorithm == dnskey_algorithm &&
285
		    hostkey_digest_type == dnskey_digest_type) {
286
			if (hostkey_digest_len == dnskey_digest_len &&
287
			    timingsafe_bcmp(hostkey_digest, dnskey_digest,
288
			    hostkey_digest_len) == 0)
289
				*flags |= DNS_VERIFY_MATCH;
290
		}
291
		free(dnskey_digest);
292
	}
293
294
	free(hostkey_digest); /* from sshkey_fingerprint_raw() */
295
	freerrset(fingerprints);
296
297
	if (*flags & DNS_VERIFY_FOUND)
298
		if (*flags & DNS_VERIFY_MATCH)
299
			debug("matching host key fingerprint found in DNS");
300
		else
301
			debug("mismatching host key fingerprint found in DNS");
302
	else
303
		debug("no host key fingerprint found in DNS");
304
305
	return 0;
306
}
307
308
/*
309
 * Export the fingerprint of a key as a DNS resource record
310
 */
311
int
312
export_dns_rr(const char *hostname, struct sshkey *key, FILE *f, int generic)
313
{
314
	u_int8_t rdata_pubkey_algorithm = 0;
315
	u_int8_t rdata_digest_type = SSHFP_HASH_RESERVED;
316
	u_int8_t dtype;
317
	u_char *rdata_digest;
318
	size_t i, rdata_digest_len;
319
	int success = 0;
320
321
	for (dtype = SSHFP_HASH_SHA1; dtype < SSHFP_HASH_MAX; dtype++) {
322
		rdata_digest_type = dtype;
323
		if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type,
324
		    &rdata_digest, &rdata_digest_len, key)) {
325
			if (generic) {
326
				fprintf(f, "%s IN TYPE%d \\# %zu %02x %02x ",
327
				    hostname, DNS_RDATATYPE_SSHFP,
328
				    2 + rdata_digest_len,
329
				    rdata_pubkey_algorithm, rdata_digest_type);
330
			} else {
331
				fprintf(f, "%s IN SSHFP %d %d ", hostname,
332
				    rdata_pubkey_algorithm, rdata_digest_type);
333
			}
334
			for (i = 0; i < rdata_digest_len; i++)
335
				fprintf(f, "%02x", rdata_digest[i]);
336
			fprintf(f, "\n");
337
			free(rdata_digest); /* from sshkey_fingerprint_raw() */
338
			success = 1;
339
		}
340
	}
341
342
	/* No SSHFP record was generated at all */
343
	if (success == 0) {
344
		error("%s: unsupported algorithm and/or digest_type", __func__);
345
	}
346
347
	return success;
348
}