1 /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18 //
19 // SSecurityVncAuth
20 //
21 // XXX not thread-safe, because d3des isn't - do we need to worry about this?
22 //
23
24 #include <rfb/SSecurityVncAuth.h>
25 #include <rdr/RandomStream.h>
26 #include <rfb/SConnection.h>
27 #include <rfb/Password.h>
28 #include <rfb/Configuration.h>
29 #include <rfb/LogWriter.h>
30 #include <rfb/util.h>
31 #include <rfb/Exception.h>
32 #include <string.h>
33 #include <stdio.h>
34 extern "C" {
35 #include <rfb/d3des.h>
36 }
37
38
39 using namespace rfb;
40
41 static LogWriter vlog("SVncAuth");
42
43 StringParameter SSecurityVncAuth::vncAuthPasswdFile
44 ("PasswordFile", "Password file for VNC authentication", "", ConfServer);
45 AliasParameter rfbauth("rfbauth", "Alias for PasswordFile",
46 &SSecurityVncAuth::vncAuthPasswdFile, ConfServer);
47 VncAuthPasswdParameter SSecurityVncAuth::vncAuthPasswd
48 ("Password", "Obfuscated binary encoding of the password which clients must supply to "
49 "access the server", &SSecurityVncAuth::vncAuthPasswdFile);
50
SSecurityVncAuth(SConnection * sc)51 SSecurityVncAuth::SSecurityVncAuth(SConnection* sc)
52 : SSecurity(sc), sentChallenge(false),
53 pg(&vncAuthPasswd), accessRights(0)
54 {
55 }
56
verifyResponse(const PlainPasswd & password)57 bool SSecurityVncAuth::verifyResponse(const PlainPasswd &password)
58 {
59 rdr::U8 expectedResponse[vncAuthChallengeSize];
60
61 // Calculate the expected response
62 rdr::U8 key[8];
63 int pwdLen = strlen(password.buf);
64 for (int i=0; i<8; i++)
65 key[i] = i<pwdLen ? password.buf[i] : 0;
66 deskey(key, EN0);
67 for (int j = 0; j < vncAuthChallengeSize; j += 8)
68 des(challenge+j, expectedResponse+j);
69
70 // Check the actual response
71 return memcmp(response, expectedResponse, vncAuthChallengeSize) == 0;
72 }
73
processMsg()74 bool SSecurityVncAuth::processMsg()
75 {
76 rdr::InStream* is = sc->getInStream();
77 rdr::OutStream* os = sc->getOutStream();
78
79 if (!sentChallenge) {
80 rdr::RandomStream rs;
81 if (!rs.hasData(vncAuthChallengeSize))
82 throw Exception("Could not generate random data for VNC auth challenge");
83 rs.readBytes(challenge, vncAuthChallengeSize);
84 os->writeBytes(challenge, vncAuthChallengeSize);
85 os->flush();
86 sentChallenge = true;
87 return false;
88 }
89
90 if (!is->hasData(vncAuthChallengeSize))
91 return false;
92
93 is->readBytes(response, vncAuthChallengeSize);
94
95 PlainPasswd passwd, passwdReadOnly;
96 pg->getVncAuthPasswd(&passwd, &passwdReadOnly);
97
98 if (!passwd.buf)
99 throw AuthFailureException("No password configured for VNC Auth");
100
101 if (verifyResponse(passwd)) {
102 accessRights = SConnection::AccessDefault;
103 return true;
104 }
105
106 if (passwdReadOnly.buf && verifyResponse(passwdReadOnly)) {
107 accessRights = SConnection::AccessView;
108 return true;
109 }
110
111 throw AuthFailureException();
112 }
113
VncAuthPasswdParameter(const char * name,const char * desc,StringParameter * passwdFile_)114 VncAuthPasswdParameter::VncAuthPasswdParameter(const char* name,
115 const char* desc,
116 StringParameter* passwdFile_)
117 : BinaryParameter(name, desc, 0, 0, ConfServer), passwdFile(passwdFile_) {
118 }
119
getVncAuthPasswd(PlainPasswd * password,PlainPasswd * readOnlyPassword)120 void VncAuthPasswdParameter::getVncAuthPasswd(PlainPasswd *password, PlainPasswd *readOnlyPassword) {
121 ObfuscatedPasswd obfuscated, obfuscatedReadOnly;
122 getData((void**)&obfuscated.buf, &obfuscated.length);
123
124 if (obfuscated.length == 0) {
125 if (passwdFile) {
126 CharArray fname(passwdFile->getData());
127 if (!fname.buf[0]) {
128 vlog.info("neither %s nor %s params set", getName(), passwdFile->getName());
129 return;
130 }
131
132 FILE* fp = fopen(fname.buf, "r");
133 if (!fp) {
134 vlog.error("opening password file '%s' failed",fname.buf);
135 return;
136 }
137
138 vlog.debug("reading password file");
139 obfuscated.buf = new char[8];
140 obfuscated.length = fread(obfuscated.buf, 1, 8, fp);
141 obfuscatedReadOnly.buf = new char[8];
142 obfuscatedReadOnly.length = fread(obfuscatedReadOnly.buf, 1, 8, fp);
143 fclose(fp);
144 } else {
145 vlog.info("%s parameter not set", getName());
146 }
147 }
148
149 try {
150 PlainPasswd plainPassword(obfuscated);
151 password->replaceBuf(plainPassword.takeBuf());
152 PlainPasswd plainPasswordReadOnly(obfuscatedReadOnly);
153 readOnlyPassword->replaceBuf(plainPasswordReadOnly.takeBuf());
154 } catch (...) {
155 }
156 }
157
158