static int exec_cmpsvr(int argc, char **argv) { CRYPT_KEYSET cakeys, store; CRYPT_SESSION session; CRYPT_CONTEXT ca_privkey; int status; const char *dbfilename = argv[0]; char cakeysfilename[4096]; /* PATH_MAX */ if (argc < 1) { fprintf(stderr, "missing dbfilename\n"); return 1; } status = cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_CMP_SERVER); WARN_AND_RETURN_IF(status); /* open store */ status = cryptKeysetOpen(&store, CRYPT_UNUSED, CRYPT_KEYSET_DATABASE_STORE, dbfilename, CRYPT_KEYOPT_NONE); WARN_AND_RETURN_IF(status); /* get ca privkey */ snprintf(cakeysfilename, 4095, "%s.keys", dbfilename); cakeysfilename[4095] = '\0'; status = cryptKeysetOpen(&cakeys, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cakeysfilename, CRYPT_KEYOPT_NONE); WARN_AND_RETURN_IF(status); status = cryptGetPrivateKey(cakeys, &ca_privkey, CRYPT_KEYID_NAME, DEFAULT_CA_PRIVKEY_LABEL, DEFAULT_PASSWORD); WARN_AND_RETURN_IF(status); status = cryptKeysetClose(cakeys); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_KEYSET, store); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, ca_privkey); WARN_AND_RETURN_IF(status); status = cryptSetAttributeString(session, CRYPT_SESSINFO_SERVER_NAME, "127.0.0.1", 9); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_SERVER_PORT, 65000); WARN_AND_RETURN_IF(status); fprintf(stderr, "before setting ACTIVE\n"); status = cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1); if (!cryptStatusOK(status)) { CRYPT_ERRTYPE_TYPE errtype; CRYPT_ATTRIBUTE_TYPE locus; char *errstring; int errstringlen; cryptGetAttribute(session, CRYPT_ATTRIBUTE_ERRORTYPE, (int *)&errtype); cryptGetAttribute(session, CRYPT_ATTRIBUTE_ERRORLOCUS, (int *)&locus); fprintf(stderr, "session errtype %d locus %d\n", errtype, locus); cryptGetAttributeString(session, CRYPT_ATTRIBUTE_ERRORMESSAGE, NULL, &errstringlen); errstring = malloc(errstringlen + 10); cryptGetAttributeString(session, CRYPT_ATTRIBUTE_ERRORMESSAGE, errstring, &errstringlen); errstring[errstringlen] = 0; fprintf(stderr, "session errmsg: %s\n", errstring); free(errstring); } WARN_AND_RETURN_IF(status); status = cryptKeysetClose(store); WARN_AND_RETURN_IF(status); status = cryptDestroyContext(ca_privkey); WARN_AND_RETURN_IF(status); status = cryptDestroySession(session); WARN_AND_RETURN_IF(status); return 0; }
static int checkPkiUserInfo( INOUT SESSION_INFO *sessionInfoPtr, INOUT SCEP_PROTOCOL_INFO *protocolInfo ) { const ATTRIBUTE_LIST *userNamePtr = \ findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_USERNAME ); MESSAGE_KEYMGMT_INFO getkeyInfo; MESSAGE_DATA msgData; BYTE keyID[ 64 + 8 ]; BYTE requestPassword[ CRYPT_MAX_TEXTSIZE + 8 ]; BYTE userPassword[ CRYPT_MAX_TEXTSIZE + 8 ]; int requestPasswordSize, userPasswordSize = DUMMY_INIT; int keyIDsize, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) ); REQUIRES( userNamePtr != NULL ); /* Get the password from the PKCS #10 request */ setMessageData( &msgData, requestPassword, CRYPT_MAX_TEXTSIZE ); status = krnlSendMessage( sessionInfoPtr->iCertRequest, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CERTINFO_CHALLENGEPASSWORD ); if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Couldn't get challenge password from PKCS #10 request" ) ); } requestPasswordSize = msgData.length; /* Since it's a cryptlib encoded user ID we need to decode it before we can look up a PKI user with it */ REQUIRES( userNamePtr->flags & ATTR_FLAG_ENCODEDVALUE ); status = decodePKIUserValue( keyID, 64, &keyIDsize, userNamePtr->value, userNamePtr->valueLength ); ENSURES( cryptStatusOK( status ) ); /* Get the user information for the request from the certificate store */ setMessageKeymgmtInfo( &getkeyInfo, CRYPT_IKEYID_KEYID, keyID, keyIDsize, NULL, 0, KEYMGMT_FLAG_NONE ); status = krnlSendMessage( sessionInfoPtr->cryptKeyset, IMESSAGE_KEY_GETKEY, &getkeyInfo, KEYMGMT_ITEM_PKIUSER ); if( cryptStatusError( status ) ) { char userID[ CRYPT_MAX_TEXTSIZE + 8 ]; int userIDlen; zeroise( requestPassword, CRYPT_MAX_TEXTSIZE ); userIDlen = min( userNamePtr->valueLength, CRYPT_MAX_TEXTSIZE ); memcpy( userID, userNamePtr->value, userIDlen ); retExtObj( status, ( status, SESSION_ERRINFO, sessionInfoPtr->cryptKeyset, "Couldn't find PKI user information for %s", sanitiseString( userID, CRYPT_MAX_TEXTSIZE, userIDlen ) ) ); } /* Get the password from the PKI user object */ setMessageData( &msgData, userPassword, CRYPT_MAX_TEXTSIZE ); status = krnlSendMessage( getkeyInfo.cryptHandle, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CERTINFO_PKIUSER_ISSUEPASSWORD ); if( cryptStatusOK( status ) ) { userPasswordSize = msgData.length; status = updateSessionInfo( &sessionInfoPtr->attributeList, CRYPT_SESSINFO_PASSWORD, userPassword, userPasswordSize, CRYPT_MAX_TEXTSIZE, ATTR_FLAG_ENCODEDVALUE ); } if( cryptStatusError( status ) ) { zeroise( requestPassword, CRYPT_MAX_TEXTSIZE ); zeroise( userPassword, CRYPT_MAX_TEXTSIZE ); krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT ); retExt( status, ( status, SESSION_ERRINFO, "Couldn't copy read PKI user data from PKI user object " "into session object" ) ); } /* Make sure that the password matches the one in the request */ if( userPasswordSize != requestPasswordSize || \ !compareDataConstTime( userPassword, requestPassword, userPasswordSize ) ) { zeroise( requestPassword, CRYPT_MAX_TEXTSIZE ); zeroise( userPassword, CRYPT_MAX_TEXTSIZE ); krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT ); retExt( status, ( status, SESSION_ERRINFO, "Password in PKCS #10 request doesn't match PKI user " "password" ) ); } zeroise( userPassword, CRYPT_MAX_TEXTSIZE ); /* If the subject only knows their CN, they may send a CN-only subject DN in the hope that we can fill it in for them. In addition there may be other constraints that the CA wants to apply, these are handled by applying the PKI user information to the request */ status = krnlSendMessage( sessionInfoPtr->iCertRequest, IMESSAGE_SETATTRIBUTE, &getkeyInfo.cryptHandle, CRYPT_IATTRIBUTE_PKIUSERINFO ); krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT ); if( cryptStatusError( status ) ) { retExt( CRYPT_ERROR_INVALID, ( CRYPT_ERROR_INVALID, SESSION_ERRINFO, "User information in PKCS #10 request can't be " "reconciled with stored information for the user" ) ); } return( CRYPT_OK ); }
static int checkScepRequest( INOUT SESSION_INFO *sessionInfoPtr, INOUT SCEP_PROTOCOL_INFO *protocolInfo, OUT BOOLEAN *requestDataAvailable ) { CRYPT_CERTIFICATE iCmsAttributes; MESSAGE_CREATEOBJECT_INFO createInfo; MESSAGE_DATA msgData; int dataLength, sigResult, value, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) ) ); assert( isWritePtr( requestDataAvailable, sizeof( BOOLEAN ) ) ); /* Clear return value */ *requestDataAvailable = FALSE; /* Phase 1: Sig-check the self-signed data */ DEBUG_DUMP_FILE( "scep_sreq2", sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufEnd ); status = envelopeSigCheck( sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufEnd, sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufSize, &dataLength, CRYPT_UNUSED, &sigResult, &protocolInfo->iScepCert, &iCmsAttributes ); if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Invalid CMS signed data in client request" ) ); } DEBUG_DUMP_FILE( "scep_sreq1", sessionInfoPtr->receiveBuffer, dataLength ); if( cryptStatusError( sigResult ) ) { /* The signed data was valid but the signature on it wasn't, this is a different style of error than the previous one */ krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT ); retExt( sigResult, ( sigResult, SESSION_ERRINFO, "Bad signature on client request data" ) ); } /* Make sure that the client certificate is valid for signing and decryption. In effect the signing capability has already been checked by the fact that the certificate signed the request but we do an explicit check here just to be thorough */ status = krnlSendMessage( protocolInfo->iScepCert, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_SIGCHECK ); if( cryptStatusOK( status ) ) status = krnlSendMessage( protocolInfo->iScepCert, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_ENCRYPT ); if( cryptStatusError( status ) ) { krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT ); retExt( CRYPT_ERROR_INVALID, ( CRYPT_ERROR_INVALID, SESSION_ERRINFO, "Ephemeral SCEP client certificate isn't valid for " "signing/encryption" ) ); } /* Get the nonce and transaction ID and save it for the reply */ setMessageData( &msgData, protocolInfo->nonce, CRYPT_MAX_HASHSIZE ); status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CERTINFO_SCEP_SENDERNONCE ); if( cryptStatusOK( status ) ) { protocolInfo->nonceSize = msgData.length; setMessageData( &msgData, protocolInfo->transID, CRYPT_MAX_HASHSIZE ); status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CERTINFO_SCEP_TRANSACTIONID ); } if( cryptStatusError( status ) ) { krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT ); retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Request is missing a nonce/transaction ID" ) ); } protocolInfo->transIDsize = msgData.length; /* We've now got enough request data available to construct a SCEP-level response to an error instead of an HTTP-level one, let the caller know. Note that this lets an attacker know that they've made it this far in the checking, but it's not obvious that this is a security problem, especially since they can get a good idea of how far they got from the different error conditions that will be returned */ *requestDataAvailable = TRUE; /* Since the request is for a cryptlib server it'll have a transaction ID that's a cryptlib-encoded PKI user ID. If we don't get this then we reject the request */ if( protocolInfo->transIDsize != 17 || \ !isPKIUserValue( protocolInfo->transID, protocolInfo->transIDsize ) ) { krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT ); retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Request has an invalid transaction ID '%s'", sanitiseString( protocolInfo->transID, protocolInfo->transIDsize, protocolInfo->transIDsize ) ) ); } status = updateSessionInfo( &sessionInfoPtr->attributeList, CRYPT_SESSINFO_USERNAME, protocolInfo->transID, protocolInfo->transIDsize, CRYPT_MAX_TEXTSIZE, ATTR_FLAG_ENCODEDVALUE ); if( cryptStatusError( status ) ) { krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT ); return( status ); } /* Check that we've been sent the correct type of message */ status = getScepStatusValue( iCmsAttributes, CRYPT_CERTINFO_SCEP_MESSAGETYPE, &value ); if( cryptStatusOK( status ) && value != MESSAGETYPE_PKCSREQ_VALUE ) status = CRYPT_ERROR_BADDATA; krnlSendNotifier( iCmsAttributes, IMESSAGE_DECREFCOUNT ); if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Incorrect SCEP message type %d", value ) ); } /* Phase 2: Decrypt the data using our CA key */ status = envelopeUnwrap( sessionInfoPtr->receiveBuffer, dataLength, sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufSize, &dataLength, sessionInfoPtr->privateKey ); if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Couldn't decrypt CMS enveloped data in client request" ) ); } /* Finally, import the request as a PKCS #10 request */ setMessageCreateObjectIndirectInfo( &createInfo, sessionInfoPtr->receiveBuffer, dataLength, CRYPT_CERTTYPE_CERTREQUEST ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT_INDIRECT, &createInfo, OBJECT_TYPE_CERTIFICATE ); if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Invalid PKCS #10 request in client request" ) ); } sessionInfoPtr->iCertRequest = createInfo.cryptHandle; return( CRYPT_OK ); }
static int readConfigOption( INOUT STREAM *stream, IN_HANDLE CRYPT_USER iCryptUser ) { CRYPT_ATTRIBUTE_TYPE attributeType; const BUILTIN_OPTION_INFO *builtinOptionInfoPtr; MESSAGE_DATA msgData; void *dataPtr DUMMY_INIT_PTR; long optionCode; int value, tag, length, status; /* Read the wrapper and option index and map it to the actual option. If we find an unknown index or one that shouldn't be writeable to persistent storage, we skip it and continue. This is done to handle new options that may have been added after this version of cryptlib was built (for unknown indices) and because the stored configuration options are an untrusted source so we have to check for attempts to feed in bogus values (for non-writeable options) */ readSequence( stream, NULL ); status = readShortInteger( stream, &optionCode ); if( cryptStatusError( status ) ) return( status ); if( optionCode < 0 || optionCode > LAST_OPTION_INDEX ) { /* Unknown option, ignore it */ return( readUniversal( stream ) ); } builtinOptionInfoPtr = getBuiltinOptionInfoByCode( optionCode ); if( builtinOptionInfoPtr == NULL || \ builtinOptionInfoPtr->index < 0 || \ builtinOptionInfoPtr->index > LAST_OPTION_INDEX || \ builtinOptionInfoPtr->index == CRYPT_UNUSED ) { /* Unknown option, ignore it */ return( readUniversal( stream ) ); } attributeType = builtinOptionInfoPtr->option; /* Read the option value and set the option. We don't treat a failure to set the option as a problem since the user probably doesn't want the entire system to fail because of a bad configuration option, and in any case we'll fall back to a safe default value */ status = tag = peekTag( stream ); if( cryptStatusError( status ) ) return( status ); if( tag == BER_BOOLEAN || tag == BER_INTEGER ) { /* It's a numeric value, read the appropriate type and try and set the option */ if( tag == BER_BOOLEAN ) status = readBoolean( stream, &value ); else { long integer; status = readShortInteger( stream, &integer ); if( cryptStatusOK( status ) ) value = ( int ) integer; } if( cryptStatusError( status ) ) return( status ); ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE, &value, attributeType ); return( CRYPT_OK ); } /* It's a string value, set the option straight from the encoded data */ status = readGenericHole( stream, &length, 1, BER_STRING_UTF8 ); if( cryptStatusOK( status ) ) status = sMemGetDataBlock( stream, &dataPtr, length ); if( cryptStatusOK( status ) ) status = sSkip( stream, length, SSKIP_MAX ); if( cryptStatusError( status ) ) return( status ); setMessageData( &msgData, dataPtr, length ); ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE_S, &msgData, attributeType ); return( CRYPT_OK ); }
static int processAdditionalScepRequest( INOUT SESSION_INFO *sessionInfoPtr, const HTTP_URI_INFO *httpReqInfo ) { HTTP_URI_INFO rewrittenHttpReqInfo; MESSAGE_DATA msgData; int operationType, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isReadPtr( httpReqInfo, sizeof( HTTP_URI_INFO ) ) ); /* If the client has fed us an HTTP GET request, find out what they want. SCEP's handling of HTTP requests is a bit different from the "attribute '=' value" lookup that's normally used for HTTP data retrieval. Instead, it uses the format "'operation =' value '&' extraData", with the search key buried in the 'extraData' value. In addition the content of the 'extraData' value isn't defined outside of "any string which is understood by the CA". However since 'value' defines what we want, we can determine what to return based on this and ignore the 'extraData' portion. In order to fix up the query information into a format that works with standard HTTP queries, we rewrite the query data from the "'operation =' value '&' extraData" form into "attribute '=' value" before we process the query */ memset( &rewrittenHttpReqInfo, 0, sizeof( HTTP_URI_INFO ) ); memcpy( rewrittenHttpReqInfo.attribute, httpReqInfo->value, httpReqInfo->valueLen ); rewrittenHttpReqInfo.attributeLen = httpReqInfo->valueLen; if( httpReqInfo->extraDataLen > 0 ) { memcpy( rewrittenHttpReqInfo.value, httpReqInfo->extraData, httpReqInfo->extraDataLen ); rewrittenHttpReqInfo.valueLen = httpReqInfo->extraDataLen; } status = processCertQuery( sessionInfoPtr, &rewrittenHttpReqInfo, certstoreReadInfo, FAILSAFE_ARRAYSIZE( certstoreReadInfo, \ CERTSTORE_READ_INFO ), &operationType, NULL, 0, NULL ); if( cryptStatusError( status ) ) { sendCertErrorResponse( sessionInfoPtr, status ); return( status ); } ENSURES( operationType == SCEP_OPERATION_GETCACAPS || \ operationType == SCEP_OPERATION_GETCACERT || \ operationType == SCEP_OPERATION_GETCACERTCHAIN ); /* If it's a CA capabilities query, return the information as raw text over HTTP */ if( operationType == SCEP_OPERATION_GETCACAPS ) { STREAM stream; sMemOpen( &stream, sessionInfoPtr->receiveBuffer, 1024 ); swrite( &stream, "POSTPKIOperation\n", 17 ); #if 0 /* 14/6/14 Too risky to implement given its current state in the spec, see the discussion on the JSCEP mailing list for details */ status = swrite( &stream, "Renewal\n", 8 ); #endif /* 0 */ if( algoAvailable( CRYPT_ALGO_SHA1 ) ) status = swrite( &stream, "SHA-1\n", 6 ); if( algoAvailable( CRYPT_ALGO_SHA2 ) ) status = swrite( &stream, "SHA-256\n", 8 ); if( algoAvailable( CRYPT_ALGO_SHAng ) ) status = swrite( &stream, "SHAng\n", 6 ); if( algoAvailable( CRYPT_ALGO_3DES ) ) status = swrite( &stream, "DES3\n", 5 ); if( algoAvailable( CRYPT_ALGO_AES ) ) status = swrite( &stream, "AES\n", 4 ); if( cryptStatusOK( status ) ) sessionInfoPtr->receiveBufEnd = stell( &stream ); sMemDisconnect( &stream ); ENSURES( cryptStatusOK( status ) ); return( writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE, SCEP_CONTENT_TYPE_LEN ) ); }
int deleteCertComponent( INOUT CERT_INFO *certInfoPtr, IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE certInfoType ) { int status; assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) ); REQUIRES( isAttribute( certInfoType ) || \ isInternalAttribute( certInfoType ) ); /* If it's a GeneralName or DN component, delete it. These are special-case attribute values so they have to come before the general attribute-handling code */ if( isGeneralNameSelectionComponent( certInfoType ) ) { /* Check whether this GeneralName is present */ status = selectGeneralName( certInfoPtr, certInfoType, MUST_BE_PRESENT ); if( cryptStatusError( status ) ) return( status ); /* Delete each field in the GeneralName */ if( deleteCompositeAttributeField( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, certInfoPtr->attributeCursor, certInfoPtr->currentSelection.dnPtr ) == OK_SPECIAL ) { /* We've deleted the attribute containing the currently selected DN, deselect it */ certInfoPtr->currentSelection.dnPtr = NULL; } return( CRYPT_OK ); } if( isGeneralNameComponent( certInfoType ) ) { SELECTION_STATE selectionState; ATTRIBUTE_PTR *attributePtr = DUMMY_INIT_PTR; /* Find the requested GeneralName component. Since selectGeneralNameComponent() changes the current selection within the GeneralName, we save the selection state around the call */ saveSelectionState( selectionState, certInfoPtr ); status = selectGeneralNameComponent( certInfoPtr, certInfoType ); if( cryptStatusOK( status ) ) attributePtr = certInfoPtr->attributeCursor; restoreSelectionState( selectionState, certInfoPtr ); if( cryptStatusError( status )) return( status ); ENSURES( attributePtr != NULL ); /* Delete the field within the GeneralName */ if( deleteAttributeField( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, attributePtr, certInfoPtr->currentSelection.dnPtr ) == OK_SPECIAL ) { /* We've deleted the attribute containing the currently selected DN, deselect it */ certInfoPtr->currentSelection.dnPtr = NULL; } return( CRYPT_OK ); } if( isDNComponent( certInfoType ) ) { status = selectDN( certInfoPtr, CRYPT_ATTRIBUTE_NONE, MUST_BE_PRESENT ); if( cryptStatusOK( status ) ) status = deleteDNComponent( certInfoPtr->currentSelection.dnPtr, certInfoType, NULL, 0 ); return( status ); } /* If it's standard certificate or CMS attribute, delete it */ if( ( certInfoType >= CRYPT_CERTINFO_FIRST_EXTENSION && \ certInfoType <= CRYPT_CERTINFO_LAST_EXTENSION ) || \ ( certInfoType >= CRYPT_CERTINFO_FIRST_CMS && \ certInfoType <= CRYPT_CERTINFO_LAST_CMS ) ) return( deleteCertAttribute( certInfoPtr, certInfoType ) ); /* If it's anything else, handle it specially */ switch( certInfoType ) { case CRYPT_CERTINFO_SELFSIGNED: if( !( certInfoPtr->flags & CERT_FLAG_SELFSIGNED ) ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->flags &= ~CERT_FLAG_SELFSIGNED; return( CRYPT_OK ); case CRYPT_CERTINFO_CURRENT_CERTIFICATE: case CRYPT_ATTRIBUTE_CURRENT_GROUP: case CRYPT_ATTRIBUTE_CURRENT: case CRYPT_ATTRIBUTE_CURRENT_INSTANCE: if( certInfoPtr->attributeCursor == NULL ) return( CRYPT_ERROR_NOTFOUND ); if( certInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP ) { status = deleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, certInfoPtr->attributeCursor, certInfoPtr->currentSelection.dnPtr ); } else { /* The current component and field are essentially the same thing since a component is one of a set of entries in a multivalued field, thus they're handled identically */ status = deleteAttributeField( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, certInfoPtr->attributeCursor, certInfoPtr->currentSelection.dnPtr ); } if( status == OK_SPECIAL ) { /* We've deleted the attribute containing the currently selected DN, deselect it */ certInfoPtr->currentSelection.dnPtr = NULL; } return( CRYPT_OK ); case CRYPT_CERTINFO_TRUSTED_USAGE: if( certInfoPtr->cCertCert->trustedUsage == CRYPT_ERROR ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->cCertCert->trustedUsage = CRYPT_ERROR; return( CRYPT_OK ); case CRYPT_CERTINFO_TRUSTED_IMPLICIT: return( krnlSendMessage( certInfoPtr->ownerHandle, IMESSAGE_USER_TRUSTMGMT, &certInfoPtr->objectHandle, MESSAGE_TRUSTMGMT_DELETE ) ); case CRYPT_CERTINFO_VALIDFROM: case CRYPT_CERTINFO_THISUPDATE: if( certInfoPtr->startTime <= 0 ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->startTime = 0; return( CRYPT_OK ); case CRYPT_CERTINFO_VALIDTO: case CRYPT_CERTINFO_NEXTUPDATE: if( certInfoPtr->endTime <= 0 ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->endTime = 0; return( CRYPT_OK ); case CRYPT_CERTINFO_SUBJECTNAME: if( certInfoPtr->currentSelection.dnPtr == &certInfoPtr->subjectName ) { /* This is the currently selected DN, deselect it before deleting it */ certInfoPtr->currentSelection.dnPtr = NULL; } deleteDN( &certInfoPtr->subjectName ); return( CRYPT_OK ); #ifdef USE_CERTREV case CRYPT_CERTINFO_REVOCATIONDATE: { time_t *revocationTimePtr = ( time_t * ) \ getRevocationTimePtr( certInfoPtr ); if( revocationTimePtr == NULL ) return( CRYPT_ERROR_NOTFOUND ); *revocationTimePtr = 0; return( CRYPT_OK ); } #endif /* USE_CERTREV */ #ifdef USE_PKIUSER case CRYPT_CERTINFO_PKIUSER_RA: if( !certInfoPtr->cCertUser->isRA ) return( CRYPT_ERROR_NOTFOUND ); certInfoPtr->cCertUser->isRA = FALSE; return( CRYPT_OK ); #endif /* USE_PKIUSER */ } retIntError(); }
int connectViaSocksProxy( INOUT STREAM *stream ) { MESSAGE_DATA msgData; BYTE socksBuffer[ 64 + CRYPT_MAX_TEXTSIZE + 8 ], *bufPtr = socksBuffer; char userName[ CRYPT_MAX_TEXTSIZE + 8 ]; int length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); REQUIRES_S( stream->type == STREAM_TYPE_NETWORK ); /* Get the SOCKS user name, defaulting to "cryptlib" if there's none set */ setMessageData( &msgData, userName, CRYPT_MAX_TEXTSIZE ); status = krnlSendMessage( DEFAULTUSER_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_OPTION_NET_SOCKS_USERNAME ); if( cryptStatusOK( status ) ) userName[ msgData.length ] = '\0'; else strlcpy_s( userName, CRYPT_MAX_TEXTSIZE, "cryptlib" ); /* Build up the SOCKSv4 request string: BYTE: version = 4 BYTE: command = 1 (connect) WORD: port LONG: IP address STRING: userName + '\0' Note that this has a potential problem in that it requires a DNS lookup by the client, which can lead to problems if the client can't get DNS requests out because only SOCKSified access is allowed. A related problem occurs when SOCKS is being used as a tunnelling interface because the DNS lookup will communicate data about the client to an observer outside the tunnel. To work around this there's a so-called SOCKSv4a protocol that has the SOCKS proxy perform the lookup: BYTE: version = 4 BYTE: command = 1 (connect) WORD: port LONG: IP address = 0x00 0x00 0x00 0xFF STRING: userName + '\0' STRING: FQDN + '\0' Unfortunately there's no way to tell whether a SOCKS server supports 4a or only 4, but in any case since SOCKS support is currently disabled we leave the poke-and-hope 4a detection until such time as someone actually requests it */ *bufPtr++ = 4; *bufPtr++ = 1; mputWord( bufPtr, netStream->port ); status = getIPAddress( stream, bufPtr, netStream->host ); strlcpy_s( bufPtr + 4, CRYPT_MAX_TEXTSIZE, userName ); length = 1 + 1 + 2 + 4 + strlen( userName ) + 1; if( cryptStatusError( status ) ) { netStream->transportDisconnectFunction( stream, TRUE ); return( status ); } /* Send the data to the server and read back the reply */ status = netStream->transportWriteFunction( stream, socksBuffer, length, TRANSPORT_FLAG_FLUSH ); if( cryptStatusOK( status ) ) status = netStream->transportReadFunction( stream, socksBuffer, 8, TRANSPORT_FLAG_BLOCKING ); if( cryptStatusError( status ) ) { /* The involvement of a proxy complicates matters somewhat because we can usually connect to the proxy OK but may run into problems going from the proxy to the remote server, so if we get an error at this stage (which will typically show up as a read error from the proxy) we report it as an open error instead */ if( status == CRYPT_ERROR_READ || status == CRYPT_ERROR_COMPLETE ) status = CRYPT_ERROR_OPEN; netStream->transportDisconnectFunction( stream, TRUE ); return( status ); } /* Make sure that everything is OK: BYTE: null = 0 BYTE: status = 90 (OK) WORD: port LONG: IP address */ if( socksBuffer[ 1 ] != 90 ) { int i; netStream->transportDisconnectFunction( stream, TRUE ); strlcpy_s( netStream->errorInfo->errorString, MAX_ERRMSG_SIZE, "Socks proxy returned" ); for( i = 0; i < 8; i++ ) { sprintf_s( netStream->errorInfo->errorString + 20 + ( i * 3 ), MAX_ERRMSG_SIZE - ( 20 + ( i * 3 ) ), " %02X", socksBuffer[ i ] ); } strlcat_s( netStream->errorInfo->errorString, MAX_ERRMSG_SIZE, "." ); netStream->errorCode = socksBuffer[ 1 ]; return( CRYPT_ERROR_OPEN ); } return( CRYPT_OK ); }
static int readCertAttributes( INOUT STREAM *stream, INOUT PKCS15_INFO *pkcs15infoPtr, IN_LENGTH const int endPos ) { int length, status = CRYPT_OK; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) ); REQUIRES( endPos > 0 && endPos > stell( stream ) && \ endPos < MAX_INTLENGTH ); if( peekTag( stream ) == BER_BOOLEAN ) /* Authority flag */ status = readUniversal( stream ); if( canContinue( stream, status, endPos ) && /* Identifier */ peekTag( stream ) == BER_SEQUENCE ) status = readUniversal( stream ); if( canContinue( stream, status, endPos ) && /* Thumbprint */ peekTag( stream ) == MAKE_CTAG( CTAG_CA_DUMMY ) ) status = readUniversal( stream ); if( canContinue( stream, status, endPos ) && /* Trusted usage */ peekTag( stream ) == MAKE_CTAG( CTAG_CA_TRUSTED_USAGE ) ) { readConstructed( stream, NULL, CTAG_CA_TRUSTED_USAGE ); status = readBitString( stream, &pkcs15infoPtr->trustedUsage ); } if( canContinue( stream, status, endPos ) && /* Identifiers */ peekTag( stream ) == MAKE_CTAG( CTAG_CA_IDENTIFIERS ) ) { status = readConstructed( stream, &length, CTAG_CA_IDENTIFIERS ); if( cryptStatusOK( status ) ) status = readKeyIdentifiers( stream, pkcs15infoPtr, stell( stream ) + length ); } if( canContinue( stream, status, endPos ) && /* Implicitly trusted */ peekTag( stream ) == MAKE_CTAG_PRIMITIVE( CTAG_CA_TRUSTED_IMPLICIT ) ) status = readBooleanTag( stream, &pkcs15infoPtr->implicitTrust, CTAG_CA_TRUSTED_IMPLICIT ); if( canContinue( stream, status, endPos ) && /* Validity */ peekTag( stream ) == MAKE_CTAG( CTAG_CA_VALIDTO ) ) { /* Due to miscommunication between PKCS #15 and 7816-15 there are two ways to encode the validity information for certificates, one based on the format used elsewhere in PKCS #15 (for PKCS #15) and the other based on the format used in certificates (for 7816-15). Luckily they can be distinguished by the tagging type */ readConstructed( stream, NULL, CTAG_CA_VALIDTO ); readUTCTime( stream, &pkcs15infoPtr->validFrom ); status = readUTCTime( stream, &pkcs15infoPtr->validTo ); } else { if( canContinue( stream, status, endPos ) && /* Start date */ peekTag( stream ) == BER_TIME_GENERALIZED ) status = readGeneralizedTime( stream, &pkcs15infoPtr->validFrom ); if( canContinue( stream, status, endPos ) && /* End date */ peekTag( stream ) == MAKE_CTAG_PRIMITIVE( CTAG_CA_VALIDTO ) ) status = readGeneralizedTimeTag( stream, &pkcs15infoPtr->validTo, CTAG_CA_VALIDTO ); } return( status ); }
static int readKeyIdentifiers( INOUT STREAM *stream, INOUT PKCS15_INFO *pkcs15infoPtr, IN_LENGTH const int endPos ) { int iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) ); REQUIRES( endPos > 0 && endPos > stell( stream ) && \ endPos < MAX_INTLENGTH ); for( status = CRYPT_OK, iterationCount = 0; cryptStatusOK( status ) && stell( stream ) < endPos && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { long value; int payloadLength; /* Read each identifier type and copy the useful ones into the PKCS #15 information */ readSequence( stream, &payloadLength ); status = readShortInteger( stream, &value ); if( cryptStatusError( status ) ) return( status ); switch( value ) { case PKCS15_KEYID_ISSUERANDSERIALNUMBER: { HASHFUNCTION_ATOMIC hashFunctionAtomic; void *iAndSPtr = DUMMY_INIT_PTR; int iAndSLength, hashSize; /* If we've already got the iAndSID, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } /* Hash the full issuerAndSerialNumber to get an iAndSID */ getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, &hashSize ); status = getStreamObjectLength( stream, &iAndSLength ); if( cryptStatusOK( status ) ) status = sMemGetDataBlock( stream, &iAndSPtr, iAndSLength ); if( cryptStatusOK( status ) ) status = sSkip( stream, iAndSLength ); if( cryptStatusError( status ) ) return( status ); hashFunctionAtomic( pkcs15infoPtr->iAndSID, KEYID_SIZE, iAndSPtr, iAndSLength ); pkcs15infoPtr->iAndSIDlength = hashSize; break; } case PKCS15_KEYID_SUBJECTKEYIDENTIFIER: status = readOctetString( stream, pkcs15infoPtr->keyID, &pkcs15infoPtr->keyIDlength, 8, CRYPT_MAX_HASHSIZE ); break; case PKCS15_KEYID_ISSUERANDSERIALNUMBERHASH: /* If we've already got the iAndSID by hashing the issuerAndSerialNumber, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } status = readOctetString( stream, pkcs15infoPtr->iAndSID, &pkcs15infoPtr->iAndSIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_ISSUERNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->issuerNameID, &pkcs15infoPtr->issuerNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_SUBJECTNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->subjectNameID, &pkcs15infoPtr->subjectNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_PGP2: status = readOctetString( stream, pkcs15infoPtr->pgp2KeyID, &pkcs15infoPtr->pgp2KeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; case PKCS15_KEYID_OPENPGP: status = readOctetString( stream, pkcs15infoPtr->openPGPKeyID, &pkcs15infoPtr->openPGPKeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; default: status = readUniversal( stream ); } } if( iterationCount >= FAILSAFE_ITERATIONS_MED ) { /* This could be either an internal error or some seriously malformed data, since we can't tell without human intervention we throw a debug exception but otherwise treat it as bad data */ DEBUG_DIAG(( "Encountered more than %d key IDs", FAILSAFE_ITERATIONS_MED )); assert( DEBUG_WARN ); return( CRYPT_ERROR_BADDATA ); } return( status ); }
static int readRsaPrivateKeyOld( INOUT STREAM *stream, INOUT CONTEXT_INFO *contextInfoPtr ) { CRYPT_ALGO_TYPE cryptAlgo; PKC_INFO *rsaKey = contextInfoPtr->ctxPKC; const int startPos = stell( stream ); int length, endPos, iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); REQUIRES( contextInfoPtr->type == CONTEXT_PKC && \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA ); /* Skip the PKCS #8 wrapper. When we read the OCTET STRING encapsulation we use MIN_PKCSIZE_THRESHOLD rather than MIN_PKCSIZE so that a too-short key will get to readBignumChecked(), which returns an appropriate error code */ readSequence( stream, &length ); /* Outer wrapper */ readShortInteger( stream, NULL ); /* Version */ status = readAlgoID( stream, &cryptAlgo, ALGOID_CLASS_PKC ); if( cryptStatusError( status ) || cryptAlgo != CRYPT_ALGO_RSA ) return( CRYPT_ERROR_BADDATA ); status = readOctetStringHole( stream, NULL, ( 2 * MIN_PKCSIZE_THRESHOLD ) + \ ( 5 * ( MIN_PKCSIZE_THRESHOLD / 2 ) ), DEFAULT_TAG ); if( cryptStatusError( status ) ) /* OCTET STRING encapsulation */ return( status ); /* Read the header */ readSequence( stream, NULL ); status = readShortInteger( stream, NULL ); if( cryptStatusError( status ) ) return( status ); /* Read the RSA key components, skipping n and e if we've already got them via the associated public key/certificate */ if( BN_is_zero( &rsaKey->rsaParam_n ) ) { status = readBignumChecked( stream, &rsaKey->rsaParam_n, RSAPARAM_MIN_N, RSAPARAM_MAX_N, NULL ); if( cryptStatusOK( status ) ) status = readBignum( stream, &rsaKey->rsaParam_e, RSAPARAM_MIN_E, RSAPARAM_MAX_E, &rsaKey->rsaParam_n ); } else { readUniversal( stream ); status = readUniversal( stream ); } if( cryptStatusOK( status ) ) { /* d isn't used so we skip it */ status = readUniversal( stream ); } if( cryptStatusOK( status ) ) status = readBignum( stream, &rsaKey->rsaParam_p, RSAPARAM_MIN_P, RSAPARAM_MAX_P, &rsaKey->rsaParam_n ); if( cryptStatusOK( status ) ) status = readBignum( stream, &rsaKey->rsaParam_q, RSAPARAM_MIN_Q, RSAPARAM_MAX_Q, &rsaKey->rsaParam_n ); if( cryptStatusOK( status ) ) status = readBignum( stream, &rsaKey->rsaParam_exponent1, RSAPARAM_MIN_EXP1, RSAPARAM_MAX_EXP1, &rsaKey->rsaParam_n ); if( cryptStatusOK( status ) ) status = readBignum( stream, &rsaKey->rsaParam_exponent2, RSAPARAM_MIN_EXP2, RSAPARAM_MAX_EXP2, &rsaKey->rsaParam_n ); if( cryptStatusOK( status ) ) status = readBignum( stream, &rsaKey->rsaParam_u, RSAPARAM_MIN_U, RSAPARAM_MAX_U, &rsaKey->rsaParam_n ); if( cryptStatusError( status ) ) return( status ); /* Check whether there are any attributes present */ if( stell( stream ) >= startPos + length ) return( CRYPT_OK ); /* Read the attribute wrapper */ status = readConstructed( stream, &length, 0 ); if( cryptStatusError( status ) ) return( status ); endPos = stell( stream ) + length; /* Read the collection of attributes. Unlike any other key-storage format, PKCS #8 stores the key usage information as an X.509 attribute alongside the encrypted private key data so we have to process whatever attributes may be present in order to find the keyUsage (if there is any) in order to set the object action permissions */ for( iterationCount = 0; stell( stream ) < endPos && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { BYTE oid[ MAX_OID_SIZE + 8 ]; int oidLength, actionFlags, value; /* Read the attribute. Since there's only one attribute type that we can use, we hardcode the read in here rather than performing a general-purpose attribute read */ readSequence( stream, NULL ); status = readEncodedOID( stream, oid, MAX_OID_SIZE, &oidLength, BER_OBJECT_IDENTIFIER ); if( cryptStatusError( status ) ) return( status ); /* If it's not a key-usage attribute, we can't do much with it */ if( oidLength != sizeofOID( OID_X509_KEYUSAGE ) || \ memcmp( oid, OID_X509_KEYUSAGE, oidLength ) ) { status = readUniversal( stream ); if( cryptStatusError( status ) ) return( status ); continue; } /* Read the keyUsage attribute and convert it into cryptlib action permissions */ readSet( stream, NULL ); status = readBitString( stream, &value ); if( cryptStatusError( status ) ) return( status ); actionFlags = ACTION_PERM_NONE; if( value & ( KEYUSAGE_SIGN | KEYUSAGE_CA ) ) { actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_SIGN, \ ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, \ ACTION_PERM_ALL ); } if( value & KEYUSAGE_CRYPT ) { actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, \ ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, \ ACTION_PERM_ALL ); } #if 0 /* 11/6/13 Windows sets these flags to what are effectively gibberish values (dataEncipherment for a signing key, digitalSignature for an encryption key) so in order to be able to use the key we have to ignore the keyUsage settings, in the same way that every other application seems to */ if( actionFlags == ACTION_PERM_NONE ) return( CRYPT_ERROR_NOTAVAIL ); status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_SETATTRIBUTE, &actionFlags, CRYPT_IATTRIBUTE_ACTIONPERMS ); if( cryptStatusError( status ) ) return( status ); #else assert( actionFlags != ACTION_PERM_NONE ); /* Warn in debug mode */ #endif /* 0 */ } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); return( CRYPT_OK ); }
static int readRsaPrivateKey( INOUT STREAM *stream, INOUT CONTEXT_INFO *contextInfoPtr ) { PKC_INFO *rsaKey = contextInfoPtr->ctxPKC; int status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); REQUIRES( contextInfoPtr->type == CONTEXT_PKC && \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA ); /* Read the header */ status = readSequence( stream, NULL ); if( cryptStatusOK( status ) && \ peekTag( stream ) == MAKE_CTAG( 0 ) ) { /* Erroneously written in older code */ status = readConstructed( stream, NULL, 0 ); } if( cryptStatusError( status ) ) return( status ); /* Read the key components */ if( peekTag( stream ) == MAKE_CTAG_PRIMITIVE( 0 ) ) { /* The public components may already have been read when we read a corresponding public key or certificate so we only read them if they're not already present */ if( BN_is_zero( &rsaKey->rsaParam_n ) && \ BN_is_zero( &rsaKey->rsaParam_e ) ) { status = readBignumTag( stream, &rsaKey->rsaParam_n, RSAPARAM_MIN_N, RSAPARAM_MAX_N, NULL, 0 ); if( cryptStatusOK( status ) ) { status = readBignumTag( stream, &rsaKey->rsaParam_e, RSAPARAM_MIN_E, RSAPARAM_MAX_E, &rsaKey->rsaParam_n, 1 ); } } else { /* The key components are already present, skip them */ REQUIRES( !BN_is_zero( &rsaKey->rsaParam_n ) && \ !BN_is_zero( &rsaKey->rsaParam_e ) ); readUniversal( stream ); status = readUniversal( stream ); } if( cryptStatusError( status ) ) return( status ); } if( peekTag( stream ) == MAKE_CTAG_PRIMITIVE( 2 ) ) { /* d isn't used so we skip it */ status = readUniversal( stream ); if( cryptStatusError( status ) ) return( status ); } status = readBignumTag( stream, &rsaKey->rsaParam_p, RSAPARAM_MIN_P, RSAPARAM_MAX_P, &rsaKey->rsaParam_n, 3 ); if( cryptStatusOK( status ) ) status = readBignumTag( stream, &rsaKey->rsaParam_q, RSAPARAM_MIN_Q, RSAPARAM_MAX_Q, &rsaKey->rsaParam_n, 4 ); if( cryptStatusError( status ) ) return( status ); if( peekTag( stream ) == MAKE_CTAG_PRIMITIVE( 5 ) ) { status = readBignumTag( stream, &rsaKey->rsaParam_exponent1, RSAPARAM_MIN_EXP1, RSAPARAM_MAX_EXP1, &rsaKey->rsaParam_n, 5 ); if( cryptStatusOK( status ) ) status = readBignumTag( stream, &rsaKey->rsaParam_exponent2, RSAPARAM_MIN_EXP2, RSAPARAM_MAX_EXP2, &rsaKey->rsaParam_n, 6 ); if( cryptStatusOK( status ) ) status = readBignumTag( stream, &rsaKey->rsaParam_u, RSAPARAM_MIN_U, RSAPARAM_MAX_U, &rsaKey->rsaParam_n, 7 ); } return( status ); }
static int fuzzSession( const CRYPT_SESSION_TYPE sessionType ) { CRYPT_SESSION cryptSession; const BOOLEAN isServer = \ ( sessionType == CRYPT_SESSION_SSH_SERVER || \ sessionType == CRYPT_SESSION_SSL_SERVER || \ sessionType == CRYPT_SESSION_OCSP_SERVER || \ sessionType == CRYPT_SESSION_TSP_SERVER || \ sessionType == CRYPT_SESSION_CMP_SERVER || \ sessionType == CRYPT_SESSION_SCEP_SERVER ) ? \ TRUE : FALSE; int status; /* Create the session */ status = cryptCreateSession( &cryptSession, CRYPT_UNUSED, sessionType ); if( cryptStatusError( status ) ) return( status ); /* Set up the various attributes needed to establish a minimal session */ if( !isServer ) { status = cryptSetAttributeString( cryptSession, CRYPT_SESSINFO_SERVER_NAME, "www.example.com", 15 ); if( cryptStatusError( status ) ) return( status ); } if( isServer ) { CRYPT_CONTEXT cryptPrivKey; char filenameBuffer[ FILENAME_BUFFER_SIZE ]; filenameFromTemplate( filenameBuffer, SERVER_PRIVKEY_FILE_TEMPLATE, 1 ); status = getPrivateKey( &cryptPrivKey, filenameBuffer, USER_PRIVKEY_LABEL, TEST_PRIVKEY_PASSWORD ); if( cryptStatusOK( status ) ) { status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_PRIVATEKEY, cryptPrivKey ); cryptDestroyContext( cryptPrivKey ); } if( cryptStatusError( status ) ) return( status ); } if( sessionType == CRYPT_SESSION_SSH || \ sessionType == CRYPT_SESSION_SSH_SERVER ) { status = cryptSetAttributeString( cryptSession, CRYPT_SESSINFO_USERNAME, SSH_USER_NAME, paramStrlen( SSH_USER_NAME ) ); if( cryptStatusOK( status ) ) { status = cryptSetAttributeString( cryptSession, CRYPT_SESSINFO_PASSWORD, SSH_PASSWORD, paramStrlen( SSH_PASSWORD ) ); } if( cryptStatusError( status ) ) return( status ); } status = cryptSetFuzzData( cryptSession, NULL, 0 ); cryptDestroySession( cryptSession ); return( CRYPT_OK ); }
static int readRDNcomponent( INOUT STREAM *stream, INOUT DN_COMPONENT **dnComponentListPtrPtr, IN_LENGTH_SHORT const int rdnDataLeft ) { CRYPT_ERRTYPE_TYPE dummy; BYTE stringBuffer[ MAX_ATTRIBUTE_SIZE + 8 ]; void *value; const int rdnStart = stell( stream ); int type, valueLength, valueStringType, stringTag; int flags = DN_FLAG_NOCHECK, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) ); REQUIRES( rdnDataLeft > 0 && rdnDataLeft < MAX_INTLENGTH_SHORT ); REQUIRES( rdnStart > 0 && rdnStart < MAX_INTLENGTH_SHORT ); /* Read the type information for this AVA */ status = readAVA( stream, &type, &valueLength, &stringTag ); if( cryptStatusError( status ) ) { /* If this is an unrecognised AVA, don't try and process it (the content will already have been skipped in readAVA()) */ if( status == OK_SPECIAL ) return( CRYPT_OK ); return( status ); } if( valueLength <= 0 ) { /* Skip broken AVAs with zero-length strings */ return( CRYPT_OK ); } status = sMemGetDataBlock( stream, &value, valueLength ); if( cryptStatusOK( status ) ) status = sSkip( stream, valueLength ); if( cryptStatusError( status ) ) return( status ); ANALYSER_HINT( value != NULL ); /* If there's room for another AVA, mark this one as being continued. The +10 value is the minimum length for an AVA: SEQUENCE { OID, value } (2-bytes SEQUENCE + 5 bytes OID + 2 bytes (tag + length) + 1 byte min- length data). We don't do a simple =/!= check to get around incorrectly encoded lengths */ if( rdnDataLeft >= ( stell( stream ) - rdnStart ) + 10 ) flags |= DN_FLAG_CONTINUED; /* Convert the string into the local character set */ status = copyFromAsn1String( stringBuffer, MAX_ATTRIBUTE_SIZE, &valueLength, &valueStringType, value, valueLength, stringTag ); if( cryptStatusError( status ) ) return( status ); /* Add the DN component to the DN. If we hit a non-memory related error we turn it into a generic CRYPT_ERROR_BADDATA error since the other codes are somewhat too specific for this case, something like CRYPT_ERROR_INITED or an arg error isn't too useful for the caller */ status = insertDNstring( dnComponentListPtrPtr, type, stringBuffer, valueLength, valueStringType, flags, &dummy ); return( ( cryptStatusError( status ) && status != CRYPT_ERROR_MEMORY ) ? \ CRYPT_ERROR_BADDATA : status ); }
static int exec_cmpcli_revoke(int argc, char **argv) { CRYPT_SESSION session; CRYPT_CONTEXT privkey; CRYPT_KEYSET privkeys; CRYPT_CERTIFICATE cert, cacert, revreq; const char *cmd, *crtfilename, *cacrtfilename, *kpfilename; void *crtdata; int status, data_len; if (argc != 4) { fprintf(stderr, "cmpcli revoke argv!=4\n"); return 1; } cmd = argv[0]; crtfilename=argv[1]; cacrtfilename=argv[2]; kpfilename = argv[3]; if (strcmp(cmd, "revoke") != 0) { fprintf(stderr, "cmpcli knows revoke only\n"); return 1; } crtdata = read_full_file(crtfilename, &data_len); if (!crtdata) return 1; status = cryptImportCert(crtdata, data_len, CRYPT_UNUSED, &cert); WARN_AND_RETURN_IF(status); free(crtdata); crtdata = read_full_file(cacrtfilename, &data_len); if (!crtdata) return 1; status = cryptImportCert(crtdata, data_len, CRYPT_UNUSED, &cacert); WARN_AND_RETURN_IF(status); free(crtdata); status = cryptKeysetOpen(&privkeys, CRYPT_UNUSED, CRYPT_KEYSET_FILE, kpfilename, CRYPT_KEYOPT_NONE); WARN_AND_RETURN_IF(status); status = cryptGetPrivateKey(privkeys, &privkey, CRYPT_KEYID_NAME, DEFAULT_PRIVKEY_LABEL, DEFAULT_PASSWORD); WARN_AND_RETURN_IF(status); status = cryptKeysetClose(privkeys); WARN_AND_RETURN_IF(status); status = cryptCreateCert(&revreq, CRYPT_UNUSED, CRYPT_CERTTYPE_REQUEST_REVOCATION); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(revreq, CRYPT_CERTINFO_CERTIFICATE, cert); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(revreq, CRYPT_CERTINFO_CRLREASON, CRYPT_CRLREASON_AFFILIATIONCHANGED); WARN_AND_RETURN_IF(status); #if 0 status = cryptSignCert(revreq, privkey); WARN_AND_RETURN_IF(status); #endif status = cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_CMP); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_CMP_REQUESTTYPE, CRYPT_REQUESTTYPE_REVOCATION); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, privkey); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_REQUEST, revreq); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_CACERTIFICATE, cacert); WARN_AND_RETURN_IF(status); #if 0 status = cryptSetAttributeString(session, CRYPT_SESSINFO_USERNAME, uid, strlen(uid)); WARN_AND_RETURN_IF(status); status = cryptSetAttributeString(session, CRYPT_SESSINFO_PASSWORD, rpwd, strlen(rpwd)); WARN_AND_RETURN_IF(status); #endif status = cryptSetAttributeString(session, CRYPT_SESSINFO_SERVER_NAME, "127.0.0.1", 9); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_SERVER_PORT, 65000); WARN_AND_RETURN_IF(status); status = cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1); if (!cryptStatusOK(status)) { CRYPT_ERRTYPE_TYPE errtype; CRYPT_ATTRIBUTE_TYPE locus; char *errstring; int errstringlen; cryptGetAttribute(session, CRYPT_ATTRIBUTE_ERRORTYPE, (int *)&errtype); cryptGetAttribute(session, CRYPT_ATTRIBUTE_ERRORLOCUS, (int *)&locus); fprintf(stderr, "session errtype %d locus %d\n", errtype, locus); cryptGetAttribute(revreq, CRYPT_ATTRIBUTE_ERRORTYPE, (int *)&errtype); cryptGetAttribute(revreq, CRYPT_ATTRIBUTE_ERRORLOCUS, (int *)&locus); fprintf(stderr, "revreq errtype %d locus %d\n", errtype, locus); cryptGetAttribute(cert, CRYPT_ATTRIBUTE_ERRORTYPE, (int *)&errtype); cryptGetAttribute(cert, CRYPT_ATTRIBUTE_ERRORLOCUS, (int *)&locus); fprintf(stderr, "cert errtype %d locus %d\n", errtype, locus); cryptGetAttribute(cacert, CRYPT_ATTRIBUTE_ERRORTYPE, (int *)&errtype); cryptGetAttribute(cacert, CRYPT_ATTRIBUTE_ERRORLOCUS, (int *)&locus); fprintf(stderr, "cacert errtype %d locus %d\n", errtype, locus); cryptGetAttributeString(session, CRYPT_ATTRIBUTE_ERRORMESSAGE, NULL, &errstringlen); errstring = malloc(errstringlen + 10); cryptGetAttributeString(session, CRYPT_ATTRIBUTE_ERRORMESSAGE, errstring, &errstringlen); errstring[errstringlen] = 0; fprintf(stderr, "session errmsg: %s\n", errstring); free(errstring); } WARN_AND_RETURN_IF(status); status = cryptDestroyContext(privkey); WARN_AND_RETURN_IF(status); status = cryptDestroySession(session); WARN_AND_RETURN_IF(status); status = cryptDestroyCert(cacert); WARN_AND_RETURN_IF(status); status = cryptDestroyCert(cert); WARN_AND_RETURN_IF(status); status = cryptDestroyCert(revreq); WARN_AND_RETURN_IF(status); return 0; }
int testReadCertLDAP( void ) { CRYPT_KEYSET cryptKeyset; static const char FAR_BSS *ldapErrorString = \ "LDAP directory read failed, probably because the standard being " "used by the\ndirectory server differs from the one used by the " "LDAP client software (pick\na standard, any standard). If you " "know how the directory being used is\nconfigured, you can try " "changing the CRYPT_OPTION_KEYS_LDAP_xxx settings to\nmatch those " "used by the server. Processing will continue without treating\n" "this as a fatal error.\n"; const C_STR ldapKeysetName = ldapUrlInfo[ LDAP_SERVER_NO ].url; char ldapAttribute1[ CRYPT_MAX_TEXTSIZE + 1 ]; char ldapAttribute2[ CRYPT_MAX_TEXTSIZE + 1 ]; char certName[ CRYPT_MAX_TEXTSIZE ], caCertName[ CRYPT_MAX_TEXTSIZE ]; char crlName[ CRYPT_MAX_TEXTSIZE ]; int length, status; /* LDAP directories come and go, check to see which one is currently around */ puts( "Testing LDAP directory availability..." ); status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_LDAP, ldapKeysetName, CRYPT_KEYOPT_READONLY ); if( status == CRYPT_ERROR_PARAM3 ) { /* LDAP keyset access not available */ return( CRYPT_ERROR_NOTAVAIL ); } if( status == CRYPT_ERROR_OPEN ) { printf( "%s not available for some odd reason,\n trying " "alternative directory instead...\n", ldapUrlInfo[ LDAP_SERVER_NO ].asciiURL ); ldapKeysetName = ldapUrlInfo[ LDAP_ALT_SERVER_NO ].url; status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_LDAP, ldapKeysetName, CRYPT_KEYOPT_READONLY ); } if( status == CRYPT_ERROR_OPEN ) { printf( "%s not available either.\n", ldapUrlInfo[ LDAP_ALT_SERVER_NO ].asciiURL ); puts( "None of the test LDAP directories are available. If you need " "to test the\nLDAP capabilities, you need to set up an LDAP " "directory that can be used\nfor the certificate store. You " "can configure the LDAP directory using the\nLDAP_KEYSET_xxx " "settings in test/test.h. Alternatively, if this message\n" "took a long time to appear you may be behind a firewall that " "blocks LDAP\ntraffic.\n" ); return( FALSE ); } if( cryptStatusError( status ) ) { printf( "cryptKeysetOpen() failed with error code %d, line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptGetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_OBJECTCLASS, ldapAttribute1, &length ); if( cryptStatusOK( status ) ) { #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ ldapAttribute1[ length ] = TEXT( '\0' ); status = cryptGetAttributeString( cryptKeyset, CRYPT_OPTION_KEYS_LDAP_OBJECTCLASS, ldapAttribute2, &length ); } if( cryptStatusOK( status ) ) { #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ ldapAttribute2[ length ] = TEXT( '\0' ); } if( cryptStatusError( status ) || \ strcmp( ldapAttribute1, ldapAttribute2 ) ) { printf( "Failed to get/set keyset attribute via equivalent global " "attribute, error\ncode %d, value '%s', should be\n'%s', " "line %d.\n", status, ldapAttribute2, ldapAttribute1, __LINE__ ); return( FALSE ); } cryptKeysetClose( cryptKeyset ); printf( " LDAP directory %s seems to be up, using that for read test.\n", ldapKeysetName ); /* Now it gets tricky, we have to jump through all sorts of hoops to run the tests in an automated manner since the innate incompatibility of LDAP directory setups means that even though cryptlib's LDAP access code retries failed queries with various options, we still need to manually override some settings here. The simplest option is a direct read with no special-case handling */ if( !paramStrcmp( ldapKeysetName, ldapUrlInfo[ LDAP_SERVER_NO ].url ) ) { puts( "Testing LDAP certificate read..." ); status = testKeysetRead( CRYPT_KEYSET_LDAP, ldapKeysetName, CRYPT_KEYID_NAME, ldapUrlInfo[ LDAP_SERVER_NO ].certName, CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_NORMAL ); if( !status ) { /* Since we can never be sure about the LDAP schema du jour, we don't treat a failure as a fatal error */ puts( ldapErrorString ); return( FALSE ); } /* This directory doesn't contain CRLs (or at least not at any known location) so we skip the CRL read test */ puts( "LDAP certificate read succeeded (CRL read skipped).\n" ); return( TRUE ); } /* The secondary LDAP directory that we're using for these tests doesn't recognise the ';binary' modifier which is required by LDAP servers in order to get them to work properly, we have to change the attribute name around the read calls to the format expected by the server. In addition because the magic formula for fetching a CRL doesn't seem to work for certificates, the CRL read is done first */ puts( "Testing LDAP CRL read..." ); status = cryptGetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CRLNAME, crlName, &length ); if( cryptStatusError( status ) ) return( FALSE ); #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ certName[ length ] = TEXT( '\0' ); cryptSetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CRLNAME, "certificateRevocationList", 25 ); status = testKeysetRead( CRYPT_KEYSET_LDAP, ldapKeysetName, CRYPT_KEYID_NAME, ldapUrlInfo[ LDAP_ALT_SERVER_NO ].crlName, CRYPT_CERTTYPE_CRL, READ_OPTION_NORMAL ); cryptSetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CRLNAME, crlName, strlen( crlName ) ); if( !status ) { /* Since we can never be sure about the LDAP schema du jour, we don't treat a failure as a fatal error */ puts( ldapErrorString ); return( FALSE ); } puts( "Testing LDAP certificate read..." ); status = cryptGetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CERTNAME, certName, &length ); if( cryptStatusError( status ) ) return( FALSE ); #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ certName[ length ] = TEXT( '\0' ); cryptSetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CERTNAME, "userCertificate", 15 ); status = cryptGetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CACERTNAME, caCertName, &length ); if( cryptStatusError( status ) ) return( FALSE ); #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ certName[ length ] = TEXT( '\0' ); cryptSetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CACERTNAME, "cACertificate", 13 ); status = testKeysetRead( CRYPT_KEYSET_LDAP, ldapKeysetName, CRYPT_KEYID_NAME, ldapUrlInfo[ LDAP_ALT_SERVER_NO ].certName, CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_NORMAL ); cryptSetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CERTNAME, certName, strlen( certName ) ); cryptSetAttributeString( CRYPT_UNUSED, CRYPT_OPTION_KEYS_LDAP_CACERTNAME, caCertName, strlen( caCertName ) ); if( !status ) { /* Since we can never be sure about the LDAP schema du jour, we don't treat a failure as a fatal error */ puts( "LDAP directory read failed, probably due to the magic " "incantatation to fetch\na certificate from this server not " "matching the one used to fetch a CRL.\nProcessing will " "continue without treating this as a fatal error.\n" ); return( FALSE ); } puts( "LDAP certificate/CRL read succeeded.\n" ); return( TRUE ); }
CHECK_RETVAL \ int deviceInitPKCS11( void ) { int tblIndex = 0, optionIndex, status; /* If we've previously tried to initialise the drivers, don't try it again */ if( pkcs11Initialised ) return( CRYPT_OK ); memset( pkcs11InfoTbl, 0, sizeof( pkcs11InfoTbl ) ); /* Try and link in each driver specified in the config options. Since this is a general systemwide config option, we always query the built- in default user object */ for( optionIndex = 0; optionIndex < MAX_PKCS11_DRIVERS; optionIndex++ ) { MESSAGE_DATA msgData; char deviceDriverName[ MAX_PATH_LENGTH + 1 + 8 ]; setMessageData( &msgData, deviceDriverName, MAX_PATH_LENGTH ); status = krnlSendMessage( DEFAULTUSER_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, optionIndex + CRYPT_OPTION_DEVICE_PKCS11_DVR01 ); if( cryptStatusError( status ) ) continue; deviceDriverName[ msgData.length ] = '\0'; status = loadPKCS11driver( &pkcs11InfoTbl[ tblIndex ], deviceDriverName ); if( cryptStatusOK( status ) ) { tblIndex++; pkcs11Initialised = TRUE; } } /* If it's a Unix system and there were no drivers explicitly specified, try with the default driver name "libpkcs11.so". Unlike Windows, where there are large numbers of PKCS #11 vendors and we have to use vendor-specific names, under Unix there are very few vendors and there's usually only one device/driver in use which inevitably co-opts /usr/lib for its own use, so we can always try for a standard name and location. As a backup measure we also try for the nCipher PKCS #11 driver, which is by far the most commonly used one on Unix systems (this may sometimes be found as /usr/lib/libcknfast.so). An unfortunate side-effect of this handling is that if there's more than one PKCS #11 driver present and the user forgets to explicitly specify it then this may load the wrong one, however the chances of there being multiple drivers present on a Unix system is close to zero so it's probably better to take the more user-friendly option of trying to load a default driver */ #ifdef __UNIX__ if( !pkcs11Initialised ) { status = loadPKCS11driver( &pkcs11InfoTbl[ tblIndex ], "libpkcs11.so" ); if( cryptStatusOK( status ) ) pkcs11Initialised = TRUE; } if( !pkcs11Initialised ) { status = loadPKCS11driver( &pkcs11InfoTbl[ tblIndex ], "/opt/nfast/toolkits/pkcs11/libcknfast.so" ); if( cryptStatusOK( status ) ) pkcs11Initialised = TRUE; } #endif /* __UNIX__ */ return( pkcs11Initialised ? CRYPT_OK : CRYPT_ERROR ); }
static int checkAddInfo( const PKCS15_INFO *pkcs15infoPtr, IN_HANDLE const CRYPT_HANDLE iCryptHandle, const BOOLEAN isCertChain, const BOOLEAN privkeyPresent, const BOOLEAN certPresent, const BOOLEAN pkcs15keyPresent, const BOOLEAN pkcs15certPresent, OUT BOOLEAN *isCertUpdate, INOUT ERROR_INFO *errorInfo ) { MESSAGE_DATA msgData; BOOLEAN unneededCert, unneededKey; int status; assert( isReadPtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) ); assert( isWritePtr( isCertUpdate, sizeof( BOOLEAN ) ) ); REQUIRES( isHandleRangeValid( iCryptHandle ) ); REQUIRES( errorInfo != NULL ); /* Clear return value */ *isCertUpdate = FALSE; /* Check what we can update (if anything) */ unneededKey = privkeyPresent & pkcs15keyPresent; unneededCert = certPresent & pkcs15certPresent; if( ( ( unneededCert && !privkeyPresent ) || \ ( unneededKey && unneededCert ) ) && \ pkcs15infoPtr->validTo > MIN_TIME_VALUE ) { time_t validTo; /* The certificate would be a duplicate, see if it's more recent than the existing one. We only perform this check if there's a validTo time stored for the certificate since without this restriction any certificate without a stored time could be overwritten */ setMessageData( &msgData, &validTo, sizeof( time_t ) ); status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CERTINFO_VALIDTO ); if( cryptStatusOK( status ) && validTo > pkcs15infoPtr->validTo ) { time_t validFrom; /* It's a newer certificate, don't treat it as a duplicate. This check is effectively impossible to perform automatically since there are an infinite number of variations that have to be taken into account, for example a certificate for the same key issued by a different CA, same CA but it's changed the bits it sets in the keyUsage (digitalSignature vs. nonRepudiation), slightly different issuer DN (Thawte certificates with a date encoded in the DN), and so on and so on. Because this really requires manual processing by a human we don't even try and sort it all out but just allow a certificate for a given key (checked by the ID match) to be replaced by a newer certificate for the same key. This is restrictive enough to prevent most obviously-wrong replacements while being permissive enough to allow most probably-OK replacements */ unneededCert = FALSE; *isCertUpdate = TRUE; /* There's one special-case situation in which odd things can happen when updating certificates and that's when adding a future-dated certificate, which would result in the certificate being replaced with one that can't be used yet. There's no clean way to handle this because in order to know what to do we'd have to be able to guess the intent of the user, however for anything but signature certificates it's likely that the hit-and-miss certificate checking performed by most software won't even notice a future-dated certificate, and for signature certificates the semantics of signing data now using a certificate that isn't valid yet are somewhat uncertain. Since in most cases no-one will even notice the problem, we throw an exception in the debug build but don't do anything in release builds. This is probably less annoying to users than having the code reject an otherwise-valid future-dated certificate. If anyone ever complains about this then we can ask the users at that time what sort of behaviour they're prefer */ setMessageData( &msgData, &validFrom, sizeof( time_t ) ); status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_CERTINFO_VALIDFROM ); if( cryptStatusOK( status ) && \ validFrom > getApproxTime() + 86400L ) { assert( !"Attempt to replace certificate with future-dated certificate" ); } } } /* Make sure that we can update at least one of the objects in the PKCS #15 personality */ if( ( unneededKey && !certPresent ) || /* Key only, duplicate */ ( unneededCert && !privkeyPresent ) || /* Certificate only, duplicate */ ( unneededKey && unneededCert ) ) /* Key+certificate, duplicate */ { /* If it's anything other than a certificate chain, we can't add anything */ if( !isCertChain ) { retExt( CRYPT_ERROR_DUPLICATE, ( CRYPT_ERROR_DUPLICATE, errorInfo, "No new data to add" ) ); } /* Tell the caller that it's an opportunistic certificate-chain update */ return( OK_SPECIAL ); } return( CRYPT_OK ); }
static CAPABILITY_INFO *getCapability( const DEVICE_INFO *deviceInfo, const PKCS11_MECHANISM_INFO *mechanismInfoPtr, const int maxMechanisms ) { VARIABLE_CAPABILITY_INFO *capabilityInfo; CK_MECHANISM_INFO pMechanism; CK_RV status; const CRYPT_ALGO_TYPE cryptAlgo = mechanismInfoPtr->cryptAlgo; const BOOLEAN isPKC = isPkcAlgo( cryptAlgo ) ? TRUE : FALSE; const CK_FLAGS keyGenFlag = isPKC ? CKF_GENERATE_KEY_PAIR : CKF_GENERATE; PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11; int hardwareOnly, i, iterationCount; assert( isReadPtr( deviceInfo, sizeof( DEVICE_INFO ) ) ); assert( isReadPtr( mechanismInfoPtr, \ maxMechanisms * sizeof( PKCS11_MECHANISM_INFO ) ) ); /* Set up canary values for the mechanism information in case the driver blindly reports success for every mechanism that we ask for */ memset( &pMechanism, 0, sizeof( CK_MECHANISM_INFO ) ); pMechanism.ulMinKeySize = 0xA5A5; pMechanism.ulMaxKeySize = 0x5A5A; /* Get the information for this mechanism. Since many PKCS #11 drivers implement some of their capabilities using God knows what sort of software implementation, we provide the option to skip emulated mechanisms if required */ status = C_GetMechanismInfo( pkcs11Info->slotID, mechanismInfoPtr->mechanism, &pMechanism ); if( status != CKR_OK ) return( NULL ); if( pMechanism.ulMinKeySize == 0xA5A5 && \ pMechanism.ulMaxKeySize == 0x5A5A ) { /* The driver reported that this mechanism is available but didn't update the mechanism information, it's lying */ DEBUG_DIAG(( "Driver reports that mechanism %X is available even " "though it isn't", mechanismInfoPtr->mechanism )); assert( DEBUG_WARN ); return( NULL ); } status = krnlSendMessage( deviceInfo->ownerHandle, IMESSAGE_GETATTRIBUTE, &hardwareOnly, CRYPT_OPTION_DEVICE_PKCS11_HARDWAREONLY ); if( cryptStatusOK( status ) && hardwareOnly && \ !( pMechanism.flags & CKF_HW ) ) { DEBUG_DIAG(( "Skipping mechanism %X, which is only available in " "software emulation", mechanismInfoPtr->mechanism )); return( NULL ); } if( mechanismInfoPtr->requiredFlags != CKF_NONE ) { /* Make sure that the driver flags indicate support for the specific functionality that we require */ if( ( mechanismInfoPtr->requiredFlags & \ pMechanism.flags ) != mechanismInfoPtr->requiredFlags ) { DEBUG_DIAG(( "Driver reports that mechanism %X only has " "capabilities %lX when we require %lX", mechanismInfoPtr->mechanism, mechanismInfoPtr->requiredFlags & pMechanism.flags, mechanismInfoPtr->requiredFlags )); ////////////////////////////////// // Kludge to allow it to be used ////////////////////////////////// // assert( DEBUG_WARN ); // return( NULL ); } } /* Copy across the template for this capability */ if( ( capabilityInfo = clAlloc( "getCapability", \ sizeof( CAPABILITY_INFO ) ) ) == NULL ) return( NULL ); for( i = 0; capabilityTemplates[ i ].cryptAlgo != cryptAlgo && \ capabilityTemplates[ i ].cryptAlgo != CRYPT_ERROR && \ i < FAILSAFE_ARRAYSIZE( capabilityTemplates, CAPABILITY_INFO ); i++ ); ENSURES_N( i < FAILSAFE_ARRAYSIZE( capabilityTemplates, CAPABILITY_INFO ) ); ENSURES_N( capabilityTemplates[ i ].cryptAlgo != CRYPT_ERROR ); memcpy( capabilityInfo, &capabilityTemplates[ i ], sizeof( CAPABILITY_INFO ) ); /* Set up the keysize information if there's anything useful available */ if( keysizeValid( cryptAlgo ) ) { int minKeySize = ( int ) pMechanism.ulMinKeySize; int maxKeySize = ( int ) pMechanism.ulMaxKeySize; /* Adjust the key size to bytes and make sure that all values are consistent. Some implementations report silly bounds (e.g. 1-bit RSA, "You naughty minKey" or alternatively 4Gbit RSA) so we adjust them to a sane value if necessary. We also limit the maximum key size to match the cryptlib native maximum key size, both for consistency and because cryptlib performs buffer allocation based on the maximum native buffer size */ if( pMechanism.ulMinKeySize < 0 || \ pMechanism.ulMinKeySize >= 10000L ) { DEBUG_DIAG(( "Driver reports invalid minimum key size %lu for " "%s algorithm", pMechanism.ulMinKeySize, capabilityInfo->algoName )); assert( DEBUG_WARN ); minKeySize = 0; } if( pMechanism.ulMaxKeySize < 0 || \ pMechanism.ulMaxKeySize >= 100000L ) { DEBUG_DIAG(( "Driver reports invalid maximum key size %lu for " "%s algorithm", pMechanism.ulMaxKeySize, capabilityInfo->algoName )); assert( DEBUG_WARN ); maxKeySize = 0; } if( !keysizeInBytes( cryptAlgo ) ) { minKeySize = bitsToBytes( minKeySize ); maxKeySize = bitsToBytes( maxKeySize ); } if( minKeySize > capabilityInfo->minKeySize ) capabilityInfo->minKeySize = minKeySize; if( capabilityInfo->keySize < capabilityInfo->minKeySize ) capabilityInfo->keySize = capabilityInfo->minKeySize; capabilityInfo->maxKeySize = min( maxKeySize, capabilityInfo->maxKeySize ); if( capabilityInfo->maxKeySize < capabilityInfo->minKeySize ) { /* Serious braindamage in the driver, we'll just have to make a sensible guess */ DEBUG_DIAG(( "Driver reports maximum key size %d < minimum key " "size %d for %s algorithm", capabilityInfo->maxKeySize, capabilityInfo->minKeySize, capabilityInfo->algoName )); assert( DEBUG_WARN ); if( isPKC ) { capabilityInfo->maxKeySize = \ max( capabilityInfo->minKeySize, bitsToBytes( 2048 ) ); } else capabilityInfo->maxKeySize = 16; } if( capabilityInfo->keySize > capabilityInfo->maxKeySize ) capabilityInfo->keySize = capabilityInfo->maxKeySize; capabilityInfo->endFunction = genericEndFunction; } /* Set up the device-specific handlers */ capabilityInfo->selfTestFunction = selfTestFunction; capabilityInfo->getInfoFunction = getDefaultInfo; if( !isPKC ) capabilityInfo->initParamsFunction = initGenericParams; capabilityInfo->endFunction = mechanismInfoPtr->endFunction; capabilityInfo->initKeyFunction = mechanismInfoPtr->initKeyFunction; if( pMechanism.flags & keyGenFlag ) capabilityInfo->generateKeyFunction = \ mechanismInfoPtr->generateKeyFunction; if( pMechanism.flags & CKF_SIGN ) { /* cryptlib treats hashing as an encrypt/decrypt operation while PKCS #11 treats it as a sign/verify operation, so we have to juggle the function pointers based on the underlying algorithm type */ if( isPKC ) capabilityInfo->signFunction = mechanismInfoPtr->signFunction; else capabilityInfo->encryptFunction = mechanismInfoPtr->encryptFunction; } if( pMechanism.flags & CKF_VERIFY ) { /* See comment above */ if( isPKC ) capabilityInfo->sigCheckFunction = mechanismInfoPtr->sigCheckFunction; else capabilityInfo->decryptFunction = mechanismInfoPtr->decryptFunction; } if( pMechanism.flags & CKF_ENCRYPT ) { /* Not all devices implement all modes, so we have to be careful to set up the pointer for the exact mode that's supported */ switch( mechanismInfoPtr->cryptMode ) { case CRYPT_MODE_CBC: capabilityInfo->encryptCBCFunction = mechanismInfoPtr->encryptFunction; break; case CRYPT_MODE_CFB: capabilityInfo->encryptCFBFunction = mechanismInfoPtr->encryptFunction; break; case CRYPT_MODE_GCM: capabilityInfo->encryptGCMFunction = mechanismInfoPtr->encryptFunction; break; default: /* ECB or a PKC */ capabilityInfo->encryptFunction = mechanismInfoPtr->encryptFunction; break; } } if( pMechanism.flags & CKF_DECRYPT ) { /* Not all devices implement all modes, so we have to be careful to set up the pointer for the exact mode that's supported */ switch( mechanismInfoPtr->cryptMode ) { case CRYPT_MODE_CBC: capabilityInfo->decryptCBCFunction = mechanismInfoPtr->decryptFunction; break; case CRYPT_MODE_CFB: capabilityInfo->decryptCFBFunction = mechanismInfoPtr->decryptFunction; break; case CRYPT_MODE_GCM: capabilityInfo->decryptGCMFunction = mechanismInfoPtr->decryptFunction; break; default: /* ECB or a PKC */ capabilityInfo->decryptFunction = mechanismInfoPtr->decryptFunction; break; } } if( cryptAlgo == CRYPT_ALGO_DH && pMechanism.flags & CKF_DERIVE ) { /* DH is a special-case that doesn't really have an encrypt function and where "decryption" is actually a derivation */ capabilityInfo->encryptFunction = mechanismInfoPtr->encryptFunction; capabilityInfo->decryptFunction = mechanismInfoPtr->decryptFunction; } /* Keygen capabilities are generally present as separate mechanisms, sometimes CKF_GENERATE/CKF_GENERATE_KEY_PAIR is set for the main mechanism and sometimes it's set for the separate one so if it isn't present in the main one we check the alternative one */ if( !( pMechanism.flags & keyGenFlag ) && \ ( mechanismInfoPtr->keygenMechanism != CKM_NONE ) ) { status = C_GetMechanismInfo( pkcs11Info->slotID, mechanismInfoPtr->keygenMechanism, &pMechanism ); if( status == CKR_OK && ( pMechanism.flags & keyGenFlag ) && \ ( !hardwareOnly || ( pMechanism.flags & CKF_HW ) ) ) { /* Some tinkertoy tokens don't implement key generation in hardware but instead do it on the host PC (!!!) and load the key into the token afterwards, so we have to perform another check here to make sure that they're doing things right */ capabilityInfo->generateKeyFunction = \ mechanismInfoPtr->generateKeyFunction; } } /* Record mechanism-specific parameters if required */ if( isConvAlgo( cryptAlgo ) || isMacAlgo( cryptAlgo ) ) { capabilityInfo->paramKeyType = mechanismInfoPtr->keyType; capabilityInfo->paramKeyGen = mechanismInfoPtr->keygenMechanism; capabilityInfo->paramDefaultMech = mechanismInfoPtr->defaultMechanism; } /* Some drivers report bizarre combinations of capabilities like (for RSA) sign, verify, and decrypt but not encrypt, which will fail later sanity checks. If we run into one of these we force the capabilities to be consistent by disabling any for which only partial capabilities are supported */ if( isPkcAlgo( cryptAlgo ) ) { if( capabilityInfo->decryptFunction != NULL && \ capabilityInfo->encryptFunction == NULL ) { DEBUG_DIAG(( "Driver reports decryption but not encryption " "capability for %s algorithm, disabling " "encryption", capabilityInfo->algoName )); capabilityInfo->decryptFunction = NULL; } if( capabilityInfo->signFunction != NULL && \ capabilityInfo->sigCheckFunction == NULL ) { DEBUG_DIAG(( "Driver reports signature-generation but not " "signature-verification capability for %s " "algorithm, disabling signing", capabilityInfo->algoName )); ////////////////////////////////// // Kludge to allow it to be used ////////////////////////////////// if( cryptAlgo == CRYPT_ALGO_ECDSA ) capabilityInfo->sigCheckFunction = capabilityInfo->signFunction; else capabilityInfo->signFunction = NULL; } /* If we've now disabled all capabilities, we can't use this algorithm */ if( capabilityInfo->decryptFunction == NULL && \ capabilityInfo->signFunction == NULL ) { DEBUG_DIAG(( "Use of algorithm %s disabled since no consistent " "set of capabilities is available", capabilityInfo->algoName )); clFree( "getCapability", capabilityInfo ); assert( DEBUG_WARN ); return( NULL ); } } /* If it's not a conventional encryption algo, we're done */ if( !isConvAlgo( cryptAlgo ) ) return( ( CAPABILITY_INFO * ) capabilityInfo ); /* PKCS #11 handles encryption modes by defining a separate mechanism for each one. In order to enumerate all the modes available for a particular algorithm we check for each mechanism in turn and set up the appropriate function pointers if it's available */ for( mechanismInfoPtr++, iterationCount = 0; mechanismInfoPtr->cryptAlgo == cryptAlgo && \ iterationCount < maxMechanisms; mechanismInfoPtr++, iterationCount++ ) { /* There's a different form of the existing mechanism available, check whether the driver implements it */ status = C_GetMechanismInfo( pkcs11Info->slotID, mechanismInfoPtr->mechanism, &pMechanism ); if( status != CKR_OK ) continue; /* Set up the pointer for the appropriate encryption mode */ switch( mechanismInfoPtr->cryptMode ) { case CRYPT_MODE_CBC: if( pMechanism.flags & CKF_ENCRYPT ) capabilityInfo->encryptCBCFunction = \ mechanismInfoPtr->encryptFunction; if( pMechanism.flags & CKF_DECRYPT ) capabilityInfo->decryptCBCFunction = \ mechanismInfoPtr->decryptFunction; break; case CRYPT_MODE_CFB: if( pMechanism.flags & CKF_ENCRYPT ) capabilityInfo->encryptCFBFunction = \ mechanismInfoPtr->encryptFunction; if( pMechanism.flags & CKF_DECRYPT ) capabilityInfo->decryptCFBFunction = \ mechanismInfoPtr->decryptFunction; break; case CRYPT_MODE_GCM: if( pMechanism.flags & CKF_ENCRYPT ) capabilityInfo->encryptGCMFunction = \ mechanismInfoPtr->encryptFunction; if( pMechanism.flags & CKF_DECRYPT ) capabilityInfo->decryptGCMFunction = \ mechanismInfoPtr->decryptFunction; break; default: retIntError_Null(); } } ENSURES_N( iterationCount < maxMechanisms ); return( ( CAPABILITY_INFO * ) capabilityInfo ); }
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 ); }
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 ) ); }
int checkServerCertValid( const CRYPT_CERTIFICATE iServerKey, INOUT ERROR_INFO *errorInfo ) { CRYPT_CERTIFICATE iServerCert; CRYPT_ERRTYPE_TYPE errorType DUMMY_INIT; CRYPT_ATTRIBUTE_TYPE errorLocus DUMMY_INIT; static const int complianceLevelStandard = CRYPT_COMPLIANCELEVEL_STANDARD; int complianceLevel, status; assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) ); REQUIRES( isHandleRangeValid( iServerKey ) ); status = krnlSendMessage( iServerKey, IMESSAGE_GETATTRIBUTE, &complianceLevel, CRYPT_OPTION_CERT_COMPLIANCELEVEL ); if( cryptStatusError( status ) ) { /* We can't do much more if we can't even get the initial compliance level */ return( CRYPT_ERROR_INVALID ); } /* Check whether the certificate is valid at a standard level of compliance, which catches expired certificates and other obvious problems */ krnlSendMessage( iServerKey, IMESSAGE_SETATTRIBUTE, ( MESSAGE_CAST ) &complianceLevelStandard, CRYPT_OPTION_CERT_COMPLIANCELEVEL ); status = krnlSendMessage( iServerKey, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_CERT ); krnlSendMessage( iServerKey, IMESSAGE_SETATTRIBUTE, ( MESSAGE_CAST ) &complianceLevel, CRYPT_OPTION_CERT_COMPLIANCELEVEL ); if( cryptStatusOK( status ) ) return( CRYPT_OK ); /* The certificate associated with the key isn't valid, get the certificate (since otherwise we'd be querying the key rather than the certificate) and fetch the extended error information */ status = krnlSendMessage( iServerKey, IMESSAGE_GETDEPENDENT, &iServerCert, OBJECT_TYPE_CERTIFICATE ); if( cryptStatusOK( status ) ) { int value; status = krnlSendMessage( iServerCert, IMESSAGE_GETATTRIBUTE, &value, CRYPT_ATTRIBUTE_ERRORLOCUS ); if( cryptStatusOK( status ) ) { errorLocus = value; /* int to enum */ status = krnlSendMessage( iServerCert, IMESSAGE_GETATTRIBUTE, &value, CRYPT_ATTRIBUTE_ERRORTYPE ); } if( cryptStatusOK( status ) ) errorType = value; /* int to enum */ } if( cryptStatusError( status ) ) { /* If we can't get extended error information then there's not much more that we can do */ retExt( CRYPT_ERROR_INVALID, ( CRYPT_ERROR_INVALID, errorInfo, "Server certificate is invalid" ) ); } /* Try and get more information on common errors and report them to the caller */ if( errorType == CRYPT_ERRTYPE_CONSTRAINT ) { switch( errorLocus ) { case CRYPT_CERTINFO_VALIDFROM: retExt( CRYPT_ERROR_INVALID, ( CRYPT_ERROR_INVALID, errorInfo, "Server certificate is not valid yet" ) ); case CRYPT_CERTINFO_VALIDTO: retExt( CRYPT_ERROR_INVALID, ( CRYPT_ERROR_INVALID, errorInfo, "Server certificate has expired" ) ); case CRYPT_CERTINFO_KEYUSAGE: retExt( CRYPT_ERROR_INVALID, ( CRYPT_ERROR_INVALID, errorInfo, "Server certificate's keyUsage doesn't allow it " "to be used" ) ); } } retExt( CRYPT_ERROR_INVALID, ( CRYPT_ERROR_INVALID, errorInfo, "Server certificate is invalid, error type %d, error " "locus %d", errorType, errorLocus ) ); }
CHECK_RETVAL \ static int selfTest( void ) { CONTEXT_INFO contextInfo; PKC_INFO contextData, *pkcInfo = &contextData; const CAPABILITY_INFO *capabilityInfoPtr; DLP_PARAMS dlpParams; BYTE buffer[ ( CRYPT_MAX_PKCSIZE * 2 ) + 32 + 8 ]; int status; /* Initialise the key components */ status = staticInitContext( &contextInfo, CONTEXT_PKC, getElgamalCapability(), &contextData, sizeof( PKC_INFO ), NULL ); if( cryptStatusError( status ) ) return( status ); status = importBignum( &pkcInfo->dlpParam_p, dlpTestKey.p, dlpTestKey.pLen, DLPPARAM_MIN_P, DLPPARAM_MAX_P, NULL, KEYSIZE_CHECK_PKC ); if( cryptStatusOK( status ) ) status = importBignum( &pkcInfo->dlpParam_g, dlpTestKey.g, dlpTestKey.gLen, DLPPARAM_MIN_G, DLPPARAM_MAX_G, &pkcInfo->dlpParam_p, KEYSIZE_CHECK_NONE ); if( cryptStatusOK( status ) ) status = importBignum( &pkcInfo->dlpParam_q, dlpTestKey.q, dlpTestKey.qLen, DLPPARAM_MIN_Q, DLPPARAM_MAX_Q, &pkcInfo->dlpParam_p, KEYSIZE_CHECK_NONE ); if( cryptStatusOK( status ) ) status = importBignum( &pkcInfo->dlpParam_y, dlpTestKey.y, dlpTestKey.yLen, DLPPARAM_MIN_Y, DLPPARAM_MAX_Y, &pkcInfo->dlpParam_p, KEYSIZE_CHECK_NONE ); if( cryptStatusOK( status ) ) status = importBignum( &pkcInfo->dlpParam_x, dlpTestKey.x, dlpTestKey.xLen, DLPPARAM_MIN_X, DLPPARAM_MAX_X, &pkcInfo->dlpParam_p, KEYSIZE_CHECK_NONE ); if( cryptStatusError( status ) ) { staticDestroyContext( &contextInfo ); retIntError(); } capabilityInfoPtr = contextInfo.capabilityInfo; ENSURES( sanityCheckPKCInfo( pkcInfo ) ); /* Perform a test a sig generation/check and test en/decryption */ #if 0 /* See comment in sig.code */ memset( buffer, '*', 20 ); status = capabilityInfoPtr->signFunction( &contextInfoPtr, buffer, -1 ); if( !cryptStatusError( status ) ) { memmove( buffer + 20, buffer, status ); memset( buffer, '*', 20 ); status = capabilityInfoPtr->sigCheckFunction( &contextInfoPtr, buffer, 20 + status ); } if( status != CRYPT_OK ) status = CRYPT_ERROR_FAILED; #endif /* 0 */ status = capabilityInfoPtr->initKeyFunction( &contextInfo, NULL, 0 ); if( cryptStatusError( status ) || \ !pairwiseConsistencyTest( &contextInfo, FALSE ) ) { staticDestroyContext( &contextInfo ); return( CRYPT_ERROR_FAILED ); } /* Finally, make sure that the memory fault-detection is working */ pkcInfo->dlpParam_p.d[ 8 ] ^= 0x0011; memset( buffer, 0, CRYPT_MAX_PKCSIZE ); memcpy( buffer + 1, "abcde", 5 ); setDLPParams( &dlpParams, buffer, bitsToBytes( contextInfo.ctxPKC->keySizeBits ), buffer, ( CRYPT_MAX_PKCSIZE * 2 ) + 32 ); status = capabilityInfoPtr->encryptFunction( &contextInfo, ( BYTE * ) &dlpParams, sizeof( DLP_PARAMS ) ); if( cryptStatusOK( status ) ) { /* The fault-detection couldn't detect a bit-flip, there's a problem */ staticDestroyContext( &contextInfo ); return( CRYPT_ERROR_FAILED ); } /* Clean up */ staticDestroyContext( &contextInfo ); return( CRYPT_OK ); }
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 BOOLEAN pairwiseConsistencyTest( INOUT CONTEXT_INFO *contextInfoPtr ) { CONTEXT_INFO checkContextInfo; PKC_INFO *sourcePkcInfo = contextInfoPtr->ctxPKC; PKC_INFO contextData, *pkcInfo = &contextData; KEYAGREE_PARAMS keyAgreeParams1, keyAgreeParams2; const CAPABILITY_INFO *capabilityInfoPtr; int bnStatus = BN_STATUS, status; assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); REQUIRES( sanityCheckPKCInfo( sourcePkcInfo ) ); /* The DH pairwise check is a bit more complex than the one for the other algorithms because there's no matched public/private key pair, so we have to load a second DH key to use for key agreement with the first one */ status = staticInitContext( &checkContextInfo, CONTEXT_PKC, getDHCapability(), &contextData, sizeof( PKC_INFO ), NULL ); if( cryptStatusError( status ) ) return( FALSE ); CKPTR( BN_copy( &pkcInfo->dlpParam_p, &sourcePkcInfo->dlpParam_p ) ); CKPTR( BN_copy( &pkcInfo->dlpParam_g, &sourcePkcInfo->dlpParam_g ) ); CKPTR( BN_copy( &pkcInfo->dlpParam_q, &sourcePkcInfo->dlpParam_q ) ); CKPTR( BN_copy( &pkcInfo->dlpParam_y, &sourcePkcInfo->dlpParam_y ) ); CKPTR( BN_copy( &pkcInfo->dlpParam_x, &sourcePkcInfo->dlpParam_x ) ); if( bnStatusError( bnStatus ) ) { staticDestroyContext( &checkContextInfo ); return( getBnStatusBool( bnStatus ) ); } /* Perform the pairwise test using the check key */ capabilityInfoPtr = checkContextInfo.capabilityInfo; memset( &keyAgreeParams1, 0, sizeof( KEYAGREE_PARAMS ) ); memset( &keyAgreeParams2, 0, sizeof( KEYAGREE_PARAMS ) ); status = capabilityInfoPtr->initKeyFunction( &checkContextInfo, NULL, 0 ); if( cryptStatusOK( status ) ) status = capabilityInfoPtr->encryptFunction( contextInfoPtr, ( BYTE * ) &keyAgreeParams1, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusOK( status ) ) status = capabilityInfoPtr->encryptFunction( &checkContextInfo, ( BYTE * ) &keyAgreeParams2, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusOK( status ) ) status = capabilityInfoPtr->decryptFunction( contextInfoPtr, ( BYTE * ) &keyAgreeParams2, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusOK( status ) ) status = capabilityInfoPtr->decryptFunction( &checkContextInfo, ( BYTE * ) &keyAgreeParams1, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusError( status ) || \ keyAgreeParams1.wrappedKeyLen != keyAgreeParams2.wrappedKeyLen || \ memcmp( keyAgreeParams1.wrappedKey, keyAgreeParams2.wrappedKey, keyAgreeParams1.wrappedKeyLen ) ) status = CRYPT_ERROR_FAILED; /* Clean up */ staticDestroyContext( &checkContextInfo ); return( cryptStatusOK( status ) ? TRUE : FALSE ); }
static int testKeysetWrite( const CRYPT_KEYSET_TYPE keysetType, const C_STR keysetName ) { CRYPT_KEYSET cryptKeyset; CRYPT_CERTIFICATE cryptCert; CRYPT_CONTEXT pubKeyContext, privKeyContext; C_CHR filenameBuffer[ FILENAME_BUFFER_SIZE ]; C_CHR name[ CRYPT_MAX_TEXTSIZE + 1 ]; int length, status; /* Import the certificate from a file - this is easier than creating one from scratch. We use one of the later certs in the test set, since this contains an email address, which the earlier ones don't */ status = importCertFromTemplate( &cryptCert, CERT_FILE_TEMPLATE, EMAILADDR_CERT_NO ); if( cryptStatusError( status ) ) { printf( "Couldn't read certificate from file, status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } /* Make sure that the certificate does actually contain an email address */ status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_EMAIL, name, &length ); if( cryptStatusError( status ) ) { printf( "Certificate doesn't contain an email address and can't be " "used for testing,\n line %d.\n", __LINE__ ); return( FALSE ); } /* Create the database keyset with a check to make sure this access method exists so we can return an appropriate error message. If the database table already exists, this will return a duplicate data error so we retry the open with no flags to open the existing database keyset for write access */ status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, keysetType, keysetName, CRYPT_KEYOPT_CREATE ); if( cryptStatusOK( status ) ) printf( "Created new certificate database '%s'.\n", keysetName ); if( status == CRYPT_ERROR_PARAM3 ) { /* This type of keyset access isn't available, return a special error code to indicate that the test wasn't performed, but that this isn't a reason to abort processing */ cryptDestroyCert( cryptCert ); return( CRYPT_ERROR_NOTAVAIL ); } if( status == CRYPT_ERROR_DUPLICATE ) status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, keysetType, keysetName, 0 ); if( cryptStatusError( status ) ) { cryptDestroyCert( cryptCert ); printf( "cryptKeysetOpen() failed with error code %d, line %d.\n", status, __LINE__ ); if( status == CRYPT_ERROR_OPEN ) return( CRYPT_ERROR_FAILED ); return( FALSE ); } /* Write the key to the database */ puts( "Adding certificate." ); status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( status == CRYPT_ERROR_DUPLICATE ) { /* The key is already present, delete it and retry the write */ status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_COMMONNAME, name, &length ); if( cryptStatusOK( status ) ) { #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ name[ length ] = TEXT( '\0' ); status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name ); } if( cryptStatusError( status ) ) return( extErrorExit( cryptKeyset, "cryptDeleteKey()", status, __LINE__ ) ); status = cryptAddPublicKey( cryptKeyset, cryptCert ); } if( cryptStatusError( status ) ) { printExtError( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ); /* LDAP writes can fail due to the chosen directory not supporting the schema du jour, so we're a bit more careful about cleaning up since we'll skip the error and continue processing */ cryptDestroyCert( cryptCert ); cryptKeysetClose( cryptKeyset ); return( FALSE ); } cryptDestroyCert( cryptCert ); /* Add a second certificate with C=US so that we've got enough certs to properly exercise the query code. This certificate is highly unusual in that it doesn't have a DN, so we have to move up the DN looking for higher-up values, in this case the OU */ if( keysetType != CRYPT_KEYSET_LDAP ) { status = importCertFromTemplate( &cryptCert, CERT_FILE_TEMPLATE, 2 ); if( cryptStatusError( status ) ) { printf( "Couldn't read certificate from file, status %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( status == CRYPT_ERROR_DUPLICATE ) { status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_COMMONNAME, name, &length ); if( cryptStatusError( status ) ) status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, name, &length ); if( cryptStatusOK( status ) ) { #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ name[ length ] = TEXT( '\0' ); status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name ); } if( cryptStatusOK( status ) ) status = cryptAddPublicKey( cryptKeyset, cryptCert ); } if( cryptStatusError( status ) ) return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ) ); cryptDestroyCert( cryptCert ); } /* Add a third certificate with a DN that'll cause problems for some storage technologies */ if( !loadRSAContexts( CRYPT_UNUSED, &pubKeyContext, &privKeyContext ) ) return( FALSE ); status = cryptCreateCert( &cryptCert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE ); if( cryptStatusError( status ) ) { printf( "cryptCreateCert() failed with error code %d, line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptSetAttribute( cryptCert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, pubKeyContext ); if( cryptStatusError( status ) ) return( attrErrorExit( cryptCert, "cryptSetAttribute()", status, __LINE__ ) ); if( !addCertFields( cryptCert, sqlCertData, __LINE__ ) ) return( FALSE ); status = cryptSignCert( cryptCert, privKeyContext ); if( cryptStatusError( status ) ) return( attrErrorExit( cryptCert, "cryptSignCert()", status, __LINE__ ) ); destroyContexts( CRYPT_UNUSED, pubKeyContext, privKeyContext ); status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( cryptStatusError( status ) ) { /* The key is already present, delete it and retry the write */ status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_COMMONNAME, name, &length ); if( cryptStatusOK( status ) ) { #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ name[ length ] = TEXT( '\0' ); status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name ); } if( cryptStatusError( status ) ) return( extErrorExit( cryptKeyset, "cryptDeleteKey()", status, __LINE__ ) ); status = cryptAddPublicKey( cryptKeyset, cryptCert ); } if( cryptStatusError( status ) ) { return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ) ); } cryptDestroyCert( cryptCert ); /* Now try the same thing with a CRL. This code also tests the duplicate-detection mechanism, if we don't get a duplicate error there's a problem */ puts( "Adding CRL." ); status = importCertFromTemplate( &cryptCert, CRL_FILE_TEMPLATE, 1 ); if( cryptStatusError( status ) ) { printf( "Couldn't read CRL from file, status %d, line %d.\n", status, __LINE__ ); return( TRUE ); } status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE ) return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ) ); status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( status != CRYPT_ERROR_DUPLICATE ) { printf( "Addition of duplicate item to keyset failed to produce " "CRYPT_ERROR_DUPLICATE, status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } cryptDestroyCert( cryptCert ); /* Finally, try it with a certificate chain */ puts( "Adding certificate chain." ); filenameParamFromTemplate( filenameBuffer, CERTCHAIN_FILE_TEMPLATE, CERT_CHAIN_NO ); status = importCertFile( &cryptCert, filenameBuffer ); if( cryptStatusError( status ) ) { printf( "Couldn't read certificate chain from file, status %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE ) return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ) ); cryptDestroyCert( cryptCert ); /* In addition to the other certs we also add the generic user certificate, which is used later in other tests. Since it may have been added earlier we try and delete it first (we can't use the existing version since the issuerAndSerialNumber won't match the one in the private-key keyset) */ status = getPublicKey( &cryptCert, USER_PRIVKEY_FILE, USER_PRIVKEY_LABEL ); if( cryptStatusError( status ) ) { printf( "Couldn't read user certificate from file, status %d, line " "%d.\n", status, __LINE__ ); return( FALSE ); } status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_COMMONNAME, name, &length ); if( cryptStatusError( status ) ) return( FALSE ); #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ name[ length ] = TEXT( '\0' ); do status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, name ); while( cryptStatusOK( status ) ); status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( status == CRYPT_ERROR_NOTFOUND ) { /* This can occur if a database keyset is defined but hasn't been initialised yet so the necessary tables don't exist, it can be opened but an attempt to add a key will return a not found error since it's the table itself rather than any item within it that isn't being found */ status = CRYPT_OK; } if( cryptStatusError( status ) ) return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ) ); cryptDestroyCert( cryptCert ); /* Finally, if ECC is enabled we also add ECC certificates that are used later in other tests */ if( cryptStatusOK( cryptQueryCapability( CRYPT_ALGO_ECDSA, NULL ) ) ) { #ifdef UNICODE_STRINGS wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ]; #endif /* UNICODE_STRINGS */ void *fileNamePtr = filenameBuffer; /* Add the P256 certificate */ filenameFromTemplate( filenameBuffer, SERVER_ECPRIVKEY_FILE_TEMPLATE, 256 ); #ifdef UNICODE_STRINGS mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 ); fileNamePtr = wcBuffer; #endif /* UNICODE_STRINGS */ status = getPublicKey( &cryptCert, fileNamePtr, USER_PRIVKEY_LABEL ); if( cryptStatusError( status ) ) { printf( "Couldn't read user certificate from file, status %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE ) return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ) ); cryptDestroyCert( cryptCert ); /* Add the P384 certificate */ filenameFromTemplate( filenameBuffer, SERVER_ECPRIVKEY_FILE_TEMPLATE, 384 ); #ifdef UNICODE_STRINGS mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 ); fileNamePtr = wcBuffer; #endif /* UNICODE_STRINGS */ status = getPublicKey( &cryptCert, fileNamePtr, USER_PRIVKEY_LABEL ); if( cryptStatusError( status ) ) { printf( "Couldn't read user certificate from file, status %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptAddPublicKey( cryptKeyset, cryptCert ); if( cryptStatusError( status ) && status != CRYPT_ERROR_DUPLICATE ) return( extErrorExit( cryptKeyset, "cryptAddPublicKey()", status, __LINE__ ) ); cryptDestroyCert( cryptCert ); } /* Make sure the deletion code works properly. This is an artifact of the way RDBMS' work, the delete query can execute successfully but not delete anything so we make sure the glue code correctly translates this into a CRYPT_DATA_NOTFOUND */ status = cryptDeleteKey( cryptKeyset, CRYPT_KEYID_NAME, TEXT( "Mr.Not Appearing in this Keyset" ) ); if( status != CRYPT_ERROR_NOTFOUND ) { puts( "Attempt to delete a nonexistant key reports success, the " "database backend glue\ncode needs to be fixed to handle this " "correctly." ); return( FALSE ); } /* Close the keyset */ status = cryptKeysetClose( cryptKeyset ); if( cryptStatusError( status ) ) { printf( "cryptKeysetClose() failed with error code %d, line %d.\n", status, __LINE__ ); } return( TRUE ); }
static int processAdditionalScepRequest( INOUT SESSION_INFO *sessionInfoPtr, const HTTP_URI_INFO *httpReqInfo ) { HTTP_URI_INFO rewrittenHttpReqInfo; MESSAGE_DATA msgData; int operationType, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isReadPtr( httpReqInfo, sizeof( HTTP_URI_INFO ) ) ); /* If the client has fed us an HTTP GET request, find out what they want. SCEP's handling of HTTP requests is a bit different from the "attribute '=' value" lookup that's normally used for HTTP data retrieval. Instead, it uses the format "'operation =' value '&' extraData", with the search key buried in the 'extraData' value. In addition the content of the 'extraData' value isn't defined outside of "any string which is understood by the CA". However since 'value' defines what we want, we can determine what to return based on this and ignore the 'extraData' portion. In order to fix up the query information into a format that works with standard HTTP queries, we rewrite the query data from the "'operation =' value '&' extraData" form into "attribute '=' value" before we process the query */ memset( &rewrittenHttpReqInfo, 0, sizeof( HTTP_URI_INFO ) ); memcpy( rewrittenHttpReqInfo.attribute, httpReqInfo->value, httpReqInfo->valueLen ); rewrittenHttpReqInfo.attributeLen = httpReqInfo->valueLen; if( httpReqInfo->extraDataLen > 0 ) { memcpy( rewrittenHttpReqInfo.value, httpReqInfo->extraData, httpReqInfo->extraDataLen ); rewrittenHttpReqInfo.valueLen = httpReqInfo->extraDataLen; } status = processCertQuery( sessionInfoPtr, &rewrittenHttpReqInfo, certstoreReadInfo, FAILSAFE_ARRAYSIZE( certstoreReadInfo, \ CERTSTORE_READ_INFO ), &operationType, NULL, 0, NULL ); if( cryptStatusError( status ) ) { sendCertErrorResponse( sessionInfoPtr, status ); return( status ); } ENSURES( operationType == SCEP_OPERATION_GETCACAPS || \ operationType == SCEP_OPERATION_GETCACERT || \ operationType == SCEP_OPERATION_GETCACERTCHAIN ); /* If it's a CA capabilities query, return the information as raw text over HTTP */ if( operationType == SCEP_OPERATION_GETCACAPS ) { STREAM stream; sMemOpen( &stream, sessionInfoPtr->receiveBuffer, 1024 ); status = swrite( &stream, "POSTPKIOperation\n", 17 ); if( algoAvailable( CRYPT_ALGO_SHA1 ) ) status = swrite( &stream, "SHA-1\n", 6 ); if( algoAvailable( CRYPT_ALGO_SHA2 ) ) status = swrite( &stream, "SHA-256\n", 8 ); if( algoAvailable( CRYPT_ALGO_SHAng ) ) status = swrite( &stream, "SHAng\n", 6 ); if( algoAvailable( CRYPT_ALGO_3DES ) ) status = swrite( &stream, "DES3\n", 5 ); if( algoAvailable( CRYPT_ALGO_AES ) ) status = swrite( &stream, "AES\n", 4 ); if( cryptStatusOK( status ) ) sessionInfoPtr->receiveBufEnd = stell( &stream ); sMemDisconnect( &stream ); ENSURES( cryptStatusOK( status ) ); return( writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE, SCEP_CONTENT_TYPE_LEN ) ); } /* Export the CA certificate and send it to the client */ setMessageData( &msgData, sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufSize ); status = krnlSendMessage( sessionInfoPtr->privateKey, IMESSAGE_CRT_EXPORT, &msgData, ( operationType == SCEP_OPERATION_GETCACERT ) ? \ CRYPT_CERTFORMAT_CERTIFICATE : \ CRYPT_CERTFORMAT_CERTCHAIN ); if( cryptStatusError( status ) ) { retExt( status, ( status, SESSION_ERRINFO, "Couldn't export CA certificate%s for '%s' request", ( operationType == SCEP_OPERATION_GETCACERT ) ? \ "" : " chain", ( operationType == SCEP_OPERATION_GETCACERT ) ? \ "GetCACert" : "GetCACertChain" ) ); } sessionInfoPtr->receiveBufEnd = msgData.length; if( operationType == SCEP_OPERATION_GETCACERT ) { return( writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE_GETCACERT, SCEP_CONTENT_TYPE_GETCACERT_LEN ) ); } return( writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE_GETCACERTCHAIN, SCEP_CONTENT_TYPE_GETCACERTCHAIN_LEN ) ); }
int testReadCert( void ) { CRYPT_CERTIFICATE cryptCert; C_CHR name[ CRYPT_MAX_TEXTSIZE + 1 ], email[ CRYPT_MAX_TEXTSIZE + 1 ]; C_CHR filenameBuffer[ FILENAME_BUFFER_SIZE ]; int length, status; /* Get the DN from one of the test certs (the one that we wrote to the keyset earlier with testKeysetWrite() */ status = importCertFromTemplate( &cryptCert, CERT_FILE_TEMPLATE, EMAILADDR_CERT_NO ); if( cryptStatusError( status ) ) { printf( "Couldn't read certificate from file, status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_COMMONNAME, name, &length ); if( cryptStatusOK( status ) ) { #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ name[ length ] = TEXT( '\0' ); status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_EMAIL, email, &length ); } if( cryptStatusOK( status ) ) { int i; #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ email[ length ] = TEXT( '\0' ); /* Mess up the case to make sure that case-insensitive matching is working */ for( i = 0; i < length; i++ ) { if( i & 1 ) email[ i ] = toupper( email[ i ] ); else email[ i ] = tolower( email[ i ] ); } } else { return( extErrorExit( cryptCert, "cryptGetAttributeString()", status, __LINE__ ) ); } cryptDestroyCert( cryptCert ); puts( "Testing certificate database read..." ); status = testKeysetRead( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME, CRYPT_KEYID_NAME, name, CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_NORMAL ); if( status == CRYPT_ERROR_NOTAVAIL ) { /* Database keyset access not available */ return( CRYPT_ERROR_NOTAVAIL ); } if( status == CRYPT_ERROR_FAILED ) { puts( "This is probably because you haven't set up a database or " "data source for use\nas a key database. For this test to " "work, you need to set up a database/data\nsource with the " "name '" DATABASE_KEYSET_NAME_ASCII "'.\n" ); return( TRUE ); } if( !status ) return( FALSE ); puts( "Reading certs using cached query." ); status = testKeysetRead( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME, CRYPT_KEYID_EMAIL, email, CRYPT_CERTTYPE_CERTIFICATE, READ_OPTION_MULTIPLE ); if( !status ) return( FALSE ); /* Get the DN from one of the test certificate chains */ filenameParamFromTemplate( filenameBuffer, CERTCHAIN_FILE_TEMPLATE, CERT_CHAIN_NO ); status = importCertFile( &cryptCert, filenameBuffer ); if( cryptStatusError( status ) ) { printf( "Couldn't read certificate chain from file, status %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptGetAttributeString( cryptCert, CRYPT_CERTINFO_COMMONNAME, name, &length ); if( cryptStatusOK( status ) ) { #ifdef UNICODE_STRINGS length /= sizeof( wchar_t ); #endif /* UNICODE_STRINGS */ name[ length ] = TEXT( '\0' ); } cryptDestroyCert( cryptCert ); /* Now read the complete certificate chain */ puts( "Reading complete certificate chain." ); status = testKeysetRead( DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME, CRYPT_KEYID_NAME, name, CRYPT_CERTTYPE_CERTCHAIN, READ_OPTION_NORMAL ); if( !status ) return( FALSE ); puts( "Certificate database read succeeded.\n" ); return( TRUE ); }
static int serverTransact( INOUT SESSION_INFO *sessionInfoPtr ) { SCEP_PROTOCOL_INFO protocolInfo; HTTP_DATA_INFO httpDataInfo; HTTP_URI_INFO httpReqInfo; BOOLEAN requestDataOK; int requestCount, length = DUMMY_INIT, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); /* SCEP is a weird protocol that started out as a basic IPsec certificate-provisioning mechanism for routers but then had a pile of additional functionality bolted onto it via HTTP mechanisms rather than having the protocol itself handle the extra functionality. Because of this we have to handle not only the standard HTTP-as-a- substrate mechanism used by the other protocols but also HTTP GET requests for additional information that the original protocol didn't accomodate */ sessionInfoPtr->receiveBufEnd = 0; sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES, STREAM_HTTPREQTYPE_ANY ); for( requestCount = 0; requestCount < 5; requestCount++ ) { initHttpDataInfoEx( &httpDataInfo, sessionInfoPtr->receiveBuffer, sessionInfoPtr->receiveBufSize, &httpReqInfo ); status = sread( &sessionInfoPtr->stream, &httpDataInfo, sizeof( HTTP_DATA_INFO ) ); if( cryptStatusError( status ) ) { sNetGetErrorInfo( &sessionInfoPtr->stream, &sessionInfoPtr->errorInfo ); return( status ); } /* If it's a proper SCEP protocol message, switch back to handling the main protocol */ if( httpDataInfo.reqType != STREAM_HTTPREQTYPE_GET ) { sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HTTPREQTYPES, STREAM_HTTPREQTYPE_POST ); length = httpDataInfo.bytesAvail; break; } /* It's one of the bolted-on additions to the basic SCEP protocol, handle it specially */ status = processAdditionalScepRequest( sessionInfoPtr, &httpReqInfo ); if( cryptStatusError( status ) ) return( status ); } if( requestCount >= 5 ) { /* The exact type of error response to send at this point is a bit tricky, the least inappropriate one is probably CRYPT_ERROR_DUPLICATE to indicate that too many duplicate requests were sent, since to get here the client would have had to send repeated identical bolt-on requests */ sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_DUPLICATE ); return( CRYPT_ERROR_OVERFLOW ); } /* Unfortunately we can't use readPkiDatagram() because of the weird dual-purpose HTTP transport used in SCEP so we have to duplicate portions of readPkiDatagram() here. See the readPkiDatagram() function for code comments explaining the following operations */ if( length < 4 || length >= MAX_INTLENGTH ) { sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA ); retExt( CRYPT_ERROR_UNDERFLOW, ( CRYPT_ERROR_UNDERFLOW, SESSION_ERRINFO, "Invalid PKI message length %d", length ) ); } status = length = \ checkObjectEncoding( sessionInfoPtr->receiveBuffer, length ); if( cryptStatusError( status ) ) { sendCertErrorResponse( sessionInfoPtr, CRYPT_ERROR_BADDATA ); retExt( status, ( status, SESSION_ERRINFO, "Invalid PKI message encoding" ) ); } sessionInfoPtr->receiveBufEnd = length; /* Process the initial message from the client */ initSCEPprotocolInfo( &protocolInfo ); status = checkScepRequest( sessionInfoPtr, &protocolInfo, &requestDataOK ); if( cryptStatusError( status ) ) { /* If we got far enough into the request data to be able to send a SCEP-level response, send that, otherwise just send an HTTP-level response */ if( requestDataOK ) sendErrorResponse( sessionInfoPtr, &protocolInfo, status ); else sendCertErrorResponse( sessionInfoPtr, status ); return( status ); } /* Issue a certificate from the request */ status = issueCertFromRequest( sessionInfoPtr, &protocolInfo ); if( cryptStatusError( status ) ) { sendErrorResponse( sessionInfoPtr, &protocolInfo, status ); destroySCEPprotocolInfo( &protocolInfo ); return( status ); } /* Return the certificate to the client */ status = createScepResponse( sessionInfoPtr, &protocolInfo ); if( cryptStatusOK( status ) ) status = writePkiDatagram( sessionInfoPtr, SCEP_CONTENT_TYPE, SCEP_CONTENT_TYPE_LEN ); destroySCEPprotocolInfo( &protocolInfo ); return( status ); }
static int exec_sign(int argc, char **argv) { const char *id, *dbfilename, *certfilename; char cakeysfilename[4096]; CRYPT_KEYID_TYPE id_type; CRYPT_KEYSET store, cakeys; CRYPT_CERTIFICATE cert, csr; CRYPT_CONTEXT ca_privkey; int status; if (argc != 4) { fprintf(stderr, "usage: sign dbfile (-e email | -n name) certfile\n"); return 1; } dbfilename = argv[0]; certfilename = argv[3]; id = argv[2]; if (strcmp(argv[1], "-e") == 0) { id_type = CRYPT_KEYID_EMAIL; } else if (strcmp(argv[1], "-n") == 0) { id_type = CRYPT_KEYID_NAME; } else { fprintf(stderr, "usage: sign dbfile (-e email | -n name) certfile\n"); return 1; } status = cryptKeysetOpen(&store, CRYPT_UNUSED, CRYPT_KEYSET_DATABASE_STORE, dbfilename, CRYPT_KEYOPT_NONE); WARN_AND_RETURN_IF(status); status = cryptCAGetItem(store, &csr, CRYPT_CERTTYPE_CERTREQUEST, id_type, id); WARN_AND_RETURN_IF(status); snprintf(cakeysfilename, 4095, "%s.keys", dbfilename); status = cryptKeysetOpen(&cakeys, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cakeysfilename, CRYPT_KEYOPT_NONE); WARN_AND_RETURN_IF(status); status = cryptGetPrivateKey(cakeys, &ca_privkey, CRYPT_KEYID_NAME, DEFAULT_CA_PRIVKEY_LABEL, DEFAULT_PASSWORD); WARN_AND_RETURN_IF(status); status = cryptKeysetClose(cakeys); WARN_AND_RETURN_IF(status); status = cryptCACertManagement(&cert, CRYPT_CERTACTION_ISSUE_CERT, store, ca_privkey, csr); if (!cryptStatusOK(status)) { int errorLocus; int errorType; cryptGetAttribute(store, CRYPT_ATTRIBUTE_ERRORLOCUS, &errorLocus); cryptGetAttribute(store, CRYPT_ATTRIBUTE_ERRORTYPE, &errorType); fprintf(stderr, "store: locus %d type %d\n", errorLocus, errorType); cryptGetAttribute(csr, CRYPT_ATTRIBUTE_ERRORLOCUS, &errorLocus); cryptGetAttribute(csr, CRYPT_ATTRIBUTE_ERRORTYPE, &errorType); fprintf(stderr, "csr: locus %d type %d\n", errorLocus, errorType); cryptGetAttribute(ca_privkey, CRYPT_ATTRIBUTE_ERRORLOCUS, &errorLocus); cryptGetAttribute(ca_privkey, CRYPT_ATTRIBUTE_ERRORTYPE, &errorType); fprintf(stderr, "ca_privkey: locus %d type %d\n", errorLocus, errorType); } WARN_AND_RETURN_IF(status); status = export_cert(cert, certfilename); WARN_AND_RETURN_IF(status); status = cryptDestroyCert(cert); WARN_AND_RETURN_IF(status); status = cryptDestroyCert(csr); WARN_AND_RETURN_IF(status); status = cryptDestroyContext(ca_privkey); WARN_AND_RETURN_IF(status); status = cryptKeysetClose(store); WARN_AND_RETURN_IF(status); return 0; }