]> ruderich.org/simon Gitweb - blhc/blhc.git/blob - bin/blhc
Prepare for multiple 'Build-Depends' checks.
[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 (index($line, 'Build-Depends: ') == 0) {
690             # If hardening wrapper is used (wraps calls to gcc and adds
691             # hardening flags automatically) we can't perform any checks,
692             # abort.
693             if ($line =~ /\bhardening-wrapper\b/) {
694                 if (not $option_buildd) {
695                     error_hardening_wrapper();
696                     $exit |= $exit_code{hardening_wrapper};
697                 } else {
698                     print "$buildd_tag{hardening_wrapper}||\n";
699                 }
700                 next FILE;
701             }
702         }
703
704         # We skip over unimportant lines at the beginning of the log to
705         # prevent false positives.
706         last if index($line, 'dpkg-buildpackage: ') == 0;
707     }
708
709     # Input lines, contain only the lines with compiler commands.
710     my @input = ();
711     # Non-verbose lines in the input. Used to reduce calls to
712     # is_non_verbose_build() (which is quite slow) in the second loop when
713     # it's already clear if a line is non-verbose or not.
714     my @input_nonverbose = ();
715
716     my $continuation = 0;
717     my $complete_line = undef;
718     while (my $line = <$fh>) {
719         # And stop at the end of the build log. Package details (reported by
720         # the buildd logs) are not important for us. This also prevents false
721         # positives.
722         last if index($line, 'Build finished at ') == 0
723                 and $line =~ /^Build finished at \d{8}-\d{4}$/;
724
725         # Detect architecture automatically unless overridden.
726         if (not $arch
727                 and index($line, 'dpkg-buildpackage: host architecture ') == 0) {
728             $arch = substr $line, 37, -1; # -1 to ignore '\n' at the end
729         }
730
731         # Ignore compiler warnings for now.
732         next if $line =~ /$warning_regex/o;
733
734         if (not $option_buildd and index($line, "\033") != -1) { # \033 = esc
735             # Remove all ANSI color sequences which are sometimes used in
736             # non-verbose builds.
737             $line = Term::ANSIColor::colorstrip($line);
738             # Also strip '\0xf' (delete previous character), used by Elinks'
739             # build system.
740             $line =~ s/\x0f//g;
741             # And "ESC(B" which seems to be used on armhf and hurd (not sure
742             # what it does).
743             $line =~ s/\033\(B//g;
744         }
745
746         # Check if this line indicates a non verbose build.
747         my $non_verbose = is_non_verbose_build($line);
748
749         # One line may contain multiple commands (";"). Treat each one as
750         # single line. parse_line() is slow, only use it when necessary.
751         my @line = (index($line, ';') == -1)
752                  ? ($line)
753                  : map {
754                        # Ensure newline at the line end - necessary for
755                        # correct parsing later.
756                        $_ =~ s/\s+$//;
757                        $_ .= "\n";
758                    } Text::ParseWords::parse_line(';', 1, $line);
759         foreach my $line (@line) {
760             if ($continuation) {
761                 $continuation = 0;
762
763                 # Join lines, but leave the "\" in place so it's clear where
764                 # the original line break was.
765                 chomp $complete_line;
766                 $complete_line .= ' ' . $line;
767             }
768             # Line continuation, line ends with "\".
769             if ($line =~ /\\$/) {
770                 $continuation = 1;
771                 # Start line continuation.
772                 if (not defined $complete_line) {
773                     $complete_line = $line;
774                 }
775                 next;
776             }
777
778             # Use the complete line if a line continuation occurred.
779             if (defined $complete_line) {
780                 $line = $complete_line;
781                 $complete_line = undef;
782             }
783
784             # Ignore lines with no compiler commands.
785             next if not $non_verbose
786                     and not $line =~ /$cc_regex_normal/o;
787             # Ignore lines with no filenames with extensions. May miss some
788             # non-verbose builds (e.g. "gcc -o test" [sic!]), but shouldn't be
789             # a problem as the log will most likely contain other non-verbose
790             # commands which are detected.
791             next if not $non_verbose
792                     and not $line =~ /$file_extension_regex/o;
793
794             # Ignore false positives.
795             #
796             # `./configure` output.
797             next if not $non_verbose
798                     and $line =~ /^(?:checking|[Cc]onfigure:) /;
799             next if $line =~ /^\s*(?:Host\s+)?(?:C(?:\+\+)?\s+)?
800                                 [Cc]ompiler[\s.]*:?\s+
801                                 /x;
802             next if $line =~ /^\s*(?:- )?(?:HOST_)?(?:CC|CXX)\s*=\s*$cc_regex_full\s*$/o;
803             # `moc-qt4`, contains '-I/usr/share/qt4/mkspecs/linux-g++' (or
804             # similar for other architectures) which gets recognized as a
805             # compiler line. Ignore it.
806             next if $line =~ m{^/usr/bin/moc-qt4
807                                \s.+\s
808                                -I/usr/share/qt4/mkspecs/[a-z]+-g\++(?:-64)?
809                                \s}x;
810             # Ignore false positives when the line contains only CC=gcc but no
811             # other gcc command.
812             if ($line =~ /(.*)CC=$cc_regex_full(.*)/o) {
813                 my $before = $1;
814                 my $after  = $2;
815                 next if     not $before =~ /$cc_regex_normal/o
816                         and not $after  =~ /$cc_regex_normal/o;
817             }
818
819             # Check if additional hardening options were used. Used to ensure
820             # they are used for the complete build.
821             $harden_pie     = 1 if any_flags_used($line, @def_cflags_pie,
822                                                          @def_ldflags_pie);
823             $harden_bindnow = 1 if any_flags_used($line, @def_ldflags_bindnow);
824
825             push @input, $line;
826             push @input_nonverbose, $non_verbose;
827         }
828     }
829
830     close $fh or die $!;
831
832     # Ignore arch if requested.
833     if (scalar @option_ignore_arch > 0 and $arch) {
834         foreach my $ignore (@option_ignore_arch) {
835             if ($arch eq $ignore) {
836                 print "ignoring architecture '$arch'\n";
837                 next FILE;
838             }
839         }
840     }
841
842     if (scalar @input == 0) {
843         if (not $option_buildd) {
844             print "No compiler commands!\n";
845             $exit |= $exit_code{no_compiler_commands};
846         } else {
847             print "$buildd_tag{no_compiler_commands}||\n";
848         }
849         next FILE;
850     }
851
852     if ($option_buildd) {
853         $statistics{commands} += scalar @input;
854     }
855
856     # Option or auto detected.
857     if ($arch) {
858         # The following was partially copied from dpkg-dev 1.16.4.3
859         # (/usr/share/perl5/Dpkg/Vendor/Debian.pm, add_hardening_flags()),
860         # copyright Raphaël Hertzog <hertzog@debian.org>, Kees Cook
861         # <kees@debian.org>, Canonical, Ltd. licensed under GPL version 2 or
862         # later. Keep it in sync.
863
864         require Dpkg::Arch;
865         my ($abi, $os, $cpu) = Dpkg::Arch::debarch_to_debtriplet($arch);
866
867         # Disable unsupported hardening options.
868         if ($cpu =~ /^(?:ia64|alpha|mips|mipsel|hppa)$/ or $arch eq 'arm') {
869             $harden_stack = 0;
870         }
871         if ($cpu =~ /^(?:ia64|hppa|avr32)$/) {
872             $harden_relro   = 0;
873             $harden_bindnow = 0;
874         }
875     }
876
877     # Default values.
878     my @cflags   = @def_cflags;
879     my @cxxflags = @def_cxxflags;
880     my @cppflags = @def_cppflags;
881     my @ldflags  = @def_ldflags;
882     # Check the specified hardening options, same order as dpkg-buildflags.
883     if ($harden_pie) {
884         @cflags   = (@cflags,   @def_cflags_pie);
885         @cxxflags = (@cxxflags, @def_cflags_pie);
886         @ldflags  = (@ldflags,  @def_ldflags_pie);
887     }
888     if ($harden_stack) {
889         @cflags   = (@cflags,   @def_cflags_stack);
890         @cxxflags = (@cxxflags, @def_cflags_stack);
891     }
892     if ($harden_fortify) {
893         @cflags   = (@cflags,   @def_cflags_fortify);
894         @cxxflags = (@cxxflags, @def_cflags_fortify);
895         @cppflags = (@cppflags, @def_cppflags_fortify);
896     }
897     if ($harden_format) {
898         @cflags   = (@cflags,   @def_cflags_format);
899         @cxxflags = (@cxxflags, @def_cflags_format);
900     }
901     if ($harden_relro) {
902         @ldflags = (@ldflags, @def_ldflags_relro);
903     }
904     if ($harden_bindnow) {
905         @ldflags = (@ldflags, @def_ldflags_bindnow);
906     }
907
908     # Hack to fix cppflags_fortify_broken() if --ignore-flag
909     # -D_FORTIFY_SOURCE=2 is used to ignore missing fortification. Only works
910     # as long as @def_cppflags_fortify contains only one variable.
911     if (scalar @def_cppflags_fortify == 0) {
912         $harden_fortify = 0;
913     }
914
915     # Ignore flags for this arch if requested.
916     if ($arch and exists $option_ignore_arch_flag{$arch}) {
917         my @local_flag_refs = (\@cflags, \@cxxflags, \@cppflags, \@ldflags);
918
919         remove_flags(\@local_flag_refs,
920                      \%flag_renames,
921                      @{$option_ignore_arch_flag{$arch}});
922     }
923
924     my @ignore_line = @option_ignore_line;
925     # Ignore lines for this arch if requested.
926     if ($arch and exists $option_ignore_arch_line{$arch}) {
927         @ignore_line = (@ignore_line, @{$option_ignore_arch_line{$arch}});
928     }
929
930 LINE:
931     for (my $i = 0; $i < scalar @input; $i++) {
932         my $line = $input[$i];
933
934         # Ignore line if requested.
935         foreach my $ignore (@ignore_line) {
936             next LINE if $line =~ /$ignore/;
937         }
938
939         my $skip = 0;
940         if ($input_nonverbose[$i]
941                 and is_non_verbose_build($line, $input[$i + 1], \$skip)) {
942             if (not $option_buildd) {
943                 error_non_verbose_build($line);
944                 $exit |= $exit_code{non_verbose_build};
945             } else {
946                 $statistics{commands_nonverbose}++;
947             }
948             next;
949         }
950         # Even if it's a verbose build, we might have to skip this line.
951         next if $skip;
952
953         # Remove everything until and including the compiler command. Makes
954         # checks easier and faster.
955         $line =~ s/^.*?$cc_regex//o;
956         # "([...] test.c)" is not detected as 'test.c' - fix this by removing
957         # the brace and similar characters at the line end.
958         $line =~ s/['")]+$//;
959
960         # Skip unnecessary tests when only preprocessing.
961         my $flag_preprocess = 0;
962
963         my $dependency = 0;
964         my $preprocess = 0;
965         my $compile    = 0;
966         my $link       = 0;
967
968         # Preprocess, compile, assemble.
969         if ($line =~ /\s(-E|-S|-c)\b/) {
970             $preprocess      = 1;
971             $flag_preprocess = 1 if $1 eq '-E';
972             $compile         = 1 if $1 eq '-S' or $1 eq '-c';
973         # Dependency generation for Makefiles. The other flags (-MF -MG -MP
974         # -MT -MQ) are always used with -M/-MM.
975         } elsif ($line =~ /\s(?:-M|-MM)\b/) {
976             $dependency = 1;
977         # Otherwise assume we are linking.
978         } else {
979             $link = 1;
980         }
981
982         # -MD/-MMD also cause dependency generation, but they don't imply -E!
983         if ($line =~ /\s(?:-MD|-MMD)\b/) {
984             $dependency      = 0;
985             $flag_preprocess = 0;
986         }
987
988         # Dependency generation for Makefiles, no preprocessing or other flags
989         # needed.
990         next if $dependency;
991
992         # Get all file extensions on this line.
993         my @extensions = $line =~ /$file_extension_regex/go;
994         # Ignore all unknown extensions to speedup the search below.
995         @extensions = grep { exists $extension{$_} } @extensions;
996
997         # These file types don't require preprocessing.
998         if (extension_found(\%extensions_no_preprocess, @extensions)) {
999             $preprocess = 0;
1000         }
1001         # These file types require preprocessing.
1002         if (extension_found(\%extensions_preprocess, @extensions)) {
1003             $preprocess = 1;
1004         }
1005
1006         if (not $flag_preprocess) {
1007             # If there are source files then it's compiling/linking in one
1008             # step and we must check both. We only check for source files
1009             # here, because header files cause too many false positives.
1010             if (extension_found(\%extensions_compile_link, @extensions)) {
1011                 # Assembly files don't need CFLAGS.
1012                 if (not extension_found(\%extensions_compile, @extensions)
1013                         and extension_found(\%extensions_no_compile, @extensions)) {
1014                     $compile = 0;
1015                 # But the rest does.
1016                 } else {
1017                     $compile = 1;
1018                 }
1019             # No compilable extensions found, either linking or compiling
1020             # header flags.
1021             #
1022             # If there are also no object files we are just compiling headers
1023             # (.h -> .h.gch). Don't check for linker flags in this case. Due
1024             # to our liberal checks for compiler lines, this also reduces the
1025             # number of false positives considerably.
1026             } elsif ($link
1027                     and not extension_found(\%extensions_object, @extensions)) {
1028                 $link = 0;
1029             }
1030         }
1031
1032         # Assume CXXFLAGS are required when a C++ file is specified in the
1033         # compiler line.
1034         my $compile_cpp = 0;
1035         if ($compile
1036                 and extension_found(\%extensions_compile_cpp, @extensions)) {
1037             $compile     = 0;
1038             $compile_cpp = 1;
1039         }
1040
1041         if ($option_buildd) {
1042             $statistics{preprocess}++  if $preprocess;
1043             $statistics{compile}++     if $compile;
1044             $statistics{compile_cpp}++ if $compile_cpp;
1045             $statistics{link}++        if $link;
1046         }
1047
1048         # Check hardening flags.
1049         my @missing;
1050         if ($compile and not all_flags_used($line, \@missing, @cflags)
1051                 # Libraries linked with -fPIC don't have to (and can't) be
1052                 # linked with -fPIE as well. It's no error if only PIE flags
1053                 # are missing.
1054                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1055                 # Assume dpkg-buildflags returns the correct flags.
1056                 and index($line, '`dpkg-buildflags --get CFLAGS`') == -1) {
1057             if (not $option_buildd) {
1058                 error_flags('CFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1059                 $exit |= $exit_code{flags_missing};
1060             } else {
1061                 $statistics{compile_missing}++;
1062             }
1063         } elsif ($compile_cpp and not all_flags_used($line, \@missing, @cflags)
1064                 # Libraries linked with -fPIC don't have to (and can't) be
1065                 # linked with -fPIE as well. It's no error if only PIE flags
1066                 # are missing.
1067                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_cflags_pie)
1068                 # Assume dpkg-buildflags returns the correct flags.
1069                 and index($line, '`dpkg-buildflags --get CXXFLAGS`') == -1) {
1070             if (not $option_buildd) {
1071                 error_flags('CXXFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1072                 $exit |= $exit_code{flags_missing};
1073             } else {
1074                 $statistics{compile_cpp_missing}++;
1075             }
1076         }
1077         if ($preprocess
1078                 and (not all_flags_used($line, \@missing, @cppflags)
1079                     # The fortify flag might be overwritten, detect that.
1080                      or ($harden_fortify
1081                          and cppflags_fortify_broken($line, \@missing)))
1082                 # Assume dpkg-buildflags returns the correct flags.
1083                 and index($line, '`dpkg-buildflags --get CPPFLAGS`') == -1) {
1084             if (not $option_buildd) {
1085                 error_flags('CPPFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1086                 $exit |= $exit_code{flags_missing};
1087             } else {
1088                 $statistics{preprocess_missing}++;
1089             }
1090         }
1091         if ($link and not all_flags_used($line, \@missing, @ldflags)
1092                 # Same here, -fPIC conflicts with -fPIE.
1093                 and not pic_pie_conflict($line, $harden_pie, \@missing, @def_ldflags_pie)
1094                 # Assume dpkg-buildflags returns the correct flags.
1095                 and index($line, '`dpkg-buildflags --get LDFLAGS`') == -1) {
1096             if (not $option_buildd) {
1097                 error_flags('LDFLAGS missing', \@missing, \%flag_renames, $input[$i]);
1098                 $exit |= $exit_code{flags_missing};
1099             } else {
1100                 $statistics{link_missing}++;
1101             }
1102         }
1103     }
1104 }
1105
1106 # Print statistics for buildd mode, only output in this mode.
1107 if ($option_buildd) {
1108     my @warning;
1109
1110     if ($statistics{preprocess_missing}) {
1111         push @warning, sprintf 'CPPFLAGS %d (of %d)',
1112                                $statistics{preprocess_missing},
1113                                $statistics{preprocess};
1114     }
1115     if ($statistics{compile_missing}) {
1116         push @warning, sprintf 'CFLAGS %d (of %d)',
1117                                $statistics{compile_missing},
1118                                $statistics{compile};
1119     }
1120     if ($statistics{compile_cpp_missing}) {
1121         push @warning, sprintf 'CXXFLAGS %d (of %d)',
1122                                $statistics{compile_cpp_missing},
1123                                $statistics{compile_cpp};
1124     }
1125     if ($statistics{link_missing}) {
1126         push @warning, sprintf 'LDFLAGS %d (of %d)',
1127                                $statistics{link_missing},
1128                                $statistics{link};
1129     }
1130     if (scalar @warning) {
1131         local $" = ', '; # array join string
1132         print "$buildd_tag{flags_missing}|@warning missing|\n";
1133     }
1134
1135     if ($statistics{commands_nonverbose}) {
1136         printf "$buildd_tag{non_verbose_build}|%d (of %d) hidden|\n",
1137                $statistics{commands_nonverbose},
1138                $statistics{commands},
1139     }
1140 }
1141
1142
1143 exit $exit;
1144
1145
1146 __END__
1147
1148 =head1 NAME
1149
1150 blhc - build log hardening check, checks build logs for missing hardening flags
1151
1152 =head1 SYNOPSIS
1153
1154 B<blhc> [I<options>] I<< <dpkg-buildpackage build log file>.. >>
1155
1156 =head1 DESCRIPTION
1157
1158 blhc is a small tool which checks build logs for missing hardening flags. It's
1159 licensed under the GPL 3 or later.
1160
1161 It's designed to check build logs generated by Debian's dpkg-buildpackage (or
1162 tools using dpkg-buildpackage like pbuilder or the official buildd build logs)
1163 to help maintainers detect missing hardening flags in their packages.
1164
1165 Only gcc is detected as compiler at the moment. If other compilers support
1166 hardening flags as well, please report them.
1167
1168 If there's no output, no flags are missing and the build log is fine.
1169
1170 =head1 OPTIONS
1171
1172 =over 8
1173
1174 =item B<--all>
1175
1176 Force check for all +all (+pie, +bindnow) hardening flags. By default it's
1177 auto detected.
1178
1179 =item B<--arch> I<architecture>
1180
1181 Set the specific architecture (e.g. amd64, armel, etc.), automatically
1182 disables hardening flags not available on this architecture. Is detected
1183 automatically if dpkg-buildpackage is used.
1184
1185 =item B<--bindnow>
1186
1187 Force check for all +bindnow hardening flags. By default it's auto detected.
1188
1189 =item B<--buildd>
1190
1191 Special mode for buildds when automatically parsing log files. The following
1192 changes are in effect:
1193
1194 =over 2
1195
1196 =item *
1197
1198 Print tags instead of normal warnings, see L</"BUILDD TAGS"> for a list of
1199 possible tags.
1200
1201 =item *
1202
1203 Don't check hardening flags in old log files (if dpkg-dev << 1.16.1 is
1204 detected).
1205
1206 =item *
1207
1208 Don't require Term::ANSIColor.
1209
1210 =item *
1211
1212 Return exit code 0, unless there was a error (-I, -W messages don't count as
1213 error).
1214
1215 =back
1216
1217 =item B<--color>
1218
1219 Use colored (ANSI) output for warning messages.
1220
1221 =item B<--ignore-arch> I<arch>
1222
1223 Ignore build logs from architectures matching I<arch>. I<arch> is a string.
1224
1225 Used to prevent false positives. This option can be specified multiple times.
1226
1227 =item B<--ignore-arch-flag> I<arch>:I<flag>
1228
1229 Like B<--ignore-flag>, but only ignore flag on I<arch>.
1230
1231 =item B<--ignore-arch-line> I<arch>:I<line>
1232
1233 Like B<--ignore-line>, but only ignore line on I<arch>.
1234
1235 =item B<--ignore-flag> I<flag>
1236
1237 Don't print an error when the specific flag is missing in a compiler line.
1238 I<flag> is a string.
1239
1240 Used to prevent false positives. This option can be specified multiple times.
1241
1242 =item B<--ignore-line> I<regex>
1243
1244 Ignore lines matching the given Perl regex. I<regex> is automatically anchored
1245 at the beginning and end of the line to prevent false negatives.
1246
1247 B<NOTE>: Not the input lines are checked, but the lines which are displayed in
1248 warnings (which have line continuation resolved).
1249
1250 Used to prevent false positives. This option can be specified multiple times.
1251
1252 =item B<--pie>
1253
1254 Force check for all +pie hardening flags. By default it's auto detected.
1255
1256 =item B<-h -? --help>
1257
1258 Print available options.
1259
1260 =item B<--version>
1261
1262 Print version number and license.
1263
1264 =back
1265
1266 Auto detection for B<--pie> and B<--bindnow> only works if at least one
1267 command uses the required hardening flag (e.g. -fPIE). Then it's required for
1268 all other commands as well.
1269
1270 =head1 EXAMPLES
1271
1272 Normal usage, parse a single log file.
1273
1274     blhc path/to/log/file
1275
1276 If there's no output, no flags are missing and the build log is fine.
1277
1278 Parse multiple log files. The exit code is ORed over all files.
1279
1280     blhc path/to/directory/with/log/files/*
1281
1282 Don't treat missing C<-g> as error:
1283
1284     blhc --ignore-flag -g path/to/log/file
1285
1286 Don't treat missing C<-pie> on kfreebsd-amd64 as error:
1287
1288     blhc --ignore-arch-flag kfreebsd-amd64:-pie path/to/log/file
1289
1290 Ignore lines consisting exactly of C<./script gcc file> which would cause a
1291 false positive.
1292
1293     blhc --ignore-line '\./script gcc file' path/to/log/file
1294
1295 Ignore lines matching C<./script gcc file> somewhere in the line.
1296
1297     blhc --ignore-line '.*\./script gcc file.*' path/to/log/file
1298
1299 Use blhc with pbuilder.
1300
1301     pbuilder path/to/package.dsc | tee path/log/file
1302     blhc path/to/file || echo flags missing
1303
1304 =head1 BUILDD TAGS
1305
1306 The following tags are used in I<--buildd> mode. In braces the additional data
1307 which is displayed.
1308
1309 =over 2
1310
1311 =item B<I-hardening-wrapper-used>
1312
1313 The package uses hardening-wrapper which intercepts calls to gcc and adds
1314 hardening flags. The build log doesn't contain any hardening flags and thus
1315 can't be checked by blhc.
1316
1317 =item B<W-compiler-flags-hidden> (summary of hidden lines)
1318
1319 Build log contains lines which hide the real compiler flags. For example:
1320
1321     CC test-a.c
1322     CC test-b.c
1323     CC test-c.c
1324     LD test
1325
1326 Most of the time either C<export V=1> or C<export verbose=1> in
1327 F<debian/rules> fixes builds with hidden compiler flags. Sometimes C<.SILENT>
1328 in a F<Makefile> must be removed. And as last resort the F<Makefile> must be
1329 patched to remove the C<@>s hiding the real compiler commands.
1330
1331 =item B<W-dpkg-buildflags-missing> (summary of missing flags)
1332
1333 CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS missing.
1334
1335 =item B<I-invalid-cmake-used> (version)
1336
1337 By default CMake ignores CPPFLAGS thus missing those hardening flags. Debian
1338 patched CMake in versions 2.8.7-1 and 2.8.7-2 to respect CPPFLAGS, but this
1339 patch was rejected by upstream and later reverted in Debian. Thus those two
1340 versions show correct usage of CPPFLAGS even if the package doesn't correctly
1341 handle them (for example by passing them to CFLAGS). To prevent false
1342 negatives just blacklist those two versions.
1343
1344 =item B<I-no-compiler-commands>
1345
1346 No compiler commands were detected. Either the log contains none or they were
1347 not correctly detected by blhc (please report the bug in this case).
1348
1349 =back
1350
1351 =head1 EXIT STATUS
1352
1353 The exit status is a "bit mask", each listed status is ORed when the error
1354 condition occurs to get the result.
1355
1356 =over 4
1357
1358 =item B<0>
1359
1360 Success.
1361
1362 =item B<1>
1363
1364 No compiler commands were found.
1365
1366 =item B<2>
1367
1368 Invalid arguments/options given to blhc.
1369
1370 =item B<4>
1371
1372 Non verbose build.
1373
1374 =item B<8>
1375
1376 Missing hardening flags.
1377
1378 =item B<16>
1379
1380 Hardening wrapper detected, no tests performed.
1381
1382 =item B<32>
1383
1384 Invalid CMake version used. See B<I-invalid-cmake-used> under L</"BUILDD
1385 TAGS"> for a detailed explanation.
1386
1387 =back
1388
1389 =head1 AUTHOR
1390
1391 Simon Ruderich, E<lt>simon@ruderich.orgE<gt>
1392
1393 Thanks to to Bernhard R. Link E<lt>brlink@debian.orgE<gt> and Jaria Alto
1394 E<lt>jari.aalto@cante.netE<gt> for their valuable input and suggestions.
1395
1396 =head1 LICENSE AND COPYRIGHT
1397
1398 Copyright (C) 2012 by Simon Ruderich
1399
1400 This program is free software: you can redistribute it and/or modify
1401 it under the terms of the GNU General Public License as published by
1402 the Free Software Foundation, either version 3 of the License, or
1403 (at your option) any later version.
1404
1405 This program is distributed in the hope that it will be useful,
1406 but WITHOUT ANY WARRANTY; without even the implied warranty of
1407 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1408 GNU General Public License for more details.
1409
1410 You should have received a copy of the GNU General Public License
1411 along with this program.  If not, see <http://www.gnu.org/licenses/>.
1412
1413 =head1 SEE ALSO
1414
1415 L<hardening-check(1)>, L<dpkg-buildflags(1)>
1416
1417 =cut