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