cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ext_add.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Certificate Attribute Add/Delete Routines *
4 * Copyright Peter Gutmann 1996-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "cert.h"
10  #include "certattr.h"
11  #include "asn1.h"
12 #else
13  #include "cert/cert.h"
14  #include "cert/certattr.h"
15  #include "enc_dec/asn1.h"
16 #endif /* Compiler-specific includes */
17 
18 #ifdef USE_CERTIFICATES
19 
20 /* Normally we use FAILSAFE_ITERATIONS_LARGE as the upper bound on loop
21  iterations, however RPKI certificates, which are general-purpose
22  certificates turned into pseudo-attribute certificates with monstrous
23  lists of capabilities, can have much larger upper bounds. To handle
24  these we have to make a special exception for the upper bound of the
25  loop sanity check */
26 
27 #define FAILSAFE_ITERATIONS_LARGE_SPECIAL ( FAILSAFE_ITERATIONS_LARGE * 10 )
28 
29 /****************************************************************************
30 * *
31 * Utility Functions *
32 * *
33 ****************************************************************************/
34 
35 /* Check the validity of an attribute field */
36 
38 static int checkAttributeField( IN_OPT const ATTRIBUTE_LIST *attributeListPtr,
39  const ATTRIBUTE_INFO *attributeInfoPtr,
43  IN_INT_Z const int value,
44  IN_FLAGS( ATTR ) const int flags,
45  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
46  CRYPT_ERRTYPE_TYPE *errorType )
47  {
48  assert( attributeListPtr == NULL || \
49  isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
50  assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
51  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
52 
53  REQUIRES( fieldID >= CRYPT_CERTINFO_FIRST_EXTENSION && \
54  fieldID <= CRYPT_CERTINFO_LAST );
55  REQUIRES( subFieldID == CRYPT_ATTRIBUTE_NONE || \
56  ( subFieldID >= CRYPT_CERTINFO_FIRST_NAME && \
57  subFieldID <= CRYPT_CERTINFO_LAST_GENERALNAME ) );
58 assert( ( flags & ~( ATTR_FLAG_NONE | ATTR_FLAG_CRITICAL | ATTR_FLAG_MULTIVALUED ) ) == 0 );
59  REQUIRES( flags >= ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
60 
61  /* Make sure that a valid field has been specified and that this field
62  isn't already present as a non-default entry unless it's a field for
63  which multiple values are allowed */
64  if( attributeInfoPtr == NULL )
65  return( CRYPT_ARGERROR_VALUE );
66  if( attributeListPtr != NULL && \
67  findAttributeField( attributeListPtr, fieldID, subFieldID ) != NULL )
68  {
69  /* If it's not multivalued, we can't have any duplicate fields */
70  if( !( ( attributeInfoPtr->encodingFlags & FL_MULTIVALUED ) || \
71  ( flags & ATTR_FLAG_MULTIVALUED ) ) )
72  {
73  *errorType = CRYPT_ERRTYPE_ATTR_PRESENT;
74  return( CRYPT_ERROR_INITED );
75  }
76  }
77 
78  switch( attributeInfoPtr->fieldType )
79  {
81  /* It's an identifier, make sure that all parameters are correct */
82  if( value != CRYPT_UNUSED )
83  return( CRYPT_ARGERROR_NUM1 );
84  break;
85 
86  case FIELDTYPE_DN:
87  /* When creating a new certificate this is a special-case field
88  that's used as a placeholder to indicate that a DN structure
89  is being instantiated. When reading an encoded certificate
90  this is the decoded DN structure */
91  ENSURES( value == CRYPT_UNUSED );
92  break;
93 
94  case BER_BOOLEAN:
95  /* BOOLEAN data is accepted as zero/nonzero so it's always
96  valid */
97  break;
98 
99  case BER_INTEGER:
100  case BER_ENUMERATED:
101  case BER_BITSTRING:
102  case BER_NULL:
103  case FIELDTYPE_CHOICE:
104  /* Check that the range is valid */
105  if( value < attributeInfoPtr->lowRange || \
106  value > attributeInfoPtr->highRange )
107  {
108  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
109  return( CRYPT_ARGERROR_NUM1 );
110  }
111  break;
112 
113  default:
114  retIntError();
115  }
116 
117  return( CRYPT_OK );
118  }
119 
120 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 2, 8, 9 ) ) \
121 static int checkAttributeFieldString( IN_OPT const ATTRIBUTE_LIST *attributeListPtr,
122  const ATTRIBUTE_INFO *attributeInfoPtr,
123  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE fieldID,
125  const CRYPT_ATTRIBUTE_TYPE subFieldID,
126  IN_BUFFER( dataLength ) const void *data,
128  IN_FLAGS( ATTR ) const int flags,
129  OUT_LENGTH_SHORT_Z int *newDataLength,
130  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
131  CRYPT_ERRTYPE_TYPE *errorType )
132  {
133  int status;
134 
135  assert( attributeListPtr == NULL || \
136  isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
137  assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
138  assert( isReadPtr( data, dataLength ) );
139  assert( isWritePtr( newDataLength, sizeof( int ) ) );
140  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
141 
142  REQUIRES( fieldID >= CRYPT_CERTINFO_FIRST_EXTENSION && \
143  fieldID <= CRYPT_CERTINFO_LAST );
144  REQUIRES( subFieldID == CRYPT_ATTRIBUTE_NONE || \
145  ( subFieldID >= CRYPT_CERTINFO_FIRST_NAME && \
146  subFieldID <= CRYPT_CERTINFO_LAST_GENERALNAME ) );
147  REQUIRES( dataLength > 0 && dataLength <= MAX_ATTRIBUTE_SIZE );
148 assert( ( flags & ~( ATTR_FLAG_NONE | ATTR_FLAG_BLOB_PAYLOAD | ATTR_FLAG_CRITICAL | ATTR_FLAG_MULTIVALUED ) ) == 0 );
149  REQUIRES( flags >= ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
150 
151  /* Clear return values */
152  *newDataLength = 0;
153 
154  /* Make sure that a valid field has been specified and that this field
155  isn't already present as a non-default entry unless it's a field for
156  which multiple values are allowed */
157  if( attributeInfoPtr == NULL )
158  return( CRYPT_ARGERROR_VALUE );
159  if( attributeListPtr != NULL && \
160  findAttributeField( attributeListPtr, fieldID, subFieldID ) != NULL )
161  {
162  /* If it's not multivalued, we can't have any duplicate fields */
163  if( !( ( attributeInfoPtr->encodingFlags & FL_MULTIVALUED ) || \
164  ( flags & ATTR_FLAG_MULTIVALUED ) ) )
165  {
166  *errorType = CRYPT_ERRTYPE_ATTR_PRESENT;
167  return( CRYPT_ERROR_INITED );
168  }
169  }
170 
171  /* If it's a blob field, don't do any type checking. This is a special
172  case that differs from FIELDTYPE_BLOB_xxx in that it corresponds to
173  an ASN.1 value that's mis-encoded by one or more implementations, so
174  we have to accept absolutely anything at this point */
175  if( flags & ATTR_FLAG_BLOB )
176  return( CRYPT_OK );
177 
178  /* If it's a DN_PTR there's no further type checking to be done either */
179  if( attributeInfoPtr->fieldType == FIELDTYPE_DN )
180  return( CRYPT_OK );
181 
182  /* If we've been passed an OID it may be encoded as a text string so
183  before we can continue we have to determine whether any
184  transformations will be necessary */
185  if( attributeInfoPtr->fieldType == BER_OBJECT_IDENTIFIER )
186  {
187  const BYTE *oidPtr = data;
188  BYTE binaryOID[ MAX_OID_SIZE + 8 ];
189 
190  /* If it's a BER/DER-encoded OID, make sure that it's valid ASN.1 */
191  if( oidPtr[ 0 ] == BER_OBJECT_IDENTIFIER )
192  {
193  if( dataLength >= MIN_OID_SIZE && dataLength <= MAX_OID_SIZE && \
194  sizeofOID( oidPtr ) == dataLength )
195  return( CRYPT_OK );
196  }
197  else
198  {
199  int length;
200 
201  /* It's a text OID, check the syntax and make sure that the
202  length is valid */
203  status = textToOID( data, dataLength, binaryOID, MAX_OID_SIZE,
204  &length );
205  if( cryptStatusOK( status ) )
206  {
207  /* The binary form of the OID differs in length from the
208  string form, tell the caller that the data length will
209  change on encoding so that they can allocate an
210  appropriately-sized buffer for it before continuing */
211  *newDataLength = length;
212  return( CRYPT_OK );
213  }
214  }
215 
216  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
217  return( CRYPT_ARGERROR_STR1 );
218  }
219 
220  /* Make sure that the data size is valid */
221  if( dataLength < attributeInfoPtr->lowRange || \
222  dataLength > attributeInfoPtr->highRange )
223  {
224  *errorType = CRYPT_ERRTYPE_ATTR_SIZE;
225  return( CRYPT_ARGERROR_NUM1 );
226  }
227 
228  /* If we're not checking the payload in order to handle CAs who stuff
229  any old rubbish into the fields exit now unless it's a blob field,
230  for which we need to find at least valid ASN.1 data */
231  if( ( flags & ATTR_FLAG_BLOB_PAYLOAD ) && \
232  !isBlobField( attributeInfoPtr->fieldType ) )
233  return( CRYPT_OK );
234 
235  /* Perform any special-case checking that may be required */
236  switch( attributeInfoPtr->fieldType )
237  {
238  case FIELDTYPE_BLOB_ANY:
239  /* It's a blob field, make sure that it's a valid ASN.1 object */
240  status = checkObjectEncoding( data, dataLength );
241  if( cryptStatusError( status ) )
242  {
243  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
244  return( CRYPT_ARGERROR_STR1 );
245  }
246  return( CRYPT_OK );
247 
250  {
251  const int firstByte = ( ( BYTE * ) data )[ 0 ];
252 
253  /* Typed blobs have a bit more information available than just
254  "it must be a valid ASN.1 object" so we can check that it's
255  the correct type of object as well as the ASN.1 validity
256  check */
257  if( attributeInfoPtr->fieldType == FIELDTYPE_BLOB_BITSTRING )
258  {
259  if( firstByte != BER_BITSTRING )
260  {
261  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
262  return( CRYPT_ARGERROR_STR1 );
263  }
264  }
265  else
266  {
267  if( firstByte != BER_SEQUENCE )
268  {
269  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
270  return( CRYPT_ARGERROR_STR1 );
271  }
272  }
273  status = checkObjectEncoding( data, dataLength );
274  if( cryptStatusError( status ) )
275  {
276  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
277  return( CRYPT_ARGERROR_STR1 );
278  }
279  return( CRYPT_OK );
280  }
281 
282  case BER_STRING_NUMERIC:
283  {
284  const char *dataPtr = data;
285  int i;
286 
287  /* Make sure that it's a numeric string */
288  for( i = 0; i < dataLength; i++ )
289  {
290  if( !isDigit( dataPtr[ i ] ) )
291  {
292  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
293  return( CRYPT_ARGERROR_STR1 );
294  }
295  }
296  return( CRYPT_OK );
297  }
298 
299  case BER_STRING_IA5:
300  case BER_STRING_ISO646:
302  /* Make sure that it's an ASCII string of the correct type */
303  if( !checkTextStringData( data, dataLength,
304  ( attributeInfoPtr->fieldType == BER_STRING_PRINTABLE ) ? \
305  TRUE : FALSE ) )
306  {
307  *errorType = CRYPT_ERRTYPE_ATTR_VALUE;
308  return( CRYPT_ARGERROR_STR1 );
309  }
310  return( CRYPT_OK );
311  }
312 
313  return( CRYPT_OK );
314  }
315 
316 /* Find the location in the attribute list at which to insert a new attribute
317  field */
318 
320 static int findFieldInsertLocation( IN_OPT const ATTRIBUTE_LIST *attributeListPtr,
321  OUT_OPT_PTR ATTRIBUTE_LIST **insertPointPtrPtr,
322  IN_ATTRIBUTE \
323  const CRYPT_ATTRIBUTE_TYPE fieldID,
325  const CRYPT_ATTRIBUTE_TYPE subFieldID )
326  {
327  const ATTRIBUTE_LIST *insertPoint, *prevElement = NULL;
328  int iterationCount;
329 
330  assert( attributeListPtr == NULL || \
331  isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST * ) ) );
332  assert( isWritePtr( insertPointPtrPtr, sizeof( ATTRIBUTE_LIST ) ) );
333 
334  REQUIRES( fieldID >= CRYPT_CERTINFO_FIRST_EXTENSION && \
335  fieldID <= CRYPT_CERTINFO_LAST );
336  REQUIRES( ( subFieldID == CRYPT_ATTRIBUTE_NONE ) || \
337  ( fieldID >= CRYPT_CERTINFO_FIRST_EXTENSION && \
338  fieldID <= CRYPT_CERTINFO_LAST ) );
339 
340  /* Clear return value */
341  *insertPointPtrPtr = NULL;
342 
343  /* Find the location at which to insert this attribute field in a list
344  sorted in order of fieldID */
345  for( insertPoint = attributeListPtr, iterationCount = 0;
346  insertPoint != NULL && \
347  insertPoint->fieldID != CRYPT_ATTRIBUTE_NONE && \
348  insertPoint->fieldID <= fieldID && \
349  iterationCount < FAILSAFE_ITERATIONS_LARGE_SPECIAL;
350  iterationCount++ )
351  {
352  ENSURES( insertPoint->next == NULL || \
353  !isValidAttributeField( insertPoint->next ) || \
354  insertPoint->attributeID <= insertPoint->next->attributeID );
355 
356  /* If it's a composite field that can have multiple fields with the
357  same field ID (e.g. a GeneralName), exit if the overall field ID
358  is greater (which means that the component belongs to a different
359  field entirely) or if the field ID is the same and the subfield
360  ID is greater (which means that the component belongs to a
361  different subfield within the field) */
362  if( subFieldID != CRYPT_ATTRIBUTE_NONE && \
363  insertPoint->fieldID == fieldID && \
364  insertPoint->subFieldID > subFieldID )
365  break;
366 
367  prevElement = insertPoint;
368  insertPoint = insertPoint->next;
369  }
370  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE_SPECIAL );
371  *insertPointPtrPtr = ( ATTRIBUTE_LIST * ) prevElement;
372 
373  return( CRYPT_OK );
374  }
375 
376 /****************************************************************************
377 * *
378 * Add Attribute Data *
379 * *
380 ****************************************************************************/
381 
382 /* Add a blob-type attribute to a list of attributes */
383 
384 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3, 6 ) ) \
385 int addAttribute( IN_ATTRIBUTE const ATTRIBUTE_TYPE attributeType,
387  IN_BUFFER( oidLength ) const BYTE *oid,
388  IN_LENGTH_OID const int oidLength,
389  const BOOLEAN critical,
390  IN_BUFFER( dataLength ) const void *data,
391  IN_LENGTH_SHORT const int dataLength,
392  IN_FLAGS_Z( ATTR ) const int flags )
393  {
394  ATTRIBUTE_LIST *newElement, *insertPoint = NULL;
395 
396  assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
397  assert( isReadPtr( oid, oidLength ) );
398  assert( isReadPtr( data, dataLength ) );
399  assert( ( flags & ( ATTR_FLAG_IGNORED | ATTR_FLAG_BLOB ) ) || \
400  !cryptStatusError( checkObjectEncoding( data, dataLength ) ) );
401 
402  REQUIRES( attributeType == ATTRIBUTE_CERTIFICATE || \
403  attributeType == ATTRIBUTE_CMS );
404  REQUIRES( oidLength >= MIN_OID_SIZE && oidLength <= MAX_OID_SIZE && \
405  oidLength == sizeofOID( oid ) );
406  REQUIRES( data != NULL && \
407  dataLength > 0 && dataLength <= MAX_ATTRIBUTE_SIZE );
408 assert( ( flags & ~( ATTR_FLAG_NONE | ATTR_FLAG_IGNORED | ATTR_FLAG_BLOB | ATTR_FLAG_MULTIVALUED ) ) == 0 );
409  REQUIRES( flags == ATTR_FLAG_NONE || flags == ATTR_FLAG_BLOB || \
410  flags == ( ATTR_FLAG_BLOB | ATTR_FLAG_IGNORED ) );
411 
412  /* If this attribute type is already handled as a non-blob attribute,
413  don't allow it to be added as a blob as well. This avoids problems
414  with the same attribute being added twice, once as a blob and once as
415  a non-blob. In addition it forces the caller to use the (recommended)
416  normal attribute handling mechanism, which allows for proper type
417  checking */
418  if( !( flags & ATTR_FLAG_BLOB ) && \
419  oidToAttribute( attributeType, oid, oidLength ) != NULL )
420  return( CRYPT_ERROR_PERMISSION );
421 
422  /* Find the correct place in the list to insert the new element */
423  if( *listHeadPtr != NULL )
424  {
425  ATTRIBUTE_LIST *prevElement = NULL;
426  int iterationCount;
427 
428  for( insertPoint = *listHeadPtr, iterationCount = 0;
429  insertPoint != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
430  insertPoint = insertPoint->next, iterationCount++ )
431  {
432  /* Make sure that this blob attribute isn't already present */
433  if( checkAttributeProperty( insertPoint,
435  sizeofOID( insertPoint->oid ) == oidLength && \
436  !memcmp( insertPoint->oid, oid, oidLength ) )
437  return( CRYPT_ERROR_INITED );
438 
439  prevElement = insertPoint;
440  }
441  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
442  insertPoint = prevElement;
443  }
444 
445  /* Allocate memory for the new element and copy the information across.
446  The data is stored in storage ... storage + dataLength, the OID in
447  storage + dataLength ... storage + dataLength + oidLength */
448  if( ( newElement = ( ATTRIBUTE_LIST * ) \
449  clAlloc( "addAttribute", sizeof( ATTRIBUTE_LIST ) + \
450  dataLength + oidLength ) ) == NULL )
451  return( CRYPT_ERROR_MEMORY );
452  initVarStruct( newElement, ATTRIBUTE_LIST, dataLength + oidLength );
453  newElement->oid = newElement->storage + dataLength;
454  memcpy( newElement->oid, oid, oidLength );
455  newElement->flags = ( flags & ATTR_FLAG_IGNORED ) | \
456  ( critical ? ATTR_FLAG_CRITICAL : ATTR_FLAG_NONE );
457  memcpy( newElement->value, data, dataLength );
458  newElement->valueLength = dataLength;
459  insertDoubleListElements( ( ATTRIBUTE_LIST ** ) listHeadPtr,
460  insertPoint, newElement, newElement );
461 
462  return( CRYPT_OK );
463  }
464 
465 /* Add an attribute field to a list of attributes at the appropriate
466  location */
467 
468 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 6, 7 ) ) \
469 int addAttributeField( INOUT ATTRIBUTE_PTR **listHeadPtr,
470  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE fieldID,
472  const CRYPT_ATTRIBUTE_TYPE subFieldID,
473  IN_INT_Z const int value,
474  IN_FLAGS_Z( ATTR ) const int flags,
475  OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
476  CRYPT_ATTRIBUTE_TYPE *errorLocus,
477  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
478  CRYPT_ERRTYPE_TYPE *errorType )
479  {
480  const ATTRIBUTE_TYPE attributeType = \
481  ( fieldID >= CRYPT_CERTINFO_FIRST_CMS ) ? \
484  const ATTRIBUTE_INFO *attributeInfoPtr = fieldIDToAttribute( attributeType,
485  fieldID, subFieldID, &attributeID );
486  ATTRIBUTE_LIST **attributeListPtr = ( ATTRIBUTE_LIST ** ) listHeadPtr;
487  ATTRIBUTE_LIST *newElement, *insertPoint;
488  int status;
489 
490  assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
491  assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
492  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
493  assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
494 
495  REQUIRES( fieldID >= CRYPT_CERTINFO_FIRST_EXTENSION && \
496  fieldID <= CRYPT_CERTINFO_LAST );
497  REQUIRES( subFieldID == CRYPT_ATTRIBUTE_NONE || \
498  ( subFieldID >= CRYPT_CERTINFO_FIRST_NAME && \
499  subFieldID <= CRYPT_CERTINFO_LAST_GENERALNAME ) );
500 assert( ( flags & ~( ATTR_FLAG_CRITICAL | ATTR_FLAG_MULTIVALUED | ATTR_FLAG_BLOB_PAYLOAD ) ) == 0 );
501  REQUIRES( flags >= ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
502 
503  /* Sanity-check the state */
504  ENSURES( attributeInfoPtr != NULL );
505 
506  /* Check the field's validity */
507  status = checkAttributeField( *attributeListPtr, attributeInfoPtr,
508  fieldID, subFieldID, value, flags,
509  errorType );
510  if( cryptStatusError( status ) )
511  {
512  if( *errorType != CRYPT_ERRTYPE_NONE )
513  {
514  /* If we encountered an error that sets the error type, record
515  the locus as well */
516  *errorLocus = fieldID;
517  }
518  return( status );
519  }
520 
521  /* Find the location at which to insert this attribute field */
522  status = findFieldInsertLocation( *attributeListPtr, &insertPoint,
523  fieldID, subFieldID );
524  ENSURES( cryptStatusOK( status ) );
525 
526  /* Allocate memory for the new element and copy the information across */
527  if( ( newElement = ( ATTRIBUTE_LIST * ) \
528  clAlloc( "addAttributeField", \
529  sizeof( ATTRIBUTE_LIST ) ) ) == NULL )
530  return( CRYPT_ERROR_MEMORY );
531  memset( newElement, 0, sizeof( ATTRIBUTE_LIST ) );
532  newElement->attributeID = attributeID;
533  newElement->fieldID = fieldID;
534  newElement->subFieldID = subFieldID;
535  newElement->flags = flags;
536  newElement->fieldType = attributeInfoPtr->fieldType;
537  switch( attributeInfoPtr->fieldType )
538  {
539  case BER_BOOLEAN:
540  /* Force it to the correct type if it's a boolean */
541  newElement->intValue = value ? TRUE : FALSE;
542  break;
543 
544  case BER_INTEGER:
545  case BER_ENUMERATED:
546  case BER_BITSTRING:
547  case BER_NULL:
548  case FIELDTYPE_CHOICE:
549  newElement->intValue = value;
550  if( attributeInfoPtr->fieldType == FIELDTYPE_CHOICE )
551  {
552  /* For encoding purposes the subfield ID is set to the ID of
553  the CHOICE selection */
554  newElement->subFieldID = newElement->intValue;
555  }
556  break;
557 
558  case FIELDTYPE_DN:
559  /* This type is present in both addAttributeField() and
560  addAttributeFieldString(), when creating a new certificate
561  this is a placeholder to indicate that a DN structure is being
562  instantiated (value == CRYPT_UNUSED) and when reading an
563  encoded certificate this is the decoded DN structure
564  (data == DN_PTR) */
565  ENSURES( value == CRYPT_UNUSED );
566  break;
567 
569  /* This is a placeholder entry with no explicit value */
570  newElement->intValue = CRYPT_UNUSED;
571  break;
572 
573  }
574  insertDoubleListElement( attributeListPtr, insertPoint, newElement );
575 
576  return( CRYPT_OK );
577  }
578 
579 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4, 7, 8 ) ) \
580 int addAttributeFieldString( INOUT ATTRIBUTE_PTR **listHeadPtr,
581  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE fieldID,
583  const CRYPT_ATTRIBUTE_TYPE subFieldID,
584  IN_BUFFER( dataLength ) const void *data,
585  IN_LENGTH_ATTRIBUTE const int dataLength,
586  IN_FLAGS_Z( ATTR ) const int flags,
587  OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
588  CRYPT_ATTRIBUTE_TYPE *errorLocus,
589  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
590  CRYPT_ERRTYPE_TYPE *errorType )
591  {
592  const ATTRIBUTE_TYPE attributeType = \
593  ( fieldID >= CRYPT_CERTINFO_FIRST_CMS ) ? \
596  const ATTRIBUTE_INFO *attributeInfoPtr = fieldIDToAttribute( attributeType,
597  fieldID, subFieldID, &attributeID );
598  ATTRIBUTE_LIST **attributeListPtr = ( ATTRIBUTE_LIST ** ) listHeadPtr;
599  ATTRIBUTE_LIST *newElement, *insertPoint;
600  int storageSize = 0, newDataLength, status;
601 
602  assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
603  assert( isReadPtr( data, dataLength ) );
604  assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
605  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
606  assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
607 
608  REQUIRES( fieldID >= CRYPT_CERTINFO_FIRST_EXTENSION && \
609  fieldID <= CRYPT_CERTINFO_LAST );
610  REQUIRES( subFieldID == CRYPT_ATTRIBUTE_NONE || \
611  ( subFieldID >= CRYPT_CERTINFO_FIRST_NAME && \
612  subFieldID <= CRYPT_CERTINFO_LAST_GENERALNAME ) );
613  REQUIRES( dataLength > 0 && dataLength <= MAX_ATTRIBUTE_SIZE );
614 assert( ( flags & ~( ATTR_FLAG_BLOB_PAYLOAD | ATTR_FLAG_CRITICAL | ATTR_FLAG_MULTIVALUED | ATTR_FLAG_BLOB_PAYLOAD ) ) == 0 );
615  REQUIRES( flags >= ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
616 
617  /* Sanity-check the state */
618  ENSURES( attributeInfoPtr != NULL );
619 
620  /* Check the field's validity */
621  status = checkAttributeFieldString( *attributeListPtr, attributeInfoPtr,
622  fieldID, subFieldID, data,
623  dataLength, flags, &newDataLength,
624  errorType );
625  if( cryptStatusError( status ) )
626  {
627  if( *errorType != CRYPT_ERRTYPE_NONE )
628  {
629  /* If we encountered an error that sets the error type, record
630  the locus as well */
631  *errorLocus = fieldID;
632  }
633  return( status );
634  }
635 
636  /* Find the location at which to insert this attribute field */
637  status = findFieldInsertLocation( *attributeListPtr, &insertPoint,
638  fieldID, subFieldID );
639  ENSURES( cryptStatusOK( status ) );
640 
641  /* Allocate memory for the new element and copy the information across */
642  if( newDataLength != 0 )
643  {
644  ENSURES( attributeInfoPtr->fieldType == BER_OBJECT_IDENTIFIER );
645 
646  /* The length has changed due to data en/decoding, use the
647  en/decoded length for the storage size */
648  storageSize = newDataLength;
649  }
650  else
651  {
652  /* If it's not a DN pointer then we copy the data in as is */
653  if( attributeInfoPtr->fieldType != FIELDTYPE_DN )
654  storageSize = dataLength;
655  }
656  if( ( newElement = ( ATTRIBUTE_LIST * ) \
657  clAlloc( "addAttributeField", sizeof( ATTRIBUTE_LIST ) + \
658  storageSize ) ) == NULL )
659  return( CRYPT_ERROR_MEMORY );
660  initVarStruct( newElement, ATTRIBUTE_LIST, storageSize );
661  newElement->attributeID = attributeID;
662  newElement->fieldID = fieldID;
663  newElement->subFieldID = subFieldID;
664  newElement->flags = flags;
665  newElement->fieldType = attributeInfoPtr->fieldType;
666  switch( attributeInfoPtr->fieldType )
667  {
669  /* If it's a BER/DER-encoded OID copy it in as is, otherwise
670  convert it from the text form */
671  if( ( ( BYTE * ) data )[ 0 ] == BER_OBJECT_IDENTIFIER )
672  {
673  memcpy( newElement->value, data, dataLength );
674  newElement->valueLength = dataLength;
675  }
676  else
677  {
678  status = textToOID( data, dataLength, newElement->value,
679  storageSize, &newElement->valueLength );
680  ENSURES( cryptStatusOK( status ) );
681  }
682  break;
683 
684  case FIELDTYPE_DN:
685  /* This type is present in both addAttributeField() and
686  addAttributeFieldString(), when creating a new certificate
687  this is a placeholder to indicate that a DN structure is being
688  instantiated (value == CRYPT_UNUSED) and when reading an
689  encoded certificate this is the decoded DN structure
690  (data == DN_PTR) */
691  newElement->value = ( void * ) data;
692  break;
693 
694  default:
695  memcpy( newElement->value, data, dataLength );
696  newElement->valueLength = dataLength;
697  break;
698  }
699  insertDoubleListElement( attributeListPtr, insertPoint, newElement );
700 
701  return( CRYPT_OK );
702  }
703 
704 /****************************************************************************
705 * *
706 * Delete Attribute Data *
707 * *
708 ****************************************************************************/
709 
710 /* Delete an attribute/attribute field from a list of attributes, updating
711  the list cursor at the same time. This is a somewhat ugly kludge, it's
712  not really possible to do this cleanly since deleting attributes affects
713  the attribute cursor */
714 
715 RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
716 int deleteAttributeField( INOUT ATTRIBUTE_PTR **attributePtr,
718  INOUT ATTRIBUTE_PTR *listItemPtr,
719  IN_OPT const DN_PTR *dnCursor )
720  {
721  ATTRIBUTE_LIST *listItem = ( ATTRIBUTE_LIST * ) listItemPtr;
722  ATTRIBUTE_LIST *listPrevPtr = listItem->prev;
723  ATTRIBUTE_LIST *listNextPtr = listItem->next;
724  BOOLEAN deletedDN = FALSE;
725 
726  assert( isWritePtr( attributePtr, sizeof( ATTRIBUTE_LIST * ) ) );
727  assert( isWritePtr( *attributePtr, sizeof( ATTRIBUTE_LIST ) ) );
728  assert( listCursorPtr == NULL || \
729  isWritePtr( listCursorPtr, sizeof( ATTRIBUTE_LIST * ) ) );
730  assert( isWritePtr( listItem, sizeof( ATTRIBUTE_LIST ) ) );
731  assert( dnCursor == NULL || \
732  isReadPtr( dnCursor, sizeof( DN_PTR_STORAGE ) ) );
733 
734  /* If we're about to delete the field that's pointed to by the attribute
735  cursor, advance the cursor to the next field. If there's no next
736  field, move it to the previous field. This behaviour is the most
737  logically consistent, it means that we can do things like deleting an
738  entire attribute list by repeatedly deleting a field */
739  if( listCursorPtr != NULL && *listCursorPtr == listItem )
740  *listCursorPtr = ( listNextPtr != NULL ) ? listNextPtr : listPrevPtr;
741 
742  /* Remove the item from the list */
743  deleteDoubleListElement( attributePtr, listItem );
744 
745  /* Clear all data in the item and free the memory */
746  if( listItem->fieldType == FIELDTYPE_DN )
747  {
748  /* If we've deleted the DN at the current cursor position, remember
749  this so that we can warn the caller */
750  if( dnCursor != NULL && dnCursor == &listItem->value )
751  deletedDN = TRUE;
752  deleteDN( ( DN_PTR ** ) &listItem->value );
753  }
754  endVarStruct( listItem, ATTRIBUTE_LIST );
755  clFree( "deleteAttributeField", listItem );
756 
757  /* If we deleted the DN at the current cursor position return a
758  special-case code to let the caller know */
759  return( deletedDN ? OK_SPECIAL : CRYPT_OK );
760  }
761 
762 RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
763 int deleteCompositeAttributeField( INOUT ATTRIBUTE_PTR **attributePtr,
764  INOUT ATTRIBUTE_PTR **listCursorPtr,
765  INOUT ATTRIBUTE_PTR *listItemPtr,
766  IN_OPT const DN_PTR *dnCursor )
767  {
768  ATTRIBUTE_LIST *listItem = ( ATTRIBUTE_LIST * ) listItemPtr;
769  const CRYPT_ATTRIBUTE_TYPE attributeID = listItem->attributeID;
770  const CRYPT_ATTRIBUTE_TYPE fieldID = listItem->fieldID;
772  BOOLEAN deletedDN = FALSE;
773  int iterationCount;
774 
775  assert( isWritePtr( attributePtr, sizeof( ATTRIBUTE_LIST * ) ) );
776  assert( isWritePtr( listCursorPtr, sizeof( ATTRIBUTE_LIST * ) ) );
777  assert( isWritePtr( listItem, sizeof( ATTRIBUTE_LIST ) ) );
778  assert( dnCursor == NULL || \
779  isReadPtr( dnCursor, sizeof( DN_PTR_STORAGE ) ) );
780 
781  /* Delete an attribute field that contains one or more entries of a
782  subfield. This is typically used with GeneralName fields, where the
783  fieldID for the GeneralName is further divided into DN, URI, email
784  address, and so on, subfields */
785  for( attributeListCursor = listItem, \
786  iterationCount = 0;
787  attributeListCursor != NULL && \
788  attributeListCursor->attributeID == attributeID && \
789  attributeListCursor->fieldID == fieldID && \
790  iterationCount < FAILSAFE_ITERATIONS_MED;
791  iterationCount++ )
792  {
793  ATTRIBUTE_LIST *itemToFree = attributeListCursor;
794 
795  attributeListCursor = attributeListCursor->next;
796  if( deleteAttributeField( attributePtr, listCursorPtr,
797  itemToFree, dnCursor ) == OK_SPECIAL )
798  {
799  /* If we've deleted the DN at the current cursor position,
800  remember this so that we can warn the caller */
801  deletedDN = TRUE;
802  }
803  }
804  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
805 
806  /* If we deleted the DN at the current cursor position return a
807  special-case code to let the caller know */
808  return( deletedDN ? OK_SPECIAL : CRYPT_OK );
809  }
810 
811 RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
812 int deleteAttribute( INOUT ATTRIBUTE_PTR **attributeListPtr,
813  INOUT_OPT ATTRIBUTE_PTR **listCursorPtr,
814  INOUT ATTRIBUTE_PTR *listItemPtr,
815  IN_OPT const DN_PTR *dnCursor )
816  {
818  ATTRIBUTE_LIST *listItem = ( ATTRIBUTE_LIST * ) listItemPtr;
820  int iterationCount, status = CRYPT_OK;
821 
822  assert( isWritePtr( attributeListPtr, sizeof( ATTRIBUTE_LIST * ) ) );
823  assert( isWritePtr( *attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
824  assert( listCursorPtr == NULL || \
825  isWritePtr( listCursorPtr, sizeof( ATTRIBUTE_LIST * ) ) );
826  assert( isWritePtr( listItem, sizeof( ATTRIBUTE_LIST ) ) );
827  assert( dnCursor == NULL || \
828  isReadPtr( dnCursor, sizeof( DN_PTR_STORAGE ) ) );
829 
830  /* If it's a blob-type attribute, everything is contained in this one
831  list item so we only need to destroy that */
832  if( checkAttributeProperty( listItem,
834  {
835  return( deleteAttributeField( attributeListPtr, listCursorPtr,
836  listItem, NULL ) );
837  }
838 
839  /* Complete attributes should be deleted with deleteCompleteAttribute() */
840  assert( !checkAttributeProperty( listItem,
842 
843  /* Find the start of the fields in this attribute */
844  attributeListCursor = findAttributeStart( listItem );
845  ENSURES( attributeListCursor != NULL );
846  attributeID = attributeListCursor->attributeID;
847 
848  /* It's an item with multiple fields, destroy each field separately */
849  for( iterationCount = 0;
850  attributeListCursor != NULL && \
851  attributeListCursor->attributeID == attributeID && \
852  iterationCount < FAILSAFE_ITERATIONS_LARGE;
853  iterationCount++ )
854  {
855  ATTRIBUTE_LIST *itemToFree = attributeListCursor;
856  int localStatus;
857 
858  attributeListCursor = attributeListCursor->next;
859  localStatus = deleteAttributeField( attributeListPtr, listCursorPtr,
860  itemToFree, dnCursor );
861  if( cryptStatusError( localStatus ) && status != OK_SPECIAL )
862  {
863  /* Remember the error code, giving priority to DN cursor-
864  modification notifications */
865  status = localStatus;
866  }
867  }
868  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
869 
870  return( status );
871  }
872 
873 RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
874 int deleteCompleteAttribute( INOUT ATTRIBUTE_PTR **attributeListPtr,
875  INOUT ATTRIBUTE_PTR **listCursorPtr,
876  const CRYPT_ATTRIBUTE_TYPE attributeID,
877  IN_OPT const DN_PTR *dnCursor )
878  {
880  int iterationCount;
881 
882  assert( isWritePtr( attributeListPtr, sizeof( ATTRIBUTE_LIST * ) ) );
883  assert( isWritePtr( *attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
884  assert( isWritePtr( listCursorPtr, sizeof( ATTRIBUTE_LIST * ) ) );
885  assert( dnCursor == NULL || \
886  isReadPtr( dnCursor, sizeof( DN_PTR_STORAGE ) ) );
887 
888  REQUIRES( attributeID >= CRYPT_CERTINFO_FIRST_EXTENSION && \
889  attributeID <= CRYPT_CERTINFO_LAST );
890 
891  /* We're deleting an entire (constructed) attribute that won't have an
892  entry in the list so we find the first field of the constructed
893  attribute that's present in the list and delete that */
894  for( attributeListCursor = *attributeListPtr, iterationCount = 0;
895  attributeListCursor != NULL && \
896  attributeListCursor->attributeID != attributeID && \
897  iterationCount < FAILSAFE_ITERATIONS_LARGE;
898  attributeListCursor = attributeListCursor->next, iterationCount++ );
899  ENSURES( attributeListCursor != NULL );
900  ENSURES( attributeListCursor->next == NULL || \
901  attributeListCursor->next->attributeID != \
902  attributeListCursor->attributeID );
903  return( deleteAttributeField( attributeListPtr, listCursorPtr,
904  attributeListCursor, dnCursor ) );
905  }
906 
907 /* Delete a complete set of attributes */
908 
909 STDC_NONNULL_ARG( ( 1 ) ) \
910 void deleteAttributes( INOUT ATTRIBUTE_PTR **attributeListPtr )
911  {
912  ATTRIBUTE_LIST *attributeListCursor = *attributeListPtr;
913  int iterationCount;
914 
915  assert( isWritePtr( attributeListPtr, sizeof( ATTRIBUTE_LIST * ) ) );
916 
917  /* If the list was empty, return now */
918  if( attributeListCursor == NULL )
919  return;
920 
921  /* Destroy any remaining list items */
922  for( iterationCount = 0;
923  attributeListCursor != NULL && \
924  iterationCount < FAILSAFE_ITERATIONS_LARGE_SPECIAL;
925  iterationCount++ )
926  {
927  ATTRIBUTE_LIST *itemToFree = attributeListCursor;
928 
929  attributeListCursor = attributeListCursor->next;
930  deleteAttributeField( attributeListPtr, NULL, itemToFree, NULL );
931  }
932  ENSURES_V( iterationCount < FAILSAFE_ITERATIONS_LARGE_SPECIAL );
933  ENSURES_V( *attributeListPtr == NULL );
934  }
935 #endif /* USE_CERTIFICATES */