ca-certs resource path and format changed
[platform/upstream/ca-certificates.git] / packaging / update-ca-certificates
1 #!/usr/bin/perl -w
2 #
3 # update-ca-certificates
4 #
5 # Copyright (c) 2010 SUSE Linux Products GmbH
6 # Author: Ludwig Nussel
7 #
8 # Inspired by Debian's update-ca-certificates
9
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301,
23 # USA.
24 #
25
26 use strict;
27
28 use File::Basename;
29 use File::Find;
30 use Getopt::Long;
31
32 my $certsconf = '/etc/ca-certificates.conf';
33 my $hooksdir1 = '/etc/ca-certificates/update.d';
34 my $hooksdir2 = '/usr/lib/ca-certificates/update.d';
35 # only search /usr/share/ca-certificates/certs because of code-signing certs
36 my $certsdir = "/usr/share/ca-certificates/certs";
37 my $localcertsdir = "/usr/local/share/ca-certificates";
38 my $etccertsdir = "/etc/ssl/certs";
39
40 my (%blacklist, %whitelist, %added, %removed);
41
42 my ($opt_verbose, $opt_fresh, $opt_help);
43
44 sub startswith($$)
45 {
46         return $_[1] eq substr($_[0], 0, length($_[1]));
47 }
48
49 sub targetfilename($)
50 {
51         my $t = $etccertsdir.'/'.basename($_[0]);
52         $t =~ s/\.crt$/.pem/;
53         return $t;
54 }
55
56 sub addcert($)
57 {
58         my $f = $_[0];
59         my $t = targetfilename($f);
60
61         return if -e $t;
62         unlink $t if -l $t; # dangling symlink
63         if (symlink($f, $t)) {
64                 $added{$t} = 1;
65                 delete $removed{$f} if exists $removed{$f};
66         } else {
67                 print STDERR "symlink of $t failed: $!\n";
68         }
69 }
70
71 sub removecert($)
72 {
73         my $t = targetfilename($_[0]);
74         if (-l $t) {
75                 $removed{$t} = 1;
76                 unlink $t;
77         }
78 }
79
80 Getopt::Long::Configure("no_ignore_case");
81 GetOptions(
82     "verbose|v"   => \$opt_verbose,
83     "fresh|f"   => \$opt_fresh,
84     "help|h"   => \$opt_help,
85     ) or die "$!\n";
86
87 if ($opt_help)
88 {
89         print "USAGE: $0 [OPTIONS]\n";
90         print "OPTIIONS:\n";
91         print "  --verbose, -v     verbose output\n";
92         print "  --fresh, -f       start from scratch\n";
93         print "  --help, -h        this screen\n";
94         exit 0;
95 }
96
97 if (open(F, '<', $certsconf)) {
98         while (<F>) {
99                 next if /^#/;
100                 chomp;
101                 next unless length($_);
102                 if (/^!/) {
103                         s/^!//;
104                         $blacklist{$_} = 1;
105                 } else {
106                         $whitelist{$_} = 1;
107                 }
108         }
109         close F;
110 }
111
112 if ($opt_fresh || %whitelist) {
113         for my $f (glob "$etccertsdir/*" ) {
114                 next unless -l $f;
115                 my $l = readlink $f;
116                 next unless defined $l;
117                 if (startswith($l, $etccertsdir)
118                 || startswith($l, $localcertsdir))
119                 {
120                         if ($opt_fresh || %whitelist &&
121                                 !exists($whitelist{basename($l)}))
122                         {
123                                 unlink $f;
124                                 $removed{$f} = 1;
125                         }
126                 }
127         }
128 }
129
130 my @files;
131 File::Find::find({
132         no_chdir => 1,
133         wanted => sub {
134                 -f && /\.(?:pem|crt|[0-9])$/ && push @files, $_;
135                 }
136         }, $certsdir);
137 for my $f (@files) {
138         my $n = substr($f, length($certsdir)+1);
139         if (exists($blacklist{$n})) {
140                 removecert($f);
141                 next;
142         }
143         next if %whitelist && !exists($whitelist{$n});
144         addcert($f);
145 }
146
147 for my $f (glob "$localcertsdir/*.{pem,crt}") {
148         addcert($f);
149 }
150
151 for my $f (glob "$etccertsdir/*.{pem,[0-9]}") {
152         if (-l $f && !-e $f) {
153                 if (startswith($f, $etccertsdir)
154                 || startswith($f, $localcertsdir))
155                 {
156                         $removed{$f} = 1;
157                 }
158                 # clean dangling symlinks
159                 unlink $f
160         }
161 }
162
163 chdir $etccertsdir || die "$!";
164 if (%added || %removed || $opt_fresh) {
165         print "Updating certificates in $etccertsdir...\n";
166 # tizen ca-certs suffix isn't .pem|.crt|.cer|.crl
167 # so c_rehash cannot be used.
168 #       my $redir = ($opt_verbose?'':'> /dev/null');
169 #       system("c_rehash . $redir");
170
171         printf("%d added, %d removed.\n",
172                 (%added?(scalar keys %added):0),
173                 (%removed?(scalar keys %removed):0));
174 }
175
176 my @args;
177 push @args, '-f' if $opt_fresh;
178 push @args, '-v' if $opt_verbose;
179 for my $f (glob("$hooksdir2/*.run"), glob("$hooksdir1/*.run")) {
180         system($f, @args);
181 }