/**************************************************************************** * * * Set Certificate Components * * Copyright Peter Gutmann 1997-2005 * * * ****************************************************************************/ #if defined( INC_ALL ) #include "cert.h" #include "certattr.h" #include "asn1.h" #include "asn1_ext.h" #else #include "cert/cert.h" #include "cert/certattr.h" #include "misc/asn1.h" #include "misc/asn1_ext.h" #endif /* Compiler-specific includes */ /* Prototypes for functions in comp_get.c */ int moveCursorToField( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE certInfoType ); int selectGeneralName( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE certInfoType, const SELECTION_OPTION option ); int selectDN( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE certInfoType, const SELECTION_OPTION option ); void syncSelection( CERT_INFO *certInfoPtr ); time_t *getRevocationTimePtr( const CERT_INFO *certInfoPtr ); /**************************************************************************** * * * Utility Routines * * * ****************************************************************************/ /* Copy the encoded issuer DN */ static int copyIssuerDnData( CERT_INFO *destCertInfoPtr, const CERT_INFO *srcCertInfoPtr ) { void *dnDataPtr; assert( srcCertInfoPtr->issuerDNptr != NULL ); if( ( dnDataPtr = clAlloc( "copyIssuerDnData", srcCertInfoPtr->issuerDNsize ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); memcpy( dnDataPtr, srcCertInfoPtr->issuerDNptr, srcCertInfoPtr->issuerDNsize ); destCertInfoPtr->issuerDNptr = destCertInfoPtr->issuerDNdata = dnDataPtr; destCertInfoPtr->issuerDNsize = srcCertInfoPtr->issuerDNsize; return( CRYPT_OK ); } /* Copy revocation information into a CRL or revocation request */ static int copyRevocationInfo( CERT_INFO *certInfoPtr, const CERT_INFO *revInfoPtr ) { int status = CRYPT_OK; assert( certInfoPtr->type == CRYPT_CERTTYPE_CRL || \ certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION ); assert( revInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \ revInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \ revInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN || \ revInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION ); /* If there's an issuer name recorded, make sure that it matches the one in the cert that's being added */ if( certInfoPtr->issuerDNptr != NULL ) { if( certInfoPtr->issuerDNsize != revInfoPtr->issuerDNsize || \ memcmp( certInfoPtr->issuerDNptr, revInfoPtr->issuerDNptr, certInfoPtr->issuerDNsize ) ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_ISSUERNAME, CRYPT_ERRTYPE_ATTR_VALUE ); status = CRYPT_ERROR_INVALID; } } else /* There's no issuer name present yet, set the CRL issuer name to the cert's issuer to make sure that we can't add certs or sign the CRL with a different issuer. We do this here rather than after setting the revocation list entry because of the difficulty of undoing the revocation entry addition */ status = copyIssuerDnData( certInfoPtr, revInfoPtr ); if( cryptStatusError( status ) ) return( status ); /* Add the cert information to the revocation list and make it the currently selected entry. The ID type isn't quite an issueAndSerialNumber, but the checking code eventually converts it into this form using the supplied issuer cert DN */ if( revInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION ) status = addRevocationEntry( &certInfoPtr->cCertRev->revocations, &certInfoPtr->cCertRev->currentRevocation, CRYPT_IKEYID_ISSUERANDSERIALNUMBER, revInfoPtr->cCertReq->serialNumber, revInfoPtr->cCertReq->serialNumberLength, FALSE ); else status = addRevocationEntry( &certInfoPtr->cCertRev->revocations, &certInfoPtr->cCertRev->currentRevocation, CRYPT_IKEYID_ISSUERANDSERIALNUMBER, revInfoPtr->cCertCert->serialNumber, revInfoPtr->cCertCert->serialNumberLength, FALSE ); if( status == CRYPT_ERROR_DUPLICATE ) /* If this cert is already present in the list, set the extended error code for it */ setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CERTIFICATE, CRYPT_ERRTYPE_ATTR_PRESENT ); return( status ); } /* Copy public key data into a certificate object */ static int copyPublicKeyInfo( CERT_INFO *certInfoPtr, const CRYPT_HANDLE cryptHandle, const CERT_INFO *srcCertInfoPtr ) { void *publicKeyInfoPtr; int length, status; assert( ( isHandleRangeValid( cryptHandle ) && srcCertInfoPtr == NULL ) || \ ( !isHandleRangeValid( cryptHandle ) && srcCertInfoPtr != NULL ) ); /* Make sure that we haven't already got a public key present */ if( certInfoPtr->iPubkeyContext != CRYPT_ERROR || \ certInfoPtr->publicKeyInfo != NULL ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } /* If we've been given a data-only cert, copy over the public key data */ if( srcCertInfoPtr != NULL ) { assert( srcCertInfoPtr->publicKeyAlgo > CRYPT_ALGO_NONE ); assert( memcmp( srcCertInfoPtr->publicKeyID, "\x00\x00\x00\x00\x00\x00\x00\x00", 8 ) ); assert( ( ( BYTE * ) srcCertInfoPtr->publicKeyInfo )[ 0 ] == 0x30 ); length = srcCertInfoPtr->publicKeyInfoSize; if( ( publicKeyInfoPtr = clAlloc( "copyPublicKeyInfo", length ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); memcpy( publicKeyInfoPtr, srcCertInfoPtr->publicKeyInfo, length ); certInfoPtr->publicKeyAlgo = srcCertInfoPtr->publicKeyAlgo; certInfoPtr->publicKeyFeatures = srcCertInfoPtr->publicKeyFeatures; memcpy( certInfoPtr->publicKeyID, srcCertInfoPtr->publicKeyID, KEYID_SIZE ); } else { CRYPT_CONTEXT iCryptContext; MESSAGE_DATA msgData; /* Get the context handle. All other checking has already been performed by the kernel */ status = krnlSendMessage( cryptHandle, IMESSAGE_GETDEPENDENT, &iCryptContext, OBJECT_TYPE_CONTEXT ); if( cryptStatusError( status ) ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, CRYPT_ERRTYPE_ATTR_VALUE ); return( status ); } assert( cryptStatusOK( \ krnlSendMessage( iCryptContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC ) ) ); /* Get the key information */ status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE, &certInfoPtr->publicKeyAlgo, CRYPT_CTXINFO_ALGO ); if( cryptStatusOK( status ) ) status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE, &certInfoPtr->publicKeyFeatures, CRYPT_IATTRIBUTE_KEYFEATURES ); if( cryptStatusOK( status ) ) { setMessageData( &msgData, certInfoPtr->publicKeyID, KEYID_SIZE ); status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_KEYID ); } if( cryptStatusError( status ) ) return( status ); /* Copy over the public-key data. We copy the data rather than keeping a reference to the context for two reasons. Firstly, when the cert is transitioned into the high state it will constrain the attached context, so a context shared between two certs could be constrained in unexpected ways. Secondly, the context could be a private-key context, and attaching that to a cert would be rather inappropriate. Furthermore, the constraint issue is even more problematic in that a context constrained by an encryption-only request could then no longer be used to sign the request or a PKI protocol message containing the request */ setMessageData( &msgData, NULL, 0 ); status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_KEY_SPKI ); if( cryptStatusOK( status ) ) { length = msgData.length; if( ( publicKeyInfoPtr = clAlloc( "copyPublicKeyInfo", length ) ) == NULL ) status = CRYPT_ERROR_MEMORY; } if( cryptStatusError( status ) ) return( status ); msgData.data = publicKeyInfoPtr; status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_KEY_SPKI ); if( cryptStatusError( status ) ) return( status ); } certInfoPtr->publicKeyData = certInfoPtr->publicKeyInfo = \ publicKeyInfoPtr; certInfoPtr->publicKeyInfoSize = length; certInfoPtr->flags |= CERT_FLAG_DATAONLY; return( CRYPT_OK ); } /* Convert a DN in string form into a certificate DN */ static int getEncodedDn( CERT_INFO *certInfoPtr, const void *dnString, const int dnStringLength ) { SELECTION_STATE savedState; int status; /* If there's already a DN set, we can't do anything else */ saveSelectionState( savedState, certInfoPtr ); status = selectDN( certInfoPtr, CRYPT_ATTRIBUTE_NONE, MUST_BE_PRESENT ); if( cryptStatusOK( status ) && \ *certInfoPtr->currentSelection.dnPtr == NULL ) /* There's a DN selected but it's empty, we're OK */ status = CRYPT_ERROR; restoreSelectionState( savedState, certInfoPtr ); if( cryptStatusOK( status ) ) return( CRYPT_ERROR_INITED ); status = selectDN( certInfoPtr, CRYPT_ATTRIBUTE_NONE, CREATE_IF_ABSENT ); if( cryptStatusError( status ) ) return( status ); /* Read the entire DN from its string form into the selected DN */ status = readDNstring( dnString, dnStringLength, certInfoPtr->currentSelection.dnPtr ); if( cryptStatusOK( status ) && \ certInfoPtr->currentSelection.updateCursor ) /* If we couldn't update the cursor earlier on because the attribute field in question hadn't been created yet, do it now */ selectGeneralName( certInfoPtr, certInfoPtr->currentSelection.generalName, MAY_BE_ABSENT ); return( status ); } /* The OCSPv1 ID doesn't contain any usable fields so we pre-encode it when the cert is added to the OCSP request and treat it as a blob thereafter */ static int writeOCSPv1ID( STREAM *stream, const CERT_INFO *certInfoPtr, const void *issuerKeyHash ) { HASHFUNCTION hashFunction; BYTE hashBuffer[ CRYPT_MAX_HASHSIZE + 8 ]; int hashSize; assert( certInfoPtr->issuerDNptr != NULL ); assert( certInfoPtr->cCertCert->serialNumber != NULL ); /* Get the issuerName hash */ getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize ); hashFunction( NULL, hashBuffer, CRYPT_MAX_HASHSIZE, certInfoPtr->issuerDNptr, certInfoPtr->issuerDNsize, HASH_ALL ); /* Write the request data */ writeSequence( stream, sizeofAlgoID( CRYPT_ALGO_SHA ) + \ sizeofObject( hashSize ) + sizeofObject( hashSize ) + \ sizeofInteger( certInfoPtr->cCertCert->serialNumber, certInfoPtr->cCertCert->serialNumberLength ) ); writeAlgoID( stream, CRYPT_ALGO_SHA ); writeOctetString( stream, hashBuffer, hashSize, DEFAULT_TAG ); writeOctetString( stream, issuerKeyHash, 20, DEFAULT_TAG ); return( writeInteger( stream, certInfoPtr->cCertCert->serialNumber, certInfoPtr->cCertCert->serialNumberLength, DEFAULT_TAG ) ); } /* Sanitise cert attributes based on a user-supplied template. This is used to prevent a user from supplying potentially dangerous attributes in a cert request, for example to request a CA certificate by setting the basicConstraints/keyUsage = CA extensions in the request in a manner that would result in the creation of a CA cert when the request is processed. We use an allow-all default rather than deny-all since deny-all would require the caller to specify a vast range of (mostly never-used) attributes to permit, when usually all they want to block is the CA flag and equivalent mechanisms */ static int sanitiseCertAttributes( CERT_INFO *certInfoPtr, const ATTRIBUTE_LIST *templateListPtr ) { const ATTRIBUTE_LIST *attributeListCursor; int iterationCount = 0; /* If there's no attributes present or no disallowed attribute template, we're done */ if( certInfoPtr->attributes == NULL || templateListPtr == NULL ) return( CRYPT_OK ); /* Walk down the template attribute list applying each one in turn to the certificate attributes */ for( attributeListCursor = templateListPtr; attributeListCursor != NULL && \ !isBlobAttribute( attributeListCursor ) && \ iterationCount++ < FAILSAFE_ITERATIONS_MAX; attributeListCursor = attributeListCursor->next ) { ATTRIBUTE_LIST *attributeList; int value; /* Check to see whether there's a constrained attribute present in the cert attributes and if it is, whether it conflicts with the constraining attribute */ attributeList = findAttributeField( certInfoPtr->attributes, attributeListCursor->fieldID, attributeListCursor->subFieldID ); if( attributeList == NULL || \ !( attributeList->intValue & attributeListCursor->intValue ) ) continue; /* If the cert attribute was provided through the application of PKI user data (indicated by it having the locked flag set), allow it even if it conflicts with the constraining attribute. This is permitted because the PKI user data was explicitly set by the issuing CA rather than being user-supplied in the cert request, so it has to be OK */ if( attributeList->flags & ATTR_FLAG_LOCKED ) continue; /* The attribute contains a value that's disallowed by the constraining attribute, correct it if possible */ value = attributeList->intValue & ~attributeListCursor->intValue; if( !value ) { /* The attribute contains only invalid bits and can't be permitted */ certInfoPtr->errorLocus = attributeList->fieldID; certInfoPtr->errorType = CRYPT_ERRTYPE_ATTR_VALUE; return( CRYPT_ERROR_INVALID ); } attributeList->intValue = value; /* Set adjusted value */ } if( iterationCount >= FAILSAFE_ITERATIONS_MAX ) retIntError(); return( CRYPT_OK ); } /**************************************************************************** * * * Serial-Number Routines * * * ****************************************************************************/ /* Set the serial number for a certificate. Ideally we would store this as a static value in the configuration database, but this has three disadvantages: Updating the serial number updates the entire configuration database (including things the user might not want updated), if the config database update fails the serial number never changes, and the predictable serial number allows tracking of the number of certificates which have been issued by the CA. Because of this, we just use a 64-bit nonce if the user doesn't supply a value */ int setSerialNumber( CERT_INFO *certInfoPtr, const void *serialNumber, const int serialNumberLength ) { MESSAGE_DATA msgData; BYTE buffer[ 128 + 8 ]; void *serialNumberPtr; int length = ( serialNumberLength > 0 ) ? \ serialNumberLength : DEFAULT_SERIALNO_SIZE; int bufPos = 0, status; assert( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \ certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \ certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN || \ certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION ); assert( ( serialNumber == NULL && serialNumberLength == 0 ) || \ ( serialNumber != NULL && serialNumberLength > 0 && \ serialNumberLength <= 100 ) ); /* If a serial number has already been set explicitly, don't override it with an implicitly-set one */ serialNumberPtr = \ ( certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION ) ? \ certInfoPtr->cCertReq->serialNumber : \ certInfoPtr->cCertCert->serialNumber; if( serialNumberPtr != NULL ) { assert( isReadPtr( serialNumberPtr, SERIALNO_BUFSIZE ) ); assert( serialNumber == NULL && serialNumberLength == 0 ); return( CRYPT_OK ); } serialNumberPtr = \ ( certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION ) ? \ certInfoPtr->cCertReq->serialNumberBuffer : \ certInfoPtr->cCertCert->serialNumberBuffer; /* If we're using user-supplied serial number data, canonicalise it into a form suitable for use as an INTEGER-hole */ if( serialNumber != NULL ) { STREAM stream; assert( isReadPtr( serialNumber, serialNumberLength ) ); sMemOpen( &stream, buffer, 128 ); status = writeInteger( &stream, serialNumber, serialNumberLength, DEFAULT_TAG ); length = stell( &stream ) - 2; sMemDisconnect( &stream ); bufPos = 2; /* Skip tag + length */ if( cryptStatusError( status ) ) return( status ); } else { /* Generate a random (but fixed-length) serial number and ensure that the first byte of the value we use is nonzero (to guarantee a DER encoding) and clear the high bit to provide a constant- length ASN.1 encoded value */ setMessageData( &msgData, buffer, DEFAULT_SERIALNO_SIZE + 1 ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); if( cryptStatusError( status ) ) return( status ); buffer[ 0 ] &= 0x7F; /* Clear the sign bit */ if( buffer[ 0 ] == 0 ) { /* The first byte is zero, try for a nonzero byte in the extra data we fetched. If that's zero too, just set it to 1 */ buffer[ 0 ] = buffer[ DEFAULT_SERIALNO_SIZE ] & 0x7F; if( buffer[ 0 ] == 0 ) buffer[ 0 ] = 1; } } /* Copy across the canonicalised serial number value */ if( length >= SERIALNO_BUFSIZE && \ ( serialNumberPtr = clDynAlloc( "setSerialNumber", length ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); if( certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION ) { certInfoPtr->cCertReq->serialNumber = serialNumberPtr; certInfoPtr->cCertReq->serialNumberLength = length; } else { certInfoPtr->cCertCert->serialNumber = serialNumberPtr; certInfoPtr->cCertCert->serialNumberLength = length; } memcpy( serialNumberPtr, buffer + bufPos, length ); return( CRYPT_OK ); } /* Compare a serial number in canonical form to a generic serial number, with special handling for leading-zero truncation. This one can get a bit tricky because Microsoft fairly consistently encode the serial numbers incorrectly, so we normalise the values to have no leading zero, which is the lowest common denominator */ BOOLEAN compareSerialNumber( const void *canonSerialNumber, const int canonSerialNumberLength, const void *serialNumber, const int serialNumberLength ) { const BYTE *canonSerialNumberPtr = canonSerialNumber; const BYTE *serialNumberPtr = serialNumber; int canonSerialLength = canonSerialNumberLength; int serialLength = serialNumberLength; /* Internal serial numbers are canonicalised, so all we need to do is strip a possible leading zero */ if( canonSerialNumberPtr[ 0 ] == 0 ) { canonSerialNumberPtr++; canonSerialLength--; } assert( canonSerialLength == 0 || canonSerialNumberPtr[ 0 ] ); /* Serial numbers from external sources can be arbitarily strangely encoded, so we strip leading zeroes until we get to actual data */ while( serialLength > 0 && serialNumberPtr[ 0 ] == 0 ) { serialNumberPtr++; serialLength--; } /* Finally we've got them in a form where we can compare them */ if( canonSerialLength != serialLength || \ memcmp( canonSerialNumberPtr, serialNumberPtr, serialLength ) ) return( TRUE ); return( FALSE ); } /**************************************************************************** * * * Copy Cert Request Info * * * ****************************************************************************/ /* Copy cert request info into a certificate object. This copies the public key context, the DN, any valid attributes, and any other relevant bits and pieces if it's a CRMF request */ static int copyCertReqInfo( CERT_INFO *certInfoPtr, CERT_INFO *certRequestInfoPtr ) { int status; assert( certRequestInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \ certRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT ); /* Copy the public key context, the DN, and the attributes. Type checking has already been performed by the kernel. We copy the attributes across after the DN because that copy is the hardest to undo: If there are already attributes present, the copied attributes will be mixed in among them so it's not really possible to undo the copy later without performing a complex selective delete */ status = copyDN( &certInfoPtr->subjectName, certRequestInfoPtr->subjectName ); if( cryptStatusOK( status ) ) { if( certRequestInfoPtr->flags & CERT_FLAG_DATAONLY ) status = copyPublicKeyInfo( certInfoPtr, CRYPT_UNUSED, certRequestInfoPtr ); else status = copyPublicKeyInfo( certInfoPtr, certRequestInfoPtr->iPubkeyContext, NULL ); } if( cryptStatusOK( status ) && \ certRequestInfoPtr->attributes != NULL ) { status = copyAttributes( &certInfoPtr->attributes, certRequestInfoPtr->attributes, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); if( cryptStatusError( status ) ) deleteDN( &certInfoPtr->subjectName ); } if( cryptStatusError( status ) ) return( status ); /* If it's a CRMF request there could also be a validity period specified */ if( certRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT ) { const time_t currentTime = getApproxTime(); /* We don't allow start times backdated by more than a year, or end times before the start time. Since these are trivial things, we don't abort if there's a problem but just quietly fix the value */ if( certRequestInfoPtr->startTime > MIN_TIME_VALUE && \ certRequestInfoPtr->startTime > currentTime - ( 86400L * 365 ) ) certInfoPtr->startTime = certRequestInfoPtr->startTime; if( certRequestInfoPtr->endTime > MIN_TIME_VALUE && \ certRequestInfoPtr->endTime > certInfoPtr->startTime ) certInfoPtr->endTime = certRequestInfoPtr->endTime; } return( CRYPT_OK ); } /* Copy what we need to identify the cert to be revoked and any revocation information into a certificate object */ static int copyRevReqInfo( CERT_INFO *certInfoPtr, CERT_INFO *revRequestInfoPtr ) { int status; status = copyRevocationInfo( certInfoPtr, revRequestInfoPtr ); if( cryptStatusError( status ) || \ revRequestInfoPtr->attributes == NULL ) return( status ); return( copyRevocationAttributes( &certInfoPtr->attributes, revRequestInfoPtr->attributes ) ); } /* Copy revocation information from an RTCS or OCSP request to a response */ static int copyRtcsReqInfo( CERT_INFO *certInfoPtr, CERT_INFO *rtcsRequestInfoPtr ) { int status; /* Copy the cert validity information and extensions */ status = copyValidityEntries( &certInfoPtr->cCertVal->validityInfo, rtcsRequestInfoPtr->cCertVal->validityInfo ); if( cryptStatusOK( status ) ) status = copyOCSPRequestAttributes( &certInfoPtr->attributes, rtcsRequestInfoPtr->attributes ); return( status ); } static int copyOcspReqInfo( CERT_INFO *certInfoPtr, CERT_INFO *ocspRequestInfoPtr ) { int status; /* Copy the revocation information and extensions */ status = copyRevocationEntries( &certInfoPtr->cCertRev->revocations, ocspRequestInfoPtr->cCertRev->revocations ); if( cryptStatusOK( status ) ) status = copyOCSPRequestAttributes( &certInfoPtr->attributes, ocspRequestInfoPtr->attributes ); if( cryptStatusError( status ) ) return( status ); return( CRYPT_OK ); } /**************************************************************************** * * * Copy Cert Template Info * * * ****************************************************************************/ /* Copy the public key, DN, and any attributes that need to be copied across. We copy the full DN rather than just the encoded form in case the user wants to query the request details after creating it */ static int copyToCRMFRequest( CERT_INFO *crmfRequestInfoPtr, CERT_INFO *certInfoPtr, const CRYPT_HANDLE iCryptHandle ) { int status; status = copyDN( &crmfRequestInfoPtr->subjectName, certInfoPtr->subjectName ); if( cryptStatusError( status ) ) return( status ); if( crmfRequestInfoPtr->iPubkeyContext == CRYPT_ERROR && \ crmfRequestInfoPtr->publicKeyInfo == NULL ) { /* Only copy the key across if a key hasn't already been added earlier as CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO. Checking for this special case (rather than returning an error) allows the DN information from an existing cert to be copied into a request for a new key */ status = copyPublicKeyInfo( crmfRequestInfoPtr, iCryptHandle, NULL ); } if( cryptStatusOK( status ) ) { /* We copy the attributes across after the DN because that copy is the hardest to undo: If there are already attributes present, the copied attributes will be mixed in among them so it's not really possible to undo the copy later without performing a complex selective delete */ status = copyCRMFRequestAttributes( &crmfRequestInfoPtr->attributes, certInfoPtr->attributes ); } if( cryptStatusError( status ) ) deleteDN( &crmfRequestInfoPtr->subjectName ); return( status ); } /* Copy across the issuer and subject DN and serial number */ static int copyToCRMFRevRequest( CERT_INFO *crmfRevRequestInfoPtr, CERT_INFO *certInfoPtr ) { int status; /* If the info is already present we can't add it again */ if( crmfRevRequestInfoPtr->issuerName != NULL ) { setErrorInfo( crmfRevRequestInfoPtr, CRYPT_CERTINFO_CERTIFICATE, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } /* Copy across the issuer name and allocate the storage that we need to copy the subject name. We don't care about any internal structure of the DNs so we just copy the pre-encoded form, we could in theory copy the full DN but it isn't really the issuer (creator) of the object so it's better if it appears to have no issuer DN than a misleading one */ status = copyIssuerDnData( crmfRevRequestInfoPtr, certInfoPtr ); if( cryptStatusError( status ) ) return( status ); status = setSerialNumber( crmfRevRequestInfoPtr, certInfoPtr->cCertCert->serialNumber, certInfoPtr->cCertCert->serialNumberLength ); if( cryptStatusOK( status ) && \ ( crmfRevRequestInfoPtr->subjectDNdata = \ clAlloc( "copyToCRMFRevRequest", certInfoPtr->subjectDNsize ) ) == NULL ) status = CRYPT_ERROR_MEMORY; if( cryptStatusError( status ) ) { clFree( "copyToCRMFRevRequest", crmfRevRequestInfoPtr->issuerDNdata ); crmfRevRequestInfoPtr->issuerDNptr = \ crmfRevRequestInfoPtr->issuerDNdata = NULL; crmfRevRequestInfoPtr->issuerDNsize = 0; if( crmfRevRequestInfoPtr->cCertCert->serialNumber != NULL && \ crmfRevRequestInfoPtr->cCertCert->serialNumber != \ crmfRevRequestInfoPtr->cCertCert->serialNumberBuffer ) clFree( "copyToCRMFRevRequest", crmfRevRequestInfoPtr->cCertCert->serialNumber ); crmfRevRequestInfoPtr->cCertCert->serialNumber = NULL; return( status ); } /* Copy the subject DN for use in CMP */ memcpy( crmfRevRequestInfoPtr->subjectDNdata, certInfoPtr->subjectDNptr, certInfoPtr->subjectDNsize ); crmfRevRequestInfoPtr->subjectDNptr = crmfRevRequestInfoPtr->subjectDNdata; crmfRevRequestInfoPtr->subjectDNsize = certInfoPtr->subjectDNsize; return( CRYPT_OK ); } /* Copy the cert information to the revocation list. First we make sure that the CA cert hash (needed for the weird cert ID) is present. We add the necessary information as a pre-encoded blob since we can't do much with the ID fields */ static int copyToOCSPRequest( CERT_INFO *ocspRequestInfoPtr, CERT_INFO *certInfoPtr ) { STREAM stream; BYTE idBuffer[ 256 + 8 ], *idBufPtr = idBuffer; const int idLength = ( int ) sizeofObject( \ sizeofAlgoID( CRYPT_ALGO_SHA ) + \ sizeofObject( 20 ) + sizeofObject( 20 ) + \ sizeofInteger( certInfoPtr->cCertCert->serialNumber, \ certInfoPtr->cCertCert->serialNumberLength ) ); int status; /* Make sure there's a CA cert hash present */ if( !ocspRequestInfoPtr->certHashSet ) { setErrorInfo( ocspRequestInfoPtr, CRYPT_CERTINFO_CACERTIFICATE, CRYPT_ERRTYPE_ATTR_ABSENT ); return( CRYPT_ERROR_NOTINITED ); } /* Generate the OCSPv1 cert ID */ if( idLength > 256 && \ ( idBufPtr = clDynAlloc( "copyToOCSPRequest", \ idLength ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); sMemOpen( &stream, idBufPtr, idLength ); status = writeOCSPv1ID( &stream, certInfoPtr, ocspRequestInfoPtr->certHash ); sMemDisconnect( &stream ); if( cryptStatusOK( status ) ) status = addRevocationEntry( &ocspRequestInfoPtr->cCertRev->revocations, &ocspRequestInfoPtr->cCertRev->currentRevocation, CRYPT_KEYID_NONE, idBufPtr, idLength, FALSE ); if( idBufPtr != idBuffer ) clFree( "copyToOCSPRequest", idBufPtr ); /* Add the cert information again as an ESSCertID extension to work around the problems inherent in OCSPv1 IDs. This isn't currently used because non-cryptlib v1 responders won't understand it and cryptlib uses RTCS, which doesn't have the OCSP problems */ if( status == CRYPT_ERROR_DUPLICATE ) { /* If this cert is already present in the list, set the extended error code for it */ setErrorInfo( ocspRequestInfoPtr, CRYPT_CERTINFO_CERTIFICATE, CRYPT_ERRTYPE_ATTR_PRESENT ); } return( status ); } /* Copy the cert hash. We read the value indirectly since it's computed on demand and may not have been evaluated yet */ static int copyToRTCSRequest( CERT_INFO *rtcsRequestInfoPtr, CERT_INFO *certInfoPtr ) { BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ]; int certHashLength = CRYPT_MAX_HASHSIZE, status; status = getCertComponent( certInfoPtr, CRYPT_CERTINFO_FINGERPRINT_SHA, certHash, &certHashLength ); if( cryptStatusOK( status ) ) status = addValidityEntry( &rtcsRequestInfoPtr->cCertVal->validityInfo, &rtcsRequestInfoPtr->cCertVal->currentValidity, certHash, certHashLength ); if( status == CRYPT_ERROR_DUPLICATE ) { /* If this cert is already present in the list, set the extended error code for it */ setErrorInfo( rtcsRequestInfoPtr, CRYPT_CERTINFO_CERTIFICATE, CRYPT_ERRTYPE_ATTR_PRESENT ); } return( status ); } /* Copy user certificate info into a certificate object */ static int copyUserCertInfo( CERT_INFO *certInfoPtr, CERT_INFO *userCertInfoPtr, const CRYPT_HANDLE iCryptHandle ) { int status; assert( userCertInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \ userCertInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN ); assert( userCertInfoPtr->certificate != NULL ); /* If it's an RTCS or OCSP request, remember the responder URL if there's one present. We can't leave it to be read out of the cert because authorityInfoAccess isn't a valid attribute for RTCS/OCSP requests */ if( ( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST && \ certInfoPtr->cCertVal->responderUrl == NULL ) || \ ( certInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST && \ certInfoPtr->cCertRev->responderUrl == NULL ) ) { const CRYPT_ATTRIBUTE_TYPE aiaAttribute = \ ( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST ) ? \ CRYPT_CERTINFO_AUTHORITYINFO_RTCS : \ CRYPT_CERTINFO_AUTHORITYINFO_OCSP; SELECTION_STATE savedState; void *responderUrl; int urlSize = 0; /* There's no responder URL set, check whether the user cert contains a responder URL in the RTCS/OCSP authorityInfoAccess GeneralName */ saveSelectionState( savedState, userCertInfoPtr ); status = selectGeneralName( userCertInfoPtr, aiaAttribute, MAY_BE_ABSENT ); if( cryptStatusOK( status ) ) status = selectGeneralName( userCertInfoPtr, CRYPT_ATTRIBUTE_NONE, MUST_BE_PRESENT ); if( cryptStatusOK( status ) ) status = getCertComponent( userCertInfoPtr, CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, NULL, &urlSize ); if( cryptStatusOK( status ) ) { /* There's a responder URL present, copy it to the request */ if( ( responderUrl = \ clAlloc( "copyUserCertInfo", urlSize ) ) == NULL ) status = CRYPT_ERROR_MEMORY; else { status = getCertComponent( userCertInfoPtr, CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, responderUrl, &urlSize ); } if( cryptStatusOK( status ) ) { if( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST ) { certInfoPtr->cCertVal->responderUrl = responderUrl; certInfoPtr->cCertVal->responderUrlSize = urlSize; } else { certInfoPtr->cCertRev->responderUrl = responderUrl; certInfoPtr->cCertRev->responderUrlSize = urlSize; } } } else /* If there's no responder URL present it's not a (fatal) error */ status = CRYPT_OK; restoreSelectionState( savedState, userCertInfoPtr ); if( cryptStatusError( status ) ) return( status ); } /* Copy the required information across to the cert */ switch( certInfoPtr->type ) { case CRYPT_CERTTYPE_CRL: return( copyRevocationInfo( certInfoPtr, userCertInfoPtr ) ); case CRYPT_CERTTYPE_REQUEST_CERT: return( copyToCRMFRequest( certInfoPtr, userCertInfoPtr, iCryptHandle ) ); case CRYPT_CERTTYPE_REQUEST_REVOCATION: return( copyToCRMFRevRequest( certInfoPtr, userCertInfoPtr ) ); case CRYPT_CERTTYPE_OCSP_REQUEST: return( copyToOCSPRequest( certInfoPtr, userCertInfoPtr ) ); case CRYPT_CERTTYPE_RTCS_REQUEST: return( copyToRTCSRequest( certInfoPtr, userCertInfoPtr ) ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } /**************************************************************************** * * * Copy Misc. Cert Info * * * ****************************************************************************/ /* Get the hash of the public key (for an OCSPv1 request), possibly overwriting a previous hash if there are multiple entries in the request */ static int copyCaCertInfo( CERT_INFO *certInfoPtr, CERT_INFO *caCertInfoPtr ) { HASHFUNCTION hashFunction; STREAM stream; int length, status; assert( caCertInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \ caCertInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN ); assert( caCertInfoPtr->publicKeyInfo != NULL ); getHashParameters( CRYPT_ALGO_SHA, &hashFunction, NULL ); /* Dig down into the encoded key data to find the weird bits of key that OCSP requires us to hash. We store the result as the cert hash, which is safe because it isn't used for an OCSP request so it can't be accessed externally */ sMemConnect( &stream, caCertInfoPtr->publicKeyInfo, caCertInfoPtr->publicKeyInfoSize ); readSequence( &stream, NULL ); /* Wrapper */ readUniversal( &stream ); /* AlgoID */ status = readBitStringHole( &stream, &length, 16, DEFAULT_TAG ); if( cryptStatusError( status ) )/* BIT STRING wrapper */ { /* There's a problem with the format of the key */ assert( NOTREACHED ); setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CACERTIFICATE, CRYPT_ERRTYPE_ATTR_VALUE ); return( CRYPT_ERROR_INVALID ); } hashFunction( NULL, certInfoPtr->certHash, KEYID_SIZE, sMemBufPtr( &stream ), length, HASH_ALL ); certInfoPtr->certHashSet = TRUE; sMemDisconnect( &stream ); return( CRYPT_OK ); } /* Set or modify data in a cert request based on the PKI user info */ static int copyPkiUserAttributes( CERT_INFO *certInfoPtr, ATTRIBUTE_LIST *pkiUserAttributes ) { ATTRIBUTE_LIST *attributeListPtr; int 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 cert but their PKI user info indicates that they're intended to be a CA user. The processing flow for this is as follows: CMP: readRequestBody() Read request into state=high cert request object; Add PKI user info to request; ca_issue: caIssuerCert() Add request to newly-created cert object; Sign cert; When augmenting the request with the PKI user info, the incoming request will contain a keyUsage of digitalSignature while the PKI user info will contain a keyUsage of keyCertSign and/or crlSign. We can't fix this up at the CMP level because the request is in the high state and no changes to the attributes can be made (the PKI user info is a special case that can 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 attributes. 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, which makes it rather non-PnP */ attributeListPtr = findAttributeField( certInfoPtr->attributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE ); if( attributeListPtr != NULL && \ attributeListPtr->intValue == CRYPT_KEYUSAGE_DIGITALSIGNATURE ) { const ATTRIBUTE_LIST *pkiAttributeListPtr = \ findAttributeField( pkiUserAttributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE ); if( pkiAttributeListPtr != NULL && \ ( pkiAttributeListPtr->intValue & ( CRYPT_KEYUSAGE_KEYCERTSIGN | \ CRYPT_KEYUSAGE_CRLSIGN ) ) ) { /* The cert contains a digitalSignature keyUsage and the PKI user info contains a CA usage, delete the digitalSignature usage to make way for the CA usage */ deleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, attributeListPtr, certInfoPtr->currentSelection.dnPtr ); } } /* Copy the attributes from the PKI user info into the cert */ status = copyAttributes( &certInfoPtr->attributes, pkiUserAttributes, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); if( cryptStatusError( status ) ) return( status ); /* The PKI user info contains an sKID that's used to uniquely identify the user, this applies to the user info itself rather than the cert 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 */ attributeListPtr = findAttributeField( certInfoPtr->attributes, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER, CRYPT_ATTRIBUTE_NONE ); if( attributeListPtr != NULL ) { deleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, attributeListPtr, certInfoPtr->currentSelection.dnPtr ); } return( CRYPT_OK ); } static int copyPkiUserInfo( CERT_INFO *certInfoPtr, CERT_INFO *pkiUserInfoPtr ) { char commonName[ CRYPT_MAX_TEXTSIZE + 8 ]; int commonNameLength, status; assert( pkiUserInfoPtr->type == CRYPT_CERTTYPE_PKIUSER ); assert( pkiUserInfoPtr->certificate != NULL ); /* If there's no DN present in the request, try and fill it in from the CA-supplied PKI user info */ if( certInfoPtr->subjectName == NULL ) { /* If neither the request nor the PKI user info has a DN present, we can't continue */ if( pkiUserInfoPtr->subjectName == NULL ) return( CRYPT_ERROR_NOTINITED ); assert( pkiUserInfoPtr->subjectDNptr != NULL ); /* There's no DN present in the request, it's been supplied by the CA in the PKI user info, copy over the DN and its encoded form from the user info */ status = copyDN( &certInfoPtr->subjectName, pkiUserInfoPtr->subjectName ); if( cryptStatusError( status ) ) return( status ); if( ( certInfoPtr->subjectDNdata = \ clAlloc( "copyPkiUserInfo", pkiUserInfoPtr->subjectDNsize ) ) == NULL ) { deleteDN( &certInfoPtr->subjectName ); return( CRYPT_ERROR_MEMORY ); } memcpy( certInfoPtr->subjectDNdata, pkiUserInfoPtr->subjectDNptr, pkiUserInfoPtr->subjectDNsize ); certInfoPtr->subjectDNptr = certInfoPtr->subjectDNdata; certInfoPtr->subjectDNsize = pkiUserInfoPtr->subjectDNsize; /* Copy any additional attributes across */ return( copyPkiUserAttributes( certInfoPtr, pkiUserInfoPtr->attributes ) ); } /* If there's no PKI user DN with the potential to conflict with the one in the request present, copy any additional attributes across and exit */ if( pkiUserInfoPtr->subjectName == NULL ) return( copyPkiUserAttributes( certInfoPtr, pkiUserInfoPtr->attributes ) ); /* There's both a request DN and PKI user DN present. If the request contains only a CN, combine it with the PKI user DN and update the request */ status = getDNComponentValue( certInfoPtr->subjectName, CRYPT_CERTINFO_COMMONNAME, commonName, &commonNameLength, CRYPT_MAX_TEXTSIZE ); if( cryptStatusOK( status ) ) { void *tempDN = NULL; BOOLEAN isCommonNameDN; /* Check whether the request DN contains only a CN. There's no easy way to do this directly, the only way we can do it is by creating a temporary DN consisting of only the CN and comparing it to the request DN. We use sizeofDN() rather than compareDN() since it's much faster than a full DN comparison, this is safe because we know that both contain at least the same CN so any size mismatch indicates a DN value mismatch */ status = insertDNComponent( &tempDN, CRYPT_CERTINFO_COMMONNAME, commonName, commonNameLength, &certInfoPtr->errorType ); if( cryptStatusError( status ) ) return( status ); isCommonNameDN = sizeofDN( certInfoPtr->subjectName ) == \ sizeofDN( tempDN ); deleteDN( &tempDN ); /* If the request DN consists only of a CN, append it to the PKI user DN */ if( isCommonNameDN ) { STREAM stream; void *tempDNdata; int tempDNsize; /* Copy the DN template, append the user-supplied CN, and allocate room for the encoded form */ status = copyDN( &tempDN, pkiUserInfoPtr->subjectName ); if( cryptStatusError( status ) ) return( status ); status = insertDNComponent( &tempDN, CRYPT_CERTINFO_COMMONNAME, commonName, commonNameLength, &certInfoPtr->errorType ); if( cryptStatusOK( status ) ) { tempDNsize = sizeofDN( tempDN ); if( ( tempDNdata = clAlloc( "copyPkiUserInfo", tempDNsize ) ) == NULL ) status = CRYPT_ERROR_MEMORY; } if( cryptStatusError( status ) ) { if( tempDN != NULL ) deleteDN( &tempDN ); return( status ); } /* Everything went OK, replace the existing DN with the new one and set up the encoded form */ deleteDN( &certInfoPtr->subjectName ); certInfoPtr->subjectName = tempDN; sMemOpen( &stream, tempDNdata, tempDNsize ); writeDN( &stream, tempDN, DEFAULT_TAG ); assert( sStatusOK( &stream ) ); sMemDisconnect( &stream ); certInfoPtr->subjectDNdata = \ certInfoPtr->subjectDNptr = tempDNdata; certInfoPtr->subjectDNsize = tempDNsize; /* Copy any additional attributes across */ return( copyPkiUserAttributes( certInfoPtr, pkiUserInfoPtr->attributes ) ); } } /* There are full DNs present in both objects, make sure that they're the same and copy any additional attributes across */ if( !compareDN( certInfoPtr->subjectName, pkiUserInfoPtr->subjectName, FALSE ) ) return( CRYPT_ERROR_INVALID ); return( copyPkiUserAttributes( certInfoPtr, pkiUserInfoPtr->attributes ) ); } /**************************************************************************** * * * Set Cert Info * * * ****************************************************************************/ /* Set XYZZY certificate info */ static int setXyzzyInfo( CERT_INFO *certInfoPtr ) { ATTRIBUTE_LIST *attributeListPtr; const int keyUsage = CRYPT_KEYUSAGE_DIGITALSIGNATURE | \ CRYPT_KEYUSAGE_NONREPUDIATION | \ CRYPT_KEYUSAGE_KEYENCIPHERMENT | \ CRYPT_KEYUSAGE_KEYCERTSIGN | \ CRYPT_KEYUSAGE_CRLSIGN; const time_t currentTime = getApproxTime(); int status; /* Make sure that we haven't already set up this certificate as a XYZZY cert */ attributeListPtr = findAttributeField( certInfoPtr->attributes, CRYPT_CERTINFO_CERTPOLICYID, CRYPT_ATTRIBUTE_NONE ); if( attributeListPtr != NULL && \ attributeListPtr->valueLength == sizeofOID( OID_CRYPTLIB_XYZZYCERT ) && \ !memcmp( attributeListPtr->value, OID_CRYPTLIB_XYZZYCERT, attributeListPtr->valueLength ) ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_XYZZY, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } /* Clear any existing attribute values before trying to set new ones */ certInfoPtr->startTime = certInfoPtr->endTime = 0; deleteCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE ); deleteCertComponent( certInfoPtr, CRYPT_CERTINFO_CERTIFICATEPOLICIES ); /* Give the cert a 20-year expiry time, make it a self-signed CA cert with all key usage types enabled, and set the policy OID to identify it as a XYZZY cert */ certInfoPtr->startTime = currentTime; certInfoPtr->endTime = certInfoPtr->startTime + ( 86400L * 365 * 20 ); certInfoPtr->flags |= CERT_FLAG_SELFSIGNED; status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_CA, MESSAGE_VALUE_TRUE, CRYPT_UNUSED ); if( cryptStatusOK( status ) ) status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE, &keyUsage, CRYPT_UNUSED ); if( cryptStatusOK( status ) ) status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_CERTPOLICYID, OID_CRYPTLIB_XYZZYCERT, sizeofOID( OID_CRYPTLIB_XYZZYCERT ) ); if( cryptStatusOK( status ) ) findAttributeFieldEx( certInfoPtr->attributes, CRYPT_CERTINFO_CERTPOLICYID )->flags |= ATTR_FLAG_LOCKED; return( status ); } /* Set certificate cursor info */ static int setCertCursorInfo( CERT_INFO *certInfoPtr, const int value ) { const BOOLEAN isCertChain = \ ( certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN ) ? \ TRUE : FALSE; const BOOLEAN isRTCS = \ ( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST || \ certInfoPtr->type == CRYPT_CERTTYPE_RTCS_RESPONSE ) ? \ TRUE : FALSE; assert( isCertChain || certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \ certInfoPtr->type == CRYPT_CERTTYPE_CRL || isRTCS || \ certInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST || \ certInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE ); /* If it's a single cert, there's nothing to do. See the CRYPT_CERTINFO_CURRENT_CERTIFICATE ACL comment for why we (apparently) allow cursor movement movement on single certificates */ if( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE ) { assert( certInfoPtr->cCertCert->chainEnd <= 0 ); return( ( value == CRYPT_CURSOR_FIRST || \ value == CRYPT_CURSOR_LAST ) ? \ CRYPT_OK : CRYPT_ERROR_NOTFOUND ); } switch( value ) { case CRYPT_CURSOR_FIRST: if( isCertChain ) /* Set the chain position to -1 (= CRYPT_ERROR) to indicate that it's at the leaf cert, which is logically at position -1 in the chain */ certInfoPtr->cCertCert->chainPos = CRYPT_ERROR; else if( isRTCS ) { CERT_VAL_INFO *certValInfo = certInfoPtr->cCertVal; certValInfo->currentValidity = certValInfo->validityInfo; if( certValInfo->currentValidity == NULL ) return( CRYPT_ERROR_NOTFOUND ); } else { CERT_REV_INFO *certRevInfo = certInfoPtr->cCertRev; certRevInfo->currentRevocation = certRevInfo->revocations; if( certRevInfo->currentRevocation == NULL ) return( CRYPT_ERROR_NOTFOUND ); } break; case CRYPT_CURSOR_PREVIOUS: if( isCertChain ) { /* Adjust the chain position. Note that the value can go to -1 (= CRYPT_ERROR) to indicate that it's at the leaf cert, which is logically at position -1 in the chain */ if( certInfoPtr->cCertCert->chainPos < 0 ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->cCertCert->chainPos--; } else if( isRTCS ) { CERT_VAL_INFO *certValInfo = certInfoPtr->cCertVal; VALIDITY_INFO *valInfo = certValInfo->validityInfo; if( valInfo == NULL || \ certValInfo->currentValidity == NULL || \ valInfo == certValInfo->currentValidity ) /* No validity info or we're already at the start of the list */ return( CRYPT_ERROR_NOTFOUND ); /* Find the previous element in the list */ while( valInfo != NULL && \ valInfo->next != certValInfo->currentValidity ) valInfo = valInfo->next; certValInfo->currentValidity = valInfo; } else { CERT_REV_INFO *certRevInfo = certInfoPtr->cCertRev; REVOCATION_INFO *revInfo = certRevInfo->revocations; if( revInfo == NULL || \ certRevInfo->currentRevocation == NULL || \ revInfo == certRevInfo->currentRevocation ) /* No revocations or we're already at the start of the list */ return( CRYPT_ERROR_NOTFOUND ); /* Find the previous element in the list */ while( revInfo != NULL && \ revInfo->next != certRevInfo->currentRevocation ) revInfo = revInfo->next; certRevInfo->currentRevocation = revInfo; } break; case CRYPT_CURSOR_NEXT: if( isCertChain ) { if( certInfoPtr->cCertCert->chainPos >= certInfoPtr->cCertCert->chainEnd - 1 ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->cCertCert->chainPos++; } else if( isRTCS ) { CERT_VAL_INFO *certValInfo = certInfoPtr->cCertVal; if( certValInfo->currentValidity == NULL || \ certValInfo->currentValidity->next == NULL ) return( CRYPT_ERROR_NOTFOUND ); certValInfo->currentValidity = certValInfo->currentValidity->next; } else { CERT_REV_INFO *certRevInfo = certInfoPtr->cCertRev; if( certRevInfo->currentRevocation == NULL || \ certRevInfo->currentRevocation->next == NULL ) return( CRYPT_ERROR_NOTFOUND ); certRevInfo->currentRevocation = certRevInfo->currentRevocation->next; } break; case CRYPT_CURSOR_LAST: if( isCertChain ) certInfoPtr->cCertCert->chainPos = certInfoPtr->cCertCert->chainEnd - 1; else if( isRTCS ) { CERT_VAL_INFO *certValInfo = certInfoPtr->cCertVal; VALIDITY_INFO *valInfo = certValInfo->validityInfo; if( valInfo == NULL ) /* No validity info present */ return( CRYPT_ERROR_NOTFOUND ); /* Go to the end of the list */ while( valInfo->next != NULL ) valInfo = valInfo->next; certValInfo->currentValidity = valInfo; } else { CERT_REV_INFO *certRevInfo = certInfoPtr->cCertRev; REVOCATION_INFO *revInfo = certRevInfo->revocations; if( revInfo == NULL ) /* No revocations present */ return( CRYPT_ERROR_NOTFOUND ); /* Go to the end of the list */ while( revInfo->next != NULL ) revInfo = revInfo->next; certRevInfo->currentRevocation = revInfo; } break; default: return( CRYPT_ARGERROR_NUM1 ); } return( CRYPT_OK ); } /* Set attribute cursor info */ static int setCursorInfo( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE certInfoType, const int value ) { assert( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP || \ certInfoType == CRYPT_ATTRIBUTE_CURRENT || \ certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE ); /* If the new position is specified relative to a previous position, try and move to that position. Note that the seemingly illogical comparison is used because the cursor positioning codes are negative values */ if( value <= CRYPT_CURSOR_FIRST && value >= CRYPT_CURSOR_LAST ) { ATTRIBUTE_LIST *attributeCursor; /* If we're moving to an extension field and there's a saved GeneralName selection present, we've tried to select a non-present GeneralName, so we can't move to a field in it */ if( certInfoType != CRYPT_ATTRIBUTE_CURRENT_GROUP && \ certInfoPtr->currentSelection.generalName != CRYPT_ATTRIBUTE_NONE ) return( CRYPT_ERROR_NOTFOUND ); /* If it's an absolute positioning code, pre-set the attribute cursor if required */ if( value == CRYPT_CURSOR_FIRST || value == CRYPT_CURSOR_LAST ) { if( certInfoPtr->attributes == NULL ) return( CRYPT_ERROR_NOTFOUND ); /* It's an absolute attribute positioning code, reset the attribute cursor to the start of the list before we try to move it */ if( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP ) certInfoPtr->attributeCursor = certInfoPtr->attributes; else /* It's a field or component positioning code, initialise the attribute cursor if necessary */ if( certInfoPtr->attributeCursor == NULL ) certInfoPtr->attributeCursor = certInfoPtr->attributes; /* If there are no attributes present, return the appropriate error code */ if( certInfoPtr->attributeCursor == NULL ) return( ( value == CRYPT_CURSOR_FIRST || \ value == CRYPT_CURSOR_LAST ) ? \ CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_NOTINITED ); } else /* It's a relative positioning code, return a not-inited error rather than a not-found error if the cursor isn't set since there may be attributes present but the cursor hasn't been initialised yet by selecting the first or last absolute attribute */ if( certInfoPtr->attributeCursor == NULL ) return( CRYPT_ERROR_NOTINITED ); /* Move the attribute cursor */ attributeCursor = certMoveAttributeCursor( certInfoPtr->attributeCursor, certInfoType, value ); if( attributeCursor == NULL ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->attributeCursor = attributeCursor; syncSelection( certInfoPtr ); return( CRYPT_OK ); } /* It's a field in an extension, try and move to the start of the extension that contains this field */ if( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP ) { ATTRIBUTE_LIST *attributeListPtr; attributeListPtr = findAttribute( certInfoPtr->attributes, value, TRUE ); if( attributeListPtr == NULL ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->attributeCursor = attributeListPtr; syncSelection( certInfoPtr ); return( CRYPT_OK ); } assert( certInfoType == CRYPT_ATTRIBUTE_CURRENT || \ certInfoType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE ); assert( value >= CRYPT_CERTINFO_FIRST_EXTENSION && \ value <= CRYPT_CERTINFO_LAST_EXTENSION ); /* If it's a GeneralName selection component, locate the attribute field that it corresponds to */ if( isGeneralNameSelectionComponent( value ) ) return( selectGeneralName( certInfoPtr, value, MAY_BE_ABSENT ) ); /* It's a standard attribute field, try and locate it */ return( moveCursorToField( certInfoPtr, value ) ); } /**************************************************************************** * * * Add a Component * * * ****************************************************************************/ /* Add a certificate component */ int addCertComponent( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE certInfoType, const void *certInfo, const int certInfoLength ) { CRYPT_CERTIFICATE addedCert; CERT_INFO *addedCertInfoPtr; int status; /* If we're adding data to a certificate, clear the error information */ if( !isPseudoInformation( certInfoType ) ) clearErrorInfo( certInfoPtr ); /* If it's a GeneralName or DN component, add it. These are special- case attribute values, so they have to come before the attribute- handling code */ if( isGeneralNameSelectionComponent( certInfoType ) ) { status = selectGeneralName( certInfoPtr, certInfoType, MAY_BE_ABSENT ); if( cryptStatusError( status ) ) return( status ); return( selectGeneralName( certInfoPtr, CRYPT_ATTRIBUTE_NONE, MUST_BE_PRESENT ) ); } if( isGeneralNameComponent( certInfoType ) ) { status = selectGeneralName( certInfoPtr, CRYPT_ATTRIBUTE_NONE, CREATE_IF_ABSENT ); if( cryptStatusOK( status ) ) status = addAttributeField( &certInfoPtr->attributes, ( certInfoPtr->attributeCursor != NULL ) ? \ certInfoPtr->attributeCursor->fieldID : \ certInfoPtr->currentSelection.generalName, certInfoType, certInfo, certInfoLength, ATTR_FLAG_NONE, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); if( cryptStatusOK( status ) && \ certInfoPtr->currentSelection.updateCursor ) /* If we couldn't update the cursor earlier on because the attribute field in question hadn't been created yet, do it now */ selectGeneralName( certInfoPtr, certInfoPtr->currentSelection.generalName, MAY_BE_ABSENT ); return( status ); } if( isDNComponent( certInfoType ) ) { /* Add the string component to the DN */ status = selectDN( certInfoPtr, CRYPT_ATTRIBUTE_NONE, CREATE_IF_ABSENT ); if( cryptStatusOK( status ) ) status = insertDNComponent( certInfoPtr->currentSelection.dnPtr, certInfoType, certInfo, certInfoLength, &certInfoPtr->errorType ); if( cryptStatusOK( status ) && \ certInfoPtr->currentSelection.updateCursor ) /* If we couldn't update the cursor earlier on because the attribute field in question hadn't been created yet, do it now */ selectGeneralName( certInfoPtr, certInfoPtr->currentSelection.generalName, MAY_BE_ABSENT ); if( cryptStatusError( status ) && status != CRYPT_ERROR_MEMORY ) certInfoPtr->errorLocus = certInfoType; return( status ); } /* If it's standard cert or CMS attribute, add it to the certificate */ if( ( certInfoType >= CRYPT_CERTINFO_FIRST_EXTENSION && \ certInfoType <= CRYPT_CERTINFO_LAST_EXTENSION ) || \ ( certInfoType >= CRYPT_CERTINFO_FIRST_CMS && \ certInfoType <= CRYPT_CERTINFO_LAST_CMS ) ) { int localCertInfoType = certInfoType; /* Revocation reason codes are actually a single range of values spread across two different extensions, so we adjust the (internal) type based on the reason code value */ if( certInfoType == CRYPT_CERTINFO_CRLREASON || \ certInfoType == CRYPT_CERTINFO_CRLEXTREASON ) localCertInfoType = \ ( *( ( int * ) certInfo ) < CRYPT_CRLREASON_LAST ) ? \ CRYPT_CERTINFO_CRLREASON : CRYPT_CERTINFO_CRLEXTREASON; /* If it's a CRL, RTCS, or OCSP per-entry attribute, add the attribute to the currently selected entry unless it's a revocation request, in which case it goes in with the main attributes */ if( isRevocationEntryComponent( localCertInfoType ) && \ certInfoPtr->type != CRYPT_CERTTYPE_REQUEST_REVOCATION ) { if( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST || \ certInfoPtr->type == CRYPT_CERTTYPE_RTCS_RESPONSE ) { if( certInfoPtr->cCertVal->currentValidity == NULL ) return( CRYPT_ERROR_NOTFOUND ); return( addAttributeField( \ &certInfoPtr->cCertVal->currentValidity->attributes, localCertInfoType, CRYPT_ATTRIBUTE_NONE, certInfo, certInfoLength, ATTR_FLAG_NONE, &certInfoPtr->errorLocus, &certInfoPtr->errorType ) ); } assert( certInfoPtr->type == CRYPT_CERTTYPE_CRL || \ certInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST || \ certInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE ); if( certInfoPtr->cCertRev->currentRevocation == NULL ) return( CRYPT_ERROR_NOTFOUND ); return( addAttributeField( \ &certInfoPtr->cCertRev->currentRevocation->attributes, localCertInfoType, CRYPT_ATTRIBUTE_NONE, certInfo, certInfoLength, ATTR_FLAG_NONE, &certInfoPtr->errorLocus, &certInfoPtr->errorType ) ); } return( addAttributeField( &certInfoPtr->attributes, localCertInfoType, CRYPT_ATTRIBUTE_NONE, certInfo, certInfoLength, ATTR_FLAG_NONE, &certInfoPtr->errorLocus, &certInfoPtr->errorType ) ); } /* If it's anything else, handle it specially */ switch( certInfoType ) { case CRYPT_CERTINFO_SELFSIGNED: if( *( ( int * ) certInfo ) ) certInfoPtr->flags |= CERT_FLAG_SELFSIGNED; else certInfoPtr->flags &= ~CERT_FLAG_SELFSIGNED; return( CRYPT_OK ); case CRYPT_CERTINFO_XYZZY: return( setXyzzyInfo( certInfoPtr ) ); case CRYPT_CERTINFO_CURRENT_CERTIFICATE: return( setCertCursorInfo( certInfoPtr, *( ( int * ) certInfo ) ) ); case CRYPT_ATTRIBUTE_CURRENT_GROUP: case CRYPT_ATTRIBUTE_CURRENT: case CRYPT_ATTRIBUTE_CURRENT_INSTANCE: return( setCursorInfo( certInfoPtr, certInfoType, *( ( int * ) certInfo ) ) ); case CRYPT_CERTINFO_TRUSTED_USAGE: certInfoPtr->cCertCert->trustedUsage = *( ( int * ) certInfo ); return( CRYPT_OK ); case CRYPT_CERTINFO_TRUSTED_IMPLICIT: return( krnlSendMessage( certInfoPtr->ownerHandle, IMESSAGE_SETATTRIBUTE, &certInfoPtr->objectHandle, *( ( int * ) certInfo ) ? \ CRYPT_IATTRIBUTE_CERT_TRUSTED : \ CRYPT_IATTRIBUTE_CERT_UNTRUSTED ) ); case CRYPT_CERTINFO_SIGNATURELEVEL: certInfoPtr->cCertRev->signatureLevel = *( ( int * ) certInfo ); return( CRYPT_OK ); case CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO: return( copyPublicKeyInfo( certInfoPtr, *( ( CRYPT_HANDLE * ) certInfo ), NULL ) ); case CRYPT_CERTINFO_CERTIFICATE: /* If it's a certificate, copy across various components or store the entire cert where required */ status = krnlSendMessage( *( ( CRYPT_HANDLE * ) certInfo ), IMESSAGE_GETDEPENDENT, &addedCert, OBJECT_TYPE_CERTIFICATE ); if( cryptStatusError( status ) ) return( status ); /* If it's a cert chain, we're adding the complete cert, just store it and exit */ if( certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN ) { int i; if( certInfoPtr->cCertCert->chainEnd >= MAX_CHAINLENGTH - 1 ) return( CRYPT_ERROR_OVERFLOW ); /* Perform a simple check to make sure that it hasn't been added already */ for( i = 0; i < certInfoPtr->cCertCert->chainEnd && \ i < MAX_CHAINLENGTH; i++ ) { if( cryptStatusOK( \ krnlSendMessage( addedCert, IMESSAGE_COMPARE, &certInfoPtr->cCertCert->chain[ i ], MESSAGE_COMPARE_CERTOBJ ) ) ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CERTIFICATE, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } } if( i >= MAX_CHAINLENGTH ) retIntError(); /* Add the user cert and increment its reference count */ krnlSendNotifier( addedCert, IMESSAGE_INCREFCOUNT ); certInfoPtr->cCertCert->chain[ certInfoPtr->cCertCert->chainEnd++ ] = addedCert; return( CRYPT_OK ); } /* For remaining operations we need access to the user cert internals */ status = krnlAcquireObject( addedCert, OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = copyUserCertInfo( certInfoPtr, addedCertInfoPtr, *( ( CRYPT_HANDLE * ) certInfo ) ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_CERTINFO_CACERTIFICATE: /* We can't add another CA cert if there's already one present, in theory this is valid but it's more likely to be an implementation problem than an attempt to query multiple CAs through a single responder */ if( certInfoPtr->certHashSet ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CACERTIFICATE, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } assert( certInfoPtr->version == 1 ); /* Get the cert handle and make sure that it really is a CA cert */ status = krnlSendMessage( *( ( CRYPT_HANDLE * ) certInfo ), IMESSAGE_GETDEPENDENT, &addedCert, OBJECT_TYPE_CERTIFICATE ); if( cryptStatusError( status ) ) return( status ); if( cryptStatusError( \ krnlSendMessage( addedCert, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_CA ) ) ) return( CRYPT_ARGERROR_NUM1 ); status = krnlAcquireObject( addedCert, OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = copyCaCertInfo( certInfoPtr, addedCertInfoPtr ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_CERTINFO_SERIALNUMBER: assert( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE ); if( certInfoPtr->cCertCert->serialNumber != NULL ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_SERIALNUMBER, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } return( setSerialNumber( certInfoPtr, certInfo, certInfoLength ) ); case CRYPT_CERTINFO_SUBJECTNAME: case CRYPT_CERTINFO_ISSUERNAME: if( *( ( int * ) certInfo ) != CRYPT_UNUSED ) return( CRYPT_ARGERROR_NUM1 ); return( selectDN( certInfoPtr, certInfoType, MAY_BE_ABSENT ) ); case CRYPT_CERTINFO_VALIDFROM: case CRYPT_CERTINFO_THISUPDATE: { time_t certTime = *( ( time_t * ) certInfo ); if( certInfoPtr->startTime > 0 ) { setErrorInfo( certInfoPtr, certInfoType, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } if( certInfoPtr->endTime > 0 && \ certTime >= certInfoPtr->endTime ) { setErrorInfo( certInfoPtr, ( certInfoType == CRYPT_CERTINFO_VALIDFROM ) ? \ CRYPT_CERTINFO_VALIDTO : CRYPT_CERTINFO_NEXTUPDATE, CRYPT_ERRTYPE_CONSTRAINT ); return( CRYPT_ARGERROR_STR1 ); } certInfoPtr->startTime = certTime; return( CRYPT_OK ); } case CRYPT_CERTINFO_VALIDTO: case CRYPT_CERTINFO_NEXTUPDATE: { time_t certTime = *( ( time_t * ) certInfo ); if( certInfoPtr->endTime > 0 ) { setErrorInfo( certInfoPtr, certInfoType, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } if( certInfoPtr->startTime > 0 && \ certTime <= certInfoPtr->startTime ) { setErrorInfo( certInfoPtr, ( certInfoType == CRYPT_CERTINFO_VALIDTO ) ? \ CRYPT_CERTINFO_VALIDFROM : CRYPT_CERTINFO_THISUPDATE, CRYPT_ERRTYPE_CONSTRAINT ); return( CRYPT_ARGERROR_STR1 ); } certInfoPtr->endTime = certTime; return( CRYPT_OK ); } case CRYPT_CERTINFO_CERTREQUEST: /* Make sure that we haven't already got a public key or DN present */ if( ( certInfoPtr->iPubkeyContext != CRYPT_ERROR || \ certInfoPtr->publicKeyInfo != NULL ) || \ certInfoPtr->subjectName != NULL ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CERTREQUEST, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } status = krnlAcquireObject( *( ( CRYPT_CERTIFICATE * ) certInfo ), OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = copyCertReqInfo( certInfoPtr, addedCertInfoPtr ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_CERTINFO_REVOCATIONDATE: { time_t certTime = *( ( time_t * ) certInfo ); time_t *revocationTimePtr = getRevocationTimePtr( certInfoPtr ); if( *revocationTimePtr > 0 ) { setErrorInfo( certInfoPtr, certInfoType, CRYPT_ERRTYPE_ATTR_PRESENT ); return( CRYPT_ERROR_INITED ); } *revocationTimePtr = certTime; return( CRYPT_OK ); } case CRYPT_CERTINFO_DN: return( getEncodedDn( certInfoPtr, certInfo, certInfoLength ) ); case CRYPT_IATTRIBUTE_CRLENTRY: { STREAM stream; assert( certInfoPtr->type == CRYPT_CERTTYPE_CRL ); /* The revocation information is being provided to us in pre- encoded form from a cert store, decode it so that we can add it to the CRL */ sMemConnect( &stream, certInfo, certInfoLength ); status = readCRLentry( &stream, &certInfoPtr->cCertRev->revocations, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); sMemDisconnect( &stream ); return( status ); } case CRYPT_IATTRIBUTE_CERTCOLLECTION: return( copyCertChain( certInfoPtr, *( ( CRYPT_CERTIFICATE * ) certInfo ), TRUE ) ); case CRYPT_IATTRIBUTE_RTCSREQUEST: status = krnlAcquireObject( *( ( CRYPT_CERTIFICATE * ) certInfo ), OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = copyRtcsReqInfo( certInfoPtr, addedCertInfoPtr ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_IATTRIBUTE_OCSPREQUEST: status = krnlAcquireObject( *( ( CRYPT_CERTIFICATE * ) certInfo ), OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = copyOcspReqInfo( certInfoPtr, addedCertInfoPtr ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_IATTRIBUTE_REVREQUEST: status = krnlAcquireObject( *( ( CRYPT_CERTIFICATE * ) certInfo ), OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = copyRevReqInfo( certInfoPtr, addedCertInfoPtr ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_IATTRIBUTE_PKIUSERINFO: status = krnlAcquireObject( *( ( CRYPT_HANDLE * ) certInfo ), OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = copyPkiUserInfo( certInfoPtr, addedCertInfoPtr ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_IATTRIBUTE_BLOCKEDATTRS: status = krnlAcquireObject( *( ( CRYPT_HANDLE * ) certInfo ), OBJECT_TYPE_CERTIFICATE, ( void ** ) &addedCertInfoPtr, CRYPT_ARGERROR_NUM1 ); if( cryptStatusError( status ) ) return( status ); status = sanitiseCertAttributes( certInfoPtr, addedCertInfoPtr->attributes ); krnlReleaseObject( addedCertInfoPtr->objectHandle ); return( status ); case CRYPT_IATTRIBUTE_AUTHCERTID: assert( certInfoLength == KEYID_SIZE ); memcpy( certInfoPtr->cCertReq->authCertID, certInfo, KEYID_SIZE ); return( CRYPT_OK ); } /* Everything else isn't available */ assert( NOTREACHED ); return( CRYPT_ARGERROR_VALUE ); }