comparison src/ch/ethz/ssh2/channel/RemoteX11AcceptThread.java @ 308:42b15aaa7ac7 ganymed

merge
author Carl Byington <carl@five-ten-sg.com>
date Wed, 30 Jul 2014 14:21:50 -0700
parents 071eccdff8ea
children
comparison
equal deleted inserted replaced
306:90e47d99ea54 308:42b15aaa7ac7
16 * RemoteX11AcceptThread. 16 * RemoteX11AcceptThread.
17 * 17 *
18 * @author Christian Plattner 18 * @author Christian Plattner
19 * @version $Id: RemoteX11AcceptThread.java 119 2014-04-12 20:30:58Z dkocher@sudo.ch $ 19 * @version $Id: RemoteX11AcceptThread.java 119 2014-04-12 20:30:58Z dkocher@sudo.ch $
20 */ 20 */
21 public class RemoteX11AcceptThread extends Thread 21 public class RemoteX11AcceptThread extends Thread {
22 { 22 private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);
23 private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class); 23
24 24 Channel c;
25 Channel c; 25
26 26 String remoteOriginatorAddress;
27 String remoteOriginatorAddress; 27 int remoteOriginatorPort;
28 int remoteOriginatorPort; 28
29 29 Socket s;
30 Socket s; 30
31 31 public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort) {
32 public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort) 32 this.c = c;
33 { 33 this.remoteOriginatorAddress = remoteOriginatorAddress;
34 this.c = c; 34 this.remoteOriginatorPort = remoteOriginatorPort;
35 this.remoteOriginatorAddress = remoteOriginatorAddress; 35 }
36 this.remoteOriginatorPort = remoteOriginatorPort; 36
37 } 37 @Override
38 38 public void run() {
39 @Override 39 try {
40 public void run() 40 /* Send Open Confirmation */
41 { 41 c.cm.sendOpenConfirmation(c);
42 try 42 /* Read startup packet from client */
43 { 43 OutputStream remote_os = c.getStdinStream();
44 /* Send Open Confirmation */ 44 InputStream remote_is = c.getStdoutStream();
45 45 /* The following code is based on the protocol description given in:
46 c.cm.sendOpenConfirmation(c); 46 * Scheifler/Gettys,
47 47 * X Windows System: Core and Extension Protocols:
48 /* Read startup packet from client */ 48 * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
49 49 * (from the ETH library - after being here for almost ten
50 OutputStream remote_os = c.getStdinStream(); 50 * years one of the few books I borrowed... sad but true =)
51 InputStream remote_is = c.getStdoutStream(); 51 */
52 52 /*
53 /* The following code is based on the protocol description given in: 53 * Client startup:
54 * Scheifler/Gettys, 54 *
55 * X Windows System: Core and Extension Protocols: 55 * 1 0X42 MSB first/0x6c lSB first - byteorder
56 * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X 56 * 1 - unused
57 * (from the ETH library - after being here for almost ten 57 * 2 card16 - protocol-major-version
58 * years one of the few books I borrowed... sad but true =) 58 * 2 card16 - protocol-minor-version
59 */ 59 * 2 n - lenght of authorization-protocol-name
60 60 * 2 d - lenght of authorization-protocol-data
61 /* 61 * 2 - unused
62 * Client startup: 62 * string8 - authorization-protocol-name
63 * 63 * p - unused, p=pad(n)
64 * 1 0X42 MSB first/0x6c lSB first - byteorder 64 * string8 - authorization-protocol-data
65 * 1 - unused 65 * q - unused, q=pad(d)
66 * 2 card16 - protocol-major-version 66 *
67 * 2 card16 - protocol-minor-version 67 * pad(X) = (4 - (X mod 4)) mod 4
68 * 2 n - lenght of authorization-protocol-name 68 *
69 * 2 d - lenght of authorization-protocol-data 69 * Server response:
70 * 2 - unused 70 *
71 * string8 - authorization-protocol-name 71 * 1 (0 failed, 2 authenticate, 1 success)
72 * p - unused, p=pad(n) 72 * ...
73 * string8 - authorization-protocol-data 73 *
74 * q - unused, q=pad(d) 74 */
75 * 75 /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
76 * pad(X) = (4 - (X mod 4)) mod 4 76 byte[] header = new byte[6];
77 * 77
78 * Server response: 78 if (remote_is.read(header) != 6)
79 * 79 throw new IOException("Unexpected EOF on X11 startup!");
80 * 1 (0 failed, 2 authenticate, 1 success) 80
81 * ... 81 if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
82 * 82 throw new IOException("Unknown endian format in X11 message!");
83 */ 83
84 84 /* Yes, I came up with this myself - shall I file an application for a patent? =) */
85 /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */ 85 int idxMSB = (header[0] == 0x42) ? 0 : 1;
86 86 /* Read authorization data header */
87 byte[] header = new byte[6]; 87 byte[] auth_buff = new byte[6];
88 88
89 if (remote_is.read(header) != 6) 89 if (remote_is.read(auth_buff) != 6)
90 throw new IOException("Unexpected EOF on X11 startup!"); 90 throw new IOException("Unexpected EOF on X11 startup!");
91 91
92 if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first 92 int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff);
93 throw new IOException("Unknown endian format in X11 message!"); 93 int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff);
94 94
95 /* Yes, I came up with this myself - shall I file an application for a patent? =) */ 95 if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))
96 96 throw new IOException("Buggy X11 authorization data");
97 int idxMSB = (header[0] == 0x42) ? 0 : 1; 97
98 98 int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
99 /* Read authorization data header */ 99 int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
100 100 byte[] authProtocolName = new byte[authProtocolNameLength];
101 byte[] auth_buff = new byte[6]; 101 byte[] authProtocolData = new byte[authProtocolDataLength];
102 102 byte[] paddingBuffer = new byte[4];
103 if (remote_is.read(auth_buff) != 6) 103
104 throw new IOException("Unexpected EOF on X11 startup!"); 104 if (remote_is.read(authProtocolName) != authProtocolNameLength)
105 105 throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");
106 int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff); 106
107 int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff); 107 if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
108 108 throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");
109 if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256)) 109
110 throw new IOException("Buggy X11 authorization data"); 110 if (remote_is.read(authProtocolData) != authProtocolDataLength)
111 111 throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");
112 int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4); 112
113 int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4); 113 if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
114 114 throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
115 byte[] authProtocolName = new byte[authProtocolNameLength]; 115
116 byte[] authProtocolData = new byte[authProtocolDataLength]; 116 if ("MIT-MAGIC-COOKIE-1".equals(StringEncoder.GetString(authProtocolName)) == false)
117 117 throw new IOException("Unknown X11 authorization protocol!");
118 byte[] paddingBuffer = new byte[4]; 118
119 119 if (authProtocolDataLength != 16)
120 if (remote_is.read(authProtocolName) != authProtocolNameLength) 120 throw new IOException("Wrong data length for X11 authorization data!");
121 throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)"); 121
122 122 StringBuilder tmp = new StringBuilder(32);
123 if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding) 123
124 throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)"); 124 for (int i = 0; i < authProtocolData.length; i++) {
125 125 String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
126 if (remote_is.read(authProtocolData) != authProtocolDataLength) 126 tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
127 throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)"); 127 }
128 128
129 if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding) 129 String hexEncodedFakeCookie = tmp.toString();
130 throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)"); 130
131 131 /* Order is very important here - it may be that a certain x11 forwarding
132 if ("MIT-MAGIC-COOKIE-1".equals(StringEncoder.GetString(authProtocolName)) == false) 132 * gets disabled right in the moment when we check and register our connection
133 throw new IOException("Unknown X11 authorization protocol!"); 133 * */
134 134
135 if (authProtocolDataLength != 16) 135 synchronized (c) {
136 throw new IOException("Wrong data length for X11 authorization data!"); 136 /* Please read the comment in Channel.java */
137 137 c.hexX11FakeCookie = hexEncodedFakeCookie;
138 StringBuilder tmp = new StringBuilder(32); 138 }
139 for (int i = 0; i < authProtocolData.length; i++) 139
140 { 140 /* Now check our fake cookie directory to see if we produced this cookie */
141 String digit2 = Integer.toHexString(authProtocolData[i] & 0xff); 141 X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
142 tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); 142
143 } 143 if (sd == null)
144 String hexEncodedFakeCookie = tmp.toString(); 144 throw new IOException("Invalid X11 cookie received.");
145 145
146 /* Order is very important here - it may be that a certain x11 forwarding 146 /* If the session which corresponds to this cookie is closed then we will
147 * gets disabled right in the moment when we check and register our connection 147 * detect this: the session's close code will close all channels
148 * */ 148 * with the session's assigned x11 fake cookie.
149 149 */
150 synchronized (c) 150 s = new Socket(sd.hostname, sd.port);
151 { 151 OutputStream x11_os = s.getOutputStream();
152 /* Please read the comment in Channel.java */ 152 InputStream x11_is = s.getInputStream();
153 c.hexX11FakeCookie = hexEncodedFakeCookie; 153 /* Now we are sending the startup packet to the real X11 server */
154 } 154 x11_os.write(header);
155 155
156 /* Now check our fake cookie directory to see if we produced this cookie */ 156 if (sd.x11_magic_cookie == null) {
157 157 byte[] emptyAuthData = new byte[6];
158 X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie); 158 /* empty auth data, hopefully you are connecting to localhost =) */
159 159 x11_os.write(emptyAuthData);
160 if (sd == null) 160 }
161 throw new IOException("Invalid X11 cookie received."); 161 else {
162 162 if (sd.x11_magic_cookie.length != 16)
163 /* If the session which corresponds to this cookie is closed then we will 163 throw new IOException("The real X11 cookie has an invalid length!");
164 * detect this: the session's close code will close all channels 164
165 * with the session's assigned x11 fake cookie. 165 /* send X11 cookie specified by client */
166 */ 166 x11_os.write(auth_buff);
167 167 x11_os.write(authProtocolName); /* re-use */
168 s = new Socket(sd.hostname, sd.port); 168 x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
169 169 x11_os.write(sd.x11_magic_cookie);
170 OutputStream x11_os = s.getOutputStream(); 170 x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
171 InputStream x11_is = s.getInputStream(); 171 }
172 172
173 /* Now we are sending the startup packet to the real X11 server */ 173 x11_os.flush();
174 174 /* Start forwarding traffic */
175 x11_os.write(header); 175 StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11");
176 176 StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
177 if (sd.x11_magic_cookie == null) 177 /* No need to start two threads, one can be executed in the current thread */
178 { 178 r2l.setDaemon(true);
179 byte[] emptyAuthData = new byte[6]; 179 r2l.start();
180 /* empty auth data, hopefully you are connecting to localhost =) */ 180 l2r.run();
181 x11_os.write(emptyAuthData); 181
182 } 182 while (r2l.isAlive()) {
183 else 183 try {
184 { 184 r2l.join();
185 if (sd.x11_magic_cookie.length != 16) 185 }
186 throw new IOException("The real X11 cookie has an invalid length!"); 186 catch (InterruptedException ignored) {
187 187 }
188 /* send X11 cookie specified by client */ 188 }
189 x11_os.write(auth_buff); 189
190 x11_os.write(authProtocolName); /* re-use */ 190 /* If the channel is already closed, then this is a no-op */
191 x11_os.write(paddingBuffer, 0, authProtocolNamePadding); 191 c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
192 x11_os.write(sd.x11_magic_cookie); 192 s.close();
193 x11_os.write(paddingBuffer, 0, authProtocolDataPadding); 193 }
194 } 194 catch (IOException e) {
195 195 log.warning("IOException in X11 proxy code: " + e.getMessage());
196 x11_os.flush(); 196
197 197 try {
198 /* Start forwarding traffic */ 198 c.cm.closeChannel(c, e, true);
199 199 }
200 StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11"); 200 catch (IOException ignored) {
201 StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote"); 201 }
202 202
203 /* No need to start two threads, one can be executed in the current thread */ 203 try {
204 204 if (s != null)
205 r2l.setDaemon(true); 205 s.close();
206 r2l.start(); 206 }
207 l2r.run(); 207 catch (IOException ignored) {
208 208 }
209 while (r2l.isAlive()) 209 }
210 { 210 }
211 try
212 {
213 r2l.join();
214 }
215 catch (InterruptedException ignored)
216 {
217 }
218 }
219
220 /* If the channel is already closed, then this is a no-op */
221
222 c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
223 s.close();
224 }
225 catch (IOException e)
226 {
227 log.warning("IOException in X11 proxy code: " + e.getMessage());
228
229 try
230 {
231 c.cm.closeChannel(c, e, true);
232 }
233 catch (IOException ignored)
234 {
235 }
236 try
237 {
238 if (s != null)
239 s.close();
240 }
241 catch (IOException ignored)
242 {
243 }
244 }
245 }
246 } 211 }