cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
comp_curs.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Manage Certificate Attribute Cursors *
4 * Copyright Peter Gutmann 1997-2009 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "cert.h"
10  #include "certattr.h"
11 #else
12  #include "cert/cert.h"
13  #include "cert/certattr.h"
14 #endif /* Compiler-specific includes */
15 
16 /* GeneralNames and DNs are handled via indirect selection. There are four
17  classes of field type that cover these names:
18 
19  GNSelection = EXCLUDEDSUBTREES | ...
20  GNValue = OTHERNAME | ... | DIRECTORYNAME
21  DNSelection = SUBJECTNAME | ISSUERNAME | DIRECTORYNAME
22  DNValue = C | O | OU | CN | ...
23 
24  Note that DIRECTORYNAME is present twice since it's both a component of a
25  GeneralName and a DN in its own right. GNSelection and DNSelection
26  components merely select a composite component, the primitive elements are
27  read and written via the GN and DN values. The selection process is as
28  follows:
29 
30  GNSelection --+ (default = subjectAltName)
31  |
32  v
33  GN -+----------------> non-DirectoryName field
34  |
35  +--+ DirectoryName
36  |
37  DNSelection --+ (default = subjectName)
38  |
39  v
40  DN ------------------> DN field
41 
42  Selecting a component can therefore lead through a complex heirarchy of
43  explicit and implicit selections, in the worst case being something like
44  subjectAltName -> directoryName -> DN field. DN and GeneralName
45  components may be absent (if we're selecting it in order to create it),
46  present (if we're about to read it), or can be created when accessed (if
47  we're about to write to it). The handling is selected by the
48  SELECTION_OPTION type, if a certificate is in the high state then
49  MAY/CREATE options are implicitly converted to MUST_BE_PRESENT during the
50  selection process.
51 
52  The selection is performed as follows:
53 
54  set attribute:
55 
56  selectionComponent:
57  selectDN subject | issuer | MAY_BE_ABSENT
58  selectGN attributeID | MAY_BE_ABSENT
59  - Select prior to use
60 
61  valueComponent:
62  selectDN - | CREATE_IF_ABSENT
63  selectGN - | CREATE_IF_ABSENT
64  - To create DN/GeneralName before adding DN/GN
65  component/setting DN string
66 
67  get attribute:
68 
69  selectionComponent:
70  check subject | issuer | other | Presence check only
71  check attributeID
72  - Return T/F if present
73 
74  valueComponent:
75  selectDN none | MUST_BE_PRESENT
76  selectGN none | MUST_BE_PRESENT
77  - To get DN/GeneralName component
78 
79  delete attribute:
80 
81  selectDN subject | issuers | MUST_BE_PRESENT
82  selectGN attributeID | MUST_BE_PRESENT
83  - To delete DN/GeneralName component
84 
85  This code is cursed */
86 
87 #ifdef USE_CERTIFICATES
88 
89 /****************************************************************************
90 * *
91 * Utility Routines *
92 * *
93 ****************************************************************************/
94 
95 /* Check whether the currently selected extension is a GeneralName */
96 
98 static BOOLEAN isGeneralNameSelected( const CERT_INFO *certInfoPtr )
99  {
101  int status;
102 
103  assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
104 
105  if( certInfoPtr->attributeCursor == NULL )
106  return( FALSE );
107  status = getAttributeIdInfo( certInfoPtr->attributeCursor,
108  NULL, &fieldID, NULL );
109  if( cryptStatusError( status ) )
110  return( FALSE );
111  return( certInfoPtr->attributeCursor != NULL && \
112  isGeneralNameSelectionComponent( fieldID ) ? \
113  TRUE : FALSE );
114  }
115 
116 /* Sanity-check the selection state */
117 
119 static BOOLEAN sanityCheckSelectionInfo( const CERT_INFO *certInfoPtr )
120  {
121  const SELECTION_INFO *currentSelection = &certInfoPtr->currentSelection;
122 
123  assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
124 
125  /* If the DN-in-extension flag is set then there must be a DN selected */
126  if( currentSelection->dnPtr == NULL && currentSelection->dnInExtension )
127  return( FALSE );
128 
129  /* If there's a DN selected and it's not in an extension then it must be
130  the subject or issuer DN */
131  if( currentSelection->dnPtr != NULL && \
132  !currentSelection->dnInExtension && \
133  currentSelection->dnPtr != &certInfoPtr->subjectName && \
134  currentSelection->dnPtr != &certInfoPtr->issuerName )
135  return( FALSE );
136 
137  /* If there's a GeneralName selected there can't also be a saved
138  GeneralName present */
139  if( isGeneralNameSelected( certInfoPtr ) && \
140  currentSelection->generalName != CRYPT_ATTRIBUTE_NONE )
141  return( FALSE );
142 
143  /* The DN component count must be consistent with the DN selection
144  state */
145  if( currentSelection->dnComponent == CRYPT_ATTRIBUTE_NONE )
146  {
147  if( currentSelection->dnComponentCount != 0 )
148  return( FALSE );
149  }
150  else
151  {
152  if( currentSelection->dnComponentCount < 0 || \
153  currentSelection->dnComponentCount > MAX_INTLENGTH_SHORT )
154  return( FALSE );
155  }
156 
157  return( TRUE );
158  }
159 
160 /* Check whether there's a DN in the currently-selected GeneralName and
161  update the various selection values if we find one */
162 
164 static int findDnInGeneralName( INOUT CERT_INFO *certInfoPtr,
165  const BOOLEAN updateCursor )
166  {
168  DN_PTR **dnPtr;
169  int status;
170 
171  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
172 
173  /* We're inside a GeneralName, clear any possible saved selection */
174  certInfoPtr->currentSelection.generalName = CRYPT_ATTRIBUTE_NONE;
175 
176  REQUIRES( sanityCheckSelectionInfo( certInfoPtr ) );
177 
178  /* Search for a DN in the current GeneralName */
179  attributePtr = findDnInAttribute( certInfoPtr->attributeCursor );
180  if( attributePtr == NULL )
181  return( CRYPT_ERROR_NOTFOUND );
182 
183  /* We found a DN, select it */
184  status = getAttributeDataDN( attributePtr, &dnPtr );
185  if( cryptStatusError( status ) )
186  return( status );
187  certInfoPtr->currentSelection.dnPtr = dnPtr;
188  if( updateCursor )
189  certInfoPtr->attributeCursor = attributePtr;
190  certInfoPtr->currentSelection.dnInExtension = TRUE;
191 
192  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
193 
194  return( CRYPT_OK );
195  }
196 
197 /* Reset the current DN selection state. Note that this only resets the
198  metadata for the selection but not the selected DN itself */
199 
200 STDC_NONNULL_ARG( ( 1 ) ) \
201 static void resetDNselection( INOUT SELECTION_INFO *selectionInfoPtr )
202  {
203  assert( isWritePtr( selectionInfoPtr, sizeof( SELECTION_INFO ) ) );
204 
205  selectionInfoPtr->dnInExtension = FALSE;
206  selectionInfoPtr->dnComponent = CRYPT_ATTRIBUTE_NONE;
207  selectionInfoPtr->dnComponentCount = 0;
208  }
209 
210 /* Synchronise DN/GeneralName selection information after moving the
211  extension cursor */
212 
213 STDC_NONNULL_ARG( ( 1 ) ) \
214 static void syncSelection( INOUT CERT_INFO *certInfoPtr )
215  {
216  SELECTION_INFO *currentSelection = &certInfoPtr->currentSelection;
217 
218  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
219 
220  /* We don't apply a REQUIRES( sanityCheckSelectionInfo() ) precondition
221  here because the purpose of syncSelection() is to restore a
222  consistent state after moving the cursor */
223 
224  /* We've moved the cursor, clear any saved GeneralName selection */
225  currentSelection->generalName = CRYPT_ATTRIBUTE_NONE;
226 
227  /* I've we've moved the cursor off the GeneralName or there's no DN in
228  the GeneralName, deselect the DN */
229  if( !isGeneralNameSelected( certInfoPtr ) || \
230  cryptStatusError( findDnInGeneralName( certInfoPtr, FALSE ) ) )
231  {
232  resetDNselection( currentSelection );
233  currentSelection->dnPtr = NULL;
234  }
235 
236  ENSURES_V( sanityCheckSelectionInfo( certInfoPtr ) );
237  }
238 
239 /* Move the extension cursor to the given extension field */
240 
242 static int moveCursorToField( INOUT CERT_INFO *certInfoPtr,
243  IN_ATTRIBUTE \
245  {
247 
248  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
249 
250  REQUIRES( sanityCheckSelectionInfo( certInfoPtr ) );
251  REQUIRES( certInfoType >= CRYPT_CERTINFO_FIRST_EXTENSION && \
252  certInfoType <= CRYPT_CERTINFO_LAST );
253 
254  /* Try and locate the given field in the extension */
255  attributePtr = findAttributeField( certInfoPtr->attributes,
256  certInfoType, CRYPT_ATTRIBUTE_NONE );
257  if( attributePtr == NULL )
258  return( CRYPT_ERROR_NOTFOUND );
259 
260  /* We've found the given field, update the cursor and select the DN within
261  it if it's present */
262  certInfoPtr->currentSelection.updateCursor = FALSE;
263  certInfoPtr->attributeCursor = ( ATTRIBUTE_PTR * ) attributePtr;
264  if( isGeneralNameSelectionComponent( certInfoType ) )
265  {
266  /* If this is a GeneralName, select the DN within it if there's one
267  present. Since this is peripheral to the main operation of
268  moving the cursor we ignore the return status */
269  ( void ) findDnInGeneralName( certInfoPtr, FALSE );
270 
271  /* We've selected the GeneralName (possibly as a side-effect of its
272  on-demand creation), clear the saved GeneralName-to-be-created
273  value */
274  certInfoPtr->currentSelection.generalName = CRYPT_ATTRIBUTE_NONE;
275  }
276 
277  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
278 
279  return( CRYPT_OK );
280  }
281 
282 /****************************************************************************
283 * *
284 * GeneralName Selection Routines *
285 * *
286 ****************************************************************************/
287 
288 /* Determine whether a component which is being added to a certificate is a
289  GeneralName selection component */
290 
291 CHECK_RETVAL_BOOL \
293  const CRYPT_ATTRIBUTE_TYPE certInfoType )
294  {
295  static const CRYPT_ATTRIBUTE_TYPE certGeneralNameTbl[] = {
324  CRYPT_ATTRIBUTE_NONE, CRYPT_ATTRIBUTE_NONE
325  };
326  static const CRYPT_ATTRIBUTE_TYPE cmsGeneralNameTbl[] = {
330  CRYPT_ATTRIBUTE_NONE, CRYPT_ATTRIBUTE_NONE
331  };
332  const CRYPT_ATTRIBUTE_TYPE *generalNameTbl;
333  int generalNameTblSize, i;
334 
335  REQUIRES_B( isAttribute( certInfoType ) || \
336  isInternalAttribute( certInfoType ) );
337 
338  /* Determine which type of attribute we're dealing with */
339  if( certInfoType >= CRYPT_CERTINFO_FIRST_EXTENSION && \
340  certInfoType <= CRYPT_CERTINFO_LAST_EXTENSION )
341  {
342  generalNameTbl = certGeneralNameTbl;
343  generalNameTblSize = FAILSAFE_ARRAYSIZE( certGeneralNameTbl, \
345  }
346  else
347  {
348  if( certInfoType >= CRYPT_CERTINFO_FIRST_CMS && \
349  certInfoType <= CRYPT_CERTINFO_LAST_CMS )
350  {
351  generalNameTbl = cmsGeneralNameTbl;
352  generalNameTblSize = FAILSAFE_ARRAYSIZE( cmsGeneralNameTbl, \
354  }
355  else
356  {
357  /* It's neither a certificate nor a CMS attribute extension, it
358  can't be a GeneralName */
359  return( FALSE );
360  }
361  }
362 
363  /* Check for membership in the GeneralName set. In theory we could
364  divide this further via binary search but we're really reaching the
365  law of diminishing returns here */
366  for( i = 0; i < generalNameTblSize && \
367  generalNameTbl[ i ] != CRYPT_ATTRIBUTE_NONE; i++ )
368  {
369  if( generalNameTbl[ i ] == certInfoType )
370  return( TRUE );
371  }
372  ENSURES_B( i < generalNameTblSize );
373 
374  return( FALSE );
375  }
376 
377 /* Handle selection of a GeneralName or GeneralName component in a certificate
378  extension */
379 
381 int selectGeneralName( INOUT CERT_INFO *certInfoPtr,
382  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE certInfoType,
384  {
385  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
386 
387  REQUIRES( ( option == MAY_BE_ABSENT && \
388  isGeneralNameSelectionComponent( certInfoType ) ) || \
389  ( ( option == MUST_BE_PRESENT || option == CREATE_IF_ABSENT ) && \
390  certInfoType == CRYPT_ATTRIBUTE_NONE ) );
391  REQUIRES( sanityCheckSelectionInfo( certInfoPtr ) );
392 
393  /* At this point we may be trying to create or access a GeneralName for an
394  attribute that's been disabled through configuration options. This can
395  happen if the access is indirect, for example by setting
396  CRYPT_ATTRIBUTE_CURRENT to the GeneralName, which won't be blocked by
397  the kernel ACLs. Before we can continue we verify that the attribute
398  containing the GeneralName that we want to use is actually available */
399  if( option == MAY_BE_ABSENT && !checkAttributeAvailable( certInfoType ) )
400  return( CRYPT_ARGERROR_NUM1 );
401 
402  certInfoPtr->currentSelection.updateCursor = FALSE;
403 
404  if( option == MAY_BE_ABSENT )
405  {
406  /* If the selection is present, update the extension cursor and
407  exit */
408  if( cryptStatusOK( moveCursorToField( certInfoPtr, certInfoType ) ) )
409  return( CRYPT_OK );
410 
411  /* If the certificate is in the high state then the MAY is treated
412  as a MUST since we can't be selecting something in order to
413  create it later as we can for a certificate in the low state */
414  if( certInfoPtr->certificate != NULL )
415  return( CRYPT_ERROR_NOTFOUND );
416 
417  /* The selection isn't present, remember it for later without
418  changing any other selection information */
419  certInfoPtr->currentSelection.generalName = certInfoType;
420  certInfoPtr->attributeCursor = NULL;
421 
422  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
423 
424  return( CRYPT_OK );
425  }
426 
427  ENSURES( option == MUST_BE_PRESENT || option == CREATE_IF_ABSENT );
428 
429  /* If there's no saved GeneralName selection present, the extension
430  cursor must be pointing to a GeneralName */
431  if( certInfoPtr->currentSelection.generalName == CRYPT_ATTRIBUTE_NONE )
432  {
433  if( isGeneralNameSelected( certInfoPtr ) )
434  return( CRYPT_OK );
435 
436  /* If there's no GeneralName explicitly selected, try for the
437  default subject altName */
438  certInfoPtr->currentSelection.generalName = \
439  CRYPT_CERTINFO_SUBJECTALTNAME;
440  }
441 
442  /* Try and move the cursor to the saved GeneralName selection */
443  if( cryptStatusOK( \
444  moveCursorToField( certInfoPtr,
445  certInfoPtr->currentSelection.generalName ) ) )
446  return( CRYPT_OK );
447  if( option == MUST_BE_PRESENT )
448  return( CRYPT_ERROR_NOTFOUND );
449 
450  /* We're creating the GeneralName extension, deselect the current DN and
451  remember that we have to update the extension cursor when we've done
452  it */
453  resetDNselection( &certInfoPtr->currentSelection );
454  certInfoPtr->currentSelection.dnPtr = NULL;
455  certInfoPtr->currentSelection.updateCursor = TRUE;
456 
457  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
458 
459  return( CRYPT_OK );
460  }
461 
463 int selectGeneralNameComponent( INOUT CERT_INFO *certInfoPtr,
464  IN_ATTRIBUTE \
465  const CRYPT_ATTRIBUTE_TYPE certInfoType )
466  {
467  CRYPT_ATTRIBUTE_TYPE generalName;
469  int status;
470 
471  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
472 
473  REQUIRES( isGeneralNameComponent( certInfoType ) );
474  REQUIRES( sanityCheckSelectionInfo( certInfoPtr ) );
475 
476  /* To select a GeneralName component we first need to have a GeneralName
477  selected */
478  status = selectGeneralName( certInfoPtr, CRYPT_ATTRIBUTE_NONE,
479  MUST_BE_PRESENT );
480  if( cryptStatusError( status ) )
481  return( status );
482  ENSURES( isGeneralNameSelected( certInfoPtr ) );
483  /* Required for MUST_BE_PRESENT */
484 
485  /* We've got the required GeneralName selected, set the cursor to the
486  field within it */
487  status = getAttributeIdInfo( certInfoPtr->attributeCursor, NULL,
488  &generalName, NULL );
489  if( cryptStatusError( status ) )
490  return( status );
491  attributePtr = findAttributeField( certInfoPtr->attributeCursor,
492  generalName, certInfoType );
493  if( attributePtr == NULL )
494  return( CRYPT_ERROR_NOTFOUND );
495  certInfoPtr->currentSelection.updateCursor = FALSE;
496  certInfoPtr->attributeCursor = ( ATTRIBUTE_PTR * ) attributePtr;
497 
498  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
499 
500  return( CRYPT_OK );
501  }
502 
503 /****************************************************************************
504 * *
505 * DN Selection Routines *
506 * *
507 ****************************************************************************/
508 
509 /* Handle selection of DNs. The subject and issuer DNs are somewhat special
510  in that they can be selected like any other attribute but aren't actually
511  certificate extensions, so that some things that work with attributes-in-
512  extensions don't work with attributes-in-a-DN. The problem is the depth
513  of the nesting, for a DN in a GeneralName the CURRENT_GROUP is the
514  extension, the CURRENT_ATTRIBUTE is the GeneralName within it, and the
515  CURRENT_INSTANCE is the GeneralName component within that, while for the
516  standalone subject/issuer DNs there's no CURRENT_GROUP, the
517  CURRENT_ATTRIBUTE is the subject or issuer DN, and the CURRENT_INSTANCE
518  is the DN component. This means that we can't select repeated instances
519  of DN components in a GeneralName (at least not without introducing a
520  fourth level of nesting), but the use of DNs in GeneralNames is
521  practically nonexistent so this shouldn't be an issue */
522 
524 int selectDN( INOUT CERT_INFO *certInfoPtr,
525  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE certInfoType,
526  IN_ENUM( SELECTION_OPTION ) const SELECTION_OPTION option )
527  {
528  CRYPT_ATTRIBUTE_TYPE generalName = \
529  certInfoPtr->currentSelection.generalName;
530  int status;
531 
532  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
533 
534  REQUIRES( ( option == MAY_BE_ABSENT && \
535  isDNSelectionComponent( certInfoType ) ) || \
536  ( ( option == MUST_BE_PRESENT || option == CREATE_IF_ABSENT ) && \
537  certInfoType == CRYPT_ATTRIBUTE_NONE ) );
538  REQUIRES( sanityCheckSelectionInfo( certInfoPtr ) );
539 
540  if( option == MAY_BE_ABSENT )
541  {
542  /* Try and select a DN based on the supplied attribute ID */
543  switch( certInfoType )
544  {
546  certInfoPtr->currentSelection.dnPtr = &certInfoPtr->subjectName;
547  break;
548 
550  certInfoPtr->currentSelection.dnPtr = &certInfoPtr->issuerName;
551 
552  /* If it's a self-signed certificate and the issuer name
553  isn't explicitly present then it must be implicitly
554  present as the subject name */
555  if( certInfoPtr->issuerName == NULL && \
556  ( certInfoPtr->flags & CERT_FLAG_SELFSIGNED ) )
557  certInfoPtr->currentSelection.dnPtr = &certInfoPtr->subjectName;
558  break;
559 
560  default:
561  retIntError();
562  }
563 
564  /* We've selected a built-in DN, remember that this isn't one in an
565  (optional) extension and clear the current DN component
566  selection. In addition we clear the current extension cursor
567  since we've implicitly moved it away from the extensions to the
568  non-extension space of the built-in DNs */
569  resetDNselection( &certInfoPtr->currentSelection );
570  certInfoPtr->attributeCursor = NULL;
571 
572  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
573 
574  return( CRYPT_OK );
575  }
576 
577  /* If there's a DN already selected, we're done */
578  if( certInfoPtr->currentSelection.dnPtr != NULL )
579  return( CRYPT_OK );
580 
581  ENSURES( option == MUST_BE_PRESENT || option == CREATE_IF_ABSENT );
582 
583  /* To select a DN in a GeneralName we first need to have a GeneralName
584  selected */
585  status = selectGeneralName( certInfoPtr, CRYPT_ATTRIBUTE_NONE, option );
586  if( cryptStatusError( status ) )
587  return( status );
588 
589  /* If we've now got a GeneralName selected, try and find a DN in it.
590  The reason why we have to perform the explicit check is because if
591  the CREATE_IF_ABSENT option is used then the GeneralName has been
592  marked for creation when a field within it is added but won't
593  actually be created until the field is added further down */
594  if( isGeneralNameSelected( certInfoPtr ) )
595  {
596  /* If there's a DN currently selected, we're done */
597  if( checkAttributeProperty( certInfoPtr->attributeCursor, \
599  {
600  DN_PTR **dnPtr;
601 
602  status = getAttributeDataDN( certInfoPtr->attributeCursor,
603  &dnPtr );
604  if( cryptStatusError( status ) )
605  return( status );
606  resetDNselection( &certInfoPtr->currentSelection );
607  certInfoPtr->currentSelection.dnPtr = dnPtr;
608  certInfoPtr->currentSelection.dnInExtension = TRUE;
609 
610  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
611 
612  return( CRYPT_OK );
613  }
614 
615  /* There's no DN selected, see if there's one present somewhere in
616  the extension */
617  if( cryptStatusOK( findDnInGeneralName( certInfoPtr, TRUE ) ) )
618  return( CRYPT_OK );
619 
620  /* If there's no DN present and we're not about to create one,
621  exit */
622  if( option == MUST_BE_PRESENT )
623  return( CRYPT_ERROR_NOTFOUND );
624 
625  /* Create the DN in the currently selected GeneralName */
626  status = getAttributeIdInfo( certInfoPtr->attributeCursor, NULL,
627  &generalName, NULL );
628  if( cryptStatusError( status ) )
629  return( status );
630  }
631 
632  /* We're being asked to instantiate the DN, create the attribute field
633  that contains it */
634  status = addAttributeField( &certInfoPtr->attributes, generalName,
636  0, &certInfoPtr->errorLocus,
637  &certInfoPtr->errorType );
638  if( cryptStatusError( status ) )
639  return( status );
640 
641  /* Find the field that we've just created. This is a newly-created
642  attribute so it's the only one present (i.e we don't have to worry
643  about finding one added at the end of the sequence of identical
644  attributes) and we also know that it must be present since we've
645  just created it */
646  return( selectGeneralName( certInfoPtr, generalName, MAY_BE_ABSENT ) );
647  }
648 
650 static int selectDNComponent( INOUT CERT_INFO *certInfoPtr,
651  IN_ATTRIBUTE \
652  const CRYPT_ATTRIBUTE_TYPE certInfoType )
653  {
654  int status;
655 
656  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
657 
658  REQUIRES( isDNComponent( certInfoType ) );
659  REQUIRES( sanityCheckSelectionInfo( certInfoPtr ) );
660 
661  /* To select a DN component we first need to have a DN selected */
662  status = selectDN( certInfoPtr, CRYPT_ATTRIBUTE_NONE, MUST_BE_PRESENT );
663  if( cryptStatusError( status ) )
664  return( status );
665 
666  /* Remember the currently selected DN component */
667  certInfoPtr->currentSelection.dnComponent = certInfoType;
668  certInfoPtr->currentSelection.dnComponentCount = 0;
669 
670  ENSURES( sanityCheckSelectionInfo( certInfoPtr ) );
671 
672  return( CRYPT_OK );
673  }
674 
675 /****************************************************************************
676 * *
677 * Certificate Cursor Movement Routines *
678 * *
679 ****************************************************************************/
680 
681 /* Set certificate cursor information */
682 
684 static int setCursorCertChain( INOUT CERT_INFO *certInfoPtr,
686  CRYPT_CURSOR_FIRST ) /* Values are -ve */
687  const int cursorMoveType )
688  {
689  CERT_CERT_INFO *certChainInfo = certInfoPtr->cCertCert;
690 
691  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
692 
693  REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
694  REQUIRES( cursorMoveType >= CRYPT_CURSOR_LAST && \
695  cursorMoveType <= CRYPT_CURSOR_FIRST ); /* Values are -ve */
696 
697  switch( cursorMoveType )
698  {
699  case CRYPT_CURSOR_FIRST:
700  /* Set the chain position to -1 (= CRYPT_ERROR) to indicate that
701  it's at the leaf certificate, which is logically at position
702  -1 in the chain */
703  certChainInfo->chainPos = CRYPT_ERROR;
704  break;
705 
707  /* Adjust the chain position. Note that the value can go to -1
708  (= CRYPT_ERROR) to indicate that it's at the leaf certificate,
709  which is logically at position -1 in the chain */
710  if( certChainInfo->chainPos < 0 )
711  return( CRYPT_ERROR_NOTFOUND );
712  certChainInfo->chainPos--;
713  break;
714 
715  case CRYPT_CURSOR_NEXT:
716  if( certChainInfo->chainPos >= certChainInfo->chainEnd - 1 )
717  return( CRYPT_ERROR_NOTFOUND );
718  certChainInfo->chainPos++;
719  break;
720 
721  case CRYPT_CURSOR_LAST:
722  certChainInfo->chainPos = certChainInfo->chainEnd - 1;
723  break;
724 
725  default:
726  return( CRYPT_ARGERROR_NUM1 );
727  }
728 
729  return( CRYPT_OK );
730  }
731 
732 #ifdef USE_CERTVAL
733 
735 static int setCursorValInfo( INOUT CERT_INFO *certInfoPtr,
737  CRYPT_CURSOR_FIRST ) /* Values are -ve */
738  const int cursorMoveType )
739  {
740  CERT_VAL_INFO *certValInfo = certInfoPtr->cCertVal;
741  VALIDITY_INFO *valInfo = certValInfo->validityInfo;
742  int iterationCount;
743 
744  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
745 
746  REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST || \
747  certInfoPtr->type == CRYPT_CERTTYPE_RTCS_RESPONSE );
748  REQUIRES( cursorMoveType >= CRYPT_CURSOR_LAST && \
749  cursorMoveType <= CRYPT_CURSOR_FIRST ); /* Values are -ve */
750 
751  switch( cursorMoveType )
752  {
753  case CRYPT_CURSOR_FIRST:
754  certValInfo->currentValidity = certValInfo->validityInfo;
755  if( certValInfo->currentValidity == NULL )
756  return( CRYPT_ERROR_NOTFOUND );
757  break;
758 
760  if( valInfo == NULL || \
761  certValInfo->currentValidity == NULL || \
762  valInfo == certValInfo->currentValidity )
763  {
764  /* No validity information or we're already at the start of
765  the list */
766  return( CRYPT_ERROR_NOTFOUND );
767  }
768 
769  /* Find the previous element in the list */
770  for( iterationCount = 0;
771  valInfo != NULL && \
772  valInfo->next != certValInfo->currentValidity && \
773  iterationCount < FAILSAFE_ITERATIONS_LARGE;
774  valInfo = valInfo->next, iterationCount++ );
775  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
776  certValInfo->currentValidity = valInfo;
777  break;
778 
779  case CRYPT_CURSOR_NEXT:
780  if( certValInfo->currentValidity == NULL || \
781  certValInfo->currentValidity->next == NULL )
782  return( CRYPT_ERROR_NOTFOUND );
783  certValInfo->currentValidity = certValInfo->currentValidity->next;
784  break;
785 
786  case CRYPT_CURSOR_LAST:
787  if( valInfo == NULL )
788  {
789  /* No validity information present */
790  return( CRYPT_ERROR_NOTFOUND );
791  }
792 
793  /* Go to the end of the list */
794  for( iterationCount = 0;
795  valInfo->next != NULL && \
796  iterationCount < FAILSAFE_ITERATIONS_LARGE;
797  valInfo = valInfo->next, iterationCount++ );
798  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
799  certValInfo->currentValidity = valInfo;
800  break;
801 
802  default:
803  return( CRYPT_ARGERROR_NUM1 );
804  }
805 
806  return( CRYPT_OK );
807  }
808 #endif /* USE_CERTVAL */
809 
810 #ifdef USE_CERTREV
811 
813 static int setCursorRevInfo( INOUT CERT_INFO *certInfoPtr,
815  CRYPT_CURSOR_FIRST ) /* Values are -ve */
816  const int cursorMoveType )
817  {
818  CERT_REV_INFO *certRevInfo = certInfoPtr->cCertRev;
819  REVOCATION_INFO *revInfo = certRevInfo->revocations;
820  int iterationCount;
821 
822  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
823 
824  REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CRL || \
825  certInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST || \
826  certInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE );
827  REQUIRES( cursorMoveType >= CRYPT_CURSOR_LAST && \
828  cursorMoveType <= CRYPT_CURSOR_FIRST ); /* Values are -ve */
829 
830  switch( cursorMoveType )
831  {
832  case CRYPT_CURSOR_FIRST:
833  certRevInfo->currentRevocation = certRevInfo->revocations;
834  if( certRevInfo->currentRevocation == NULL )
835  return( CRYPT_ERROR_NOTFOUND );
836  break;
837 
839  if( revInfo == NULL || \
840  certRevInfo->currentRevocation == NULL || \
841  revInfo == certRevInfo->currentRevocation )
842  {
843  /* No revocations or we're already at the start of the
844  list */
845  return( CRYPT_ERROR_NOTFOUND );
846  }
847 
848  /* Find the previous element in the list. We use
849  FAILSAFE_ITERATIONS_MAX as the bound because CRLs can become
850  enormous */
851  for( iterationCount = 0;
852  revInfo != NULL && \
853  revInfo->next != certRevInfo->currentRevocation && \
854  iterationCount < FAILSAFE_ITERATIONS_MAX;
855  revInfo = revInfo->next, iterationCount++ );
856  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
857  certRevInfo->currentRevocation = revInfo;
858  break;
859 
860  case CRYPT_CURSOR_NEXT:
861  if( certRevInfo->currentRevocation == NULL || \
862  certRevInfo->currentRevocation->next == NULL )
863  return( CRYPT_ERROR_NOTFOUND );
864  certRevInfo->currentRevocation = certRevInfo->currentRevocation->next;
865  break;
866 
867  case CRYPT_CURSOR_LAST:
868  if( revInfo == NULL )
869  {
870  /* No revocations present */
871  return( CRYPT_ERROR_NOTFOUND );
872  }
873 
874  /* Go to the end of the list. We use FAILSAFE_ITERATIONS_MAX as
875  the bound because CRLs can become enormous */
876  for( iterationCount = 0;
877  revInfo->next != NULL && \
878  iterationCount < FAILSAFE_ITERATIONS_MAX;
879  revInfo = revInfo->next, iterationCount++ );
880  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
881  certRevInfo->currentRevocation = revInfo;
882  break;
883 
884  default:
885  return( CRYPT_ARGERROR_NUM1 );
886  }
887 
888  return( CRYPT_OK );
889  }
890 #endif /* USE_CERTREV */
891 
893 int setCertificateCursor( INOUT CERT_INFO *certInfoPtr,
895  CRYPT_CURSOR_FIRST ) /* Values are -ve */
896  const int cursorMoveType )
897  {
898  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
899 
900  REQUIRES( cursorMoveType >= CRYPT_CURSOR_LAST && \
901  cursorMoveType <= CRYPT_CURSOR_FIRST ); /* Values are -ve */
902 
903  /* If it's a single certificate, there's nothing to do. See the
904  CRYPT_CERTINFO_CURRENT_CERTIFICATE ACL comment for why we
905  (apparently) allow cursor movement movement in single certificates */
906  if( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE )
907  {
908  REQUIRES( certInfoPtr->cCertCert->chainEnd == 0 );
909 
910  return( ( cursorMoveType == CRYPT_CURSOR_FIRST || \
911  cursorMoveType == CRYPT_CURSOR_LAST ) ? \
913  }
914 
915  /* Move the cursor in an object-specific manner */
916  switch( certInfoPtr->type )
917  {
919  return( setCursorCertChain( certInfoPtr, cursorMoveType ) );
920 
921 #ifdef USE_CERTVAL
924  return( setCursorValInfo( certInfoPtr, cursorMoveType ) );
925 #endif /* USE_CERTVAL */
926 
927 #ifdef USE_CERTREV
928  case CRYPT_CERTTYPE_CRL:
931  return( setCursorRevInfo( certInfoPtr, cursorMoveType ) );
932 #endif /* USE_CERTREV */
933  }
934 
935  retIntError();
936  }
937 
938 /****************************************************************************
939 * *
940 * Attribute Cursor Movement Routines *
941 * *
942 ****************************************************************************/
943 
944 /* Set attribute cursor information */
945 
947 static int setAttributeCursorDN( INOUT SELECTION_INFO *currentSelection,
948  IN const int moveType )
949  {
950  const DN_PTR *dnComponentList = *currentSelection->dnPtr;
951  int count = 0, iterationCount;
952 
953  assert( isWritePtr( currentSelection, sizeof( SELECTION_INFO ) ) );
954 
955  REQUIRES( moveType <= CRYPT_CURSOR_FIRST && \
956  moveType >= CRYPT_CURSOR_LAST );
957 
958  switch( moveType )
959  {
960  case CRYPT_CURSOR_FIRST:
961  /* Select the first instance of this attribute */
962  currentSelection->dnComponentCount = 0;
963  break;
964 
966  /* Adjust the instance selection value */
967  if( currentSelection->dnComponentCount <= 0 )
968  return( CRYPT_ERROR_NOTFOUND );
969  currentSelection->dnComponentCount--;
970  break;
971 
972  case CRYPT_CURSOR_NEXT:
973  case CRYPT_CURSOR_LAST:
974  /* Find the number of occurrences of the DN component that we're
975  enumerating and use that to move the cursor, which is
976  actually just an iteration count of the number of components
977  to skip */
978  for( iterationCount = 0;
979  iterationCount < FAILSAFE_ITERATIONS_MED;
980  iterationCount++ )
981  {
982  int dummy;
983 
984  if( cryptStatusError( \
985  getDNComponentValue( dnComponentList,
986  currentSelection->dnComponent,
987  count + 1, NULL, 0, &dummy ) ) )
988  break;
989  count++;
990  }
991  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
992  if( moveType == CRYPT_CURSOR_LAST )
993  currentSelection->dnComponentCount = count;
994  else
995  {
996  if( currentSelection->dnComponentCount >= count )
997  return( CRYPT_ERROR_NOTFOUND );
998  currentSelection->dnComponentCount++;
999  }
1000  break;
1001 
1002  default:
1003  retIntError();
1004  }
1005 
1006  return( CRYPT_OK );
1007  }
1008 
1009 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1010 static int setAttributeCursorRelative( INOUT CERT_INFO *certInfoPtr,
1011  IN_ATTRIBUTE \
1012  const CRYPT_ATTRIBUTE_TYPE certInfoType,
1013  IN const int value )
1014  {
1015  ATTRIBUTE_PTR *attributeCursor;
1016 
1017  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
1018 
1019  REQUIRES( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP || \
1020  certInfoType == CRYPT_ATTRIBUTE_CURRENT || \
1021  certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE );
1022  REQUIRES( ( value <= CRYPT_CURSOR_FIRST && \
1023  value >= CRYPT_CURSOR_LAST ) || \
1024  ( value >= CRYPT_CERTINFO_FIRST_EXTENSION && \
1025  value <= CRYPT_CERTINFO_LAST_EXTENSION ) || \
1026  ( certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE && \
1027  ( isDNComponent( value ) || \
1028  isGeneralNameComponent( value ) ) ) );
1029  /* See comment in setCursorInfo for the odd CRYPT_CURSOR_xxx
1030  comparison */
1031 
1032  /* If we're moving to a field in an extension and there's a saved
1033  GeneralName selection present (which means that it's for a
1034  GeneralName that's not present yet but has been tagged for creation
1035  the next time that an attribute is added) then we can't move to a
1036  field in it since it hasn't been created yet */
1037  if( certInfoType != CRYPT_ATTRIBUTE_CURRENT_GROUP && \
1038  certInfoPtr->currentSelection.generalName != CRYPT_ATTRIBUTE_NONE )
1039  return( CRYPT_ERROR_NOTFOUND );
1040 
1041  /* The subject and issuer DNs aren't standard certificate extensions but
1042  (for cursor-positioning purposes) can be manipulated as such by moving
1043  from one instance (e.g. one OU) to the next. If there's no current
1044  attribute selected but there is a subject or issuer DN and a component
1045  within that DN selected (performing this DN selection inmplicitly de-
1046  selects any attribute, otherwise the selected DN could be one that's
1047  within a GeneralName in an attribute), then we allow a pseudo-move
1048  to/from identical DN components */
1049  if( certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE && \
1050  certInfoPtr->attributeCursor == NULL && \
1051  certInfoPtr->currentSelection.dnPtr != NULL && \
1052  certInfoPtr->currentSelection.dnComponent != CRYPT_ATTRIBUTE_NONE )
1053  {
1054  return( setAttributeCursorDN( &certInfoPtr->currentSelection,
1055  value ) );
1056  }
1057 
1058  /* If it's an absolute positioning code, pre-set the attribute cursor
1059  if required */
1060  if( value == CRYPT_CURSOR_FIRST || value == CRYPT_CURSOR_LAST )
1061  {
1062  if( certInfoPtr->attributes == NULL )
1063  return( CRYPT_ERROR_NOTFOUND );
1064 
1065  /* It's a full-attribute positioning code, reset the attribute
1066  cursor to the start of the list before we try to move it */
1067  if( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP )
1068  certInfoPtr->attributeCursor = certInfoPtr->attributes;
1069  else
1070  {
1071  /* It's a field or component positioning code, initialise the
1072  attribute cursor if necessary */
1073  if( certInfoPtr->attributeCursor == NULL )
1074  certInfoPtr->attributeCursor = certInfoPtr->attributes;
1075  }
1076 
1077  /* If there are no attributes present return the appropriate error
1078  code */
1079  if( certInfoPtr->attributeCursor == NULL )
1080  {
1081  return( ( value == CRYPT_CURSOR_FIRST || \
1082  value == CRYPT_CURSOR_LAST ) ? \
1084  }
1085  }
1086  else
1087  {
1088  /* It's a relative positioning code, return a not-inited error
1089  rather than a not-found error if the cursor isn't set since there
1090  may be attributes present but the cursor hasn't been initialised
1091  yet by selecting the first or last absolute attribute */
1092  if( certInfoPtr->attributeCursor == NULL )
1093  return( CRYPT_ERROR_NOTINITED );
1094  }
1095 
1096  /* Move the attribute cursor */
1097  attributeCursor = certMoveAttributeCursor( certInfoPtr->attributeCursor,
1098  certInfoType, value );
1099  if( attributeCursor == NULL )
1100  return( CRYPT_ERROR_NOTFOUND );
1101  certInfoPtr->attributeCursor = attributeCursor;
1102  syncSelection( certInfoPtr );
1103 
1104  return( CRYPT_OK );
1105  }
1106 
1107 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1108 int setAttributeCursor( INOUT CERT_INFO *certInfoPtr,
1109  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE certInfoType,
1110  IN const int value )
1111  {
1112  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
1113 
1114  REQUIRES( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP || \
1115  certInfoType == CRYPT_ATTRIBUTE_CURRENT || \
1116  certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE );
1117  REQUIRES( ( value <= CRYPT_CURSOR_FIRST && \
1118  value >= CRYPT_CURSOR_LAST ) ||
1119  ( value >= CRYPT_CERTINFO_FIRST_EXTENSION && \
1120  value <= CRYPT_CERTINFO_LAST_EXTENSION ) ||
1121  ( certInfoType == CRYPT_ATTRIBUTE_CURRENT && \
1122  ( value == CRYPT_CERTINFO_ISSUERNAME || \
1123  value == CRYPT_CERTINFO_SUBJECTNAME ) ) ||
1124  ( certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE && \
1125  ( isDNComponent( value ) || \
1126  isGeneralNameComponent( value ) ) ) );
1127  /* See comment below for the odd CRYPT_CURSOR_xxx comparison */
1128 
1129  /* If the new position is specified relative to a previous position, try
1130  and move to that position. Note that the seemingly illogical
1131  comparison is used because the cursor positioning codes are negative
1132  values */
1134  {
1135  return( setAttributeCursorRelative( certInfoPtr, certInfoType,
1136  value ) );
1137  }
1138 
1139  /* It's a field in an extension, try and move to the start of the
1140  extension that contains this field */
1141  if( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP )
1142  {
1144 
1145  attributePtr = findAttribute( certInfoPtr->attributes, value, TRUE );
1146  if( attributePtr == NULL )
1147  return( CRYPT_ERROR_NOTFOUND );
1148  certInfoPtr->attributeCursor = attributePtr;
1149  syncSelection( certInfoPtr );
1150  return( CRYPT_OK );
1151  }
1152 
1153  /* Beyond the standard attribute-selection values there are two special
1154  cases that we have to deal with. The subject and issuer DN aren't
1155  standard attributes but can be selected as if they were in order to
1156  perform extended operations on them, and GeneralName components are
1157  deeply nested enough that what's a per-instance operation for any
1158  other attribute type becomes an attribute subtype in its own right */
1159  ENSURES( certInfoType == CRYPT_ATTRIBUTE_CURRENT || \
1160  certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE );
1161  ENSURES( ( value >= CRYPT_CERTINFO_FIRST_EXTENSION && \
1162  value <= CRYPT_CERTINFO_LAST_EXTENSION ) || \
1163  ( certInfoType == CRYPT_ATTRIBUTE_CURRENT && \
1164  ( value == CRYPT_CERTINFO_ISSUERNAME || \
1165  value == CRYPT_CERTINFO_SUBJECTNAME ) ) ||
1166  ( certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE && \
1167  ( isDNComponent( value ) || \
1168  isGeneralNameComponent( value ) ) ) );
1169 
1170  /* If it's a GeneralName selection or GeneralName component, locate the
1171  attribute field that it corresponds to. Note the difference in
1172  selection options, for the GeneralName as a whole we're indicating
1173  which GeneralName we want to work with, including potentially
1174  creating it when we set the first field in it while for a
1175  GeneralName component we're merely selecting a field in an already-
1176  existing GeneralName */
1177  if( isGeneralNameSelectionComponent( value ) )
1178  return( selectGeneralName( certInfoPtr, value, MAY_BE_ABSENT ) );
1179  if( isGeneralNameComponent( value ) )
1180  return( selectGeneralNameComponent( certInfoPtr, value ) );
1181 
1182  /* If it's a DN, select it. If it's a DN component, locate the RDN that
1183  it corresponds to */
1184  if( value == CRYPT_CERTINFO_ISSUERNAME || \
1185  value == CRYPT_CERTINFO_SUBJECTNAME )
1186  return( selectDN( certInfoPtr, value, MAY_BE_ABSENT ) );
1187  if( isDNComponent( value ) )
1188  return( selectDNComponent( certInfoPtr, value ) );
1189 
1190  /* It's a standard attribute field, try and locate it */
1191  return( moveCursorToField( certInfoPtr, value ) );
1192  }
1193 #endif /* USE_CERTIFICATES */