1 /****************************************************************************
2 * *
3 * cryptlib DBMS Backend Interface *
4 * Copyright Peter Gutmann 1996-2007 *
5 * *
6 ****************************************************************************/
7
8 #include <ctype.h>
9 #include <stdarg.h>
10 #if defined( INC_ALL )
11 #include "crypt.h"
12 #include "dbms.h"
13 #include "keyset.h"
14 #else
15 #include "crypt.h"
16 #include "keyset/dbms.h"
17 #include "keyset/keyset.h"
18 #endif /* Compiler-specific includes */
19
20 #ifdef USE_DBMS
21
22 /****************************************************************************
23 * *
24 * Backend Database Access Functions *
25 * *
26 ****************************************************************************/
27
28 /* Dispatch functions for various database types. ODBC is the native keyset
29 for Windows and (frequently) Unix, the rest are accessible via database
30 plugins */
31
32 #ifdef USE_ODBC
33 int initDispatchODBC( DBMS_INFO *dbmsInfo );
34 #else
35 #define initDispatchODBC( dbmsInfo ) CRYPT_ERROR
36 #endif /* USE_ODBC */
37 #if defined( USE_DATABASE )
38 int initDispatchDatabase( DBMS_INFO *dbmsInfo );
39 #else
40 #define initDispatchDatabase( dbmsInfo ) CRYPT_ERROR
41 #endif /* General database interface */
42
43 /* Database access functions */
44
45 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
openDatabase(INOUT DBMS_INFO * dbmsInfo,IN_BUFFER (nameLen)const char * name,IN_LENGTH_NAME const int nameLen,IN_ENUM_OPT (CRYPT_KEYOPT)const CRYPT_KEYOPT_TYPE options,OUT_FLAGS_Z (DBMS_FEATURE)int * featureFlags)46 static int openDatabase( INOUT DBMS_INFO *dbmsInfo,
47 IN_BUFFER( nameLen ) const char *name,
48 IN_LENGTH_NAME const int nameLen,
49 IN_ENUM_OPT( CRYPT_KEYOPT ) \
50 const CRYPT_KEYOPT_TYPE options,
51 OUT_FLAGS_Z( DBMS_FEATURE ) int *featureFlags )
52 {
53 DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;
54 int status;
55
56 assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
57 assert( isReadPtr( name, nameLen ) );
58 assert( isWritePtr( featureFlags, sizeof( int ) ) );
59
60 REQUIRES( nameLen >= MIN_NAME_LENGTH && \
61 nameLen < MAX_ATTRIBUTE_SIZE );
62 REQUIRES( options >= CRYPT_KEYOPT_NONE && options < CRYPT_KEYOPT_LAST );
63
64 /* Clear return value */
65 *featureFlags = DBMS_FEATURE_FLAG_NONE;
66
67 status = dbmsInfo->openDatabaseBackend( dbmsStateInfo, name, nameLen,
68 options, featureFlags );
69 if( cryptStatusError( status ) )
70 return( status );
71
72 /* Make long-term information returned as a back-end interface-specific
73 feature flags persistent if necessary */
74 if( *featureFlags & DBMS_FEATURE_FLAG_BINARYBLOBS )
75 dbmsInfo->flags |= DBMS_FLAG_BINARYBLOBS;
76
77 return( CRYPT_OK );
78 }
79
80 STDC_NONNULL_ARG( ( 1 ) ) \
closeDatabase(INOUT DBMS_INFO * dbmsInfo)81 static void closeDatabase( INOUT DBMS_INFO *dbmsInfo )
82 {
83 DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;
84
85 assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
86
87 dbmsInfo->closeDatabaseBackend( dbmsStateInfo );
88 }
89
90 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
performUpdate(INOUT DBMS_INFO * dbmsInfo,IN_STRING_OPT const char * command,IN_ARRAY_OPT_C (BOUND_DATA_MAXITEMS)TYPECAST (BOUND_DATA)const void * boundData,IN_ENUM (DBMS_UPDATE)const DBMS_UPDATE_TYPE updateType)91 static int performUpdate( INOUT DBMS_INFO *dbmsInfo,
92 IN_STRING_OPT const char *command,
93 IN_ARRAY_OPT_C( BOUND_DATA_MAXITEMS ) \
94 TYPECAST( BOUND_DATA ) const void *boundData,
95 IN_ENUM( DBMS_UPDATE ) \
96 const DBMS_UPDATE_TYPE updateType )
97 {
98 DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;
99 const int commandLength = ( command != NULL ) ? strlen( command ) : 0;
100 int status;
101
102 assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
103 assert( ( command == NULL && commandLength == 0 && \
104 updateType == DBMS_UPDATE_ABORT ) || \
105 isReadPtr( command, commandLength ) );
106 assert( ( boundData == NULL ) || \
107 isReadPtr( boundData, \
108 sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );
109
110 ANALYSER_HINT_STRING( command );
111
112 REQUIRES( ( updateType == DBMS_UPDATE_ABORT && \
113 command == NULL && commandLength == 0 ) || \
114 ( updateType != DBMS_UPDATE_ABORT && \
115 command != NULL && \
116 commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
117 REQUIRES( updateType > DBMS_UPDATE_NONE && \
118 updateType < DBMS_UPDATE_LAST );
119
120 /* If we're trying to abort a transaction that was never begun, don't
121 do anything */
122 if( updateType == DBMS_UPDATE_ABORT && \
123 !( dbmsInfo->flags & DBMS_FLAG_UPDATEACTIVE ) )
124 {
125 DEBUG_DIAG(( "Tried to roll back non-begun transaction." ));
126 return( CRYPT_OK );
127 }
128
129 /* Process the update */
130 status = dbmsInfo->performUpdateBackend( dbmsStateInfo, command,
131 commandLength, boundData,
132 updateType );
133 if( cryptStatusOK( status ) && updateType == DBMS_UPDATE_BEGIN )
134 {
135 /* We've successfully started a transaction, record the update
136 state */
137 dbmsInfo->flags |= DBMS_FLAG_UPDATEACTIVE;
138 }
139 if( updateType == DBMS_UPDATE_COMMIT || \
140 updateType == DBMS_UPDATE_ABORT )
141 {
142 /* Final commits or rollbacks always end a transaction, even if
143 they're performed as part of a failed transaction */
144 dbmsInfo->flags &= ~DBMS_FLAG_UPDATEACTIVE;
145 }
146 return( status );
147 }
148
149 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
performStaticUpdate(INOUT DBMS_INFO * dbmsInfo,IN_STRING const char * command)150 static int performStaticUpdate( INOUT DBMS_INFO *dbmsInfo,
151 IN_STRING const char *command )
152 {
153 return( performUpdate( dbmsInfo, command, NULL, DBMS_UPDATE_NORMAL ) );
154 }
155
156 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
performQuery(INOUT DBMS_INFO * dbmsInfo,IN_STRING_OPT const char * command,OUT_BUFFER_OPT (dataMaxLength,* dataLength)void * data,IN_LENGTH_SHORT_Z const int dataMaxLength,OUT_OPT_LENGTH_SHORT_Z int * dataLength,IN_ARRAY_OPT_C (BOUND_DATA_MAXITEMS)TYPECAST (BOUND_DATA)const void * boundData,IN_ENUM_OPT (DBMS_CACHEDQUERY)const DBMS_CACHEDQUERY_TYPE queryEntry,IN_ENUM (DBMS_QUERY)const DBMS_QUERY_TYPE queryType)157 static int performQuery( INOUT DBMS_INFO *dbmsInfo,
158 IN_STRING_OPT const char *command,
159 OUT_BUFFER_OPT( dataMaxLength, *dataLength ) \
160 void *data,
161 IN_LENGTH_SHORT_Z const int dataMaxLength,
162 OUT_OPT_LENGTH_SHORT_Z int *dataLength,
163 IN_ARRAY_OPT_C( BOUND_DATA_MAXITEMS ) \
164 TYPECAST( BOUND_DATA ) const void *boundData,
165 IN_ENUM_OPT( DBMS_CACHEDQUERY ) \
166 const DBMS_CACHEDQUERY_TYPE queryEntry,
167 IN_ENUM( DBMS_QUERY ) const DBMS_QUERY_TYPE queryType )
168 {
169 DBMS_STATE_INFO *dbmsStateInfo = dbmsInfo->stateInfo;
170 const int commandLength = ( command != NULL ) ? strlen( command ) : 0;
171 int status;
172
173 assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
174 assert( ( command == NULL && commandLength == 0 && \
175 ( queryType == DBMS_QUERY_CONTINUE || \
176 queryType == DBMS_QUERY_CANCEL ) ) || \
177 isReadPtr( command, commandLength ) );
178 assert( ( data == NULL && dataLength == NULL ) || \
179 isWritePtr( data, MAX_QUERY_RESULT_SIZE ) );
180 assert( ( boundData == NULL ) || \
181 isReadPtr( boundData, \
182 sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );
183
184 ANALYSER_HINT_STRING( command );
185
186 REQUIRES( ( ( queryType == DBMS_QUERY_CONTINUE || \
187 queryType == DBMS_QUERY_CANCEL ) && \
188 command == NULL && commandLength == 0 ) || \
189 ( ( queryType == DBMS_QUERY_START || \
190 queryType == DBMS_QUERY_CHECK || \
191 queryType == DBMS_QUERY_NORMAL ) && \
192 command != NULL && \
193 commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
194 REQUIRES( ( data == NULL && dataMaxLength == 0 && \
195 dataLength == NULL ) || \
196 ( data != NULL && dataMaxLength >= 16 && \
197 dataMaxLength < MAX_INTLENGTH_SHORT && \
198 dataLength != NULL ) );
199 REQUIRES( queryEntry >= DBMS_CACHEDQUERY_NONE && \
200 queryEntry < DBMS_CACHEDQUERY_LAST );
201 REQUIRES( queryType > DBMS_QUERY_NONE && \
202 queryType < DBMS_QUERY_LAST );
203
204 /* Additional state checks: If we're starting a new query or performing
205 a point query there can't already be one active and if we're
206 continuing or cancelling an existing query there has to be one
207 already active */
208 REQUIRES( ( ( queryType == DBMS_QUERY_START || \
209 queryType == DBMS_QUERY_CHECK || \
210 queryType == DBMS_QUERY_NORMAL ) && \
211 !( dbmsInfo->flags & DBMS_FLAG_QUERYACTIVE ) ) ||
212 ( ( queryType == DBMS_QUERY_CONTINUE || \
213 queryType == DBMS_QUERY_CANCEL ) && \
214 ( dbmsInfo->flags & DBMS_FLAG_QUERYACTIVE ) ) );
215
216 /* Clear return value */
217 if( data != NULL )
218 {
219 memset( data, 0, min( 16, dataMaxLength ) );
220 *dataLength = 0;
221 }
222
223 /* Process the query */
224 status = dbmsInfo->performQueryBackend( dbmsStateInfo, command,
225 commandLength, data, dataMaxLength,
226 dataLength, boundData,
227 queryEntry, queryType );
228 if( cryptStatusError( status ) )
229 return( status );
230
231 /* Sanity-check the result data from the back-end */
232 if( dataLength != NULL && \
233 ( *dataLength <= 0 || *dataLength > MAX_QUERY_RESULT_SIZE ) )
234 {
235 DEBUG_DIAG(( "Database backend return invalid data size" ));
236 assert( DEBUG_WARN );
237 memset( data, 0, min( 16, dataMaxLength ) );
238 *dataLength = 0;
239 return( CRYPT_ERROR_BADDATA );
240 }
241
242 /* Update the state information based on the query that we've just
243 performed */
244 if( queryType == DBMS_QUERY_START )
245 dbmsInfo->flags |= DBMS_FLAG_QUERYACTIVE;
246 if( queryType == DBMS_QUERY_CANCEL )
247 dbmsInfo->flags &= ~DBMS_FLAG_QUERYACTIVE;
248 return( CRYPT_OK );
249 }
250
251 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
performStaticQuery(INOUT DBMS_INFO * dbmsInfo,IN_STRING_OPT const char * command,IN_ENUM_OPT (DBMS_CACHEDQUERY)const DBMS_CACHEDQUERY_TYPE queryEntry,IN_ENUM (DBMS_QUERY)const DBMS_QUERY_TYPE queryType)252 static int performStaticQuery( INOUT DBMS_INFO *dbmsInfo,
253 IN_STRING_OPT const char *command,
254 IN_ENUM_OPT( DBMS_CACHEDQUERY ) \
255 const DBMS_CACHEDQUERY_TYPE queryEntry,
256 IN_ENUM( DBMS_QUERY ) \
257 const DBMS_QUERY_TYPE queryType )
258 {
259 return( performQuery( dbmsInfo, command, NULL, 0, NULL, NULL,
260 queryEntry, queryType ) );
261 }
262
263 /****************************************************************************
264 * *
265 * SQL Rewrite Routines *
266 * *
267 ****************************************************************************/
268
269 /* In order to allow general certificate database queries we have to be able
270 to process user-supplied query strings. The cryptlib manual contains
271 strong warnings about the correct way to do this (if it's done at all),
272 the best that we can do is apply assorted safety checks of the query data
273 to try and reduce the chances of SQL injection. Unfortunately this can
274 get arbitrarily complicated:
275
276 '; The standard SQL-injection method, used with values like
277 'foo; DROP TABLE bar', or '1=1' to return all entries in a table.
278
279 -- Comment delimiter (other values also exist, e.g. MySQL's '#') to
280 truncate queries beyond the end of the injected SQL.
281
282 char(0xNN) Bypass the first level of filtering, e.g. char(0x41)
283 produces the banned character '.
284
285 One additional check that we could do is to try and explicitly strip
286 SQL keywords from queries but this is somewhat problematic because apart
287 from the usual trickery (e.g. embedding one SQL keyword inside another so
288 that stripping SELECT from SELSELECTECT will still leave the outer
289 SELECT, requiring recursive stripping, or taking advantage of the fact
290 that VARBINARY values are implicitly cast to VARCHARS so that 0x42434445
291 would turn into ABCD, or further escaping the encoding with values like
292 'sel'+'ect') there are also any number of backend-specific custom
293 keywords and ways of escaping keywords that we can't know about and
294 therefore can't easily strip */
295
296 #define SQL_ESCAPE '\''
297
298 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
copyChar(OUT_BUFFER (bufMaxLen,* bufPos)char * buffer,IN_LENGTH_SHORT const int bufMaxLen,OUT_LENGTH_BOUNDED_Z (bufMaxLen)int * bufPos,IN_BYTE const int ch,const BOOLEAN escapeQuotes)299 static int copyChar( OUT_BUFFER( bufMaxLen, *bufPos ) char *buffer,
300 IN_LENGTH_SHORT const int bufMaxLen,
301 OUT_LENGTH_BOUNDED_Z( bufMaxLen ) int *bufPos,
302 IN_BYTE const int ch, const BOOLEAN escapeQuotes )
303 {
304 int position = 0;
305
306 assert( isWritePtr( buffer, bufMaxLen ) );
307 assert( isWritePtr( bufPos, sizeof( int ) ) );
308
309 REQUIRES( bufMaxLen > 0 && bufMaxLen < MAX_INTLENGTH_SHORT );
310 REQUIRES( ch >= 0 && ch <= 0xFF );
311
312 /* Clear return value */
313 *bufPos = 0;
314
315 /* If it's a control character, skip it */
316 if( ( ch & 0x7F ) < ' ' )
317 return( CRYPT_OK );
318
319 /* Escape metacharacters that could be misused in queries. We catch the
320 obvious ' and ; as well as the less obvious %, which could be used to
321 hide other metacharacters, and \, used by some databases (e.g. MySQL)
322 as an escape. Note that none of these characters are valid in base64,
323 which makes it safe to escape them in the few instances where they do
324 occur */
325 if( ( ch == '\'' && escapeQuotes ) || \
326 ch == '\\' || ch == ';' || ch == '%' )
327 {
328 /* Escape the character */
329 buffer[ position++ ] = SQL_ESCAPE;
330 if( position >= bufMaxLen )
331 return( CRYPT_ERROR_OVERFLOW );
332 }
333
334 /* Bypass various dangerous SQL "enhancements". For Windows ODBC (at
335 least for MS Jet 3.x, but not 4.x any more) the driver will execute
336 anything delimited by '|'s as an expression (an example being
337 '|shell("cmd /c echo " & chr(124) & " format c:")|'), because of this
338 we strip gazintas. Since ODBC uses '{' and '}' as escape delimiters
339 we also strip these */
340 if( ch != '|' && ch != '{' && ch != '}' )
341 buffer[ position++ ] = intToByte( ch );
342
343 /* Make sure that we haven't overflowed the output buffer. This
344 overflowing can be done deliberately, for example by using large
345 numbers of escape chars (which are in turn escaped) to force
346 truncation of the query beyond the injected SQL if the processing
347 simply stops at a given point */
348 if( position >= bufMaxLen )
349 return( CRYPT_ERROR_OVERFLOW );
350 *bufPos = position;
351
352 return( CRYPT_OK );
353 }
354
355 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
copyStringArg(OUT_BUFFER (bufMaxLen,* bufPos)char * buffer,IN_LENGTH_SHORT_Z const int bufMaxLen,OUT_LENGTH_BOUNDED_Z (bufMaxLen)int * bufPos,IN_BUFFER (stringLen)const char * string,IN_LENGTH_SHORT const int stringLen)356 static int copyStringArg( OUT_BUFFER( bufMaxLen, *bufPos ) char *buffer,
357 IN_LENGTH_SHORT_Z const int bufMaxLen,
358 OUT_LENGTH_BOUNDED_Z( bufMaxLen ) int *bufPos,
359 IN_BUFFER( stringLen ) const char *string,
360 IN_LENGTH_SHORT const int stringLen )
361 {
362 int index, position = 0;
363
364 assert( isWritePtr( buffer, bufMaxLen ) );
365 assert( isWritePtr( bufPos, sizeof( int ) ) );
366 assert( isReadPtr( string, stringLen ) );
367
368 REQUIRES( bufMaxLen >= 0 && bufMaxLen < MAX_INTLENGTH_SHORT );
369 REQUIRES( stringLen > 0 && stringLen < MAX_INTLENGTH_SHORT );
370
371 /* Make sure that there's room for at least one more character of
372 output */
373 if( bufMaxLen < 1 )
374 return( CRYPT_ERROR_OVERFLOW );
375
376 /* Copy the string to the output buffer with conversion of any special
377 characters that are used by SQL */
378 for( index = 0; index < stringLen && \
379 index < FAILSAFE_ITERATIONS_MAX; index++ )
380 {
381 int charsWritten, status;
382
383 status = copyChar( buffer + position, bufMaxLen - position,
384 &charsWritten, byteToInt( string[ index ] ),
385 TRUE );
386 if( cryptStatusError( status ) )
387 return( status );
388 position += charsWritten;
389 if( position > bufMaxLen )
390 {
391 /* Already checked in copyChar() but we double-check here to be
392 safe */
393 return( CRYPT_ERROR_OVERFLOW );
394 }
395 }
396 ENSURES( index < FAILSAFE_ITERATIONS_MAX );
397 *bufPos = position;
398
399 return( CRYPT_OK );
400 }
401
402 /* Format input parameters into SQL queries, replacing meta-values with
403 actual column names, and null-terminate the resulting string so that
404 it can be fed to the database backend */
405
406 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
dbmsFormatQuery(OUT_BUFFER (outMaxLength,* outLength)char * output,IN_LENGTH_SHORT const int outMaxLength,OUT_LENGTH_BOUNDED_Z (outMaxLength)int * outLength,IN_BUFFER (inputLength)const char * input,IN_LENGTH_SHORT const int inputLength)407 int dbmsFormatQuery( OUT_BUFFER( outMaxLength, *outLength ) char *output,
408 IN_LENGTH_SHORT const int outMaxLength,
409 OUT_LENGTH_BOUNDED_Z( outMaxLength ) int *outLength,
410 IN_BUFFER( inputLength ) const char *input,
411 IN_LENGTH_SHORT const int inputLength )
412 {
413 int inPos, outPos = 0, status = CRYPT_OK;
414
415 assert( isWritePtr( output, outMaxLength ) );
416 assert( isWritePtr( outLength, sizeof( int ) ) );
417 assert( isReadPtr( input, inputLength ) );
418
419 REQUIRES( outMaxLength >= 0 && outMaxLength < MAX_INTLENGTH_SHORT );
420 REQUIRES( inputLength > 0 && inputLength < MAX_INTLENGTH_SHORT );
421
422 /* Clear return values */
423 memset( output, 0, min( 16, outMaxLength ) );
424 *outLength = 0;
425
426 for( inPos = 0; inPos < inputLength && inPos < FAILSAFE_ITERATIONS_MAX; )
427 {
428 int length;
429
430 if( input[ inPos ] == '$' )
431 {
432 typedef struct {
433 BUFFER_FIXED( sourceLength ) \
434 char *sourceName;
435 int sourceLength;
436 BUFFER_FIXED( destLength ) \
437 char *destName;
438 int destLength;
439 } NAMEMAP_INFO;
440 static const NAMEMAP_INFO nameMapTbl[] = {
441 { "C", 1, "C", 1 }, { "SP", 2, "SP", 2 },
442 { "L", 1, "L", 1 }, { "O", 1, "O", 1 },
443 { "OU", 2, "OU", 2 }, { "CN", 2, "CN", 2 },
444 { "email", 5, "email", 5 }, { "uri", 3, "email", 5 },
445 { "date", 4, "validTo", 7 },
446 { NULL, 0, NULL, 0 }, { NULL, 0, NULL, 0 }
447 };
448 const int fieldPos = inPos + 1;
449 const char *fieldName = input + fieldPos;
450 int i;
451
452 inPos++; /* Skip '$' */
453
454 /* Extract the field name and translate it into the table
455 column name */
456 while( inPos < inputLength && isAlpha( input[ inPos ] ) )
457 inPos++;
458 length = inPos - fieldPos;
459 if( length <= 0 || length > 5 )
460 {
461 status = CRYPT_ERROR_BADDATA;
462 break;
463 }
464 for( i = 0; nameMapTbl[ i ].sourceName != NULL && \
465 i < FAILSAFE_ARRAYSIZE( nameMapTbl, NAMEMAP_INFO );
466 i++ )
467 {
468 if( length == nameMapTbl[ i ].sourceLength && \
469 !strCompare( fieldName, nameMapTbl[ i ].sourceName, \
470 length ) )
471 break;
472 }
473 ENSURES( i < FAILSAFE_ARRAYSIZE( nameMapTbl, NAMEMAP_INFO ) );
474 if( nameMapTbl[ i ].sourceName == NULL )
475 {
476 status = CRYPT_ERROR_BADDATA;
477 break;
478 }
479
480 /* Copy the translated name to the output buffer */
481 status = copyStringArg( output + outPos, outMaxLength - outPos,
482 &length, nameMapTbl[ i ].destName,
483 nameMapTbl[ i ].destLength );
484 }
485 else
486 {
487 /* Just copy the character over, with a length check. We don't
488 escape single quotes in this case because we use these
489 ourselves in SQL queries */
490 status = copyChar( output + outPos, outMaxLength - outPos,
491 &length, input[ inPos++ ], FALSE );
492 }
493 if( cryptStatusError( status ) )
494 break;
495 outPos += length;
496 }
497 ENSURES( inPos < FAILSAFE_ITERATIONS_MAX );
498 if( cryptStatusError( status ) )
499 outPos = 0;
500 output[ outPos ] = '\0'; /* Add der terminador */
501 *outLength = outPos;
502
503 return( status );
504 }
505
506 /****************************************************************************
507 * *
508 * Back-end Interface Routines *
509 * *
510 ****************************************************************************/
511
512 /* Parse a user-supplied database name into individual components, used by
513 the database back-end connect functions. We don't do a syntax check
514 (since the exact syntax is database-specific) but merely break the single
515 string up into any recognisable components. The database back-end can
516 determine whether the format is valid or not. The general format that we
517 look for is:
518
519 [generic name]
520 user:pass
521 user@server
522 user:pass@server
523 user:pass@server/name
524
525 One distinction that we make is that if there's something after an '@'
526 and there's no server/name separator present we treat it as a name rather
527 than a server. In other words @foo results in name=foo while @foo/bar
528 results in server=foo, name=bar. This is because the most common
529 situation that we have to handle is ODBC, which identifies the database
530 by name rather than by server */
531
532 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
dbmsParseName(OUT DBMS_NAME_INFO * nameInfo,IN_BUFFER (nameLen)const char * name,IN_LENGTH_NAME const int nameLen)533 int dbmsParseName( OUT DBMS_NAME_INFO *nameInfo,
534 IN_BUFFER( nameLen ) const char *name,
535 IN_LENGTH_NAME const int nameLen )
536 {
537 int offset, offset2, length;
538
539 assert( isWritePtr( nameInfo, sizeof( DBMS_NAME_INFO ) ) );
540 assert( isReadPtr( name, nameLen ) );
541
542 REQUIRES( nameLen >= MIN_NAME_LENGTH && \
543 nameLen < MAX_ATTRIBUTE_SIZE );
544
545 /* Clear return value */
546 memset( nameInfo, 0, sizeof( DBMS_NAME_INFO ) );
547
548 /* Check for a complex database name */
549 if( ( offset = strFindCh( name, nameLen, ':' ) ) < 0 && \
550 ( offset = strFindCh( name, nameLen, '@' ) ) < 0 )
551 {
552 /* It's a straightforward name, use it directly */
553 nameInfo->name = ( char * ) name;
554 nameInfo->nameLen = nameLen;
555
556 return( CRYPT_OK );
557 }
558
559 /* Extract the user name */
560 length = min( offset, CRYPT_MAX_TEXTSIZE );
561 if( length <= 0 )
562 return( CRYPT_ERROR_OPEN );
563 memcpy( nameInfo->userBuffer, name, length );
564 nameInfo->user = nameInfo->userBuffer;
565 nameInfo->userLen = length;
566
567 /* We're either at the server name or password, extract the password
568 if there is one */
569 ENSURES( name[ offset ] == ':' || name[ offset ] == '@' );
570 if( name[ offset++ ] == ':' )
571 {
572 offset2 = strFindCh( name + offset, nameLen - offset, '@' );
573 if( offset2 < 0 )
574 offset2 = nameLen - offset; /* Password is rest of string */
575 length = min( offset2, CRYPT_MAX_TEXTSIZE );
576 if( length <= 0 )
577 return( CRYPT_ERROR_OPEN );
578 memcpy( nameInfo->passwordBuffer, name + offset, length );
579 nameInfo->password = nameInfo->passwordBuffer;
580 nameInfo->passwordLen = length;
581 offset += offset2 + 1;
582 if( offset >= nameLen )
583 return( CRYPT_OK );
584 }
585
586 /* Separate the server and database name if necessary */
587 offset2 = strFindCh( name + offset, nameLen - offset, '/' );
588 if( offset2 >= 0 )
589 {
590 /* There's a distinction between the server name and database name,
591 extract the server name */
592 length = min( offset2, CRYPT_MAX_TEXTSIZE );
593 if( length <= 0 )
594 return( CRYPT_ERROR_OPEN );
595 memcpy( nameInfo->serverBuffer, name + offset, length );
596 nameInfo->server = nameInfo->serverBuffer;
597 nameInfo->serverLen = length;
598 offset += offset2 + 1;
599 }
600
601 /* Extract the database name if there is one */
602 if( offset < nameLen )
603 {
604 length = nameLen - offset;
605 if( length <= 0 || length > CRYPT_MAX_TEXTSIZE )
606 return( CRYPT_ERROR_OPEN );
607 memcpy( nameInfo->nameBuffer, name + offset, length );
608 nameInfo->name = nameInfo->nameBuffer;
609 nameInfo->nameLen = length;
610 }
611
612 return( CRYPT_OK );
613 }
614
615 /* Initialise and shut down a session with a database back-end */
616
617 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
initDbxSession(INOUT KEYSET_INFO * keysetInfoPtr,IN_ENUM (CRYPT_KEYSET)const CRYPT_KEYSET_TYPE type)618 int initDbxSession( INOUT KEYSET_INFO *keysetInfoPtr,
619 IN_ENUM( CRYPT_KEYSET ) const CRYPT_KEYSET_TYPE type )
620 {
621 DBMS_INFO *dbmsInfo = keysetInfoPtr->keysetDBMS;
622 int status = CRYPT_ERROR;
623
624 assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
625
626 REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
627 REQUIRES( type > CRYPT_KEYSET_NONE && type < CRYPT_KEYSET_LAST );
628
629 /* Select the appropriate dispatch function for the keyset type */
630 switch( type )
631 {
632 case CRYPT_KEYSET_ODBC:
633 case CRYPT_KEYSET_ODBC_STORE:
634 status = initDispatchODBC( dbmsInfo );
635 break;
636
637 case CRYPT_KEYSET_DATABASE:
638 case CRYPT_KEYSET_DATABASE_STORE:
639 status = initDispatchDatabase( dbmsInfo );
640 break;
641
642 default:
643 retIntError();
644 }
645 if( cryptStatusError( status ) )
646 return( CRYPT_ARGERROR_NUM1 );
647
648 /* Set up the remaining function pointers */
649 dbmsInfo->openDatabaseFunction = openDatabase;
650 dbmsInfo->closeDatabaseFunction = closeDatabase;
651 dbmsInfo->performUpdateFunction = performUpdate;
652 dbmsInfo->performStaticUpdateFunction = performStaticUpdate;
653 dbmsInfo->performQueryFunction = performQuery;
654 dbmsInfo->performStaticQueryFunction = performStaticQuery;
655
656 /* Allocate the database session state information */
657 if( ( keysetInfoPtr->keyData = \
658 clAlloc( "initDbxSession", sizeof( DBMS_STATE_INFO ) ) ) == NULL )
659 return( CRYPT_ERROR_MEMORY );
660 memset( keysetInfoPtr->keyData, 0, sizeof( DBMS_STATE_INFO ) );
661 keysetInfoPtr->keyDataSize = sizeof( DBMS_STATE_INFO );
662 dbmsInfo->stateInfo = keysetInfoPtr->keyData;
663 if( type == CRYPT_KEYSET_ODBC_STORE || \
664 type == CRYPT_KEYSET_DATABASE_STORE )
665 dbmsInfo->flags |= DBMS_FLAG_CERTSTORE | DBMS_FLAG_CERTSTORE_FIELDS;
666
667 return( CRYPT_OK );
668 }
669
670 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
endDbxSession(INOUT KEYSET_INFO * keysetInfoPtr)671 int endDbxSession( INOUT KEYSET_INFO *keysetInfoPtr )
672 {
673 assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
674
675 REQUIRES( keysetInfoPtr->type == KEYSET_DBMS );
676
677 /* Free the database session state information if necessary */
678 if( keysetInfoPtr->keyData != NULL )
679 {
680 memset( keysetInfoPtr->keyData, 0, keysetInfoPtr->keyDataSize );
681 clFree( "endDbxSession", keysetInfoPtr->keyData );
682 keysetInfoPtr->keyData = NULL;
683 }
684
685 return( CRYPT_OK );
686 }
687 #endif /* USE_DBMS */
688