source: box/trunk/bin/bbstored/bbstored-certs.in @ 2819

Revision 2819, 6.1 KB checked in by chris, 17 months ago (diff)

Fix date overflow in new CA certificates, make them valid until 2038,
thanks to JP Vossen for reporting this.

  • Property svn:eol-style set to native
Line 
1#!@PERL@
2use strict;
3
4# validity period for root certificates -- default is 2038, the best we can do for now
5my $root_sign_period = int(((1<<31) - time()) / 86400);
6
7# but less so for client certificates
8my $sign_period = '5000';
9
10# check and get command line parameters
11if($#ARGV < 1)
12{
13        print <<__E;
14
15bbstored certificates utility.
16
17Bad command line parameters.
18Usage:
19        bbstored-certs certs-dir command [arguments]
20
21certs-dir is the directory holding the root keys and certificates for the backup system
22command is the action to perform, taking parameters.
23
24Commands are
25
26        init
27                -- generate initial root certificates (certs-dir must not already exist)
28        sign certificate-name
29                -- sign a client certificate
30        sign-server certificate-name
31                -- sign a server certificate
32
33Signing requires confirmation that the certificate is correct and should be signed.
34
35__E
36        exit(1);
37}
38
39# check for OPENSSL_CONF environment var being set
40if(exists $ENV{'OPENSSL_CONF'})
41{
42        print <<__E;
43
44---------------------------------------
45
46WARNING:
47    You have the OPENSSL_CONF environment variable set.
48    Use of non-standard openssl configs may cause problems.
49
50---------------------------------------
51
52__E
53}
54
55# directory structure:
56#
57# roots/
58#       clientCA.pem -- root certificate for client (used on server)
59#       serverCA.pem -- root certificate for servers (used on clients)
60# keys/
61#   clientRootKey.pem -- root key for clients
62#   serverRootKey.pem -- root key for servers
63# servers/
64#   hostname.pem -- certificate for server 'hostname'
65# clients/
66#   account.pem -- certficiate for account 'account' (ID in hex)
67#
68
69
70# check parameters
71my ($cert_dir,$command,@args) = @ARGV;
72
73# check directory exists
74if($command ne 'init')
75{
76        if(!-d $cert_dir)
77        {
78                die "$cert_dir does not exist";
79        }
80}
81
82# run command
83if($command eq 'init') {&cmd_init;}
84elsif($command eq 'sign') {&cmd_sign;}
85elsif($command eq 'sign-server') {&cmd_sign_server;}
86else
87{
88        die "Unknown command $command"
89}
90
91sub cmd_init
92{
93        # create directories
94        unless(mkdir($cert_dir,0700)
95                && mkdir($cert_dir.'/roots',0700)
96                && mkdir($cert_dir.'/keys',0700)
97                && mkdir($cert_dir.'/servers',0700)
98                && mkdir($cert_dir.'/clients',0700))
99        {
100                die "Failed to create directory structure"
101        }
102
103        # create root keys and certrs
104        cmd_init_create_root('client');
105        cmd_init_create_root('server');
106}
107
108sub cmd_init_create_root
109{
110        my $entity = $_[0];
111
112        my $cert = "$cert_dir/roots/".$entity.'CA.pem';
113        my $serial = "$cert_dir/roots/".$entity.'CA.srl';
114        my $key = "$cert_dir/keys/".$entity.'RootKey.pem';
115        my $csr = "$cert_dir/keys/".$entity.'RootCSR.pem';
116
117        # generate key
118        if(system("openssl genrsa -out $key 2048") != 0)
119        {
120                die "Couldn't generate private key."
121        }
122       
123        # make CSR
124        die "Couldn't run openssl for CSR generation" unless
125                open(CSR,"|openssl req -new -key $key -sha1 -out $csr");
126        print CSR <<__E;
127.
128.
129.
130.
131.
132Backup system $entity root
133.
134.
135.
136
137__E
138        close CSR;
139        print "\n\n";
140        die "Certificate request wasn't created.\n" unless -f $csr;
141       
142        # sign it to make a self-signed root CA key
143        if(system("openssl x509 -req -in $csr -sha1 -extensions v3_ca -signkey $key -out $cert -days $root_sign_period") != 0)
144        {
145                die "Couldn't generate root certificate."
146        }
147       
148        # write the initial serial number
149        open SERIAL,">$serial" or die "Can't open $serial for writing";
150        print SERIAL "00\n";
151        close SERIAL;
152}
153
154sub cmd_sign
155{
156        my $csr = $args[0];
157       
158        if(!-f $csr)
159        {
160                die "$csr does not exist";
161        }
162       
163        # get the common name specified in this certificate
164        my $common_name = get_csr_common_name($csr);
165       
166        # look OK?
167        unless($common_name =~ m/\ABACKUP-([A-Fa-f0-9]+)\Z/)
168        {
169                die "The certificate presented does not appear to be a backup client certificate"
170        }
171       
172        my $acc = $1;
173       
174        # check against filename
175        if(!($csr =~ m/(\A|\/)([A-Fa-f0-9]+)-/) || $2 ne $acc)
176        {
177                die "Certificate request filename does not match name in certificate ($common_name)"
178        }
179               
180        print <<__E;
181
182This certificate is for backup account
183
184   $acc
185
186Ensure this matches the account number you are expecting. The filename is
187
188   $csr
189
190which should include this account number, and additionally, you should check
191that you received it from the right person.
192
193Signing the wrong certificate compromises the security of your backup system.
194
195Would you like to sign this certificate? (type 'yes' to confirm)
196__E
197
198        return unless get_confirmation();
199
200        # out certificate
201        my $out_cert = "$cert_dir/clients/$acc"."-cert.pem";
202
203        # sign it!
204        if(system("openssl x509 -req -in $csr -sha1 -extensions usr_crt -CA $cert_dir/roots/clientCA.pem -CAkey $cert_dir/keys/clientRootKey.pem -out $out_cert -days $sign_period") != 0)
205        {
206                die "Signing failed"
207        }
208       
209        # tell user what to do next
210        print <<__E;
211
212
213Certificate signed.
214
215Send the files
216
217   $out_cert
218   $cert_dir/roots/serverCA.pem
219
220to the client.
221
222__E
223}
224
225sub cmd_sign_server
226{
227        my $csr = $args[0];
228       
229        if(!-f $csr)
230        {
231                die "$csr does not exist";
232        }
233       
234        # get the common name specified in this certificate
235        my $common_name = get_csr_common_name($csr);
236       
237        # look OK?
238        if($common_name !~ m/\A[-a-zA-Z0-9.]+\Z/)
239        {
240                die "Invalid server name"
241        }
242       
243        print <<__E;
244
245This certificate is for backup server
246
247   $common_name
248
249Signing the wrong certificate compromises the security of your backup system.
250
251Would you like to sign this certificate? (type 'yes' to confirm)
252__E
253
254        return unless get_confirmation();
255
256        # out certificate
257        my $out_cert = "$cert_dir/servers/$common_name"."-cert.pem";
258
259        # sign it!
260        if(system("openssl x509 -req -in $csr -sha1 -extensions usr_crt -CA $cert_dir/roots/serverCA.pem -CAkey $cert_dir/keys/serverRootKey.pem -out $out_cert -days $sign_period") != 0)
261        {
262                die "Signing failed"
263        }
264       
265        # tell user what to do next
266        print <<__E;
267
268
269Certificate signed.
270
271Install the files
272
273   $out_cert
274   $cert_dir/roots/clientCA.pem
275
276on the server.
277
278__E
279}
280
281
282sub get_csr_common_name
283{
284        my $csr = $_[0];
285       
286        open CSRTEXT,"openssl req -text -in $csr |" or die "Can't open openssl for reading";
287       
288        my $subject;
289        while(<CSRTEXT>)
290        {
291                $subject = $1 if m/Subject:.+?CN=([-\.\w]+)/
292        }       
293        close CSRTEXT;
294
295        if($subject eq '')
296        {
297                die "No subject found in CSR $csr"
298        }
299       
300        return $subject
301}
302
303sub get_confirmation()
304{
305        my $line = <STDIN>;
306        chomp $line;
307        if(lc $line ne 'yes')
308        {
309                print "CANCELLED\n";
310                return 0;
311        }
312       
313        return 1;
314}
315
316
317
318
319
Note: See TracBrowser for help on using the repository browser.