summaryrefslogtreecommitdiffstats
path: root/scripts/Dpkg/Vendor
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scripts/Dpkg/Vendor.pm289
-rw-r--r--scripts/Dpkg/Vendor/Debian.pm522
-rw-r--r--scripts/Dpkg/Vendor/Default.pm229
-rw-r--r--scripts/Dpkg/Vendor/Devuan.pm68
-rw-r--r--scripts/Dpkg/Vendor/Ubuntu.pm174
5 files changed, 1282 insertions, 0 deletions
diff --git a/scripts/Dpkg/Vendor.pm b/scripts/Dpkg/Vendor.pm
new file mode 100644
index 0000000..059e442
--- /dev/null
+++ b/scripts/Dpkg/Vendor.pm
@@ -0,0 +1,289 @@
+# Copyright © 2008-2009 Raphaël Hertzog <hertzog@debian.org>
+# Copyright © 2008-2009, 2012-2017, 2022 Guillem Jover <guillem@debian.org>
+#
+# 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/>.
+
+package Dpkg::Vendor;
+
+use strict;
+use warnings;
+use feature qw(state);
+
+our $VERSION = '1.02';
+our @EXPORT_OK = qw(
+ get_current_vendor
+ get_vendor_info
+ get_vendor_file
+ get_vendor_dir
+ get_vendor_object
+ run_vendor_hook
+);
+
+use Exporter qw(import);
+use List::Util qw(uniq);
+
+use Dpkg ();
+use Dpkg::ErrorHandling;
+use Dpkg::Gettext;
+use Dpkg::BuildEnv;
+use Dpkg::Control::HashCore;
+
+my $origins = "$Dpkg::CONFDIR/origins";
+$origins = $ENV{DPKG_ORIGINS_DIR} if $ENV{DPKG_ORIGINS_DIR};
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::Vendor - get access to some vendor specific information
+
+=head1 DESCRIPTION
+
+The files in $Dpkg::CONFDIR/origins/ can provide information about various
+vendors who are providing Debian packages. Currently those files look like
+this:
+
+ Vendor: Debian
+ Vendor-URL: https://www.debian.org/
+ Bugs: debbugs://bugs.debian.org
+
+If the vendor derives from another vendor, the file should document
+the relationship by listing the base distribution in the Parent field:
+
+ Parent: Debian
+
+The file should be named according to the vendor name. The usual convention
+is to name the vendor file using the vendor name in all lowercase, but some
+variation is permitted. Namely, spaces are mapped to dashes ('-'), and the
+file can have the same casing as the Vendor field, or it can be capitalized.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item $dir = get_vendor_dir()
+
+Returns the current dpkg origins directory name, where the vendor files
+are stored.
+
+=cut
+
+sub get_vendor_dir {
+ return $origins;
+}
+
+=item $fields = get_vendor_info($name)
+
+Returns a Dpkg::Control object with the information parsed from the
+corresponding vendor file in $Dpkg::CONFDIR/origins/. If $name is omitted,
+it will use $Dpkg::CONFDIR/origins/default which is supposed to be a symlink
+to the vendor of the currently installed operating system. Returns undef
+if there's no file for the given vendor.
+
+=cut
+
+my $vendor_sep_regex = qr{[^A-Za-z0-9]+};
+
+sub get_vendor_info(;$) {
+ my $vendor = shift || 'default';
+ my $vendor_key = lc $vendor =~ s{$vendor_sep_regex}{}gr;
+ state %VENDOR_CACHE;
+ return $VENDOR_CACHE{$vendor_key} if exists $VENDOR_CACHE{$vendor_key};
+
+ my $file = get_vendor_file($vendor);
+ return unless $file;
+ my $fields = Dpkg::Control::HashCore->new();
+ $fields->load($file, compression => 0) or error(g_('%s is empty'), $file);
+ $VENDOR_CACHE{$vendor_key} = $fields;
+ return $fields;
+}
+
+=item $name = get_vendor_file($name)
+
+Check if there's a file for the given vendor and returns its
+name.
+
+The vendor filename will be derived from the vendor name, by replacing any
+number of non-alphanumeric characters (that is B<[^A-Za-z0-9]>) into "B<->",
+then the resulting name will be tried in sequence by lower-casing it,
+keeping it as is, lower-casing then capitalizing it, and capitalizing it.
+
+In addition, for historical and backwards compatibility, the name will
+be tried keeping it as is without non-alphanumeric characters remapping,
+then the resulting name will be tried in sequence by lower-casing it,
+keeping it as is, lower-casing then capitalizing it, and capitalizing it.
+And finally the name will be tried by replacing only spaces to "B<->",
+then the resulting name will be tried in sequence by lower-casing it,
+keeping it as is, lower-casing then capitalizing it, and capitalizing it.
+
+But these backwards compatible name lookups will be removed during
+the dpkg 1.22.x release cycle.
+
+=cut
+
+sub get_vendor_file(;$) {
+ my $vendor = shift || 'default';
+
+ my @names;
+ my $vendor_sep = $vendor =~ s{$vendor_sep_regex}{-}gr;
+ push @names, lc $vendor_sep, $vendor_sep, ucfirst lc $vendor_sep, ucfirst $vendor_sep;
+
+ # XXX: Backwards compatibility, remove on 1.22.x.
+ my %name_seen = map { $_ => 1 } @names;
+ my @obsolete_names = uniq grep {
+ my $seen = exists $name_seen{$_};
+ $name_seen{$_} = 1;
+ not $seen;
+ } (
+ (lc $vendor, $vendor, ucfirst lc $vendor, ucfirst $vendor),
+ ($vendor =~ s{\s+}{-}g) ?
+ (lc $vendor, $vendor, ucfirst lc $vendor, ucfirst $vendor) : ()
+ );
+ my %obsolete_name = map { $_ => 1 } @obsolete_names;
+ push @names, @obsolete_names;
+
+ foreach my $name (uniq @names) {
+ next unless -e "$origins/$name";
+ if (exists $obsolete_name{$name}) {
+ warning(g_('%s origin filename is deprecated; ' .
+ 'it should have only alphanumeric or dash characters'),
+ $name);
+ }
+ return "$origins/$name";
+ }
+ return;
+}
+
+=item $name = get_current_vendor()
+
+Returns the name of the current vendor. If DEB_VENDOR is set, it uses
+that first, otherwise it falls back to parsing $Dpkg::CONFDIR/origins/default.
+If that file doesn't exist, it returns undef.
+
+=cut
+
+sub get_current_vendor() {
+ my $f;
+ if (Dpkg::BuildEnv::has('DEB_VENDOR')) {
+ $f = get_vendor_info(Dpkg::BuildEnv::get('DEB_VENDOR'));
+ return $f->{'Vendor'} if defined $f;
+ }
+ $f = get_vendor_info();
+ return $f->{'Vendor'} if defined $f;
+ return;
+}
+
+=item $object = get_vendor_object($name)
+
+Return the Dpkg::Vendor::* object of the corresponding vendor.
+If $name is omitted, return the object of the current vendor.
+If no vendor can be identified, then return the Dpkg::Vendor::Default
+object.
+
+The module name will be derived from the vendor name, by splitting parts
+around groups of non alphanumeric character (that is B<[^A-Za-z0-9]>)
+separators, by either capitalizing or lower-casing and capitalizing each part
+and then joining them without the separators. So the expected casing is based
+on the one from the B<Vendor> field in the F<origins> file.
+
+In addition, for historical and backwards compatibility, the module name
+will also be looked up without non-alphanumeric character stripping, by
+capitalizing, lower-casing then capitalizing, as-is or lower-casing.
+But these name lookups will be removed during the 1.22.x release cycle.
+
+=cut
+
+sub get_vendor_object {
+ my $vendor = shift || get_current_vendor() || 'Default';
+ my $vendor_key = lc $vendor =~ s{$vendor_sep_regex}{}gr;
+ state %OBJECT_CACHE;
+ return $OBJECT_CACHE{$vendor_key} if exists $OBJECT_CACHE{$vendor_key};
+
+ my ($obj, @names);
+
+ my @vendor_parts = split m{$vendor_sep_regex}, $vendor;
+ push @names, join q{}, map { ucfirst } @vendor_parts;
+ push @names, join q{}, map { ucfirst lc } @vendor_parts;
+
+ # XXX: Backwards compatibility, remove on 1.22.x.
+ my %name_seen = map { $_ => 1 } @names;
+ my @obsolete_names = uniq grep {
+ my $seen = exists $name_seen{$_};
+ $name_seen{$_} = 1;
+ not $seen;
+ } (ucfirst $vendor, ucfirst lc $vendor, $vendor, lc $vendor);
+ my %obsolete_name = map { $_ => 1 } @obsolete_names;
+ push @names, @obsolete_names;
+
+ foreach my $name (uniq @names) {
+ eval qq{
+ pop \@INC if \$INC[-1] eq '.';
+ require Dpkg::Vendor::$name;
+ \$obj = Dpkg::Vendor::$name->new();
+ };
+ unless ($@) {
+ $OBJECT_CACHE{$vendor_key} = $obj;
+ if (exists $obsolete_name{$name}) {
+ warning(g_('%s module name is deprecated; ' .
+ 'it should be capitalized with only alphanumeric characters'),
+ "Dpkg::Vendor::$name");
+ }
+ return $obj;
+ }
+ }
+
+ my $info = get_vendor_info($vendor);
+ if (defined $info and defined $info->{'Parent'}) {
+ return get_vendor_object($info->{'Parent'});
+ } else {
+ return get_vendor_object('Default');
+ }
+}
+
+=item run_vendor_hook($hookid, @params)
+
+Run a hook implemented by the current vendor object.
+
+=cut
+
+sub run_vendor_hook {
+ my $vendor_obj = get_vendor_object();
+ $vendor_obj->run_hook(@_);
+}
+
+=back
+
+=head1 CHANGES
+
+=head2 Version 1.02 (dpkg 1.21.10)
+
+Deprecated behavior: get_vendor_file() loading vendor files with no special
+characters remapping. get_vendor_object() loading vendor module names with
+no special character stripping.
+
+=head2 Version 1.01 (dpkg 1.17.0)
+
+New function: get_vendor_dir().
+
+=head2 Version 1.00 (dpkg 1.16.1)
+
+Mark the module as public.
+
+=head1 SEE ALSO
+
+deb-origin(5).
+
+=cut
+
+1;
diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm
new file mode 100644
index 0000000..06aa49a
--- /dev/null
+++ b/scripts/Dpkg/Vendor/Debian.pm
@@ -0,0 +1,522 @@
+# 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/>.
+
+package Dpkg::Vendor::Debian;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+use Dpkg;
+use Dpkg::Gettext;
+use Dpkg::ErrorHandling;
+use Dpkg::Control::Types;
+
+use parent qw(Dpkg::Vendor::Default);
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::Vendor::Debian - Debian vendor class
+
+=head1 DESCRIPTION
+
+This vendor class customizes the behaviour of dpkg scripts for Debian
+specific behavior and policies.
+
+=cut
+
+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 => {
+ lfs => 0,
+ },
+ qa => {
+ bug => 0,
+ 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,
+ fortify => 1,
+ format => 1,
+ relro => 1,
+ bindnow => 0,
+ },
+ );
+
+ my %builtin_feature = (
+ hardening => {
+ pie => 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});
+ }
+
+ require Dpkg::Arch;
+
+ my $arch = Dpkg::Arch::get_host_arch();
+ my ($abi, $libc, $os, $cpu) = Dpkg::Arch::debarch_to_debtuple($arch);
+
+ unless (defined $abi and defined $libc and defined $os and defined $cpu) {
+ warning(g_("unknown host architecture '%s'"), $arch);
+ ($abi, $os, $cpu) = ('', '', '');
+ }
+
+ ## Area: future
+
+ if ($use_feature{future}{lfs}) {
+ my ($abi_bits, $abi_endian) = Dpkg::Arch::debarch_to_abiattrs($arch);
+ my $cpu_bits = Dpkg::Arch::debarch_to_cpubits($arch);
+
+ if ($abi_bits != 32 or $cpu_bits != 32) {
+ $use_feature{future}{lfs} = 0;
+ }
+ }
+
+ ## 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 builtin features that are not enabled by default in the compiler.
+ my %builtin_pie_arch = map { $_ => 1 } qw(
+ amd64
+ arm64
+ armel
+ armhf
+ hurd-i386
+ i386
+ kfreebsd-amd64
+ kfreebsd-i386
+ mips
+ mipsel
+ mips64el
+ powerpc
+ ppc64
+ ppc64el
+ riscv64
+ s390x
+ sparc
+ sparc64
+ );
+ if (not exists $builtin_pie_arch{$arch}) {
+ $builtin_feature{hardening}{pie} = 0;
+ }
+
+ # Mask features that are not available on certain architectures.
+ if ($os !~ /^(?:linux|kfreebsd|knetbsd|hurd)$/ or
+ $cpu =~ /^(?:hppa|avr32)$/) {
+ # Disabled on non-(linux/kfreebsd/knetbsd/hurd).
+ # Disabled on hppa, avr32
+ # (#574716).
+ $use_feature{hardening}{pie} = 0;
+ }
+ if ($cpu =~ /^(?: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 ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
+ # relro not implemented on ia64, hppa, avr32.
+ $use_feature{hardening}{relro} = 0;
+ }
+
+ # 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;
+ }
+
+ # 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
+ GCJFLAGS
+ );
+
+ 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('DFLAGS', $default_d_flags);
+
+ ## Area: future
+
+ if ($flags->use_feature('future', 'lfs')) {
+ $flags->append('CPPFLAGS',
+ '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64');
+ }
+
+ ## Area: qa
+
+ # Warnings that detect actual bugs.
+ if ($flags->use_feature('qa', 'bug')) {
+ # C flags
+ my @cflags = qw(
+ implicit-function-declaration
+ );
+ foreach my $warnflag (@cflags) {
+ $flags->append('CFLAGS', "-Werror=$warnflag");
+ }
+
+ # 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;
+ }
+
+ # Fortify Source
+ if ($flags->use_feature('hardening', 'fortify')) {
+ $flags->append('CPPFLAGS', '-D_FORTIFY_SOURCE=2');
+ }
+
+ # 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');
+ }
+}
+
+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;
diff --git a/scripts/Dpkg/Vendor/Default.pm b/scripts/Dpkg/Vendor/Default.pm
new file mode 100644
index 0000000..8362b5b
--- /dev/null
+++ b/scripts/Dpkg/Vendor/Default.pm
@@ -0,0 +1,229 @@
+# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org>
+#
+# 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/>.
+
+package Dpkg::Vendor::Default;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+# If you use this file as template to create a new vendor class, please
+# uncomment the following lines
+#use parent qw(Dpkg::Vendor::Default);
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::Vendor::Default - default vendor class
+
+=head1 DESCRIPTION
+
+A vendor class is used to provide vendor specific behaviour
+in various places. This is the default class used in case
+there's none for the current vendor or in case the vendor could
+not be identified (see Dpkg::Vendor documentation).
+
+It provides some hooks that are called by various dpkg-* tools.
+If you need a new hook, please file a bug against dpkg-dev and explain
+your need. Note that the hook API has no guarantee to be stable over an
+extended period of time. If you run an important distribution that makes
+use of vendor hooks, you'd better submit them for integration so that
+we avoid breaking your code.
+
+=head1 METHODS
+
+=over 4
+
+=item $vendor_obj = Dpkg::Vendor::Default->new()
+
+Creates the default vendor object. Can be inherited by all vendor objects
+if they don't need any specific initialization at object creation time.
+
+=cut
+
+sub new {
+ my $this = shift;
+ my $class = ref($this) || $this;
+ my $self = {};
+ bless $self, $class;
+ return $self;
+}
+
+=item $vendor_obj->run_hook($id, @params)
+
+Run the corresponding hook. The parameters are hook-specific. The
+supported hooks are:
+
+=over 8
+
+=item before-source-build ($srcpkg)
+
+The first parameter is a Dpkg::Source::Package object. The hook is called
+just before the execution of $srcpkg->build().
+
+=item package-keyrings ()
+
+The hook is called when dpkg-source is checking a signature on a source
+package (since dpkg 1.18.11). It takes no parameters, but returns a
+(possibly empty) list of vendor-specific keyrings.
+
+=item archive-keyrings ()
+
+The hook is called when there is a need to check signatures on artifacts
+from repositories, for example by a download method (since dpkg 1.18.11).
+It takes no parameters, but returns a (possibly empty) list of
+vendor-specific keyrings.
+
+=item archive-keyrings-historic ()
+
+The hook is called when there is a need to check signatures on artifacts
+from historic repositories, for example by a download method
+(since dpkg 1.18.11). It takes no parameters, but returns a (possibly empty)
+list of vendor-specific keyrings.
+
+=item builtin-build-depends ()
+
+The hook is called when dpkg-checkbuilddeps is initializing the source
+package build dependencies (since dpkg 1.18.2). It takes no parameters,
+but returns a (possibly empty) list of vendor-specific B<Build-Depends>.
+
+=item builtin-build-conflicts ()
+
+The hook is called when dpkg-checkbuilddeps is initializing the source
+package build conflicts (since dpkg 1.18.2). It takes no parameters,
+but returns a (possibly empty) list of vendor-specific B<Build-Conflicts>.
+
+=item register-custom-fields ()
+
+The hook is called in Dpkg::Control::Fields to register custom fields.
+You should return a list of arrays. Each array is an operation to perform.
+The first item is the name of the operation and corresponds
+to a field_* function provided by Dpkg::Control::Fields. The remaining
+fields are the parameters that are passed unchanged to the corresponding
+function.
+
+Known operations are "register", "insert_after" and "insert_before".
+
+=item post-process-changelog-entry ($fields)
+
+The hook is called in Dpkg::Changelog to post-process a
+Dpkg::Changelog::Entry after it has been created and filled with the
+appropriate values.
+
+=item update-buildflags ($flags)
+
+The hook is called in Dpkg::BuildFlags to allow the vendor to override
+the default values set for the various build flags. $flags is a
+Dpkg::BuildFlags object.
+
+=item builtin-system-build-paths ()
+
+The hook is called by dpkg-genbuildinfo to determine if the current path
+should be recorded in the B<Build-Path> field (since dpkg 1.18.11). It takes
+no parameters, but returns a (possibly empty) list of root paths considered
+acceptable. As an example, if the list contains "/build/", a Build-Path
+field will be created if the current directory is "/build/dpkg-1.18.0". If
+the list contains "/", the path will always be recorded. If the list is
+empty, the current path will never be recorded.
+
+=item build-tainted-by ()
+
+The hook is called by dpkg-genbuildinfo to determine if the current system
+has been tainted in some way that could affect the resulting build, which
+will be recorded in the B<Build-Tainted-By> field (since dpkg 1.19.5). It
+takes no parameters, but returns a (possibly empty) list of tainted reason
+tags (formed by alphanumeric and dash characters).
+
+=item sanitize-environment ()
+
+The hook is called by dpkg-buildpackage to sanitize its build environment
+(since dpkg 1.20.0).
+
+=item backport-version-regex ()
+
+The hook is called by dpkg-genchanges and dpkg-mergechangelog to determine
+the backport version string that should be specially handled as not an earlier
+than version or remapped so that it does not get considered as a pre-release
+(since dpkg 1.21.3).
+The returned string is a regex with one capture group for the backport
+delimiter string, or undef if there is no regex.
+
+=back
+
+=cut
+
+sub run_hook {
+ my ($self, $hook, @params) = @_;
+
+ if ($hook eq 'before-source-build') {
+ my $srcpkg = shift @params;
+ } elsif ($hook eq 'package-keyrings') {
+ return ();
+ } elsif ($hook eq 'archive-keyrings') {
+ return ();
+ } elsif ($hook eq 'archive-keyrings-historic') {
+ return ();
+ } elsif ($hook eq 'register-custom-fields') {
+ return ();
+ } elsif ($hook eq 'builtin-build-depends') {
+ return ();
+ } elsif ($hook eq 'builtin-build-conflicts') {
+ return ();
+ } elsif ($hook eq 'post-process-changelog-entry') {
+ my $fields = shift @params;
+ } elsif ($hook eq 'extend-patch-header') {
+ my ($textref, $ch_info) = @params;
+ } elsif ($hook eq 'update-buildflags') {
+ my $flags = shift @params;
+ } elsif ($hook eq 'builtin-system-build-paths') {
+ return ();
+ } elsif ($hook eq 'build-tainted-by') {
+ return ();
+ } elsif ($hook eq 'sanitize-environment') {
+ return;
+ } elsif ($hook eq 'backport-version-regex') {
+ return;
+ }
+
+ # Default return value for unknown/unimplemented hooks
+ return;
+}
+
+=item $vendor->set_build_features($flags)
+
+Sets the vendor build features, which will then be used to initialize the
+build flags.
+
+=cut
+
+sub set_build_features {
+ my ($self, $flags) = @_;
+
+ return;
+}
+
+=back
+
+=head1 CHANGES
+
+=head2 Version 0.xx
+
+This is a private module.
+
+=cut
+
+1;
diff --git a/scripts/Dpkg/Vendor/Devuan.pm b/scripts/Dpkg/Vendor/Devuan.pm
new file mode 100644
index 0000000..661dc9e
--- /dev/null
+++ b/scripts/Dpkg/Vendor/Devuan.pm
@@ -0,0 +1,68 @@
+# Copyright © 2022 Guillem Jover <guillem@debian.org>
+#
+# 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/>.
+
+package Dpkg::Vendor::Devuan;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+use parent qw(Dpkg::Vendor::Debian);
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::Vendor::Devuan - Devuan vendor class
+
+=head1 DESCRIPTION
+
+This vendor class customizes the behaviour of dpkg scripts for Devuan
+specific behavior and policies.
+
+=cut
+
+sub run_hook {
+ my ($self, $hook, @params) = @_;
+
+ if ($hook eq 'package-keyrings') {
+ return ('/usr/share/keyrings/devuan-keyring.gpg',
+ '/usr/share/keyrings/devuan-maintainers.gpg');
+ } elsif ($hook eq 'archive-keyrings') {
+ return ('/usr/share/keyrings/devuan-archive-keyring.gpg');
+ } elsif ($hook eq 'archive-keyrings-historic') {
+ return ('/usr/share/keyrings/devuan-archive-removed-keys.gpg');
+ } 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-Devuan: https://bugs.devuan.org/$bug\n";
+ }
+ }
+ } else {
+ return $self->SUPER::run_hook($hook, @params);
+ }
+}
+
+=head1 CHANGES
+
+=head2 Version 0.xx
+
+This is a private module.
+
+=cut
+
+1;
diff --git a/scripts/Dpkg/Vendor/Ubuntu.pm b/scripts/Dpkg/Vendor/Ubuntu.pm
new file mode 100644
index 0000000..9c77519
--- /dev/null
+++ b/scripts/Dpkg/Vendor/Ubuntu.pm
@@ -0,0 +1,174 @@
+# Copyright © 2008 Ian Jackson <ijackson@chiark.greenend.org.uk>
+# Copyright © 2008 Canonical, Ltd.
+# written by Colin Watson <cjwatson@ubuntu.com>
+# Copyright © 2008 James Westby <jw+debian@jameswestby.net>
+# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org>
+#
+# 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/>.
+
+package Dpkg::Vendor::Ubuntu;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+use List::Util qw(any);
+
+use Dpkg::ErrorHandling;
+use Dpkg::Gettext;
+use Dpkg::Control::Types;
+
+use parent qw(Dpkg::Vendor::Debian);
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::Vendor::Ubuntu - Ubuntu vendor class
+
+=head1 DESCRIPTION
+
+This vendor class customizes the behaviour of dpkg scripts for Ubuntu
+specific behavior and policies.
+
+=cut
+
+sub run_hook {
+ my ($self, $hook, @params) = @_;
+
+ if ($hook eq 'before-source-build') {
+ my $src = shift @params;
+ my $fields = $src->{fields};
+
+ # check that Maintainer/XSBC-Original-Maintainer comply to
+ # https://wiki.ubuntu.com/DebianMaintainerField
+ if (defined($fields->{'Version'}) and defined($fields->{'Maintainer'}) and
+ $fields->{'Version'} =~ /ubuntu/) {
+ if ($fields->{'Maintainer'} !~ /(?:ubuntu|canonical)/i) {
+ if (length $ENV{DEBEMAIL} and $ENV{DEBEMAIL} =~ /\@(?:ubuntu|canonical)\.com/) {
+ error(g_('Version number suggests Ubuntu changes, but Maintainer: does not have Ubuntu address'));
+ } else {
+ warning(g_('Version number suggests Ubuntu changes, but Maintainer: does not have Ubuntu address'));
+ }
+ }
+ unless ($fields->{'Original-Maintainer'}) {
+ warning(g_('Version number suggests Ubuntu changes, but there is no XSBC-Original-Maintainer field'));
+ }
+ }
+ } elsif ($hook eq 'package-keyrings') {
+ return ($self->SUPER::run_hook($hook),
+ '/usr/share/keyrings/ubuntu-archive-keyring.gpg');
+ } elsif ($hook eq 'archive-keyrings') {
+ return ($self->SUPER::run_hook($hook),
+ '/usr/share/keyrings/ubuntu-archive-keyring.gpg');
+ } elsif ($hook eq 'archive-keyrings-historic') {
+ return ($self->SUPER::run_hook($hook),
+ '/usr/share/keyrings/ubuntu-archive-removed-keys.gpg');
+ } elsif ($hook eq 'register-custom-fields') {
+ my @field_ops = $self->SUPER::run_hook($hook);
+ push @field_ops, [
+ 'register', 'Launchpad-Bugs-Fixed',
+ CTRL_FILE_CHANGES | CTRL_CHANGELOG,
+ ], [
+ 'insert_after', CTRL_FILE_CHANGES, 'Closes', 'Launchpad-Bugs-Fixed',
+ ], [
+ 'insert_after', CTRL_CHANGELOG, 'Closes', 'Launchpad-Bugs-Fixed',
+ ];
+ return @field_ops;
+ } elsif ($hook eq 'post-process-changelog-entry') {
+ my $fields = shift @params;
+
+ # Add Launchpad-Bugs-Fixed field
+ my $bugs = find_launchpad_closes($fields->{'Changes'} // '');
+ if (scalar(@$bugs)) {
+ $fields->{'Launchpad-Bugs-Fixed'} = join(' ', @$bugs);
+ }
+ } elsif ($hook eq 'update-buildflags') {
+ my $flags = shift @params;
+
+ # Run the Debian hook to add hardening flags
+ $self->SUPER::run_hook($hook, $flags);
+
+ # Per https://wiki.ubuntu.com/DistCompilerFlags
+ $flags->prepend('LDFLAGS', '-Wl,-Bsymbolic-functions');
+ } else {
+ return $self->SUPER::run_hook($hook, @params);
+ }
+}
+
+# Override Debian default features.
+sub init_build_features {
+ my ($self, $use_feature, $builtin_feature) = @_;
+
+ $self->SUPER::init_build_features($use_feature, $builtin_feature);
+
+ require Dpkg::Arch;
+ my $arch = Dpkg::Arch::get_host_arch();
+
+ if (any { $_ eq $arch } qw(amd64 arm64 ppc64el s390x)) {
+ $use_feature->{optimize}{lto} = 1;
+ }
+}
+
+sub set_build_features {
+ my ($self, $flags) = @_;
+
+ $self->SUPER::set_build_features($flags);
+
+ require Dpkg::Arch;
+ my $arch = Dpkg::Arch::get_host_arch();
+
+ if ($arch eq 'ppc64el' && $flags->get_option_value('optimize-level') != 0) {
+ $flags->set_option_value('optimize-level', 3);
+ }
+}
+
+=head1 PUBLIC FUNCTIONS
+
+=over
+
+=item $bugs = Dpkg::Vendor::Ubuntu::find_launchpad_closes($changes)
+
+Takes one string as argument and finds "LP: #123456, #654321" statements,
+which are references to bugs on Launchpad. Returns all closed bug
+numbers in an array reference.
+
+=cut
+
+sub find_launchpad_closes {
+ my $changes = shift;
+ my %closes;
+
+ while ($changes &&
+ ($changes =~ /lp:\s+\#\d+(?:,\s*\#\d+)*/pig)) {
+ $closes{$_} = 1 foreach (${^MATCH} =~ /\#?\s?(\d+)/g);
+ }
+
+ my @closes = sort { $a <=> $b } keys %closes;
+
+ return \@closes;
+}
+
+=back
+
+=head1 CHANGES
+
+=head2 Version 0.xx
+
+This is a semi-private module. Only documented functions are public.
+
+=cut
+
+1;