1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
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 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
24 //
25 
26 #include "ClientCreditsList.h"	// Interface declarations
27 
28 
29 #include <protocol/ed2k/Constants.h>
30 #include <common/Macros.h>
31 #include <common/DataFileVersion.h>
32 #include <common/FileFunctions.h>	// Needed for GetFileSize
33 
34 
35 #include "GetTickCount.h"	// Needed for GetTickCount
36 #include "Preferences.h"	// Needed for thePrefs
37 #include "ClientCredits.h"	// Needed for CClientCredits
38 #include "amule.h"		// Needed for theApp
39 #include "CFile.h"		// Needed for CFile
40 #include "Logger.h"		// Needed for Add(Debug)LogLine
41 #include "CryptoPP_Inc.h"	// Needed for Crypto functions
42 
43 
44 #define CLIENTS_MET_FILENAME		wxT("clients.met")
45 #define CLIENTS_MET_BAK_FILENAME	wxT("clients.met.bak")
46 #define CRYPTKEY_FILENAME		wxT("cryptkey.dat")
47 
48 
CClientCreditsList()49 CClientCreditsList::CClientCreditsList()
50 {
51 	m_nLastSaved = ::GetTickCount();
52 	LoadList();
53 
54 	InitalizeCrypting();
55 }
56 
57 
~CClientCreditsList()58 CClientCreditsList::~CClientCreditsList()
59 {
60 	DeleteContents(m_mapClients);
61 	delete static_cast<CryptoPP::RSASSA_PKCS1v15_SHA_Signer *>(m_pSignkey);
62 }
63 
64 
LoadList()65 void CClientCreditsList::LoadList()
66 {
67 	CFile file;
68 	CPath fileName = CPath(thePrefs::GetConfigDir() + CLIENTS_MET_FILENAME);
69 
70 	if (!fileName.FileExists()) {
71 		return;
72 	}
73 
74 	try {
75 		file.Open(fileName, CFile::read);
76 
77 		if (file.ReadUInt8() != CREDITFILE_VERSION) {
78 			AddDebugLogLineC( logCredits, wxT("Creditfile is outdated and will be replaced") );
79 			file.Close();
80 			return;
81 		}
82 
83 		// everything is ok, lets see if the backup exist...
84 		CPath bakFileName = CPath(thePrefs::GetConfigDir() + CLIENTS_MET_BAK_FILENAME);
85 
86 		bool bCreateBackup = TRUE;
87 		if (bakFileName.FileExists()) {
88 			// Ok, the backup exist, get the size
89 			CFile hBakFile(bakFileName);
90 			if ( hBakFile.GetLength() > file.GetLength()) {
91 				// the size of the backup was larger then the
92 				// org. file, something is wrong here, don't
93 				// overwrite old backup..
94 				bCreateBackup = FALSE;
95 			}
96 			// else: backup is smaller or the same size as org.
97 			// file, proceed with copying of file
98 		}
99 
100 		//else: the backup doesn't exist, create it
101 		if (bCreateBackup) {
102 			file.Close(); // close the file before copying
103 			if (!CPath::CloneFile(fileName, bakFileName, true)) {
104 				AddDebugLogLineC(logCredits,
105 					CFormat(wxT("Could not create backup file '%s'")) % fileName);
106 			}
107 			// reopen file
108 			if (!file.Open(fileName, CFile::read)) {
109 				AddDebugLogLineC( logCredits,
110 					wxT("Failed to load creditfile") );
111 				return;
112 			}
113 
114 			file.Seek(1);
115 		}
116 
117 
118 		uint32 count = file.ReadUInt32();
119 
120 		const uint32 dwExpired = time(NULL) - 12960000; // today - 150 day
121 		uint32 cDeleted = 0;
122 		for (uint32 i = 0; i < count; i++){
123 			CreditStruct* newcstruct = new CreditStruct();
124 
125 			newcstruct->key					= file.ReadHash();
126 			newcstruct->uploaded            = file.ReadUInt32();
127 			newcstruct->downloaded          = file.ReadUInt32();
128 			newcstruct->nLastSeen           = file.ReadUInt32();
129 			newcstruct->uploaded            += static_cast<uint64>(file.ReadUInt32()) << 32;
130 			newcstruct->downloaded          += static_cast<uint64>(file.ReadUInt32()) << 32;
131 			newcstruct->nReserved3          = file.ReadUInt16();
132 			newcstruct->nKeySize            = file.ReadUInt8();
133 			file.Read(newcstruct->abySecureIdent, MAXPUBKEYSIZE);
134 
135 			if ( newcstruct->nKeySize > MAXPUBKEYSIZE ) {
136 				// Oh dear, this is bad mojo, the file is most likely corrupt
137 				// We can no longer assume that any of the clients in the file are valid
138 				// and will have to discard it.
139 				delete newcstruct;
140 
141 				DeleteContents(m_mapClients);
142 
143 				AddDebugLogLineC( logCredits,
144 					wxT("WARNING: Corruptions found while reading Creditfile!") );
145 				return;
146 			}
147 
148 			if (newcstruct->nLastSeen < dwExpired){
149 				cDeleted++;
150 				delete newcstruct;
151 				continue;
152 			}
153 
154 			CClientCredits* newcredits = new CClientCredits(newcstruct);
155 			m_mapClients[newcredits->GetKey()] = newcredits;
156 		}
157 
158 		AddLogLineN(CFormat(wxPLURAL("Creditfile loaded, %u client is known", "Creditfile loaded, %u clients are known", count - cDeleted)) % (count - cDeleted));
159 
160 		if (cDeleted) {
161 			AddLogLineN(CFormat(wxPLURAL(" - Credits expired for %u client!", " - Credits expired for %u clients!", cDeleted)) % cDeleted);
162 		}
163 	} catch (const CSafeIOException& e) {
164 		AddDebugLogLineC(logCredits, wxT("IO error while loading clients.met file: ") + e.what());
165 	}
166 }
167 
168 
SaveList()169 void CClientCreditsList::SaveList()
170 {
171 	AddDebugLogLineN( logCredits, wxT("Saved Credit list"));
172 	m_nLastSaved = ::GetTickCount();
173 
174 	wxString name(thePrefs::GetConfigDir() + CLIENTS_MET_FILENAME);
175 	CFile file;
176 
177 	if ( !file.Create(name, true) ) {
178 		AddDebugLogLineC( logCredits, wxT("Failed to create creditfile") );
179 		return;
180 	}
181 
182 	if ( file.Open(name, CFile::write) ) {
183 		try {
184 			uint32 count = 0;
185 
186 			file.WriteUInt8( CREDITFILE_VERSION );
187 			// Temporary place-holder for number of stucts
188 			file.WriteUInt32( 0 );
189 
190 			ClientMap::iterator it = m_mapClients.begin();
191 			for ( ; it != m_mapClients.end(); ++it ) {
192 				CClientCredits* cur_credit = it->second;
193 
194 				if ( cur_credit->GetUploadedTotal() || cur_credit->GetDownloadedTotal() ) {
195 					const CreditStruct* const cstruct = cur_credit->GetDataStruct();
196 					file.WriteHash(cstruct->key);
197 					file.WriteUInt32(static_cast<uint32>(cstruct->uploaded));
198 					file.WriteUInt32(static_cast<uint32>(cstruct->downloaded));
199 					file.WriteUInt32(cstruct->nLastSeen);
200 					file.WriteUInt32(static_cast<uint32>(cstruct->uploaded >> 32));
201 					file.WriteUInt32(static_cast<uint32>(cstruct->downloaded >> 32));
202 					file.WriteUInt16(cstruct->nReserved3);
203 					file.WriteUInt8(cstruct->nKeySize);
204 					// Doesn't matter if this saves garbage, will be fixed on load.
205 					file.Write(cstruct->abySecureIdent, MAXPUBKEYSIZE);
206 					count++;
207 				}
208 			}
209 
210 			// Write the actual number of structs
211 			file.Seek( 1 );
212 			file.WriteUInt32( count );
213 		} catch (const CIOFailureException& e) {
214 			AddDebugLogLineC(logCredits, wxT("IO failure while saving clients.met: ") + e.what());
215 		}
216 	} else {
217 		AddDebugLogLineC(logCredits, wxT("Failed to open existing creditfile!"));
218 	}
219 }
220 
221 
GetCredit(const CMD4Hash & key)222 CClientCredits* CClientCreditsList::GetCredit(const CMD4Hash& key)
223 {
224 	CClientCredits* result;
225 
226 	ClientMap::iterator it = m_mapClients.find( key );
227 
228 
229 	if ( it == m_mapClients.end() ){
230 		result = new CClientCredits(key);
231 		m_mapClients[result->GetKey()] = result;
232 	} else {
233 		result = it->second;
234 	}
235 
236 	result->SetLastSeen();
237 
238 	return result;
239 }
240 
241 
Process()242 void CClientCreditsList::Process()
243 {
244 	if (::GetTickCount() - m_nLastSaved > MIN2MS(13))
245 		SaveList();
246 }
247 
248 
CreateKeyPair()249 bool CClientCreditsList::CreateKeyPair()
250 {
251 	try {
252 		CryptoPP::AutoSeededX917RNG<CryptoPP::DES_EDE3> rng;
253 		CryptoPP::InvertibleRSAFunction privkey;
254 		privkey.Initialize(rng, RSAKEYSIZE);
255 
256 		// Nothing we can do against this filename2char :/
257 		wxCharBuffer filename = filename2char(thePrefs::GetConfigDir() + CRYPTKEY_FILENAME);
258 		CryptoPP::FileSink *fileSink = new CryptoPP::FileSink(filename);
259 		CryptoPP::Base64Encoder *privkeysink = new CryptoPP::Base64Encoder(fileSink);
260 		privkey.DEREncode(*privkeysink);
261 		privkeysink->MessageEnd();
262 
263 		// Do not delete these pointers or it will blow in your face.
264 		// cryptopp semantics is giving ownership of these objects.
265 		//
266 		// delete privkeysink;
267 		// delete fileSink;
268 
269 		AddDebugLogLineN(logCredits, wxT("Created new RSA keypair"));
270 	} catch(const CryptoPP::Exception& e) {
271 		AddDebugLogLineC(logCredits,
272 			wxString(wxT("Failed to create new RSA keypair: ")) +
273 			wxString(char2unicode(e.what())));
274 		wxFAIL;
275 		return false;
276 	}
277 
278 	return true;
279 }
280 
281 
InitalizeCrypting()282 void CClientCreditsList::InitalizeCrypting()
283 {
284 	m_nMyPublicKeyLen = 0;
285 	memset(m_abyMyPublicKey,0,80); // not really needed; better for debugging tho
286 	m_pSignkey = NULL;
287 
288 	if (!thePrefs::IsSecureIdentEnabled()) {
289 		return;
290 	}
291 
292 	try {
293 		// check if keyfile is there
294 		if (wxFileExists(thePrefs::GetConfigDir() + CRYPTKEY_FILENAME)) {
295 			off_t keySize = CPath::GetFileSize(thePrefs::GetConfigDir() + CRYPTKEY_FILENAME);
296 
297 			if (keySize == wxInvalidOffset) {
298 				AddDebugLogLineC(logCredits, wxT("Cannot access 'cryptkey.dat', please check permissions."));
299 				return;
300 			} else if (keySize == 0) {
301 				AddDebugLogLineC(logCredits, wxT("'cryptkey.dat' is empty, recreating keypair."));
302 				CreateKeyPair();
303 			}
304 		} else {
305 			AddLogLineN(_("No 'cryptkey.dat' file found, creating.") );
306 			CreateKeyPair();
307 		}
308 
309 		// load private key
310 		CryptoPP::FileSource filesource(filename2char(thePrefs::GetConfigDir() + CRYPTKEY_FILENAME), true, new CryptoPP::Base64Decoder);
311 		m_pSignkey = new CryptoPP::RSASSA_PKCS1v15_SHA_Signer(filesource);
312 		// calculate and store public key
313 		CryptoPP::RSASSA_PKCS1v15_SHA_Verifier pubkey(*static_cast<CryptoPP::RSASSA_PKCS1v15_SHA_Signer *>(m_pSignkey));
314 		CryptoPP::ArraySink asink(m_abyMyPublicKey, 80);
315 		pubkey.GetMaterial().Save(asink);
316 		m_nMyPublicKeyLen = asink.TotalPutLength();
317 		asink.MessageEnd();
318 	} catch (const CryptoPP::Exception& e) {
319 		delete static_cast<CryptoPP::RSASSA_PKCS1v15_SHA_Signer *>(m_pSignkey);
320 		m_pSignkey = NULL;
321 
322 		AddDebugLogLineC(logCredits,
323 			wxString(wxT("Error while initializing encryption keys: ")) +
324 			wxString(char2unicode(e.what())));
325 	}
326 }
327 
328 
CreateSignature(CClientCredits * pTarget,byte * pachOutput,uint8 nMaxSize,uint32 ChallengeIP,uint8 byChaIPKind,void * sigkey)329 uint8 CClientCreditsList::CreateSignature(CClientCredits* pTarget, byte* pachOutput, uint8 nMaxSize, uint32 ChallengeIP, uint8 byChaIPKind, void* sigkey)
330 {
331 	CryptoPP::RSASSA_PKCS1v15_SHA_Signer* signer =
332 		static_cast<CryptoPP::RSASSA_PKCS1v15_SHA_Signer *>(sigkey);
333 	// signer param is used for debug only
334 	if (signer == NULL)
335 		signer = static_cast<CryptoPP::RSASSA_PKCS1v15_SHA_Signer *>(m_pSignkey);
336 
337 	// create a signature of the public key from pTarget
338 	wxASSERT( pTarget );
339 	wxASSERT( pachOutput );
340 
341 	if ( !CryptoAvailable() ) {
342 		return 0;
343 	}
344 
345 	try {
346 		CryptoPP::SecByteBlock sbbSignature(signer->SignatureLength());
347 		CryptoPP::AutoSeededX917RNG<CryptoPP::DES_EDE3> rng;
348 		byte abyBuffer[MAXPUBKEYSIZE+9];
349 		uint32 keylen = pTarget->GetSecIDKeyLen();
350 		memcpy(abyBuffer,pTarget->GetSecureIdent(),keylen);
351 		// 4 additional bytes random data send from this client
352 		uint32 challenge = pTarget->m_dwCryptRndChallengeFrom;
353 		wxASSERT ( challenge != 0 );
354 		PokeUInt32(abyBuffer+keylen,challenge);
355 
356 		uint16 ChIpLen = 0;
357 		if ( byChaIPKind != 0){
358 			ChIpLen = 5;
359 			PokeUInt32(abyBuffer+keylen+4, ChallengeIP);
360 			PokeUInt8(abyBuffer+keylen+4+4,byChaIPKind);
361 		}
362 		signer->SignMessage(rng, abyBuffer ,keylen+4+ChIpLen , sbbSignature.begin());
363 		CryptoPP::ArraySink asink(pachOutput, nMaxSize);
364 		asink.Put(sbbSignature.begin(), sbbSignature.size());
365 
366 		return asink.TotalPutLength();
367 	} catch (const CryptoPP::Exception& e) {
368 		AddDebugLogLineC(logCredits, wxString(wxT("Error while creating signature: ")) + wxString(char2unicode(e.what())));
369 		wxFAIL;
370 
371 		return 0;
372 	}
373 }
374 
375 
VerifyIdent(CClientCredits * pTarget,const byte * pachSignature,uint8 nInputSize,uint32 dwForIP,uint8 byChaIPKind)376 bool CClientCreditsList::VerifyIdent(CClientCredits* pTarget, const byte* pachSignature, uint8 nInputSize, uint32 dwForIP, uint8 byChaIPKind)
377 {
378 	wxASSERT( pTarget );
379 	wxASSERT( pachSignature );
380 	if ( !CryptoAvailable() ){
381 		pTarget->SetIdentState(IS_NOTAVAILABLE);
382 		return false;
383 	}
384 	bool bResult;
385 	try {
386 		CryptoPP::StringSource ss_Pubkey((byte*)pTarget->GetSecureIdent(),pTarget->GetSecIDKeyLen(),true,0);
387 		CryptoPP::RSASSA_PKCS1v15_SHA_Verifier pubkey(ss_Pubkey);
388 		// 4 additional bytes random data send from this client +5 bytes v2
389 		byte abyBuffer[MAXPUBKEYSIZE+9];
390 		memcpy(abyBuffer,m_abyMyPublicKey,m_nMyPublicKeyLen);
391 		uint32 challenge = pTarget->m_dwCryptRndChallengeFor;
392 		wxASSERT ( challenge != 0 );
393 		PokeUInt32(abyBuffer+m_nMyPublicKeyLen, challenge);
394 
395 		// v2 security improvments (not supported by 29b, not used as default by 29c)
396 		uint8 nChIpSize = 0;
397 		if (byChaIPKind != 0){
398 			nChIpSize = 5;
399 			uint32 ChallengeIP = 0;
400 			switch (byChaIPKind) {
401 				case CRYPT_CIP_LOCALCLIENT:
402 					ChallengeIP = dwForIP;
403 					break;
404 				case CRYPT_CIP_REMOTECLIENT:
405 					// Ignore local ip...
406 					if (!theApp->GetPublicIP(true)) {
407 						if (::IsLowID(theApp->GetED2KID())){
408 							AddDebugLogLineN(logCredits, wxT("Warning: Maybe SecureHash Ident fails because LocalIP is unknown"));
409 							// Fallback to local ip...
410 							ChallengeIP = theApp->GetPublicIP();
411 						} else {
412 							ChallengeIP = theApp->GetED2KID();
413 						}
414 					} else {
415 						ChallengeIP = theApp->GetPublicIP();
416 					}
417 					break;
418 				case CRYPT_CIP_NONECLIENT: // maybe not supported in future versions
419 					ChallengeIP = 0;
420 					break;
421 			}
422 			PokeUInt32(abyBuffer+m_nMyPublicKeyLen+4, ChallengeIP);
423 			PokeUInt8(abyBuffer+m_nMyPublicKeyLen+4+4, byChaIPKind);
424 		}
425 		//v2 end
426 
427 		bResult = pubkey.VerifyMessage(abyBuffer, m_nMyPublicKeyLen+4+nChIpSize, pachSignature, nInputSize);
428 	} catch (const CryptoPP::Exception& e) {
429 		AddDebugLogLineC(logCredits, wxString(wxT("Error while verifying identity: ")) + wxString(char2unicode(e.what())));
430 		bResult = false;
431 	}
432 
433 	if (!bResult){
434 		if (pTarget->GetIdentState() == IS_IDNEEDED)
435 			pTarget->SetIdentState(IS_IDFAILED);
436 	} else {
437 		pTarget->Verified(dwForIP);
438 	}
439 
440 	return bResult;
441 }
442 
443 
CryptoAvailable() const444 bool CClientCreditsList::CryptoAvailable() const
445 {
446 	return m_nMyPublicKeyLen > 0 && m_pSignkey != NULL;
447 }
448 
449 
450 #ifdef _DEBUG
Debug_CheckCrypting()451 bool CClientCreditsList::Debug_CheckCrypting(){
452 	// create random key
453 	CryptoPP::AutoSeededX917RNG<CryptoPP::DES_EDE3> rng;
454 
455 	CryptoPP::RSASSA_PKCS1v15_SHA_Signer priv(rng, 384);
456 	CryptoPP::RSASSA_PKCS1v15_SHA_Verifier pub(priv);
457 
458 	byte abyPublicKey[80];
459 	CryptoPP::ArraySink asink(abyPublicKey, 80);
460 	pub.DEREncode(asink);
461 	int8 PublicKeyLen = asink.TotalPutLength();
462 	asink.MessageEnd();
463 	uint32 challenge = rand();
464 	// create fake client which pretends to be this emule
465 	CreditStruct* newcstruct = new CreditStruct();
466 	CClientCredits newcredits(newcstruct);
467 	newcredits.SetSecureIdent(m_abyMyPublicKey,m_nMyPublicKeyLen);
468 	newcredits.m_dwCryptRndChallengeFrom = challenge;
469 	// create signature with fake priv key
470 	byte pachSignature[200];
471 	memset(pachSignature,0,200);
472 	uint8 sigsize = CreateSignature(&newcredits,pachSignature,200,0,false, &priv);
473 
474 
475 	// next fake client uses the random created public key
476 	CreditStruct* newcstruct2 = new CreditStruct();
477 	CClientCredits newcredits2(newcstruct2);
478 	newcredits2.m_dwCryptRndChallengeFor = challenge;
479 
480 	// if you uncomment one of the following lines the check has to fail
481 	//abyPublicKey[5] = 34;
482 	//m_abyMyPublicKey[5] = 22;
483 	//pachSignature[5] = 232;
484 
485 	newcredits2.SetSecureIdent(abyPublicKey,PublicKeyLen);
486 
487 	//now verify this signature - if it's true everything is fine
488 	return VerifyIdent(&newcredits2,pachSignature,sigsize,0,0);
489 }
490 #endif
491 // File_checked_for_headers
492