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