• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Dropbear - a SSH2 server
3  *
4  * Copyright (c) 2002,2003 Matt Johnston
5  * All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE. */
24 
25 #include "includes.h"
26 
27 #ifndef DISABLE_X11FWD
28 #include "x11fwd.h"
29 #include "session.h"
30 #include "ssh.h"
31 #include "dbutil.h"
32 #include "chansession.h"
33 #include "channel.h"
34 #include "packet.h"
35 #include "buffer.h"
36 
37 #define X11BASEPORT 6000
38 #define X11BINDBASE 6010
39 
40 static void x11accept(struct Listener* listener, int sock);
41 static int bindport(int fd);
42 static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr);
43 
44 /* called as a request for a session channel, sets up listening X11 */
45 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
x11req(struct ChanSess * chansess)46 int x11req(struct ChanSess * chansess) {
47 
48 	int fd;
49 
50 	/* we already have an x11 connection */
51 	if (chansess->x11listener != NULL) {
52 		return DROPBEAR_FAILURE;
53 	}
54 
55 	chansess->x11singleconn = buf_getbool(ses.payload);
56 	chansess->x11authprot = buf_getstring(ses.payload, NULL);
57 	chansess->x11authcookie = buf_getstring(ses.payload, NULL);
58 	chansess->x11screennum = buf_getint(ses.payload);
59 
60 	/* create listening socket */
61 	fd = socket(PF_INET, SOCK_STREAM, 0);
62 	if (fd < 0) {
63 		goto fail;
64 	}
65 
66 	/* allocate port and bind */
67 	chansess->x11port = bindport(fd);
68 	if (chansess->x11port < 0) {
69 		goto fail;
70 	}
71 
72 	/* listen */
73 	if (listen(fd, 20) < 0) {
74 		goto fail;
75 	}
76 
77 	/* set non-blocking */
78 	setnonblocking(fd);
79 
80 	/* listener code will handle the socket now.
81 	 * No cleanup handler needed, since listener_remove only happens
82 	 * from our cleanup anyway */
83 	chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL);
84 	if (chansess->x11listener == NULL) {
85 		goto fail;
86 	}
87 
88 	return DROPBEAR_SUCCESS;
89 
90 fail:
91 	/* cleanup */
92 	m_free(chansess->x11authprot);
93 	m_free(chansess->x11authcookie);
94 	close(fd);
95 
96 	return DROPBEAR_FAILURE;
97 }
98 
99 /* accepts a new X11 socket */
100 /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
x11accept(struct Listener * listener,int sock)101 static void x11accept(struct Listener* listener, int sock) {
102 
103 	int fd;
104 	struct sockaddr_in addr;
105 	int len;
106 	int ret;
107 	struct ChanSess * chansess = (struct ChanSess *)(listener->typedata);
108 
109 	len = sizeof(addr);
110 
111 	fd = accept(sock, (struct sockaddr*)&addr, &len);
112 	if (fd < 0) {
113 		return;
114 	}
115 
116 	/* if single-connection we close it up */
117 	if (chansess->x11singleconn) {
118 		x11cleanup(chansess);
119 	}
120 
121 	ret = send_msg_channel_open_x11(fd, &addr);
122 	if (ret == DROPBEAR_FAILURE) {
123 		close(fd);
124 	}
125 }
126 
127 /* This is called after switching to the user, and sets up the xauth
128  * and environment variables.  */
x11setauth(struct ChanSess * chansess)129 void x11setauth(struct ChanSess *chansess) {
130 
131 	char display[20]; /* space for "localhost:12345.123" */
132 	FILE * authprog = NULL;
133 	int val;
134 
135 	if (chansess->x11listener == NULL) {
136 		return;
137 	}
138 
139 	/* create the DISPLAY string */
140 	val = snprintf(display, sizeof(display), "localhost:%d.%d",
141 			chansess->x11port - X11BASEPORT, chansess->x11screennum);
142 	if (val < 0 || val >= (int)sizeof(display)) {
143 		/* string was truncated */
144 		return;
145 	}
146 
147 	addnewvar("DISPLAY", display);
148 
149 	/* create the xauth string */
150 	val = snprintf(display, sizeof(display), "unix:%d.%d",
151 			chansess->x11port - X11BASEPORT, chansess->x11screennum);
152 	if (val < 0 || val >= (int)sizeof(display)) {
153 		/* string was truncated */
154 		return;
155 	}
156 
157 	/* popen is a nice function - code is strongly based on OpenSSH's */
158 	authprog = popen(XAUTH_COMMAND, "w");
159 	if (authprog) {
160 		fprintf(authprog, "add %s %s %s\n",
161 				display, chansess->x11authprot, chansess->x11authcookie);
162 		pclose(authprog);
163 	} else {
164 		fprintf(stderr, "Failed to run %s\n", XAUTH_COMMAND);
165 	}
166 }
167 
x11cleanup(struct ChanSess * chansess)168 void x11cleanup(struct ChanSess *chansess) {
169 
170 	m_free(chansess->x11authprot);
171 	m_free(chansess->x11authcookie);
172 
173 	TRACE(("chansess %s", chansess))
174 	if (chansess->x11listener != NULL) {
175 		remove_listener(chansess->x11listener);
176 		chansess->x11listener = NULL;
177 	}
178 }
179 
180 static const struct ChanType chan_x11 = {
181 	0, /* sepfds */
182 	"x11",
183 	NULL, /* inithandler */
184 	NULL, /* checkclose */
185 	NULL, /* reqhandler */
186 	NULL /* closehandler */
187 };
188 
189 
send_msg_channel_open_x11(int fd,struct sockaddr_in * addr)190 static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) {
191 
192 	char* ipstring = NULL;
193 
194 	if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) {
195 		ipstring = inet_ntoa(addr->sin_addr);
196 		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
197 		buf_putint(ses.writepayload, addr->sin_port);
198 
199 		encrypt_packet();
200 		return DROPBEAR_SUCCESS;
201 	} else {
202 		return DROPBEAR_FAILURE;
203 	}
204 
205 }
206 
207 /* returns the port bound to, or -1 on failure.
208  * Will attempt to bind to a port X11BINDBASE (6010 usually) or upwards */
bindport(int fd)209 static int bindport(int fd) {
210 
211 	struct sockaddr_in addr;
212 	uint16_t port;
213 
214 	memset((void*)&addr, 0x0, sizeof(addr));
215 	addr.sin_family = AF_INET;
216 	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
217 
218 	/* if we can't find one in 2000 ports free, something's wrong */
219 	for (port = X11BINDBASE; port < X11BINDBASE + 2000; port++) {
220 		addr.sin_port = htons(port);
221 		if (bind(fd, (struct sockaddr*)&addr,
222 					sizeof(struct sockaddr_in)) == 0) {
223 			/* success */
224 			return port;
225 		}
226 		if (errno == EADDRINUSE) {
227 			/* try the next port */
228 			continue;
229 		}
230 		/* otherwise it was an error we don't know about */
231 		dropbear_log(LOG_DEBUG, "failed to bind x11 socket");
232 		break;
233 	}
234 	return -1;
235 }
236 #endif /* DROPBEAR_X11FWD */
237