1
#!/usr/bin/perl -w
2
# 
3
# ***** BEGIN LICENSE BLOCK *****
4
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
#
6
# The contents of this file are subject to the Mozilla Public License Version
7
# 1.1 (the "License"); you may not use this file except in compliance with
8
# the License. You may obtain a copy of the License at
9
# http://www.mozilla.org/MPL/
10
#
11
# Software distributed under the License is distributed on an "AS IS" basis,
12
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
# for the specific language governing rights and limitations under the
14
# License.
15
#
16
# The Original Code is the Netscape security libraries.
17
#
18
# The Initial Developer of the Original Code is
19
# Netscape Communications Corporation.
20
# Portions created by the Initial Developer are Copyright (C) 1994-2000
21
# the Initial Developer. All Rights Reserved.
22
#
23
# Contributor(s):
24
#
25
# Alternatively, the contents of this file may be used under the terms of
26
# either the GNU General Public License Version 2 or later (the "GPL"), or
27
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
# in which case the provisions of the GPL or the LGPL are applicable instead
29
# of those above. If you wish to allow use of your version of this file only
30
# under the terms of either the GPL or the LGPL, and not to allow others to
31
# use your version of this file under the terms of the MPL, indicate your
32
# decision by deleting the provisions above and replace them with the notice
33
# and other provisions required by the GPL or the LGPL. If you do not delete
34
# the provisions above, a recipient may use your version of this file under
35
# the terms of any one of the MPL, the GPL or the LGPL.
36
#
37
# ***** END LICENSE BLOCK *****
38
use strict;
39
use Encode;
40
41
my $count = 0;
42
my @certificates = ();
43
my %trusts = ();
44
my $object = undef;
45
my $output_trustbits;
46
47
my %trust_types = (
48
  "CKA_TRUST_DIGITAL_SIGNATURE" => "digital-signature",
49
  "CKA_TRUST_NON_REPUDIATION" => "non-repudiation",
50
  "CKA_TRUST_KEY_ENCIPHERMENT" => "key-encipherment",
51
  "CKA_TRUST_DATA_ENCIPHERMENT" => "data-encipherment",
52
  "CKA_TRUST_KEY_AGREEMENT" => "key-agreement",
53
  "CKA_TRUST_KEY_CERT_SIGN" => "cert-sign",
54
  "CKA_TRUST_CRL_SIGN" => "crl-sign",
55
  "CKA_TRUST_SERVER_AUTH" => "server-auth",
56
  "CKA_TRUST_CLIENT_AUTH" => "client-auth",
57
  "CKA_TRUST_CODE_SIGNING" => "code-signing",
58
  "CKA_TRUST_EMAIL_PROTECTION" => "email-protection",
59
  "CKA_TRUST_IPSEC_END_SYSTEM" => "ipsec-end-system",
60
  "CKA_TRUST_IPSEC_TUNNEL" => "ipsec-tunnel",
61
  "CKA_TRUST_IPSEC_USER" => "ipsec-user",
62
  "CKA_TRUST_TIME_STAMPING" => "time-stamping",
63
  "CKA_TRUST_STEP_UP_APPROVED" => "step-up-approved",
64
);
65
66
my %openssl_trust = (
67
  CKA_TRUST_SERVER_AUTH => 'serverAuth',
68
  CKA_TRUST_CLIENT_AUTH => 'clientAuth',
69
  CKA_TRUST_EMAIL_PROTECTION => 'emailProtection',
70
  CKA_TRUST_CODE_SIGNING => 'codeSigning',
71
);
72
73
if (@ARGV && $ARGV[0] eq '--trustbits') {
74
	shift @ARGV;
75
	$output_trustbits = 1;
76
}
77
78
sub colonhex
79
{
80
  return join(':', unpack("(H2)*", $_[0]));
81
}
82
83
sub handle_object($)
84
{
85
  my $object = shift;
86
  return unless $object;
87
  if($object->{'CKA_CLASS'} eq 'CKO_CERTIFICATE' && $object->{'CKA_CERTIFICATE_TYPE'} eq 'CKC_X_509') {
88
    push @certificates, $object;
89
  } elsif ($object->{'CKA_CLASS'} eq 'CKO_NETSCAPE_TRUST') {
90
    my $label = $object->{'CKA_LABEL'};
91
    my $serial = colonhex($object->{'CKA_SERIAL_NUMBER'});
92
    die "$label exists ($serial)" if exists($trusts{$label.$serial});
93
    $trusts{$label.$serial} = $object;
94
  } elsif ($object->{'CKA_CLASS'} eq 'CKO_NETSCAPE_BUILTIN_ROOT_LIST') {
95
    # ignore
96
  } else {
97
    print STDERR "class ", $object->{'CKA_CLASS'} ," not handled\n";
98
  }
99
}
100
101
while(<>) {
102
  my @fields = ();
103
104
  s/^((?:[^"#]+|"[^"]*")*)(\s*#.*$)/$1/;
105
  next if (/^\s*$/);
106
107
  if( /(^CVS_ID\s+)(.*)/ ) {
108
    next;
109
  }
110
111
  # This was taken from the perl faq #4.
112
  my $text = $_;
113
  push(@fields, $+) while $text =~ m{
114
      "([^\"\\]*(?:\\.[^\"\\]*)*)"\s?  # groups the phrase inside the quotes
115
    | ([^\s]+)\s?
116
    | \s
117
  }gx;
118
  push(@fields, undef) if substr($text,-1,1) eq '\s';
119
120
  if( $fields[0] =~ /BEGINDATA/ ) {
121
    next;
122
  }
123
124
  if( $fields[1] =~ /MULTILINE/ ) {
125
    die "expected MULTILINE_OCTAL" unless $fields[1] eq 'MULTILINE_OCTAL';
126
    $fields[2] = "";
127
    while(<>) {
128
      last if /END/;
129
      chomp;
130
      $fields[2] .= pack("C", oct($+)) while $_ =~ /\G\\([0-3][0-7][0-7])/g;
131
    }
132
  }
133
134
  if( $fields[0] =~ /CKA_CLASS/ ) {
135
    $count++;
136
    handle_object($object);
137
    $object = {};
138
  }
139
140
  $object->{$fields[0]} = $fields[2];
141
}
142
handle_object($object);
143
undef $object;
144
145
use MIME::Base64;
146
for my $cert (@certificates) {
147
  my $alias = $cert->{'CKA_LABEL'};
148
  my $serial = colonhex($cert->{'CKA_SERIAL_NUMBER'});
149
  if(!exists($trusts{$alias.$serial})) {
150
    print STDERR "NO TRUST: $alias\n";
151
    next;
152
  }
153
  # check trust. We only include certificates that are trusted for identifying
154
  # web sites
155
  my $trust = $trusts{$alias.$serial};
156
  my @addtrust;
157
  my @addtrust_openssl;
158
  my $trusted;
159
  if ($output_trustbits) {
160
	  for my $type (keys %trust_types) {
161
		  if (exists $trust->{$type}
162
		  && $trust->{$type} eq 'CKT_NETSCAPE_TRUSTED_DELEGATOR') {
163
			  push @addtrust, $trust_types{$type};
164
			  if (exists $openssl_trust{$type}) {
165
				  push @addtrust_openssl, $openssl_trust{$type};
166
			  }
167
			  $trusted = 1;
168
		  }
169
	  }
170
  } else {
171
	  if($trust->{'CKA_TRUST_SERVER_AUTH'} eq 'CKT_NETSCAPE_TRUSTED_DELEGATOR') {
172
		  $trusted = 1;
173
	  }
174
  }
175
176
  if (!$trusted) {
177
	  my $t = $trust->{'CKA_TRUST_SERVER_AUTH'};
178
	  $t =~ s/CKT_NETSCAPE_//;
179
	  print STDERR "$t: $alias\n";
180
	  next;
181
  }
182
183
  if ($alias =~ /\\x[0-9a-fA-F]{2}/) {
184
	  $alias =~ s/\\x([0-9a-fA-F]{2})/chr(hex($1))/ge; # thanks mls!
185
	  $alias = Encode::decode("UTF-8", $alias);
186
  }
187
  my $file = $alias;
188
  $alias =~ s/'/-/g;
189
  $file =~ s/[^[:alnum:]\\]+/_/g;
190
  $file = Encode::encode("UTF-8", $file);
191
  if (-e $file.'.pem') {
192
    my $i = 1;
193
    while (-e $file.".$i.pem") {
194
      ++$i;
195
    }
196
    $file .= ".$i.pem";
197
  } else {
198
    $file .= '.pem';
199
  }
200
  if (!open(O, '>', $file)) {
201
	  print STDERR "$file: $!\n";
202
	  next;
203
  }
204
  print "$file\n" if $ENV{'VERBOSE'};
205
  my $value = $cert->{'CKA_VALUE'};
206
  if ($output_trustbits) {
207
	  print O "# alias=",Encode::encode("UTF-8", $alias),"\n";
208
	  print O "# trust=",join(" ", @addtrust),"\n";
209
	  if (@addtrust_openssl) {
210
		  print O "# openssl-trust=",join(" ", @addtrust_openssl),"\n";
211
	  }
212
  }
213
  print O "-----BEGIN CERTIFICATE-----\n";
214
  print O encode_base64($value);
215
  print O "-----END CERTIFICATE-----\n";
216
  close O;
217
}