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