| // Copyright 2006-2009 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // ======================================================================== |
| // |
| // Applies a tag to a signed file. |
| |
| #include "omaha/common/apply_tag.h" |
| #include <atlrx.h> |
| #include <vector> |
| #include "base/scoped_ptr.h" |
| #include "omaha/common/utils.h" |
| #include "omaha/common/extractor.h" |
| |
| namespace omaha { |
| |
| const char kMagicBytes[] = "Gact"; |
| const uint32 kPEHeaderOffset = 60; |
| |
| ApplyTag::ApplyTag() |
| : prev_tag_string_length_(0), |
| prev_cert_length_(0), |
| append_(0) {} |
| |
| bool ApplyTag::IsValidTagString(const char* tag_string) { |
| ASSERT1(tag_string); |
| |
| CAtlRegExp<CAtlRECharTraitsA> regex; |
| REParseError error = regex.Parse(kValidTagStringRegEx); |
| if (error != REPARSE_ERROR_OK) { |
| return false; |
| } |
| |
| CAtlREMatchContext<CAtlRECharTraitsA> context; |
| return !!regex.Match(tag_string, &context); |
| } |
| |
| HRESULT ApplyTag::Init(const TCHAR* signed_exe_file, |
| const char* tag_string, |
| int tag_string_length, |
| const TCHAR* tagged_file, |
| bool append) { |
| ASSERT1(signed_exe_file); |
| ASSERT1(tag_string); |
| ASSERT1(tagged_file); |
| |
| signed_exe_file_ = signed_exe_file; |
| tagged_file_ = tagged_file; |
| append_ = append; |
| |
| // Check the tag_string for invalid characters. |
| if (!IsValidTagString(tag_string)) { |
| return E_INVALIDARG; |
| } |
| |
| for (int i = 0; i < tag_string_length; ++i) { |
| tag_string_.push_back(tag_string[i]); |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT ApplyTag::EmbedTagString() { |
| std::vector<byte> input_file_buffer; |
| HRESULT hr = ReadEntireFile(signed_exe_file_, 0, &input_file_buffer); |
| if (FAILED(hr)) { |
| return hr; |
| } |
| |
| ASSERT1(!input_file_buffer.empty()); |
| VERIFY1(ReadExistingTag(&input_file_buffer)); |
| if (!append_ && prev_tag_string_length_) { |
| // If there is a previous tag and the append flag is not set, then |
| // we should error out. |
| return APPLYTAG_E_ALREADY_TAGGED; |
| } |
| |
| if (!CreateBufferToWrite()) { |
| return E_FAIL; |
| } |
| |
| // The input_file_buffer might contain the previously read tag, in which |
| // case the buffer_data_ is larger than the actual output buffer length. |
| // The real output buffer length is returned by the ApplyTagToBuffer |
| // method. |
| buffer_data_.resize(input_file_buffer.size() + tag_buffer_.size()); |
| |
| copy(input_file_buffer.begin(), |
| input_file_buffer.end(), |
| buffer_data_.begin()); |
| |
| int output_length = 0; |
| if (!ApplyTagToBuffer(&output_length)) |
| return E_FAIL; |
| |
| std::vector<byte> output_buffer(output_length); |
| ASSERT1(static_cast<size_t>(output_length) <= buffer_data_.size()); |
| copy(buffer_data_.begin(), |
| buffer_data_.begin() + output_length, |
| output_buffer.begin()); |
| return WriteEntireFile(tagged_file_, output_buffer); |
| } |
| |
| uint32 ApplyTag::GetUint32(const void* p) { |
| ASSERT1(p); |
| |
| const uint32* pu = reinterpret_cast<const uint32*>(p); |
| return *pu; |
| } |
| |
| void ApplyTag::PutUint32(uint32 i, void* p) { |
| ASSERT1(p); |
| |
| uint32* pu = reinterpret_cast<uint32*>(p); |
| *pu = i; |
| } |
| |
| bool ApplyTag::ReadExistingTag(std::vector<byte>* binary) { |
| ASSERT1(binary); |
| |
| int len = 0; |
| TagExtractor tag; |
| char* bin = reinterpret_cast<char*>(&binary->front()); |
| ASSERT1(bin); |
| if (tag.ExtractTag(bin, binary->size(), NULL, &len)) { |
| prev_tag_string_.resize(len); |
| if (tag.ExtractTag(bin, binary->size(), &prev_tag_string_.front(), &len)) { |
| // The extractor returns the actual length |
| // of the string + 1 for the terminating null. |
| prev_tag_string_length_ = len - 1; |
| } |
| } |
| |
| // Set the existing certificate length even if previous |
| // tag does not exist. |
| prev_cert_length_ = tag.cert_length(); |
| return true; |
| } |
| |
| bool ApplyTag::CreateBufferToWrite() { |
| ASSERT1(!append_ && !prev_tag_string_length_ || append_); |
| ASSERT1(!tag_string_.empty()); |
| ASSERT1(!prev_tag_string_.size() || |
| prev_tag_string_.size() == |
| static_cast<size_t>(prev_tag_string_length_ + 1)); |
| |
| // Build the tag buffer. |
| // The format of the tag buffer is: |
| // 000000-000003: 4-byte magic (big-endian) |
| // 000004-000005: unsigned 16-bit int string length (big-endian) |
| // 000006-??????: ASCII string |
| int tag_string_len = tag_string_.size() + prev_tag_string_length_; |
| int kMagicBytesLen = ::lstrlenA(kMagicBytes); |
| int tag_header_len = kMagicBytesLen + 2; |
| int unpadded_tag_buffer_len = tag_string_len + tag_header_len; |
| // The tag buffer should be padded to multiples of 8, otherwise it will |
| // break the signature of the executable file. |
| int padded_tag_buffer_length = (unpadded_tag_buffer_len + 15) & (-8); |
| |
| tag_buffer_.clear(); |
| tag_buffer_.resize(padded_tag_buffer_length, 0); |
| memcpy(&tag_buffer_.front(), kMagicBytes, kMagicBytesLen); |
| tag_buffer_[kMagicBytesLen] = |
| static_cast<char>((tag_string_len & 0xff00) >> 8); |
| tag_buffer_[kMagicBytesLen+1] = static_cast<char>(tag_string_len & 0xff); |
| |
| if (prev_tag_string_length_ > 0) { |
| copy(prev_tag_string_.begin(), |
| prev_tag_string_.end(), |
| tag_buffer_.begin() + tag_header_len); |
| } |
| |
| copy(tag_string_.begin(), |
| tag_string_.end(), |
| tag_buffer_.begin() + tag_header_len + prev_tag_string_length_); |
| ASSERT1(static_cast<int>(tag_buffer_.size()) == padded_tag_buffer_length); |
| |
| return true; |
| } |
| |
| bool ApplyTag::ApplyTagToBuffer(int* output_len) { |
| ASSERT1(output_len); |
| |
| uint32 original_data_len = buffer_data_.size() - tag_buffer_.size(); |
| uint32 peheader = GetUint32(&buffer_data_.front() + kPEHeaderOffset); |
| uint32 kCertDirAddressOffset = 152; |
| uint32 kCertDirInfoSize = 4 + 4; |
| |
| ASSERT1(peheader + kCertDirAddressOffset + kCertDirInfoSize <= |
| original_data_len); |
| |
| // Read certificate directory info. |
| uint32 cert_dir_offset = GetUint32(&buffer_data_.front() + peheader + |
| kCertDirAddressOffset); |
| if (cert_dir_offset == 0) |
| return false; |
| uint32 cert_dir_len = GetUint32(&buffer_data_.front() + peheader + |
| kCertDirAddressOffset + 4); |
| ASSERT1(cert_dir_offset + cert_dir_len <= original_data_len); |
| |
| // Calculate the new output length. |
| int prev_pad_length = cert_dir_len - prev_cert_length_ - |
| prev_tag_string_length_; |
| ASSERT1(prev_pad_length >= 0); |
| int orig_dir_len = cert_dir_len - prev_tag_string_length_ - |
| prev_pad_length; |
| ASSERT1(orig_dir_len == prev_cert_length_); |
| int output_length = original_data_len - prev_tag_string_length_ - |
| prev_pad_length + tag_buffer_.size(); |
| *output_len = output_length; |
| ASSERT1(static_cast<size_t>(output_length) <= buffer_data_.size()); |
| ASSERT1(output_length >= orig_dir_len); |
| |
| // Increase the size of certificate directory. |
| int new_cert_len = prev_cert_length_ + tag_buffer_.size(); |
| PutUint32(new_cert_len, |
| &buffer_data_.front() + peheader + kCertDirAddressOffset + 4); |
| |
| // Read certificate struct info. |
| uint32 cert_struct_len = GetUint32(&buffer_data_.front() + cert_dir_offset); |
| ASSERT1(!(cert_struct_len > cert_dir_len || |
| cert_struct_len < cert_dir_len - 8)); |
| |
| // Increase the certificate struct size. |
| PutUint32(new_cert_len, &buffer_data_.front() + cert_dir_offset); |
| |
| // Copy the tag buffer. |
| copy(tag_buffer_.begin(), tag_buffer_.end(), |
| buffer_data_.begin() + cert_dir_offset + prev_cert_length_); |
| |
| return true; |
| } |
| |
| } // namespace omaha. |