@@ -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 @@ using namespace rfb; | |||
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 @@ void SConnection::processSecurityMsg() | |||
if (done) { | |||
state_ = RFBSTATE_QUERYING; | |||
queryConnection(ssecurity->getUserName()); | |||
setAccessRights(ssecurity->getAccessRights()); | |||
} | |||
} catch (AuthFailureException& e) { | |||
vlog.error("AuthFailureException: %s", e.str()); |
@@ -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 @@ namespace rfb { | |||
// 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 |
@@ -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 @@ namespace rfb { | |||
// 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; } | |||
}; | |||
} |
@@ -49,10 +49,27 @@ VncAuthPasswdParameter SSecurityVncAuth::vncAuthPasswd | |||
"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 @@ bool SSecurityVncAuth::processMsg(SConnection* sc) | |||
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 @@ VncAuthPasswdParameter::VncAuthPasswdParameter(const char* name, | |||
: 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 @@ char* VncAuthPasswdParameter::getVncAuthPasswd() { | |||
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 @@ char* VncAuthPasswdParameter::getVncAuthPasswd() { | |||
} | |||
try { | |||
PlainPasswd password(obfuscated); | |||
return password.takeBuf(); | |||
PlainPasswd plainPassword(obfuscated); | |||
password->replaceBuf(plainPassword.takeBuf()); | |||
PlainPasswd plainPasswordReadOnly(obfuscatedReadOnly); | |||
readOnlyPassword->replaceBuf(plainPasswordReadOnly.takeBuf()); | |||
} catch (...) { | |||
return 0; | |||
} | |||
} | |||
@@ -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 @@ namespace rfb { | |||
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 @@ namespace rfb { | |||
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 |
@@ -25,7 +25,6 @@ | |||
#include <rdr/types.h> | |||
#include <rfb/Configuration.h> | |||
#include <rfb/CSecurity.h> | |||
#include <rfb/SSecurity.h> | |||
#include <list> | |||
@@ -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: |
@@ -89,6 +89,10 @@ rfb::BoolParameter rfb::Server::sendCutText | |||
("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.", |
@@ -46,6 +46,7 @@ namespace rfb { | |||
static BoolParameter acceptPointerEvents; | |||
static BoolParameter acceptCutText; | |||
static BoolParameter sendCutText; | |||
static BoolParameter acceptSetDesktopSize; | |||
static BoolParameter queryConnect; | |||
}; |
@@ -584,6 +584,9 @@ void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height, | |||
{ | |||
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, |
@@ -81,6 +81,36 @@ static int encrypt_pipe() { | |||
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 @@ int main(int argc, char** argv) | |||
} | |||
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 @@ int main(int argc, char** argv) | |||
} | |||
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; |