Mercurial > trustbridge
view common/binverify.c @ 1280:6e454e24b8af
MErged.
author | Emanuel Schuetze <emanuel@intevation.de> |
---|---|
date | Mon, 29 Sep 2014 11:43:23 +0200 |
parents | 3cd8dd706aaa |
children | 28885e8c891f |
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik * Software engineering by Intevation GmbH * * This file is Free Software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include "binverify.h" #include "strhelp.h" #include "logging.h" #include "listutil.h" #ifdef RELEASE_BUILD #include "pubkey-release.h" #else #include "pubkey-test.h" #endif bin_verify_result verify_binary(const char *filename, size_t name_len) { if (!filename || !name_len) { bin_verify_result retval; retval.fptr = NULL; retval.result = VerifyUnknownError; return retval; } #ifdef WIN32 return verify_binary_win(filename, name_len); #else return verify_binary_linux(filename, name_len); #endif } #ifdef WIN32 #include <polarssl/x509_crt.h> #include <windows.h> #include <wincrypt.h> #include <wintrust.h> #include <stdio.h> /** @brief Check if the certificate @a pCCertContext is pinned * * Compares the certificate's binary data (public key and attributes) * with each other to validate that the certificate pCCertContext has * exactly the same data as the builtin public certificate. * * @param[in] pCCertContext pointer to the certificate to check * * @returns true if the certificate matches, false otherwise. */ static bool check_certificate (PCCERT_CONTEXT pCCertContext) { x509_crt codesign_cert; int ret = 0; DWORD dwI = 0; bool retval = false; if (pCCertContext == NULL) { ERRORPRINTF ("Invalid call to check_certificate"); return false; } x509_crt_init(&codesign_cert); /* Parse the pinned certificate */ ret = x509_crt_parse(&codesign_cert, public_key_codesign_pem, public_key_codesign_pem_size); if (ret != 0) { ERRORPRINTF ("x509_crt_parse failed with -0x%04x\n\n", -ret); goto done; } if (codesign_cert.raw.len != pCCertContext->cbCertEncoded || codesign_cert.raw.len <= 0) { ERRORPRINTF ("Certificate size mismatch"); goto done; } /* Check that the certificate is exactly the same as the pinned one */ for (dwI = 0; dwI < pCCertContext->cbCertEncoded; dwI++) { if (pCCertContext->pbCertEncoded[dwI] != codesign_cert.raw.p[dwI]) { ERRORPRINTF ("Certificate content mismatch"); goto done; } } retval = true; done: x509_crt_free(&codesign_cert); return retval; } bin_verify_result verify_binary_win(const char *filename, size_t name_len) { bin_verify_result retval; WCHAR *filenameW = NULL; BOOL result = FALSE; DWORD dwEncoding = 0, dwContentType = 0, dwFormatType = 0, dwSignerInfoSize = 0; HCERTSTORE hStore = NULL; HCRYPTMSG hMsg = NULL; PCERT_INFO pSignerCert = NULL; PCCERT_CONTEXT pSignerCertContext = NULL; FILE *fptr = NULL; size_t data_size = 0; char *data = NULL; int ret = -1; CRYPT_INTEGER_BLOB blob; retval.result = VerifyUnknownError; retval.fptr = NULL; if (!filename || name_len > MAX_PATH || strlen(filename) != name_len) { ERRORPRINTF ("Invalid parameters\n"); return retval; } ret = read_file(filename, &data, &data_size, MAX_VALID_BIN_SIZE, &fptr); if (ret != 0) { ERRORPRINTF ("Read file failed with error: %i\n", ret); retval.result = VerifyReadFailed; return retval; } blob.cbData = (DWORD) data_size; blob.pbData = (PBYTE) data; result = CryptQueryObject (CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding, &dwContentType, &dwFormatType, &hStore, &hMsg, NULL); if (!result || !hMsg) { PRINTLASTERROR ("Failed to query crypto object"); retval.result = VerifyReadFailed; goto done; } /* Get the cert info so that we can look up the signer in the store later */ if (CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, NULL, &dwSignerInfoSize) && dwSignerInfoSize > 0) { pSignerCert = xmalloc (dwSignerInfoSize); } else { ERRORPRINTF ("Failed to get signer cert size."); retval.result = VerifyUnknownError; goto done; } if (!(CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, pSignerCert, &dwSignerInfoSize))) { ERRORPRINTF ("Failed to get signer cert."); retval.result = VerifyUnknownError; goto done; } pSignerCertContext = CertGetSubjectCertificateFromStore( hStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, pSignerCert); if (!pSignerCertContext) { ERRORPRINTF ("Failed to find signer cert in store."); retval.result = VerifyUnknownError; goto done; } /* Verify that the signature is actually valid */ if(!CryptMsgControl(hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE, pSignerCertContext->pCertInfo)) { ERRORPRINTF ("The signature is invalid. \n"); retval.result = VerifyInvalidSignature; syslog_error_printf ("Software update embedded signature is invalid."); goto done; } if(check_certificate(pSignerCertContext)) { DEBUGPRINTF ("Valid signature with pinned certificate."); retval.result = VerifyValid; retval.fptr = fptr; goto done; } else { ERRORPRINTF ("Certificate mismatch. \n"); retval.result = VerifyInvalidCertificate; syslog_error_printf ("Software update embedded signature " "created with wrong certificate."); goto done; } done: xfree(data); xfree(filenameW); xfree(pSignerCert); if (retval.result != VerifyValid) { fclose(fptr); } if(pSignerCertContext) { CertFreeCertificateContext(pSignerCertContext); } if (hStore) { CertCloseStore(hStore, 0); } if (hMsg) { CryptMsgClose(hMsg); } return retval; } #else /* WIN32 */ #ifndef __clang__ #pragma GCC diagnostic ignored "-Wconversion" #endif /* Polarssl mh.h contains a conversion which gcc warns about */ #include <polarssl/pk.h> #include <polarssl/base64.h> #include <polarssl/sha256.h> #include <polarssl/error.h> #include <polarssl/x509_crt.h> #ifndef __clang__ #pragma GCC diagnostic pop #endif bin_verify_result verify_binary_linux(const char *filename, size_t name_len) { int ret = -1; const size_t sig_b64_size = TRUSTBRIDGE_RSA_KEY_SIZE / 8 * 4 / 3; char *data = NULL, signature_b64[sig_b64_size + 1]; size_t data_size = 0, sig_size = TRUSTBRIDGE_RSA_KEY_SIZE / 8; unsigned char signature[sig_size], hash[32]; FILE *fptr = NULL; bin_verify_result retval; retval.result = VerifyUnknownError; retval.fptr = NULL; x509_crt codesign_cert; if (strnlen(filename, name_len + 1) != name_len || name_len == 0) { ERRORPRINTF ("Invalid call to verify_binary_linux\n"); retval.result = VerifyUnknownError; return retval; } ret = read_file(filename, &data, &data_size, MAX_VALID_BIN_SIZE, &fptr); if (ret != 0) { ERRORPRINTF ("Read file failed with error: %i\n", ret); retval.result = VerifyReadFailed; return retval; } /* Fetch the signature from the end of data */ if (data_size < sig_b64_size + 5) { ERRORPRINTF ("File to small to contain a signature.\n"); retval.result = VerifyInvalidSignature; goto done; } if (data[data_size - sig_b64_size - 2] != ':' || data[data_size - sig_b64_size - 3] != 'S' || data[data_size - sig_b64_size - 4] != '\n'|| data[data_size - sig_b64_size - 5] != '\r') { ERRORPRINTF ("Failed to find valid signature line.\n"); retval.result = VerifyInvalidSignature; goto done; } strncpy(signature_b64, data + (data_size - sig_b64_size - 1), sig_b64_size); signature_b64[sig_b64_size] = '\0'; ret = base64_decode(signature, &sig_size, (unsigned char *)signature_b64, sig_b64_size); if (ret != 0 || sig_size != TRUSTBRIDGE_RSA_KEY_SIZE / 8) { ERRORPRINTF ("Base 64 decode failed with error: %i\n", ret); goto done; } /* Hash is calculated over the data without the signature at the end. */ sha256((unsigned char *)data, data_size - sig_b64_size - 5, hash, 0); x509_crt_init(&codesign_cert); /* Parse the pinned certificate */ ret = x509_crt_parse(&codesign_cert, public_key_codesign_pem, public_key_codesign_pem_size); if (ret != 0) { char errbuf[1020]; polarssl_strerror(ret, errbuf, 1020); errbuf[1019] = '\0'; /* Just to be sure */ ERRORPRINTF ("x509_crt_parse failed with -0x%04x\n%s\n", -ret, errbuf); x509_crt_free(&codesign_cert); retval.result = VerifyUnknownError; goto done; } ret = pk_verify(&codesign_cert.pk, POLARSSL_MD_SHA256, hash, 0, signature, sig_size); if (ret != 0) { char errbuf[1020]; polarssl_strerror(ret, errbuf, 1020); errbuf[1019] = '\0'; /* Just to be sure */ ERRORPRINTF ("pk_verify failed with -0x%04x\n %s\n", -ret, errbuf); x509_crt_free(&codesign_cert); retval.result = VerifyInvalidSignature; goto done; } x509_crt_free(&codesign_cert); retval.result = VerifyValid; retval.fptr = fptr; done: if (retval.result != VerifyValid) { if (fptr) { fclose(fptr); } } xfree (data); return retval; } #endif /* WIN32 */