1/*
2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1999 by Internet Software Consortium, Inc.
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
18#ifndef lint
19static const char rcsid[] = "$Id: ns_sign.c,v 1.6 2006/03/09 23:57:56 marka Exp $";
20#endif
21
22/* Import. */
23
24#include "port_before.h"
25#include "fd_setsize.h"
26
27#include <sys/types.h>
28#include <sys/param.h>
29
30#include <netinet/in.h>
31#include <arpa/nameser.h>
32#include <arpa/inet.h>
33
34#include <errno.h>
35#include <netdb.h>
36#include <resolv.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <time.h>
41#include <unistd.h>
42
43#include <isc/dst.h>
44#include <isc/assertions.h>
45
46#include "port_after.h"
47
48#define BOUNDS_CHECK(ptr, count) \
49	do { \
50		if ((ptr) + (count) > eob) { \
51			errno = EMSGSIZE; \
52			return(NS_TSIG_ERROR_NO_SPACE); \
53		} \
54	} while (0)
55
56/*%
57 *  ns_sign
58 *
59 * Parameters:
60 *\li	msg		message to be sent
61 *\li	msglen		input - length of message
62 *			output - length of signed message
63 *\li	msgsize		length of buffer containing message
64 *\li	error		value to put in the error field
65 *\li	key		tsig key used for signing
66 *\li	querysig	(response), the signature in the query
67 *\li	querysiglen	(response), the length of the signature in the query
68 *\li	sig		a buffer to hold the generated signature
69 *\li	siglen		input - length of signature buffer
70 *			output - length of signature
71 *
72 * Errors:
73 *\li	- bad input data (-1)
74 *\li	- bad key / sign failed (-BADKEY)
75 *\li	- not enough space (NS_TSIG_ERROR_NO_SPACE)
76 */
77int
78ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k,
79	const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
80	time_t in_timesigned)
81{
82	return(ns_sign2(msg, msglen, msgsize, error, k,
83			querysig, querysiglen, sig, siglen,
84			in_timesigned, NULL, NULL));
85}
86
87int
88ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k,
89	 const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
90	 time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr)
91{
92	HEADER *hp = (HEADER *)msg;
93	DST_KEY *key = (DST_KEY *)k;
94	u_char *cp, *eob;
95	u_char *lenp;
96	u_char *alg;
97	int n;
98	time_t timesigned;
99        u_char name[NS_MAXCDNAME];
100
101	dst_init();
102	if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL)
103		return (-1);
104
105	cp = msg + *msglen;
106	eob = msg + msgsize;
107
108	/* Name. */
109	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
110		n = ns_name_pton(key->dk_key_name, name, sizeof name);
111		if (n != -1)
112			n = ns_name_pack(name, cp, eob - cp,
113					 (const u_char **)dnptrs,
114					 (const u_char **)lastdnptr);
115
116	} else {
117		n = ns_name_pton("", name, sizeof name);
118		if (n != -1)
119			n = ns_name_pack(name, cp, eob - cp, NULL, NULL);
120	}
121	if (n < 0)
122		return (NS_TSIG_ERROR_NO_SPACE);
123	cp += n;
124
125	/* Type, class, ttl, length (not filled in yet). */
126	BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ);
127	PUTSHORT(ns_t_tsig, cp);
128	PUTSHORT(ns_c_any, cp);
129	PUTLONG(0, cp);		/*%< TTL */
130	lenp = cp;
131	cp += 2;
132
133	/* Alg. */
134	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
135		if (key->dk_alg != KEY_HMAC_MD5)
136			return (-ns_r_badkey);
137		n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
138	}
139	else
140		n = dn_comp("", cp, eob - cp, NULL, NULL);
141	if (n < 0)
142		return (NS_TSIG_ERROR_NO_SPACE);
143	alg = cp;
144	cp += n;
145
146	/* Time. */
147	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
148	PUTSHORT(0, cp);
149	timesigned = time(NULL);
150	if (error != ns_r_badtime)
151		PUTLONG(timesigned, cp);
152	else
153		PUTLONG(in_timesigned, cp);
154	PUTSHORT(NS_TSIG_FUDGE, cp);
155
156	/* Compute the signature. */
157	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
158		void *ctx;
159		u_char buf[NS_MAXCDNAME], *cp2;
160		int n;
161
162		dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
163
164		/* Digest the query signature, if this is a response. */
165		if (querysiglen > 0 && querysig != NULL) {
166			u_int16_t len_n = htons(querysiglen);
167			dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
168				      (u_char *)&len_n, INT16SZ, NULL, 0);
169			dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
170				      querysig, querysiglen, NULL, 0);
171		}
172
173		/* Digest the message. */
174		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen,
175			      NULL, 0);
176
177		/* Digest the key name. */
178		n = ns_name_ntol(name, buf, sizeof(buf));
179		INSIST(n > 0);
180		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
181
182		/* Digest the class and TTL. */
183		cp2 = buf;
184		PUTSHORT(ns_c_any, cp2);
185		PUTLONG(0, cp2);
186		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
187			      NULL, 0);
188
189		/* Digest the algorithm. */
190		n = ns_name_ntol(alg, buf, sizeof(buf));
191		INSIST(n > 0);
192		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
193
194		/* Digest the time signed, fudge, error, and other data */
195		cp2 = buf;
196		PUTSHORT(0, cp2);	/*%< Top 16 bits of time */
197		if (error != ns_r_badtime)
198			PUTLONG(timesigned, cp2);
199		else
200			PUTLONG(in_timesigned, cp2);
201		PUTSHORT(NS_TSIG_FUDGE, cp2);
202		PUTSHORT(error, cp2);	/*%< Error */
203		if (error != ns_r_badtime)
204			PUTSHORT(0, cp2);	/*%< Other data length */
205		else {
206			PUTSHORT(INT16SZ+INT32SZ, cp2);	/*%< Other data length */
207			PUTSHORT(0, cp2);	/*%< Top 16 bits of time */
208			PUTLONG(timesigned, cp2);
209		}
210		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
211			      NULL, 0);
212
213		n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
214				  sig, *siglen);
215		if (n < 0)
216			return (-ns_r_badkey);
217		*siglen = n;
218	} else
219		*siglen = 0;
220
221	/* Add the signature. */
222	BOUNDS_CHECK(cp, INT16SZ + (*siglen));
223	PUTSHORT(*siglen, cp);
224	memcpy(cp, sig, *siglen);
225	cp += (*siglen);
226
227	/* The original message ID & error. */
228	BOUNDS_CHECK(cp, INT16SZ + INT16SZ);
229	PUTSHORT(ntohs(hp->id), cp);	/*%< already in network order */
230	PUTSHORT(error, cp);
231
232	/* Other data. */
233	BOUNDS_CHECK(cp, INT16SZ);
234	if (error != ns_r_badtime)
235		PUTSHORT(0, cp);	/*%< Other data length */
236	else {
237		PUTSHORT(INT16SZ+INT32SZ, cp);	/*%< Other data length */
238		BOUNDS_CHECK(cp, INT32SZ+INT16SZ);
239		PUTSHORT(0, cp);	/*%< Top 16 bits of time */
240		PUTLONG(timesigned, cp);
241	}
242
243	/* Go back and fill in the length. */
244	PUTSHORT(cp - lenp - INT16SZ, lenp);
245
246	hp->arcount = htons(ntohs(hp->arcount) + 1);
247	*msglen = (cp - msg);
248	return (0);
249}
250
251int
252ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen,
253		 ns_tcp_tsig_state *state)
254{
255	dst_init();
256	if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
257		return (-1);
258	state->counter = -1;
259	state->key = k;
260	if (state->key->dk_alg != KEY_HMAC_MD5)
261		return (-ns_r_badkey);
262	if (querysiglen > (int)sizeof(state->sig))
263		return (-1);
264	memcpy(state->sig, querysig, querysiglen);
265	state->siglen = querysiglen;
266	return (0);
267}
268
269int
270ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error,
271	    ns_tcp_tsig_state *state, int done)
272{
273	return (ns_sign_tcp2(msg, msglen, msgsize, error, state,
274			     done, NULL, NULL));
275}
276
277int
278ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error,
279	     ns_tcp_tsig_state *state, int done,
280	     u_char **dnptrs, u_char **lastdnptr)
281{
282	u_char *cp, *eob, *lenp;
283	u_char buf[MAXDNAME], *cp2;
284	HEADER *hp = (HEADER *)msg;
285	time_t timesigned;
286	int n;
287
288	if (msg == NULL || msglen == NULL || state == NULL)
289		return (-1);
290
291	state->counter++;
292	if (state->counter == 0)
293		return (ns_sign2(msg, msglen, msgsize, error, state->key,
294				 state->sig, state->siglen,
295				 state->sig, &state->siglen, 0,
296				 dnptrs, lastdnptr));
297
298	if (state->siglen > 0) {
299		u_int16_t siglen_n = htons(state->siglen);
300		dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx,
301			      NULL, 0, NULL, 0);
302		dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
303			      (u_char *)&siglen_n, INT16SZ, NULL, 0);
304		dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
305			      state->sig, state->siglen, NULL, 0);
306		state->siglen = 0;
307	}
308
309	dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen,
310		      NULL, 0);
311
312	if (done == 0 && (state->counter % 100 != 0))
313		return (0);
314
315	cp = msg + *msglen;
316	eob = msg + msgsize;
317
318	/* Name. */
319	n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr);
320	if (n < 0)
321		return (NS_TSIG_ERROR_NO_SPACE);
322	cp += n;
323
324	/* Type, class, ttl, length (not filled in yet). */
325	BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ);
326	PUTSHORT(ns_t_tsig, cp);
327	PUTSHORT(ns_c_any, cp);
328	PUTLONG(0, cp);		/*%< TTL */
329	lenp = cp;
330	cp += 2;
331
332	/* Alg. */
333	n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
334	if (n < 0)
335		return (NS_TSIG_ERROR_NO_SPACE);
336	cp += n;
337
338	/* Time. */
339	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
340	PUTSHORT(0, cp);
341	timesigned = time(NULL);
342	PUTLONG(timesigned, cp);
343	PUTSHORT(NS_TSIG_FUDGE, cp);
344
345	/*
346	 * Compute the signature.
347	 */
348
349	/* Digest the time signed and fudge. */
350	cp2 = buf;
351	PUTSHORT(0, cp2);	/*%< Top 16 bits of time */
352	PUTLONG(timesigned, cp2);
353	PUTSHORT(NS_TSIG_FUDGE, cp2);
354
355	dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
356		      buf, cp2 - buf, NULL, 0);
357
358	n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
359			  state->sig, sizeof(state->sig));
360	if (n < 0)
361		return (-ns_r_badkey);
362	state->siglen = n;
363
364	/* Add the signature. */
365	BOUNDS_CHECK(cp, INT16SZ + state->siglen);
366	PUTSHORT(state->siglen, cp);
367	memcpy(cp, state->sig, state->siglen);
368	cp += state->siglen;
369
370	/* The original message ID & error. */
371	BOUNDS_CHECK(cp, INT16SZ + INT16SZ);
372	PUTSHORT(ntohs(hp->id), cp);	/*%< already in network order */
373	PUTSHORT(error, cp);
374
375	/* Other data. */
376	BOUNDS_CHECK(cp, INT16SZ);
377	PUTSHORT(0, cp);
378
379	/* Go back and fill in the length. */
380	PUTSHORT(cp - lenp - INT16SZ, lenp);
381
382	hp->arcount = htons(ntohs(hp->arcount) + 1);
383	*msglen = (cp - msg);
384	return (0);
385}
386
387/*! \file */
388