1#! /usr/bin/perl
2use strict;
3use warnings;
4use File::Spec qw(rel2abs);
5use File::Basename;
6
7my @args = ();
8my $enabled = 0;
9my $debug = 0;
10my $debug_symlinks = 0;
11my $debug_fd = *STDERR;
12
13# Set up defaults
14my %default;
15$default{'DEB_BUILD_HARDENING'}=0;
16$default{'DEB_BUILD_HARDENING_DEBUG'}=0;
17$default{'DEB_BUILD_HARDENING_DEBUG_SYMLINKS'}=0;
18
19# Architecture settings
20# #OS# #ARCH#
21$default{'DEB_BUILD_HARDENING_STACKPROTECTOR'}=1;
22$default{'DEB_BUILD_HARDENING_FORTIFY'}=1;
23$default{'DEB_BUILD_HARDENING_FORMAT'}=1;
24$default{'DEB_BUILD_HARDENING_PIE'}=1;
25
26# System settings
27my $system_conf = '/etc/hardening-wrapper.conf';
28if (-r $system_conf) {
29    open(CONF,$system_conf) || warn "Cannot read $system_conf\n";
30    while (my $line = <CONF>) {
31        if ($line =~ /^\s*(DEB_BUILD_HARDENING[_A-Z]*)\s*=\s*(\d)$/) {
32            $default{$1}=$2+0;
33        }
34    }
35    close(CONF);
36}
37
38# Environment settings
39$enabled =          defined($ENV{'DEB_BUILD_HARDENING'}) ?
40                            $ENV{'DEB_BUILD_HARDENING'} :
41                            $default{'DEB_BUILD_HARDENING'};
42$debug =            defined($ENV{'DEB_BUILD_HARDENING_DEBUG'}) ?
43                            $ENV{'DEB_BUILD_HARDENING_DEBUG'} :
44                            $default{'DEB_BUILD_HARDENING_DEBUG'};
45$debug_symlinks =   defined($ENV{'DEB_BUILD_HARDENING_DEBUG_SYMLINKS'}) ?
46                            $ENV{'DEB_BUILD_HARDENING_DEBUG_SYMLINKS'} :
47                            $default{'DEB_BUILD_HARDENING_DEBUG_SYMLINKS'};
48my $force_stack =   defined($ENV{'DEB_BUILD_HARDENING_STACKPROTECTOR'}) ?
49                            $ENV{'DEB_BUILD_HARDENING_STACKPROTECTOR'} :
50                            $default{'DEB_BUILD_HARDENING_STACKPROTECTOR'};
51my $force_fortify = defined($ENV{'DEB_BUILD_HARDENING_FORTIFY'}) ?
52                            $ENV{'DEB_BUILD_HARDENING_FORTIFY'} :
53                            $default{'DEB_BUILD_HARDENING_FORTIFY'};
54my $force_format =  defined($ENV{'DEB_BUILD_HARDENING_FORMAT'}) ?
55                            $ENV{'DEB_BUILD_HARDENING_FORMAT'} :
56                            $default{'DEB_BUILD_HARDENING_FORMAT'};
57my $force_pie =     defined($ENV{'DEB_BUILD_HARDENING_PIE'}) ?
58                            $ENV{'DEB_BUILD_HARDENING_PIE'} :
59                            $default{'DEB_BUILD_HARDENING_PIE'};
60my $force_fPIE =    defined($ENV{'DEB_BUILD_HARDENING_PIE'}) ?
61                            $ENV{'DEB_BUILD_HARDENING_PIE'} :
62                            $default{'DEB_BUILD_HARDENING_PIE'};
63if (defined($ENV{'DEB_BUILD_HARDENING_DEBUG_OUTPUT'})) {
64    $debug_fd = undef;
65    if (!open($debug_fd, ">>$ENV{'DEB_BUILD_HARDENING_DEBUG_OUTPUT'}")) {
66        die "Cannot open $ENV{'DEB_BUILD_HARDENING_DEBUG_OUTPUT'}: $!\n";
67    }
68}
69
70# Figure out which tool we were called as.
71my $self = "\Qhardened-cc\E";
72my $link = "";
73my $arg0 = File::Spec->rel2abs(basename($0),dirname($0));
74my $tool = $arg0;
75if ($tool =~ /$self$/) {
76    $tool = "/usr/bin/cc";
77}
78
79sub resolve_link($)
80{
81    my $origin = $_[0];
82    my $link = readlink($origin);
83    return File::Spec->rel2abs($link,dirname($origin));
84}
85
86while (-l $tool && ($link = resolve_link($tool)) !~ /$self$/) {
87    print $debug_fd "$tool -> $link\n" if ($debug_symlinks);
88    $tool = $link;
89}
90if (-x "$tool.real") {
91    print $debug_fd "$tool -real> $tool.real\n" if ($debug_symlinks);
92    $tool = "$tool.real";
93}
94# Abort if we ended up on a circular symlink resolution
95if ($tool eq $arg0) {
96    my $short = $tool;
97    $short =~ s/.*\///g;
98	print STDERR "$tool: not found (maybe $short is not installed?)\n";
99	exit(127);
100}
101
102# Set per-tool flags.
103my $ssp_strong = 1;
104my $gcc_version = basename($tool);
105# Disable stack-protector-strong on pre-4.9 GCC.
106if ($gcc_version =~ /4\.[2345678]($|\D)/) {
107    $ssp_strong = 0;
108}
109
110if ($enabled) {
111    # Scan arguments
112    my $linking = 1;
113    foreach my $arg (@ARGV) {
114        if ($arg eq "-fno-PIC" ||
115            $arg eq "-fno-pic" ||
116            $arg eq "-fno-PIE" ||
117            $arg eq "-fno-pie" ||
118            $arg eq "-nopie" ||
119            $arg eq "-static" ||
120            $arg eq "-shared" ||
121            $arg eq "-D__KERNEL__" ||
122            $arg eq "-nostdlib" ||
123            $arg eq "-nostartfiles")
124        {
125            # If any PIC or PIE things are explicitly disabled,
126            # disable all our PIE flags.
127            $force_fPIE = 0;
128            $force_pie = 0;
129        }
130        if ($arg eq "-fPIC" ||
131            $arg eq "-fpic")
132        {
133            # fPIC is a stricter version of fPIE, so don't use fPIE when
134            # we encounter fPIC.  However, the inclusion of -fPIC does
135            # not mean we need to block the use of "-pie", which is still
136            # possible with fPIC.
137            $force_fPIE = 0;
138        }
139        if ($arg eq "-c") {
140            $linking = 0;
141        }
142        if ($arg =~ /^-D_FORTIFY_SOURCE(=|$)/) {
143            $force_fortify = 0;
144        }
145        if ($arg eq "-nostdlib" ||
146            $arg eq "-ffreestanding") {
147            $force_stack = 0;
148        }
149    }
150
151    # Enable SSP by default
152    if ($force_stack) {
153        if ($ssp_strong) {
154            push(@args,'-fstack-protector-strong');
155        } else {
156            push(@args,'-fstack-protector','--param=ssp-buffer-size=4');
157        }
158    }
159
160    # Enable -fPIE by default
161    if ($force_fPIE) {
162        push(@args, '-fPIE');
163    }
164    if ($force_pie) {
165        if ($linking) {
166            # "-pie" is really only meaningful when calling the linker
167            push(@args, '-pie');
168        }
169    }
170
171    # Enable glibc protections by default (-02 should already be defined...)
172    # (disable with -D_FORTIFY_SOURCE=0)
173    if ($force_fortify) {
174        push(@args,'-D_FORTIFY_SOURCE=2');
175    }
176
177    # Enable format string checking
178    if ($force_format) {
179        push(@args,'-Wformat','-Wformat-security','-Werror=format-security');
180    }
181}
182
183my @target = ($tool, @args, @ARGV);
184
185print $debug_fd join(" ",@target),"\n" if ($debug);
186
187exec @target or die "Unable to exec $target[0]: $!\n";
188