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