10 # This option-parsing mechanism borrowed from a Autoconf-generated
11 # configure script under the following license:
13 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
14 # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
15 # This configure script is free software; the Free Software Foundation
16 # gives unlimited permission to copy, distribute and modify it.
18 # If the previous option needs an argument, assign it.
19 if test -n "$prev"; then
25 *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;;
29 case $dashdash$option in
34 ofp-pki, for managing a simple OpenFlow public key infrastructure
35 usage: $0 [OPTION...] COMMAND [ARG...]
37 The valid stand-alone commands and their arguments are:
38 init Initialize the PKI
39 req NAME Create new private key and certificate request
40 named NAME-privkey.pem and NAME-req.pem, resp.
41 sign NAME [TYPE] Sign switch certificate request NAME-req.pem,
42 producing certificate NAME-cert.pem
43 req+sign NAME [TYPE] Combine the above two steps, producing all three files.
44 verify NAME [TYPE] Checks that NAME-cert.pem is a valid TYPE certificate
45 fingerprint FILE Prints the fingerprint for FILE
47 The following additional commands manage an online PKI:
48 ls [PREFIX] [TYPE] Lists incoming requests of the given TYPE, optionally
49 limited to those whose fingerprint begins with PREFIX
50 flush [TYPE] Rejects all incoming requests of the given TYPE
51 reject PREFIX [TYPE] Rejects the incoming request(s) whose fingerprint begins
52 with PREFIX and has the given TYPE
53 approve PREFIX [TYPE] Approves the incoming request whose fingerprint begins
54 with PREFIX and has the given TYPE
55 expire [AGE] Rejects all incoming requests older than AGE, in
56 one of the forms Ns, Nmin, Nh, Nday (default: 1day)
57 prompt [TYPE] Interactively prompts to accept or reject each incoming
58 request of the given TYPE
60 Each TYPE above is a certificate type: 'switch' (default) or 'controller'.
62 The valid OPTIONS are:
63 -d, --dir=DIR Directory where the PKI is located
65 -f, --force Continue even if file or directory already exists
66 -b, --batch Skip fingerprint verification
67 -l, --log=FILE Log openssl output to FILE (default: ofp-log.log)
68 -h, --help Print this usage message.
91 echo "unrecognized option $option" >&2
95 if test -z "$command"; then
97 elif test -z "${arg1+set}"; then
99 elif test -z "${arg2+set}"; then
102 echo "$option: only two arguments may be specified" >&2
109 if test -n "$prev"; then
110 option=--`echo $prev | sed 's/_/-/g'`
111 { echo "$as_me: error: missing argument to $option" >&2
112 { (exit 1); exit 1; }; }
114 if test -z "$command"; then
115 echo "$0: missing command name; use --help for help" >&2
119 if test "$command" = "init"; then
120 if test -e "$pkidir" && test "$force" != "yes"; then
121 echo "$0: $pkidir already exists" >&2
125 if test ! -d "$pkidir"; then
131 if test ! -e dsaparam.pem; then
132 echo "Generating DSA parameters, please wait..." >&2
133 openssl dsaparam -out dsaparam.pem 2048 1>&3 2>&3
136 # Create the request configuration.
137 if test ! -e req.cnf; then
141 distinguished_name = req_distinguished_name
143 [ req_distinguished_name ]
148 OU = OpenFlow certifier
149 CN = OpenFlow certificate
154 for ca in controllerca switchca; do
155 echo "Creating $ca..." >&2
160 mkdir -p certs crl newcerts
161 mkdir -p -m 0700 private
162 mkdir -p -m 0733 incoming
164 test -e crlnumber || echo 01 > crlnumber
165 test -e serial || echo 01 > serial
167 # Put DSA parameters in directory.
168 if test ! -e dsaparam.pem; then
172 # Write CA configuration file.
173 if test ! -e ca.cnf; then
174 sed "s/@ca@/$ca/g" > ca.cnf <<'EOF'
177 distinguished_name = req_distinguished_name
179 [ req_distinguished_name ]
185 CN = OpenFlow @ca@ CA Certificate
192 database = $dir/index.txt # index file.
193 new_certs_dir = $dir/newcerts # new certs dir
194 certificate = $dir/cacert.pem # The CA cert
195 serial = $dir/serial # serial no file
196 private_key = $dir/private/cakey.pem# CA private key
197 RANDFILE = $dir/private/.rand # random number file
198 default_days = 365 # how long to certify for
199 default_crl_days= 30 # how long before next CRL
200 default_md = md5 # md to use
201 policy = policy # default policy
202 email_in_dn = no # Don't add the email into cert DN
203 name_opt = ca_default # Subject name display option
204 cert_opt = ca_default # Certificate display option
205 copy_extensions = none # Don't copy extensions from request
209 countryName = optional
210 stateOrProvinceName = optional
211 organizationName = match
212 organizationalUnitName = optional
213 commonName = supplied
214 emailAddress = optional
218 # Create certificate authority.
219 openssl req -config ca.cnf -nodes \
220 -newkey dsa:dsaparam.pem -keyout private/cakey.pem -out careq.pem \
222 openssl ca -config ca.cnf -create_serial -out cacert.pem \
223 -days 1095 -batch -keyfile private/cakey.pem -selfsign \
224 -infiles careq.pem 1>&3 2>&3
225 chmod 0700 private/cakey.pem
233 if test -z "$arg1" || test -n "$arg2"; then
234 echo "$0: $command must have exactly one argument; use --help for help" >&2
240 if test -n "$arg2"; then
241 echo "$0: $command must have zero or one arguments; use --help for help" >&2
247 if test -z "$arg1"; then
248 echo "$0: $command must have one or two arguments; use --help for help" >&2
254 if test -e "$1" && test "$force" != "yes"; then
255 echo "$0: $1 already exists and --force not supplied" >&2
261 test -n "$type" || exit 123 # Forgot to call check_type?
267 echo "Prefix $arg1 is too short (less than 4 hex digits)"
272 fingerprint=$(cd "$pkidir/${type}ca/incoming" && echo "$1"*-req.pem
273 | sed 's/-req\.pem$//')
276 echo "No certificate requests matching $1"
280 echo "$1 matches more than one certificate request:"
281 echo $fingerprint | sed 's/ /\
289 req="$pkidir/${type}ca/incoming/$fingerprint-req.pem"
290 cert="$pkidir/${type}ca/certs/$fingerprint-cert.pem"
294 TMP=/tmp/ofp-pki.tmp$$
303 local date=$(date -r $file)
305 if grep -q -e '-BEGIN CERTIFICATE-' "$file"; then
306 fingerprint=$(openssl x509 -noout -in "$file" -fingerprint |
307 sed 's/SHA1 Fingerprint=//' | tr -d ':')
309 fingerprint=$(sha1sum "$file" | awk '{print $1}')
311 printf "$name\\t$date\\n"
314 printf "\\t(correct fingerprint in filename)\\n"
317 printf "\\tfingerprint $fingerprint\\n"
322 verify_fingerprint() {
324 if test $batch != yes; then
325 echo "Does fingerprint match? (yes/no)"
327 if test "$answer" != yes; then
328 echo "Match failure, aborting" >&2
335 if test x = x"$1"; then
337 elif test "$1" = switch || test "$1" = controller; then
340 echo "$0: type argument must be 'switch' or 'controller'" >&2
346 number=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\1/')
347 unit=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\2/')
362 echo "$1: age not in the form Ns, Nmin, Nh, Nday (e.g. 1day)" >&2
366 echo $(($number * $factor))
370 if test ! -e "$1"; then
371 echo "$0: $1 does not exist" >&2
376 pkidir_must_exist() {
377 if test ! -e "$pkidir"; then
378 echo "$0: $pkidir does not exist (need to run 'init' or use '--dir'?)" >&2
380 elif test ! -d "$pkidir"; then
381 echo "$0: $pkidir is not a directory" >&2
387 must_not_exist "$arg1-privkey.pem"
388 must_not_exist "$arg1-req.pem"
390 openssl req -config "$pkidir/req.cnf" -text -nodes \
391 -newkey "dsa:$pkidir/dsaparam.pem" -keyout "$1-privkey.pem" \
392 -out "$1-req.pem" 1>&3 2>&3
400 (cd "$pkidir/${type}ca" &&
401 openssl ca -config ca.cnf -batch -in /dev/stdin) \
402 < "$1" > "$2.tmp$$" 2>&3
407 local files=$(echo $1)
408 if test "$files" != "$1"; then
418 exec 3>>$pkidir/$log || true
422 if test "$command" = req; then
426 fingerprint "$arg1-req.pem"
427 elif test "$command" = sign; then
430 verify_fingerprint "$arg1-req.pem"
432 sign_request "$arg1-req.pem" "$arg2-cert.pem"
433 elif test "$command" = req+sign; then
438 sign_request "$arg1-req.pem" "$arg1-cert.pem"
439 fingerprint "$arg1-req.pem"
440 elif test "$command" = verify; then
442 must_exist "$arg1-cert.pem"
446 openssl verify -CAfile "$pkidir/${type}ca/cacert.pem" "$arg1-cert.pem"
447 elif test "$command" = fingerprint; then
451 elif test "$command" = ls; then
454 cd "$pkidir/${type}ca/incoming"
455 for file in $(glob "$arg1*-req.pem"); do
458 elif test "$command" = flush; then
461 rm -f "$pkidir/${type}ca/incoming/"*
462 elif test "$command" = reject; then
465 resolve_prefix "$arg1"
468 elif test "$command" = approve; then
471 resolve_prefix "$arg1"
474 cp "$req" "$TMP/$req"
475 verify_fingerprint "$TMP/$req"
476 sign_request "$TMP/$req"
477 rm -f "$req" "$TMP/$req"
478 elif test "$command" = prompt; then
483 cd "$pkidir/${type}ca/incoming"
484 for req in $(glob "*-req.pem"); do
485 cp "$req" "$TMP/$req"
487 cert=$(echo "$pkidir/${type}ca/certs/$req" |
488 sed 's/-req.pem/-cert.pem/')
489 if test -f $cert; then
490 echo "Request $req already approved--dropping duplicate request"
491 rm -f "$req" "$TMP/$req"
497 fingerprint "$TMP/$req" "$req"
498 printf "Disposition for this request (skip/approve/reject)? "
502 echo "Approving $req"
503 sign_request "$TMP/$req" "$cert"
504 rm -f "$req" "$TMP/$req"
507 echo "Rejecting $req"
508 rm -f "$req" "$TMP/$req"
515 elif test "$command" = expire; then
517 cutoff=$(($(date +%s) - $(parse_age ${arg1-1day})))
518 for type in switch controller; do
519 cd "$pkidir/${type}ca/incoming" || exit 1
520 for file in $(glob "*"); do
521 time=$(date -r "$file" +%s)
522 if test "$time" -lt "$cutoff"; then
528 echo "$0: $command command unknown; use --help for help" >&2