Merge branch 'readonlypassword' of https://github.com/michalsrb/tigervnc into viewonly
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index eeaeafe..20182a0 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -28,6 +28,7 @@
 #include <rfb/ServerCore.h>
 #include <rfb/encodings.h>
 #include <rfb/EncodeManager.h>
+#include <rfb/SSecurity.h>
 
 #include <rfb/LogWriter.h>
 
@@ -36,13 +37,14 @@
 static LogWriter vlog("SConnection");
 
 // AccessRights values
-const SConnection::AccessRights SConnection::AccessView       = 0x0001;
-const SConnection::AccessRights SConnection::AccessKeyEvents  = 0x0002;
-const SConnection::AccessRights SConnection::AccessPtrEvents  = 0x0004;
-const SConnection::AccessRights SConnection::AccessCutText    = 0x0008;
-const SConnection::AccessRights SConnection::AccessDefault    = 0x03ff;
-const SConnection::AccessRights SConnection::AccessNoQuery    = 0x0400;
-const SConnection::AccessRights SConnection::AccessFull       = 0xffff;
+const SConnection::AccessRights SConnection::AccessView           = 0x0001;
+const SConnection::AccessRights SConnection::AccessKeyEvents      = 0x0002;
+const SConnection::AccessRights SConnection::AccessPtrEvents      = 0x0004;
+const SConnection::AccessRights SConnection::AccessCutText        = 0x0008;
+const SConnection::AccessRights SConnection::AccessSetDesktopSize = 0x0010;
+const SConnection::AccessRights SConnection::AccessDefault        = 0x03ff;
+const SConnection::AccessRights SConnection::AccessNoQuery        = 0x0400;
+const SConnection::AccessRights SConnection::AccessFull           = 0xffff;
 
 
 SConnection::SConnection(bool reverseConnection_)
@@ -223,6 +225,7 @@
     if (done) {
       state_ = RFBSTATE_QUERYING;
       queryConnection(ssecurity->getUserName());
+      setAccessRights(ssecurity->getAccessRights());
     }
   } catch (AuthFailureException& e) {
     vlog.error("AuthFailureException: %s", e.str());
diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h
index 7718f4c..0379b17 100644
--- a/common/rfb/SConnection.h
+++ b/common/rfb/SConnection.h
@@ -28,7 +28,6 @@
 #include <rdr/OutStream.h>
 #include <rfb/SMsgHandler.h>
 #include <rfb/SecurityServer.h>
-#include <rfb/SSecurity.h>
 
 namespace rfb {
 
@@ -123,13 +122,14 @@
     // is up to the derived class.
 
     typedef rdr::U16 AccessRights;
-    static const AccessRights AccessView;         // View display contents
-    static const AccessRights AccessKeyEvents;    // Send key events
-    static const AccessRights AccessPtrEvents;    // Send pointer events
-    static const AccessRights AccessCutText;      // Send/receive clipboard events
-    static const AccessRights AccessDefault;      // The default rights, INCLUDING FUTURE ONES
-    static const AccessRights AccessNoQuery;      // Connect without local user accepting
-    static const AccessRights AccessFull;         // All of the available AND FUTURE rights
+    static const AccessRights AccessView;           // View display contents
+    static const AccessRights AccessKeyEvents;      // Send key events
+    static const AccessRights AccessPtrEvents;      // Send pointer events
+    static const AccessRights AccessCutText;        // Send/receive clipboard events
+    static const AccessRights AccessSetDesktopSize; // Change desktop size
+    static const AccessRights AccessDefault;        // The default rights, INCLUDING FUTURE ONES
+    static const AccessRights AccessNoQuery;        // Connect without local user accepting
+    static const AccessRights AccessFull;           // All of the available AND FUTURE rights
     virtual void setAccessRights(AccessRights ar) = 0;
 
     // Other methods
diff --git a/common/rfb/SSecurity.h b/common/rfb/SSecurity.h
index afc744e..6da63c3 100644
--- a/common/rfb/SSecurity.h
+++ b/common/rfb/SSecurity.h
@@ -44,13 +44,12 @@
 #define __RFB_SSECURITY_H__
 
 #include <rdr/types.h>
+#include <rfb/SConnection.h>
 #include <rfb/util.h>
 #include <list>
 
 namespace rfb {
 
-  class SConnection;
-
   class SSecurity {
   public:
     virtual ~SSecurity() {}
@@ -63,6 +62,8 @@
     // necessary.  Null may be returned to indicate that there is no user name
     // for this security type.
     virtual const char* getUserName() const = 0;
+
+    virtual SConnection::AccessRights getAccessRights() const { return SConnection::AccessDefault; }
   };
 
 }
diff --git a/common/rfb/SSecurityVncAuth.cxx b/common/rfb/SSecurityVncAuth.cxx
index ca81bf3..05488f6 100644
--- a/common/rfb/SSecurityVncAuth.cxx
+++ b/common/rfb/SSecurityVncAuth.cxx
@@ -49,10 +49,27 @@
  "access the server", &SSecurityVncAuth::vncAuthPasswdFile);
 
 SSecurityVncAuth::SSecurityVncAuth(void)
-  : sentChallenge(false), responsePos(0), pg(&vncAuthPasswd)
+  : sentChallenge(false), responsePos(0), pg(&vncAuthPasswd), accessRights(0)
 {
 }
 
+bool SSecurityVncAuth::verifyResponse(const PlainPasswd &password)
+{
+  rdr::U8 expectedResponse[vncAuthChallengeSize];
+
+  // Calculate the expected response
+  rdr::U8 key[8];
+  int pwdLen = strlen(password.buf);
+  for (int i=0; i<8; i++)
+    key[i] = i<pwdLen ? password.buf[i] : 0;
+  deskey(key, EN0);
+  for (int j = 0; j < vncAuthChallengeSize; j += 8)
+    des(challenge+j, expectedResponse+j);
+
+  // Check the actual response
+  return memcmp(response, expectedResponse, vncAuthChallengeSize) == 0;
+}
+
 bool SSecurityVncAuth::processMsg(SConnection* sc)
 {
   rdr::InStream* is = sc->getInStream();
@@ -72,25 +89,23 @@
 
   if (responsePos < vncAuthChallengeSize) return false;
 
-  PlainPasswd passwd(pg->getVncAuthPasswd());
+  PlainPasswd passwd, passwdReadOnly;
+  pg->getVncAuthPasswd(&passwd, &passwdReadOnly);
 
   if (!passwd.buf)
     throw AuthFailureException("No password configured for VNC Auth");
 
-  // Calculate the expected response
-  rdr::U8 key[8];
-  int pwdLen = strlen(passwd.buf);
-  for (int i=0; i<8; i++)
-    key[i] = i<pwdLen ? passwd.buf[i] : 0;
-  deskey(key, EN0);
-  for (int j = 0; j < vncAuthChallengeSize; j += 8)
-    des(challenge+j, challenge+j);
+  if (verifyResponse(passwd)) {
+    accessRights = SConnection::AccessDefault;
+    return true;
+  }
 
-  // Check the actual response
-  if (memcmp(challenge, response, vncAuthChallengeSize) != 0)
-    throw AuthFailureException();
+  if (passwdReadOnly.buf && verifyResponse(passwdReadOnly)) {
+    accessRights = SConnection::AccessView;
+    return true;
+  }
 
-  return true;
+  throw AuthFailureException();
 }
 
 VncAuthPasswdParameter::VncAuthPasswdParameter(const char* name,
@@ -99,8 +114,8 @@
 : BinaryParameter(name, desc, 0, 0, ConfServer), passwdFile(passwdFile_) {
 }
 
-char* VncAuthPasswdParameter::getVncAuthPasswd() {
-  ObfuscatedPasswd obfuscated;
+void VncAuthPasswdParameter::getVncAuthPasswd(PlainPasswd *password, PlainPasswd *readOnlyPassword) {
+  ObfuscatedPasswd obfuscated, obfuscatedReadOnly;
   getData((void**)&obfuscated.buf, &obfuscated.length);
 
   if (obfuscated.length == 0) {
@@ -108,18 +123,20 @@
       CharArray fname(passwdFile->getData());
       if (!fname.buf[0]) {
         vlog.info("neither %s nor %s params set", getName(), passwdFile->getName());
-        return 0;
+        return;
       }
 
       FILE* fp = fopen(fname.buf, "r");
       if (!fp) {
         vlog.error("opening password file '%s' failed",fname.buf);
-        return 0;
+        return;
       }
 
       vlog.debug("reading password file");
-      obfuscated.buf = new char[128];
-      obfuscated.length = fread(obfuscated.buf, 1, 128, fp);
+      obfuscated.buf = new char[8];
+      obfuscated.length = fread(obfuscated.buf, 1, 8, fp);
+      obfuscatedReadOnly.buf = new char[8];
+      obfuscatedReadOnly.length = fread(obfuscatedReadOnly.buf, 1, 8, fp);
       fclose(fp);
     } else {
       vlog.info("%s parameter not set", getName());
@@ -127,10 +144,11 @@
   }
 
   try {
-    PlainPasswd password(obfuscated);
-    return password.takeBuf();
+    PlainPasswd plainPassword(obfuscated);
+    password->replaceBuf(plainPassword.takeBuf());
+    PlainPasswd plainPasswordReadOnly(obfuscatedReadOnly);
+    readOnlyPassword->replaceBuf(plainPasswordReadOnly.takeBuf());
   } catch (...) {
-    return 0;
   }
 }
 
diff --git a/common/rfb/SSecurityVncAuth.h b/common/rfb/SSecurityVncAuth.h
index 8a2d0f6..e9f379b 100644
--- a/common/rfb/SSecurityVncAuth.h
+++ b/common/rfb/SSecurityVncAuth.h
@@ -25,6 +25,7 @@
 #define __RFB_SSECURITYVNCAUTH_H__
 
 #include <rfb/Configuration.h>
+#include <rfb/Password.h>
 #include <rfb/SSecurity.h>
 #include <rfb/Security.h>
 #include <rdr/types.h>
@@ -33,15 +34,15 @@
 
   class VncAuthPasswdGetter {
   public:
-    // getPasswd() returns a string or null if unsuccessful.  The
-    // SSecurityVncAuth object delete[]s the string when done.
-    virtual char* getVncAuthPasswd()=0;
+    // getVncAuthPasswd() fills buffer of given password and readOnlyPassword.
+    // If there was no read only password in the file, readOnlyPassword buffer is null.
+    virtual void getVncAuthPasswd(PlainPasswd *password, PlainPasswd *readOnlyPassword)=0;
   };
 
   class VncAuthPasswdParameter : public VncAuthPasswdGetter, BinaryParameter {
   public:
     VncAuthPasswdParameter(const char* name, const char* desc, StringParameter* passwdFile_);
-    virtual char* getVncAuthPasswd();
+    virtual void getVncAuthPasswd(PlainPasswd *password, PlainPasswd *readOnlyPassword);
   protected:
     StringParameter* passwdFile;
   };
@@ -52,15 +53,18 @@
     virtual bool processMsg(SConnection* sc);
     virtual int getType() const {return secTypeVncAuth;}
     virtual const char* getUserName() const {return 0;}
+    virtual SConnection::AccessRights getAccessRights() const { return accessRights; }
     static StringParameter vncAuthPasswdFile;
     static VncAuthPasswdParameter vncAuthPasswd;
   private:
+    bool verifyResponse(const PlainPasswd &password);
     enum {vncAuthChallengeSize = 16};
     rdr::U8 challenge[vncAuthChallengeSize];
     rdr::U8 response[vncAuthChallengeSize];
     bool sentChallenge;
     int responsePos;
     VncAuthPasswdGetter* pg;
+    SConnection::AccessRights accessRights;
   };
 }
 #endif
diff --git a/common/rfb/Security.h b/common/rfb/Security.h
index 196eb42..85bc325 100644
--- a/common/rfb/Security.h
+++ b/common/rfb/Security.h
@@ -25,7 +25,6 @@
 #include <rdr/types.h>
 #include <rfb/Configuration.h>
 #include <rfb/CSecurity.h>
-#include <rfb/SSecurity.h>
 
 #include <list>
 
diff --git a/common/rfb/SecurityServer.h b/common/rfb/SecurityServer.h
index 0986619..019d67f 100644
--- a/common/rfb/SecurityServer.h
+++ b/common/rfb/SecurityServer.h
@@ -22,9 +22,10 @@
 
 #include <rfb/Configuration.h>
 #include <rfb/Security.h>
-#include <rfb/SSecurity.h>
 
 namespace rfb {
+  
+  class SSecurity;
 
   class SecurityServer : public Security {
   public:
diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx
index ae2fd24..b11a352 100644
--- a/common/rfb/ServerCore.cxx
+++ b/common/rfb/ServerCore.cxx
@@ -89,6 +89,10 @@
 ("SendCutText",
  "Send clipboard changes to clients.",
  true);
+rfb::BoolParameter rfb::Server::acceptSetDesktopSize
+("AcceptSetDesktopSize",
+ "Accept set desktop size events from clients.",
+ true);
 rfb::BoolParameter rfb::Server::queryConnect
 ("QueryConnect",
  "Prompt the local user to accept or reject incoming connections.",
diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h
index e12a8bc..5fc996f 100644
--- a/common/rfb/ServerCore.h
+++ b/common/rfb/ServerCore.h
@@ -46,6 +46,7 @@
     static BoolParameter acceptPointerEvents;
     static BoolParameter acceptCutText;
     static BoolParameter sendCutText;
+    static BoolParameter acceptSetDesktopSize;
     static BoolParameter queryConnect;
 
   };
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index 618048a..274c496 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -584,6 +584,9 @@
 {
   unsigned int result;
 
+  if (!(accessRights & AccessSetDesktopSize)) return;
+  if (!rfb::Server::acceptSetDesktopSize) return;
+
   // Don't bother the desktop with an invalid configuration
   if (!layout.validate(fb_width, fb_height)) {
     writer()->writeExtendedDesktopSize(reasonClient, resultInvalid,
diff --git a/unix/vncpasswd/vncpasswd.cxx b/unix/vncpasswd/vncpasswd.cxx
index 7ba0b22..16c925e 100644
--- a/unix/vncpasswd/vncpasswd.cxx
+++ b/unix/vncpasswd/vncpasswd.cxx
@@ -81,6 +81,36 @@
   else return 1;
 }
 
+static ObfuscatedPasswd* readpassword() {
+  while (true) {
+    PlainPasswd passwd(getpassword("Password:"));
+    if (!passwd.buf) {
+      perror("getpassword error");
+      exit(1);
+    }
+    if (strlen(passwd.buf) < 6) {
+      if (strlen(passwd.buf) == 0) {
+        fprintf(stderr,"Password not changed\n");
+        exit(1);
+      }
+      fprintf(stderr,"Password must be at least 6 characters - try again\n");
+      continue;
+    }
+
+    PlainPasswd passwd2(getpassword("Verify:"));
+    if (!passwd2.buf) {
+      perror("getpass error");
+      exit(1);
+    }
+    if (strcmp(passwd.buf, passwd2.buf) != 0) {
+      fprintf(stderr,"Passwords don't match - try again\n");
+      continue;
+    }
+
+    return new ObfuscatedPasswd(passwd);
+  }
+}
+
 int main(int argc, char** argv)
 {
   prog = argv[0];
@@ -113,28 +143,13 @@
   }
 
   while (true) {
-    PlainPasswd passwd(getpassword("Password:"));
-    if (!passwd.buf) {
-      perror("getpassword error");
-      exit(1);
-    }   
-    if (strlen(passwd.buf) < 6) {
-      if (strlen(passwd.buf) == 0) {
-        fprintf(stderr,"Password not changed\n");
-        exit(1);
-      }
-      fprintf(stderr,"Password must be at least 6 characters - try again\n");
-      continue;
-    }
+    ObfuscatedPasswd* obfuscated = readpassword();
+    ObfuscatedPasswd* obfuscatedReadOnly = 0;
 
-    PlainPasswd passwd2(getpassword("Verify:"));
-    if (!passwd2.buf) {
-      perror("getpass error");
-      exit(1);
-    }   
-    if (strcmp(passwd.buf, passwd2.buf) != 0) {
-      fprintf(stderr,"Passwords don't match - try again\n");
-      continue;
+    fprintf(stderr, "Would you like to enter a view-only password (y/n)? ");
+    char yesno[3];
+    if (fgets(yesno, 3, stdin) != NULL && (yesno[0] == 'y' || yesno[0] == 'Y')) {
+      obfuscatedReadOnly = readpassword();
     }
 
     FILE* fp = fopen(fname,"w");
@@ -144,13 +159,18 @@
     }
     chmod(fname, S_IRUSR|S_IWUSR);
 
-    ObfuscatedPasswd obfuscated(passwd);
-
-    if (fwrite(obfuscated.buf, obfuscated.length, 1, fp) != 1) {
+    if (fwrite(obfuscated->buf, obfuscated->length, 1, fp) != 1) {
       fprintf(stderr,"Writing to %s failed\n",fname);
       exit(1);
     }
 
+    if (obfuscatedReadOnly) {
+      if (fwrite(obfuscatedReadOnly->buf, obfuscatedReadOnly->length, 1, fp) != 1) {
+        fprintf(stderr,"Writing to %s failed\n",fname);
+        exit(1);
+      }
+    }
+
     fclose(fp);
 
     return 0;