cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
dn_rw.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Certificate DN Read/Write Routines *
4 * Copyright Peter Gutmann 1996-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "cert.h"
10  #include "dn.h"
11  #include "asn1.h"
12 #else
13  #include "cert/cert.h"
14  #include "cert/dn.h"
15  #include "enc_dec/asn1.h"
16 #endif /* Compiler-specific includes */
17 
18 #ifdef USE_CERTIFICATES
19 
20 /****************************************************************************
21 * *
22 * Read a DN *
23 * *
24 ****************************************************************************/
25 
26 /* Parse an AVA. This determines the AVA type and leaves the stream pointer
27  at the start of the data value */
28 
29 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
30 static int readAVABitstring( INOUT STREAM *stream,
33  {
34  long streamPos;
35  int bitStringLength, innerTag, innerLength = DUMMY_INIT, status;
36 
37  assert( isWritePtr( stream, sizeof( STREAM ) ) );
38  assert( isWritePtr( length, sizeof( int ) ) );
39  assert( isWritePtr( stringTag, sizeof( int ) ) );
40 
41  /* Bitstrings are used for uniqueIdentifiers, however these usually
42  encapsulate something else:
43 
44  BIT STRING {
45  IA5String 'xxxxx'
46  }
47 
48  so we try and dig one level deeper to find the encapsulated string if
49  there is one. This gets a bit complicated because we have to
50  speculatively try and decode the inner content and if that fails
51  assume that it's raw bitstring data. First we read the bitstring
52  wrapper and remember where the bitstring data starts */
53  status = readBitStringHole( stream, &bitStringLength, 2, DEFAULT_TAG );
54  if( cryptStatusError( status ) )
55  return( status );
56  streamPos = stell( stream );
57 
58  /* Then we try and read any inner content */
59  status = innerTag = peekTag( stream );
60  if( !cryptStatusError( status ) )
61  status = readGenericHole( stream, &innerLength, 1, innerTag );
62  if( !cryptStatusError( status ) && \
63  bitStringLength == sizeofObject( innerLength ) )
64  {
65  /* There was inner content present, treat it as the actual type and
66  value of the bitstring. This assumes that the inner content is
67  a string data type, which always seems to be the case (in any
68  event it's not certain what we should be returning to the user if
69  we find, for example, a SEQUENCE with further encapsulated
70  content at this point) */
71  *stringTag = innerTag;
72  *length = innerLength;
73 
74  return( CRYPT_OK );
75  }
76 
77  /* We couldn't identify any (obvious) inner content, it must be raw
78  bitstring data. Unfortunately we have no idea what format this is
79  in, it could in fact really be raw binary data but never actually
80  seems to be this, it's usually ASCII text so we mark it as such and
81  let the string-read routines sort it out */
82  sClearError( stream );
83  sseek( stream, streamPos );
84  *stringTag = BER_STRING_IA5;
85  *length = bitStringLength;
86 
87  return( CRYPT_OK );
88  }
89 
90 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4 ) ) \
91 static int readAVA( INOUT STREAM *stream,
92  OUT_INT_Z int *type,
93  OUT_LENGTH_SHORT_Z int *length,
94  OUT_TAG_ENCODED_Z int *stringTag )
95  {
96  const DN_COMPONENT_INFO *dnComponentInfo;
97  BYTE oid[ MAX_OID_SIZE + 8 ];
98  int oidLength, tag, status;
99 
100  assert( isWritePtr( stream, sizeof( STREAM ) ) );
101  assert( isWritePtr( type, sizeof( int ) ) );
102  assert( isWritePtr( length, sizeof( int ) ) );
103  assert( isWritePtr( stringTag, sizeof( int ) ) );
104 
105  /* Clear return values */
106  *type = 0;
107  *length = 0;
108  *stringTag = 0;
109 
110  /* Read the start of the AVA and determine the type from the
111  AttributeType field. If we find something that we don't recognise we
112  indicate it as a non-component type that can be read or written but
113  not directly accessed by the user (although it can still be accessed
114  using the cursor functions) */
115  readSequence( stream, NULL );
116  status = readEncodedOID( stream, oid, MAX_OID_SIZE, &oidLength,
118  if( cryptStatusError( status ) )
119  return( status );
120  dnComponentInfo = findDNInfoByOID( oid, oidLength );
121  if( dnComponentInfo == NULL )
122  {
123  /* If we don't recognise the component type at all, skip it */
124  status = readUniversal( stream );
125  return( cryptStatusError( status ) ? status : OK_SPECIAL );
126  }
127  *type = dnComponentInfo->type;
128 
129  /* We've reached the data value, make sure that it's in order. When we
130  read the wrapper around the string type with readGenericHole() we have
131  to allow a minimum length of zero instead of one because of broken
132  AVAs with zero-length strings */
133  tag = peekTag( stream );
134  if( cryptStatusError( tag ) )
135  return( tag );
136  if( tag == BER_BITSTRING )
137  return( readAVABitstring( stream, length, stringTag ) );
138  *stringTag = tag;
139  return( readGenericHole( stream, length, 0, tag ) );
140  }
141 
142 /* Read an RDN/DN component */
143 
144 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
145 static int readRDNcomponent( INOUT STREAM *stream,
147  IN_LENGTH_SHORT const int rdnDataLeft )
148  {
150  BYTE stringBuffer[ MAX_ATTRIBUTE_SIZE + 8 ];
151  void *value;
152  const int rdnStart = stell( stream );
153  int type, valueLength, valueStringType, stringTag;
154  int flags = DN_FLAG_NOCHECK, status;
155 
156  assert( isWritePtr( stream, sizeof( STREAM ) ) );
157  assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) );
158 
159  REQUIRES( rdnDataLeft > 0 && rdnDataLeft < MAX_INTLENGTH_SHORT );
160  REQUIRES( rdnStart > 0 && rdnStart < MAX_INTLENGTH_SHORT );
161 
162  /* Read the type information for this AVA */
163  status = readAVA( stream, &type, &valueLength, &stringTag );
164  if( cryptStatusError( status ) )
165  return( status );
166  if( valueLength <= 0 )
167  {
168  /* Skip broken AVAs with zero-length strings */
169  return( CRYPT_OK );
170  }
171  status = sMemGetDataBlock( stream, &value, valueLength );
172  if( cryptStatusOK( status ) )
173  status = sSkip( stream, valueLength );
174  if( cryptStatusError( status ) )
175  return( status );
176  ANALYSER_HINT( value != NULL );
177 
178  /* If there's room for another AVA, mark this one as being continued. The
179  +10 value is the minimum length for an AVA: SEQUENCE { OID, value }
180  (2-bytes SEQUENCE + 5 bytes OID + 2 bytes (tag + length) + 1 byte min-
181  length data). We don't do a simple =/!= check to get around incorrectly
182  encoded lengths */
183  if( rdnDataLeft >= ( stell( stream ) - rdnStart ) + 10 )
184  flags |= DN_FLAG_CONTINUED;
185 
186  /* Convert the string into the local character set */
187  status = copyFromAsn1String( stringBuffer, MAX_ATTRIBUTE_SIZE,
188  &valueLength, &valueStringType, value,
189  valueLength, stringTag );
190  if( cryptStatusError( status ) )
191  return( status );
192 
193  /* Add the DN component to the DN. If we hit a non-memory related error
194  we turn it into a generic CRYPT_ERROR_BADDATA error since the other
195  codes are somewhat too specific for this case, something like
196  CRYPT_ERROR_INITED or an arg error isn't too useful for the caller */
197  status = insertDNstring( dnComponentListPtrPtr, type, stringBuffer,
198  valueLength, valueStringType, flags, &dummy );
199  return( ( cryptStatusError( status ) && status != CRYPT_ERROR_MEMORY ) ? \
200  CRYPT_ERROR_BADDATA : status );
201  }
202 
203 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
204 static int readDNComponent( INOUT STREAM *stream,
205  INOUT DN_COMPONENT **dnComponentListPtrPtr )
206  {
207  int rdnLength, iterationCount, status;
208 
209  /* Read the start of the RDN */
210  status = readSet( stream, &rdnLength );
211  if( cryptStatusError( status ) )
212  return( status );
213 
214  /* Read each RDN component */
215  for( iterationCount = 0;
216  rdnLength > 0 && iterationCount < FAILSAFE_ITERATIONS_MED;
217  iterationCount++ )
218  {
219  const int rdnStart = stell( stream );
220 
221  REQUIRES( rdnStart > 0 && rdnStart < MAX_INTLENGTH_SHORT );
222 
223  status = readRDNcomponent( stream, dnComponentListPtrPtr,
224  rdnLength );
225  if( cryptStatusError( status ) && status != OK_SPECIAL )
226  return( status );
227 
228  rdnLength -= stell( stream ) - rdnStart;
229  }
230  if( rdnLength < 0 || iterationCount >= FAILSAFE_ITERATIONS_MED )
231  return( CRYPT_ERROR_BADDATA );
232 
233  return( CRYPT_OK );
234  }
235 
236 /* Read a DN */
237 
238 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
239 int readDN( INOUT STREAM *stream,
240  OUT_OPT_PTR DN_PTR **dnComponentListPtrPtr )
241  {
242  DN_COMPONENT *dnComponentListPtr = NULL;
243  int length, iterationCount, status;
244 
245  assert( isWritePtr( stream, sizeof( STREAM ) ) );
246  assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) );
247 
248  /* Clear return value */
249  *dnComponentListPtrPtr = NULL;
250 
251  /* Read the encoded DN into the local copy of the DN (in other words
252  into the dnComponentListPtr, not the externally-visible
253  dnComponentListPtrPtr) */
254  status = readSequence( stream, &length );
255  if( cryptStatusError( status ) )
256  return( status );
257  for( iterationCount = 0;
258  length > 0 && iterationCount < FAILSAFE_ITERATIONS_MED;
259  iterationCount++ )
260  {
261  const int startPos = stell( stream );
262 
263  REQUIRES( startPos > 0 && startPos < MAX_INTLENGTH_SHORT );
264 
265  status = readDNComponent( stream, &dnComponentListPtr );
266  if( cryptStatusError( status ) )
267  break;
268  length -= stell( stream ) - startPos;
269  }
270  if( cryptStatusError( status ) || \
271  length < 0 || iterationCount >= FAILSAFE_ITERATIONS_MED )
272  {
273  /* Delete the local copy of the DN read so far if necessary */
274  if( dnComponentListPtr != NULL )
275  deleteDN( ( DN_PTR ** ) &dnComponentListPtr );
276  return( cryptStatusError( status ) ? status : CRYPT_ERROR_BADDATA );
277  }
278 
279  /* Copy the local copy of the DN back to the caller */
280  *dnComponentListPtrPtr = dnComponentListPtr;
281  return( CRYPT_OK );
282  }
283 
284 /****************************************************************************
285 * *
286 * Write a DN *
287 * *
288 ****************************************************************************/
289 
290 /* Perform the pre-encoding processing for a DN. Note that sizeofDN() takes
291  a slightly anomalous non-const parameter because the pre-encoding process
292  required to determine the DN's size modifies portions of the DN component
293  values related to the encoding process */
294 
295 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
296 static int preEncodeDN( INOUT DN_COMPONENT *dnComponentPtr,
297  OUT_LENGTH_SHORT_Z int *length )
298  {
299  int size = 0, iterationCount;
300 
301  assert( isWritePtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) );
302  assert( isWritePtr( length, sizeof( int ) ) );
303 
304  /* Clear return value */
305  *length = 0;
306 
307  assert( isReadPtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) );
308 
309 #if 0 /* 18/7/08 Should never happen */
310  /* If we're being fed an entry in the middle of a DN, move back to the
311  start */
312  for( iterationCount = 0;
313  dnComponentPtr->prev != NULL && \
314  iterationCount < FAILSAFE_ITERATIONS_MED;
315  dnComponentPtr = dnComponentPtr->prev, iterationCount++ );
316  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
317 #endif /* 0 */
318  ENSURES( dnComponentPtr->prev == NULL );
319 
320  /* Walk down the DN pre-encoding each AVA */
321  for( iterationCount = 0;
322  dnComponentPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED;
323  dnComponentPtr = dnComponentPtr->next, iterationCount++ )
324  {
325  DN_COMPONENT *rdnStartPtr = dnComponentPtr;
326  int innerIterationCount;
327 
328  /* Calculate the size of every AVA in this RDN */
329  for( innerIterationCount = 0;
330  dnComponentPtr != NULL && \
331  innerIterationCount < FAILSAFE_ITERATIONS_MED;
332  dnComponentPtr = dnComponentPtr->next, innerIterationCount++ )
333  {
334  const DN_COMPONENT_INFO *dnComponentInfo = dnComponentPtr->typeInfo;
335  int dnStringLength, status;
336 
337  status = getAsn1StringInfo( dnComponentPtr->value,
338  dnComponentPtr->valueLength,
339  &dnComponentPtr->valueStringType,
340  &dnComponentPtr->asn1EncodedStringType,
341  &dnStringLength );
342  if( cryptStatusError( status ) )
343  return( status );
344  dnComponentPtr->encodedAVAdataSize = ( int ) \
345  sizeofOID( dnComponentInfo->oid ) + \
346  sizeofObject( dnStringLength );
347  dnComponentPtr->encodedRDNdataSize = 0;
348  rdnStartPtr->encodedRDNdataSize += ( int ) \
349  sizeofObject( dnComponentPtr->encodedAVAdataSize );
350  if( !( dnComponentPtr->flags & DN_FLAG_CONTINUED ) )
351  break;
352  }
353  ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
354 
355  /* Calculate the overall size of the RDN */
356  size += ( int ) sizeofObject( rdnStartPtr->encodedRDNdataSize );
357 
358  /* If the inner loop terminated because it reached the end of the DN
359  then we need to explicitly exit the outer loop as well before it
360  tries to follow the 'next' link in the dnComponentPtr */
361  if( dnComponentPtr == NULL )
362  break;
363  }
364  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
365  *length = size;
366 
367  return( CRYPT_OK );
368  }
369 
370 CHECK_RETVAL \
372  {
373  int length, status;
374 
375  assert( dnComponentList == NULL || \
376  isWritePtr( dnComponentList, sizeof( DN_COMPONENT ) ) );
377 
378  /* Null DNs produce a zero-length SEQUENCE */
379  if( dnComponentList == NULL )
380  return( sizeofObject( 0 ) );
381 
382  status = preEncodeDN( dnComponentList, &length );
383  if( cryptStatusError( status ) )
384  return( status );
385  return( sizeofObject( length ) );
386  }
387 
388 /* Write a DN */
389 
391 int writeDN( INOUT STREAM *stream,
392  IN_OPT const DN_PTR *dnComponentList,
393  IN_TAG const int tag )
394  {
395  DN_COMPONENT *dnComponentPtr;
396  int size, iterationCount, status;
397 
398  assert( isWritePtr( stream, sizeof( STREAM ) ) );
399  assert( dnComponentList == NULL || \
400  isReadPtr( dnComponentList, sizeof( DN_COMPONENT ) ) );
401 
402  REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
403 
404  /* Special case for empty DNs */
405  if( dnComponentList == NULL )
406  return( writeConstructed( stream, 0, tag ) );
407 
408  status = preEncodeDN( ( DN_COMPONENT * ) dnComponentList, &size );
409  if( cryptStatusError( status ) )
410  return( status );
411 
412  /* Write the DN */
413  writeConstructed( stream, size, tag );
414  for( dnComponentPtr = ( DN_COMPONENT * ) dnComponentList, \
415  iterationCount = 0;
416  dnComponentPtr != NULL && \
417  iterationCount < FAILSAFE_ITERATIONS_MED;
418  dnComponentPtr = dnComponentPtr->next, iterationCount++ )
419  {
420  const DN_COMPONENT_INFO *dnComponentInfo = dnComponentPtr->typeInfo;
421  BYTE dnString[ MAX_ATTRIBUTE_SIZE + 8 ];
422  int dnStringLength;
423 
424  /* Write the RDN wrapper */
425  if( dnComponentPtr->encodedRDNdataSize > 0 )
426  {
427  /* If it's the start of an RDN, write the RDN header */
428  writeSet( stream, dnComponentPtr->encodedRDNdataSize );
429  }
430  writeSequence( stream, dnComponentPtr->encodedAVAdataSize );
431  status = swrite( stream, dnComponentInfo->oid,
432  sizeofOID( dnComponentInfo->oid ) );
433  if( cryptStatusError( status ) )
434  return( status );
435 
436  /* Convert the string to an ASN.1-compatible format and write it
437  out */
438  status = copyToAsn1String( dnString, MAX_ATTRIBUTE_SIZE,
439  &dnStringLength, dnComponentPtr->value,
440  dnComponentPtr->valueLength,
441  dnComponentPtr->valueStringType );
442  if( cryptStatusError( status ) )
443  return( status );
444  if( dnComponentPtr->asn1EncodedStringType == BER_STRING_IA5 && \
445  !dnComponentInfo->ia5OK )
446  {
447  /* If an IA5String isn't allowed in this instance, use a
448  T61String instead */
449  dnComponentPtr->asn1EncodedStringType = BER_STRING_T61;
450  }
451  status = writeCharacterString( stream, dnString, dnStringLength,
452  dnComponentPtr->asn1EncodedStringType );
453  if( cryptStatusError( status ) )
454  return( status );
455  }
456  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
457 
458  return( CRYPT_OK );
459  }
460 
461 /****************************************************************************
462 * *
463 * DN String Routines *
464 * *
465 ****************************************************************************/
466 
467 /* Note that the ability to specify free-form DNs means that users can
468  create arbitrarily garbled and broken DNs (the creation of weird
469  nonstandard DNs is pretty much the main reason why the DN-string
470  capability exists). This includes DNs that can't be easily handled
471  through normal cryptlib facilities, for example ones where the CN
472  component consists of illegal characters or is in a form that isn't
473  usable as a search key for functions like cryptGetPublicKey(). If users
474  want to use this oddball-DN facility it's up to them to make sure that
475  the resulting DN information works with whatever environment they're
476  intending to use it in */
477 
478 #ifdef USE_CERT_DNSTRING
479 
480 #if defined( _MSC_VER )
481  #pragma message( " Building with string-form DNs enabled." )
482 #endif /* Warn with VC++ */
483 
484 /* Check whether a string can be represented as a textual DN string */
485 
486 static BOOLEAN isTextString( IN_BUFFER( stringLength ) const BYTE *string,
488  {
489  int i;
490 
491  assert( isReadPtr( string, stringLength ) );
492 
493  REQUIRES( stringLength > 0 && stringLength <= MAX_ATTRIBUTE_SIZE );
494 
495  /* Make sure that there are no control characters in the string. We
496  allow high-bit-set characters in order to support latin-1 strings,
497  see also the comment at the start of this section about the general
498  caveat-emptor philosophy for this interface */
499  for( i = 0; i < stringLength; i++ )
500  {
501  if( ( string[ i ] & 0x7F ) < ' ' )
502  return( FALSE );
503  }
504 
505  return( TRUE );
506  }
507 
508 /* Read a DN in string form */
509 
510 typedef struct {
511  BUFFER_FIXED( labelLen ) \
512  const BYTE *label;
513  BUFFER_FIXED( textLen ) \
514  const BYTE *text;
515  int labelLen, textLen; /* DN component label and value */
516  BOOLEAN isContinued; /* Whether there are further AVAs in this RDN */
517  } DN_STRING_INFO;
518 
519 #define MAX_DNSTRING_COMPONENTS 32
520 
522 static BOOLEAN parseDNString( INOUT_ARRAY( MAX_DNSTRING_COMPONENTS ) \
523  DN_STRING_INFO *dnStringInfo,
524  OUT_RANGE( 0, MAX_DNSTRING_COMPONENTS ) \
525  int *dnStringInfoIndex,
526  IN_BUFFER( stringLength ) const BYTE *string,
527  IN_LENGTH_ATTRIBUTE const int stringLength )
528  {
529  int stringPos, stringInfoIndex;
530 
531  assert( isWritePtr( dnStringInfo, sizeof( DN_STRING_INFO ) * \
532  MAX_DNSTRING_COMPONENTS ) );
533  assert( isReadPtr( string, stringLength ) );
534 
535  REQUIRES( stringLength > 0 && stringLength <= MAX_ATTRIBUTE_SIZE );
536 
537  /* Clear return values */
538  memset( dnStringInfo, 0,
539  sizeof( DN_STRING_INFO ) * MAX_DNSTRING_COMPONENTS );
540  *dnStringInfoIndex = 0;
541 
542  /* Make sure that the string is decodable as a text string */
543  if( !isTextString( string, stringLength ) )
544  return( FALSE );
545 
546  /* Verify that a DN string is of the form:
547 
548  dnString ::= assignment '\0' | assignment ',' assignment
549  assignment ::= label '=' text */
550  for( stringPos = 0, stringInfoIndex = 0;
551  stringPos < stringLength && \
552  stringInfoIndex < MAX_DNSTRING_COMPONENTS;
553  stringInfoIndex++ )
554  {
555  DN_STRING_INFO *dnStringInfoPtr = &dnStringInfo[ stringInfoIndex ];
556  int i;
557 
558  /* Check for label '=' ... */
559  for( i = stringPos; i < stringLength; i++ )
560  {
561  const int ch = byteToInt( string[ i ] );
562 
563  if( ch == '\\' || ch == '+' || ch == ',' )
564  {
565  /* The label component can't have special characters in it */
566  return( FALSE );
567  }
568  if( ch == '=' )
569  break;
570  }
571  if( i <= stringPos || i >= stringLength - 1 ) /* -1 for '=' */
572  {
573  /* The label component is empty */
574  return( FALSE );
575  }
576  ENSURES( rangeCheckZ( stringPos, i - stringPos, stringLength ) );
577  dnStringInfoPtr->label = string + stringPos;
578  dnStringInfoPtr->labelLen = i - stringPos;
579  stringPos = i + 1; /* Skip text + '=' */
580 
581  /* Check for ... text { EOT | ',' ... | '+' ... }. Note that we
582  make the loop variable increment part of the loop code rather
583  than having it in the for() statement to avoid a series of messy
584  fencepost-error adjustments in the checks that follow */
585  for( i = stringPos; i < stringLength && \
586  i < MAX_ATTRIBUTE_SIZE; i++ )
587  {
588  const int ch = byteToInt( string[ i ] );
589 
590  /* Check for invalid data */
591  if( ch == '=' )
592  {
593  /* Spurious '=' */
594  return( FALSE );
595  }
596  if( ch == '\\' )
597  {
598  if( i >= stringLength - 1 )
599  {
600  /* Missing escaped character */
601  return( FALSE );
602  }
603 
604  /* It's an escaped character that isn't subject to the usual
605  checks, skip it and continue */
606  i++;
607  continue;
608  }
609 
610  /* If this isn't a continuation symbol, continue */
611  if( ch != ',' && ch != '+' )
612  continue;
613 
614  /* We've reached a continuation symbol, make sure that there's
615  room for at least another 'x=y' after this point */
616  if( i >= stringLength - 3 )
617  return( FALSE );
618 
619  break;
620  }
621  ENSURES( i < MAX_ATTRIBUTE_SIZE );
622  ENSURES( rangeCheck( stringPos, i - stringPos, stringLength ) );
623  dnStringInfoPtr->text = string + stringPos;
624  dnStringInfoPtr->textLen = i - stringPos;
625  if( string[ i ] == ',' || string[ i ] == '+' )
626  {
627  /* Skip the final ',' or '+' and remember whether this is a
628  continued RDN */
629  if( string[ i ] == '+' )
630  dnStringInfoPtr->isContinued = TRUE;
631  i++;
632  }
633  stringPos = i; /* Skip text + optional ','/'+' */
634 
635  /* Strip leading and trailing whitespace on the label and text */
636  dnStringInfoPtr->labelLen = \
637  strStripWhitespace( ( const char ** ) &dnStringInfoPtr->label,
638  dnStringInfoPtr->label,
639  dnStringInfoPtr->labelLen );
640  dnStringInfoPtr->textLen = \
641  strStripWhitespace( ( const char ** ) &dnStringInfoPtr->text,
642  dnStringInfoPtr->text,
643  dnStringInfoPtr->textLen );
644  if( dnStringInfoPtr->labelLen < 1 || \
645  dnStringInfoPtr->labelLen > MAX_ATTRIBUTE_SIZE || \
646  dnStringInfoPtr->textLen < 1 || \
647  dnStringInfoPtr->textLen > MAX_ATTRIBUTE_SIZE )
648  return( FALSE );
649  }
650  if( stringInfoIndex <= 0 || stringInfoIndex >= MAX_DNSTRING_COMPONENTS )
651  return( FALSE );
652  *dnStringInfoIndex = stringInfoIndex;
653 
654  return( TRUE );
655  }
656 
657 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
658 int readDNstring( INOUT_PTR DN_PTR **dnComponentListPtrPtr,
659  IN_BUFFER( stringLength ) const BYTE *string,
660  IN_LENGTH_ATTRIBUTE const int stringLength )
661  {
662  DN_STRING_INFO dnStringInfo[ MAX_DNSTRING_COMPONENTS + 8 ];
663  DN_COMPONENT *dnComponentPtr = NULL;
664  int stringInfoIndex, iterationCount;
665 
666  assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) );
667  assert( isReadPtr( string, stringLength ) );
668 
669  REQUIRES( stringLength > 0 && stringLength <= MAX_ATTRIBUTE_SIZE );
670 
671  /* Clear return value */
672  *dnComponentListPtrPtr = NULL;
673 
674  /* We have to perform the text string to DN translation in two stages
675  thanks to the backwards encoding required by RFC 1779. First we
676  parse it forwards to separate out the RDN components, then we move
677  through the parsed information backwards adding it to the RDN (with
678  special handling for multi-AVA RDNs as for writeDNstring()). Overall
679  this isn't so bad because it means that we can perform a general
680  firewall check to make sure that the DN string is well-formed and
681  then leave the encoding as a separate pass */
682  if( !parseDNString( dnStringInfo, &stringInfoIndex, string,
683  stringLength ) )
684  return( CRYPT_ARGERROR_STR1 );
685 
686  /* parseDNString() returns the number of entries parsed, since we're
687  using zero-based indexing we have to decrement the value returned to
688  provide the actual index into the dnStringInfo[] array */
689  stringInfoIndex--;
690 
691  for( iterationCount = 0;
692  stringInfoIndex >= 0 && iterationCount < FAILSAFE_ITERATIONS_MED;
693  stringInfoIndex--, iterationCount++ )
694  {
695  const DN_STRING_INFO *dnStringInfoPtr;
696  BOOLEAN isContinued;
697  int innerIterationCount;
698 
699  /* Find the start of the RDN */
700  while( stringInfoIndex > 0 && \
701  dnStringInfo[ stringInfoIndex - 1 ].isContinued )
702  stringInfoIndex--;
703  dnStringInfoPtr = &dnStringInfo[ stringInfoIndex ];
704 
705  for( isContinued = TRUE, innerIterationCount = 0;
706  isContinued && innerIterationCount < FAILSAFE_ITERATIONS_MED;
707  innerIterationCount++ )
708  {
710  const DN_COMPONENT_INFO *dnComponentInfo = NULL;
711  BYTE textBuffer[ MAX_ATTRIBUTE_SIZE + 8 ];
713  int i, textIndex = 0, valueStringType, dummy1, dummy2, status;
714 
715  /* Look up the DN component information */
716  dnComponentInfo = findDNInfoByLabel( dnStringInfoPtr->label,
717  dnStringInfoPtr->labelLen );
718  if( dnComponentInfo == NULL )
719  {
720  if( dnComponentPtr != NULL )
721  deleteDN( ( DN_PTR ** ) &dnComponentPtr );
722  return( CRYPT_ARGERROR_STR1 );
723  }
724  type = dnComponentInfo->type;
725 
726  /* Convert the text to canonical form, removing any escapes for
727  special characters */
728  for( i = 0; i < dnStringInfoPtr->textLen && \
729  i < MAX_ATTRIBUTE_SIZE; i++ )
730  {
731  int ch = byteToInt( dnStringInfoPtr->text[ i ] );
732 
733  if( ch == '\\' )
734  {
735  /* Skip '\\' */
736  i++;
737  if( i >= dnStringInfoPtr->textLen )
738  {
739  if( dnComponentPtr != NULL )
740  deleteDN( ( DN_PTR ** ) &dnComponentPtr );
741  return( CRYPT_ARGERROR_STR1 );
742  }
743  ch = byteToInt( dnStringInfoPtr->text[ i ] );
744  }
745  textBuffer[ textIndex++ ] = intToByte( ch );
746  }
747  ENSURES( i < MAX_ATTRIBUTE_SIZE );
748 
749  /* The value is coming from an external source, make sure that
750  it's representable as a certificate string type. All that
751  we care about here is the validity so we ignore the returned
752  encoding information */
753  status = getAsn1StringInfo( textBuffer, textIndex,
754  &valueStringType, &dummy1, &dummy2 );
755  if( cryptStatusError( status ) )
756  {
757  if( dnComponentPtr != NULL )
758  deleteDN( ( DN_PTR ** ) &dnComponentPtr );
759  return( CRYPT_ARGERROR_STR1 );
760  }
761 
762  /* Add the AVA to the DN */
763  if( type == CRYPT_CERTINFO_COUNTRYNAME )
764  {
765  /* If it's a country code force it to uppercase as per ISO 3166 */
766  if( textIndex != 2 )
767  {
768  if( dnComponentPtr != NULL )
769  deleteDN( ( DN_PTR ** ) &dnComponentPtr );
770  return( CRYPT_ARGERROR_STR1 );
771  }
772  textBuffer[ 0 ] = intToByte( toUpper( textBuffer[ 0 ] ) );
773  textBuffer[ 1 ] = intToByte( toUpper( textBuffer[ 1 ] ) );
774  }
775  status = insertDNstring( &dnComponentPtr, type,
776  textBuffer, textIndex, valueStringType,
777  ( dnStringInfoPtr->isContinued ) ? \
779  DN_FLAG_NOCHECK, &dummy );
780  if( cryptStatusError( status ) )
781  {
782  if( dnComponentPtr != NULL )
783  deleteDN( ( DN_PTR ** ) &dnComponentPtr );
784  return( status );
785  }
786 
787  /* Move on to the next AVA */
788  isContinued = dnStringInfoPtr->isContinued;
789  dnStringInfoPtr++;
790  }
791  ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
792  }
793  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
794 
795  /* We're done, lock the DN against further updates */
796  *dnComponentListPtrPtr = dnComponentPtr;
797  for( iterationCount = 0;
798  dnComponentPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
799  dnComponentPtr = dnComponentPtr->next, iterationCount++ )
800  dnComponentPtr->flags |= DN_FLAG_LOCKED;
801  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
802 
803  return( CRYPT_OK );
804  }
805 
806 /* Write a DN in string form */
807 
808 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
809 static int writeAVAString( INOUT STREAM *stream,
810  const DN_COMPONENT *dnComponentPtr )
811  {
812  const DN_COMPONENT_INFO *componentInfoPtr = dnComponentPtr->typeInfo;
813  int i, status;
814 
815  assert( isWritePtr( stream, sizeof( STREAM ) ) );
816  assert( isReadPtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) );
817 
818  /* Print the current AVA */
819  swrite( stream, componentInfoPtr->name, componentInfoPtr->nameLen );
820  status = sputc( stream, '=' );
821  if( cryptStatusError( status ) )
822  return( status );
823  for( i = 0; i < dnComponentPtr->valueLength; i++ )
824  {
825  const int ch = byteToInt( ( ( BYTE * ) dnComponentPtr->value )[ i ] );
826 
827  if( ch == ',' || ch == '=' || ch == '+' || ch == ';' || \
828  ch == '\\' || ch == '"' )
829  sputc( stream, '\\' );
830  status = sputc( stream, ch );
831  if( cryptStatusError( status ) )
832  return( status );
833  }
834 
835  return( CRYPT_OK );
836  }
837 
839 int writeDNstring( INOUT STREAM *stream,
840  IN_OPT const DN_PTR *dnComponentList )
841  {
842  const DN_COMPONENT *dnComponentPtr = dnComponentList;
843  int iterationCount, status;
844 
845  assert( isWritePtr( stream, sizeof( STREAM ) ) );
846  assert( dnComponentList == NULL || \
847  isReadPtr( dnComponentList, sizeof( DN_COMPONENT ) ) );
848 
849  /* If it's an empty DN there's nothing to write */
850  if( dnComponentPtr == NULL )
851  return( CRYPT_OK );
852 
853  /* Find the end of the DN string. We have to print the RDNs backwards
854  because of ISODE's JANET memorial backwards encoding. This also
855  provides a convenient location to check that the DN data is actually
856  representable as a text string.
857 
858  Some string types can't be represented as text strings which means
859  that we can't send them directly to the output. The details of what
860  to do here are a bit complex, in theory we could send them out as
861  UTF-8 but few environments are going to expect this as a returned
862  type, particularly when the existing expectation is for oddball
863  characters in text strings to be latin-1. The least ugly solution
864  seems to be to just return an indicator that this string can't be
865  represented */
866  for( iterationCount = 0;
867  dnComponentPtr->next != NULL && \
868  iterationCount < FAILSAFE_ITERATIONS_MED;
869  dnComponentPtr = dnComponentPtr->next, iterationCount++ )
870  {
871  /* Make sure that the DN component is representable as a text
872  string. Exactly what we should return if this check fails is a
873  bit uncertain since there's no error code that it really
874  corresponds to, CRYPT_ERROR_NOTAVAIL appears to be the least
875  inappropriate one to use. An alternative is to return a special-
876  case string like "(DN can't be represented in string form)" but
877  this then looks (from the return status) as if it was actually
878  representable, requiring special-case checks for valid-but-not-
879  valid returned data, so the error status is probably the best
880  option */
881  if( !isTextString( dnComponentPtr->value,
882  dnComponentPtr->valueLength ) )
883  return( CRYPT_ERROR_NOTAVAIL );
884  }
885  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
886 
887  for( iterationCount = 0;
888  dnComponentPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED;
889  iterationCount++ )
890  {
891  const DN_COMPONENT *dnComponentCursor;
892  int innerIterationCount;
893 
894  /* Find the start of the RDN */
895  for( innerIterationCount = 0;
896  dnComponentPtr->prev != NULL && \
897  ( dnComponentPtr->prev->flags & DN_FLAG_CONTINUED ) && \
898  innerIterationCount < FAILSAFE_ITERATIONS_MED;
899  dnComponentPtr = dnComponentPtr->prev, innerIterationCount++ );
900  ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
901  dnComponentCursor = dnComponentPtr;
902  dnComponentPtr = dnComponentPtr->prev;
903 
904  /* Print the current RDN */
905  for( status = CRYPT_OK, innerIterationCount = 0;
906  cryptStatusOK( status ) && \
907  innerIterationCount < FAILSAFE_ITERATIONS_MED;
908  innerIterationCount++ )
909  {
910  status = writeAVAString( stream, dnComponentCursor );
911  if( cryptStatusError( status ) )
912  return( status );
913 
914  /* If this is the last AVA in the RDN, we're done */
915  if( !( dnComponentCursor->flags & DN_FLAG_CONTINUED ) )
916  break;
917 
918  /* There are more AVAs in this RDN, print a continuation
919  indicator and move on to the next AVA */
920  status = swrite( stream, " + ", 3 );
921  if( cryptStatusError( status ) )
922  return( status );
923  dnComponentCursor = dnComponentCursor->next;
924  }
925  ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
926 
927  /* If there are more components to come print an RDN separator */
928  if( dnComponentPtr != NULL )
929  {
930  status = swrite( stream, ", ", 2 );
931  if( cryptStatusError( status ) )
932  return( status );
933  }
934  }
935  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
936 
937  return( CRYPT_OK );
938  }
939 #endif /* USE_CERT_DNSTRING */
940 #endif /* USE_CERTIFICATES */