2 * Dropbear - a SSH2 server
4 * Copyright (c) 2002,2003 Matt Johnston
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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
27 #ifndef DISABLE_X11FWD
32 #include "chansession.h"
38 #define X11BASEPORT 6000
39 #define X11BINDBASE 6010
41 static void x11accept(struct Listener
* listener
, int sock
);
42 static int bindport(int fd
);
43 static int send_msg_channel_open_x11(int fd
, struct sockaddr_in
* addr
);
45 /* called as a request for a session channel, sets up listening X11 */
46 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
47 int x11req(struct ChanSess
* chansess
) {
51 if (!svr_pubkey_allows_x11fwd()) {
52 return DROPBEAR_FAILURE
;
55 /* we already have an x11 connection */
56 if (chansess
->x11listener
!= NULL
) {
57 return DROPBEAR_FAILURE
;
60 chansess
->x11singleconn
= buf_getbool(ses
.payload
);
61 chansess
->x11authprot
= buf_getstring(ses
.payload
, NULL
);
62 chansess
->x11authcookie
= buf_getstring(ses
.payload
, NULL
);
63 chansess
->x11screennum
= buf_getint(ses
.payload
);
65 /* create listening socket */
66 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
71 /* allocate port and bind */
72 chansess
->x11port
= bindport(fd
);
73 if (chansess
->x11port
< 0) {
78 if (listen(fd
, 20) < 0) {
82 /* set non-blocking */
85 /* listener code will handle the socket now.
86 * No cleanup handler needed, since listener_remove only happens
87 * from our cleanup anyway */
88 chansess
->x11listener
= new_listener( &fd
, 1, 0, chansess
, x11accept
, NULL
);
89 if (chansess
->x11listener
== NULL
) {
93 return DROPBEAR_SUCCESS
;
97 m_free(chansess
->x11authprot
);
98 m_free(chansess
->x11authcookie
);
101 return DROPBEAR_FAILURE
;
104 /* accepts a new X11 socket */
105 /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
106 static void x11accept(struct Listener
* listener
, int sock
) {
109 struct sockaddr_in addr
;
112 struct ChanSess
* chansess
= (struct ChanSess
*)(listener
->typedata
);
116 fd
= accept(sock
, (struct sockaddr
*)&addr
, &len
);
121 /* if single-connection we close it up */
122 if (chansess
->x11singleconn
) {
123 x11cleanup(chansess
);
126 ret
= send_msg_channel_open_x11(fd
, &addr
);
127 if (ret
== DROPBEAR_FAILURE
) {
132 /* This is called after switching to the user, and sets up the xauth
133 * and environment variables. */
134 void x11setauth(struct ChanSess
*chansess
) {
136 char display
[20]; /* space for "localhost:12345.123" */
137 FILE * authprog
= NULL
;
140 if (chansess
->x11listener
== NULL
) {
144 /* create the DISPLAY string */
145 val
= snprintf(display
, sizeof(display
), "localhost:%d.%d",
146 chansess
->x11port
- X11BASEPORT
, chansess
->x11screennum
);
147 if (val
< 0 || val
>= (int)sizeof(display
)) {
148 /* string was truncated */
152 addnewvar("DISPLAY", display
);
154 /* create the xauth string */
155 val
= snprintf(display
, sizeof(display
), "unix:%d.%d",
156 chansess
->x11port
- X11BASEPORT
, chansess
->x11screennum
);
157 if (val
< 0 || val
>= (int)sizeof(display
)) {
158 /* string was truncated */
162 /* popen is a nice function - code is strongly based on OpenSSH's */
163 authprog
= popen(XAUTH_COMMAND
, "w");
165 fprintf(authprog
, "add %s %s %s\n",
166 display
, chansess
->x11authprot
, chansess
->x11authcookie
);
169 fprintf(stderr
, "Failed to run %s\n", XAUTH_COMMAND
);
173 void x11cleanup(struct ChanSess
*chansess
) {
175 m_free(chansess
->x11authprot
);
176 m_free(chansess
->x11authcookie
);
178 TRACE(("chansess %p", chansess
))
179 if (chansess
->x11listener
!= NULL
) {
180 remove_listener(chansess
->x11listener
);
181 chansess
->x11listener
= NULL
;
185 static const struct ChanType chan_x11
= {
188 NULL
, /* inithandler */
189 NULL
, /* checkclose */
190 NULL
, /* reqhandler */
191 NULL
/* closehandler */
195 static int send_msg_channel_open_x11(int fd
, struct sockaddr_in
* addr
) {
197 char* ipstring
= NULL
;
199 if (send_msg_channel_open_init(fd
, &chan_x11
) == DROPBEAR_SUCCESS
) {
200 ipstring
= inet_ntoa(addr
->sin_addr
);
201 buf_putstring(ses
.writepayload
, ipstring
, strlen(ipstring
));
202 buf_putint(ses
.writepayload
, addr
->sin_port
);
205 return DROPBEAR_SUCCESS
;
207 return DROPBEAR_FAILURE
;
212 /* returns the port bound to, or -1 on failure.
213 * Will attempt to bind to a port X11BINDBASE (6010 usually) or upwards */
214 static int bindport(int fd
) {
216 struct sockaddr_in addr
;
219 memset((void*)&addr
, 0x0, sizeof(addr
));
220 addr
.sin_family
= AF_INET
;
221 addr
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
223 /* if we can't find one in 2000 ports free, something's wrong */
224 for (port
= X11BINDBASE
; port
< X11BINDBASE
+ 2000; port
++) {
225 addr
.sin_port
= htons(port
);
226 if (bind(fd
, (struct sockaddr
*)&addr
,
227 sizeof(struct sockaddr_in
)) == 0) {
231 if (errno
== EADDRINUSE
) {
232 /* try the next port */
235 /* otherwise it was an error we don't know about */
236 dropbear_log(LOG_DEBUG
, "Failed to bind x11 socket");
241 #endif /* DROPBEAR_X11FWD */