Annotation of src/usr.bin/ssh/channel-tcpfwd.c, Revision 1.1
1.1 ! markus 1: /*
! 2: * Author: Tatu Ylonen <ylo@cs.hut.fi>
! 3: * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
! 4: * All rights reserved
! 5: *
! 6: * As far as I am concerned, the code I have written for this software
! 7: * can be used freely for any purpose. Any derived versions of this
! 8: * software must be clearly marked as such, and if the derived work is
! 9: * incompatible with the protocol description in the RFC file, it must be
! 10: * called by a name other than "ssh" or "Secure Shell".
! 11: *
! 12: *
! 13: * Copyright (c) 2001 Markus Friedl. All rights reserved.
! 14: *
! 15: * Redistribution and use in source and binary forms, with or without
! 16: * modification, are permitted provided that the following conditions
! 17: * are met:
! 18: * 1. Redistributions of source code must retain the above copyright
! 19: * notice, this list of conditions and the following disclaimer.
! 20: * 2. Redistributions in binary form must reproduce the above copyright
! 21: * notice, this list of conditions and the following disclaimer in the
! 22: * documentation and/or other materials provided with the distribution.
! 23: *
! 24: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 25: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 26: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 27: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 28: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 29: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 30: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 31: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 32: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 33: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 34: */
! 35:
! 36: #include "includes.h"
! 37: RCSID("$OpenBSD: channels.c,v 1.119 2001/05/28 23:25:24 markus Exp $");
! 38:
! 39: #include "ssh.h"
! 40: #include "ssh1.h"
! 41: #include "ssh2.h"
! 42: #include "packet.h"
! 43: #include "xmalloc.h"
! 44: #include "log.h"
! 45: #include "channel.h"
! 46: #include "compat.h"
! 47:
! 48: /*
! 49: * Data structure for storing which hosts are permitted for forward requests.
! 50: * The local sides of any remote forwards are stored in this array to prevent
! 51: * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
! 52: * network (which might be behind a firewall).
! 53: */
! 54: typedef struct {
! 55: char *host_to_connect; /* Connect to 'host'. */
! 56: u_short port_to_connect; /* Connect to 'port'. */
! 57: u_short listen_port; /* Remote side should listen port number. */
! 58: } ForwardPermission;
! 59:
! 60: /* List of all permitted host/port pairs to connect. */
! 61: static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
! 62:
! 63: /* Number of permitted host/port pairs in the array. */
! 64: static int num_permitted_opens = 0;
! 65: /*
! 66: * If this is true, all opens are permitted. This is the case on the server
! 67: * on which we have to trust the client anyway, and the user could do
! 68: * anything after logging in anyway.
! 69: */
! 70: static int all_opens_permitted = 0;
! 71:
! 72: /* AF_UNSPEC or AF_INET or AF_INET6 */
! 73: extern int IPv4or6;
! 74:
! 75: /*
! 76: * Initiate forwarding of connections to local port "port" through the secure
! 77: * channel to host:port from remote side.
! 78: */
! 79: int
! 80: channel_request_local_forwarding(u_short listen_port, const char *host_to_connect,
! 81: u_short port_to_connect, int gateway_ports)
! 82: {
! 83: return channel_request_forwarding(
! 84: NULL, listen_port,
! 85: host_to_connect, port_to_connect,
! 86: gateway_ports, /*remote_fwd*/ 0);
! 87: }
! 88:
! 89: /*
! 90: * If 'remote_fwd' is true we have a '-R style' listener for protocol 2
! 91: * (SSH_CHANNEL_RPORT_LISTENER).
! 92: */
! 93: int
! 94: channel_request_forwarding(
! 95: const char *listen_address, u_short listen_port,
! 96: const char *host_to_connect, u_short port_to_connect,
! 97: int gateway_ports, int remote_fwd)
! 98: {
! 99: Channel *c;
! 100: int success, sock, on = 1, ctype;
! 101: struct addrinfo hints, *ai, *aitop;
! 102: char ntop[NI_MAXHOST], strport[NI_MAXSERV];
! 103: const char *host;
! 104: struct linger linger;
! 105:
! 106: success = 0;
! 107:
! 108: if (remote_fwd) {
! 109: host = listen_address;
! 110: ctype = SSH_CHANNEL_RPORT_LISTENER;
! 111: } else {
! 112: host = host_to_connect;
! 113: ctype =SSH_CHANNEL_PORT_LISTENER;
! 114: }
! 115:
! 116: if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
! 117: error("Forward host name too long.");
! 118: return success;
! 119: }
! 120:
! 121: /* XXX listen_address is currently ignored */
! 122: /*
! 123: * getaddrinfo returns a loopback address if the hostname is
! 124: * set to NULL and hints.ai_flags is not AI_PASSIVE
! 125: */
! 126: memset(&hints, 0, sizeof(hints));
! 127: hints.ai_family = IPv4or6;
! 128: hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
! 129: hints.ai_socktype = SOCK_STREAM;
! 130: snprintf(strport, sizeof strport, "%d", listen_port);
! 131: if (getaddrinfo(NULL, strport, &hints, &aitop) != 0)
! 132: packet_disconnect("getaddrinfo: fatal error");
! 133:
! 134: for (ai = aitop; ai; ai = ai->ai_next) {
! 135: if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
! 136: continue;
! 137: if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
! 138: strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
! 139: error("channel_request_forwarding: getnameinfo failed");
! 140: continue;
! 141: }
! 142: /* Create a port to listen for the host. */
! 143: sock = socket(ai->ai_family, SOCK_STREAM, 0);
! 144: if (sock < 0) {
! 145: /* this is no error since kernel may not support ipv6 */
! 146: verbose("socket: %.100s", strerror(errno));
! 147: continue;
! 148: }
! 149: /*
! 150: * Set socket options. We would like the socket to disappear
! 151: * as soon as it has been closed for whatever reason.
! 152: */
! 153: setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
! 154: linger.l_onoff = 1;
! 155: linger.l_linger = 5;
! 156: setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
! 157: debug("Local forwarding listening on %s port %s.", ntop, strport);
! 158:
! 159: /* Bind the socket to the address. */
! 160: if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
! 161: /* address can be in use ipv6 address is already bound */
! 162: verbose("bind: %.100s", strerror(errno));
! 163: close(sock);
! 164: continue;
! 165: }
! 166: /* Start listening for connections on the socket. */
! 167: if (listen(sock, 5) < 0) {
! 168: error("listen: %.100s", strerror(errno));
! 169: close(sock);
! 170: continue;
! 171: }
! 172: /* Allocate a channel number for the socket. */
! 173: c = channel_new("port listener", ctype, sock, sock, -1,
! 174: CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
! 175: 0, xstrdup("port listener"), 1);
! 176: if (c == NULL) {
! 177: error("channel_request_forwarding: channel_new failed");
! 178: close(sock);
! 179: continue;
! 180: }
! 181: strlcpy(c->path, host, sizeof(c->path));
! 182: c->host_port = port_to_connect;
! 183: c->listening_port = listen_port;
! 184: success = 1;
! 185: }
! 186: if (success == 0)
! 187: error("channel_request_forwarding: cannot listen to port: %d",
! 188: listen_port);
! 189: freeaddrinfo(aitop);
! 190: return success;
! 191: }
! 192:
! 193: /*
! 194: * Initiate forwarding of connections to port "port" on remote host through
! 195: * the secure channel to host:port from local side.
! 196: */
! 197:
! 198: void
! 199: channel_request_remote_forwarding(u_short listen_port,
! 200: const char *host_to_connect, u_short port_to_connect)
! 201: {
! 202: int payload_len, type, success = 0;
! 203:
! 204: /* Record locally that connection to this host/port is permitted. */
! 205: if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
! 206: fatal("channel_request_remote_forwarding: too many forwards");
! 207:
! 208: /* Send the forward request to the remote side. */
! 209: if (compat20) {
! 210: const char *address_to_bind = "0.0.0.0";
! 211: packet_start(SSH2_MSG_GLOBAL_REQUEST);
! 212: packet_put_cstring("tcpip-forward");
! 213: packet_put_char(0); /* boolean: want reply */
! 214: packet_put_cstring(address_to_bind);
! 215: packet_put_int(listen_port);
! 216: packet_send();
! 217: packet_write_wait();
! 218: /* Assume that server accepts the request */
! 219: success = 1;
! 220: } else {
! 221: packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
! 222: packet_put_int(listen_port);
! 223: packet_put_cstring(host_to_connect);
! 224: packet_put_int(port_to_connect);
! 225: packet_send();
! 226: packet_write_wait();
! 227:
! 228: /* Wait for response from the remote side. */
! 229: type = packet_read(&payload_len);
! 230: switch (type) {
! 231: case SSH_SMSG_SUCCESS:
! 232: success = 1;
! 233: break;
! 234: case SSH_SMSG_FAILURE:
! 235: log("Warning: Server denied remote port forwarding.");
! 236: break;
! 237: default:
! 238: /* Unknown packet */
! 239: packet_disconnect("Protocol error for port forward request:"
! 240: "received packet type %d.", type);
! 241: }
! 242: }
! 243: if (success) {
! 244: permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
! 245: permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
! 246: permitted_opens[num_permitted_opens].listen_port = listen_port;
! 247: num_permitted_opens++;
! 248: }
! 249: }
! 250:
! 251: /*
! 252: * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates
! 253: * listening for the port, and sends back a success reply (or disconnect
! 254: * message if there was an error). This never returns if there was an error.
! 255: */
! 256:
! 257: void
! 258: channel_input_port_forward_request(int is_root, int gateway_ports)
! 259: {
! 260: u_short port, host_port;
! 261: char *hostname;
! 262:
! 263: /* Get arguments from the packet. */
! 264: port = packet_get_int();
! 265: hostname = packet_get_string(NULL);
! 266: host_port = packet_get_int();
! 267:
! 268: /*
! 269: * Check that an unprivileged user is not trying to forward a
! 270: * privileged port.
! 271: */
! 272: if (port < IPPORT_RESERVED && !is_root)
! 273: packet_disconnect("Requested forwarding of port %d but user is not root.",
! 274: port);
! 275: /* Initiate forwarding */
! 276: channel_request_local_forwarding(port, hostname, host_port, gateway_ports);
! 277:
! 278: /* Free the argument string. */
! 279: xfree(hostname);
! 280: }
! 281:
! 282: /*
! 283: * Permits opening to any host/port if permitted_opens[] is empty. This is
! 284: * usually called by the server, because the user could connect to any port
! 285: * anyway, and the server has no way to know but to trust the client anyway.
! 286: */
! 287: void
! 288: channel_permit_all_opens()
! 289: {
! 290: if (num_permitted_opens == 0)
! 291: all_opens_permitted = 1;
! 292: }
! 293:
! 294: void
! 295: channel_add_permitted_opens(char *host, int port)
! 296: {
! 297: if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
! 298: fatal("channel_request_remote_forwarding: too many forwards");
! 299: debug("allow port forwarding to host %s port %d", host, port);
! 300:
! 301: permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
! 302: permitted_opens[num_permitted_opens].port_to_connect = port;
! 303: num_permitted_opens++;
! 304:
! 305: all_opens_permitted = 0;
! 306: }
! 307:
! 308: void
! 309: channel_clear_permitted_opens(void)
! 310: {
! 311: int i;
! 312:
! 313: for (i = 0; i < num_permitted_opens; i++)
! 314: xfree(permitted_opens[i].host_to_connect);
! 315: num_permitted_opens = 0;
! 316:
! 317: }
! 318:
! 319:
! 320: /* return socket to remote host, port */
! 321: int
! 322: connect_to(const char *host, u_short port)
! 323: {
! 324: struct addrinfo hints, *ai, *aitop;
! 325: char ntop[NI_MAXHOST], strport[NI_MAXSERV];
! 326: int gaierr;
! 327: int sock = -1;
! 328:
! 329: memset(&hints, 0, sizeof(hints));
! 330: hints.ai_family = IPv4or6;
! 331: hints.ai_socktype = SOCK_STREAM;
! 332: snprintf(strport, sizeof strport, "%d", port);
! 333: if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
! 334: error("connect_to %.100s: unknown host (%s)", host,
! 335: gai_strerror(gaierr));
! 336: return -1;
! 337: }
! 338: for (ai = aitop; ai; ai = ai->ai_next) {
! 339: if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
! 340: continue;
! 341: if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
! 342: strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
! 343: error("connect_to: getnameinfo failed");
! 344: continue;
! 345: }
! 346: sock = socket(ai->ai_family, SOCK_STREAM, 0);
! 347: if (sock < 0) {
! 348: error("socket: %.100s", strerror(errno));
! 349: continue;
! 350: }
! 351: if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
! 352: fatal("connect_to: F_SETFL: %s", strerror(errno));
! 353: if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 &&
! 354: errno != EINPROGRESS) {
! 355: error("connect_to %.100s port %s: %.100s", ntop, strport,
! 356: strerror(errno));
! 357: close(sock);
! 358: continue; /* fail -- try next */
! 359: }
! 360: break; /* success */
! 361:
! 362: }
! 363: freeaddrinfo(aitop);
! 364: if (!ai) {
! 365: error("connect_to %.100s port %d: failed.", host, port);
! 366: return -1;
! 367: }
! 368: /* success */
! 369: return sock;
! 370: }
! 371:
! 372: int
! 373: channel_connect_by_listen_adress(u_short listen_port)
! 374: {
! 375: int i;
! 376:
! 377: for (i = 0; i < num_permitted_opens; i++)
! 378: if (permitted_opens[i].listen_port == listen_port)
! 379: return connect_to(
! 380: permitted_opens[i].host_to_connect,
! 381: permitted_opens[i].port_to_connect);
! 382: error("WARNING: Server requests forwarding for unknown listen_port %d",
! 383: listen_port);
! 384: return -1;
! 385: }
! 386:
! 387: /* Check if connecting to that port is permitted and connect. */
! 388: int
! 389: channel_connect_to(const char *host, u_short port)
! 390: {
! 391: int i, permit;
! 392:
! 393: permit = all_opens_permitted;
! 394: if (!permit) {
! 395: for (i = 0; i < num_permitted_opens; i++)
! 396: if (permitted_opens[i].port_to_connect == port &&
! 397: strcmp(permitted_opens[i].host_to_connect, host) == 0)
! 398: permit = 1;
! 399:
! 400: }
! 401: if (!permit) {
! 402: log("Received request to connect to host %.100s port %d, "
! 403: "but the request was denied.", host, port);
! 404: return -1;
! 405: }
! 406: return connect_to(host, port);
! 407: }