]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-pki.in
ovs-lib: Handle daemon segfaults during exit.
[mirror_ovs.git] / utilities / ovs-pki.in
1 #! /bin/sh
2
3 # Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at:
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 set -e
18
19 pkidir='@PKIDIR@'
20 command=
21 prev=
22 force=no
23 batch=no
24 unique_name=no
25 log='@LOGDIR@/ovs-pki.log'
26 keytype=rsa
27 bits=2048
28
29 # OS-specific compatibility routines
30 case $(uname -s) in
31 FreeBSD|NetBSD|Darwin)
32 file_mod_epoch()
33 {
34 stat -r "$1" | awk '{print $10}'
35 }
36
37 file_mod_date()
38 {
39 stat -f '%Sm' "$1"
40 }
41
42 sha1sum()
43 {
44 sha1 "$@"
45 }
46 ;;
47 *)
48 file_mod_epoch()
49 {
50 date -r "$1" +%s
51 }
52
53 file_mod_date()
54 {
55 date -r "$1"
56 }
57 ;;
58 esac
59
60 for option; do
61 # This option-parsing mechanism borrowed from a Autoconf-generated
62 # configure script under the following license:
63
64 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
65 # 2002, 2003, 2004, 2005, 2006, 2009 Free Software Foundation, Inc.
66 # This configure script is free software; the Free Software Foundation
67 # gives unlimited permission to copy, distribute and modify it.
68
69 # If the previous option needs an argument, assign it.
70 if test -n "$prev"; then
71 eval $prev=\$option
72 prev=
73 continue
74 fi
75 case $option in
76 *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;;
77 *) optarg=yes ;;
78 esac
79
80 case $dashdash$option in
81 --)
82 dashdash=yes ;;
83 -h|--help)
84 cat <<EOF
85 ovs-pki, for managing a simple OpenFlow public key infrastructure
86 usage: $0 [OPTION...] COMMAND [ARG...]
87
88 The valid stand-alone commands and their arguments are:
89 init Initialize the PKI
90 req NAME Create new private key and certificate request
91 named NAME-privkey.pem and NAME-req.pem, resp.
92 sign NAME [TYPE] Sign switch certificate request NAME-req.pem,
93 producing certificate NAME-cert.pem
94 req+sign NAME [TYPE] Combine the above two steps, producing all three files.
95 verify NAME [TYPE] Checks that NAME-cert.pem is a valid TYPE certificate
96 fingerprint FILE Prints the fingerprint for FILE
97 self-sign NAME Sign NAME-req.pem with NAME-privkey.pem,
98 producing self-signed certificate NAME-cert.pem
99 Each TYPE above is a certificate type: 'switch' (default) or 'controller'.
100
101 Options for 'init', 'req', and 'req+sign' only:
102 -k, --key=rsa|dsa Type of keys to use (default: rsa)
103 -B, --bits=NBITS Number of bits in keys (default: 2048). For DSA keys,
104 this has an effect only on 'init'.
105 -D, --dsaparam=FILE File with DSA parameters (DSA only)
106 (default: dsaparam.pem within PKI directory)
107 Options for use with the 'sign' command:
108 -b, --batch Skip fingerprint verification
109 Options that apply to any command:
110 -d, --dir=DIR Directory where the PKI is located
111 (default: $pkidir)
112 -f, --force Continue even if file or directory already exists
113 -l, --log=FILE Log openssl output to FILE (default: ovs-log.log)
114 -u, --unique NAME is unique (don't append UUID/date)
115 -h, --help Print this usage message.
116 -V, --version Display version information.
117 EOF
118 exit 0
119 ;;
120 -V|--version)
121 echo "ovs-pki (Open vSwitch) @VERSION@"
122 exit 0
123 ;;
124 --di*=*)
125 pkidir=$optarg
126 ;;
127 --di*|-d)
128 prev=pkidir
129 ;;
130 --k*=*)
131 keytype=$optarg
132 ;;
133 --k*|-k)
134 prev=keytype
135 ;;
136 --bi*=*)
137 bits=$optarg
138 ;;
139 --bi*|-B)
140 prev=bits
141 ;;
142 --ds*=*)
143 dsaparam=$optarg
144 ;;
145 --ds*|-D)
146 prev=dsaparam
147 ;;
148 --l*=*)
149 log=$optarg
150 ;;
151 --l*|-l)
152 prev=log
153 ;;
154 --force|-f)
155 force=yes
156 ;;
157 --ba*|-b)
158 batch=yes
159 ;;
160 --un*|-u)
161 unique_name=yes
162 ;;
163 -*)
164 echo "unrecognized option $option" >&2
165 exit 1
166 ;;
167 *)
168 if test -z "$command"; then
169 command=$option
170 elif test -z "${arg1+set}"; then
171 arg1=$option
172 elif test -z "${arg2+set}"; then
173 arg2=$option
174 else
175 echo "$option: only two arguments may be specified" >&2
176 exit 1
177 fi
178 ;;
179 esac
180 shift
181 done
182 if test -n "$prev"; then
183 option=--`echo $prev | sed 's/_/-/g'`
184 { echo "$as_me: error: missing argument to $option" >&2
185 { (exit 1); exit 1; }; }
186 fi
187 if test -z "$command"; then
188 echo "$0: missing command name; use --help for help" >&2
189 exit 1
190 fi
191 if test "$keytype" != rsa && test "$keytype" != dsa; then
192 echo "$0: argument to -k or --key must be rsa or dsa" >&2
193 exit 1
194 fi
195 if test "$bits" -lt 1024; then
196 echo "$0: argument to -B or --bits must be at least 1024" >&2
197 exit 1
198 fi
199 if test -z "$dsaparam"; then
200 dsaparam=$pkidir/dsaparam.pem
201 fi
202 case $log in
203 /* | ?:[\\/]*) ;;
204 *) log=`pwd`/$log ;;
205 esac
206
207 logdir=$(dirname "$log")
208 if test ! -d "$logdir"; then
209 mkdir -p -m750 "$logdir" 2>/dev/null || true
210 if test ! -d "$logdir"; then
211 echo "$0: log directory $logdir does not exist and cannot be created" >&2
212 exit 1
213 fi
214 fi
215
216 if test "$command" = "init"; then
217 if test -e "$pkidir" && test "$force" != "yes"; then
218 echo "$0: $pkidir already exists and --force not specified" >&2
219 exit 1
220 fi
221
222 if test ! -d "$pkidir"; then
223 mkdir -p "$pkidir"
224 fi
225 cd "$pkidir"
226 exec 3>>$log
227
228 if test $keytype = dsa && test ! -e dsaparam.pem; then
229 echo "Generating DSA parameters, please wait..." >&2
230 openssl dsaparam -out dsaparam.pem $bits 1>&3 2>&3
231 fi
232
233 # Get the current date to add some uniqueness to this certificate
234 curr_date=`date +"%Y %b %d %T"`
235
236 # Create the CAs.
237 for ca in controllerca switchca; do
238 echo "Creating $ca..." >&2
239 oldpwd=`pwd`
240 mkdir -p $ca
241 cd $ca
242
243 mkdir -p certs crl newcerts
244 mkdir -p -m 0700 private
245 touch index.txt
246 test -e crlnumber || echo 01 > crlnumber
247 test -e serial || echo 01 > serial
248
249 # Put DSA parameters in directory.
250 if test $keytype = dsa && test ! -e dsaparam.pem; then
251 cp ../dsaparam.pem .
252 fi
253
254 # Write CA configuration file.
255 if test ! -e ca.cnf; then
256 sed "s/@ca@/$ca/g;s/@curr_date@/$curr_date/g" > ca.cnf <<'EOF'
257 [ req ]
258 prompt = no
259 distinguished_name = req_distinguished_name
260
261 [ req_distinguished_name ]
262 C = US
263 ST = CA
264 L = Palo Alto
265 O = Open vSwitch
266 OU = @ca@
267 CN = OVS @ca@ CA Certificate (@curr_date@)
268
269 [ ca ]
270 default_ca = the_ca
271
272 [ the_ca ]
273 dir = . # top dir
274 database = $dir/index.txt # index file.
275 new_certs_dir = $dir/newcerts # new certs dir
276 certificate = $dir/cacert.pem # The CA cert
277 serial = $dir/serial # serial no file
278 private_key = $dir/private/cakey.pem# CA private key
279 RANDFILE = $dir/private/.rand # random number file
280 default_days = 3650 # how long to certify for
281 default_crl_days= 30 # how long before next CRL
282 default_md = sha512 # message digest to use
283 policy = policy # default policy
284 email_in_dn = no # Don't add the email into cert DN
285 name_opt = ca_default # Subject name display option
286 cert_opt = ca_default # Certificate display option
287 copy_extensions = copy # Copy extensions from request
288 unique_subject = no # Allow certs with duplicate subjects
289
290 # For the CA policy
291 [ policy ]
292 countryName = optional
293 stateOrProvinceName = optional
294 organizationName = match
295 organizationalUnitName = optional
296 commonName = supplied
297 emailAddress = optional
298
299 # For the x509v3 extension
300 [ ca_cert ]
301 basicConstraints=CA:true
302
303 [ usr_cert ]
304 basicConstraints=CA:false
305 EOF
306 fi
307
308 # Create certificate authority.
309 if test $keytype = dsa; then
310 newkey=dsa:dsaparam.pem
311 else
312 newkey=rsa:$bits
313 fi
314 openssl req -config ca.cnf -nodes \
315 -newkey $newkey -keyout private/cakey.pem -out careq.pem \
316 1>&3 2>&3
317 openssl ca -config ca.cnf -create_serial \
318 -extensions ca_cert -out cacert.pem \
319 -days 3650 -batch -keyfile private/cakey.pem -selfsign \
320 -infiles careq.pem 1>&3 2>&3
321 chmod 0700 private/cakey.pem
322
323 cd "$oldpwd"
324 done
325 exit 0
326 fi
327
328 one_arg() {
329 if test -z "$arg1" || test -n "$arg2"; then
330 echo "$0: $command must have exactly one argument; use --help for help" >&2
331 exit 1
332 fi
333 }
334
335 one_or_two_args() {
336 if test -z "$arg1"; then
337 echo "$0: $command must have one or two arguments; use --help for help" >&2
338 exit 1
339 fi
340 }
341
342 must_not_exist() {
343 if test -e "$1" && test "$force" != "yes"; then
344 echo "$0: $1 already exists and --force not supplied" >&2
345 exit 1
346 fi
347 }
348
349 make_tmpdir() {
350 TMP=/tmp/ovs-pki.tmp$$
351 rm -rf $TMP
352 trap "rm -rf $TMP" 0
353 mkdir -m 0700 $TMP
354 }
355
356 fingerprint() {
357 file=$1
358 name=${1-$2}
359 date=$(file_mod_date "$file")
360 if grep -e '-BEGIN CERTIFICATE-' "$file" > /dev/null; then
361 fingerprint=$(openssl x509 -noout -in "$file" -fingerprint |
362 sed 's/SHA1 Fingerprint=//' | tr -d ':')
363 else
364 fingerprint=$(sha1sum "$file" | awk '{print $1}')
365 fi
366 printf "$name\\t$date\\n"
367 case $file in
368 $fingerprint*)
369 printf "\\t(correct fingerprint in filename)\\n"
370 ;;
371 *)
372 printf "\\tfingerprint $fingerprint\\n"
373 ;;
374 esac
375 }
376
377 verify_fingerprint() {
378 fingerprint "$@"
379 if test $batch != yes; then
380 echo "Does fingerprint match? (yes/no)"
381 read answer
382 if test "$answer" != yes; then
383 echo "Match failure, aborting" >&2
384 exit 1
385 fi
386 fi
387 }
388
389 check_type() {
390 if test x = x"$1"; then
391 type=switch
392 elif test "$1" = switch || test "$1" = controller; then
393 type=$1
394 else
395 echo "$0: type argument must be 'switch' or 'controller'" >&2
396 exit 1
397 fi
398 }
399
400 parse_age() {
401 number=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\1/')
402 unit=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\2/')
403 case $unit in
404 s)
405 factor=1
406 ;;
407 min)
408 factor=60
409 ;;
410 h)
411 factor=3600
412 ;;
413 day)
414 factor=86400
415 ;;
416 *)
417 echo "$1: age not in the form Ns, Nmin, Nh, Nday (e.g. 1day)" >&2
418 exit 1
419 ;;
420 esac
421 echo $(($number * $factor))
422 }
423
424 must_exist() {
425 if test ! -e "$1"; then
426 echo "$0: $1 does not exist" >&2
427 exit 1
428 fi
429 }
430
431 pkidir_must_exist() {
432 if test ! -e "$pkidir"; then
433 echo "$0: $pkidir does not exist (need to run 'init' or use '--dir'?)" >&2
434 exit 1
435 elif test ! -d "$pkidir"; then
436 echo "$0: $pkidir is not a directory" >&2
437 exit 1
438 fi
439 }
440
441 make_request() {
442 must_not_exist "$arg1-privkey.pem"
443 must_not_exist "$arg1-req.pem"
444 make_tmpdir
445 if test $unique_name != yes; then
446 # Use uuidgen or date to create unique subject DNs.
447 unique=`(uuidgen) 2>/dev/null` || unique=`date +"%Y %b %d %T"`
448 cn="$arg1 id:$unique"
449 else
450 cn="$arg1"
451 fi
452 cat > "$TMP/req.cnf" <<EOF
453 [ req ]
454 prompt = no
455 distinguished_name = req_distinguished_name
456 req_extensions = v3_req
457
458 [ req_distinguished_name ]
459 C = US
460 ST = CA
461 L = Palo Alto
462 O = Open vSwitch
463 OU = Open vSwitch certifier
464 CN = $cn
465
466 [ v3_req ]
467 subjectAltName = DNS:$cn
468 EOF
469 if test $keytype = rsa; then
470 (umask 077 && openssl genrsa -out "$1-privkey.pem" $bits) 1>&3 2>&3 \
471 || exit $?
472 else
473 must_exist "$dsaparam"
474 (umask 077 && openssl gendsa -out "$1-privkey.pem" "$dsaparam") \
475 1>&3 2>&3 || exit $?
476 fi
477 openssl req -config "$TMP/req.cnf" -new -text \
478 -key "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3
479 }
480
481 sign_request() {
482 must_exist "$1"
483 must_not_exist "$2"
484 pkidir_must_exist
485
486 case "$1" in
487 /* | ?:[\\/]*)
488 request_file="$1"
489 ;;
490 *)
491 request_file="`pwd`/$1"
492 ;;
493 esac
494
495 (cd "$pkidir/${type}ca" &&
496 openssl ca -config ca.cnf -extensions usr_cert -batch -in "$request_file") \
497 > "$2.tmp$$" 2>&3
498 mv "$2.tmp$$" "$2"
499 }
500
501 glob() {
502 files=$(echo $1)
503 if test "$files" != "$1"; then
504 echo "$files"
505 fi
506 }
507
508 exec 3>>$log || true
509 if test "$command" = req; then
510 one_arg
511
512 make_request "$arg1"
513 fingerprint "$arg1-req.pem"
514 elif test "$command" = sign; then
515 one_or_two_args
516 check_type "$arg2"
517 verify_fingerprint "$arg1-req.pem"
518
519 sign_request "$arg1-req.pem" "$arg1-cert.pem"
520 elif test "$command" = req+sign; then
521 one_or_two_args
522 check_type "$arg2"
523
524 pkidir_must_exist
525 make_request "$arg1"
526 sign_request "$arg1-req.pem" "$arg1-cert.pem"
527 fingerprint "$arg1-req.pem"
528 elif test "$command" = verify; then
529 one_or_two_args
530 must_exist "$arg1-cert.pem"
531 check_type "$arg2"
532
533 pkidir_must_exist
534 openssl verify -CAfile "$pkidir/${type}ca/cacert.pem" "$arg1-cert.pem"
535 elif test "$command" = fingerprint; then
536 one_arg
537
538 fingerprint "$arg1"
539 elif test "$command" = self-sign; then
540 one_arg
541 must_exist "$arg1-req.pem"
542 must_exist "$arg1-privkey.pem"
543 must_not_exist "$arg1-cert.pem"
544 make_tmpdir
545 cat > "$TMP/v3.ext" <<EOF
546 subjectAltName = DNS:$arg1
547 EOF
548
549 # Create both the private key and certificate with restricted permissions.
550 (umask 077 && \
551 openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem.tmp" \
552 -signkey "$arg1-privkey.pem" -req -days 3650 -text \
553 -extfile $TMP/v3.ext) 2>&3 || exit $?
554
555 # Reset the permissions on the certificate to the user's default.
556 cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem"
557 rm -f "$arg1-cert.pem.tmp"
558 else
559 echo "$0: $command command unknown; use --help for help" >&2
560 exit 1
561 fi