cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
dbx_rpcc.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Database RPC Client Interface *
4 * Copyright Peter Gutmann 1996-2006 *
5 * *
6 ****************************************************************************/
7 
8 #include <ctype.h>
9 #include <stdarg.h>
10 #if defined( INC_ALL )
11  #include "crypt.h"
12  #include "keyset.h"
13  #include "dbms.h"
14  #include "rpc.h"
15 #else
16  #include "crypt.h"
17  #include "keyset/keyset.h"
18  #include "keyset/dbms.h"
19  #include "misc/rpc.h"
20 #endif /* Compiler-specific includes */
21 
22 #ifdef USE_DBMS
23 
24 /****************************************************************************
25 * *
26 * Network Database Interface Routines *
27 * *
28 ****************************************************************************/
29 
30 #ifdef USE_DATABASE_PLUGIN
31 
32 #ifdef USE_RPCAPI
33 
34 static void netEncodeError( BYTE *buffer, const int status )
35  {
36  putMessageType( buffer, COMMAND_RESULT, 0, 1, 0 );
38  putMessageWord( buffer + COMMAND_WORD1_OFFSET, status );
39  }
40 
41 void netProcessCommand( void *stateInfo, BYTE *buffer )
42  {
43  DBMS_STATE_INFO *dbmsInfo = ( DBMS_STATE_INFO * ) stateInfo;
44  COMMAND_INFO cmd;
45  int length, status;
46 
47  memset( &cmd, 0, sizeof( COMMAND_INFO ) );
48 
49  /* Get the messge information from the header */
50  getMessageType( buffer, cmd.type, cmd.flags,
51  cmd.noArgs, cmd.noStrArgs );
52  length = getMessageLength( buffer + COMMAND_WORDSIZE );
53  if( cmd.type == DBX_COMMAND_OPEN )
54  {
56  BYTE *bufPtr = buffer + COMMAND_FIXED_DATA_SIZE + COMMAND_WORDSIZE;
57  int nameLen;
58 
59  /* Get the length of the server name and null-terminate it */
60  nameLen = getMessageWord( bufPtr );
61  bufPtr += COMMAND_WORDSIZE;
62  bufPtr[ nameLen ] = '\0';
63 
64  /* Connect to the plugin */
67  connectInfo.name = bufPtr;
68  status = sNetConnect( &dbmsInfo->stream, STREAM_PROTOCOL_TCPIP,
69  &connectInfo, dbmsInfo->errorMessage,
70  &dbmsInfo->errorCode );
71  if( cryptStatusError( status ) )
72  {
73  netEncodeError( buffer, status );
74  return;
75  }
76  }
77 
78  /* Send the command to the plugin and read back the response */
79  status = swrite( &dbmsInfo->stream, buffer,
80  COMMAND_FIXED_DATA_SIZE + COMMAND_WORDSIZE + length );
81  if( cryptStatusOK( status ) )
82  status = sread( &dbmsInfo->stream, buffer, COMMAND_FIXED_DATA_SIZE );
83  if( !cryptStatusError( status ) )
84  {
85  /* Perform a consistency check on the returned data */
86  getMessageType( buffer, cmd.type, cmd.flags,
87  cmd.noArgs, cmd.noStrArgs );
88  length = getMessageLength( buffer + COMMAND_WORDSIZE );
89  if( !dbxCheckCommandInfo( &cmd, length ) || \
90  cmd.type != COMMAND_RESULT )
91  status = CRYPT_ERROR_BADDATA;
92  }
93  if( !cryptStatusError( status ) )
94  /* Read the rest of the message */
95  status = sread( &dbmsInfo->stream, buffer + COMMAND_FIXED_DATA_SIZE,
96  length );
97 
98  /* If it's a close command, terminate the connection to the plugin. We
99  don't do any error checking once we get this far since there's not
100  much that we can still do at this point */
101  if( cmd.type == DBX_COMMAND_CLOSE )
102  sNetDisconnect( &dbmsInfo->stream );
103  else
104  if( cryptStatusError( status ) )
105  netEncodeError( buffer, status );
106  }
107 #else
108 
109 int initDispatchNet( DBMS_INFO *dbmsInfo )
110  {
111  return( CRYPT_ERROR );
112  }
113 #endif /* USE_RPCAPI */
114 
115 #endif /* USE_DATABASE_PLUGIN */
116 
117 /****************************************************************************
118 * *
119 * Database RPC Routines *
120 * *
121 ****************************************************************************/
122 
123 /* Dispatch functions for various database types. ODBC is the native keyset
124  for Windows and (if possible) Unix, a cryptlib-native plugin is the
125  fallback for Unix, and the rest are only accessible via database network
126  plugins */
127 
128 #ifdef USE_ODBC
129  void odbcProcessCommand( void *stateInfo, BYTE *buffer );
130  #define initDispatchODBC( dbmsInfo ) \
131  ( dbmsInfo->dispatchFunction = odbcProcessCommand ) != NULL
132 #else
133  #define initDispatchODBC( dbmsInfo ) CRYPT_ERROR
134 #endif /* USE_ODBC */
135 #if defined( USE_DATABASE )
136  void databaseProcessCommand( void *stateInfo, BYTE *buffer );
137  #define initDispatchDatabase( dbmsInfo ) \
138  ( dbmsInfo->dispatchFunction = databaseProcessCommand ) != NULL
139 #else
140  #define initDispatchDatabase( dbmsInfo ) CRYPT_ERROR
141 #endif /* General database interface */
142 #ifdef USE_DATABASE_PLUGIN
143  int initDispatchNet( DBMS_INFO *dbmsInfo );
144 #else
145  #define initDispatchNet( dbmsInfo ) CRYPT_ERROR
146 #endif /* USE_DATABASE_PLUGIN */
147 
148 /* Make sure that we can fit the largest possible SQL query into the RPC
149  buffer */
150 
151 #if MAX_SQL_QUERY_SIZE + 256 >= DBX_IO_BUFSIZE
152  #error Database RPC buffer size is too small, increase DBX_IO_BUFSIZE and rebuild
153 #endif /* SQL query size larger than RPC buffer size */
154 
155 /* Dispatch data to the back-end */
156 
157 static int dispatchCommand( COMMAND_INFO *cmd, void *stateInfo,
158  DISPATCH_FUNCTION dispatchFunction )
159  {
160  COMMAND_INFO sentCmd = *cmd;
161  BYTE buffer[ DBX_IO_BUFSIZE + 8 ], *bufPtr = buffer;
162  BYTE header[ COMMAND_FIXED_DATA_SIZE + 8 ];
163  const int payloadLength = ( cmd->noArgs * COMMAND_WORDSIZE ) + \
164  ( cmd->noStrArgs * COMMAND_WORDSIZE ) + \
165  cmd->strArgLen[ 0 ] + cmd->strArgLen[ 1 ] + \
166  cmd->strArgLen[ 2 ];
167  long resultLength;
168  int i;
169 
170  assert( payloadLength + 32 < DBX_IO_BUFSIZE );
171  assert( dispatchFunction != NULL );
172 
173  /* Clear the return value */
174  memset( cmd, 0, sizeof( COMMAND_INFO ) );
175 
176  /* Write the header and message fields to the buffer */
177  putMessageType( bufPtr, sentCmd.type, sentCmd.flags,
178  sentCmd.noArgs, sentCmd.noStrArgs );
179  putMessageLength( bufPtr + COMMAND_WORDSIZE, payloadLength );
180  bufPtr += COMMAND_FIXED_DATA_SIZE;
181  for( i = 0; i < sentCmd.noArgs; i++ )
182  {
183  putMessageWord( bufPtr, sentCmd.arg[ i ] );
184  bufPtr += COMMAND_WORDSIZE;
185  }
186  for( i = 0; i < sentCmd.noStrArgs; i++ )
187  {
188  const int argLength = sentCmd.strArgLen[ i ];
189 
190  putMessageWord( bufPtr, argLength );
191  if( argLength > 0 )
192  memcpy( bufPtr + COMMAND_WORDSIZE, sentCmd.strArg[ i ],
193  argLength );
194  bufPtr += COMMAND_WORDSIZE + argLength;
195  }
196 
197  /* Send the command to the server and read back the server's message
198  header */
199  dispatchFunction( stateInfo, buffer );
200  memcpy( header, buffer, COMMAND_FIXED_DATA_SIZE );
201 
202  /* Process the fixed message header and make sure that it's valid */
203  getMessageType( header, cmd->type, cmd->flags,
204  cmd->noArgs, cmd->noStrArgs );
205  resultLength = getMessageLength( header + COMMAND_WORDSIZE );
206  if( !dbxCheckCommandInfo( cmd, resultLength ) || \
207  cmd->type != COMMAND_RESULT )
208  return( CRYPT_ERROR );
209  if( ( cmd->noStrArgs && cmd->strArgLen[ 0 ] ) && \
210  ( sentCmd.type != DBX_COMMAND_QUERY && \
211  sentCmd.type != DBX_COMMAND_GETERRORINFO ) )
212  /* Only these commands can return data */
213  return( CRYPT_ERROR );
214 
215  /* Read the rest of the server's message */
216  bufPtr = buffer + COMMAND_FIXED_DATA_SIZE;
217  for( i = 0; i < cmd->noArgs; i++ )
218  {
219  cmd->arg[ i ] = getMessageWord( bufPtr );
220  bufPtr += COMMAND_WORDSIZE;
221  }
222  for( i = 0; i < cmd->noStrArgs; i++ )
223  {
224  cmd->strArgLen[ i ] = getMessageWord( bufPtr );
225  cmd->strArg[ i ] = bufPtr + COMMAND_WORDSIZE;
226  bufPtr += COMMAND_WORDSIZE + cmd->strArgLen[ i ];
227  }
228 
229  /* The first value returned is the status code, if it's nonzero return
230  it to the caller, otherwise move the other values down */
231  if( cryptStatusError( cmd->arg[ 0 ] ) )
232  return( cmd->arg[ 0 ] );
233  assert( cryptStatusOK( cmd->arg[ 0 ] ) );
234  for( i = 1; i < cmd->noArgs; i++ )
235  cmd->arg[ i - 1 ] = cmd->arg[ i ];
236  cmd->arg[ i ] = 0;
237  cmd->noArgs--;
238 
239  /* Copy any string arg data back to the caller */
240  if( cmd->noStrArgs && cmd->strArgLen[ 0 ] )
241  {
242  const int maxBufSize = ( sentCmd.type == DBX_COMMAND_QUERY ) ? \
244  const int argIndex = sentCmd.noStrArgs;
245 
246  memcpy( sentCmd.strArg[ argIndex ], cmd->strArg[ 0 ],
247  min( cmd->strArgLen[ 0 ], maxBufSize ) );
248  cmd->strArg[ 0 ] = sentCmd.strArg[ argIndex ];
249  }
250 
251  return( CRYPT_OK );
252  }
253 
254 /* Initialise query data prior to sending it to the database back-end */
255 
256 static int initQueryData( COMMAND_INFO *cmd, const COMMAND_INFO *cmdTemplate,
257  BYTE *encodedDate, DBMS_INFO *dbmsInfo,
258  const char *command, const void *boundData,
259  const int boundDataLength, const time_t boundDate,
260  const int type )
261  {
262  int argIndex = 1;
263 
264  memcpy( cmd, cmdTemplate, sizeof( COMMAND_INFO ) );
265  cmd->arg[ 0 ] = type;
266  if( command != NULL )
267  {
268  cmd->strArg[ 0 ] = ( void * ) command;
269  cmd->strArgLen[ 0 ] = strlen( command );
270  }
271  if( boundDate > 0 )
272  {
273 #ifndef SYSTEM_64BIT
274  assert( sizeof( time_t ) <= 4 );
275 #endif /* !SYSTEM_64BIT */
276 
277  /* Encode the date as a 64-bit value */
278  memset( encodedDate, 0, 8 );
279 #ifdef SYSTEM_64BIT
280  encodedDate[ 3 ] = ( BYTE )( ( boundDate >> 32 ) & 0xFF );
281 #endif /* SYSTEM_64BIT */
282  encodedDate[ 4 ] = ( BYTE )( ( boundDate >> 24 ) & 0xFF );
283  encodedDate[ 5 ] = ( BYTE )( ( boundDate >> 16 ) & 0xFF );
284  encodedDate[ 6 ] = ( BYTE )( ( boundDate >> 8 ) & 0xFF );
285  encodedDate[ 7 ] = ( BYTE )( ( boundDate ) & 0xFF );
286  cmd->noStrArgs++;
287  cmd->strArg[ argIndex ] = encodedDate;
288  cmd->strArgLen[ argIndex++ ] = 8;
289  }
290  if( boundData != NULL )
291  {
292  /* Copy the bound data into non-ephemeral storage where it'll be
293  accessible to the back-end */
294  memcpy( dbmsInfo->boundData, boundData, boundDataLength );
295  cmd->noStrArgs++;
296  cmd->strArg[ argIndex ] = dbmsInfo->boundData;
297  cmd->strArgLen[ argIndex++ ] = boundDataLength;
298  }
299 
300  return( argIndex );
301  }
302 
303 /* Database access functions */
304 
305 static int openDatabase( DBMS_INFO *dbmsInfo, const char *name,
306  const int nameLength, const int options,
307  int *featureFlags )
308  {
309  static const COMMAND_INFO cmdTemplate = \
311  COMMAND_INFO cmd;
312  int status;
313 
314  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
315 
316  /* Dispatch the command */
317  memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
318  cmd.arg[ 0 ] = options;
319  cmd.strArg[ 0 ] = ( void * ) name;
320  cmd.strArgLen[ 0 ] = nameLength;
321  status = DISPATCH_COMMAND_DBX( cmdOpen, cmd, dbmsInfo );
322  if( cryptStatusOK( status ) && \
323  ( cmd.arg[ 0 ] & DBMS_HAS_BINARYBLOBS ) )
324  dbmsInfo->flags |= DBMS_FLAG_BINARYBLOBS;
325  return( status );
326  }
327 
328 static void closeDatabase( DBMS_INFO *dbmsInfo )
329  {
330  static const COMMAND_INFO cmdTemplate = \
332  COMMAND_INFO cmd;
333 
334  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
335 
336  /* Dispatch the command */
337  memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
338  DISPATCH_COMMAND_DBX( cmdClose, cmd, dbmsInfo );
339  }
340 
341 static void performErrorQuery( DBMS_INFO *dbmsInfo )
342  {
343  static const COMMAND_INFO cmdTemplate = \
345  COMMAND_INFO cmd;
346  int status;
347 
348  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
349 
350  /* Clear the return values */
351  memset( dbmsInfo->errorMessage, 0, MAX_ERRMSG_SIZE );
352  dbmsInfo->errorCode = 0;
353 
354  /* Dispatch the command */
355  memcpy( &cmd, &cmdTemplate, sizeof( COMMAND_INFO ) );
356  cmd.strArg[ 0 ] = dbmsInfo->errorMessage;
357  cmd.strArgLen[ 0 ] = 0;
358  status = DISPATCH_COMMAND_DBX( cmdGetErrorInfo, cmd, dbmsInfo );
359  if( cryptStatusOK( status ) )
360  {
361  dbmsInfo->errorCode = cmd.arg[ 0 ];
362  dbmsInfo->errorMessage[ cmd.strArgLen[ 0 ] ] = '\0';
363  }
364  }
365 
366 static int performUpdate( DBMS_INFO *dbmsInfo, const char *command,
367  const void *boundData, const int boundDataLength,
368  const time_t boundDate,
369  const DBMS_UPDATE_TYPE updateType )
370  {
371  static const COMMAND_INFO cmdTemplate = \
373  COMMAND_INFO cmd;
374  BYTE encodedDate[ 8 + 8 ];
375  int status;
376 
377  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
378  assert( updateType > DBMS_UPDATE_NONE && \
379  updateType < DBMS_UPDATE_LAST );
380 
381  /* If we're trying to abort a transaction that was never begun, don't
382  do anything */
383  if( updateType == DBMS_UPDATE_ABORT && \
384  !( dbmsInfo->flags & DBMS_FLAG_UPDATEACTIVE ) )
385  return( CRYPT_OK );
386 
387  /* Dispatch the command */
388  initQueryData( &cmd, &cmdTemplate, encodedDate, dbmsInfo, command,
389  boundData, boundDataLength, boundDate, updateType );
390  status = DISPATCH_COMMAND_DBX( cmdUpdate, cmd, dbmsInfo );
391  if( cryptStatusError( status ) )
392  performErrorQuery( dbmsInfo );
393  else
394  {
395  /* If we're starting or ending an update, record the update state */
396  if( updateType == DBMS_UPDATE_BEGIN )
397  dbmsInfo->flags |= DBMS_FLAG_UPDATEACTIVE;
398  if( updateType == DBMS_UPDATE_COMMIT || \
399  updateType == DBMS_UPDATE_ABORT )
400  dbmsInfo->flags &= ~DBMS_FLAG_UPDATEACTIVE;
401  }
402  return( status );
403  }
404 
405 static int performStaticUpdate( DBMS_INFO *dbmsInfo, const char *command )
406  {
407  return( performUpdate( dbmsInfo, command, NULL, 0, 0,
408  DBMS_UPDATE_NORMAL ) );
409  }
410 
411 static int performQuery( DBMS_INFO *dbmsInfo, const char *command,
412  char *data, int *dataLength, const char *queryData,
413  const int queryDataLength, const time_t queryDate,
414  const DBMS_CACHEDQUERY_TYPE queryEntry,
415  const DBMS_QUERY_TYPE queryType )
416  {
417  static const COMMAND_INFO cmdTemplate = \
419  COMMAND_INFO cmd;
420  BYTE encodedDate[ 8 + 8 ];
421  int argIndex, status;
422 
423  assert( isWritePtr( dbmsInfo, sizeof( DBMS_INFO ) ) );
424  assert( ( data == NULL && dataLength == NULL ) || \
425  isWritePtr( data, 16 ) );
426  assert( ( queryData == NULL && queryDataLength == 0 ) || \
427  ( queryDataLength > 0 && \
428  isReadPtr( queryData, queryDataLength ) ) );
429  assert( queryEntry >= DBMS_CACHEDQUERY_NONE && \
430  queryEntry < DBMS_CACHEDQUERY_LAST );
431  assert( queryType > DBMS_QUERY_NONE && queryType < DBMS_QUERY_LAST );
432 
433  /* Additional state checks: If we're starting a new query or performing
434  a point query there can't already be one active, and if we're
435  continuing or cancelling an existing query there has to be one
436  already active */
437  if( ( ( queryType == DBMS_QUERY_START || \
438  queryType == DBMS_QUERY_CHECK || \
439  queryType == DBMS_QUERY_NORMAL ) && \
440  ( dbmsInfo->flags & DBMS_FLAG_QUERYACTIVE ) ) ||
441  ( ( queryType == DBMS_QUERY_CONTINUE || \
442  queryType == DBMS_QUERY_CANCEL ) && \
443  !( dbmsInfo->flags & DBMS_FLAG_QUERYACTIVE ) ) )
444  retIntError();
445 
446  /* Clear return value */
447  if( data != NULL )
448  {
449  memset( data, 0, 16 );
450  *dataLength = 0;
451  }
452 
453  /* Dispatch the command */
454  argIndex = initQueryData( &cmd, &cmdTemplate, encodedDate, dbmsInfo,
455  command, queryData, queryDataLength,
456  queryDate, queryType );
457  cmd.arg[ 1 ] = queryEntry;
458  cmd.strArg[ argIndex ] = data;
459  cmd.strArgLen[ argIndex ] = 0;
460  cmd.noStrArgs = argIndex + 1;
461  status = DISPATCH_COMMAND_DBX( cmdQuery, cmd, dbmsInfo );
462  if( cryptStatusError( status ) )
463  {
464  performErrorQuery( dbmsInfo );
465  return( status );
466  }
467 
468  /* Update the state information based on the query that we've just
469  performed */
470  if( queryType == DBMS_QUERY_START )
471  dbmsInfo->flags |= DBMS_FLAG_QUERYACTIVE;
472  if( queryType == DBMS_QUERY_CANCEL )
473  dbmsInfo->flags &= ~DBMS_FLAG_QUERYACTIVE;
474  if( dataLength != NULL )
475  {
476  *dataLength = cmd.strArgLen[ argIndex ];
477  if( *dataLength <= 0 || *dataLength > MAX_QUERY_RESULT_SIZE )
478  {
479  assert( NOTREACHED );
480  memset( data, 0, 16 );
481  *dataLength = 0;
482  return( CRYPT_ERROR_BADDATA );
483  }
484  }
485  return( CRYPT_OK );
486  }
487 
488 static int performStaticQuery( DBMS_INFO *dbmsInfo, const char *command,
489  const DBMS_CACHEDQUERY_TYPE queryEntry,
490  const DBMS_QUERY_TYPE queryType )
491  {
492  return( performQuery( dbmsInfo, command, NULL, NULL, NULL, 0, 0,
493  queryEntry, queryType ) );
494  }
495 #endif /* USE_DBMS */