static int adjustPkiUserAttributes( INOUT CERT_INFO *certInfoPtr ) { int status; assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) ); /* The SCEP protocol requires that the SCEP challenge password be placed in the PKCS #10 request instead of being included in the SCEP metadata. This shouldn't have been copied across to the certificate request since the NOCOPY flag was set for the attribute, but to make absolutely certain we try and delete it anyway. Since this could in theory be present in non-SCEP requests as well (the request- processing code just knows about PKCS #10 requests as a whole, not requests from a SCEP source vs. requests not from a SCEP source), we make the delete unconditional */ if( findAttributeField( certInfoPtr->attributes, CRYPT_CERTINFO_CHALLENGEPASSWORD, CRYPT_ATTRIBUTE_NONE ) != NULL ) { status = deleteCompleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, CRYPT_CERTINFO_CHALLENGEPASSWORD, certInfoPtr->currentSelection.dnPtr ); if( cryptStatusError( status ) ) return( status ); } /* The PKI user information contains an sKID that's used to uniquely identify the user, this applies to the user information itself rather than the certificate that'll be issued from it. Since this will have been copied over alongside the other attributes we need to explicitly delete it before we continue */ if( findAttributeField( certInfoPtr->attributes, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER, CRYPT_ATTRIBUTE_NONE ) != NULL ) { status = deleteCompleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER, certInfoPtr->currentSelection.dnPtr ); if( cryptStatusError( status ) ) return( status ); } return( CRYPT_OK ); }
static int deleteCertAttribute( INOUT CERT_INFO *certInfoPtr, IN_ATTRIBUTE \ const CRYPT_ATTRIBUTE_TYPE certInfoType ) { ATTRIBUTE_PTR **attributeListHeadPtrPtr; ATTRIBUTE_PTR *attributePtr; const BOOLEAN isRevocationEntry = \ isRevocationEntryComponent( certInfoType ) ? TRUE : FALSE; int status; assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) ); REQUIRES( isAttribute( certInfoType ) || \ isInternalAttribute( certInfoType ) ); /* Try and find this attribute in the attribute list */ attributePtr = findAttributeComponent( certInfoPtr, certInfoType ); if( attributePtr == NULL ) return( CRYPT_ERROR_NOTFOUND ); /* If this is a non-present field with a default value in a present attribute (so that its value can be read but the field itself isn't really there) there isn't any terribly satisfactory return code to indicate this. Returning CRYPT_OK is wrong because the caller can keep deleting the same field over and over, and returning CRYPT_ERROR_NOTFOUND is wrong because the caller may have added the attribute at an earlier date but it was never written because it had the default value so that to the caller it appears that the field they added has been lost. The least unexpected action is probably to return CRYPT_OK */ if( checkAttributeProperty( attributePtr, ATTRIBUTE_PROPERTY_DEFAULTVALUE ) ) return( CRYPT_OK ); /* If this is a complete attribute (e.g. CRYPT_CERTINFO_SUBJECTINFOACCESS rather than one of its fields like CRYPT_CERTINFO_SUBJECTINFO_CAREPOSITORY), delete the entire attribute */ if( checkAttributeProperty( attributePtr, ATTRIBUTE_PROPERTY_COMPLETEATRIBUTE ) ) { ATTRIBUTE_PTR *fieldAttributePtr; /* If the certificate has a fleur de lis make sure that it can't be scraped off */ fieldAttributePtr = findAttribute( certInfoPtr->attributes, certInfoType, TRUE ); if( fieldAttributePtr != NULL && \ checkAttributeProperty( fieldAttributePtr, ATTRIBUTE_PROPERTY_LOCKED ) ) return( CRYPT_ERROR_PERMISSION ); /* This is an ID for an entire (constructed) attribute, delete the attribute */ if( isRevocationEntry ) { attributeListHeadPtrPtr = getEntryAttributeListHead( certInfoPtr ); ENSURES( attributeListHeadPtrPtr != NULL ); } else attributeListHeadPtrPtr = &certInfoPtr->attributes; return( deleteCompleteAttribute( attributeListHeadPtrPtr, &certInfoPtr->attributeCursor, certInfoType, certInfoPtr->currentSelection.dnPtr ) ); } /* If the certificate has a fleur de lis make sure that it can't be scraped off */ if( checkAttributeProperty( attributePtr, ATTRIBUTE_PROPERTY_LOCKED ) ) return( CRYPT_ERROR_PERMISSION ); /* It's a single field, delete that */ if( isRevocationEntry ) { attributeListHeadPtrPtr = getEntryAttributeListHead( certInfoPtr ); ENSURES( attributeListHeadPtrPtr != NULL ); } else attributeListHeadPtrPtr = &certInfoPtr->attributes; status = deleteAttributeField( attributeListHeadPtrPtr, &certInfoPtr->attributeCursor, attributePtr, certInfoPtr->currentSelection.dnPtr ); /* If we've deleted the attribute containing the currently selected DN, deselect it */ if( status == OK_SPECIAL ) { certInfoPtr->currentSelection.dnPtr = NULL; status = CRYPT_OK; } return( status ); }
static int copyPkiUserAttributes( INOUT CERT_INFO *certInfoPtr, INOUT ATTRIBUTE_PTR *pkiUserAttributes ) { ATTRIBUTE_PTR *requestAttrPtr, *pkiUserAttrPtr; int value, status; assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) ); assert( isWritePtr( pkiUserAttributes, sizeof( ATTRIBUTE_LIST ) ) ); REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \ certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT ); /* If there are altNames present in both the PKI user data and the request, make sure that they match */ requestAttrPtr = findAttribute( certInfoPtr->attributes, CRYPT_CERTINFO_SUBJECTALTNAME, FALSE ); pkiUserAttrPtr = findAttribute( pkiUserAttributes, CRYPT_CERTINFO_SUBJECTALTNAME, FALSE ); if( requestAttrPtr != NULL && pkiUserAttrPtr != NULL ) { /* Both the certificate request and the PKI user have altNames, make sure that they're identical */ if( !compareAttribute( requestAttrPtr, pkiUserAttrPtr ) ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_SUBJECTALTNAME, CRYPT_ERRTYPE_ISSUERCONSTRAINT ); return( CRYPT_ERROR_INVALID ); } /* The two altNames are identical, delete the one in the request in order to allow the one from the PKI user data to be copied across when we call copyAttributes() */ status = deleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, requestAttrPtr, certInfoPtr->currentSelection.dnPtr ); if( cryptStatusError( status ) ) return( status ); } /* There's one rather ugly special-case situation that we have to handle which is when the user has submitted a PnP PKI request for a generic signing certificate but their PKI user information indicates that they're intended to be a CA user. The processing flow for this is: Read request data from an external source into certificate request object, creating a state=high object; Add PKI user information to state=high request; When augmenting the request with the PKI user information the incoming request will contain a keyUsage of digitalSignature while the PKI user information will contain a keyUsage of keyCertSign and/or crlSign. We can't fix this up at the PnP processing level because the request object will be in the high state once it's instantiated and no changes to the attributes can be made (the PKI user information is a special case that can be added to an object in the high state but which modifies attributes in it as if it were still in the low state). To avoid the attribute conflict, if we find this situation in the request/pkiUser combination we delete the keyUsage in the request to allow it to be replaced by the pkiUser keyUsage. Hardcoding in this special case isn't very elegant but it's the only way to make the PnP PKI issue work without requiring that the user explicitly specify that they want to be a CA in the request's keyUsage, which makes it rather non-PnP and would also lead to slightly strange requests since basicConstraints can't be specified in requests while the CA keyUsage can */ status = getAttributeFieldValue( certInfoPtr->attributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE, &value ); if( cryptStatusOK( status ) && value == CRYPT_KEYUSAGE_DIGITALSIGNATURE ) { status = getAttributeFieldValue( pkiUserAttributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE, &value ); if( cryptStatusOK( status ) && ( value & KEYUSAGE_CA ) ) { /* The certificate contains a digitalSignature keyUsage and the PKI user information contains a CA usage, delete the certificate's keyUsage to make way for the PKI user's CA keyUsage */ status = deleteCompleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, CRYPT_CERTINFO_KEYUSAGE, certInfoPtr->currentSelection.dnPtr ); if( cryptStatusError( status ) ) return( status ); } } /* Copy the attributes from the PKI user information into the certificate */ status = copyAttributes( &certInfoPtr->attributes, pkiUserAttributes, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); if( cryptStatusError( status ) ) return( status ); /* Perform any special-case adjustments on attributes that may be required after they've been copied from the PKI user to the certificate request */ return( adjustPkiUserAttributes( certInfoPtr ) ); }