- (djm) Merge OpenBSD changes:
   - markus@cvs.openbsd.org  2000/11/06 16:04:56
     [channels.c channels.h clientloop.c nchan.c serverloop.c]
     [session.c ssh.c]
     agent forwarding and -R for ssh2, based on work from
     jhuuskon@messi.uku.fi
   - markus@cvs.openbsd.org  2000/11/06 16:13:27
     [ssh.c sshconnect.c sshd.c]
     do not disabled rhosts(rsa) if server port > 1024; from
     pekkas@netcore.fi
   - markus@cvs.openbsd.org  2000/11/06 16:16:35
     [sshconnect.c]
     downgrade client to 1.3 if server is 1.4; help from mdb@juniper.net
   - markus@cvs.openbsd.org  2000/11/09 18:04:40
     [auth1.c]
     typo; from mouring@pconline.com
   - markus@cvs.openbsd.org  2000/11/12 12:03:28
     [ssh-agent.c]
     off-by-one when removing a key from the agent
   - markus@cvs.openbsd.org  2000/11/12 12:50:39
     [auth-rh-rsa.c auth2.c authfd.c authfd.h]
     [authfile.c hostfile.c kex.c kex.h key.c key.h myproposal.h]
     [readconf.c readconf.h rsa.c rsa.h servconf.c servconf.h ssh-add.c]
     [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config]
     [sshconnect1.c sshconnect2.c sshd.8 sshd.c sshd_config ssh-dss.c]
     [ssh-dss.h ssh-rsa.c ssh-rsa.h dsa.c dsa.h]
     add support for RSA to SSH2.  please test.
     there are now 3 types of keys: RSA1 is used by ssh-1 only,
     RSA and DSA are used by SSH2.
     you can use 'ssh-keygen -t rsa -f ssh2_rsa_file' to generate RSA
     keys for SSH2 and use the RSA keys for hostkeys or for user keys.
     SSH2 RSA or DSA keys are added to .ssh/authorised_keys2 as before.
 - (djm) Fix up Makefile and Redhat init script to create RSA host keys
 - (djm) Change to interim version
diff --git a/channels.c b/channels.c
index 028c09e..0886a91 100644
--- a/channels.c
+++ b/channels.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.72 2000/10/27 07:48:22 markus Exp $");
+RCSID("$OpenBSD: channels.c,v 1.73 2000/11/06 23:04:55 markus Exp $");
 
 #include "ssh.h"
 #include "packet.h"
@@ -588,9 +588,12 @@
 	struct sockaddr addr;
 	int newsock, newch;
 	socklen_t addrlen;
-	char buf[1024], *remote_hostname;
+	char buf[1024], *remote_hostname, *rtype;
 	int remote_port;
 
+	rtype = (c->type == SSH_CHANNEL_RPORT_LISTENER) ?
+	    "forwarded-tcpip" : "direct-tcpip";
+
 	if (FD_ISSET(c->sock, readset)) {
 		debug("Connection to port %d forwarding "
 		    "to %.100s port %d requested.",
@@ -608,19 +611,26 @@
 		    "connect from %.200s port %d",
 		    c->listening_port, c->path, c->host_port,
 		    remote_hostname, remote_port);
-		newch = channel_new("direct-tcpip",
+
+		newch = channel_new(rtype,
 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
 		    c->local_window_max, c->local_maxpacket,
 		    0, xstrdup(buf), 1);
 		if (compat20) {
 			packet_start(SSH2_MSG_CHANNEL_OPEN);
-			packet_put_cstring("direct-tcpip");
+			packet_put_cstring(rtype);
 			packet_put_int(newch);
 			packet_put_int(c->local_window_max);
 			packet_put_int(c->local_maxpacket);
-			/* target host and port */
-			packet_put_string(c->path, strlen(c->path));
-			packet_put_int(c->host_port);
+			if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
+				/* listen address, port */
+				packet_put_string(c->path, strlen(c->path));
+				packet_put_int(c->listening_port);
+			} else {
+				/* target host, port */
+				packet_put_string(c->path, strlen(c->path));
+				packet_put_int(c->host_port);
+			}
 			/* originator host and port */
 			packet_put_cstring(remote_hostname);
 			packet_put_int(remote_port);
@@ -657,10 +667,20 @@
 			error("accept from auth socket: %.100s", strerror(errno));
 			return;
 		}
-		newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
-		    xstrdup("accepted auth socket"));
-		packet_start(SSH_SMSG_AGENT_OPEN);
-		packet_put_int(newch);
+		newch = channel_new("accepted auth socket",
+		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
+		    c->local_window_max, c->local_maxpacket,
+		    0, xstrdup("accepted auth socket"), 1);
+		if (compat20) {
+			packet_start(SSH2_MSG_CHANNEL_OPEN);
+			packet_put_cstring("auth-agent@openssh.com");
+			packet_put_int(newch);
+			packet_put_int(c->local_window_max);
+			packet_put_int(c->local_maxpacket);
+		} else {
+			packet_start(SSH_SMSG_AGENT_OPEN);
+			packet_put_int(newch);
+		}
 		packet_send();
 	}
 }
@@ -820,11 +840,15 @@
 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_20;
 	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_X11_LISTENER] =		&channel_pre_listener;
+	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
 
 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open_2;
 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
+	channel_post[SSH_CHANNEL_RPORT_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;
 }
 
 void
@@ -1326,6 +1350,7 @@
 			channel_free(i);
 			break;
 		case SSH_CHANNEL_PORT_LISTENER:
+		case SSH_CHANNEL_RPORT_LISTENER:
 		case SSH_CHANNEL_X11_LISTENER:
 			close(channels[i].sock);
 			channel_free(i);
@@ -1369,6 +1394,7 @@
 		case SSH_CHANNEL_FREE:
 		case SSH_CHANNEL_X11_LISTENER:
 		case SSH_CHANNEL_PORT_LISTENER:
+		case SSH_CHANNEL_RPORT_LISTENER:
 		case SSH_CHANNEL_CLOSED:
 		case SSH_CHANNEL_AUTH_SOCKET:
 			continue;
@@ -1414,6 +1440,7 @@
 		case SSH_CHANNEL_FREE:
 		case SSH_CHANNEL_X11_LISTENER:
 		case SSH_CHANNEL_PORT_LISTENER:
+		case SSH_CHANNEL_RPORT_LISTENER:
 		case SSH_CHANNEL_CLOSED:
 		case SSH_CHANNEL_AUTH_SOCKET:
 			continue;
@@ -1446,19 +1473,44 @@
  * Initiate forwarding of connections to local port "port" through the secure
  * channel to host:port from remote side.
  */
-
 void
-channel_request_local_forwarding(u_short port, const char *host,
-				 u_short host_port, int gateway_ports)
+channel_request_local_forwarding(u_short listen_port, const char *host_to_connect,
+    u_short port_to_connect, int gateway_ports)
 {
-	int success, ch, sock, on = 1;
+	channel_request_forwarding(
+	    NULL, listen_port,
+	    host_to_connect, port_to_connect,
+	    gateway_ports, /*remote_fwd*/ 0);
+}
+
+/*
+ * If 'remote_fwd' is true we have a '-R style' listener for protocol 2
+ * (SSH_CHANNEL_RPORT_LISTENER).
+ */
+void
+channel_request_forwarding(
+    const char *listen_address, u_short listen_port,
+    const char *host_to_connect, u_short port_to_connect,
+    int gateway_ports, int remote_fwd)
+{
+	int success, ch, sock, on = 1, ctype;
 	struct addrinfo hints, *ai, *aitop;
 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+	const char *host;
 	struct linger linger;
 
+	if (remote_fwd) {
+		host = listen_address;
+	    	ctype = SSH_CHANNEL_RPORT_LISTENER;
+	} else {
+		host = host_to_connect;
+		ctype  =SSH_CHANNEL_PORT_LISTENER;
+	}
+
 	if (strlen(host) > sizeof(channels[0].path) - 1)
 		packet_disconnect("Forward host name too long.");
 
+	/* XXX listen_address is currently ignored */
 	/*
 	 * getaddrinfo returns a loopback address if the hostname is
 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
@@ -1467,7 +1519,7 @@
 	hints.ai_family = IPv4or6;
 	hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
 	hints.ai_socktype = SOCK_STREAM;
-	snprintf(strport, sizeof strport, "%d", port);
+	snprintf(strport, sizeof strport, "%d", listen_port);
 	if (getaddrinfo(NULL, strport, &hints, &aitop) != 0)
 		packet_disconnect("getaddrinfo: fatal error");
 
@@ -1477,7 +1529,7 @@
 			continue;
 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
-			error("channel_request_local_forwarding: getnameinfo failed");
+			error("channel_request_forwarding: getnameinfo failed");
 			continue;
 		}
 		/* Create a port to listen for the host. */
@@ -1515,18 +1567,16 @@
 			continue;
 		}
 		/* Allocate a channel number for the socket. */
-		ch = channel_new(
-		    "port listener", SSH_CHANNEL_PORT_LISTENER,
-		    sock, sock, -1,
+		ch = channel_new("port listener", ctype, sock, sock, -1,
 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
 		    0, xstrdup("port listener"), 1);
 		strlcpy(channels[ch].path, host, sizeof(channels[ch].path));
-		channels[ch].host_port = host_port;
-		channels[ch].listening_port = port;
+		channels[ch].host_port = port_to_connect;
+		channels[ch].listening_port = listen_port;
 		success = 1;
 	}
 	if (success == 0)
-		packet_disconnect("cannot listen port: %d", port);
+		packet_disconnect("cannot listen port: %d", listen_port);	/*XXX ?disconnect? */
 	freeaddrinfo(aitop);
 }
 
@@ -1536,19 +1586,15 @@
  */
 
 void
-channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect,
-				  u_short port_to_connect)
+channel_request_remote_forwarding(u_short listen_port,
+    const char *host_to_connect, u_short port_to_connect)
 {
-	int payload_len;
+	int payload_len, type, success = 0;
+
 	/* Record locally that connection to this host/port is permitted. */
 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
 		fatal("channel_request_remote_forwarding: too many forwards");
 
-	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
-	permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
-	permitted_opens[num_permitted_opens].listen_port = listen_port;
-	num_permitted_opens++;
-
 	/* Send the forward request to the remote side. */
 	if (compat20) {
 		const char *address_to_bind = "0.0.0.0";
@@ -1557,6 +1603,10 @@
 		packet_put_char(0);			/* boolean: want reply */
 		packet_put_cstring(address_to_bind);
 		packet_put_int(listen_port);
+		packet_send();
+		packet_write_wait();
+		/* Assume that server accepts the request */
+		success = 1;
 	} else {
 		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
 		packet_put_int(listen_port);
@@ -1564,11 +1614,27 @@
 		packet_put_int(port_to_connect);
 		packet_send();
 		packet_write_wait();
-		/*
-		 * Wait for response from the remote side.  It will send a disconnect
-		 * message on failure, and we will never see it here.
-		 */
-		packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);
+
+		/* Wait for response from the remote side. */
+		type = packet_read(&payload_len);
+		switch (type) {
+		case SSH_SMSG_SUCCESS:
+			success = 1;
+			break;
+		case SSH_SMSG_FAILURE:
+			log("Warning: Server denied remote port forwarding.");
+			break;
+		default:
+			/* Unknown packet */
+			packet_disconnect("Protocol error for port forward request:"
+			    "received packet type %d.", type);
+		}
+	}
+	if (success) {
+		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
+		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
+		permitted_opens[num_permitted_opens].listen_port = listen_port;
+		num_permitted_opens++;
 	}
 }
 
@@ -1598,9 +1664,7 @@
 		packet_disconnect("Requested forwarding of port %d but user is not root.",
 				  port);
 #endif
-	/*
-	 * Initiate forwarding,
-	 */
+	/* Initiate forwarding */
 	channel_request_local_forwarding(port, hostname, host_port, gateway_ports);
 
 	/* Free the argument string. */
@@ -1656,6 +1720,18 @@
 	/* success */
 	return sock;
 }
+int
+channel_connect_by_listen_adress(u_short listen_port)
+{
+	int i;
+	for (i = 0; i < num_permitted_opens; i++)
+		if (permitted_opens[i].listen_port == listen_port)
+			return channel_connect_to(
+			    permitted_opens[i].host_to_connect,
+			    permitted_opens[i].port_to_connect);
+	debug("channel_connect_by_listen_adress: unknown listen_port %d", listen_port);
+	return -1;
+}
 
 /*
  * This is called after receiving PORT_OPEN message.  This attempts to
@@ -2233,8 +2309,11 @@
 		packet_disconnect("listen: %.100s", strerror(errno));
 
 	/* Allocate a channel for the authentication agent socket. */
-	newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock,
-				 xstrdup("auth socket"));
+	newch = channel_new("auth socket",
+	    SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
+	    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
+	    0, xstrdup("auth socket"), 1);
+
 	strlcpy(channels[newch].path, channel_forwarded_auth_socket_name,
 	    sizeof(channels[newch].path));
 	return 1;