1 /****************************************************************************
2 *																			*
3 *						cryptlib Internal Time/Timer API					*
4 *						Copyright Peter Gutmann 1992-2014					*
5 *																			*
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9   #include "crypt.h"
10 #else
11   #include "crypt.h"
12 #endif /* Compiler-specific includes */
13 
14 /****************************************************************************
15 *																			*
16 *								Time Functions 								*
17 *																			*
18 ****************************************************************************/
19 
20 /* Get the system time safely.  The first function implements hard failures,
21    converting invalid time values to zero, which yield a warning date of
22    1/1/1970 rather than an out-of-bounds or garbage value.  The second
23    function implements soft failures, returning an estimate of the
24    approximate current date.  The third function is used for operations such
25    as signing certs and timestamping and tries to get the time from a
26    hardware time source if one is available.
27 
28    Because of the implementation-dependent behaviour of the time_t type we
29    perform an explicit check against '( time_t ) -1' as well as a general
30    range check to avoid being caught by conversion problems if time_t is a
31    type too far removed from int */
32 
33 #ifndef NDEBUG
34 static time_t testTimeValue = 0;
35 #endif /* NDEBUG */
36 
getTime(void)37 time_t getTime( void )
38 	{
39 	const time_t theTime = time( NULL );
40 
41 	if( ( theTime == ( time_t ) -1 ) || ( theTime <= MIN_TIME_VALUE ) )
42 		{
43 		DEBUG_DIAG(( "No time source available" ));
44 		assert( DEBUG_WARN );
45 		return( 0 );
46 		}
47 	return( theTime );
48 	}
49 
getApproxTime(void)50 time_t getApproxTime( void )
51 	{
52 	const time_t theTime = time( NULL );
53 
54 	/* If we're running a self-test with externally-controlled time, return
55 	   the pre-set time value */
56 #ifndef NDEBUG
57 	if( testTimeValue != 0 )
58 		return( testTimeValue );
59 #endif /* NDEBUG */
60 
61 	if( ( theTime == ( time_t ) -1 ) || ( theTime <= MIN_TIME_VALUE ) )
62 		{
63 		DEBUG_DIAG(( "No time source available" ));
64 		assert( DEBUG_WARN );
65 		return( CURRENT_TIME_VALUE );
66 		}
67 	return( theTime );
68 	}
69 
getReliableTime(IN_HANDLE const CRYPT_HANDLE cryptHandle)70 time_t getReliableTime( IN_HANDLE const CRYPT_HANDLE cryptHandle )
71 	{
72 	CRYPT_DEVICE cryptDevice;
73 	MESSAGE_DATA msgData;
74 	time_t theTime;
75 	int status;
76 
77 	REQUIRES_EXT( ( cryptHandle == SYSTEM_OBJECT_HANDLE || \
78 					isHandleRangeValid( cryptHandle ) ), 0 );
79 
80 	/* Get the dependent device for the object that needs the time.  This
81 	   is typically a private key being used for signing something that
82 	   needs a timestamp, so what we're doing here is finding a time source
83 	   associated with that key, for example the HSM that the key is stored
84 	   in */
85 	status = krnlSendMessage( cryptHandle, IMESSAGE_GETDEPENDENT,
86 							  &cryptDevice, OBJECT_TYPE_DEVICE );
87 	if( cryptStatusError( status ) )
88 		cryptDevice = SYSTEM_OBJECT_HANDLE;
89 
90 	/* Try and get the time from the device */
91 	setMessageData( &msgData, &theTime, sizeof( time_t ) );
92 	status = krnlSendMessage( cryptDevice, IMESSAGE_GETATTRIBUTE_S,
93 							  &msgData, CRYPT_IATTRIBUTE_TIME );
94 	if( cryptStatusError( status ) && cryptDevice != SYSTEM_OBJECT_HANDLE )
95 		{
96 		/* We couldn't get the time from a crypto token, fall back to the
97 		   system device */
98 		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
99 								  IMESSAGE_GETATTRIBUTE_S, &msgData,
100 								  CRYPT_IATTRIBUTE_TIME );
101 		}
102 	if( cryptStatusError( status ) || \
103 		( theTime == ( time_t ) -1 ) || ( theTime <= MIN_TIME_VALUE ) )
104 		{
105 		DEBUG_DIAG(( "Error: No time source available" ));
106 		assert( DEBUG_WARN );
107 		return( 0 );
108 		}
109 	return( theTime );
110 	}
111 
112 /****************************************************************************
113 *																			*
114 *								Timer Functions 							*
115 *																			*
116 ****************************************************************************/
117 
118 /* Monotonic timer interface that protects against the system clock being
119    changed during a timed operation like network I/O, so we have to abstract
120    the standard time API into a monotonic time API.  Since these functions
121    are purely peripheral to other operations (for example handling timeouts
122    for network I/O) they never fail but simply return good-enough results if
123    there's a problem (although they assert in debug mode).  This is because
124    we don't want to abort a network session just because we've detected some
125    trivial clock irregularity.
126 
127    The way this works is that we record the following information for each
128    timing interval:
129 
130 			(endTime - \
131 				timeRemaining)			 endTime
132 	................+-----------------------+...............
133 		^			|						|		^
134 	currentTime		|<--- timeRemaining --->|	currentTime'
135 		  ....<------- origTimeout -------->|
136 
137    When currentTime falls outside the timeRemaining interval we know that a
138    clock change has occurred and can try and correct it.  Moving forwards
139    by an unexpected amount is a bit more tricky than moving back because
140    it's hard to define "unexpected", so we use an estimation method that
141    detects the typical reasons for a clock leap (an attempt to handle a DST
142    adjust by changing the clock) without yielding false positives */
143 
144 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
sanityCheck(const MONOTIMER_INFO * timerInfo)145 static BOOLEAN sanityCheck( const MONOTIMER_INFO *timerInfo )
146 	{
147 	/* Make sure that the basic timer values are within bounds.  We can't
148 	   check endTime for a maximum range value since it's a time_t */
149 	if( timerInfo->origTimeout < 0 || \
150 		timerInfo->origTimeout > MAX_INTLENGTH || \
151 		timerInfo->timeRemaining < 0 || \
152 		timerInfo->timeRemaining > MAX_INTLENGTH || \
153 		timerInfo->endTime < 0 )
154 		return( FALSE );
155 
156 	/* Make sure that time ranges are withing bounds.  This can generally
157 	   only happen when a time_t over/underflow has occurred */
158 	if( timerInfo->endTime < timerInfo->timeRemaining || \
159 		timerInfo->origTimeout < timerInfo->timeRemaining )
160 		return( FALSE );
161 
162 	return( TRUE );
163 	}
164 
165 STDC_NONNULL_ARG( ( 1 ) ) \
handleTimeOutOfBounds(INOUT MONOTIMER_INFO * timerInfo)166 static void handleTimeOutOfBounds( INOUT MONOTIMER_INFO *timerInfo )
167 	{
168 	assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
169 
170 	DEBUG_DIAG(( "time_t underflow/overflow has occurred" ));
171 	assert( DEBUG_WARN );
172 
173 	/* We've run into an overflow condition in the calculations that we've
174 	   performed on a time_t, this is a bit tricky to handle because we
175 	   can't just give up on (say) performing network I/O just because we
176 	   can't reliably set a timeout.  The best that we can do is warn in
177 	   debug mode and set a zero timeout so that at least one lot of I/O
178 	   will still take place */
179 	timerInfo->origTimeout = timerInfo->timeRemaining = 0;
180 	}
181 
182 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
correctMonoTimer(INOUT MONOTIMER_INFO * timerInfo,const time_t currentTime)183 static BOOLEAN correctMonoTimer( INOUT MONOTIMER_INFO *timerInfo,
184 								 const time_t currentTime )
185 	{
186 	BOOLEAN needsCorrection = FALSE;
187 
188 	assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
189 
190 	/* If a time_t over/underflow has occurred, make a best-effort attempt
191 	   to recover */
192 	if( !sanityCheck( timerInfo ) )
193 		{
194 		handleTimeOutOfBounds( timerInfo );
195 		return( FALSE );
196 		}
197 
198 	/* If the clock has been rolled back to before the start time, we need
199 	   to correct this.  The range check for endTime vs. timeRemaining has
200 	   already been done as part of the sanity check */
201 	if( currentTime < timerInfo->endTime - timerInfo->timeRemaining )
202 		needsCorrection = TRUE;
203 	else
204 		{
205 		/* If we're past the timer end time, check to see whether it's
206 		   jumped by a suspicious amount.  If we're more than 30 minutes
207 		   past the timeout (which will catch things like attempted DST
208 		   corrections) and the initial timeout was less than the change (to
209 		   avoid a false positive if we've been waiting > 30 minutes for a
210 		   legitimate timeout), we need to correct this.  This can still
211 		   fail if (for example) we have a relatively short timeout and
212 		   we're being run in a VM that gets suspended for more than 30
213 		   minutes and then restarted, but by then any peer communicating
214 		   with us should have long since given up waiting for a response
215 		   and timed out the connection.  In any case someone fiddling with
216 		   suspending processes in this manner, which will cause problems
217 		   for anything doing network I/O, should be prepared to handle any
218 		   problems that arise, for example by ensuring that current network
219 		   I/O has completed before suspending the process */
220 		if( currentTime > timerInfo->endTime )
221 			{
222 			const time_t delta = currentTime - timerInfo->endTime;
223 
224 			if( ( delta < 0 || delta > ( 30 * 60 ) ) && \
225 				timerInfo->origTimeout < delta )
226 				needsCorrection = TRUE;
227 			}
228 		}
229 	if( !needsCorrection )
230 		return( TRUE );
231 
232 	/* The time information has been changed, correct the recorded time
233 	   information for the new time.
234 
235 	   The checking for overflow in time_t is impossible to perform when the
236 	   compiler uses gcc's braindamaged interpretation of the C standard.
237 	   A compiler like MSVC knows that a time_t is an int or long and
238 	   produces the expected code from the following, a compiler like gcc
239 	   also knows that a time_t is an int or a long but assumes that it
240 	   could also be a GUID or a variant record or anonymous union or packed
241 	   bitfield and therefore removes the checks in the code, because trying
242 	   to perform the check on a time_t is undefined behaviour (UB).  There
243 	   is no way to work around this issue apart from switching to a less
244 	   braindamaged compiler, so we leave the code there for sane compilers
245 	   under the acknowledgement that there's no way to address this with
246 	   gcc */
247 	if( currentTime >= ( MAX_INTLENGTH - timerInfo->timeRemaining ) )
248 		{
249 		DEBUG_DIAG(( "Invalid monoTimer time correction period" ));
250 		assert( DEBUG_WARN );
251 		handleTimeOutOfBounds( timerInfo );
252 		return( FALSE );
253 		}
254 	timerInfo->endTime = currentTime + timerInfo->timeRemaining;
255 	if( timerInfo->endTime < currentTime || \
256 		timerInfo->endTime < currentTime + max( timerInfo->timeRemaining,
257 												timerInfo->origTimeout ) )
258 		{
259 		/* There's a problem with the time calculations, handle the overflow
260 		   condition and tell the caller not to try anything further */
261 		handleTimeOutOfBounds( timerInfo );
262 		return( FALSE );
263 		}
264 
265 	return( TRUE );
266 	}
267 
268 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
setMonoTimer(OUT MONOTIMER_INFO * timerInfo,IN_INT_Z const int duration)269 int setMonoTimer( OUT MONOTIMER_INFO *timerInfo,
270 				  IN_INT_Z const int duration )
271 	{
272 	const time_t currentTime = getApproxTime();
273 	BOOLEAN initOK;
274 
275 	assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
276 
277 	REQUIRES( duration >= 0 && duration < MAX_INTLENGTH );
278 
279 	memset( timerInfo, 0, sizeof( MONOTIMER_INFO ) );
280 	if( currentTime >= ( MAX_INTLENGTH - duration ) )
281 		{
282 		DEBUG_DIAG(( "Invalid monoTimer time period" ));
283 		assert( DEBUG_WARN );
284 		handleTimeOutOfBounds( timerInfo );
285 		return( CRYPT_OK );
286 		}
287 	timerInfo->endTime = currentTime + duration;
288 	timerInfo->timeRemaining = timerInfo->origTimeout = duration;
289 	initOK = correctMonoTimer( timerInfo, currentTime );
290 	ENSURES( initOK );
291 
292 	return( CRYPT_OK );
293 	}
294 
295 STDC_NONNULL_ARG( ( 1 ) ) \
extendMonoTimer(INOUT MONOTIMER_INFO * timerInfo,IN_INT const int duration)296 void extendMonoTimer( INOUT MONOTIMER_INFO *timerInfo,
297 					  IN_INT const int duration )
298 	{
299 	const time_t currentTime = getApproxTime();
300 
301 	assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
302 
303 	REQUIRES_V( duration >= 0 && duration < MAX_INTLENGTH );
304 
305 	/* Correct the timer for clock skew if required */
306 	if( !correctMonoTimer( timerInfo, currentTime ) )
307 		return;
308 
309 	/* Extend the monotonic timer's timeout interval to allow for further
310 	   data to be processed */
311 	if( timerInfo->origTimeout >= ( MAX_INTLENGTH - duration ) || \
312 		timerInfo->endTime >= ( MAX_INTLENGTH - duration ) || \
313 		timerInfo->endTime < currentTime )
314 		{
315 		DEBUG_DIAG(( "Invalid monoTimer time period extension" ));
316 		assert( DEBUG_WARN );
317 		handleTimeOutOfBounds( timerInfo );
318 		return;
319 		}
320 	timerInfo->origTimeout += duration;
321 	timerInfo->endTime += duration;
322 	timerInfo->timeRemaining = timerInfo->endTime - currentTime;
323 
324 	/* Re-correct the timer in case overflow occurred */
325 	( void ) correctMonoTimer( timerInfo, currentTime );
326 	}
327 
328 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
checkMonoTimerExpiryImminent(INOUT MONOTIMER_INFO * timerInfo,IN_INT_Z const int timeLeft)329 BOOLEAN checkMonoTimerExpiryImminent( INOUT MONOTIMER_INFO *timerInfo,
330 									  IN_INT_Z const int timeLeft )
331 	{
332 	const time_t currentTime = getApproxTime();
333 	time_t timeRemaining;
334 
335 	assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
336 
337 	REQUIRES_B( timeLeft >= 0 && timeLeft < MAX_INTLENGTH );
338 
339 	/* If the timeout has expired, don't try doing anything else */
340 	if( timerInfo->timeRemaining <= 0 )
341 		return( TRUE );
342 
343 	/* Correct the monotonic timer for clock skew if required */
344 	if( !correctMonoTimer( timerInfo, currentTime ) )
345 		return( TRUE );
346 
347 	/* Check whether the time will expire within timeLeft seconds */
348 	if( timerInfo->endTime < currentTime )
349 		{
350 		DEBUG_DIAG(( "Invalid monoTimer expiry time period" ));
351 		assert( DEBUG_WARN );
352 		handleTimeOutOfBounds( timerInfo );
353 		return( TRUE );
354 		}
355 	timeRemaining = timerInfo->endTime - currentTime;
356 	if( timeRemaining > timerInfo->timeRemaining )
357 		{
358 		handleTimeOutOfBounds( timerInfo );
359 		timeRemaining = 0;
360 		}
361 	timerInfo->timeRemaining = timeRemaining;
362 	return( ( timerInfo->timeRemaining <= timeLeft ) ? TRUE : FALSE );
363 	}
364 
365 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
checkMonoTimerExpired(INOUT MONOTIMER_INFO * timerInfo)366 BOOLEAN checkMonoTimerExpired( INOUT MONOTIMER_INFO *timerInfo )
367 	{
368 	return( checkMonoTimerExpiryImminent( timerInfo, 0 ) );
369 	}
370 
371 /****************************************************************************
372 *																			*
373 *								Self-test Functions							*
374 *																			*
375 ****************************************************************************/
376 
377 /* Test code for the above functions */
378 
379 #ifndef NDEBUG
380 
setTestTime(const time_t value)381 static void setTestTime( const time_t value )
382 	{
383 	testTimeValue = MIN_TIME_VALUE + value;
384 	}
385 
clearTestTime(void)386 static void clearTestTime( void )
387 	{
388 	testTimeValue = 0;
389 	}
390 
391 CHECK_RETVAL_BOOL \
testIntTime(void)392 BOOLEAN testIntTime( void )
393 	{
394 	MONOTIMER_INFO timerInfo;
395 	int status;
396 
397 	/* Test basic timer functions */
398 	setTestTime( 1000 );
399 	status = setMonoTimer( &timerInfo, 0 );
400 	if( cryptStatusError( status ) )
401 		return( FALSE );
402 	if( !checkMonoTimerExpiryImminent( &timerInfo, 1 ) )
403 		return( FALSE );
404 	status = setMonoTimer( &timerInfo, 10 );
405 	if( cryptStatusError( status ) )
406 		return( FALSE );
407 	if( checkMonoTimerExpiryImminent( &timerInfo, 0 ) || \
408 		checkMonoTimerExpiryImminent( &timerInfo, 9 ) )
409 		return( FALSE );
410 	if( !checkMonoTimerExpiryImminent( &timerInfo, 10 ) )
411 		return( FALSE );
412 
413 	/* Check timer period extension functionality */
414 	setTestTime( 1000 );
415 	status = setMonoTimer( &timerInfo, 0 );
416 	if( cryptStatusError( status ) )
417 		return( FALSE );
418 	extendMonoTimer( &timerInfo, 10 );
419 	if( checkMonoTimerExpiryImminent( &timerInfo, 0 ) || \
420 		checkMonoTimerExpiryImminent( &timerInfo, 9 ) || \
421 		!checkMonoTimerExpiryImminent( &timerInfo, 10 ) )
422 		return( FALSE );
423 
424 	/* Check clock going forwards normally */
425 	setTestTime( 1000 );
426 	status = setMonoTimer( &timerInfo, 10 );
427 	if( cryptStatusError( status ) )
428 		return( FALSE );
429 	setTestTime( 1009 );
430 	if( checkMonoTimerExpiryImminent( &timerInfo, 0 ) || \
431 		!checkMonoTimerExpiryImminent( &timerInfo, 1 ) )
432 		return( FALSE );
433 
434 	/* Check clock going backwards.  This recovers by correcting to allow
435 	   the original timeout */
436 	setTestTime( 1000 );
437 	status = setMonoTimer( &timerInfo, 10 );
438 	if( cryptStatusError( status ) )
439 		return( FALSE );
440 	setTestTime( 999 );
441 	if( checkMonoTimerExpiryImminent( &timerInfo, 0 ) || \
442 		checkMonoTimerExpiryImminent( &timerInfo, 9 ) || \
443 		!checkMonoTimerExpiryImminent( &timerInfo, 10 ) )
444 		return( FALSE );
445 
446 	/* Check clock going forwards too far.  This recovers from a time jump
447 	   of > 30 minutes by correcting to allow the original timeout period on
448 	   the assumption that the problem is with the time source rather than
449 	   that we've waited for over half an hour for a network packet to
450 	   arrive */
451 	setTestTime( 1000 );
452 	status = setMonoTimer( &timerInfo, 10 );
453 	if( cryptStatusError( status ) )
454 		return( FALSE );
455 	setTestTime( 1000 + ( 45 * 60 ) );
456 	if( checkMonoTimerExpiryImminent( &timerInfo, 0 ) || \
457 		checkMonoTimerExpiryImminent( &timerInfo, 9 ) || \
458 		!checkMonoTimerExpiryImminent( &timerInfo, 10 ) )
459 		return( FALSE );
460 
461 	clearTestTime();
462 	return( TRUE );
463 	}
464 #endif /* !NDEBUG */
465