diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:35:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:35:28 +0000 |
commit | ea314d2f45c40a006c0104157013ab4b857f665f (patch) | |
tree | 3ef2971cb3675c318b8d9effd987854ad3f6d3e8 /scripts/Dpkg/Vendor/Debian.pm | |
parent | Initial commit. (diff) | |
download | dpkg-ea314d2f45c40a006c0104157013ab4b857f665f.tar.xz dpkg-ea314d2f45c40a006c0104157013ab4b857f665f.zip |
Adding upstream version 1.22.4.upstream/1.22.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scripts/Dpkg/Vendor/Debian.pm')
-rw-r--r-- | scripts/Dpkg/Vendor/Debian.pm | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm new file mode 100644 index 0000000..2d07794 --- /dev/null +++ b/scripts/Dpkg/Vendor/Debian.pm @@ -0,0 +1,633 @@ +# Copyright © 2009-2011 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2009, 2011-2017 Guillem Jover <guillem@debian.org> +# +# Hardening build flags handling derived from work of: +# Copyright © 2009-2011 Kees Cook <kees@debian.org> +# Copyright © 2007-2008 Canonical, Ltd. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +=encoding utf8 + +=head1 NAME + +Dpkg::Vendor::Debian - Debian vendor class + +=head1 DESCRIPTION + +This vendor class customizes the behavior of dpkg scripts for Debian +specific behavior and policies. + +B<Note>: This is a private module, its API can change at any time. + +=cut + +package Dpkg::Vendor::Debian 0.01; + +use strict; +use warnings; + +use List::Util qw(any none); + +use Dpkg; +use Dpkg::Gettext; +use Dpkg::ErrorHandling; +use Dpkg::Control::Types; + +use parent qw(Dpkg::Vendor::Default); + +sub run_hook { + my ($self, $hook, @params) = @_; + + if ($hook eq 'package-keyrings') { + return ('/usr/share/keyrings/debian-keyring.gpg', + '/usr/share/keyrings/debian-nonupload.gpg', + '/usr/share/keyrings/debian-maintainers.gpg'); + } elsif ($hook eq 'archive-keyrings') { + return ('/usr/share/keyrings/debian-archive-keyring.gpg'); + } elsif ($hook eq 'archive-keyrings-historic') { + return ('/usr/share/keyrings/debian-archive-removed-keys.gpg'); + } elsif ($hook eq 'builtin-build-depends') { + return qw(build-essential:native); + } elsif ($hook eq 'builtin-build-conflicts') { + return (); + } elsif ($hook eq 'register-custom-fields') { + } elsif ($hook eq 'extend-patch-header') { + my ($textref, $ch_info) = @params; + if ($ch_info->{'Closes'}) { + foreach my $bug (split(/\s+/, $ch_info->{'Closes'})) { + $$textref .= "Bug-Debian: https://bugs.debian.org/$bug\n"; + } + } + + # XXX: Layer violation... + require Dpkg::Vendor::Ubuntu; + my $b = Dpkg::Vendor::Ubuntu::find_launchpad_closes($ch_info->{'Changes'}); + foreach my $bug (@$b) { + $$textref .= "Bug-Ubuntu: https://bugs.launchpad.net/bugs/$bug\n"; + } + } elsif ($hook eq 'update-buildflags') { + $self->set_build_features(@params); + $self->_add_build_flags(@params); + } elsif ($hook eq 'builtin-system-build-paths') { + return qw(/build/); + } elsif ($hook eq 'build-tainted-by') { + return $self->_build_tainted_by(); + } elsif ($hook eq 'sanitize-environment') { + # Reset umask to a sane default. + umask 0022; + # Reset locale to a sane default. + $ENV{LC_COLLATE} = 'C.UTF-8'; + } elsif ($hook eq 'backport-version-regex') { + return qr/~(bpo|deb)/; + } else { + return $self->SUPER::run_hook($hook, @params); + } +} + +sub init_build_features { + my ($self, $use_feature, $builtin_feature) = @_; +} + +sub set_build_features { + my ($self, $flags) = @_; + + # Default feature states. + my %use_feature = ( + future => { + # XXX: Should start a deprecation cycle at some point. + lfs => 0, + }, + abi => { + # XXX: This is set to undef so that we can handle the alias from + # the future feature area. + lfs => undef, + time64 => 0, + }, + qa => { + bug => 0, + 'bug-implicit-func' => undef, + canary => 0, + }, + reproducible => { + timeless => 1, + fixfilepath => 1, + fixdebugpath => 1, + }, + optimize => { + lto => 0, + }, + sanitize => { + address => 0, + thread => 0, + leak => 0, + undefined => 0, + }, + hardening => { + # XXX: This is set to undef so that we can cope with the brokenness + # of gcc managing this feature builtin. + pie => undef, + stackprotector => 1, + stackprotectorstrong => 1, + stackclash => 1, + fortify => 1, + format => 1, + relro => 1, + bindnow => 0, + branch => 1, + }, + ); + + my %builtin_feature = ( + abi => { + lfs => 0, + time64 => 0, + }, + hardening => { + pie => 1, + }, + ); + + require Dpkg::Arch; + + my $arch = Dpkg::Arch::get_host_arch(); + my ($abi, $libc, $os, $cpu) = Dpkg::Arch::debarch_to_debtuple($arch); + my ($abi_bits, $abi_endian) = Dpkg::Arch::debarch_to_abiattrs($arch); + + unless (defined $abi and defined $libc and defined $os and defined $cpu) { + warning(g_("unknown host architecture '%s'"), $arch); + ($abi, $os, $cpu) = ('', '', ''); + } + unless (defined $abi_bits and defined $abi_endian) { + warning(g_("unknown abi attributes for architecture '%s'"), $arch); + ($abi_bits, $abi_endian) = (0, 'unknown'); + } + + # Mask builtin features that are not enabled by default in the compiler. + my %builtin_pie_arch = map { $_ => 1 } qw( + amd64 + arm64 + armel + armhf + hurd-amd64 + hurd-i386 + i386 + kfreebsd-amd64 + kfreebsd-i386 + loong64 + mips + mips64 + mips64el + mips64r6 + mips64r6el + mipsel + mipsn32 + mipsn32el + mipsn32r6 + mipsn32r6el + mipsr6 + mipsr6el + powerpc + ppc64 + ppc64el + riscv64 + s390x + sparc + sparc64 + ); + if (not exists $builtin_pie_arch{$arch}) { + $builtin_feature{hardening}{pie} = 0; + } + + if ($abi_bits != 32) { + $builtin_feature{abi}{lfs} = 1; + } + + # On glibc, new ports default to time64, old ports currently default + # to time32, so we track the latter as that is a list that is not + # going to grow further, and might shrink. + # On musl libc based systems all ports use time64. + my %time32_arch = map { $_ => 1 } qw( + arm + armeb + armel + armhf + hppa + i386 + hurd-i386 + kfreebsd-i386 + m68k + mips + mipsel + mipsn32 + mipsn32el + mipsn32r6 + mipsn32r6el + mipsr6 + mipsr6el + nios2 + powerpc + powerpcel + powerpcspe + s390 + sh3 + sh3eb + sh4 + sh4eb + sparc + ); + if ($abi_bits != 32 or + not exists $time32_arch{$arch} or + $libc eq 'musl') { + $builtin_feature{abi}{time64} = 1; + } + + $self->init_build_features(\%use_feature, \%builtin_feature); + + ## Setup + + require Dpkg::BuildOptions; + + # Adjust features based on user or maintainer's desires. + my $opts_build = Dpkg::BuildOptions->new(envvar => 'DEB_BUILD_OPTIONS'); + my $opts_maint = Dpkg::BuildOptions->new(envvar => 'DEB_BUILD_MAINT_OPTIONS'); + + foreach my $area (sort keys %use_feature) { + $opts_build->parse_features($area, $use_feature{$area}); + $opts_maint->parse_features($area, $use_feature{$area}); + } + + ## Area: abi + + if ($use_feature{abi}{time64} && ! $builtin_feature{abi}{time64}) { + # On glibc 64-bit time_t support requires LFS. + $use_feature{abi}{lfs} = 1 if $libc eq 'gnu'; + } + + # XXX: Handle lfs alias from future abi feature area. + $use_feature{abi}{lfs} //= $use_feature{future}{lfs}; + # XXX: Once the feature is set in the abi area, we always override the + # one in the future area. + $use_feature{future}{lfs} = $use_feature{abi}{lfs}; + + ## Area: qa + + $use_feature{qa}{'bug-implicit-func'} //= $use_feature{qa}{bug}; + + ## Area: reproducible + + # Mask features that might have an unsafe usage. + if ($use_feature{reproducible}{fixfilepath} or + $use_feature{reproducible}{fixdebugpath}) { + require Cwd; + + my $build_path =$ENV{DEB_BUILD_PATH} || Cwd::getcwd(); + + $flags->set_option_value('build-path', $build_path); + + # If we have any unsafe character in the path, disable the flag, + # so that we do not need to worry about escaping the characters + # on output. + if ($build_path =~ m/[^-+:.0-9a-zA-Z~\/_]/) { + $use_feature{reproducible}{fixfilepath} = 0; + $use_feature{reproducible}{fixdebugpath} = 0; + } + } + + ## Area: optimize + + if ($opts_build->has('noopt')) { + $flags->set_option_value('optimize-level', 0); + } else { + $flags->set_option_value('optimize-level', 2); + } + + ## Area: sanitize + + # Handle logical feature interactions. + if ($use_feature{sanitize}{address} and $use_feature{sanitize}{thread}) { + # Disable the thread sanitizer when the address one is active, they + # are mutually incompatible. + $use_feature{sanitize}{thread} = 0; + } + if ($use_feature{sanitize}{address} or $use_feature{sanitize}{thread}) { + # Disable leak sanitizer, it is implied by the address or thread ones. + $use_feature{sanitize}{leak} = 0; + } + + ## Area: hardening + + # Mask features that are not available on certain architectures. + if (none { $os eq $_ } qw(linux kfreebsd hurd) or + any { $cpu eq $_ } qw(alpha hppa ia64)) { + # Disabled on non-(linux/kfreebsd/hurd). + # Disabled on alpha, hppa, ia64. + $use_feature{hardening}{pie} = 0; + } + if (any { $cpu eq $_ } qw(ia64 alpha hppa nios2) or $arch eq 'arm') { + # Stack protector disabled on ia64, alpha, hppa, nios2. + # "warning: -fstack-protector not supported for this target" + # Stack protector disabled on arm (ok on armel). + # compiler supports it incorrectly (leads to SEGV) + $use_feature{hardening}{stackprotector} = 0; + } + if (none { $arch eq $_ } qw(amd64 arm64 armhf armel)) { + # Stack clash protector only available on amd64 and arm. + $use_feature{hardening}{stackclash} = 0; + } + if (any { $cpu eq $_ } qw(ia64 hppa)) { + # relro not implemented on ia64, hppa. + $use_feature{hardening}{relro} = 0; + } + if (none { $cpu eq $_ } qw(amd64 arm64)) { + # On amd64 use -fcf-protection. + # On arm64 use -mbranch-protection=standard. + $use_feature{hardening}{branch} = 0; + } + $flags->set_option_value('hardening-branch-cpu', $cpu); + + # Mask features that might be influenced by other flags. + if ($flags->get_option_value('optimize-level') == 0) { + # glibc 2.16 and later warn when using -O0 and _FORTIFY_SOURCE. + $use_feature{hardening}{fortify} = 0; + } + $flags->set_option_value('fortify-level', 2); + + # Handle logical feature interactions. + if ($use_feature{hardening}{relro} == 0) { + # Disable bindnow if relro is not enabled, since it has no + # hardening ability without relro and may incur load penalties. + $use_feature{hardening}{bindnow} = 0; + } + if ($use_feature{hardening}{stackprotector} == 0) { + # Disable stackprotectorstrong if stackprotector is disabled. + $use_feature{hardening}{stackprotectorstrong} = 0; + } + + ## Commit + + # Set used features to their builtin setting if unset. + foreach my $area (sort keys %builtin_feature) { + while (my ($feature, $enabled) = each %{$builtin_feature{$area}}) { + $flags->set_builtin($area, $feature, $enabled); + } + } + + # Store the feature usage. + foreach my $area (sort keys %use_feature) { + while (my ($feature, $enabled) = each %{$use_feature{$area}}) { + $flags->set_feature($area, $feature, $enabled); + } + } +} + +sub _add_build_flags { + my ($self, $flags) = @_; + + ## Global default flags + + my @compile_flags = qw( + CFLAGS + CXXFLAGS + OBJCFLAGS + OBJCXXFLAGS + FFLAGS + FCFLAGS + ); + + my $default_flags; + my $default_d_flags; + + my $optimize_level = $flags->get_option_value('optimize-level'); + $default_flags = "-g -O$optimize_level"; + if ($optimize_level == 0) { + $default_d_flags = '-fdebug'; + } else { + $default_d_flags = '-frelease'; + } + + $flags->append($_, $default_flags) foreach @compile_flags; + $flags->append($_ . '_FOR_BUILD', $default_flags) foreach @compile_flags; + $flags->append('DFLAGS', $default_d_flags); + $flags->append('DFLAGS_FOR_BUILD', $default_d_flags); + + ## Area: abi + + my %abi_builtins = $flags->get_builtins('abi'); + if ($flags->use_feature('abi', 'lfs') && ! $abi_builtins{lfs}) { + $flags->append('CPPFLAGS', + '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'); + } + + if ($flags->use_feature('abi', 'time64') && ! $abi_builtins{time64}) { + $flags->append('CPPFLAGS', '-D_TIME_BITS=64'); + } + + ## Area: qa + + # Warnings that detect actual bugs. + if ($flags->use_feature('qa', 'bug-implicit-func')) { + $flags->append('CFLAGS', '-Werror=implicit-function-declaration'); + } + if ($flags->use_feature('qa', 'bug')) { + # C/C++ flags + my @cfamilyflags = qw( + array-bounds + clobbered + volatile-register-var + ); + foreach my $warnflag (@cfamilyflags) { + $flags->append('CFLAGS', "-Werror=$warnflag"); + $flags->append('CXXFLAGS', "-Werror=$warnflag"); + } + } + + # Inject dummy canary options to detect issues with build flag propagation. + if ($flags->use_feature('qa', 'canary')) { + require Digest::MD5; + my $id = Digest::MD5::md5_hex(int rand 4096); + + foreach my $flag (qw(CPPFLAGS CFLAGS OBJCFLAGS CXXFLAGS OBJCXXFLAGS)) { + $flags->append($flag, "-D__DEB_CANARY_${flag}_${id}__"); + } + $flags->append('LDFLAGS', "-Wl,-z,deb-canary-${id}"); + } + + ## Area: reproducible + + # Warn when the __TIME__, __DATE__ and __TIMESTAMP__ macros are used. + if ($flags->use_feature('reproducible', 'timeless')) { + $flags->append('CPPFLAGS', '-Wdate-time'); + } + + # Avoid storing the build path in the binaries. + if ($flags->use_feature('reproducible', 'fixfilepath') or + $flags->use_feature('reproducible', 'fixdebugpath')) { + my $build_path = $flags->get_option_value('build-path'); + my $map; + + # -ffile-prefix-map is a superset of -fdebug-prefix-map, prefer it + # if both are set. + if ($flags->use_feature('reproducible', 'fixfilepath')) { + $map = '-ffile-prefix-map=' . $build_path . '=.'; + } else { + $map = '-fdebug-prefix-map=' . $build_path . '=.'; + } + + $flags->append($_, $map) foreach @compile_flags; + } + + ## Area: optimize + + if ($flags->use_feature('optimize', 'lto')) { + my $flag = '-flto=auto -ffat-lto-objects'; + $flags->append($_, $flag) foreach (@compile_flags, 'LDFLAGS'); + } + + ## Area: sanitize + + if ($flags->use_feature('sanitize', 'address')) { + my $flag = '-fsanitize=address -fno-omit-frame-pointer'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('LDFLAGS', '-fsanitize=address'); + } + + if ($flags->use_feature('sanitize', 'thread')) { + my $flag = '-fsanitize=thread'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('LDFLAGS', $flag); + } + + if ($flags->use_feature('sanitize', 'leak')) { + $flags->append('LDFLAGS', '-fsanitize=leak'); + } + + if ($flags->use_feature('sanitize', 'undefined')) { + my $flag = '-fsanitize=undefined'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('LDFLAGS', $flag); + } + + ## Area: hardening + + # PIE + my $use_pie = $flags->get_feature('hardening', 'pie'); + my %hardening_builtins = $flags->get_builtins('hardening'); + if (defined $use_pie && $use_pie && ! $hardening_builtins{pie}) { + my $flag = "-specs=$Dpkg::DATADIR/pie-compile.specs"; + $flags->append($_, $flag) foreach @compile_flags; + $flags->append('LDFLAGS', "-specs=$Dpkg::DATADIR/pie-link.specs"); + } elsif (defined $use_pie && ! $use_pie && $hardening_builtins{pie}) { + my $flag = "-specs=$Dpkg::DATADIR/no-pie-compile.specs"; + $flags->append($_, $flag) foreach @compile_flags; + $flags->append('LDFLAGS', "-specs=$Dpkg::DATADIR/no-pie-link.specs"); + } + + # Stack protector + if ($flags->use_feature('hardening', 'stackprotectorstrong')) { + my $flag = '-fstack-protector-strong'; + $flags->append($_, $flag) foreach @compile_flags; + } elsif ($flags->use_feature('hardening', 'stackprotector')) { + my $flag = '-fstack-protector --param=ssp-buffer-size=4'; + $flags->append($_, $flag) foreach @compile_flags; + } + + # Stack clash + if ($flags->use_feature('hardening', 'stackclash')) { + my $flag = '-fstack-clash-protection'; + $flags->append($_, $flag) foreach @compile_flags; + } + + # Fortify Source + if ($flags->use_feature('hardening', 'fortify')) { + my $fortify_level = $flags->get_option_value('fortify-level'); + $flags->append('CPPFLAGS', "-D_FORTIFY_SOURCE=$fortify_level"); + } + + # Format Security + if ($flags->use_feature('hardening', 'format')) { + my $flag = '-Wformat -Werror=format-security'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('OBJCFLAGS', $flag); + $flags->append('OBJCXXFLAGS', $flag); + } + + # Read-only Relocations + if ($flags->use_feature('hardening', 'relro')) { + $flags->append('LDFLAGS', '-Wl,-z,relro'); + } + + # Bindnow + if ($flags->use_feature('hardening', 'bindnow')) { + $flags->append('LDFLAGS', '-Wl,-z,now'); + } + + # Branch protection + if ($flags->use_feature('hardening', 'branch')) { + my $cpu = $flags->get_option_value('hardening-branch-cpu'); + my $flag; + if ($cpu eq 'arm64') { + $flag = '-mbranch-protection=standard'; + } elsif ($cpu eq 'amd64') { + $flag = '-fcf-protection'; + } + $flags->append($_, $flag) foreach @compile_flags; + } +} + +sub _build_tainted_by { + my $self = shift; + my %tainted; + + foreach my $pathname (qw(/bin /sbin /lib /lib32 /libo32 /libx32 /lib64)) { + next unless -l $pathname; + + my $linkname = readlink $pathname; + if ($linkname eq "usr$pathname" or $linkname eq "/usr$pathname") { + $tainted{'merged-usr-via-aliased-dirs'} = 1; + last; + } + } + + require File::Find; + my %usr_local_types = ( + configs => [ qw(etc) ], + includes => [ qw(include) ], + programs => [ qw(bin sbin) ], + libraries => [ qw(lib) ], + ); + foreach my $type (keys %usr_local_types) { + File::Find::find({ + wanted => sub { $tainted{"usr-local-has-$type"} = 1 if -f }, + no_chdir => 1, + }, grep { -d } map { "/usr/local/$_" } @{$usr_local_types{$type}}); + } + + my @tainted = sort keys %tainted; + return @tainted; +} + +=head1 CHANGES + +=head2 Version 0.xx + +This is a private module. + +=cut + +1; |