/*- *********************************************************************** * * $Id: dsv.c,v 1.1 2006/05/25 20:28:41 mavrik Exp $ * *********************************************************************** * * Copyright 2006-2006 The WebJob Project, All Rights Reserved. * *********************************************************************** */ #include "all-includes.h" /*- *********************************************************************** * * DsvAddCertNode * *********************************************************************** */ DSV_CERT_NODE * DsvAddCertNode(DSV_CERT_NODE *psHead, DSV_CERT_NODE *psNode) { DSV_CERT_NODE *psTemp = NULL; DSV_CERT_NODE *psTail = NULL; /*- ********************************************************************* * * If the new node is NULL, there's nothing to do. * ********************************************************************* */ if (psNode == NULL) { return psHead; } /*- ********************************************************************* * * If the head is NULL, make the new node the head. Otherwise, find * the tail, and attach the new node to it. * ********************************************************************* */ if (psHead == NULL) { psHead = psNode; } else { psTemp = psHead; while (psTemp != NULL) { if (psTemp->psNext == NULL) { psTail = psTemp; } psTemp = psTemp->psNext; } psTail->psNext = psNode; } psTail = psNode; psTail->psNext = NULL; return psHead; } /*- *********************************************************************** * * DsvChopTrailingEquals * *********************************************************************** */ int DsvChopTrailingEquals(char *pcData, int iFlags) { int iLength = 0; iLength = strlen(pcData); while (iLength > 0 && pcData[iLength - 1] == '=') { if ((iFlags & DSV_CHOP) == DSV_CHOP) { pcData[--iLength] = 0; } else { --iLength; /* Adjust the length, but do not chop. */ } } return iLength; } /*- *********************************************************************** * * DsvChopTrailingSlashes * *********************************************************************** */ int DsvChopTrailingSlashes(char *pcData, int iFlags) { int iLength = 0; iLength = strlen(pcData); while (iLength > 0 && pcData[iLength - 1] == DSV_SLASHCHAR) { if ((iFlags & DSV_CHOP) == DSV_CHOP) { pcData[--iLength] = 0; } else { --iLength; /* Adjust the length, but do not chop. */ } } return iLength; } /*- *********************************************************************** * * DsvChopTrailingWhitespace * *********************************************************************** */ int DsvChopTrailingWhitespace(char *pcData, int iFlags) { int iLength = 0; iLength = strlen(pcData); while (iLength > 0 && isspace((int) pcData[iLength - 1])) { if ((iFlags & DSV_CHOP) == DSV_CHOP) { pcData[--iLength] = 0; } else { --iLength; /* Adjust the length, but do not chop. */ } } return iLength; } /*- *********************************************************************** * * DsvDecodeSignature * *********************************************************************** */ int DsvDecodeSignature(DSV_SIGNATURE *psSignature, char *pcError) { const char acRoutine[] = "DsvDecodeSignature()"; int iError = 0; int iActualBase64Length = 0; int iActualBinaryLength = 0; int iActualBufferSize = 0; int iLength = 0; int iNChars = 0; /*- ********************************************************************* * * Check required inputs. * ********************************************************************* */ if (psSignature->pucBase64Signature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: Undefined base64 signature.", acRoutine); return ER; } iActualBase64Length = strlen((char *) psSignature->pucBase64Signature); /*- ********************************************************************* * * Calculate the signature's actual length. Then, calculate how many * bytes are required to hold it safely -- i.e., so EVP_DecodeBlock() * won't overflow the buffer. * ********************************************************************* */ iLength = DsvChopTrailingEquals((char *) psSignature->pucBase64Signature, 0); iNChars = iActualBase64Length - (iActualBase64Length - iLength); iActualBinaryLength = (iNChars / 4 * 3) + ((iNChars % 4) ? (iNChars % 4) - 1 : 0); iActualBufferSize = iActualBase64Length / 4 * 3; /*- ********************************************************************* * * Allocate and clear memory to hold the signature. * ********************************************************************* */ psSignature->pucBinarySignature = (unsigned char *) calloc(iActualBufferSize, 1); if (psSignature->pucBinarySignature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); return ER; } /*- ********************************************************************* * * Decode the signature (base64 --> binary). * ********************************************************************* */ iError = EVP_DecodeBlock(psSignature->pucBinarySignature, psSignature->pucBase64Signature, iActualBase64Length); if (iError == ER) { snprintf(pcError, MESSAGE_SIZE, "%s: EVP_DecodeBlock(): Unable to decode signature.", acRoutine); return ER; } psSignature->iBinaryLength = iActualBinaryLength; return ER_OK; } /*- *********************************************************************** * * DsvEncodeSignature * *********************************************************************** */ int DsvEncodeSignature(DSV_SIGNATURE *psSignature, char *pcError) { const char acRoutine[] = "DsvEncodeSignature()"; int iActualBase64Length = 0; int iTargetBase64Length = 0; /*- ********************************************************************* * * Check required inputs. * ********************************************************************* */ if (psSignature->pucBinarySignature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: Undefined binary signature.", acRoutine); return ER; } /*- ********************************************************************* * * Calculate the number of bytes are required to hold the signature. * ********************************************************************* */ iTargetBase64Length = ((psSignature->iBinaryLength / 3) + ((psSignature->iBinaryLength % 3) ? 1 : 0)) * 4; /*- ********************************************************************* * * Allocate and clear memory to hold the signature. * ********************************************************************* */ psSignature->pucBase64Signature = (unsigned char *) calloc(iTargetBase64Length + 1, 1); if (psSignature->pucBase64Signature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); return ER; } /*- ********************************************************************* * * Encode the signature (binary --> base64). * ********************************************************************* */ iActualBase64Length = EVP_EncodeBlock(psSignature->pucBase64Signature, psSignature->pucBinarySignature, psSignature->iBinaryLength); if (iActualBase64Length != iTargetBase64Length) { snprintf(pcError, MESSAGE_SIZE, "%s: Base64 signature length mismatch!: [%d] != [%d]", acRoutine, iActualBase64Length, iTargetBase64Length); return ER; } psSignature->pucBase64Signature[iActualBase64Length] = 0; psSignature->iBase64Length = iActualBase64Length; return ER_OK; } /*- *********************************************************************** * * DsvFreeCertNode * *********************************************************************** */ void DsvFreeCertNode(DSV_CERT_NODE *psCertNode) { if (psCertNode != NULL) { if (psCertNode->pcFile != NULL) { free(psCertNode->pcFile); } if (psCertNode->pcCommonName != NULL) { free(psCertNode->pcCommonName); } if (psCertNode->psPublicKey != NULL) { EVP_PKEY_free(psCertNode->psPublicKey); } if (psCertNode->psX509Cert != NULL) { X509_free(psCertNode->psX509Cert); } free(psCertNode); psCertNode = NULL; } } /*- *********************************************************************** * * DsvFreeSignature * *********************************************************************** */ void DsvFreeSignature(DSV_SIGNATURE *psSignature) { if (psSignature != NULL) { if (psSignature->pucBase64Signature != NULL) { free(psSignature->pucBase64Signature); } if (psSignature->pucBinarySignature != NULL) { free(psSignature->pucBinarySignature); } #if (OPENSSL_VERSION_NUMBER >= 0x00090700fL) /* EVP_MD_CTX_cleanup was added in OpenSSL 0.9.7. */ EVP_MD_CTX_cleanup(&psSignature->sMdRsaCtx); EVP_MD_CTX_cleanup(&psSignature->sMdDsaCtx); #endif free(psSignature); } } /*- *********************************************************************** * * DsvGenerateSeed * *********************************************************************** */ unsigned char * DsvGenerateSeed(unsigned char *pucSeed, unsigned long iLength) { unsigned long i; unsigned long j; unsigned long ulLRS32b; #ifdef WIN32 ulLRS32b = (unsigned long) GetTickCount() ^ (unsigned long) time(NULL); #else ulLRS32b = (((unsigned long) getpid()) << 16) ^ (unsigned long) time(NULL); #endif for (i = 0; i < iLength; i++) { for (j = 0, pucSeed[i] = 0; j < 8; j++) { pucSeed[i] |= (ulLRS32b & 1) << j; ulLRS32b = ((((ulLRS32b >> 7) ^ (ulLRS32b >> 6) ^ (ulLRS32b >> 2) ^ (ulLRS32b >> 0)) & 1) << 31) | (ulLRS32b >> 1); } } return pucSeed; } /*- *********************************************************************** * * DsvGetEnvValue * *********************************************************************** */ char * DsvGetEnvValue(char *pcName) { #ifdef WIN32 char *pcValue = NULL; DWORD dwCount = 0; #define DSV_MAX_ENV_SIZE 32767 /* Source = MSDN documentation for GetEnvironmentVariable() */ pcValue = calloc(DSV_MAX_ENV_SIZE, 1); if (pcValue == NULL) { return NULL; } dwCount = GetEnvironmentVariable(TEXT(pcName), pcValue, DSV_MAX_ENV_SIZE); return (dwCount == 0 || dwCount > DSV_MAX_ENV_SIZE) ? NULL : pcValue; #else return getenv(pcName); #endif } /*- *********************************************************************** * * DsvLoadPrivateKey * *********************************************************************** */ EVP_PKEY * DsvLoadPrivateKey(char *pcFile, char *pcError) { const char acRoutine[] = "DsvLoadPrivateKey()"; char *pcPassPhrase = NULL; EVP_PKEY *psPKey = NULL; FILE *pFile = NULL; int iLength = 0; /*- ********************************************************************* * * Check the environment for a passphrase. Enforce a hard size limit * to help prevent environment-based abuses. * ********************************************************************* */ #ifdef WIN32 pcPassPhrase = DsvGetEnvValue("DSV_PASSPHRASE"); #else pcPassPhrase = DsvGetEnvValue("DSV_PASSPHRASE"); #endif if (pcPassPhrase != NULL) { iLength = strlen(pcPassPhrase); if (iLength < 1 || iLength > DSV_MAX_PASSPHRASE_LENGTH) { pcPassPhrase = NULL; } } /*- ********************************************************************* * * Open and process the specified private key. * ********************************************************************* */ pFile = fopen(pcFile, "rb"); if (pFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); return NULL; } psPKey = PEM_read_PrivateKey(pFile, NULL, NULL, pcPassPhrase); if (psPKey == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: PEM_read_PrivateKey(): Error reading private key from %s: %s", acRoutine, pcFile, ERR_error_string(ERR_get_error(), NULL)); fclose(pFile); return NULL; } fclose(pFile); return psPKey; } /*- *********************************************************************** * * DsvLoadPublicKey * *********************************************************************** */ DSV_CERT_NODE * DsvLoadPublicKey(char *pcFile, char *pcError) { const char acRoutine[] = "DsvLoadPublicKey()"; char acLocalError[MESSAGE_SIZE] = ""; DSV_CERT_NODE *psCertNode = NULL; FILE *pFile = NULL; int iLength = 0; /*- ********************************************************************* * * Allocate a new certificate node. * ********************************************************************* */ psCertNode = DsvNewCertNode(pcFile, acLocalError); if (psCertNode == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError); return NULL; } /*- ********************************************************************* * * Open and process the specified certificate. * ********************************************************************* */ pFile = fopen(pcFile, "rb"); if (pFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); DsvFreeCertNode(psCertNode); return NULL; } psCertNode->psX509Cert = PEM_read_X509(pFile, NULL, NULL, NULL); if (psCertNode->psX509Cert == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: PEM_read_X509(): File = [%s], %s", acRoutine, pcFile, ERR_error_string(ERR_get_error(), NULL)); DsvFreeCertNode(psCertNode); fclose(pFile); return NULL; } fclose(pFile); /*- ********************************************************************* * * Extract the public key from the certificate. * ********************************************************************* */ psCertNode->psPublicKey = X509_get_pubkey(psCertNode->psX509Cert); if (psCertNode->psPublicKey == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: X509_get_pubkey(): File = [%s], %s", acRoutine, pcFile, ERR_error_string(ERR_get_error(), NULL)); DsvFreeCertNode(psCertNode); return NULL; } /*- ********************************************************************* * * Verify that the key type is valid. * ********************************************************************* */ switch (EVP_PKEY_type(psCertNode->psPublicKey->type)) { case EVP_PKEY_RSA: case EVP_PKEY_DSA: break; default: snprintf(pcError, MESSAGE_SIZE, "%s: EVP_PKEY_type(): File = [%s], File contains an unknown or unsupported public key type.", acRoutine, pcFile); DsvFreeCertNode(psCertNode); return NULL; break; } /*- ********************************************************************* * * Extract the common name. * ********************************************************************* */ iLength = X509_NAME_get_text_by_NID(X509_get_subject_name(psCertNode->psX509Cert), NID_commonName, NULL, 0); iLength = (iLength < 0) ? 1 : iLength + 1; psCertNode->pcCommonName = (char *) calloc(iLength, 1); if (psCertNode->pcCommonName == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); DsvFreeCertNode(psCertNode); return NULL; } if (iLength == 1) { psCertNode->pcCommonName[0] = 0; /* No common name was found or there was an error. */ } else { X509_NAME_get_text_by_NID(X509_get_subject_name(psCertNode->psX509Cert), NID_commonName, psCertNode->pcCommonName, iLength); } return psCertNode; } /*- *********************************************************************** * * DsvLoadPublicKeys * *********************************************************************** */ DSV_CERT_NODE * DsvLoadPublicKeys(char *pcTree, char *pcError) { const char acRoutine[] = "DsvLoadPublicKeys()"; char acLocalError[MESSAGE_SIZE] = ""; char *pcPath = NULL; DSV_CERT_NODE *psCertList = NULL; DSV_CERT_NODE *psCertNode = NULL; int iLength = 0; int iSize = 0; #ifdef WIN32 BOOL bFinished = FALSE; char acSearchPath[DSV_MAX_PATH] = { 0 }; HANDLE hSearch; WIN32_FIND_DATA sFindData = { 0 }; #else struct stat sStatEntry = { 0 }; DIR *psTree = NULL; struct dirent *psDirEntry = NULL; #endif /*- ********************************************************************* * * Chop trailing slashes, and finalize the tree's length. * ********************************************************************* */ iLength = DsvChopTrailingSlashes(pcTree, DSV_CHOP); #ifdef WIN32 /*- ********************************************************************* * * Append "\*" to the specified tree to create a search path. * ********************************************************************* */ if (iLength < 1 || iLength > DSV_MAX_PATH - 3) { snprintf(pcError, MESSAGE_SIZE, "%s: SearchPath = [%s\\*], Length (%d) falls outside allowed limits.", acRoutine, pcTree, iLength); return NULL; } snprintf(acSearchPath, DSV_MAX_PATH, "%s\\*", pcTree); /*- ********************************************************************* * * Acquire a search handle for the specified tree. * ********************************************************************* */ hSearch = FindFirstFile(acSearchPath, &sFindData); if (hSearch == INVALID_HANDLE_VALUE) { /* FIXME Replace GetLastError() with a text-based message. */ snprintf(pcError, MESSAGE_SIZE, "%s: FindFirstFile(): Unable to open %s: %d", acRoutine, pcTree, GetLastError()); return NULL; } /*- ********************************************************************* * * Attempt to load all certificate files in the specified tree. Add * a node to the certificate list for each successful load. Because * there can be many certificates in the specified tree, it is more * important to keep going than it is to abort on a load error. * ********************************************************************* */ bFinished = FALSE; while (!bFinished) { if (!(sFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) /* If it's not a dir, then its assumed to be a file. */ { iSize = iLength + strlen(sFindData.cFileName) + 2; pcPath = malloc(iSize); snprintf(pcPath, iSize, "%s\\%s", pcTree, sFindData.cFileName); psCertNode = DsvLoadPublicKey(pcPath, acLocalError); if (psCertNode != NULL) { psCertList = DsvAddCertNode(psCertList, psCertNode); } free(pcPath); } if (!FindNextFile(hSearch, &sFindData)) { if (GetLastError() == ERROR_NO_MORE_FILES) { bFinished = TRUE; } } } FindClose(hSearch); #else /*- ********************************************************************* * * Acquire a directory handle for the specified tree. * ********************************************************************* */ if ((psTree = opendir(pcTree)) == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: opendir(): Unable to open %s: %s", acRoutine, pcTree, strerror(errno)); return NULL; } /*- ********************************************************************* * * Attempt to load all certificate files in the specified tree. Add * a node to the certificate list for each successful load. Because * there can be many certificates in the specified tree, it is more * important to keep going than it is to abort on a load error. * ********************************************************************* */ while ((psDirEntry = readdir(psTree)) != NULL) { iSize = iLength + strlen(psDirEntry->d_name) + 2; pcPath = malloc(iSize); snprintf(pcPath, iSize, "%s/%s", pcTree, psDirEntry->d_name); if (stat(pcPath, &sStatEntry) == ER_OK && (sStatEntry.st_mode & S_IFMT) == S_IFREG) { psCertNode = DsvLoadPublicKey(pcPath, acLocalError); if (psCertNode != NULL) { psCertList = DsvAddCertNode(psCertList, psCertNode); } } free(pcPath); } closedir(psTree); #endif if (psCertList == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: The certificate list is empty. At least one valid certificate is required.", acRoutine); return NULL; } return psCertList; } /*- *********************************************************************** * * DsvLoadSignature * *********************************************************************** */ DSV_SIGNATURE * DsvLoadSignature(char *pcFile, char *pcError) { const char acRoutine[] = "DsvLoadSignature()"; char acLocalError[MESSAGE_SIZE] = ""; DSV_SIGNATURE *psSignature = NULL; FILE *pFile = NULL; int iError = 0; int iNRead = 0; int iLength = 0; struct stat sStatEntry = { 0 }; /*- ********************************************************************* * * Allocate a new signature. * ********************************************************************* */ psSignature = DsvNewSignature(acLocalError); if (psSignature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError); return NULL; } /*- ********************************************************************* * * Open the signature file in binary mode, determine its size, and * abort if it's bigger than the size of the read buffer, which is * currently much bigger than a signature can be. * ********************************************************************* */ pFile = fopen(pcFile, "rb"); if (pFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); DsvFreeSignature(psSignature); return NULL; } if (fstat(fileno(pFile), &sStatEntry) == ER) { snprintf(pcError, MESSAGE_SIZE, "%s: fstat(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); DsvFreeSignature(psSignature); fclose(pFile); return NULL; } iLength = (int) sStatEntry.st_size; if (iLength < 0 || iLength > DSV_READ_SIZE) { snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Size (%d) falls outside allowed limits.", acRoutine, pcFile, iLength); DsvFreeSignature(psSignature); fclose(pFile); return NULL; } /*- ********************************************************************* * * Allocate and clear memory to hold the encoded signature. * ********************************************************************* */ psSignature->pucBase64Signature = (unsigned char *) calloc(iLength + 1, 1); if (psSignature->pucBase64Signature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); DsvFreeSignature(psSignature); fclose(pFile); return NULL; } /*- ********************************************************************* * * Read the encoded signature. Then, verify that the number of bytes * we got was the number we requested, and terminate the string. * ********************************************************************* */ iNRead = fread(psSignature->pucBase64Signature, 1, iLength, pFile); if (ferror(pFile)) { snprintf(pcError, MESSAGE_SIZE, "%s: fread(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); DsvFreeSignature(psSignature); fclose(pFile); return NULL; } fclose(pFile); if (iNRead != iLength) { snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Expected %d bytes, but got %d instead.", acRoutine, pcFile, iLength, iNRead); DsvFreeSignature(psSignature); return NULL; } psSignature->pucBase64Signature[iLength] = 0; /*- ********************************************************************* * * Chop trailing white space, and finalize the signature's length. * ********************************************************************* */ psSignature->iBase64Length = DsvChopTrailingWhitespace((char *) psSignature->pucBase64Signature, DSV_CHOP); /*- ********************************************************************* * * Decode the signature (base64 --> binary). * ********************************************************************* */ iError = DsvDecodeSignature(psSignature, acLocalError); if (iError != ER_OK) { snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError); DsvFreeSignature(psSignature); return NULL; } return psSignature; } /*- *********************************************************************** * * DsvNewCertNode * *********************************************************************** */ DSV_CERT_NODE * DsvNewCertNode(char *pcFile, char *pcError) { const char acRoutine[] = "DsvNewCertNode()"; DSV_CERT_NODE *psCertNode = NULL; int iLength = 0; psCertNode = (DSV_CERT_NODE *) calloc(sizeof(DSV_CERT_NODE), 1); if (psCertNode == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); return NULL; } iLength = strlen(pcFile); psCertNode->pcFile = (char *) calloc(iLength + 1, 1); if (psCertNode->pcFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); DsvFreeCertNode(psCertNode); return NULL; } strncpy(psCertNode->pcFile, pcFile, iLength + 1); return psCertNode; } /*- *********************************************************************** * * DsvNewSignature * *********************************************************************** */ DSV_SIGNATURE * DsvNewSignature(char *pcError) { const char acRoutine[] = "DsvNewSignature()"; DSV_SIGNATURE *psSignature = NULL; psSignature = (DSV_SIGNATURE *) calloc(sizeof(DSV_SIGNATURE), 1); if (psSignature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); return NULL; } return psSignature; } /*- *********************************************************************** * * DsvProcessPayloadFile * *********************************************************************** */ int DsvProcessPayloadFile(char *pcFile, DSV_SIGNATURE *psSignature, char *pcError) { const char acRoutine[] = "DsvProcessPayloadFile()"; char acLocalError[MESSAGE_SIZE] = ""; FILE *pFile = NULL; int iError = 0; pFile = fopen(pcFile, "rb"); if (pFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); return ER; } iError = DsvProcessPayloadStream(pFile, psSignature, acLocalError); if (iError != ER_OK) { snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], %s", acRoutine, pcFile, acLocalError); fclose(pFile); return ER; } fclose(pFile); return ER_OK; } /*- *********************************************************************** * * DsvProcessPayloadStream * *********************************************************************** */ int DsvProcessPayloadStream(FILE *pFile, DSV_SIGNATURE *psSignature, char *pcError) { const char acRoutine[] = "DsvProcessPayloadStream()"; unsigned char aucData[DSV_READ_SIZE] = ""; int iNRead = 0; /*- ********************************************************************* * * We hedge our bets by calculating digests for both types of keys. * This amounts to more work here, but it's still better than hashing * the file once for each key in the certificates directory. The only * exception to that is when there is only one certificate to check. * ********************************************************************* */ EVP_VerifyInit(&psSignature->sMdRsaCtx, EVP_sha1()); EVP_VerifyInit(&psSignature->sMdDsaCtx, EVP_dss1()); while ((iNRead = fread(aucData, 1, DSV_READ_SIZE, pFile)) > 0) { EVP_VerifyUpdate(&psSignature->sMdRsaCtx, aucData, iNRead); EVP_VerifyUpdate(&psSignature->sMdDsaCtx, aucData, iNRead); } if (ferror(pFile)) { snprintf(pcError, MESSAGE_SIZE, "%s: fread(): %s", acRoutine, strerror(errno)); return ER; } return ER_OK; } /*- *********************************************************************** * * DsvSignPayload * *********************************************************************** */ int DsvSignPayload(char *pcFile, EVP_PKEY *psPrivateKey, DSV_SIGNATURE *psSignature, char *pcError) { const char acRoutine[] = "DsvSignPayload()"; char acLocalError[MESSAGE_SIZE] = ""; char acData[DSV_READ_SIZE] = ""; EVP_MD_CTX sMdCtx = { 0 }; EVP_MD *psMdType = NULL; FILE *pFile = NULL; int iError = 0; int iLength = 0; /*- ********************************************************************* * * Obtain and check the private key type. * ********************************************************************* */ switch (EVP_PKEY_type(psPrivateKey->type)) { case EVP_PKEY_RSA: psMdType = (EVP_MD *) EVP_sha1(); break; case EVP_PKEY_DSA: psMdType = (EVP_MD *) EVP_dss1(); break; default: snprintf(pcError, MESSAGE_SIZE, "%s: EVP_PKEY_type(): Unknown or unsupported private key type.", acRoutine); return ER; break; } /*- ********************************************************************* * * Allocate and clear memory for the signature. * ********************************************************************* */ psSignature->pucBinarySignature = (unsigned char *) calloc(EVP_PKEY_size(psPrivateKey), 1); if (psSignature->pucBinarySignature == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); return ER; } /*- ********************************************************************* * * Open the payload file. * ********************************************************************* */ pFile = fopen(pcFile, "rb"); if (pFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); return ER; } /*- ********************************************************************* * * Initialize the signature. * ********************************************************************* */ EVP_SignInit(&sMdCtx, psMdType); /*- ********************************************************************* * * Loop over the payload data updating the signature as we go. * ********************************************************************* */ while ((iLength = fread(acData, 1, DSV_READ_SIZE, pFile)) > 0) { /*- ******************************************************************* * * Note: EVP_SignUpdate(a,b,c) is defined as EVP_DigestUpdate(a,b,c) * in evp.h. Prior to OpenSSL 0.9.7, EVP_SignUpdate() would return a * void. Now, it returns an int. Therefore, if the code is compiled * against an old version of OpenSSL, both cases must be handled. At * some point, we should just require a minimum version of OpenSSL. * * Here's the relevant message from the CHANGES file for the 0.9.7 * release: * * *) Modify EVP_Digest*() routines so they now return values. * Although the internal software routines can never fail * additional hardware versions might. * [Steve Henson] * ******************************************************************* */ #if (OPENSSL_VERSION_NUMBER >= 0x00090700fL) iError = EVP_SignUpdate(&sMdCtx, acData, iLength); if (iError != 1) { snprintf(pcError, MESSAGE_SIZE, "%s: EVP_SignUpdate(): %s", acRoutine, ERR_error_string(ERR_get_error(), NULL)); return ER; } #else EVP_SignUpdate(&sMdCtx, acData, iLength); #endif } if (ferror(pFile)) { snprintf(pcError, MESSAGE_SIZE, "%s: fread(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); return ER; } fclose(pFile); /*- ********************************************************************* * * Finalize the signature. * ********************************************************************* */ iError = EVP_SignFinal(&sMdCtx, psSignature->pucBinarySignature, (unsigned *) &psSignature->iBinaryLength, psPrivateKey); if (iError != 1) { snprintf(pcError, MESSAGE_SIZE, "%s: EVP_SignFinal(): %s", acRoutine, ERR_error_string(ERR_get_error(), NULL)); return ER; } /*- ********************************************************************* * * Encode the signature. * ********************************************************************* */ iError = DsvEncodeSignature(psSignature, acLocalError); if (iError != ER_OK) { snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError); return ER; } return ER_OK; } /*- *********************************************************************** * * DsvVerifySignature * *********************************************************************** */ int DsvVerifySignature(DSV_CERT_NODE *psCertList, int iVerifyMode, DSV_SIGNATURE *psSignature, char *pcError) { const char acRoutine[] = "DsvVerifySignature()"; int iError = 0; DSV_CERT_NODE *psTemp = NULL; /*- ********************************************************************* * * Finalize the signature for each key in the certificate list, and * return a pass/fail result. However, if the caller specified file * verify mode, then we are required to check for and report errors * as well. This makes it possible for the caller to debug/verify a * single certificate. * ********************************************************************* */ for (psTemp = psCertList; psTemp != NULL; psTemp = psTemp->psNext) { if (EVP_PKEY_type(psTemp->psPublicKey->type) == EVP_PKEY_RSA) { iError = EVP_VerifyFinal(&psSignature->sMdRsaCtx, psSignature->pucBinarySignature, psSignature->iBinaryLength, psTemp->psPublicKey); } else { iError = EVP_VerifyFinal(&psSignature->sMdDsaCtx, psSignature->pucBinarySignature, psSignature->iBinaryLength, psTemp->psPublicKey); } if (iError == 1) { psSignature->pcCommonName = psTemp->pcCommonName; return DSV_SIGNATURE_VERIFICATION_PASSED; } else { if (iVerifyMode == DSV_VERIFY_VIA_FILE && iError == -1) { snprintf(pcError, MESSAGE_SIZE, "%s: EVP_VerifyFinal(): %s", acRoutine, ERR_error_string(ERR_get_error(), NULL)); return DSV_SIGNATURE_VERIFICATION_ERROR; } } } return DSV_SIGNATURE_VERIFICATION_FAILED; } /*- *********************************************************************** * * DsvWriteSignature * *********************************************************************** */ int DsvWriteSignature(char *pcFile, char *pcExtension, char *pcSignature, char *pcError) { const char acRoutine[] = "DsvWriteSignature()"; char *pcSigFile = NULL; FILE *pFile = NULL; int iLength = 0; iLength = strlen(pcFile) + 1 + strlen(pcExtension) + 1; if (iLength < 1 || iLength > DSV_MAX_PATH - 1) { snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s.%s], Length (%d) falls outside allowed limits.", acRoutine, pcFile, pcExtension, iLength); return ER; } pcSigFile = (char *) calloc(iLength, 1); if (pcSigFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno)); return ER; } snprintf(pcSigFile, iLength, "%s.%s", pcFile, pcExtension); pFile = fopen(pcSigFile, "wb"); if (pFile == NULL) { snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); free(pcSigFile); return ER; } fprintf(pFile, "%s\n", pcSignature); if (ferror(pFile)) { snprintf(pcError, MESSAGE_SIZE, "%s: fprintf(): File = [%s], %s", acRoutine, pcFile, strerror(errno)); free(pcSigFile); fclose(pFile); return ER; } free(pcSigFile); fclose(pFile); return ER_OK; }