Package: dpkg
Version: 1.16.1.2
Severity: normal
Tags: patch

Hi,

In working through the requirements for adding hardening build checks to
lintian[1], it has become clear that there needs to be a way to ask
dpkg-buildflags what its expected features area for a given situation.

This patch adds that ability, and lets the environment correctly adjust it:

$ dpkg-buildflags --features hardening
-bindnow,+format,+fortify,-pie,+relro,+stackprotector

$ DEB_HOST_ARCH=ia64 dpkg-buildflags --features hardening
-bindnow,+format,+fortify,-pie,-relro,-stackprotector

I'm obviously open to changing how this works, what the output looks
like, etc. I'm just interested in the behavior to query expected
hardening features. Hopefully I can deprecate hardening-wrapper and
hardening-includes to just use dpkg-buildflags directly, as well as hook
it up to lintian.

Thanks!

-Kees

[1] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=650536

-- 
Kees Cook                                            @debian.org
>From 8a8a1414ad6cac4d22ca732eaa9e14f802e82e29 Mon Sep 17 00:00:00 2001
From: Kees Cook <k...@debian.org>
Date: Thu, 8 Dec 2011 15:53:14 -0800
Subject: [PATCH] dpkg-buildflags: provide feature query ability

Since the logic for having a hardening flag enabled or disabled depends
on the architecture, and since the flags may change over time for each
hardening feature, there needs to be a way to externally query the state
of the hardening features. Specifically, lintian needs this to be able
to figure out if a binary package is missing expected hardening features.
Instead of maintaining multiple hard-coded lists of expected hardening
features, this makes dpkg-buildflags the canonical location of the
information, which can be queried externally. (See bug 650536.)

Signed-off-by: Kees Cook <k...@debian.org>
---
 man/dpkg-buildflags.1         |    5 +++++
 scripts/Dpkg/BuildFlags.pm    |   24 ++++++++++++++++++++++++
 scripts/Dpkg/Vendor/Debian.pm |   22 ++++++++++++++++++++++
 scripts/dpkg-buildflags.pl    |   20 +++++++++++++++++++-
 4 files changed, 70 insertions(+), 1 deletions(-)

diff --git a/man/dpkg-buildflags.1 b/man/dpkg-buildflags.1
index a018edb..dad6be6 100644
--- a/man/dpkg-buildflags.1
+++ b/man/dpkg-buildflags.1
@@ -87,6 +87,11 @@ the flag is set/modified by a user-specific configuration;
 the flag is set/modified by an environment-specific configuration.
 .RE
 .TP
+.BI \-\-features " area"
+Print the features enabled for a given area. The only currently recognized
+area is \fBhardening\fP. Exits with 0 if the area is known otherwise exits
+with 1.
+.TP
 .B \-\-help
 Show the usage message and exit.
 .TP
diff --git a/scripts/Dpkg/BuildFlags.pm b/scripts/Dpkg/BuildFlags.pm
index 6112a9f..261e649 100644
--- a/scripts/Dpkg/BuildFlags.pm
+++ b/scripts/Dpkg/BuildFlags.pm
@@ -68,6 +68,7 @@ sub load_vendor_defaults {
     my ($self) = @_;
     $self->{'options'} = {};
     $self->{'source'} = {};
+    $self->{'features'} = {};
     my $build_opts = Dpkg::BuildOptions->new();
     my $default_flags = $build_opts->has("noopt") ? "-g -O0" : "-g -O2";
     $self->{flags} = {
@@ -306,6 +307,17 @@ sub get {
     return $self->{'flags'}{$key};
 }
 
+=item $bf->get_features($area)
+
+Return the list of features enabled for the given area.
+
+=cut
+
+sub get_features {
+    my ($self, $key) = @_;
+    return $self->{'features'}{$key};
+}
+
 =item $bf->get_origin($flag)
 
 Return the origin associated to the flag. It might be undef if the
@@ -318,6 +330,18 @@ sub get_origin {
     return $self->{'origin'}{$key};
 }
 
+=item $bf->features($area)
+
+Returns a list of enabled features for a given area. Currently the
+only recognized area is "hardening".
+
+=cut
+
+sub features {
+    my ($self, $key) = @_;
+    return exists $self->{'features'}{$key};
+}
+
 =item $bf->has($option)
 
 Returns a boolean indicating whether the flags exists in the object.
diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm
index e824d0e..8106c47 100644
--- a/scripts/Dpkg/Vendor/Debian.pm
+++ b/scripts/Dpkg/Vendor/Debian.pm
@@ -122,6 +122,10 @@ sub add_hardening_flags {
 	$flags->append("CFLAGS", "-fPIE");
 	$flags->append("CXXFLAGS", "-fPIE");
 	$flags->append("LDFLAGS", "-fPIE -pie");
+	$flags->{'features'}{'hardening'}{'pie'} = 1;
+    }
+    else {
+	$flags->{'features'}{'hardening'}{'pie'} = 0;
     }
     # Stack protector
     if ($use_feature{"stackprotector"} and
@@ -132,26 +136,44 @@ sub add_hardening_flags {
 	#   compiler supports it incorrectly (leads to SEGV)
 	$flags->append("CFLAGS", "-fstack-protector --param=ssp-buffer-size=4");
 	$flags->append("CXXFLAGS", "-fstack-protector --param=ssp-buffer-size=4");
+	$flags->{'features'}{'hardening'}{'stackprotector'} = 1;
+    }
+    else {
+	$flags->{'features'}{'hardening'}{'stackprotector'} = 0;
     }
     # Fortify
     if ($use_feature{"fortify"}) {
 	$flags->append("CPPFLAGS", "-D_FORTIFY_SOURCE=2");
+	$flags->{'features'}{'hardening'}{'fortify'} = 1;
+    }
+    else {
+	$flags->{'features'}{'hardening'}{'fortify'} = 0;
     }
     # Format
     if ($use_feature{"format"}) {
 	$flags->append("CFLAGS", "-Wformat -Wformat-security -Werror=format-security");
 	$flags->append("CXXFLAGS", "-Wformat -Wformat-security -Werror=format-security");
+	$flags->{'features'}{'hardening'}{'format'} = 1;
+    }
+    else {
+	$flags->{'features'}{'hardening'}{'format'} = 0;
     }
     # Relro
     if ($use_feature{"relro"} and $cpu !~ /^(ia64|hppa|avr32)$/) {
 	$flags->append("LDFLAGS", "-Wl,-z,relro");
+	$flags->{'features'}{'hardening'}{'relro'} = 1;
     } else {
 	# Disable full relro if relro is not enabled.
 	$use_feature{"bindnow"} = 0;
+	$flags->{'features'}{'hardening'}{'relro'} = 0;
     }
     # Bindnow
     if ($use_feature{"bindnow"}) {
 	$flags->append("LDFLAGS", "-Wl,-z,now");
+	$flags->{'features'}{'hardening'}{'bindnow'} = 1;
+    }
+    else {
+	$flags->{'features'}{'hardening'}{'bindnow'} = 0;
     }
 }
 
diff --git a/scripts/dpkg-buildflags.pl b/scripts/dpkg-buildflags.pl
index ee33961..29ab48d 100755
--- a/scripts/dpkg-buildflags.pl
+++ b/scripts/dpkg-buildflags.pl
@@ -47,6 +47,7 @@ Actions:
   --get <flag>       output the requested flag to stdout.
   --origin <flag>    output the origin of the flag to stdout:
                      value is one of vendor, system, user, env.
+  --features <area>  output the enabled features for the given area.
   --list             output a list of the flags supported by the current vendor.
   --export=(sh|make|configure)
                      output something convenient to import the
@@ -62,7 +63,7 @@ my ($param, $action);
 
 while (@ARGV) {
     $_ = shift(@ARGV);
-    if (m/^--(get|origin)$/) {
+    if (m/^--(get|origin|features)$/) {
         usageerr(_g("two commands specified: --%s and --%s"), $1, $action)
             if defined($action);
         $action = $1;
@@ -115,6 +116,23 @@ if ($action eq "get") {
 	print $build_flags->get_origin($param) . "\n";
 	exit(0);
     }
+} elsif ($action eq "features") {
+    if ($build_flags->features($param)) {
+	my $features = $build_flags->get_features($param);
+	my @report;
+	foreach my $feature (sort keys %{$features}) {
+	    my $item = $feature;
+	    if ($features->{$feature} == 1) {
+		$item = "+$item";
+	    }
+	    else {
+		$item = "-$item";
+	    }
+	    push(@report, $item);
+	}
+	print join(",", @report), "\n";
+	exit(0);
+    }
 } elsif ($action =~ m/^export-(.*)$/) {
     my $export_type = $1;
     foreach my $flag ($build_flags->list()) {
-- 
1.7.5.4

Reply via email to