-
Notifications
You must be signed in to change notification settings - Fork 2
/
cryptlib.c
executable file
·500 lines (437 loc) · 17.7 KB
/
cryptlib.c
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
/****************************************************************************
* *
* cryptlib Core Routines *
* Copyright Peter Gutmann 1992-2008 *
* *
****************************************************************************/
#include "crypt.h"
/* Prototypes for functions in init.c. These should actually be annotated
with CHECK_RETVAL_ACQUIRELOCK( MUTEX_LOCKNAME( initialisation ) and
RELEASELOCK( MUTEX_LOCKNAME( initialisation ) ) but the mutex locking
types aren't visible outside the kernel and in any case the annotation is
only required where the functions are defined, so we just annotate them
normally here */
CHECK_RETVAL \
int krnlBeginInit( void );
void krnlCompleteInit( void );
CHECK_RETVAL \
int krnlBeginShutdown( void );
RETVAL \
int krnlCompleteShutdown( void );
/* Temporary kludge for functions that have to be performed mid-startup or
mid-shutdown */
int destroyObjects( void );
CHECK_RETVAL \
int testKernel( void );
/* Some messages communicate standard data values that are used again and
again so we predefine values for these that can be used globally */
const int messageValueTrue = TRUE;
const int messageValueFalse = FALSE;
const int messageValueCryptOK = CRYPT_OK;
const int messageValueCryptError = CRYPT_ERROR;
const int messageValueCryptUnused = CRYPT_UNUSED;
const int messageValueCryptUseDefault = CRYPT_USE_DEFAULT;
const int messageValueCursorFirst = CRYPT_CURSOR_FIRST;
const int messageValueCursorNext = CRYPT_CURSOR_NEXT;
const int messageValueCursorPrevious = CRYPT_CURSOR_PREVIOUS;
const int messageValueCursorLast = CRYPT_CURSOR_LAST;
/* OS X Snow Leopard broke dlopen(), if it's called from a (sub-)thread then
it dies with a SIGTRAP. Specifically, if you dlopen() a shared library
linked with CoreFoundation from a thread and the calling app wasn't
linked with CoreFoundation then the function CFInitialize() inside
dlopen() checks if the thread is the main thread (specifically
CFInitialize is declared with __attribute__ ((constructor))) and if it
isn't being called from the main thread it crashes with a SIGTRAP. The
inability to call dlopen() from a thread was apparently a requirement in
pre-Snow Leopard versions as well but was never enforced. One possible
workaround for this would be to require that any application that uses
cryptlib also link in CoreFoundation, but this will be rather error-
prone, so we disable asynchronous driver binding instead */
#if defined( __APPLE__ )
#undef USE_THREADS
#endif /* __APPLE__ */
/* If we're fuzzing we also disable threaded init in order to make the
startup behaviour deterministic */
#if defined( CONFIG_FUZZ )
#undef USE_THREADS
#endif /* CONFIG_FUZZ */
/****************************************************************************
* *
* Startup/Shutdown Routines *
* *
****************************************************************************/
/* The initialisation and shutdown actions performed for various object
types. The pre-init actions are used to handle various preparatory
actions that are required before the actual init can be performed, for
example to create the system device and user object, which are needed by
the init routines. The pre-shutdown actions are used to signal to various
subsystems that a shutdown is about to occur, for example to allow the
networking subsystem to gracefully exit from any currently occurring
network I/O.
The certificate init is somewhat special in that it only performs an
internal consistency check rather than performing any actual
initialisation. As such it's not performed as part of the asynchronous
init since it has the potential to abort the cryptlib startup and as
such can't be allowed to come back at a later date an retroactively shut
things down after other crypto operations have already occurred. In fact
since it's part of the startup self-test it's done in the pre-init, as a
failure to complete the self-test will result in an immediate abort of the
init process.
The order of the init/shutdown actions is:
Object type Action
----------- ------
Pre-init: Cert Self-test only
Device Create system object
Init: User Create default user object
Keyset Drivers - keysets | Done async.
Device Drivers - devices | if
Session Drivers - networking | available
[Several] Kernel self-test
Pre-shutdown: Session Networking - signal socket close
Device System object - signal entropy poll end
Shutdown: User Destroy default user object | Done by
Device Destroy system object | kernel
Keyset Drivers - keysets
Device Drivers - devices
Session Drivers - networking
The init order is determined by the following object dependencies:
All -> Device
(System object handles many message types).
User -> Keyset, Cert
(Default user object reads config data from the default keyset
to init drivers for keysets, devices, and networking, and
trusted certs. The default keyset isn't read via a loadable
keyset driver so it doesn't require the keyset driver init).
Self-test -> Several
(Kernel self-test creates several ephemeral objects in order to
test the kernel mechanisms).
The shutdown order is determined by the following dependencies:
Session (Networking needs to shut down to release any objects that are
blocked waiting on network I/O)
Device (System object needs to shut down ongoing entropy poll)
After this the shutdown proper can take place. The shutdown order is
noncritical, provided that the pre-shutdown actions have occurred.
In theory the user and system objects are destroyed as part of the
standard shutdown, however the kernel prevents these objects from ever
being explicitly destroyed so they're destroyed implicitly by the
destroyObjects() cleanup call */
CHECK_RETVAL \
int certManagementFunction( IN_ENUM( MANAGEMENT_ACTION ) \
const MANAGEMENT_ACTION_TYPE action );
CHECK_RETVAL \
int deviceManagementFunction( IN_ENUM( MANAGEMENT_ACTION ) \
const MANAGEMENT_ACTION_TYPE action );
CHECK_RETVAL \
int keysetManagementFunction( IN_ENUM( MANAGEMENT_ACTION ) \
const MANAGEMENT_ACTION_TYPE action );
CHECK_RETVAL \
int sessionManagementFunction( IN_ENUM( MANAGEMENT_ACTION ) \
const MANAGEMENT_ACTION_TYPE action );
CHECK_RETVAL \
int userManagementFunction( IN_ENUM( MANAGEMENT_ACTION ) \
const MANAGEMENT_ACTION_TYPE action );
typedef CHECK_RETVAL \
int ( *MANAGEMENT_FUNCTION )( IN_ENUM( MANAGEMENT_ACTION ) \
const MANAGEMENT_ACTION_TYPE action );
static const MANAGEMENT_FUNCTION preInitFunctions[] = {
#ifdef USE_CERTIFICATES
certManagementFunction,
#endif /* USE_CERTIFICATES */
deviceManagementFunction,
NULL, NULL
};
static const MANAGEMENT_FUNCTION initFunctions[] = {
userManagementFunction,
NULL, NULL
};
static const MANAGEMENT_FUNCTION asyncInitFunctions[] = {
#ifdef USE_KEYSETS
keysetManagementFunction,
#endif /* USE_KEYSETS */
deviceManagementFunction,
#ifdef USE_SESSIONS
sessionManagementFunction,
#endif /* USE_SESSIONS */
NULL, NULL
};
static const MANAGEMENT_FUNCTION preShutdownFunctions[] = {
#ifdef USE_SESSIONS
sessionManagementFunction,
#endif /* USE_SESSIONS */
deviceManagementFunction,
NULL, NULL
};
static const MANAGEMENT_FUNCTION shutdownFunctions[] = {
/*userManagementFunction,*/ /*deviceManagementFunction,*/
#ifdef USE_KEYSETS
keysetManagementFunction,
#endif /* USE_KEYSETS */
deviceManagementFunction,
#ifdef USE_SESSIONS
sessionManagementFunction,
#endif /* USE_SESSIONS */
NULL, NULL
};
/* Dispatch a set of management actions */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int dispatchManagementAction( IN_ARRAY( mgmtFunctionCount ) \
const MANAGEMENT_FUNCTION *mgmtFunctions,
IN_INT_SHORT const int mgmtFunctionCount,
IN_ENUM( MANAGEMENT_ACTION ) \
const MANAGEMENT_ACTION_TYPE action )
{
int i, status = CRYPT_OK;
assert( isReadPtr( mgmtFunctions, \
sizeof( MANAGEMENT_FUNCTION ) * mgmtFunctionCount ) );
REQUIRES( mgmtFunctionCount > 0 && \
mgmtFunctionCount < MAX_INTLENGTH_SHORT );
REQUIRES( action > MANAGEMENT_ACTION_NONE && \
action < MANAGEMENT_ACTION_LAST );
/* If we're performing a startup and the kernel is shutting down, bail
out now */
if( ( action == MANAGEMENT_ACTION_INIT ) && krnlIsExiting() )
return( CRYPT_ERROR_PERMISSION );
/* Dispatch each management action in turn */
for( i = 0; i < mgmtFunctionCount && \
mgmtFunctions[ i ] != NULL && \
i < FAILSAFE_ITERATIONS_MED; i++ )
{
const int localStatus = mgmtFunctions[ i ]( action );
if( cryptStatusError( localStatus ) && cryptStatusOK( status ) )
status = localStatus;
/* If we're performing a startup and the kernel is shutting down,
bail out now */
if( ( action == MANAGEMENT_ACTION_INIT ) && krnlIsExiting() )
return( CRYPT_ERROR_PERMISSION );
}
ENSURES( i < FAILSAFE_ITERATIONS_MED );
return( status );
}
/* Under various OSes we bind to a number of drivers at runtime. We can
either do this sychronously or asynchronously depending on the setting of
a config option. By default we use the async init since it speeds up the
startup. Synchronisation is achieved by having the open/init functions
in the modules that require the drivers call krnlWaitSemaphore() on the
driver binding semaphore, which blocks until the drivers are bound if an
async bind is in progress, or returns immediately if no bind is in
progress */
#ifdef USE_THREADS
void threadedBind( const THREAD_PARAMS *threadParams )
{
assert( isReadPtr( threadParams, sizeof( THREAD_PARAMS ) ) );
( void ) dispatchManagementAction( asyncInitFunctions,
FAILSAFE_ARRAYSIZE( asyncInitFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_INIT );
}
#endif /* USE_THREADS */
/* Perform various sanity checks on the build process. Since this will
typically be running on an embedded system there's not much that we can
(safely) do in terms of user I/O except to return a special-case return
code and hope that the user checks the embedded systems section of the
manual for more details, although we do try and produce diagnostic output
if this is enabled */
static BOOLEAN buildSanityCheck( void )
{
FNPTR_DECLARE( void *, testPtr );
void *testPtrResult;
/* If we're using a user-defined endianness override (i.e. it's a cross-
compile from a difference architecture) perform a sanity check to
make sure that the endianness was set right. The crypto self-test that's
performed a few lines further down will catch this problem as well but
it's better to do an explicit check here that catches the endianness
problem rather than just returning a generic self-test fail error */
#if defined( CONFIG_DATA_LITTLEENDIAN ) || defined( CONFIG_DATA_BIGENDIAN )
#ifdef DATA_LITTLEENDIAN
if( *( ( long * ) "\x80\x00\x00\x00\x00\x00\x00\x00" ) < 0 )
#else
if( *( ( long * ) "\x80\x00\x00\x00\x00\x00\x00\x00" ) >= 0 )
#endif /* DATA_LITTLEENDIAN */
{
/* We should probably sound klaxons as well at this point */
DEBUG_PRINT(( "CPU endianness is configured incorrectly, see "
"the cryptlib manual for details" ));
return( FALSE );
}
#endif /* Big/little-endian override check */
/* Make sure that the compiler doesn't use variable-size enums. This is
done by, for example, the PalmOS SDK for backwards compatibility with
architectural decisions made for 68K-based PalmPilots, and at least
one ARM compiler, as permitted by the ARM AAPCS (section 7.1.3),
which says that "this ABI delegates a choice of representation of
enumerated types to a platform ABI", followed by a long discussion of
all the problems that this causes, after which it's allowed anyway,
but it can also be optionally enabled for compilers like gcc, so just
to be safe we check all compilers except the ones that we know never
do this */
#if !defined( _MSC_VER )
if( sizeof( CRYPT_ALGO_TYPE ) != sizeof( int ) || \
sizeof( CRYPT_MODE_TYPE ) != sizeof( int ) ||
sizeof( CRYPT_ATTRIBUTE_TYPE ) != sizeof( int ) )
{
DEBUG_PRINT(( "Compiler uses variable-length enumerated types, see "
"the cryptlib manual for details" ));
return( FALSE );
}
#endif /* Microsoft compilers */
/* Make sure that the handling of safe function pointers is OK */
FNPTR_SET( testPtr, ( void * ) buildSanityCheck );
testPtrResult = FNPTR_GET( testPtr );
if( testPtrResult != buildSanityCheck )
{
DEBUG_PRINT(( "Handling of safe function pointers is broken" ));
return( FALSE );
}
return( TRUE );
}
/* Initialise and shut down the system */
CHECK_RETVAL \
int initCryptlib( void )
{
int initLevel = 0, status;
/* Let the user know that we're in the cryptlib startup code if they're in
debug mode */
DEBUG_PRINT(( "\n" ));
DEBUG_PRINT(( "***************************\n" ));
DEBUG_PRINT(( "* Beginning cryptlib init *\n" ));
DEBUG_PRINT(( "***************************\n" ));
/* Perform any required sanity checks on the build process. This would
be caught by the self-test but sometimes people don't run this so we
perform a minimal sanity check here to avoid failing in the startup
self-tests that follow */
if( !buildSanityCheck() )
{
DEBUG_DIAG(( "Build sanity-check failed" ));
retIntError();
}
/* Initiate the kernel startup */
status = krnlBeginInit();
if( cryptStatusError( status ) )
{
DEBUG_DIAG(( "Kernel init failed" ));
return( status );
}
/* Perform OS-specific additional initialisation inside the kernel init
lock */
status = initSysVars();
if( cryptStatusError( status ) )
{
DEBUG_DIAG(( "OS-specific initialisation failed" ));
assert( DEBUG_WARN );
krnlCompleteShutdown();
return( CRYPT_ERROR_FAILED );
}
/* Perform the multi-phase bootstrap */
status = dispatchManagementAction( preInitFunctions,
FAILSAFE_ARRAYSIZE( preInitFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_PRE_INIT );
assertNoFault( cryptStatusOK( status ) );
if( cryptStatusOK( status ) )
{
initLevel = 1;
status = dispatchManagementAction( initFunctions,
FAILSAFE_ARRAYSIZE( initFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_INIT );
assertNoFault( cryptStatusOK( status ) );
}
if( cryptStatusOK( status ) )
{
#ifdef USE_THREADS
BOOLEAN asyncInit = FALSE;
#endif /* USE_THREADS */
initLevel = 2;
/* Perform the final init phase asynchronously or synchronously
depending on the config option setting. We always send this
query to the default user object since no other user objects
exist at this time */
#ifdef USE_THREADS
status = krnlSendMessage( DEFAULTUSER_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE, &asyncInit,
CRYPT_OPTION_MISC_ASYNCINIT );
if( cryptStatusOK( status ) && asyncInit )
{
/* We use the kernel's thread storage for this thread, so we
specify the thread data storage as NULL */
status = krnlDispatchThread( threadedBind, NULL, NULL, 0,
SEMAPHORE_DRIVERBIND );
if( cryptStatusError( status ) )
{
/* The thread couldn't be started, try again with a
synchronous init */
asyncInit = FALSE;
}
}
if( !asyncInit )
#endif /* USE_THREADS */
status = dispatchManagementAction( asyncInitFunctions,
FAILSAFE_ARRAYSIZE( asyncInitFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_INIT );
assertNoFault( cryptStatusOK( status ) );
}
if( cryptStatusOK( status ) )
{
#ifndef CONFIG_FUZZ
/* Everything's set up, verify that the core crypto algorithms and
kernel security mechanisms are working as required, unless we're
running a fuzzing build for which we don't want to get held up
too long in startup */
status = testKernel();
assertNoFault( cryptStatusOK( status ) );
#endif /* CONFIG_FUZZ */
}
/* If anything failed, shut down the internal functions and services
before we exit */
if( cryptStatusError( status ) )
{
if( initLevel >= 1 )
{
/* Shut down any external interfaces */
( void ) dispatchManagementAction( preShutdownFunctions,
FAILSAFE_ARRAYSIZE( preShutdownFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_PRE_SHUTDOWN );
destroyObjects();
( void ) dispatchManagementAction( shutdownFunctions,
FAILSAFE_ARRAYSIZE( shutdownFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_SHUTDOWN );
}
krnlCompleteShutdown();
return( status );
}
/* Complete the kernel startup */
krnlCompleteInit();
/* Let the user know that the cryptlib startup has completed
successfully if they're in debug mode */
DEBUG_PRINT(( "\n" ));
DEBUG_PRINT(( "***************************\n" ));
DEBUG_PRINT(( "* cryptlib init completed *\n" ));
DEBUG_PRINT(( "***************************\n" ));
DEBUG_PRINT(( "\n" ));
return( CRYPT_OK );
}
CHECK_RETVAL \
int endCryptlib( void )
{
int status;
/* Initiate the kernel shutdown */
status = krnlBeginShutdown();
if( cryptStatusError( status ) )
return( status );
/* Reverse the process carried out in the multi-phase bootstrap */
( void ) dispatchManagementAction( preShutdownFunctions,
FAILSAFE_ARRAYSIZE( preShutdownFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_PRE_SHUTDOWN );
status = destroyObjects();
( void ) dispatchManagementAction( shutdownFunctions,
FAILSAFE_ARRAYSIZE( shutdownFunctions, \
MANAGEMENT_FUNCTION ),
MANAGEMENT_ACTION_SHUTDOWN );
/* Complete the kernel shutdown */
krnlCompleteShutdown();
return( status );
}