8 log='@LOGDIR@/ofp-pki.log'
12 # This option-parsing mechanism borrowed from a Autoconf-generated
13 # configure script under the following license:
15 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
16 # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
17 # This configure script is free software; the Free Software Foundation
18 # gives unlimited permission to copy, distribute and modify it.
20 # If the previous option needs an argument, assign it.
21 if test -n "$prev"; then
27 *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;;
31 case $dashdash$option in
36 ofp-pki, for managing a simple OpenFlow public key infrastructure
37 usage: $0 [OPTION...] COMMAND [ARG...]
39 The valid stand-alone commands and their arguments are:
40 init Initialize the PKI
41 req NAME Create new private key and certificate request
42 named NAME-privkey.pem and NAME-req.pem, resp.
43 sign NAME [TYPE] Sign switch certificate request NAME-req.pem,
44 producing certificate NAME-cert.pem
45 req+sign NAME [TYPE] Combine the above two steps, producing all three files.
46 verify NAME [TYPE] Checks that NAME-cert.pem is a valid TYPE certificate
47 fingerprint FILE Prints the fingerprint for FILE
48 self-sign NAME Sign NAME-req.pem with NAME-privkey.pem,
49 producing self-signed certificate NAME-cert.pem
51 The following additional commands manage an online PKI:
52 ls [PREFIX] [TYPE] Lists incoming requests of the given TYPE, optionally
53 limited to those whose fingerprint begins with PREFIX
54 flush [TYPE] Rejects all incoming requests of the given TYPE
55 reject PREFIX [TYPE] Rejects the incoming request(s) whose fingerprint begins
56 with PREFIX and has the given TYPE
57 approve PREFIX [TYPE] Approves the incoming request whose fingerprint begins
58 with PREFIX and has the given TYPE
59 expire [AGE] Rejects all incoming requests older than AGE, in
60 one of the forms Ns, Nmin, Nh, Nday (default: 1day)
61 prompt [TYPE] Interactively prompts to accept or reject each incoming
62 request of the given TYPE
64 Each TYPE above is a certificate type: 'switch' (default) or 'controller'.
66 Options for 'init', 'req', and 'req+sign' only:
67 -k, --key=rsa|dsa Type of keys to use (default: rsa)
68 -B, --bits=NBITS Number of bits in keys (default: 2048). For DSA keys,
69 this has an effect only on 'init'.
70 -D, --dsaparam=FILE File with DSA parameters (DSA only)
71 (default: dsaparam.pem within PKI directory)
72 Options for use with the 'sign' and 'approve' commands:
73 -b, --batch Skip fingerprint verification
74 Options that apply to any command:
75 -d, --dir=DIR Directory where the PKI is located
77 -f, --force Continue even if file or directory already exists
78 -l, --log=FILE Log openssl output to FILE (default: ofp-log.log)
79 -h, --help Print this usage message.
120 echo "unrecognized option $option" >&2
124 if test -z "$command"; then
126 elif test -z "${arg1+set}"; then
128 elif test -z "${arg2+set}"; then
131 echo "$option: only two arguments may be specified" >&2
138 if test -n "$prev"; then
139 option=--`echo $prev | sed 's/_/-/g'`
140 { echo "$as_me: error: missing argument to $option" >&2
141 { (exit 1); exit 1; }; }
143 if test -z "$command"; then
144 echo "$0: missing command name; use --help for help" >&2
147 if test "$keytype" != rsa && test "$keytype" != dsa; then
148 echo "$0: argument to -k or --key must be rsa or dsa"
151 if test "$bits" -lt 1024; then
152 echo "$0: argument to -B or --bits must be at least 1024"
155 if test -z "$dsaparam"; then
156 dsaparam=$pkidir/dsaparam.pem
159 if test "$command" = "init"; then
160 if test -e "$pkidir" && test "$force" != "yes"; then
161 echo "$0: $pkidir already exists and --force not specified" >&2
165 if test ! -d "$pkidir"; then
171 if test $keytype = dsa && test ! -e dsaparam.pem; then
172 echo "Generating DSA parameters, please wait..." >&2
173 openssl dsaparam -out dsaparam.pem $bits 1>&3 2>&3
177 for ca in controllerca switchca; do
178 echo "Creating $ca..." >&2
183 mkdir -p certs crl newcerts
184 mkdir -p -m 0700 private
185 mkdir -p -m 0733 incoming
187 test -e crlnumber || echo 01 > crlnumber
188 test -e serial || echo 01 > serial
190 # Put DSA parameters in directory.
191 if test $keytype = dsa && test ! -e dsaparam.pem; then
195 # Write CA configuration file.
196 if test ! -e ca.cnf; then
197 sed "s/@ca@/$ca/g" > ca.cnf <<'EOF'
200 distinguished_name = req_distinguished_name
202 [ req_distinguished_name ]
208 CN = OpenFlow @ca@ CA Certificate
215 database = $dir/index.txt # index file.
216 new_certs_dir = $dir/newcerts # new certs dir
217 certificate = $dir/cacert.pem # The CA cert
218 serial = $dir/serial # serial no file
219 private_key = $dir/private/cakey.pem# CA private key
220 RANDFILE = $dir/private/.rand # random number file
221 default_days = 365 # how long to certify for
222 default_crl_days= 30 # how long before next CRL
223 default_md = md5 # md to use
224 policy = policy # default policy
225 email_in_dn = no # Don't add the email into cert DN
226 name_opt = ca_default # Subject name display option
227 cert_opt = ca_default # Certificate display option
228 copy_extensions = none # Don't copy extensions from request
232 countryName = optional
233 stateOrProvinceName = optional
234 organizationName = match
235 organizationalUnitName = optional
236 commonName = supplied
237 emailAddress = optional
241 # Create certificate authority.
242 if test $keytype = dsa; then
243 newkey=dsa:dsaparam.pem
247 openssl req -config ca.cnf -nodes \
248 -newkey $newkey -keyout private/cakey.pem -out careq.pem \
250 openssl ca -config ca.cnf -create_serial -out cacert.pem \
251 -days 1095 -batch -keyfile private/cakey.pem -selfsign \
252 -infiles careq.pem 1>&3 2>&3
253 chmod 0700 private/cakey.pem
261 if test -z "$arg1" || test -n "$arg2"; then
262 echo "$0: $command must have exactly one argument; use --help for help" >&2
268 if test -n "$arg2"; then
269 echo "$0: $command must have zero or one arguments; use --help for help" >&2
275 if test -z "$arg1"; then
276 echo "$0: $command must have one or two arguments; use --help for help" >&2
282 if test -e "$1" && test "$force" != "yes"; then
283 echo "$0: $1 already exists and --force not supplied" >&2
289 test -n "$type" || exit 123 # Forgot to call check_type?
295 echo "Prefix $arg1 is too short (less than 4 hex digits)"
300 fingerprint=$(cd "$pkidir/${type}ca/incoming" && echo "$1"*-req.pem
301 | sed 's/-req\.pem$//')
304 echo "No certificate requests matching $1"
308 echo "$1 matches more than one certificate request:"
309 echo $fingerprint | sed 's/ /\
317 req="$pkidir/${type}ca/incoming/$fingerprint-req.pem"
318 cert="$pkidir/${type}ca/certs/$fingerprint-cert.pem"
322 TMP=/tmp/ofp-pki.tmp$$
331 local date=$(date -r $file)
333 if grep -q -e '-BEGIN CERTIFICATE-' "$file"; then
334 fingerprint=$(openssl x509 -noout -in "$file" -fingerprint |
335 sed 's/SHA1 Fingerprint=//' | tr -d ':')
337 fingerprint=$(sha1sum "$file" | awk '{print $1}')
339 printf "$name\\t$date\\n"
342 printf "\\t(correct fingerprint in filename)\\n"
345 printf "\\tfingerprint $fingerprint\\n"
350 verify_fingerprint() {
352 if test $batch != yes; then
353 echo "Does fingerprint match? (yes/no)"
355 if test "$answer" != yes; then
356 echo "Match failure, aborting" >&2
363 if test x = x"$1"; then
365 elif test "$1" = switch || test "$1" = controller; then
368 echo "$0: type argument must be 'switch' or 'controller'" >&2
374 number=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\1/')
375 unit=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\2/')
390 echo "$1: age not in the form Ns, Nmin, Nh, Nday (e.g. 1day)" >&2
394 echo $(($number * $factor))
398 if test ! -e "$1"; then
399 echo "$0: $1 does not exist" >&2
404 pkidir_must_exist() {
405 if test ! -e "$pkidir"; then
406 echo "$0: $pkidir does not exist (need to run 'init' or use '--dir'?)" >&2
408 elif test ! -d "$pkidir"; then
409 echo "$0: $pkidir is not a directory" >&2
415 must_not_exist "$arg1-privkey.pem"
416 must_not_exist "$arg1-req.pem"
418 cat > "$TMP/req.cnf" <<EOF
421 distinguished_name = req_distinguished_name
423 [ req_distinguished_name ]
428 OU = OpenFlow certifier
429 CN = OpenFlow certificate for $arg1
431 if test $keytype = rsa; then
434 must_exist "$dsaparam"
437 openssl req -config "$TMP/req.cnf" -text -nodes \
438 -newkey $newkey -keyout "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3
446 (cd "$pkidir/${type}ca" &&
447 openssl ca -config ca.cnf -batch -in /dev/stdin) \
448 < "$1" > "$2.tmp$$" 2>&3
453 local files=$(echo $1)
454 if test "$files" != "$1"; then
464 exec 3>>$pkidir/$log || true
468 if test "$command" = req; then
472 fingerprint "$arg1-req.pem"
473 elif test "$command" = sign; then
476 verify_fingerprint "$arg1-req.pem"
478 sign_request "$arg1-req.pem" "$arg2-cert.pem"
479 elif test "$command" = req+sign; then
485 sign_request "$arg1-req.pem" "$arg1-cert.pem"
486 fingerprint "$arg1-req.pem"
487 elif test "$command" = verify; then
489 must_exist "$arg1-cert.pem"
493 openssl verify -CAfile "$pkidir/${type}ca/cacert.pem" "$arg1-cert.pem"
494 elif test "$command" = fingerprint; then
498 elif test "$command" = self-sign; then
500 must_exist "$arg1-req.pem"
501 must_exist "$arg1-privkey.pem"
502 must_not_exist "$arg1-cert.pem"
504 openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem" \
505 -signkey "$arg1-privkey.pem" -req -text
506 elif test "$command" = ls; then
509 cd "$pkidir/${type}ca/incoming"
510 for file in $(glob "$arg1*-req.pem"); do
513 elif test "$command" = flush; then
516 rm -f "$pkidir/${type}ca/incoming/"*
517 elif test "$command" = reject; then
520 resolve_prefix "$arg1"
523 elif test "$command" = approve; then
526 resolve_prefix "$arg1"
529 cp "$req" "$TMP/$req"
530 verify_fingerprint "$TMP/$req"
531 sign_request "$TMP/$req"
532 rm -f "$req" "$TMP/$req"
533 elif test "$command" = prompt; then
538 cd "$pkidir/${type}ca/incoming"
539 for req in $(glob "*-req.pem"); do
540 cp "$req" "$TMP/$req"
542 cert=$(echo "$pkidir/${type}ca/certs/$req" |
543 sed 's/-req.pem/-cert.pem/')
544 if test -f $cert; then
545 echo "Request $req already approved--dropping duplicate request"
546 rm -f "$req" "$TMP/$req"
552 fingerprint "$TMP/$req" "$req"
553 printf "Disposition for this request (skip/approve/reject)? "
557 echo "Approving $req"
558 sign_request "$TMP/$req" "$cert"
559 rm -f "$req" "$TMP/$req"
562 echo "Rejecting $req"
563 rm -f "$req" "$TMP/$req"
570 elif test "$command" = expire; then
572 cutoff=$(($(date +%s) - $(parse_age ${arg1-1day})))
573 for type in switch controller; do
574 cd "$pkidir/${type}ca/incoming" || exit 1
575 for file in $(glob "*"); do
576 time=$(date -r "$file" +%s)
577 if test "$time" -lt "$cutoff"; then
583 echo "$0: $command command unknown; use --help for help" >&2