Developer Documentation

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
LicenseManagerActive.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41
42 /*===========================================================================*\
43 * *
44 * $Revision$ *
45 * $LastChangedBy$ *
46 * $Date$ *
47 * *
48 \*===========================================================================*/
49
50
51
52 /*
53 License File format:
54
55 0: Signature over all other entries
56 1: Expiry date
57 2: Plugin filename
58 3: coreHash
59 4: pluginHash
60 5: cpuHash
61 6: windowsProductId (Windows only otherwise filled with "-" before hashing)
62 7..?: mac Address hashes
63
64 */
65
66
67 // Windows required to get network address infos
68 #ifdef WIN32
69 #include <winsock2.h>
70 #include <iphlpapi.h>
71 #pragma comment(lib, "IPHLPAPI.lib")
72 #endif
73
74 #ifdef ARCH_DARWIN
75 #include <sys/types.h>
76 #include <sys/sysctl.h>
77 #endif
78
79
80 #include <OpenFlipper/LicenseManager/LicenseManagerActive.hh>
81 #include <OpenFlipper/common/GlobalOptions.hh>
82 #include <QFile>
83 #include <QString>
84 #include <QCryptographicHash>
85 #include <QNetworkInterface>
86
87 #include <limits>
88
89
98 QByteArray decodeString(const QString& _string ,bool _utf8){
99
100 if (_utf8)
101 return _string.toUtf8();
102 else
103 return _string.toLatin1();
104
105 }
106
107 LicenseManager::~LicenseManager()
108 {
109 exit(0);
110 }
111
112 LicenseManager::LicenseManager()
113 {
114
115 authenticated_ = false;
116
117 // On startup, block all signals by default until the plugin is authenticated!
118 QObject::blockSignals( true );
119 }
120
121 // Override default block signals. Transparent if authenticated, otherwise
122 // the function will always block the signals automatically
123 void LicenseManager::blockSignals( bool _state) {
124
125 if ( !authenticated() ) {
126 QObject::blockSignals( true );
127 } else {
128 QObject::blockSignals(_state);
129 }
130
131 }
132
133
134 bool LicenseManager::timestampOk() {
135
136 bool notExpired = false;
137 bool gotTimestampEntry = false;
138
139 quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
140 quint64 lastTimestamp = timestamp;
141 quint64 timestampEntry = 0;
142 quint64 lastTimestampEntryNum = 0;
143
144
145 // ===============================================================================================
146 // Read last Timestemp
147 // ===============================================================================================
148
149 const QString title = "Timestamp/"+pluginFileName();
150
151 QString lastTimestampEntry = OpenFlipperSettings().value(title,"empty").toString();
152
153 if( lastTimestampEntry==QString("empty") ){
154 notExpired = true;
155 } else {
156 lastTimestampEntryNum = lastTimestampEntry.toULongLong(&gotTimestampEntry,16);
157 }
158
159 // ===============================================================================================
160 // Decrypt last Timestamp
161 // ===============================================================================================
162
163 const unsigned int factor = 30000;
164 const std::string name = pluginFileName().toStdString();
165 const unsigned int moduloFactor = 72; //<100
166 const int nameSize = pluginFileName().size();
167 const int nameEncr = ( name[0] ) + ( name[nameSize-1] ) + (name[(nameSize-1) / 2] );
168
169 bool moduloOK = (lastTimestampEntryNum -( 100 * (lastTimestampEntryNum / 100) ) == (lastTimestampEntryNum/100) % moduloFactor );
170
171 lastTimestampEntryNum = lastTimestampEntryNum / 100;
172 lastTimestampEntryNum = lastTimestampEntryNum - nameEncr;
173 lastTimestampEntryNum = lastTimestampEntryNum * factor;
174
175 lastTimestamp = lastTimestampEntryNum;
176
177 // ===============================================================================================
178 // Check last Timestemp
179 // ===============================================================================================
180
181 if (notExpired||(timestamp>(lastTimestamp-360000) && moduloOK)) {
182 notExpired = true;
183 } else {
184 timestamp = std::numeric_limits<quint64>::max();
185 }
186
187
188 // ===============================================================================================
189 // Encrypt new Timestamp
190 // ===============================================================================================
191
192 timestampEntry = timestamp;
193
194 timestampEntry = timestampEntry / factor;
195 timestampEntry = timestampEntry + nameEncr;
196 timestampEntry = (timestampEntry * 100) + (timestampEntry % moduloFactor);
197
198
199 // ===============================================================================================
200 // Write new Timestemp
201 // ===============================================================================================
202
203 OpenFlipperSettings().setValue ( title, QString::number(timestampEntry,16) );
204
205 return notExpired;
206
207 }
208
209
210 // Plugin authentication function.
211 bool LicenseManager::authenticate() {
212
213 // Construct license string (will be cleaned up if license valid)
214 authstring_ = "==\n";
215 authstring_ += "PluginName: " + pluginFileName() + "\n";
216
217 // ===============================================================================================
218 // Read License file, if exists
219 // ===============================================================================================
220 QString saltPre;
221 ADD_SALT_PRE(saltPre);
222
223 QString saltPost;
224 ADD_SALT_POST(saltPost);
225
226 QString licenseFileName = OpenFlipper::Options::licenseDirStr() + QDir::separator() + pluginFileName() + ".lic";
227 QFile file( licenseFileName );
228 QStringList elements; //has no elements, if file is invalid or was not found
229 bool signatureOk = false;
230
231 bool utf8Encoded = true;
232
233 if (file.open(QIODevice::ReadOnly|QIODevice::Text))
234 {
235 QString licenseContents = file.readAll();
236 elements = licenseContents.split('\n',QString::SkipEmptyParts);
237 bool fileOk = !elements.empty() && elements[0] != "ERROR";
238
239 if (fileOk)
240 {
241 // simplify license file entries
242 for ( int i = 0 ; i < elements.size(); ++i )
243 elements[i] = elements[i].simplified();
244
245 // Check the signature of the file (excluding first element as this is the signature itself)
246 QString license = saltPre;
247 for ( int i = 1 ; i < elements.size(); ++i )
248 license += elements[i];
249 license += saltPost;
250 QString licenseHash = QCryptographicHash::hash ( license.toUtf8() , QCryptographicHash::Sha1 ).toHex();
251 signatureOk = licenseHash == elements[0];
252
253 if (signatureOk)
254 utf8Encoded = true;
255 else
256 {
257 licenseHash = QCryptographicHash::hash ( license.toLatin1() , QCryptographicHash::Sha1 ).toHex();
258 signatureOk = licenseHash == elements[0];
259 if (signatureOk)
260 utf8Encoded = false;
261 }
262
263 }
264 else
265 elements = QStringList();
266 }
267
268 // ===============================================================================================
269 // Compute hash value of Core application binary
270 // ===============================================================================================
271
272 #ifdef WIN32
273 QFile coreApp(OpenFlipper::Options::applicationDirStr() + QDir::separator() + "OpenFlipper.exe");
274 #elif defined ARCH_DARWIN
275 QFile coreApp(OpenFlipper::Options::applicationDirStr() + QDir::separator() + ".." +
276 QDir::separator() + "MacOS"+
277 QDir::separator() + "OpenFlipper");
278 #else
279 QFile coreApp(OpenFlipper::Options::applicationDirStr() + QDir::separator() + "bin" + QDir::separator() + TOSTRING(PRODUCT_STRING));
280 #endif
281
282 if ( ! coreApp.exists() ) {
283 std::cerr << "Error finding core application for security check! : " << coreApp.fileName().toStdString() << std::endl;
284 return false;
285 }
286
287 coreApp.open(QIODevice::ReadOnly);
288 QCryptographicHash sha1sumCore( QCryptographicHash::Sha1 );
289 sha1sumCore.addData(coreApp.readAll() );
290 coreApp.close();
291
292 QString coreHash = QString(sha1sumCore.result().toHex());
293
294
295 // ===============================================================================================
296 // Compute hash of Plugin binary
297 // ===============================================================================================
298
299 #ifdef WIN32
300 QFile pluginFile(OpenFlipper::Options::pluginDirStr() + QDir::separator() + pluginFileName() + ".dll");
301 #elif defined ARCH_DARWIN
302 QFile pluginFile(OpenFlipper::Options::pluginDirStr() + QDir::separator() + "lib" + pluginFileName() + ".so");
303 #else
304 QFile pluginFile(OpenFlipper::Options::pluginDirStr() + QDir::separator() + "lib" + pluginFileName() + ".so");
305 #endif
306
307 if ( ! pluginFile.exists() ) {
308 std::cerr << "Error finding plugin file for security check!" << std::endl;
309 std::cerr << "Path: " << pluginFile.fileName().toStdString() << std::endl;
310 return false;
311 }
312
313 pluginFile.open(QIODevice::ReadOnly);
314 QCryptographicHash sha1sumPlugin( QCryptographicHash::Sha1 );
315 sha1sumPlugin.addData(pluginFile.readAll());
316 pluginFile.close();
317
318 QString pluginHash = QString(sha1sumPlugin.result().toHex());
319
320 // ===============================================================================================
321 // Compute hash of network interfaces
322 // ===============================================================================================
323
324 QStringList macHashes;
325
326 #ifdef WIN32
327
328 #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
329 #define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
330
331 // Pointer for iterating over adapters
332 PIP_ADAPTER_ADDRESSES pAddresses = NULL;
333
334 // Length of the buffer to get information
335 ULONG outBufLen = 0;
336
337 // Allocate enough for one info struct
338 outBufLen = sizeof (IP_ADAPTER_ADDRESSES);
339 pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
340
341 // default to unspecified address family ( get all interfaces .. 4 and 6)
342 ULONG family = AF_UNSPEC;
343
344 // Set the flags to pass to GetAdaptersAddresses
345 ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
346
347 // Make an initial call to GetAdaptersAddresses to get the
348 // size needed into the outBufLen variable
349 if (GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) {
350 FREE(pAddresses);
351 pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
352 }
353
354 // If we allocated the required memory
355 if (pAddresses != NULL) {
356
357 // Get the required info
358 DWORD dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
359
360 if (dwRetVal == NO_ERROR) {
361
362 // pointer to iterate over all available structs .. initialize to first one
363 PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses;
364
365 while (pCurrAddresses) {
366
367 // Check if this device contains a mac
368 if (pCurrAddresses->PhysicalAddressLength != 0) {
369 QString currentMac = "";
370
371 for (uint i = 0; i < pCurrAddresses->PhysicalAddressLength; i++) {
372
373 currentMac += QString("%1").arg( (int) pCurrAddresses->PhysicalAddress[i] , 2 ,16,QChar('0'));
374
375 if (i != (pCurrAddresses->PhysicalAddressLength - 1))
376 currentMac +=":";
377 }
378
379 // Ignore non ethernet macs with more than 5 blocks
380 // Only check ethernet and wireless interfaces
381 if ( (currentMac.count(":") == 5) &&
382 ( pCurrAddresses->IfType == IF_TYPE_IEEE80211 || pCurrAddresses->IfType == IF_TYPE_ETHERNET_CSMACD ) ) {
383 // Cleanup and remember mac adress
384
385 currentMac = (decodeString(currentMac,utf8Encoded)).toUpper();
386 currentMac = currentMac.remove(":");
387 macHashes.push_back(currentMac);
388 }
389
390 }
391
392 // Next interface
393 pCurrAddresses = pCurrAddresses->Next;
394 }
395
396 }
397
398 }
399
400 FREE(pAddresses);
401
402 #else
403
404 // Get all Network Interfaces
405 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
406 foreach ( QNetworkInterface netInterface, interfaces ) {
407
408 // Ignore loopback interfaces
409 if ( ( netInterface.flags() & QNetworkInterface::IsLoopBack ) ) {
410 continue;
411 }
412
413 // Ignore non ethernet macs
414 if ( netInterface.hardwareAddress().count(":") != 5 ) {
415 continue;
416 }
417
418 // Cleanup mac adress
419 QString currentMac = (decodeString(netInterface.hardwareAddress(),utf8Encoded)).toUpper();
420 currentMac = currentMac.remove(":");
421
422 macHashes.push_back(currentMac);
423 }
424
425 #endif
426
427
428 // cleanup the list from duplicates (virtual interfaces on windows connected to an existing device ... )
429 macHashes.removeDuplicates();
430
431 // generate hashes
432 for (int i = 0 ; i < macHashes.size(); ++i )
433 // Cleanup mac adress
434 macHashes[i] = QCryptographicHash::hash ( decodeString(macHashes[i],utf8Encoded) , QCryptographicHash::Sha1 ).toHex();
435
436 // ===============================================================================================
437 // Compute hash of processor information
438 // ===============================================================================================
439
440 QString processor("Unknown");
441
442 #ifdef WIN32
443 QSettings registryCPU("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor", QSettings::NativeFormat);
444
445 QStringList cpus = registryCPU.childGroups();
446 if ( cpus.size() != 0 ) {
447 processor = registryCPU.value( cpus[0]+"/ProcessorNameString", "Unknown" ).toString();
448 }
449
450 #elif defined ARCH_DARWIN
451
452 size_t lenCPU;
453 char *pCPU;
454
455 // First call to get required size
456 sysctlbyname("machdep.cpu.brand_string", NULL, &lenCPU, NULL, 0);
457
458 // allocate
459 pCPU = (char * )malloc(lenCPU);
460
461 // Second call to get data
462 sysctlbyname("machdep.cpu.brand_string", pCPU, &lenCPU, NULL, 0);
463
464 // Output
465 processor = QString(pCPU);
466
467 // free memory
468 free(pCPU);
469
470 #else
471 QFile cpuinfo("/proc/cpuinfo");
472 if ( cpuinfo.exists() ) {
473 cpuinfo.open(QFile::ReadOnly);
474 QTextStream stream(&cpuinfo);
475 QStringList splitted = stream.readAll().split("\n",QString::SkipEmptyParts);
476
477 int position = splitted.indexOf ( QRegExp("^model name.*") );
478 if ( position != -1 ){
479 QString cpuModel = splitted[position].section(':', -1).simplified();
480 processor = cpuModel;
481 }
482 }
483 #endif
484
485 QString cpuHash = QCryptographicHash::hash ( decodeString(processor,utf8Encoded) , QCryptographicHash::Sha1 ).toHex();
486
487 // ===============================================================================================
488 // Get windows product id
489 // ===============================================================================================
490
491
492
493 #ifdef WIN32
494 QSettings registryProduct("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", QSettings::NativeFormat);
495 QString productId = registryProduct.value( "ProductId", "Unknown" ).toString();
496 #else
497 QString productId = "-";
498 #endif
499
500 QString productHash = QCryptographicHash::hash ( decodeString(productId,utf8Encoded) , QCryptographicHash::Sha1 ).toHex();
501
502 // ===============================================================================================
503 // Check License or generate request
504 // ===============================================================================================
505
506 if (!elements.empty()) //valid file was found
507 {
508
509 // Check expiry date
510 QDate currentDate = QDate::currentDate();
511 QDate expiryDate = QDate::fromString(elements[1],Qt::ISODate);
512 bool expired = (currentDate > expiryDate);
513 // Get number of available mac adresses
514 QStringList licensedMacs;
515 for ( int i = 7 ; i < elements.size(); ++i ) {
516 licensedMacs.push_back(elements[i]);
517 }
518
519 bool macFound = false;
520 for ( int i = 0; i < macHashes.size(); ++i ) {
521 if ( licensedMacs.contains(macHashes[i]) )
522 macFound = true;
523 }
524
525 if ( !signatureOk ) {
526 authstring_ += tr("License Error: The license file signature for Plugin \"") + name() + tr("\" is invalid!\n\n");
527 } else if ( expired ) {
528 authstring_ += tr("License Error: The license for plugin \"") + name() + tr("\" has expired on ") + elements[1] + "!\n\n";
529 } else if ( !timestampOk() ) {
530 authstring_ += tr("License Error: System time has been reset. The license for plugin \"") + name() + tr("\" has been expired!\n\n");
531 } else if ( elements[2] != pluginFileName() ) {
532 authstring_ += tr("License Error: The license file contains plugin name\"") + elements[2] + tr("\" but this is plugin \"") + name() + "\"!\n\n";
533 } else if ( elements[3] != coreHash ) {
534 authstring_ += tr("License Error: The license file for plugin \"") + name() + tr("\" is invalid for the currently running OpenFlipper Core!\n\n");
535 } else if ( elements[4] != pluginHash ) {
536 authstring_ += tr("License Error: The plugin \"") + name() + tr("\" is a different version than specified in license file!\n\n");
537 } else if ( elements[5] != cpuHash ) {
538 authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed CPU?)!\n\n";
539 } else if ( elements[6] != productHash ) {
540 authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed OS?)!\n\n";
541 } else if ( !macFound ) {
542 authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed Network?)!\n\n";
543 } else {
544 authenticated_ = true;
545 }
546
547 // Clean it on success
548 if ( authenticated_ )
549 authstring_ = "";
550
551 }
552
553 if ( authenticated_ ) {
554 blockSignals(false);
555 } else {
556 authstring_ += tr("Message: License check for plugin failed.\n");
557 authstring_ += tr("Message: Please get a valid License!\n");
558 authstring_ += tr("Message: Send the following Information to \n");
559 authstring_ += tr("Contact mail: ") + CONTACTMAIL + "\n\n";
560 authstring_ += pluginFileName() +"\n";
561 authstring_ += coreHash +"\n";
562 authstring_ += pluginHash +"\n";
563 authstring_ += cpuHash +"\n";
564 authstring_ += productHash +"\n";
565
566 for ( int i = 0 ; i < macHashes.size(); ++i )
567 authstring_ += macHashes[i] +"\n";
568
569 QString keyRequest = saltPre + pluginFileName() + coreHash + pluginHash + cpuHash + productHash + macHashes.join("") + saltPost;
570 QString requestSig = QCryptographicHash::hash ( keyRequest.toUtf8() , QCryptographicHash::Sha1 ).toHex();
571 authstring_ += requestSig + "\n";
572
573 authenticated_ = false;
574 }
575
576 return authenticated_;
577 }
578
579 QString LicenseManager::licenseError() {
580 return authstring_;
581 }
582
583 bool LicenseManager::authenticated() {
584 // Function to check if the plugin is authenticated
585 return authenticated_;
586 }
587
588 void LicenseManager::connectNotify ( const char * /*signal*/ ) {
589
590 // if the plugin is not authenticated and something wants to connect, we block all signals and force a direct disconnect
591 // here, rendering all signal/slot connections useless.
592 if ( !authenticated() ) {
593 blockSignals(true);
594 disconnect();
595 }
596
597 }
598
599 QString LicenseManager::pluginFileName() {
600 // FileName of the plugin. has to be set via the salt file
601 QString pluginFileName;
602 ADD_PLUGIN_FILENAME(pluginFileName);
603 return pluginFileName;
604 }
605
OpenFlipperSettings
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
Definition: GlobalOptions.cc:938
LicenseManager::pluginFileName
virtual QString pluginFileName()
Definition: LicenseManagerActive.cc:599
LicenseManager::licenseError
QString licenseError()
Definition: LicenseManagerActive.cc:579
LicenseManager::authenticated
bool authenticated()
Definition: LicenseManagerActive.cc:583
LicenseManager::authenticated_
bool authenticated_
This flag is true if authentication was successful.
Definition: LicenseManagerActive.hh:125
TOSTRING
#define TOSTRING(x)
QSettings object containing all program settings of OpenFlipper.
Definition: GlobalOptions.hh:168
LicenseManager::authstring_
QString authstring_
License information string.
Definition: LicenseManagerActive.hh:128
GlobalOptions.hh
LicenseManager::connectNotify
void connectNotify(const char *signal)
Definition: LicenseManagerActive.cc:588
OpenFlipperQSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: OpenFlipperQSettings.cc:53
OpenFlipperQSettings::setValue
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
Definition: OpenFlipperQSettings.cc:68
LicenseManager::blockSignals
void blockSignals(bool _state)
Definition: LicenseManagerActive.cc:123
LicenseManager::authenticate
bool authenticate()
Definition: LicenseManagerActive.cc:211
LicenseManager::name
virtual QString name()=0