1 /***************************************************************************
2 cclientssl.cpp - description
3 -------------------
4 begin : Sat Dec 7 2002
5 copyright : (C) 2002-2003 by Mathias Küster
6 email : mathen@users.berlios.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "cclientssl.h"
19
20 #include <stdio.h>
21 #include <string.h>
22
23 #include "cclient.h"
24 #include "core/types.h"
25
26 // needed to parse tag to check version for backwards compatibility
27 #include "cutils.h"
28
29 #include "dclib-ssl-use.h"
30
31 // define DEBUG_SSL_CHAT 1
32
33 /** */
CClientSSL()34 CClientSSL::CClientSSL()
35 {
36 m_pCryptPrivateMessageList = new CStringList<CSSLObject>();
37 }
38
39 /** */
~CClientSSL()40 CClientSSL::~CClientSSL()
41 {
42 delete m_pCryptPrivateMessageList;
43 m_pCryptPrivateMessageList = 0;
44 }
45
46
47 #if DCLIB_HAS_SSL == 1
48 /** */
PrivateChat(CClient * client,CMessagePrivateChat * msg)49 void CClientSSL::PrivateChat( CClient * client, CMessagePrivateChat * msg )
50 {
51 /**
52 * STATE:
53 *
54 * CLIENT1 CLIENT2
55 * none S0 none S0
56 * send request S0 --> receive req S1
57 * receive response S1 <-- send response S1
58 * send pk S2 --> receive pk S2
59 * receive pk S2 <-- send pk S2
60 * send sk S3 <-> send sk S3
61 * crypted <*> crypted
62 *
63 */
64
65 CSSLObject * SSLObject = 0;
66
67 #ifdef DEBUG_SSL_CHAT
68 printf("msg->m_sMessage=%s ", msg->m_sMessage.Data());
69 if ( m_pCryptPrivateMessageList->Get( msg->m_sSrcNick, &SSLObject ) != 0 )
70 {
71 printf("no SSLObject\n");
72 }
73 else
74 {
75 printf("m_bHandshakeState=%d\n", SSLObject->m_bHandshakeState);
76 }
77
78 SSLObject = 0;
79 #endif
80
81 if ( msg->m_sMessage == "<request secchannel>" )
82 {
83 // send response
84 if ( m_pCryptPrivateMessageList->Get( msg->m_sSrcNick, &SSLObject ) != 0 )
85 m_pCryptPrivateMessageList->Add( msg->m_sSrcNick, SSLObject = new CSSLObject() );
86
87 bool isOldVersion = false;
88 CMessageMyInfo remoteinfo;
89 client->UserList()->GetUserMyInfo( msg->m_sSrcNick, &remoteinfo );
90 if ( remoteinfo.m_eClientVersion == eucvDCGUI )
91 {
92 int major = 0;
93 int minor = 0;
94 int micro = 0;
95 if ( CUtils::ParseVersionTag( remoteinfo.m_sVerComment, major, minor, micro ) )
96 {
97 if ( (major > 0) ||
98 ((major == 0) && (minor > 3)) ||
99 ((major == 0) && (minor == 3) && (micro >= 14))
100 )
101 {
102 // dclib 0.3.14 or higher, OK
103 }
104 else
105 {
106 #ifdef DEBUG_SSL_CHAT
107 printf("Detected dclib <= 0.3.13, using old handshaking\n");
108 #endif
109 isOldVersion = true;
110 }
111 }
112 }
113
114 if ( SSLObject->m_bHandshakeState != 0 )
115 {
116 if ( isOldVersion )
117 {
118 if ( SSLObject->m_bHandshakeState == 1 )
119 {
120 #ifdef DEBUG_SSL_CHAT
121 printf("<request secchannel> in wrong state %d, adjusting to 0 (old version)\n", SSLObject->m_bHandshakeState);
122 #endif
123 SSLObject->m_bHandshakeState = 0;
124 }
125 else
126 {
127 #ifdef DEBUG_SSL_CHAT
128 printf("<request secchannel> in wrong state %d, ignoring (old version)\n", SSLObject->m_bHandshakeState);
129 #endif
130 }
131 }
132 else
133 {
134 #ifdef DEBUG_SSL_CHAT
135 printf("<request secchannel> in wrong state %d, resetting (new version)\n", SSLObject->m_bHandshakeState);
136 #endif
137 SSLObject->m_bHandshakeState = 0;
138 }
139
140 // clear message
141 msg->m_sMessage.Empty();
142 return;
143 }
144
145 msg->m_eSecureState = esecsHANDSHAKE;
146 SSLObject->m_bHandshakeState = 2;
147
148 /*
149 * Before 0.3.14, each client had to start from getting a <request secchannel>.
150 * The problem with this is that hubs will block it for PM spam
151 * It's now been changed so that a client can start from <request secchannel> or <response secchannel>
152 */
153 //client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "<request secchannel>" );
154
155 if ( isOldVersion )
156 {
157 #ifdef DEBUG_SSL_CHAT
158 printf("Sending extra <request secchannel> for backwards compatibility\n");
159 #endif
160 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "<request secchannel>" );
161 SSLObject->m_bHandshakeState = 1;
162 }
163
164 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "<response secchannel>" );
165
166 if ( isOldVersion == false )
167 {
168 if ( !m_pRSA )
169 if ( GenerateRsaKey() == false )
170 SSLObject->m_bHandshakeState = 0;
171
172 CString s = GetPublicRsaKey();
173
174 if ( s.IsEmpty() )
175 return;
176
177 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "PK:"+s );
178 }
179
180 // clear message
181 msg->m_sMessage.Empty();
182 }
183 else if ( msg->m_sMessage == "<response secchannel>" )
184 {
185 // send pk
186 if ( m_pCryptPrivateMessageList->Get( msg->m_sSrcNick, &SSLObject ) != 0 )
187 {
188 m_pCryptPrivateMessageList->Add( msg->m_sSrcNick, SSLObject = new CSSLObject() );
189 SSLObject->m_bHandshakeState = 0;
190 }
191
192 // SSLObject->m_bHandshakeState may be 1 with dclib <= 0.3.13
193 if ( (SSLObject->m_bHandshakeState != 0) && (SSLObject->m_bHandshakeState != 1) )
194 {
195 // SSLObject->m_bHandshakeState = 0;
196 #ifdef DEBUG_SSL_CHAT
197 printf("<response secchannel> in wrong state %d, ignoring\n", SSLObject->m_bHandshakeState);
198 #endif
199 msg->m_sMessage.Empty();
200 return;
201 }
202
203 if ( !m_pRSA )
204 if ( GenerateRsaKey() == false )
205 SSLObject->m_bHandshakeState = 0;
206
207 CString s = GetPublicRsaKey();
208
209 if ( s.IsEmpty() )
210 return;
211
212 msg->m_eSecureState = esecsHANDSHAKE;
213
214 SSLObject->m_bHandshakeState = 2;
215
216 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "PK:"+s );
217
218 // clear message
219 msg->m_sMessage.Empty();
220 }
221 else if ( msg->m_sMessage.StartsWith("PK:",3) )
222 {
223 // receive pk, send sk
224 if ( m_pCryptPrivateMessageList->Get( msg->m_sSrcNick, &SSLObject ) != 0 )
225 return;
226 if ( SSLObject->m_bHandshakeState != 2 )
227 {
228 // SSLObject->m_bHandshakeState = 0;
229 #ifdef DEBUG_SSL_CHAT
230 printf("PK: in wrong state %d, ignoring\n", SSLObject->m_bHandshakeState);
231 #endif
232 msg->m_sMessage.Empty();
233 return;
234 }
235 if ( SetPublicKey( SSLObject, msg->m_sMessage.Mid(3,msg->m_sMessage.Length()-3) ) == false )
236 {
237 SSLObject->m_bHandshakeState = 0;
238 return;
239 }
240
241 InitSessionKey( SSLObject );
242
243 CString s = GetSessionKey(SSLObject);
244
245 if ( s.IsEmpty() )
246 return;
247
248 msg->m_eSecureState = esecsHANDSHAKE;
249
250 SSLObject->m_bHandshakeState = 3;
251
252 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "SK:"+s );
253
254 // clear message
255 msg->m_sMessage.Empty();
256 }
257 else if ( msg->m_sMessage.StartsWith("SK:",3) )
258 {
259 // now we have a secure line
260 if ( m_pCryptPrivateMessageList->Get( msg->m_sSrcNick, &SSLObject ) != 0 )
261 return;
262 if ( SSLObject->m_bHandshakeState != 3 )
263 {
264 // SSLObject->m_bHandshakeState = 0;
265 #ifdef DEBUG_SSL_CHAT
266 printf("SK: in wrong state %d, ignoring\n", SSLObject->m_bHandshakeState);
267 #endif
268 msg->m_sMessage.Empty();
269 return;
270 }
271 if ( SetSessionKey( SSLObject, msg->m_sMessage.Mid(3,msg->m_sMessage.Length()-3) ) == false )
272 {
273 SSLObject->m_bHandshakeState = 0;
274 return;
275 }
276
277 msg->m_eSecureState = esecsENCRYPTED;
278
279 SSLObject->m_bHandshakeState = 4;
280
281 CString s = EncryptData( SSLObject, "Secure channel created." );
282
283 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "SEC:"+s );
284
285 // clear message
286 msg->m_sMessage.Empty();
287 }
288 else if ( msg->m_sMessage.StartsWith("SEC:",4) )
289 {
290 // secure data
291 if ( m_pCryptPrivateMessageList->Get( msg->m_sSrcNick, &SSLObject ) != 0 )
292 return;
293 if ( SSLObject->m_bHandshakeState != 4 )
294 {
295 // you end up here after <close secchannel>
296 #ifdef DEBUG_SSL_CHAT
297 printf("SEC: in wrong state %d, resetting\n", SSLObject->m_bHandshakeState);
298 #endif
299 SSLObject->m_bHandshakeState = 0;
300
301 // clear message
302 msg->m_sMessage.Empty();
303
304 return;
305 }
306
307 CString s = DecryptData( SSLObject, msg->m_sMessage.Mid(4,msg->m_sMessage.Length()-4) );
308
309 if ( s.NotEmpty() )
310 {
311 msg->m_sMessage = s;
312
313 if ( s == "<close secchannel>" )
314 {
315 SSLObject->m_bHandshakeState = 0;
316
317 CString s = EncryptData( SSLObject, "<close secchannel>" );
318
319 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "SEC:"+s );
320 client->CDCProto::SendPrivateMessage( client->GetNick(), msg->m_sSrcNick, "Secure channel closed." );
321
322 // clear message
323 msg->m_sMessage.Empty();
324 }
325 else
326 {
327 msg->m_eSecureState = esecsENCRYPTED;
328 }
329 }
330 }
331 else
332 {
333 // unsecure data
334 if ( m_pCryptPrivateMessageList->Get( msg->m_sSrcNick, &SSLObject ) != 0 )
335 return;
336
337 // reset handshake mode
338 SSLObject->m_bHandshakeState = 0;
339 }
340 }
341
342 /** */
EncryptMessage(CClient *,CString nick,CString message)343 CString CClientSSL::EncryptMessage( CClient * /*client*/, CString nick, CString message )
344 {
345 CString s;
346 CSSLObject * SSLObject;
347
348 if ( m_pCryptPrivateMessageList->Get( nick, &SSLObject ) == 0 )
349 {
350 if ( SSLObject->m_bHandshakeState == 4 )
351 {
352 s = EncryptData( SSLObject, message );
353
354 if ( s.NotEmpty() )
355 {
356 s = "SEC:" + s;
357 }
358 }
359 }
360
361 return s;
362 }
363
364 /** */
LeaveHub(CClient *,CString nick)365 void CClientSSL::LeaveHub( CClient * /*client*/, CString nick )
366 {
367 CSSLObject * SSLObject;
368
369 if ( m_pCryptPrivateMessageList->Get( nick, &SSLObject ) == 0 )
370 {
371 SSLObject->m_bHandshakeState = 0;
372 }
373 }
374
375 /** */
Init()376 void CClientSSL::Init()
377 {
378 m_pCryptPrivateMessageList->Clear();
379 }
380
381 #else // DCLIB_HAS_SSL
382
383 /** */
PrivateChat(CClient *,CMessagePrivateChat *)384 void CClientSSL::PrivateChat( CClient * /* client */, CMessagePrivateChat * /* msg */ )
385 {
386 // nothing
387 }
388
389 /** */
EncryptMessage(CClient *,CString,CString)390 CString CClientSSL::EncryptMessage( CClient * /* client */, CString /* nick */, CString /* message */ )
391 {
392 return CString();
393 }
394
395 /** */
LeaveHub(CClient *,CString)396 void CClientSSL::LeaveHub( CClient * /* client */, CString /* nick */ )
397 {
398 // nothing
399 }
400
401 /** */
Init()402 void CClientSSL::Init()
403 {
404 // nothing
405 }
406
407 #endif // DCLIB_HAS_SSL
408