/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
 
198
210
set -e
204
216
    cat >"$BATCHFILE" <<-EOF
205
217
        Key-Type: $KEYTYPE
206
218
        Key-Length: $KEYLENGTH
207
 
        #Key-Usage: encrypt,sign,auth
 
219
        Key-Usage: sign,auth
208
220
        Subkey-Type: $SUBKEYTYPE
209
221
        Subkey-Length: $SUBKEYLENGTH
210
 
        #Subkey-Usage: encrypt,sign,auth
 
222
        Subkey-Usage: encrypt
211
223
        Name-Real: $KEYNAME
212
224
        $KEYCOMMENTLINE
213
225
        $KEYEMAILLINE
216
228
        #Handle: <no-spaces>
217
229
        #%pubring pubring.gpg
218
230
        #%secring secring.gpg
 
231
        %no-protection
219
232
        %commit
220
233
        EOF
221
234
    
229
242
        date
230
243
    fi
231
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
232
290
    # Generate a new key in the key rings
233
291
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
234
292
        --homedir "$RINGDIR" --trust-model always \
270
328
fi
271
329
 
272
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
    
273
355
    # Import key into temporary key rings
274
356
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
275
357
        --homedir "$RINGDIR" --trust-model always --armor \
280
362
    
281
363
    # Get fingerprint of key
282
364
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
283
 
        --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
 
365
        --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
284
366
        --fingerprint --with-colons \
285
367
        | sed --quiet \
286
368
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
287
369
    
288
370
    test -n "$FINGERPRINT"
289
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
    
290
383
    FILECOMMENT="Encrypted password for a Mandos client"
291
384
    
292
 
    if [ -n "$PASSFILE" ]; then
293
 
        cat "$PASSFILE"
294
 
    else
295
 
        stty -echo
296
 
        echo -n "Enter passphrase: " >&2
297
 
        first="$(head --lines=1 | tr --delete '\n')"
298
 
        echo >&2
299
 
        echo -n "Repeat passphrase: " >&2
300
 
        second="$(head --lines=1 | tr --delete '\n')"
301
 
        echo >&2
302
 
        stty echo
303
 
        if [ "$first" != "$second" ]; then
304
 
            echo "Passphrase mismatch" >&2
305
 
            touch "$RINGDIR"/mismatch
 
385
    while [ ! -s "$SECFILE" ]; do
 
386
        if [ -n "$PASSFILE" ]; then
 
387
            cat "$PASSFILE"
306
388
        else
307
 
            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
308
416
        fi
309
 
    fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
310
 
        --homedir "$RINGDIR" --trust-model always --armor --encrypt \
311
 
        --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \
312
 
        > "$SECFILE"
313
 
    if [ -e "$RINGDIR"/mismatch ]; then
314
 
        rm --force "$RINGDIR"/mismatch
315
 
        exit 1
316
 
    fi
 
417
    done
317
418
    
318
419
    cat <<-EOF
319
420
        [$KEYNAME]
320
421
        host = $KEYNAME
 
422
        key_id = $KEY_ID
321
423
        fingerprint = $FINGERPRINT
322
424
        secret =
323
425
        EOF
330
432
                /^[^-]/s/^/    /p
331
433
            }
332
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
333
439
fi
334
440
 
335
441
trap - EXIT
340
446
    shred --remove "$SECFILE"
341
447
fi
342
448
# Remove the key rings
343
 
shred --remove "$RINGDIR"/sec*
 
449
shred --remove "$RINGDIR"/sec* 2>/dev/null
344
450
rm --recursive --force "$RINGDIR"