diff options
Diffstat (limited to 'scripts/Dpkg/Vendor')
-rw-r--r-- | scripts/Dpkg/Vendor/Debian.pm | 484 | ||||
-rw-r--r-- | scripts/Dpkg/Vendor/Default.pm | 201 | ||||
-rw-r--r-- | scripts/Dpkg/Vendor/Ubuntu.pm | 165 |
3 files changed, 850 insertions, 0 deletions
diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm new file mode 100644 index 0000000..a352bbd --- /dev/null +++ b/scripts/Dpkg/Vendor/Debian.pm @@ -0,0 +1,484 @@ +# 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 object + +=head1 DESCRIPTION + +This vendor object 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-maintainers.gpg'); + } elsif ($hook eq 'keyrings') { + warnings::warnif('deprecated', 'deprecated keyrings vendor hook'); + return $self->run_hook('package-keyrings', @params); + } 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->_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(); + } else { + return $self->SUPER::run_hook($hook, @params); + } +} + +sub _add_build_flags { + my ($self, $flags) = @_; + + # Default feature states. + my %use_feature = ( + future => { + lfs => 0, + }, + qa => { + bug => 0, + canary => 0, + }, + reproducible => { + timeless => 1, + fixfilepath => 0, + fixdebugpath => 1, + }, + 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, + }, + ); + + ## 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) = ('', '', ''); + } + + ## Global defaults + + my $default_flags; + if ($opts_build->has('noopt')) { + $default_flags = '-g -O0'; + } else { + $default_flags = '-g -O2'; + } + $flags->append('CFLAGS', $default_flags); + $flags->append('CXXFLAGS', $default_flags); + $flags->append('OBJCFLAGS', $default_flags); + $flags->append('OBJCXXFLAGS', $default_flags); + $flags->append('FFLAGS', $default_flags); + $flags->append('FCFLAGS', $default_flags); + $flags->append('GCJFLAGS', $default_flags); + + ## 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 and $cpu_bits == 32) { + $flags->append('CPPFLAGS', + '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'); + } + } + + ## Area: qa + + # Warnings that detect actual bugs. + if ($use_feature{qa}{bug}) { + foreach my $warnflag (qw(array-bounds clobbered volatile-register-var + implicit-function-declaration)) { + $flags->append('CFLAGS', "-Werror=$warnflag"); + $flags->append('CXXFLAGS', "-Werror=$warnflag"); + } + } + + # Inject dummy canary options to detect issues with build flag propagation. + if ($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 + + my $build_path; + + # Mask features that might have an unsafe usage. + if ($use_feature{reproducible}{fixfilepath} or + $use_feature{reproducible}{fixdebugpath}) { + require Cwd; + + $build_path = $ENV{DEB_BUILD_PATH} || Cwd::getcwd(); + + # 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; + } + } + + # Warn when the __TIME__, __DATE__ and __TIMESTAMP__ macros are used. + if ($use_feature{reproducible}{timeless}) { + $flags->append('CPPFLAGS', '-Wdate-time'); + } + + # Avoid storing the build path in the binaries. + if ($use_feature{reproducible}{fixfilepath} or + $use_feature{reproducible}{fixdebugpath}) { + my $map; + + # -ffile-prefix-map is a superset of -fdebug-prefix-map, prefer it + # if both are set. + if ($use_feature{reproducible}{fixfilepath}) { + $map = '-ffile-prefix-map=' . $build_path . '=.'; + } else { + $map = '-fdebug-prefix-map=' . $build_path . '=.'; + } + + $flags->append('CFLAGS', $map); + $flags->append('CXXFLAGS', $map); + $flags->append('OBJCFLAGS', $map); + $flags->append('OBJCXXFLAGS', $map); + $flags->append('FFLAGS', $map); + $flags->append('FCFLAGS', $map); + $flags->append('GCJFLAGS', $map); + } + + ## 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; + } + + if ($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 ($use_feature{sanitize}{thread}) { + my $flag = '-fsanitize=thread'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('LDFLAGS', $flag); + } + + if ($use_feature{sanitize}{leak}) { + $flags->append('LDFLAGS', '-fsanitize=leak'); + } + + if ($use_feature{sanitize}{undefined}) { + my $flag = '-fsanitize=undefined'; + $flags->append('CFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('LDFLAGS', $flag); + } + + ## 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 ($opts_build->has('noopt')) { + # 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; + } + + # PIE + if (defined $use_feature{hardening}{pie} and + $use_feature{hardening}{pie} and + not $builtin_feature{hardening}{pie}) { + my $flag = "-specs=$Dpkg::DATADIR/pie-compile.specs"; + $flags->append('CFLAGS', $flag); + $flags->append('OBJCFLAGS', $flag); + $flags->append('OBJCXXFLAGS', $flag); + $flags->append('FFLAGS', $flag); + $flags->append('FCFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('GCJFLAGS', $flag); + $flags->append('LDFLAGS', "-specs=$Dpkg::DATADIR/pie-link.specs"); + } elsif (defined $use_feature{hardening}{pie} and + not $use_feature{hardening}{pie} and + $builtin_feature{hardening}{pie}) { + my $flag = "-specs=$Dpkg::DATADIR/no-pie-compile.specs"; + $flags->append('CFLAGS', $flag); + $flags->append('OBJCFLAGS', $flag); + $flags->append('OBJCXXFLAGS', $flag); + $flags->append('FFLAGS', $flag); + $flags->append('FCFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('GCJFLAGS', $flag); + $flags->append('LDFLAGS', "-specs=$Dpkg::DATADIR/no-pie-link.specs"); + } + + # Stack protector + if ($use_feature{hardening}{stackprotectorstrong}) { + my $flag = '-fstack-protector-strong'; + $flags->append('CFLAGS', $flag); + $flags->append('OBJCFLAGS', $flag); + $flags->append('OBJCXXFLAGS', $flag); + $flags->append('FFLAGS', $flag); + $flags->append('FCFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('GCJFLAGS', $flag); + } elsif ($use_feature{hardening}{stackprotector}) { + my $flag = '-fstack-protector --param=ssp-buffer-size=4'; + $flags->append('CFLAGS', $flag); + $flags->append('OBJCFLAGS', $flag); + $flags->append('OBJCXXFLAGS', $flag); + $flags->append('FFLAGS', $flag); + $flags->append('FCFLAGS', $flag); + $flags->append('CXXFLAGS', $flag); + $flags->append('GCJFLAGS', $flag); + } + + # Fortify Source + if ($use_feature{hardening}{fortify}) { + $flags->append('CPPFLAGS', '-D_FORTIFY_SOURCE=2'); + } + + # Format Security + if ($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 ($use_feature{hardening}{relro}) { + $flags->append('LDFLAGS', '-Wl,-z,relro'); + } + + # Bindnow + if ($use_feature{hardening}{bindnow}) { + $flags->append('LDFLAGS', '-Wl,-z,now'); + } + + ## Commit + + # Set used features to their builtin setting if unset. + foreach my $area (sort keys %builtin_feature) { + foreach my $feature (keys %{$builtin_feature{$area}}) { + $use_feature{$area}{$feature} //= $builtin_feature{$area}{$feature}; + } + } + + # 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 _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") { + $tainted{'merged-usr-via-symlinks'} = 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, + }, 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..ed05c30 --- /dev/null +++ b/scripts/Dpkg/Vendor/Default.pm @@ -0,0 +1,201 @@ +# 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 object, please +# uncomment the following lines +#use parent qw(Dpkg::Vendor::Default); + +=encoding utf8 + +=head1 NAME + +Dpkg::Vendor::Default - default vendor object + +=head1 DESCRIPTION + +A vendor object is used to provide vendor specific behaviour +in various places. This is the default object 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). + +=back + +=cut + +sub run_hook { + my ($self, $hook, @params) = @_; + + if ($hook eq 'before-source-build') { + my $srcpkg = shift @params; + } elsif ($hook eq 'keyrings') { + warnings::warnif('deprecated', 'obsolete keyrings vendor hook'); + return (); + } 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 (); + } + + # Default return value for unknown/unimplemented hooks + return; +} + +=back + +=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..e6335c2 --- /dev/null +++ b/scripts/Dpkg/Vendor/Ubuntu.pm @@ -0,0 +1,165 @@ +# 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 Dpkg::ErrorHandling; +use Dpkg::Gettext; +use Dpkg::Control::Types; + +use parent qw(Dpkg::Vendor::Debian); + +=encoding utf8 + +=head1 NAME + +Dpkg::Vendor::Ubuntu - Ubuntu vendor object + +=head1 DESCRIPTION + +This vendor object 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/i) { + if (length $ENV{DEBEMAIL} and $ENV{DEBEMAIL} =~ /\@ubuntu\.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 'keyrings') { + return $self->run_hook('package-keyrings', @params); + } 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); + + require Dpkg::BuildOptions; + + my $build_opts = Dpkg::BuildOptions->new(); + + if (!$build_opts->has('noopt')) { + require Dpkg::Arch; + + my $arch = Dpkg::Arch::get_host_arch(); + if (Dpkg::Arch::debarch_eq($arch, 'ppc64el')) { + for my $flag (qw(CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS GCJFLAGS + FFLAGS FCFLAGS)) { + my $value = $flags->get($flag); + $value =~ s/-O[0-9]/-O3/; + $flags->set($flag, $value); + } + } + } + # Per https://wiki.ubuntu.com/DistCompilerFlags + $flags->prepend('LDFLAGS', '-Wl,-Bsymbolic-functions'); + } else { + return $self->SUPER::run_hook($hook, @params); + } + +} + +=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; |