libressl: use the C c_rehash tool written by Timo Teras via Alpine.

Thanks to Timo Teras for doing the hard work!
This commit is contained in:
Juan RP 2014-10-20 13:14:36 +02:00
parent 14eac4987a
commit 04251ee7fe
3 changed files with 242 additions and 185 deletions

View file

@ -1,180 +0,0 @@
#!/usr/bin/perl
# Perl c_rehash script, scan all files in a directory
# and add symbolic links to their hash values.
my $openssl;
my $dir = "/etc/ssl";
my $prefix = "/usr";
if(defined $ENV{OPENSSL}) {
$openssl = $ENV{OPENSSL};
} else {
$openssl = "openssl";
$ENV{OPENSSL} = $openssl;
}
my $pwd;
eval "require Cwd";
if (defined(&Cwd::getcwd)) {
$pwd=Cwd::getcwd();
} else {
$pwd=`pwd`; chomp($pwd);
}
my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':'; # DOS/Win32 or Unix delimiter?
$ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : ""); # prefix our path
if(! -x $openssl) {
my $found = 0;
foreach (split /$path_delim/, $ENV{PATH}) {
if(-x "$_/$openssl") {
$found = 1;
$openssl = "$_/$openssl";
last;
}
}
if($found == 0) {
print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n";
exit 0;
}
}
if(@ARGV) {
@dirlist = @ARGV;
} elsif($ENV{SSL_CERT_DIR}) {
@dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR};
} else {
$dirlist[0] = "$dir/certs";
}
if (-d $dirlist[0]) {
chdir $dirlist[0];
$openssl="$pwd/$openssl" if (!-x $openssl);
chdir $pwd;
}
foreach (@dirlist) {
if(-d $_ and -w $_) {
hash_dir($_);
}
}
sub hash_dir {
my %hashlist;
print "Doing $_[0]\n";
chdir $_[0];
opendir(DIR, ".");
my @flist = readdir(DIR);
# Delete any existing symbolic links
foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) {
if(-l $_) {
unlink $_;
}
}
closedir DIR;
FILE: foreach $fname (grep {/\.pem$/} @flist) {
# Check to see if certificates and/or CRLs present.
my ($cert, $crl) = check_file($fname);
if(!$cert && !$crl) {
print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n";
next;
}
link_hash_cert($fname) if($cert);
link_hash_crl($fname) if($crl);
}
}
sub check_file {
my ($is_cert, $is_crl) = (0,0);
my $fname = $_[0];
open IN, $fname;
while(<IN>) {
if(/^-----BEGIN (.*)-----/) {
my $hdr = $1;
if($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) {
$is_cert = 1;
last if($is_crl);
} elsif($hdr eq "X509 CRL") {
$is_crl = 1;
last if($is_cert);
}
}
}
close IN;
return ($is_cert, $is_crl);
}
# Link a certificate to its subject name hash value, each hash is of
# the form <hash>.<n> where n is an integer. If the hash value already exists
# then we need to up the value of n, unless its a duplicate in which
# case we skip the link. We check for duplicates by comparing the
# certificate fingerprints
sub link_hash_cert {
my $fname = $_[0];
$fname =~ s/'/'\\''/g;
my ($hash, $fprint) = `"$openssl" x509 -hash -fingerprint -noout -in "$fname"`;
chomp $hash;
chomp $fprint;
$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";
print "$fname => $hash\n";
$symlink_exists=eval {symlink("",""); 1};
if ($symlink_exists) {
symlink $fname, $hash;
} 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 <IN>; # does the job for small text files
close OUT;
close IN;
}
$hashlist{$hash} = $fprint;
}
# Same as above except for a CRL. CRL links are of the form <hash>.r<n>
sub link_hash_crl {
my $fname = $_[0];
$fname =~ s/'/'\\''/g;
my ($hash, $fprint) = `"$openssl" crl -hash -fingerprint -noout -in '$fname'`;
chomp $hash;
chomp $fprint;
$fprint =~ s/^.*=//;
$fprint =~ tr/://d;
my $suffix = 0;
# Search for an unused hash filename
while(exists $hashlist{"$hash.r$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";
return;
}
$suffix++;
}
$hash .= ".r$suffix";
print "$fname => $hash\n";
$symlink_exists=eval {symlink("",""); 1};
if ($symlink_exists) {
symlink $fname, $hash;
} else {
system ("cp", $fname, $hash);
}
$hashlist{$hash} = $fprint;
}

View file

@ -0,0 +1,236 @@
/* c_rehash.c - C implementation based on the Perl and shell versions
*
* Copyright (c) 2013 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This software is licensed under the MIT License.
* Full license available at: http://opensource.org/licenses/MIT
*/
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <ctype.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <fnmatch.h>
#include <sys/stat.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#define countof(x) (sizeof(x) / sizeof(x[0]))
struct hash_info {
struct hash_info *next;
int type;
unsigned long hash;
unsigned char digest[EVP_MAX_MD_SIZE];
};
enum Type {
TYPE_CERT = 0,
TYPE_CRL,
MAX_TYPES,
};
static const EVP_MD *evpmd;
static int evpmdsize;
static const char *prefixes[MAX_TYPES] = { "", "r" };
static struct hash_info *hash_table[257];
static int get_link_id(int type, unsigned long hash, unsigned char *digest)
{
unsigned int bucket = hash % countof(hash_table);
struct hash_info *hi;
int count = 0;
for (hi = hash_table[bucket]; hi; hi = hi->next) {
if (hi->type != type || hi->hash != hash)
continue;
if (memcmp(digest, hi->digest, evpmdsize) == 0)
return -1;
count++;
}
hi = malloc(sizeof(*hi));
hi->next = hash_table[bucket];
hi->type = type;
hi->hash = hash;
memcpy(hi->digest, digest, evpmdsize);
hash_table[bucket] = hi;
return count;
}
static int link_file(int dirfd, const char *filename, int type, unsigned long hash, unsigned char *digest)
{
char linkfn[32];
int id;
id = get_link_id(type, hash, digest);
if (id < 0) {
fprintf(stderr, "WARNING: Skipping duplicate certificate in file %s\n",
filename);
return -1;
}
snprintf(linkfn, sizeof(linkfn),
"%08lx.%s%d", hash, prefixes[type], id);
fprintf(stdout, "%s => %s\n", linkfn, filename);
if (symlinkat(filename, dirfd, linkfn) < 0)
perror(linkfn);
return 0;
}
static BIO *BIO_openat(int dirfd, const char *filename)
{
FILE *fp;
BIO *bio;
int fd;
fd = openat(dirfd, filename, O_RDONLY);
if (fd < 0) {
perror(filename);
return NULL;
}
fp = fdopen(fd, "r");
if (fp == NULL) {
close(fd);
return NULL;
}
bio = BIO_new_fp(fp, BIO_CLOSE);
if (!bio) {
fclose(fp);
return NULL;
}
return bio;
}
static int hash_file(int dirfd, const char *filename)
{
STACK_OF(X509_INFO) *inf;
X509_INFO *x;
BIO *b;
int i, count = 0;
unsigned char digest[EVP_MAX_MD_SIZE];
b = BIO_openat(dirfd, filename);
if (!b)
return -1;
inf = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL);
BIO_free(b);
if (!inf)
return -1;
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
x = sk_X509_INFO_value(inf, i);
if (x->x509) {
X509_digest(x->x509, evpmd, digest, NULL);
link_file(dirfd, filename, TYPE_CERT,
X509_subject_name_hash(x->x509), digest);
count++;
}
if (x->crl) {
X509_CRL_digest(x->crl, evpmd, digest, NULL);
link_file(dirfd, filename, TYPE_CRL,
X509_NAME_hash(X509_CRL_get_issuer(x->crl)),
digest);
count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
if (count == 0) {
fprintf(stderr,
"WARNING: %s does not contain a certificate or CRL: skipping\n",
filename);
}
return count;
}
static int is_hash_filename(const char *fn)
{
int i;
for (i = 0; i < 8; i++)
if (!isxdigit(fn[i]))
return 0;
if (fn[i++] != '.')
return 0;
if (fn[i] == 'r') i++;
for (; fn[i] != 0; i++)
if (!isdigit(fn[i]))
return 0;
return 1;
}
static int hash_dir(const char *dirname)
{
struct dirent *de;
struct stat st;
int dirfd;
DIR *d;
fprintf(stdout, "Doing %s\n", dirname);
dirfd = open(dirname, O_RDONLY | O_DIRECTORY);
if (dirfd < 0) {
perror(dirname);
return -1;
}
d = opendir(dirname);
if (!d) {
close(dirfd);
return -1;
}
while ((de = readdir(d)) != NULL) {
if (fstatat(dirfd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
continue;
if (!S_ISLNK(st.st_mode))
continue;
if (!is_hash_filename(de->d_name))
continue;
if (unlinkat(dirfd, de->d_name, 0) < 0)
perror(de->d_name);
}
rewinddir(d);
while ((de = readdir(d)) != NULL) {
if (fnmatch("*.pem", de->d_name, FNM_NOESCAPE) == 0)
hash_file(dirfd, de->d_name);
}
closedir(d);
return 0;
}
int main(int argc, char **argv)
{
const char *env;
int i;
evpmd = EVP_sha1();
evpmdsize = EVP_MD_size(evpmd);
if (argc > 1) {
for (i = 1; i < argc; i++)
hash_dir(argv[i]);
} else if ((env = getenv("SSL_CERT_DIR")) != NULL) {
char *e, *m;
m = strdup(env);
for (e = strtok(m, ":"); e != NULL; e = strtok(NULL, ":"))
hash_dir(e);
free(m);
} else {
hash_dir("/etc/ssl/certs");
}
return 0;
}

View file

@ -11,6 +11,9 @@ homepage="http://www.libressl.org/"
distfiles="http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/${pkgname}-${version}.tar.gz"
checksum=fb5ada41a75b31c8dd9ff013daca57b253047ad14e43f65d8b41879b7b8e3c17
post_build() {
$CC -Wall $CFLAGS -Iinclude -Lcrypto/.libs $LDFLAGS ${FILESDIR}/c_rehash.c -o ${wrksrc}/c_rehash -lcrypto
}
post_install() {
vlicense COPYING
mv ${DESTDIR}/usr/share/man/man1/{passwd.1,libressl-passwd.1}
@ -30,15 +33,13 @@ libressl-devel_package() {
}
libressl-openssl_package() {
short_desc+=" - utilities"
# required by c_rehash
depends="perl"
provides="openssl-${version}_${revision} openssl-crehash-${version}_${revision}"
replaces="libressl<2.0.1_2 openssl>=0 openssl-crehash>=0"
provides="openssl-${version}_${revision}"
replaces="libressl<2.0.1_2 openssl>=0"
conf_files="/etc/ssl/openssl.cnf"
pkg_install() {
vinstall ${FILESDIR}/openssl.cnf 644 etc/ssl
vmove usr/bin
vmove usr/share/man/man1
vbin ${FILESDIR}/c_rehash
vbin ${wrksrc}/c_rehash
}
}