summaryrefslogtreecommitdiffstats
path: root/scripts/Dpkg/Vendor
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:35:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:35:28 +0000
commitea314d2f45c40a006c0104157013ab4b857f665f (patch)
tree3ef2971cb3675c318b8d9effd987854ad3f6d3e8 /scripts/Dpkg/Vendor
parentInitial commit. (diff)
downloaddpkg-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')
-rw-r--r--scripts/Dpkg/Vendor/Debian.pm633
-rw-r--r--scripts/Dpkg/Vendor/Default.pm231
-rw-r--r--scripts/Dpkg/Vendor/Devuan.pm68
-rw-r--r--scripts/Dpkg/Vendor/Ubuntu.pm176
4 files changed, 1108 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;
diff --git a/scripts/Dpkg/Vendor/Default.pm b/scripts/Dpkg/Vendor/Default.pm
new file mode 100644
index 0000000..fc0e6be
--- /dev/null
+++ b/scripts/Dpkg/Vendor/Default.pm
@@ -0,0 +1,231 @@
+# 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/>.
+
+=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 L<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.
+
+B<Note>: This is a private module, its API can change at any time.
+
+=cut
+
+package Dpkg::Vendor::Default 0.01;
+
+use strict;
+use warnings;
+
+# If you use this file as template to create a new vendor class, please
+# uncomment the following lines
+#use parent qw(Dpkg::Vendor::Default);
+
+=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 L<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 L<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 L<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 L<Dpkg::Changelog> to post-process a
+L<Dpkg::Changelog::Entry> after it has been created and filled with the
+appropriate values.
+
+=item update-buildflags ($flags)
+
+The hook is called in L<Dpkg::BuildFlags> to allow the vendor to override
+the default values set for the various build flags. $flags is a
+L<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..1ff5adf
--- /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/>.
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::Vendor::Devuan - Devuan vendor class
+
+=head1 DESCRIPTION
+
+This vendor class customizes the behavior of dpkg scripts for Devuan
+specific behavior and policies.
+
+B<Note>: This is a private module, its API can change at any time.
+
+=cut
+
+package Dpkg::Vendor::Devuan 0.01;
+
+use strict;
+use warnings;
+
+use parent qw(Dpkg::Vendor::Debian);
+
+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..b50da37
--- /dev/null
+++ b/scripts/Dpkg/Vendor/Ubuntu.pm
@@ -0,0 +1,176 @@
+# 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/>.
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::Vendor::Ubuntu - Ubuntu vendor class
+
+=head1 DESCRIPTION
+
+This vendor class customizes the behavior of dpkg scripts for Ubuntu
+specific behavior and policies.
+
+B<Note>: This is a private module, its API can change at any time.
+
+=cut
+
+package Dpkg::Vendor::Ubuntu 0.01;
+
+use strict;
+use warnings;
+
+use List::Util qw(any);
+
+use Dpkg::ErrorHandling;
+use Dpkg::Gettext;
+use Dpkg::Control::Types;
+
+use parent qw(Dpkg::Vendor::Debian);
+
+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);
+ }
+
+ $flags->set_option_value('fortify-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;