File: CertStatusCache.cpp

package info (click to toggle)
beid 3.5.2.dfsg-10
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 147,240 kB
  • ctags: 34,507
  • sloc: cpp: 149,944; ansic: 41,577; java: 8,927; cs: 6,528; sh: 2,426; perl: 1,866; xml: 805; python: 463; makefile: 263; lex: 92
file content (575 lines) | stat: -rw-r--r-- 14,431 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
/* ****************************************************************************

 * eID Middleware Project.
 * Copyright (C) 2008-2009 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see
 * http://www.gnu.org/licenses/.

**************************************************************************** */
#include <time.h>
#include <stdio.h>

#include "CertStatusCache.h"
#include "APLConfig.h"
#include "MiscUtil.h"
#include "Thread.h"
#include "MWException.h"
#include "eidErrors.h"
#include "Log.h"
#include <errno.h>

#ifndef WIN32
#include "Util.h"
#include <fcntl.h>
#endif

namespace eIDMW 
{

/* ****************
*** APL_CscLine ***
***************** */

//Take a line from the file cache and split it into field
APL_CscLine::APL_CscLine(const char *lineIn)
{
	char line[CSC_MAX_LINE_LENGHT];

	int i;
	int sep=0;
	char *field[4];
	char *stop;

	field[sep++]=&line[0];

	for(i=0;i<CSC_MAX_LINE_LENGHT-1;i++)
	{
		if(lineIn[i]=='\n' || lineIn[i]=='\0')
		{
			break;
		}
		else if(lineIn[i]=='|')
		{
			if(sep>3)
				break;
			line[i]='\0';
			field[sep++]=&line[i+1];
		}
		else
		{
			line[i]=lineIn[i];
		}
	}
	line[i]='\0';

	if(sep==4)
	{
		m_ulUniqueID=strtoul(field[0],&stop,10);
		m_ulFlags=strtoul(field[1],&stop,10);
		m_Status=(CSC_Status)atoi(field[2]);
		m_Validity.assign(field[3]); 
	}
	else
	{	
		m_ulUniqueID=0;
		m_ulFlags=0;
		m_Status=CSC_STATUS_NONE;
		m_Validity=""; 
	}
}

APL_CscLine::APL_CscLine(unsigned long ulUniqueID,CSC_Validation validationType,bool bAllowTestRoot,bool bAllowBadDate)
{
	m_ulUniqueID=ulUniqueID;
	m_ulFlags=	validationType 
				+ (bAllowTestRoot?1:0) * CSC_VALIDATION_FLAG_TESTROOT
				+ (bAllowBadDate?1:0) * CSC_VALIDATION_FLAG_BADDATE;
	m_Status=CSC_STATUS_NONE;
	m_Validity=""; 
}

APL_CscLine::APL_CscLine(unsigned long ulUniqueID,unsigned long ulFlags)
{
	m_ulUniqueID=ulUniqueID;
	m_ulFlags=ulFlags;
	m_Status=CSC_STATUS_NONE;
	m_Validity=""; 
}

APL_CscLine::~APL_CscLine()
{
}

//Two lines are equal if the UniqueID AND the ValidationType are booth equal
bool APL_CscLine::operator== (const APL_CscLine& cscline)
{
	return isEqual(cscline.m_ulUniqueID,cscline.m_ulFlags);
}

bool APL_CscLine::isEqual (unsigned long ulUniqueID,unsigned long ulFlags)
{
	if(m_ulUniqueID==ulUniqueID 
		&& m_ulFlags==ulFlags)
		return true;
	else 
		return false;
}

//Set Validity timestamp in format YYYYMMDDThhmmss by default
void APL_CscLine::setValidity(unsigned long delay)
{
	CTimestampUtil::getTimestamp(m_Validity,delay,CSC_VALIDITY_FORMAT);
}

//PRIVATE: return true is Line Validity > now
bool APL_CscLine::checkValidity()
{
	return CTimestampUtil::checkTimestamp(m_Validity,CSC_VALIDITY_FORMAT);
}

void APL_CscLine::writeLine(FILE *f)
{
#ifdef WIN32
	fprintf_s(f,"%lu|%lu|%d|%s\n",m_ulUniqueID,m_ulFlags,m_Status,m_Validity.c_str());
#else
	fprintf(f,"%lu|%lu|%d|%s\n",m_ulUniqueID,m_ulFlags,m_Status,m_Validity.c_str());
#endif
}

int APL_CscLine::compareValidity(const APL_CscLine &line1,const APL_CscLine &line2)
{
	return line1.m_Validity.compare(line2.m_Validity);
}

/* *********************
*** APL_CertStatusCache ***
********************** */
APL_CertStatusCache::APL_CertStatusCache(APL_CryptoFwk *cryptoFwk)
{
	MWLOG(LEV_INFO, MOD_APL, L"Create CertStatusCache object");

	m_cryptoFwk=cryptoFwk;

	APL_Config conf_file(CConfig::EIDMW_CONFIG_PARAM_CERTCACHE_CACHEFILE);     
	m_cachefilename = conf_file.getString();

	APL_Config conf_NbrLine(CConfig::EIDMW_CONFIG_PARAM_CERTCACHE_LINENUMB);     
	m_ulMaxNbrLine = conf_NbrLine.getLong();

	APL_Config conf_NormalDelay(CConfig::EIDMW_CONFIG_PARAM_CERTCACHE_VALIDITY);     
	m_ulNormalDelay = conf_NormalDelay.getLong();
	
	APL_Config conf_WaitDelay(CConfig::EIDMW_CONFIG_PARAM_CERTCACHE_WAITDELAY);     
	m_ulWaitDelay = conf_WaitDelay.getLong();

	m_f=NULL;
}

APL_CertStatusCache::~APL_CertStatusCache(void)
{
	resetLines();

	if(m_f)				//Should not happen
		closeFile();

	MWLOG(LEV_INFO, MOD_APL, L"Delete CertStatusCache object");
}

void APL_CertStatusCache::Init(unsigned long ulMaxNbrLine,unsigned long ulNormalDelay,unsigned long ulWaitDelay,std::string cachefilename)
{
	m_ulMaxNbrLine=ulMaxNbrLine;

	if(ulNormalDelay>0)
		m_ulNormalDelay=ulNormalDelay;

	if(ulWaitDelay>0)
		m_ulWaitDelay=ulWaitDelay;

	if(cachefilename!="")
		m_cachefilename=cachefilename;
}


//Get the certificate status
CSC_Status APL_CertStatusCache::getCertStatus(unsigned long ulUniqueID,const CSC_Validation validationType,APL_Certifs *certStore)
{
	if(certStore==NULL)
		throw CMWEXCEPTION(EIDMW_ERR_CHECK);

	CSC_Status status;

	APL_CscLine line(ulUniqueID,validationType,certStore->getAllowTestCard(),certStore->getAllowBadDate());
	unsigned long ulFlags=line.getFlags();

	//Check if the certificate is in the cache and the status still valid
	do
	{
		//If another the status is being validate
		//	=> Wait and re-enter the function
		status=getStatusFromCache(ulUniqueID,ulFlags);

		if(status==CSC_STATUS_WAIT)
			CThread::SleepMillisecs(100);

	} while(status==CSC_STATUS_WAIT);

	//IF NOT YET IN THE CACHE
	if(status==CSC_STATUS_NONE)
	{

		//Run the validation process
		status=checkCertValidation(ulUniqueID,ulFlags,certStore);

		//Add the status to the cache.
		addStatusToCache(ulUniqueID,ulFlags,status);
	}
	return status;
}

//PRIVATE: Check if the certificate is in the cache and still valid
CSC_Status APL_CertStatusCache::getStatusFromCache(unsigned long ulUniqueID,unsigned long ulFlags)
{
	CAutoMutex autoMutex(&m_Mutex);
	APL_CscLine *pLine=NULL;
	int find=-1;

	//Load the file
	loadFile();

	//Find if the certificate is in the cache
	for(unsigned int i=0;i<m_lines.size();i++)
	{
		if(m_lines[i]->isEqual(ulUniqueID,ulFlags))
		{
			pLine=m_lines[i];
			find=i;
			break;
		}
	}

	//If found => check the validity
	//if not valid => erase
	// or if connection problem (in this case, we must try again) => erase
	// or if it was an issuer problem, we try again in the case of the issuer has been added => erase
	// or if it was another error, we try again => erase
	if(pLine)
	{
		if(!pLine->checkValidity() 
			|| pLine->getStatus()==CSC_STATUS_CONNECT
			|| pLine->getStatus()==CSC_STATUS_ISSUER 
			|| pLine->getStatus()==CSC_STATUS_ERROR)
		{
			delete pLine;
			m_lines.erase(m_lines.begin()+find);
			pLine=NULL;
		}
	}

	//If found return the Status
	if(pLine)
	{
		CSC_Status status=pLine->getStatus();
		closeFile();
		return status;
	}

	//IF NOT YET IN THE CACHE

	//Create the line in the file with status=CSC_STATUS_WAIT
	//   and write the file to avoid other process to do the validation
	pLine=new APL_CscLine(ulUniqueID,ulFlags);
	pLine->setStatus(CSC_STATUS_WAIT);
	pLine->setValidity(m_ulWaitDelay);
	m_lines.push_back(pLine);

	writeFile();

	return CSC_STATUS_NONE;
}

CSC_Status APL_CertStatusCache::convertStatus(APL_CertifStatus status)
{
	//Convert the APL CertifStatus into a status for the cache

	switch(status)
	{
	case APL_CERTIF_STATUS_VALID:
		return CSC_STATUS_VALID_SIGN;

	case APL_CERTIF_STATUS_VALID_CRL:
	case APL_CERTIF_STATUS_VALID_OCSP:
		return CSC_STATUS_VALID_FULL;

	case APL_CERTIF_STATUS_TEST:
		return CSC_STATUS_TEST;

	case APL_CERTIF_STATUS_DATE:
		return CSC_STATUS_DATE;

	case APL_CERTIF_STATUS_CONNECT:
		return CSC_STATUS_CONNECT;

	case APL_CERTIF_STATUS_ISSUER:
		return CSC_STATUS_ISSUER;

	case APL_CERTIF_STATUS_REVOKED:
		return CSC_STATUS_REVOKED;

	case APL_CERTIF_STATUS_UNKNOWN:
	default:
		return CSC_STATUS_UNKNOWN;
	}
}

//PRIVATE : Do the CRL/OCSP validation
CSC_Status APL_CertStatusCache::checkCertValidation(unsigned long ulUniqueID,unsigned long ulFlags,APL_Certifs *certStore)
{
	CSC_Status issuerstatus;
	CSC_Status certstatus;

	APL_Certif *cert=certStore->getCertUniqueId(ulUniqueID);

	bool bRoot=cert->isRoot();
	bool bTest=cert->isTest();

	if(bTest && !APL_CscLine::allowTestRoot(ulFlags))
		return CSC_STATUS_TEST;

	//If this is not a root
	if(bRoot)
	{
		issuerstatus=CSC_STATUS_VALID_SIGN;
	}
	else
	{
		//We check the issuer
		APL_Certif *issuer=NULL;
		if(NULL == (issuer = cert->getIssuer()))
			return CSC_STATUS_ISSUER;

		issuerstatus=checkCertValidation(issuer->getUniqueId(),ulFlags,certStore);
		//If the issuer is not valid we return this status
		if(issuerstatus!=CSC_STATUS_VALID_SIGN && issuerstatus!=CSC_STATUS_VALID_FULL)	
			return issuerstatus;
	}

	bool bDateOk=m_cryptoFwk->VerifyDateValidity(cert->getData());

	//Check date validity
	if(!bDateOk && !APL_CscLine::allowBadDate(ulFlags))
		return CSC_STATUS_DATE;

	switch(APL_CscLine::getValidationType(ulFlags))
	{
	case CSC_VALIDATION_NONE:
		certstatus = CSC_STATUS_VALID_SIGN;
		break;

	case CSC_VALIDATION_CRL:
		certstatus = convertStatus(cert->validationCRL());
		break;

	case CSC_VALIDATION_OCSP:
		certstatus = convertStatus(cert->validationOCSP());
		break;

	default:
		throw CMWEXCEPTION(EIDMW_ERR_CHECK); //No other validation type are allowd
	}

	return certstatus;
}

//PRIVATE : Add the certificate and its status to cache
void APL_CertStatusCache::addStatusToCache(unsigned long ulUniqueID,unsigned long ulFlags,CSC_Status status)
{
	CAutoMutex autoMutex(&m_Mutex);
	APL_CscLine *pLine=NULL;

	//To be sure that the cache has not be modified by another process, we reload it
	loadFile();

	//find the line
	for(unsigned int i=0;i<m_lines.size();i++)
	{
		if(m_lines[i]->isEqual(ulUniqueID,ulFlags))
			pLine=m_lines[i];

	}

	//if not find, we add it
	if(!pLine)
	{
		pLine=new APL_CscLine(ulUniqueID,ulFlags);
		m_lines.push_back(pLine);
	}

	pLine->setStatus(status);
	pLine->setValidity(m_ulNormalDelay);

	//Write the file
	writeFile();
}

void APL_CertStatusCache::resetLines()
{
	while(m_lines.size()>0)
	{
		delete m_lines[m_lines.size()-1];
		m_lines.pop_back();
	}
}

bool APL_CertStatusCache::loadFile()
{
	//If the file is already open, it's an implementation problem
	if(m_f)
		throw CMWEXCEPTION(EIDMW_ERR_CHECK);

	resetLines();

	char line[CSC_MAX_LINE_LENGHT];
	APL_CscLine *pLine;
	int err=0;

#ifndef WIN32
	m_tFl.l_type   = F_RDLCK;  /* F_RDLCK, F_WRLCK, F_UNLCK    */
	m_tFl.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
	m_tFl.l_start  = 0;        /* Offset from l_whence         */
	m_tFl.l_len    = 0;        /* length, 0 = to EOF           */
	m_tFl.l_pid    = getpid(); /* our PID                      */
#endif

	do
	{
#ifdef WIN32
		err = fopen_s(&m_f, m_cachefilename.c_str(), "r");
#else
		m_f = fopen(m_cachefilename.c_str(), "r");
		if (m_f == NULL) err=errno;		
#endif
		if (err != 0 && err != EACCES && err != ENOENT ) return false;							// Added for unit testing

		if(err==EACCES) 
			CThread::SleepMillisecs(100);
//TODO use timeout and if reach, don't use the cache
	} while(err==EACCES);

	//If the file doesn't exist, we create it
	if(m_f==NULL)
	{
#ifdef WIN32
		err = fopen_s(&m_f, m_cachefilename.c_str(), "w");
		SetFileAttributesA(m_cachefilename.c_str(),FILE_ATTRIBUTE_HIDDEN);
#else
		m_f = fopen(m_cachefilename.c_str(), "w");
		if (m_f == NULL) err=errno;		
#endif
		if (err != 0) return false;							// Added for unit testing	
#ifndef WIN32
		m_tFl.l_type   = F_WRLCK; // has to be the same type as the mode used to open the file
#endif
	}

	if(m_f==NULL)
		return false;

#ifdef WIN32
	_lock_file(m_f);		//Lock the file to avoid other process to access it
#else
	// on Linux/Mac we set an advisory lock, i.e. it prevents
	// other processes from using the file only if they are collaborative 
	// and check for the lock, otherwise they can do whatever they like ..
	
	if( fcntl(fileno(m_f), F_SETLKW, &m_tFl) == -1){  /* set the lock, waiting if necessary */
	  printf("APL_CertStatusCache::loadFile: fcntl %s\n",strerror(errno));
	  exit(1);
	}
#endif

	while( fgets (line, CSC_MAX_LINE_LENGHT, m_f) != NULL ) 
	{
		pLine=new APL_CscLine(line);
		m_lines.push_back(pLine);
	}

	return true;
}

bool APL_CertStatusCache::writeFile()
{

#ifdef WIN32
	_unlock_file(m_f);
	fclose(m_f);	//As the file is set with hidden attribute, we need to remove it before open it in write mode
	remove(m_cachefilename.c_str());
	int res = fopen_s(&m_f, m_cachefilename.c_str(), "w");
	_lock_file(m_f);
	if (res != 0) {m_f=NULL; return false;}							// Added for unit testing
	SetFileAttributesA(m_cachefilename.c_str(),FILE_ATTRIBUTE_HIDDEN);
#else
	m_f = freopen(m_cachefilename.c_str(), "w",m_f);
	if (m_f == NULL) return false;		
#endif

	//If there is to much line, we have to delete the oldest
	while(m_lines.size()>m_ulMaxNbrLine)
	{
		APL_CscLine *lineOldest=m_lines[0];
		int find=0;
		for(unsigned int i=1;i<m_lines.size();i++)
		{
			if(APL_CscLine::compareValidity(*lineOldest,*m_lines[i])>0)
			{
				lineOldest=m_lines[i];
				find=i;
			}
		}
		delete lineOldest;
		m_lines.erase(m_lines.begin()+find);
	}

	for(unsigned int i=0;i<m_lines.size();i++)
	{
		//if the line is not valid anymore it is not written in the file
		if(m_lines[i]->checkValidity())
			m_lines[i]->writeLine(m_f);
	}

	return closeFile();
}

bool APL_CertStatusCache::closeFile()
{
	//If the file is not open, it's an implementation problem
	if(m_f)
	{
#ifdef WIN32
		_unlock_file(m_f);
#else	
		m_tFl.l_type   = F_UNLCK;  /* tell it to unlock the region */
		if( fcntl(fileno(m_f), F_SETLKW, &m_tFl) == -1){  /* set the lock, waiting if necessary */
		  printf("APL_CertStatusCache::closeFile: fcntl %s\n",strerror(errno));
		  exit(1);
		}
#endif
		fclose(m_f);
	}

	m_f=NULL;

	return true;
}

}