1/*
2 * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
9 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
11 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
14 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * See the COPYRIGHT file distributed with this work for additional
17 * information regarding copyright ownership.
18 *
19 * Portions Copyright (C) Network Associates, Inc.
20 *
21 * Permission to use, copy, modify, and/or distribute this software for any
22 * purpose with or without fee is hereby granted, provided that the above
23 * copyright notice and this permission notice appear in all copies.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
26 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
27 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
28 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
29 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
30 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
31 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 */
33
34/*
35 * Principal Author: Brian Wellington
36 * $Id: dst_api.c,v 1.16 2020/09/14 08:40:43 florian Exp $
37 */
38
39/*! \file */
40#include <stdlib.h>
41#include <string.h>
42
43#include <isc/buffer.h>
44#include <isc/refcount.h>
45#include <isc/util.h>
46
47#include <dns/keyvalues.h>
48
49#include <dst/result.h>
50
51#include "dst_internal.h"
52
53static dst_func_t *dst_t_func[DST_MAX_ALGS];
54static int dst_initialized = 0;
55
56/*
57 * Static functions.
58 */
59static dst_key_t *	get_key_struct(unsigned int alg,
60				       unsigned int flags,
61				       unsigned int protocol,
62				       unsigned int bits);
63static isc_result_t	computeid(dst_key_t *key);
64static isc_result_t	frombuffer(unsigned int alg,
65				   unsigned int flags,
66				   unsigned int protocol,
67				   isc_buffer_t *source,
68				   dst_key_t **keyp);
69
70static isc_result_t	algorithm_status(unsigned int alg);
71
72#define RETERR(x)				\
73	do {					\
74		result = (x);			\
75		if (result != ISC_R_SUCCESS)	\
76			goto out;		\
77	} while (0)
78
79#define CHECKALG(alg)				\
80	do {					\
81		isc_result_t _r;		\
82		_r = algorithm_status(alg);	\
83		if (_r != ISC_R_SUCCESS)	\
84			return (_r);		\
85	} while (0);				\
86
87isc_result_t
88dst_lib_init(void) {
89	isc_result_t result;
90
91	REQUIRE(!dst_initialized);
92
93	dst_result_register();
94
95	memset(dst_t_func, 0, sizeof(dst_t_func));
96	RETERR(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1]));
97	RETERR(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224]));
98	RETERR(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256]));
99	RETERR(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384]));
100	RETERR(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512]));
101	RETERR(dst__openssl_init());
102	dst_initialized = 1;
103	return (ISC_R_SUCCESS);
104
105 out:
106	/* avoid immediate crash! */
107	dst_initialized = 1;
108	dst_lib_destroy();
109	return (result);
110}
111
112void
113dst_lib_destroy(void) {
114	RUNTIME_CHECK(dst_initialized);
115	dst_initialized = 0;
116
117	dst__openssl_destroy();
118}
119
120int
121dst_algorithm_supported(unsigned int alg) {
122	REQUIRE(dst_initialized);
123
124	if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
125		return (0);
126	return (1);
127}
128
129isc_result_t
130dst_context_create3(dst_key_t *key,
131		    isc_logcategory_t *category, int useforsigning,
132		    dst_context_t **dctxp)
133{
134	dst_context_t *dctx;
135	isc_result_t result;
136
137	REQUIRE(dst_initialized);
138	REQUIRE(dctxp != NULL && *dctxp == NULL);
139
140	dctx = malloc(sizeof(dst_context_t));
141	if (dctx == NULL)
142		return (ISC_R_NOMEMORY);
143	memset(dctx, 0, sizeof(*dctx));
144	dst_key_attach(key, &dctx->key);
145	dctx->category = category;
146	if (useforsigning)
147		dctx->use = DO_SIGN;
148	else
149		dctx->use = DO_VERIFY;
150	result = key->func->createctx(key, dctx);
151	if (result != ISC_R_SUCCESS) {
152		if (dctx->key != NULL)
153			dst_key_free(&dctx->key);
154		free(dctx);
155		return (result);
156	}
157	*dctxp = dctx;
158	return (ISC_R_SUCCESS);
159}
160
161void
162dst_context_destroy(dst_context_t **dctxp) {
163	dst_context_t *dctx;
164
165	REQUIRE(dctxp != NULL);
166
167	dctx = *dctxp;
168	dctx->key->func->destroyctx(dctx);
169	if (dctx->key != NULL)
170		dst_key_free(&dctx->key);
171	free(dctx);
172	*dctxp = NULL;
173}
174
175isc_result_t
176dst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
177	REQUIRE(data != NULL);
178	return (dctx->key->func->adddata(dctx, data));
179}
180
181isc_result_t
182dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
183	dst_key_t *key;
184
185	REQUIRE(sig != NULL);
186
187	key = dctx->key;
188	CHECKALG(key->key_alg);
189
190	return (key->func->sign(dctx, sig));
191}
192
193isc_result_t
194dst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
195	REQUIRE(sig != NULL);
196
197	CHECKALG(dctx->key->key_alg);
198
199	return (dctx->key->func->verify(dctx, sig));
200}
201
202isc_result_t
203dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
204	REQUIRE(dst_initialized);
205	REQUIRE(target != NULL);
206
207	CHECKALG(key->key_alg);
208
209	if (isc_buffer_availablelength(target) < 4)
210		return (ISC_R_NOSPACE);
211	isc_buffer_putuint16(target, (uint16_t)(key->key_flags & 0xffff));
212	isc_buffer_putuint8(target, (uint8_t)key->key_proto);
213	isc_buffer_putuint8(target, (uint8_t)key->key_alg);
214
215	if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
216		if (isc_buffer_availablelength(target) < 2)
217			return (ISC_R_NOSPACE);
218		isc_buffer_putuint16(target,
219				     (uint16_t)((key->key_flags >> 16)
220						    & 0xffff));
221	}
222
223	return (key->func->todns(key, target));
224}
225
226isc_result_t
227dst_key_frombuffer(unsigned int alg, unsigned int flags, unsigned int protocol,
228		   isc_buffer_t *source, dst_key_t **keyp)
229{
230	dst_key_t *key = NULL;
231	isc_result_t result;
232
233	REQUIRE(dst_initialized);
234
235	result = frombuffer(alg, flags, protocol, source, &key);
236	if (result != ISC_R_SUCCESS)
237		return (result);
238
239	result = computeid(key);
240	if (result != ISC_R_SUCCESS) {
241		dst_key_free(&key);
242		return (result);
243	}
244
245	*keyp = key;
246	return (ISC_R_SUCCESS);
247}
248
249void
250dst_key_attach(dst_key_t *source, dst_key_t **target) {
251
252	REQUIRE(dst_initialized);
253	REQUIRE(target != NULL && *target == NULL);
254
255	isc_refcount_increment(&source->refs, NULL);
256	*target = source;
257}
258
259void
260dst_key_free(dst_key_t **keyp) {
261	dst_key_t *key;
262	unsigned int refs;
263
264	REQUIRE(dst_initialized);
265	REQUIRE(keyp != NULL);
266
267	key = *keyp;
268
269	isc_refcount_decrement(&key->refs, &refs);
270	if (refs != 0)
271		return;
272
273	isc_refcount_destroy(&key->refs);
274	key->func->destroy(key);
275	freezero(key, sizeof(*key));
276	*keyp = NULL;
277}
278
279isc_result_t
280dst_key_sigsize(const dst_key_t *key, unsigned int *n) {
281	REQUIRE(dst_initialized);
282	REQUIRE(n != NULL);
283
284	/* XXXVIX this switch statement is too sparse to gen a jump table. */
285	switch (key->key_alg) {
286	case DST_ALG_HMACSHA1:
287		*n = ISC_SHA1_DIGESTLENGTH;
288		break;
289	case DST_ALG_HMACSHA224:
290		*n = ISC_SHA224_DIGESTLENGTH;
291		break;
292	case DST_ALG_HMACSHA256:
293		*n = ISC_SHA256_DIGESTLENGTH;
294		break;
295	case DST_ALG_HMACSHA384:
296		*n = ISC_SHA384_DIGESTLENGTH;
297		break;
298	case DST_ALG_HMACSHA512:
299		*n = ISC_SHA512_DIGESTLENGTH;
300		break;
301	default:
302		return (DST_R_UNSUPPORTEDALG);
303	}
304	return (ISC_R_SUCCESS);
305}
306
307/***
308 *** Static methods
309 ***/
310
311/*%
312 * Allocates a key structure and fills in some of the fields.
313 */
314static dst_key_t *
315get_key_struct(unsigned int alg,
316	       unsigned int flags, unsigned int protocol,
317	       unsigned int bits)
318{
319	dst_key_t *key;
320	isc_result_t result;
321
322	key = (dst_key_t *) malloc(sizeof(dst_key_t));
323	if (key == NULL)
324		return (NULL);
325
326	memset(key, 0, sizeof(dst_key_t));
327
328	result = isc_refcount_init(&key->refs, 1);
329	if (result != ISC_R_SUCCESS) {
330		free(key);
331		return (NULL);
332	}
333	key->key_alg = alg;
334	key->key_flags = flags;
335	key->key_proto = protocol;
336	key->key_size = bits;
337	key->func = dst_t_func[alg];
338	return (key);
339}
340
341static isc_result_t
342computeid(dst_key_t *key) {
343	isc_buffer_t dnsbuf;
344	unsigned char dns_array[DST_KEY_MAXSIZE];
345	isc_region_t r;
346	isc_result_t ret;
347
348	isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
349	ret = dst_key_todns(key, &dnsbuf);
350	if (ret != ISC_R_SUCCESS)
351		return (ret);
352
353	isc_buffer_usedregion(&dnsbuf, &r);
354	return (ISC_R_SUCCESS);
355}
356
357static isc_result_t
358frombuffer(unsigned int alg, unsigned int flags,
359	   unsigned int protocol, isc_buffer_t *source, dst_key_t **keyp)
360{
361	dst_key_t *key;
362	isc_result_t ret;
363
364	REQUIRE(source != NULL);
365	REQUIRE(keyp != NULL && *keyp == NULL);
366
367	key = get_key_struct(alg, flags, protocol, 0);
368	if (key == NULL)
369		return (ISC_R_NOMEMORY);
370
371	if (isc_buffer_remaininglength(source) > 0) {
372		ret = algorithm_status(alg);
373		if (ret != ISC_R_SUCCESS) {
374			dst_key_free(&key);
375			return (ret);
376		}
377
378		ret = key->func->fromdns(key, source);
379		if (ret != ISC_R_SUCCESS) {
380			dst_key_free(&key);
381			return (ret);
382		}
383	}
384
385	*keyp = key;
386	return (ISC_R_SUCCESS);
387}
388
389static isc_result_t
390algorithm_status(unsigned int alg) {
391	REQUIRE(dst_initialized);
392
393	if (dst_algorithm_supported(alg))
394		return (ISC_R_SUCCESS);
395	return (DST_R_UNSUPPORTEDALG);
396}
397