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