1/*
2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1999-2003 by Internet Software Consortium
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 *   Internet Systems Consortium, Inc.
18 *   950 Charter Street
19 *   Redwood City, CA 94063
20 *   <info@isc.org>
21 *   http://www.isc.org/
22 */
23
24#ifndef lint
25static const char rcsid[] = "$Id: ns_sign.c,v 1.5 2007/05/27 16:27:57 tls Exp $";
26#endif
27
28#if defined (TRACING)
29#define time(x)		trace_mr_time (x)
30time_t trace_mr_time (time_t *);
31#endif
32
33/* Import. */
34
35#include <sys/types.h>
36#include <sys/param.h>
37
38#include <netinet/in.h>
39#include <arpa/inet.h>
40#include <sys/socket.h>
41
42#include <errno.h>
43#include <netdb.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <time.h>
49
50#include "minires/minires.h"
51#include "omapip/trace_mr.h"
52#include "arpa/nameser.h"
53
54#include <isc-dhcp/dst.h>
55
56#define BOUNDS_CHECK(ptr, count) \
57	do { \
58		if ((ptr) + (count) > eob) { \
59			return ISC_R_NOSPACE; \
60		} \
61	} while (0)
62
63/* ns_sign
64 * Parameters:
65 *	msg		message to be sent
66 *	msglen		input - length of message
67 *			output - length of signed message
68 *	msgsize		length of buffer containing message
69 *	error		value to put in the error field
70 *	key		tsig key used for signing
71 *	querysig	(response), the signature in the query
72 *	querysiglen	(response), the length of the signature in the query
73 *	sig		a buffer to hold the generated signature
74 *	siglen		input - length of signature buffer
75 *			output - length of signature
76 *
77 * Errors:
78 *	- bad input data (-1)
79 *	- bad key / sign failed (-BADKEY)
80 *	- not enough space (NS_TSIG_ERROR_NO_SPACE)
81 */
82isc_result_t
83ns_sign(u_char *msg, unsigned *msglen, unsigned msgsize, int error, void *k,
84	const u_char *querysig, unsigned querysiglen, u_char *sig,
85	unsigned *siglen, time_t in_timesigned)
86{
87	HEADER *hp = (HEADER *)msg;
88	DST_KEY *key = (DST_KEY *)k;
89	u_char *cp = msg + *msglen, *eob = msg + msgsize;
90	u_char *lenp;
91	u_char *name, *alg;
92	unsigned n;
93	time_t timesigned;
94
95	dst_init();
96	if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL)
97		return ISC_R_INVALIDARG;
98
99	/* Name. */
100	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey)
101		n = dn_comp(key->dk_key_name,
102			    cp, (unsigned)(eob - cp), NULL, NULL);
103	else
104		n = dn_comp("", cp, (unsigned)(eob - cp), NULL, NULL);
105	if (n < 0)
106		return ISC_R_NOSPACE;
107	name = cp;
108	cp += n;
109
110	/* Type, class, ttl, length (not filled in yet). */
111	BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ);
112	PUTSHORT(ns_t_tsig, cp);
113	PUTSHORT(ns_c_any, cp);
114	PUTLONG(0, cp);		/* TTL */
115	lenp = cp;
116	cp += 2;
117
118	/* Alg. */
119	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
120		if (key->dk_alg != KEY_HMAC_MD5)
121			return ISC_R_BADKEY;
122		n = dn_comp(NS_TSIG_ALG_HMAC_MD5,
123			    cp, (unsigned)(eob - cp), NULL, NULL);
124	}
125	else
126		n = dn_comp("", cp, (unsigned)(eob - cp), NULL, NULL);
127	if (n < 0)
128		return ISC_R_NOSPACE;
129	alg = cp;
130	cp += n;
131
132	/* Time. */
133	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
134	PUTSHORT(0, cp);
135	timesigned = time(NULL);
136	if (error != ns_r_badtime)
137		PUTLONG(timesigned, cp);
138	else
139		PUTLONG(in_timesigned, cp);
140	PUTSHORT(NS_TSIG_FUDGE, cp);
141
142	/* Compute the signature. */
143	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
144		void *ctx;
145		u_char buf[MAXDNAME], *cp2;
146		unsigned n;
147
148		dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
149
150		/* Digest the query signature, if this is a response. */
151		if (querysiglen > 0 && querysig != NULL) {
152			u_int16_t len_n = htons(querysiglen);
153			dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
154				      (u_char *)&len_n, INT16SZ, NULL, 0);
155			dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
156				      querysig, querysiglen, NULL, 0);
157		}
158
159		/* Digest the message. */
160		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen,
161			      NULL, 0);
162
163		/* Digest the key name. */
164		n = ns_name_ntol(name, buf, sizeof(buf));
165		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
166
167		/* Digest the class and TTL. */
168		cp2 = buf;
169		PUTSHORT(ns_c_any, cp2);
170		PUTLONG(0, cp2);
171		dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
172			      buf, (unsigned)(cp2-buf), NULL, 0);
173
174		/* Digest the algorithm. */
175		n = ns_name_ntol(alg, buf, sizeof(buf));
176		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
177
178		/* Digest the time signed, fudge, error, and other data */
179		cp2 = buf;
180		PUTSHORT(0, cp2);	/* Top 16 bits of time */
181		if (error != ns_r_badtime)
182			PUTLONG(timesigned, cp2);
183		else
184			PUTLONG(in_timesigned, cp2);
185		PUTSHORT(NS_TSIG_FUDGE, cp2);
186		PUTSHORT(error, cp2);	/* Error */
187		if (error != ns_r_badtime)
188			PUTSHORT(0, cp2);	/* Other data length */
189		else {
190			PUTSHORT(INT16SZ+INT32SZ, cp2);	/* Other data length */
191			PUTSHORT(0, cp2);	/* Top 16 bits of time */
192			PUTLONG(timesigned, cp2);
193		}
194		dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
195			      buf, (unsigned)(cp2-buf), NULL, 0);
196
197		n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
198				  sig, *siglen);
199		if (n < 0)
200			return ISC_R_BADKEY;
201		*siglen = n;
202	} else
203		*siglen = 0;
204
205	/* Add the signature. */
206	BOUNDS_CHECK(cp, INT16SZ + (*siglen));
207	PUTSHORT(*siglen, cp);
208	memcpy(cp, sig, *siglen);
209	cp += (*siglen);
210
211	/* The original message ID & error. */
212	BOUNDS_CHECK(cp, INT16SZ + INT16SZ);
213	PUTSHORT(ntohs(hp->id), cp);	/* already in network order */
214	PUTSHORT(error, cp);
215
216	/* Other data. */
217	BOUNDS_CHECK(cp, INT16SZ);
218	if (error != ns_r_badtime)
219		PUTSHORT(0, cp);	/* Other data length */
220	else {
221		PUTSHORT(INT16SZ+INT32SZ, cp);	/* Other data length */
222		BOUNDS_CHECK(cp, INT32SZ+INT16SZ);
223		PUTSHORT(0, cp);	/* Top 16 bits of time */
224		PUTLONG(timesigned, cp);
225	}
226
227	/* Go back and fill in the length. */
228	PUTSHORT(cp - lenp - INT16SZ, lenp);
229
230	hp->arcount = htons(ntohs(hp->arcount) + 1);
231	*msglen = (cp - msg);
232	return ISC_R_SUCCESS;
233}
234
235#if 0
236isc_result_t
237ns_sign_tcp_init(void *k, const u_char *querysig, unsigned querysiglen,
238		 ns_tcp_tsig_state *state)
239{
240	dst_init();
241	if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
242		return ISC_R_INVALIDARG;
243	state->counter = -1;
244	state->key = k;
245	if (state->key->dk_alg != KEY_HMAC_MD5)
246		return ISC_R_BADKEY;
247	if (querysiglen > sizeof(state->sig))
248		return ISC_R_NOSPACE;
249	memcpy(state->sig, querysig, querysiglen);
250	state->siglen = querysiglen;
251	return ISC_R_SUCCESS;
252}
253
254isc_result_t
255ns_sign_tcp(u_char *msg, unsigned *msglen, unsigned msgsize, int error,
256	    ns_tcp_tsig_state *state, int done)
257{
258	u_char *cp, *eob, *lenp;
259	u_char buf[MAXDNAME], *cp2;
260	HEADER *hp = (HEADER *)msg;
261	time_t timesigned;
262	int n;
263
264	if (msg == NULL || msglen == NULL || state == NULL)
265		return ISC_R_INVALIDARG;
266
267	state->counter++;
268	if (state->counter == 0)
269		return ns_sign(msg, msglen, msgsize, error, state->key,
270			       state->sig, state->siglen,
271			       state->sig, &state->siglen, 0);
272
273	if (state->siglen > 0) {
274		u_int16_t siglen_n = htons(state->siglen);
275		dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx,
276			      NULL, 0, NULL, 0);
277		dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
278			      (u_char *)&siglen_n, INT16SZ, NULL, 0);
279		dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
280			      state->sig, state->siglen, NULL, 0);
281		state->siglen = 0;
282	}
283
284	dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen,
285		      NULL, 0);
286
287	if (done == 0 && (state->counter % 100 != 0))
288		return ISC_R_SUCCESS;
289
290	cp = msg + *msglen;
291	eob = msg + msgsize;
292
293	/* Name. */
294	n = dn_comp(state->key->dk_key_name,
295		    cp, (unsigned)(eob - cp), NULL, NULL);
296	if (n < 0)
297		return ISC_R_NOSPACE;
298	cp += n;
299
300	/* Type, class, ttl, length (not filled in yet). */
301	BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ);
302	PUTSHORT(ns_t_tsig, cp);
303	PUTSHORT(ns_c_any, cp);
304	PUTLONG(0, cp);		/* TTL */
305	lenp = cp;
306	cp += 2;
307
308	/* Alg. */
309	n = dn_comp(NS_TSIG_ALG_HMAC_MD5,
310		    cp, (unsigned)(eob - cp), NULL, NULL);
311	if (n < 0)
312		return ISC_R_NOSPACE;
313	cp += n;
314
315	/* Time. */
316	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
317	PUTSHORT(0, cp);
318	timesigned = time(NULL);
319	PUTLONG(timesigned, cp);
320	PUTSHORT(NS_TSIG_FUDGE, cp);
321
322	/*
323	 * Compute the signature.
324	 */
325
326	/* Digest the time signed and fudge. */
327	cp2 = buf;
328	PUTSHORT(0, cp2);	/* Top 16 bits of time */
329	PUTLONG(timesigned, cp2);
330	PUTSHORT(NS_TSIG_FUDGE, cp2);
331
332	dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
333		      buf, (unsigned)(cp2 - buf), NULL, 0);
334
335	n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
336			  state->sig, sizeof(state->sig));
337	if (n < 0)
338		return ISC_R_BADKEY;
339	state->siglen = n;
340
341	/* Add the signature. */
342	BOUNDS_CHECK(cp, INT16SZ + state->siglen);
343	PUTSHORT(state->siglen, cp);
344	memcpy(cp, state->sig, state->siglen);
345	cp += state->siglen;
346
347	/* The original message ID & error. */
348	BOUNDS_CHECK(cp, INT16SZ + INT16SZ);
349	PUTSHORT(ntohs(hp->id), cp);	/* already in network order */
350	PUTSHORT(error, cp);
351
352	/* Other data. */
353	BOUNDS_CHECK(cp, INT16SZ);
354	PUTSHORT(0, cp);
355
356	/* Go back and fill in the length. */
357	PUTSHORT(cp - lenp - INT16SZ, lenp);
358
359	hp->arcount = htons(ntohs(hp->arcount) + 1);
360	*msglen = (cp - msg);
361	return ISC_R_SUCCESS;
362}
363#endif
364