diff options
author | djm@openbsd.org <djm@openbsd.org> | 2017-10-25 00:15:35 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2017-10-25 12:26:06 +1100 |
commit | acf559e1cffbd1d6167cc1742729fc381069f06b (patch) | |
tree | fc127e0bce21056c96dec59ebdc9e2ff9f5b1e4a /servconf.c | |
parent | b9903ee8ee8671b447fc260c2bee3761e26c7227 (diff) |
upstream commit
Add optional rdomain qualifier to sshd_config's
ListenAddress option to allow listening on a different rdomain(4), e.g.
ListenAddress 0.0.0.0 rdomain 4
Upstream-ID: 24b6622c376feeed9e9be8b9605e593695ac9091
Diffstat (limited to 'servconf.c')
-rw-r--r-- | servconf.c | 229 |
1 files changed, 162 insertions, 67 deletions
diff --git a/servconf.c b/servconf.c index a96df4f67..68db047f2 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -1,5 +1,5 @@ | |||
1 | 1 | ||
2 | /* $OpenBSD: servconf.c,v 1.314 2017/10/05 15:52:03 djm Exp $ */ | 2 | /* $OpenBSD: servconf.c,v 1.315 2017/10/25 00:15:35 djm Exp $ */ |
3 | /* | 3 | /* |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
5 | * All rights reserved | 5 | * All rights reserved |
@@ -15,10 +15,16 @@ | |||
15 | 15 | ||
16 | #include <sys/types.h> | 16 | #include <sys/types.h> |
17 | #include <sys/socket.h> | 17 | #include <sys/socket.h> |
18 | #ifdef HAVE_SYS_SYSCTL_H | ||
19 | #include <sys/sysctl.h> | ||
20 | #endif | ||
18 | 21 | ||
19 | #include <netinet/in.h> | 22 | #include <netinet/in.h> |
20 | #include <netinet/in_systm.h> | 23 | #include <netinet/in_systm.h> |
21 | #include <netinet/ip.h> | 24 | #include <netinet/ip.h> |
25 | #ifdef HAVE_NET_ROUTE_H | ||
26 | #include <net/route.h> | ||
27 | #endif | ||
22 | 28 | ||
23 | #include <ctype.h> | 29 | #include <ctype.h> |
24 | #include <netdb.h> | 30 | #include <netdb.h> |
@@ -58,8 +64,10 @@ | |||
58 | #include "myproposal.h" | 64 | #include "myproposal.h" |
59 | #include "digest.h" | 65 | #include "digest.h" |
60 | 66 | ||
61 | static void add_listen_addr(ServerOptions *, char *, int); | 67 | static void add_listen_addr(ServerOptions *, const char *, |
62 | static void add_one_listen_addr(ServerOptions *, char *, int); | 68 | const char *, int); |
69 | static void add_one_listen_addr(ServerOptions *, const char *, | ||
70 | const char *, int); | ||
63 | 71 | ||
64 | /* Use of privilege separation or not */ | 72 | /* Use of privilege separation or not */ |
65 | extern int use_privsep; | 73 | extern int use_privsep; |
@@ -81,6 +89,7 @@ initialize_server_options(ServerOptions *options) | |||
81 | options->queued_listen_addrs = NULL; | 89 | options->queued_listen_addrs = NULL; |
82 | options->num_queued_listens = 0; | 90 | options->num_queued_listens = 0; |
83 | options->listen_addrs = NULL; | 91 | options->listen_addrs = NULL; |
92 | options->num_listen_addrs = 0; | ||
84 | options->address_family = -1; | 93 | options->address_family = -1; |
85 | options->num_host_key_files = 0; | 94 | options->num_host_key_files = 0; |
86 | options->num_host_cert_files = 0; | 95 | options->num_host_cert_files = 0; |
@@ -252,7 +261,7 @@ fill_default_server_options(ServerOptions *options) | |||
252 | if (options->address_family == -1) | 261 | if (options->address_family == -1) |
253 | options->address_family = AF_UNSPEC; | 262 | options->address_family = AF_UNSPEC; |
254 | if (options->listen_addrs == NULL) | 263 | if (options->listen_addrs == NULL) |
255 | add_listen_addr(options, NULL, 0); | 264 | add_listen_addr(options, NULL, NULL, 0); |
256 | if (options->pid_file == NULL) | 265 | if (options->pid_file == NULL) |
257 | options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); | 266 | options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); |
258 | if (options->login_grace_time == -1) | 267 | if (options->login_grace_time == -1) |
@@ -658,23 +667,51 @@ derelativise_path(const char *path) | |||
658 | } | 667 | } |
659 | 668 | ||
660 | static void | 669 | static void |
661 | add_listen_addr(ServerOptions *options, char *addr, int port) | 670 | add_listen_addr(ServerOptions *options, const char *addr, |
671 | const char *rdomain, int port) | ||
662 | { | 672 | { |
663 | u_int i; | 673 | u_int i; |
664 | 674 | ||
665 | if (port == 0) | 675 | if (port > 0) |
666 | for (i = 0; i < options->num_ports; i++) | 676 | add_one_listen_addr(options, addr, rdomain, port); |
667 | add_one_listen_addr(options, addr, options->ports[i]); | 677 | else { |
668 | else | 678 | for (i = 0; i < options->num_ports; i++) { |
669 | add_one_listen_addr(options, addr, port); | 679 | add_one_listen_addr(options, addr, rdomain, |
680 | options->ports[i]); | ||
681 | } | ||
682 | } | ||
670 | } | 683 | } |
671 | 684 | ||
672 | static void | 685 | static void |
673 | add_one_listen_addr(ServerOptions *options, char *addr, int port) | 686 | add_one_listen_addr(ServerOptions *options, const char *addr, |
687 | const char *rdomain, int port) | ||
674 | { | 688 | { |
675 | struct addrinfo hints, *ai, *aitop; | 689 | struct addrinfo hints, *ai, *aitop; |
676 | char strport[NI_MAXSERV]; | 690 | char strport[NI_MAXSERV]; |
677 | int gaierr; | 691 | int gaierr; |
692 | u_int i; | ||
693 | |||
694 | /* Find listen_addrs entry for this rdomain */ | ||
695 | for (i = 0; i < options->num_listen_addrs; i++) { | ||
696 | if (rdomain == NULL && options->listen_addrs[i].rdomain == NULL) | ||
697 | break; | ||
698 | if (rdomain == NULL || options->listen_addrs[i].rdomain == NULL) | ||
699 | continue; | ||
700 | if (strcmp(rdomain, options->listen_addrs[i].rdomain) == 0) | ||
701 | break; | ||
702 | } | ||
703 | if (i >= options->num_listen_addrs) { | ||
704 | /* No entry for this rdomain; allocate one */ | ||
705 | if (i >= INT_MAX) | ||
706 | fatal("%s: too many listen addresses", __func__); | ||
707 | options->listen_addrs = xrecallocarray(options->listen_addrs, | ||
708 | options->num_listen_addrs, options->num_listen_addrs + 1, | ||
709 | sizeof(*options->listen_addrs)); | ||
710 | i = options->num_listen_addrs++; | ||
711 | if (rdomain != NULL) | ||
712 | options->listen_addrs[i].rdomain = xstrdup(rdomain); | ||
713 | } | ||
714 | /* options->listen_addrs[i] points to the addresses for this rdomain */ | ||
678 | 715 | ||
679 | memset(&hints, 0, sizeof(hints)); | 716 | memset(&hints, 0, sizeof(hints)); |
680 | hints.ai_family = options->address_family; | 717 | hints.ai_family = options->address_family; |
@@ -687,8 +724,37 @@ add_one_listen_addr(ServerOptions *options, char *addr, int port) | |||
687 | ssh_gai_strerror(gaierr)); | 724 | ssh_gai_strerror(gaierr)); |
688 | for (ai = aitop; ai->ai_next; ai = ai->ai_next) | 725 | for (ai = aitop; ai->ai_next; ai = ai->ai_next) |
689 | ; | 726 | ; |
690 | ai->ai_next = options->listen_addrs; | 727 | ai->ai_next = options->listen_addrs[i].addrs; |
691 | options->listen_addrs = aitop; | 728 | options->listen_addrs[i].addrs = aitop; |
729 | } | ||
730 | |||
731 | /* Returns nonzero if the routing domain name is valid */ | ||
732 | static int | ||
733 | valid_rdomain(const char *name) | ||
734 | { | ||
735 | const char *errstr; | ||
736 | long long num; | ||
737 | struct rt_tableinfo info; | ||
738 | int mib[6]; | ||
739 | size_t miblen = sizeof(mib); | ||
740 | |||
741 | if (name == NULL) | ||
742 | return 1; | ||
743 | |||
744 | num = strtonum(name, 0, 255, &errstr); | ||
745 | if (errstr != NULL) | ||
746 | return 0; | ||
747 | |||
748 | /* Check whether the table actually exists */ | ||
749 | memset(mib, 0, sizeof(mib)); | ||
750 | mib[0] = CTL_NET; | ||
751 | mib[1] = PF_ROUTE; | ||
752 | mib[4] = NET_RT_TABLE; | ||
753 | mib[5] = (int)num; | ||
754 | if (sysctl(mib, 6, &info, &miblen, NULL, 0) == -1) | ||
755 | return 0; | ||
756 | |||
757 | return 1; | ||
692 | } | 758 | } |
693 | 759 | ||
694 | /* | 760 | /* |
@@ -696,18 +762,19 @@ add_one_listen_addr(ServerOptions *options, char *addr, int port) | |||
696 | * and AddressFamily options. | 762 | * and AddressFamily options. |
697 | */ | 763 | */ |
698 | static void | 764 | static void |
699 | queue_listen_addr(ServerOptions *options, char *addr, int port) | 765 | queue_listen_addr(ServerOptions *options, const char *addr, |
766 | const char *rdomain, int port) | ||
700 | { | 767 | { |
701 | options->queued_listen_addrs = xreallocarray( | 768 | struct queued_listenaddr *qla; |
702 | options->queued_listen_addrs, options->num_queued_listens + 1, | 769 | |
703 | sizeof(addr)); | 770 | options->queued_listen_addrs = xrecallocarray( |
704 | options->queued_listen_ports = xreallocarray( | 771 | options->queued_listen_addrs, |
705 | options->queued_listen_ports, options->num_queued_listens + 1, | 772 | options->num_queued_listens, options->num_queued_listens + 1, |
706 | sizeof(port)); | 773 | sizeof(*options->queued_listen_addrs)); |
707 | options->queued_listen_addrs[options->num_queued_listens] = | 774 | qla = &options->queued_listen_addrs[options->num_queued_listens++]; |
708 | xstrdup(addr); | 775 | qla->addr = xstrdup(addr); |
709 | options->queued_listen_ports[options->num_queued_listens] = port; | 776 | qla->port = port; |
710 | options->num_queued_listens++; | 777 | qla->rdomain = rdomain == NULL ? NULL : xstrdup(rdomain); |
711 | } | 778 | } |
712 | 779 | ||
713 | /* | 780 | /* |
@@ -717,6 +784,7 @@ static void | |||
717 | process_queued_listen_addrs(ServerOptions *options) | 784 | process_queued_listen_addrs(ServerOptions *options) |
718 | { | 785 | { |
719 | u_int i; | 786 | u_int i; |
787 | struct queued_listenaddr *qla; | ||
720 | 788 | ||
721 | if (options->num_ports == 0) | 789 | if (options->num_ports == 0) |
722 | options->ports[options->num_ports++] = SSH_DEFAULT_PORT; | 790 | options->ports[options->num_ports++] = SSH_DEFAULT_PORT; |
@@ -724,15 +792,13 @@ process_queued_listen_addrs(ServerOptions *options) | |||
724 | options->address_family = AF_UNSPEC; | 792 | options->address_family = AF_UNSPEC; |
725 | 793 | ||
726 | for (i = 0; i < options->num_queued_listens; i++) { | 794 | for (i = 0; i < options->num_queued_listens; i++) { |
727 | add_listen_addr(options, options->queued_listen_addrs[i], | 795 | qla = &options->queued_listen_addrs[i]; |
728 | options->queued_listen_ports[i]); | 796 | add_listen_addr(options, qla->addr, qla->rdomain, qla->port); |
729 | free(options->queued_listen_addrs[i]); | 797 | free(qla->addr); |
730 | options->queued_listen_addrs[i] = NULL; | 798 | free(qla->rdomain); |
731 | } | 799 | } |
732 | free(options->queued_listen_addrs); | 800 | free(options->queued_listen_addrs); |
733 | options->queued_listen_addrs = NULL; | 801 | options->queued_listen_addrs = NULL; |
734 | free(options->queued_listen_ports); | ||
735 | options->queued_listen_ports = NULL; | ||
736 | options->num_queued_listens = 0; | 802 | options->num_queued_listens = 0; |
737 | } | 803 | } |
738 | 804 | ||
@@ -1127,20 +1193,33 @@ process_server_config_line(ServerOptions *options, char *line, | |||
1127 | /* check for bare IPv6 address: no "[]" and 2 or more ":" */ | 1193 | /* check for bare IPv6 address: no "[]" and 2 or more ":" */ |
1128 | if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL | 1194 | if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL |
1129 | && strchr(p+1, ':') != NULL) { | 1195 | && strchr(p+1, ':') != NULL) { |
1130 | queue_listen_addr(options, arg, 0); | ||
1131 | break; | ||
1132 | } | ||
1133 | p = hpdelim(&arg); | ||
1134 | if (p == NULL) | ||
1135 | fatal("%s line %d: bad address:port usage", | ||
1136 | filename, linenum); | ||
1137 | p = cleanhostname(p); | ||
1138 | if (arg == NULL) | ||
1139 | port = 0; | 1196 | port = 0; |
1140 | else if ((port = a2port(arg)) <= 0) | 1197 | p = arg; |
1141 | fatal("%s line %d: bad port number", filename, linenum); | 1198 | } else { |
1199 | p = hpdelim(&arg); | ||
1200 | if (p == NULL) | ||
1201 | fatal("%s line %d: bad address:port usage", | ||
1202 | filename, linenum); | ||
1203 | p = cleanhostname(p); | ||
1204 | if (arg == NULL) | ||
1205 | port = 0; | ||
1206 | else if ((port = a2port(arg)) <= 0) | ||
1207 | fatal("%s line %d: bad port number", | ||
1208 | filename, linenum); | ||
1209 | } | ||
1210 | /* Optional routing table */ | ||
1211 | arg2 = NULL; | ||
1212 | if ((arg = strdelim(&cp)) != NULL) { | ||
1213 | if (strcmp(arg, "rdomain") != 0 || | ||
1214 | (arg2 = strdelim(&cp)) == NULL) | ||
1215 | fatal("%s line %d: bad ListenAddress syntax", | ||
1216 | filename, linenum); | ||
1217 | if (!valid_rdomain(arg2)) | ||
1218 | fatal("%s line %d: bad routing domain", | ||
1219 | filename, linenum); | ||
1220 | } | ||
1142 | 1221 | ||
1143 | queue_listen_addr(options, p, port); | 1222 | queue_listen_addr(options, p, arg2, port); |
1144 | 1223 | ||
1145 | break; | 1224 | break; |
1146 | 1225 | ||
@@ -2251,45 +2330,61 @@ dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) | |||
2251 | printf("\n"); | 2330 | printf("\n"); |
2252 | } | 2331 | } |
2253 | 2332 | ||
2254 | void | 2333 | static char * |
2255 | dump_config(ServerOptions *o) | 2334 | format_listen_addrs(struct listenaddr *la) |
2256 | { | 2335 | { |
2257 | u_int i; | 2336 | int r; |
2258 | int ret; | ||
2259 | struct addrinfo *ai; | 2337 | struct addrinfo *ai; |
2260 | char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; | 2338 | char addr[NI_MAXHOST], port[NI_MAXSERV]; |
2261 | char *laddr1 = xstrdup(""), *laddr2 = NULL; | 2339 | char *laddr1 = xstrdup(""), *laddr2 = NULL; |
2262 | 2340 | ||
2263 | /* these are usually at the top of the config */ | ||
2264 | for (i = 0; i < o->num_ports; i++) | ||
2265 | printf("port %d\n", o->ports[i]); | ||
2266 | dump_cfg_fmtint(sAddressFamily, o->address_family); | ||
2267 | |||
2268 | /* | 2341 | /* |
2269 | * ListenAddress must be after Port. add_one_listen_addr pushes | 2342 | * ListenAddress must be after Port. add_one_listen_addr pushes |
2270 | * addresses onto a stack, so to maintain ordering we need to | 2343 | * addresses onto a stack, so to maintain ordering we need to |
2271 | * print these in reverse order. | 2344 | * print these in reverse order. |
2272 | */ | 2345 | */ |
2273 | for (ai = o->listen_addrs; ai; ai = ai->ai_next) { | 2346 | for (ai = la->addrs; ai; ai = ai->ai_next) { |
2274 | if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, | 2347 | if ((r = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, |
2275 | sizeof(addr), port, sizeof(port), | 2348 | sizeof(addr), port, sizeof(port), |
2276 | NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { | 2349 | NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { |
2277 | error("getnameinfo failed: %.100s", | 2350 | error("getnameinfo: %.100s", ssh_gai_strerror(r)); |
2278 | (ret != EAI_SYSTEM) ? gai_strerror(ret) : | 2351 | continue; |
2279 | strerror(errno)); | 2352 | } |
2353 | laddr2 = laddr1; | ||
2354 | if (ai->ai_family == AF_INET6) { | ||
2355 | xasprintf(&laddr1, "listenaddress [%s]:%s%s%s\n%s", | ||
2356 | addr, port, | ||
2357 | la->rdomain == NULL ? "" : " rdomain ", | ||
2358 | la->rdomain == NULL ? "" : la->rdomain, | ||
2359 | laddr2); | ||
2280 | } else { | 2360 | } else { |
2281 | laddr2 = laddr1; | 2361 | xasprintf(&laddr1, "listenaddress %s:%s%s%s\n%s", |
2282 | if (ai->ai_family == AF_INET6) | 2362 | addr, port, |
2283 | xasprintf(&laddr1, "listenaddress [%s]:%s\n%s", | 2363 | la->rdomain == NULL ? "" : " rdomain ", |
2284 | addr, port, laddr2); | 2364 | la->rdomain == NULL ? "" : la->rdomain, |
2285 | else | 2365 | laddr2); |
2286 | xasprintf(&laddr1, "listenaddress %s:%s\n%s", | ||
2287 | addr, port, laddr2); | ||
2288 | free(laddr2); | ||
2289 | } | 2366 | } |
2367 | free(laddr2); | ||
2368 | } | ||
2369 | return laddr1; | ||
2370 | } | ||
2371 | |||
2372 | void | ||
2373 | dump_config(ServerOptions *o) | ||
2374 | { | ||
2375 | char *s; | ||
2376 | u_int i; | ||
2377 | |||
2378 | /* these are usually at the top of the config */ | ||
2379 | for (i = 0; i < o->num_ports; i++) | ||
2380 | printf("port %d\n", o->ports[i]); | ||
2381 | dump_cfg_fmtint(sAddressFamily, o->address_family); | ||
2382 | |||
2383 | for (i = 0; i < o->num_listen_addrs; i++) { | ||
2384 | s = format_listen_addrs(&o->listen_addrs[i]); | ||
2385 | printf("%s", s); | ||
2386 | free(s); | ||
2290 | } | 2387 | } |
2291 | printf("%s", laddr1); | ||
2292 | free(laddr1); | ||
2293 | 2388 | ||
2294 | /* integer arguments */ | 2389 | /* integer arguments */ |
2295 | #ifdef USE_PAM | 2390 | #ifdef USE_PAM |