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