cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
int_time.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Internal Time/Timer API *
4 * Copyright Peter Gutmann 1992-2007 *
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 time_t getTime( void )
34  {
35  const time_t theTime = time( NULL );
36 
37  if( ( theTime == ( time_t ) -1 ) || ( theTime <= MIN_TIME_VALUE ) )
38  {
39  DEBUG_DIAG(( "No time source available" ));
40  assert( DEBUG_WARN );
41  return( 0 );
42  }
43  return( theTime );
44  }
45 
46 time_t getApproxTime( void )
47  {
48  const time_t theTime = time( NULL );
49 
50  if( ( theTime == ( time_t ) -1 ) || ( theTime <= MIN_TIME_VALUE ) )
51  {
52  DEBUG_DIAG(( "No time source available" ));
53  assert( DEBUG_WARN );
54  return( CURRENT_TIME_VALUE );
55  }
56  return( theTime );
57  }
58 
60  {
61  CRYPT_DEVICE cryptDevice;
63  time_t theTime;
64  int status;
65 
66  REQUIRES_EXT( ( cryptHandle == SYSTEM_OBJECT_HANDLE || \
67  isHandleRangeValid( cryptHandle ) ), 0 );
68 
69  /* Get the dependent device for the object that needs the time */
70  status = krnlSendMessage( cryptHandle, IMESSAGE_GETDEPENDENT,
71  &cryptDevice, OBJECT_TYPE_DEVICE );
72  if( cryptStatusError( status ) )
73  cryptDevice = SYSTEM_OBJECT_HANDLE;
74 
75  /* Try and get the time from the device */
76  setMessageData( &msgData, &theTime, sizeof( time_t ) );
77  status = krnlSendMessage( cryptDevice, IMESSAGE_GETATTRIBUTE_S,
78  &msgData, CRYPT_IATTRIBUTE_TIME );
79  if( cryptStatusError( status ) && cryptDevice != SYSTEM_OBJECT_HANDLE )
80  {
81  /* We couldn't get the time from a crypto token, fall back to the
82  system device */
84  IMESSAGE_GETATTRIBUTE_S, &msgData,
85  CRYPT_IATTRIBUTE_TIME );
86  }
87  if( cryptStatusError( status ) || \
88  ( theTime == ( time_t ) -1 ) || ( theTime <= MIN_TIME_VALUE ) )
89  {
90  DEBUG_DIAG(( "Error: No time source available" ));
91  assert( DEBUG_WARN );
92  return( 0 );
93  }
94  return( theTime );
95  }
96 
97 /****************************************************************************
98 * *
99 * Timer Functions *
100 * *
101 ****************************************************************************/
102 
103 /* Monotonic timer interface that protects against the system clock being
104  changed during a timed operation like network I/O. Even without
105  deliberate fiddling with the system clock, a timeout during a DST switch
106  can cause something like a 5s wait to turn into a 1hr 5s wait, so we have
107  to abstract the standard time API into a monotonic time API. Since these
108  functions are purely peripheral to other operations (for example handling
109  timeouts for network I/O) they never fail but simply return good-enough
110  results if there's a problem (although they assert in debug mode). This
111  is because we don't want to abort a network session just because we've
112  detected some trivial clock irregularity.
113 
114  The way this works is that we record the following information for each
115  timing interval:
116 
117  (endTime - \
118  timeRemaining) endTime
119  ................+-----------------------+...............
120  ^ | | ^
121  currentTime |<--- timeRemaining --->| currentTime'
122  ....<------- origTimeout -------->|
123 
124  When currentTime falls outside the timeRemaining interval we know that a
125  clock change has occurred and can try and correct it. Moving forwards
126  by an unexpected amount is a bit more tricky than moving back because
127  it's hard to define "unexpected", so we use an estimation method that
128  detects the typical reasons for a clock leap (DST adjust) without
129  yielding false positives */
130 
132 static BOOLEAN sanityCheck( const MONOTIMER_INFO *timerInfo )
133  {
134  /* Make sure that the basic timer values are within bounds. We can't
135  check endTime for a maximum range value since it's a time_t */
136  if( timerInfo->origTimeout < 0 || \
137  timerInfo->origTimeout > MAX_INTLENGTH || \
138  timerInfo->timeRemaining < 0 || \
139  timerInfo->timeRemaining > MAX_INTLENGTH || \
140  timerInfo->endTime < 0 )
141  return( FALSE );
142 
143  /* Make sure that time ranges are withing bounds. This can generally
144  only happen when a time_t over/underflow has occurred */
145  if( timerInfo->endTime < timerInfo->timeRemaining || \
146  timerInfo->origTimeout < timerInfo->timeRemaining )
147  return( FALSE );
148 
149  return( TRUE );
150  }
151 
152 STDC_NONNULL_ARG( ( 1 ) ) \
153 static void handleTimeOutOfBounds( INOUT MONOTIMER_INFO *timerInfo )
154  {
155  assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
156 
157  DEBUG_DIAG(( "time_t underflow/overflow has occurred" ));
158  assert( DEBUG_WARN );
159 
160  /* We've run into an overflow condition in the calculations that we've
161  performed on a time_t, this is a bit tricky to handle because we
162  can't just give up on (say) performing network I/O just because we
163  can't reliably set a timeout. The best that we can do is warn in
164  debug mode and set a zero timeout so that at least one lot of I/O
165  will still take place */
166  timerInfo->origTimeout = timerInfo->timeRemaining = 0;
167  }
168 
170 static BOOLEAN correctMonoTimer( INOUT MONOTIMER_INFO *timerInfo,
171  const time_t currentTime )
172  {
173  BOOLEAN needsCorrection = FALSE;
174 
175  assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
176 
177  /* If a time_t over/underflow has occurred, make a best-effort attempt
178  to recover */
179  if( !sanityCheck( timerInfo ) )
180  {
181  handleTimeOutOfBounds( timerInfo );
182  return( FALSE );
183  }
184 
185  /* If the clock has been rolled back to before the start time, we need
186  to correct this. The range check for endTime vs. timeRemaining has
187  already been done as part of the sanity check */
188  if( currentTime < timerInfo->endTime - timerInfo->timeRemaining )
189  needsCorrection = TRUE;
190  else
191  {
192  /* If we're past the timer end time, check to see whether it's
193  jumped by a suspicious amount. If we're more than 30 minutes
194  past the timeout (which will catch things like DST changes) and
195  the initial timeout was less than the change (to avoid a false
196  positive if we've been waiting > 30 minutes for a legitimate
197  timeout), we need to correct this. This can still fail if (for
198  example) we have a relatively short timeout and we're being run
199  in a VM that gets suspended for more than 30 minutes and then
200  restarted, but by then any peer communicating with us should have
201  long since given up waiting for a response and timed out the
202  connection. In any case someone fiddling with suspending
203  processes in this manner, which will cause problems for anything
204  doing network I/O, should be prepared to handle any problems that
205  arise, for example by ensuring that current network I/O has
206  completed before suspending the process */
207  if( currentTime > timerInfo->endTime )
208  {
209  const time_t delta = currentTime - timerInfo->endTime;
210 
211  if( ( delta < 0 || delta > ( 30 * 60 ) ) && \
212  timerInfo->origTimeout < delta )
213  needsCorrection = TRUE;
214  }
215  }
216  if( !needsCorrection )
217  return( TRUE );
218 
219  /* The time information has been changed, correct the recorded time
220  information for the new time */
221  if( currentTime >= ( MAX_INTLENGTH - timerInfo->timeRemaining ) )
222  {
223  DEBUG_DIAG(( "Invalid monoTimer time correction period" ));
224  assert( DEBUG_WARN );
225  handleTimeOutOfBounds( timerInfo );
226  return( FALSE );
227  }
228  timerInfo->endTime = currentTime + timerInfo->timeRemaining;
229  if( timerInfo->endTime < currentTime || \
230  timerInfo->endTime < currentTime + max( timerInfo->timeRemaining,
231  timerInfo->origTimeout ) )
232  {
233  /* There's a problem with the time calculations, handle the overflow
234  condition and tell the caller not to try anything further */
235  handleTimeOutOfBounds( timerInfo );
236  return( FALSE );
237  }
238 
239  return( TRUE );
240  }
241 
243 int setMonoTimer( INOUT MONOTIMER_INFO *timerInfo,
244  IN_INT const int duration )
245  {
246  const time_t currentTime = getApproxTime();
247  BOOLEAN initOK;
248 
249  assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
250 
251  REQUIRES( duration >= 0 && duration < MAX_INTLENGTH );
252 
253  memset( timerInfo, 0, sizeof( MONOTIMER_INFO ) );
254  if( currentTime >= ( MAX_INTLENGTH - duration ) )
255  {
256  DEBUG_DIAG(( "Invalid monoTimer time period" ));
257  assert( DEBUG_WARN );
258  handleTimeOutOfBounds( timerInfo );
259  return( CRYPT_OK );
260  }
261  timerInfo->endTime = currentTime + duration;
262  timerInfo->timeRemaining = timerInfo->origTimeout = duration;
263  initOK = correctMonoTimer( timerInfo, currentTime );
264  ENSURES( initOK );
265 
266  return( CRYPT_OK );
267  }
268 
269 STDC_NONNULL_ARG( ( 1 ) ) \
270 void extendMonoTimer( INOUT MONOTIMER_INFO *timerInfo,
271  IN_INT const int duration )
272  {
273  const time_t currentTime = getApproxTime();
274 
275  assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
276 
277  REQUIRES_V( duration >= 0 && duration < MAX_INTLENGTH );
278 
279  /* Correct the timer for clock skew if required */
280  if( !correctMonoTimer( timerInfo, currentTime ) )
281  return;
282 
283  /* Extend the monotonic timer's timeout interval to allow for further
284  data to be processed */
285  if( timerInfo->origTimeout >= ( MAX_INTLENGTH - duration ) || \
286  timerInfo->endTime >= ( MAX_INTLENGTH - duration ) || \
287  timerInfo->endTime < currentTime )
288  {
289  DEBUG_DIAG(( "Invalid monoTimer time period extension" ));
290  assert( DEBUG_WARN );
291  handleTimeOutOfBounds( timerInfo );
292  return;
293  }
294  timerInfo->origTimeout += duration;
295  timerInfo->endTime += duration;
296  timerInfo->timeRemaining = timerInfo->endTime - currentTime;
297 
298  /* Re-correct the timer in case overflow occurred */
299  ( void ) correctMonoTimer( timerInfo, currentTime );
300  }
301 
303 BOOLEAN checkMonoTimerExpiryImminent( INOUT MONOTIMER_INFO *timerInfo,
304  IN_INT const int timeLeft )
305  {
306  const time_t currentTime = getApproxTime();
307  time_t timeRemaining;
308 
309  assert( isWritePtr( timerInfo, sizeof( MONOTIMER_INFO ) ) );
310 
311  REQUIRES_B( timeLeft >= 0 && timeLeft < MAX_INTLENGTH );
312 
313  /* If the timeout has expired, don't try doing anything else */
314  if( timerInfo->timeRemaining <= 0 )
315  return( TRUE );
316 
317  /* Correct the monotonic timer for clock skew if required */
318  if( !correctMonoTimer( timerInfo, currentTime ) )
319  return( TRUE );
320 
321  /* Check whether the time will expire within timeLeft seconds */
322  if( timerInfo->endTime < currentTime )
323  {
324  DEBUG_DIAG(( "Invalid monoTimer expiry time period" ));
325  assert( DEBUG_WARN );
326  handleTimeOutOfBounds( timerInfo );
327  return( TRUE );
328  }
329  timeRemaining = timerInfo->endTime - currentTime;
330  if( timeRemaining > timerInfo->timeRemaining )
331  {
332  handleTimeOutOfBounds( timerInfo );
333  timeRemaining = 0;
334  }
335  timerInfo->timeRemaining = timeRemaining;
336  return( ( timerInfo->timeRemaining < timeLeft ) ? TRUE : FALSE );
337  }
338 
340 BOOLEAN checkMonoTimerExpired( INOUT MONOTIMER_INFO *timerInfo )
341  {
342  return( checkMonoTimerExpiryImminent( timerInfo, 0 ) );
343  }