/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: 2021-02-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

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,2009 Teddy Hogeborn
6
 
# Copyright © 2008,2009 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-2019 Teddy Hogeborn
 
6
# Copyright © 2008-2019 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.0.9"
 
26
VERSION="1.8.14"
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
175
187
    
176
188
    # Create temporary gpg batch file
177
189
    BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
 
190
    TLS_PRIVKEYTMP="`mktemp -t mandos-keygen-privkey.XXXXXXXXXX`"
178
191
fi
179
192
 
180
193
if [ "$mode" = password ]; then
189
202
trap "
190
203
set +e; \
191
204
test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
192
 
shred --remove \"$RINGDIR\"/sec*;
 
205
test -n \"$TLS_PRIVKEYTMP\" && shred --remove \"$TLS_PRIVKEYTMP\"; \
 
206
shred --remove \"$RINGDIR\"/sec* 2>/dev/null;
193
207
test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
194
208
rm --recursive --force \"$RINGDIR\";
195
 
stty echo; \
 
209
tty --quiet && stty echo; \
196
210
" EXIT
197
211
 
 
212
set -e
 
213
 
198
214
umask 077
199
215
 
200
216
if [ "$mode" = keygen ]; then
202
218
    cat >"$BATCHFILE" <<-EOF
203
219
        Key-Type: $KEYTYPE
204
220
        Key-Length: $KEYLENGTH
205
 
        #Key-Usage: encrypt,sign,auth
 
221
        Key-Usage: sign,auth
206
222
        Subkey-Type: $SUBKEYTYPE
207
223
        Subkey-Length: $SUBKEYLENGTH
208
 
        #Subkey-Usage: encrypt,sign,auth
 
224
        Subkey-Usage: encrypt
209
225
        Name-Real: $KEYNAME
210
226
        $KEYCOMMENTLINE
211
227
        $KEYEMAILLINE
214
230
        #Handle: <no-spaces>
215
231
        #%pubring pubring.gpg
216
232
        #%secring secring.gpg
 
233
        %no-protection
217
234
        %commit
218
235
        EOF
219
236
    
 
237
    if tty --quiet; then
 
238
        cat <<-EOF
 
239
        Note: Due to entropy requirements, key generation could take
 
240
        anything from a few minutes to SEVERAL HOURS.  Please be
 
241
        patient and/or supply the system with more entropy if needed.
 
242
        EOF
 
243
        echo -n "Started: "
 
244
        date
 
245
    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 || :
 
256
        fi
 
257
        if cp --backup=numbered --force "$TLS_PUBKEYFILE" "$TLS_PUBKEYFILE" \
 
258
              2>/dev/null; then
 
259
            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
 
277
        fi
 
278
    fi
 
279
    
 
280
    # Make sure trustdb.gpg exists;
 
281
    # this is a workaround for Debian bug #737128
 
282
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
283
        --homedir "$RINGDIR" \
 
284
        --import-ownertrust < /dev/null
220
285
    # Generate a new key in the key rings
221
286
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
222
287
        --homedir "$RINGDIR" --trust-model always \
223
288
        --gen-key "$BATCHFILE"
224
289
    rm --force "$BATCHFILE"
225
290
    
 
291
    if tty --quiet; then
 
292
        echo -n "Finished: "
 
293
        date
 
294
    fi
 
295
    
226
296
    # Backup any old key files
227
297
    if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
228
298
        2>/dev/null; then
229
 
        shred --remove "$SECKEYFILE"
 
299
        shred --remove "$SECKEYFILE" 2>/dev/null || :
230
300
    fi
231
301
    if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
232
302
        2>/dev/null; then
253
323
fi
254
324
 
255
325
if [ "$mode" = password ]; then
 
326
    
 
327
    # Make SSH be 0 or 1
 
328
    case "$SSH" in
 
329
        [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) SSH=1;;
 
330
        [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) SSH=0;;
 
331
    esac
 
332
    
 
333
    if [ $SSH -eq 1 ]; then
 
334
        for ssh_keytype in ecdsa-sha2-nistp256 ed25519 rsa; do
 
335
            set +e
 
336
            ssh_fingerprint="`ssh-keyscan -t $ssh_keytype localhost 2>/dev/null`"
 
337
            err=$?
 
338
            set -e
 
339
            if [ $err -ne 0 ]; then
 
340
                ssh_fingerprint=""
 
341
                continue
 
342
            fi
 
343
            if [ -n "$ssh_fingerprint" ]; then
 
344
                ssh_fingerprint="${ssh_fingerprint#localhost }"
 
345
                break
 
346
            fi
 
347
        done
 
348
    fi
 
349
    
256
350
    # Import key into temporary key rings
257
351
    gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
258
352
        --homedir "$RINGDIR" --trust-model always --armor \
263
357
    
264
358
    # Get fingerprint of key
265
359
    FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
266
 
        --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
 
360
        --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
267
361
        --fingerprint --with-colons \
268
362
        | sed --quiet \
269
363
        --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
270
364
    
271
365
    test -n "$FINGERPRINT"
272
366
    
 
367
    if [ -r "$TLS_PUBKEYFILE" ]; then
 
368
       KEY_ID="$(certtool --key-id --hash=sha256 \
 
369
                       --infile="$TLS_PUBKEYFILE" 2>/dev/null || :)"
 
370
 
 
371
       if [ -z "$KEY_ID" ]; then
 
372
           KEY_ID=$(openssl pkey -pubin -in "$TLS_PUBKEYFILE" \
 
373
                            -outform der \
 
374
                        | openssl sha256 \
 
375
                        | sed --expression='s/^.*[^[:xdigit:]]//')
 
376
       fi
 
377
       test -n "$KEY_ID"
 
378
    fi
 
379
    
273
380
    FILECOMMENT="Encrypted password for a Mandos client"
274
381
    
275
 
    if [ -n "$PASSFILE" ]; then
276
 
        cat "$PASSFILE"
277
 
    else
278
 
        stty -echo
279
 
        echo -n "Enter passphrase: " >&2
280
 
        first="$(head --lines=1 | tr --delete '\n')"
281
 
        echo -n -e "\nRepeat passphrase: " >&2
282
 
        second="$(head --lines=1 | tr --delete '\n')"
283
 
        echo >&2
284
 
        stty echo
285
 
        if [ "$first" != "$second" ]; then
286
 
            echo -e "Passphrase mismatch" >&2
287
 
            touch "$RINGDIR"/mismatch
 
382
    while [ ! -s "$SECFILE" ]; do
 
383
        if [ -n "$PASSFILE" ]; then
 
384
            cat -- "$PASSFILE"
288
385
        else
289
 
            echo -n "$first"
 
386
            tty --quiet && stty -echo
 
387
            echo -n "Enter passphrase: " >/dev/tty
 
388
            read -r first
 
389
            tty --quiet && echo >&2
 
390
            echo -n "Repeat passphrase: " >/dev/tty
 
391
            read -r second
 
392
            if tty --quiet; then
 
393
                echo >&2
 
394
                stty echo
 
395
            fi
 
396
            if [ "$first" != "$second" ]; then
 
397
                echo "Passphrase mismatch" >&2
 
398
                touch "$RINGDIR"/mismatch
 
399
            else
 
400
                echo -n "$first"
 
401
            fi
 
402
        fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
 
403
            --homedir "$RINGDIR" --trust-model always --armor \
 
404
            --encrypt --sign --recipient "$FINGERPRINT" --comment \
 
405
            "$FILECOMMENT" > "$SECFILE"
 
406
        if [ -e "$RINGDIR"/mismatch ]; then
 
407
            rm --force "$RINGDIR"/mismatch
 
408
            if tty --quiet; then
 
409
                > "$SECFILE"
 
410
            else
 
411
                exit 1
 
412
            fi
290
413
        fi
291
 
    fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
292
 
        --homedir "$RINGDIR" --trust-model always --armor --encrypt \
293
 
        --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \
294
 
        > "$SECFILE"
295
 
    if [ -e "$RINGDIR"/mismatch ]; then
296
 
        rm --force "$RINGDIR"/mismatch
297
 
        exit 1
298
 
    fi
 
414
    done
299
415
    
300
416
    cat <<-EOF
301
417
        [$KEYNAME]
302
418
        host = $KEYNAME
 
419
        EOF
 
420
    if [ -n "$KEY_ID" ]; then
 
421
        echo "key_id = $KEY_ID"
 
422
    fi
 
423
    cat <<-EOF
303
424
        fingerprint = $FINGERPRINT
304
425
        secret =
305
426
        EOF
312
433
                /^[^-]/s/^/    /p
313
434
            }
314
435
        }' < "$SECFILE"
 
436
    if [ -n "$ssh_fingerprint" ]; then
 
437
        echo 'checker = ssh-keyscan -t '"$ssh_keytype"' %%(host)s 2>/dev/null | grep --fixed-strings --line-regexp --quiet --regexp=%%(host)s" %(ssh_fingerprint)s"'
 
438
        echo "ssh_fingerprint = ${ssh_fingerprint}"
 
439
    fi
315
440
fi
316
441
 
317
442
trap - EXIT
319
444
set +e
320
445
# Remove the password file, if any
321
446
if [ -n "$SECFILE" ]; then
322
 
    shred --remove "$SECFILE"
 
447
    shred --remove "$SECFILE" 2>/dev/null
323
448
fi
324
449
# Remove the key rings
325
 
shred --remove "$RINGDIR"/sec*
 
450
shred --remove "$RINGDIR"/sec* 2>/dev/null
326
451
rm --recursive --force "$RINGDIR"