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 19 static 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 */ 77 int 78 ns_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 87 int 88 ns_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 251 int 252 ns_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 269 int 270 ns_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 277 int 278 ns_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