static int addStandardExtensions( INOUT CERT_INFO *certInfoPtr ) { BOOLEAN isCA = FALSE; int keyUsage, extKeyUsage, value, status; assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) ); /* Get the implicit keyUsage flags (based on any extended key usage extensions present) and explicit key usage flags, which we use to extend the basic keyUsage flags if required */ status = getKeyUsageFromExtKeyUsage( certInfoPtr, &extKeyUsage, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); if( cryptStatusError( status ) ) return( status ); status = getAttributeFieldValue( certInfoPtr->attributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE, &keyUsage ); if( cryptStatusError( status ) ) { if( status != CRYPT_ERROR_NOTFOUND ) return( status ); /* There's no keyUsage attribute present, mark the value as being not set so that we explicitly set it later */ keyUsage = CRYPT_ERROR; } /* If there's an explicit key usage present, make sure that it's consistent with the implicit key usage flags derived from the extended key usage. We mask out the nonRepudiation bit for reasons given in chk_cert.c. This check is also performed by checkCert(), however we need to explicitly perform it here as well since we need to add a key usage to match the extKeyUsage before calling checkCert() if one wasn't explicitly set or checkCert() will reject the certificate because of the inconsistent keyUsage */ if( keyUsage > 0 ) { const int effectiveKeyUsage = \ extKeyUsage & ~CRYPT_KEYUSAGE_NONREPUDIATION; if( ( keyUsage & effectiveKeyUsage ) != effectiveKeyUsage ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ERRTYPE_CONSTRAINT ); return( CRYPT_ERROR_INVALID ); } } /* Check whether this is a CA certificate. If there's no basicConstraints attribute present, add one and make it a non-CA certificate */ status = getAttributeFieldValue( certInfoPtr->attributes, CRYPT_CERTINFO_CA, CRYPT_ATTRIBUTE_NONE, &value ); if( cryptStatusOK( status ) ) isCA = ( value > 0 ) ? TRUE : FALSE; else { status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_CA, FALSE ); if( cryptStatusError( status ) ) return( status ); } /* If there's no explicit keyUsage information present add it based on various implicit information. We also add key feature information which is used to help automate key management, for example to inhibit speculative reads of keys held in removable tokens, which can result in spurious insert-token dialogs being presented to the user outside the control of cryptlib if the token isn't present */ if( keyUsage <= 0 ) { /* If there's no implicit key usage present and it's not a CA (for which we don't want to set things like encryption flags for the CA certificate), set the key usage flags based on the capabilities of the associated context. Because no-one can figure out what the nonRepudiation flag signifies we don't set this, if the user wants it they have to specify it explicitly. Similarly we don't try and set the keyAgreement encipher/decipher- only flags, which were tacked on as variants of keyAgreement long after the basic keyAgreement flag was defined */ if( extKeyUsage <= 0 && !isCA ) { keyUsage = 0; /* Reset key usage */ if( certInfoPtr->iPubkeyContext != CRYPT_ERROR ) { /* There's a context present, check its capabilities. This has the advantage that it takes into account any ACLs that may exist for the key */ if( cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_SIGCHECK ) ) ) keyUsage = CRYPT_KEYUSAGE_DIGITALSIGNATURE; if( cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_ENCRYPT ) ) ) keyUsage |= CRYPT_KEYUSAGE_KEYENCIPHERMENT; if( cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_KA_EXPORT ) ) || \ cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_KA_IMPORT ) ) ) keyUsage |= CRYPT_KEYUSAGE_KEYAGREEMENT; } else { /* There's no context present (the key is present as encoded data), assume we can do whatever the algorithm allows */ if( isSigAlgo( certInfoPtr->publicKeyAlgo ) ) keyUsage = CRYPT_KEYUSAGE_DIGITALSIGNATURE; if( isCryptAlgo( certInfoPtr->publicKeyAlgo ) ) keyUsage |= CRYPT_KEYUSAGE_KEYENCIPHERMENT; if( isKeyxAlgo( certInfoPtr->publicKeyAlgo ) ) keyUsage |= CRYPT_KEYUSAGE_KEYAGREEMENT; } } else { /* There's an extended key usage set but no basic keyUsage, make the keyUsage consistent with the usage flags derived from the extended usage */ keyUsage = extKeyUsage; /* If it's a CA key, make sure that it's a signing key and enable its use for certification-related purposes*/ if( isCA ) { BOOLEAN usageOK; if( certInfoPtr->iPubkeyContext != CRYPT_ERROR ) { usageOK = cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_SIGCHECK ) ); } else usageOK = isSigAlgo( certInfoPtr->publicKeyAlgo ); if( !usageOK ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CA, CRYPT_ERRTYPE_CONSTRAINT ); return( CRYPT_ERROR_INVALID ); } keyUsage |= KEYUSAGE_CA; } } ENSURES( keyUsage > CRYPT_KEYUSAGE_NONE && \ keyUsage < CRYPT_KEYUSAGE_LAST ); status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE, keyUsage ); if( cryptStatusError( status ) ) return( status ); } if( certInfoPtr->publicKeyFeatures > 0 ) { /* This is a bitstring so we only add it if there are feature flags present to avoid writing zero-length values */ status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYFEATURES, certInfoPtr->publicKeyFeatures ); if( cryptStatusError( status ) && status != CRYPT_ERROR_INITED ) return( status ); } /* Add the subjectKeyIdentifier */ return( addCertComponentString( certInfoPtr, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER, certInfoPtr->publicKeyID, KEYID_SIZE ) ); }
static int activateConnection( INOUT SESSION_INFO *sessionInfoPtr ) { CRYPT_ATTRIBUTE_TYPE errorAttribute; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); /* Make sure that everything is set up ready to go */ errorAttribute = isServer( sessionInfoPtr ) ? \ checkServerParameters( sessionInfoPtr ) : \ checkClientParameters( sessionInfoPtr ); if( errorAttribute != CRYPT_ATTRIBUTE_NONE ) { setErrorInfo( sessionInfoPtr, errorAttribute, CRYPT_ERRTYPE_ATTR_ABSENT ); return( CRYPT_ERROR_NOTINITED ); } ENSURES( isServer( sessionInfoPtr ) || \ findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_SERVER_NAME ) != NULL || \ sessionInfoPtr->networkSocket != CRYPT_ERROR ); ENSURES( findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_SERVER_PORT ) != NULL || \ sessionInfoPtr->protocolInfo->port > 0 ); /* Allocate the send and receive buffers if necessary. The send buffer isn't used for request-response session types that use the receive buffer for both outgoing and incoming data so we only allocate it if it's actually required */ if( sessionInfoPtr->sendBuffer == NULL ) { REQUIRES( sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->receiveBufSize < MAX_BUFFER_SIZE ); REQUIRES( ( sessionInfoPtr->sendBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->sendBufSize < MAX_BUFFER_SIZE ) || \ sessionInfoPtr->sendBufSize == CRYPT_UNUSED ); if( ( sessionInfoPtr->receiveBuffer = \ clAlloc( "activateConnection", \ sessionInfoPtr->receiveBufSize + 8 ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); if( sessionInfoPtr->sendBufSize != CRYPT_UNUSED ) { /* When allocating the send buffer we use the size given for the receive buffer since the user may have overridden the default buffer size */ if( ( sessionInfoPtr->sendBuffer = \ clAlloc( "activateConnection", \ sessionInfoPtr->receiveBufSize + 8 ) ) == NULL ) { clFree( "activateConnection", sessionInfoPtr->receiveBuffer ); sessionInfoPtr->receiveBuffer = NULL; return( CRYPT_ERROR_MEMORY ); } sessionInfoPtr->sendBufSize = sessionInfoPtr->receiveBufSize; } } ENSURES( sessionInfoPtr->receiveBuffer != NULL && \ sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->receiveBufSize < MAX_BUFFER_SIZE ); ENSURES( sessionInfoPtr->sendBufSize == CRYPT_UNUSED || \ sessionInfoPtr->sendBuffer != NULL ); /* Set timeouts if they're not set yet. If there's an error then we use the default value rather than aborting the entire session because of a minor difference in timeout values, although we also warn the caller in debug mode */ if( sessionInfoPtr->connectTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_CONNECTTIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->connectTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get connect timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->connectTimeout = 30; } } if( sessionInfoPtr->readTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_READTIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->readTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get read timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->readTimeout = 30; } } if( sessionInfoPtr->writeTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_WRITETIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->writeTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get write timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->writeTimeout = 30; } } /* Wait for any async driver binding to complete. We can delay this until this very late stage because no networking functionality is used until this point */ if( !krnlWaitSemaphore( SEMAPHORE_DRIVERBIND ) ) { /* The kernel is shutting down, bail out */ return( CRYPT_ERROR_PERMISSION ); } /* If this is the first time that we've got here, activate the session */ if( !( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) ) { REQUIRES( !( sessionInfoPtr->flags & SESSION_ISOPEN ) ) status = sessionInfoPtr->connectFunction( sessionInfoPtr ); if( cryptStatusError( status ) ) return( status ); } /* If it's a secure data transport session, complete the session state setup. Note that some sessions dynamically change the protocol information during the handshake to accommodate parameters negotiated during the handshake so we can only access the protocol information after the handshake has completed */ if( !sessionInfoPtr->protocolInfo->isReqResp ) { /* Complete the session handshake to set up the secure state */ status = sessionInfoPtr->transactFunction( sessionInfoPtr ); if( cryptStatusError( status ) ) { /* If we need feedback from the user before we can complete the handshake (for example checking a user name and password or certificate supplied by the other side) we remain in the handshake state so that the user can re-activate the session after confirming (or denying) the check */ if( status == CRYPT_ENVELOPE_RESOURCE ) sessionInfoPtr->flags |= SESSION_PARTIALOPEN; return( status ); } /* Notify the kernel that the session key context is attached to the session object. Note that we increment its reference count even though it's an internal object used only by the session because otherwise it'll be automatically destroyed by the kernel as a zero-reference dependent object when the session object is destroyed (but before the session object itself since the context is just a dependent object). This automatic cleanup could cause problems for lower-level session management code that tries to work with the (apparently still-valid) handle, for example protocols that need to encrypt a close-channel message on session shutdown */ krnlSendMessage( sessionInfoPtr->objectHandle, IMESSAGE_SETDEPENDENT, &sessionInfoPtr->iCryptInContext, SETDEP_OPTION_INCREF ); /* Set up the buffer management variables */ sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd = 0; sessionInfoPtr->sendBufPos = sessionInfoPtr->sendBufStartOfs; /* For data transport sessions, partial reads and writes (that is, sending and receiving partial packets in the presence of timeouts) are permitted */ sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALREAD, TRUE ); sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALWRITE, TRUE ); } /* The handshake has been completed, switch from the handshake timeout to the data transfer timeout and remember that the session has been successfully established */ sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HANDSHAKECOMPLETE, TRUE ); sessionInfoPtr->flags &= ~SESSION_PARTIALOPEN; sessionInfoPtr->flags |= SESSION_ISOPEN; return( CRYPT_OK ); }
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 ) ); }
BOOLEAN checkAttributesConsistent( INOUT SESSION_INFO *sessionInfoPtr, IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute ) { static const MAP_TABLE excludedAttrTbl[] = { { CRYPT_SESSINFO_REQUEST, CHECK_ATTR_REQUEST | CHECK_ATTR_PRIVKEY | CHECK_ATTR_PRIVKEYSET }, { CRYPT_SESSINFO_PRIVATEKEY, CHECK_ATTR_PRIVKEY | CHECK_ATTR_PRIVKEYSET }, { CRYPT_SESSINFO_CACERTIFICATE, CHECK_ATTR_CACERT | CHECK_ATTR_FINGERPRINT }, { CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1, CHECK_ATTR_FINGERPRINT | CHECK_ATTR_CACERT }, { CRYPT_ERROR, 0 }, { CRYPT_ERROR, 0 } }; int flags = 0, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); REQUIRES_B( attribute == CRYPT_SESSINFO_REQUEST || \ attribute == CRYPT_SESSINFO_PRIVATEKEY || \ attribute == CRYPT_SESSINFO_CACERTIFICATE || \ attribute == CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1 ); /* Find the excluded-attribute information for this attribute */ status = mapValue( attribute, &flags, excludedAttrTbl, FAILSAFE_ARRAYSIZE( excludedAttrTbl, MAP_TABLE ) ); ENSURES( cryptStatusOK( status ) ); /* Make sure that none of the excluded attributes are present */ if( ( flags & CHECK_ATTR_REQUEST ) && \ sessionInfoPtr->iCertRequest != CRYPT_ERROR ) { setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_REQUEST, CRYPT_ERRTYPE_ATTR_PRESENT ); return( FALSE ); } if( ( flags & CHECK_ATTR_PRIVKEYSET ) && \ sessionInfoPtr->privKeyset != CRYPT_ERROR ) { setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_CMP_PRIVKEYSET, CRYPT_ERRTYPE_ATTR_PRESENT ); return( FALSE ); } if( ( flags & CHECK_ATTR_CACERT ) && \ sessionInfoPtr->iAuthInContext != CRYPT_ERROR ) { setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_CACERTIFICATE, CRYPT_ERRTYPE_ATTR_PRESENT ); return( FALSE ); } if( ( flags & CHECK_ATTR_FINGERPRINT ) && \ findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1 ) != NULL ) { setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1, CRYPT_ERRTYPE_ATTR_PRESENT ); return( FALSE ); } return( TRUE ); }