From c55a574547f73ee9347a0345f9ce376850466e5f Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Mon, 28 Sep 2020 13:17:23 -0700 Subject: [PATCH 1/4] Apply an ad-hoc signature after modifying IDs If we make any changes to a signed binary, we invalidate the signature. Without resigning, the resulting binary is unusable. To my knowledge, there's no disadvantage to signing an unsigned binary using an ad-hoc signature, so we can just do this unconditionally to ensure we don't end up creating bad binaries. The ad-hoc signature should always be available and always work, so this should be safe to do in all circumstances. --- lib/macho.rb | 19 +++++++++++++++++++ lib/macho/tools.rb | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/macho.rb b/lib/macho.rb index c913dff93..a590c3eb2 100644 --- a/lib/macho.rb +++ b/lib/macho.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "English" require_relative "macho/structure" require_relative "macho/view" require_relative "macho/headers" @@ -39,4 +40,22 @@ def self.open(filename) file end + + # Signs the dylib using an ad-hoc identity. + # Necessary after making any changes to a dylib, since otherwise + # changing a signed file invalidates its signature. + # @param filename [String] the file being opened + # @return [void] + # @raise [ModificationError] if the operation fails + def self.codesign(filename) + # codesign binary is not available on Linux + return unless RUBY_PLATFORM =~ /darwin/ + raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) + + system("codesign", "--sign", "-", "--force", + "--preserve-metadata=entitlements,requirements,flags,runtime", + filename) + + raise ModificationError, "#{filename}: signing failed!" unless $CHILD_STATUS.success? + end end diff --git a/lib/macho/tools.rb b/lib/macho/tools.rb index 2bb05fdd6..a8990e58d 100644 --- a/lib/macho/tools.rb +++ b/lib/macho/tools.rb @@ -25,6 +25,8 @@ def self.change_dylib_id(filename, new_id, options = {}) file.change_dylib_id(new_id, options) file.write! + + MachO.codesign(filename) end # Changes a shared library install name in a Mach-O or Fat binary, @@ -41,6 +43,8 @@ def self.change_install_name(filename, old_name, new_name, options = {}) file.change_install_name(old_name, new_name, options) file.write! + + MachO.codesign(filename) end # Changes a runtime path in a Mach-O or Fat binary, overwriting the source @@ -57,6 +61,8 @@ def self.change_rpath(filename, old_path, new_path, options = {}) file.change_rpath(old_path, new_path, options) file.write! + + MachO.codesign(filename) end # Add a runtime path to a Mach-O or Fat binary, overwriting the source file. @@ -71,6 +77,8 @@ def self.add_rpath(filename, new_path, options = {}) file.add_rpath(new_path, options) file.write! + + MachO.codesign(filename) end # Delete a runtime path from a Mach-O or Fat binary, overwriting the source @@ -86,6 +94,8 @@ def self.delete_rpath(filename, old_path, options = {}) file.delete_rpath(old_path, options) file.write! + + MachO.codesign(filename) end # Merge multiple Mach-Os into one universal (Fat) binary. @@ -106,6 +116,8 @@ def self.merge_machos(filename, *files, fat64: false) fat_macho = MachO::FatFile.new_from_machos(*machos, :fat64 => fat64) fat_macho.write(filename) + + MachO.codesign(filename) end end end From 8174d55516d1367704ca7986a0a8516cbdc1a352 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 29 Sep 2020 11:08:36 -0400 Subject: [PATCH 2/4] Update lib/macho.rb Co-authored-by: Mike McQuaid --- lib/macho.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/macho.rb b/lib/macho.rb index a590c3eb2..bebd0f5d2 100644 --- a/lib/macho.rb +++ b/lib/macho.rb @@ -49,7 +49,7 @@ def self.open(filename) # @raise [ModificationError] if the operation fails def self.codesign(filename) # codesign binary is not available on Linux - return unless RUBY_PLATFORM =~ /darwin/ + return if RUBY_PLATFORM !~ /darwin/ raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) system("codesign", "--sign", "-", "--force", From 68cc7297b8fd889705442aba40332e46c3603ddb Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 29 Sep 2020 11:09:59 -0400 Subject: [PATCH 3/4] Update macho.rb --- lib/macho.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/macho.rb b/lib/macho.rb index bebd0f5d2..bfe5c62d4 100644 --- a/lib/macho.rb +++ b/lib/macho.rb @@ -47,7 +47,7 @@ def self.open(filename) # @param filename [String] the file being opened # @return [void] # @raise [ModificationError] if the operation fails - def self.codesign(filename) + def self.codesign!(filename) # codesign binary is not available on Linux return if RUBY_PLATFORM !~ /darwin/ raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) From 668be5fc006dc01e31f42279a5ecd6ef6f3a59a5 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 29 Sep 2020 11:10:33 -0400 Subject: [PATCH 4/4] Update tools.rb --- lib/macho/tools.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/macho/tools.rb b/lib/macho/tools.rb index a8990e58d..00c80ef1c 100644 --- a/lib/macho/tools.rb +++ b/lib/macho/tools.rb @@ -26,7 +26,7 @@ def self.change_dylib_id(filename, new_id, options = {}) file.change_dylib_id(new_id, options) file.write! - MachO.codesign(filename) + MachO.codesign!(filename) end # Changes a shared library install name in a Mach-O or Fat binary, @@ -44,7 +44,7 @@ def self.change_install_name(filename, old_name, new_name, options = {}) file.change_install_name(old_name, new_name, options) file.write! - MachO.codesign(filename) + MachO.codesign!(filename) end # Changes a runtime path in a Mach-O or Fat binary, overwriting the source @@ -62,7 +62,7 @@ def self.change_rpath(filename, old_path, new_path, options = {}) file.change_rpath(old_path, new_path, options) file.write! - MachO.codesign(filename) + MachO.codesign!(filename) end # Add a runtime path to a Mach-O or Fat binary, overwriting the source file. @@ -78,7 +78,7 @@ def self.add_rpath(filename, new_path, options = {}) file.add_rpath(new_path, options) file.write! - MachO.codesign(filename) + MachO.codesign!(filename) end # Delete a runtime path from a Mach-O or Fat binary, overwriting the source @@ -95,7 +95,7 @@ def self.delete_rpath(filename, old_path, options = {}) file.delete_rpath(old_path, options) file.write! - MachO.codesign(filename) + MachO.codesign!(filename) end # Merge multiple Mach-Os into one universal (Fat) binary. @@ -117,7 +117,7 @@ def self.merge_machos(filename, *files, fat64: false) fat_macho = MachO::FatFile.new_from_machos(*machos, :fat64 => fat64) fat_macho.write(filename) - MachO.codesign(filename) + MachO.codesign!(filename) end end end