diff options
author | Szymon Starzycki <sstar@google.com> | 2013-10-02 17:21:41 -0700 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2013-12-04 15:00:52 -0800 |
commit | 4662a114a416e636de805fbd875f8b90fc5965b9 (patch) | |
tree | cd62973e812f8f2972893ba2ef4dc405192258c6 /fastbootd | |
parent | 27ea99fb99153e3b2d45efd6fce9785538469e49 (diff) | |
download | system_core-4662a114a416e636de805fbd875f8b90fc5965b9.zip system_core-4662a114a416e636de805fbd875f8b90fc5965b9.tar.gz system_core-4662a114a416e636de805fbd875f8b90fc5965b9.tar.bz2 |
Revert "Revert "Fastbootd: flashing certification""
CMS functionality is now available
This reverts commit 068b71dd9cd6cb03dfcdc0c9deced361780bc0d3.
Conflicts:
fastbootd/Android.mk
fastbootd/fastbootd.c
fastbootd/utils.c
fastbootd/utils.h
Change-Id: I1a27459b41d9297603deb124c65f237ff971e5b6
Diffstat (limited to 'fastbootd')
-rw-r--r-- | fastbootd/Android.mk | 3 | ||||
-rw-r--r-- | fastbootd/commands.c | 39 | ||||
-rw-r--r-- | fastbootd/commands/flash.c | 47 | ||||
-rw-r--r-- | fastbootd/commands/flash.h | 2 | ||||
-rw-r--r-- | fastbootd/fastbootd.c | 2 | ||||
-rw-r--r-- | fastbootd/other/sign/src/SignImg.java | 181 | ||||
-rw-r--r-- | fastbootd/secure.c | 166 | ||||
-rw-r--r-- | fastbootd/secure.h | 53 |
8 files changed, 485 insertions, 8 deletions
diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk index c051721..0f32dbf 100644 --- a/fastbootd/Android.mk +++ b/fastbootd/Android.mk @@ -33,6 +33,7 @@ LOCAL_SRC_FILES := \ protocol.c \ network_discovery.c \ socket_client.c \ + secure.c \ transport.c \ transport_socket.c \ trigger.c \ @@ -41,7 +42,7 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := fastbootd LOCAL_MODULE_TAGS := optional -LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter +LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -DFLASH_CERT LOCAL_LDFLAGS := -ldl LOCAL_SHARED_LIBRARIES := \ diff --git a/fastbootd/commands.c b/fastbootd/commands.c index 83e86b0..2f6e86a 100644 --- a/fastbootd/commands.c +++ b/fastbootd/commands.c @@ -52,6 +52,8 @@ static void cmd_boot(struct protocol_handle *phandle, const char *arg) { + int sz, atags_sz, new_atags_sz; + int rv; unsigned kernel_actual; unsigned ramdisk_actual; unsigned second_actual; @@ -59,9 +61,11 @@ static void cmd_boot(struct protocol_handle *phandle, const char *arg) void *ramdisk_ptr; void *second_ptr; struct boot_img_hdr *hdr; - int sz, atags_sz, new_atags_sz; - int rv; - char *ptr = NULL, *atags_ptr = NULL, *new_atags = NULL; + char *ptr = NULL; + char *atags_ptr = NULL; + char *new_atags = NULL; + int data_fd = 0; + D(DEBUG, "cmd_boot %s\n", arg); if (phandle->download_fd < 0) { @@ -75,9 +79,18 @@ static void cmd_boot(struct protocol_handle *phandle, const char *arg) goto error; } - sz = get_file_size(phandle->download_fd); + // TODO: With cms we can also verify partition name included as + // cms signed attribute + if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) { + fastboot_fail(phandle, "Access forbiden you need the certificate"); + return; + } + + sz = get_file_size(data_fd); + ptr = (char *) mmap(NULL, sz, PROT_READ, - MAP_POPULATE | MAP_PRIVATE, phandle->download_fd, 0); + MAP_POPULATE | MAP_PRIVATE, data_fd, 0); + hdr = (struct boot_img_hdr *) ptr; if (ptr == MAP_FAILED) { @@ -130,6 +143,7 @@ static void cmd_boot(struct protocol_handle *phandle, const char *arg) free(atags_ptr); munmap(ptr, sz); free(new_atags); + close(data_fd); D(INFO, "Kexec going to reboot"); reboot(LINUX_REBOOT_CMD_KEXEC); @@ -256,6 +270,7 @@ static void cmd_flash(struct protocol_handle *phandle, const char *arg) char data[BOOT_MAGIC_SIZE]; char path[PATH_MAX]; ssize_t header_sz = 0; + int data_fd = 0; D(DEBUG, "cmd_flash %s\n", arg); @@ -273,9 +288,14 @@ static void cmd_flash(struct protocol_handle *phandle, const char *arg) return; } + if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) { + fastboot_fail(phandle, "Access forbiden you need certificate"); + return; + } + // TODO: Maybe its goot idea to check whether the partition is just bootable partition if (!strcmp(arg, "boot") || !strcmp(arg, "recovery")) { - if (read_data_once(phandle->download_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) { + if (read_data_once(data_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) { fastboot_fail(phandle, "incoming data read error, cannot read boot header"); return; } @@ -287,7 +307,10 @@ static void cmd_flash(struct protocol_handle *phandle, const char *arg) partition = flash_get_partiton(path); - sz = get_file_size64(phandle->download_fd); + sz = get_file_size64(data_fd); + + sz -= header_sz; + if (sz > get_file_size64(partition)) { flash_close(partition); D(WARN, "size of file too large"); @@ -304,6 +327,8 @@ static void cmd_flash(struct protocol_handle *phandle, const char *arg) D(INFO, "partition '%s' updated\n", arg); flash_close(partition); + close(data_fd); + //TODO: check who is closing phandle->download_fd fastboot_okay(phandle, ""); } diff --git a/fastbootd/commands/flash.c b/fastbootd/commands/flash.c index 5f8b931..0954217 100644 --- a/fastbootd/commands/flash.c +++ b/fastbootd/commands/flash.c @@ -39,6 +39,9 @@ #include "utils.h" #include "commands/partitions.h" +#ifdef FLASH_CERT +#include "secure.h" +#endif #define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-." #define BUFFER_SIZE 1024 * 1024 @@ -112,3 +115,47 @@ int flash_write(int partition_fd, int data_fd, ssize_t size, ssize_t skip) return 0; } + +#ifdef FLASH_CERT + +int flash_validate_certificate(int signed_fd, int *data_fd) { + int ret = 0; + const char *cert_path; + X509_STORE *store = NULL; + CMS_ContentInfo *content_info; + BIO *content; + + cert_path = fastboot_getvar("certificate-path"); + if (!strcmp(cert_path, "")) { + D(ERR, "could not find cert-key value in config file"); + goto finish; + } + + store = cert_store_from_path(cert_path); + if (store == NULL) { + D(ERR, "unable to create certification store"); + goto finish; + } + + if (cert_read(signed_fd, &content_info, &content)) { + D(ERR, "reading data failed"); + goto finish; + } + + ret = cert_verify(content, content_info, store, data_fd); + cert_release(content, content_info); + + return ret; + +finish: + if (store != NULL) + cert_release_store(store); + + return ret; +} + +#else +int flash_validate_certificate(int signed_fd, int *data_fd) { + return 1; +} +#endif diff --git a/fastbootd/commands/flash.h b/fastbootd/commands/flash.h index 8ffd688..86dc811 100644 --- a/fastbootd/commands/flash.h +++ b/fastbootd/commands/flash.h @@ -58,5 +58,7 @@ static inline ssize_t read_data_once(int fd, char *buffer, ssize_t size) { return readcount; } +int flash_validate_certificate(int signed_fd, int *data_fd); + #endif diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c index 085cbfa..9318c99 100644 --- a/fastbootd/fastbootd.c +++ b/fastbootd/fastbootd.c @@ -23,6 +23,7 @@ #include "debug.h" #include "trigger.h" #include "socket_client.h" +#include "secure.h" unsigned int debug_level = DEBUG; @@ -72,6 +73,7 @@ int main(int argc, char **argv) run_socket_client(); } else { + cert_init_crypto(); config_init(); load_trigger(); commands_init(); diff --git a/fastbootd/other/sign/src/SignImg.java b/fastbootd/other/sign/src/SignImg.java new file mode 100644 index 0000000..338d427 --- /dev/null +++ b/fastbootd/other/sign/src/SignImg.java @@ -0,0 +1,181 @@ +package signtool; + +import java.io.*; +import java.util.Properties; +import java.util.ArrayList; + +import javax.mail.internet.*; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.activation.MailcapCommandMap; +import javax.activation.CommandMap; + +import java.security.PrivateKey; +import java.security.Security; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateEncodingException; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedGenerator; +import org.bouncycastle.cms.CMSSignedDataGenerator; +import org.bouncycastle.cms.CMSSignedGenerator; +import org.bouncycastle.cms.CMSProcessable; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.CMSTypedData; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.util.Store; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.DEROutputStream; +import org.bouncycastle.asn1.ASN1Object; + + +public class SignImg { + + /* It reads private key in pkcs#8 formate + * Conversion: + * openssl pkcs8 -topk8 -nocrypt -outform DER < inkey.pem > outkey.pk8 + */ + private static PrivateKey getPrivateKey(String path) throws IOException, FileNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException { + File file = new File(path); + FileInputStream fis = new FileInputStream(file); + byte[] data = new byte[(int)file.length()]; + fis.read(data); + fis.close(); + + PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(data); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = kf.generatePrivate(kspec); + + return privateKey; + } + + private static MimeBodyPart getContent(String path) throws IOException, FileNotFoundException, MessagingException { + MimeBodyPart body = new MimeBodyPart(); + + File file = new File(path); + FileInputStream fis = new FileInputStream(file); + byte[] data = new byte[(int)file.length()]; + fis.read(data); + fis.close(); + + body.setContent(data, "application/octet-stream"); + + return body; + } + + private static CMSProcessableByteArray getCMSContent(String path) throws IOException, FileNotFoundException, MessagingException { + File file = new File(path); + FileInputStream fis = new FileInputStream(file); + byte[] data = new byte[(int)file.length()]; + fis.read(data); + fis.close(); + CMSProcessableByteArray cms = new CMSProcessableByteArray(data); + + return cms; + } + + private static X509Certificate readCert(String path) throws IOException, FileNotFoundException, CertificateException { + File file = new File(path); + FileInputStream is = new FileInputStream(file); + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Certificate cert = cf.generateCertificate(is); + is.close(); + + return (X509Certificate) cert; + } + + private static void save(MimeBodyPart content, String path) throws IOException, FileNotFoundException, MessagingException { + File file = new File(path); + FileOutputStream os = new FileOutputStream(file); + + content.writeTo(os); + + os.close(); + } + + private static Store certToStore(X509Certificate certificate) throws CertificateEncodingException { + ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(); + certList.add(certificate); + return new JcaCertStore(certList); + } + + public static void setDefaultMailcap() + { + MailcapCommandMap _mailcap = + (MailcapCommandMap)CommandMap.getDefaultCommandMap(); + + _mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature"); + _mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime"); + _mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature"); + _mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime"); + _mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed"); + + CommandMap.setDefaultCommandMap(_mailcap); + } + + public static void main(String[] args) { + try { + if (args.length < 4) { + System.out.println("Usage: signimg data private_key certificate output"); + return; + } + System.out.println("Signing the image"); + setDefaultMailcap(); + + Security.addProvider(new BouncyCastleProvider()); + + PrivateKey key = getPrivateKey(args[1]); + System.out.println("File read sucessfully"); + + CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); + + CMSTypedData body = getCMSContent(args[0]); + System.out.println("Content read sucessfully"); + + X509Certificate cert = (X509Certificate) readCert(args[2]); + System.out.println("Certificate read sucessfully"); + + ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(key); + + Store certs = certToStore(cert); + + generator.addCertificates(certs); + generator.addSignerInfoGenerator( + new JcaSignerInfoGeneratorBuilder( + new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()) + .build(sha256Signer, cert)); + + CMSSignedData signed = generator.generate(body, true); + System.out.println("Signed"); + + Properties props = System.getProperties(); + Session session = Session.getDefaultInstance(props, null); + + File file = new File(args[3]); + FileOutputStream os = new FileOutputStream(file); + + ASN1InputStream asn1 = new ASN1InputStream(signed.getEncoded()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(os); + dOut.writeObject(ASN1Object.fromByteArray(signed.getEncoded())); + + } + catch (Exception ex) { + System.out.println("Exception during programm execution: " + ex.getMessage()); + } + } +} diff --git a/fastbootd/secure.c b/fastbootd/secure.c new file mode 100644 index 0000000..75a6f3c --- /dev/null +++ b/fastbootd/secure.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include <openssl/pem.h> +#include <openssl/engine.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <openssl/cms.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "secure.h" +#include "debug.h" +#include "utils.h" + + +void cert_init_crypto() { + CRYPTO_malloc_init(); + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + ENGINE_load_builtin_engines(); +} + +X509_STORE *cert_store_from_path(const char *path) { + + X509_STORE *store; + struct stat st; + X509_LOOKUP *lookup; + + if (stat(path, &st)) { + D(ERR, "Unable to stat cert path"); + goto error; + } + + if (!(store = X509_STORE_new())) { + goto error; + } + + if (S_ISDIR(st.st_mode)) { + lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir()); + if (lookup == NULL) + goto error; + if (!X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM)) { + D(ERR, "Error loading cert directory %s", path); + goto error; + } + } + else if(S_ISREG(st.st_mode)) { + lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file()); + if (lookup == NULL) + goto error; + if (!X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM)) { + D(ERR, "Error loading cert directory %s", path); + goto error; + } + } + else { + D(ERR, "cert path is not directory or regular file"); + goto error; + } + + return store; + +error: + return NULL; +} + + +int cert_read(int fd, CMS_ContentInfo **content, BIO **output) { + BIO *input; + *output = NULL; + + + input = BIO_new_fd(fd, BIO_NOCLOSE); + if (input == NULL) { + D(ERR, "Unable to open input"); + goto error; + } + + *content = SMIME_read_CMS(input, output); + if (*content == NULL) { + unsigned long err = ERR_peek_last_error(); + D(ERR, "Unable to parse input file: %s", ERR_lib_error_string(err)); + goto error_read; + } + + BIO_free(input); + + return 0; + +error_read: + BIO_free(input); +error: + return 1; +} + +int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd) { + BIO *output_temp; + int ret; + + *out_fd = create_temp_file(); + if (*out_fd < 0) { + D(ERR, "unable to create temporary file"); + return -1; + } + + output_temp = BIO_new_fd(*out_fd, BIO_NOCLOSE); + if (output_temp == NULL) { + D(ERR, "unable to create temporary bio"); + close(*out_fd); + return -1; + } + + ret = CMS_verify(content_info, NULL ,store, content, output_temp, 0); + + if (ret == 0) { + char buf[256]; + unsigned long err = ERR_peek_last_error(); + D(ERR, "Verification failed with reason: %s, %s", ERR_lib_error_string(err), ERR_error_string(err, buf)); + D(ERR, "Data used: content %d", (int) content); + } + + ERR_clear_error(); + ERR_remove_state(0); + + BIO_free(output_temp); + + return ret; +} + +void cert_release(BIO *content, CMS_ContentInfo *info) { + BIO_free(content); + CMS_ContentInfo_free(info); +} + diff --git a/fastbootd/secure.h b/fastbootd/secure.h new file mode 100644 index 0000000..878a643 --- /dev/null +++ b/fastbootd/secure.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOTD_SECURE_H +#define _FASTBOOTD_SECURE_H + +#include <openssl/ssl.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <openssl/cms.h> + +void cert_init_crypto(); + +X509_STORE *cert_store_from_path(const char*stream); + +static inline void cert_release_store(X509_STORE *store) { + X509_STORE_free(store); +} + +int cert_read(int fd, CMS_ContentInfo **content, BIO **output); +int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd); +void cert_release(BIO *content, CMS_ContentInfo *info); + +#endif |