1 /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright 2011-2019 Pierre Ossman for Cendio AB
3 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19 #include <stdio.h>
20 #include <string.h>
21 #include <rfb/Exception.h>
22 #include <rfb/Security.h>
23 #include <rfb/clipboardTypes.h>
24 #include <rfb/msgTypes.h>
25 #include <rfb/fenceTypes.h>
26 #include <rfb/SMsgReader.h>
27 #include <rfb/SMsgWriter.h>
28 #include <rfb/SConnection.h>
29 #include <rfb/ServerCore.h>
30 #include <rfb/encodings.h>
31 #include <rfb/EncodeManager.h>
32 #include <rfb/SSecurity.h>
33
34 #include <rfb/LogWriter.h>
35
36 using namespace rfb;
37
38 static LogWriter vlog("SConnection");
39
40 // AccessRights values
41 const SConnection::AccessRights SConnection::AccessView = 0x0001;
42 const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
43 const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
44 const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
45 const SConnection::AccessRights SConnection::AccessSetDesktopSize = 0x0010;
46 const SConnection::AccessRights SConnection::AccessNonShared = 0x0020;
47 const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
48 const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
49 const SConnection::AccessRights SConnection::AccessFull = 0xffff;
50
51
SConnection()52 SConnection::SConnection()
53 : readyForSetColourMapEntries(false),
54 is(0), os(0), reader_(0), writer_(0), ssecurity(0),
55 authFailureTimer(this, &SConnection::handleAuthFailureTimeout),
56 state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw),
57 clientClipboard(NULL), hasLocalClipboard(false),
58 unsolicitedClipboardAttempt(false)
59 {
60 defaultMajorVersion = 3;
61 defaultMinorVersion = 8;
62 if (rfb::Server::protocol3_3)
63 defaultMinorVersion = 3;
64
65 client.setVersion(defaultMajorVersion, defaultMinorVersion);
66 }
67
~SConnection()68 SConnection::~SConnection()
69 {
70 cleanup();
71 }
72
setStreams(rdr::InStream * is_,rdr::OutStream * os_)73 void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
74 {
75 is = is_;
76 os = os_;
77 }
78
initialiseProtocol()79 void SConnection::initialiseProtocol()
80 {
81 char str[13];
82
83 sprintf(str, "RFB %03d.%03d\n", defaultMajorVersion, defaultMinorVersion);
84 os->writeBytes(str, 12);
85 os->flush();
86
87 state_ = RFBSTATE_PROTOCOL_VERSION;
88 }
89
processMsg()90 bool SConnection::processMsg()
91 {
92 switch (state_) {
93 case RFBSTATE_PROTOCOL_VERSION: return processVersionMsg(); break;
94 case RFBSTATE_SECURITY_TYPE: return processSecurityTypeMsg(); break;
95 case RFBSTATE_SECURITY: return processSecurityMsg(); break;
96 case RFBSTATE_SECURITY_FAILURE: return processSecurityFailure(); break;
97 case RFBSTATE_INITIALISATION: return processInitMsg(); break;
98 case RFBSTATE_NORMAL: return reader_->readMsg(); break;
99 case RFBSTATE_QUERYING:
100 throw Exception("SConnection::processMsg: bogus data from client while "
101 "querying");
102 case RFBSTATE_CLOSING:
103 throw Exception("SConnection::processMsg: called while closing");
104 case RFBSTATE_UNINITIALISED:
105 throw Exception("SConnection::processMsg: not initialised yet?");
106 default:
107 throw Exception("SConnection::processMsg: invalid state");
108 }
109 }
110
processVersionMsg()111 bool SConnection::processVersionMsg()
112 {
113 char verStr[13];
114 int majorVersion;
115 int minorVersion;
116
117 vlog.debug("reading protocol version");
118
119 if (!is->hasData(12))
120 return false;
121
122 is->readBytes(verStr, 12);
123 verStr[12] = '\0';
124
125 if (sscanf(verStr, "RFB %03d.%03d\n",
126 &majorVersion, &minorVersion) != 2) {
127 state_ = RFBSTATE_INVALID;
128 throw Exception("reading version failed: not an RFB client?");
129 }
130
131 client.setVersion(majorVersion, minorVersion);
132
133 vlog.info("Client needs protocol version %d.%d",
134 client.majorVersion, client.minorVersion);
135
136 if (client.majorVersion != 3) {
137 // unknown protocol version
138 throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d",
139 client.majorVersion, client.minorVersion,
140 defaultMajorVersion, defaultMinorVersion);
141 }
142
143 if (client.minorVersion != 3 && client.minorVersion != 7 && client.minorVersion != 8) {
144 vlog.error("Client uses unofficial protocol version %d.%d",
145 client.majorVersion,client.minorVersion);
146 if (client.minorVersion >= 8)
147 client.minorVersion = 8;
148 else if (client.minorVersion == 7)
149 client.minorVersion = 7;
150 else
151 client.minorVersion = 3;
152 vlog.error("Assuming compatibility with version %d.%d",
153 client.majorVersion,client.minorVersion);
154 }
155
156 versionReceived();
157
158 std::list<rdr::U8> secTypes;
159 std::list<rdr::U8>::iterator i;
160 secTypes = security.GetEnabledSecTypes();
161
162 if (client.isVersion(3,3)) {
163
164 // cope with legacy 3.3 client only if "no authentication" or "vnc
165 // authentication" is supported.
166 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
167 if (*i == secTypeNone || *i == secTypeVncAuth) break;
168 }
169 if (i == secTypes.end()) {
170 throwConnFailedException("No supported security type for %d.%d client",
171 client.majorVersion, client.minorVersion);
172 }
173
174 os->writeU32(*i);
175 if (*i == secTypeNone) os->flush();
176 state_ = RFBSTATE_SECURITY;
177 ssecurity = security.GetSSecurity(this, *i);
178 return true;
179 }
180
181 // list supported security types for >=3.7 clients
182
183 if (secTypes.empty())
184 throwConnFailedException("No supported security types");
185
186 os->writeU8(secTypes.size());
187 for (i=secTypes.begin(); i!=secTypes.end(); i++)
188 os->writeU8(*i);
189 os->flush();
190 state_ = RFBSTATE_SECURITY_TYPE;
191
192 return true;
193 }
194
195
processSecurityTypeMsg()196 bool SConnection::processSecurityTypeMsg()
197 {
198 vlog.debug("processing security type message");
199
200 if (!is->hasData(1))
201 return false;
202
203 int secType = is->readU8();
204
205 processSecurityType(secType);
206
207 return true;
208 }
209
processSecurityType(int secType)210 void SConnection::processSecurityType(int secType)
211 {
212 // Verify that the requested security type should be offered
213 std::list<rdr::U8> secTypes;
214 std::list<rdr::U8>::iterator i;
215
216 secTypes = security.GetEnabledSecTypes();
217 for (i=secTypes.begin(); i!=secTypes.end(); i++)
218 if (*i == secType) break;
219 if (i == secTypes.end())
220 throw Exception("Requested security type not available");
221
222 vlog.info("Client requests security type %s(%d)",
223 secTypeName(secType),secType);
224
225 try {
226 state_ = RFBSTATE_SECURITY;
227 ssecurity = security.GetSSecurity(this, secType);
228 } catch (rdr::Exception& e) {
229 throwConnFailedException("%s", e.str());
230 }
231 }
232
processSecurityMsg()233 bool SConnection::processSecurityMsg()
234 {
235 vlog.debug("processing security message");
236 try {
237 if (!ssecurity->processMsg())
238 return false;
239 } catch (AuthFailureException& e) {
240 vlog.error("AuthFailureException: %s", e.str());
241 state_ = RFBSTATE_SECURITY_FAILURE;
242 // Introduce a slight delay of the authentication failure response
243 // to make it difficult to brute force a password
244 authFailureMsg.replaceBuf(strDup(e.str()));
245 authFailureTimer.start(100);
246 return true;
247 }
248
249 state_ = RFBSTATE_QUERYING;
250 setAccessRights(ssecurity->getAccessRights());
251 queryConnection(ssecurity->getUserName());
252
253 // If the connection got approved right away then we can continue
254 if (state_ == RFBSTATE_INITIALISATION)
255 return true;
256
257 // Otherwise we need to wait for the result
258 // (or give up if if was rejected)
259 return false;
260 }
261
processSecurityFailure()262 bool SConnection::processSecurityFailure()
263 {
264 // Silently drop any data if we are currently delaying an
265 // authentication failure response as otherwise we would close
266 // the connection on unexpected data, and an attacker could use
267 // that to detect our delayed state.
268
269 if (!is->hasData(1))
270 return false;
271
272 is->skip(is->avail());
273
274 return true;
275 }
276
processInitMsg()277 bool SConnection::processInitMsg()
278 {
279 vlog.debug("reading client initialisation");
280 return reader_->readClientInit();
281 }
282
handleAuthFailureTimeout(Timer * t)283 bool SConnection::handleAuthFailureTimeout(Timer* t)
284 {
285 if (state_ != RFBSTATE_SECURITY_FAILURE) {
286 close("SConnection::handleAuthFailureTimeout: invalid state");
287 return false;
288 }
289
290 try {
291 os->writeU32(secResultFailed);
292 if (!client.beforeVersion(3,8)) { // 3.8 onwards have failure message
293 const char* reason = authFailureMsg.buf;
294 os->writeU32(strlen(reason));
295 os->writeBytes(reason, strlen(reason));
296 }
297 os->flush();
298 } catch (rdr::Exception& e) {
299 close(e.str());
300 return false;
301 }
302
303 close(authFailureMsg.buf);
304
305 return false;
306 }
307
throwConnFailedException(const char * format,...)308 void SConnection::throwConnFailedException(const char* format, ...)
309 {
310 va_list ap;
311 char str[256];
312
313 va_start(ap, format);
314 (void) vsnprintf(str, sizeof(str), format, ap);
315 va_end(ap);
316
317 vlog.info("Connection failed: %s", str);
318
319 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
320 if (client.majorVersion == 3 && client.minorVersion == 3) {
321 os->writeU32(0);
322 os->writeU32(strlen(str));
323 os->writeBytes(str, strlen(str));
324 os->flush();
325 } else {
326 os->writeU8(0);
327 os->writeU32(strlen(str));
328 os->writeBytes(str, strlen(str));
329 os->flush();
330 }
331 }
332
333 state_ = RFBSTATE_INVALID;
334 throw ConnFailedException(str);
335 }
336
setAccessRights(AccessRights ar)337 void SConnection::setAccessRights(AccessRights ar)
338 {
339 accessRights = ar;
340 }
341
accessCheck(AccessRights ar) const342 bool SConnection::accessCheck(AccessRights ar) const
343 {
344 return (accessRights & ar) == ar;
345 }
346
setEncodings(int nEncodings,const rdr::S32 * encodings)347 void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
348 {
349 int i;
350
351 preferredEncoding = encodingRaw;
352 for (i = 0;i < nEncodings;i++) {
353 if (EncodeManager::supported(encodings[i])) {
354 preferredEncoding = encodings[i];
355 break;
356 }
357 }
358
359 SMsgHandler::setEncodings(nEncodings, encodings);
360
361 if (client.supportsEncoding(pseudoEncodingExtendedClipboard)) {
362 rdr::U32 sizes[] = { 0 };
363 writer()->writeClipboardCaps(rfb::clipboardUTF8 |
364 rfb::clipboardRequest |
365 rfb::clipboardPeek |
366 rfb::clipboardNotify |
367 rfb::clipboardProvide,
368 sizes);
369 }
370 }
371
clientCutText(const char * str)372 void SConnection::clientCutText(const char* str)
373 {
374 hasLocalClipboard = false;
375
376 strFree(clientClipboard);
377 clientClipboard = NULL;
378
379 clientClipboard = latin1ToUTF8(str);
380
381 handleClipboardAnnounce(true);
382 }
383
handleClipboardRequest(rdr::U32 flags)384 void SConnection::handleClipboardRequest(rdr::U32 flags)
385 {
386 if (!(flags & rfb::clipboardUTF8)) {
387 vlog.debug("Ignoring clipboard request for unsupported formats 0x%x", flags);
388 return;
389 }
390 if (!hasLocalClipboard) {
391 vlog.debug("Ignoring unexpected clipboard request");
392 return;
393 }
394 handleClipboardRequest();
395 }
396
handleClipboardPeek(rdr::U32 flags)397 void SConnection::handleClipboardPeek(rdr::U32 flags)
398 {
399 if (client.clipboardFlags() & rfb::clipboardNotify)
400 writer()->writeClipboardNotify(hasLocalClipboard ? rfb::clipboardUTF8 : 0);
401 }
402
handleClipboardNotify(rdr::U32 flags)403 void SConnection::handleClipboardNotify(rdr::U32 flags)
404 {
405 strFree(clientClipboard);
406 clientClipboard = NULL;
407
408 if (flags & rfb::clipboardUTF8) {
409 hasLocalClipboard = false;
410 handleClipboardAnnounce(true);
411 } else {
412 handleClipboardAnnounce(false);
413 }
414 }
415
handleClipboardProvide(rdr::U32 flags,const size_t * lengths,const rdr::U8 * const * data)416 void SConnection::handleClipboardProvide(rdr::U32 flags,
417 const size_t* lengths,
418 const rdr::U8* const* data)
419 {
420 if (!(flags & rfb::clipboardUTF8)) {
421 vlog.debug("Ignoring clipboard provide with unsupported formats 0x%x", flags);
422 return;
423 }
424
425 strFree(clientClipboard);
426 clientClipboard = NULL;
427
428 clientClipboard = convertLF((const char*)data[0], lengths[0]);
429
430 // FIXME: Should probably verify that this data was actually requested
431 handleClipboardData(clientClipboard);
432 }
433
supportsQEMUKeyEvent()434 void SConnection::supportsQEMUKeyEvent()
435 {
436 writer()->writeQEMUKeyEvent();
437 }
438
versionReceived()439 void SConnection::versionReceived()
440 {
441 }
442
authSuccess()443 void SConnection::authSuccess()
444 {
445 }
446
queryConnection(const char * userName)447 void SConnection::queryConnection(const char* userName)
448 {
449 approveConnection(true);
450 }
451
approveConnection(bool accept,const char * reason)452 void SConnection::approveConnection(bool accept, const char* reason)
453 {
454 if (state_ != RFBSTATE_QUERYING)
455 throw Exception("SConnection::approveConnection: invalid state");
456
457 if (!client.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
458 if (accept) {
459 os->writeU32(secResultOK);
460 } else {
461 os->writeU32(secResultFailed);
462 if (!client.beforeVersion(3,8)) { // 3.8 onwards have failure message
463 if (!reason)
464 reason = "Authentication failure";
465 os->writeU32(strlen(reason));
466 os->writeBytes(reason, strlen(reason));
467 }
468 }
469 os->flush();
470 }
471
472 if (accept) {
473 state_ = RFBSTATE_INITIALISATION;
474 reader_ = new SMsgReader(this, is);
475 writer_ = new SMsgWriter(&client, os);
476 authSuccess();
477 } else {
478 state_ = RFBSTATE_INVALID;
479 if (reason)
480 throw AuthFailureException(reason);
481 else
482 throw AuthFailureException();
483 }
484 }
485
clientInit(bool shared)486 void SConnection::clientInit(bool shared)
487 {
488 writer_->writeServerInit(client.width(), client.height(),
489 client.pf(), client.name());
490 state_ = RFBSTATE_NORMAL;
491 }
492
close(const char * reason)493 void SConnection::close(const char* reason)
494 {
495 state_ = RFBSTATE_CLOSING;
496 cleanup();
497 }
498
setPixelFormat(const PixelFormat & pf)499 void SConnection::setPixelFormat(const PixelFormat& pf)
500 {
501 SMsgHandler::setPixelFormat(pf);
502 readyForSetColourMapEntries = true;
503 if (!pf.trueColour)
504 writeFakeColourMap();
505 }
506
framebufferUpdateRequest(const Rect & r,bool incremental)507 void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
508 {
509 if (!readyForSetColourMapEntries) {
510 readyForSetColourMapEntries = true;
511 if (!client.pf().trueColour) {
512 writeFakeColourMap();
513 }
514 }
515 }
516
fence(rdr::U32 flags,unsigned len,const char data[])517 void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
518 {
519 if (!(flags & fenceFlagRequest))
520 return;
521
522 // We cannot guarantee any synchronisation at this level
523 flags = 0;
524
525 writer()->writeFence(flags, len, data);
526 }
527
enableContinuousUpdates(bool enable,int x,int y,int w,int h)528 void SConnection::enableContinuousUpdates(bool enable,
529 int x, int y, int w, int h)
530 {
531 }
532
handleClipboardRequest()533 void SConnection::handleClipboardRequest()
534 {
535 }
536
handleClipboardAnnounce(bool available)537 void SConnection::handleClipboardAnnounce(bool available)
538 {
539 }
540
handleClipboardData(const char * data)541 void SConnection::handleClipboardData(const char* data)
542 {
543 }
544
requestClipboard()545 void SConnection::requestClipboard()
546 {
547 if (clientClipboard != NULL) {
548 handleClipboardData(clientClipboard);
549 return;
550 }
551
552 if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
553 (client.clipboardFlags() & rfb::clipboardRequest))
554 writer()->writeClipboardRequest(rfb::clipboardUTF8);
555 }
556
announceClipboard(bool available)557 void SConnection::announceClipboard(bool available)
558 {
559 hasLocalClipboard = available;
560 unsolicitedClipboardAttempt = false;
561
562 if (client.supportsEncoding(pseudoEncodingExtendedClipboard)) {
563 // Attempt an unsolicited transfer?
564 if (available &&
565 (client.clipboardSize(rfb::clipboardUTF8) > 0) &&
566 (client.clipboardFlags() & rfb::clipboardProvide)) {
567 vlog.debug("Attempting unsolicited clipboard transfer...");
568 unsolicitedClipboardAttempt = true;
569 handleClipboardRequest();
570 return;
571 }
572
573 if (client.clipboardFlags() & rfb::clipboardNotify) {
574 writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
575 return;
576 }
577 }
578
579 if (available)
580 handleClipboardRequest();
581 }
582
sendClipboardData(const char * data)583 void SConnection::sendClipboardData(const char* data)
584 {
585 if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
586 (client.clipboardFlags() & rfb::clipboardProvide)) {
587 CharArray filtered(convertCRLF(data));
588 size_t sizes[1] = { strlen(filtered.buf) + 1 };
589 const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
590
591 if (unsolicitedClipboardAttempt) {
592 unsolicitedClipboardAttempt = false;
593 if (sizes[0] > client.clipboardSize(rfb::clipboardUTF8)) {
594 vlog.debug("Clipboard was too large for unsolicited clipboard transfer");
595 if (client.clipboardFlags() & rfb::clipboardNotify)
596 writer()->writeClipboardNotify(rfb::clipboardUTF8);
597 return;
598 }
599 }
600
601 writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
602 } else {
603 CharArray latin1(utf8ToLatin1(data));
604
605 writer()->writeServerCutText(latin1.buf);
606 }
607 }
608
cleanup()609 void SConnection::cleanup()
610 {
611 delete ssecurity;
612 ssecurity = NULL;
613 delete reader_;
614 reader_ = NULL;
615 delete writer_;
616 writer_ = NULL;
617 strFree(clientClipboard);
618 clientClipboard = NULL;
619 }
620
writeFakeColourMap(void)621 void SConnection::writeFakeColourMap(void)
622 {
623 int i;
624 rdr::U16 red[256], green[256], blue[256];
625
626 for (i = 0;i < 256;i++)
627 client.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
628
629 writer()->writeSetColourMapEntries(0, 256, red, green, blue);
630 }
631