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