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