1#!/usr/bin/perl -w 2# *************************************************************************** 3# * _ _ ____ _ 4# * Project ___| | | | _ \| | 5# * / __| | | | |_) | | 6# * | (__| |_| | _ <| |___ 7# * \___|\___/|_| \_\_____| 8# * 9# * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. 10# * 11# * This software is licensed as described in the file COPYING, which 12# * you should have received as part of this distribution. The terms 13# * are also available at http://curl.haxx.se/docs/copyright.html. 14# * 15# * You may opt to use, copy, modify, merge, publish, distribute and/or sell 16# * copies of the Software, and permit persons to whom the Software is 17# * furnished to do so, under the terms of the COPYING file. 18# * 19# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20# * KIND, either express or implied. 21# * 22# *************************************************************************** 23# This Perl script creates a fresh ca-bundle.crt file for use with libcurl. 24# It downloads certdata.txt from Mozilla's source tree (see URL below), 25# then parses certdata.txt and extracts CA Root Certificates into PEM format. 26# These are then processed with the OpenSSL commandline tool to produce the 27# final ca-bundle.crt file. 28# The script is based on the parse-certs script written by Roland Krikava. 29# This Perl script works on almost any platform since its only external 30# dependency is the OpenSSL commandline tool for optional text listing. 31# Hacked by Guenter Knauf. 32# 33use Getopt::Std; 34use MIME::Base64; 35use LWP::UserAgent; 36use strict; 37use vars qw($opt_b $opt_h $opt_i $opt_l $opt_n $opt_q $opt_t $opt_u $opt_v); 38 39my $url = 'http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1'; 40# If the OpenSSL commandline is not in search path you can configure it here! 41my $openssl = 'openssl'; 42 43my $version = '1.16'; 44 45getopts('bhilnqtuv'); 46 47if ($opt_i) { 48 print ("=" x 78 . "\n"); 49 print "Script Version : $version\n"; 50 print "Perl Version : $]\n"; 51 print "Operating System Name : $^O\n"; 52 print "Getopt::Std.pm Version : ${Getopt::Std::VERSION}\n"; 53 print "MIME::Base64.pm Version : ${MIME::Base64::VERSION}\n"; 54 print "LWP::UserAgent.pm Version : ${LWP::UserAgent::VERSION}\n"; 55 print "LWP.pm Version : ${LWP::VERSION}\n"; 56 print ("=" x 78 . "\n"); 57} 58 59$0 =~ s@.*(/|\\)@@; 60if ($opt_h) { 61 printf("Usage:\t%s [-b] [-i] [-l] [-n] [-q] [-t] [-u] [-v] [<outputfile>]\n", $0); 62 print "\t-b\tbackup an existing version of ca-bundle.crt\n"; 63 print "\t-i\tprint version info about used modules\n"; 64 print "\t-l\tprint license info about certdata.txt\n"; 65 print "\t-n\tno download of certdata.txt (to use existing)\n"; 66 print "\t-q\tbe really quiet (no progress output at all)\n"; 67 print "\t-t\tinclude plain text listing of certificates\n"; 68 print "\t-u\tunlink (remove) certdata.txt after processing\n"; 69 print "\t-v\tbe verbose and print out processed CAs\n"; 70 exit; 71} 72 73my $crt = $ARGV[0] || 'ca-bundle.crt'; 74(my $txt = $url) =~ s@(.*/|\?.*)@@g; 75 76my $resp; 77 78unless ($opt_n and -e $txt) { 79 print "Downloading '$txt' ...\n" if (!$opt_q); 80 my $ua = new LWP::UserAgent(agent => "$0/$version"); 81 $ua->env_proxy(); 82 $resp = $ua->mirror($url, $txt); 83 if ($resp && $resp->code eq '304') { 84 print "Not modified\n" unless $opt_q; 85 exit 0; 86 } 87} 88 89my $currentdate = scalar gmtime($resp ? $resp->last_modified : (stat($txt))[9]); 90 91if ($opt_b && -e $crt) { 92 my $bk = 1; 93 while (-e "$crt.~${bk}~") { 94 $bk++; 95 } 96 rename $crt, "$crt.~${bk}~"; 97} 98 99my $format = $opt_t ? "plain text and " : ""; 100open(CRT,">$crt") or die "Couldn't open $crt: $!"; 101print CRT <<EOT; 102## 103## $crt -- Bundle of CA Root Certificates 104## 105## Certificate data from Mozilla as of: ${currentdate} 106## 107## This is a bundle of X.509 certificates of public Certificate Authorities 108## (CA). These were automatically extracted from Mozilla's root certificates 109## file (certdata.txt). This file can be found in the mozilla source tree: 110## $url 111## 112## It contains the certificates in ${format}PEM format and therefore 113## can be directly used with curl / libcurl / php_curl, or with 114## an Apache+mod_ssl webserver for SSL client authentication. 115## Just configure this file as the SSLCACertificateFile. 116## 117 118EOT 119 120close(CRT) or die "Couldn't close $crt: $!"; 121 122print "Processing '$txt' ...\n" if (!$opt_q); 123my $caname; 124my $certnum = 0; 125my $skipnum = 0; 126open(TXT,"$txt") or die "Couldn't open $txt: $!"; 127while (<TXT>) { 128 if (/\*\*\*\*\* BEGIN LICENSE BLOCK \*\*\*\*\*/) { 129 open(CRT, ">>$crt") or die "Couldn't open $crt: $!"; 130 print CRT; 131 print if ($opt_l); 132 while (<TXT>) { 133 print CRT; 134 print if ($opt_l); 135 last if (/\*\*\*\*\* END LICENSE BLOCK \*\*\*\*\*/); 136 } 137 close(CRT) or die "Couldn't close $crt: $!"; 138 } 139 next if /^#|^\s*$/; 140 chomp; 141 if (/^CVS_ID\s+\"(.*)\"/) { 142 open(CRT, ">>$crt") or die "Couldn't open $crt: $!"; 143 print CRT "# $1\n"; 144 close(CRT) or die "Couldn't close $crt: $!"; 145 } 146 if (/^CKA_LABEL\s+[A-Z0-9]+\s+\"(.*)\"/) { 147 $caname = $1; 148 } 149 my $untrusted = 0; 150 if (/^CKA_VALUE MULTILINE_OCTAL/) { 151 my $data; 152 while (<TXT>) { 153 last if (/^END/); 154 chomp; 155 my @octets = split(/\\/); 156 shift @octets; 157 for (@octets) { 158 $data .= chr(oct); 159 } 160 } 161 while (<TXT>) { 162 last if (/^#$/); 163 $untrusted = 1 if (/^CKA_TRUST_SERVER_AUTH\s+CK_TRUST\s+CKT_NSS_NOT_TRUSTED$/ 164 or /^CKA_TRUST_SERVER_AUTH\s+CK_TRUST\s+CKT_NSS_TRUST_UNKNOWN$/); 165 } 166 if ($untrusted) { 167 $skipnum ++; 168 } else { 169 my $pem = "-----BEGIN CERTIFICATE-----\n" 170 . MIME::Base64::encode($data) 171 . "-----END CERTIFICATE-----\n"; 172 open(CRT, ">>$crt") or die "Couldn't open $crt: $!"; 173 print CRT "\n$caname\n"; 174 print CRT ("=" x length($caname) . "\n"); 175 if (!$opt_t) { 176 print CRT $pem; 177 } 178 close(CRT) or die "Couldn't close $crt: $!"; 179 if ($opt_t) { 180 open(TMP, "|$openssl x509 -md5 -fingerprint -text -inform PEM >> $crt") or die "Couldn't open openssl pipe: $!"; 181 print TMP $pem; 182 close(TMP) or die "Couldn't close openssl pipe: $!"; 183 } 184 print "Parsing: $caname\n" if ($opt_v); 185 $certnum ++; 186 } 187 } 188} 189close(TXT) or die "Couldn't close $txt: $!"; 190unlink $txt if ($opt_u); 191print "Done ($certnum CA certs processed, $skipnum untrusted skipped).\n" if (!$opt_q); 192 193exit; 194 195 196