Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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