]> git.proxmox.com Git - mirror_ovs.git/blame - utilities/ovs-pki.in
ovs-lib: Handle daemon segfaults during exit.
[mirror_ovs.git] / utilities / ovs-pki.in
CommitLineData
064af421
BP
1#! /bin/sh
2
9ff33ca7 3# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
a14bc59f
BP
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
064af421
BP
17set -e
18
19pkidir='@PKIDIR@'
20command=
21prev=
22force=no
23batch=no
674ebdd1 24unique_name=no
064af421
BP
25log='@LOGDIR@/ovs-pki.log'
26keytype=rsa
27bits=2048
0f5ed573
EM
28
29# OS-specific compatibility routines
30case $(uname -s) in
8be8c95e 31FreeBSD|NetBSD|Darwin)
0f5ed573
EM
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 ;;
58esac
59
064af421
BP
60for 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
85ovs-pki, for managing a simple OpenFlow public key infrastructure
86usage: $0 [OPTION...] COMMAND [ARG...]
87
88The 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
064af421
BP
99Each TYPE above is a certificate type: 'switch' (default) or 'controller'.
100
101Options 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)
2562714a 107Options for use with the 'sign' command:
064af421
BP
108 -b, --batch Skip fingerprint verification
109Options 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)
674ebdd1 114 -u, --unique NAME is unique (don't append UUID/date)
064af421 115 -h, --help Print this usage message.
d3e565ce 116 -V, --version Display version information.
064af421
BP
117EOF
118 exit 0
119 ;;
d3e565ce
BP
120 -V|--version)
121 echo "ovs-pki (Open vSwitch) @VERSION@"
122 exit 0
123 ;;
064af421
BP
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 ;;
674ebdd1
LR
160 --un*|-u)
161 unique_name=yes
162 ;;
064af421
BP
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
181done
182if 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; }; }
186fi
187if test -z "$command"; then
188 echo "$0: missing command name; use --help for help" >&2
189 exit 1
190fi
191if test "$keytype" != rsa && test "$keytype" != dsa; then
7cdc630f 192 echo "$0: argument to -k or --key must be rsa or dsa" >&2
064af421
BP
193 exit 1
194fi
195if test "$bits" -lt 1024; then
7cdc630f 196 echo "$0: argument to -B or --bits must be at least 1024" >&2
064af421
BP
197 exit 1
198fi
199if test -z "$dsaparam"; then
200 dsaparam=$pkidir/dsaparam.pem
201fi
202case $log in
d1cec3c6 203 /* | ?:[\\/]*) ;;
37d03458 204 *) log=`pwd`/$log ;;
064af421
BP
205esac
206
ccc9fc5a
BP
207logdir=$(dirname "$log")
208if test ! -d "$logdir"; then
03736a67 209 mkdir -p -m750 "$logdir" 2>/dev/null || true
ccc9fc5a
BP
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
214fi
215
064af421
BP
216if 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
a20d2466
JP
233 # Get the current date to add some uniqueness to this certificate
234 curr_date=`date +"%Y %b %d %T"`
235
064af421
BP
236 # Create the CAs.
237 for ca in controllerca switchca; do
238 echo "Creating $ca..." >&2
37d03458 239 oldpwd=`pwd`
064af421
BP
240 mkdir -p $ca
241 cd $ca
242
243 mkdir -p certs crl newcerts
244 mkdir -p -m 0700 private
064af421
BP
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
a20d2466 254 # Write CA configuration file.
064af421 255 if test ! -e ca.cnf; then
a20d2466 256 sed "s/@ca@/$ca/g;s/@curr_date@/$curr_date/g" > ca.cnf <<'EOF'
064af421
BP
257[ req ]
258prompt = no
259distinguished_name = req_distinguished_name
260
261[ req_distinguished_name ]
262C = US
263ST = CA
264L = Palo Alto
265O = Open vSwitch
266OU = @ca@
a20d2466 267CN = OVS @ca@ CA Certificate (@curr_date@)
064af421
BP
268
269[ ca ]
270default_ca = the_ca
271
272[ the_ca ]
273dir = . # top dir
274database = $dir/index.txt # index file.
275new_certs_dir = $dir/newcerts # new certs dir
276certificate = $dir/cacert.pem # The CA cert
277serial = $dir/serial # serial no file
278private_key = $dir/private/cakey.pem# CA private key
279RANDFILE = $dir/private/.rand # random number file
d652859b 280default_days = 3650 # how long to certify for
064af421 281default_crl_days= 30 # how long before next CRL
29dd784d 282default_md = sha512 # message digest to use
064af421
BP
283policy = policy # default policy
284email_in_dn = no # Don't add the email into cert DN
285name_opt = ca_default # Subject name display option
286cert_opt = ca_default # Certificate display option
c8efec6e 287copy_extensions = copy # Copy extensions from request
c6c9e1e3 288unique_subject = no # Allow certs with duplicate subjects
064af421
BP
289
290# For the CA policy
291[ policy ]
292countryName = optional
293stateOrProvinceName = optional
294organizationName = match
295organizationalUnitName = optional
296commonName = supplied
297emailAddress = optional
c8efec6e
QX
298
299# For the x509v3 extension
300[ ca_cert ]
301basicConstraints=CA:true
302
303[ usr_cert ]
304basicConstraints=CA:false
064af421
BP
305EOF
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
c8efec6e
QX
317 openssl ca -config ca.cnf -create_serial \
318 -extensions ca_cert -out cacert.pem \
d652859b 319 -days 3650 -batch -keyfile private/cakey.pem -selfsign \
064af421
BP
320 -infiles careq.pem 1>&3 2>&3
321 chmod 0700 private/cakey.pem
322
323 cd "$oldpwd"
324 done
325 exit 0
326fi
327
328one_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
064af421
BP
335one_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
342must_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
064af421
BP
349make_tmpdir() {
350 TMP=/tmp/ovs-pki.tmp$$
351 rm -rf $TMP
352 trap "rm -rf $TMP" 0
353 mkdir -m 0700 $TMP
354}
355
356fingerprint() {
bb60fa74
BP
357 file=$1
358 name=${1-$2}
0f5ed573 359 date=$(file_mod_date "$file")
00961f7c 360 if grep -e '-BEGIN CERTIFICATE-' "$file" > /dev/null; then
064af421
BP
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
377verify_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
389check_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
400parse_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
424must_exist() {
425 if test ! -e "$1"; then
426 echo "$0: $1 does not exist" >&2
427 exit 1
428 fi
429}
430
431pkidir_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
441make_request() {
442 must_not_exist "$arg1-privkey.pem"
443 must_not_exist "$arg1-req.pem"
444 make_tmpdir
674ebdd1
LR
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
064af421
BP
452 cat > "$TMP/req.cnf" <<EOF
453[ req ]
454prompt = no
455distinguished_name = req_distinguished_name
c8efec6e 456req_extensions = v3_req
064af421
BP
457
458[ req_distinguished_name ]
459C = US
460ST = CA
461L = Palo Alto
462O = Open vSwitch
463OU = Open vSwitch certifier
674ebdd1 464CN = $cn
c8efec6e
QX
465
466[ v3_req ]
467subjectAltName = DNS:$cn
064af421
BP
468EOF
469 if test $keytype = rsa; then
99e5e05d
BP
470 (umask 077 && openssl genrsa -out "$1-privkey.pem" $bits) 1>&3 2>&3 \
471 || exit $?
064af421
BP
472 else
473 must_exist "$dsaparam"
99e5e05d
BP
474 (umask 077 && openssl gendsa -out "$1-privkey.pem" "$dsaparam") \
475 1>&3 2>&3 || exit $?
064af421 476 fi
99e5e05d
BP
477 openssl req -config "$TMP/req.cnf" -new -text \
478 -key "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3
064af421
BP
479}
480
481sign_request() {
482 must_exist "$1"
483 must_not_exist "$2"
484 pkidir_must_exist
485
8492d147 486 case "$1" in
d1cec3c6 487 /* | ?:[\\/]*)
8492d147
GS
488 request_file="$1"
489 ;;
490 *)
491 request_file="`pwd`/$1"
492 ;;
493 esac
494
064af421 495 (cd "$pkidir/${type}ca" &&
c8efec6e 496 openssl ca -config ca.cnf -extensions usr_cert -batch -in "$request_file") \
8492d147 497 > "$2.tmp$$" 2>&3
064af421
BP
498 mv "$2.tmp$$" "$2"
499}
500
501glob() {
bb60fa74 502 files=$(echo $1)
064af421
BP
503 if test "$files" != "$1"; then
504 echo "$files"
505 fi
506}
507
508exec 3>>$log || true
509if test "$command" = req; then
510 one_arg
511
512 make_request "$arg1"
513 fingerprint "$arg1-req.pem"
514elif test "$command" = sign; then
515 one_or_two_args
516 check_type "$arg2"
517 verify_fingerprint "$arg1-req.pem"
518
af32ce7a 519 sign_request "$arg1-req.pem" "$arg1-cert.pem"
064af421
BP
520elif 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"
528elif 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"
535elif test "$command" = fingerprint; then
536 one_arg
537
538 fingerprint "$arg1"
539elif 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"
c8efec6e
QX
544 make_tmpdir
545 cat > "$TMP/v3.ext" <<EOF
546subjectAltName = DNS:$arg1
547EOF
064af421 548
99e5e05d
BP
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" \
c8efec6e
QX
552 -signkey "$arg1-privkey.pem" -req -days 3650 -text \
553 -extfile $TMP/v3.ext) 2>&3 || exit $?
99e5e05d
BP
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"
064af421
BP
558else
559 echo "$0: $command command unknown; use --help for help" >&2
560 exit 1
561fi