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) { andre@771: if (!filename || !name_len) andre@771: return VerifyUnknownError; aheinecke@586: #ifdef WIN32 aheinecke@586: return verify_binary_win(filename, name_len); aheinecke@586: #else andre@771: return verify_binary_linux(filename, name_len); 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: } andre@771: #else /* WIN32 */ andre@771: andre@771: #include "listutil.h" andre@771: andre@771: #pragma GCC diagnostic ignored "-Wconversion" andre@771: /* Polarssl mh.h contains a conversion which gcc warns about */ andre@771: #include andre@771: #include andre@771: #include andre@771: #pragma GCC diagnostic pop andre@771: andre@771: bin_verify_result andre@771: verify_binary_linux(const char *filename, size_t name_len) andre@771: { andre@771: int ret = -1; andre@771: const size_t sig_b64_size = TRUSTBRIDGE_RSA_KEY_SIZE / 8 * 4 / 3; andre@771: char *data = NULL, andre@771: signature_b64[sig_b64_size + 1]; andre@771: size_t data_size = 0, andre@771: sig_size = TRUSTBRIDGE_RSA_KEY_SIZE / 8; andre@771: unsigned char signature[sig_size], andre@771: hash[32]; andre@771: andre@771: bin_verify_result retval = VerifyUnknownError; andre@771: pk_context pub_key_ctx; andre@771: andre@771: if (strnlen(filename, name_len + 1) != name_len || name_len == 0) andre@771: { andre@771: ERRORPRINTF ("Invalid call to verify_binary_linux\n"); andre@771: return VerifyUnknownError; andre@771: } andre@771: andre@771: ret = read_file(filename, &data, &data_size, MAX_VALID_BIN_SIZE); andre@771: andre@771: if (ret != 0) andre@771: { andre@771: ERRORPRINTF ("Read file failed with error: %i\n", ret); andre@771: return VerifyReadFailed; andre@771: } andre@771: andre@771: /* Fetch the signature from the end of data */ andre@771: if (data_size < sig_b64_size + 4) andre@771: { andre@771: ERRORPRINTF ("File to small to contain a signature.\n"); andre@771: retval = VerifyInvalidSignature; andre@771: goto done; andre@771: } andre@771: andre@771: if (data[data_size - sig_b64_size - 1] != ':' || andre@771: data[data_size - sig_b64_size - 2] != 'S' || andre@771: data[data_size - sig_b64_size - 3] != '\n'|| andre@771: data[data_size - sig_b64_size - 4] != '\r') andre@771: { andre@771: ERRORPRINTF ("Failed to find valid signature line.\n"); andre@771: retval = VerifyInvalidSignature; andre@771: goto done; andre@771: } andre@771: andre@771: strncpy(signature_b64, data - sig_b64_size, sig_b64_size); andre@771: signature_b64[sig_b64_size] = '\0'; andre@771: andre@771: ret = base64_decode(signature, &sig_size, andre@771: (unsigned char *)signature_b64, sig_b64_size); andre@771: andre@771: if (ret != 0 || sig_size != TRUSTBRIDGE_RSA_KEY_SIZE / 8) andre@771: { andre@771: goto done; andre@771: } andre@771: andre@771: /* Hash is calculated over the data without the signature at the end. */ andre@771: sha256((unsigned char *)data, data_size - sig_b64_size - 4, hash, 0); andre@771: andre@771: pk_init(&pub_key_ctx); andre@771: andre@771: ret = pk_parse_public_key(&pub_key_ctx, public_key_codesign_pem, andre@771: public_key_codesign_pem_size); andre@771: if (ret != 0) andre@771: { andre@771: ERRORPRINTF ("pk_parse_public_key failed with -0x%04x\n\n", -ret); andre@771: pk_free(&pub_key_ctx); andre@771: return VerifyUnknownError; andre@771: } andre@771: andre@771: ret = pk_verify(&pub_key_ctx, POLARSSL_MD_SHA256, hash, 0, andre@771: signature, sig_size); andre@771: andre@771: if (ret != 0) andre@771: { andre@771: ERRORPRINTF ("pk_verify failed with -0x%04x\n\n", -ret); andre@771: } andre@771: pk_free(&pub_key_ctx); andre@771: andre@771: return VerifyValid; andre@771: andre@771: done: andre@771: xfree (data); andre@771: return retval; andre@771: } andre@771: aheinecke@579: #endif /* WIN32 */