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