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