1 /* towire.c 2 * 3 * Copyright (c) 2018-2021 Apple, Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * DNS to-wire wire-format functions. 18 * 19 * These are really simple functions for constructing DNS messages in wire format. 20 * The flow is that there is a transaction structure which contains pointers to both 21 * a message output buffer and a response input buffer. The structure is initialized, 22 * and then the various wire format functions are called repeatedly to store data. 23 * If an error occurs during this process, it's okay to just keep going, because the 24 * error is recorded in the transaction; once all of the copy-in functions have been 25 * called, the error status can be checked once at the end. 26 */ 27 28 #include <stdio.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <errno.h> 32 #ifndef THREAD_DEVKIT_ADK 33 #include <arpa/inet.h> 34 #endif 35 #include <stdlib.h> 36 37 #include "srp.h" 38 #include "dns-msg.h" 39 #include "srp-crypto.h" 40 41 #ifndef NO_CLOCK 42 #include <sys/time.h> 43 #endif 44 45 static int 46 dns_parse_label(const char *cur, const char *NONNULL *NONNULL nextp, uint8_t *NONNULL lenp, uint8_t *NONNULL buf, 47 ssize_t max) 48 { 49 const char *end; 50 int tlen; 51 const char *s; 52 uint8_t *t; 53 54 end = strchr(cur, '.'); 55 if (end == NULL) { 56 end = cur + strlen(cur); 57 if (end == cur) { 58 *lenp = 0; 59 *nextp = NULL; 60 return 0; 61 } 62 *nextp = NULL; 63 } else { 64 if (end == cur) { 65 return EINVAL; 66 } 67 *nextp = end + 1; 68 } 69 70 // Figure out the length of the label after escapes have been converted. 71 tlen = 0; 72 for (s = cur; s < end; s++) { 73 if (*s == '\\') { 74 if (s + 4 <= end) { 75 tlen++; 76 s += 3; 77 } else { 78 tlen++; 79 } 80 } else { 81 tlen++; 82 } 83 } 84 85 // Is there no space? 86 87 if (tlen >= max) { 88 return ENOBUFS; 89 } 90 91 // Is the label too long? 92 if (end - cur > DNS_MAX_LABEL_SIZE) { 93 return ENAMETOOLONG; 94 } 95 96 // Store the label length 97 *lenp = (uint8_t)(tlen); 98 99 // Store the label. 100 t = buf; 101 for (s = cur; s < end; s++) { 102 if (*s == '\\') { 103 if (s + 4 <= end) { 104 int v0 = s[1] - '0'; 105 int v1 = s[2] - '0'; 106 int v2 = s[3] - '0'; 107 int val = v0 * 100 + v1 * 10 + v2; 108 if (val < 255) { 109 *t++ = (uint8_t)val; 110 s += 3; 111 } else { 112 return EINVAL; 113 } 114 } else { 115 return EINVAL; 116 } 117 } else { 118 *t++ = (uint8_t)*s; 119 } 120 } 121 return 0; 122 } 123 124 // Convert a name to wire format. Does not store the root label (0) at the end. Does not support binary labels. 125 void 126 dns_name_to_wire_(dns_name_pointer_t *NULLABLE r_pointer, dns_towire_state_t *NONNULL txn, 127 const char *NONNULL name, int line) 128 { 129 const char *next, *cur; 130 int status; 131 dns_name_pointer_t np; 132 133 if (!txn->error) { 134 memset(&np, 0, sizeof np); 135 np.message_start = (uint8_t *)txn->message; 136 np.name_start = txn->p; 137 138 cur = name; 139 do { 140 // Note that nothing is stored through txn->p until dns_name_parse has verified that 141 // there is space in the buffer for the label as well as the length. 142 status = dns_parse_label(cur, &next, txn->p, txn->p + 1, txn->lim - txn->p - 1); 143 if (status) { 144 if (status == ENOBUFS) { 145 txn->truncated = true; 146 } 147 txn->error = (unsigned)status; 148 txn->line = line; 149 return; 150 } 151 152 // Don't use the root label if it was parsed. 153 if (*txn->p != 0) { 154 np.num_labels++; 155 np.length += 1 + *txn->p; 156 txn->p = txn->p + *txn->p + 1; 157 cur = next; 158 } 159 } while (next != NULL); 160 161 if (np.length > DNS_MAX_NAME_SIZE) { 162 txn->error = ENAMETOOLONG; 163 txn->line = line; 164 return; 165 } 166 if (r_pointer != NULL) { 167 *r_pointer = np; 168 } 169 } 170 } 171 172 // Like dns_name_to_wire, but includes the root label at the end. 173 void 174 dns_full_name_to_wire_(dns_name_pointer_t *NULLABLE r_pointer, dns_towire_state_t *NONNULL txn, 175 const char *NONNULL name, int line) 176 { 177 dns_name_pointer_t np; 178 if (!txn->error) { 179 memset(&np, 0, sizeof np); 180 dns_name_to_wire(&np, txn, name); 181 if (!txn->error) { 182 if (txn->p + 1 >= txn->lim) { 183 txn->error = ENOBUFS; 184 txn->truncated = true; 185 txn->line = line; 186 return; 187 } 188 *txn->p++ = 0; 189 np.num_labels++; 190 np.length += 1; 191 if (np.length > DNS_MAX_NAME_SIZE) { 192 txn->error = ENAMETOOLONG; 193 txn->line = line; 194 return; 195 } 196 if (r_pointer) { 197 *r_pointer = np; 198 } 199 } 200 } 201 } 202 203 // Store a pointer to a name that's already in the message. 204 void 205 dns_pointer_to_wire_(dns_name_pointer_t *NULLABLE r_pointer, dns_towire_state_t *NONNULL txn, 206 dns_name_pointer_t *NONNULL pointer, int line) 207 { 208 if (!txn->error) { 209 uint16_t offset = (uint16_t)(pointer->name_start - pointer->message_start); 210 if (offset > DNS_MAX_POINTER) { 211 txn->error = ETOOMANYREFS; 212 txn->line = line; 213 return; 214 } 215 if (txn->p + 2 >= txn->lim) { 216 txn->error = ENOBUFS; 217 txn->truncated = true; 218 txn->line = line; 219 return; 220 } 221 *txn->p++ = 0xc0 | (offset >> 8); 222 *txn->p++ = offset & 0xff; 223 if (r_pointer) { 224 r_pointer->num_labels += pointer->num_labels; 225 r_pointer->length += pointer->length + 1; 226 if (r_pointer->length > DNS_MAX_NAME_SIZE) { 227 txn->error = ENAMETOOLONG; 228 txn->line = line; 229 return; 230 } 231 } 232 } 233 } 234 235 void 236 dns_u8_to_wire_(dns_towire_state_t *NONNULL txn, uint8_t val, int line) 237 { 238 if (!txn->error) { 239 if (txn->p + 1 >= txn->lim) { 240 txn->error = ENOBUFS; 241 txn->truncated = true; 242 txn->line = line; 243 return; 244 } 245 *txn->p++ = val; 246 } 247 } 248 249 // Store a 16-bit integer in network byte order 250 void 251 dns_u16_to_wire_(dns_towire_state_t *NONNULL txn, uint16_t val, int line) 252 { 253 if (!txn->error) { 254 if (txn->p + 2 >= txn->lim) { 255 txn->error = ENOBUFS; 256 txn->truncated = true; 257 txn->line = line; 258 return; 259 } 260 *txn->p++ = val >> 8; 261 *txn->p++ = val & 0xff; 262 } 263 } 264 265 void 266 dns_u32_to_wire_(dns_towire_state_t *NONNULL txn, uint32_t val, int line) 267 { 268 if (!txn->error) { 269 if (txn->p + 4 >= txn->lim) { 270 txn->error = ENOBUFS; 271 txn->truncated = true; 272 txn->line = line; 273 return; 274 } 275 *txn->p++ = val >> 24; 276 *txn->p++ = (val >> 16) & 0xff; 277 *txn->p++ = (val >> 8) & 0xff; 278 *txn->p++ = val & 0xff; 279 } 280 } 281 282 void 283 dns_u64_to_wire_(dns_towire_state_t *NONNULL txn, uint64_t val, int line) 284 { 285 if (!txn->error) { 286 if (txn->p + 8 >= txn->lim) { 287 txn->error = ENOBUFS; 288 txn->truncated = true; 289 txn->line = line; 290 return; 291 } 292 *txn->p++ = val >> 56; 293 *txn->p++ = (val >> 48) & 0xff; 294 *txn->p++ = (val >> 40) & 0xff; 295 *txn->p++ = (val >> 32) & 0xff; 296 *txn->p++ = (val >> 24) & 0xff; 297 *txn->p++ = (val >> 16) & 0xff; 298 *txn->p++ = (val >> 8) & 0xff; 299 *txn->p++ = val & 0xff; 300 } 301 } 302 303 void 304 dns_ttl_to_wire_(dns_towire_state_t *NONNULL txn, int32_t val, int line) 305 { 306 if (!txn->error) { 307 dns_u32_to_wire_(txn, (uint32_t)val, line); 308 } 309 } 310 311 void 312 dns_rdlength_begin_(dns_towire_state_t *NONNULL txn, int line) 313 { 314 if (!txn->error) { 315 if (txn->p + 2 >= txn->lim) { 316 txn->error = ENOBUFS; 317 txn->truncated = true; 318 txn->line = line; 319 return; 320 } 321 if (txn->p_rdlength != NULL) { 322 txn->error = EINVAL; 323 txn->line = line; 324 return; 325 } 326 txn->p_rdlength = txn->p; 327 txn->p += 2; 328 } 329 } 330 331 void 332 dns_rdlength_end_(dns_towire_state_t *NONNULL txn, int line) 333 { 334 ssize_t rdlength; 335 if (!txn->error) { 336 if (txn->p_rdlength == NULL) { 337 txn->error = EINVAL; 338 txn->line = line; 339 return; 340 } 341 rdlength = txn->p - txn->p_rdlength - 2; 342 txn->p_rdlength[0] = (uint8_t)(rdlength >> 8); 343 txn->p_rdlength[1] = (uint8_t)(rdlength & 0xff); 344 txn->p_rdlength = NULL; 345 } 346 } 347 348 #ifndef THREAD_DEVKIT_ADK 349 void 350 dns_rdata_a_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL ip_address, int line) 351 { 352 if (!txn->error) { 353 if (txn->p + 4 >= txn->lim) { 354 txn->error = ENOBUFS; 355 txn->truncated = true; 356 txn->line = line; 357 return; 358 } 359 if (!inet_pton(AF_INET, ip_address, txn->p)) { 360 txn->error = EINVAL; 361 txn->line = line; 362 return; 363 } 364 txn->p += 4; 365 } 366 } 367 368 void 369 dns_rdata_aaaa_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL ip_address, int line) 370 { 371 if (!txn->error) { 372 if (txn->p + 16 >= txn->lim) { 373 txn->error = ENOBUFS; 374 txn->truncated = true; 375 txn->line = line; 376 return; 377 } 378 if (!inet_pton(AF_INET6, ip_address, txn->p)) { 379 txn->error = EINVAL; 380 txn->line = line; 381 return; 382 } 383 txn->p += 16; 384 } 385 } 386 #endif 387 388 uint16_t 389 dns_rdata_key_to_wire_(dns_towire_state_t *NONNULL txn, unsigned key_type, unsigned name_type, 390 uint8_t signatory, srp_key_t *key, int line) 391 { 392 size_t key_len = srp_pubkey_length(key), copied_len; 393 uint8_t *rdata = txn->p; 394 uint32_t key_tag; 395 int i; 396 ssize_t rdlen; 397 398 if (!txn->error) { 399 if (key_type > 3 || name_type > 3 || signatory > 15) { 400 txn->error = EINVAL; 401 txn->line = line; 402 return 0; 403 } 404 if (txn->p + key_len + 4 >= txn->lim) { 405 txn->error = ENOBUFS; 406 txn->truncated = true; 407 txn->line = line; 408 return 0; 409 } 410 *txn->p++ = (uint8_t)((key_type << 6) | name_type); 411 *txn->p++ = signatory; 412 *txn->p++ = 3; // protocol type is always 3 413 *txn->p++ = srp_key_algorithm(key); 414 copied_len = srp_pubkey_copy(txn->p, key_len, key); 415 if (copied_len == 0) { 416 txn->error = EINVAL; 417 txn->line = line; 418 return 0; 419 } 420 txn->p += key_len; 421 } 422 rdlen = txn->p - rdata; 423 424 // Compute the key tag 425 key_tag = 0; 426 for (i = 0; i < rdlen; i++) { 427 key_tag += (i & 1) ? rdata[i] : (uint16_t)(rdata[i] << 8); 428 } 429 key_tag += (key_tag >> 16) & 0xFFFF; 430 return (uint16_t)(key_tag & 0xFFFF); 431 } 432 433 void 434 dns_rdata_txt_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL txt_record, int line) 435 { 436 if (!txn->error) { 437 size_t len = strlen(txt_record); 438 if (txn->p + len + 1 >= txn->lim) { 439 txn->error = ENOBUFS; 440 txn->truncated = true; 441 txn->line = line; 442 return; 443 } 444 if (len > 255) { 445 txn->error = ENAMETOOLONG; 446 txn->line = line; 447 return; 448 } 449 *txn->p++ = (uint8_t)len; 450 memcpy(txn->p, txt_record, len); 451 txn->p += len; 452 } 453 } 454 455 void 456 dns_rdata_raw_data_to_wire_(dns_towire_state_t *NONNULL txn, const void *NONNULL raw_data, size_t length, int line) 457 { 458 if (!txn->error) { 459 if (txn->p + length > txn->lim) { 460 txn->error = ENOBUFS; 461 txn->truncated = true; 462 txn->line = line; 463 return; 464 } 465 memcpy(txn->p, raw_data, length); 466 txn->p += length; 467 } 468 } 469 470 void 471 dns_edns0_header_to_wire_(dns_towire_state_t *NONNULL txn, uint16_t mtu, uint8_t xrcode, uint8_t version, bool DO, int line) 472 { 473 if (!txn->error) { 474 if (txn->p + 9 >= txn->lim) { 475 txn->error = ENOBUFS; 476 txn->truncated = true; 477 txn->line = line; 478 return; 479 } 480 *txn->p++ = 0; // root label 481 dns_u16_to_wire(txn, dns_rrtype_opt); 482 dns_u16_to_wire(txn, mtu); 483 *txn->p++ = xrcode; 484 *txn->p++ = version; 485 *txn->p++ = DO ? 1 << 7 : 0; // flags (usb) 486 *txn->p++ = 0; // flags (lsb, mbz) 487 } 488 } 489 490 void 491 dns_edns0_option_begin_(dns_towire_state_t *NONNULL txn, int line) 492 { 493 if (!txn->error) { 494 if (txn->p + 2 >= txn->lim) { 495 txn->error = ENOBUFS; 496 txn->truncated = true; 497 txn->line = line; 498 return; 499 } 500 if (txn->p_opt != NULL) { 501 txn->error = EINVAL; 502 txn->line = line; 503 return; 504 } 505 txn->p_opt = txn->p; 506 txn->p += 2; 507 } 508 } 509 510 void 511 dns_edns0_option_end_(dns_towire_state_t *NONNULL txn, int line) 512 { 513 ssize_t opt_length; 514 if (!txn->error) { 515 if (txn->p_opt == NULL) { 516 txn->error = EINVAL; 517 txn->line = line; 518 return; 519 } 520 opt_length = txn->p - txn->p_opt - 2; 521 txn->p_opt[0] = (uint8_t)(opt_length >> 8); 522 txn->p_opt[1] = opt_length & 0xff; 523 txn->p_opt = NULL; 524 } 525 } 526 527 void 528 dns_sig0_signature_to_wire_(dns_towire_state_t *NONNULL txn, srp_key_t *key, uint16_t key_tag, 529 dns_name_pointer_t *NONNULL signer, const char *NONNULL signer_hostname, 530 const char *NONNULL signer_domain, uint32_t timenow, int line) 531 { 532 size_t siglen = srp_signature_length(key); 533 uint8_t *start, *p_signer, *p_signature, *rrstart = txn->p; 534 535 // 1 name (root) 536 // 2 type (SIG) 537 // 2 class (255) ANY 538 // 4 TTL (0) 539 // 18 SIG RDATA up to signer name 540 // 2 signer name (always a pointer) 541 // 29 bytes so far 542 // signature data (depends on algorithm, e.g. 64 for ECDSASHA256) 543 // so e.g. 93 bytes total 544 545 if (!txn->error) { 546 dns_u8_to_wire(txn, 0); // root label 547 dns_u16_to_wire(txn, dns_rrtype_sig); 548 dns_u16_to_wire(txn, dns_qclass_any); // class 549 dns_ttl_to_wire(txn, 0); // SIG RR TTL 550 dns_rdlength_begin(txn); 551 start = txn->p; 552 dns_u16_to_wire(txn, 0); // type = 0 for transaction signature 553 dns_u8_to_wire(txn, srp_key_algorithm(key)); 554 dns_u8_to_wire(txn, 0); // labels field doesn't apply for transaction signature 555 dns_ttl_to_wire(txn, 0); // original ttl doesn't apply 556 // If timenow is <300, it's either just after the epoch, or the caller doesn't know what time it is. 557 if (timenow < 300) { 558 dns_u32_to_wire(txn, 0); // Indicate that we have no clock: set expiry and inception times to zero 559 dns_u32_to_wire(txn, 0); 560 } else { 561 dns_u32_to_wire(txn, timenow + 300); // signature expiration time is five minutes from now 562 dns_u32_to_wire(txn, timenow - 300); // signature inception time, five minutes in the past 563 } 564 dns_u16_to_wire(txn, key_tag); 565 566 p_signer = txn->p; 567 // We store the name in uncompressed form because that's what we have to sign 568 if (signer_hostname != NULL) { 569 dns_name_to_wire(NULL, txn, signer_hostname); 570 } 571 dns_full_name_to_wire(NULL, txn, signer_domain); 572 // And that means we're going to have to copy the signature back earlier in the packet. 573 p_signature = txn->p; 574 575 // Sign the message, signature RRDATA (less signature) first. 576 if (!srp_sign(txn->p, siglen, (uint8_t *)txn->message, (size_t)(rrstart - (uint8_t *)txn->message), 577 start, (size_t)(txn->p - start), key)) { 578 txn->error = true; 579 txn->line = __LINE__; 580 } else { 581 // Now that it's signed, back up and store the pointer to the name, because we're trying 582 // to be as compact as possible. 583 txn->p = p_signer; 584 dns_pointer_to_wire(NULL, txn, signer); // Pointer to the owner name the key is attached to 585 // And move the signature earlier in the packet. 586 memmove(txn->p, p_signature, siglen); 587 588 txn->p += siglen; 589 dns_rdlength_end(txn); 590 } 591 592 if (txn->error) { 593 txn->outer_line = line; 594 } 595 } 596 } 597 598 // Local Variables: 599 // mode: C 600 // tab-width: 4 601 // c-file-style: "bsd" 602 // c-basic-offset: 4 603 // fill-column: 108 604 // indent-tabs-mode: nil 605 // End: 606