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 #include "port_before.h" 19 #include "fd_setsize.h" 20 21 #include <sys/types.h> 22 #include <sys/param.h> 23 24 #include <netinet/in.h> 25 #include <arpa/nameser.h> 26 #include <arpa/inet.h> 27 28 #include <errno.h> 29 #include <netdb.h> 30 #include <resolv.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <time.h> 35 #include <unistd.h> 36 37 #include <isc/dst.h> 38 39 #include "port_after.h" 40 41 /* Private. */ 42 43 #define BOUNDS_CHECK(ptr, count) \ 44 do { \ 45 if ((ptr) + (count) > eom) { \ 46 return (NS_TSIG_ERROR_FORMERR); \ 47 } \ 48 } while (0) 49 50 /* Public. */ 51 52 u_char * 53 ns_find_tsig(u_char *msg, u_char *eom) { 54 HEADER *hp = (HEADER *)msg; 55 int n, type; 56 u_char *cp = msg, *start; 57 58 if (msg == NULL || eom == NULL || msg > eom) 59 return (NULL); 60 61 if (cp + HFIXEDSZ >= eom) 62 return (NULL); 63 64 if (hp->arcount == 0) 65 return (NULL); 66 67 cp += HFIXEDSZ; 68 69 n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount)); 70 if (n < 0) 71 return (NULL); 72 cp += n; 73 74 n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount)); 75 if (n < 0) 76 return (NULL); 77 cp += n; 78 79 n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount)); 80 if (n < 0) 81 return (NULL); 82 cp += n; 83 84 n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1); 85 if (n < 0) 86 return (NULL); 87 cp += n; 88 89 start = cp; 90 n = dn_skipname(cp, eom); 91 if (n < 0) 92 return (NULL); 93 cp += n; 94 if (cp + INT16SZ >= eom) 95 return (NULL); 96 97 GETSHORT(type, cp); 98 if (type != ns_t_tsig) 99 return (NULL); 100 return (start); 101 } 102 103 /* ns_verify 104 * 105 * Parameters: 106 *\li statp res stuff 107 *\li msg received message 108 *\li msglen length of message 109 *\li key tsig key used for verifying. 110 *\li querysig (response), the signature in the query 111 *\li querysiglen (response), the length of the signature in the query 112 *\li sig (query), a buffer to hold the signature 113 *\li siglen (query), input - length of signature buffer 114 * output - length of signature 115 * 116 * Errors: 117 *\li - bad input (-1) 118 *\li - invalid dns message (NS_TSIG_ERROR_FORMERR) 119 *\li - TSIG is not present (NS_TSIG_ERROR_NO_TSIG) 120 *\li - key doesn't match (-ns_r_badkey) 121 *\li - TSIG verification fails with BADKEY (-ns_r_badkey) 122 *\li - TSIG verification fails with BADSIG (-ns_r_badsig) 123 *\li - TSIG verification fails with BADTIME (-ns_r_badtime) 124 *\li - TSIG verification succeeds, error set to BAKEY (ns_r_badkey) 125 *\li - TSIG verification succeeds, error set to BADSIG (ns_r_badsig) 126 *\li - TSIG verification succeeds, error set to BADTIME (ns_r_badtime) 127 */ 128 int 129 ns_verify(u_char *msg, int *msglen, void *k, 130 const u_char *querysig, int querysiglen, u_char *sig, int *siglen, 131 time_t *timesigned, int nostrip) 132 { 133 HEADER *hp = (HEADER *)msg; 134 DST_KEY *key = (DST_KEY *)k; 135 u_char *cp = msg, *eom; 136 char name[MAXDNAME], alg[MAXDNAME]; 137 u_char *recstart, *rdatastart; 138 u_char *sigstart, *otherstart; 139 int n; 140 int error; 141 u_int16_t type, length; 142 u_int16_t fudge, sigfieldlen, otherfieldlen; 143 144 dst_init(); 145 if (msg == NULL || msglen == NULL || *msglen < 0) 146 return (-1); 147 148 eom = msg + *msglen; 149 150 recstart = ns_find_tsig(msg, eom); 151 if (recstart == NULL) 152 return (NS_TSIG_ERROR_NO_TSIG); 153 154 cp = recstart; 155 156 /* Read the key name. */ 157 n = dn_expand(msg, eom, cp, name, MAXDNAME); 158 if (n < 0) 159 return (NS_TSIG_ERROR_FORMERR); 160 cp += n; 161 162 /* Read the type. */ 163 BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); 164 GETSHORT(type, cp); 165 if (type != ns_t_tsig) 166 return (NS_TSIG_ERROR_NO_TSIG); 167 168 /* Skip the class and TTL, save the length. */ 169 cp += INT16SZ + INT32SZ; 170 GETSHORT(length, cp); 171 if (eom - cp != length) 172 return (NS_TSIG_ERROR_FORMERR); 173 174 /* Read the algorithm name. */ 175 rdatastart = cp; 176 n = dn_expand(msg, eom, cp, alg, MAXDNAME); 177 if (n < 0) 178 return (NS_TSIG_ERROR_FORMERR); 179 if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) 180 return (-ns_r_badkey); 181 cp += n; 182 183 /* Read the time signed and fudge. */ 184 BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); 185 cp += INT16SZ; 186 GETLONG((*timesigned), cp); 187 GETSHORT(fudge, cp); 188 189 /* Read the signature. */ 190 BOUNDS_CHECK(cp, INT16SZ); 191 GETSHORT(sigfieldlen, cp); 192 BOUNDS_CHECK(cp, sigfieldlen); 193 sigstart = cp; 194 cp += sigfieldlen; 195 196 /* Skip id and read error. */ 197 BOUNDS_CHECK(cp, 2*INT16SZ); 198 cp += INT16SZ; 199 GETSHORT(error, cp); 200 201 /* Parse the other data. */ 202 BOUNDS_CHECK(cp, INT16SZ); 203 GETSHORT(otherfieldlen, cp); 204 BOUNDS_CHECK(cp, otherfieldlen); 205 otherstart = cp; 206 cp += otherfieldlen; 207 208 if (cp != eom) 209 return (NS_TSIG_ERROR_FORMERR); 210 211 /* Verify that the key used is OK. */ 212 if (key != NULL) { 213 if (key->dk_alg != KEY_HMAC_MD5) 214 return (-ns_r_badkey); 215 if (error != ns_r_badsig && error != ns_r_badkey) { 216 if (ns_samename(key->dk_key_name, name) != 1) 217 return (-ns_r_badkey); 218 } 219 } 220 221 hp->arcount = htons(ntohs(hp->arcount) - 1); 222 223 /* 224 * Do the verification. 225 */ 226 227 if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { 228 void *ctx; 229 u_char buf[MAXDNAME]; 230 u_char buf2[MAXDNAME]; 231 232 /* Digest the query signature, if this is a response. */ 233 dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); 234 if (querysiglen > 0 && querysig != NULL) { 235 u_int16_t len_n = htons(querysiglen); 236 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, 237 (u_char *)&len_n, INT16SZ, NULL, 0); 238 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, 239 querysig, querysiglen, NULL, 0); 240 } 241 242 /* Digest the message. */ 243 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg, 244 NULL, 0); 245 246 /* Digest the key name. */ 247 n = ns_name_pton(name, buf2, sizeof(buf2)); 248 if (n < 0) 249 return (-1); 250 n = ns_name_ntol(buf2, buf, sizeof(buf)); 251 if (n < 0) 252 return (-1); 253 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); 254 255 /* Digest the class and TTL. */ 256 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, 257 recstart + dn_skipname(recstart, eom) + INT16SZ, 258 INT16SZ + INT32SZ, NULL, 0); 259 260 /* Digest the algorithm. */ 261 n = ns_name_pton(alg, buf2, sizeof(buf2)); 262 if (n < 0) 263 return (-1); 264 n = ns_name_ntol(buf2, buf, sizeof(buf)); 265 if (n < 0) 266 return (-1); 267 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); 268 269 /* Digest the time signed and fudge. */ 270 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, 271 rdatastart + dn_skipname(rdatastart, eom), 272 INT16SZ + INT32SZ + INT16SZ, NULL, 0); 273 274 /* Digest the error and other data. */ 275 dst_verify_data(SIG_MODE_UPDATE, key, &ctx, 276 otherstart - INT16SZ - INT16SZ, 277 otherfieldlen + INT16SZ + INT16SZ, NULL, 0); 278 279 n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, 280 sigstart, sigfieldlen); 281 282 if (n < 0) 283 return (-ns_r_badsig); 284 285 if (sig != NULL && siglen != NULL) { 286 if (*siglen < sigfieldlen) 287 return (NS_TSIG_ERROR_NO_SPACE); 288 memcpy(sig, sigstart, sigfieldlen); 289 *siglen = sigfieldlen; 290 } 291 } else { 292 if (sigfieldlen > 0) 293 return (NS_TSIG_ERROR_FORMERR); 294 if (sig != NULL && siglen != NULL) 295 *siglen = 0; 296 } 297 298 /* Reset the counter, since we still need to check for badtime. */ 299 hp->arcount = htons(ntohs(hp->arcount) + 1); 300 301 /* Verify the time. */ 302 if (abs((*timesigned) - time(NULL)) > fudge) 303 return (-ns_r_badtime); 304 305 if (nostrip == 0) { 306 *msglen = recstart - msg; 307 hp->arcount = htons(ntohs(hp->arcount) - 1); 308 } 309 310 if (error != NOERROR) 311 return (error); 312 313 return (0); 314 } 315 316 int 317 ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen, 318 ns_tcp_tsig_state *state) 319 { 320 dst_init(); 321 if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) 322 return (-1); 323 state->counter = -1; 324 state->key = k; 325 if (state->key->dk_alg != KEY_HMAC_MD5) 326 return (-ns_r_badkey); 327 if (querysiglen > (int)sizeof(state->sig)) 328 return (-1); 329 memcpy(state->sig, querysig, querysiglen); 330 state->siglen = querysiglen; 331 return (0); 332 } 333 334 int 335 ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state, 336 int required) 337 { 338 HEADER *hp = (HEADER *)msg; 339 u_char *recstart, *sigstart; 340 unsigned int sigfieldlen, otherfieldlen; 341 u_char *cp, *eom, *cp2; 342 char name[MAXDNAME], alg[MAXDNAME]; 343 u_char buf[MAXDNAME]; 344 int n, type, length, fudge, error; 345 time_t timesigned; 346 347 if (msg == NULL || msglen == NULL || state == NULL) 348 return (-1); 349 350 eom = msg + *msglen; 351 352 state->counter++; 353 if (state->counter == 0) 354 return (ns_verify(msg, msglen, state->key, 355 state->sig, state->siglen, 356 state->sig, &state->siglen, ×igned, 0)); 357 358 if (state->siglen > 0) { 359 u_int16_t siglen_n = htons(state->siglen); 360 361 dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx, 362 NULL, 0, NULL, 0); 363 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, 364 (u_char *)&siglen_n, INT16SZ, NULL, 0); 365 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, 366 state->sig, state->siglen, NULL, 0); 367 state->siglen = 0; 368 } 369 370 cp = recstart = ns_find_tsig(msg, eom); 371 372 if (recstart == NULL) { 373 if (required) 374 return (NS_TSIG_ERROR_NO_TSIG); 375 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, 376 msg, *msglen, NULL, 0); 377 return (0); 378 } 379 380 hp->arcount = htons(ntohs(hp->arcount) - 1); 381 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, 382 msg, recstart - msg, NULL, 0); 383 384 /* Read the key name. */ 385 n = dn_expand(msg, eom, cp, name, MAXDNAME); 386 if (n < 0) 387 return (NS_TSIG_ERROR_FORMERR); 388 cp += n; 389 390 /* Read the type. */ 391 BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); 392 GETSHORT(type, cp); 393 if (type != ns_t_tsig) 394 return (NS_TSIG_ERROR_NO_TSIG); 395 396 /* Skip the class and TTL, save the length. */ 397 cp += INT16SZ + INT32SZ; 398 GETSHORT(length, cp); 399 if (eom - cp != length) 400 return (NS_TSIG_ERROR_FORMERR); 401 402 /* Read the algorithm name. */ 403 n = dn_expand(msg, eom, cp, alg, MAXDNAME); 404 if (n < 0) 405 return (NS_TSIG_ERROR_FORMERR); 406 if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) 407 return (-ns_r_badkey); 408 cp += n; 409 410 /* Verify that the key used is OK. */ 411 if ((ns_samename(state->key->dk_key_name, name) != 1 || 412 state->key->dk_alg != KEY_HMAC_MD5)) 413 return (-ns_r_badkey); 414 415 /* Read the time signed and fudge. */ 416 BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); 417 cp += INT16SZ; 418 GETLONG(timesigned, cp); 419 GETSHORT(fudge, cp); 420 421 /* Read the signature. */ 422 BOUNDS_CHECK(cp, INT16SZ); 423 GETSHORT(sigfieldlen, cp); 424 BOUNDS_CHECK(cp, sigfieldlen); 425 sigstart = cp; 426 cp += sigfieldlen; 427 428 /* Skip id and read error. */ 429 BOUNDS_CHECK(cp, 2*INT16SZ); 430 cp += INT16SZ; 431 GETSHORT(error, cp); 432 433 /* Parse the other data. */ 434 BOUNDS_CHECK(cp, INT16SZ); 435 GETSHORT(otherfieldlen, cp); 436 BOUNDS_CHECK(cp, otherfieldlen); 437 cp += otherfieldlen; 438 439 if (cp != eom) 440 return (NS_TSIG_ERROR_FORMERR); 441 442 /* 443 * Do the verification. 444 */ 445 446 /* Digest the time signed and fudge. */ 447 cp2 = buf; 448 PUTSHORT(0, cp2); /*%< Top 16 bits of time. */ 449 PUTLONG(timesigned, cp2); 450 PUTSHORT(NS_TSIG_FUDGE, cp2); 451 452 dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, 453 buf, cp2 - buf, NULL, 0); 454 455 n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, 456 sigstart, sigfieldlen); 457 if (n < 0) 458 return (-ns_r_badsig); 459 460 if (sigfieldlen > sizeof(state->sig)) 461 return (NS_TSIG_ERROR_NO_SPACE); 462 463 memcpy(state->sig, sigstart, sigfieldlen); 464 state->siglen = sigfieldlen; 465 466 /* Verify the time. */ 467 if (abs(timesigned - time(NULL)) > fudge) 468 return (-ns_r_badtime); 469 470 *msglen = recstart - msg; 471 472 if (error != NOERROR) 473 return (error); 474 475 return (0); 476 } 477 478 /*! \file */ 479