#! /bin/sh
-# Copyright (c) 2008, 2009, 2010 Nicira Networks, Inc.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
log='@LOGDIR@/ovs-pki.log'
keytype=rsa
bits=2048
+
+# OS-specific compatibility routines
+case $(uname -s) in
+FreeBSD|NetBSD)
+ file_mod_epoch()
+ {
+ stat -r "$1" | awk '{print $10}'
+ }
+
+ file_mod_date()
+ {
+ stat -f '%Sm' "$1"
+ }
+
+ sha1sum()
+ {
+ sha1 "$@"
+ }
+ ;;
+*)
+ file_mod_epoch()
+ {
+ date -r "$1" +%s
+ }
+
+ file_mod_date()
+ {
+ date -r "$1"
+ }
+ ;;
+esac
+
for option; do
# This option-parsing mechanism borrowed from a Autoconf-generated
# configure script under the following license:
fingerprint FILE Prints the fingerprint for FILE
self-sign NAME Sign NAME-req.pem with NAME-privkey.pem,
producing self-signed certificate NAME-cert.pem
-
-The following additional commands manage an online PKI:
- ls [PREFIX] [TYPE] Lists incoming requests of the given TYPE, optionally
- limited to those whose fingerprint begins with PREFIX
- flush [TYPE] Rejects all incoming requests of the given TYPE
- reject PREFIX [TYPE] Rejects the incoming request(s) whose fingerprint begins
- with PREFIX and has the given TYPE
- approve PREFIX [TYPE] Approves the incoming request whose fingerprint begins
- with PREFIX and has the given TYPE
- expire [AGE] Rejects all incoming requests older than AGE, in
- one of the forms Ns, Nmin, Nh, Nday (default: 1day)
- prompt [TYPE] Interactively prompts to accept or reject each incoming
- request of the given TYPE
-
Each TYPE above is a certificate type: 'switch' (default) or 'controller'.
Options for 'init', 'req', and 'req+sign' only:
this has an effect only on 'init'.
-D, --dsaparam=FILE File with DSA parameters (DSA only)
(default: dsaparam.pem within PKI directory)
-Options for use with the 'sign' and 'approve' commands:
+Options for use with the 'sign' command:
-b, --batch Skip fingerprint verification
Options that apply to any command:
-d, --dir=DIR Directory where the PKI is located
-f, --force Continue even if file or directory already exists
-l, --log=FILE Log openssl output to FILE (default: ovs-log.log)
-h, --help Print this usage message.
+ -V, --version Display version information.
EOF
exit 0
;;
+ -V|--version)
+ echo "ovs-pki (Open vSwitch) @VERSION@"
+ exit 0
+ ;;
--di*=*)
pkidir=$optarg
;;
exit 1
fi
if test "$keytype" != rsa && test "$keytype" != dsa; then
- echo "$0: argument to -k or --key must be rsa or dsa"
+ echo "$0: argument to -k or --key must be rsa or dsa" >&2
exit 1
fi
if test "$bits" -lt 1024; then
- echo "$0: argument to -B or --bits must be at least 1024"
+ echo "$0: argument to -B or --bits must be at least 1024" >&2
exit 1
fi
if test -z "$dsaparam"; then
fi
case $log in
/*) ;;
- *) $log="$PWD/$log" ;;
+ *) log=`pwd`/$log ;;
esac
+logdir=$(dirname "$log")
+if test ! -d "$logdir"; then
+ mkdir -p -m755 "$logdir" 2>/dev/null || true
+ if test ! -d "$logdir"; then
+ echo "$0: log directory $logdir does not exist and cannot be created" >&2
+ exit 1
+ fi
+fi
+
if test "$command" = "init"; then
if test -e "$pkidir" && test "$force" != "yes"; then
echo "$0: $pkidir already exists and --force not specified" >&2
# Create the CAs.
for ca in controllerca switchca; do
echo "Creating $ca..." >&2
- oldpwd=$PWD
+ oldpwd=`pwd`
mkdir -p $ca
cd $ca
mkdir -p certs crl newcerts
mkdir -p -m 0700 private
- mkdir -p -m 0733 incoming
touch index.txt
test -e crlnumber || echo 01 > crlnumber
test -e serial || echo 01 > serial
serial = $dir/serial # serial no file
private_key = $dir/private/cakey.pem# CA private key
RANDFILE = $dir/private/.rand # random number file
-default_days = 365 # how long to certify for
+default_days = 3650 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = md5 # md to use
policy = policy # default policy
-newkey $newkey -keyout private/cakey.pem -out careq.pem \
1>&3 2>&3
openssl ca -config ca.cnf -create_serial -out cacert.pem \
- -days 2191 -batch -keyfile private/cakey.pem -selfsign \
+ -days 3650 -batch -keyfile private/cakey.pem -selfsign \
-infiles careq.pem 1>&3 2>&3
chmod 0700 private/cakey.pem
fi
}
-zero_or_one_args() {
- if test -n "$arg2"; then
- echo "$0: $command must have zero or one arguments; use --help for help" >&2
- exit 1
- fi
-}
-
one_or_two_args() {
if test -z "$arg1"; then
echo "$0: $command must have one or two arguments; use --help for help" >&2
fi
}
-resolve_prefix() {
- test -n "$type" || exit 123 # Forgot to call check_type?
-
- case $1 in
- ????*)
- ;;
- *)
- echo "Prefix $arg1 is too short (less than 4 hex digits)"
- exit 0
- ;;
- esac
-
- fingerprint=$(cd "$pkidir/${type}ca/incoming" && echo "$1"*-req.pem | sed 's/-req\.pem$//')
- case $fingerprint in
- "${1}*")
- echo "No certificate requests matching $1"
- exit 1
- ;;
- *" "*)
- echo "$1 matches more than one certificate request:"
- echo $fingerprint | sed 's/ /\
-/g'
- exit 1
- ;;
- *)
- # Nothing to do.
- ;;
- esac
- req="$pkidir/${type}ca/incoming/$fingerprint-req.pem"
- cert="$pkidir/${type}ca/certs/$fingerprint-cert.pem"
-}
-
make_tmpdir() {
TMP=/tmp/ovs-pki.tmp$$
rm -rf $TMP
fingerprint() {
file=$1
name=${1-$2}
- date=$(date -r $file)
- if grep -q -e '-BEGIN CERTIFICATE-' "$file"; then
+ date=$(file_mod_date "$file")
+ if grep -e '-BEGIN CERTIFICATE-' "$file" > /dev/null; then
fingerprint=$(openssl x509 -noout -in "$file" -fingerprint |
sed 's/SHA1 Fingerprint=//' | tr -d ':')
else
must_not_exist "$arg1-privkey.pem"
must_not_exist "$arg1-req.pem"
make_tmpdir
+ # Use uuidgen or date to create unique subject DNs.
+ unique=`(uuidgen) 2>/dev/null` || unique=`date +"%Y %b %d %T"`
cat > "$TMP/req.cnf" <<EOF
[ req ]
prompt = no
L = Palo Alto
O = Open vSwitch
OU = Open vSwitch certifier
-CN = Open vSwitch certificate for $arg1
+CN = $arg1 id:$unique
EOF
if test $keytype = rsa; then
- newkey=rsa:$bits
+ (umask 077 && openssl genrsa -out "$1-privkey.pem" $bits) 1>&3 2>&3 \
+ || exit $?
else
must_exist "$dsaparam"
- newkey=dsa:$dsaparam
+ (umask 077 && openssl gendsa -out "$1-privkey.pem" "$dsaparam") \
+ 1>&3 2>&3 || exit $?
fi
- openssl req -config "$TMP/req.cnf" -text -nodes \
- -newkey $newkey -keyout "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3
+ openssl req -config "$TMP/req.cnf" -new -text \
+ -key "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3
}
sign_request() {
must_exist "$arg1-privkey.pem"
must_not_exist "$arg1-cert.pem"
- openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem" \
- -signkey "$arg1-privkey.pem" -req -text 2>&3
-elif test "$command" = ls; then
- check_type "$arg2"
-
- cd "$pkidir/${type}ca/incoming"
- for file in $(glob "$arg1*-req.pem"); do
- fingerprint $file
- done
-elif test "$command" = flush; then
- check_type "$arg1"
-
- rm -f "$pkidir/${type}ca/incoming/"*
-elif test "$command" = reject; then
- one_or_two_args
- check_type "$arg2"
- resolve_prefix "$arg1"
-
- rm -f "$req"
-elif test "$command" = approve; then
- one_or_two_args
- check_type "$arg2"
- resolve_prefix "$arg1"
-
- make_tmpdir
- cp "$req" "$TMP/$req"
- verify_fingerprint "$TMP/$req"
- sign_request "$TMP/$req"
- rm -f "$req" "$TMP/$req"
-elif test "$command" = prompt; then
- zero_or_one_args
- check_type "$arg1"
-
- make_tmpdir
- cd "$pkidir/${type}ca/incoming"
- for req in $(glob "*-req.pem"); do
- cp "$req" "$TMP/$req"
-
- cert=$(echo "$pkidir/${type}ca/certs/$req" |
- sed 's/-req.pem/-cert.pem/')
- if test -f $cert; then
- echo "Request $req already approved--dropping duplicate request"
- rm -f "$req" "$TMP/$req"
- continue
- fi
+ # Create both the private key and certificate with restricted permissions.
+ (umask 077 && \
+ openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem.tmp" \
+ -signkey "$arg1-privkey.pem" -req -days 3650 -text) 2>&3 || exit $?
- echo
- echo
- fingerprint "$TMP/$req" "$req"
- printf "Disposition for this request (skip/approve/reject)? "
- read answer
- case $answer in
- approve)
- echo "Approving $req"
- sign_request "$TMP/$req" "$cert"
- rm -f "$req" "$TMP/$req"
- ;;
- r*)
- echo "Rejecting $req"
- rm -f "$req" "$TMP/$req"
- ;;
- *)
- echo "Skipping $req"
- ;;
- esac
- done
-elif test "$command" = expire; then
- zero_or_one_args
- cutoff=$(($(date +%s) - $(parse_age ${arg1-1day})))
- for type in switch controller; do
- cd "$pkidir/${type}ca/incoming" || exit 1
- for file in $(glob "*"); do
- time=$(date -r "$file" +%s)
- if test "$time" -lt "$cutoff"; then
- rm -f "$file"
- fi
- done
- done
+ # Reset the permissions on the certificate to the user's default.
+ cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem"
+ rm -f "$arg1-cert.pem.tmp"
else
echo "$0: $command command unknown; use --help for help" >&2
exit 1