]> ruderich.org/simon Gitweb - blhc/blhc.git/blob - bin/blhc
d276bc001b37b302672e35c6bff4abc1c7ba56ac
[blhc/blhc.git] / bin / blhc
1 #!/usr/bin/perl
2
3 # Build log hardening check, checks build logs for missing hardening flags.
4
5 # Copyright (C) 2012  Simon Ruderich
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20
21 use strict;
22 use warnings;
23
24 use Getopt::Long ();
25 use Text::ParseWords ();
26
27 our $VERSION = '0.03';
28
29
30 # CONSTANTS/VARIABLES
31
32 # Regex to catch compiler commands.
33 my $cc_regex = qr/
34     (?<!\s-)               # ignore options, e.g. "-c++" [sic!] (used by swig)
35     (?<!\.)                # ignore file names, e.g. "test.gcc"
36     (?:cc|gcc|g\+\+|c\+\+)
37     (?:-[\d.]+)?           # version suffix, e.g. "gcc-4.6"
38     /x;
39 # Full regex which matches the complete compiler name. Used in a few places to
40 # prevent false negatives.
41 my $cc_regex_full = qr/
42     (?:[a-z0-9_]+-(?:linux-|kfreebsd-)?gnu(?:eabi|eabihf)?-)?
43     $cc_regex
44     /x;
45 # Regex to check if a line contains a compiler command.
46 my $cc_regex_normal = qr/
47     \b$cc_regex(?:\s|\\)
48     /x;
49 # Regex to catch (GCC) compiler warnings.
50 my $warning_regex = qr/^(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]$/;
51
52 # List of source file extensions which require preprocessing.
53 my @source_preprocess_compile_cpp = (
54     # C++
55     qw( cc cp cxx cpp CPP c++ C ),
56     # Objective-C++
57     qw( mm M ),
58 );
59 my @source_preprocess_compile = (
60     # C
61     qw( c ),
62     # Objective-C
63     qw( m ),
64     # (Objective-)C++
65     @source_preprocess_compile_cpp,
66     # Fortran
67     qw( F FOR fpp FPP FTN F90 F95 F03 F08 ),
68 );
69 my @source_preprocess_no_compile = (
70     # Assembly
71     qw( S sx ),
72 );
73 my @source_preprocess = (
74     @source_preprocess_compile,
75     @source_preprocess_no_compile,
76 );
77 # List of source file extensions which don't require preprocessing.
78 my @source_no_preprocess_compile_cpp = (
79     # C++
80     qw( ii ),
81     # Objective-C++
82     qw( mii ),
83 );
84 my @source_no_preprocess_compile = (
85     # C
86     qw( i ),
87     # (Objective-)C++
88     @source_no_preprocess_compile_cpp,
89     # Objective-C
90     qw( mi ),
91     # Fortran
92     qw( f for ftn f90 f95 f03 f08 ),
93     # Ada body
94     qw( adb ),
95 );
96 my @source_no_preprocess_no_compile = (
97     # Assembly
98     qw( s ),
99     # Ada specification
100     qw( ads ),
101 );
102 my @source_no_preprocess = (
103     @source_no_preprocess_compile,
104     @source_no_preprocess_no_compile,
105 );
106 # List of header file extensions which require preprocessing.
107 my @header_preprocess = (
108     # C, C++, Objective-C, Objective-C++
109     qw( h ),
110     # C++
111     qw( hh H hp hxx hpp HPP h++ tcc ),
112 );
113 # Object files.
114 my @object = (
115     # Normal object files.
116     qw ( o ),
117     # Libtool object files.
118     qw ( lo la ),
119     # Dynamic libraries. bzip2 uses .sho.
120     qw ( so sho ),
121     # Static libraries.
122     qw ( a ),
123 );
124
125 # Hashes for fast extensions lookup to check if a file falls in one of these
126 # categories.
127 my %extensions_no_preprocess = map { $_ => 1 } (
128     # There's no @header_no_preprocess.
129     @source_no_preprocess,
130 );
131 my %extensions_preprocess = map { $_ => 1 } (
132     @header_preprocess,
133     @source_preprocess,
134 );
135 my %extensions_compile_link = map { $_ => 1 } (
136     @source_preprocess,
137     @source_no_preprocess,
138 );
139 my %extensions_compile = map { $_ => 1 } (
140     @source_preprocess_compile,
141     @source_no_preprocess_compile,
142 );
143 my %extensions_no_compile = map { $_ => 1 } (
144     @source_preprocess_no_compile,
145     @source_no_preprocess_no_compile,
146 );
147 my %extensions_compile_cpp = map { $_ => 1 } (
148     @source_preprocess_compile_cpp,
149     @source_no_preprocess_compile_cpp,
150 );
151 my %extensions_object = map { $_ => 1 } (
152     @object,
153 );
154 my %extension = map { $_ => 1 } (
155     @source_no_preprocess,
156     @header_preprocess,
157     @source_preprocess,
158     @object,
159 );
160
161 # Regexp to match file extensions.
162 my $file_extension_regex = qr/
163     \s
164     \S+             # Filename without extension.
165     \.
166     ([^\/\\.,;:\s]+)# File extension.
167     (?=\s|\\)       # At end of word. Can't use \b because some files have non
168                     # word characters at the end and because \b matches double
169                     # extensions (like .cpp.o). Works always as all lines are
170                     # terminated with "\n".
171     /x;
172
173 # Expected (hardening) flags. All flags are used as regexps.
174 my @def_cflags = (
175     '-g',
176     '-O(?:2|3)',
177 );
178 my @def_cflags_format = (
179     '-Wformat',
180     '-Werror=format-security', # implies -Wformat-security
181 );
182 my @def_cflags_fortify = (
183     # fortify needs at least -O1, but -O2 is recommended anyway
184 );
185 my @def_cflags_stack = (
186     '-fstack-protector',
187     '--param=ssp-buffer-size=4',
188 );
189 my @def_cflags_pie = (
190     '-fPIE',
191 );
192 my @def_cxxflags = (
193     @def_cflags,
194 );
195 # @def_cxxflags_* is the same as @def_cflags_*.
196 my @def_cppflags = ();
197 my @def_cppflags_fortify = (
198     '-D_FORTIFY_SOURCE=2', # must be first, see cppflags_fortify_broken()
199     # If you add another flag fix hack below (search for "Hack to fix").
200 );
201 my @def_cppflags_fortify_bad = (
202     # These flags may overwrite -D_FORTIFY_SOURCE=2.
203     '-U_FORTIFY_SOURCE',
204     '-D_FORTIFY_SOURCE=0',
205     '-D_FORTIFY_SOURCE=1',
206 );
207 my @def_ldflags = ();
208 my @def_ldflags_relro = (
209     '-Wl,(?:-z,)?relro',
210 );
211 my @def_ldflags_bindnow = (
212     '-Wl,(?:-z,)?now',
213 );
214 my @def_ldflags_pie = (
215     '-fPIE',
216     '-pie',
217 );
218 my @def_ldflags_pic = (
219     '-fPIC',
220     '-fpic',
221     '-shared',
222 );
223 # References to all flags checked by the flag checker.
224 my @flag_refs = (
225     \@def_cflags,
226     \@def_cflags_format,
227     \@def_cflags_fortify,
228     \@def_cflags_stack,
229     \@def_cflags_pie,
230     \@def_cxxflags,
231     \@def_cppflags,
232     \@def_cppflags_fortify,
233     \@def_ldflags,
234     \@def_ldflags_relro,
235     \@def_ldflags_bindnow,
236     \@def_ldflags_pie,
237 );
238 # References to all used flags.
239 my @flag_refs_all = (
240     @flag_refs,
241     \@def_cppflags_fortify_bad,
242     \@def_ldflags_pic,
243 );
244 # Renaming rules for the output so the regex parts are not visible. Also
245 # stores string values of flag regexps above, see compile_flag_regexp().
246 my %flag_renames = (
247     '-O(?:2|3)'         => '-O2',
248     '-Wl,(?:-z,)?relro' => '-Wl,-z,relro',
249     '-Wl,(?:-z,)?now'   => '-Wl,-z,now',
250 );
251
252 my %exit_code = (
253     no_compiler_commands => 1 << 0,
254     # used by POD::Usage => 1 << 1,
255     non_verbose_build    => 1 << 2,
256     flags_missing        => 1 << 3,
257     hardening_wrapper    => 1 << 4,
258     invalid_cmake        => 1 << 5,
259 );
260
261 my %buildd_tag = (
262     no_compiler_commands => 'I-no-compiler-commands',
263     non_verbose_build    => 'W-compiler-flags-hidden',
264     flags_missing        => 'W-dpkg-buildflags-missing',
265     hardening_wrapper    => 'I-hardening-wrapper-used',
266     invalid_cmake        => 'I-invalid-cmake-used',
267 );
268
269 # Statistics of missing flags and non-verbose build commands. Used for
270 # $option_buildd.
271 my %statistics = (
272     preprocess          => 0,
273     preprocess_missing  => 0,
274     compile             => 0,
275     compile_missing     => 0,
276     compile_cpp         => 0,
277     compile_cpp_missing => 0,
278     link                => 0,
279     link_missing        => 0,
280     commands            => 0,
281     commands_nonverbose => 0,
282 );
283
284 # Use colored (ANSI) output?
285 my $option_color;
286
287
288 # FUNCTIONS
289
290 sub error_flags {
291     my ($message, $missing_flags_ref, $flag_renames_ref, $line) = @_;
292
293     # Get string value of qr//-escaped regexps and if requested rename them.
294     my @missing_flags = map {
295             $flag_renames_ref->{$_}
296         } @{$missing_flags_ref};
297
298     my $flags = join ' ', @missing_flags;
299     printf '%s (%s)%s %s',
300            error_color($message, 'red'), $flags, error_color(':', 'yellow'),
301            $line;
302 }
303 sub error_non_verbose_build {
304     my ($line) = @_;
305
306     printf '%s%s %s',
307            error_color('NONVERBOSE BUILD', 'red'),
308            error_color(':', 'yellow'),
309            $line;
310 }
311 sub error_invalid_cmake {
312     my ($version) = @_;
313
314     printf "%s%s %s\n",
315             error_color('INVALID CMAKE', 'red'),
316             error_color(':', 'yellow'),
317             $version;
318 }
319 sub error_hardening_wrapper {
320     printf "%s%s %s\n",
321             error_color('HARDENING WRAPPER', 'red'),
322             error_color(':', 'yellow'),
323             'no checks possible, aborting';
324 }
325 sub error_color {
326     my ($message, $color) = @_;
327
328     if ($option_color) {
329         return Term::ANSIColor::colored($message, $color);
330     } else {
331         return $message;
332     }
333 }
334
335 sub any_flags_used {
336     my ($line, @flags) = @_;
337
338     foreach my $flag (@flags) {
339         return 1 if $line =~ /$flag/;
340     }
341
342     return 0;
343 }
344 sub all_flags_used {
345     my ($line, $missing_flags_ref, @flags) = @_;
346
347     my @missing_flags = ();
348     foreach my $flag (@flags) {
349         if (not $line =~ /$flag/) {
350             push @missing_flags, $flag;
351         }
352     }
353
354     return 1 if scalar @missing_flags == 0;
355
356     @{$missing_flags_ref} = @missing_flags;
357     return 0;
358 }
359
360 sub cppflags_fortify_broken {
361     my ($line, $missing_flags) = @_;
362
363     # This doesn't take the position into account, but is a simple solution.
364     # And if the build system tries to force -D_FORTIFY_SOURCE=0/1, something
365     # is wrong anyway.
366
367     if (any_flags_used($line, @def_cppflags_fortify_bad)) {
368         # $def_cppflags_fortify[0] must be -D_FORTIFY_SOURCE=2!
369         push @{$missing_flags}, $def_cppflags_fortify[0];
370         return 1;
371     }
372
373     return 0;
374 }
375
376 # Modifies $missing_flags_ref array.
377 sub pic_pie_conflict {
378     my ($line, $pie, $missing_flags_ref, @flags_pie) = @_;
379
380     return 0 if not $pie;
381     return 0 if not any_flags_used($line, @def_ldflags_pic);
382
383     my %flags = map { $_ => 1 } @flags_pie;
384
385     # Remove all PIE flags from @missing_flags as they are not required with
386     # -fPIC.
387     my @result = grep {
388         not exists $flags{$_}
389     } @{$missing_flags_ref};
390     @{$missing_flags_ref} = @result;
391
392     # We got a conflict when no flags are left, thus only PIE flags were
393     # missing. If other flags were missing abort because the conflict is not
394     # the problem.
395     return scalar @result == 0;
396 }
397
398 sub is_non_verbose_build {
399     my ($line, $next_line, $skip_ref) = @_;
400
401     if (not (index($line, 'checking if you want to see long compiling messages... no') == 0
402                 or $line =~ /^\s*\[?(?:CC|CCLD|C\+\+|CXX|CXXLD|LD|LINK)\]?\s+(.+?)$/
403                 or $line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/
404                 or $line =~ /^\s*[Bb]uilding (?:program|shared library)\s+(.+?)$/
405                 or $line =~ /^\s*\[[\d ]+%\] Building (?:C|CXX) object (.+?)$/)) {
406         return 0;
407     }
408
409     # False positives.
410     #
411     # C++ compiler setting.
412     return 0 if $line =~ /^\s*C\+\+.+?:\s+(?:yes|no)\s*$/;
413     # "Compiling" with no file name.
414     if ($line =~ /^\s*[Cc]ompiling\s+(.+?)(?:\.\.\.)?$/) {
415         # $file_extension_regex may need spaces around the filename.
416         return 0 if not " $1 " =~ /$file_extension_regex/o;
417     }
418
419     my $file = $1;
420
421     # On the first pass we only check if this line is verbose or not.
422     return 1 if not defined $next_line;
423
424     # Second pass, we have access to the next line.
425     ${$skip_ref} = 0;
426
427     # CMake and other build systems print the non-verbose messages also when
428     # building verbose. If a compiler and the file name occurs in the next
429     # line, treat it as verbose build.
430     if (defined $file) {
431         # Get filename, we can't use the complete path as only parts of it are
432         # used in the real compiler command.
433         $file =~ m{/([^/\s]+)$};
434         $file = $1;
435
436         if (index($next_line, $file) != -1 and $next_line =~ /$cc_regex/o) {
437             # Not a non-verbose line, but we still have to skip the current line
438             # as it doesn't contain any compiler commands.
439             ${$skip_ref} = 1;
440             return 0;
441         }
442     }
443
444     return 1;
445 }
446
447 sub remove_flags {
448     my ($flag_refs_ref, $flag_renames_ref, @flags) = @_;
449
450     my %removes = map { $_ => 1 } @flags;
451     foreach my $flags (@{$flag_refs_ref}) {
452         @{$flags} = grep {
453             # Flag found as string.
454             not exists $removes{$_}
455             # Flag found as string representation of regexp.
456                 and (not defined $flag_renames_ref->{$_}
457                         or not exists $removes{$flag_renames_ref->{$_}})
458         } @{$flags};
459     }
460 }
461
462 sub compile_flag_regexp {
463     my ($flag_renames_ref, @flags) = @_;
464
465     my @result = ();
466     foreach my $flag (@flags) {
467         # Store flag name in replacement string for correct flags in messages
468         # with qr//ed flag regexps.
469         $flag_renames_ref->{qr/\s$flag(?:\s|\\)/}
470             = (exists $flag_renames_ref->{$flag})
471                 ? $flag_renames_ref->{$flag}
472                 : $flag;
473
474         # Compile flag regexp for faster execution.
475         push @result, qr/\s$flag(?:\s|\\)/;
476     }
477     return @result;
478 }
479
480 sub extension_found {
481     my ($extensions_ref, @extensions) = @_;
482
483     my $found = 0;
484     foreach my $extension (@extensions) {
485         if (exists $extensions_ref->{$extension}) {
486             $found = 1;
487             last;
488         }
489     }
490     return $found;
491 }
492
493
494 # MAIN
495
496 # Parse command line arguments.
497 my $option_help             = 0;
498 my $option_version          = 0;
499 my $option_pie              = 0;
500 my $option_bindnow          = 0;
501 my @option_ignore_arch      = ();
502 my @option_ignore_flag      = ();
503 my @option_ignore_arch_flag = ();
504 my @option_ignore_line      = ();
505 my @option_ignore_arch_line = ();
506 my $option_all              = 0;
507 my $option_arch             = undef;
508 my $option_buildd           = 0;
509    $option_color            = 0;
510 if (not Getopt::Long::GetOptions(
511             'help|h|?'           => \$option_help,
512             'version'            => \$option_version,
513             # Hardening options.
514             'pie'                => \$option_pie,
515             'bindnow'            => \$option_bindnow,
516             'all'                => \$option_all,
517             # Ignore.
518             'ignore-arch=s'      => \@option_ignore_arch,
519             'ignore-flag=s'      => \@option_ignore_flag,
520             'ignore-arch-flag=s' => \@option_ignore_arch_flag,
521             'ignore-line=s'      => \@option_ignore_line,
522             'ignore-arch-line=s' => \@option_ignore_arch_line,
523             # Misc.
524             'color'              => \$option_color,
525             'arch=s'             => \$option_arch,
526             'buildd'             => \$option_buildd,
527         )) {
528     require Pod::Usage;
529     Pod::Usage::pod2usage(2);
530 }
531 if ($option_help) {
532     require Pod::Usage;
533     Pod::Usage::pod2usage(1);
534 }
535 if ($option_version) {
536     print "blhc $VERSION  Copyright (C) 2012  Simon Ruderich
537
538 This program is free software: you can redistribute it and/or modify
539 it under the terms of the GNU General Public License as published by
540 the Free Software Foundation, either version 3 of the License, or
541 (at your option) any later version.
542
543 This program is distributed in the hope that it will be useful,
544 but WITHOUT ANY WARRANTY; without even the implied warranty of
545 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
546 GNU General Public License for more details.
547
548 You should have received a copy of the GNU General Public License
549 along with this program.  If not, see <http://www.gnu.org/licenses/>.
550 ";
551     exit 0;
552 }
553
554 # Arguments missing.
555 if (scalar @ARGV == 0) {
556     require Pod::Usage;
557     Pod::Usage::pod2usage(2);
558 }
559
560 # Don't load Term::ANSIColor in buildd mode because Term::ANSIColor is not
561 # installed on Debian's buildds.
562 if (not $option_buildd) {
563     require Term::ANSIColor;
564 }
565
566 if ($option_all) {
567     $option_pie     = 1;
568     $option_bindnow = 1;
569 }
570
571 # Precompiled ignores for faster lookup.
572 my %option_ignore_arch_flag = ();
573 my %option_ignore_arch_line = ();
574
575 # Strip flags which should be ignored.
576 if (scalar @option_ignore_flag > 0) {
577     remove_flags(\@flag_refs, \%flag_renames, @option_ignore_flag);
578 }
579 # Same for arch specific ignore flags, but only prepare here.
580 if (scalar @option_ignore_arch_flag > 0) {
581     foreach my $ignore (@option_ignore_arch_flag) {
582         my ($ignore_arch, $ignore_flag) = split /:/, $ignore, 2;
583
584         if (not $ignore_arch or not $ignore_flag) {
585             printf STDERR 'Value "%s" invalid for option ignore-arch-flag '
586                         . '("arch:flag" expected)' . "\n", $ignore;
587             require Pod::Usage;
588             Pod::Usage::pod2usage(2);
589         }
590
591         push @{$option_ignore_arch_flag{$ignore_arch}}, $ignore_flag;
592     }
593 }
594
595 # Precompile all flag regexps. any_flags_used(), all_flags_used() get a lot
596 # faster with this.
597 foreach my $flags (@flag_refs_all) {
598     @{$flags} = compile_flag_regexp(\%flag_renames, @{$flags});
599 }
600
601 # Precompile ignore line regexps, also anchor at beginning and end of line.
602 foreach my $ignore (@option_ignore_line) {
603     $ignore = qr/^$ignore$/;
604 }
605 # Same for arch specific ignore lines.
606 if (scalar @option_ignore_arch_line > 0) {
607     foreach my $ignore (@option_ignore_arch_line) {
608         my ($ignore_arch, $ignore_line) = split /:/, $ignore, 2;
609
610         if (not $ignore_arch or not $ignore_line) {
611             printf STDERR 'Value "%s" invalid for option ignore-arch-line '
612                         . '("arch:line" expected)' . "\n", $ignore;
613             require Pod::Usage;
614             Pod::Usage::pod2usage(2);
615         }
616
617         push @{$option_ignore_arch_line{$ignore_arch}}, qr/^$ignore_line$/;
618     }
619 }
620
621 # Final exit code.
622 my $exit = 0;
623
624 FILE:
625 foreach my $file (@ARGV) {
626     print "checking '$file'...\n" if scalar @ARGV > 1;
627
628     -f $file or die "No such file: $file";
629
630     open my $fh, '<', $file or die $!;
631
632     # Architecture of this file.
633     my $arch = $option_arch;
634
635     # Hardening options. Not all architectures support all hardening options.
636     my $harden_format  = 1;
637     my $harden_fortify = 1;
638     my $harden_stack   = 1;
639     my $harden_relro   = 1;
640     my $harden_bindnow = $option_bindnow; # defaults to 0
641     my $harden_pie     = $option_pie;     # defaults to 0
642
643     while (my $line = <$fh>) {
644         # Detect architecture automatically unless overridden. For buildd logs
645         # only, doesn't use the dpkg-buildpackage header. Necessary to ignore
646         # build logs which aren't built (wrong architecture, build error,
647         # etc.).
648         if (not $arch and index($line, 'Architecture: ') == 0) {
649             $arch = substr $line, 14, -1; # -1 to ignore '\n' at the end
650         }
651
652         # dpkg-buildflags only provides hardening flags since 1.16.1, don't
653         # check for hardening flags in buildd mode if an older dpkg-dev is
654         # used. Default flags (-g -O2) are still checked.
655         #
656         # Packages which were built before 1.16.1 but used their own hardening
657         # flags are not checked.
658         if ($option_buildd
659                 and index($line, 'Toolchain package versions: ') == 0) {
660             require Dpkg::Version;
661             if (not $line =~ /\bdpkg-dev_(\S+)/
662                     or Dpkg::Version::version_compare($1, '1.16.1') < 0) {
663                 $harden_format  = 0;
664                 $harden_fortify = 0;
665                 $harden_stack   = 0;
666                 $harden_relro   = 0;
667                 $harden_bindnow = 0;
668                 $harden_pie     = 0;
669             }
670         }
671
672         # The following two versions of CMake in Debian obeyed CPPFLAGS, but
673         # this was later dropped because upstream rejected the patch. Thus
674         # build logs with these versions will have fortify hardening flags
675         # enabled, even though they may be not correctly set and are missing
676         # when build with later CMake versions. Thanks to Aron Xu for letting
677         # me know.
678         if (index($line, 'Package versions: ') == 0
679                 and $line =~ /\bcmake_(\S+)/
680                 and ($1 eq '2.8.7-1' or $1 eq '2.8.7-2')) {
681             if (not $option_buildd) {
682                 error_invalid_cmake($1);
683                 $exit |= $exit_code{invalid_cmake};
684             } else {
685                 print "$buildd_tag{invalid_cmake}|$1|\n";
686             }
687         }
688
689         # If hardening wrapper is used (wraps calls to gcc and adds hardening
690         # flags automatically) we can't perform any checks, abort.
691         if (index($line, 'Build-Depends: ') == 0
692                 and $line =~ /\bhardening-wrapper\b/) {
693             if (not $option_buildd) {
694                 error_hardening_wrapper();
695                 $exit |= $exit_code{hardening_wrapper};
696             } else {
697                 print "$buildd_tag{hardening_wrapper}||\n";
698             }
699             next FILE;
700         }
701
702         # We skip over unimportant lines at the beginning of the log to
703         # prevent false positives.
704         last if index($line, 'dpkg-buildpackage: ') == 0;
705     }
706
707     # Input lines, contain only the lines with compiler commands.
708     my @input = ();
709     # Non-verbose lines in the input. Used to reduce calls to
710     # is_non_verbose_build() (which is quite slow) in the second loop when
711     # it's already clear if a line is non-verbose or not.
712     my @input_nonverbose = ();
713
714     my $continuation = 0;
715     my $complete_line = undef;
716     while (my $line = <$fh>) {
717         # And stop at the end of the build log. Package details (reported by
718         # the buildd logs) are not important for us. This also prevents false
719         # positives.
720         last if index($line, 'Build finished at ') == 0
721                 and $line =~ /^Build finished at \d{8}-\d{4}$/;
722
723         # Detect architecture automatically unless overridden.
724         if (not $arch
725                 and index($line, 'dpkg-buildpackage: host architecture ') == 0) {
726             $arch = substr $line, 37, -1; # -1 to ignore '\n' at the end
727         }
728
729         # Ignore compiler warnings for now.
730         next if $line =~ /$warning_regex/o;
731
732         if (not $option_buildd and index($line, "\033") != -1) { # \033 = esc
733             # Remove all ANSI color sequences which are sometimes used in
734             # non-verbose builds.
735             $line = Term::ANSIColor::colorstrip($line);
736             # Also strip '\0xf' (delete previous character), used by Elinks'
737             # build system.
738             $line =~ s/\x0f//g;
739             # And "ESC(B" which seems to be used on armhf and hurd (not sure
740             # what it does).
741             $line =~ s/\033\(B//g;
742         }
743
744         # Check if this line indicates a non verbose build.
745         my $non_verbose = is_non_verbose_build($line);
746
747         # One line may contain multiple commands (";"). Treat each one as
748         # single line. parse_line() is slow, only use it when necessary.
749         my @line = (index($line, ';') == -1)
750                  ? ($line)
751                  : map {
752                        # Ensure newline at the line end - necessary for
753                        # correct parsing later.
754                        $_ =~ s/\s+$//;
755                        $_ .= "\n";
756                    } Text::ParseWords::parse_line(';', 1, $line);
757         foreach my $line (@line) {
758             if ($continuation) {
759                 $continuation = 0;
760
761                 # Join lines, but leave the "\" in place so it's clear where
762                 # the original line break was.
763                 chomp $complete_line;
764                 $complete_line .= ' ' . $line;
765             }
766             # Line continuation, line ends with "\".
767             if ($line =~ /\\$/) {
768                 $continuation = 1;
769                 # Start line continuation.
770                 if (not defined $complete_line) {
771                     $complete_line = $line;
772                 }
773                 next;
774             }
775
776             # Use the complete line if a line continuation occurred.
777             if (defined $complete_line) {
778                 $line = $complete_line;
779                 $complete_line = undef;
780             }
781
782             # Ignore lines with no compiler commands.
783             next if not $non_verbose
784                     and not $line =~ /$cc_regex_normal/o;
785             # Ignore lines with no filenames with extensions. May miss some
786             # non-verbose builds (e.g. "gcc -o test" [sic!]), but shouldn't be
787             # a problem as the log will most likely contain other non-verbose
788             # commands which are detected.
789             next if not $non_verbose
790                     and not $line =~ /$file_extension_regex/o;
791
792             # Ignore false positives.
793             #
794             # `./configure` output.
795             next if not $non_verbose
796                     and $line =~ /^(?:checking|[Cc]onfigure:) /;
797             next if $line =~ /^\s*(?:Host\s+)?(?:C(?:\+\+)?\s+)?
798                                 [Cc]ompiler[\s.]*:?\s+
799                                 /x;
800             next if $line =~ /^\s*(?:- )?(?:HOST_)?(?:CC|CXX)\s*=\s*$cc_regex_full\s*$/o;
801             # `moc-qt4`, contains '-I/usr/share/qt4/mkspecs/linux-g++' (or
802             # similar for other architectures) which gets recognized as a
803             # compiler line. Ignore it.
804             next if $line =~ m{^/usr/bin/moc-qt4
805                                \s.+\s
806                                -I/usr/share/qt4/mkspecs/[a-z]+-g\++(?:-64)?
807                                \s}x;
808             # Ignore false positives when the line contains only CC=gcc but no
809             # other gcc command.
810             if ($line =~ /(.*)CC=$cc_regex_full(.*)/o) {
811                 my $before = $1;
812                 my $after  = $2;
813                 next if     not $before =~ /$cc_regex_normal/o
814                         and not $after  =~ /$cc_regex_normal/o;
815             }
816
817             # Check if additional hardening options were used. Used to ensure
818             # they are used for the complete build.
819             $harden_pie     = 1 if any_flags_used($line, @def_cflags_pie,
820                                                          @def_ldflags_pie);
821             $harden_bindnow = 1 if any_flags_used($line, @def_ldflags_bindnow);
822
823             push @input, $line;
824             push @input_nonverbose, $non_verbose;
825         }
826     }
827
828     close $fh or die $!;
829
830     # Ignore arch if requested.
831     if (scalar @option_ignore_arch > 0 and $arch) {
832         foreach my $ignore (@option_ignore_arch) {
833             if ($arch eq $ignore) {
834                 print "ignoring architecture '$arch'\n";
835                 next FILE;
836             }
837         }
838     }
839
840     if (scalar @input == 0) {
841         if (not $option_buildd) {
842             print "No compiler commands!\n";
843             $exit |= $exit_code{no_compiler_commands};
844         } else {
845             print "$buildd_tag{no_compiler_commands}||\n";
846         }
847         next FILE;
848     }
849
850     if ($option_buildd) {
851         $statistics{commands} += scalar @input;
852     }
853
854     # Option or auto detected.
855     if ($arch) {
856         # The following was partially copied from dpkg-dev 1.16.4.3
857         # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, add_hardening_flags()),
858         # copyright Raphaël Hertzog <hertzog@debian.org>, Kees Cook
859         # <kees@debian.org>, Canonical, Ltd. licensed under GPL version 2 or
860         # later. Keep it in sync.
861
862         require Dpkg::Arch;
863         my ($abi, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch);
864
865         # Disable unsupported hardening options.
866         if ($cpu =~ /^(?:ia64|alpha|mips|mipsel|hppa)$/ or $arch eq 'arm') {
867             $harden_stack = 0;
868         }
869         if ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
870             $harden_relro   = 0;
871             $harden_bindnow = 0;
872         }
873     }
874
875     # Default values.
876     my @cflags   = @def_cflags;
877     my @cxxflags = @def_cxxflags;
878     my @cppflags = @def_cppflags;
879     my @ldflags  = @def_ldflags;
880     # Check the specified hardening options, same order as dpkg-buildflags.
881     if ($harden_pie) {
882         @cflags   = (@cflags,   @def_cflags_pie);
883         @cxxflags = (@cxxflags, @def_cflags_pie);
884         @ldflags  = (@ldflags,  @def_ldflags_pie);
885     }
886     if ($harden_stack) {
887         @cflags   = (@cflags,   @def_cflags_stack);
888         @cxxflags = (@cxxflags, @def_cflags_stack);
889     }
890     if ($harden_fortify) {
891         @cflags   = (@cflags,   @def_cflags_fortify);
892         @cxxflags = (@cxxflags, @def_cflags_fortify);
893         @cppflags = (@cppflags, @def_cppflags_fortify);
894     }
895     if ($harden_format) {
896         @cflags   = (@cflags,   @def_cflags_format);
897         @cxxflags = (@cxxflags, @def_cflags_format);
898     }
899     if ($harden_relro) {
900         @ldflags = (@ldflags, @def_ldflags_relro);
901     }
902     if ($harden_bindnow) {
903         @ldflags = (@ldflags, @def_ldflags_bindnow);
904     }
905
906     # Hack to fix cppflags_fortify_broken() if --ignore-flag
907     # -D_FORTIFY_SOURCE=2 is used to ignore missing fortification. Only works
908     # as long as @def_cppflags_fortify contains only one variable.
909     if (scalar @def_cppflags_fortify == 0) {
910         $harden_fortify = 0;
911     }
912
913     # Ignore flags for this arch if requested.
914     if ($arch and exists $option_ignore_arch_flag{$arch}) {
915         my @local_flag_refs = (\@cflags, \@cxxflags, \@cppflags, \@ldflags);
916
917         remove_flags(\@local_flag_refs,
918                      \%flag_renames,
919                      @{$option_ignore_arch_flag{$arch}});
920     }
921
922     my @ignore_line = @option_ignore_line;
923     # Ignore lines for this arch if requested.
924     if ($arch and exists $option_ignore_arch_line{$arch}) {
925         @ignore_line = (@ignore_line, @{$option_ignore_arch_line{$arch}});
926     }
927
928 LINE:
929     for (my $i = 0; $i < scalar @input; $i++) {
930         my $line = $input[$i];
931
932         # Ignore line if requested.
933         foreach my $ignore (@ignore_line) {
934             next LINE if $line =~ /$ignore/;
935         }
936
937         my $skip = 0;
938         if ($input_nonverbose[$i]
939                 and is_non_verbose_build($line, $input[$i + 1], \$skip)) {
940             if (not $option_buildd) {
941                 error_non_verbose_build($line);
942                 $exit |= $exit_code{non_verbose_build};
943             } else {
944                 $statistics{commands_nonverbose}++;
945             }
946             next;
947         }
948         # Even if it's a verbose build, we might have to skip this line.
949         next if $skip;
950
951         # Remove everything until and including the compiler command. Makes
952         # checks easier and faster.
953         $line =~ s/^.*?$cc_regex//o;
954         # "([...] test.c)" is not detected as 'test.c' - fix this by removing
955         # the brace and similar characters at the line end.
956         $line =~ s/['")]+$//;
957
958         # Skip unnecessary tests when only preprocessing.
959         my $flag_preprocess = 0;
960
961         my $dependency = 0;
962         my $preprocess = 0;
963         my $compile    = 0;
964         my $link       = 0;
965
966         # Preprocess, compile, assemble.
967         if ($line =~ /\s(-E|-S|-c)\b/) {
968             $preprocess      = 1;
969             $flag_preprocess = 1 if $1 eq '-E';
970             $compile         = 1 if $1 eq '-S' or $1 eq '-c';
971         # Dependency generation for Makefiles. The other flags (-MF -MG -MP
972         # -MT -MQ) are always used with -M/-MM.
973         } elsif ($line =~ /\s(?:-M|-MM)\b/) {
974             $dependency = 1;
975         # Otherwise assume we are linking.
976         } else {
977             $link = 1;
978         }
979
980         # -MD/-MMD also cause dependency generation, but they don't imply -E!
981         if ($line =~ /\s(?:-MD|-MMD)\b/) {
982             $dependency      = 0;
983             $flag_preprocess = 0;
984         }
985
986         # Dependency generation for Makefiles, no preprocessing or other flags
987         # needed.
988         next if $dependency;
989
990         # Get all file extensions on this line.
991         my @extensions = $line =~ /$file_extension_regex/go;
992         # Ignore all unknown extensions to speedup the search below.
993         @extensions = grep { exists $extension{$_} } @extensions;
994
995         # These file types don't require preprocessing.
996         if (extension_found(\%extensions_no_preprocess, @extensions)) {
997             $preprocess = 0;
998         }
999         # These file types require preprocessing.
1000         if (extension_found(\%extensions_preprocess, @extensions)) {
1001             $preprocess = 1;
1002         }
1003
1004         if (not $flag_preprocess) {
1005             # If there are source files then it's compiling/linking in one
1006             # step and we must check both. We only check for source files
1007             # here, because header files cause too many false positives.
1008             if (extension_found(\%extensions_compile_link, @extensions)) {
1009                 # Assembly files don't need CFLAGS.
1010                 if (not extension_found(\%extensions_compile, @extensions)
1011                         and extension_found(\%extensions_no_compile, @extensions)) {
1012                     $compile = 0;
1013                 # But the rest does.
1014                 } else {
1015                     $compile = 1;
1016                 }
1017             # No compilable extensions found, either linking or compiling
1018             # header flags.
1019             #
1020             # If there are also no object files we are just compiling headers
1021             # (.h -> .h.gch). Don't check for linker flags in this case. Due
1022             # to our liberal checks for compiler lines, this also reduces the
1023             # number of false positives considerably.
1024             } elsif ($link
1025                     and not extension_found(\%extensions_object, @extensions)) {
1026                 $link = 0;
1027             }
1028         }
1029
1030         # Assume CXXFLAGS are required when a C++ file is specified in the
1031         # compiler line.
1032         my $compile_cpp = 0;
1033         if ($compile
1034                 and extension_found(\%extensions_compile_cpp, @extensions)) {
1035             $compile     = 0;
1036             $compile_cpp = 1;
1037         }
1038
1039         if ($option_buildd) {
1040             $statistics{preprocess}++  if $preprocess;
1041             $statistics{compile}++     if $compile;
1042             $statistics{compile_cpp}++ if $compile_cpp;
1043             $statistics{link}++        if $link;
1044         }
1045
1046         # Check hardening flags.
1047         my @missing;
1048         if ($compile and not all_flags_used($line, \@missing, @cflags)
1049                 # Libraries linked with -fPIC don't have to (and can't) be
1050                 # linked with -fPIE as well. It's no error if only PIE flags
1051                 # are missing.
1052                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1053                 # Assume dpkg-buildflags returns the correct flags.
1054                 and index($line, '`dpkg-buildflags --get CFLAGS`') == -1) {
1055             if (not $option_buildd) {
1056                 error_flags('CFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1057                 $exit |= $exit_code{flags_missing};
1058             } else {
1059                 $statistics{compile_missing}++;
1060             }
1061         } elsif ($compile_cpp and not all_flags_used($line, \@missing, @cflags)
1062                 # Libraries linked with -fPIC don't have to (and can't) be
1063                 # linked with -fPIE as well. It's no error if only PIE flags
1064                 # are missing.
1065                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1066                 # Assume dpkg-buildflags returns the correct flags.
1067                 and index($line, '`dpkg-buildflags --get CXXFLAGS`') == -1) {
1068             if (not $option_buildd) {
1069                 error_flags('CXXFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1070                 $exit |= $exit_code{flags_missing};
1071             } else {
1072                 $statistics{compile_cpp_missing}++;
1073             }
1074         }
1075         if ($preprocess
1076                 and (not all_flags_used($line, \@missing, @cppflags)
1077                     # The fortify flag might be overwritten, detect that.
1078                      or ($harden_fortify
1079                          and cppflags_fortify_broken($line, \@missing)))
1080                 # Assume dpkg-buildflags returns the correct flags.
1081                 and index($line, '`dpkg-buildflags --get CPPFLAGS`') == -1) {
1082             if (not $option_buildd) {
1083                 error_flags('CPPFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1084                 $exit |= $exit_code{flags_missing};
1085             } else {
1086                 $statistics{preprocess_missing}++;
1087             }
1088         }
1089         if ($link and not all_flags_used($line, \@missing, @ldflags)
1090                 # Same here, -fPIC conflicts with -fPIE.
1091                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_ldflags_pie)
1092                 # Assume dpkg-buildflags returns the correct flags.
1093                 and index($line, '`dpkg-buildflags --get LDFLAGS`') == -1) {
1094             if (not $option_buildd) {
1095                 error_flags('LDFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1096                 $exit |= $exit_code{flags_missing};
1097             } else {
1098                 $statistics{link_missing}++;
1099             }
1100         }
1101     }
1102 }
1103
1104 # Print statistics for buildd mode, only output in this mode.
1105 if ($option_buildd) {
1106     my @warning;
1107
1108     if ($statistics{preprocess_missing}) {
1109         push @warning, sprintf 'CPPFLAGS %d (of %d)',
1110                                $statistics{preprocess_missing},
1111                                $statistics{preprocess};
1112     }
1113     if ($statistics{compile_missing}) {
1114         push @warning, sprintf 'CFLAGS %d (of %d)',
1115                                $statistics{compile_missing},
1116                                $statistics{compile};
1117     }
1118     if ($statistics{compile_cpp_missing}) {
1119         push @warning, sprintf 'CXXFLAGS %d (of %d)',
1120                                $statistics{compile_cpp_missing},
1121                                $statistics{compile_cpp};
1122     }
1123     if ($statistics{link_missing}) {
1124         push @warning, sprintf 'LDFLAGS %d (of %d)',
1125                                $statistics{link_missing},
1126                                $statistics{link};
1127     }
1128     if (scalar @warning) {
1129         local $" = ', '; # array join string
1130         print "$buildd_tag{flags_missing}|@warning missing|\n";
1131     }
1132
1133     if ($statistics{commands_nonverbose}) {
1134         printf "$buildd_tag{non_verbose_build}|%d (of %d) hidden|\n",
1135                $statistics{commands_nonverbose},
1136                $statistics{commands},
1137     }
1138 }
1139
1140
1141 exit $exit;
1142
1143
1144 __END__
1145
1146 =head1 NAME
1147
1148 blhc - build log hardening check, checks build logs for missing hardening flags
1149
1150 =head1 SYNOPSIS
1151
1152 B<blhc> [I<options>] I<< <dpkg-buildpackage build log file>.. >>
1153
1154 =head1 DESCRIPTION
1155
1156 blhc is a small tool which checks build logs for missing hardening flags. It's
1157 licensed under the GPL 3 or later.
1158
1159 It's designed to check build logs generated by Debian's dpkg-buildpackage (or
1160 tools using dpkg-buildpackage like pbuilder or the official buildd build logs)
1161 to help maintainers detect missing hardening flags in their packages.
1162
1163 Only gcc is detected as compiler at the moment. If other compilers support
1164 hardening flags as well, please report them.
1165
1166 If there's no output, no flags are missing and the build log is fine.
1167
1168 =head1 OPTIONS
1169
1170 =over 8
1171
1172 =item B<--all>
1173
1174 Force check for all +all (+pie, +bindnow) hardening flags. By default it's
1175 auto detected.
1176
1177 =item B<--arch> I<architecture>
1178
1179 Set the specific architecture (e.g. amd64, armel, etc.), automatically
1180 disables hardening flags not available on this architecture. Is detected
1181 automatically if dpkg-buildpackage is used.
1182
1183 =item B<--bindnow>
1184
1185 Force check for all +bindnow hardening flags. By default it's auto detected.
1186
1187 =item B<--buildd>
1188
1189 Special mode for buildds when automatically parsing log files. The following
1190 changes are in effect:
1191
1192 =over 2
1193
1194 =item *
1195
1196 Print tags instead of normal warnings, see L</"BUILDD TAGS"> for a list of
1197 possible tags.
1198
1199 =item *
1200
1201 Don't check hardening flags in old log files (if dpkg-dev << 1.16.1 is
1202 detected).
1203
1204 =item *
1205
1206 Don't require Term::ANSIColor.
1207
1208 =item *
1209
1210 Return exit code 0, unless there was a error (-I, -W messages don't count as
1211 error).
1212
1213 =back
1214
1215 =item B<--color>
1216
1217 Use colored (ANSI) output for warning messages.
1218
1219 =item B<--ignore-arch> I<arch>
1220
1221 Ignore build logs from architectures matching I<arch>. I<arch> is a string.
1222
1223 Used to prevent false positives. This option can be specified multiple times.
1224
1225 =item B<--ignore-arch-flag> I<arch>:I<flag>
1226
1227 Like B<--ignore-flag>, but only ignore flag on I<arch>.
1228
1229 =item B<--ignore-arch-line> I<arch>:I<line>
1230
1231 Like B<--ignore-line>, but only ignore line on I<arch>.
1232
1233 =item B<--ignore-flag> I<flag>
1234
1235 Don't print an error when the specific flag is missing in a compiler line.
1236 I<flag> is a string.
1237
1238 Used to prevent false positives. This option can be specified multiple times.
1239
1240 =item B<--ignore-line> I<regex>
1241
1242 Ignore lines matching the given Perl regex. I<regex> is automatically anchored
1243 at the beginning and end of the line to prevent false negatives.
1244
1245 B<NOTE>: Not the input lines are checked, but the lines which are displayed in
1246 warnings (which have line continuation resolved).
1247
1248 Used to prevent false positives. This option can be specified multiple times.
1249
1250 =item B<--pie>
1251
1252 Force check for all +pie hardening flags. By default it's auto detected.
1253
1254 =item B<-h -? --help>
1255
1256 Print available options.
1257
1258 =item B<--version>
1259
1260 Print version number and license.
1261
1262 =back
1263
1264 Auto detection for B<--pie> and B<--bindnow> only works if at least one
1265 command uses the required hardening flag (e.g. -fPIE). Then it's required for
1266 all other commands as well.
1267
1268 =head1 EXAMPLES
1269
1270 Normal usage, parse a single log file.
1271
1272     blhc path/to/log/file
1273
1274 If there's no output, no flags are missing and the build log is fine.
1275
1276 Parse multiple log files. The exit code is ORed over all files.
1277
1278     blhc path/to/directory/with/log/files/*
1279
1280 Don't treat missing C<-g> as error:
1281
1282     blhc --ignore-flag -g path/to/log/file
1283
1284 Don't treat missing C<-pie> on kfreebsd-amd64 as error:
1285
1286     blhc --ignore-arch-flag kfreebsd-amd64:-pie path/to/log/file
1287
1288 Ignore lines consisting exactly of C<./script gcc file> which would cause a
1289 false positive.
1290
1291     blhc --ignore-line '\./script gcc file' path/to/log/file
1292
1293 Ignore lines matching C<./script gcc file> somewhere in the line.
1294
1295     blhc --ignore-line '.*\./script gcc file.*' path/to/log/file
1296
1297 Use blhc with pbuilder.
1298
1299     pbuilder path/to/package.dsc | tee path/log/file
1300     blhc path/to/file || echo flags missing
1301
1302 =head1 BUILDD TAGS
1303
1304 The following tags are used in I<--buildd> mode. In braces the additional data
1305 which is displayed.
1306
1307 =over 2
1308
1309 =item B<I-hardening-wrapper-used>
1310
1311 The package uses hardening-wrapper which intercepts calls to gcc and adds
1312 hardening flags. The build log doesn't contain any hardening flags and thus
1313 can't be checked by blhc.
1314
1315 =item B<W-compiler-flags-hidden> (summary of hidden lines)
1316
1317 Build log contains lines which hide the real compiler flags. For example:
1318
1319     CC test-a.c
1320     CC test-b.c
1321     CC test-c.c
1322     LD test
1323
1324 Most of the time either C<export V=1> or C<export verbose=1> in
1325 F<debian/rules> fixes builds with hidden compiler flags. Sometimes C<.SILENT>
1326 in a F<Makefile> must be removed. And as last resort the F<Makefile> must be
1327 patched to remove the C<@>s hiding the real compiler commands.
1328
1329 =item B<W-dpkg-buildflags-missing> (summary of missing flags)
1330
1331 CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS missing.
1332
1333 =item B<I-invalid-cmake-used> (version)
1334
1335 By default CMake ignores CPPFLAGS thus missing those hardening flags. Debian
1336 patched CMake in versions 2.8.7-1 and 2.8.7-2 to respect CPPFLAGS, but this
1337 patch was rejected by upstream and later reverted in Debian. Thus those two
1338 versions show correct usage of CPPFLAGS even if the package doesn't correctly
1339 handle them (for example by passing them to CFLAGS). To prevent false
1340 negatives just blacklist those two versions.
1341
1342 =item B<I-no-compiler-commands>
1343
1344 No compiler commands were detected. Either the log contains none or they were
1345 not correctly detected by blhc (please report the bug in this case).
1346
1347 =back
1348
1349 =head1 EXIT STATUS
1350
1351 The exit status is a "bit mask", each listed status is ORed when the error
1352 condition occurs to get the result.
1353
1354 =over 4
1355
1356 =item B<0>
1357
1358 Success.
1359
1360 =item B<1>
1361
1362 No compiler commands were found.
1363
1364 =item B<2>
1365
1366 Invalid arguments/options given to blhc.
1367
1368 =item B<4>
1369
1370 Non verbose build.
1371
1372 =item B<8>
1373
1374 Missing hardening flags.
1375
1376 =item B<16>
1377
1378 Hardening wrapper detected, no tests performed.
1379
1380 =item B<32>
1381
1382 Invalid CMake version used. See B<I-invalid-cmake-used> under L</"BUILDD
1383 TAGS"> for a detailed explanation.
1384
1385 =back
1386
1387 =head1 AUTHOR
1388
1389 Simon Ruderich, E<lt>simon@ruderich.orgE<gt>
1390
1391 Thanks to to Bernhard R. Link E<lt>brlink@debian.orgE<gt> and Jaria Alto
1392 E<lt>jari.aalto@cante.netE<gt> for their valuable input and suggestions.
1393
1394 =head1 LICENSE AND COPYRIGHT
1395
1396 Copyright (C) 2012 by Simon Ruderich
1397
1398 This program is free software: you can redistribute it and/or modify
1399 it under the terms of the GNU General Public License as published by
1400 the Free Software Foundation, either version 3 of the License, or
1401 (at your option) any later version.
1402
1403 This program is distributed in the hope that it will be useful,
1404 but WITHOUT ANY WARRANTY; without even the implied warranty of
1405 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1406 GNU General Public License for more details.
1407
1408 You should have received a copy of the GNU General Public License
1409 along with this program.  If not, see <http://www.gnu.org/licenses/>.
1410
1411 =head1 SEE ALSO
1412
1413 L<hardening-check(1)>, L<dpkg-buildflags(1)>
1414
1415 =cut