]> git.proxmox.com Git - ovs.git/blob - utilities/ovs-pki.in
ovs-pki: Remove "online PKI" features and ovs-pki-cgi.
[ovs.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