| 1 | /* |
| 2 | * tsig.c |
| 3 | * |
| 4 | * contains the functions needed for TSIG [RFC2845] |
| 5 | * |
| 6 | * (c) 2005-2006 NLnet Labs |
| 7 | * See the file LICENSE for the license |
| 8 | */ |
| 9 | |
| 10 | #include <ldns/config.h> |
| 11 | |
| 12 | #include <ldns/ldns.h> |
| 13 | |
| 14 | #include <strings.h> |
| 15 | |
| 16 | #ifdef HAVE_SSL |
| 17 | #include <openssl/hmac.h> |
| 18 | #include <openssl/md5.h> |
| 19 | #endif /* HAVE_SSL */ |
| 20 | |
| 21 | char * |
| 22 | ldns_tsig_algorithm(ldns_tsig_credentials *tc) |
| 23 | { |
| 24 | return tc->algorithm; |
| 25 | } |
| 26 | |
| 27 | char * |
| 28 | ldns_tsig_keyname(ldns_tsig_credentials *tc) |
| 29 | { |
| 30 | return tc->keyname; |
| 31 | } |
| 32 | |
| 33 | char * |
| 34 | ldns_tsig_keydata(ldns_tsig_credentials *tc) |
| 35 | { |
| 36 | return tc->keydata; |
| 37 | } |
| 38 | |
| 39 | char * |
| 40 | ldns_tsig_keyname_clone(ldns_tsig_credentials *tc) |
| 41 | { |
| 42 | return strdup(tc->keyname); |
| 43 | } |
| 44 | |
| 45 | char * |
| 46 | ldns_tsig_keydata_clone(ldns_tsig_credentials *tc) |
| 47 | { |
| 48 | return strdup(tc->keydata); |
| 49 | } |
| 50 | |
| 51 | /* |
| 52 | * Makes an exact copy of the wire, but with the tsig rr removed |
| 53 | */ |
| 54 | uint8_t * |
| 55 | ldns_tsig_prepare_pkt_wire(uint8_t *wire, size_t wire_len, size_t *result_len) |
| 56 | { |
| 57 | uint8_t *wire2 = NULL; |
| 58 | uint16_t qd_count; |
| 59 | uint16_t an_count; |
| 60 | uint16_t ns_count; |
| 61 | uint16_t ar_count; |
| 62 | ldns_rr *rr; |
| 63 | |
| 64 | size_t pos; |
| 65 | uint16_t i; |
| 66 | |
| 67 | ldns_status status; |
| 68 | |
| 69 | if(wire_len < LDNS_HEADER_SIZE) { |
| 70 | return NULL; |
| 71 | } |
| 72 | /* fake parse the wire */ |
| 73 | qd_count = LDNS_QDCOUNT(wire); |
| 74 | an_count = LDNS_ANCOUNT(wire); |
| 75 | ns_count = LDNS_NSCOUNT(wire); |
| 76 | ar_count = LDNS_ARCOUNT(wire); |
| 77 | |
| 78 | if (ar_count > 0) { |
| 79 | ar_count--; |
| 80 | } else { |
| 81 | return NULL; |
| 82 | } |
| 83 | |
| 84 | pos = LDNS_HEADER_SIZE; |
| 85 | |
| 86 | for (i = 0; i < qd_count; i++) { |
| 87 | status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_QUESTION); |
| 88 | if (status != LDNS_STATUS_OK) { |
| 89 | return NULL; |
| 90 | } |
| 91 | ldns_rr_free(rr); |
| 92 | } |
| 93 | |
| 94 | for (i = 0; i < an_count; i++) { |
| 95 | status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_ANSWER); |
| 96 | if (status != LDNS_STATUS_OK) { |
| 97 | return NULL; |
| 98 | } |
| 99 | ldns_rr_free(rr); |
| 100 | } |
| 101 | |
| 102 | for (i = 0; i < ns_count; i++) { |
| 103 | status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_AUTHORITY); |
| 104 | if (status != LDNS_STATUS_OK) { |
| 105 | return NULL; |
| 106 | } |
| 107 | ldns_rr_free(rr); |
| 108 | } |
| 109 | |
| 110 | for (i = 0; i < ar_count; i++) { |
| 111 | status = ldns_wire2rr(&rr, wire, wire_len, &pos, |
| 112 | LDNS_SECTION_ADDITIONAL); |
| 113 | if (status != LDNS_STATUS_OK) { |
| 114 | return NULL; |
| 115 | } |
| 116 | ldns_rr_free(rr); |
| 117 | } |
| 118 | |
| 119 | *result_len = pos; |
| 120 | wire2 = LDNS_XMALLOC(uint8_t, *result_len); |
| 121 | if(!wire2) { |
| 122 | return NULL; |
| 123 | } |
| 124 | memcpy(wire2, wire, *result_len); |
| 125 | |
| 126 | ldns_write_uint16(wire2 + LDNS_ARCOUNT_OFF, ar_count); |
| 127 | |
| 128 | return wire2; |
| 129 | } |
| 130 | |
| 131 | #ifdef HAVE_SSL |
| 132 | static const EVP_MD * |
| 133 | ldns_digest_function(char *name) |
| 134 | { |
| 135 | /* these are the mandatory algorithms from RFC4635 */ |
| 136 | /* The optional algorithms are not yet implemented */ |
| 137 | if (strlen(name) == 12 && strncasecmp(name, "hmac-sha256.", 11) == 0) { |
| 138 | #ifdef HAVE_EVP_SHA256 |
| 139 | return EVP_sha256(); |
| 140 | #else |
| 141 | return NULL; |
| 142 | #endif |
| 143 | } else if (strlen(name) == 10 && strncasecmp(name, "hmac-sha1.", 9) == 0) |
| 144 | return EVP_sha1(); |
| 145 | else if (strlen(name) == 25 && strncasecmp(name, |
| 146 | "hmac-md5.sig-alg.reg.int.", 25) == 0) |
| 147 | return EVP_md5(); |
| 148 | else |
| 149 | return NULL; |
| 150 | } |
| 151 | #endif |
| 152 | |
| 153 | #ifdef HAVE_SSL |
| 154 | static ldns_status |
| 155 | ldns_tsig_mac_new(ldns_rdf **tsig_mac, uint8_t *pkt_wire, size_t pkt_wire_size, |
| 156 | const char *key_data, ldns_rdf *key_name_rdf, ldns_rdf *fudge_rdf, |
| 157 | ldns_rdf *algorithm_rdf, ldns_rdf *time_signed_rdf, ldns_rdf *error_rdf, |
| 158 | ldns_rdf *other_data_rdf, ldns_rdf *orig_mac_rdf, int tsig_timers_only) |
| 159 | { |
| 160 | char *wireformat; |
| 161 | int wiresize; |
| 162 | unsigned char *mac_bytes; |
| 163 | unsigned char *key_bytes; |
| 164 | int key_size; |
| 165 | const EVP_MD *digester; |
| 166 | char *algorithm_name; |
| 167 | unsigned int md_len = EVP_MAX_MD_SIZE; |
| 168 | ldns_rdf *result = NULL; |
| 169 | ldns_buffer *data_buffer = NULL; |
| 170 | |
| 171 | /* |
| 172 | * prepare the digestable information |
| 173 | */ |
| 174 | data_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN); |
| 175 | if(!data_buffer) { |
| 176 | return LDNS_STATUS_MEM_ERR; |
| 177 | } |
| 178 | /* if orig_mac is not NULL, add it too */ |
| 179 | if (orig_mac_rdf) { |
| 180 | (void) ldns_rdf2buffer_wire(data_buffer, orig_mac_rdf); |
| 181 | } |
| 182 | ldns_buffer_write(data_buffer, pkt_wire, pkt_wire_size); |
| 183 | if (!tsig_timers_only) { |
| 184 | (void)ldns_rdf2buffer_wire(data_buffer, key_name_rdf); |
| 185 | ldns_buffer_write_u16(data_buffer, LDNS_RR_CLASS_ANY); |
| 186 | ldns_buffer_write_u32(data_buffer, 0); |
| 187 | (void)ldns_rdf2buffer_wire(data_buffer, algorithm_rdf); |
| 188 | } |
| 189 | (void)ldns_rdf2buffer_wire(data_buffer, time_signed_rdf); |
| 190 | (void)ldns_rdf2buffer_wire(data_buffer, fudge_rdf); |
| 191 | if (!tsig_timers_only) { |
| 192 | (void)ldns_rdf2buffer_wire(data_buffer, error_rdf); |
| 193 | (void)ldns_rdf2buffer_wire(data_buffer, other_data_rdf); |
| 194 | } |
| 195 | |
| 196 | wireformat = (char *) data_buffer->_data; |
| 197 | wiresize = (int) ldns_buffer_position(data_buffer); |
| 198 | |
| 199 | algorithm_name = ldns_rdf2str(algorithm_rdf); |
| 200 | if(!algorithm_name) { |
| 201 | ldns_buffer_free(data_buffer); |
| 202 | return LDNS_STATUS_MEM_ERR; |
| 203 | } |
| 204 | |
| 205 | /* prepare the key */ |
| 206 | key_bytes = LDNS_XMALLOC(unsigned char, |
| 207 | ldns_b64_pton_calculate_size(strlen(key_data))); |
| 208 | if(!key_bytes) { |
| 209 | LDNS_FREE(algorithm_name); |
| 210 | ldns_buffer_free(data_buffer); |
| 211 | return LDNS_STATUS_MEM_ERR; |
| 212 | } |
| 213 | key_size = ldns_b64_pton(key_data, key_bytes, |
| 214 | ldns_b64_pton_calculate_size(strlen(key_data))); |
| 215 | if (key_size < 0) { |
| 216 | LDNS_FREE(algorithm_name); |
| 217 | LDNS_FREE(key_bytes); |
| 218 | ldns_buffer_free(data_buffer); |
| 219 | /* LDNS_STATUS_INVALID_B64 */ |
| 220 | return LDNS_STATUS_INVALID_B64; |
| 221 | } |
| 222 | /* hmac it */ |
| 223 | /* 2 spare bytes for the length */ |
| 224 | mac_bytes = LDNS_XMALLOC(unsigned char, md_len+2); |
| 225 | if(!mac_bytes) { |
| 226 | LDNS_FREE(algorithm_name); |
| 227 | LDNS_FREE(key_bytes); |
| 228 | ldns_buffer_free(data_buffer); |
| 229 | return LDNS_STATUS_MEM_ERR; |
| 230 | } |
| 231 | memset(mac_bytes, 0, md_len+2); |
| 232 | |
| 233 | digester = ldns_digest_function(algorithm_name); |
| 234 | |
| 235 | if (digester) { |
| 236 | (void) HMAC(digester, key_bytes, key_size, (void *)wireformat, |
| 237 | (size_t) wiresize, mac_bytes + 2, &md_len); |
| 238 | |
| 239 | ldns_write_uint16(mac_bytes, md_len); |
| 240 | result = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT16_DATA, md_len + 2, |
| 241 | mac_bytes); |
| 242 | } else { |
| 243 | LDNS_FREE(algorithm_name); |
| 244 | LDNS_FREE(mac_bytes); |
| 245 | LDNS_FREE(key_bytes); |
| 246 | ldns_buffer_free(data_buffer); |
| 247 | return LDNS_STATUS_CRYPTO_UNKNOWN_ALGO; |
| 248 | } |
| 249 | |
| 250 | LDNS_FREE(algorithm_name); |
| 251 | LDNS_FREE(mac_bytes); |
| 252 | LDNS_FREE(key_bytes); |
| 253 | ldns_buffer_free(data_buffer); |
| 254 | |
| 255 | *tsig_mac = result; |
| 256 | |
| 257 | return LDNS_STATUS_OK; |
| 258 | } |
| 259 | #endif /* HAVE_SSL */ |
| 260 | |
| 261 | |
| 262 | #ifdef HAVE_SSL |
| 263 | bool |
| 264 | ldns_pkt_tsig_verify(ldns_pkt *pkt, uint8_t *wire, size_t wirelen, const char *key_name, |
| 265 | const char *key_data, ldns_rdf *orig_mac_rdf) |
| 266 | { |
| 267 | return ldns_pkt_tsig_verify_next(pkt, wire, wirelen, key_name, key_data, orig_mac_rdf, 0); |
| 268 | } |
| 269 | |
| 270 | bool |
| 271 | ldns_pkt_tsig_verify_next(ldns_pkt *pkt, uint8_t *wire, size_t wirelen, const char* key_name, |
| 272 | const char *key_data, ldns_rdf *orig_mac_rdf, int tsig_timers_only) |
| 273 | { |
| 274 | ldns_rdf *fudge_rdf; |
| 275 | ldns_rdf *algorithm_rdf; |
| 276 | ldns_rdf *time_signed_rdf; |
| 277 | ldns_rdf *orig_id_rdf; |
| 278 | ldns_rdf *error_rdf; |
| 279 | ldns_rdf *other_data_rdf; |
| 280 | ldns_rdf *pkt_mac_rdf; |
| 281 | ldns_rdf *my_mac_rdf; |
| 282 | ldns_rdf *key_name_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, key_name); |
| 283 | uint16_t pkt_id, orig_pkt_id; |
| 284 | ldns_status status; |
| 285 | |
| 286 | uint8_t *prepared_wire = NULL; |
| 287 | size_t prepared_wire_size = 0; |
| 288 | |
| 289 | ldns_rr *orig_tsig = ldns_pkt_tsig(pkt); |
| 290 | |
| 291 | if (!orig_tsig) { |
| 292 | ldns_rdf_deep_free(key_name_rdf); |
| 293 | return false; |
| 294 | } |
| 295 | algorithm_rdf = ldns_rr_rdf(orig_tsig, 0); |
| 296 | time_signed_rdf = ldns_rr_rdf(orig_tsig, 1); |
| 297 | fudge_rdf = ldns_rr_rdf(orig_tsig, 2); |
| 298 | pkt_mac_rdf = ldns_rr_rdf(orig_tsig, 3); |
| 299 | orig_id_rdf = ldns_rr_rdf(orig_tsig, 4); |
| 300 | error_rdf = ldns_rr_rdf(orig_tsig, 5); |
| 301 | other_data_rdf = ldns_rr_rdf(orig_tsig, 6); |
| 302 | |
| 303 | /* remove temporarily */ |
| 304 | ldns_pkt_set_tsig(pkt, NULL); |
| 305 | /* temporarily change the id to the original id */ |
| 306 | pkt_id = ldns_pkt_id(pkt); |
| 307 | orig_pkt_id = ldns_rdf2native_int16(orig_id_rdf); |
| 308 | ldns_pkt_set_id(pkt, orig_pkt_id); |
| 309 | |
| 310 | prepared_wire = ldns_tsig_prepare_pkt_wire(wire, wirelen, &prepared_wire_size); |
| 311 | |
| 312 | status = ldns_tsig_mac_new(&my_mac_rdf, prepared_wire, prepared_wire_size, |
| 313 | key_data, key_name_rdf, fudge_rdf, algorithm_rdf, |
| 314 | time_signed_rdf, error_rdf, other_data_rdf, orig_mac_rdf, tsig_timers_only); |
| 315 | |
| 316 | LDNS_FREE(prepared_wire); |
| 317 | |
| 318 | if (status != LDNS_STATUS_OK) { |
| 319 | ldns_rdf_deep_free(key_name_rdf); |
| 320 | return false; |
| 321 | } |
| 322 | /* Put back the values */ |
| 323 | ldns_pkt_set_tsig(pkt, orig_tsig); |
| 324 | ldns_pkt_set_id(pkt, pkt_id); |
| 325 | |
| 326 | ldns_rdf_deep_free(key_name_rdf); |
| 327 | |
| 328 | if (ldns_rdf_compare(pkt_mac_rdf, my_mac_rdf) == 0) { |
| 329 | ldns_rdf_deep_free(my_mac_rdf); |
| 330 | return true; |
| 331 | } else { |
| 332 | ldns_rdf_deep_free(my_mac_rdf); |
| 333 | return false; |
| 334 | } |
| 335 | } |
| 336 | #endif /* HAVE_SSL */ |
| 337 | |
| 338 | #ifdef HAVE_SSL |
| 339 | ldns_status |
| 340 | ldns_pkt_tsig_sign(ldns_pkt *pkt, const char *key_name, const char *key_data, |
| 341 | uint16_t fudge, const char *algorithm_name, ldns_rdf *query_mac) |
| 342 | { |
| 343 | return ldns_pkt_tsig_sign_next(pkt, key_name, key_data, fudge, algorithm_name, query_mac, 0); |
| 344 | } |
| 345 | |
| 346 | ldns_status |
| 347 | ldns_pkt_tsig_sign_next(ldns_pkt *pkt, const char *key_name, const char *key_data, |
| 348 | uint16_t fudge, const char *algorithm_name, ldns_rdf *query_mac, int tsig_timers_only) |
| 349 | { |
| 350 | ldns_rr *tsig_rr; |
| 351 | ldns_rdf *key_name_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, key_name); |
| 352 | ldns_rdf *fudge_rdf = NULL; |
| 353 | ldns_rdf *orig_id_rdf = NULL; |
| 354 | ldns_rdf *algorithm_rdf; |
| 355 | ldns_rdf *error_rdf = NULL; |
| 356 | ldns_rdf *mac_rdf = NULL; |
| 357 | ldns_rdf *other_data_rdf = NULL; |
| 358 | |
| 359 | ldns_status status = LDNS_STATUS_OK; |
| 360 | |
| 361 | uint8_t *pkt_wire = NULL; |
| 362 | size_t pkt_wire_len; |
| 363 | |
| 364 | struct timeval tv_time_signed; |
| 365 | uint8_t *time_signed = NULL; |
| 366 | ldns_rdf *time_signed_rdf = NULL; |
| 367 | |
| 368 | algorithm_rdf = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, algorithm_name); |
| 369 | if(!key_name_rdf || !algorithm_rdf) { |
| 370 | status = LDNS_STATUS_MEM_ERR; |
| 371 | goto clean; |
| 372 | } |
| 373 | |
| 374 | /* eww don't have create tsigtime rdf yet :( */ |
| 375 | /* bleh :p */ |
| 376 | if (gettimeofday(&tv_time_signed, NULL) == 0) { |
| 377 | time_signed = LDNS_XMALLOC(uint8_t, 6); |
| 378 | if(!time_signed) { |
| 379 | status = LDNS_STATUS_MEM_ERR; |
| 380 | goto clean; |
| 381 | } |
| 382 | ldns_write_uint64_as_uint48(time_signed, |
| 383 | (uint64_t)tv_time_signed.tv_sec); |
| 384 | } else { |
| 385 | status = LDNS_STATUS_INTERNAL_ERR; |
| 386 | goto clean; |
| 387 | } |
| 388 | |
| 389 | time_signed_rdf = ldns_rdf_new(LDNS_RDF_TYPE_TSIGTIME, 6, time_signed); |
| 390 | if(!time_signed_rdf) { |
| 391 | LDNS_FREE(time_signed); |
| 392 | status = LDNS_STATUS_MEM_ERR; |
| 393 | goto clean; |
| 394 | } |
| 395 | |
| 396 | fudge_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, fudge); |
| 397 | |
| 398 | orig_id_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, ldns_pkt_id(pkt)); |
| 399 | |
| 400 | error_rdf = ldns_native2rdf_int16(LDNS_RDF_TYPE_INT16, 0); |
| 401 | |
| 402 | other_data_rdf = ldns_native2rdf_int16_data(0, NULL); |
| 403 | |
| 404 | if(!fudge_rdf || !orig_id_rdf || !error_rdf || !other_data_rdf) { |
| 405 | status = LDNS_STATUS_MEM_ERR; |
| 406 | goto clean; |
| 407 | } |
| 408 | |
| 409 | if (ldns_pkt2wire(&pkt_wire, pkt, &pkt_wire_len) != LDNS_STATUS_OK) { |
| 410 | status = LDNS_STATUS_ERR; |
| 411 | goto clean; |
| 412 | } |
| 413 | |
| 414 | status = ldns_tsig_mac_new(&mac_rdf, pkt_wire, pkt_wire_len, |
| 415 | key_data, key_name_rdf, fudge_rdf, algorithm_rdf, |
| 416 | time_signed_rdf, error_rdf, other_data_rdf, query_mac, tsig_timers_only); |
| 417 | |
| 418 | if (!mac_rdf) { |
| 419 | goto clean; |
| 420 | } |
| 421 | |
| 422 | LDNS_FREE(pkt_wire); |
| 423 | |
| 424 | /* Create the TSIG RR */ |
| 425 | tsig_rr = ldns_rr_new(); |
| 426 | if(!tsig_rr) { |
| 427 | status = LDNS_STATUS_MEM_ERR; |
| 428 | goto clean; |
| 429 | } |
| 430 | ldns_rr_set_owner(tsig_rr, key_name_rdf); |
| 431 | ldns_rr_set_class(tsig_rr, LDNS_RR_CLASS_ANY); |
| 432 | ldns_rr_set_type(tsig_rr, LDNS_RR_TYPE_TSIG); |
| 433 | ldns_rr_set_ttl(tsig_rr, 0); |
| 434 | |
| 435 | ldns_rr_push_rdf(tsig_rr, algorithm_rdf); |
| 436 | ldns_rr_push_rdf(tsig_rr, time_signed_rdf); |
| 437 | ldns_rr_push_rdf(tsig_rr, fudge_rdf); |
| 438 | ldns_rr_push_rdf(tsig_rr, mac_rdf); |
| 439 | ldns_rr_push_rdf(tsig_rr, orig_id_rdf); |
| 440 | ldns_rr_push_rdf(tsig_rr, error_rdf); |
| 441 | ldns_rr_push_rdf(tsig_rr, other_data_rdf); |
| 442 | |
| 443 | ldns_pkt_set_tsig(pkt, tsig_rr); |
| 444 | |
| 445 | return status; |
| 446 | |
| 447 | clean: |
| 448 | LDNS_FREE(pkt_wire); |
| 449 | ldns_rdf_free(key_name_rdf); |
| 450 | ldns_rdf_free(algorithm_rdf); |
| 451 | ldns_rdf_free(time_signed_rdf); |
| 452 | ldns_rdf_free(fudge_rdf); |
| 453 | ldns_rdf_free(orig_id_rdf); |
| 454 | ldns_rdf_free(error_rdf); |
| 455 | ldns_rdf_free(other_data_rdf); |
| 456 | return status; |
| 457 | } |
| 458 | #endif /* HAVE_SSL */ |