diff options
Diffstat (limited to 'crypto/openssh/channels.c')
-rw-r--r-- | crypto/openssh/channels.c | 827 |
1 files changed, 645 insertions, 182 deletions
diff --git a/crypto/openssh/channels.c b/crypto/openssh/channels.c index 9efe89c..a84b487 100644 --- a/crypto/openssh/channels.c +++ b/crypto/openssh/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.331 2014/02/26 20:29:29 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.347 2015/07/01 02:26:31 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -42,6 +42,8 @@ #include "includes.h" #include <sys/types.h> +#include <sys/param.h> /* MIN MAX */ +#include <sys/stat.h> #include <sys/ioctl.h> #include <sys/un.h> #include <sys/socket.h> @@ -55,6 +57,9 @@ #include <errno.h> #include <fcntl.h> #include <netdb.h> +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -107,10 +112,15 @@ static int channel_max_fd = 0; * a corrupt remote server from accessing arbitrary TCP/IP ports on our local * network (which might be behind a firewall). */ +/* XXX: streamlocal wants a path instead of host:port */ +/* Overload host_to_connect; we could just make this match Forward */ +/* XXX - can we use listen_host instead of listen_path? */ typedef struct { char *host_to_connect; /* Connect to 'host'. */ - u_short port_to_connect; /* Connect to 'port'. */ - u_short listen_port; /* Remote side should listen port number. */ + int port_to_connect; /* Connect to 'port'. */ + char *listen_host; /* Remote side should listen address. */ + char *listen_path; /* Remote side should listen path. */ + int listen_port; /* Remote side should listen port. */ } ForwardPermission; /* List of all permitted host/port pairs to connect by the user. */ @@ -151,6 +161,9 @@ static char *x11_saved_proto = NULL; static char *x11_saved_data = NULL; static u_int x11_saved_data_len = 0; +/* Deadline after which all X11 connections are refused */ +static u_int x11_refuse_time; + /* * Fake X11 authentication data. This is what the server will be sending us; * we should replace any occurrences of this by the real data. @@ -296,7 +309,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, if (channels_alloc > 10000) fatal("channel_new: internal error: channels_alloc %d " "too big.", channels_alloc); - channels = xrealloc(channels, channels_alloc + 10, + channels = xreallocarray(channels, channels_alloc + 10, sizeof(Channel *)); channels_alloc += 10; debug2("channel: expanding %d", channels_alloc); @@ -473,6 +486,8 @@ channel_stop_listening(void) case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: channel_close_fd(&c->sock); channel_free(c); break; @@ -535,6 +550,8 @@ channel_still_open(void) case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: if (!compat20) @@ -581,6 +598,8 @@ channel_find_open(void) case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_AUTH_SOCKET: @@ -631,6 +650,8 @@ channel_open_message(void) case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_OPENING: @@ -655,7 +676,7 @@ channel_open_message(void) } } buffer_append(&buffer, "\0", 1); - cp = xstrdup(buffer_ptr(&buffer)); + cp = xstrdup((char *)buffer_ptr(&buffer)); buffer_free(&buffer); return cp; } @@ -894,6 +915,13 @@ x11_open_helper(Buffer *b) u_char *ucp; u_int proto_len, data_len; + /* Is this being called after the refusal deadline? */ + if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { + verbose("Rejected X11 connection after ForwardX11Timeout " + "expired"); + return -1; + } + /* Check if the fixed size part of the packet is in buffer. */ if (buffer_len(b) < 12) return 0; @@ -1041,7 +1069,7 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) len = sizeof(s4_req); if (have < len) return 0; - p = buffer_ptr(&c->input); + p = (char *)buffer_ptr(&c->input); need = 1; /* SOCKS4A uses an invalid IP address 0.0.0.x */ @@ -1071,7 +1099,7 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) buffer_get(&c->input, (char *)&s4_req.dest_port, 2); buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); have = buffer_len(&c->input); - p = buffer_ptr(&c->input); + p = (char *)buffer_ptr(&c->input); if (memchr(p, '\0', have) == NULL) fatal("channel %d: decode socks4: user not nul terminated", c->self); @@ -1091,7 +1119,7 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) c->path = xstrdup(host); } else { /* SOCKS4A: two strings */ have = buffer_len(&c->input); - p = buffer_ptr(&c->input); + p = (char *)buffer_ptr(&c->input); len = strlen(p); debug2("channel %d: decode socks4a: host %s/%d", c->self, p, len); @@ -1386,7 +1414,6 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) static void port_open_helper(Channel *c, char *rtype) { - int direct; char buf[1024]; char *local_ipaddr = get_local_ipaddr(c->sock); int local_port = c->sock == -1 ? 65536 : get_sock_port(c->sock, 1); @@ -1400,8 +1427,6 @@ port_open_helper(Channel *c, char *rtype) remote_port = 65535; } - direct = (strcmp(rtype, "direct-tcpip") == 0); - snprintf(buf, sizeof buf, "%s: listening port %d for %.100s port %d, " "connect from %.200s port %d to %.100s port %d", @@ -1417,18 +1442,29 @@ port_open_helper(Channel *c, char *rtype) packet_put_int(c->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); - if (direct) { + if (strcmp(rtype, "direct-tcpip") == 0) { /* target host, port */ packet_put_cstring(c->path); packet_put_int(c->host_port); + } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { + /* target path */ + packet_put_cstring(c->path); + } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* listen path */ + packet_put_cstring(c->path); } else { /* listen address, port */ packet_put_cstring(c->path); packet_put_int(local_port); } - /* originator host and port */ - packet_put_cstring(remote_ipaddr); - packet_put_int((u_int)remote_port); + if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* reserved for future owner/mode info */ + packet_put_cstring(""); + } else { + /* originator host and port */ + packet_put_cstring(remote_ipaddr); + packet_put_int((u_int)remote_port); + } packet_send(); } else { packet_start(SSH_MSG_PORT_OPEN); @@ -1457,6 +1493,12 @@ channel_set_reuseaddr(int fd) error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); } +void +channel_set_x11_refuse_time(u_int refuse_time) +{ + x11_refuse_time = refuse_time; +} + /* * This socket is listening for connections to a forwarded TCP/IP port. */ @@ -1478,14 +1520,18 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) if (c->type == SSH_CHANNEL_RPORT_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-tcpip"; + } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "forwarded-streamlocal@openssh.com"; + } else if (c->host_port == PORT_STREAMLOCAL) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-streamlocal@openssh.com"; + } else if (c->host_port == 0) { + nextstate = SSH_CHANNEL_DYNAMIC; + rtype = "dynamic-tcpip"; } else { - if (c->host_port == 0) { - nextstate = SSH_CHANNEL_DYNAMIC; - rtype = "dynamic-tcpip"; - } else { - nextstate = SSH_CHANNEL_OPENING; - rtype = "direct-tcpip"; - } + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-tcpip"; } addrlen = sizeof(addr); @@ -1498,7 +1544,8 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) c->notbefore = monotime() + 1; return; } - set_nodelay(newsock); + if (c->host_port != PORT_STREAMLOCAL) + set_nodelay(newsock); nc = channel_new(rtype, nextstate, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, rtype, 1); nc->listening_port = c->listening_port; @@ -1987,6 +2034,8 @@ channel_handler_init_20(void) channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; @@ -1997,6 +2046,8 @@ channel_handler_init_20(void) channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; @@ -2151,14 +2202,14 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, nfdset = howmany(n+1, NFDBITS); /* Explicitly test here, because xrealloc isn't always called */ - if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) + if (nfdset && SIZE_MAX / nfdset < sizeof(fd_mask)) fatal("channel_prepare_select: max_fd (%d) is too large", n); sz = nfdset * sizeof(fd_mask); /* perhaps check sz < nalloc/2 and shrink? */ if (*readsetp == NULL || sz > *nallocp) { - *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); - *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); + *readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask)); + *writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask)); *nallocp = sz; } *maxfdp = n; @@ -2235,7 +2286,7 @@ channel_output_poll(void) packet_put_int(c->remote_id); packet_put_string(data, dlen); packet_send(); - c->remote_window -= dlen + 4; + c->remote_window -= dlen; free(data); } continue; @@ -2311,11 +2362,11 @@ channel_output_poll(void) /* -- protocol input */ /* ARGSUSED */ -void +int channel_input_data(int type, u_int32_t seq, void *ctxt) { int id; - char *data; + const u_char *data; u_int data_len, win_len; Channel *c; @@ -2328,7 +2379,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) /* Ignore any data for non-open channels (might happen on close) */ if (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_X11_OPEN) - return; + return 0; /* Get the data. */ data = packet_get_string_ptr(&data_len); @@ -2348,7 +2399,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) c->local_window -= win_len; c->local_consumed += win_len; } - return; + return 0; } if (compat20) { @@ -2359,7 +2410,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) if (win_len > c->local_window) { logit("channel %d: rcvd too much data %d, win %d", c->self, win_len, c->local_window); - return; + return 0; } c->local_window -= win_len; } @@ -2368,10 +2419,11 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) else buffer_append(&c->output, data, data_len); packet_check_eom(); + return 0; } /* ARGSUSED */ -void +int channel_input_extended_data(int type, u_int32_t seq, void *ctxt) { int id; @@ -2387,7 +2439,7 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) packet_disconnect("Received extended_data for bad channel %d.", id); if (c->type != SSH_CHANNEL_OPEN) { logit("channel %d: ext data for non open", id); - return; + return 0; } if (c->flags & CHAN_EOF_RCVD) { if (datafellows & SSH_BUG_EXTEOF) @@ -2401,7 +2453,7 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) c->extended_usage != CHAN_EXTENDED_WRITE || tcode != SSH2_EXTENDED_DATA_STDERR) { logit("channel %d: bad ext data", c->self); - return; + return 0; } data = packet_get_string(&data_len); packet_check_eom(); @@ -2409,16 +2461,17 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) logit("channel %d: rcvd too much extended_data %d, win %d", c->self, data_len, c->local_window); free(data); - return; + return 0; } debug2("channel %d: rcvd ext data %d", c->self, data_len); c->local_window -= data_len; buffer_append(&c->extended, data, data_len); free(data); + return 0; } /* ARGSUSED */ -void +int channel_input_ieof(int type, u_int32_t seq, void *ctxt) { int id; @@ -2438,11 +2491,11 @@ channel_input_ieof(int type, u_int32_t seq, void *ctxt) if (buffer_len(&c->input) == 0) chan_ibuf_empty(c); } - + return 0; } /* ARGSUSED */ -void +int channel_input_close(int type, u_int32_t seq, void *ctxt) { int id; @@ -2477,11 +2530,12 @@ channel_input_close(int type, u_int32_t seq, void *ctxt) buffer_clear(&c->input); c->type = SSH_CHANNEL_OUTPUT_DRAINING; } + return 0; } /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ /* ARGSUSED */ -void +int channel_input_oclose(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); @@ -2491,10 +2545,11 @@ channel_input_oclose(int type, u_int32_t seq, void *ctxt) if (c == NULL) packet_disconnect("Received oclose for nonexistent channel %d.", id); chan_rcvd_oclose(c); + return 0; } /* ARGSUSED */ -void +int channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); @@ -2508,10 +2563,11 @@ channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) packet_disconnect("Received close confirmation for " "non-closed channel %d (type %d).", id, c->type); channel_free(c); + return 0; } /* ARGSUSED */ -void +int channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) { int id, remote_id; @@ -2540,6 +2596,7 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) c->remote_window, c->remote_maxpacket); } packet_check_eom(); + return 0; } static char * @@ -2559,7 +2616,7 @@ reason2txt(int reason) } /* ARGSUSED */ -void +int channel_input_open_failure(int type, u_int32_t seq, void *ctxt) { int id, reason; @@ -2591,18 +2648,19 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt) packet_check_eom(); /* Schedule the channel for cleanup/deletion. */ chan_mark_dead(c); + return 0; } /* ARGSUSED */ -void +int channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) { Channel *c; int id; - u_int adjust; + u_int adjust, tmp; if (!compat20) - return; + return 0; /* Get the channel number and verify it. */ id = packet_get_int(); @@ -2610,16 +2668,20 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) if (c == NULL) { logit("Received window adjust for non-open channel %d.", id); - return; + return 0; } adjust = packet_get_int(); packet_check_eom(); debug2("channel %d: rcvd adjust %u", id, adjust); - c->remote_window += adjust; + if ((tmp = c->remote_window + adjust) < c->remote_window) + fatal("channel %d: adjust %u overflows remote window %u", + id, adjust, c->remote_window); + c->remote_window = tmp; + return 0; } /* ARGSUSED */ -void +int channel_input_port_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -2637,7 +2699,7 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt) originator_string = xstrdup("unknown (remote did not supply name)"); } packet_check_eom(); - c = channel_connect_to(host, host_port, + c = channel_connect_to_port(host, host_port, "connected socket", originator_string); free(originator_string); free(host); @@ -2647,10 +2709,11 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt) packet_send(); } else c->remote_id = remote_id; + return 0; } /* ARGSUSED */ -void +int channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) { Channel *c; @@ -2667,15 +2730,15 @@ channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) if ((c = channel_lookup(id)) == NULL) { logit("channel_input_status_confirm: %d: unknown", id); - return; + return 0; } - ; if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) - return; + return 0; cc->cb(type, c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); explicit_bzero(cc, sizeof(*cc)); free(cc); + return 0; } /* -- tcp forwarding */ @@ -2700,23 +2763,24 @@ channel_set_af(int af) * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR * "" (empty string), "*" -> wildcard v4/v6 * "localhost" -> loopback v4/v6 + * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set */ static const char * channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, - int is_client, int gateway_ports) + int is_client, struct ForwardOptions *fwd_opts) { const char *addr = NULL; int wildcard = 0; if (listen_addr == NULL) { /* No address specified: default to gateway_ports setting */ - if (gateway_ports) + if (fwd_opts->gateway_ports) wildcard = 1; - } else if (gateway_ports || is_client) { + } else if (fwd_opts->gateway_ports || is_client) { if (((datafellows & SSH_OLD_FORWARD_ADDR) && strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || - (!is_client && gateway_ports == 1)) { + (!is_client && fwd_opts->gateway_ports == 1)) { wildcard = 1; /* * Notify client if they requested a specific listen @@ -2729,9 +2793,20 @@ channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, "\"%s\" overridden by server " "GatewayPorts", listen_addr); } - } - else if (strcmp(listen_addr, "localhost") != 0) + } else if (strcmp(listen_addr, "localhost") != 0 || + strcmp(listen_addr, "127.0.0.1") == 0 || + strcmp(listen_addr, "::1") == 0) { + /* Accept localhost address when GatewayPorts=yes */ addr = listen_addr; + } + } else if (strcmp(listen_addr, "127.0.0.1") == 0 || + strcmp(listen_addr, "::1") == 0) { + /* + * If a specific IPv4/IPv6 localhost address has been + * requested then accept it even if gateway_ports is in + * effect. This allows the client to prefer IPv4 or IPv6. + */ + addr = listen_addr; } if (wildcardp != NULL) *wildcardp = wildcard; @@ -2739,9 +2814,8 @@ channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, } static int -channel_setup_fwd_listener(int type, const char *listen_addr, - u_short listen_port, int *allocated_listen_port, - const char *host_to_connect, u_short port_to_connect, int gateway_ports) +channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, + int *allocated_listen_port, struct ForwardOptions *fwd_opts) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; @@ -2750,23 +2824,27 @@ channel_setup_fwd_listener(int type, const char *listen_addr, char ntop[NI_MAXHOST], strport[NI_MAXSERV]; in_port_t *lport_p; - host = (type == SSH_CHANNEL_RPORT_LISTENER) ? - listen_addr : host_to_connect; is_client = (type == SSH_CHANNEL_PORT_LISTENER); - if (host == NULL) { - error("No forward host name."); - return 0; - } - if (strlen(host) >= NI_MAXHOST) { - error("Forward host name too long."); - return 0; + if (is_client && fwd->connect_path != NULL) { + host = fwd->connect_path; + } else { + host = (type == SSH_CHANNEL_RPORT_LISTENER) ? + fwd->listen_host : fwd->connect_host; + if (host == NULL) { + error("No forward host name."); + return 0; + } + if (strlen(host) >= NI_MAXHOST) { + error("Forward host name too long."); + return 0; + } } /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ - addr = channel_fwd_bind_addr(listen_addr, &wildcard, - is_client, gateway_ports); - debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", + addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, + is_client, fwd_opts); + debug3("%s: type %d wildcard %d addr %s", __func__, type, wildcard, (addr == NULL) ? "NULL" : addr); /* @@ -2777,15 +2855,14 @@ channel_setup_fwd_listener(int type, const char *listen_addr, hints.ai_family = IPv4or6; hints.ai_flags = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", listen_port); + snprintf(strport, sizeof strport, "%d", fwd->listen_port); if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { if (addr == NULL) { /* This really shouldn't happen */ packet_disconnect("getaddrinfo: fatal error: %s", ssh_gai_strerror(r)); } else { - error("channel_setup_fwd_listener: " - "getaddrinfo(%.64s): %s", addr, + error("%s: getaddrinfo(%.64s): %s", __func__, addr, ssh_gai_strerror(r)); } return 0; @@ -2809,13 +2886,13 @@ channel_setup_fwd_listener(int type, const char *listen_addr, * If allocating a port for -R forwards, then use the * same port for all address families. */ - if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && + if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port > 0) *lport_p = htons(*allocated_listen_port); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("channel_setup_fwd_listener: getnameinfo failed"); + error("%s: getnameinfo failed", __func__); continue; } /* Create a port to listen for the host. */ @@ -2852,10 +2929,10 @@ channel_setup_fwd_listener(int type, const char *listen_addr, } /* - * listen_port == 0 requests a dynamically allocated port - + * fwd->listen_port == 0 requests a dynamically allocated port - * record what we got. */ - if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && + if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port == 0) { *allocated_listen_port = get_sock_port(sock, 1); @@ -2868,24 +2945,98 @@ channel_setup_fwd_listener(int type, const char *listen_addr, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "port listener", 1); c->path = xstrdup(host); - c->host_port = port_to_connect; + c->host_port = fwd->connect_port; c->listening_addr = addr == NULL ? NULL : xstrdup(addr); - if (listen_port == 0 && allocated_listen_port != NULL && + if (fwd->listen_port == 0 && allocated_listen_port != NULL && !(datafellows & SSH_BUG_DYNAMIC_RPORT)) c->listening_port = *allocated_listen_port; else - c->listening_port = listen_port; + c->listening_port = fwd->listen_port; success = 1; } if (success == 0) - error("channel_setup_fwd_listener: cannot listen to port: %d", - listen_port); + error("%s: cannot listen to port: %d", __func__, + fwd->listen_port); freeaddrinfo(aitop); return success; } -int -channel_cancel_rport_listener(const char *host, u_short port) +static int +channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, + struct ForwardOptions *fwd_opts) +{ + struct sockaddr_un sunaddr; + const char *path; + Channel *c; + int port, sock; + mode_t omask; + + switch (type) { + case SSH_CHANNEL_UNIX_LISTENER: + if (fwd->connect_path != NULL) { + if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { + error("Local connecting path too long: %s", + fwd->connect_path); + return 0; + } + path = fwd->connect_path; + port = PORT_STREAMLOCAL; + } else { + if (fwd->connect_host == NULL) { + error("No forward host name."); + return 0; + } + if (strlen(fwd->connect_host) >= NI_MAXHOST) { + error("Forward host name too long."); + return 0; + } + path = fwd->connect_host; + port = fwd->connect_port; + } + break; + case SSH_CHANNEL_RUNIX_LISTENER: + path = fwd->listen_path; + port = PORT_STREAMLOCAL; + break; + default: + error("%s: unexpected channel type %d", __func__, type); + return 0; + } + + if (fwd->listen_path == NULL) { + error("No forward path name."); + return 0; + } + if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { + error("Local listening path too long: %s", fwd->listen_path); + return 0; + } + + debug3("%s: type %d path %s", __func__, type, fwd->listen_path); + + /* Start a Unix domain listener. */ + omask = umask(fwd_opts->streamlocal_bind_mask); + sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, + fwd_opts->streamlocal_bind_unlink); + umask(omask); + if (sock < 0) + return 0; + + debug("Local forwarding listening on path %s.", fwd->listen_path); + + /* Allocate a channel number for the socket. */ + c = channel_new("unix listener", type, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "unix listener", 1); + c->path = xstrdup(path); + c->host_port = port; + c->listening_port = PORT_STREAMLOCAL; + c->listening_addr = xstrdup(fwd->listen_path); + return 1; +} + +static int +channel_cancel_rport_listener_tcpip(const char *host, u_short port) { u_int i; int found = 0; @@ -2904,13 +3055,44 @@ channel_cancel_rport_listener(const char *host, u_short port) return (found); } +static int +channel_cancel_rport_listener_streamlocal(const char *path) +{ + u_int i; + int found = 0; + + for (i = 0; i < channels_alloc; i++) { + Channel *c = channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) + continue; + if (c->path == NULL) + continue; + if (strcmp(c->path, path) == 0) { + debug2("%s: close channel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + int -channel_cancel_lport_listener(const char *lhost, u_short lport, - int cport, int gateway_ports) +channel_cancel_rport_listener(struct Forward *fwd) +{ + if (fwd->listen_path != NULL) + return channel_cancel_rport_listener_streamlocal(fwd->listen_path); + else + return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port); +} + +static int +channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, + int cport, struct ForwardOptions *fwd_opts) { u_int i; int found = 0; - const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, gateway_ports); + const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; @@ -2939,24 +3121,68 @@ channel_cancel_lport_listener(const char *lhost, u_short lport, return (found); } +static int +channel_cancel_lport_listener_streamlocal(const char *path) +{ + u_int i; + int found = 0; + + if (path == NULL) { + error("%s: no path specified.", __func__); + return 0; + } + + for (i = 0; i < channels_alloc; i++) { + Channel *c = channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) + continue; + if (c->listening_addr == NULL) + continue; + if (strcmp(c->listening_addr, path) == 0) { + debug2("%s: close channel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + +int +channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) +{ + if (fwd->listen_path != NULL) + return channel_cancel_lport_listener_streamlocal(fwd->listen_path); + else + return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts); +} + /* protocol local port fwd, used by ssh (and sshd in v1) */ int -channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, - const char *host_to_connect, u_short port_to_connect, int gateway_ports) +channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts) { - return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, - listen_host, listen_port, NULL, host_to_connect, port_to_connect, - gateway_ports); + if (fwd->listen_path != NULL) { + return channel_setup_fwd_listener_streamlocal( + SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); + } else { + return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER, + fwd, NULL, fwd_opts); + } } /* protocol v2 remote port fwd, used by sshd */ int -channel_setup_remote_fwd_listener(const char *listen_address, - u_short listen_port, int *allocated_listen_port, int gateway_ports) +channel_setup_remote_fwd_listener(struct Forward *fwd, + int *allocated_listen_port, struct ForwardOptions *fwd_opts) { - return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, - listen_address, listen_port, allocated_listen_port, - NULL, 0, gateway_ports); + if (fwd->listen_path != NULL) { + return channel_setup_fwd_listener_streamlocal( + SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); + } else { + return channel_setup_fwd_listener_tcpip( + SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, + fwd_opts); + } } /* @@ -2987,27 +3213,32 @@ channel_rfwd_bind_host(const char *listen_host) * channel_update_permitted_opens(). */ int -channel_request_remote_forwarding(const char *listen_host, u_short listen_port, - const char *host_to_connect, u_short port_to_connect) +channel_request_remote_forwarding(struct Forward *fwd) { int type, success = 0, idx = -1; /* Send the forward request to the remote side. */ if (compat20) { packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("tcpip-forward"); - packet_put_char(1); /* boolean: want reply */ - packet_put_cstring(channel_rfwd_bind_host(listen_host)); - packet_put_int(listen_port); + if (fwd->listen_path != NULL) { + packet_put_cstring("streamlocal-forward@openssh.com"); + packet_put_char(1); /* boolean: want reply */ + packet_put_cstring(fwd->listen_path); + } else { + packet_put_cstring("tcpip-forward"); + packet_put_char(1); /* boolean: want reply */ + packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host)); + packet_put_int(fwd->listen_port); + } packet_send(); packet_write_wait(); /* Assume that server accepts the request */ success = 1; - } else { + } else if (fwd->listen_path == NULL) { packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); - packet_put_int(listen_port); - packet_put_cstring(host_to_connect); - packet_put_int(port_to_connect); + packet_put_int(fwd->listen_port); + packet_put_cstring(fwd->connect_host); + packet_put_int(fwd->connect_port); packet_send(); packet_write_wait(); @@ -3024,25 +3255,102 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, packet_disconnect("Protocol error for port forward request:" "received packet type %d.", type); } + } else { + logit("Warning: Server does not support remote stream local forwarding."); } if (success) { /* Record that connection to this host/port is permitted. */ - permitted_opens = xrealloc(permitted_opens, + permitted_opens = xreallocarray(permitted_opens, num_permitted_opens + 1, sizeof(*permitted_opens)); idx = num_permitted_opens++; - permitted_opens[idx].host_to_connect = xstrdup(host_to_connect); - permitted_opens[idx].port_to_connect = port_to_connect; - permitted_opens[idx].listen_port = listen_port; + if (fwd->connect_path != NULL) { + permitted_opens[idx].host_to_connect = + xstrdup(fwd->connect_path); + permitted_opens[idx].port_to_connect = + PORT_STREAMLOCAL; + } else { + permitted_opens[idx].host_to_connect = + xstrdup(fwd->connect_host); + permitted_opens[idx].port_to_connect = + fwd->connect_port; + } + if (fwd->listen_path != NULL) { + permitted_opens[idx].listen_host = NULL; + permitted_opens[idx].listen_path = + xstrdup(fwd->listen_path); + permitted_opens[idx].listen_port = PORT_STREAMLOCAL; + } else { + permitted_opens[idx].listen_host = + fwd->listen_host ? xstrdup(fwd->listen_host) : NULL; + permitted_opens[idx].listen_path = NULL; + permitted_opens[idx].listen_port = fwd->listen_port; + } } return (idx); } +static int +open_match(ForwardPermission *allowed_open, const char *requestedhost, + int requestedport) +{ + if (allowed_open->host_to_connect == NULL) + return 0; + if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && + allowed_open->port_to_connect != requestedport) + return 0; + if (strcmp(allowed_open->host_to_connect, requestedhost) != 0) + return 0; + return 1; +} + +/* + * Note that in the listen host/port case + * we don't support FWD_PERMIT_ANY_PORT and + * need to translate between the configured-host (listen_host) + * and what we've sent to the remote server (channel_rfwd_bind_host) + */ +static int +open_listen_match_tcpip(ForwardPermission *allowed_open, + const char *requestedhost, u_short requestedport, int translate) +{ + const char *allowed_host; + + if (allowed_open->host_to_connect == NULL) + return 0; + if (allowed_open->listen_port != requestedport) + return 0; + if (!translate && allowed_open->listen_host == NULL && + requestedhost == NULL) + return 1; + allowed_host = translate ? + channel_rfwd_bind_host(allowed_open->listen_host) : + allowed_open->listen_host; + if (allowed_host == NULL || + strcmp(allowed_host, requestedhost) != 0) + return 0; + return 1; +} + +static int +open_listen_match_streamlocal(ForwardPermission *allowed_open, + const char *requestedpath) +{ + if (allowed_open->host_to_connect == NULL) + return 0; + if (allowed_open->listen_port != PORT_STREAMLOCAL) + return 0; + if (allowed_open->listen_path == NULL || + strcmp(allowed_open->listen_path, requestedpath) != 0) + return 0; + return 1; +} + /* * Request cancellation of remote forwarding of connection host:port from * local side. */ -int -channel_request_rforward_cancel(const char *host, u_short port) +static int +channel_request_rforward_cancel_tcpip(const char *host, u_short port) { int i; @@ -3050,8 +3358,7 @@ channel_request_rforward_cancel(const char *host, u_short port) return -1; for (i = 0; i < num_permitted_opens; i++) { - if (permitted_opens[i].host_to_connect != NULL && - permitted_opens[i].listen_port == port) + if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0)) break; } if (i >= num_permitted_opens) { @@ -3069,9 +3376,64 @@ channel_request_rforward_cancel(const char *host, u_short port) permitted_opens[i].port_to_connect = 0; free(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; + free(permitted_opens[i].listen_host); + permitted_opens[i].listen_host = NULL; + permitted_opens[i].listen_path = NULL; + + return 0; +} + +/* + * Request cancellation of remote forwarding of Unix domain socket + * path from local side. + */ +static int +channel_request_rforward_cancel_streamlocal(const char *path) +{ + int i; + + if (!compat20) + return -1; + + for (i = 0; i < num_permitted_opens; i++) { + if (open_listen_match_streamlocal(&permitted_opens[i], path)) + break; + } + if (i >= num_permitted_opens) { + debug("%s: requested forward not found", __func__); + return -1; + } + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-streamlocal-forward@openssh.com"); + packet_put_char(0); + packet_put_cstring(path); + packet_send(); + + permitted_opens[i].listen_port = 0; + permitted_opens[i].port_to_connect = 0; + free(permitted_opens[i].host_to_connect); + permitted_opens[i].host_to_connect = NULL; + permitted_opens[i].listen_host = NULL; + free(permitted_opens[i].listen_path); + permitted_opens[i].listen_path = NULL; return 0; } + +/* + * Request cancellation of remote forwarding of a connection from local side. + */ +int +channel_request_rforward_cancel(struct Forward *fwd) +{ + if (fwd->listen_path != NULL) { + return (channel_request_rforward_cancel_streamlocal( + fwd->listen_path)); + } else { + return (channel_request_rforward_cancel_tcpip(fwd->listen_host, + fwd->listen_port ? fwd->listen_port : fwd->allocated_port)); + } +} /* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates @@ -3079,36 +3441,35 @@ channel_request_rforward_cancel(const char *host, u_short port) * message if there was an error). */ int -channel_input_port_forward_request(int is_root, int gateway_ports) +channel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts) { - u_short port, host_port; int success = 0; - char *hostname; + struct Forward fwd; /* Get arguments from the packet. */ - port = packet_get_int(); - hostname = packet_get_string(NULL); - host_port = packet_get_int(); + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_port = packet_get_int(); + fwd.connect_host = packet_get_string(NULL); + fwd.connect_port = packet_get_int(); #ifndef HAVE_CYGWIN /* * Check that an unprivileged user is not trying to forward a * privileged port. */ - if (port < IPPORT_RESERVED && !is_root) + if (fwd.listen_port < IPPORT_RESERVED && !is_root) packet_disconnect( "Requested forwarding of port %d but user is not root.", - port); - if (host_port == 0) + fwd.listen_port); + if (fwd.connect_port == 0) packet_disconnect("Dynamic forwarding denied."); #endif /* Initiate forwarding */ - success = channel_setup_local_fwd_listener(NULL, port, hostname, - host_port, gateway_ports); + success = channel_setup_local_fwd_listener(&fwd, fwd_opts); /* Free the argument string. */ - free(hostname); + free(fwd.connect_host); return (success ? 0 : -1); } @@ -3130,10 +3491,13 @@ channel_add_permitted_opens(char *host, int port) { debug("allow port forwarding to host %s port %d", host, port); - permitted_opens = xrealloc(permitted_opens, + permitted_opens = xreallocarray(permitted_opens, num_permitted_opens + 1, sizeof(*permitted_opens)); permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); permitted_opens[num_permitted_opens].port_to_connect = port; + permitted_opens[num_permitted_opens].listen_host = NULL; + permitted_opens[num_permitted_opens].listen_path = NULL; + permitted_opens[num_permitted_opens].listen_port = 0; num_permitted_opens++; all_opens_permitted = 0; @@ -3165,6 +3529,10 @@ channel_update_permitted_opens(int idx, int newport) permitted_opens[idx].port_to_connect = 0; free(permitted_opens[idx].host_to_connect); permitted_opens[idx].host_to_connect = NULL; + free(permitted_opens[idx].listen_host); + permitted_opens[idx].listen_host = NULL; + free(permitted_opens[idx].listen_path); + permitted_opens[idx].listen_path = NULL; } } @@ -3173,11 +3541,14 @@ channel_add_adm_permitted_opens(char *host, int port) { debug("config allows port forwarding to host %s port %d", host, port); - permitted_adm_opens = xrealloc(permitted_adm_opens, + permitted_adm_opens = xreallocarray(permitted_adm_opens, num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); permitted_adm_opens[num_adm_permitted_opens].host_to_connect = xstrdup(host); permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; + permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL; + permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL; + permitted_adm_opens[num_adm_permitted_opens].listen_port = 0; return ++num_adm_permitted_opens; } @@ -3185,7 +3556,7 @@ void channel_disable_adm_local_opens(void) { channel_clear_adm_permitted_opens(); - permitted_adm_opens = xmalloc(sizeof(*permitted_adm_opens)); + permitted_adm_opens = xcalloc(sizeof(*permitted_adm_opens), 1); permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; num_adm_permitted_opens = 1; } @@ -3195,8 +3566,11 @@ channel_clear_permitted_opens(void) { int i; - for (i = 0; i < num_permitted_opens; i++) + for (i = 0; i < num_permitted_opens; i++) { free(permitted_opens[i].host_to_connect); + free(permitted_opens[i].listen_host); + free(permitted_opens[i].listen_path); + } free(permitted_opens); permitted_opens = NULL; num_permitted_opens = 0; @@ -3207,8 +3581,11 @@ channel_clear_adm_permitted_opens(void) { int i; - for (i = 0; i < num_adm_permitted_opens; i++) + for (i = 0; i < num_adm_permitted_opens; i++) { free(permitted_adm_opens[i].host_to_connect); + free(permitted_adm_opens[i].listen_host); + free(permitted_adm_opens[i].listen_path); + } free(permitted_adm_opens); permitted_adm_opens = NULL; num_adm_permitted_opens = 0; @@ -3246,30 +3623,32 @@ permitopen_port(const char *p) return -1; } -static int -port_match(u_short allowedport, u_short requestedport) -{ - if (allowedport == FWD_PERMIT_ANY_PORT || - allowedport == requestedport) - return 1; - return 0; -} - /* Try to start non-blocking connect to next host in cctx list */ static int connect_next(struct channel_connect *cctx) { int sock, saved_errno; - char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + struct sockaddr_un *sunaddr; + char ntop[NI_MAXHOST], strport[MAX(NI_MAXSERV,sizeof(sunaddr->sun_path))]; for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { - if (cctx->ai->ai_family != AF_INET && - cctx->ai->ai_family != AF_INET6) - continue; - if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("connect_next: getnameinfo failed"); + switch (cctx->ai->ai_family) { + case AF_UNIX: + /* unix:pathname instead of host:port */ + sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; + strlcpy(ntop, "unix", sizeof(ntop)); + strlcpy(strport, sunaddr->sun_path, sizeof(strport)); + break; + case AF_INET: + case AF_INET6: + if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("connect_next: getnameinfo failed"); + continue; + } + break; + default: continue; } if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, @@ -3292,10 +3671,11 @@ connect_next(struct channel_connect *cctx) errno = saved_errno; continue; /* fail -- try next */ } + if (cctx->ai->ai_family != AF_UNIX) + set_nodelay(sock); debug("connect_next: host %.100s ([%.100s]:%s) " "in progress, fd=%d", cctx->host, ntop, strport, sock); cctx->ai = cctx->ai->ai_next; - set_nodelay(sock); return sock; } return -1; @@ -3305,14 +3685,18 @@ static void channel_connect_ctx_free(struct channel_connect *cctx) { free(cctx->host); - if (cctx->aitop) - freeaddrinfo(cctx->aitop); + if (cctx->aitop) { + if (cctx->aitop->ai_family == AF_UNIX) + free(cctx->aitop); + else + freeaddrinfo(cctx->aitop); + } memset(cctx, 0, sizeof(*cctx)); } -/* Return CONNECTING channel to remote host, port */ +/* Return CONNECTING channel to remote host:port or local socket path */ static Channel * -connect_to(const char *host, u_short port, char *ctype, char *rname) +connect_to(const char *name, int port, char *ctype, char *rname) { struct addrinfo hints; int gaierr; @@ -3322,23 +3706,51 @@ connect_to(const char *host, u_short port, char *ctype, char *rname) Channel *c; memset(&cctx, 0, sizeof(cctx)); - memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; - hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", port); - if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { - error("connect_to %.100s: unknown host (%s)", host, - ssh_gai_strerror(gaierr)); - return NULL; + + if (port == PORT_STREAMLOCAL) { + struct sockaddr_un *sunaddr; + struct addrinfo *ai; + + if (strlen(name) > sizeof(sunaddr->sun_path)) { + error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); + return (NULL); + } + + /* + * Fake up a struct addrinfo for AF_UNIX connections. + * channel_connect_ctx_free() must check ai_family + * and use free() not freeaddirinfo() for AF_UNIX. + */ + ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); + memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); + ai->ai_addr = (struct sockaddr *)(ai + 1); + ai->ai_addrlen = sizeof(*sunaddr); + ai->ai_family = AF_UNIX; + ai->ai_socktype = SOCK_STREAM; + ai->ai_protocol = PF_UNSPEC; + sunaddr = (struct sockaddr_un *)ai->ai_addr; + sunaddr->sun_family = AF_UNIX; + strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); + cctx.aitop = ai; + } else { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) { + error("connect_to %.100s: unknown host (%s)", name, + ssh_gai_strerror(gaierr)); + return NULL; + } } - cctx.host = xstrdup(host); + cctx.host = xstrdup(name); cctx.port = port; cctx.ai = cctx.aitop; if ((sock = connect_next(&cctx)) == -1) { error("connect to %.100s port %d failed: %s", - host, port, strerror(errno)); + name, port, strerror(errno)); channel_connect_ctx_free(&cctx); return NULL; } @@ -3349,13 +3761,14 @@ connect_to(const char *host, u_short port, char *ctype, char *rname) } Channel * -channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) +channel_connect_by_listen_address(const char *listen_host, + u_short listen_port, char *ctype, char *rname) { int i; for (i = 0; i < num_permitted_opens; i++) { - if (permitted_opens[i].host_to_connect != NULL && - port_match(permitted_opens[i].listen_port, listen_port)) { + if (open_listen_match_tcpip(&permitted_opens[i], listen_host, + listen_port, 1)) { return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect, ctype, rname); @@ -3366,29 +3779,45 @@ channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) return NULL; } +Channel * +channel_connect_by_listen_path(const char *path, char *ctype, char *rname) +{ + int i; + + for (i = 0; i < num_permitted_opens; i++) { + if (open_listen_match_streamlocal(&permitted_opens[i], path)) { + return connect_to( + permitted_opens[i].host_to_connect, + permitted_opens[i].port_to_connect, ctype, rname); + } + } + error("WARNING: Server requests forwarding for unknown path %.100s", + path); + return NULL; +} + /* Check if connecting to that port is permitted and connect. */ Channel * -channel_connect_to(const char *host, u_short port, char *ctype, char *rname) +channel_connect_to_port(const char *host, u_short port, char *ctype, char *rname) { int i, permit, permit_adm = 1; permit = all_opens_permitted; if (!permit) { for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].host_to_connect != NULL && - port_match(permitted_opens[i].port_to_connect, port) && - strcmp(permitted_opens[i].host_to_connect, host) == 0) + if (open_match(&permitted_opens[i], host, port)) { permit = 1; + break; + } } if (num_adm_permitted_opens > 0) { permit_adm = 0; for (i = 0; i < num_adm_permitted_opens; i++) - if (permitted_adm_opens[i].host_to_connect != NULL && - port_match(permitted_adm_opens[i].port_to_connect, port) && - strcmp(permitted_adm_opens[i].host_to_connect, host) - == 0) + if (open_match(&permitted_adm_opens[i], host, port)) { permit_adm = 1; + break; + } } if (!permit || !permit_adm) { @@ -3399,6 +3828,38 @@ channel_connect_to(const char *host, u_short port, char *ctype, char *rname) return connect_to(host, port, ctype, rname); } +/* Check if connecting to that path is permitted and connect. */ +Channel * +channel_connect_to_path(const char *path, char *ctype, char *rname) +{ + int i, permit, permit_adm = 1; + + permit = all_opens_permitted; + if (!permit) { + for (i = 0; i < num_permitted_opens; i++) + if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) { + permit = 1; + break; + } + } + + if (num_adm_permitted_opens > 0) { + permit_adm = 0; + for (i = 0; i < num_adm_permitted_opens; i++) + if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) { + permit_adm = 1; + break; + } + } + + if (!permit || !permit_adm) { + logit("Received request to connect to path %.100s, " + "but the request was denied.", path); + return NULL; + } + return connect_to(path, PORT_STREAMLOCAL, ctype, rname); +} + void channel_send_window_changes(void) { @@ -3669,7 +4130,7 @@ x11_connect_display(void) */ /* ARGSUSED */ -void +int x11_input_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; @@ -3709,11 +4170,12 @@ x11_input_open(int type, u_int32_t seq, void *ctxt) packet_put_int(c->self); } packet_send(); + return 0; } /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ /* ARGSUSED */ -void +int deny_input_open(int type, u_int32_t seq, void *ctxt) { int rchan = packet_get_int(); @@ -3733,6 +4195,7 @@ deny_input_open(int type, u_int32_t seq, void *ctxt) packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_send(); + return 0; } /* |