- Based on work by John Kilburg http://www.physics.unlv.edu/~john/hacks/vnc/
- Update to VNC 3.3.7
- Supports (optionally) a password for the read only port
Patches:
Start script¶
diff -urN vnc-3.3.7/vncserver vnc-3.3.7.readonly/vncserver --- vnc-3.3.7/vncserver 2007-06-09 22:27:05.000000000 +0200 +++ vnc-3.3.7.readonly/vncserver 2007-06-11 18:53:00.000000000 +0200 @@ -115,6 +115,9 @@ if ($pixelformat) { $opt{'-pixelformat'} = $pixelformat; } +if (!$useVncReadOnlyPort) { + $useVncReadOnlyPort="true"; +} chop($host = `uname -n`); @@ -192,6 +195,16 @@ } } +# Check if we should use a password for readonly port + +($z,$z,$mode) = stat("$vncPasswdFile.readonly"); +if ((-e "$vncPasswdFile.readonly") || ($mode & 077)) { + $useReadOnlyPasswd="true"; +} +else { + $useReadOnlyPasswd="false"; +} + # Find display number. if ((@ARGV > 0) && ($ARGV[0] =~ /^:(\d+)$/)) { @@ -207,6 +220,10 @@ } $vncPort = 5900 + $displayNumber; +if($vncPort > 5949) { + die "Only port numbers below :49 (5949) are allowed\n"; +} +$vncReadOnlyPort = 50 + $vncPort; $desktopLog = "$vncUserDir/$host:$displayNumber.log"; unlink($desktopLog); @@ -247,6 +264,12 @@ $cmd .= " -rfbwait $rfbwait"; $cmd .= " -rfbauth $vncPasswdFile"; $cmd .= " -rfbport $vncPort"; +if ($useVncReadOnlyPort eq "true") { + $cmd .= " -readonlyport $vncReadOnlyPort"; +} +if ($useReadOnlyPasswd eq "true") { + $cmd .= " -readonlyauth $vncPasswdFile.readonly"; +} $cmd .= ' -fp "' . $fontPath . '"'; $cmd .= " -co $colorPath" if ($colorPath); $cmd .= " -alwaysshared" if ($opt{'-alwaysshared'});
C-code¶
diff -urNb vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/auth.c vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/auth.c --- vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/auth.c 2002-09-01 17:58:21.000000000 +0200 +++ vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/auth.c 2007-06-11 19:22:04.000000000 +0200 @@ -36,12 +36,18 @@ over MAX_AUTH_TRIES */ static int rfbAuthFailure(); +static int rfbReadOnlyAuthFailure(); static CARD32 rfbAuthReenable(OsTimerPtr timer, CARD32 now, pointer arg); +static CARD32 rfbReadOnlyAuthReenable(OsTimerPtr timerReadOnly, CARD32 now, pointer arg); char *rfbAuthPasswdFile = NULL; +char *rfbReadOnlyAuthPasswdFile = NULL; int rfbAuthTries = 0; +int rfbReadOnlyAuthTries = 0; Bool rfbAuthTooManyTries = FALSE; +Bool rfbReadOnlyAuthTooManyTries = FALSE; static OsTimerPtr timer = NULL; +static OsTimerPtr timerReadOnly = NULL; /* @@ -85,6 +91,45 @@ } } +/* rfbAuthNewReadOnlyClient is called when we reach the point of authenticating + * a new read only client. As authentication isn't being used we simply send + * rfbNoAuth. + */ + +void +rfbAuthNewReadOnlyClient(cl) + rfbClientPtr cl; +{ + char buf[4 + CHALLENGESIZE]; + int len; + + cl->state = RFB_AUTHENTICATION; + + if (rfbReadOnlyAuthPasswdFile && !cl->reverseConnection) { + + if (rfbReadOnlyAuthTooManyTries) { + rfbClientConnFailed(cl, "Too many authentication failures on read only port"); + return; + } + + *(CARD32 *)buf = Swap32IfLE(rfbVncAuth); + vncRandomBytes(cl->authChallenge); + memcpy(&buf[4], (char *)cl->authChallenge, CHALLENGESIZE); + len = 4 + CHALLENGESIZE; + + } else { + + *(CARD32 *)buf = Swap32IfLE(rfbNoAuth); + len = 4; + cl->state = RFB_INITIALISATION; + } + + if (WriteExact(cl->sock, buf, len) < 0) { + rfbLogPerror("rfbAuthNewReadOnlyClient: write"); + rfbCloseSock(cl->sock); + return; + } +} /* * rfbAuthProcessClientMessage is called when the client sends its @@ -107,16 +152,20 @@ rfbLog("rfbAuthProcessClientMessage: read failed\n"); else rfbLogPerror("rfbAuthProcessClientMessage: read"); - rfbAuthFailure(); + if(!cl->readOnly) rfbAuthFailure(); + else rfbReadOnlyAuthFailure(); rfbCloseSock(cl->sock); return; } - passwd = vncDecryptPasswdFromFile(rfbAuthPasswdFile); + if (!cl->readOnly) passwd = vncDecryptPasswdFromFile(rfbAuthPasswdFile); + else passwd = vncDecryptPasswdFromFile(rfbReadOnlyAuthPasswdFile); if (passwd == NULL) { - rfbLog("rfbAuthProcessClientMessage: could not get password from %s\n", + if (!cl->readOnly) rfbLog("rfbAuthProcessClientMessage: could not get password from %s\n", rfbAuthPasswdFile); + else rfbLog("rfbAuthProcessClientMessage: could not get (read-only) password from %s\n", + rfbReadOnlyAuthPasswdFile); authResult = Swap32IfLE(rfbVncAuthFailed); @@ -140,7 +189,8 @@ rfbLog("rfbAuthProcessClientMessage: authentication failed from %s\n", cl->host); - authResult = rfbAuthFailure(); + if (!cl->readOnly) authResult = rfbAuthFailure(); + else authResult = rfbReadOnlyAuthFailure(); authResult = Swap32IfLE(authResult); if (WriteExact(cl->sock, (char *)&authResult, 4) < 0) { @@ -150,7 +200,8 @@ return; } - rfbAuthTries = 0; + if (!cl->readOnly) rfbAuthTries = 0; + else rfbReadOnlyAuthTries = 0; authResult = Swap32IfLE(rfbVncAuthOK); @@ -184,6 +235,25 @@ return rfbVncAuthFailed; } +static int rfbReadOnlyAuthFailure() +{ + int i; + + rfbReadOnlyAuthTries++; + + if (rfbReadOnlyAuthTries >= MAX_AUTH_TRIES) { + + CARD32 delay = AUTH_TOO_MANY_BASE_DELAY; + for (i = MAX_AUTH_TRIES; i < rfbReadOnlyAuthTries; i++) + delay *= 2; + timerReadOnly = TimerSet(timerReadOnly, 0, delay, rfbReadOnlyAuthReenable, NULL); + + rfbReadOnlyAuthTooManyTries = TRUE; + return rfbVncAuthTooMany; + } + + return rfbVncAuthFailed; +} static CARD32 rfbAuthReenable(OsTimerPtr timer, CARD32 now, pointer arg) @@ -191,3 +261,10 @@ rfbAuthTooManyTries = FALSE; return 0; } + +static CARD32 +rfbReadOnlyAuthReenable(OsTimerPtr timerReadOnly, CARD32 now, pointer arg) +{ + rfbReadOnlyAuthTooManyTries = FALSE; + return 0; +} diff -urNb vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/init.c vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/init.c --- vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/init.c 2003-02-28 19:47:10.000000000 +0100 +++ vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/init.c 2007-06-11 19:25:10.000000000 +0200 @@ -213,6 +213,19 @@ return 2; } + if (strcasecmp(argv[i], "-readonlyport") == 0) { /* -readonlyport port */ + if (i + 1 >= argc) UseMsg(); + readOnlyPort = atoi(argv[i+1]); + rfbAlwaysShared = TRUE; + return 2; + } + + if (strcasecmp(argv[i], "-readonlymsg") == 0) { /* -readonlymsg msg */ + if (i + 1 >= argc) UseMsg(); + readOnlyMsg = argv[i+1]; + return 2; + } + if (strcasecmp(argv[i], "-rfbport") == 0) { /* -rfbport port */ if (i + 1 >= argc) UseMsg(); rfbPort = atoi(argv[i+1]); @@ -236,6 +249,12 @@ return 2; } + if (strcasecmp(argv[i], "-readonlyauth") == 0) { /* -readonlyauth passwd-file */ + if (i + 1 >= argc) UseMsg(); + rfbReadOnlyAuthPasswdFile = argv[i+1]; + return 2; + } + if (strcasecmp(argv[i], "-httpd") == 0) { if (i + 1 >= argc) UseMsg(); httpDir = argv[i+1]; @@ -874,6 +893,12 @@ ErrorF("-nocursor don't put up a cursor\n"); ErrorF("-rfbauth passwd-file use authentication on RFB protocol\n"); ErrorF("-httpd dir serve files via HTTP from here\n"); + ErrorF("-readonlyport port port for read-only RFB\n"); + ErrorF("-readonlymsg msg message to alert authenticated user that\n" + " the readonly port is active.\n"); + ErrorF("-readonlyauth passwd-file use authentication on RFB protocol\n" + " for the read-only port\n"); + ErrorF("-httpport port port for HTTP\n"); ErrorF("-deferupdate time time in ms to defer updates " "(default 40)\n"); diff -urNb vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/rfb.h vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/rfb.h --- vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/rfb.h 2003-02-28 19:47:10.000000000 +0100 +++ vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/rfb.h 2007-06-11 19:22:04.000000000 +0200 @@ -133,6 +133,8 @@ RFB_NORMAL /* normal protocol messages */ } state; + Bool readOnly; + Bool reverseConnection; Bool readyForSetColourMapEntries; @@ -293,6 +295,10 @@ extern int rfbMaxClientWait; +extern int readOnlyPort; +extern int readOnlyListenSock; +extern char *readOnlyMsg; + extern int rfbPort; extern int rfbListenSock; extern Bool rfbLocalhostOnly; @@ -418,9 +424,11 @@ /* auth.c */ extern char *rfbAuthPasswdFile; +extern char *rfbReadOnlyAuthPasswdFile; extern Bool rfbAuthenticating; extern void rfbAuthNewClient(rfbClientPtr cl); +extern void rfbAuthNewReadOnlyClient(rfbClientPtr cl); extern void rfbAuthProcessClientMessage(rfbClientPtr cl); diff -urNb vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/rfbserver.c vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/rfbserver.c --- vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/rfbserver.c 2003-02-28 19:47:10.000000000 +0100 +++ vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/rfbserver.c 2007-06-11 19:22:04.000000000 +0200 @@ -53,6 +53,8 @@ Bool rfbDontDisconnect = FALSE; int rfbMaxRects = 50; +char *readOnlyMsg = "READONLY PORT ACTIVIATED"; + static rfbClientPtr rfbNewClient(int sock); static void rfbProcessClientProtocolVersion(rfbClientPtr cl); static void rfbProcessClientNormalMessage(rfbClientPtr cl); @@ -72,6 +74,27 @@ rfbClientPtr cl; cl = rfbNewClient(sock); + cl->readOnly = 0; + +#ifdef CORBA + if (cl != NULL) + newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE), 1, 1, 1); +#endif +} + +/* + * rfbNewReadOnlyClientConnection is called from sockets.c when a new + * read-only connection comes in. + */ + +void +rfbNewReadOnlyClientConnection(sock) + int sock; +{ + rfbClientPtr cl; + + cl = rfbNewClient(sock); + cl->readOnly = 1; #ifdef CORBA if (cl != NULL) @@ -324,8 +347,8 @@ /* Minor version mismatch - warn but try to continue */ rfbLog("Ignoring minor version mismatch\n"); } - - rfbAuthNewClient(cl); + if (cl->readOnly) rfbAuthNewReadOnlyClient(cl); + else rfbAuthNewClient(cl); } @@ -364,11 +387,12 @@ rfbClientPtr cl; { rfbClientInitMsg ci; - char buf[256]; - rfbServerInitMsg *si = (rfbServerInitMsg *)buf; + char *buf; + rfbServerInitMsg *si; struct passwd *user; int len, n; rfbClientPtr otherCl, nextCl; + char *format, *msg; if ((n = ReadExact(cl->sock, (char *)&ci,sz_rfbClientInitMsg)) <= 0) { if (n == 0) @@ -379,34 +403,62 @@ return; } - si->framebufferWidth = Swap16IfLE(rfbScreen.width); - si->framebufferHeight = Swap16IfLE(rfbScreen.height); - si->format = rfbServerFormat; - si->format.redMax = Swap16IfLE(si->format.redMax); - si->format.greenMax = Swap16IfLE(si->format.greenMax); - si->format.blueMax = Swap16IfLE(si->format.blueMax); - user = getpwuid(getuid()); if (strlen(desktopName) > 128) /* sanity check on desktop name len */ desktopName[128] = 0; + #define FORMAT1 "%s's %s desktop (%s:%s) %s" + #define FORMAT2 "%s desktop (%s:%s) %s" + + if (!cl->readOnly && readOnlyMsg != NULL && readOnlyListenSock >= 0) + { + msg = readOnlyMsg; + } + else msg = ""; + + if (user) format = FORMAT1; + else format = FORMAT2; + + len = sz_rfbServerInitMsg + strlen(format) + + strlen(user->pw_name) + strlen(desktopName) + + strlen(rfbThisHost) + strlen(display) + + strlen(msg); + + buf = (char *)malloc(len + 100); /* mmmm paranoid */ + if (user) { - sprintf(buf + sz_rfbServerInitMsg, "%s's %s desktop (%s:%s)", - user->pw_name, desktopName, rfbThisHost, display); + sprintf(buf + sz_rfbServerInitMsg, FORMAT1, + user->pw_name, desktopName, rfbThisHost, display, msg); } else { - sprintf(buf + sz_rfbServerInitMsg, "%s desktop (%s:%s)", - desktopName, rfbThisHost, display); + sprintf(buf + sz_rfbServerInitMsg, FORMAT2, + desktopName, rfbThisHost, display, msg); } + + si = (rfbServerInitMsg *)buf; + len = strlen(buf + sz_rfbServerInitMsg); si->nameLength = Swap32IfLE(len); + si->framebufferWidth = Swap16IfLE(rfbScreen.width); + si->framebufferHeight = Swap16IfLE(rfbScreen.height); + si->format = rfbServerFormat; + si->format.redMax = Swap16IfLE(si->format.redMax); + si->format.greenMax = Swap16IfLE(si->format.greenMax); + si->format.blueMax = Swap16IfLE(si->format.blueMax); + + if (WriteExact(cl->sock, buf, sz_rfbServerInitMsg + len) < 0) { rfbLogPerror("rfbProcessClientInitMessage: write"); rfbCloseSock(cl->sock); + free(buf); return; } + free(buf); + buf = NULL; + si = NULL; + cl->state = RFB_NORMAL; if (!cl->reverseConnection && @@ -650,6 +702,7 @@ if (!isKeyboardEnabled(cl)) return; #endif + if (!cl->readOnly) KbdAddEvent(msg.ke.down, (KeySym)Swap32IfLE(msg.ke.key), cl); return; @@ -681,6 +734,7 @@ else pointerClient = cl; + if (!cl->readOnly) PtrAddEvent(msg.pe.buttonMask, Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl); return; @@ -708,6 +762,7 @@ return; } + if (!cl->readOnly) rfbSetXCutText(str, msg.cct.length); xfree(str); diff -urNb vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/sockets.c vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/sockets.c --- vnc-3.3.7/Xvnc/programs/Xserver/hw/vnc/sockets.c 2007-06-09 22:27:05.000000000 +0200 +++ vnc-3.3.7.readonly/Xvnc/programs/Xserver/hw/vnc/sockets.c 2007-06-11 19:22:04.000000000 +0200 @@ -61,6 +61,9 @@ int rfbListenSock = -1; Bool rfbLocalhostOnly = FALSE; +int readOnlyPort = 0; +int readOnlyListenSock = -1; + static fd_set allFds; static int maxFd = 0; @@ -117,6 +120,17 @@ FD_ZERO(&allFds); FD_SET(rfbListenSock, &allFds); maxFd = rfbListenSock; + + if (readOnlyPort != 0) { + rfbLog("rfbInitSockets: listening for readonly connections on %d\n",readOnlyPort); + if ((readOnlyListenSock = ListenOnTCPPort(readOnlyPort)) < 0) { + rfbLogPerror("ListenOnTCPPort(read-only)"); + exit(1); + } + AddEnabledDevice(readOnlyListenSock); + FD_SET(readOnlyListenSock, &allFds); + maxFd = max(readOnlyListenSock,maxFd); + } } @@ -143,7 +157,6 @@ rfbNewClientConnection(inetdSock); inetdInitDone = TRUE; } - memcpy((char *)&fds, (char *)&allFds, sizeof(fd_set)); tv.tv_sec = 0; tv.tv_usec = 0; @@ -155,21 +168,17 @@ rfbLogPerror("rfbCheckFds: select"); return; } - if (rfbListenSock != -1 && FD_ISSET(rfbListenSock, &fds)) { - if ((sock = accept(rfbListenSock, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("rfbCheckFds: accept"); return; } - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { rfbLogPerror("rfbCheckFds: fcntl"); close(sock); return; } - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("rfbCheckFds: setsockopt"); @@ -191,6 +200,41 @@ return; } + if ((readOnlyListenSock != -1) && FD_ISSET(readOnlyListenSock, &fds)) { + if ((sock = accept(readOnlyListenSock, + (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("rfbCheckFds: accept"); + return; + } + + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("rfbCheckFds: fcntl"); + close(sock); + return; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("rfbCheckFds: setsockopt"); + close(sock); + return; + } + + fprintf(stderr,"\n"); + rfbLog("Got read-only connection from client %s\n", + inet_ntoa(addr.sin_addr)); + + AddEnabledDevice(sock); + FD_SET(sock, &allFds); + maxFd = max(sock,maxFd); + + rfbNewReadOnlyClientConnection(sock); + + FD_CLR(rfbListenSock, &fds); + if (--nfds == 0) + return; + } + for (sock = 0; sock <= maxFd; sock++) { if (FD_ISSET(sock, &fds) && FD_ISSET(sock, &allFds)) { rfbProcessClientMessage(sock);