From ce60b13707add7e6b54c5817376234c4043506ed Mon Sep 17 00:00:00 2001 From: Daniel Fiala Date: Sun, 29 May 2022 20:11:24 +0200 Subject: [PATCH] Fix file operations in c_rehash. CVE-2022-2068 Reviewed-by: Richard Levitte Reviewed-by: Matt Caswell diff -wpruN '--exclude=*.orig' a~/tools/c_rehash.in a/tools/c_rehash.in --- a~/tools/c_rehash.in 1970-01-01 00:00:00 +++ a/tools/c_rehash.in 1970-01-01 00:00:00 @@ -84,24 +84,53 @@ if (-d $dirlist[0]) { } foreach (@dirlist) { - if(-d $_ and -w $_) { + if (-d $_ ) { + if ( -w $_) { hash_dir($_); + } else { + print "Skipping $_, can't write\n"; + $errorcount++; + } + } +} +exit($errorcount); + +sub copy_file { + my ($src_fname, $dst_fname) = @_; + + if (open(my $in, "<", $src_fname)) { + if (open(my $out, ">", $dst_fname)) { + print $out $_ while (<$in>); + close $out; + } else { + warn "Cannot open $dst_fname for write, $!"; + } + close $in; + } else { + warn "Cannot open $src_fname for read, $!"; } } sub hash_dir { + my $dir = shift; my %hashlist; - print "Doing $_[0]\n"; - chdir $_[0]; - opendir(DIR, "."); - my @flist = readdir(DIR); + + print "Doing $dir\n"; + + if (!chdir $dir) { + print STDERR "WARNING: Cannot chdir to '$dir', $!\n"; + return; + } + + opendir(DIR, ".") || print STDERR "WARNING: Cannot opendir '.', $!\n"; + my @flist = sort readdir(DIR); closedir DIR; if ( $removelinks ) { # Delete any existing symbolic links foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) { if(-l $_) { - unlink $_; - print "unlink $_" if $verbose; + print "unlink $_\n" if $verbose; + unlink $_ || warn "Can't unlink $_, $!\n"; } } } @@ -115,13 +144,16 @@ sub hash_dir { link_hash_cert($fname) if($cert); link_hash_crl($fname) if($crl); } + + chdir $pwd; } sub check_file { my ($is_cert, $is_crl) = (0,0); my $fname = $_[0]; - open IN, $fname; - while() { + + open(my $in, "<", $fname); + while(<$in>) { if(/^-----BEGIN (.*)-----/) { my $hdr = $1; if($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) { @@ -133,7 +165,7 @@ sub check_file { } } } - close IN; + close $in; return ($is_cert, $is_crl); } @@ -162,70 +194,49 @@ sub compute_hash { # certificate fingerprints sub link_hash_cert { - my $fname = $_[0]; - my ($hash, $fprint) = compute_hash($openssl, "x509", $x509hash, - "-fingerprint", "-noout", - "-in", $fname); - chomp $hash; - chomp $fprint; - return if !$hash; - $fprint =~ s/^.*=//; - $fprint =~ tr/://d; - my $suffix = 0; - # Search for an unused hash filename - while(exists $hashlist{"$hash.$suffix"}) { - # Hash matches: if fingerprint matches its a duplicate cert - if($hashlist{"$hash.$suffix"} eq $fprint) { - print STDERR "WARNING: Skipping duplicate certificate $fname\n"; - return; - } - $suffix++; - } - $hash .= ".$suffix"; - if ($symlink_exists) { - symlink $fname, $hash; - print "link $fname -> $hash\n" if $verbose; - } else { - open IN,"<$fname" or die "can't open $fname for read"; - open OUT,">$hash" or die "can't open $hash for write"; - print OUT ; # does the job for small text files - close OUT; - close IN; - print "copy $fname -> $hash\n" if $verbose; - } - $hashlist{$hash} = $fprint; + link_hash($_[0], 'cert'); } # Same as above except for a CRL. CRL links are of the form .r sub link_hash_crl { - my $fname = $_[0]; - my ($hash, $fprint) = compute_hash($openssl, "crl", $crlhash, + link_hash($_[0], 'crl'); +} + +sub link_hash { + my ($fname, $type) = @_; + my $is_cert = $type eq 'cert'; + + my ($hash, $fprint) = compute_hash($openssl, + $is_cert ? "x509" : "crl", + $is_cert ? $x509hash : $crlhash, "-fingerprint", "-noout", "-in", $fname); chomp $hash; + $hash =~ s/^.*=// if !$is_cert; chomp $fprint; return if !$hash; $fprint =~ s/^.*=//; $fprint =~ tr/://d; my $suffix = 0; # Search for an unused hash filename - while(exists $hashlist{"$hash.r$suffix"}) { + my $crlmark = $is_cert ? "" : "r"; + while(exists $hashlist{"$hash.$crlmark$suffix"}) { # Hash matches: if fingerprint matches its a duplicate cert - if($hashlist{"$hash.r$suffix"} eq $fprint) { - print STDERR "WARNING: Skipping duplicate CRL $fname\n"; + if ($hashlist{"$hash.$crlmark$suffix"} eq $fprint) { + my $what = $is_cert ? 'certificate' : 'CRL'; + print STDERR "WARNING: Skipping duplicate $what $fname\n"; return; } $suffix++; } - $hash .= ".r$suffix"; + $hash .= ".$crlmark$suffix"; if ($symlink_exists) { - symlink $fname, $hash; print "link $fname -> $hash\n" if $verbose; + symlink $fname, $hash || warn "Can't symlink, $!"; } else { - system ("cp", $fname, $hash); - print "cp $fname -> $hash\n" if $verbose; + print "copy $fname -> $hash\n" if $verbose; + copy_file($fname, $hash); } $hashlist{$hash} = $fprint; } -