1 /* towire.c
2  *
3  * Copyright (c) 2018-2019 Apple Computer, 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
dns_parse_label(const char * cur,const char * NONNULL * NONNULL nextp,uint8_t * NONNULL lenp,uint8_t * NONNULL buf,ssize_t max)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++ = val;
110                     s += 3;
111                 } else {
112                     return EINVAL;
113                 }
114             } else {
115                 return EINVAL;
116             }
117         } else {
118             *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
dns_name_to_wire_(dns_name_pointer_t * NULLABLE r_pointer,dns_towire_state_t * NONNULL txn,const char * NONNULL name,int line)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 = 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
dns_full_name_to_wire_(dns_name_pointer_t * NULLABLE r_pointer,dns_towire_state_t * NONNULL txn,const char * NONNULL name,int line)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
dns_pointer_to_wire_(dns_name_pointer_t * NULLABLE r_pointer,dns_towire_state_t * NONNULL txn,dns_name_pointer_t * NONNULL pointer,int line)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 = 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
dns_u8_to_wire_(dns_towire_state_t * NONNULL txn,uint8_t val,int line)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
dns_u16_to_wire_(dns_towire_state_t * NONNULL txn,uint16_t val,int line)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
dns_u32_to_wire_(dns_towire_state_t * NONNULL txn,uint32_t val,int line)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
dns_ttl_to_wire_(dns_towire_state_t * NONNULL txn,int32_t val,int line)283 dns_ttl_to_wire_(dns_towire_state_t *NONNULL txn, int32_t val, int line)
284 {
285     if (!txn->error) {
286         dns_u32_to_wire_(txn, (uint32_t)val, line);
287     }
288 }
289 
290 void
dns_rdlength_begin_(dns_towire_state_t * NONNULL txn,int line)291 dns_rdlength_begin_(dns_towire_state_t *NONNULL txn, int line)
292 {
293     if (!txn->error) {
294         if (txn->p + 2 >= txn->lim) {
295             txn->error = ENOBUFS;
296             txn->truncated = true;
297             txn->line = line;
298             return;
299         }
300         if (txn->p_rdlength != NULL) {
301             txn->error = EINVAL;
302             txn->line = line;
303             return;
304         }
305         txn->p_rdlength = txn->p;
306         txn->p += 2;
307     }
308 }
309 
310 void
dns_rdlength_end_(dns_towire_state_t * NONNULL txn,int line)311 dns_rdlength_end_(dns_towire_state_t *NONNULL txn, int line)
312 {
313     ssize_t rdlength;
314     if (!txn->error) {
315         if (txn->p_rdlength == NULL) {
316             txn->error = EINVAL;
317             txn->line = line;
318             return;
319         }
320         rdlength = txn->p - txn->p_rdlength - 2;
321         txn->p_rdlength[0] = rdlength >> 8;
322         txn->p_rdlength[1] = rdlength & 0xff;
323         txn->p_rdlength = NULL;
324     }
325 }
326 
327 #ifndef THREAD_DEVKIT_ADK
328 void
dns_rdata_a_to_wire_(dns_towire_state_t * NONNULL txn,const char * NONNULL ip_address,int line)329 dns_rdata_a_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL ip_address, int line)
330 {
331     if (!txn->error) {
332         if (txn->p + 4 >= txn->lim) {
333             txn->error = ENOBUFS;
334             txn->truncated = true;
335             txn->line = line;
336             return;
337         }
338         if (!inet_pton(AF_INET, ip_address, txn->p)) {
339             txn->error = EINVAL;
340             txn->line = line;
341             return;
342         }
343         txn->p += 4;
344     }
345 }
346 
347 void
dns_rdata_aaaa_to_wire_(dns_towire_state_t * NONNULL txn,const char * NONNULL ip_address,int line)348 dns_rdata_aaaa_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL ip_address, int line)
349 {
350     if (!txn->error) {
351         if (txn->p + 16 >= txn->lim) {
352             txn->error = ENOBUFS;
353             txn->truncated = true;
354             txn->line = line;
355             return;
356         }
357         if (!inet_pton(AF_INET6, ip_address, txn->p)) {
358             txn->error = EINVAL;
359             txn->line = line;
360             return;
361         }
362         txn->p += 16;
363     }
364 }
365 #endif
366 
367 uint16_t
dns_rdata_key_to_wire_(dns_towire_state_t * NONNULL txn,unsigned key_type,unsigned name_type,unsigned signatory,srp_key_t * key,int line)368 dns_rdata_key_to_wire_(dns_towire_state_t *NONNULL txn, unsigned key_type, unsigned name_type,
369                        unsigned signatory, srp_key_t *key, int line)
370 {
371     ssize_t key_len = srp_pubkey_length(key), copied_len;
372     uint8_t *rdata = txn->p;
373     uint32_t key_tag;
374     int i;
375     ssize_t rdlen;
376 
377     if (!txn->error) {
378         if (key_type > 3 || name_type > 3 || signatory > 15) {
379             txn->error = EINVAL;
380             txn->line = line;
381             return 0;
382         }
383         if (txn->p + key_len + 4 >= txn->lim) {
384             txn->error = ENOBUFS;
385             txn->truncated = true;
386             txn->line = line;
387             return 0;
388         }
389         *txn->p++ = (key_type << 6) | name_type;
390         *txn->p++ = signatory;
391         *txn->p++ = 3; // protocol type is always 3
392         *txn->p++ = srp_key_algorithm(key);
393         copied_len = srp_pubkey_copy(txn->p, key_len, key);
394         if (copied_len == 0) {
395             txn->error = EINVAL;
396             txn->line = line;
397             return 0;
398         }
399         txn->p += key_len;
400     }
401     rdlen = txn->p - rdata;
402 
403     // Compute the key tag
404     key_tag = 0;
405     for (i = 0; i < rdlen; i++) {
406         key_tag += (i & 1) ? rdata[i] : rdata[i] << 8;
407     }
408     key_tag += (key_tag >> 16) & 0xFFFF;
409     return (uint16_t)(key_tag & 0xFFFF);
410 }
411 
412 void
dns_rdata_txt_to_wire_(dns_towire_state_t * NONNULL txn,const char * NONNULL txt_record,int line)413 dns_rdata_txt_to_wire_(dns_towire_state_t *NONNULL txn, const char *NONNULL txt_record, int line)
414 {
415     if (!txn->error) {
416         ssize_t len = strlen(txt_record);
417         if (txn->p + len + 1 >= txn->lim) {
418             txn->error = ENOBUFS;
419             txn->truncated = true;
420             txn->line = line;
421             return;
422         }
423         if (len > 255) {
424             txn->error = ENAMETOOLONG;
425             txn->line = line;
426             return;
427         }
428         *txn->p++ = (uint8_t)len;
429         memcpy(txn->p, txt_record, len);
430         txn->p += len;
431     }
432 }
433 
434 void
dns_rdata_raw_data_to_wire_(dns_towire_state_t * NONNULL txn,const void * NONNULL raw_data,size_t length,int line)435 dns_rdata_raw_data_to_wire_(dns_towire_state_t *NONNULL txn, const void *NONNULL raw_data, size_t length, int line)
436 {
437     if (!txn->error) {
438         if (txn->p + length >= txn->lim) {
439             txn->error = ENOBUFS;
440             txn->truncated = true;
441             txn->line = line;
442             return;
443         }
444         memcpy(txn->p, raw_data, length);
445         txn->p += length;
446     }
447 }
448 
449 void
dns_edns0_header_to_wire_(dns_towire_state_t * NONNULL txn,int mtu,int xrcode,int version,int DO,int line)450 dns_edns0_header_to_wire_(dns_towire_state_t *NONNULL txn, int mtu, int xrcode, int version, int DO, int line)
451 {
452     if (!txn->error) {
453         if (txn->p + 9 >= txn->lim) {
454             txn->error = ENOBUFS;
455             txn->truncated = true;
456             txn->line = line;
457             return;
458         }
459         *txn->p++ = 0; // root label
460         dns_u16_to_wire(txn, dns_rrtype_opt);
461         dns_u16_to_wire(txn, mtu);
462         *txn->p++ = xrcode;
463         *txn->p++ = version;
464         *txn->p++ = DO << 7; // flags (usb)
465         *txn->p++ = 0;       // flags (lsb, mbz)
466     }
467 }
468 
469 void
dns_edns0_option_begin_(dns_towire_state_t * NONNULL txn,int line)470 dns_edns0_option_begin_(dns_towire_state_t *NONNULL txn, int line)
471 {
472     if (!txn->error) {
473         if (txn->p + 2 >= txn->lim) {
474             txn->error = ENOBUFS;
475             txn->truncated = true;
476             txn->line = line;
477             return;
478         }
479         if (txn->p_opt != NULL) {
480             txn->error = EINVAL;
481             txn->line = line;
482             return;
483         }
484         txn->p_opt = txn->p;
485         txn->p += 2;
486     }
487 }
488 
489 void
dns_edns0_option_end_(dns_towire_state_t * NONNULL txn,int line)490 dns_edns0_option_end_(dns_towire_state_t *NONNULL txn, int line)
491 {
492     ssize_t opt_length;
493     if (!txn->error) {
494         if (txn->p_opt == NULL) {
495             txn->error = EINVAL;
496             txn->line = line;
497             return;
498         }
499         opt_length = txn->p - txn->p_opt - 2;
500         txn->p_opt[0] = opt_length >> 8;
501         txn->p_opt[1] = opt_length & 0xff;
502         txn->p_opt = NULL;
503     }
504 }
505 
506 void
dns_sig0_signature_to_wire_(dns_towire_state_t * NONNULL txn,srp_key_t * key,uint16_t key_tag,dns_name_pointer_t * NONNULL signer,const char * NONNULL signer_hostname,const char * NONNULL signer_domain,int line)507 dns_sig0_signature_to_wire_(dns_towire_state_t *NONNULL txn, srp_key_t *key, uint16_t key_tag,
508                             dns_name_pointer_t *NONNULL signer, const char *NONNULL signer_hostname,
509                             const char *NONNULL signer_domain, int line)
510 {
511     ssize_t siglen = srp_signature_length(key);
512     uint8_t *start, *p_signer, *p_signature, *rrstart = txn->p;
513 #ifndef NO_CLOCK
514     struct timeval now;
515 #endif
516 
517     // 1 name (root)
518     // 2 type (SIG)
519     // 2 class (0)
520     // 4 TTL (0)
521     // 18 SIG RDATA up to signer name
522     // 2 signer name (always a pointer)
523     // 29 bytes so far
524     // signature data (depends on algorithm, e.g. 64 for ECDSASHA256)
525     // so e.g. 93 bytes total
526 
527     if (!txn->error) {
528         dns_u8_to_wire(txn, 0);	// root label
529         dns_u16_to_wire(txn, dns_rrtype_sig);
530         dns_u16_to_wire(txn, 0); // class
531         dns_ttl_to_wire(txn, 0); // SIG RR TTL
532         dns_rdlength_begin(txn);
533         start = txn->p;
534         dns_u16_to_wire(txn, 0); // type = 0 for transaction signature
535         dns_u8_to_wire(txn, srp_key_algorithm(key));
536         dns_u8_to_wire(txn, 0); // labels field doesn't apply for transaction signature
537         dns_ttl_to_wire(txn, 0); // original ttl doesn't apply
538 #ifndef NO_CLOCK
539         gettimeofday(&now, NULL);
540         uint32_t sec = (uint32_t)now.tv_sec;
541         // In te extraordinarily unlikely event that time_t has rolled over
542         if (sec < 300) {
543 #endif
544             dns_u32_to_wire(txn, 0); // Indicate that we have no clock: set expiry and inception times to zero
545             dns_u32_to_wire(txn, 0);
546 #ifndef NO_CLOCK
547         } else {
548             dns_u32_to_wire(txn, sec + 300); // signature expiration time is five minutes from now
549             dns_u32_to_wire(txn, sec - 300); // signature inception time, five minutes in the past
550         }
551 #endif
552         dns_u16_to_wire(txn, key_tag);
553 
554         p_signer = txn->p;
555         // We store the name in uncompressed form because that's what we have to sign
556         if (signer_hostname != NULL) {
557             dns_name_to_wire(NULL, txn, signer_hostname);
558         }
559         dns_full_name_to_wire(NULL, txn, signer_domain);
560         // And that means we're going to have to copy the signature back earlier in the packet.
561         p_signature = txn->p;
562 
563         // Sign the message, signature RRDATA (less signature) first.
564         if (!srp_sign(txn->p, siglen, (uint8_t *)txn->message, rrstart - (uint8_t *)txn->message,
565                       start, txn->p - start, key)) {
566             txn->error = true;
567             txn->line = __LINE__;
568         } else {
569             // Now that it's signed, back up and store the pointer to the name, because we're trying
570             // to be as compact as possible.
571             txn->p = p_signer;
572             dns_pointer_to_wire(NULL, txn, signer); // Pointer to the owner name the key is attached to
573             // And move the signature earlier in the packet.
574             memmove(txn->p, p_signature, siglen);
575 
576             txn->p += siglen;
577             dns_rdlength_end(txn);
578         }
579 
580         if (txn->error) {
581             txn->outer_line = line;
582         }
583     }
584 }
585 
586 #ifdef MALLOC_DEBUG_LOGGING
587 #undef malloc
588 #undef calloc
589 #undef strdup
590 #undef free
591 
592 void *
debug_malloc(size_t len,const char * file,int line)593 debug_malloc(size_t len, const char *file, int line)
594 {
595     void *ret = malloc(len);
596     INFO("%p: malloc(%zu) at " PUB_S_SRP ":%d", ret, len, file, line);
597     return ret;
598 }
599 
600 void *
debug_calloc(size_t count,size_t len,const char * file,int line)601 debug_calloc(size_t count, size_t len, const char *file, int line)
602 {
603     void *ret = calloc(count, len);
604     INFO("%p: calloc(%zu, %zu) at " PUB_S_SRP ":%d", ret, count, len, file, line);
605     return ret;
606 }
607 
608 char *
debug_strdup(const char * s,const char * file,int line)609 debug_strdup(const char *s, const char *file, int line)
610 {
611     char *ret = strdup(s);
612     INFO("%p: strdup(%p) at " PUB_S_SRP ":%d", ret, s, file, line);
613     return ret;
614 }
615 
616 void
debug_free(void * p,const char * file,int line)617 debug_free(void *p, const char *file, int line)
618 {
619     INFO("%p: free() at " PUB_S_SRP ":%d", p, file, line);
620     free(p);
621 }
622 #endif
623 
624 // Local Variables:
625 // mode: C
626 // tab-width: 4
627 // c-file-style: "bsd"
628 // c-basic-offset: 4
629 // fill-column: 108
630 // indent-tabs-mode: nil
631 // End:
632