]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/srp-gw.c
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / srp-gw.c
1 /* srp-gw.c
2 *
3 * Copyright (c) 2018 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 * This is a DNSSD Service Registration Protocol gateway. The purpose of this is to make it possible
18 * for SRP clients to update DNS servers that don't support SRP.
19 *
20 * The way it works is that this gateway listens on port ANY:53 and forwards either to another port on
21 * the same host (not recommended) or to any port (usually 53) on a different host. Requests are accepted
22 * over both TCP and UDP in principle, but UDP requests should be from constrained nodes, and rely on
23 * network topology for authentication.
24 *
25 * Note that this is not a full DNS proxy, so you can't just put it in front of a DNS server.
26 */
27
28 // Get DNS server IP address
29 // Get list of permitted source subnets for TCP updates
30 // Get list of permitted source subnet/interface tuples for UDP updates
31 // Set up UDP listener
32 // Set up TCP listener (no TCP Fast Open)
33 // Event loop
34 // Transaction processing:
35 // 1. If UDP, validate that it's from a subnet that is valid for the interface on which it was received.
36 // 2. If TCP, validate that it's from a permitted subnet
37 // 3. Check that the message is a valid SRP update according to the rules
38 // 4. Check the signature
39 // 5. Do a DNS Update with prerequisites to prevent overwriting a host record with the same owner name but
40 // a different key.
41 // 6. Send back the response
42
43 #define __APPLE_USE_RFC_3542
44
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stdio.h>
48 #include <unistd.h>
49 #include <sys/errno.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>
53 #include <sys/event.h>
54 #include <fcntl.h>
55 #include <sys/time.h>
56
57 #include "srp.h"
58 #include "dns-msg.h"
59 #include "srp-crypto.h"
60 #include "ioloop.h"
61 #include "dnssd-proxy.h"
62
63 #pragma mark structures
64
65 typedef struct subnet subnet_t;
66 struct subnet {
67 subnet_t *NULLABLE next;
68 uint8_t preflen;
69 uint8_t family;
70 char bytes[8];
71 };
72
73 typedef struct udp_validator udp_validator_t;
74 struct udp_validator {
75 udp_validator_t *NULLABLE next;
76 char *NONNULL ifname;
77 int ifindex;
78 subnet_t *subnets;
79 };
80
81 static int
82 usage(const char *progname)
83 {
84 ERROR("usage: %s -s <addr> <port> -t <subnet> ... -u <ifname> <subnet> ...", progname);
85 ERROR(" -s can only appear once.");
86 ERROR(" -t can only appear once, and is followed by one or more subnets.");
87 ERROR(" -u can appear more than once, is followed by one interface name, and");
88 ERROR(" one or more subnets.");
89 ERROR(" <addr> is an IPv4 address or IPv6 address.");
90 ERROR(" <port> is a UDP port number.");
91 ERROR(" <subnet> is an IP address followed by a slash followed by the prefix width.");
92 ERROR(" <ifname> is the printable name of the interface.");
93 ERROR("ex: srp-gw -s 2001:DB8::1 53 -t 2001:DB8:1300::/48 -u en0 2001:DB8:1300:1100::/56");
94 return 1;
95 }
96
97 typedef struct delete delete_t;
98 struct delete {
99 delete_t *next;
100 dns_name_t *name;
101 };
102
103 typedef struct dns_host_description dns_host_description_t;
104 struct dns_host_description {
105 dns_name_t *name;
106 dns_rr_t *a, *aaaa, *key;
107 delete_t *delete;
108 int num_instances;
109 };
110
111 typedef struct service_instance service_instance_t;
112 struct service_instance {
113 service_instance_t *next;
114 dns_host_description_t *host_description;
115 dns_name_t *name;
116 delete_t *delete;
117 int num_instances;
118 dns_rr_t *srv, *txt;
119 };
120
121 typedef struct service service_t;
122 struct service {
123 service_t *next;
124 service_instance_t *instance;
125 dns_name_t *name;
126 dns_rr_t *rr;
127 };
128
129 bool
130 srp_relay(comm_t *comm, dns_message_t *message)
131 {
132 dns_name_t *update_zone;
133 bool updating_services_dot_arpa = false;
134 int i;
135 dns_host_description_t *host_description = NULL;
136 delete_t *deletes = NULL, *dp, **dpp = &deletes;
137 service_instance_t *service_instances = NULL, *sip, **sipp = &service_instances;
138 service_t *services = NULL, *sp, **spp = &services;
139 dns_rr_t *signature;
140 char namebuf[DNS_MAX_NAME_SIZE + 1], namebuf1[DNS_MAX_NAME_SIZE + 1];
141 bool ret = false;
142 struct timeval now;
143
144 // Update requires a single SOA record as the question
145 if (message->qdcount != 1) {
146 ERROR("srp_relay: update received with qdcount > 1");
147 return false;
148 }
149
150 // Update should contain zero answers.
151 if (message->ancount != 0) {
152 ERROR("srp_relay: update received with ancount > 0");
153 return false;
154 }
155
156 if (message->questions[0].type != dns_rrtype_soa) {
157 ERROR("srp_relay: update received with rrtype %d instead of SOA in question section.",
158 message->questions[0].type);
159 return false;
160 }
161 update_zone = message->questions[0].name;
162
163 // What zone are we updating?
164 if (dns_names_equal_text(update_zone, "services.arpa")) {
165 updating_services_dot_arpa = true;
166 }
167
168 // Scan over the authority RRs; do the delete consistency check. We can't do other consistency checks
169 // because we can't assume a particular order to the records other than that deletes have to come before
170 // adds.
171 for (i = 0; i < message->nscount; i++) {
172 dns_rr_t *rr = &message->authority[i];
173
174 // If this is a delete for all the RRs on a name, record it in the list of deletes.
175 if (rr->type == dns_rrtype_any && rr->qclass == dns_qclass_any && rr->ttl == 0) {
176 for (dp = deletes; dp; dp = dp->next) {
177 if (dns_names_equal(dp->name, rr->name)) {
178 ERROR("srp_relay: two deletes for the same name: %s",
179 dns_name_print(rr->name, namebuf, sizeof namebuf));
180 goto out;
181 }
182 }
183 dp = calloc(sizeof *dp, 1);
184 if (!dp) {
185 ERROR("srp_relay: no memory.");
186 goto out;
187 }
188 dp->name = rr->name;
189 *dpp = dp;
190 dpp = &dp->next;
191 }
192
193 // Otherwise if it's an A or AAAA record, it's part of a hostname entry.
194 else if (rr->type == dns_rrtype_a || rr->type == dns_rrtype_aaaa || rr->type == dns_rrtype_key) {
195 // Allocate the hostname record
196 if (!host_description) {
197 host_description = calloc(sizeof *host_description, 1);
198 if (!host_description) {
199 ERROR("srp_relay: no memory");
200 goto out;
201 }
202 }
203
204 // Make sure it's preceded by a deletion of all the RRs on the name.
205 if (!host_description->delete) {
206 for (dp = deletes; dp; dp = dp->next) {
207 if (dns_names_equal(dp->name, rr->name)) {
208 break;
209 }
210 }
211 if (dp == NULL) {
212 ERROR("srp_relay: ADD for hostname %s without a preceding delete.",
213 dns_name_print(rr->name, namebuf, sizeof namebuf));
214 goto out;
215 }
216 host_description->delete = dp;
217 host_description->name = dp->name;
218 }
219
220 if (rr->type == dns_rrtype_a) {
221 if (host_description->a != NULL) {
222 ERROR("srp_relay: more than one A rrset received for name: %s",
223 dns_name_print(rr->name, namebuf, sizeof namebuf));
224 goto out;
225 }
226 host_description->a = rr;
227 } else if (rr->type == dns_rrtype_aaaa) {
228 if (host_description->aaaa != NULL) {
229 ERROR("srp_relay: more than one AAAA rrset received for name: %s",
230 dns_name_print(rr->name, namebuf, sizeof namebuf));
231 goto out;
232 }
233 host_description->aaaa = rr;
234 } else if (rr->type == dns_rrtype_key) {
235 if (host_description->key != NULL) {
236 ERROR("srp_relay: more than one KEY rrset received for name: %s",
237 dns_name_print(rr->name, namebuf, sizeof namebuf));
238 goto out;
239 }
240 host_description->key = rr;
241 }
242 }
243
244 // Otherwise if it's an SRV entry, that should be a service instance name.
245 else if (rr->type == dns_rrtype_srv || rr->type == dns_rrtype_txt) {
246 // Should be a delete that precedes this service instance.
247 for (dp = deletes; dp; dp = dp->next) {
248 if (dns_names_equal(dp->name, rr->name)) {
249 break;
250 }
251 }
252 if (dp == NULL) {
253 ERROR("srp_relay: ADD for service instance not preceded by delete: %s",
254 dns_name_print(rr->name, namebuf, sizeof namebuf));
255 goto out;
256 }
257 for (sip = service_instances; sip; sip = sip->next) {
258 if (dns_names_equal(sip->name, rr->name)) {
259 break;
260 }
261 }
262 if (!sip) {
263 sip = calloc(sizeof *sip, 1);
264 if (sip == NULL) {
265 ERROR("srp_relay: no memory");
266 goto out;
267 }
268 sip->delete = dp;
269 sip->name = dp->name;
270 *sipp = sip;
271 sipp = &sip->next;
272 }
273 if (rr->type == dns_rrtype_srv) {
274 if (sip->srv != NULL) {
275 ERROR("srp_relay: more than one SRV rr received for service instance: %s",
276 dns_name_print(rr->name, namebuf, sizeof namebuf));
277 goto out;
278 }
279 sip->srv = rr;
280 } else if (rr->type == dns_rrtype_txt) {
281 if (sip->txt != NULL) {
282 ERROR("srp_relay: more than one SRV rr received for service instance: %s",
283 dns_name_print(rr->name, namebuf, sizeof namebuf));
284 }
285 sip->txt = rr;
286 }
287 }
288
289 // Otherwise if it's a PTR entry, that should be a service name
290 else if (rr->type == dns_rrtype_ptr) {
291 sp = calloc(sizeof *sp, 1);
292 if (sp == NULL) {
293 ERROR("srp_relay: no memory");
294 goto out;
295 }
296 sp->rr = rr;
297 *spp = sp;
298 spp = &sp->next;
299 }
300
301 // Otherwise it's not a valid update
302 else {
303 ERROR("srp_relay: unexpected rrtype %d on %s in update.", rr->type,
304 dns_name_print(rr->name, namebuf, sizeof namebuf));
305 goto out;
306 }
307 }
308
309 // Now that we've scanned the whole update, do the consistency checks for updates that might
310 // not have come in order.
311
312 // First, make sure there's a host description.
313 if (host_description == NULL) {
314 ERROR("srp_relay: SRP update does not include a host description.");
315 goto out;
316 }
317
318 // Make sure that each service add references a service instance that's in the same update.
319 for (sp = services; sp; sp = sp->next) {
320 for (sip = service_instances; sip; sip = sip->next) {
321 if (dns_names_equal(sip->name, sp->rr->data.ptr.name)) {
322 // Note that we have already verified that there is only one service instance
323 // with this name, so this could only ever happen once in this loop even without
324 // the break statement.
325 sp->instance = sip;
326 sip->num_instances++;
327 break;
328 }
329 }
330 // If this service doesn't point to a service instance that's in the update, then the
331 // update fails validation.
332 if (sip == NULL) {
333 ERROR("srp_relay: service %s points to an instance that's not included: %s",
334 dns_name_print(sp->name, namebuf, sizeof namebuf),
335 dns_name_print(sip->name, namebuf1, sizeof namebuf1));
336 goto out;
337 }
338 }
339
340 for (sip = service_instances; sip; sip = sip->next) {
341 // For each service instance, make sure that at least one service references it
342 if (sip->num_instances == 0) {
343 ERROR("srp_relay: service instance update for %s is not referenced by a service update.",
344 dns_name_print(sip->name, namebuf, sizeof namebuf));
345 goto out;
346 }
347
348 // For each service instance, make sure that it references the host description
349 if (dns_names_equal(host_description->name, sip->srv->data.srv.name)) {
350 sip->host_description = host_description;
351 host_description->num_instances++;
352 }
353 }
354
355 // Make sure that at least one service instance references the host description
356 if (host_description->num_instances == 0) {
357 ERROR("srp_relay: host description %s is not referenced by any service instances.",
358 dns_name_print(host_description->name, namebuf, sizeof namebuf));
359 goto out;
360 }
361
362 // Make sure the host description has at least one address record.
363 if (host_description->a == NULL && host_description->aaaa == NULL) {
364 ERROR("srp_relay: host description %s doesn't contain any IP addresses.",
365 dns_name_print(host_description->name, namebuf, sizeof namebuf));
366 goto out;
367 }
368 // And make sure it has a key record
369 if (host_description->key == NULL) {
370 ERROR("srp_relay: host description %s doesn't contain a key.",
371 dns_name_print(host_description->name, namebuf, sizeof namebuf));
372 goto out;
373 }
374
375 // The signature should be the last thing in the additional section. Even if the signature
376 // is valid, if it's not at the end we reject it. Note that we are just checking for SIG(0)
377 // so if we don't find what we're looking for, we forward it to the DNS auth server which
378 // will either accept or reject it.
379 if (message->arcount < 1) {
380 ERROR("srp_relay: signature not present");
381 goto out;
382 }
383 signature = &message->additional[message->arcount -1];
384 if (signature->type != dns_rrtype_sig) {
385 ERROR("srp_relay: signature is not at the end or is not present");
386 goto out;
387 }
388
389 // Make sure that the signer name is the hostname. If it's not, it could be a legitimate
390 // update with a different key, but it's not an SRP update, so we pass it on.
391 if (!dns_names_equal(signature->data.sig.signer, host_description->name)) {
392 ERROR("srp_relay: signer %s doesn't match host %s",
393 dns_name_print(signature->data.sig.signer, namebuf, sizeof namebuf),
394 dns_name_print(host_description->name, namebuf1, sizeof namebuf1));
395 goto out;
396 }
397
398 // Make sure we're in the time limit for the signature. Zeroes for the inception and expiry times
399 // mean the host that send this doesn't have a working clock. One being zero and the other not isn't
400 // valid unless it's 1970.
401 if (signature->data.sig.inception != 0 || signature->data.sig.expiry != 0) {
402 gettimeofday(&now, NULL);
403 // The sender does the bracketing, so we can just do a simple comparison.
404 if (now.tv_sec > signature->data.sig.expiry || now.tv_sec < signature->data.sig.inception) {
405 ERROR("signature is not timely: %lu < %lu < %lu does not hold",
406 (unsigned long)signature->data.sig.inception, (unsigned long)now.tv_sec,
407 (unsigned long)signature->data.sig.expiry);
408 goto badsig;
409 }
410 }
411
412 // Now that we have the key, we can validate the signature. If the signature doesn't validate,
413 // there is no need to pass the message on.
414 if (!srp_sig0_verify(message->wire, host_description->key, signature)) {
415 ERROR("signature is not valid");
416 goto badsig;
417 }
418
419 badsig:
420 // True means we consumed it, not that it was valid.
421 ret = true;
422
423 out:
424 // free everything we allocated but (it turns out) aren't going to use
425 for (dp = deletes; dp; ) {
426 delete_t *next = dp->next;
427 free(dp);
428 dp = next;
429 }
430 for (sip = service_instances; sip; ) {
431 service_instance_t *next = sip->next;
432 free(sip);
433 sip = next;
434 }
435 for (sp = services; sp; ) {
436 service_t *next = sp->next;
437 free(sp);
438 sp = next;
439 }
440 if (host_description != NULL) {
441 free(host_description);
442 }
443 return ret;
444 }
445
446 void
447 dns_evaluate(comm_t *comm)
448 {
449 dns_message_t *message;
450
451 // Drop incoming responses--we're a server, so we only accept queries.
452 if (dns_qr_get(&comm->message->wire) == dns_qr_response) {
453 return;
454 }
455
456 // Forward incoming messages that are queries but not updates.
457 // XXX do this later--for now we operate only as a translator, not a proxy.
458 if (dns_opcode_get(&comm->message->wire) != dns_opcode_update) {
459 return;
460 }
461
462 // Parse the UPDATE message.
463 if (!dns_wire_parse(&message, &comm->message->wire, comm->message->length)) {
464 ERROR("dns_wire_parse failed.");
465 return;
466 }
467
468 // We need the wire message to validate the signature...
469 message->wire = &comm->message->wire;
470 if (!srp_relay(comm, message)) {
471 // The message wasn't invalid, but wasn't an SRP message.
472 // dns_forward(comm)
473 }
474 // But we don't save it.
475 message->wire = NULL;
476
477 //dns_message_free(message);
478 }
479
480 void dns_input(comm_t *comm)
481 {
482 dns_evaluate(comm);
483 message_free(comm->message);
484 comm->message = NULL;
485 }
486
487 int
488 main(int argc, char **argv)
489 {
490 int i;
491 subnet_t *tcp_validators = NULL;
492 udp_validator_t *udp_validators = NULL;
493 udp_validator_t *NULLABLE *NONNULL up = &udp_validators;
494 subnet_t *NULLABLE *NONNULL nt = &tcp_validators;
495 subnet_t *NULLABLE *NONNULL sp;
496 addr_t server, pref;
497 uint16_t port;
498 socklen_t len, prefalen;
499 char *s, *p;
500 int width;
501 uint16_t listen_port;
502
503 listen_port = htons(53);
504
505 // Read the configuration from the command line.
506 for (i = 1; i < argc; i++) {
507 if (!strcmp(argv[i], "-s")) {
508 if (i++ == argc) {
509 ERROR("-s is missing server IP address.");
510 return usage(argv[0]);
511 }
512 len = getipaddr(&server, argv[i]);
513 if (!len) {
514 ERROR("Invalid IP address: %s.", argv[i]);
515 return usage(argv[0]);
516 }
517 server.sa.sa_len = len;
518 if (i++ == argc) {
519 ERROR("-s is missing server port.");
520 return usage(argv[0]);
521 }
522 port = strtol(argv[i], &s, 10);
523 if (s == argv[i] || s[0] != '\0') {
524 ERROR("Invalid port number: %s", argv[i]);
525 return usage(argv[0]);
526 }
527 if (server.sa.sa_family == AF_INET) {
528 server.sin.sin_port = htons(port);
529 } else {
530 server.sin6.sin6_port = htons(port);
531 }
532 i += 2;
533 } else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "-u")) {
534 if (!strcmp(argv[i], "-u")) {
535 if (i++ == argc) {
536 ERROR("-u is missing interface name.");
537 return usage(argv[0]);
538 }
539 *up = calloc(1, sizeof **up);
540 if (*up == NULL) {
541 ERROR("udp_validators: out of memory.");
542 return usage(argv[0]);
543 }
544 (*up)->ifname = strdup(argv[i]);
545 if ((*up)->ifname == NULL) {
546 ERROR("udp validators: ifname: out of memory.");
547 return usage(argv[0]);
548 }
549 sp = &((*up)->subnets);
550 } else {
551 sp = nt;
552 }
553
554 if (i++ == argc) {
555 ERROR("%s requires at least one prefix.", argv[i - 1]);
556 return usage(argv[0]);
557 }
558 s = strchr(argv[i], '/');
559 if (s == NULL) {
560 ERROR("%s is not a prefix.", argv[i]);
561 return usage(argv[0]);
562 }
563 *s = 0;
564 ++s;
565 prefalen = getipaddr(&pref, argv[i]);
566 if (!prefalen) {
567 ERROR("%s is not a valid prefix address.", argv[i]);
568 return usage(argv[0]);
569 }
570 width = strtol(s, &p, 10);
571 if (s == p || p[0] != '\0') {
572 ERROR("%s (prefix width) is not a number.", p);
573 return usage(argv[0]);
574 }
575 if (width < 0 ||
576 (pref.sa.sa_family == AF_INET && width > 32) ||
577 (pref.sa.sa_family == AF_INET6 && width > 64)) {
578 ERROR("%s is not a valid prefix length for %s", p,
579 pref.sa.sa_family == AF_INET ? "IPv4" : "IPv6");
580 return usage(argv[0]);
581 }
582
583 *nt = calloc(1, sizeof **nt);
584 if (!*nt) {
585 ERROR("tcp_validators: out of memory.");
586 return 1;
587 }
588
589 (*nt)->preflen = width;
590 (*nt)->family = pref.sa.sa_family;
591 if (pref.sa.sa_family == AF_INET) {
592 memcpy((*nt)->bytes, &pref.sin.sin_addr, 4);
593 } else {
594 memcpy((*nt)->bytes, &pref.sin6.sin6_addr, 8);
595 }
596
597 // *up will be non-null for -u and null for -t.
598 if (*up) {
599 up = &((*up)->next);
600 } else {
601 nt = sp;
602 }
603 }
604 }
605
606 if (!ioloop_init()) {
607 return 1;
608 }
609
610 // Set up listeners
611 if (!setup_listener_socket(AF_INET, IPPROTO_UDP, listen_port, "UDPv4 listener", dns_input, 0, 0)) {
612 ERROR("UDPv4 listener: fail.");
613 return 1;
614 }
615 if (!setup_listener_socket(AF_INET6, IPPROTO_UDP, listen_port, "UDPv6 listener", dns_input, 0, 0)) {
616 ERROR("UDPv6 listener: fail.");
617 return 1;
618 }
619 if (!setup_listener_socket(AF_INET, IPPROTO_TCP, listen_port, "TCPv4 listener", dns_input, 0, 0)) {
620 ERROR("TCPv4 listener: fail.");
621 return 1;
622 }
623 if (!setup_listener_socket(AF_INET6, IPPROTO_TCP, listen_port, "TCPv6 listener", dns_input, 0, 0)) {
624 ERROR("TCPv4 listener: fail.");
625 return 1;
626 }
627
628 do {
629 int something = 0;
630 something = ioloop_events(0);
631 INFO("dispatched %d events.", something);
632 } while (1);
633 }
634
635 // Local Variables:
636 // mode: C
637 // tab-width: 4
638 // c-file-style: "bsd"
639 // c-basic-offset: 4
640 // fill-column: 108
641 // indent-tabs-mode: nil
642 // End: