/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-keygen

  • Committer: Teddy Hogeborn
  • Date: 2019-02-09 23:23:26 UTC
  • Revision ID: teddy@recompile.se-20190209232326-z1z2kzpgfixz7iaj
Add support for using raw public keys in TLS (RFC 7250)

Since GnuTLS removed support for OpenPGP keys in TLS (RFC 6091), and
no other library supports it, we have to change the protocol to use
something else.  We choose to use "raw public keys" (RFC 7250).  Since
we still use OpenPGP keys to decrypt the secret password, this means
that each client will have two keys: One OpenPGP key and one TLS
public/private key, and the key ID of the latter key is used to
identify clients instead of the fingerprint of the OpenPGP key.

Note that this code is still compatible with GnuTLS before version
3.6.0 (when OpenPGP key support was removed).  This commit merely adds
support for using raw pulic keys instead with GnuTLS 3.6.6. or later.

* DBUS-API (Signals/ClientNotFound): Change name of first parameter
                                     from "Fingerprint" to "KeyID".
  (Mandos Client Interface/Properties/KeyID): New.
* INSTALL: Document conflict with GnuTLS 3.6.0 (which removed OpenPGP
           key support) up until 3.6.6, when support for raw public
           keys was added.  Also document new dependency of client on
           "gnutls-bin" package (for certtool).
* Makefile (run-client): Depend on TLS key files, and also pass them
                         as arguments to client.
  (keydir/tls-privkey.pem, keydir/tls-pubkey.pem): New.
  (confdir/clients.conf): Add dependency on TLS public key.
  (purge-client): Add removal of TLS key files.
* clients.conf ([foo]/key_id, [bar]/key_id): New.
* debian/control (Source: mandos/Build-Depends): Also allow
                                                 libgnutls30 (>= 3.6.6)
  (Package: mandos/Depends): - '' -
  (Package: mandos/Description): Alter description to match new
                                 design.
  (Package: mandos-client/Description): - '' -
  (Package: mandos-client/Depends): Move "gnutls-bin | openssl" to
                                    here from "Recommends".
* debian/mandos-client.README.Debian: Add --tls-privkey and
                                      --tls-pubkey options to test
                                      command.
* debian/mandos-client.postinst (create_key): Renamed to "create_keys"
                                             (all callers changed),
                                             and also create TLS key.
* debian/mandos-client.postrm (purge): Also remove TLS key files.
* intro.xml (DESCRIPTION): Describe new dual-key design.
* mandos (GnuTLS): Define different functions depending on whether
                   support for raw public keys is detected.
  (Client.key_id): New attribute.
  (ClientDBus.KeyID_dbus_property): New method.
  (ProxyClient.__init__): Take new "key_id" parameter.
  (ClientHandler.handle): Use key IDs when using raw public keys and
                          use fingerprints when using OpenPGP keys.
  (ClientHandler.peer_certificate): Also handle raw public keys.
  (ClientHandler.key_id): New.
  (MandosServer.handle_ipc): Pass key ID over the pipe IPC.  Also
                             check for key ID matches when looking up
                             clients.
  (main): Default GnuTLS priority string depends on whether we are
          using raw public keys or not.  When unpickling clients, set
          key_id if not set in the pickle.
  (main/MandosDBusService.ClientNotFound): Change name of first
                                           parameter from
                                           "Fingerprint" to "KeyID".
* mandos-clients.conf.xml (OPTIONS): Document new "key_id" option.
  (OPTIONS/secret): Mention new key ID matchning.
  (EXPANSION/RUNTIME EXPANSION): Add new "key_id" option.
  (EXAMPLE): - '' -
* mandos-ctl (tablewords, main/keywords): Add new "KeyID" property.
* mandos-keygen: Create TLS key files.  New "--tls-keytype" (-T)
                 option.  Alter help text to be more clear about key
                 types.  When in password mode, also output "key_id"
                 option.
* mandos-keygen.xml (SYNOPSIS): Add new "--tls-keytype" (-T) option.
  (DESCRIPTION): Alter to match new dual-key design.
  (OVERVIEW): - '' -
  (FILES): Add TLS key files.
* mandos-options.xml (priority): Document new default priority string
                                 when using raw public keys.
* mandos.xml (NETWORK PROTOCOL): Describe new protocol using key ID.
  (BUGS): Remove issue about checking expire times of OpenPGP keys,
          since TLS public keys do not have expiration times.
  (SECURITY/CLIENT): Alter description to match new design.
  (SEE ALSO/GnuTLS): - '' -
  (SEE ALSO): Add reference to RFC 7250, and alter description of when
              RFC 6091 is used.
* overview.xml: Alter text to match new design.
* plugin-runner.xml (EXAMPLE): Add --tls-pubkey and --tls-privkey
                               options to mandos-client options.
* plugins.d/mandos-client.c: Use raw public keys when compiling with
                             supporting GnuTLS versions. Add new
                             "--tls-pubkey" and "--tls-privkey"
                             options (which do nothing if GnuTLS
                             library does not support raw public
                             keys).  Alter text throughout to reflect
                             new design.  Only generate new DH
                             parameters (based on size of OpenPGP key)
                             when using OpenPGP in TLS.  Default
                             GnuTLS priority string depends on whether
                             we are using raw public keys or not.
* plugins.d/mandos-client.xml (SYNOPSIS): Add new "--tls-privkey" (-t)
                                          and "--tls-pubkey" (-T)
                                          options.
  (DESCRIPTION): Describe new dual-key design.
  (OPTIONS): Document new "--tls-privkey" (-t) and "--tls-pubkey" (-T)
             options.
  (OPTIONS/--dh-bits): No longer necessarily depends on OpenPGP key
                       size.
  (FILES): Add default locations for TLS public and private key files.
  (EXAMPLE): Use new --tls-pubkey and --tls-privkey options.
  (SECURITY): Alter wording slightly to reflect new dual-key design.
  (SEE ALSO/GnuTLS): Alter description to match new design.
  (SEE ALSO): Add reference to RFC 7250, and alter description of when
              RFC 6091 is used.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/bin/sh -e
2
2
3
 
# Mandos key generator - create a new OpenPGP key for a Mandos client
4
 
5
 
# Copyright © 2008-2010 Teddy Hogeborn
6
 
# Copyright © 2008-2010 Björn Påhlsson
7
 
8
 
# This program is free software: you can redistribute it and/or modify
9
 
# it under the terms of the GNU General Public License as published by
 
3
# Mandos key generator - create new keys for a Mandos client
 
4
 
5
# Copyright © 2008-2018 Teddy Hogeborn
 
6
# Copyright © 2008-2018 Björn Påhlsson
 
7
 
8
# This file is part of Mandos.
 
9
#
 
10
# Mandos is free software: you can redistribute it and/or modify it
 
11
# under the terms of the GNU General Public License as published by
10
12
# the Free Software Foundation, either version 3 of the License, or
11
13
# (at your option) any later version.
12
14
#
13
 
#     This program is distributed in the hope that it will be useful,
14
 
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
#     Mandos is distributed in the hope that it will be useful, but
 
16
#     WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
18
#     GNU General Public License for more details.
17
19
18
20
# You should have received a copy of the GNU General Public License
19
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
21
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
20
22
21
 
# Contact the authors at <mandos@fukt.bsnet.se>.
 
23
# Contact the authors at <mandos@recompile.se>.
22
24
23
25
 
24
 
VERSION="1.2"
 
26
VERSION="1.7.20"
25
27
 
26
28
KEYDIR="/etc/keys/mandos"
27
 
KEYTYPE=DSA
28
 
KEYLENGTH=2048
29
 
SUBKEYTYPE=ELG-E
30
 
SUBKEYLENGTH=2048
 
29
KEYTYPE=RSA
 
30
KEYLENGTH=4096
 
31
SUBKEYTYPE=RSA
 
32
SUBKEYLENGTH=4096
31
33
KEYNAME="`hostname --fqdn 2>/dev/null || hostname`"
32
34
KEYEMAIL=""
33
 
KEYCOMMENT="Mandos client key"
 
35
KEYCOMMENT=""
34
36
KEYEXPIRE=0
 
37
TLS_KEYTYPE=ed25519
35
38
FORCE=no
 
39
SSH=yes
36
40
KEYCOMMENT_ORIG="$KEYCOMMENT"
37
41
mode=keygen
38
42
 
41
45
fi
42
46
 
43
47
# Parse options
44
 
TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:f \
45
 
    --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \
 
48
TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:T:fS \
 
49
    --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,tls-keytype:,force,no-ssh \
46
50
    --name "$0" -- "$@"`
47
51
 
48
52
help(){
49
 
basename="`basename $0`"
 
53
basename="`basename "$0"`"
50
54
cat <<EOF
51
55
Usage: $basename [ -v | --version ]
52
56
       $basename [ -h | --help ]
60
64
  -v, --version         Show program's version number and exit
61
65
  -h, --help            Show this help message and exit
62
66
  -d DIR, --dir DIR     Target directory for key files
63
 
  -t TYPE, --type TYPE  Key type.  Default is DSA.
 
67
  -t TYPE, --type TYPE  OpenPGP key type.  Default is RSA.
64
68
  -l BITS, --length BITS
65
 
                        Key length in bits.  Default is 2048.
 
69
                        OpenPGP key length in bits.  Default is 4096.
66
70
  -s TYPE, --subtype TYPE
67
 
                        Subkey type.  Default is ELG-E.
 
71
                        OpenPGP subkey type.  Default is RSA.
68
72
  -L BITS, --sublength BITS
69
 
                        Subkey length in bits.  Default is 2048.
 
73
                        OpenPGP subkey length in bits.  Default 4096.
70
74
  -n NAME, --name NAME  Name of key.  Default is the FQDN.
71
75
  -e ADDRESS, --email ADDRESS
72
 
                        Email address of key.  Default is empty.
 
76
                        Email address of OpenPGP key.  Default empty.
73
77
  -c TEXT, --comment TEXT
74
 
                        Comment field for key.  The default value is
75
 
                        "Mandos client key".
 
78
                        Comment field for OpenPGP key.  Default empty.
76
79
  -x TIME, --expire TIME
77
 
                        Key expire time.  Default is no expiration.
 
80
                        OpenPGP key expire time.  Default is none.
78
81
                        See gpg(1) for syntax.
 
82
  -T TYPE, --tls-keytype TYPE
 
83
                        TLS key type.  Default is ed25519.
79
84
  -f, --force           Force overwriting old key files.
80
85
 
81
86
Password creation options:
86
91
                        Encrypt a password from FILE using the key in
87
92
                        the key directory.  All options other than
88
93
                        --dir and --name are ignored.
 
94
  -S, --no-ssh          Don't get SSH key or set "checker" option.
89
95
EOF
90
96
}
91
97
 
103
109
        -e|--email) KEYEMAIL="$2"; shift 2;;
104
110
        -c|--comment) KEYCOMMENT="$2"; shift 2;;
105
111
        -x|--expire) KEYEXPIRE="$2"; shift 2;;
 
112
        -T|--tls-keytype) TLS_KEYTYPE="$2"; shift 2;;
106
113
        -f|--force) FORCE=yes; shift;;
 
114
        -S|--no-ssh) SSH=no; shift;;
107
115
        -v|--version) echo "$0 $VERSION"; exit;;
108
116
        -h|--help) help; exit;;
109
117
        --) shift; break;;
111
119
    esac
112
120
done
113
121
if [ "$#" -gt 0 ]; then
114
 
    echo "Unknown arguments: '$@'" >&2
 
122
    echo "Unknown arguments: '$*'" >&2
115
123
    exit 1
116
124
fi
117
125
 
118
126
SECKEYFILE="$KEYDIR/seckey.txt"
119
127
PUBKEYFILE="$KEYDIR/pubkey.txt"
 
128
TLS_PRIVKEYFILE="$KEYDIR/tls-privkey.pem"
 
129
TLS_PUBKEYFILE="$KEYDIR/tls-pubkey.pem"
120
130
 
121
131
# Check for some invalid values
122
132
if [ ! -d "$KEYDIR" ]; then
159
169
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
160
170
    esac
161
171
    
162
 
    if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \
163
 
        -a "$FORCE" -eq 0 ]; then
 
172
    if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ] \
 
173
             || [ -e "$TLS_PRIVKEYFILE" ] \
 
174
             || [ -e "$TLS_PUBKEYFILE" ]; } \
 
175
        && [ "$FORCE" -eq 0 ]; then
164
176
        echo "Refusing to overwrite old key files; use --force" >&2
165
177
        exit 1
166
178
    fi
189
201
trap "
190
202
set +e; \
191
203
test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
192
 
shred --remove \"$RINGDIR\"/sec*;
 
204
shred --remove \"$RINGDIR\"/sec* 2>/dev/null;
193
205
test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
194
206
rm --recursive --force \"$RINGDIR\";
195
 
stty echo; \
 
207
tty --quiet && stty echo; \
196
208
" EXIT
197
209
 
 
210
set -e
 
211
 
198
212
umask 077
199
213
 
200
214
if [ "$mode" = keygen ]; then
202
216
    cat >"$BATCHFILE" <<-EOF
203
217
        Key-Type: $KEYTYPE
204
218
        Key-Length: $KEYLENGTH
205
 
        #Key-Usage: encrypt,sign,auth
 
219
        Key-Usage: sign,auth
206
220
        Subkey-Type: $SUBKEYTYPE
207
221
        Subkey-Length: $SUBKEYLENGTH
208
 
        #Subkey-Usage: encrypt,sign,auth
 
222
        Subkey-Usage: encrypt
209
223
        Name-Real: $KEYNAME
210
224
        $KEYCOMMENTLINE
211
225
        $KEYEMAILLINE
214
228
        #Handle: <no-spaces>
215
229
        #%pubring pubring.gpg
216
230
        #%secring secring.gpg
 
231
        %no-protection
217
232
        %commit
218
233
        EOF
219
234
    
227
242
        date
228
243
    fi
229
244
    
 
245
    # Backup any old key files
 
246
    if cp --backup=numbered --force "$TLS_PRIVKEYFILE" "$TLS_PRIVKEYFILE" \
 
247
        2>/dev/null; then
 
248
        shred --remove "$TLS_PRIVKEYFILE"
 
249
    fi
 
250
    if cp --backup=numbered --force "$TLS_PUBKEYFILE" "$TLS_PUBKEYFILE" \
 
251
        2>/dev/null; then
 
252
        rm --force "$TLS_PUBKEYFILE"
 
253
    fi
 
254
 
 
255
    ## Generate TLS private key
 
256
 
 
257
    # First try certtool from GnuTLS
 
258
    if ! certtool --generate-privkey --password='' \
 
259
         --outfile "$TLS_PRIVKEYFILE" --sec-param ultra \
 
260
         --key-type="$TLS_KEYTYPE" --pkcs8 --no-text 2>/dev/null; then
 
261
        # Otherwise try OpenSSL
 
262
        if ! openssl genpkey -algorithm X25519 -out \
 
263
             /etc/keys/mandos/tls-privkey.pem; then
 
264
            rm --force /etc/keys/mandos/tls-privkey.pem
 
265
            # None of the commands succeded; give up
 
266
            return 1
 
267
        fi
 
268
    fi
 
269
 
 
270
    ## TLS public key
 
271
 
 
272
    # First try certtool from GnuTLS
 
273
    if ! certtool --password='' --load-privkey="$TLS_PRIVKEYFILE" \
 
274
         --outfile="$TLS_PUBKEYFILE" --pubkey-info --no-text \
 
275
         2>/dev/null; then
 
276
        # Otherwise try OpenSSL
 
277
        if ! openssl pkey -in "$TLS_PRIVKEYFILE" \
 
278
             -out "$TLS_PUBKEYFILE" -pubout; then
 
279
            rm --force "$TLS_PUBKEYFILE"
 
280
            # None of the commands succeded; give up
 
281
            return 1
 
282
        fi
 
283
    fi
 
284
    
 
285
    # Make sure trustdb.gpg exists;
 
286
    # this is a workaround for Debian bug #737128
 
287
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
288
        --homedir "$RINGDIR" \
 
289
        --import-ownertrust < /dev/null
230
290
    # Generate a new key in the key rings
231
291
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
232
292
        --homedir "$RINGDIR" --trust-model always \
268
328
fi
269
329
 
270
330
if [ "$mode" = password ]; then
 
331
    
 
332
    # Make SSH be 0 or 1
 
333
    case "$SSH" in
 
334
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) SSH=1;;
 
335
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) SSH=0;;
 
336
    esac
 
337
    
 
338
    if [ $SSH -eq 1 ]; then
 
339
        for ssh_keytype in ecdsa-sha2-nistp256 ed25519 rsa; do
 
340
            set +e
 
341
            ssh_fingerprint="`ssh-keyscan -t $ssh_keytype localhost 2>/dev/null`"
 
342
            err=$?
 
343
            set -e
 
344
            if [ $err -ne 0 ]; then
 
345
                ssh_fingerprint=""
 
346
                continue
 
347
            fi
 
348
            if [ -n "$ssh_fingerprint" ]; then
 
349
                ssh_fingerprint="${ssh_fingerprint#localhost }"
 
350
                break
 
351
            fi
 
352
        done
 
353
    fi
 
354
    
271
355
    # Import key into temporary key rings
272
356
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
273
357
        --homedir "$RINGDIR" --trust-model always --armor \
278
362
    
279
363
    # Get fingerprint of key
280
364
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
281
 
        --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
 
365
        --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
282
366
        --fingerprint --with-colons \
283
367
        | sed --quiet \
284
368
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
285
369
    
286
370
    test -n "$FINGERPRINT"
287
371
    
 
372
    KEY_ID="$(certtool --key-id --hash=sha256 \
 
373
                       --infile="$TLS_PUBKEYFILE" 2>/dev/null || :)"
 
374
 
 
375
    if [ -z "$KEY_ID" ]; then
 
376
        KEY_ID=$(openssl pkey -pubin -in /tmp/tls-pubkey.pem \
 
377
                         -outform der \
 
378
                     | openssl sha256 \
 
379
                     | sed --expression='s/^.*[^[:xdigit:]]//')
 
380
    fi
 
381
    test -n "$KEY_ID"
 
382
    
288
383
    FILECOMMENT="Encrypted password for a Mandos client"
289
384
    
290
 
    if [ -n "$PASSFILE" ]; then
291
 
        cat "$PASSFILE"
292
 
    else
293
 
        stty -echo
294
 
        echo -n "Enter passphrase: " >&2
295
 
        first="$(head --lines=1 | tr --delete '\n')"
296
 
        echo >&2
297
 
        echo -n "Repeat passphrase: " >&2
298
 
        second="$(head --lines=1 | tr --delete '\n')"
299
 
        echo >&2
300
 
        stty echo
301
 
        if [ "$first" != "$second" ]; then
302
 
            echo "Passphrase mismatch" >&2
303
 
            touch "$RINGDIR"/mismatch
 
385
    while [ ! -s "$SECFILE" ]; do
 
386
        if [ -n "$PASSFILE" ]; then
 
387
            cat "$PASSFILE"
304
388
        else
305
 
            echo -n "$first"
 
389
            tty --quiet && stty -echo
 
390
            echo -n "Enter passphrase: " >/dev/tty
 
391
            read -r first
 
392
            tty --quiet && echo >&2
 
393
            echo -n "Repeat passphrase: " >/dev/tty
 
394
            read -r second
 
395
            if tty --quiet; then
 
396
                echo >&2
 
397
                stty echo
 
398
            fi
 
399
            if [ "$first" != "$second" ]; then
 
400
                echo "Passphrase mismatch" >&2
 
401
                touch "$RINGDIR"/mismatch
 
402
            else
 
403
                echo -n "$first"
 
404
            fi
 
405
        fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
406
            --homedir "$RINGDIR" --trust-model always --armor \
 
407
            --encrypt --sign --recipient "$FINGERPRINT" --comment \
 
408
            "$FILECOMMENT" > "$SECFILE"
 
409
        if [ -e "$RINGDIR"/mismatch ]; then
 
410
            rm --force "$RINGDIR"/mismatch
 
411
            if tty --quiet; then
 
412
                > "$SECFILE"
 
413
            else
 
414
                exit 1
 
415
            fi
306
416
        fi
307
 
    fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
308
 
        --homedir "$RINGDIR" --trust-model always --armor --encrypt \
309
 
        --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \
310
 
        > "$SECFILE"
311
 
    if [ -e "$RINGDIR"/mismatch ]; then
312
 
        rm --force "$RINGDIR"/mismatch
313
 
        exit 1
314
 
    fi
 
417
    done
315
418
    
316
419
    cat <<-EOF
317
420
        [$KEYNAME]
318
421
        host = $KEYNAME
 
422
        key_id = $KEY_ID
319
423
        fingerprint = $FINGERPRINT
320
424
        secret =
321
425
        EOF
328
432
                /^[^-]/s/^/    /p
329
433
            }
330
434
        }' < "$SECFILE"
 
435
    if [ -n "$ssh_fingerprint" ]; then
 
436
        echo 'checker = ssh-keyscan -t '"$ssh_keytype"' %%(host)s 2>/dev/null | grep --fixed-strings --line-regexp --quiet --regexp=%%(host)s" %(ssh_fingerprint)s"'
 
437
        echo "ssh_fingerprint = ${ssh_fingerprint}"
 
438
    fi
331
439
fi
332
440
 
333
441
trap - EXIT
338
446
    shred --remove "$SECFILE"
339
447
fi
340
448
# Remove the key rings
341
 
shred --remove "$RINGDIR"/sec*
 
449
shred --remove "$RINGDIR"/sec* 2>/dev/null
342
450
rm --recursive --force "$RINGDIR"