Home | History | Annotate | Download | only in shims
      1 //
      2 // Copyright (C) 2013 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include <unistd.h>
     18 
     19 #include <limits>
     20 #include <string>
     21 #include <vector>
     22 
     23 #include <base/command_line.h>
     24 #include <base/logging.h>
     25 #include <base/posix/eintr_wrapper.h>
     26 #include <brillo/syslog_logging.h>
     27 #include <openssl/bio.h>
     28 #include <openssl/conf.h>
     29 #include <openssl/err.h>
     30 #include <openssl/evp.h>
     31 #include <openssl/pem.h>
     32 #include <openssl/rsa.h>
     33 #include <openssl/sha.h>
     34 #include <openssl/x509.h>
     35 
     36 #include "shill/shims/protos/crypto_util.pb.h"
     37 
     38 using shill_protos::EncryptDataMessage;
     39 using shill_protos::EncryptDataResponse;
     40 using shill_protos::VerifyCredentialsMessage;
     41 using shill_protos::VerifyCredentialsResponse;
     42 using std::numeric_limits;
     43 using std::string;
     44 using std::vector;
     45 
     46 namespace {
     47 
     48 const char kTrustedCAModulus[] =
     49     "BC2280BD80F63A21003BAE765E357F3DC3645C559486342F058728CDF7698C17B350A7B8"
     50     "82FADFC7432DD67EABA06FB7137280A44715C1209950CDEC1462095BA498CDD241B6364E"
     51     "FFE82E32304A81A842A36C9B336ECAB2F55366E02753861A851EA7393F4A778EFB546666"
     52     "FB5854C05E39C7F550060BE08AD4CEE16A551F8B1700E669A327E60825693C129D8D052C"
     53     "D62EA231DEB45250D62049DE71A0F9AD204012F1DD25EBD5E6B836F4D68F7FCA43DCD710"
     54     "5BE63F518A85B3F3FFF6032DCB234F9CAD18E793058CAC529AF74CE9997ABE6E7E4D0AE3"
     55     "C61CA993FA3AA5915D1CBD66EBCC60DC8674CACFF8921C987D57FA61479EAB80B7E44880"
     56     "2A92C51B";
     57 const char kCommandVerify[] = "verify";
     58 const char kCommandEncrypt[] = "encrypt";
     59 const size_t kMacLength = 12;
     60 
     61 // Encrypt |data| with |public_key|.  |public_key| is the raw bytes of a key in
     62 // RSAPublicKey format.  |data| is some string of bytes smaller than the
     63 // maximum length permissable for encryption with a key of |public_key| size.
     64 // |rsa_ptr| should point to NULL (but should not be NULL).  This function may
     65 // set *|rsa_ptr| to an RSA object which should be freed in the caller.
     66 // Returns the encrypted result in |encrypted_output| and returns true on
     67 // success.  Returns false on failure.
     68 bool EncryptByteStringImpl(const string& public_key,
     69                            const string& data,
     70                            RSA** rsa_ptr,
     71                            string* encrypted_output) {
     72   CHECK(rsa_ptr);
     73   CHECK(!*rsa_ptr);
     74   CHECK(encrypted_output);
     75 
     76   // This pointer will be incremented internally by the parsing routine.
     77   const unsigned char* throwaway_ptr =
     78       reinterpret_cast<const unsigned char*>(public_key.data());
     79   *rsa_ptr = d2i_RSAPublicKey(NULL, &throwaway_ptr, public_key.length());
     80   RSA* rsa = *rsa_ptr;
     81   if (!rsa) {
     82     LOG(ERROR) << "Failed to parse public key.";
     83     return false;
     84   }
     85 
     86   vector<unsigned char> rsa_output(RSA_size(rsa));
     87   LOG(INFO) << "Encrypting data with public key.";
     88   const int encrypted_length = RSA_public_encrypt(
     89       data.length(),
     90       // The API helpfully tells us that this operation will treat this buffer
     91       // as read only, but fails to mark the parameter const.
     92       reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())),
     93       rsa_output.data(),
     94       rsa,
     95       RSA_PKCS1_PADDING);
     96   if (encrypted_length <= 0) {
     97     LOG(ERROR) << "Error during encryption.";
     98     return false;
     99   }
    100 
    101   encrypted_output->assign(reinterpret_cast<char*>(rsa_output.data()),
    102                            encrypted_length);
    103   return true;
    104 }
    105 
    106 // Parse the EncryptDataMessage contained in |raw_input| and return an
    107 // EncryptDataResponse in output on success.  Returns true on success and
    108 // false otherwise.
    109 bool EncryptByteString(const string& raw_input, string* output) {
    110   EncryptDataMessage message;
    111   if (!message.ParseFromString(raw_input)) {
    112     LOG(ERROR) << "Failed to read VerifyCredentialsMessage from stdin.";
    113     return false;
    114   }
    115 
    116   if (!message.has_public_key() || !message.has_data()) {
    117     LOG(ERROR) << "Request lacked necessary fields.";
    118     return false;
    119   }
    120 
    121   RSA* rsa = NULL;
    122   string encrypted_output;
    123   bool operation_successful = EncryptByteStringImpl(
    124       message.public_key(), message.data(), &rsa, &encrypted_output);
    125   if (rsa) {
    126     RSA_free(rsa);
    127     rsa = NULL;
    128   }
    129 
    130   if (operation_successful) {
    131     LOG(INFO) << "Filling out protobuf.";
    132     EncryptDataResponse response;
    133     response.set_encrypted_data(encrypted_output);
    134     response.set_ret(shill_protos::OK);
    135     output->clear();
    136     LOG(INFO) << "Serializing protobuf.";
    137     if (!response.SerializeToString(output)) {
    138       LOG(ERROR) << "Failed while writing encrypted data.";
    139       return false;
    140     }
    141     LOG(INFO) << "Encoding finished successfully.";
    142   }
    143 
    144   return operation_successful;
    145 }
    146 
    147 // Verify that the destination described by |certificate| is valid.
    148 //
    149 // 1) The MAC address listed in the certificate matches |connected_mac|.
    150 // 2) The certificate is a valid PEM encoded certificate signed by our
    151 //    trusted CA.
    152 // 3) |signed_data| matches the hashed |unsigned_data| encrypted with
    153 //    the public key in |certificate|.
    154 //
    155 // All pointers should be valid, but point to NULL values.  Sets* ptr to
    156 // NULL or a valid object which should be freed with the appropriate destructor
    157 // upon completion.
    158 bool VerifyCredentialsImpl(const string& certificate,
    159                            const string& signed_data,
    160                            const string& unsigned_data,
    161                            const string& connected_mac,
    162                            RSA** rsa_ptr,
    163                            EVP_PKEY** pkey_ptr,
    164                            BIO** raw_certificate_bio_ptr,
    165                            X509** x509_ptr) {
    166   CHECK(rsa_ptr);
    167   CHECK(pkey_ptr);
    168   CHECK(raw_certificate_bio_ptr);
    169   CHECK(x509_ptr);
    170   CHECK(!*rsa_ptr);
    171   CHECK(!*pkey_ptr);
    172   CHECK(!*raw_certificate_bio_ptr);
    173   CHECK(!*x509_ptr);
    174 
    175   *rsa_ptr = RSA_new();
    176   RSA* rsa = *rsa_ptr;
    177   *pkey_ptr = EVP_PKEY_new();
    178   EVP_PKEY* pkey = *pkey_ptr;
    179   if (!rsa || !pkey) {
    180     LOG(ERROR) << "Failed to allocate key.";
    181     return false;
    182   }
    183 
    184   rsa->e = BN_new();
    185   rsa->n = BN_new();
    186   if (!rsa->e || !rsa->n ||
    187       !BN_set_word(rsa->e, RSA_F4) ||
    188       !BN_hex2bn(&rsa->n, kTrustedCAModulus)) {
    189     LOG(ERROR) << "Failed to allocate key pieces.";
    190     return false;
    191   }
    192 
    193   if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
    194     LOG(ERROR) << "Failed to assign RSA to PKEY.";
    195     return false;
    196   }
    197 
    198   *rsa_ptr = NULL;  // pkey took ownership
    199   // Another helpfully unmarked const interface.
    200   *raw_certificate_bio_ptr = BIO_new_mem_buf(
    201       const_cast<char*>(certificate.data()), certificate.length());
    202   BIO* raw_certificate_bio = *raw_certificate_bio_ptr;
    203   if (!raw_certificate_bio) {
    204     LOG(ERROR) << "Failed to allocate openssl certificate buffer.";
    205     return false;
    206   }
    207 
    208   // No callback for a passphrase, and no passphrase either.
    209   *x509_ptr = PEM_read_bio_X509(raw_certificate_bio, NULL, NULL, NULL);
    210   X509* x509 = *x509_ptr;
    211   if (!x509) {
    212     LOG(ERROR) << "Failed to parse certificate.";
    213     return false;
    214   }
    215 
    216   if (X509_verify(x509, pkey) <= 0) {
    217     LOG(ERROR) << "Failed to verify certificate.";
    218     return false;
    219   }
    220 
    221   // Check that the device listed in the certificate is correct.
    222   char device_name[100];  // A longer CN will truncate.
    223   const int device_name_length = X509_NAME_get_text_by_NID(
    224       x509->cert_info->subject,
    225       NID_commonName,
    226       device_name,
    227       arraysize(device_name));
    228   if (device_name_length == -1) {
    229     LOG(ERROR) << "Subject invalid.";
    230     return false;
    231   }
    232 
    233   // Something like evt_e161 001a11ffacdf
    234   string device_cn(device_name, device_name_length);
    235   const size_t space_idx = device_cn.rfind(' ');
    236   if (space_idx == string::npos) {
    237     LOG(ERROR) << "Badly formatted subject";
    238     return false;
    239   }
    240 
    241   string device_mac;
    242   for (size_t i = space_idx + 1; i < device_cn.length(); ++i) {
    243     device_mac.push_back(tolower(device_cn[i]));
    244   }
    245   if (connected_mac != device_mac) {
    246     LOG(ERROR) << "MAC addresses don't match.";
    247     return false;
    248   }
    249 
    250   // Excellent, the certificate checks out, now make sure that the certificate
    251   // matches the unsigned data presented.
    252   // We're going to verify that hash(unsigned_data) == public(signed_data)
    253   EVP_PKEY* cert_pubkey = X509_get_pubkey(x509);
    254   if (!cert_pubkey) {
    255     LOG(ERROR) << "Unable to extract public key from certificate.";
    256     return false;
    257   }
    258 
    259   RSA* cert_rsa = EVP_PKEY_get1_RSA(cert_pubkey);
    260   if (!cert_rsa) {
    261     LOG(ERROR) << "Failed to extract RSA key from certificate.";
    262     return false;
    263   }
    264 
    265   const unsigned char* signature =
    266       reinterpret_cast<const unsigned char*>(signed_data.data());
    267   const size_t signature_len = signed_data.length();
    268   unsigned char* unsigned_data_bytes =
    269       reinterpret_cast<unsigned char*>(const_cast<char*>(
    270           unsigned_data.data()));
    271   const size_t unsigned_data_len = unsigned_data.length();
    272   unsigned char digest[SHA_DIGEST_LENGTH];
    273   if (signature_len > numeric_limits<unsigned int>::max()) {
    274     LOG(ERROR) << "Arguments to signature match were too large.";
    275     return false;
    276   }
    277   SHA1(unsigned_data_bytes, unsigned_data_len, digest);
    278   if (RSA_verify(NID_sha1, digest, arraysize(digest),
    279                  signature, signature_len, cert_rsa) != 1) {
    280     LOG(ERROR) << "Signed blobs did not match.";
    281     return false;
    282   }
    283 
    284   return true;
    285 }
    286 
    287 // Verify the credentials of the destination described in |raw_input|.  Takes
    288 // a serialized VerifyCredentialsMessage protobuffer in |raw_input|, returns a
    289 // serialized VerifyCredentialsResponse protobuffer in |output| on success.
    290 // Returns false if the credentials fail to meet a check, and true on success.
    291 bool VerifyCredentials(const string& raw_input, string* output) {
    292   VerifyCredentialsMessage message;
    293   if (!message.ParseFromString(raw_input)) {
    294     LOG(ERROR) << "Failed to read VerifyCredentialsMessage from stdin.";
    295     return false;
    296   }
    297 
    298   if (!message.has_certificate() || !message.has_signed_data() ||
    299       !message.has_unsigned_data() || !message.has_mac_address()) {
    300     LOG(ERROR) << "Request lacked necessary fields.";
    301     return false;
    302   }
    303 
    304   string connected_mac;
    305   for (size_t i = 0; i < message.mac_address().length(); ++i) {
    306     const char c = message.mac_address()[i];
    307     if (c != ':') {
    308       connected_mac.push_back(tolower(c));
    309     }
    310   }
    311   if (connected_mac.length() != kMacLength) {
    312     LOG(ERROR) << "shill gave us a bad MAC?";
    313     return false;
    314   }
    315 
    316   RSA* rsa = NULL;
    317   EVP_PKEY* pkey = NULL;
    318   BIO* raw_certificate_bio = NULL;
    319   X509* x509 = NULL;
    320   bool operation_successful = VerifyCredentialsImpl(message.certificate(),
    321       message.signed_data(), message.unsigned_data(), connected_mac,
    322       &rsa, &pkey, &raw_certificate_bio, &x509);
    323   if (x509) {
    324     X509_free(x509);
    325     x509 = NULL;
    326   }
    327   if (raw_certificate_bio) {
    328     BIO_free(raw_certificate_bio);
    329     raw_certificate_bio = NULL;
    330   }
    331   if (pkey) {
    332     EVP_PKEY_free(pkey);
    333     pkey = NULL;
    334   }
    335   if (rsa) {
    336     RSA_free(rsa);
    337     rsa = NULL;
    338   }
    339 
    340   if (operation_successful) {
    341     LOG(INFO) << "Filling out protobuf.";
    342     VerifyCredentialsResponse response;
    343     response.set_ret(shill_protos::OK);
    344     output->clear();
    345     LOG(INFO) << "Serializing protobuf.";
    346     if (!response.SerializeToString(output)) {
    347       LOG(ERROR) << "Failed while writing encrypted data.";
    348       return false;
    349     }
    350     LOG(INFO) << "Encoding finished successfully.";
    351   }
    352 
    353   return operation_successful;
    354 }
    355 
    356 // Read the full stdin stream into a buffer, and execute the operation
    357 // described in |command| with the contends of the stdin buffer.  Write
    358 // the serialized protocol buffer output of the command to stdout.
    359 bool ParseAndExecuteCommand(const string& command) {
    360   string raw_input;
    361   char input_buffer[512];
    362   LOG(INFO) << "Reading input for command " << command << ".";
    363   while (true) {
    364     const ssize_t bytes_read = HANDLE_EINTR(read(STDIN_FILENO,
    365                                                  input_buffer,
    366                                                  arraysize(input_buffer)));
    367     if (bytes_read < 0) {
    368       // Abort abort abort.
    369       LOG(ERROR) << "Failed while reading from stdin.";
    370       return false;
    371     } else if (bytes_read > 0) {
    372       raw_input.append(input_buffer, bytes_read);
    373     } else {
    374       break;
    375     }
    376   }
    377   LOG(INFO) << "Read " << raw_input.length() << " bytes.";
    378   ERR_clear_error();
    379   string raw_output;
    380   bool ret = false;
    381   if (command == kCommandVerify) {
    382     ret = VerifyCredentials(raw_input, &raw_output);
    383   } else if (command == kCommandEncrypt) {
    384     ret = EncryptByteString(raw_input, &raw_output);
    385   } else {
    386     LOG(ERROR) << "Invalid usage.";
    387     return false;
    388   }
    389   if (!ret) {
    390     LOG(ERROR) << "Last OpenSSL error: "
    391                << ERR_reason_error_string(ERR_get_error());
    392   }
    393   size_t total_bytes_written = 0;
    394   while (total_bytes_written < raw_output.length()) {
    395     const ssize_t bytes_written = HANDLE_EINTR(write(
    396         STDOUT_FILENO,
    397         raw_output.data() + total_bytes_written,
    398         raw_output.length() - total_bytes_written));
    399     if (bytes_written < 0) {
    400       LOG(ERROR) << "Result write failed with: " << errno;
    401       return false;
    402     }
    403     total_bytes_written += bytes_written;
    404   }
    405   return ret;
    406 }
    407 
    408 }  // namespace
    409 
    410 int main(int argc, char** argv) {
    411   base::CommandLine::Init(argc, argv);
    412   brillo::InitLog(brillo::kLogToStderr | brillo::kLogHeader);
    413   LOG(INFO) << "crypto-util in action";
    414 
    415   if (argc != 2) {
    416     LOG(ERROR) << "Invalid usage";
    417     return EXIT_FAILURE;
    418   }
    419   const char* command = argv[1];
    420   if (strcmp(kCommandVerify, command) && strcmp(kCommandEncrypt, command)) {
    421     LOG(ERROR) << "Invalid command";
    422     return EXIT_FAILURE;
    423   }
    424 
    425   CRYPTO_malloc_init();
    426   ERR_load_crypto_strings();
    427   OpenSSL_add_all_algorithms();
    428   int return_code = EXIT_FAILURE;
    429   if (ParseAndExecuteCommand(command)) {
    430     return_code = EXIT_SUCCESS;
    431   }
    432   close(STDOUT_FILENO);
    433   close(STDIN_FILENO);
    434 
    435   CONF_modules_unload(1);
    436   OBJ_cleanup();
    437   EVP_cleanup();
    438   CRYPTO_cleanup_all_ex_data();
    439   ERR_remove_thread_state(NULL);
    440   ERR_free_strings();
    441 
    442   return return_code;
    443 }
    444