1/*
2 * Copyright (c) 1999 by Internet Software Consortium, Inc.
3 *
4 * Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
9 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
10 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
11 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
13 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15 * SOFTWARE.
16 */
17
18#ifndef __APPLE__
19#ifndef lint
20static const char rcsid[] = "$Id: ns_sign.c,v 1.1 2006/03/01 19:01:37 majka Exp $";
21#endif
22#endif
23
24/* Import. */
25
26#ifndef __APPLE__
27#include "port_before.h"
28#include "fd_setsize.h"
29#endif
30
31#include <sys/types.h>
32#include <sys/param.h>
33
34#include <netinet/in.h>
35#include <arpa/nameser.h>
36#include <arpa/inet.h>
37
38#include <errno.h>
39#include <netdb.h>
40#include <resolv.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <time.h>
45#include <unistd.h>
46
47#ifndef __APPLE__
48#include <isc/dst.h>
49#include "port_after.h"
50#else
51#include "dst_internal.h"
52#include "res_private.h"
53#endif
54
55#define BOUNDS_CHECK(ptr, count) \
56	do { \
57		if ((ptr) + (count) > eob) { \
58			errno = EMSGSIZE; \
59			return(NS_TSIG_ERROR_NO_SPACE); \
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 */
82int
83ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k,
84	const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
85	time_t in_timesigned)
86{
87	return(ns_sign2(msg, msglen, msgsize, error, k,
88			querysig, querysiglen, sig, siglen,
89			in_timesigned, NULL, NULL));
90}
91
92int
93ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k,
94	 const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
95	 time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr)
96{
97	HEADER *hp = (HEADER *)msg;
98	DST_KEY *key = (DST_KEY *)k;
99	u_char *cp = msg + *msglen, *eob = msg + msgsize;
100	u_char *lenp;
101	u_char *name, *alg;
102	int n;
103	time_t timesigned;
104
105	dst_init();
106	if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL)
107		return (-1);
108
109	/* Name. */
110	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey)
111		n = dn_comp(key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr);
112	else
113		n = dn_comp("", cp, eob - cp, NULL, NULL);
114	if (n < 0)
115		return (NS_TSIG_ERROR_NO_SPACE);
116	name = cp;
117	cp += n;
118
119	/* Type, class, ttl, length (not filled in yet). */
120	BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
121	NS_PUT16(ns_t_tsig, cp);
122	NS_PUT16(ns_c_any, cp);
123	NS_PUT32(0, cp);		/* TTL */
124	lenp = cp;
125	cp += 2;
126
127	/* Alg. */
128	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
129		if (key->dk_alg != KEY_HMAC_MD5)
130			return (-ns_r_badkey);
131		n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
132	}
133	else
134		n = dn_comp("", cp, eob - cp, NULL, NULL);
135	if (n < 0)
136		return (NS_TSIG_ERROR_NO_SPACE);
137	alg = cp;
138	cp += n;
139
140	/* Time. */
141	BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
142	NS_PUT16(0, cp);
143	timesigned = time(NULL);
144	if (error != ns_r_badtime)
145		NS_PUT32(timesigned, cp);
146	else
147		NS_PUT32(in_timesigned, cp);
148	NS_PUT16(NS_TSIG_FUDGE, cp);
149
150	/* Compute the signature. */
151	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
152		void *ctx;
153		u_char buf[NS_MAXDNAME], *cp2;
154		int n;
155
156		dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
157
158		/* Digest the query signature, if this is a response. */
159		if (querysiglen > 0 && querysig != NULL) {
160			u_int16_t len_n = htons(querysiglen);
161			dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
162				      (u_char *)&len_n, NS_INT16SZ, NULL, 0);
163			dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
164				      querysig, querysiglen, NULL, 0);
165		}
166
167		/* Digest the message. */
168		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen,
169			      NULL, 0);
170
171		/* Digest the key name. */
172		n = ns_name_ntol(name, buf, sizeof(buf));
173		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
174
175		/* Digest the class and TTL. */
176		cp2 = buf;
177		NS_PUT16(ns_c_any, cp2);
178		NS_PUT32(0, cp2);
179		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
180			      NULL, 0);
181
182		/* Digest the algorithm. */
183		n = ns_name_ntol(alg, buf, sizeof(buf));
184		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
185
186		/* Digest the time signed, fudge, error, and other data */
187		cp2 = buf;
188		NS_PUT16(0, cp2);	/* Top 16 bits of time */
189		if (error != ns_r_badtime)
190			NS_PUT32(timesigned, cp2);
191		else
192			NS_PUT32(in_timesigned, cp2);
193		NS_PUT16(NS_TSIG_FUDGE, cp2);
194		NS_PUT16(error, cp2);	/* Error */
195		if (error != ns_r_badtime)
196			NS_PUT16(0, cp2);	/* Other data length */
197		else {
198			NS_PUT16(NS_INT16SZ+NS_INT32SZ, cp2);	/* Other data length */
199			NS_PUT16(0, cp2);	/* Top 16 bits of time */
200			NS_PUT32(timesigned, cp2);
201		}
202		dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
203			      NULL, 0);
204
205		n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
206				  sig, *siglen);
207		if (n < 0)
208			return (-ns_r_badkey);
209		*siglen = n;
210	} else
211		*siglen = 0;
212
213	/* Add the signature. */
214	BOUNDS_CHECK(cp, NS_INT16SZ + (*siglen));
215	NS_PUT16(*siglen, cp);
216	memcpy(cp, sig, *siglen);
217	cp += (*siglen);
218
219	/* The original message ID & error. */
220	BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ);
221	NS_PUT16(ntohs(hp->id), cp);	/* already in network order */
222	NS_PUT16(error, cp);
223
224	/* Other data. */
225	BOUNDS_CHECK(cp, NS_INT16SZ);
226	if (error != ns_r_badtime)
227		NS_PUT16(0, cp);	/* Other data length */
228	else {
229		NS_PUT16(NS_INT16SZ+NS_INT32SZ, cp);	/* Other data length */
230		BOUNDS_CHECK(cp, NS_INT32SZ+NS_INT16SZ);
231		NS_PUT16(0, cp);	/* Top 16 bits of time */
232		NS_PUT32(timesigned, cp);
233	}
234
235	/* Go back and fill in the length. */
236	NS_PUT16(cp - lenp - NS_INT16SZ, lenp);
237
238	hp->arcount = htons(ntohs(hp->arcount) + 1);
239	*msglen = (cp - msg);
240	return (0);
241}
242
243int
244ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen,
245		 ns_tcp_tsig_state *state)
246{
247	dst_init();
248	if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
249		return (-1);
250	state->counter = -1;
251	state->key = k;
252	if (state->key->dk_alg != KEY_HMAC_MD5)
253		return (-ns_r_badkey);
254	if (querysiglen > (int)sizeof(state->sig))
255		return (-1);
256	memcpy(state->sig, querysig, querysiglen);
257	state->siglen = querysiglen;
258	return (0);
259}
260
261int
262ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error,
263	    ns_tcp_tsig_state *state, int done)
264{
265	return (ns_sign_tcp2(msg, msglen, msgsize, error, state,
266			     done, NULL, NULL));
267}
268
269int
270ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error,
271	     ns_tcp_tsig_state *state, int done,
272	     u_char **dnptrs, u_char **lastdnptr)
273{
274	u_char *cp, *eob, *lenp;
275	u_char buf[NS_MAXDNAME], *cp2;
276	HEADER *hp = (HEADER *)msg;
277	time_t timesigned;
278	int n;
279
280	if (msg == NULL || msglen == NULL || state == NULL)
281		return (-1);
282
283	state->counter++;
284	if (state->counter == 0)
285		return (ns_sign2(msg, msglen, msgsize, error, state->key,
286				 state->sig, state->siglen,
287				 state->sig, &state->siglen, 0,
288				 dnptrs, lastdnptr));
289
290	if (state->siglen > 0) {
291		u_int16_t siglen_n = htons(state->siglen);
292		dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx,
293			      NULL, 0, NULL, 0);
294		dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
295			      (u_char *)&siglen_n, NS_INT16SZ, NULL, 0);
296		dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
297			      state->sig, state->siglen, NULL, 0);
298		state->siglen = 0;
299	}
300
301	dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen,
302		      NULL, 0);
303
304	if (done == 0 && (state->counter % 100 != 0))
305		return (0);
306
307	cp = msg + *msglen;
308	eob = msg + msgsize;
309
310	/* Name. */
311	n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr);
312	if (n < 0)
313		return (NS_TSIG_ERROR_NO_SPACE);
314	cp += n;
315
316	/* Type, class, ttl, length (not filled in yet). */
317	BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
318	NS_PUT16(ns_t_tsig, cp);
319	NS_PUT16(ns_c_any, cp);
320	NS_PUT32(0, cp);		/* TTL */
321	lenp = cp;
322	cp += 2;
323
324	/* Alg. */
325	n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
326	if (n < 0)
327		return (NS_TSIG_ERROR_NO_SPACE);
328	cp += n;
329
330	/* Time. */
331	BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ);
332	NS_PUT16(0, cp);
333	timesigned = time(NULL);
334	NS_PUT32(timesigned, cp);
335	NS_PUT16(NS_TSIG_FUDGE, cp);
336
337	/*
338	 * Compute the signature.
339	 */
340
341	/* Digest the time signed and fudge. */
342	cp2 = buf;
343	NS_PUT16(0, cp2);	/* Top 16 bits of time */
344	NS_PUT32(timesigned, cp2);
345	NS_PUT16(NS_TSIG_FUDGE, cp2);
346
347	dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
348		      buf, cp2 - buf, NULL, 0);
349
350	n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
351			  state->sig, sizeof(state->sig));
352	if (n < 0)
353		return (-ns_r_badkey);
354	state->siglen = n;
355
356	/* Add the signature. */
357	BOUNDS_CHECK(cp, NS_INT16SZ + state->siglen);
358	NS_PUT16(state->siglen, cp);
359	memcpy(cp, state->sig, state->siglen);
360	cp += state->siglen;
361
362	/* The original message ID & error. */
363	BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ);
364	NS_PUT16(ntohs(hp->id), cp);	/* already in network order */
365	NS_PUT16(error, cp);
366
367	/* Other data. */
368	BOUNDS_CHECK(cp, NS_INT16SZ);
369	NS_PUT16(0, cp);
370
371	/* Go back and fill in the length. */
372	NS_PUT16(cp - lenp - NS_INT16SZ, lenp);
373
374	hp->arcount = htons(ntohs(hp->arcount) + 1);
375	*msglen = (cp - msg);
376	return (0);
377}
378