cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
odbc.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib ODBC Mapping Routines *
4 * Copyright Peter Gutmann 1996-2007 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdio.h> /* For sprintf() */
9 #if defined( INC_ALL )
10  #include "crypt.h"
11  #include "keyset.h"
12  #include "dbms.h"
13 #else
14  #include "crypt.h"
15  #include "keyset/keyset.h"
16  #include "keyset/dbms.h"
17 #endif /* Compiler-specific includes */
18 
19 /* ODBC functions can return either SQL_SUCCESS or SQL_SUCCESS_WITH_INFO to
20  indicate successful completion, to make them easier to work with we use
21  a general status-check macro along the lines of cryptStatusOK() */
22 
23 #define sqlStatusOK( status ) \
24  ( ( status ) == SQL_SUCCESS || ( status ) == SQL_SUCCESS_WITH_INFO )
25 
26 /* DBMS back-ends that require special handling */
27 
29 
30 /* The level at which we want SQLDiagRec() to return error information to
31  us */
32 
35 
36 /* When rewriting an SQL query we have to provide a slightly larger buffer
37  to allow for possible expansion of some SQL strings */
38 
39 #define SQL_QUERY_BUFSIZE ( MAX_SQL_QUERY_SIZE + 64 )
40 
41 /* Some ODBC functions take either pointers or small integer values cast to
42 pointers to indicate certain magic values. This causes problems in 64-bit
43 environments because the LLP64 model means that pointers are 64 bits while
44 ints and longs are 32 bits. To deal with this we define the following data-
45 conversion macros for 32- and 64-bit environments */
46 
47 #ifdef __WIN64__
48  #define VALUE_TO_PTR ULongToPtr
49 #else
50  #define VALUE_TO_PTR ( SQLPOINTER )
51 #endif /* 32- vs. 64-bit environment */
52 
53 #ifdef USE_ODBC
54 
55 /* When processing bound data we need to store the state information used by
56  SQLBindParameter() until the SQL operation completes. The following
57  structure provides the necessary state storage */
58 
59 typedef struct {
60  SQLINTEGER lengthStorage[ BOUND_DATA_MAXITEMS + 8 ];
61  SQL_TIMESTAMP_STRUCT timestampStorage;
62  } BOUND_DATA_STATE;
63 
64 /****************************************************************************
65 * *
66 * Init/Shutdown Routines *
67 * *
68 ****************************************************************************/
69 
70 #ifdef DYNAMIC_LOAD
71 
72 /* Global function pointers. These are necessary because the functions need
73  to be dynamically linked since not all systems contain the necessary
74  shared libraries. Explicitly linking to them will make cryptlib
75  unloadable on some systems.
76 
77  MSDN updates from late 2000 defined SQLROWCOUNT themselves (which could be
78  fixed by undefining it), however after late 2002 the value was typedef'd,
79  requring all sorts of extra trickery to handle the different cases.
80  Because of this issue, this particular function is typedef'd with a _FN
81  suffix.
82 
83  Several of the ODBC functions allow such a mess of parameters, with
84  options for pointers to be cast to integers indexing a table of data
85  values hidden under someone's bed and other peculiarities, that several
86  of the following markups are only approximations for the way the
87  functions are used here. If markups are absent entirely (e.g. for the
88  SQLSetXXXAttr() functions) it's because the polymorphism of parameters
89  doesn't allow any coherent annotation to be given */
90 
91 static INSTANCE_HANDLE hODBC = NULL_INSTANCE;
92 
93 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLALLOCHANDLE ) \
94  ( SQLSMALLINT HandleType, IN SQLHANDLE InputHandle,
95  OUT_PTR SQLHANDLE *OutputHandlePtr );
96 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLBINDPARAMETER ) \
97  ( IN SQLHSTMT StatementHandle,
98  SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType,
99  SQLSMALLINT ValueType, SQLSMALLINT ParameterType,
100  SQLUINTEGER ColumnSize, SQLSMALLINT DecimalDigits,
101  IN_BUFFER_OPT( BufferLength ) SQLPOINTER ParameterValuePtr,
102  SQLINTEGER BufferLength,
103  INOUT_OPT SQLINTEGER *StrLen_or_IndPtr );
104 typedef SQLRETURN ( SQL_API *SQLCLOSECURSOR )( IN SQLHSTMT StatementHandle );
105 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLCONNECT ) \
106  ( IN SQLHDBC ConnectionHandle,
107  IN_BUFFER( NameLength1 ) SQLCHAR *ServerName,
108  SQLSMALLINT NameLength1,
109  IN_BUFFER( NameLength2 ) SQLCHAR *UserName,
110  SQLSMALLINT NameLength2,
111  IN_BUFFER( NameLength3 ) SQLCHAR *Authentication,
112  SQLSMALLINT NameLength3 );
113 typedef SQLRETURN ( SQL_API *SQLDISCONNECT )( IN SQLHDBC ConnectionHandle );
114 typedef SQLRETURN ( SQL_API *SQLENDTRAN )( SQLSMALLINT HandleType,
115  IN SQLHANDLE Handle, SQLSMALLINT CompletionType );
116 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLEXECDIRECT ) \
117  ( SQLHSTMT StatementHandle,
118  IN_BUFFER( TextLength ) SQLCHAR *StatementText,
119  SQLINTEGER TextLength );
120 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLEXECUTE ) \
121  ( IN SQLHSTMT StatementHandle );
122 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLFETCH ) \
123  ( IN SQLHSTMT StatementHandle );
124 typedef SQLRETURN ( SQL_API *SQLFREEHANDLE )( SQLSMALLINT HandleType,
125  IN SQLHANDLE Handle );
126 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLGETDATA ) \
127  ( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber,
128  SQLSMALLINT TargetType,
129  OUT_BUFFER( BufferLength, *StrLen_or_IndPtr ) \
130  SQLPOINTER TargetValuePtr,
131  SQLINTEGER BufferLength, SQLINTEGER *StrLen_or_IndPtr );
132 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLGETDIAGREC ) \
133  ( SQLSMALLINT HandleType,
134  IN SQLHANDLE Handle, SQLSMALLINT RecNumber,
135  OUT_BUFFER_FIXED( SQL_SQLSTATE_SIZE ) SQLCHAR *Sqlstate,
136  OUT SQLINTEGER *NativeErrorPtr,
137  OUT_BUFFER( BufferLength, *TextLengthPtr ) \
138  SQLCHAR *MessageText,
139  SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr );
140 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLGETINFO ) \
141  ( IN SQLHDBC ConnectionHandle,
142  SQLUSMALLINT InfoType,
143  OUT_BUFFER( BufferLength, *StringLengthPtr ) \
144  SQLPOINTER InfoValuePtr,
145  SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr );
146 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLGETSTMTATTR ) \
147  ( IN SQLHSTMT StatementHandle,
148  SQLINTEGER Attribute, OUT SQLPOINTER ValuePtr,
149  SQLINTEGER BufferLength,
150  STDC_UNUSED SQLINTEGER *StringLengthPtr );
151 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLGETTYPEINFO ) \
152  ( IN SQLHSTMT StatementHandle,
153  SQLSMALLINT DataType );
154 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLPREPARE ) \
155  ( IN SQLHSTMT StatementHandle,
156  IN_BUFFER( TextLength ) SQLCHAR *StatementText,
157  SQLINTEGER TextLength );
158 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLROWCOUNT_FN ) \
159  ( IN SQLHSTMT StatementHandle,
160  OUT SQLINTEGER *RowCountPtr );
161 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLSETCONNECTATTR ) \
162  ( IN SQLHDBC ConnectionHandle,
163  SQLINTEGER Attribute, SQLPOINTER ValuePtr,
164  SQLINTEGER StringLength );
165 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLSETENVATTR ) \
166  ( IN SQLHENV EnvironmentHandle,
167  SQLINTEGER Attribute, SQLPOINTER ValuePtr,
168  SQLINTEGER StringLength );
169 typedef CHECK_RETVAL_FNPTR SQLRETURN ( SQL_API *SQLSETSTMTATTR ) \
170  ( IN SQLHSTMT StatementHandle,
171  SQLINTEGER Attribute, SQLPOINTER ValuePtr,
172  SQLINTEGER StringLength );
173 
174 static SQLALLOCHANDLE pSQLAllocHandle = NULL;
175 static SQLBINDPARAMETER pSQLBindParameter = NULL;
176 static SQLCLOSECURSOR pSQLCloseCursor = NULL;
177 static SQLCONNECT pSQLConnect = NULL;
178 static SQLDISCONNECT pSQLDisconnect = NULL;
179 static SQLENDTRAN pSQLEndTran = NULL;
180 static SQLEXECDIRECT pSQLExecDirect = NULL;
181 static SQLEXECUTE pSQLExecute = NULL;
182 static SQLFETCH pSQLFetch = NULL;
183 static SQLFREEHANDLE pSQLFreeHandle = NULL;
184 static SQLGETDATA pSQLGetData = NULL;
185 static SQLGETDIAGREC pSQLGetDiagRec = NULL;
186 static SQLGETINFO pSQLGetInfo = NULL;
187 static SQLGETSTMTATTR pSQLGetStmtAttr = NULL;
188 static SQLGETTYPEINFO pSQLGetTypeInfo = NULL;
189 static SQLPREPARE pSQLPrepare = NULL;
190 static SQLROWCOUNT_FN pSQLRowCount = NULL;
191 static SQLSETCONNECTATTR pSQLSetConnectAttr = NULL;
192 static SQLSETENVATTR pSQLSetEnvAttr = NULL;
193 static SQLSETSTMTATTR pSQLSetStmtAttr = NULL;
194 
195 /* The use of dynamically bound function pointers vs. statically linked
196  functions requires a bit of sleight of hand since we can't give the
197  pointers the same names as prototyped functions. To get around this we
198  redefine the actual function names to the names of the pointers */
199 
200 #define SQLAllocHandle pSQLAllocHandle
201 #define SQLBindParameter pSQLBindParameter
202 #define SQLCloseCursor pSQLCloseCursor
203 #define SQLConnect pSQLConnect
204 #define SQLDisconnect pSQLDisconnect
205 #define SQLEndTran pSQLEndTran
206 #define SQLExecDirect pSQLExecDirect
207 #define SQLExecute pSQLExecute
208 #define SQLFetch pSQLFetch
209 #define SQLFreeHandle pSQLFreeHandle
210 #define SQLGetData pSQLGetData
211 #define SQLGetDiagRec pSQLGetDiagRec
212 #define SQLGetInfo pSQLGetInfo
213 #define SQLGetStmtAttr pSQLGetStmtAttr
214 #define SQLGetTypeInfo pSQLGetTypeInfo
215 #define SQLPrepare pSQLPrepare
216 #define SQLRowCount pSQLRowCount
217 #define SQLSetConnectAttr pSQLSetConnectAttr
218 #define SQLSetEnvAttr pSQLSetEnvAttr
219 #define SQLSetStmtAttr pSQLSetStmtAttr
220 
221 /* Depending on whether we're running under Win16, Win32, or Unix we load the
222  ODBC driver under a different name */
223 
224 #if defined( __WIN16__ )
225  #define ODBC_LIBNAME "ODBC.DLL"
226 #elif defined( __WIN32__ )
227  #define ODBC_LIBNAME "ODBC32.DLL"
228 #elif defined( __UNIX__ )
229  #if defined( __APPLE__ )
230  /* OS X has built-in ODBC support via iODBC */
231  #define ODBC_LIBNAME "libiodbc.dylib"
232  #else
233  /* Currently we default to UnixODBC, which uses libodbc.so. If this
234  fails, we fall back to the next-most-common one, iODBC. If you're
235  using something else, you'll need to change the entry below to
236  specify your library name */
237  #define ODBC_LIBNAME "libodbc.so"
238  #define ODBC_ALT_LIBNAME "libiodbc.so"
239  #endif /* Mac OS X vs. other Unixen */
240 #endif /* System-specific ODBC library names */
241 
242 /* Dynamically load and unload any necessary DBMS libraries */
243 
244 CHECK_RETVAL \
245 int dbxInitODBC( void )
246  {
247 #ifdef __WIN16__
248  UINT errorMode;
249 #endif /* __WIN16__ */
250 
251  /* If the ODBC module is already linked in, don't do anything */
252  if( hODBC != NULL_INSTANCE )
253  return( CRYPT_OK );
254 
255  /* Obtain a handle to the module containing the ODBC functions */
256 #if defined( __WIN16__ )
257  errorMode = SetErrorMode( SEM_NOOPENFILEERRORBOX );
258  hODBC = LoadLibrary( ODBC_LIBNAME );
259  SetErrorMode( errorMode );
260  if( hODBC < HINSTANCE_ERROR )
261  {
262  hODBC = NULL_INSTANCE;
263  return( CRYPT_ERROR );
264  }
265 #elif defined( __UNIX__ ) && !defined( __APPLE__ )
266  if( ( hODBC = DynamicLoad( ODBC_LIBNAME ) ) == NULL_INSTANCE && \
267  ( hODBC = DynamicLoad( ODBC_ALT_LIBNAME ) ) == NULL_INSTANCE )
268  return( CRYPT_ERROR );
269 #else
270  if( ( hODBC = DynamicLoad( ODBC_LIBNAME ) ) == NULL_INSTANCE )
271  return( CRYPT_ERROR );
272 #endif /* __WIN32__ */
273 
274  /* Now get pointers to the functions */
275  pSQLAllocHandle = ( SQLALLOCHANDLE ) DynamicBind( hODBC, "SQLAllocHandle" );
276  pSQLBindParameter = ( SQLBINDPARAMETER ) DynamicBind( hODBC, "SQLBindParameter" );
277  pSQLCloseCursor = ( SQLCLOSECURSOR ) DynamicBind( hODBC, "SQLCloseCursor" );
278  pSQLConnect = ( SQLCONNECT ) DynamicBind( hODBC, "SQLConnect" );
279  pSQLDisconnect = ( SQLDISCONNECT ) DynamicBind( hODBC, "SQLDisconnect" );
280  pSQLEndTran = ( SQLENDTRAN ) DynamicBind( hODBC, "SQLEndTran" );
281  pSQLExecDirect = ( SQLEXECDIRECT ) DynamicBind( hODBC, "SQLExecDirect" );
282  pSQLExecute = ( SQLEXECUTE ) DynamicBind( hODBC, "SQLExecute" );
283  pSQLFetch = ( SQLFETCH ) DynamicBind( hODBC, "SQLFetch" );
284  pSQLFreeHandle = ( SQLFREEHANDLE ) DynamicBind( hODBC, "SQLFreeHandle" );
285  pSQLGetData = ( SQLGETDATA ) DynamicBind( hODBC, "SQLGetData" );
286  pSQLGetDiagRec = ( SQLGETDIAGREC ) DynamicBind( hODBC, "SQLGetDiagRec" );
287  pSQLGetInfo = ( SQLGETINFO ) DynamicBind( hODBC, "SQLGetInfo" );
288  pSQLGetStmtAttr = ( SQLGETSTMTATTR ) DynamicBind( hODBC, "SQLGetStmtAttr" );
289  pSQLGetTypeInfo = ( SQLGETTYPEINFO ) DynamicBind( hODBC, "SQLGetTypeInfo" );
290  pSQLPrepare = ( SQLPREPARE ) DynamicBind( hODBC, "SQLPrepare" );
291  pSQLRowCount = ( SQLROWCOUNT_FN ) DynamicBind( hODBC, "SQLRowCount" );
292  pSQLSetConnectAttr = ( SQLSETCONNECTATTR ) DynamicBind( hODBC, "SQLSetConnectAttr" );
293  pSQLSetEnvAttr = ( SQLSETENVATTR ) DynamicBind( hODBC, "SQLSetEnvAttr" );
294  pSQLSetStmtAttr = ( SQLSETSTMTATTR ) DynamicBind( hODBC, "SQLSetStmtAttr" );
295 
296  /* Make sure that we got valid pointers for every ODBC function */
297  if( pSQLAllocHandle == NULL || pSQLBindParameter == NULL ||
298  pSQLCloseCursor == NULL || pSQLConnect == NULL ||
299  pSQLDisconnect == NULL || pSQLEndTran == NULL ||
300  pSQLExecDirect == NULL || pSQLExecute == NULL ||
301  pSQLFetch == NULL || pSQLFreeHandle == NULL ||
302  pSQLGetData == NULL || pSQLGetDiagRec == NULL ||
303  pSQLGetInfo == NULL || pSQLGetStmtAttr == NULL ||
304  pSQLGetTypeInfo == NULL || pSQLPrepare == NULL ||
305  pSQLSetConnectAttr == NULL || pSQLSetEnvAttr == NULL ||
306  pSQLSetStmtAttr == NULL )
307  {
308  /* Free the library reference and reset the handle */
309  DynamicUnload( hODBC );
310  hODBC = NULL_INSTANCE;
311  return( CRYPT_ERROR );
312  }
313 
314  return( CRYPT_OK );
315  }
316 
317 void dbxEndODBC( void )
318  {
319  if( hODBC != NULL_INSTANCE )
320  DynamicUnload( hODBC );
321  hODBC = NULL_INSTANCE;
322  }
323 #else
324 
325 CHECK_RETVAL \
326 int dbxInitODBC( void )
327  {
328  return( CRYPT_OK );
329  }
330 
331 void dbxEndODBC( void )
332  {
333  }
334 #endif /* DYNAMIC_LOAD */
335 
336 /****************************************************************************
337 * *
338 * Utility Routines *
339 * *
340 ****************************************************************************/
341 
342 /* Get information on an ODBC error. The statement handle is specified as a
343  distinct parameter because it may be an ephemeral handle not part of the
344  state information data */
345 
347 static int getErrorInfo( INOUT DBMS_STATE_INFO *dbmsInfo,
348  IN_ENUM( SQL_ERRLVL ) const SQL_ERRLVL_TYPE errorLevel,
349  const SQLHSTMT hStmt,
350  IN_ERROR const int defaultStatus )
351  {
352 #ifdef USE_ERRMSGS
353  ERROR_INFO *errorInfo = &dbmsInfo->errorInfo;
354 #endif /* USE_ERRMSGS */
355  char szSqlState[ SQL_SQLSTATE_SIZE + 8 ];
356  char errorString[ MAX_ERRMSG_SIZE + 8 ];
357  SQLHANDLE handle;
358  SQLINTEGER dwNativeError = 0;
359  SQLSMALLINT handleType, errorStringLength;
360  SQLRETURN sqlStatus;
361 
362  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
363 
364  REQUIRES( errorLevel == SQL_ERRLVL_STMT || \
365  errorLevel == SQL_ERRLVL_DBC || \
366  errorLevel == SQL_ERRLVL_ENV );
367  REQUIRES( cryptStatusError( defaultStatus ) );
368 
369  /* Set up the handle information for the diagnostic information that we
370  want to retrieve */
371  switch( errorLevel )
372  {
373  case SQL_ERRLVL_STMT:
374  handleType = SQL_HANDLE_STMT;
375  handle = hStmt;
376  break;
377 
378  case SQL_ERRLVL_DBC:
379  handleType = SQL_HANDLE_DBC;
380  handle = dbmsInfo->hDbc;
381  break;
382 
383  case SQL_ERRLVL_ENV:
384  handleType = SQL_HANDLE_ENV;
385  handle = dbmsInfo->hEnv;
386  break;
387 
388  default:
389  retIntError();
390  }
391 
392  /* Get the ODBC error information at the most detailed level that we can
393  manage */
394  sqlStatus = SQLGetDiagRec( handleType, handle, 1, szSqlState,
395  &dwNativeError, errorString, MAX_ERRMSG_SIZE,
396  &errorStringLength );
397  if( !sqlStatusOK( sqlStatus ) && errorLevel == SQL_ERRLVL_STMT )
398  {
399  /* If we couldn't get information at the statement-handle level, try
400  again at the connection handle level */
401  sqlStatus = SQLGetDiagRec( SQL_HANDLE_DBC, dbmsInfo->hDbc, 1,
402  szSqlState, &dwNativeError,
403  errorString, MAX_ERRMSG_SIZE,
404  &errorStringLength );
405  }
406  if( !sqlStatusOK( sqlStatus ) )
407  {
408  DEBUG_DIAG(( "Couldn't read error information from database "
409  "backend" ));
410  assert( DEBUG_WARN ); /* Catch this if it ever occurs */
411  setErrorString( errorInfo,
412  "Couldn't get error information from database "
413  "backend", 52 );
414  return( CRYPT_ERROR_READ );
415  }
416 
417  /* In some (rare) cases SQLGetDiagRec() can return an empty error string
418  with only szSqlState set, in which case we clear the error string */
419  if( errorStringLength > 0 )
420  {
421  setErrorString( errorInfo, errorString, errorStringLength );
422  }
423  else
424  clearErrorString( errorInfo );
425 
426  /* Check for a not-found error status. We can also get an sqlStatus of
427  SQL_NO_DATA with SQLSTATE set to "00000" and the error message string
428  empty in some cases, in which case we provide our own error string */
429  if( !memcmp( szSqlState, "S0002", 5 ) || /* ODBC 2.x */
430  !memcmp( szSqlState, "42S02", 5 ) || /* ODBC 3.x */
431  ( !memcmp( szSqlState, "00000", 5 ) && \
432  sqlStatus == SQL_NO_DATA ) )
433  {
434  /* Make sure that the caller gets a sensible error message if they
435  try to examine the extended error information */
436  if( errorStringLength <= 0 )
437  setErrorString( errorInfo, "No data found", 13 );
438 
439  return( CRYPT_ERROR_NOTFOUND );
440  }
441 
442  /* When we're trying to create a new keyset, there may already be one
443  present giving an S0001/42S01 (table already exists) or S0011/42S11
444  (index already exists) error . We could check for the table by doing
445  a dummy read, but it's easier to just try the update anyway and
446  convert the error code to the correct value here if there's a
447  problem */
448  if( !memcmp( szSqlState, "S0001", 5 ) ||
449  !memcmp( szSqlState, "S0011", 5 ) || /* ODBC 2.x */
450  !memcmp( szSqlState, "42S01", 5 ) ||
451  !memcmp( szSqlState, "42S11", 5 ) ) /* ODBX 3.x */
452  return( CRYPT_ERROR_DUPLICATE );
453 
454  /* This one is a bit odd: An integrity constraint violation occurred,
455  which means (among other things) that an attempt was made to write a
456  duplicate value to a column constrained to contain unique values. It
457  can also include things like writing a NULL value to a column
458  constrained to be NOT NULL, but this wouldn't normally happen so we
459  can convert this one to a duplicate data error */
460  if( !memcmp( szSqlState, "23000", 5 ) )
461  return( CRYPT_ERROR_DUPLICATE );
462 
463  return( defaultStatus );
464  }
465 
466 /* Rewrite the SQL query to handle the back-end specific blob and date type,
467  and to work around problems with some back-end types (and we're
468  specifically talking Access here) */
469 
470 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 6 ) ) \
471 static int rewriteString( OUT_BUFFER( stringMaxLength, \
472  *stringLength ) char *string,
473  IN_LENGTH_SHORT const int stringMaxLength,
475  IN_LENGTH_SHORT const int subStringLength,
476  IN_LENGTH_SHORT const int origStringLength,
477  IN_BUFFER( newSubStringLength ) \
478  const char *newSubString,
479  IN_LENGTH_SHORT const int newSubStringLength )
480  {
481  const int remainder = origStringLength - subStringLength;
482  const int newStringLength = newSubStringLength + remainder;
483 
484  assert( isWritePtr( string, stringMaxLength ) );
485  assert( isReadPtr( newSubString, newSubStringLength ) );
486 
487  REQUIRES( stringMaxLength > 0 && stringMaxLength < MAX_INTLENGTH_SHORT );
488  REQUIRES( subStringLength > 0 && \
489  subStringLength < origStringLength && \
490  subStringLength < MAX_INTLENGTH_SHORT );
491  REQUIRES( origStringLength > 0 && \
492  origStringLength <= stringMaxLength && \
493  origStringLength < MAX_INTLENGTH_SHORT );
494  REQUIRES( newSubStringLength > 0 && \
495  newSubStringLength < MAX_INTLENGTH_SHORT );
496 
497  /* Clear return value */
498  *stringLength = 0;
499 
500  /* Make sure that the parameters are in order and there's room to
501  rewrite the string */
502  ENSURES( remainder > 0 && newStringLength > 0 && \
503  newStringLength < stringMaxLength );
504 
505  /* Open/close up a gap and replace the existing substring with the new
506  one:
507 
508  origStringLength
509  |<----------- string -------------->|
510  +---------------+-------------------+-----------+
511  |///////////////|...................| |
512  +---------------+-------------------+-----------+
513  |<- subString ->| |
514  stringMaxLength */
515  REQUIRES( rangeCheck( newSubStringLength, remainder, stringMaxLength ) );
516  memmove( string + newSubStringLength, string + subStringLength,
517  remainder );
518  memcpy( string, newSubString, newSubStringLength );
519  *stringLength = newSubStringLength - subStringLength;
520 
521  return( CRYPT_OK );
522  }
523 
524 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
525 static int convertQuery( INOUT DBMS_STATE_INFO *dbmsInfo,
526  OUT_BUFFER( queryMaxLen, *queryLength ) char *query,
527  IN_LENGTH_SHORT_MIN( 16 ) const int queryMaxLen,
528  OUT_LENGTH_SHORT_Z int *queryLength,
529  IN_BUFFER( commandLength ) const char *command,
530  IN_LENGTH_SHORT const int commandLength )
531  {
532  int currentLength = commandLength;
533  int offset, length, status;
534 
535  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
536  assert( isWritePtr( query, queryMaxLen ) );
537  assert( isReadPtr( command, commandLength ) );
538 
539  REQUIRES( queryMaxLen >= 16 && queryMaxLen < MAX_INTLENGTH_SHORT );
540  REQUIRES( commandLength > 0 && commandLength < queryMaxLen && \
541  commandLength < MAX_INTLENGTH_SHORT );
542 
543  /* Clear return value */
544  *queryLength = 0;
545 
546  /* Copy the SQL command across to the query buffer */
547  memcpy( query, command, commandLength );
548 
549  /* If it's a CREATE TABLE command rewrite the blob and date types to the
550  appropriate values for the database back-end */
551  if( !strCompare( command, "CREATE TABLE", 12 ) )
552  {
553  offset = strFindStr( query, currentLength, " BLOB", 5 );
554  if( offset > 0 )
555  {
556  offset++; /* Skip space before blob name */
557 
558  status = rewriteString( query + offset, queryMaxLen - offset,
559  &length, 4, currentLength - offset,
560  dbmsInfo->blobName,
561  dbmsInfo->blobNameLength );
562  if( cryptStatusError( status ) )
563  return( status );
564  currentLength += length;
565  }
566  offset = strFindStr( query, currentLength, " DATETIME", 9 );
567  if( offset > 0 && \
568  !( dbmsInfo->dateTimeNameLength == 8 && \
569  !strCompare( dbmsInfo->dateTimeName, "DATETIME", 8 ) ) )
570  {
571  offset++; /* Skip space before date/time name */
572  status = rewriteString( query + offset, queryMaxLen - offset,
573  &length, 8, currentLength - offset,
574  dbmsInfo->dateTimeName,
575  dbmsInfo->dateTimeNameLength );
576  if( cryptStatusError( status ) )
577  return( status );
578  currentLength += length;
579  }
580  }
581 
582  /* If it's not one of the back-ends that require special-case handling,
583  we're done */
584  switch( dbmsInfo->backendType )
585  {
586  case DBMS_ACCESS:
587  /* If it's not a SELECT/DELETE with wildcards used, there's
588  nothing to do */
589  if( ( strCompare( query, "SELECT", 6 ) && \
590  strCompare( query, "DELETE", 6 ) ) || \
591  strFindStr( query, currentLength, " LIKE ", 6 ) <= 7 )
592  {
593  *queryLength = currentLength;
594  return( CRYPT_OK );
595  }
596  break;
597 
598  case DBMS_INTERBASE:
599  /* If it's not a CREATE TABLE/INSERT/DELETE/SELECT with the
600  'type' column involved, there's nothing to do */
601  if( strCompare( query, "CREATE TABLE", 12 ) && \
602  strCompare( query, "SELECT", 6 ) && \
603  strCompare( query, "DELETE", 6 ) && \
604  strCompare( query, "INSERT", 6 ) )
605  {
606  *queryLength = currentLength;
607  return( CRYPT_OK );
608  }
609  if( strFindStr( query, currentLength, " type ", 6 ) <= 7 )
610  {
611  *queryLength = currentLength;
612  return( CRYPT_OK );
613  }
614  break;
615 
616  default:
617  /* Currently no other back-ends need special handling */
618  *queryLength = currentLength;
619  return( CRYPT_OK );
620  }
621 
622  /* Unlike everything else in the known universe Access uses * and ?
623  instead of the standard SQL wildcards so if we find a LIKE ... %
624  we rewrite the % as a * */
625  if( ( dbmsInfo->backendType == DBMS_ACCESS ) && \
626  ( offset = strFindStr( query, currentLength, " LIKE ", 6 ) ) > 0 )
627  {
628  int i;
629 
630  /* Search up to 6 characters ahead for a wildcard and replace it
631  with the one needed by Access if we find it. We search 6 chars
632  ahead because the higher-level SQL code uses expressions like
633  "SELECT .... WHERE foo LIKE '--%'", which is 5 chars plus one as
634  a safety margin */
635  for( i = offset + 7; i < offset + 11 && i < currentLength; i++ )
636  {
637  if( query[ i ] == '%' )
638  query[ i ] = '*';
639  }
640  }
641 
642  /* Interbase treats TYPE as a reserved word so we can't use 'type' for a
643  column name */
644  if( ( dbmsInfo->backendType == DBMS_INTERBASE ) && \
645  ( offset = strFindStr( query, currentLength, " type ", 6 ) ) > 0 )
646  {
647  offset++; /* Skip space before type name */
648  status = rewriteString( query + offset, queryMaxLen - offset,
649  &length, 4, currentLength - offset,
650  "ctype", 5 );
651  if( cryptStatusError( status ) )
652  return( status );
653  currentLength += length;
654  }
655 
656  *queryLength = currentLength;
657  return( CRYPT_OK );
658  }
659 
660 /* Bind parameters for a query/update. The caller has to supply the bound
661  data storage since it still has to exist later on when the query is
662  executed */
663 
664 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3, 4 ) ) \
665 static int bindParameters( const SQLHSTMT hStmt,
667  const BOUND_DATA *boundData,
668  INOUT BOUND_DATA_STATE *boundDataState,
669  INOUT DBMS_STATE_INFO *dbmsInfo )
670  {
671  SQLUSMALLINT paramNo = 1;
672  int i;
673 
674  assert( isReadPtr( boundData, \
675  sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );
676  assert( isWritePtr( boundDataState, sizeof( BOUND_DATA_STATE ) ) );
677  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
678 
679  /* Bind in any necessary parameters to the hStmt */
680  for( i = 0; i < BOUND_DATA_MAXITEMS && \
681  boundData[ i ].type != BOUND_DATA_NONE; i++ )
682  {
683  const BOUND_DATA *boundDataPtr = &boundData[ i ];
684  SQLSMALLINT valueType, parameterType;
685  SQLRETURN sqlStatus;
686 
687  if( boundDataPtr->type == BOUND_DATA_TIME )
688  {
689  SQL_TIMESTAMP_STRUCT *timestampStorage = \
690  &boundDataState->timestampStorage;
691  struct tm timeInfo, *timeInfoPtr = &timeInfo;
692 
693  REQUIRES( boundDataPtr->dataLength == sizeof( time_t ) );
694 
695  /* Sanity check the input parameters */
696  timeInfoPtr = gmTime_s( boundDataPtr->data, timeInfoPtr );
697  if( timeInfoPtr == NULL )
698  return( CRYPT_ERROR_BADDATA );
699 
700  /* Bind in the date. The handling of the ColumnSize parameter
701  is ugly, this value should be implicit in the underlying data
702  type but a small number of back-ends (e.g. ones derived from
703  the Sybase 4.2 code line, which includes the current Sybase
704  and SQL Server) may support multiple time representations and
705  require an explicit length indicator to decide which one they
706  should use. This means that we have to provide an explicit
707  length value as a hint to the driver, see the comment in
708  getDatatypeInfo() for how this is obtained */
709  memset( timestampStorage, 0, sizeof( SQL_TIMESTAMP_STRUCT ) );
710  timestampStorage->year = ( SQLSMALLINT ) ( timeInfoPtr->tm_year + 1900 );
711  timestampStorage->month = ( SQLUSMALLINT ) ( timeInfoPtr->tm_mon + 1 );
712  timestampStorage->day = ( SQLUSMALLINT ) timeInfoPtr->tm_mday;
713  timestampStorage->hour = ( SQLUSMALLINT ) timeInfoPtr->tm_hour;
714  timestampStorage->minute = ( SQLUSMALLINT ) timeInfoPtr->tm_min;
715  timestampStorage->second = ( SQLUSMALLINT ) timeInfoPtr->tm_sec;
716  sqlStatus = SQLBindParameter( hStmt, paramNo++, SQL_PARAM_INPUT,
717  SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP,
718  dbmsInfo->dateTimeNameColSize, 0,
719  timestampStorage, 0, NULL );
720  if( !sqlStatusOK( sqlStatus ) )
721  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
723  continue;
724  }
725 
726  assert( ( boundDataPtr->dataLength == 0 ) || \
727  isReadPtr( boundDataPtr->data, boundDataPtr->dataLength ) );
728 
729  REQUIRES( boundDataPtr->type == BOUND_DATA_STRING || \
730  boundDataPtr->type == BOUND_DATA_BLOB );
731  REQUIRES( dbmsInfo->hasBinaryBlobs || \
732  ( !dbmsInfo->hasBinaryBlobs && \
733  boundDataPtr->type == BOUND_DATA_STRING ) );
734  REQUIRES( ( boundDataPtr == NULL ) || \
735  ( boundDataPtr != NULL && \
736  ( boundDataPtr->data == NULL && \
737  boundDataPtr->dataLength == 0 ) || \
738  ( boundDataPtr->data != NULL && \
739  boundDataPtr->dataLength > 0 &&
740  boundDataPtr->dataLength < MAX_INTLENGTH_SHORT ) ) );
741  /* Bound data of { NULL, 0 } denotes a null parameter */
742 
743  /* Null parameters have to be handled specially. Note that we have
744  to set the ColumnSize parameter (no.6) to a nonzero value (even
745  though it's ignored, since this is a null parameter) otherwise
746  some drivers will complain about an "Invalid precision value" */
747  if( boundDataPtr->dataLength <= 0 )
748  {
749  static const SQLINTEGER nullDataValue = SQL_NULL_DATA;
750 
751  sqlStatus = SQLBindParameter( hStmt, paramNo++, SQL_PARAM_INPUT,
752  SQL_C_CHAR, SQL_C_CHAR, 1, 0, NULL, 0,
753  ( SQLINTEGER * ) &nullDataValue );
754  if( !sqlStatusOK( sqlStatus ) )
755  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
757  continue;
758  }
759 
760  /* Bind in the query data */
761  if( boundDataPtr->type == BOUND_DATA_BLOB )
762  {
763  valueType = SQL_C_BINARY;
764  parameterType = dbmsInfo->blobType;
765  }
766  else
767  valueType = parameterType = SQL_C_CHAR;
768  boundDataState->lengthStorage[ i ] = boundDataPtr->dataLength;
769  sqlStatus = SQLBindParameter( hStmt, paramNo++, SQL_PARAM_INPUT,
770  valueType, parameterType,
771  boundDataPtr->dataLength, 0,
772  ( SQLPOINTER ) boundDataPtr->data,
773  boundDataPtr->dataLength,
774  &boundDataState->lengthStorage[ i ] );
775  if( !sqlStatusOK( sqlStatus ) )
776  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
778  }
780 
781  return( CRYPT_OK );
782  }
783 
784 /****************************************************************************
785 * *
786 * Get Database Back-end Information *
787 * *
788 ****************************************************************************/
789 
790 /* Get data type information for this data source. Since SQLGetTypeInfo()
791  returns a variable (and arbitrary) length result set we have to call
792  SQLCloseCursor() after each fetch before performing a new query */
793 
794 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
795 static int getBlobInfo( INOUT DBMS_STATE_INFO *dbmsInfo,
796  const SQLSMALLINT type,
797  OUT_LENGTH_SHORT_Z int *maxFieldSize )
798  {
799  const SQLHSTMT hStmt = dbmsInfo->hStmt[ DBMS_CACHEDQUERY_NONE ];
800  SQLRETURN sqlStatus;
801  SQLINTEGER blobNameLength, count, dummy;
802 
803  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
804 
805  /* Clear return value */
806  *maxFieldSize = 0;
807 
808  /* Check for support for the requested type and get the results of the
809  transaction. If the database doesn't support this we'll get an
810  SQL_NO_DATA status */
811  sqlStatus = SQLGetTypeInfo( hStmt, type );
812  if( sqlStatusOK( sqlStatus ) )
813  sqlStatus = SQLFetch( hStmt );
814  if( !sqlStatusOK( sqlStatus ) )
815  return( CRYPT_ERROR );
816 
817  /* Get the type name (result column 1) and column size (= maximum
818  possible field length, result column 3). We only check the second
819  return code since they both apply to the same row */
820  SQLGetData( hStmt, 1, SQL_C_CHAR, dbmsInfo->blobName,
821  CRYPT_MAX_TEXTSIZE, &blobNameLength );
822  sqlStatus = SQLGetData( hStmt, 3, SQL_C_SLONG, &count,
823  sizeof( SQLINTEGER ), &dummy );
824  SQLCloseCursor( hStmt );
825  if( !sqlStatusOK( sqlStatus ) )
826  return( CRYPT_ERROR );
827  if( dbmsInfo->backendType == DBMS_MYSQL && blobNameLength == 0 )
828  {
829  /* Some older versions of the MySQL ODBC driver don't return a
830  length value so we have to set it ourselves by taking the length
831  of the returned string. The null-termination occurs as a side-
832  effect of the buffer being initialised to zeroes */
833  blobNameLength = strlen( dbmsInfo->blobName );
834  }
835  dbmsInfo->blobNameLength = ( int ) blobNameLength;
836 #ifdef __UNIX__
837  if( dummy != sizeof( SQLINTEGER ) )
838  {
839  fprintf( stderr, "\ncryptlib: The ODBC driver is erroneously "
840  "returning a %ld-byte integer value\n when a "
841  "%d-byte SQLINTEGER value is requested, which will "
842  "overwrite\n adjacent memory locations. To fix "
843  "this you need to recompile\n with whatever "
844  "preprocessor options your ODBC header files require\n"
845  " to force the use of 64-bit ODBC data types (and "
846  "report this issue\n to the ODBC driver vendor so "
847  "that they can sync the driver and\n headers)."
848  "\n\n", ( long ) dummy, sizeof( SQLINTEGER ) );
849  }
850 #endif /* __UNIX__ */
851  *maxFieldSize = count;
852 
853  /* We've got the type information, remember the details. Postgres has
854  problems handling blobs via ODBC (or even in general) since it uses
855  its own BYTEA (byte array) type that's not really usable as an SQL
856  blob type because of weird escaping requirements when
857  sending/receiving data. In addition it requires other Postgres-
858  specific oddities like specifying 'ByteaAsLongVarBinary=1' in the
859  connect string. So even though the back-end sort-of supports blobs
860  we can't actually use them */
861  if( ( type == SQL_LONGVARBINARY ) && \
862  ( dbmsInfo->backendType != DBMS_POSTGRES ) )
863  dbmsInfo->hasBinaryBlobs = TRUE;
864  dbmsInfo->blobType = type;
865 
866  return( CRYPT_OK );
867  }
868 
870 static int getDateTimeInfo( INOUT DBMS_STATE_INFO *dbmsInfo )
871  {
872  const SQLHSTMT hStmt = dbmsInfo->hStmt[ DBMS_CACHEDQUERY_NONE ];
873  SQLRETURN sqlStatus;
874  SQLINTEGER dateTimeNameLength, columnSize, dummy;
875 
876  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
877 
878  /* The Postgres driver doesn't correctly detect the date/time type used
879  by the back-end so we have to hard-code in the actual value */
880  if( dbmsInfo->backendType == DBMS_POSTGRES )
881  {
882  memcpy( dbmsInfo->dateTimeName, "TIMESTAMP", 9 );
883  dbmsInfo->dateTimeNameLength = 9;
884  dbmsInfo->dateTimeNameColSize = 16;
885 
886  return( CRYPT_OK );
887  }
888 
889  /* Get information on the back-end's date+time data type This changed
890  from SQL_TIMESTAMP in ODBC 2.x to SQL_TYPE_TIMESTAMP in ODBC 3.x,
891  since 3.x will be more common we try the 3.x version first and if
892  that fails fall back to 2.x */
893  sqlStatus = SQLGetTypeInfo( hStmt, SQL_TYPE_TIMESTAMP );
894  if( !sqlStatusOK( sqlStatus ) )
895  {
896  DEBUG_DIAG(( "Database backend uses pre-ODBC 3.0 data types" ));
897  assert( DEBUG_WARN ); /* Warn of absenceof ODBC 3.0 types */
898  sqlStatus = SQLGetTypeInfo( hStmt, SQL_TIMESTAMP );
899  }
900  if( !sqlStatusOK( sqlStatus ) )
901  return( CRYPT_ERROR );
902 
903  /* Fetch the results of the transaction and get the type name (result
904  column 1) and column size (result column 3) */
905  sqlStatus = SQLFetch( hStmt );
906  if( !sqlStatusOK( sqlStatus ) )
907  {
908  SQLCloseCursor( hStmt );
909  return( CRYPT_ERROR );
910  }
911  sqlStatus = SQLGetData( hStmt, 1, SQL_C_CHAR, dbmsInfo->dateTimeName,
912  CRYPT_MAX_TEXTSIZE, &dateTimeNameLength );
913  if( sqlStatusOK( sqlStatus ) )
914  sqlStatus = SQLGetData( hStmt, 3, SQL_C_SLONG,
915  &dbmsInfo->dateTimeNameColSize,
916  sizeof( SQLINTEGER ), &dummy );
917  if( !sqlStatusOK( sqlStatus ) )
918  {
919  SQLCloseCursor( hStmt );
920  return( CRYPT_ERROR );
921  }
922  if( dbmsInfo->backendType == DBMS_MYSQL && dateTimeNameLength == 0 )
923  {
924  /* Some older versions of the MySQL ODBC driver don't return a
925  length value so we have to set it ourselves by taking the length
926  of the returned string. The null-termination occurs as a side-
927  effect of the buffer being initialised to zeroes */
928  dateTimeNameLength = strlen( dbmsInfo->dateTimeName );
929  }
930  dbmsInfo->dateTimeNameLength = ( int ) dateTimeNameLength;
931 
932  /* The column size argument is quite problematic because although some
933  back-ends have a fixed size for this (and usually ignore the column-
934  size parameter) others allow multiple time representations and
935  require an explicit column-size indicator to decide which one they
936  should use. The ODBC standard representation for example uses 19
937  chars (yyyy-mm-dd hh:mm:ss) for the full date+time that we use here
938  but also allows a 16-char version without the seconds and a 20+n-char
939  version for n digits of fractional seconds. The back-end however may
940  use a completely different value, for example Oracle encodes the full
941  date+time as 7 bytes (century, year, month, day, hour, minute,
942  second). To get around this we get the first column-size value
943  (which is usually the only one available) and if this is the same as
944  the ODBC standard minimum-size column we try for more results to see
945  if the full date+time form is available, and use that if it is */
946  if( dbmsInfo->dateTimeNameColSize != 16 )
947  {
948  /* This isn't a potentially problematic column size, we're done */
949  SQLCloseCursor( hStmt );
950  return( CRYPT_OK );
951  }
952 
953  /* If the back-end has reported the short (no-seconds) ODBC-default
954  format, see whether it'll support the longer (with seconds) format
955  instead */
956  sqlStatus = SQLFetch( hStmt );
957  if( !sqlStatusOK( sqlStatus ) )
958  {
959  SQLCloseCursor( hStmt );
960  return( CRYPT_ERROR );
961  }
962  sqlStatus = SQLGetData( hStmt, 3, SQL_C_SLONG, &columnSize,
963  sizeof( SQLINTEGER ), &dummy );
964  if( sqlStatusOK( sqlStatus ) && columnSize == 19 )
965  dbmsInfo->dateTimeNameColSize = columnSize;
966  SQLCloseCursor( hStmt );
967 
968  return( CRYPT_OK );
969  }
970 
971 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
972 static int getDatatypeInfo( INOUT DBMS_STATE_INFO *dbmsInfo,
973  OUT_FLAGS_Z( DBMS ) int *featureFlags )
974  {
975 #ifdef USE_ERRMSGS
976  ERROR_INFO *errorInfo = &dbmsInfo->errorInfo;
977 #endif /* USE_ERRMSGS */
978  const SQLHSTMT hStmt = dbmsInfo->hStmt[ DBMS_CACHEDQUERY_NONE ];
979  SQLRETURN sqlStatus;
980  SQLSMALLINT bufLen;
981  SQLUSMALLINT transactBehaviour;
982  SQLINTEGER attrLength;
983  SQLUINTEGER privileges;
984  char buffer[ 8 + 8 ];
985  int maxBlobSize, status;
986 
987  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
988  assert( isWritePtr( featureFlags, sizeof( int ) ) );
989 
990  /* Clear return value */
991  *featureFlags = DBMS_FEATURE_FLAG_NONE;
992 
993  /* First we see what the back-end's blob data type is. Usually it'll
994  be binary blobs, if that doesn't work we try for char blobs */
995  status = getBlobInfo( dbmsInfo, SQL_LONGVARBINARY, &maxBlobSize );
996  if( cryptStatusError( status ) )
997  status = getBlobInfo( dbmsInfo, SQL_LONGVARCHAR, &maxBlobSize );
998  if( cryptStatusError( status ) )
999  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1000  CRYPT_ERROR_OPEN ) );
1001  if( dbmsInfo->hasBinaryBlobs )
1002  *featureFlags |= DBMS_FEATURE_FLAG_BINARYBLOBS;
1003 
1004  /* If we couldn't get a blob type or the type is too short to use,
1005  report it back as a database open failure */
1006  if( maxBlobSize < MAX_ENCODED_CERT_SIZE )
1007  {
1008  char errorMessage[ 128 + 8 ];
1009  int errorMessageLength;
1010 
1011  errorMessageLength = \
1012  sprintf_s( errorMessage, 128,
1013  "Database blob type can only store %d bytes, we need "
1014  "at least %d", maxBlobSize, MAX_ENCODED_CERT_SIZE );
1015  setErrorString( errorInfo, errorMessage, errorMessageLength );
1016  return( CRYPT_ERROR_OPEN );
1017  }
1018 
1019  /* Sanity check, make sure that the source can return the required
1020  amount of data. A number of data sources don't support this
1021  attribute (it's mostly meant to be set by applications rather than
1022  being read, and is intended to be used to reduce network traffic) and
1023  in addition the maximum query size is pretty short (the longest is a
1024  few hundred bytes for the table creation commands) so we don't worry
1025  if it's not available. In addition to the maximum-size check we also
1026  have to perform a minimum-size check since a value of zero is used to
1027  indicate no length limit */
1028  sqlStatus = SQLGetStmtAttr( hStmt, SQL_ATTR_MAX_LENGTH,
1029  ( SQLPOINTER ) &attrLength, SQL_IS_INTEGER,
1030  NULL );
1031  if( sqlStatusOK( sqlStatus ) && \
1032  attrLength > 0 && attrLength < MAX_SQL_QUERY_SIZE )
1033  {
1034  char errorMessage[ 128 + 8 ];
1035  int errorMessageLength;
1036 
1037  errorMessageLength = \
1038  sprintf_s( errorMessage, 128,
1039  "Database back-end can only transmit %d bytes per "
1040  "message, we need at least %d", attrLength,
1042  setErrorString( errorInfo, errorMessage, errorMessageLength );
1043  return( CRYPT_ERROR_OPEN );
1044  }
1045 
1046  /* Now do the same thing for the date+time data type */
1047  status = getDateTimeInfo( dbmsInfo );
1048  if( cryptStatusError( status ) )
1049  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1050  CRYPT_ERROR_OPEN ) );
1051 
1052  /* Determine whether we can write to the database (result = 'Y') or not
1053  (result = 'N') */
1054  sqlStatus = SQLGetInfo( dbmsInfo->hDbc, SQL_DATA_SOURCE_READ_ONLY,
1055  buffer, 8, &bufLen );
1056  if( sqlStatusOK( sqlStatus ) && *buffer == 'Y' )
1057  *featureFlags |= DBMS_FEATURE_FLAG_READONLY;
1058 
1059  /* Determine whether GRANT/REVOKE capabilities are available. This gets
1060  a bit messy because it only specifies which extended GRANT/REVOKE
1061  options are available rather than whether GRANT/REVOKE is available
1062  at all. To handle this we treat GRANT/REVOKE as being available if
1063  any information is returned (SQL Server typically returns only
1064  SQL_SG_WITH_GRANT_OPTION while other sources like DB2, Postgres, and
1065  Sybase return the correct set of flags) and not available if nothing
1066  is returned (Access, dBASE, Paradox, etc). To make things especially
1067  challenging, Informix returns nothing for SQL_SQL92_GRANT but does
1068  return something for SQL_SQL92_REVOKE so we have to check both and
1069  allow GRANT/REVOKE if either test positive */
1070  sqlStatus = SQLGetInfo( dbmsInfo->hDbc, SQL_SQL92_GRANT,
1071  ( SQLPOINTER ) &privileges,
1072  sizeof( SQLUINTEGER ), &bufLen );
1073  if( sqlStatusOK( sqlStatus ) && privileges )
1074  *featureFlags |= DBMS_FEATURE_FLAG_PRIVILEGES;
1075  sqlStatus = SQLGetInfo( dbmsInfo->hDbc, SQL_SQL92_REVOKE,
1076  ( SQLPOINTER ) &privileges,
1077  sizeof( SQLUINTEGER ), &bufLen );
1078  if( sqlStatusOK( sqlStatus ) && privileges )
1079  *featureFlags |= DBMS_FEATURE_FLAG_PRIVILEGES;
1080 
1081  /* Check how the back-end reacts to commit/rollback operations. If
1082  transactions are destructive (that is, prepared statements are
1083  cleared when a commit/rollback is performed) we have to clear the
1084  hStmtPrepared[] array to indicate that all statements have to be
1085  re-prepared. Fortunately this is quite rare, both because most
1086  back-ends don't do this (for virtually all ODBC-accessible data
1087  sources (SQL Server, Access, dBASE, Paradox, etc etc) the behaviour
1088  is SQL_CB_CLOSE, meaning that the currently active cursor is closed
1089  but there's no need to call SQLPrepare() again) and because
1090  transactions are used with CA certificate stores opened in read/write
1091  mode */
1092  sqlStatus = SQLGetInfo( dbmsInfo->hDbc, SQL_CURSOR_COMMIT_BEHAVIOR,
1093  &transactBehaviour, sizeof( SQLUSMALLINT ),
1094  &bufLen );
1095  if( sqlStatusOK( sqlStatus ) && transactBehaviour == SQL_CB_DELETE )
1096  {
1097  DEBUG_DIAG(( "Database uses destructive transactions" ));
1098  assert( DEBUG_WARN );
1099  dbmsInfo->transactIsDestructive = TRUE;
1100  }
1101  sqlStatus = SQLGetInfo( dbmsInfo->hDbc, SQL_CURSOR_ROLLBACK_BEHAVIOR,
1102  &transactBehaviour, sizeof( SQLUSMALLINT ),
1103  &bufLen );
1104  if( sqlStatusOK( sqlStatus ) && transactBehaviour == SQL_CB_DELETE )
1105  {
1106  DEBUG_DIAG(( "Database uses destructive transactions" ));
1107  assert( DEBUG_WARN );
1108  dbmsInfo->transactIsDestructive = TRUE;
1109  }
1110 
1111  return( CRYPT_OK );
1112  }
1113 
1114 /* Get the back-end type for this data source, which allows us to work
1115  around back-end-specific bugs and peculiarities */
1116 
1117 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1118 static int getBackendInfo( INOUT DBMS_STATE_INFO *dbmsInfo )
1119  {
1120  SQLRETURN sqlStatus;
1121  SQLSMALLINT bufLen;
1122  char buffer[ 128 + 8 ];
1123 
1124  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
1125 
1126  /* Check for various back-ends that require special-case handling */
1127  sqlStatus = SQLGetInfo( dbmsInfo->hDbc, SQL_DBMS_NAME, buffer, 128 - 1,
1128  &bufLen );
1129  if( sqlStatusOK( sqlStatus ) )
1130  {
1131  buffer[ bufLen ] = '\0'; /* Keep static source anal.tools happy */
1132  if( bufLen >= 6 && !strCompare( buffer, "Access", 6 ) )
1133  dbmsInfo->backendType = DBMS_ACCESS;
1134  if( bufLen >= 9 && !strCompare( buffer, "Interbase", 9 ) )
1135  dbmsInfo->backendType = DBMS_INTERBASE;
1136  if( bufLen >= 5 && !strCompare( buffer, "MySQL", 5 ) )
1137  dbmsInfo->backendType = DBMS_MYSQL;
1138  if( bufLen >= 12 && !strCompare( buffer, "PostgreSQL", 10 ) )
1139  dbmsInfo->backendType = DBMS_POSTGRES;
1140  }
1141 
1142  return( CRYPT_OK );
1143  }
1144 
1145 /****************************************************************************
1146 * *
1147 * Database Open/Close Routines *
1148 * *
1149 ****************************************************************************/
1150 
1151 /* Close a previously-opened ODBC connection. We have to have this before
1152  openDatabase() since it may be called by openDatabase() if the open
1153  process fails */
1154 
1155 STDC_NONNULL_ARG( ( 1 ) ) \
1156 static void closeDatabase( INOUT DBMS_STATE_INFO *dbmsInfo )
1157  {
1158  int i;
1159 
1160  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
1161 
1162  /* Commit the transaction. The default transaction mode is auto-commit
1163  so the SQLEndTran() call isn't strictly necessary, but we play it
1164  safe anyway */
1165  if( dbmsInfo->needsUpdate )
1166  {
1167  SQLEndTran( SQL_HANDLE_DBC, dbmsInfo->hDbc, SQL_COMMIT );
1168  dbmsInfo->needsUpdate = FALSE;
1169  }
1170 
1171  /* Clean up */
1172  for( i = 0; i < NO_CACHED_QUERIES; i++ )
1173  {
1174  if( dbmsInfo->hStmt[ i ] != NULL )
1175  {
1176  SQLFreeHandle( SQL_HANDLE_STMT, dbmsInfo->hStmt[ i ] );
1177  dbmsInfo->hStmtPrepared[ i ] = FALSE;
1178  dbmsInfo->hStmt[ i ] = NULL;
1179  }
1180  }
1181  SQLDisconnect( dbmsInfo->hDbc );
1182  SQLFreeHandle( SQL_HANDLE_DBC, dbmsInfo->hDbc );
1183  SQLFreeHandle( SQL_HANDLE_ENV, dbmsInfo->hEnv );
1184  dbmsInfo->hDbc = NULL;
1185  dbmsInfo->hEnv = NULL;
1186  }
1187 
1188 /* Open a connection to a data source. We don't check the return codes for
1189  many of the parameter-fiddling functions since the worst that can happen
1190  if they fail is that performance will be somewhat suboptimal and it's not
1191  worth abandoning the database open just because some obscure tweak isn't
1192  supported.
1193 
1194  In addition to the main hStmt handle we also allocate a number of
1195  additional hStmts used to contain pre-prepared, cached instances of
1196  frequently-executed queries. This means that the expensive step of
1197  parsing the SQL query, validating it against the system catalog,
1198  preparing an access plan, and optimising the plan, are only performed
1199  once on the first query rather than at every single access */
1200 
1201 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
1202 static int openDatabase( INOUT DBMS_STATE_INFO *dbmsInfo,
1203  IN_BUFFER( nameLen ) const char *name,
1204  IN_LENGTH_NAME const int nameLen,
1205  IN_ENUM_OPT( CRYPT_KEYOPT ) \
1206  const CRYPT_KEYOPT_TYPE options,
1207  OUT_FLAGS_Z( DBMS ) int *featureFlags )
1208  {
1209 #ifdef USE_ERRMSGS
1210  ERROR_INFO *errorInfo = &dbmsInfo->errorInfo;
1211 #endif /* USE_ERRMSGS */
1212  DBMS_NAME_INFO nameInfo;
1213  SQLRETURN sqlStatus;
1214  int i, status;
1215 
1216  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
1217  assert( isReadPtr( name, nameLen ) );
1218  assert( isWritePtr( featureFlags, sizeof( int ) ) );
1219 
1220  REQUIRES( nameLen >= MIN_NAME_LENGTH && \
1221  nameLen < MAX_ATTRIBUTE_SIZE );
1222  REQUIRES( options >= CRYPT_KEYOPT_NONE && options < CRYPT_KEYOPT_LAST );
1223 
1224  /* Clear return values */
1225  memset( dbmsInfo, 0, sizeof( DBMS_STATE_INFO ) );
1226  *featureFlags = DBMS_FEATURE_FLAG_NONE;
1227 
1228 #ifdef DYNAMIC_LOAD
1229  /* Make sure that the driver is bound in */
1230  if( hODBC == NULL_INSTANCE )
1231  return( CRYPT_ERROR_OPEN );
1232 #endif /* DYNAMIC_LOAD */
1233 
1234  /* Parse the data source into its individual components */
1235  status = dbmsParseName( &nameInfo, name, nameLen );
1236  if( cryptStatusError( status ) )
1237  return( status );
1238 
1239  /* Allocate environment and connection handles. Before we do anything
1240  with the environment handle we have to set the ODBC version to 3 or
1241  any succeeding calls will fail with a function sequence error. God
1242  knows why they couldn't assume a default setting of ODBC 3.x for this
1243  value when it requires an ODBC 3.x function call to get here in the
1244  first place */
1245  sqlStatus = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE,
1246  &dbmsInfo->hEnv );
1247  if( !sqlStatusOK( sqlStatus ) )
1248  {
1249  /* We can't get any error details without at least an environment
1250  handle so all that we can do is return a generic allocation error
1251  message. If we get a failure at this point (and in particular
1252  on the very first ODBC call) it's usually a sign of an incorrect
1253  ODBC install or config (on non-Windows systems where it's not
1254  part of the OS), since the ODBC driver can't initialise itself */
1255 #ifdef __WINDOWS__
1256  setErrorString( errorInfo,
1257  "Couldn't allocate database connection handle", 44 );
1258 #else
1259  setErrorString( errorInfo,
1260  "Couldn't allocate database connection handle, this "
1261  "is probably due to an incorrect ODBC driver install "
1262  "or an invalid configuration", 130 );
1263 #endif /* __WINDOWS__ */
1264  return( CRYPT_ERROR_OPEN );
1265  }
1266  sqlStatus = SQLSetEnvAttr( dbmsInfo->hEnv, SQL_ATTR_ODBC_VERSION,
1267  VALUE_TO_PTR( SQL_OV_ODBC3 ),
1268  SQL_IS_INTEGER );
1269  if( sqlStatusOK( sqlStatus ) )
1270  sqlStatus = SQLAllocHandle( SQL_HANDLE_DBC, dbmsInfo->hEnv,
1271  &dbmsInfo->hDbc );
1272  if( !sqlStatusOK( sqlStatus ) )
1273  {
1274  status = getErrorInfo( dbmsInfo, SQL_ERRLVL_ENV, SQL_NULL_HSTMT,
1275  CRYPT_ERROR_OPEN );
1276  SQLFreeHandle( SQL_HANDLE_ENV, dbmsInfo->hEnv );
1277  return( status );
1278  }
1279 
1280  /* Once everything is set up the way that we want it, try to connect to
1281  a data source and allocate a statement handle */
1282  sqlStatus = SQLConnect( dbmsInfo->hDbc,
1283  nameInfo.name, ( SQLSMALLINT ) nameInfo.nameLen,
1284  nameInfo.user, ( SQLSMALLINT ) nameInfo.userLen,
1285  nameInfo.password, ( SQLSMALLINT ) nameInfo.passwordLen );
1286  if( !sqlStatusOK( sqlStatus ) )
1287  {
1288  status = getErrorInfo( dbmsInfo, SQL_ERRLVL_DBC, SQL_NULL_HSTMT,
1289  CRYPT_ERROR_OPEN );
1290  closeDatabase( dbmsInfo );
1291  return( status );
1292  }
1293 
1294  /* Now that the connection is open, allocate the statement handles */
1295  for( i = 0; i < NO_CACHED_QUERIES && sqlStatusOK( sqlStatus ); i++ )
1296  sqlStatus = SQLAllocHandle( SQL_HANDLE_STMT, dbmsInfo->hDbc,
1297  &dbmsInfo->hStmt[ i ] );
1298  if( !sqlStatusOK( sqlStatus ) )
1299  {
1300  status = getErrorInfo( dbmsInfo, SQL_ERRLVL_DBC, SQL_NULL_HSTMT,
1301  CRYPT_ERROR_OPEN );
1302  closeDatabase( dbmsInfo );
1303  return( status );
1304  }
1305 
1306  /* Set the access mode to read-only if we can. The default is R/W, but
1307  setting it to read-only optimises transaction management */
1308  if( options == CRYPT_KEYOPT_READONLY )
1309  {
1310  ( void ) SQLSetStmtAttr( dbmsInfo->hDbc, SQL_ATTR_ACCESS_MODE,
1311  VALUE_TO_PTR( SQL_MODE_READ_ONLY ),
1312  SQL_IS_INTEGER );
1313  }
1314 
1315  /* Set the cursor type to forward-only (which should be the default
1316  anyway), concurrency to read-only if we're opening the database in
1317  read-only mode (this again should be the default), and turn off
1318  scanning for escape clauses in the SQL strings, which lets the driver
1319  pass the string directly to the data source. The latter improves
1320  both performance and (to some extent) security by reducing the
1321  chances of hostile SQL injection, or at least by requiring specially
1322  crafted back-end specific SQL rather than generic ODBC SQL to
1323  function */
1324  for( i = 0; i < NO_CACHED_QUERIES; i++ )
1325  {
1326  ( void ) SQLSetStmtAttr( dbmsInfo->hStmt[ i ], SQL_ATTR_CURSOR_TYPE,
1327  VALUE_TO_PTR( SQL_CURSOR_FORWARD_ONLY ),
1328  SQL_IS_INTEGER );
1329  if( options == CRYPT_KEYOPT_READONLY )
1330  {
1331  ( void ) SQLSetStmtAttr( dbmsInfo->hStmt[ i ],
1332  SQL_ATTR_CONCURRENCY,
1333  VALUE_TO_PTR( SQL_CONCUR_READ_ONLY ),
1334  SQL_IS_INTEGER );
1335  }
1336  ( void ) SQLSetStmtAttr( dbmsInfo->hStmt[ i ], SQL_ATTR_NOSCAN,
1337  VALUE_TO_PTR( SQL_NOSCAN_ON ),
1338  SQL_IS_INTEGER );
1339  }
1340 
1341  /* Get various driver and data source-specific information that we may
1342  need later on */
1343  status = getDatatypeInfo( dbmsInfo, featureFlags );
1344  if( cryptStatusOK( status ) )
1345  status = getBackendInfo( dbmsInfo );
1346  if( cryptStatusError( status ) )
1347  {
1348  closeDatabase( dbmsInfo );
1349  return( status );
1350  }
1351 
1352  return( CRYPT_OK );
1353  }
1354 
1355 /****************************************************************************
1356 * *
1357 * Database Read Routines *
1358 * *
1359 ****************************************************************************/
1360 
1361 /* Fetch data from a query */
1362 
1363 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 4, 6 ) ) \
1364 static int fetchData( const SQLHSTMT hStmt,
1366  char *data,
1367  IN_LENGTH_SHORT const int dataMaxLength,
1369  IN_ENUM( DBMS_QUERY ) const DBMS_QUERY_TYPE queryType,
1370  INOUT DBMS_STATE_INFO *dbmsInfo )
1371  {
1372 #ifdef USE_ERRMSGS
1373  ERROR_INFO *errorInfo = &dbmsInfo->errorInfo;
1374 #endif /* USE_ERRMSGS */
1375  const SQLSMALLINT dataType = ( SQLSMALLINT ) \
1376  ( ( dbmsInfo->hasBinaryBlobs ) ? \
1377  SQL_C_BINARY : SQL_C_CHAR );
1378  SQLRETURN sqlStatus;
1379  SQLINTEGER length;
1380 
1381  assert( ( queryType == DBMS_QUERY_CHECK && \
1382  data == NULL && dataMaxLength == 0 && dataLength == NULL ) || \
1383  ( queryType != DBMS_QUERY_CHECK && \
1384  isWritePtr( data, dataMaxLength ) && \
1385  isWritePtr( dataLength, sizeof( int ) ) ) );
1386  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
1387 
1388  REQUIRES( ( queryType == DBMS_QUERY_CHECK && \
1389  data == NULL && dataMaxLength == 0 && \
1390  dataLength == NULL ) || \
1391  ( queryType != DBMS_QUERY_CHECK && \
1392  data != NULL && dataMaxLength >= 16 && \
1393  dataMaxLength < MAX_INTLENGTH_SHORT && \
1394  dataLength != NULL ) );
1395  REQUIRES( queryType > DBMS_QUERY_NONE && \
1396  queryType < DBMS_QUERY_LAST );
1397 
1398  /* Clear return value */
1399  if( data != NULL )
1400  {
1401  memset( data, 0, min( 16, dataMaxLength ) );
1402  *dataLength = 0;
1403  }
1404 
1405  /* Get the results of the transaction */
1406  sqlStatus = SQLFetch( hStmt );
1407  if( !sqlStatusOK( sqlStatus ) )
1408  {
1409  /* If the fetch status is SQL_NO_DATA, indicating the end of the
1410  result set, we handle it specially since some drivers only return
1411  the basic error code and don't provide any further diagnostic
1412  information to be fetched by SQLGetDiagRec() */
1413  if( sqlStatus == SQL_NO_DATA )
1414  {
1415  if( queryType == DBMS_QUERY_CONTINUE )
1416  {
1417  setErrorString( errorInfo, "No more data found", 18 );
1418  }
1419  else
1420  {
1421  setErrorString( errorInfo, "No data found", 13 );
1422  }
1423  return( CRYPT_ERROR_NOTFOUND );
1424  }
1425  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1426  CRYPT_ERROR_READ ) );
1427  }
1428 
1429  /* If we're just doing a presence check we don't bother fetching data */
1430  if( queryType == DBMS_QUERY_CHECK )
1431  return( CRYPT_OK );
1432 
1433  /* Read the data */
1434  sqlStatus = SQLGetData( hStmt, 1, dataType, data, dataMaxLength,
1435  &length );
1436  if( !sqlStatusOK( sqlStatus ) )
1437  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1438  CRYPT_ERROR_READ ) );
1439  *dataLength = ( int ) length;
1440  return( CRYPT_OK );
1441  }
1442 
1443 /* Perform a transaction that returns information */
1444 
1445 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1446 static int performQuery( INOUT DBMS_STATE_INFO *dbmsInfo,
1447  IN_BUFFER_OPT( commandLength ) const char *command,
1448  IN_LENGTH_SHORT_Z const int commandLength,
1449  OUT_BUFFER_OPT( dataMaxLength, *dataLength ) \
1450  void *data,
1451  IN_LENGTH_SHORT_Z const int dataMaxLength,
1452  OUT_LENGTH_SHORT_Z int *dataLength,
1454  TYPECAST( BOUND_DATA ) const void *boundData,
1455  IN_ENUM_OPT( DBMS_CACHEDQUERY ) \
1456  const DBMS_CACHEDQUERY_TYPE queryEntry,
1457  IN_ENUM( DBMS_QUERY ) const DBMS_QUERY_TYPE queryType )
1458  {
1459  const SQLHSTMT hStmt = dbmsInfo->hStmt[ queryEntry ];
1460  BOUND_DATA_STATE boundDataState;
1461  SQLRETURN sqlStatus;
1462  int status;
1463 
1464  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
1465  assert( ( command == NULL && commandLength == 0 && \
1466  ( queryType == DBMS_QUERY_CONTINUE || \
1467  queryType == DBMS_QUERY_CANCEL ) ) || \
1468  isReadPtr( command, commandLength ) );
1469  assert( ( data == NULL && dataMaxLength == 0 && dataLength == NULL ) || \
1470  ( isWritePtr( data, dataMaxLength ) && \
1471  isWritePtr( dataLength, sizeof( int ) ) ) );
1472  assert( ( boundData == NULL ) || \
1473  isReadPtr( boundData, \
1474  sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );
1475 
1476  REQUIRES( ( ( queryType == DBMS_QUERY_CONTINUE || \
1477  queryType == DBMS_QUERY_CANCEL ) && \
1478  command == NULL && commandLength == 0 ) || \
1479  ( ( queryType == DBMS_QUERY_START || \
1480  queryType == DBMS_QUERY_CHECK || \
1481  queryType == DBMS_QUERY_NORMAL ) && \
1482  command != NULL && \
1483  commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
1484  REQUIRES( ( data == NULL && dataMaxLength == 0 && \
1485  dataLength == NULL ) || \
1486  ( data != NULL && dataMaxLength >= 16 && \
1487  dataMaxLength < MAX_INTLENGTH_SHORT && \
1488  dataLength != NULL ) );
1489  REQUIRES( queryEntry >= DBMS_CACHEDQUERY_NONE && \
1490  queryEntry < DBMS_CACHEDQUERY_LAST );
1491  REQUIRES( queryType > DBMS_QUERY_NONE && \
1492  queryType < DBMS_QUERY_LAST );
1493 
1494  /* Clear return value */
1495  if( dataLength != NULL )
1496  *dataLength = 0;
1497 
1498  /* If we're starting a new query, handle the query initialisation and
1499  parameter binding */
1500  if( queryType == DBMS_QUERY_START || \
1501  queryType == DBMS_QUERY_CHECK || \
1502  queryType == DBMS_QUERY_NORMAL )
1503  {
1504  /* Prepare the query for execution if necessary. The entry at
1505  position DBMS_CACHEDQUERY_NONE is never cached so the following
1506  code is always executed for this case */
1507  if( !dbmsInfo->hStmtPrepared[ queryEntry ] )
1508  {
1509  char query[ SQL_QUERY_BUFSIZE + 8 ];
1510  int queryLength;
1511 
1512  status = convertQuery( dbmsInfo, query, SQL_QUERY_BUFSIZE,
1513  &queryLength, command, commandLength );
1514  if( cryptStatusError( status ) )
1515  return( status );
1516  sqlStatus = SQLPrepare( hStmt, query, queryLength );
1517  if( !sqlStatusOK( sqlStatus ) )
1518  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1519  CRYPT_ERROR_READ ) );
1520  if( queryEntry != DBMS_CACHEDQUERY_NONE )
1521  dbmsInfo->hStmtPrepared[ queryEntry ] = TRUE;
1522  }
1523 
1524  /* Bind in any query parameters that may be required */
1525  if( boundData != NULL )
1526  {
1527  status = bindParameters( hStmt, boundData, &boundDataState,
1528  dbmsInfo );
1529  if( cryptStatusError( status ) )
1530  return( status );
1531  }
1532  }
1533 
1534  switch( queryType )
1535  {
1536  case DBMS_QUERY_START:
1537  /* Execute the query */
1538  sqlStatus = SQLExecute( hStmt );
1539  if( !sqlStatusOK( sqlStatus ) )
1540  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1541  CRYPT_ERROR_READ ) );
1542 
1543  /* If we're starting an ongoing query with results to be fetched
1544  later, we're done */
1545  if( data == NULL )
1546  return( CRYPT_OK );
1547 
1548  /* Drop through to fetch the first set of results */
1549 
1550  case DBMS_QUERY_CONTINUE:
1551  assert( isWritePtr( data, dataMaxLength ) );
1552 
1553  REQUIRES( data != NULL && \
1554  dataMaxLength >= 16 && \
1555  dataMaxLength < MAX_INTLENGTH_SHORT );
1556 
1557  /* We're in the middle of a continuing query, fetch the next set
1558  of results. If we've run out of results (indicated by a not-
1559  found status) we explicitly signal to the caller that the
1560  query has completed */
1561  status = fetchData( dbmsInfo->hStmt[ queryEntry ], data,
1562  dataMaxLength, dataLength,
1563  DBMS_QUERY_CONTINUE, dbmsInfo );
1564  return( cryptStatusOK( status ) ? CRYPT_OK : \
1565  ( status == CRYPT_ERROR_NOTFOUND ) ? \
1566  CRYPT_ERROR_COMPLETE : status );
1567 
1568  case DBMS_QUERY_CANCEL:
1569  /* Cancel any outstanding requests to clear the hStmt and make
1570  it ready for re-use */
1571  SQLCloseCursor( dbmsInfo->hStmt[ queryEntry ] );
1572  return( CRYPT_OK );
1573 
1574  case DBMS_QUERY_CHECK:
1575  case DBMS_QUERY_NORMAL:
1576  /* Only return a maximum of a single row in response to a point
1577  query. This is a simple optimisation to ensure that the
1578  database client doesn't start sucking across huge amounts of
1579  data when it's not necessary */
1580  ( void ) SQLSetStmtAttr( hStmt, SQL_ATTR_MAX_ROWS,
1581  VALUE_TO_PTR( 1 ), SQL_IS_INTEGER );
1582 
1583  /* Execute the SQL statement and fetch the results */
1584  sqlStatus = SQLExecute( hStmt );
1585  if( sqlStatusOK( sqlStatus ) )
1586  {
1587  status = fetchData( hStmt, data, dataMaxLength, dataLength,
1588  queryType, dbmsInfo );
1589  SQLCloseCursor( hStmt );
1590  }
1591  else
1592  {
1593  status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1594  CRYPT_ERROR_READ );
1595  }
1596 
1597  /* Reset the statement handle's multi-row result handling */
1598  ( void ) SQLSetStmtAttr( hStmt, SQL_ATTR_MAX_ROWS,
1599  VALUE_TO_PTR( 0 ), SQL_IS_INTEGER );
1600  return( status );
1601  }
1602 
1603  retIntError();
1604  }
1605 
1606 /****************************************************************************
1607 * *
1608 * Database Write Routines *
1609 * *
1610 ****************************************************************************/
1611 
1612 /* Perform a transaction that updates the database without returning any
1613  data */
1614 
1615 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1616 static int performUpdate( INOUT DBMS_STATE_INFO *dbmsInfo,
1617  IN_BUFFER_OPT( commandLength ) const char *command,
1618  IN_LENGTH_SHORT_Z const int commandLength,
1620  TYPECAST( BOUND_DATA ) const void *boundData,
1621  IN_ENUM( DBMS_UPDATE ) \
1622  const DBMS_UPDATE_TYPE updateType )
1623  {
1624  const SQLHSTMT hStmt = dbmsInfo->hStmt[ DBMS_CACHEDQUERY_NONE ];
1625  BOUND_DATA_STATE boundDataState;
1626  SQLRETURN sqlStatus;
1627  char query[ SQL_QUERY_BUFSIZE + 8 ];
1628  int queryLength, status;
1629 
1630  assert( isWritePtr( dbmsInfo, sizeof( DBMS_STATE_INFO ) ) );
1631  assert( ( command == NULL && commandLength == 0 && \
1632  updateType == DBMS_UPDATE_ABORT ) || \
1633  isReadPtr( command, commandLength ) );
1634  assert( ( boundData == NULL ) || \
1635  isReadPtr( boundData, \
1636  sizeof( BOUND_DATA ) * BOUND_DATA_MAXITEMS ) );
1637 
1638  REQUIRES( ( updateType == DBMS_UPDATE_ABORT && \
1639  command == NULL && commandLength == 0 ) || \
1640  ( updateType != DBMS_UPDATE_ABORT && \
1641  command != NULL && \
1642  commandLength > 0 && commandLength < MAX_INTLENGTH_SHORT ) );
1643  REQUIRES( updateType > DBMS_UPDATE_NONE && \
1644  updateType < DBMS_UPDATE_LAST );
1645 
1646  /* If we're aborting a transaction, roll it back, re-enable autocommit,
1647  and clean up */
1648  if( updateType == DBMS_UPDATE_ABORT )
1649  {
1650  sqlStatus = SQLEndTran( SQL_HANDLE_DBC, dbmsInfo->hDbc,
1651  SQL_ROLLBACK );
1652  ( void ) SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
1653  VALUE_TO_PTR( SQL_AUTOCOMMIT_ON ),
1654  SQL_IS_UINTEGER );
1655  if( !sqlStatusOK( sqlStatus ) )
1656  return( getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1657  CRYPT_ERROR_WRITE ) );
1658  return( CRYPT_OK );
1659  }
1660 
1661  /* If it's the start of a transaction, turn autocommit off */
1662  if( updateType == DBMS_UPDATE_BEGIN )
1663  {
1664  ( void ) SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
1665  VALUE_TO_PTR( SQL_AUTOCOMMIT_OFF ),
1666  SQL_IS_UINTEGER );
1667  }
1668 
1669  /* Bind in any necessary parameters to the hStmt */
1670  if( boundData != NULL )
1671  {
1672  status = bindParameters( hStmt, boundData, &boundDataState,
1673  dbmsInfo );
1674  if( cryptStatusError( status ) )
1675  return( status );
1676  }
1677 
1678  /* Execute the command */
1679  status = convertQuery( dbmsInfo, query, SQL_QUERY_BUFSIZE, &queryLength,
1680  command, commandLength );
1681  if( cryptStatusError( status ) )
1682  return( status );
1683  sqlStatus = SQLExecDirect( hStmt, query, queryLength );
1684  if( !sqlStatusOK( sqlStatus ) )
1685  {
1686  /* The return status from a delete operation can be reported in
1687  several ways at the whim of the driver. Some drivers always
1688  report success even though nothing was found to delete (more
1689  common in ODBC 2.x drivers, see the code further on for the
1690  handling for this), others report a failure to delete anything
1691  with an SQL_NO_DATA status (more common in ODBC 3.x drivers).
1692  For this case we convert the overall status to a
1693  CRYPT_ERROR_NOTFOUND and update the sqlStatus as required if we
1694  need to continue */
1695  if( sqlStatus == SQL_NO_DATA && \
1696  command != NULL && commandLength >= 6 && \
1697  !strCompare( command, "DELETE", 6 ) )
1698  {
1699  status = CRYPT_ERROR_NOTFOUND;
1700  if( updateType != DBMS_UPDATE_COMMIT )
1701  return( status );
1702  }
1703  else
1704  {
1705  /* If we hit an error at this point we can only exit if we're
1706  not finishing a transaction. If we are, the commit turns
1707  into an abort further down */
1708  status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1710  if( updateType != DBMS_UPDATE_COMMIT )
1711  return( status );
1712  }
1713  }
1714  else
1715  {
1716  /* If we're performing a delete the operation will succeed even
1717  though nothing was found to delete so we make sure that we
1718  actually changed something */
1719  if( command != NULL && commandLength >= 6 && \
1720  !strCompare( command, "DELETE", 6 ) )
1721  {
1722  SQLINTEGER rowCount;
1723 
1724  sqlStatus = SQLRowCount( hStmt, &rowCount );
1725  if( !sqlStatusOK( sqlStatus ) || rowCount <= 0 )
1726  status = CRYPT_ERROR_NOTFOUND;
1727  }
1728  }
1729 
1730  /* If it's the end of a transaction, commit the transaction and turn
1731  autocommit on again */
1732  if( updateType == DBMS_UPDATE_COMMIT )
1733  {
1734  /* If we've had a failure before this point, abort, otherwise
1735  commit. The SQLSMALLINT cast is necessary (although spurious) in
1736  some development environments */
1737  sqlStatus = SQLEndTran( SQL_HANDLE_DBC, dbmsInfo->hDbc,
1738  ( SQLSMALLINT ) \
1739  ( cryptStatusError( status ) ? \
1740  SQL_ROLLBACK : SQL_COMMIT ) );
1741  if( dbmsInfo->transactIsDestructive )
1742  {
1743  int i;
1744 
1745  /* If transactions are destructive for this back-end type,
1746  invalidate all prepared statements */
1747  for( i = 0; i < NO_CACHED_QUERIES; i++ )
1748  dbmsInfo->hStmtPrepared[ i ] = FALSE;
1749  }
1750  ( void ) SQLSetConnectAttr( dbmsInfo->hDbc, SQL_ATTR_AUTOCOMMIT,
1751  VALUE_TO_PTR( SQL_AUTOCOMMIT_ON ),
1752  SQL_IS_UINTEGER );
1753  if( cryptStatusOK( status ) && !sqlStatusOK( sqlStatus ) )
1754  {
1755  status = getErrorInfo( dbmsInfo, SQL_ERRLVL_STMT, hStmt,
1757  }
1758  }
1759 
1760  return( status );
1761  }
1762 
1763 #ifndef USE_RPCAPI
1764 
1765 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1766 int initDispatchODBC( INOUT DBMS_INFO *dbmsInfo )
1767  {
1768  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
1769 
1770  dbmsInfo->openDatabaseBackend = openDatabase;
1771  dbmsInfo->closeDatabaseBackend = closeDatabase;
1772  dbmsInfo->performUpdateBackend = performUpdate;
1773  dbmsInfo->performQueryBackend = performQuery;
1774 
1775  return( CRYPT_OK );
1776  }
1777 #else
1778 
1779 /* Pull in the shared database RPC routines, renaming the generic dispatch
1780  function to the ODBC-specific one which is called directly by the
1781  marshalling code */
1782 
1783 #define processCommand( stateInfo, buffer ) \
1784  odbcProcessCommand( stateInfo, buffer )
1785 #include "dbx_rpc.c"
1786 
1787 #endif /* !USE_RPCAPI */
1788 
1789 #endif /* USE_ODBC */