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