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