aheinecke@579: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@579: * Software engineering by Intevation GmbH aheinecke@579: * aheinecke@579: * This file is Free Software under the GNU GPL (v>=2) aheinecke@579: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@579: * See LICENSE.txt for details. aheinecke@579: */ aheinecke@579: aheinecke@579: #include "binverify.h" aheinecke@579: aheinecke@579: #include "strhelp.h" aheinecke@579: #include "logging.h" aheinecke@579: andre@629: #ifdef RELEASE_BUILD andre@629: #include "pubkey-release.h" andre@629: #else andre@629: #include "pubkey-test.h" andre@629: #endif andre@629: aheinecke@586: bin_verify_result aheinecke@586: verify_binary(const char *filename, size_t name_len) { aheinecke@586: #ifdef WIN32 aheinecke@586: return verify_binary_win(filename, name_len); aheinecke@586: #else aheinecke@586: /* TODO */ aheinecke@590: if (filename && name_len) aheinecke@590: return VerifyValid; aheinecke@590: return VerifyUnknownError; aheinecke@586: #endif aheinecke@586: } aheinecke@586: aheinecke@579: #ifdef WIN32 aheinecke@579: andre@629: #include andre@629: aheinecke@579: #include aheinecke@579: #include aheinecke@579: #include aheinecke@579: #include aheinecke@579: andre@629: andre@629: /** @brief Check if the certificate @a pCCertContext is pinned andre@629: * andre@629: * Compares the certificate's binary data (public key and attributes) andre@629: * with each other to validate that the certificate pCCertContext has andre@629: * exactly the same data as the builtin public certificate. andre@629: * andre@629: * @param[in] pCCertContext pointer to the certificate to check andre@629: * andre@629: * @returns true if the certificate matches, false otherwise. andre@629: */ andre@629: static bool andre@629: check_certificate (PCCERT_CONTEXT pCCertContext) andre@629: { andre@629: x509_crt codesign_cert; andre@629: int ret = 0; andre@629: DWORD dwI = 0; andre@629: bool retval = false; andre@629: andre@629: if (pCCertContext == NULL) andre@629: { andre@629: ERRORPRINTF ("Invalid call to check_certificate"); andre@629: return false; andre@629: } andre@629: andre@629: x509_crt_init(&codesign_cert); andre@629: andre@629: /* Parse the pinned certificate */ andre@629: ret = x509_crt_parse(&codesign_cert, andre@629: public_key_codesign_pem, andre@629: public_key_codesign_pem_size); andre@629: if (ret != 0) andre@629: { andre@629: ERRORPRINTF ("x509_crt_parse failed with -0x%04x\n\n", -ret); andre@629: goto done; andre@629: } andre@629: andre@629: if (codesign_cert.raw.len != pCCertContext->cbCertEncoded || andre@629: codesign_cert.raw.len <= 0) andre@629: { andre@629: ERRORPRINTF ("Certificate size mismatch"); andre@629: goto done; andre@629: } andre@629: andre@629: /* Check that the certificate is exactly the same as the pinned one */ andre@629: for (dwI = 0; dwI < pCCertContext->cbCertEncoded; dwI++) andre@629: { andre@629: if (pCCertContext->pbCertEncoded[dwI] != codesign_cert.raw.p[dwI]) andre@629: { andre@629: ERRORPRINTF ("Certificate content mismatch"); andre@629: goto done; andre@629: } andre@629: } andre@629: andre@629: retval = true; andre@629: andre@629: done: andre@629: x509_crt_free(&codesign_cert); andre@629: return retval; andre@629: } andre@629: aheinecke@579: bin_verify_result aheinecke@579: verify_binary_win(const char *filename, size_t name_len) { aheinecke@586: bin_verify_result retval = VerifyUnknownError; aheinecke@579: WCHAR *filenameW = NULL; aheinecke@579: BOOL result = FALSE; aheinecke@579: DWORD dwEncoding = 0, aheinecke@579: dwContentType = 0, aheinecke@579: dwFormatType = 0, aheinecke@579: dwSignerInfoSize = 0; aheinecke@579: HCERTSTORE hStore = NULL; aheinecke@579: HCRYPTMSG hMsg = NULL; aheinecke@579: PCERT_INFO pSignerCert = NULL; aheinecke@579: PCCERT_CONTEXT pSignerCertContext = NULL; aheinecke@579: aheinecke@579: if (!filename || name_len > MAX_PATH || strlen(filename) != name_len) aheinecke@579: { aheinecke@579: ERRORPRINTF ("Invalid parameters\n"); aheinecke@586: return VerifyUnknownError; aheinecke@579: } aheinecke@579: aheinecke@591: filenameW = utf8_to_wchar(filename, name_len); aheinecke@579: aheinecke@579: result = CryptQueryObject (CERT_QUERY_OBJECT_FILE, aheinecke@579: filenameW, aheinecke@579: CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, aheinecke@579: CERT_QUERY_FORMAT_FLAG_BINARY, aheinecke@579: 0, aheinecke@579: &dwEncoding, aheinecke@579: &dwContentType, aheinecke@579: &dwFormatType, aheinecke@579: &hStore, aheinecke@579: &hMsg, aheinecke@579: NULL); aheinecke@579: aheinecke@579: if (!result || !hMsg) aheinecke@579: { aheinecke@579: PRINTLASTERROR ("Failed to query crypto object"); aheinecke@586: retval = VerifyReadFailed; aheinecke@579: goto done; aheinecke@579: } aheinecke@579: aheinecke@579: /* Get the cert info so that we can look up the signer in the store later */ aheinecke@579: if (CryptMsgGetParam(hMsg, aheinecke@579: CMSG_SIGNER_CERT_INFO_PARAM, aheinecke@579: 0, aheinecke@579: NULL, aheinecke@579: &dwSignerInfoSize) && dwSignerInfoSize > 0) aheinecke@579: { aheinecke@579: pSignerCert = xmalloc (dwSignerInfoSize); aheinecke@579: } aheinecke@579: else aheinecke@579: { aheinecke@579: ERRORPRINTF ("Failed to get signer cert size."); aheinecke@586: retval = VerifyUnknownError; aheinecke@579: goto done; aheinecke@579: } aheinecke@579: aheinecke@579: if (!(CryptMsgGetParam(hMsg, aheinecke@579: CMSG_SIGNER_CERT_INFO_PARAM, aheinecke@579: 0, aheinecke@579: pSignerCert, aheinecke@579: &dwSignerInfoSize))) aheinecke@579: { aheinecke@579: ERRORPRINTF ("Failed to get signer cert."); aheinecke@586: retval = VerifyUnknownError; aheinecke@579: goto done; aheinecke@579: } aheinecke@579: aheinecke@579: pSignerCertContext = CertGetSubjectCertificateFromStore( aheinecke@579: hStore, aheinecke@579: PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, aheinecke@579: pSignerCert); aheinecke@579: aheinecke@579: if (!pSignerCertContext) aheinecke@579: { aheinecke@579: ERRORPRINTF ("Failed to find signer cert in store."); aheinecke@586: retval = VerifyUnknownError; aheinecke@579: goto done; aheinecke@579: } aheinecke@579: aheinecke@579: /* Verify that the signature is actually valid */ andre@629: if(!CryptMsgControl(hMsg, andre@629: 0, andre@629: CMSG_CTRL_VERIFY_SIGNATURE, andre@629: pSignerCertContext->pCertInfo)) aheinecke@579: { andre@629: ERRORPRINTF ("The signature is invalid. \n"); andre@629: retval = VerifyInvalidSignature; andre@629: syslog_error_printf ("Software update embedded signature is invalid."); andre@629: goto done; andre@629: } andre@629: andre@629: if(check_certificate(pSignerCertContext)) andre@629: { andre@629: DEBUGPRINTF ("Valid signature with pinned certificate."); aheinecke@586: retval = VerifyValid; andre@629: goto done; andre@629: } andre@629: else andre@629: { andre@629: ERRORPRINTF ("Certificate mismatch. \n"); andre@637: retval = VerifyInvalidCertificate; andre@629: syslog_error_printf ("Software update embedded signature " andre@629: "created with wrong certificate."); aheinecke@579: goto done; aheinecke@579: } aheinecke@579: aheinecke@579: done: aheinecke@579: xfree(filenameW); aheinecke@579: xfree(pSignerCert); aheinecke@579: aheinecke@579: if(pSignerCertContext) aheinecke@579: { aheinecke@579: CertFreeCertificateContext(pSignerCertContext); aheinecke@579: } aheinecke@579: if (hStore) aheinecke@579: { aheinecke@579: CertCloseStore(hStore, 0); aheinecke@579: } aheinecke@579: if (hMsg) aheinecke@579: { aheinecke@579: CryptMsgClose(hMsg); aheinecke@579: } aheinecke@579: return retval; aheinecke@579: } aheinecke@579: #endif /* WIN32 */