/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:
2
2
3
3
# Mandos key generator - create new keys for a Mandos client
4
4
5
 
# Copyright © 2008-2019 Teddy Hogeborn
6
 
# Copyright © 2008-2019 Björn Påhlsson
 
5
# Copyright © 2008-2018 Teddy Hogeborn
 
6
# Copyright © 2008-2018 Björn Påhlsson
7
7
8
8
# This file is part of Mandos.
9
9
#
23
23
# Contact the authors at <mandos@recompile.se>.
24
24
25
25
 
26
 
VERSION="1.8.17"
 
26
VERSION="1.7.20"
27
27
 
28
28
KEYDIR="/etc/keys/mandos"
29
29
KEYTYPE=RSA
147
147
        echo "Empty key type" >&2
148
148
        exit 1
149
149
    fi
150
 
 
 
150
    
151
151
    if [ -z "$KEYNAME" ]; then
152
152
        echo "Empty key name" >&2
153
153
        exit 1
154
154
    fi
155
 
 
 
155
    
156
156
    if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then
157
157
        echo "Invalid key length" >&2
158
158
        exit 1
159
159
    fi
160
 
 
 
160
    
161
161
    if [ -z "$KEYEXPIRE" ]; then
162
162
        echo "Empty key expiration" >&2
163
163
        exit 1
164
164
    fi
165
 
 
 
165
    
166
166
    # Make FORCE be 0 or 1
167
167
    case "$FORCE" in
168
168
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;;
169
169
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
170
170
    esac
171
 
 
 
171
    
172
172
    if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ] \
173
173
             || [ -e "$TLS_PRIVKEYFILE" ] \
174
174
             || [ -e "$TLS_PUBKEYFILE" ]; } \
176
176
        echo "Refusing to overwrite old key files; use --force" >&2
177
177
        exit 1
178
178
    fi
179
 
 
 
179
    
180
180
    # Set lines for GnuPG batch file
181
181
    if [ -n "$KEYCOMMENT" ]; then
182
182
        KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT"
184
184
    if [ -n "$KEYEMAIL" ]; then
185
185
        KEYEMAILLINE="Name-Email: $KEYEMAIL"
186
186
    fi
187
 
 
 
187
    
188
188
    # Create temporary gpg batch file
189
189
    BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
190
 
    TLS_PRIVKEYTMP="`mktemp -t mandos-keygen-privkey.XXXXXXXXXX`"
191
190
fi
192
191
 
193
192
if [ "$mode" = password ]; then
202
201
trap "
203
202
set +e; \
204
203
test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
205
 
test -n \"$TLS_PRIVKEYTMP\" && shred --remove \"$TLS_PRIVKEYTMP\"; \
206
204
shred --remove \"$RINGDIR\"/sec* 2>/dev/null;
207
205
test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
208
206
rm --recursive --force \"$RINGDIR\";
233
231
        %no-protection
234
232
        %commit
235
233
        EOF
236
 
 
 
234
    
237
235
    if tty --quiet; then
238
236
        cat <<-EOF
239
237
        Note: Due to entropy requirements, key generation could take
243
241
        echo -n "Started: "
244
242
        date
245
243
    fi
246
 
 
247
 
    # Generate TLS private key
248
 
    if certtool --generate-privkey --password='' \
249
 
                --outfile "$TLS_PRIVKEYTMP" --sec-param ultra \
250
 
                --key-type="$TLS_KEYTYPE" --pkcs8 --no-text 2>/dev/null; then
251
 
        
252
 
        # Backup any old key files
253
 
        if cp --backup=numbered --force "$TLS_PRIVKEYFILE" "$TLS_PRIVKEYFILE" \
254
 
              2>/dev/null; then
255
 
            shred --remove "$TLS_PRIVKEYFILE" 2>/dev/null || :
 
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
256
267
        fi
257
 
        if cp --backup=numbered --force "$TLS_PUBKEYFILE" "$TLS_PUBKEYFILE" \
258
 
              2>/dev/null; then
 
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
259
279
            rm --force "$TLS_PUBKEYFILE"
260
 
        fi
261
 
        cp --archive "$TLS_PRIVKEYTMP" "$TLS_PRIVKEYFILE"
262
 
        shred --remove "$TLS_PRIVKEYTMP" 2>/dev/null || :
263
 
 
264
 
        ## TLS public key
265
 
 
266
 
        # First try certtool from GnuTLS
267
 
        if ! certtool --password='' --load-privkey="$TLS_PRIVKEYFILE" \
268
 
             --outfile="$TLS_PUBKEYFILE" --pubkey-info --no-text \
269
 
             2>/dev/null; then
270
 
            # Otherwise try OpenSSL
271
 
            if ! openssl pkey -in "$TLS_PRIVKEYFILE" \
272
 
                 -out "$TLS_PUBKEYFILE" -pubout; then
273
 
                rm --force "$TLS_PUBKEYFILE"
274
 
                # None of the commands succeded; give up
275
 
                return 1
276
 
            fi
 
280
            # None of the commands succeded; give up
 
281
            return 1
277
282
        fi
278
283
    fi
279
 
 
 
284
    
280
285
    # Make sure trustdb.gpg exists;
281
286
    # this is a workaround for Debian bug #737128
282
287
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
287
292
        --homedir "$RINGDIR" --trust-model always \
288
293
        --gen-key "$BATCHFILE"
289
294
    rm --force "$BATCHFILE"
290
 
 
 
295
    
291
296
    if tty --quiet; then
292
297
        echo -n "Finished: "
293
298
        date
294
299
    fi
295
 
 
 
300
    
296
301
    # Backup any old key files
297
302
    if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
298
303
        2>/dev/null; then
299
 
        shred --remove "$SECKEYFILE" 2>/dev/null || :
 
304
        shred --remove "$SECKEYFILE"
300
305
    fi
301
306
    if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
302
307
        2>/dev/null; then
303
308
        rm --force "$PUBKEYFILE"
304
309
    fi
305
 
 
 
310
    
306
311
    FILECOMMENT="Mandos client key for $KEYNAME"
307
312
    if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then
308
313
        FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)"
309
314
    fi
310
 
 
 
315
    
311
316
    if [ -n "$KEYEMAIL" ]; then
312
317
        FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
313
318
    fi
314
 
 
 
319
    
315
320
    # Export key from key rings to key files
316
321
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
317
322
        --homedir "$RINGDIR" --armor --export-options export-minimal \
323
328
fi
324
329
 
325
330
if [ "$mode" = password ]; then
326
 
 
 
331
    
327
332
    # Make SSH be 0 or 1
328
333
    case "$SSH" in
329
334
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) SSH=1;;
330
335
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) SSH=0;;
331
336
    esac
332
 
 
 
337
    
333
338
    if [ $SSH -eq 1 ]; then
334
 
        # The -q option is new in OpenSSH 9.8
335
 
        for ssh_keyscan_quiet in "-q " ""; do
336
 
            for ssh_keytype in ecdsa-sha2-nistp256 ed25519 rsa; do
337
 
                set +e
338
 
                ssh_fingerprint="`ssh-keyscan ${ssh_keyscan_quiet}-t $ssh_keytype localhost 2>/dev/null`"
339
 
                err=$?
340
 
                set -e
341
 
                if [ $err -ne 0 ]; then
342
 
                    ssh_fingerprint=""
343
 
                    continue
344
 
                fi
345
 
                if [ -n "$ssh_fingerprint" ]; then
346
 
                    ssh_fingerprint="${ssh_fingerprint#localhost }"
347
 
                    break 2
348
 
                fi
349
 
            done
 
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
350
352
        done
351
353
    fi
352
 
 
 
354
    
353
355
    # Import key into temporary key rings
354
356
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
355
357
        --homedir "$RINGDIR" --trust-model always --armor \
357
359
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
358
360
        --homedir "$RINGDIR" --trust-model always --armor \
359
361
        --import "$PUBKEYFILE"
360
 
 
 
362
    
361
363
    # Get fingerprint of key
362
364
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
363
365
        --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
364
366
        --fingerprint --with-colons \
365
367
        | sed --quiet \
366
368
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
367
 
 
 
369
    
368
370
    test -n "$FINGERPRINT"
369
 
 
370
 
    if [ -r "$TLS_PUBKEYFILE" ]; then
371
 
       KEY_ID="$(certtool --key-id --hash=sha256 \
 
371
    
 
372
    KEY_ID="$(certtool --key-id --hash=sha256 \
372
373
                       --infile="$TLS_PUBKEYFILE" 2>/dev/null || :)"
373
374
 
374
 
       if [ -z "$KEY_ID" ]; then
375
 
           KEY_ID=$(openssl pkey -pubin -in "$TLS_PUBKEYFILE" \
376
 
                            -outform der \
377
 
                        | openssl sha256 \
378
 
                        | sed --expression='s/^.*[^[:xdigit:]]//')
379
 
       fi
380
 
       test -n "$KEY_ID"
 
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:]]//')
381
380
    fi
382
 
 
 
381
    test -n "$KEY_ID"
 
382
    
383
383
    FILECOMMENT="Encrypted password for a Mandos client"
384
 
 
 
384
    
385
385
    while [ ! -s "$SECFILE" ]; do
386
386
        if [ -n "$PASSFILE" ]; then
387
 
            cat -- "$PASSFILE"
 
387
            cat "$PASSFILE"
388
388
        else
389
389
            tty --quiet && stty -echo
390
390
            echo -n "Enter passphrase: " >/dev/tty
400
400
                echo "Passphrase mismatch" >&2
401
401
                touch "$RINGDIR"/mismatch
402
402
            else
403
 
                printf "%s" "$first"
 
403
                echo -n "$first"
404
404
            fi
405
405
        fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
406
406
            --homedir "$RINGDIR" --trust-model always --armor \
415
415
            fi
416
416
        fi
417
417
    done
418
 
 
 
418
    
419
419
    cat <<-EOF
420
420
        [$KEYNAME]
421
421
        host = $KEYNAME
422
 
        EOF
423
 
    if [ -n "$KEY_ID" ]; then
424
 
        echo "key_id = $KEY_ID"
425
 
    fi
426
 
    cat <<-EOF
 
422
        key_id = $KEY_ID
427
423
        fingerprint = $FINGERPRINT
428
424
        secret =
429
425
        EOF
437
433
            }
438
434
        }' < "$SECFILE"
439
435
    if [ -n "$ssh_fingerprint" ]; then
440
 
        echo 'checker = ssh-keyscan '"$ssh_keyscan_quiet"'-t '"$ssh_keytype"' %%(host)s 2>/dev/null | grep --fixed-strings --line-regexp --quiet --regexp=%%(host)s" %(ssh_fingerprint)s"'
 
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"'
441
437
        echo "ssh_fingerprint = ${ssh_fingerprint}"
442
438
    fi
443
439
fi
447
443
set +e
448
444
# Remove the password file, if any
449
445
if [ -n "$SECFILE" ]; then
450
 
    shred --remove "$SECFILE" 2>/dev/null
 
446
    shred --remove "$SECFILE"
451
447
fi
452
448
# Remove the key rings
453
449
shred --remove "$RINGDIR"/sec* 2>/dev/null