static int initFunction( DEVICE_INFO *deviceInfo, const char *name, const int nameLength ) { CK_SESSION_HANDLE hSession; CK_SLOT_ID slotList[ MAX_PKCS11_SLOTS + 8 ]; CK_ULONG slotCount = MAX_PKCS11_SLOTS; CK_SLOT_INFO slotInfo; CK_TOKEN_INFO tokenInfo; CK_RV status; PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11; const PKCS11_MECHANISM_INFO *mechanismInfoPtr; char *labelPtr; int tokenSlot = DEFAULT_SLOT, i, labelLength, mechanismInfoSize; int cryptStatus, cryptStatus2; assert( isWritePtr( deviceInfo, sizeof( DEVICE_INFO ) ) ); assert( isReadPtr( name, nameLength ) ); /* Get information on all available slots */ memset( slotList, 0, sizeof( slotList ) ); status = C_GetSlotList( TRUE, slotList, &slotCount ); if( status != CKR_OK ) return( pkcs11MapError( status, CRYPT_ERROR_OPEN ) ); if( slotCount <= 0 ) { /* There are token slots present but no tokens in the slots */ return( CRYPT_ERROR_OPEN ); } /* Check whether a token name (used to select the slot) has been specified */ for( i = 1; i < nameLength - 1; i++ ) { if( name[ i ] == ':' && name[ i + 1 ] == ':' ) break; } if( i < nameLength - 1 ) { const char *tokenName = name + i + 2; /* Skip '::' */ const int tokenNameLength = nameLength - ( i + 2 ); if( tokenNameLength <= 0 ) return( CRYPT_ARGERROR_STR1 ); /* Some tokens don't implement named slots, so we also allow them to be specified using slot counts */ if( tokenNameLength == 1 && isDigit( *tokenName ) ) { tokenSlot = *tokenName - '0'; if( tokenSlot < 0 || tokenSlot > 9 ) return( CRYPT_ARGERROR_STR1 ); if( tokenSlot > slotCount - 1 ) /* Slots numbered from zero */ return( CRYPT_ERROR_NOTFOUND ); status = C_GetTokenInfo( slotList[ tokenSlot ], &tokenInfo ); if( status != CKR_OK ) return( CRYPT_ERROR_NOTFOUND ); } else { /* Check each (named) slot for a token matching the given name */ for( tokenSlot = 0; tokenSlot < slotCount && \ tokenSlot < FAILSAFE_ITERATIONS_MED; tokenSlot++ ) { status = C_GetTokenInfo( slotList[ tokenSlot ], &tokenInfo ); if( status == CKR_OK && \ !strCompare( tokenName, tokenInfo.label, tokenNameLength ) ) break; } ENSURES( tokenSlot < FAILSAFE_ITERATIONS_MED ); if( tokenSlot >= slotCount ) return( CRYPT_ERROR_NOTFOUND ); } } pkcs11Info->slotID = slotList[ tokenSlot ]; /* Get information on device-specific capabilities */ status = C_GetSlotInfo( pkcs11Info->slotID, &slotInfo ); if( status != CKR_OK ) { shutdownFunction( deviceInfo ); return( pkcs11MapError( status, CRYPT_ERROR_OPEN ) ); } if( slotInfo.flags & CKF_REMOVABLE_DEVICE ) { /* The device is removable */ deviceInfo->flags |= DEVICE_REMOVABLE; } status = C_GetTokenInfo( pkcs11Info->slotID, &tokenInfo ); if( status != CKR_OK ) { shutdownFunction( deviceInfo ); return( pkcs11MapError( status, CRYPT_ERROR_OPEN ) ); } if( tokenInfo.flags & CKF_RNG ) { /* The device has an onboard RNG that we can use */ deviceInfo->getRandomFunction = getRandomFunction; } #if 0 /* The Spyrus driver for pre-Lynks-II cards returns the local system time (with a GMT/localtime offset), ignoring the fact that the token has an onboard clock, so having the CKF_CLOCK_ON_TOKEN not set is accurate, although having it ignore the presence of the clock isn't very valid */ if( !( tokenInfo.flags & CKF_CLOCK_ON_TOKEN ) && \ ( !strCompare( tokenInfo.label, "Lynks Token", 11 ) || \ !strCompare( tokenInfo.model, "Rosetta", 7 ) ) ) { /* Fix buggy Spyrus PKCS #11 drivers which claim that the token doesn't have a RTC even though it does (the Rosetta (smart card) form of the token is even worse, it returns garbage in the label and manufacturer fields, but the model field is OK). There is a chance that there's a genuine problem with the clock (there are batches of tokens with bad clocks) but the time check that follows below will catch those */ tokenInfo.flags |= CKF_CLOCK_ON_TOKEN; } #endif /* 0 */ if( tokenInfo.flags & CKF_CLOCK_ON_TOKEN ) { const time_t theTime = getTokenTime( &tokenInfo ); const time_t currentTime = getTime(); /* The token claims to have an onboard clock that we can use. Since this could be arbitrarily inaccurate we compare it with the system time and only rely on it if it's within +/- 1 day of the system time. There is a second check that we should make to catch drivers that claim to read the time from the token but actually use the local computer's time, but this isn't easy to do. The most obvious way is to set the system time to a bogus value and check whether this matches the returned time, but this is somewhat drastic and requires superuser privs on most systems. An alternative is to check whether the claimed token time exactly matches the system time, but this will produce false positives if (for example) the token has been recently synchronised to the system time. For now all we can do is throw an exception if it appears that the token time is faked */ if( theTime > MIN_TIME_VALUE && \ theTime >= currentTime - 86400 && \ theTime <= currentTime + 86400 ) deviceInfo->flags |= DEVICE_TIME; /* If this check is triggered then the token time may be faked since it's identical to the host system time, see the comment above for details. We make an exception for soft-tokens (if we can detect them), which will (by definition) have the same time as the system time */ if( !( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name[ 0 ] && \ !strCompare( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name, "Software", 8 ) ) && \ theTime == currentTime ) { DEBUG_DIAG(( "PKCS #11 token time is the same as the host " "system time, token time may be faked by the " "driver." )); assert( DEBUG_WARN ); } } if( tokenInfo.flags & CKF_WRITE_PROTECTED ) { /* The device can't have data on it changed */ deviceInfo->flags |= DEVICE_READONLY; } if( ( tokenInfo.flags & CKF_LOGIN_REQUIRED ) || \ !( tokenInfo.flags & CKF_USER_PIN_INITIALIZED ) ) { /* The user needs to log in before using various device functions. We check for the absence of CKF_USER_PIN_INITIALIZED as well as the more obvious CKF_LOGIN_REQUIRED because if we've got an uninitialised device there's no PIN set so some devices will report that there's no login required (or at least none is possible). We need to introduce some sort of pipeline stall if this is the case because otherwise the user could successfully perform some functions that don't require a login (where the exact details of what's allowed without a login are device- specific) before running into mysterious failures when they get to functions that do require a login. To avoid this, we make an uninitialised device look like a login-required device, so the user gets an invalid-PIN error if they try and proceed */ deviceInfo->flags |= DEVICE_NEEDSLOGIN; } if( ( pkcs11Info->minPinSize = ( int ) tokenInfo.ulMinPinLen ) < 4 ) { /* Some devices report silly PIN sizes */ DEBUG_DIAG(( "Driver reports suspicious minimum PIN size %lu, " "using 4", tokenInfo.ulMinPinLen )); pkcs11Info->minPinSize = 4; } if( ( pkcs11Info->maxPinSize = ( int ) tokenInfo.ulMaxPinLen ) < 4 ) { /* Some devices report silly PIN sizes (setting this to ULONG_MAX or 4GB, which becomes -1 as an int, counts as silly). Since we can't differentiate between 0xFFFFFFFF = bogus value and 0xFFFFFFFF = ULONG_MAX we play it safe and set the limit to 8 bytes, which most devices should be able to handle */ DEBUG_DIAG(( "Driver reports suspicious maximum PIN size %lu, " "using 8", tokenInfo.ulMinPinLen )); pkcs11Info->maxPinSize = 8; } labelPtr = ( char * ) tokenInfo.label; for( labelLength = 32; labelLength > 0 && \ ( labelPtr[ labelLength - 1 ] == ' ' || \ !labelPtr[ labelLength - 1 ] ); labelLength-- ); /* Strip trailing blanks/nulls */ while( labelLength > 0 && *labelPtr == ' ' ) { /* Strip leading blanks */ labelPtr++; labelLength--; } if( labelLength > 0 ) { memcpy( pkcs11Info->label, labelPtr, labelLength ); pkcs11Info->labelLen = labelLength; sanitiseString( pkcs11Info->label, CRYPT_MAX_TEXTSIZE, labelLength ); } else { /* There's no label for the token, use the device label instead */ if( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name[ 0 ] ) { labelLength = \ min( strlen( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name ), CRYPT_MAX_TEXTSIZE ); memcpy( pkcs11Info->label, pkcs11InfoTbl[ pkcs11Info->deviceNo ].name, labelLength ); } } pkcs11Info->hActiveSignObject = CK_OBJECT_NONE; deviceInfo->label = pkcs11Info->label; deviceInfo->labelLen = pkcs11Info->labelLen; /* Open a session with the device. This gets a bit awkward because we can't tell whether a R/W session is OK without opening a session, but we can't open a session unless we know whether a R/W session is OK, so we first try for a RW session and if that fails we go for a read- only session */ status = C_OpenSession( pkcs11Info->slotID, CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSession ); if( status == CKR_TOKEN_WRITE_PROTECTED ) { status = C_OpenSession( pkcs11Info->slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSession ); } if( status != CKR_OK ) { cryptStatus = pkcs11MapError( status, CRYPT_ERROR_OPEN ); if( cryptStatus == CRYPT_ERROR_OPEN && \ !( tokenInfo.flags & CKF_USER_PIN_INITIALIZED ) ) { /* We couldn't do much with the error code, it could be that the token hasn't been initialised yet but unfortunately PKCS #11 doesn't define an error code for this condition. In addition many tokens will allow a session to be opened and then fail with a "PIN not set" error at a later point (which allows for more accurate error reporting), however a small number won't allow a session to be opened and return some odd-looking error because there's nothing useful available. The best way to report this in a meaningful manner to the caller is to check whether the user PIN has been initialised, if it hasn't then it's likely that the token as a whole hasn't been initialised so we return a not initialised error */ cryptStatus = CRYPT_ERROR_NOTINITED; } return( cryptStatus ); } ENSURES( hSession != CK_OBJECT_NONE ); pkcs11Info->hSession = hSession; deviceInfo->flags |= DEVICE_ACTIVE; /* Set up the capability information for this device. Since there can be devices that have one set of capabilities but not the other (e.g. a smart card that only performs RSA ops), we allow one of the two sets of mechanism information setups to fail, but not both */ mechanismInfoPtr = getMechanismInfoPKC( &mechanismInfoSize ); cryptStatus = getCapabilities( deviceInfo, mechanismInfoPtr, mechanismInfoSize ); mechanismInfoPtr = getMechanismInfoConv( &mechanismInfoSize ); cryptStatus2 = getCapabilities( deviceInfo, mechanismInfoPtr, mechanismInfoSize ); if( cryptStatusError( cryptStatus ) && cryptStatusError( cryptStatus2 ) ) { shutdownFunction( deviceInfo ); return( ( cryptStatus == CRYPT_ERROR ) ? \ CRYPT_ERROR_OPEN : ( int ) cryptStatus ); } 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 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 ); }
errCode writeManifestInTxt(const char *manPath, const sManifest *writeMe, const sTex **refarrTexs){ static const char *HEAD_SHEET = "sheet_count="; static const char *HEAD_SEQUENCES = "sequences_count="; static const char *HEAD_STILLS = "stills_count="; static const char *HEAD_FONTS = "fonts_count="; static const char *FILE_TYPE = ".txt"; static const char *NEW_LINE="\n"; static const size_t LENBUFF = 128; char buff[ LENBUFF ]; FILE *handFile; sTex const *refTex; unsigned int i, j; snprintf(buff, LENBUFF, "%s%s", manPath, FILE_TYPE); handFile = fopen(buff, "w"); if(manPath == NULL) return ERROR; if(writeMe == NULL) return ERROR; /** Sheets */ { sSheet const *refSheet; fprintf(handFile, "%s%i%s", HEAD_SHEET, writeMe->refSheets->num, NEW_LINE); for(i=0; i < writeMe->refSheets->num; ++i){ refSheet = writeMe->refSheets->dynarrSheets[i]; fprintf(handFile, "%s,%i,%i%s", refSheet->name, refSheet->w, refSheet->h, NEW_LINE ); } } /** Stills */ fprintf(handFile, "%s%i%s", HEAD_STILLS, writeMe->refStills->num, NEW_LINE); for(i=0; i < writeMe->refStills->num; ++i){ refTex = refarrTexs[ writeMe->refStills->dynarrTexIDs[i] ]; strncpy(buff, refTex->name, LENBUFF); sanitiseString(buff); fprintf(handFile, "%s,%i,%i,%i,%i%s", refTex->name, refTex->x, refTex->y, refTex->w, refTex->h, NEW_LINE ); } /** Sequences */ { sTexSeq const* refSeq; size_t posDelim; fprintf(handFile, "%s%i%s", HEAD_SEQUENCES, writeMe->refSeqs->num, NEW_LINE); for(i=0; i < writeMe->refSeqs->num; ++i){ refSeq = &writeMe->refSeqs->dynarrSeqs[i]; if(refSeq == NULL) continue; if(refSeq->num == 0){ WARN("Empty sequence"); continue; } refTex = refarrTexs[ refSeq->dynarrTexIDs[0] ]; posDelim = strcspn(refTex->name, "1234567890"); if(posDelim > 0){ memcpy(buff, refTex->name, posDelim); buff[ (posDelim < LENBUFF) ? posDelim : LENBUFF -1 ] = '\0'; }else{ strncpy(buff, refTex->name, LENBUFF); } sanitiseString(buff); fprintf(handFile, "%s,%i,%i,%i,(", buff, refSeq->num, refTex->w, refTex->h); for(j=0; j < refSeq->num; ++j){ refTex = refarrTexs[ refSeq->dynarrTexIDs[j] ]; fprintf(handFile, "(%i,%i,%i),", refTex->x, refTex->y, refSeq->dynarrSheetIDs[j]); } fprintf(handFile, ")%s", NEW_LINE); } } /** Fonts */ { sFont *refFnt; size_t posDelim; fprintf(handFile, "%s%i%s", HEAD_FONTS, writeMe->refFonts->num, NEW_LINE); for(i=0; i < writeMe->refFonts->num; ++i){ refFnt = writeMe->refFonts->dynarrFonts[i]; if(refFnt->num == 0){ WARN("Empty font"); continue; } refTex = refarrTexs[ refFnt->dynarrTexIDs[0] ]; posDelim = strcspn(refTex->name, "1234567890"); if(posDelim > 0){ memcpy(buff, refTex->name, posDelim); buff[ (posDelim < LENBUFF) ? posDelim : LENBUFF -1 ] = '\0'; }else{ strncpy(buff, refTex->name, LENBUFF); } sanitiseString(buff); fprintf(handFile, "%s,%i,(", buff, refFnt->num); for(j=0; j < refFnt->num; ++j){ refTex = refarrTexs[ refFnt->dynarrTexIDs[j] ]; fprintf(handFile, "(%i,%i,%i,%i,%i,%i,%i),", refFnt->dynarrCharcodes[j], refFnt->dynarrSheetIDs[j], refTex->x, refTex->y, refTex->w, refTex->h, refFnt->dynarrOffsetY[j] ); } fprintf(handFile, ")%s", NEW_LINE); } } fclose(handFile); return NOPROB; /* if(handFile==NULL){ WARN("Unable to write file %s.", manPath); return PROBLEM; } if(writeMe->dynarrSheets != NULL){ sSheet **itrSheet; unsigned int numSheets=0; for(itrSheet = writeMe->dynarrSheets; (*itrSheet) != NULL; ++itrSheet) ++numSheets; fprintf(handFile, "%s%i%s", HEAD_SHEET, numSheets, NEW_LINE); for(itrSheet = writeMe->dynarrSheets; (*itrSheet) != NULL; ++itrSheet){ fprintf(handFile, "%i,%i%s", (*itrSheet)->w, (*itrSheet)->h, NEW_LINE); } }else{ fclose(handFile); return PROBLEM; } if(writeMe->dynarrSequences != NULL){ sSequence **itrSeq; unsigned int numSeq=0; for(itrSeq= writeMe->dynarrSequences; (*itrSeq) != NULL; ++itrSeq) ++numSeq; fprintf(handFile, "%s%i%s", HEAD_SEQUENCES, numSeq, NEW_LINE); unsigned int idxSeqSheet, idxFrame; itrSeq= writeMe->dynarrSequences; while( *itrSeq!=NULL ){ if((*itrSeq)==NULL || (*itrSeq)->name==NULL) continue; fprintf(handFile, "\"%s\",%i,%i,%i,%i,(", (*itrSeq)->name, (*itrSeq)->numFrames, (*itrSeq)->w, (*itrSeq)->h, (*itrSeq)->sizeSheets ); for(idxFrame=0; idxFrame < (*itrSeq)->numFrames; ++idxFrame) fprintf(handFile, "%u %u,", (*itrSeq)->dynarrFrames[idxFrame].x, (*itrSeq)->dynarrFrames[idxFrame].y); fprintf(handFile, "),("); for(idxSeqSheet=0; idxSeqSheet < (*itrSeq)->sizeSheets; ++idxSeqSheet) fprintf(handFile, "%u,", (*itrSeq)->dynarrSheetID[idxSeqSheet]); fprintf(handFile, "),("); if((*itrSeq)->dynarrNextSheetOnFrame!=NULL){ for(idxSeqSheet=0; idxSeqSheet < (*itrSeq)->sizeSheets -1; ++idxSeqSheet) fprintf(handFile, "%u,", (*itrSeq)->dynarrNextSheetOnFrame[idxSeqSheet]); } fprintf(handFile, ")%s", NEW_LINE); ++itrSeq; } } if(writeMe->dynarrStills != NULL){ sStill **itrStill; unsigned int numStills=0; for(itrStill = writeMe->dynarrStills; (*itrStill) != NULL; ++itrStill ) ++numStills; fprintf(handFile, "%s%i%s", HEAD_STILLS, numStills, NEW_LINE); for(itrStill = writeMe->dynarrStills; (*itrStill) != NULL; ++itrStill ){ fprintf(handFile, "\"%s\",%i,%i,%i,%i,%i%s", (*itrStill)->name, (*itrStill)->x, (*itrStill)->y, (*itrStill)->w, (*itrStill)->h, (*itrStill)->sheetID, NEW_LINE ); } } fclose(handFile); return NOPROB; */ }
errCode genMan( const char *strManName, const sSeqList *refSeqs, const sStillList *refStills, const sFontList *refFonts, const sSheetList *refSheets, const sTex **refarrTexs, sManifest *outMan ){ static const size_t LENBUFF = 256; sTex const *refTex; char buff[LENBUFF]; unsigned int i; outMan->refSeqs = refSeqs; outMan->refStills = refStills; outMan->refFonts = refFonts; outMan->refSheets = refSheets; for(i=0; i < refSheets->num; ++i){ snprintf(buff, LENBUFF, "%s%i", strManName, i); copyString(&refSheets->dynarrSheets[i]->name, buff); } if(refStills->num > 0){ char *refDelim; size_t posDelim; outMan->dynarrStrStillNames = calloc(refStills->num, sizeof(char*)); memset(outMan->dynarrStrStillNames, 0, refStills->num *sizeof(char*)); for(i=0; i < refStills->num; ++i){ refTex = refarrTexs[ refStills->dynarrTexIDs[i] ]; refDelim = strrchr(refTex->name, '.'); posDelim = (refDelim != NULL && (refDelim - refTex->name) / sizeof(char) < LENBUFF) ? (refDelim - refTex->name) / sizeof(char) : LENBUFF ; memcpy(buff, refTex->name, posDelim); buff[posDelim] = '\0'; sanitiseString(buff); copyString(&outMan->dynarrStrStillNames[i], buff); } } if(refSeqs->num > 0){ outMan->dynarrStrSeqNames = calloc_chk(refSeqs->num, sizeof(char*)); memset(outMan->dynarrStrSeqNames, 0, refSeqs->num *sizeof(char*)); for(i=0; i < refSeqs->num; ++i){ if(refSeqs->dynarrSeqs[i].num == 0) continue; refTex = refarrTexs[ refSeqs->dynarrSeqs[i].dynarrTexIDs[0] ]; imgFNameToWSearch(refTex->name, buff, LENBUFF); copyWithoutChars(&(outMan->dynarrStrSeqNames[i]), buff, "#*"); sanitiseString(outMan->dynarrStrSeqNames[i]); } } if(refFonts->num > 0){ char const *refCCodeDelim; outMan->dynarrStrFontNames = calloc_chk(refFonts->num, sizeof(char*)); memset(outMan->dynarrStrFontNames, 0, refFonts->num * sizeof(char*)); for(i=0; i < refFonts->num; ++i){ refTex = refarrTexs[ refFonts->dynarrFonts[i]->dynarrTexIDs[0] ]; if(refFonts->dynarrFonts[i]->num == 0) continue; refCCodeDelim = strrchr(refTex->name, (unsigned int)'_'); if(refCCodeDelim == NULL) continue; size_t lenName = (size_t)( refCCodeDelim - refTex->name ) / sizeof(char) ; outMan->dynarrStrFontNames[i] = calloc_chk(lenName +1, sizeof(char)); memcpy( outMan->dynarrStrFontNames[i], refTex->name, lenName *sizeof(char) ); outMan->dynarrStrFontNames[i][lenName] = '\0'; sanitiseString(outMan->dynarrStrFontNames[i]); } } return NOPROB; }
int exchangeServerKeys( INOUT SESSION_INFO *sessionInfoPtr, INOUT SSL_HANDSHAKE_INFO *handshakeInfo ) { STREAM *stream = &handshakeInfo->stream; int length, status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) ); /* Read the response from the client and, if we're expecting a client certificate, make sure that it's present */ status = readHSPacketSSL( sessionInfoPtr, handshakeInfo, &length, SSL_MSG_HANDSHAKE ); if( cryptStatusError( status ) ) return( status ); sMemConnect( stream, sessionInfoPtr->receiveBuffer, length ); if( clientCertAuthRequired( sessionInfoPtr ) ) { /* Read the client certificate chain and make sure that the certificate being presented is valid for access */ status = readCheckClientCerts( sessionInfoPtr, handshakeInfo, stream ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } /* Read the next packet(s) if necessary */ status = refreshHSStream( sessionInfoPtr, handshakeInfo ); if( cryptStatusError( status ) ) return( status ); } /* Process the client key exchange packet: byte ID = SSL_HAND_CLIENT_KEYEXCHANGE uint24 len DH: uint16 yLen byte[] y ECDH: uint16 ecPointLen byte[] ecPoint PSK: uint16 userIDLen byte[] userID RSA: [ uint16 encKeyLen -- TLS 1.x ] byte[] rsaPKCS1( byte[2] { 0x03, 0x0n } || byte[46] random ) */ status = checkHSPacketHeader( sessionInfoPtr, stream, &length, SSL_HAND_CLIENT_KEYEXCHANGE, UINT16_SIZE + 1 ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } if( isKeyxAlgo( handshakeInfo->keyexAlgo ) ) { KEYAGREE_PARAMS keyAgreeParams; const BOOLEAN isECC = isEccAlgo( handshakeInfo->keyexAlgo ); memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) ); if( isECC ) { status = readEcdhValue( stream, keyAgreeParams.publicValue, CRYPT_MAX_PKCSIZE, &keyAgreeParams.publicValueLen ); } else { status = readInteger16UChecked( stream, keyAgreeParams.publicValue, &keyAgreeParams.publicValueLen, MIN_PKCSIZE, CRYPT_MAX_PKCSIZE ); } if( cryptStatusError( status ) ) { sMemDisconnect( stream ); /* Some misconfigured clients may use very short keys, we perform a special-case check for these and return a more specific message than the generic bad-data error */ if( status == CRYPT_ERROR_NOSECURE ) { retExt( CRYPT_ERROR_NOSECURE, ( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO, "Insecure key used in key exchange" ) ); } retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid DH/ECDH phase 2 key agreement data" ) ); } /* Perform phase 2 of the DH/ECDH key agreement */ status = krnlSendMessage( handshakeInfo->dhContext, IMESSAGE_CTX_DECRYPT, &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); if( cryptStatusError( status ) ) { zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); sMemDisconnect( stream ); retExt( status, ( status, SESSION_ERRINFO, "Invalid DH/ECDH phase 2 key agreement value" ) ); } if( isECC ) { const int xCoordLen = ( keyAgreeParams.wrappedKeyLen - 1 ) / 2; /* The output of the ECDH operation is an ECC point, but for some unknown reason TLS only uses the x coordinate and not the full point. To work around this we have to rewrite the point as a standalone x coordinate, which is relatively easy because we're using an "uncompressed" point format: +---+---------------+---------------+ |04 | qx | qy | +---+---------------+---------------+ |<- fldSize --> |<- fldSize --> | */ REQUIRES( keyAgreeParams.wrappedKeyLen >= MIN_PKCSIZE_ECCPOINT && \ keyAgreeParams.wrappedKeyLen <= MAX_PKCSIZE_ECCPOINT && \ ( keyAgreeParams.wrappedKeyLen & 1 ) == 1 && \ keyAgreeParams.wrappedKey[ 0 ] == 0x04 ); memmove( keyAgreeParams.wrappedKey, keyAgreeParams.wrappedKey + 1, xCoordLen ); keyAgreeParams.wrappedKeyLen = xCoordLen; } ENSURES( rangeCheckZ( 0, keyAgreeParams.wrappedKeyLen, CRYPT_MAX_PKCSIZE + CRYPT_MAX_TEXTSIZE ) ); memcpy( handshakeInfo->premasterSecret, keyAgreeParams.wrappedKey, keyAgreeParams.wrappedKeyLen ); handshakeInfo->premasterSecretSize = keyAgreeParams.wrappedKeyLen; zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) ); } else { if( handshakeInfo->authAlgo == CRYPT_ALGO_NONE ) { const ATTRIBUTE_LIST *attributeListPtr; BYTE userID[ CRYPT_MAX_TEXTSIZE + 8 ]; /* Read the client user ID and make sure that it's a valid user. Handling non-valid users is somewhat problematic, we can either bail out immediately or invent a fake password for the (non-)user and continue with that. The problem with this is that it doesn't really help hide whether the user is valid or not due to the fact that we're still vulnerable to a timing attack because it takes considerably longer to generate the random password than it does to read a fixed password string from memory, so an attacker can tell from the timing whether the username is valid or not. In addition usability research on real-world users indicates that this actually reduces security while having little to no tangible benefit. Because of this we don't try and fake out the valid/invalid user name indication but just exit immediately if an invalid name is found */ length = readUint16( stream ); if( length < 1 || length > CRYPT_MAX_TEXTSIZE || \ cryptStatusError( sread( stream, userID, length ) ) ) { sMemDisconnect( stream ); retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid client user ID" ) ); } attributeListPtr = \ findSessionInfoEx( sessionInfoPtr->attributeList, CRYPT_SESSINFO_USERNAME, userID, length ); if( attributeListPtr == NULL ) { sMemDisconnect( stream ); retExt( CRYPT_ERROR_WRONGKEY, ( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, "Unknown user name '%s'", sanitiseString( userID, CRYPT_MAX_TEXTSIZE, length ) ) ); } /* Select the attribute with the user ID and move on to the associated password */ sessionInfoPtr->attributeListCurrent = \ ( ATTRIBUTE_LIST * ) attributeListPtr; attributeListPtr = attributeListPtr->next; ENSURES( attributeListPtr != NULL && \ attributeListPtr->attributeID == CRYPT_SESSINFO_PASSWORD ); /* Create the shared premaster secret from the user password */ status = createSharedPremasterSecret( \ handshakeInfo->premasterSecret, CRYPT_MAX_PKCSIZE + CRYPT_MAX_TEXTSIZE, &handshakeInfo->premasterSecretSize, attributeListPtr->value, attributeListPtr->valueLength, ( attributeListPtr->flags & ATTR_FLAG_ENCODEDVALUE ) ? \ TRUE : FALSE ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); retExt( status, ( status, SESSION_ERRINFO, "Couldn't create master secret from shared " "secret/password value" ) ); } } else { BYTE wrappedKey[ CRYPT_MAX_PKCSIZE + 8 ]; if( sessionInfoPtr->version <= SSL_MINOR_VERSION_SSL ) { /* The original Netscape SSL implementation didn't provide a length for the encrypted key and everyone copied that so it became the de facto standard way to do it (sic faciunt omnes. The spec itself is ambiguous on the topic). This was fixed in TLS (although the spec is still ambigous) so the encoding differs slightly between SSL and TLS. To work around this we have to duplicate a certain amount of the integer-read code here */ if( isShortPKCKey( length ) ) status = CRYPT_ERROR_NOSECURE; else { if( length < MIN_PKCSIZE || length > CRYPT_MAX_PKCSIZE || \ cryptStatusError( sread( stream, wrappedKey, length ) ) ) status = CRYPT_ERROR_BADDATA; } } else { status = readInteger16UChecked( stream, wrappedKey, &length, MIN_PKCSIZE, CRYPT_MAX_PKCSIZE ); } if( cryptStatusError( status ) ) { sMemDisconnect( stream ); /* Some misconfigured clients may use very short keys, we perform a special-case check for these and return a more specific message than the generic bad-data */ if( status == CRYPT_ERROR_NOSECURE ) { retExt( CRYPT_ERROR_NOSECURE, ( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO, "Insecure key used in key exchange" ) ); } retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid RSA encrypted key data" ) ); } /* Decrypt the pre-master secret */ status = unwrapPremasterSecret( sessionInfoPtr, handshakeInfo, wrappedKey, length ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } } } /* If we're expecting a client certificate, process the client certificate verify */ if( clientCertAuthRequired( sessionInfoPtr ) ) { const BOOLEAN isECC = isEccAlgo( handshakeInfo->keyexAlgo ); /* Since the client certificate-verify message requires the hash of all handshake packets up to this point, we have to interrupt the processing to calculate the hash before we continue */ status = createCertVerifyHash( sessionInfoPtr, handshakeInfo ); if( cryptStatusError( status ) ) return( status ); /* Read the next packet(s) if necessary */ status = refreshHSStream( sessionInfoPtr, handshakeInfo ); if( cryptStatusError( status ) ) return( status ); /* Process the client certificate verify packet: byte ID = SSL_HAND_CLIENT_CERTVERIFY uint24 len byte[] signature */ status = checkHSPacketHeader( sessionInfoPtr, stream, &length, SSL_HAND_CLIENT_CERTVERIFY, isECC ? MIN_PKCSIZE_ECCPOINT : \ MIN_PKCSIZE ); if( cryptStatusOK( status ) ) status = checkCertVerify( sessionInfoPtr, handshakeInfo, stream, length ); if( cryptStatusError( status ) ) { sMemDisconnect( stream ); return( status ); } } sMemDisconnect( stream ); return( CRYPT_OK ); }