version 1.314, 2017/10/05 15:52:03 |
version 1.315, 2017/10/25 00:15:35 |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/queue.h> |
#include <sys/queue.h> |
|
#include <sys/sysctl.h> |
|
|
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <netinet/ip.h> |
#include <netinet/ip.h> |
|
#include <net/route.h> |
|
|
#include <ctype.h> |
#include <ctype.h> |
#include <netdb.h> |
#include <netdb.h> |
|
|
#include "myproposal.h" |
#include "myproposal.h" |
#include "digest.h" |
#include "digest.h" |
|
|
static void add_listen_addr(ServerOptions *, char *, int); |
static void add_listen_addr(ServerOptions *, const char *, |
static void add_one_listen_addr(ServerOptions *, char *, int); |
const char *, int); |
|
static void add_one_listen_addr(ServerOptions *, const char *, |
|
const char *, int); |
|
|
/* Use of privilege separation or not */ |
/* Use of privilege separation or not */ |
extern int use_privsep; |
extern int use_privsep; |
|
|
options->queued_listen_addrs = NULL; |
options->queued_listen_addrs = NULL; |
options->num_queued_listens = 0; |
options->num_queued_listens = 0; |
options->listen_addrs = NULL; |
options->listen_addrs = NULL; |
|
options->num_listen_addrs = 0; |
options->address_family = -1; |
options->address_family = -1; |
options->num_host_key_files = 0; |
options->num_host_key_files = 0; |
options->num_host_cert_files = 0; |
options->num_host_cert_files = 0; |
|
|
if (options->address_family == -1) |
if (options->address_family == -1) |
options->address_family = AF_UNSPEC; |
options->address_family = AF_UNSPEC; |
if (options->listen_addrs == NULL) |
if (options->listen_addrs == NULL) |
add_listen_addr(options, NULL, 0); |
add_listen_addr(options, NULL, NULL, 0); |
if (options->pid_file == NULL) |
if (options->pid_file == NULL) |
options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); |
options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); |
if (options->login_grace_time == -1) |
if (options->login_grace_time == -1) |
|
|
} |
} |
|
|
static void |
static void |
add_listen_addr(ServerOptions *options, char *addr, int port) |
add_listen_addr(ServerOptions *options, const char *addr, |
|
const char *rdomain, int port) |
{ |
{ |
u_int i; |
u_int i; |
|
|
if (port == 0) |
if (port > 0) |
for (i = 0; i < options->num_ports; i++) |
add_one_listen_addr(options, addr, rdomain, port); |
add_one_listen_addr(options, addr, options->ports[i]); |
else { |
else |
for (i = 0; i < options->num_ports; i++) { |
add_one_listen_addr(options, addr, port); |
add_one_listen_addr(options, addr, rdomain, |
|
options->ports[i]); |
|
} |
|
} |
} |
} |
|
|
static void |
static void |
add_one_listen_addr(ServerOptions *options, char *addr, int port) |
add_one_listen_addr(ServerOptions *options, const char *addr, |
|
const char *rdomain, int port) |
{ |
{ |
struct addrinfo hints, *ai, *aitop; |
struct addrinfo hints, *ai, *aitop; |
char strport[NI_MAXSERV]; |
char strport[NI_MAXSERV]; |
int gaierr; |
int gaierr; |
|
u_int i; |
|
|
|
/* Find listen_addrs entry for this rdomain */ |
|
for (i = 0; i < options->num_listen_addrs; i++) { |
|
if (rdomain == NULL && options->listen_addrs[i].rdomain == NULL) |
|
break; |
|
if (rdomain == NULL || options->listen_addrs[i].rdomain == NULL) |
|
continue; |
|
if (strcmp(rdomain, options->listen_addrs[i].rdomain) == 0) |
|
break; |
|
} |
|
if (i >= options->num_listen_addrs) { |
|
/* No entry for this rdomain; allocate one */ |
|
if (i >= INT_MAX) |
|
fatal("%s: too many listen addresses", __func__); |
|
options->listen_addrs = xrecallocarray(options->listen_addrs, |
|
options->num_listen_addrs, options->num_listen_addrs + 1, |
|
sizeof(*options->listen_addrs)); |
|
i = options->num_listen_addrs++; |
|
if (rdomain != NULL) |
|
options->listen_addrs[i].rdomain = xstrdup(rdomain); |
|
} |
|
/* options->listen_addrs[i] points to the addresses for this rdomain */ |
|
|
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = options->address_family; |
hints.ai_family = options->address_family; |
hints.ai_socktype = SOCK_STREAM; |
hints.ai_socktype = SOCK_STREAM; |
|
|
ssh_gai_strerror(gaierr)); |
ssh_gai_strerror(gaierr)); |
for (ai = aitop; ai->ai_next; ai = ai->ai_next) |
for (ai = aitop; ai->ai_next; ai = ai->ai_next) |
; |
; |
ai->ai_next = options->listen_addrs; |
ai->ai_next = options->listen_addrs[i].addrs; |
options->listen_addrs = aitop; |
options->listen_addrs[i].addrs = aitop; |
} |
} |
|
|
|
/* Returns nonzero if the routing domain name is valid */ |
|
static int |
|
valid_rdomain(const char *name) |
|
{ |
|
const char *errstr; |
|
long long num; |
|
struct rt_tableinfo info; |
|
int mib[6]; |
|
size_t miblen = sizeof(mib); |
|
|
|
if (name == NULL) |
|
return 1; |
|
|
|
num = strtonum(name, 0, 255, &errstr); |
|
if (errstr != NULL) |
|
return 0; |
|
|
|
/* Check whether the table actually exists */ |
|
memset(mib, 0, sizeof(mib)); |
|
mib[0] = CTL_NET; |
|
mib[1] = PF_ROUTE; |
|
mib[4] = NET_RT_TABLE; |
|
mib[5] = (int)num; |
|
if (sysctl(mib, 6, &info, &miblen, NULL, 0) == -1) |
|
return 0; |
|
|
|
return 1; |
|
} |
|
|
/* |
/* |
* Queue a ListenAddress to be processed once we have all of the Ports |
* Queue a ListenAddress to be processed once we have all of the Ports |
* and AddressFamily options. |
* and AddressFamily options. |
*/ |
*/ |
static void |
static void |
queue_listen_addr(ServerOptions *options, char *addr, int port) |
queue_listen_addr(ServerOptions *options, const char *addr, |
|
const char *rdomain, int port) |
{ |
{ |
options->queued_listen_addrs = xreallocarray( |
struct queued_listenaddr *qla; |
options->queued_listen_addrs, options->num_queued_listens + 1, |
|
sizeof(addr)); |
options->queued_listen_addrs = xrecallocarray( |
options->queued_listen_ports = xreallocarray( |
options->queued_listen_addrs, |
options->queued_listen_ports, options->num_queued_listens + 1, |
options->num_queued_listens, options->num_queued_listens + 1, |
sizeof(port)); |
sizeof(*options->queued_listen_addrs)); |
options->queued_listen_addrs[options->num_queued_listens] = |
qla = &options->queued_listen_addrs[options->num_queued_listens++]; |
xstrdup(addr); |
qla->addr = xstrdup(addr); |
options->queued_listen_ports[options->num_queued_listens] = port; |
qla->port = port; |
options->num_queued_listens++; |
qla->rdomain = rdomain == NULL ? NULL : xstrdup(rdomain); |
} |
} |
|
|
/* |
/* |
|
|
process_queued_listen_addrs(ServerOptions *options) |
process_queued_listen_addrs(ServerOptions *options) |
{ |
{ |
u_int i; |
u_int i; |
|
struct queued_listenaddr *qla; |
|
|
if (options->num_ports == 0) |
if (options->num_ports == 0) |
options->ports[options->num_ports++] = SSH_DEFAULT_PORT; |
options->ports[options->num_ports++] = SSH_DEFAULT_PORT; |
|
|
options->address_family = AF_UNSPEC; |
options->address_family = AF_UNSPEC; |
|
|
for (i = 0; i < options->num_queued_listens; i++) { |
for (i = 0; i < options->num_queued_listens; i++) { |
add_listen_addr(options, options->queued_listen_addrs[i], |
qla = &options->queued_listen_addrs[i]; |
options->queued_listen_ports[i]); |
add_listen_addr(options, qla->addr, qla->rdomain, qla->port); |
free(options->queued_listen_addrs[i]); |
free(qla->addr); |
options->queued_listen_addrs[i] = NULL; |
free(qla->rdomain); |
} |
} |
free(options->queued_listen_addrs); |
free(options->queued_listen_addrs); |
options->queued_listen_addrs = NULL; |
options->queued_listen_addrs = NULL; |
free(options->queued_listen_ports); |
|
options->queued_listen_ports = NULL; |
|
options->num_queued_listens = 0; |
options->num_queued_listens = 0; |
} |
} |
|
|
|
|
/* check for bare IPv6 address: no "[]" and 2 or more ":" */ |
/* check for bare IPv6 address: no "[]" and 2 or more ":" */ |
if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL |
if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL |
&& strchr(p+1, ':') != NULL) { |
&& strchr(p+1, ':') != NULL) { |
queue_listen_addr(options, arg, 0); |
|
break; |
|
} |
|
p = hpdelim(&arg); |
|
if (p == NULL) |
|
fatal("%s line %d: bad address:port usage", |
|
filename, linenum); |
|
p = cleanhostname(p); |
|
if (arg == NULL) |
|
port = 0; |
port = 0; |
else if ((port = a2port(arg)) <= 0) |
p = arg; |
fatal("%s line %d: bad port number", filename, linenum); |
} else { |
|
p = hpdelim(&arg); |
|
if (p == NULL) |
|
fatal("%s line %d: bad address:port usage", |
|
filename, linenum); |
|
p = cleanhostname(p); |
|
if (arg == NULL) |
|
port = 0; |
|
else if ((port = a2port(arg)) <= 0) |
|
fatal("%s line %d: bad port number", |
|
filename, linenum); |
|
} |
|
/* Optional routing table */ |
|
arg2 = NULL; |
|
if ((arg = strdelim(&cp)) != NULL) { |
|
if (strcmp(arg, "rdomain") != 0 || |
|
(arg2 = strdelim(&cp)) == NULL) |
|
fatal("%s line %d: bad ListenAddress syntax", |
|
filename, linenum); |
|
if (!valid_rdomain(arg2)) |
|
fatal("%s line %d: bad routing domain", |
|
filename, linenum); |
|
} |
|
|
queue_listen_addr(options, p, port); |
queue_listen_addr(options, p, arg2, port); |
|
|
break; |
break; |
|
|
|
|
printf("\n"); |
printf("\n"); |
} |
} |
|
|
void |
static char * |
dump_config(ServerOptions *o) |
format_listen_addrs(struct listenaddr *la) |
{ |
{ |
u_int i; |
int r; |
int ret; |
|
struct addrinfo *ai; |
struct addrinfo *ai; |
char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; |
char addr[NI_MAXHOST], port[NI_MAXSERV]; |
char *laddr1 = xstrdup(""), *laddr2 = NULL; |
char *laddr1 = xstrdup(""), *laddr2 = NULL; |
|
|
/* these are usually at the top of the config */ |
|
for (i = 0; i < o->num_ports; i++) |
|
printf("port %d\n", o->ports[i]); |
|
dump_cfg_fmtint(sAddressFamily, o->address_family); |
|
|
|
/* |
/* |
* ListenAddress must be after Port. add_one_listen_addr pushes |
* ListenAddress must be after Port. add_one_listen_addr pushes |
* addresses onto a stack, so to maintain ordering we need to |
* addresses onto a stack, so to maintain ordering we need to |
* print these in reverse order. |
* print these in reverse order. |
*/ |
*/ |
for (ai = o->listen_addrs; ai; ai = ai->ai_next) { |
for (ai = la->addrs; ai; ai = ai->ai_next) { |
if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, |
if ((r = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, |
sizeof(addr), port, sizeof(port), |
sizeof(addr), port, sizeof(port), |
NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { |
NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { |
error("getnameinfo failed: %.100s", |
error("getnameinfo: %.100s", ssh_gai_strerror(r)); |
(ret != EAI_SYSTEM) ? gai_strerror(ret) : |
continue; |
strerror(errno)); |
} |
|
laddr2 = laddr1; |
|
if (ai->ai_family == AF_INET6) { |
|
xasprintf(&laddr1, "listenaddress [%s]:%s%s%s\n%s", |
|
addr, port, |
|
la->rdomain == NULL ? "" : " rdomain ", |
|
la->rdomain == NULL ? "" : la->rdomain, |
|
laddr2); |
} else { |
} else { |
laddr2 = laddr1; |
xasprintf(&laddr1, "listenaddress %s:%s%s%s\n%s", |
if (ai->ai_family == AF_INET6) |
addr, port, |
xasprintf(&laddr1, "listenaddress [%s]:%s\n%s", |
la->rdomain == NULL ? "" : " rdomain ", |
addr, port, laddr2); |
la->rdomain == NULL ? "" : la->rdomain, |
else |
laddr2); |
xasprintf(&laddr1, "listenaddress %s:%s\n%s", |
|
addr, port, laddr2); |
|
free(laddr2); |
|
} |
} |
|
free(laddr2); |
} |
} |
printf("%s", laddr1); |
return laddr1; |
free(laddr1); |
} |
|
|
|
void |
|
dump_config(ServerOptions *o) |
|
{ |
|
char *s; |
|
u_int i; |
|
|
|
/* these are usually at the top of the config */ |
|
for (i = 0; i < o->num_ports; i++) |
|
printf("port %d\n", o->ports[i]); |
|
dump_cfg_fmtint(sAddressFamily, o->address_family); |
|
|
|
for (i = 0; i < o->num_listen_addrs; i++) { |
|
s = format_listen_addrs(&o->listen_addrs[i]); |
|
printf("%s", s); |
|
free(s); |
|
} |
|
|
/* integer arguments */ |
/* integer arguments */ |
dump_cfg_int(sLoginGraceTime, o->login_grace_time); |
dump_cfg_int(sLoginGraceTime, o->login_grace_time); |